diff --git a/.editorconfig b/.editorconfig index 4c92a8eef97a3..e95967a09e126 100644 --- a/.editorconfig +++ b/.editorconfig @@ -154,7 +154,7 @@ dotnet_diagnostic.IDE0055.severity = warning # https://github.com/dotnet/roslyn-analyzers/issues/7436 - False positives from valid GetDeclaredSymbol calls dotnet_diagnostic.RS1039.severity = none -# These xUnit analyzers were disabled temporarily to let us move to the +# These xUnit analyzers were disabled temporarily to let us move to the # new xUnit and get past several component governance issues. The # following issue tracks enabling them # diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 34ac25cb4275e..e5433ff66698f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,5 +1,6 @@ # Automatically request reviews when a pull request changes any owned files -# More information: https://github.com/blog/2392-introducing-code-owners + +# More information: https://github.com/blog/2392-introducing-code-owners *.yml @dotnet/roslyn-infrastructure .github/ @dotnet/roslyn-infrastructure @@ -15,15 +16,20 @@ scripts/ @dotnet/roslyn-infrastructure src/Analyzers/ @dotnet/roslyn-ide src/CodeStyle/ @dotnet/roslyn-ide src/Compilers/ @dotnet/roslyn-compiler + # Both IDE and Compiler traits are in this file, so we don't want to ping each other for changes to just this file. + # Technically this means that if someone changes _just_ this file no reviewers will be tagged, but this isn't likely + # to happen. + src/Compilers/Test/Core/Traits/Traits.cs src/EditorFeatures/ @dotnet/roslyn-ide src/Features/ @dotnet/roslyn-ide src/Interactive/ @dotnet/roslyn-interactive src/LanguageServer/ @dotnet/roslyn-ide src/NuGet/ @dotnet/roslyn-infrastructure +src/RoslynAnalyzers/ @dotnet/roslyn-ide src/Scripting/ @dotnet/roslyn-interactive src/Setup/ @dotnet/roslyn-infrastructure src/Tools/AnalyzerRunner @dotnet/roslyn-ide diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index 379cea47b9f11..b87713780d643 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -27,23 +27,6 @@ configuration: eventResponderTasks: - - description: Auto-approve auto-merge PRs - triggerOnOwnActions: false - if: - - payloadType: Pull_Request - - isPullRequest - - labelAdded: - label: auto-merge - - or: - - activitySenderHasPermission: - permission: Admin - - isActivitySender: - user: dotnet-bot - issueAuthor: False - then: - - approvePullRequest: - comment: Auto-approval - - description: Auto-approve maestro PRs triggerOnOwnActions: false if: @@ -80,7 +63,7 @@ configuration: - addMilestone: milestone: Next - - description: Auto-approve OneLoc PRs + - description: Auto-approve/merge OneLoc PRs triggerOnOwnActions: false if: - payloadType: Pull_Request @@ -94,8 +77,29 @@ configuration: - isAction: action: Opened then: - - addLabel: - label: auto-merge + - approvePullRequest: + comment: Auto-approve + - enableAutoMerge: + mergeMethod: merge + + - description: Auto-approve/merge automated merge PRs + triggerOnOwnActions: false + if: + - payloadType: Pull_Request + - isPullRequest + - isActivitySender: + user: github-actions[bot] + issueAuthor: False + - titleContains: + pattern: "[automated] Merge branch" + isRegex: False + - isAction: + action: Opened + then: + - approvePullRequest: + comment: Auto-approve + - enableAutoMerge: + mergeMethod: merge - description: Remove "Need More Info" on comment triggerOnOwnActions: false @@ -147,7 +151,7 @@ configuration: issueAuthor: False - not: isActivitySender: - user: github-actions + user: github-actions[bot] issueAuthor: False then: - addLabel: @@ -168,7 +172,7 @@ configuration: issueAuthor: False - not: isActivitySender: - user: github-actions + user: github-actions[bot] issueAuthor: False - or: - isAction: diff --git a/.github/workflows/main-merge.yml b/.github/workflows/main-merge.yml index 21dfaefe4cd4c..8f90cffb5883b 100644 --- a/.github/workflows/main-merge.yml +++ b/.github/workflows/main-merge.yml @@ -1,18 +1,25 @@ -# Merges any changes from release/prerelease to main (e.g. servicing changes) +# See https://github.com/dotnet/arcade/blob/e52018a/Documentation/Maestro/New-Inter-Branch-Merge-Approach.md -name: Flow main to release/dev18.0 +name: Inter-branch merge on: schedule: - # once a day at 13:00 UTC to cleanup old runs - - cron: '0 13 * * *' + # Create a merge every 3 hours (works only for merges from `main`, others would need a `push` trigger). + - cron: '0 */3 * * *' workflow_dispatch: + inputs: + configuration_file_branch: + description: 'Branch to use for configuration file' + required: true + default: 'main' permissions: contents: write pull-requests: write jobs: - check-script: + merge: + if: github.repository == 'dotnet/roslyn' uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@main with: - configuration_file_path: '.config/branch-merge.json' \ No newline at end of file + configuration_file_path: 'eng/config/branch-merge.jsonc' + configuration_file_branch: ${{ inputs.configuration_file_branch || 'main' }} diff --git a/Roslyn.sln b/Roslyn.sln index e78ca2d58f566..704cad8e79ba5 100644 --- a/Roslyn.sln +++ b/Roslyn.sln @@ -352,8 +352,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalAccess", "ExternalA EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.FSharp", "src\VisualStudio\ExternalAccess\FSharp\Microsoft.CodeAnalysis.ExternalAccess.FSharp.csproj", "{BD8CE303-5F04-45EC-8DCF-73C9164CD614}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Razor", "src\Tools\ExternalAccess\Razor\Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj", "{2FB6C157-DF91-4B1C-9827-A4D1C08C73EC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LanguageServices.CodeLens", "src\VisualStudio\CodeLens\Microsoft.VisualStudio.LanguageServices.CodeLens.csproj", "{5E6E9184-DEC5-4EC5-B0A4-77CFDC8CDEBE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.LanguageServer.Protocol", "src\LanguageServer\Protocol\Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj", "{686BF57E-A6FF-467B-AAB3-44DE916A9772}" @@ -579,8 +577,158 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.Cont EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Contracts.Package", "src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.Package.csproj", "{A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA}" EndProject +Project("{9a19103f-16f7-4668-be54-9a1e7a4f7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Razor", "src\Tools\ExternalAccess\Razor\EditorFeatures\Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj", "{068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared", "src\Tools\ExternalAccess\Razor\Shared\Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.shproj", "{4853A78A-4EC4-4D86-9F02-D0DDEAE85520}" +EndProject +Project("{9a19103f-16f7-4668-be54-9a1e7a4f7556}") = "Microsoft.CodeAnalysis.ExternalAccess.Razor.Features", "src\Tools\ExternalAccess\Razor\Features\Microsoft.CodeAnalysis.ExternalAccess.Razor.Features.csproj", "{D5A8E20C-E8D2-4A57-906A-263994D8731D}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.LanguageServices.ExternalAccess.Copilot", "src\VisualStudio\ExternalAccess\Copilot\Microsoft.VisualStudio.LanguageServices.ExternalAccess.Copilot.csproj", "{9EB058F3-10C9-8F3F-AD9E-72CB362A0928}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Text.Analyzers", "src\RoslynAnalyzers\Text.Analyzers\Core\Text.Analyzers.csproj", "{8087BDE4-6707-05A5-5F84-DFE6628E8EC8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Text.CSharp.Analyzers", "src\RoslynAnalyzers\Text.Analyzers\CSharp\Text.CSharp.Analyzers.csproj", "{B095320B-6854-EBEC-98DB-26E06D97183A}" +EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Text.VisualBasic.Analyzers", "src\RoslynAnalyzers\Text.Analyzers\VisualBasic\Text.VisualBasic.Analyzers.vbproj", "{451EC146-D85B-EA72-D064-0A75E9F52C7F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Text.Analyzers", "Text.Analyzers", "{6FE0875A-E178-4766-BCC6-87D37F273102}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Text.Analyzers.Setup", "src\RoslynAnalyzers\Text.Analyzers\Setup\Text.Analyzers.Setup.csproj", "{2C3B6892-A23A-9A8F-431E-30869231F832}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Text.Analyzers.UnitTests", "src\RoslynAnalyzers\Text.Analyzers\UnitTests\Text.Analyzers.UnitTests.csproj", "{DE29FFE1-ABB8-3B45-0D58-768727CA8DB1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PublicApiAnalyzers", "src\RoslynAnalyzers\PublicApiAnalyzers\Core\Analyzers\Microsoft.CodeAnalysis.PublicApiAnalyzers.csproj", "{CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PublicApiAnalyzers", "PublicApiAnalyzers", "{0AD92ED3-387B-4975-BA2E-68A76E727959}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes", "src\RoslynAnalyzers\PublicApiAnalyzers\Core\CodeFixes\Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes.csproj", "{6167C216-0C39-EBBA-2841-A8F864642DF1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PublicApiAnalyzers.Setup", "src\RoslynAnalyzers\PublicApiAnalyzers\Setup\Microsoft.CodeAnalysis.PublicApiAnalyzers.Setup.csproj", "{B3AADD0B-E103-A0F9-385F-1D18262B9C41}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests", "src\RoslynAnalyzers\PublicApiAnalyzers\UnitTests\Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests.csproj", "{E8D25897-E52E-47DE-B3B8-CE916C9B9B80}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers", "src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\Core\Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.csproj", "{A6844326-E313-F10A-7E99-89F54C73774C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PerformanceSensitiveAnalyzers", "PerformanceSensitiveAnalyzers", "{76B913A2-E33D-4C48-B206-8F14F0F94B06}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers", "src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\CSharp\Analyzers\Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.csproj", "{C7CAB909-965B-8674-56F1-98F3BE0BD87A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes", "src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\CSharp\CodeFixes\Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes.csproj", "{24CB433D-438B-04D0-29F7-9F29EC7DC3C6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Setup", "src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\Setup\Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Setup.csproj", "{9A98EE2B-BBBC-0D54-60D8-C36ABE80B340}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests", "src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\UnitTests\Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.csproj", "{C5591759-58B0-73D0-6EDC-4FFCBE819303}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.Diagnostics.Analyzers", "src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\Core\Roslyn.Diagnostics.Analyzers.csproj", "{AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Roslyn.Diagnostic.Analyzers", "Roslyn.Diagnostic.Analyzers", "{56053920-5095-430D-9C8B-AFB2C40407B4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.Diagnostics.CSharp.Analyzers", "src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\CSharp\Roslyn.Diagnostics.CSharp.Analyzers.csproj", "{DEE77B19-9559-D47D-8EC4-C207152A9C2C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.Diagnostics.Analyzers.Setup", "src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\Setup\Roslyn.Diagnostics.Analyzers.Setup.csproj", "{93256CB0-F5C1-CF7B-1D19-307E6E01191B}" +EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Roslyn.Diagnostics.VisualBasic.Analyzers", "src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\VisualBasic\Roslyn.Diagnostics.VisualBasic.Analyzers.vbproj", "{A4A3650E-164F-9339-FA7B-606EFFD06268}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.Diagnostics.Analyzers.UnitTests", "src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\UnitTests\Roslyn.Diagnostics.Analyzers.UnitTests.csproj", "{B0453D30-45FE-B2BC-5404-4A9C985D7BFF}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Analyzer.Utilities", "src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.shproj", "{08735294-3E6B-4420-9916-E7B8C4EB874D}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Analyzer.CSharp.Utilities", "src\RoslynAnalyzers\Utilities\Compiler.CSharp\Analyzer.CSharp.Utilities.shproj", "{BE5FE32C-DA73-4EC5-809E-E11B05ACA398}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "FlowAnalysis.Utilities", "src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis.Utilities.shproj", "{FCB56CBA-FA35-46A8-86B7-BAE5433197D9}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Refactoring.Utilities", "src\RoslynAnalyzers\Utilities\Refactoring\Refactoring.Utilities.shproj", "{68528C1C-B163-49A6-A19D-24E10F500F90}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Refactoring.CSharp.Utilities", "src\RoslynAnalyzers\Utilities\Refactoring.CSharp\Refactoring.CSharp.Utilities.shproj", "{3055F932-0D1E-4823-A03A-7B62C7639BDA}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Refactoring.VisualBasic.Utilities", "src\RoslynAnalyzers\Utilities\Refactoring.VisualBasic\Refactoring.VisualBasic.Utilities.shproj", "{4C362C30-C4B1-4C4B-A545-DBF67C7E9153}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Workspaces.Utilities", "src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.shproj", "{9C4BC501-C0A6-4130-BA68-EF5FB0C640B0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{0DDCFE67-7D4E-4709-9882-EC032A031789}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Analyzer.Utilities.UnitTests", "src\RoslynAnalyzers\Utilities.UnitTests\Analyzer.Utilities.UnitTests.csproj", "{34A07D0C-32E6-E19B-337E-4E6CB722C606}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResxSourceGenerator", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator.csproj", "{FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ResxSourceGenerator", "ResxSourceGenerator", "{616B9154-1EE8-45AE-9299-EBD0D51446E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp\Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp.csproj", "{5DB3749D-E83C-7093-75B9-B617D1FB72C0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic\Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic.csproj", "{DD05B30B-7C7D-CFF3-9C36-839D54A13B0C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests\Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests.csproj", "{7BA618DE-0BC8-886C-36DD-989B9C05FA96}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.BannedApiAnalyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers\Core\Microsoft.CodeAnalysis.BannedApiAnalyzers.csproj", "{8CE66034-25E2-8FF2-93A1-0BD3672AD86D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BannedApiAnalyzers", "BannedApiAnalyzers", "{FBF49D7B-F60C-41B5-8CEF-F1D41F84A387}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers\CSharp\Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.csproj", "{3C4BCE69-B353-B5AA-E824-209A6D627BA3}" +EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers\VisualBasic\Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.vbproj", "{E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.BannedApiAnalyzers.Setup", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers\Setup\Microsoft.CodeAnalysis.BannedApiAnalyzers.Setup.csproj", "{D21CCB70-1EDB-8D13-F00F-CA280DE4C83E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers\UnitTests\Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests.csproj", "{801E5330-F8E0-CC40-CC9A-47FE42284871}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.AnalyzerUtilities", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.AnalyzerUtilities\Microsoft.CodeAnalysis.AnalyzerUtilities.csproj", "{0A1267E9-52FF-B8DE-8522-802BE55F41DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenerateAnalyzerNuspec", "src\RoslynAnalyzers\Tools\GenerateAnalyzerNuspec\GenerateAnalyzerNuspec.csproj", "{4312B163-9507-6A25-893B-C18ECBF896BF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Metrics", "src\RoslynAnalyzers\Tools\Metrics\Metrics.csproj", "{7005DD7B-D3B6-1360-313B-975974AA6254}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RulesetToEditorconfigConverter", "src\RoslynAnalyzers\Tools\RulesetToEditorconfigConverter\Source\RulesetToEditorconfigConverter.csproj", "{10E2810B-AEDC-AE32-911A-750DF29322F0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RulesetToEditorconfigConverter.UnitTests", "src\RoslynAnalyzers\Tools\RulesetToEditorconfigConverter\Tests\RulesetToEditorconfigConverter.UnitTests.csproj", "{6A85289A-97BE-DC01-FB92-7FD258FCDDB7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{482C1FC7-4FD6-4381-8078-73BEBFAF4349}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Analyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\Core\Microsoft.CodeAnalysis.Analyzers.csproj", "{923E34BA-CA8A-971E-7FF7-51DD346394A1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.CodeAnalysis.Analyzers", "Microsoft.CodeAnalysis.Analyzers", "{A4E65246-D2F6-492F-8BD5-FE11DD89A19B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.CSharp.Analyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\CSharp\Microsoft.CodeAnalysis.CSharp.Analyzers.csproj", "{CBC75308-5C91-83F0-5472-39C9445C8FFC}" +EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Microsoft.CodeAnalysis.VisualBasic.Analyzers", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\VisualBasic\Microsoft.CodeAnalysis.VisualBasic.Analyzers.vbproj", "{BB08E86A-946F-C3BD-C607-989716B05D46}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Analyzers.UnitTests", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\UnitTests\Microsoft.CodeAnalysis.Analyzers.UnitTests.csproj", "{290ED315-ED26-F504-0583-848669298657}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Analyzers.Setup", "src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\Setup\Microsoft.CodeAnalysis.Analyzers.Setup.csproj", "{E234FD18-B133-4CF4-2AC1-7E219BF54242}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Metrics.Legacy", "src\RoslynAnalyzers\Tools\Metrics.Legacy\Metrics.Legacy.csproj", "{8252A386-069A-0D7D-EAB4-B2E77833B443}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test.Utilities", "src\RoslynAnalyzers\Test.Utilities\Test.Utilities.csproj", "{3F65D29D-ED95-2D6F-B927-47C5CF070AA5}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{7F040742-3C10-406A-A0FF-7FE621DEF671}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Analyzers.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.Analyzers\Microsoft.CodeAnalysis.Analyzers.Package.csproj", "{BEF66A04-80EF-F6FD-E969-F441F709ECDA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.AnalyzerUtilities.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.AnalyzerUtilities\Microsoft.CodeAnalysis.AnalyzerUtilities.Package.csproj", "{612E7B7D-8BE1-C279-2BD9-6191A97F2D03}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.BannedApiAnalyzers.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.BannedApiAnalyzers\Microsoft.CodeAnalysis.BannedApiAnalyzers.Package.csproj", "{491BC06C-DEEC-0D65-985A-E192EC710BEF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.Metrics.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.Metrics\Microsoft.CodeAnalysis.Metrics.Package.csproj", "{E297099A-564C-A98A-BED5-547AD4799C81}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.ResxSourceGenerator.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator.Package.csproj", "{839F00DC-B547-E6C3-AB0B-EFABD62C5C82}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.RulesetToEditorconfigConverter.Package", "src\RoslynAnalyzers\NuGet\Microsoft.CodeAnalysis.RulesetToEditorconfigConverter\Microsoft.CodeAnalysis.RulesetToEditorconfigConverter.Package.csproj", "{5F15CE31-9DF5-CD0F-F998-8BF358F11752}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Package", "src\RoslynAnalyzers\NuGet\PerformanceSensitiveAnalyzers\Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Package.csproj", "{B2C73083-9F69-D41B-572D-050157413338}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CodeAnalysis.PublicApiAnalyzers.Package", "src\RoslynAnalyzers\NuGet\PublicApiAnalyzers\Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj", "{E27FA26C-6E34-F3F5-3005-67EF18A5CB8B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Roslyn.Diagnostics.Analyzers.Package", "src\RoslynAnalyzers\NuGet\Roslyn.Diagnostics.Analyzers\Roslyn.Diagnostics.Analyzers.Package.csproj", "{295E850B-D94D-3328-75AB-62828B4B85BA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Text.Analyzers.Package", "src\RoslynAnalyzers\NuGet\Text.Analyzers\Text.Analyzers.Package.csproj", "{A8C385EE-1798-2802-5533-9E9A12CC603E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenerateDocumentationAndConfigFiles", "src\RoslynAnalyzers\Tools\GenerateDocumentationAndConfigFiles\GenerateDocumentationAndConfigFiles.csproj", "{A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GenerateDocumentationAndConfigFilesForBrokenRuntime", "src\RoslynAnalyzers\Tools\GenerateDocumentationAndConfigFilesForBrokenRuntime\GenerateDocumentationAndConfigFilesForBrokenRuntime.csproj", "{29080628-23A6-1DCB-F15E-93F1D1682CC1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestReferenceAssembly", "src\RoslynAnalyzers\TestReferenceAssembly\TestReferenceAssembly.csproj", "{31EB654C-B562-73B4-2456-78FA875515D2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -1111,10 +1259,6 @@ Global {BD8CE303-5F04-45EC-8DCF-73C9164CD614}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD8CE303-5F04-45EC-8DCF-73C9164CD614}.Release|Any CPU.ActiveCfg = Release|Any CPU {BD8CE303-5F04-45EC-8DCF-73C9164CD614}.Release|Any CPU.Build.0 = Release|Any CPU - {2FB6C157-DF91-4B1C-9827-A4D1C08C73EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2FB6C157-DF91-4B1C-9827-A4D1C08C73EC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2FB6C157-DF91-4B1C-9827-A4D1C08C73EC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2FB6C157-DF91-4B1C-9827-A4D1C08C73EC}.Release|Any CPU.Build.0 = Release|Any CPU {5E6E9184-DEC5-4EC5-B0A4-77CFDC8CDEBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5E6E9184-DEC5-4EC5-B0A4-77CFDC8CDEBE}.Debug|Any CPU.Build.0 = Debug|Any CPU {5E6E9184-DEC5-4EC5-B0A4-77CFDC8CDEBE}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -1427,6 +1571,230 @@ Global {9EB058F3-10C9-8F3F-AD9E-72CB362A0928}.Debug|Any CPU.Build.0 = Debug|Any CPU {9EB058F3-10C9-8F3F-AD9E-72CB362A0928}.Release|Any CPU.ActiveCfg = Release|Any CPU {9EB058F3-10C9-8F3F-AD9E-72CB362A0928}.Release|Any CPU.Build.0 = Release|Any CPU + {8087BDE4-6707-05A5-5F84-DFE6628E8EC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8087BDE4-6707-05A5-5F84-DFE6628E8EC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8087BDE4-6707-05A5-5F84-DFE6628E8EC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8087BDE4-6707-05A5-5F84-DFE6628E8EC8}.Release|Any CPU.Build.0 = Release|Any CPU + {B095320B-6854-EBEC-98DB-26E06D97183A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B095320B-6854-EBEC-98DB-26E06D97183A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B095320B-6854-EBEC-98DB-26E06D97183A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B095320B-6854-EBEC-98DB-26E06D97183A}.Release|Any CPU.Build.0 = Release|Any CPU + {451EC146-D85B-EA72-D064-0A75E9F52C7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {451EC146-D85B-EA72-D064-0A75E9F52C7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {451EC146-D85B-EA72-D064-0A75E9F52C7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {451EC146-D85B-EA72-D064-0A75E9F52C7F}.Release|Any CPU.Build.0 = Release|Any CPU + {2C3B6892-A23A-9A8F-431E-30869231F832}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C3B6892-A23A-9A8F-431E-30869231F832}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C3B6892-A23A-9A8F-431E-30869231F832}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C3B6892-A23A-9A8F-431E-30869231F832}.Release|Any CPU.Build.0 = Release|Any CPU + {DE29FFE1-ABB8-3B45-0D58-768727CA8DB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE29FFE1-ABB8-3B45-0D58-768727CA8DB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE29FFE1-ABB8-3B45-0D58-768727CA8DB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE29FFE1-ABB8-3B45-0D58-768727CA8DB1}.Release|Any CPU.Build.0 = Release|Any CPU + {CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83}.Release|Any CPU.Build.0 = Release|Any CPU + {6167C216-0C39-EBBA-2841-A8F864642DF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6167C216-0C39-EBBA-2841-A8F864642DF1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6167C216-0C39-EBBA-2841-A8F864642DF1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6167C216-0C39-EBBA-2841-A8F864642DF1}.Release|Any CPU.Build.0 = Release|Any CPU + {B3AADD0B-E103-A0F9-385F-1D18262B9C41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3AADD0B-E103-A0F9-385F-1D18262B9C41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3AADD0B-E103-A0F9-385F-1D18262B9C41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3AADD0B-E103-A0F9-385F-1D18262B9C41}.Release|Any CPU.Build.0 = Release|Any CPU + {E8D25897-E52E-47DE-B3B8-CE916C9B9B80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8D25897-E52E-47DE-B3B8-CE916C9B9B80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8D25897-E52E-47DE-B3B8-CE916C9B9B80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8D25897-E52E-47DE-B3B8-CE916C9B9B80}.Release|Any CPU.Build.0 = Release|Any CPU + {A6844326-E313-F10A-7E99-89F54C73774C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6844326-E313-F10A-7E99-89F54C73774C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6844326-E313-F10A-7E99-89F54C73774C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6844326-E313-F10A-7E99-89F54C73774C}.Release|Any CPU.Build.0 = Release|Any CPU + {C7CAB909-965B-8674-56F1-98F3BE0BD87A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7CAB909-965B-8674-56F1-98F3BE0BD87A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7CAB909-965B-8674-56F1-98F3BE0BD87A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7CAB909-965B-8674-56F1-98F3BE0BD87A}.Release|Any CPU.Build.0 = Release|Any CPU + {24CB433D-438B-04D0-29F7-9F29EC7DC3C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24CB433D-438B-04D0-29F7-9F29EC7DC3C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24CB433D-438B-04D0-29F7-9F29EC7DC3C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24CB433D-438B-04D0-29F7-9F29EC7DC3C6}.Release|Any CPU.Build.0 = Release|Any CPU + {9A98EE2B-BBBC-0D54-60D8-C36ABE80B340}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A98EE2B-BBBC-0D54-60D8-C36ABE80B340}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A98EE2B-BBBC-0D54-60D8-C36ABE80B340}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A98EE2B-BBBC-0D54-60D8-C36ABE80B340}.Release|Any CPU.Build.0 = Release|Any CPU + {C5591759-58B0-73D0-6EDC-4FFCBE819303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5591759-58B0-73D0-6EDC-4FFCBE819303}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5591759-58B0-73D0-6EDC-4FFCBE819303}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5591759-58B0-73D0-6EDC-4FFCBE819303}.Release|Any CPU.Build.0 = Release|Any CPU + {AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B}.Release|Any CPU.Build.0 = Release|Any CPU + {DEE77B19-9559-D47D-8EC4-C207152A9C2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEE77B19-9559-D47D-8EC4-C207152A9C2C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEE77B19-9559-D47D-8EC4-C207152A9C2C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEE77B19-9559-D47D-8EC4-C207152A9C2C}.Release|Any CPU.Build.0 = Release|Any CPU + {93256CB0-F5C1-CF7B-1D19-307E6E01191B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93256CB0-F5C1-CF7B-1D19-307E6E01191B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93256CB0-F5C1-CF7B-1D19-307E6E01191B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93256CB0-F5C1-CF7B-1D19-307E6E01191B}.Release|Any CPU.Build.0 = Release|Any CPU + {A4A3650E-164F-9339-FA7B-606EFFD06268}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4A3650E-164F-9339-FA7B-606EFFD06268}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4A3650E-164F-9339-FA7B-606EFFD06268}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4A3650E-164F-9339-FA7B-606EFFD06268}.Release|Any CPU.Build.0 = Release|Any CPU + {B0453D30-45FE-B2BC-5404-4A9C985D7BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0453D30-45FE-B2BC-5404-4A9C985D7BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0453D30-45FE-B2BC-5404-4A9C985D7BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0453D30-45FE-B2BC-5404-4A9C985D7BFF}.Release|Any CPU.Build.0 = Release|Any CPU + {34A07D0C-32E6-E19B-337E-4E6CB722C606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {34A07D0C-32E6-E19B-337E-4E6CB722C606}.Debug|Any CPU.Build.0 = Debug|Any CPU + {34A07D0C-32E6-E19B-337E-4E6CB722C606}.Release|Any CPU.ActiveCfg = Release|Any CPU + {34A07D0C-32E6-E19B-337E-4E6CB722C606}.Release|Any CPU.Build.0 = Release|Any CPU + {FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F}.Release|Any CPU.Build.0 = Release|Any CPU + {5DB3749D-E83C-7093-75B9-B617D1FB72C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DB3749D-E83C-7093-75B9-B617D1FB72C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DB3749D-E83C-7093-75B9-B617D1FB72C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DB3749D-E83C-7093-75B9-B617D1FB72C0}.Release|Any CPU.Build.0 = Release|Any CPU + {DD05B30B-7C7D-CFF3-9C36-839D54A13B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD05B30B-7C7D-CFF3-9C36-839D54A13B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD05B30B-7C7D-CFF3-9C36-839D54A13B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD05B30B-7C7D-CFF3-9C36-839D54A13B0C}.Release|Any CPU.Build.0 = Release|Any CPU + {7BA618DE-0BC8-886C-36DD-989B9C05FA96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BA618DE-0BC8-886C-36DD-989B9C05FA96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BA618DE-0BC8-886C-36DD-989B9C05FA96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BA618DE-0BC8-886C-36DD-989B9C05FA96}.Release|Any CPU.Build.0 = Release|Any CPU + {8CE66034-25E2-8FF2-93A1-0BD3672AD86D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8CE66034-25E2-8FF2-93A1-0BD3672AD86D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8CE66034-25E2-8FF2-93A1-0BD3672AD86D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8CE66034-25E2-8FF2-93A1-0BD3672AD86D}.Release|Any CPU.Build.0 = Release|Any CPU + {3C4BCE69-B353-B5AA-E824-209A6D627BA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C4BCE69-B353-B5AA-E824-209A6D627BA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C4BCE69-B353-B5AA-E824-209A6D627BA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C4BCE69-B353-B5AA-E824-209A6D627BA3}.Release|Any CPU.Build.0 = Release|Any CPU + {E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286}.Release|Any CPU.Build.0 = Release|Any CPU + {D21CCB70-1EDB-8D13-F00F-CA280DE4C83E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D21CCB70-1EDB-8D13-F00F-CA280DE4C83E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D21CCB70-1EDB-8D13-F00F-CA280DE4C83E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D21CCB70-1EDB-8D13-F00F-CA280DE4C83E}.Release|Any CPU.Build.0 = Release|Any CPU + {801E5330-F8E0-CC40-CC9A-47FE42284871}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {801E5330-F8E0-CC40-CC9A-47FE42284871}.Debug|Any CPU.Build.0 = Debug|Any CPU + {801E5330-F8E0-CC40-CC9A-47FE42284871}.Release|Any CPU.ActiveCfg = Release|Any CPU + {801E5330-F8E0-CC40-CC9A-47FE42284871}.Release|Any CPU.Build.0 = Release|Any CPU + {0A1267E9-52FF-B8DE-8522-802BE55F41DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A1267E9-52FF-B8DE-8522-802BE55F41DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A1267E9-52FF-B8DE-8522-802BE55F41DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A1267E9-52FF-B8DE-8522-802BE55F41DA}.Release|Any CPU.Build.0 = Release|Any CPU + {4312B163-9507-6A25-893B-C18ECBF896BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4312B163-9507-6A25-893B-C18ECBF896BF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4312B163-9507-6A25-893B-C18ECBF896BF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4312B163-9507-6A25-893B-C18ECBF896BF}.Release|Any CPU.Build.0 = Release|Any CPU + {7005DD7B-D3B6-1360-313B-975974AA6254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7005DD7B-D3B6-1360-313B-975974AA6254}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7005DD7B-D3B6-1360-313B-975974AA6254}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7005DD7B-D3B6-1360-313B-975974AA6254}.Release|Any CPU.Build.0 = Release|Any CPU + {10E2810B-AEDC-AE32-911A-750DF29322F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10E2810B-AEDC-AE32-911A-750DF29322F0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10E2810B-AEDC-AE32-911A-750DF29322F0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10E2810B-AEDC-AE32-911A-750DF29322F0}.Release|Any CPU.Build.0 = Release|Any CPU + {6A85289A-97BE-DC01-FB92-7FD258FCDDB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6A85289A-97BE-DC01-FB92-7FD258FCDDB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6A85289A-97BE-DC01-FB92-7FD258FCDDB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6A85289A-97BE-DC01-FB92-7FD258FCDDB7}.Release|Any CPU.Build.0 = Release|Any CPU + {923E34BA-CA8A-971E-7FF7-51DD346394A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {923E34BA-CA8A-971E-7FF7-51DD346394A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {923E34BA-CA8A-971E-7FF7-51DD346394A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {923E34BA-CA8A-971E-7FF7-51DD346394A1}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC75308-5C91-83F0-5472-39C9445C8FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC75308-5C91-83F0-5472-39C9445C8FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC75308-5C91-83F0-5472-39C9445C8FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC75308-5C91-83F0-5472-39C9445C8FFC}.Release|Any CPU.Build.0 = Release|Any CPU + {BB08E86A-946F-C3BD-C607-989716B05D46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB08E86A-946F-C3BD-C607-989716B05D46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB08E86A-946F-C3BD-C607-989716B05D46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB08E86A-946F-C3BD-C607-989716B05D46}.Release|Any CPU.Build.0 = Release|Any CPU + {290ED315-ED26-F504-0583-848669298657}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {290ED315-ED26-F504-0583-848669298657}.Debug|Any CPU.Build.0 = Debug|Any CPU + {290ED315-ED26-F504-0583-848669298657}.Release|Any CPU.ActiveCfg = Release|Any CPU + {290ED315-ED26-F504-0583-848669298657}.Release|Any CPU.Build.0 = Release|Any CPU + {E234FD18-B133-4CF4-2AC1-7E219BF54242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E234FD18-B133-4CF4-2AC1-7E219BF54242}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E234FD18-B133-4CF4-2AC1-7E219BF54242}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E234FD18-B133-4CF4-2AC1-7E219BF54242}.Release|Any CPU.Build.0 = Release|Any CPU + {8252A386-069A-0D7D-EAB4-B2E77833B443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8252A386-069A-0D7D-EAB4-B2E77833B443}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8252A386-069A-0D7D-EAB4-B2E77833B443}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8252A386-069A-0D7D-EAB4-B2E77833B443}.Release|Any CPU.Build.0 = Release|Any CPU + {3F65D29D-ED95-2D6F-B927-47C5CF070AA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F65D29D-ED95-2D6F-B927-47C5CF070AA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F65D29D-ED95-2D6F-B927-47C5CF070AA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F65D29D-ED95-2D6F-B927-47C5CF070AA5}.Release|Any CPU.Build.0 = Release|Any CPU + {BEF66A04-80EF-F6FD-E969-F441F709ECDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BEF66A04-80EF-F6FD-E969-F441F709ECDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BEF66A04-80EF-F6FD-E969-F441F709ECDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BEF66A04-80EF-F6FD-E969-F441F709ECDA}.Release|Any CPU.Build.0 = Release|Any CPU + {612E7B7D-8BE1-C279-2BD9-6191A97F2D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {612E7B7D-8BE1-C279-2BD9-6191A97F2D03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {612E7B7D-8BE1-C279-2BD9-6191A97F2D03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {612E7B7D-8BE1-C279-2BD9-6191A97F2D03}.Release|Any CPU.Build.0 = Release|Any CPU + {491BC06C-DEEC-0D65-985A-E192EC710BEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {491BC06C-DEEC-0D65-985A-E192EC710BEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {491BC06C-DEEC-0D65-985A-E192EC710BEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {491BC06C-DEEC-0D65-985A-E192EC710BEF}.Release|Any CPU.Build.0 = Release|Any CPU + {E297099A-564C-A98A-BED5-547AD4799C81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E297099A-564C-A98A-BED5-547AD4799C81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E297099A-564C-A98A-BED5-547AD4799C81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E297099A-564C-A98A-BED5-547AD4799C81}.Release|Any CPU.Build.0 = Release|Any CPU + {839F00DC-B547-E6C3-AB0B-EFABD62C5C82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {839F00DC-B547-E6C3-AB0B-EFABD62C5C82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {839F00DC-B547-E6C3-AB0B-EFABD62C5C82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {839F00DC-B547-E6C3-AB0B-EFABD62C5C82}.Release|Any CPU.Build.0 = Release|Any CPU + {5F15CE31-9DF5-CD0F-F998-8BF358F11752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F15CE31-9DF5-CD0F-F998-8BF358F11752}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F15CE31-9DF5-CD0F-F998-8BF358F11752}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F15CE31-9DF5-CD0F-F998-8BF358F11752}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C73083-9F69-D41B-572D-050157413338}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C73083-9F69-D41B-572D-050157413338}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C73083-9F69-D41B-572D-050157413338}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C73083-9F69-D41B-572D-050157413338}.Release|Any CPU.Build.0 = Release|Any CPU + {E27FA26C-6E34-F3F5-3005-67EF18A5CB8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E27FA26C-6E34-F3F5-3005-67EF18A5CB8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E27FA26C-6E34-F3F5-3005-67EF18A5CB8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E27FA26C-6E34-F3F5-3005-67EF18A5CB8B}.Release|Any CPU.Build.0 = Release|Any CPU + {295E850B-D94D-3328-75AB-62828B4B85BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {295E850B-D94D-3328-75AB-62828B4B85BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {295E850B-D94D-3328-75AB-62828B4B85BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {295E850B-D94D-3328-75AB-62828B4B85BA}.Release|Any CPU.Build.0 = Release|Any CPU + {A8C385EE-1798-2802-5533-9E9A12CC603E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8C385EE-1798-2802-5533-9E9A12CC603E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8C385EE-1798-2802-5533-9E9A12CC603E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8C385EE-1798-2802-5533-9E9A12CC603E}.Release|Any CPU.Build.0 = Release|Any CPU + {A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7}.Release|Any CPU.Build.0 = Release|Any CPU + {29080628-23A6-1DCB-F15E-93F1D1682CC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29080628-23A6-1DCB-F15E-93F1D1682CC1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29080628-23A6-1DCB-F15E-93F1D1682CC1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29080628-23A6-1DCB-F15E-93F1D1682CC1}.Release|Any CPU.Build.0 = Release|Any CPU + {31EB654C-B562-73B4-2456-78FA875515D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31EB654C-B562-73B4-2456-78FA875515D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31EB654C-B562-73B4-2456-78FA875515D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31EB654C-B562-73B4-2456-78FA875515D2}.Release|Any CPU.Build.0 = Release|Any CPU + {068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3}.Release|Any CPU.Build.0 = Release|Any CPU + {D5A8E20C-E8D2-4A57-906A-263994D8731D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5A8E20C-E8D2-4A57-906A-263994D8731D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5A8E20C-E8D2-4A57-906A-263994D8731D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5A8E20C-E8D2-4A57-906A-263994D8731D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1586,7 +1954,6 @@ Global {6362616E-6A47-48F0-9EE0-27800B306ACB} = {AFA5F921-0650-45E8-B293-51A0BB89DEA0} {8977A560-45C2-4EC2-A849-97335B382C74} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC} {BD8CE303-5F04-45EC-8DCF-73C9164CD614} = {5880FECB-91F1-4AB8-8726-75EAFA8A918E} - {2FB6C157-DF91-4B1C-9827-A4D1C08C73EC} = {8977A560-45C2-4EC2-A849-97335B382C74} {5E6E9184-DEC5-4EC5-B0A4-77CFDC8CDEBE} = {8DBA5174-B0AA-4561-82B1-A46607697753} {686BF57E-A6FF-467B-AAB3-44DE916A9772} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} {1DDE89EE-5819-441F-A060-2FF4A986F372} = {D449D505-CC6A-4E0B-AF1B-976E2D0AE67A} @@ -1695,6 +2062,80 @@ Global {BD974609-C68B-4BE6-9682-EB132462B50D} = {C2D1346B-9665-4150-B644-075CF1636BAA} {A8D5CFFA-7F9E-C35B-4F19-D63F6EC1D5CA} = {C2D1346B-9665-4150-B644-075CF1636BAA} {9EB058F3-10C9-8F3F-AD9E-72CB362A0928} = {5880FECB-91F1-4AB8-8726-75EAFA8A918E} + {8087BDE4-6707-05A5-5F84-DFE6628E8EC8} = {6FE0875A-E178-4766-BCC6-87D37F273102} + {B095320B-6854-EBEC-98DB-26E06D97183A} = {6FE0875A-E178-4766-BCC6-87D37F273102} + {451EC146-D85B-EA72-D064-0A75E9F52C7F} = {6FE0875A-E178-4766-BCC6-87D37F273102} + {6FE0875A-E178-4766-BCC6-87D37F273102} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {2C3B6892-A23A-9A8F-431E-30869231F832} = {6FE0875A-E178-4766-BCC6-87D37F273102} + {DE29FFE1-ABB8-3B45-0D58-768727CA8DB1} = {6FE0875A-E178-4766-BCC6-87D37F273102} + {CE7C15D3-3B1B-84CF-1B4B-DA45533E9E83} = {0AD92ED3-387B-4975-BA2E-68A76E727959} + {0AD92ED3-387B-4975-BA2E-68A76E727959} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {6167C216-0C39-EBBA-2841-A8F864642DF1} = {0AD92ED3-387B-4975-BA2E-68A76E727959} + {B3AADD0B-E103-A0F9-385F-1D18262B9C41} = {0AD92ED3-387B-4975-BA2E-68A76E727959} + {E8D25897-E52E-47DE-B3B8-CE916C9B9B80} = {0AD92ED3-387B-4975-BA2E-68A76E727959} + {A6844326-E313-F10A-7E99-89F54C73774C} = {76B913A2-E33D-4C48-B206-8F14F0F94B06} + {76B913A2-E33D-4C48-B206-8F14F0F94B06} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {C7CAB909-965B-8674-56F1-98F3BE0BD87A} = {76B913A2-E33D-4C48-B206-8F14F0F94B06} + {24CB433D-438B-04D0-29F7-9F29EC7DC3C6} = {76B913A2-E33D-4C48-B206-8F14F0F94B06} + {9A98EE2B-BBBC-0D54-60D8-C36ABE80B340} = {76B913A2-E33D-4C48-B206-8F14F0F94B06} + {C5591759-58B0-73D0-6EDC-4FFCBE819303} = {76B913A2-E33D-4C48-B206-8F14F0F94B06} + {AD0837BE-7912-AEDF-BDA9-2D2E4FE0CE0B} = {56053920-5095-430D-9C8B-AFB2C40407B4} + {56053920-5095-430D-9C8B-AFB2C40407B4} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {DEE77B19-9559-D47D-8EC4-C207152A9C2C} = {56053920-5095-430D-9C8B-AFB2C40407B4} + {93256CB0-F5C1-CF7B-1D19-307E6E01191B} = {56053920-5095-430D-9C8B-AFB2C40407B4} + {A4A3650E-164F-9339-FA7B-606EFFD06268} = {56053920-5095-430D-9C8B-AFB2C40407B4} + {B0453D30-45FE-B2BC-5404-4A9C985D7BFF} = {56053920-5095-430D-9C8B-AFB2C40407B4} + {08735294-3E6B-4420-9916-E7B8C4EB874D} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {BE5FE32C-DA73-4EC5-809E-E11B05ACA398} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {FCB56CBA-FA35-46A8-86B7-BAE5433197D9} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {68528C1C-B163-49A6-A19D-24E10F500F90} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {3055F932-0D1E-4823-A03A-7B62C7639BDA} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {4C362C30-C4B1-4C4B-A545-DBF67C7E9153} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {9C4BC501-C0A6-4130-BA68-EF5FB0C640B0} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {0DDCFE67-7D4E-4709-9882-EC032A031789} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {34A07D0C-32E6-E19B-337E-4E6CB722C606} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {FCE0046B-03F8-78F6-86A1-8DDCEE8F4C9F} = {616B9154-1EE8-45AE-9299-EBD0D51446E5} + {616B9154-1EE8-45AE-9299-EBD0D51446E5} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {5DB3749D-E83C-7093-75B9-B617D1FB72C0} = {616B9154-1EE8-45AE-9299-EBD0D51446E5} + {DD05B30B-7C7D-CFF3-9C36-839D54A13B0C} = {616B9154-1EE8-45AE-9299-EBD0D51446E5} + {7BA618DE-0BC8-886C-36DD-989B9C05FA96} = {616B9154-1EE8-45AE-9299-EBD0D51446E5} + {8CE66034-25E2-8FF2-93A1-0BD3672AD86D} = {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} + {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {3C4BCE69-B353-B5AA-E824-209A6D627BA3} = {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} + {E11BAB8D-39ED-AC6C-6D69-1E5B5F3FB286} = {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} + {D21CCB70-1EDB-8D13-F00F-CA280DE4C83E} = {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} + {801E5330-F8E0-CC40-CC9A-47FE42284871} = {FBF49D7B-F60C-41B5-8CEF-F1D41F84A387} + {0A1267E9-52FF-B8DE-8522-802BE55F41DA} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {4312B163-9507-6A25-893B-C18ECBF896BF} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {7005DD7B-D3B6-1360-313B-975974AA6254} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {10E2810B-AEDC-AE32-911A-750DF29322F0} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {6A85289A-97BE-DC01-FB92-7FD258FCDDB7} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {482C1FC7-4FD6-4381-8078-73BEBFAF4349} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {923E34BA-CA8A-971E-7FF7-51DD346394A1} = {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} + {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {CBC75308-5C91-83F0-5472-39C9445C8FFC} = {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} + {BB08E86A-946F-C3BD-C607-989716B05D46} = {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} + {290ED315-ED26-F504-0583-848669298657} = {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} + {E234FD18-B133-4CF4-2AC1-7E219BF54242} = {A4E65246-D2F6-492F-8BD5-FE11DD89A19B} + {8252A386-069A-0D7D-EAB4-B2E77833B443} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {3F65D29D-ED95-2D6F-B927-47C5CF070AA5} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {7F040742-3C10-406A-A0FF-7FE621DEF671} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {BEF66A04-80EF-F6FD-E969-F441F709ECDA} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {612E7B7D-8BE1-C279-2BD9-6191A97F2D03} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {491BC06C-DEEC-0D65-985A-E192EC710BEF} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {E297099A-564C-A98A-BED5-547AD4799C81} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {839F00DC-B547-E6C3-AB0B-EFABD62C5C82} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {5F15CE31-9DF5-CD0F-F998-8BF358F11752} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {B2C73083-9F69-D41B-572D-050157413338} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {E27FA26C-6E34-F3F5-3005-67EF18A5CB8B} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {295E850B-D94D-3328-75AB-62828B4B85BA} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {A8C385EE-1798-2802-5533-9E9A12CC603E} = {7F040742-3C10-406A-A0FF-7FE621DEF671} + {A1EBC0BC-D378-B9B7-7A57-EF7DF11E3ED7} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {29080628-23A6-1DCB-F15E-93F1D1682CC1} = {482C1FC7-4FD6-4381-8078-73BEBFAF4349} + {31EB654C-B562-73B4-2456-78FA875515D2} = {0DDCFE67-7D4E-4709-9882-EC032A031789} + {068CD9AA-CEC3-CA68-1BAB-2B1B9FD711D3} = {8977A560-45C2-4EC2-A849-97335B382C74} + {4853A78A-4EC4-4D86-9F02-D0DDEAE85520} = {8977A560-45C2-4EC2-A849-97335B382C74} + {D5A8E20C-E8D2-4A57-906A-263994D8731D} = {8977A560-45C2-4EC2-A849-97335B382C74} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29} @@ -1703,7 +2144,11 @@ Global src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\VisualBasic\VisualBasicWorkspaceExtensions.projitems*{0141285d-8f6c-42c7-baf3-3c0ccd61c716}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{0161e25c-918a-4dc8-9648-30fdcc8e31e9}*SharedItemsImports = 5 + src\Tools\ExternalAccess\Razor\Shared\Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems*{068cd9aa-cec3-ca68-1bab-2b1b9fd711d3}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{0c2e1633-1462-4712-88f4-a0c945bad3a8}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{08735294-3e6b-4420-9916-e7b8c4eb874d}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{0a1267e9-52ff-b8de-8522-802be55f41da}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis.Utilities.projitems*{0a1267e9-52ff-b8de-8522-802be55f41da}*SharedItemsImports = 5 src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{1b6c4a1a-413b-41fb-9f85-5c09118e541b}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 @@ -1712,6 +2157,7 @@ Global src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{21b239d0-d144-430f-a394-c066d58ee267}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{24cb433d-438b-04d0-29f7-9f29ec7dc3c6}*SharedItemsImports = 5 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{2531a8c4-97dd-47bc-a79c-b7846051e137}*SharedItemsImports = 5 @@ -1725,14 +2171,19 @@ Global src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\CompilerExtensions.projitems*{275812ee-dedb-4232-9439-91c9757d2ae4}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{2801f82b-78ce-4bae-b06f-537574751e2e}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Refactoring.CSharp\Refactoring.CSharp.Utilities.projitems*{3055f932-0d1e-4823-a03a-7b62c7639bda}*SharedItemsImports = 13 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e35}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{3140fe61-0856-4367-9aa3-8081b9a80e36}*SharedItemsImports = 13 src\Analyzers\CSharp\Analyzers\CSharpAnalyzers.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{41ed1bfa-fdad-4fe4-8118-db23fb49b0b0}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{3f65d29d-ed95-2d6f-b927-47c5cf070aa5}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{3f65d29d-ed95-2d6f-b927-47c5cf070aa5}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{438db8af-f3f0-4ed9-80b5-13fddd5b8787}*SharedItemsImports = 13 + src\Tools\ExternalAccess\Razor\Shared\Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems*{4853a78a-4ec4-4d86-9f02-d0ddeae85520}*SharedItemsImports = 13 src\Compilers\CSharp\csc\CscCommandLine.projitems*{4b45ca0c-03a0-400f-b454-3d4bcb16af38}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Refactoring.VisualBasic\Refactoring.VisualBasic.Utilities.projitems*{4c362c30-c4b1-4c4b-a545-dbf67c7e9153}*SharedItemsImports = 13 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{5018d049-5870-465a-889b-c742ce1e31cb}*SharedItemsImports = 5 src\Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{54e08bf5-f819-404f-a18d-0ab9ea81ea04}*SharedItemsImports = 13 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{57ca988d-f010-4bf2-9a2e-07d6dcd2ff2c}*SharedItemsImports = 5 @@ -1747,34 +2198,51 @@ Global src\Analyzers\Core\CodeFixes\CodeFixes.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{5ff1e493-69cc-4d0b-83f2-039f469a04e1}*SharedItemsImports = 5 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{60db272a-21c9-4e8d-9803-ff4e132392c8}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{6167c216-0c39-ebba-2841-a8f864642df1}*SharedItemsImports = 5 src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems*{64eaded3-4b5d-4431-bbe5-a4aba1c38c00}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Refactoring\Refactoring.Utilities.projitems*{68528c1c-b163-49a6-a19d-24e10f500f90}*SharedItemsImports = 13 src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Shared.projitems*{686bf57e-a6ff-467b-aab3-44de916a9772}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{699fea05-aea7-403d-827e-53cf4e826955}*SharedItemsImports = 13 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{6fc8e6f5-659c-424d-aeb5-331b95883e29}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{7005dd7b-d3b6-1360-313b-975974aa6254}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{7005dd7b-d3b6-1360-313b-975974aa6254}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{76242a2d-2600-49dd-8c15-fea07ecb1843}*SharedItemsImports = 5 src\Analyzers\Core\Analyzers\Analyzers.projitems*{76e96966-4780-4040-8197-bde2879516f4}*SharedItemsImports = 13 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{7ad4fe65-9a30-41a6-8004-aa8f89bcb7f3}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{7b7f4153-ae93-4908-b8f0-430871589f83}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{8087bde4-6707-05a5-5f84-dfe6628e8ec8}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{8087bde4-6707-05a5-5f84-dfe6628e8ec8}*SharedItemsImports = 5 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{810b02ad-2ea5-4422-88ac-b71b8ab0df0b}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{8252a386-069a-0d7d-eab4-b2e77833b443}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{8252a386-069a-0d7d-eab4-b2e77833b443}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{8ce66034-25e2-8ff2-93a1-0bd3672ad86d}*SharedItemsImports = 5 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{8e2a252e-a140-45a6-a81a-2652996ea589}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{923e34ba-ca8a-971e-7ff7-51dd346394a1}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{923e34ba-ca8a-971e-7ff7-51dd346394a1}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{94faf461-2e74-4dbb-9813-6b2cde6f1880}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{9508f118-f62e-4c16-a6f4-7c3b56e166ad}*SharedItemsImports = 5 src\Dependencies\Threading\Microsoft.CodeAnalysis.Threading.projitems*{967723e8-4fdd-447b-99f6-4f8c47cb5433}*SharedItemsImports = 13 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{975cd834-45f4-4ea0-a395-cb60dbd0e214}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\Core\WorkspaceExtensions.projitems*{99f594b1-3916-471d-a761-a6731fc50e9a}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{9c4bc501-c0a6-4130-ba68-ef5fb0c640b0}*SharedItemsImports = 13 src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{9f9ccc78-7487-4127-9d46-db23e501f001}*SharedItemsImports = 13 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{a07abcf5-bc43-4ee9-8fd8-b2d77fd54d73}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Workspace\CSharp\CSharpWorkspaceExtensions.projitems*{a07abcf5-bc43-4ee9-8fd8-b2d77fd54d73}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Analyzers\VisualBasicAnalyzers.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 5 src\Analyzers\VisualBasic\CodeFixes\VisualBasicCodeFixes.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 5 src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Refactoring.VisualBasic\Refactoring.VisualBasic.Utilities.projitems*{a4a3650e-164f-9339-fa7b-606effd06268}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{a6844326-e313-f10a-7e99-89f54c73774c}*SharedItemsImports = 5 src\Analyzers\CSharp\Analyzers\CSharpAnalyzers.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\CSharp\CSharpCompilerExtensions.projitems*{aa87bfed-089a-4096-b8d5-690bdc7d5b24}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{abdbac1e-350e-4dc3-bb45-3504404545ee}*SharedItemsImports = 5 src\ExpressionEvaluator\VisualBasic\Source\ResultProvider\BasicResultProvider.projitems*{ace53515-482c-4c6a-e2d2-4242a687dfee}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{ad0837be-7912-aedf-bda9-2d2e4fe0ce0b}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Refactoring\Refactoring.Utilities.projitems*{ad0837be-7912-aedf-bda9-2d2e4fe0ce0b}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Workspaces\Workspaces.Utilities.projitems*{ad0837be-7912-aedf-bda9-2d2e4fe0ce0b}*SharedItemsImports = 5 src\Compilers\CSharp\csc\CscCommandLine.projitems*{b021ccbc-b2af-4560-af28-ed055f0ed696}*SharedItemsImports = 13 src\Dependencies\Collections\Microsoft.CodeAnalysis.Collections.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{b1481d94-682e-46ec-adbe-a16eb46feee9}*SharedItemsImports = 5 @@ -1783,16 +2251,20 @@ Global src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{bb3ca047-5d00-48d4-b7d3-233c1265c065}*SharedItemsImports = 13 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{bd9539eb-aa5e-4e67-ac7f-97d7cbc4d0c9}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{bd974609-c68b-4be6-9682-eb132462b50d}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler.CSharp\Analyzer.CSharp.Utilities.projitems*{be5fe32c-da73-4ec5-809e-e11b05aca398}*SharedItemsImports = 13 src\ExpressionEvaluator\CSharp\Source\ResultProvider\CSharpResultProvider.projitems*{bf9dac1e-3a5e-4dc3-bb44-9a64e0d4e9d4}*SharedItemsImports = 5 src\Dependencies\PooledObjects\Microsoft.CodeAnalysis.PooledObjects.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{ce7c15d3-3b1b-84cf-1b4b-da45533e9e83}*SharedItemsImports = 5 src\Workspaces\SharedUtilitiesAndExtensions\Compiler\VisualBasic\VisualBasicCompilerExtensions.projitems*{cec0dce7-8d52-45c3-9295-fc7b16bd2451}*SharedItemsImports = 13 src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{d2589bce-4f2e-4113-b7e7-37392c0c5492}*SharedItemsImports = 5 + src\Tools\ExternalAccess\Razor\Shared\Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems*{d5a8e20c-e8d2-4a57-906a-263994d8731d}*SharedItemsImports = 5 src\Dependencies\CodeAnalysis.Debugging\Microsoft.CodeAnalysis.Debugging.projitems*{d73adf7d-2c1c-42ae-b2ab-edc9497e4b71}*SharedItemsImports = 13 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{d8ef0777-9d65-4849-a7d6-ac81e58e2317}*SharedItemsImports = 13 src\Analyzers\CSharp\CodeFixes\CSharpCodeFixes.projitems*{da973826-c985-4128-9948-0b445e638bdb}*SharedItemsImports = 13 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{db96c25f-39a9-4a6a-92bc-d1e42717308f}*SharedItemsImports = 5 src\Compilers\Server\VBCSCompiler\VBCSCompilerCommandLine.projitems*{dc8c78cc-b6fe-47bf-93b1-b65a1c67c08d}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\Refactoring.CSharp\Refactoring.CSharp.Utilities.projitems*{dee77b19-9559-d47d-8ec4-c207152a9c2c}*SharedItemsImports = 5 src\Analyzers\VisualBasic\Tests\VisualBasicAnalyzers.UnitTests.projitems*{e512c6c1-f085-4ad7-b0d9-e8f1a0a2a510}*SharedItemsImports = 5 src\Compilers\VisualBasic\vbc\VbcCommandLine.projitems*{e58ee9d7-1239-4961-a0c1-f9ec3952c4c1}*SharedItemsImports = 5 src\Analyzers\CSharp\Tests\CSharpAnalyzers.UnitTests.projitems*{e645b517-5766-46fb-aa4a-d4d30c9e3be6}*SharedItemsImports = 5 @@ -1808,5 +2280,7 @@ Global src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{fa0e905d-ec46-466d-b7b2-3b5557f9428c}*SharedItemsImports = 5 src\ExpressionEvaluator\Core\Source\ResultProvider\ResultProvider.projitems*{fa0e905d-ec46-466d-b7b2-3b5557f9428c}*SharedItemsImports = 5 src\Dependencies\Contracts\Microsoft.CodeAnalysis.Contracts.projitems*{fce88bbd-9bbd-4871-b9b0-de176d73a6b0}*SharedItemsImports = 5 + src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis.Utilities.projitems*{fcb56cba-fa35-46a8-86b7-bae5433197d9}*SharedItemsImports = 13 + src\RoslynAnalyzers\Utilities\Compiler\Analyzer.Utilities.projitems*{fce0046b-03f8-78f6-86a1-8ddcee8f4c9f}*SharedItemsImports = 5 EndGlobalSection EndGlobal diff --git a/azure-pipelines-integration.yml b/azure-pipelines-integration.yml index 241c0fec59dfc..afddf6a20c094 100644 --- a/azure-pipelines-integration.yml +++ b/azure-pipelines-integration.yml @@ -10,9 +10,9 @@ trigger: exclude: # Since the version of VS on the integration VM images are a moving target, # we are unable to reliably run integration tests on servicing branches. - - release/dev17.0-vs-deps - - release/dev17.2 - - release/dev17.3 + - release/dev17.8 + - release/dev17.10 + - release/dev17.12 # Branches that trigger builds on PR pr: @@ -26,19 +26,25 @@ pr: exclude: # Since the version of VS on the integration VM images are a moving target, # we are unable to reliably run integration tests on servicing branches. - - release/dev17.0-vs-deps - - release/dev17.2 - - release/dev17.3 + - release/dev17.8 + - release/dev17.10 + - release/dev17.12 paths: exclude: - docs/* - eng/config/OptProf.json - eng/config/PublishData.json + - eng/setup-pr-validation.ps1 - .vscode/* - .github/* - .devcontainer/* - .git-blame-ignore-revs - .vsconfig + - azure-pipelines-compliance.yml + - azure-pipelines-integration-dartlab.yml + - azure-pipelines-integration-scouting.yml + - azure-pipelines-official.yml + - azure-pipelines-pr-validation.yml - CODE-OF-CONDUCT.md - CONTRIBUTING.md - README.md diff --git a/azure-pipelines-official.yml b/azure-pipelines-official.yml index 9010a8640f791..f5b090c67753f 100644 --- a/azure-pipelines-official.yml +++ b/azure-pipelines-official.yml @@ -7,6 +7,7 @@ trigger: - release/dev17.* - release/dev18.* - features/lsp_tools_host + - features/runtime-async exclude: - release/dev17.0 pr: none @@ -163,7 +164,7 @@ extends: - output: pipelineArtifact displayName: 'Publish Ngen Logs' - condition: succeeded() + condition: and(succeeded(), ${{ not(parameters.SkipApplyOptimizationData) }}) targetPath: '$(Build.SourcesDirectory)\artifacts\log\$(BuildConfiguration)\ngen' artifactName: 'NGen Logs' publishLocation: Container @@ -304,7 +305,7 @@ extends: -configuration $(BuildConfiguration) -officialBuildId $(Build.BuildNumber) -officialSkipTests $(SkipTests) - -officialSkipApplyOptimizationData $(SkipApplyOptimizationData) + -officialSkipApplyOptimizationData ${{ parameters.SkipApplyOptimizationData }} -officialSourceBranchName $(SourceBranchName) -officialIbcDrop $(IbcDrop) -officialVisualStudioDropAccessToken $(_DevDivDropAccessToken) diff --git a/azure-pipelines-pr-validation.yml b/azure-pipelines-pr-validation.yml index d779e1a90d00a..39295f2ece19a 100644 --- a/azure-pipelines-pr-validation.yml +++ b/azure-pipelines-pr-validation.yml @@ -13,6 +13,9 @@ parameters: type: number - name: CommitSHA type: string +- name: EnforceLatestCommit + type: boolean + default: true - name: VisualStudioBranchName type: string default: default @@ -95,7 +98,7 @@ extends: - output: pipelineArtifact displayName: 'Publish Ngen Logs' - condition: succeeded() + condition: and(succeeded(), ${{ not(parameters.SkipApplyOptimizationData) }}) targetPath: '$(Build.SourcesDirectory)\artifacts\log\$(BuildConfiguration)\ngen' artifactName: 'NGen Logs' publishLocation: Container @@ -195,7 +198,7 @@ extends: displayName: Setup branch for insertion validation inputs: filePath: 'eng\setup-pr-validation.ps1' - arguments: '-sourceBranchName $(SourceBranchName) -prNumber ${{ parameters.PRNumber }} -commitSHA ${{ parameters.CommitSHA }}' + arguments: "-sourceBranchName $(SourceBranchName) -prNumber ${{ parameters.PRNumber }} -commitSHA ${{ parameters.CommitSHA }} -enforceLatestCommit ${{ iif(parameters.EnforceLatestCommit, '1', '0') }}" condition: succeeded() - powershell: Write-Host "##vso[task.setvariable variable=VisualStudio.DropName]Products/$(System.TeamProject)/$(Build.Repository.Name)/$(SourceBranchName)/$(OriginalBuildNumber)" @@ -239,7 +242,7 @@ extends: -configuration $(BuildConfiguration) -officialBuildId $(OriginalBuildNumber) -officialSkipTests $(SkipTests) - -officialSkipApplyOptimizationData $(SkipApplyOptimizationData) + -officialSkipApplyOptimizationData ${{ parameters.SkipApplyOptimizationData }} -officialSourceBranchName $(SourceBranchName) -officialIbcDrop $(IbcDrop) -officialVisualStudioDropAccessToken $(_DevDivDropAccessToken) diff --git a/azure-pipelines-richnav.yml b/azure-pipelines-richnav.yml deleted file mode 100644 index 26079c6b43d89..0000000000000 --- a/azure-pipelines-richnav.yml +++ /dev/null @@ -1,71 +0,0 @@ -# Branches that trigger a build on commit -trigger: -- main -- main-vs-deps -- release/* -- features/* -- demos/* - -# Branches that trigger builds on PR -pr: none -# Temporarily disabling richnav job on PRs -# pr: -# - main -# - main-vs-deps -# - release/* -# - features/* -# - demos/* - -variables: -- name: Codeql.Enabled - value: false -- name: Codeql.SkipTaskAutoInjection - value: true - -parameters: -- name: poolName - displayName: Pool Name - type: string - default: NetCore-Public - values: - - NetCore-Public - - NetCore-Svc-Public -- name: queueName - displayName: Queue Name - type: string - default: windows.vs2022preview.amd64.open - values: - - windows.vs2022.amd64.open - - windows.vs2022.scout.amd64.open - - windows.vs2022preview.amd64.open - - windows.vs2022preview.scout.amd64.open -- name: timeout - displayName: Timeout in Minutes - type: number - default: 260 - -jobs: -- job: RichCodeNav_Indexing - pool: - name: ${{ parameters.poolName }} - demands: ImageOverride -equals ${{ parameters.queueName }} - timeoutInMinutes: ${{ parameters.timeout }} - variables: - EnableRichCodeNavigation: true - - steps: - - task: PowerShell@2 - displayName: Build - inputs: - filePath: eng/build.ps1 - arguments: -ci -restore -build -configuration Debug -prepareMachine - - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: 'csharp' - environment: production - richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin - continueOnError: true - env: - DOTNET_ROOT: $(Build.SourcesDirectory)/.dotnet diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f26c4fff3483..494714e8fde8b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -18,13 +18,20 @@ pr: paths: exclude: - docs/* + - eng/config/OptProf.json - eng/config/PublishData.json + - eng/setup-pr-validation.ps1 - src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/README.md - .vscode/* - .github/* - .devcontainer/* - .git-blame-ignore-revs - .vsconfig + - azure-pipelines-compliance.yml + - azure-pipelines-integration-dartlab.yml + - azure-pipelines-integration-scouting.yml + - azure-pipelines-official.yml + - azure-pipelines-pr-validation.yml - CODE-OF-CONDUCT.md - CONTRIBUTING.md - README.md @@ -61,9 +68,9 @@ variables: - name: UbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: - value: Build.Ubuntu.2004.Amd64.Open + value: Build.Ubuntu.2204.Amd64.Open ${{ else }}: - value: Build.Ubuntu.2004.Amd64 + value: Build.Ubuntu.2204.Amd64 - name: WindowsQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: @@ -85,15 +92,15 @@ variables: - name: HelixUbuntuQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: - value: Ubuntu.2004.Amd64.Open + value: Ubuntu.2204.Amd64.Open ${{ else }}: - value: Ubuntu.2004.Amd64 + value: Ubuntu.2204.Amd64 - name: HelixMacOsQueueName ${{ if eq(variables['System.TeamProject'], 'public') }}: - value: OSX.13.Amd64.Open + value: OSX.15.Amd64.Open ${{ else }}: - value: OSX.13.Amd64 + value: OSX.15.Amd64 parameters: # These pools allow us to configure the pools once for multiple jobs. @@ -399,7 +406,7 @@ stages: - powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -restore -binaryLogName Restore.binlog displayName: Restore - - powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -build -pack -publish -sign -binaryLogName Build.binlog /p:DotnetPublishUsingPipelines=true + - powershell: eng/build.ps1 -configuration Release -prepareMachine -ci -build -pack -publish -sign -binaryLogName Build.binlog /p:DotnetPublishUsingPipelines=true /p:ContinuousIntegrationBuildCorrectness=true displayName: Build # While this task is not executed in the official build, this serves as a PR check for whether symbol exclusions diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index a35439764e167..38a1ed4a00216 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -11,7 +11,6 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | IDE Buddy | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | --------- | | [User Defined Compound Assignment Operators](https://github.com/dotnet/csharplang/issues/9101) | [UserDefinedCompoundAssignment](https://github.com/dotnet/roslyn/tree/features/UserDefinedCompoundAssignment) | [In Progress](https://github.com/dotnet/roslyn/issues/76934) | [AlekseyTs](https://github.com/AlekseyTs) | [333fred](https://github.com/333fred), [cston](https://github.com/cston) | TBD | [AlekseyTs](https://github.com/AlekseyTs) | -| [Partial Events and Constructors](https://github.com/dotnet/csharplang/issues/9058) | [PartialEventsCtors](https://github.com/dotnet/roslyn/tree/features/PartialEventsCtors) | [In Progress](https://github.com/dotnet/roslyn/issues/76859) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | | | | Runtime Async | [runtime-async](https://github.com/dotnet/roslyn/tree/features/runtime-async) | [In Progress](https://github.com/dotnet/roslyn/issues/75960) | [333fred](https://github.com/333fred) | [jcouv](https://github.com/jcouv), [RikkiGibson](https://github.com/RikkiGibson) | | | | [Null-conditional assignment](https://github.com/dotnet/csharplang/issues/6045) | [null-conditional-assignment](https://github.com/dotnet/roslyn/tree/features/null-conditional-assignment) | [In Progress](https://github.com/dotnet/roslyn/issues/75554) | [RikkiGibson](https://github.com/RikkiGibson) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | TBD | [RikkiGibson](https://github.com/RikkiGibson) | | [Extensions](https://github.com/dotnet/csharplang/issues/8697) | [extensions](https://github.com/dotnet/roslyn/tree/features/extensions) | [In Progress](https://github.com/dotnet/roslyn/issues/76130) | [jcouv](https://github.com/jcouv), [AlekseyTs](https://github.com/AlekseyTs) | [jjonescz](https://github.com/jjonescz), TBD | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [MadsTorgersen](https://github.com/MadsTorgersen) | @@ -21,6 +20,7 @@ efforts behind them. | [Unbound generic types in `nameof`](https://github.com/dotnet/csharplang/issues/8480) | [PR](https://github.com/dotnet/roslyn/pull/75368) | [Merged into 17.13p2](https://github.com/dotnet/roslyn/pull/75368) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv), [AlekseyTs](https://github.com/AlekseyTs) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | | [String literals in data section as UTF8](https://github.com/dotnet/roslyn/blob/main/docs/features/string-literals-data-section.md) | [PR](https://github.com/dotnet/roslyn/pull/76036) | [Merged into 17.13p4](https://github.com/dotnet/roslyn/issues/76234) | [jjonescz](https://github.com/jjonescz) | [AlekseyTs](https://github.com/AlekseyTs), [cston](https://github.com/cston) | N/A | N/A | | [Simple lambda parameters with modifiers](https://github.com/dotnet/csharplang/blob/main/proposals/simple-lambda-parameters-with-modifiers.md) | [PR](https://github.com/dotnet/roslyn/pull/75400) | [Merged into 17.14p1](https://github.com/dotnet/roslyn/pull/75400) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jjonescz](https://github.com/jjonescz), [cston](https://github.com/cston) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | +| [Partial Events and Constructors](https://github.com/dotnet/csharplang/issues/9058) | [PartialEventsCtors](https://github.com/dotnet/roslyn/tree/features/PartialEventsCtors) | [Merged into 17.14p3](https://github.com/dotnet/roslyn/issues/76859) | [jjonescz](https://github.com/jjonescz) | [cston](https://github.com/cston), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jaredpar](https://github.com/jaredpar) | # Working Set VB diff --git a/docs/analyzers/FixAllProvider.md b/docs/analyzers/FixAllProvider.md index c6b947950fb72..98548349e887a 100644 --- a/docs/analyzers/FixAllProvider.md +++ b/docs/analyzers/FixAllProvider.md @@ -1,120 +1,120 @@ -Introduction -============ +# Introduction This document covers the following: - - Introductory definitions - - What is a FixAll occurrences code fix? - - How is a FixAll occurrences code fix computed? - - Adding FixAll support to your code fixer - - Selecting an Equivalence key for code actions - - Spectrum of FixAll providers - - Built-in FixAllProvider and its limitations - - Implementing a custom FixAllProvider - -Definitions -=========== - - - **Analyzer:** An instance of a type derived from `DiagnosticAnalyzer` that reports diagnostics. - - **Code fixer:** An instance of a type derived from `CodeFixProvider` that provides code fixes for compiler and/or analyzer diagnostics. - - **Code refactoring:** An instance of a type derived from `CodeRefactoringProvider` that provides source code refactorings. - - **Code action:** An action registered by `CodeFixProvider.RegisterCodeFixesAsync` that performs a code fix OR an action registered by `CodeRefactoringProvider.ComputeRefactoringsAsync` that performs a code refactoring. - - **Equivalence Key:** A string value representing an equivalence class of all code actions registered by a code fixer or refactoring. Two code actions are treated as equivalent if they have equal `EquivalenceKey` values and were generated by the same code fixer or refactoring. - - **FixAll provider:** An instance of a type derived from `FixAllProvider` that provides a FixAll occurrences code fix. A FixAll provider is associated with a corresponding code fixer by `CodeFixProvider.GetFixAllProvider` method. - - **FixAll occurrences code fix:** A code action returned by `FixAllProvider.GetFixAsync`, that fixes all or multiple occurrences of diagnostics fixed by the corresponding code fixer, within a given `FixAllScope`. - -What is a FixAll occurrences code fix? -====================================== + +- Introductory definitions +- What is a FixAll occurrences code fix? +- How is a FixAll occurrences code fix computed? +- Adding FixAll support to your code fixer +- Selecting an Equivalence key for code actions +- Spectrum of FixAll providers +- Built-in FixAllProvider and its limitations +- Implementing a custom FixAllProvider + +# Definitions + +- **Analyzer:** An instance of a type derived from `DiagnosticAnalyzer` that reports diagnostics. +- **Code fixer:** An instance of a type derived from `CodeFixProvider` that provides code fixes for compiler and/or analyzer diagnostics. +- **Code refactoring:** An instance of a type derived from `CodeRefactoringProvider` that provides source code refactorings. +- **Code action:** An action registered by `CodeFixProvider.RegisterCodeFixesAsync` that performs a code fix OR an action registered by `CodeRefactoringProvider.ComputeRefactoringsAsync` that performs a code refactoring. +- **Equivalence Key:** A string value representing an equivalence class of all code actions registered by a code fixer or refactoring. Two code actions are treated as equivalent if they have equal `EquivalenceKey` values and were generated by the same code fixer or refactoring. +- **FixAll provider:** An instance of a type derived from `FixAllProvider` that provides a FixAll occurrences code fix. A FixAll provider is associated with a corresponding code fixer by `CodeFixProvider.GetFixAllProvider` method. +- **FixAll occurrences code fix:** A code action returned by `FixAllProvider.GetFixAsync`, that fixes all or multiple occurrences of diagnostics fixed by the corresponding code fixer, within a given `FixAllScope`. + +# What is a FixAll occurrences code fix? In layman terms, a FixAll occurrences code fix means: I have a code fix 'C', that fixes a specific instance of diagnostic 'D' in my source and I want to apply this fix to all instances of 'D' across a broader scope, such as a document or a project or the entire solution. In more technical terms: Given a particular code action registered by a code fixer to fix one or more diagnostics, a corresponding code action registered by its FixAll provider, that applies the original trigger code action across a broader scope (such as a document/project/solution) to fix multiple instances of such diagnostics. -How is a FixAll occurrences code fix computed? -============================================== +# How is a FixAll occurrences code fix computed? Following steps are used to compute a FixAll occurrences code fix: - - Given a specific instance of a diagnostic, compute the set of code actions that claim to fix the diagnostic. - - Select a specific code action from this set. In the Visual Studio IDE, this is done by selecting a specific code action in the light bulb menu. - - The equivalence key of the selected code action represents the class of code actions that must be applied as part of a FixAll occurrences code fix. - - Given this code action, get the FixAll provider corresponding to the code fixer that registered this code action. - - If non-null, then request the FixAll provider for its supported FixAllScopes. - - Select a specific `FixAllScope` from this set. In the Visual Studio IDE, this is done by clicking on the scope hyperlink in the preview dialog. - - Given the trigger diagnostic(s), the equivalence key of the trigger code action, and the FixAll scope, invoke `FixAllProvider.GetFixAsync` to compute the FixAll occurrences code fix. -Adding FixAll support to your code fixer -======================================== +- Given a specific instance of a diagnostic, compute the set of code actions that claim to fix the diagnostic. +- Select a specific code action from this set. In the Visual Studio IDE, this is done by selecting a specific code action in the light bulb menu. +- The equivalence key of the selected code action represents the class of code actions that must be applied as part of a FixAll occurrences code fix. +- Given this code action, get the FixAll provider corresponding to the code fixer that registered this code action. +- If non-null, then request the FixAll provider for its supported FixAllScopes. +- Select a specific `FixAllScope` from this set. In the Visual Studio IDE, this is done by clicking on the scope hyperlink in the preview dialog. +- Given the trigger diagnostic(s), the equivalence key of the trigger code action, and the FixAll scope, invoke `FixAllProvider.GetFixAsync` to compute the FixAll occurrences code fix. + +# Adding FixAll support to your code fixer Follow the below steps to add FixAll support to your code fixer: - - Override the `CodeFixProvider.GetFixAllProvider` method and return a non-null instance of a `FixAllProvider`. You may either use our built-in FixAllProvider or implement a custom FixAllProvider. See the following sections in this document for determining the correct approach for your fixer. - - Ensure that all the code actions registered by your code fixer have a non-null equivalence key. See the following section to determine how to select an equivalence key. -Selecting an Equivalence key for code actions -============================================= +- Override the `CodeFixProvider.GetFixAllProvider` method and return a non-null instance of a `FixAllProvider`. You may either use our built-in FixAllProvider or implement a custom FixAllProvider. See the following sections in this document for determining the correct approach for your fixer. +- Ensure that all the code actions registered by your code fixer have a non-null equivalence key. See the following section to determine how to select an equivalence key. + +# Selecting an Equivalence key for code actions Each unique equivalence key for a code fixer defines a unique equivalence class of code actions. Equivalence key of the trigger code action is part of the `FixAllContext` and is used to determine the FixAll occurrences code fix. Normally, you can use the **'title'** of the code action as the equivalence key. However, there are cases where you may desire to have different values. Let us take an example to get a better understanding. Let us consider the [C# SimplifyTypeNamesCodeFixProvider](https://github.com/dotnet/roslyn/blob/main/src/Features/CSharp/Portable/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs) that registers multiple code actions and also has FixAll support. This code fixer offers fixes to simplify the following expressions: - - `this` expressions of the form 'this.x' to 'x'. - - Qualified type names of the form 'A.B' to 'B'. - - Member access expressions of the form 'A.M' to 'M'. + +- `this` expressions of the form 'this.x' to 'x'. +- Qualified type names of the form 'A.B' to 'B'. +- Member access expressions of the form 'A.M' to 'M'. This fixer needs the following semantics for the corresponding FixAll occurrences code fixes: - - `this` expression simplification: Fix all should simplify all this expressions, regardless of the member being accessed (this.x, this.y, this.z, etc.). - - Qualified type name simplification: Fix all should simplify all qualified type names 'A.B' to 'B'. However, we don't want to simplify **all** qualified type names, such as 'C.D', 'E.F', etc. as that would be too generic a fix, which is not likely intended by the user. - - Member access expressions: Fix all should simplify all member access expressions 'A.M' to 'M'. + +- `this` expression simplification: Fix all should simplify all this expressions, regardless of the member being accessed (this.x, this.y, this.z, etc.). +- Qualified type name simplification: Fix all should simplify all qualified type names 'A.B' to 'B'. However, we don't want to simplify **all** qualified type names, such as 'C.D', 'E.F', etc. as that would be too generic a fix, which is not likely intended by the user. +- Member access expressions: Fix all should simplify all member access expressions 'A.M' to 'M'. It uses the below equivalence keys for its registered code actions to get the desired FixAll behavior: - - `this` expression simplification: Generic resource string "Simplify this expression", which explicitly excludes the contents of the node being simplified. - - Qualified type name simplification: Formatted resource string "Simplify type name A.B", which explicitly includes the contents of the node being simplified. - - Member access expressions: Formatted resource string "Simplify type name A.M", which explicitly includes the contents of the node being simplified. + +- `this` expression simplification: Generic resource string "Simplify this expression", which explicitly excludes the contents of the node being simplified. +- Qualified type name simplification: Formatted resource string "Simplify type name A.B", which explicitly includes the contents of the node being simplified. +- Member access expressions: Formatted resource string "Simplify type name A.M", which explicitly includes the contents of the node being simplified. Note that '`this` expression simplification' fix requires a different kind of an equivalence class from the other two simplifications. See method [GetCodeActionId](https://github.com/dotnet/roslyn/blob/main/src/Features/Core/Portable/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs) for the actual implementation. To summarize, use the equivalence key that best suits the category of fixes to be applied as part of a FixAll operation. -Spectrum of FixAll providers -============================ +# Spectrum of FixAll providers When multiple fixes need to be applied to documents, there are various ways to do it: + - **Sequential approach**: One way to do it is to compute diagnostics, pick one, ask a fixer to produce a code action to fix that, apply it. Now for the resulting new compilation, recompute diagnostics, pick the next one and repeat the process. This approach would be very slow but would lead to correct results (unless it doesn't converge where one fix introduces a diagnostic that was just fixed by a previous fix). We chose to not implement this approach. - **Batch fix approach**: Another way is to compute all the diagnostics, pick each diagnostic and give it to a fixer and apply it to produce a new solution. If there were 'n' diagnostics, there would be 'n' new solutions. Now just merge them all together in one go. This may produce incorrect results (when different fixes change the same region of code in different ways) but it is very fast. We have one implementation of this approach in `WellKnownFixAllProviders.BatchFixer` - **Custom approach**: Depending on the fix, there may be a custom solution to fix multiple issues. For example, consider an analyzer that simply needs to generate one file as the fix for any instance of the issue. Instead of generating the same file over and over using the previous two approaches, one could write a custom `FixAllProvider` that simply generates the file once if there were any diagnostics at all. Since there are various ways of fixing all issues, we've implemented a framework and provided the one general implementation that we think is useful in many cases. -Built-in FixAllProvider -======================= +# Built-in FixAllProvider We provide a default `BatchFixAllProvider` implementation of a FixAll provider that uses the underlying code fixer to compute the FixAll occurrences code fixes. To use the batch fixer, you should return the static `WellKnownFixAllProviders.BatchFixer` instance in the `CodeFixProvider.GetFixAllProvider` override. NOTE: See the following section on **'Limitations of the BatchFixer'** to determine if the batch fixer can be used by your code fixer. Given a trigger diagnostic, a trigger code action, the underlying code fixer and the FixAll scope, the BatchFixer computes FixAll occurrences code fix with the following steps: - - Compute all instances of the trigger diagnostic across the FixAll scope. - - For each computed diagnostic, invoke the underlying code fixer to compute the set of code actions to fix the diagnostic. - - Collect all the registered code actions that have the same equivalence key as the trigger code action. - - Apply all these code actions on the original solution snapshot to compute new solution snapshots. The batch fixer only batches code action operations of type `ApplyChangesOperation` present within the individual code actions, other types of operations are ignored. - - Sequentially merge the new solution snapshots into a final solution snapshot. Only non-conflicting code actions whose fix spans don't overlap the fix spans of prior merged code actions are retained. -Limitations of the BatchFixer -============================= +- Compute all instances of the trigger diagnostic across the FixAll scope. +- For each computed diagnostic, invoke the underlying code fixer to compute the set of code actions to fix the diagnostic. +- Collect all the registered code actions that have the same equivalence key as the trigger code action. +- Apply all these code actions on the original solution snapshot to compute new solution snapshots. The batch fixer only batches code action operations of type `ApplyChangesOperation` present within the individual code actions, other types of operations are ignored. +- Sequentially merge the new solution snapshots into a final solution snapshot. Only non-conflicting code actions whose fix spans don't overlap the fix spans of prior merged code actions are retained. + +# Limitations of the BatchFixer The BatchFixer is designed for a common category of fixers where fix spans for diagnostics don't overlap with each other. For example, assume there is a diagnostic that spans a particular expression, and a fixer that fixes that expression. If all the instances of this diagnostic are guaranteed to have non-overlapping spans, then their fixes can be computed independently and this batch of fixes can be subsequently merged together. However, there are cases where the BatchFixer might not work for your fixer. Following are some such examples: - - Code fixer registers code actions without an equivalence key or with a null equivalence key. - - Code fixer registers non-local code actions, i.e. a code action whose fix span is completely distinct from diagnostic span. For example, a fix that adds a new declaration node. Multiple such fixes are likely to have overlapping spans and hence could be conflicting. - - Diagnostics to be fixed as part of FixAll occurrences have overlapping spans. It is likely that fixes for such diagnostics will have overlapping spans too, and hence would conflict with each other. - - Code fixer registers code actions with operations other than `ApplyChangesOperation`. BatchFixer ignores such operations and hence may produce unexpected results. -Implementing a custom FixAllProvider -==================================== +- Code fixer registers code actions without an equivalence key or with a null equivalence key. +- Code fixer registers non-local code actions, i.e. a code action whose fix span is completely distinct from diagnostic span. For example, a fix that adds a new declaration node. Multiple such fixes are likely to have overlapping spans and hence could be conflicting. +- Diagnostics to be fixed as part of FixAll occurrences have overlapping spans. It is likely that fixes for such diagnostics will have overlapping spans too, and hence would conflict with each other. +- Code fixer registers code actions with operations other than `ApplyChangesOperation`. BatchFixer ignores such operations and hence may produce unexpected results. + +# Implementing a custom FixAllProvider For cases where you cannot use the BatchFixer, you must implement your own `FixAllProvider`. It is recommended that you create a singleton instance of the FixAll provider, instead of creating a new instance for every `CodeFixProvider.GetFixAllProvider` invocation. Following guidelines should help in the implementation: - - **GetFixAsync:** Primary method to compute the FixAll occurrences code fix for a given `FixAllContext`. You may use the set of 'GetXXXDiagnosticsAsync' methods on the `FixAllContext` to compute the diagnostics to be fixed. You must return a single code action that fixes all the diagnostics in the given FixAll scope. - - **GetSupportedFixAllScopes:** Virtual method to get all the supported FixAll scopes. By default, it returns all the three supported scopes: document, project and solution scopes. Generally, you need not override this method. However, you may do so if you wish to support a subset of these scopes. - - **GetSupportedFixAllDiagnosticIds:** Virtual method to get all the fixable diagnostic ids. By default, it returns the underlying code fixer's `FixableDiagnosticIds`. Generally, you need not override this method. However, you may do so if you wish to support FixAll only for a subset of these ids. -See [DeclarePublicAPIFix](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs) for an example implementation of a custom FixAllProvider. +- **GetFixAsync:** Primary method to compute the FixAll occurrences code fix for a given `FixAllContext`. You may use the set of 'GetXXXDiagnosticsAsync' methods on the `FixAllContext` to compute the diagnostics to be fixed. You must return a single code action that fixes all the diagnostics in the given FixAll scope. +- **GetSupportedFixAllScopes:** Virtual method to get all the supported FixAll scopes. By default, it returns all the three supported scopes: document, project and solution scopes. Generally, you need not override this method. However, you may do so if you wish to support a subset of these scopes. +- **GetSupportedFixAllDiagnosticIds:** Virtual method to get all the fixable diagnostic ids. By default, it returns the underlying code fixer's `FixableDiagnosticIds`. Generally, you need not override this method. However, you may do so if you wish to support FixAll only for a subset of these ids. + +See [DeclarePublicAPIFix](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs) for an example implementation of a custom FixAllProvider. diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md index da3ecdd5166a7..af85d951a3b53 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md @@ -314,3 +314,46 @@ CSharpCompilation.Create("test", Similarly this can be observed when using the command-line argument `/refonly` or the `ProduceOnlyReferenceAssembly` MSBuild property. + +## `partial` cannot be a return type of methods + +***Introduced in Visual Studio 2022 version 17.14*** + +The [partial events and constructors](https://github.com/dotnet/csharplang/issues/9058) language feature +allows the `partial` modifier in more places and so it cannot be a return type unless escaped: + +```cs +class C +{ + partial F() => new partial(); // previously worked + @partial F() => new partial(); // workaround +} + +class partial { } +``` + +## `extension` treated as a contextual keyword + +PROTOTYPE record which version this break is introduced in + +Starting with C# 14, the `extension` keyword serves a special purpose in denoting extension containers. +This changes how the compiler interprets certain code constructs. + +If you need to use "extension" as an identifier rather than a keyword, escape it with the `@` prefix: `@extension`. This tells the compiler to treat it as a regular identifier instead of a keyword. + +The compiler will parse this as an extension container rather than a constructor. +```csharp +class extension +{ + extension(object o) { } // parsed as an extension container +} +``` + +The compiler will fail to parse this as a method with return type `extension`. +```csharp +class extension +{ + extension M() { } // will not compile +} +``` + diff --git a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md index 3ebc24cd2fe0a..ede353e5c7d71 100644 --- a/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md +++ b/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md @@ -183,4 +183,4 @@ of the value is as follows: 5. `omit_if_default`. Redundant modifiers are disallowed. For example a private class member will be disallowed from using `private`, and a public interface member will be disallowed from using `public`. This is the option to use if you feel that restating the accessibility when it matches what the language chooses by default is redundant and - should be disalloed. + should be disallowed. diff --git a/docs/contributing/Building, Debugging, and Testing on Windows.md b/docs/contributing/Building, Debugging, and Testing on Windows.md index 87a3722a7adb5..235ae3e82854c 100644 --- a/docs/contributing/Building, Debugging, and Testing on Windows.md +++ b/docs/contributing/Building, Debugging, and Testing on Windows.md @@ -223,15 +223,16 @@ Before pushing a relevant fix to CI, you can validate locally using the `-testUs ### Running the PublicAPI fixer -1. Install `dotnet-format` as a global tool. It does ship as part of the SDK, but a separate version can be installed as a global tool and invoked with `dotnet-format {options}`. -`C:\Source\roslyn> dotnet tool install -g dotnet-format --version "6.*" --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json` -2. Restore and build `Compilers.slnf`. This is necessary to ensure the source generator project is built and we can load the generator assembly when running `dotnet-format`. -`C:\Source\roslyn> .\restore.cmd` -`C:\Source\roslyn> .\build.cmd` -3. Invoke the `dotnet-format` global tool. Running only the analyzers subcommand and fixing only the "missing Public API signature" diagnostic. We must also pass the `--include-generated` flag to include source generated documents in the analysis. -`C:\Source\roslyn> cd ..` -`C:\Source> dotnet-format analyzers .\roslyn\Compilers.slnf --diagnostics=RS0016 --no-restore --include-generated -v diag` - +1. Restore and build `Compilers.slnf`. This is necessary to ensure the source generator project is built and we can load the generator assembly when running `dotnet format`. + ```ps1 + C:\Source\roslyn> .\restore.cmd + C:\Source\roslyn> .\build.cmd + ``` +2. Invoke `dotnet format` (the one included in .NET SDK, not the global tool `dotnet-format` which is deprecated). Running only the analyzers subcommand and fixing only the "missing Public API signature" diagnostic. We must also pass the `--include-generated` flag to include source generated documents in the analysis. + ```ps1 + C:\Source\roslyn> cd .. + C:\Source> dotnet format analyzers .\roslyn\Compilers.slnf --diagnostics=RS0016 --no-restore --include-generated -v diag + ``` ## Contributing diff --git a/docs/features/field-nullability.md b/docs/features/field-nullability.md new file mode 100644 index 0000000000000..5dd2f97992271 --- /dev/null +++ b/docs/features/field-nullability.md @@ -0,0 +1,23 @@ +# `field` keyword nullable analysis implementation + +See also [specification](https://github.com/dotnet/csharplang/blob/94205582d0f5c73e5765cb5888311c2f14890b95/proposals/field-keyword.md#nullability). + +## Symbol API behavior + +In order to decide the NullableAnnotation of a SynthesizedBackingFieldSymbol we need to: +1. Decide if the getter associated with this field requires a null-resilience analysis. +2. If it *doesn't* require such an analysis, then the nullable annotation of the property is used. +3. If it *does*, then a binding+nullable analysis of the getter must be performed to decide the nullability of the backing field. + +There are some significant problems with directly exposing the nullable annotation on this field symbol. +1. Cost. It's not clear if it's OK for reading the NullableAnnotation off a field to cause us to bind and nullable analyze something. If some tooling is traversing member symbols for indexing or some such, it may be a problem if that causes unexpected method binding to occur where it didn't before. +2. Stack cycles. In the process of binding+flow analyzing, we may want to access the nullable annotation of the field. If we are *already* in the process of determining that field's nullable annotation, then we would need to "short-circuit" and return some "placeholder" value. We would need to take care to isolate the "internal" implementation from any public implementation, in order to ensure that a consistent answer is given to users of the public API. +... + +It's tempting to avoid exposing the inferred nullable annotation in the public symbol model. This might be problematic for automated tooling which is trying to reason about nullable initialization. For example, if a diagnostic suppressor wants to suppress `CS8618` nullable initialization warnings under certain conditions. How should it decide whether/why a property like `string Prop => field ??= GetValue();` requires initialization in constructors? It's unclear to me whether this matters. + +In the interests of *simplicity* (first make it correct, then make it fast), I'd like to move forward by *not* exposing the inferred annotation in the symbol model. Rather, we introduce a new internal API `SynthesizedBackingFieldSymbol.GetInferredNullableAnnotation()`, which `NullableWalker` will use to decide initial nullable state, report warnings, and so on. The implementation simply does an on-demand binding of the get accessor, then nullable analyzes and decides the nullable annotation. Care needs to be taken to avoid using this API in a re-entrant manner--so, the `NullableWalker` passes which are used to infer the nullability annotation must avoid calling it. + +This means that the ordinary `FieldSymbol.TypeWithAnnotations` API would not expose the inferred nullability, and neither would `IFieldSymbol.Type` or `IFieldSymbol.NullableAnnotation`. Instead the field's nullability would always match the property's. This is something I would actually like to fix, maybe before merging to main. It feels like Quick Info, etc., should expose the inferred nullability through the ordinary APIs. + +Once we get a handle on the behaviors of the getter null resilience, we can start talking about how to reduce cost associated with it. For example, we could try to reduce redundant binding, by forcing nullable annotations to be inferred prior to analyzing certain methods. Before processing a constructor in `MethodCompiler`, for example, maybe we would identify the getters that need to be compiled for purposes of inference, and force those to compile first. \ No newline at end of file diff --git a/docs/roslyn-analyzers/README.md b/docs/roslyn-analyzers/README.md new file mode 100644 index 0000000000000..be255b10a37e8 --- /dev/null +++ b/docs/roslyn-analyzers/README.md @@ -0,0 +1,37 @@ +# Roslyn Analyzers + +## Microsoft.CodeAnalysis.Analyzers + +*Latest stable version:* [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.Analyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.Analyzers) + +*Latest pre-release version:* [here](https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet7/NuGet/Microsoft.CodeAnalysis.Analyzers/versions) + +This package contains rules for correct usage of APIs from the [Microsoft.CodeAnalysis](https://www.nuget.org/packages/Microsoft.CodeAnalysis) NuGet package, i.e. .NET Compiler Platform ("Roslyn") APIs. These are primarily aimed towards helping authors of diagnostic analyzers and code fix providers to invoke the Microsoft.CodeAnalysis APIs in a recommended manner. [More info about rules in this package](../../src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md) + +## Roslyn.Diagnostics.Analyzers + +*Latest stable version:* [![NuGet](https://img.shields.io/nuget/v/Roslyn.Diagnostics.Analyzers.svg)](https://www.nuget.org/packages/Roslyn.Diagnostics.Analyzers) + +*Latest pre-release version:* [here](https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet7/NuGet/Roslyn.Diagnostics.Analyzers/versions) + +This package contains rules that are very specific to the .NET Compiler Platform ("Roslyn") project, i.e. [dotnet/roslyn](https://github.com/dotnet/roslyn) repo. This analyzer package is *not intended for general consumption* outside the Roslyn repo. [More info about rules in this package](../../src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.md) + +## Microsoft.CodeAnalysis.BannedApiAnalyzers + +*Latest stable version:* [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.BannedApiAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.BannedApiAnalyzers) + +*Latest pre-release version:* [here](https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet7/NuGet/Microsoft.CodeAnalysis.BannedApiAnalyzers/versions) + +This package contains customizable rules for identifying references to banned APIs. [More info about rules in this package](../../src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.md) + +For instructions on using this analyzer, see [Instructions](../../src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md). + +## Microsoft.CodeAnalysis.PublicApiAnalyzers + +*Latest stable version:* [![NuGet](https://img.shields.io/nuget/v/Microsoft.CodeAnalysis.PublicApiAnalyzers.svg)](https://www.nuget.org/packages/Microsoft.CodeAnalysis.PublicApiAnalyzers) + +*Latest pre-release version:* [here](https://dev.azure.com/dnceng/public/_artifacts/feed/dotnet7/NuGet/Microsoft.CodeAnalysis.PublicApiAnalyzers/versions) + +This package contains rules to help library authors monitoring change to their public APIs. [More info about rules in this package](../../src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md) + +For instructions on using this analyzer, see [Instructions](../../src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md). \ No newline at end of file diff --git a/docs/roslyn-analyzers/rules/RS1022.md b/docs/roslyn-analyzers/rules/RS1022.md new file mode 100644 index 0000000000000..ecdb021e08d2b --- /dev/null +++ b/docs/roslyn-analyzers/rules/RS1022.md @@ -0,0 +1,22 @@ +## RS1022: Do not use types from Workspaces assembly in an analyzer + +Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +> **Warning** +> +> The analysis performed by RS1022 is slow and relies on implementation details of the JIT compiler for correctness. +> Authors of compiler extensions are encouraged to use the stricter (and faster) analyzer RS1038 instead of this rule. +> +> RS1038 is enabled by default. To enable RS1022 instead, the following configuration may be added to **.globalconfig**: +> +> ```ini +> roslyn_correctness.assembly_reference_validation = relaxed +> ``` diff --git a/docs/roslyn-analyzers/rules/RS1038.md b/docs/roslyn-analyzers/rules/RS1038.md new file mode 100644 index 0000000000000..e0d73e8db2d4d --- /dev/null +++ b/docs/roslyn-analyzers/rules/RS1038.md @@ -0,0 +1,46 @@ +## RS1038: Compiler extensions should be implemented in assemblies with compiler-provided references + +Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +This rule helps ensure compiler extensions (e.g. analyzers and source generators) will load correctly in all compilation +scenarios. Depending on the manner in which the compiler is invoked, some assemblies may not be present during a build, +and attempting to reference them will result in exceptions that prevent the compiler extension from loading. RS1038 is +the most strict and best performing validation for this scenario. + +RS1038 is enabled by default unless relaxed validation has been manually enabled in **.globalconfig** as described in +[RS1022](RS1022.md). + +### Rules for compiler feature references + +* Compiler features supporting C# code should only reference the NuGet packages **Microsoft.CodeAnalysis.Common** and/or **Microsoft.CodeAnalysis.CSharp** +* Compiler features supporting Visual Basic code should only reference **Microsoft.CodeAnalysis.Common** and/or **Microsoft.CodeAnalysis.VisualBasic** +* Compiler features supporting both C# and Visual Basic should only reference **Microsoft.CodeAnalysis.Common** +* Compiler features should not be implemented in assemblies containing a reference to **Microsoft.CodeAnalysis.Workspaces.Common** + +> **Note** +> +> This analyzer only checks references to the core Roslyn assemblies. Compiler extensions with other dependencies may +> face restrictions and/or packaging requirements outside the scope of this analyzer. + +### Compiler extension points + +The following compiler extension points are examined by this analyzer: + +* `DiagnosticAnalyzer` +* `DiagnosticSuppressor` +* `ISourceGenerator` +* `IIncrementalGenerator` + +### Other extension points + +Some extension points provided by Roslyn are IDE extensions (e.g. code fixes and completion providers). These features +may ship in the same package as compiler features, but should be implemented in their own assembly since they require a +reference to non-compiler package **Microsoft.CodeAnalysis.Workspaces.Common**. diff --git a/docs/roslyn-analyzers/rules/RS1041.md b/docs/roslyn-analyzers/rules/RS1041.md new file mode 100644 index 0000000000000..4d60ec77358bb --- /dev/null +++ b/docs/roslyn-analyzers/rules/RS1041.md @@ -0,0 +1,25 @@ +## RS1041: Compiler extensions should be implemented in assemblies targeting netstandard2.0 + +Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +This rule helps ensure compiler extensions (e.g. analyzers and source generators) will load correctly in all compilation +scenarios. Depending on the manner in which the compiler is invoked, the compiler may execute under .NET Framework or +.NET, and compiler extensions are expected to work consistently in both cases. By targeting netstandard2.0, compiler +extensions are known to be compatible with both execution environments. + +### Compiler extension points + +The following compiler extension points are examined by this analyzer: + +* `DiagnosticAnalyzer` +* `DiagnosticSuppressor` +* `ISourceGenerator` +* `IIncrementalGenerator` diff --git a/docs/specs/PortablePdb-Metadata.md b/docs/specs/PortablePdb-Metadata.md index e4d139ba0a351..7c4c9cab968f1 100644 --- a/docs/specs/PortablePdb-Metadata.md +++ b/docs/specs/PortablePdb-Metadata.md @@ -1,4 +1,4 @@ # Portable PDB v1.0: Format Specification -Moved to -https://github.com/dotnet/corefx/blob/main/src/System.Reflection.Metadata/specs/PortablePdb-Metadata.md +Moved to +https://github.com/dotnet/runtime/blob/main/docs/design/specs/PortablePdb-Metadata.md diff --git a/docs/wiki/NuGet-packages.md b/docs/wiki/NuGet-packages.md index 3caad8005c828..f7a0c0c41b582 100644 --- a/docs/wiki/NuGet-packages.md +++ b/docs/wiki/NuGet-packages.md @@ -51,6 +51,7 @@ Below are the versions of the language available in the NuGet packages. Remember - Version `4.10` includes C# 12.0 (Visual Studio 2022 version 17.10, .NET 8) - Version `4.11` includes C# 12.0 (Visual Studio 2022 version 17.11, .NET 8) - Version `4.12` includes C# 13.0 (Visual Studio 2022 version 17.12, .NET 9) +- Version `4.13` includes C# 13.0 (Visual Studio 2022 version 17.13, .NET 9) See the [history of C# language features](https://github.com/dotnet/csharplang/blob/main/Language-Version-History.md) for more details. diff --git a/eng/Directory.Packages.props b/eng/Directory.Packages.props index c21a629429963..20ded129bd665 100644 --- a/eng/Directory.Packages.props +++ b/eng/Directory.Packages.props @@ -21,6 +21,8 @@ 8.0.10 <_xunitVersion>2.9.2 2.1.0 + + 2.14.1 @@ -225,8 +227,12 @@ + + + + - + @@ -237,10 +243,15 @@ + + + + + @@ -301,7 +312,6 @@ - @@ -314,5 +324,15 @@ the generators we build would load on the command line but not load in IDEs. --> - + + + + + + + + diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 140179baf5d0d..1de1443142a26 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -3,52 +3,61 @@ - + overridden by previous repo outputs or come from previously source-built artifacts. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1fe07fb69f12a..ed9fd3a97dcec 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,6 +1,11 @@ + + + https://github.com/dotnet/roslyn + ae1fff344d46976624e68ae17164e0607ab68b10 + https://github.com/dotnet/source-build-externals @@ -8,9 +13,9 @@ - + https://github.com/dotnet/source-build-reference-packages - d794781cc75921b4ebbefe2eabdb0d6cd1713005 + 1556b6c639edcaee959013681afcf2e66b118537 @@ -24,117 +29,117 @@ - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 https://github.com/dotnet/runtime 5535e31a712343a63f5d7d796cd874e563e5ac14 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee https://github.com/dotnet/symreader @@ -150,9 +155,9 @@ https://github.com/dotnet/roslyn 5d10d428050c0d6afef30a072c4ae68776621877 - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee https://github.com/dotnet/roslyn-analyzers @@ -160,9 +165,9 @@ - + https://github.com/dotnet/runtime - 5535e31a712343a63f5d7d796cd874e563e5ac14 + 9d5a6a9aa463d6d10b0b0ba6d5982cc82f363dc3 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index e9301bb188b0b..3ff54c131b63b 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 4 14 0 - 2 + 3 $(MajorVersion).$(MinorVersion).$(PatchVersion) - 6.1.0 - 4.6.0 + 6.1.2 + 4.6.1 4.9.0 - 4.6.0 - 4.6.0 - 6.1.0 - 4.6.0 + 4.6.2 + 4.6.1 + 6.1.1 + 4.6.2 6.0.1 @@ -47,38 +47,38 @@ --> 2.0.0-beta4.24528.1 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.1 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 - 8.0.5 - 8.0.0 - 8.0.0 - 8.0.0 + 9.0.0 + 9.0.0 + 9.0.0 + 9.0.0 3.11.0 3.3.0 8.0.0-preview.23468.1 2.0.0 - 8.0.0 - 8.0.0 + 9.0.0 + 9.0.0 8.0.0 - 8.0.0 - 8.0.0 - 8.0.0 + 9.0.0 + 9.0.0 + 9.0.0 8.0.0 - 8.0.0 - 8.0.0 - 17.9.3137-preview3 + 9.0.0 + 9.0.0 + 17.14.1043-preview2 2.4.1 + + + + + 3.11.0 + 1.2.1 + 3.11.0 + 3.11.0 + 4.0.1 + 3.11.0 + 3.11.0 + 3.11.0 + 3.11.0 + 4.0.1 + + + 4.9.0-3.final + 4.6.0-1.final + true false @@ -104,7 +157,7 @@ false true - true + true + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + $(IntermediateOutputPath)$(NuspecPackageId).nuspec + $(ArtifactsBinDir) + false + $(TargetFrameworks) + $(TargetFramework) + + + + + false + false + false + MIT + + + + $(DotNetRoot)dotnet.exe + $(DotNetRoot)dotnet + \ + + + + + + <_GeneratedRulesetsDir>$(IntermediateOutputPath)Rulesets + <_GeneratedEditorconfigsDir>$(IntermediateOutputPath)Editorconfig + <_GeneratedGlobalAnalyzerConfigsDir>$(IntermediateOutputPath)GlobalAnalyzerConfigs + <_ValidateOffline Condition="'$(DotNetBuildSourceOnly)' == 'true' or '$(DotNetBuild)' == 'true'">true + <_ValidateOffline Condition="'$(_ValidateOffline)' == ''">false + false + true + true + true + true + true + true + + + + + true + + false + + + + false + + + + $(IntermediateOutputPath)Build + $(NuspecPackageId).props + + + + + + + DisableNETAnalyzersForNuGetPackage.props + + + + + + + $(IntermediateOutputPath)Build + $(NuspecPackageId).targets + + + + $(RepoRoot)src\RoslynAnalyzers\$(NuspecPackageId) + $(NuspecPackageId).md + + + + $(RepoRoot)src\RoslynAnalyzers\$(NuspecPackageId) + $(NuspecPackageId).sarif + + + + $(RepoRoot)docs\roslyn-analyzers + Analyzer Configuration.md + + + + + ForBrokenRuntime + + + + + + + + + + + + + + + + + + + + + + + + <_GenerateAnalyzerNuspecPath>$(ArtifactsBinDir)GenerateAnalyzerNuspec\$(Configuration)\$(NetRoslyn)\GenerateAnalyzerNuspec.dll + + + + + + + + + + + + <_NuspecMetadata Include="version=$(PackageVersion)" /> + <_NuspecMetadata Include="id=$(NuspecPackageId)" /> + <_NuspecMetadata Include="authors=$(Authors)" /> + <_NuspecMetadata Include="owners=$(Authors)" /> + <_NuspecMetadata Include="requireLicenseAcceptance=$(PackageRequireLicenseAcceptance)" /> + <_NuspecMetadata Include="description=$(Description)" /> + <_NuspecMetadata Include="copyright=$(Copyright)" /> + <_NuspecMetadata Include="license=$(PackageLicenseExpression)" /> + <_NuspecMetadata Include="projectUrl=$(PackageProjectUrl)" /> + <_NuspecMetadata Include="icon=$(PackageIcon)" /> + <_NuspecMetadata Include="releaseNotes=$(PackageReleaseNotes)" /> + <_NuspecMetadata Include="tags=$(PackageTags)" /> + <_NuspecMetadata Include="serviceable=$(Serviceable)" /> + <_NuspecMetadata Include="developmentDependency=$(DevelopmentDependency)" /> + <_NuspecMetadata Include="repositoryType=$(RepositoryType)" /> + <_NuspecMetadata Include="repositoryCommit=$(SourceRevisionId)" /> + <_NuspecMetadata Include="repositoryUrl=$(PrivateRepositoryUrl)" /> + <_NuspecMetadata Include="readme=$(PackageReadmeFile)" /> + + + + + + + \ No newline at end of file diff --git a/eng/targets/Imports.targets b/eng/targets/Imports.targets index fdd6d2f8e4eaa..c646c49984d5d 100644 --- a/eng/targets/Imports.targets +++ b/eng/targets/Imports.targets @@ -4,7 +4,6 @@ - $(NoWarn);NU1507 + $(NoWarn);RSEXPERIMENTAL005 + CommonExtensions Microsoft\VBCSharp\LanguageServices @@ -168,13 +171,6 @@ false - - - - - - - - $(CollectUpToDateCheckInputDesignTimeDependsOn);AddUpToDateCheckVSIXSourceItems - - - - - - - - - - - - \ No newline at end of file diff --git a/eng/targets/VisualStudio.targets b/eng/targets/VisualStudio.targets index 93a24918b4da5..c8bd4122f585a 100644 --- a/eng/targets/VisualStudio.targets +++ b/eng/targets/VisualStudio.targets @@ -45,7 +45,7 @@ @@ -59,7 +59,7 @@ We join PackageReference list with ReferenceCopyLocalPaths items to get the list of dlls to include in the VSIX. --> - <_NuGetPackageToIncludeInVsix Include="@(PackageReference)" Condition="'%(PackageReference.IncludeInVsix)' == 'true' or '%(PackageReference.PkgDefEntry)' != ''"/> + <_NuGetPackageToIncludeInVsix Include="@(PackageReference)" Condition="'%(PackageReference.ForceIncludeInVsix)' == 'true' or '%(PackageReference.PkgDefEntry)' != ''"/> <_RuntimeAssetsByPackageId Include="@(ReferenceCopyLocalPaths->'%(NuGetPackageId)')" Condition="%(ReferenceCopyLocalPaths.AssetType) == 'runtime'"> %(ReferenceCopyLocalPaths.Identity) @@ -73,7 +73,7 @@ - + true true true @@ -86,7 +86,7 @@ - <_UnknownAssets Include="@(_NuGetPackageToIncludeInVsix)" Condition="'%(_NuGetPackageToIncludeInVsix.IncludeInVsix)' == 'true'" /> + <_UnknownAssets Include="@(_NuGetPackageToIncludeInVsix)" Condition="'%(_NuGetPackageToIncludeInVsix.ForceIncludeInVsix)' == 'true'" /> <_UnknownAssets Remove="@(_RuntimeAssetsWithMetadata)" /> @@ -186,7 +186,7 @@ @@ -194,7 +194,7 @@ - + diff --git a/eng/targets/WpfWorkarounds.targets b/eng/targets/WpfWorkarounds.targets deleted file mode 100644 index 4bc46a00f1ccb..0000000000000 --- a/eng/targets/WpfWorkarounds.targets +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - <_WindowsDesktopSDKDir>$(NuGetPackageRoot)microsoft.net.sdk.windowsdesktop\9.0.0-rc.1.24425.3\ - <_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' == 'Core'">net9.0 - <_PresentationBuildTasksTfm Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 - <_PresentationBuildTasksAssembly Condition="'$(_PresentationBuildTasksAssembly)'==''">$([MSBuild]::Unescape($([System.IO.Path]::GetFullPath('$(_WindowsDesktopSDKDir)tools\$(_PresentationBuildTasksTfm)\PresentationBuildTasks.dll')))) - - \ No newline at end of file diff --git a/eng/targets/XUnit.targets b/eng/targets/XUnit.targets index d6f404fe5ad65..c516e0e27296a 100644 --- a/eng/targets/XUnit.targets +++ b/eng/targets/XUnit.targets @@ -19,12 +19,12 @@ - + diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 index 3a16cef0b11b5..18ef2f3fb1df1 100644 --- a/eng/test-determinism.ps1 +++ b/eng/test-determinism.ps1 @@ -29,7 +29,10 @@ $script:skipList = @( "Microsoft.CodeAnalysis.EditorFeatures2.UnitTests.dll", # Work around XLF issues https://github.com/dotnet/roslyn/issues/58840 - "Roslyn.VisualStudio.DiagnosticsWindow.dll.key" + "Roslyn.VisualStudio.DiagnosticsWindow.dll.key", + + # Work around resx issues https://github.com/dotnet/roslyn/issues/77544 + "Text.Analyzers.dll.key" ) function Run-Build([string]$rootDir, [string]$logFileName) { diff --git a/eng/test-rebuild.ps1 b/eng/test-rebuild.ps1 index 74017ea313302..1401cb308e4c4 100644 --- a/eng/test-rebuild.ps1 +++ b/eng/test-rebuild.ps1 @@ -73,6 +73,7 @@ try { # The assemblies are not marked with ReferenceAssemblyAttribute attribute. " --exclude net8.0\GeneratedRefAssemblies\Microsoft.CodeAnalysis.dll" + " --exclude net8.0\GeneratedRefAssemblies\Microsoft.CodeAnalysis.CSharp.dll" + + " --exclude net8.0\GeneratedRefAssemblies\System.Collections.Immutable.dll" + " --debugPath `"$ArtifactsDir/BuildValidator`"" + " --sourcePath `"$RepoRoot/`"" + diff --git a/global.json b/global.json index ef8ab9a2d7469..b094e3f86ce6c 100644 --- a/global.json +++ b/global.json @@ -1,18 +1,18 @@ { "sdk": { - "version": "9.0.103", + "version": "9.0.104", "allowPrerelease": false, "rollForward": "patch" }, "tools": { - "dotnet": "9.0.103", + "dotnet": "9.0.104", "vs": { "version": "17.8.0" } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25111.5", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25111.5", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25164.2", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.25164.2", "Microsoft.Build.Traversal": "3.4.0" } } diff --git a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs index 0e3b9dbb57afb..cc3aee619211d 100644 --- a/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs +++ b/src/Analyzers/CSharp/Analyzers/AddAccessibilityModifiers/CSharpAddAccessibilityModifiers.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers; diff --git a/src/Analyzers/CSharp/Analyzers/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs index f5abee1dd2a0c..34118b0e488ba 100644 --- a/src/Analyzers/CSharp/Analyzers/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/AddBraces/CSharpAddBracesDiagnosticAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using FormattingRangeHelper = Microsoft.CodeAnalysis.CSharp.Utilities.FormattingRangeHelper; namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces; diff --git a/src/Analyzers/CSharp/Analyzers/AddRequiredParentheses/CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/AddRequiredParentheses/CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer.cs index 4979dc2c8f9ea..ae50bf8ea186b 100644 --- a/src/Analyzers/CSharp/Analyzers/AddRequiredParentheses/CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/AddRequiredParentheses/CSharpAddRequiredExpressionParenthesesDiagnosticAnalyzer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Precedence; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AddRequiredParentheses; diff --git a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx index 4ebe1d626a4ea..622002791820c 100644 --- a/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx +++ b/src/Analyzers/CSharp/Analyzers/CSharpAnalyzersResources.resx @@ -431,4 +431,7 @@ Use unbound generic type + + Implement with Copilot + \ No newline at end of file diff --git a/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs index e4fbc30f17fee..7595c2653512b 100644 --- a/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToBlockScopedNamespaceDiagnosticAnalyzer.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace; diff --git a/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs index 89eafd4a3148f..84096e2b2af3f 100644 --- a/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertNamespace/ConvertToFileScopedNamespaceDiagnosticAnalyzer.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.ConvertNamespace; diff --git a/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.cs index 67e5ccb33901f..0ed3abec8cc64 100644 --- a/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionDiagnosticAnalyzer.cs @@ -7,7 +7,6 @@ using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionHelpers.cs b/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionHelpers.cs index 3a26494309d74..31dacb65a3c9c 100644 --- a/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionHelpers.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.ConvertSwitchStatementToExpression; diff --git a/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs index 8beb2a5c885fb..f8a82406481d4 100644 --- a/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/ConvertTypeofToNameof/CSharpConvertTypeOfToNameOfDiagnosticAnalyzer.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.ConvertTypeOfToNameOf; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/CSharp/Analyzers/FileHeaders/CSharpFileHeaderHelper.cs b/src/Analyzers/CSharp/Analyzers/FileHeaders/CSharpFileHeaderHelper.cs index b02038e02b62b..07731ad060358 100644 --- a/src/Analyzers/CSharp/Analyzers/FileHeaders/CSharpFileHeaderHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/FileHeaders/CSharpFileHeaderHelper.cs @@ -5,7 +5,6 @@ using System; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.FileHeaders; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.FileHeaders; diff --git a/src/Analyzers/CSharp/Analyzers/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs index dc8321bd3ca71..8757ccf17a123 100644 --- a/src/Analyzers/CSharp/Analyzers/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/InvokeDelegateWithConditionalAccess/InvokeDelegateWithConditionalAccessAnalyzer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs index 745c105ea3e46..86eac8a5562e3 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeLocalFunctionStatic/MakeLocalFunctionStaticDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableDiagnosticAnalyzer.cs index d32f40f33f558..419164ef2831f 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeStructFieldsWritable/CSharpMakeStructFieldsWritableDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeQuality; diff --git a/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs index cd8ed002775e4..e48b7cb79de81 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeStructMemberReadOnly/CSharpMakeStructMemberReadOnlyAnalyzer.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/MakeStructReadOnly/CSharpMakeStructReadOnlyDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/MakeStructReadOnly/CSharpMakeStructReadOnlyDiagnosticAnalyzer.cs index 3d98b37a812ab..5196ad7362b29 100644 --- a/src/Analyzers/CSharp/Analyzers/MakeStructReadOnly/CSharpMakeStructReadOnlyDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/MakeStructReadOnly/CSharpMakeStructReadOnlyDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementDiagnosticAnalyzer.cs index 82381f588feb8..7749d8ea7072a 100644 --- a/src/Analyzers/CSharp/Analyzers/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/NewLines/ConsecutiveBracePlacement/ConsecutiveBracePlacementDiagnosticAnalyzer.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; diff --git a/src/Analyzers/CSharp/Analyzers/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementDiagnosticAnalyzer.cs index 28d61950d84f8..b13ead044f34e 100644 --- a/src/Analyzers/CSharp/Analyzers/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/NewLines/ConstructorInitializerPlacement/ConstructorInitializerPlacementDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/OrderModifiers/CSharpOrderModifiersDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/OrderModifiers/CSharpOrderModifiersDiagnosticAnalyzer.cs index b4220b4071083..da2fc052e794e 100644 --- a/src/Analyzers/CSharp/Analyzers/OrderModifiers/CSharpOrderModifiersDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/OrderModifiers/CSharpOrderModifiersDiagnosticAnalyzer.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Diagnostics; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/CSharp/Analyzers/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionDiagnosticAnalyzer.cs index 9fa30811f8c11..ceff4a71942d0 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveConfusingSuppression/CSharpRemoveConfusingSuppressionDiagnosticAnalyzer.cs @@ -2,11 +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.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveConfusingSuppression; diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs index a313c1d8a5d35..f4f616f2f1e07 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.RemoveUnnecessaryCast; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryCast; diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer.cs index f1ed887255e6a..01f60a7845fc8 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.LanguageService; diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveDiagnosticAnalyzer.cs index 2b51deb885fd6..f2db2bf4e7ad5 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Concurrent; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; diff --git a/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/RemoveUnreachableCodeHelpers.cs b/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/RemoveUnreachableCodeHelpers.cs index deb1fb1dbc378..e68dc53df57db 100644 --- a/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/RemoveUnreachableCodeHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/RemoveUnreachableCode/RemoveUnreachableCodeHelpers.cs @@ -2,9 +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.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/CSharpSimplifyPropertyPatternDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/CSharpSimplifyPropertyPatternDiagnosticAnalyzer.cs index a38d9870c2d2d..3cd17d794b3e7 100644 --- a/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/CSharpSimplifyPropertyPatternDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/CSharpSimplifyPropertyPatternDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/SimplifyPropertyPatternHelpers.cs b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/SimplifyPropertyPatternHelpers.cs index 02ebe5f6963b3..369af480ac10d 100644 --- a/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/SimplifyPropertyPatternHelpers.cs +++ b/src/Analyzers/CSharp/Analyzers/SimplifyPropertyPattern/SimplifyPropertyPatternHelpers.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs index ea3ff1d9974e4..614c3841f23fd 100644 --- a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs index 8ce1cd87c339a..e238eba97b3ed 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForCreateDiagnosticAnalyzer.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.CodeStyle; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.UseCollectionInitializer; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyDiagnosticAnalyzer.cs index a1e87aac06ea7..161ac829c1d07 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs index 32cab2512c9eb..24db63134a0ba 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionExpression/CSharpUseCollectionExpressionForNewDiagnosticAnalyzer.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Shared.CodeStyle; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.UseCollectionInitializer; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs index fabc39b46613b..6a93604eb7369 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCollectionInitializer/CSharpUseCollectionInitializerAnalyzer.cs @@ -48,8 +48,11 @@ protected override bool HasExistingInvalidInitializerForCollection() protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpression( ArrayBuilder> preMatches, ArrayBuilder> postMatches, + out bool mayChangeSemantics, CancellationToken cancellationToken) { + mayChangeSemantics = false; + // Constructor wasn't called with any arguments. Nothing to validate. var argumentList = _objectCreationExpression.ArgumentList; if (argumentList is null || argumentList.Arguments.Count == 0) @@ -63,20 +66,21 @@ protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre if (this.SemanticModel.GetSymbolInfo(_objectCreationExpression, cancellationToken).Symbol is not IMethodSymbol { MethodKind: MethodKind.Constructor, - Parameters.Length: 1, + Parameters: [var firstParameter], } constructor) { return false; } - var ienumerableOfTType = this.SemanticModel.Compilation.IEnumerableOfTType(); - var firstParameter = constructor.Parameters[0]; - if (Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) || - firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) + if (CanSpreadFirstParameter(constructor.ContainingType, firstParameter)) { // Took a single argument that implements IEnumerable. We handle this by spreading that argument as the // first thing added to the collection. preMatches.Add(new(argumentList.Arguments[0].Expression, UseSpread: true)); + + // Can't be certain that spreading the elements will be the same as passing to the constructor. So pass + // that uncertainty up to the caller so they can inform the user. + mayChangeSemantics = true; return true; } else if (firstParameter is { Type.SpecialType: SpecialType.System_Int32, Name: "capacity" }) @@ -195,5 +199,36 @@ protected override bool AnalyzeMatchesAndCollectionConstructorForCollectionExpre } return false; + + bool CanSpreadFirstParameter(INamedTypeSymbol constructedType, IParameterSymbol firstParameter) + { + var compilation = this.SemanticModel.Compilation; + + var ienumerableOfTType = compilation.IEnumerableOfTType(); + if (!Equals(firstParameter.Type.OriginalDefinition, ienumerableOfTType) && + !firstParameter.Type.AllInterfaces.Any(i => Equals(i.OriginalDefinition, ienumerableOfTType))) + { + return false; + } + + // Looks like something passed to the constructor call that we could potentially spread instead. e.g. `new + // HashSet(someList)` can become `[.. someList]`. However, check for certain cases we know where this is + // wrong and we can't do this. + + // BlockingCollection and Collection both take ownership of the collection passed to them. So adds to + // them will add through to the original collection. They do not take the original collection and add their + // elements to itself. + + var collectionType = compilation.CollectionOfTType(); + var blockingCollectionType = compilation.BlockingCollectionOfTType(); + if (constructedType.GetBaseTypesAndThis().Any( + t => Equals(collectionType, t.OriginalDefinition) || + Equals(blockingCollectionType, t.OriginalDefinition))) + { + return false; + } + + return true; + } } } diff --git a/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs index 11a7bc2ca3928..3d39d9fb1637e 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; diff --git a/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/Utilities.cs b/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/Utilities.cs index 3a2bed7c95e77..e9607afdd5ef0 100644 --- a/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/Utilities.cs +++ b/src/Analyzers/CSharp/Analyzers/UseCompoundAssignment/Utilities.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment; diff --git a/src/Analyzers/CSharp/Analyzers/UseDefaultLiteral/CSharpUseDefaultLiteralDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseDefaultLiteral/CSharpUseDefaultLiteralDiagnosticAnalyzer.cs index ecc03d73a3d9f..4cf5a40ab9c57 100644 --- a/src/Analyzers/CSharp/Analyzers/UseDefaultLiteral/CSharpUseDefaultLiteralDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseDefaultLiteral/CSharpUseDefaultLiteralDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 7e7dbaa7c19de..1e5c0660e3aba 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index cedba7091590f..31b42df2a7e65 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index d963bbcd567ec..1278c59a1d3e2 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs index 1037084de70a6..2f51fcf2a6579 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs index d09df06b807fb..c6f9a78066f71 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForLocalFunctionHelper.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index f7b57bc77ca5a..d05bc5bfb5231 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index dd75fabe5d456..5965c35c1d8ba 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 5681617779e31..7d6679ca5135c 100644 --- a/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Analyzers/CSharp/Analyzers/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs index 418df4bc955ee..cb74292d302d9 100644 --- a/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseImplicitObjectCreation/CSharpUseImplicitObjectCreationDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs index 747bd949e2fca..69b85a660d257 100644 --- a/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseImplicitlyTypedLambdaExpression/CSharpUseImplicitlyTypedLambdaExpressionDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; @@ -12,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs index 3999c3accfdc2..def0b366e6230 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseIndexOperatorDiagnosticAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator; diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs index 7c9fd695c5004..9843f50846533 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs index 71d9f11c5ccda..f6bfbb9944301 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator; diff --git a/src/Analyzers/CSharp/Analyzers/UseInferredMemberName/CSharpUseInferredMemberNameDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseInferredMemberName/CSharpUseInferredMemberNameDiagnosticAnalyzer.cs index 6750377d7e3e8..40a6644d602ec 100644 --- a/src/Analyzers/CSharp/Analyzers/UseInferredMemberName/CSharpUseInferredMemberNameDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseInferredMemberName/CSharpUseInferredMemberNameDiagnosticAnalyzer.cs @@ -2,16 +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.Collections.Immutable; -using System.Threading; -using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UseInferredMemberName; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseInferredMemberName; diff --git a/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckDiagnosticAnalyzer.cs index ca11e6fbb1ee3..d8c7f61244005 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckDiagnosticAnalyzer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseIsNullCheck; diff --git a/src/Analyzers/CSharp/Analyzers/UseNameofInNullableAttribute/CSharpUseNameofInNullableAttributeDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseNameofInNullableAttribute/CSharpUseNameofInNullableAttributeDiagnosticAnalyzer.cs index 14504a2b6eb16..1ea6b49bf4b2d 100644 --- a/src/Analyzers/CSharp/Analyzers/UseNameofInNullableAttribute/CSharpUseNameofInNullableAttributeDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseNameofInNullableAttribute/CSharpUseNameofInNullableAttributeDiagnosticAnalyzer.cs @@ -4,18 +4,12 @@ using System; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Linq.Expressions; -using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseNameofInAttribute; diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsAnalyzer.cs index 31f759e0dffe3..553259e2d5568 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternCombinators/CSharpUsePatternCombinatorsAnalyzer.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UsePatternCombinators; diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndMemberAccessDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndMemberAccessDiagnosticAnalyzer.cs index c082fd6d437b3..937afd4fb19d7 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndMemberAccessDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpAsAndMemberAccessDiagnosticAnalyzer.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UsePatternMatching; diff --git a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpIsAndCastCheckDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpIsAndCastCheckDiagnosticAnalyzer.cs index 3d490403c7d1e..40f3aa5fc10e6 100644 --- a/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpIsAndCastCheckDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UsePatternMatching/CSharpIsAndCastCheckDiagnosticAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/Analyzers/UseSystemThreadingLock/CSharpUseSystemThreadingLockDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseSystemThreadingLock/CSharpUseSystemThreadingLockDiagnosticAnalyzer.cs index 84b0b251f3ee1..3f750b3e7483f 100644 --- a/src/Analyzers/CSharp/Analyzers/UseSystemThreadingLock/CSharpUseSystemThreadingLockDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseSystemThreadingLock/CSharpUseSystemThreadingLockDiagnosticAnalyzer.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf index b42bdd3e44dda..576a142a0ecc7 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.cs.xlf @@ -82,6 +82,11 @@ Vložené příkazy musí mít svůj vlastní řádek. + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified Indexování může být zjednodušené. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf index 059b0b878c2a3..96dccc829b54f 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.de.xlf @@ -82,6 +82,11 @@ Eingebettete Anweisungen müssen in einer eigenen Zeile enthalten sein. + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified Die Indizierung kann vereinfacht werden diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf index 7e39549a2581d..808bededb715e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.es.xlf @@ -82,6 +82,11 @@ Las instrucciones incrustadas deben estar en su propia línea + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified La indexación de direcciones se puede simplificar diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf index 5af0777e2db40..7125923272b2b 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.fr.xlf @@ -82,6 +82,11 @@ Les instructions imbriquées doivent être placées sur leur propre ligne + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified L'indexation peut être simplifiée diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf index c29a6dfa1f5ae..1c47f131846cf 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.it.xlf @@ -82,6 +82,11 @@ Le istruzioni incorporate devono essere posizionate nella rispettiva riga + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified L'indicizzazione può essere semplificata diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf index 5bcffb1b763ce..498434b70baad 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ja.xlf @@ -82,6 +82,11 @@ 埋め込みステートメントは独自の行に配置する必要があります + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified インデックスの作成を簡素化することができます diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf index 2154b0285991f..38e28ac74b712 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ko.xlf @@ -82,6 +82,11 @@ 포함 문은 고유한 줄에 있어야 합니다. + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified 인덱싱을 단순화할 수 있습니다. diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf index cb8612fbc4bf2..6df51c97b6ce0 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pl.xlf @@ -82,6 +82,11 @@ Osadzone instrukcje muszą znajdować się w osobnym wierszu + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified Indeksowanie można uprościć diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf index 55bcedeceae12..3d1c3ff54e34e 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.pt-BR.xlf @@ -82,6 +82,11 @@ As instruções inseridas precisam estar nas próprias linhas + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified A indexação pode ser simplificada diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf index f62102c4a4559..f4c2e65836c99 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.ru.xlf @@ -82,6 +82,11 @@ Встроенные операторы должны располагаться на отдельной строке. + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified Вы можете упростить индексирование diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf index b70da67fc51ba..fd2429f73c4f1 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.tr.xlf @@ -82,6 +82,11 @@ Katıştırılmış deyimler kendi satırına yerleştirilmelidir + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified Dizin oluşturma basitleştirilebilir diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf index 86416561f2480..e86aa58e241ca 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hans.xlf @@ -82,6 +82,11 @@ 嵌入的语句必须放在其自己的行上 + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified 索引可以简化 diff --git a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf index b3503b34e2199..c2ee335464254 100644 --- a/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf +++ b/src/Analyzers/CSharp/Analyzers/xlf/CSharpAnalyzersResources.zh-Hant.xlf @@ -82,6 +82,11 @@ 內嵌陳述式必須位於自己的行中 + + Implement with Copilot + Implement with Copilot + + Indexing can be simplified 可簡化索引 diff --git a/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs index 68b7d5aed208f..3a616c4443e65 100644 --- a/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Analyzers/CSharp/CodeFixes/ConditionalExpressionInStringInterpolation/CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ConditionalExpressionInStringInterpolation/CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider.cs index be9a3102fe507..61aee9bcc433a 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConditionalExpressionInStringInterpolation/CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConditionalExpressionInStringInterpolation/CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; diff --git a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs index 43785bf880ddb..752bc049b3622 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertSwitchStatementToExpression/ConvertSwitchStatementToExpressionCodeFixProvider.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Analyzers/CSharp/CodeFixes/ConvertTypeOfToNameOf/CSharpConvertTypeOfToNameOfCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ConvertTypeOfToNameOf/CSharpConvertTypeOfToNameOfCodeFixProvider.cs index 54b91bec84e9a..f0ea3d4eecd0d 100644 --- a/src/Analyzers/CSharp/CodeFixes/ConvertTypeOfToNameOf/CSharpConvertTypeOfToNameOfCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ConvertTypeOfToNameOf/CSharpConvertTypeOfToNameOfCodeFixProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertTypeOfToNameOf; diff --git a/src/Analyzers/CSharp/CodeFixes/ForEachCast/CSharpForEachCastCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/ForEachCast/CSharpForEachCastCodeFixProvider.cs index c066622935221..a3d6e24716c20 100644 --- a/src/Analyzers/CSharp/CodeFixes/ForEachCast/CSharpForEachCastCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/ForEachCast/CSharpForEachCastCodeFixProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ForEachCast; using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.ForEachCast; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateConstructor/GenerateConstructorCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/GenerateConstructor/GenerateConstructorCodeFixProvider.cs index e2b20ae46539f..2aaa29a31d5f1 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateConstructor/GenerateConstructorCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateConstructor/GenerateConstructorCodeFixProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.GenerateMember; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateEnumMember/GenerateEnumMemberCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/GenerateEnumMember/GenerateEnumMemberCodeFixProvider.cs index 0c6119fbe7905..d823bca790c2f 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateEnumMember/GenerateEnumMemberCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateEnumMember/GenerateEnumMemberCodeFixProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.GenerateMember; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GenerateMember.GenerateEnumMember; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateConversionCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateConversionCodeFixProvider.cs index af47c46976f2f..0c47a95aa4c16 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateConversionCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateConversionCodeFixProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.GenerateMember; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateMethodCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateMethodCodeFixProvider.cs index ebdc100c1d1a4..db9456c44dcf5 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateMethodCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateMethod/GenerateMethodCodeFixProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.GenerateMember; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateConversionService.cs b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateConversionService.cs index 084ccf0323348..a06d54e729e62 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateConversionService.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateConversionService.cs @@ -28,9 +28,9 @@ internal sealed partial class CSharpGenerateConversionService() : protected override bool IsImplicitConversionGeneration(SyntaxNode node) { return node is ExpressionSyntax && - (node.Parent is AssignmentExpressionSyntax || node.Parent is EqualsValueClauseSyntax) && - !(node is CastExpressionSyntax) && - !(node is MemberAccessExpressionSyntax); + node.Parent is AssignmentExpressionSyntax or EqualsValueClauseSyntax && + node is not CastExpressionSyntax && + node is not MemberAccessExpressionSyntax; } protected override bool IsExplicitConversionGeneration(SyntaxNode node) diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateDeconstructMethodService.cs b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateDeconstructMethodService.cs index 862f7db6e3f54..e5e245993adc7 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateDeconstructMethodService.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateDeconstructMethodService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Composition; diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateMethodService.cs b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateMethodService.cs index f6986582d93d0..2c92ece627cf0 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateMethodService.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateMethodService.cs @@ -87,15 +87,18 @@ protected override bool TryInitializeSimpleNameState( identifierToken = simpleName.Identifier; var memberAccess = simpleName?.Parent as MemberAccessExpressionSyntax; - var conditionalMemberAccess = simpleName?.Parent?.Parent?.Parent as ConditionalAccessExpressionSyntax; - var inConditionalMemberAccess = conditionalMemberAccess != null; + var (conditionalAccessExpression, invocation) = + simpleName is { Parent: MemberBindingExpressionSyntax { Parent: InvocationExpressionSyntax { Parent: ConditionalAccessExpressionSyntax conditionalAccessExpression1 } invocation1 } memberBinding } && + conditionalAccessExpression1.WhenNotNull == invocation1 && + invocation1.Expression == memberBinding && + memberBinding.Name == simpleName ? (conditionalAccessExpression1, invocation1) : default; if (memberAccess != null) { simpleNameOrMemberAccessExpression = memberAccess; } - else if (inConditionalMemberAccess) + else if (conditionalAccessExpression != null) { - simpleNameOrMemberAccessExpression = conditionalMemberAccess; + simpleNameOrMemberAccessExpression = conditionalAccessExpression; } else { @@ -106,37 +109,29 @@ protected override bool TryInitializeSimpleNameState( { if (simpleNameOrMemberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression, out invocationExpressionOpt)) { - isInConditionalAccessExpression = inConditionalMemberAccess; + // want to look for anything of the form: a?.B() a?.B.C() a?.B.C.D() etc + isInConditionalAccessExpression = invocationExpressionOpt.Parent is ConditionalAccessExpressionSyntax { WhenNotNull: var whenNotNull } && + whenNotNull == invocationExpressionOpt; return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing; } - // We need to check that the tree is structured like so: - // ConditionalAccessExpressionSyntax - // -> InvocationExpressionSyntax - // -> MemberBindingExpressionSyntax - // and that the name at the end of this expression matches the simple name we were given - else if ((((simpleNameOrMemberAccessExpression as ConditionalAccessExpressionSyntax) - ?.WhenNotNull as InvocationExpressionSyntax) - ?.Expression as MemberBindingExpressionSyntax) - ?.Name == simpleName) + + if (conditionalAccessExpression != null) { - invocationExpressionOpt = (InvocationExpressionSyntax)((ConditionalAccessExpressionSyntax)simpleNameOrMemberAccessExpression).WhenNotNull; - isInConditionalAccessExpression = inConditionalMemberAccess; + invocationExpressionOpt = invocation; + isInConditionalAccessExpression = true; return !invocationExpressionOpt.ArgumentList.CloseParenToken.IsMissing; } - else if (simpleName.IsKind(SyntaxKind.IdentifierName)) + + // If we don't have an invocation node, then see if we can infer a delegate in + // this location. Check if this is a place where a delegate can go. Only do this + // for identifier names. for now. It gets really funky if you have to deal with + // a generic name here. + if (simpleName is IdentifierNameSyntax && + !simpleNameOrMemberAccessExpression.IsLeftSideOfAnyAssignExpression()) { - // If we don't have an invocation node, then see if we can infer a delegate in - // this location. Check if this is a place where a delegate can go. Only do this - // for identifier names. for now. It gets really funky if you have to deal with - // a generic name here. - - // Can't assign into a method. - if (!simpleNameOrMemberAccessExpression.IsLeftSideOfAnyAssignExpression()) - { - invocationExpressionOpt = null; - isInConditionalAccessExpression = inConditionalMemberAccess; - return true; - } + invocationExpressionOpt = null; + isInConditionalAccessExpression = conditionalAccessExpression != null; + return true; } } diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs index 85694ee84cbb9..23a79435f3a53 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateParameterizedMember/CSharpGenerateParameterizedMemberService.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; namespace Microsoft.CodeAnalysis.CSharp.GenerateMember.GenerateMethod; @@ -51,15 +50,15 @@ protected override ITypeSymbol DetermineReturnTypeWorker(CancellationToken cance protected override ImmutableArray GetCapturedTypeParameters(CancellationToken cancellationToken) { - var result = new List(); + using var _ = ArrayBuilder.GetInstance(out var result); var semanticModel = Document.SemanticModel; foreach (var argument in _invocationExpression.ArgumentList.Arguments) { var type = argument.DetermineParameterType(semanticModel, cancellationToken); - type.GetReferencedTypeParameters(result); + type.AddReferencedTypeParameters(result); } - return [.. result]; + return result.ToImmutableAndClear(); } protected override ImmutableArray GenerateTypeParameters(CancellationToken cancellationToken) diff --git a/src/Analyzers/CSharp/CodeFixes/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs index efef64913eba6..cccff24a15d89 100644 --- a/src/Analyzers/CSharp/CodeFixes/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/GenerateVariable/CSharpGenerateVariableCodeFixProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -39,14 +38,8 @@ protected override bool IsCandidate(SyntaxNode node, SyntaxToken token, Diagnost protected override SyntaxNode? GetTargetNode(SyntaxNode node) { - if (node.IsKind(SyntaxKind.MemberBindingExpression)) - { - var nameNode = node.ChildNodes().FirstOrDefault(n => n.IsKind(SyntaxKind.IdentifierName)); - if (nameNode != null) - { - return nameNode; - } - } + if (node is MemberBindingExpressionSyntax memberBinding) + return memberBinding.Name; return base.GetTargetNode(node); } diff --git a/src/Analyzers/CSharp/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs index 9a25f4ae39100..368c6cf882b65 100644 --- a/src/Analyzers/CSharp/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/Iterator/CSharpAddYieldCodeFixProvider.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.Iterator; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs b/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs index bdea1f83c6eb7..0be3c5fc586cf 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeLocalFunctionStatic/MakeLocalFunctionStaticCodeFixHelper.cs @@ -2,14 +2,12 @@ // 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.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs index 457ed7d684699..3e8d66b3a3a39 100644 --- a/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/MakeMemberRequired/CSharpMakeMemberRequiredCodeFixProvider.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeMemberRequired; diff --git a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs index db2870ee9e8f6..239e5b7fc3a68 100644 --- a/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/Nullable/CSharpDeclareAsNullableCodeFixProvider.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs index 15e80e1eee832..9c5c3e275524b 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveAsyncModifier/CSharpRemoveAsyncModifierCodeFixProvider.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.RemoveAsyncModifier; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier; diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs index 726efeaf9df28..d901756a39dfb 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryCast/CSharpRemoveUnnecessaryCastCodeFixProvider.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryCast; diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveCodeFixProvider.cs index 616707576572e..43efc0ada18ef 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs index b459db309e58d..0e85b35b2c7a4 100644 --- a/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/RemoveUnusedParametersAndValues/CSharpRemoveUnusedValuesCodeFixProvider.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.RemoveUnusedParametersAndValues; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues; diff --git a/src/Analyzers/CSharp/CodeFixes/UpdateProjectToAllowUnsafe/CSharpUpdateProjectToAllowUnsafeCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UpdateProjectToAllowUnsafe/CSharpUpdateProjectToAllowUnsafeCodeFixProvider.cs index 4c7fe2d7c1435..3151c2fcc7490 100644 --- a/src/Analyzers/CSharp/CodeFixes/UpdateProjectToAllowUnsafe/CSharpUpdateProjectToAllowUnsafeCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UpdateProjectToAllowUnsafe/CSharpUpdateProjectToAllowUnsafeCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.UpgradeProject; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UpdateProjectToAllowUnsafe; diff --git a/src/Analyzers/CSharp/CodeFixes/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs index f76ddccb4aa3f..3c7b708944495 100644 --- a/src/Analyzers/CSharp/CodeFixes/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UpgradeProject/CSharpUpgradeProjectCodeFixProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.UpgradeProject; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UpgradeProject; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCoalesceExpression/CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCoalesceExpression/CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs index ebb1f88d3d23a..3943d6e13cabf 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCoalesceExpression/CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCoalesceExpression/CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.UseCoalesceExpression; namespace Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs index 23b878b52e152..1ef954157683c 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpCollectionExpressionRewriter.cs @@ -5,13 +5,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; -using System.Linq.Expressions; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs index 99e656162ca91..ca2da4d011601 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForArrayCodeFixProvider.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UseCollectionExpression; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs index d29cecd68c36e..461891172f126 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForCreateCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyCodeFixProvider.cs index 8dd7fc80b9d32..be93c2fc486f8 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForEmptyCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs index 7a2f79d97a432..0d6ebea4aa39d 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForFluentCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -22,7 +21,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.UseCollectionExpression; using Microsoft.CodeAnalysis.UseCollectionInitializer; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForStackAllocCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForStackAllocCodeFixProvider.cs index 50e3a5d17a274..2c43d39c30587 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForStackAllocCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionExpression/CSharpUseCollectionExpressionForStackAllocCodeFixProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.UseCollectionExpression; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs index 849f1568fbcfd..44ff887dca1e2 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionExpression.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using Microsoft.CodeAnalysis.UseCollectionExpression; -using Microsoft.CodeAnalysis.UseCollectionInitializer; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer; diff --git a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionInitializer.cs b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionInitializer.cs index a3fc69030e6d3..62b5922704c0c 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionInitializer.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCollectionInitializer/CSharpUseCollectionInitializerCodeFixProvider_CollectionInitializer.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.UseCollectionExpression; -using Microsoft.CodeAnalysis.UseCollectionInitializer; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer; diff --git a/src/Analyzers/CSharp/CodeFixes/UseConditionalExpression/CSharpUseConditionalExpressionHelpers.cs b/src/Analyzers/CSharp/CodeFixes/UseConditionalExpression/CSharpUseConditionalExpressionHelpers.cs index 135ffe0faee5b..59e78ac8f5e71 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseConditionalExpression/CSharpUseConditionalExpressionHelpers.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseConditionalExpression/CSharpUseConditionalExpressionHelpers.cs @@ -2,9 +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.Linq; -using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs index 5c49751d76deb..6b3a01664eb5f 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseIsNullCheck/CSharpUseNullCheckOverTypeCheckCodeFixProvider.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; diff --git a/src/Analyzers/CSharp/CodeFixes/UseNameofInAttribute/CSharpUseNameofInAttributeCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseNameofInAttribute/CSharpUseNameofInAttributeCodeFixProvider.cs index 56a91d73f3a41..59059f826e54d 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseNameofInAttribute/CSharpUseNameofInAttributeCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseNameofInAttribute/CSharpUseNameofInAttributeCodeFixProvider.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.UseNameofInAttribute; diff --git a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs index 09fb8e4b3b937..038b093530b9c 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseSimpleUsingStatement/UseSimpleUsingStatementCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Composition; using System.Linq; -using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -20,7 +19,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement; diff --git a/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs b/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs index cbe2148fab052..80c96a2536846 100644 --- a/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs +++ b/src/Analyzers/CSharp/Tests/Formatting/FormattingAnalyzerTests.cs @@ -4,7 +4,6 @@ using System; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; diff --git a/src/Analyzers/CSharp/Tests/GenerateMethod/GenerateMethodTests.cs b/src/Analyzers/CSharp/Tests/GenerateMethod/GenerateMethodTests.cs index 5dc8bbc1fc38c..37b12812aab52 100644 --- a/src/Analyzers/CSharp/Tests/GenerateMethod/GenerateMethodTests.cs +++ b/src/Analyzers/CSharp/Tests/GenerateMethod/GenerateMethodTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod; using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; @@ -7686,6 +7687,50 @@ internal int C() """); } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1064748")] + public async Task TestGenerateMethodInConditionalAccess7_B() + { + await TestInRegularAndScriptAsync( + """ + class C + { + public C B { get; private set; } + public E D { get; private set; } + + void Main(C a) + { + int? x = a?.B.B.D.[|C|](); + } + + public class E + { + } + } + """, + """ + using System; + + class C + { + public C B { get; private set; } + public E D { get; private set; } + + void Main(C a) + { + int? x = a?.B.B.D.C(); + } + + public class E + { + internal int C() + { + throw new NotImplementedException(); + } + } + } + """); + } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1064748")] public async Task TestGenerateMethodInConditionalAccess8() { @@ -11145,4 +11190,80 @@ private static void Test() } """, parseOptions: CSharpParseOptions.Default); } + + [Fact] + public async Task TestNullConditionalAssignment1() + { + await TestAsync( + """ + using System; + + internal class Program + { + int x; + + void M(Program p) + { + p?.x = [|Goo|](); + } + } + """, + """ + using System; + + internal class Program + { + int x; + + void M(Program p) + { + p?.x = [|Goo|](); + } + + private int Goo() + { + throw new NotImplementedException(); + } + } + """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); + } + + [Fact] + public async Task TestNullConditionalAssignment2() + { + await TestAsync( + """ + using System; + + internal class Program + { + Program P; + int x; + + void M(Program p) + { + p?.P.x = [|Goo|](); + } + } + """, + """ + using System; + + internal class Program + { + Program P; + int x; + + void M(Program p) + { + p?.P.x = [|Goo|](); + } + + private int Goo() + { + throw new NotImplementedException(); + } + } + """, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); + } } diff --git a/src/Analyzers/CSharp/Tests/MakeRefStruct/MakeRefStructTests.cs b/src/Analyzers/CSharp/Tests/MakeRefStruct/MakeRefStructTests.cs index 151a515c1e686..23b1e49a73dcd 100644 --- a/src/Analyzers/CSharp/Tests/MakeRefStruct/MakeRefStructTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeRefStruct/MakeRefStructTests.cs @@ -2,17 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.MakeRefStruct; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; diff --git a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs index 0ef176175dd9e..5f5a53e1780a5 100644 --- a/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs +++ b/src/Analyzers/CSharp/Tests/MakeStructMemberReadOnly/MakeStructMemberReadOnlyTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.MakeStructMemberReadOnly; diff --git a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs index 3752f621c91a2..a011670dbdd0d 100644 --- a/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs +++ b/src/Analyzers/CSharp/Tests/NamingStyles/NamingStylesTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs index f1a7c333867f5..d5dc138ae07f0 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ArrowExpressionClausePlacement/ArrowExpressionClausePlacementTests.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.NewLines.ArrowExpressionClausePlacement; -using Microsoft.CodeAnalysis.CSharp.NewLines.ConstructorInitializerPlacement; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Xunit; diff --git a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs index de48866e2e01c..0cde7328329e9 100644 --- a/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs +++ b/src/Analyzers/CSharp/Tests/NewLines/ConditionalExpressionPlacement/ConditionalExpressionPlacementTests.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.NewLines.ConditionalExpressionPlacement; -using Microsoft.CodeAnalysis.CSharp.NewLines.ConstructorInitializerPlacement; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Xunit; diff --git a/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchExpressionTests_FixAllTests.cs b/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchExpressionTests_FixAllTests.cs index f34cf81e504b1..d2c22cb448ae9 100644 --- a/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchExpressionTests_FixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchExpressionTests_FixAllTests.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.PopulateSwitch; diff --git a/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchStatementTests_FixAllTests.cs b/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchStatementTests_FixAllTests.cs index 95918924d3f2a..73de5c402ff37 100644 --- a/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchStatementTests_FixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/PopulateSwitch/PopulateSwitchStatementTests_FixAllTests.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.PopulateSwitch; diff --git a/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs b/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs index e10214afc70e1..2175522703dea 100644 --- a/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/QualifyMemberAccess/QualifyMemberAccessTests_FixAllTests.cs @@ -6,10 +6,8 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.QualifyMemberAccess; diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveTests.cs index 9843d4e171bee..b020574a54c38 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveRedundantNullableDirectiveTests.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.RemoveUnnecessaryNullableDirective; diff --git a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveTests.cs index 3096264bec24d..6acbfd5841f8a 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnnecessaryNullableDirective/CSharpRemoveUnnecessaryNullableDirectiveTests.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.RemoveUnnecessaryNullableDirective; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.RemoveUnnecessaryNullableDirective; diff --git a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs index a0cd8ae3d72fe..70413925b1361 100644 --- a/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/RemoveUnusedParametersAndValues/RemoveUnusedValueAssignmentTests.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using static Roslyn.Test.Utilities.TestHelpers; diff --git a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqTypeCheckAndCastTests.cs b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqTypeCheckAndCastTests.cs index 8553feaa83b5e..af8407685b83b 100644 --- a/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqTypeCheckAndCastTests.cs +++ b/src/Analyzers/CSharp/Tests/SimplifyLinqExpression/CSharpSimplifyLinqTypeCheckAndCastTests.cs @@ -5,9 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.SimplifyLinqExpression; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.Analyzers.UnitTests.SimplifyLinqExpression; diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs index 8e8bf8c71c873..49622fce27f7e 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForArrayTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForEmptyTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForEmptyTests.cs index a8319d76ccc68..8caff98c38684 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForEmptyTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForEmptyTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; diff --git a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForStackAllocTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForStackAllocTests.cs index 208437f6edf7c..14a7dec386faf 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForStackAllocTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionExpression/UseCollectionExpressionForStackAllocTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.UseCollectionExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; diff --git a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs index d70e33ad6cbad..f33a25452afca 100644 --- a/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; using Xunit; @@ -1826,4 +1827,42 @@ void M(List? list1) LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77416")] + public async Task TestNoCollectionExpressionForBlockingCollection() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Concurrent; + + class A + { + public void Main(ConcurrentQueue queue) + { + BlockingCollection bc = [|new|](queue); + [|bc.Add(|]42); + } + } + """, + FixedCode = """ + using System; + using System.Collections.Concurrent; + + class A + { + public void Main(ConcurrentQueue queue) + { + BlockingCollection bc = new(queue) + { + 42 + }; + } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + ReferenceAssemblies = ReferenceAssemblies.Net.Net90, + }.RunAsync(); + } } diff --git a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs index 8055ed9663f4a..29193e52345ca 100644 --- a/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseConditionalExpression/UseConditionalExpressionForAssignmentTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.UseConditionalExpression; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -2487,4 +2488,40 @@ public void Convert(Type type, string body) } """); } + + [Fact] + public async Task TestOnNullConditionalAssignment1() + { + await TestInRegularAndScript1Async( + """ + class C + { + int i; + + void M(C c) + { + [|if|] (true) + { + c?.i = 0; + } + else + { + c?.i = 1; + } + } + } + """, + """ + class C + { + int i; + + void M(C c) + { + c?.i = true ? 0 : 1; + } + } + """, + languageVersion: LanguageVersionExtensions.CSharpNext); + } } diff --git a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs index cb0d5a578ce24..0b9c88a1085a7 100644 --- a/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs +++ b/src/Analyzers/CSharp/Tests/UseNullPropagation/UseNullPropagationTests.cs @@ -2,8 +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.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.UseNullPropagation; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; @@ -20,7 +23,11 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseNullPropagation; [Trait(Traits.Feature, Traits.Features.CodeActionsUseNullPropagation)] public partial class UseNullPropagationTests { - private static async Task TestInRegularAndScript1Async(string testCode, string fixedCode, OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary) + private static async Task TestInRegularAndScript1Async( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string fixedCode, + OutputKind outputKind = OutputKind.DynamicallyLinkedLibrary, + LanguageVersion languageVersion = LanguageVersion.CSharp9) { await new VerifyCS.Test { @@ -30,7 +37,7 @@ private static async Task TestInRegularAndScript1Async(string testCode, string f // by just rewriting `x.Y` into `x?.Y`. That is not correct. the RHS of the `?` should `.Y()` not // `.Y`. CodeActionValidationMode = CodeActionValidationMode.None, - LanguageVersion = LanguageVersion.CSharp9, + LanguageVersion = languageVersion, TestState = { OutputKind = outputKind, @@ -38,7 +45,9 @@ private static async Task TestInRegularAndScript1Async(string testCode, string f }.RunAsync(); } - private static async Task TestMissingInRegularAndScriptAsync(string testCode, LanguageVersion languageVersion = LanguageVersion.CSharp9) + private static async Task TestMissingInRegularAndScriptAsync( + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string testCode, + LanguageVersion languageVersion = LanguageVersion.CSharp9) { await new VerifyCS.Test { @@ -2508,4 +2517,58 @@ void M(byte? o) } """); } + + [Fact] + public async Task TestNullConditionalAssignment1() + { + await TestInRegularAndScript1Async( + """ + using System; + + class C + { + int x; + + void M(C c) + { + [|if|] (c != null) + c.x = 1; + } + } + """, + """ + using System; + + class C + { + int x; + + void M(C c) + { + c?.x = 1; + } + } + """, + languageVersion: LanguageVersionExtensions.CSharpNext); + } + + [Fact] + public async Task TestNullConditionalAssignment2() + { + await TestMissingInRegularAndScriptAsync( + """ + using System; + + class C + { + int x; + + void M(C c) + { + if (c != null) + c.x = 1; + } + } + """); + } } diff --git a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs index 250125e5fea12..97c9f4d03f53b 100644 --- a/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs +++ b/src/Analyzers/CSharp/Tests/UsePatternMatching/CSharpAsAndMemberAccessTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.UsePatternMatching; diff --git a/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests.cs b/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests.cs index c3fe4a3616fae..449b210f685b1 100644 --- a/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests.cs +++ b/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests.cs @@ -5,13 +5,11 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UseThrowExpression; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.UseThrowExpression; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; diff --git a/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests_FixAllTests.cs b/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests_FixAllTests.cs index 90265892987c6..d8bf92d5cbcb4 100644 --- a/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests_FixAllTests.cs +++ b/src/Analyzers/CSharp/Tests/UseThrowExpression/UseThrowExpressionTests_FixAllTests.cs @@ -5,7 +5,6 @@ #nullable disable using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; diff --git a/src/Analyzers/CSharp/Tests/UseUtf8StringLiteral/UseUtf8StringLiteralTests.cs b/src/Analyzers/CSharp/Tests/UseUtf8StringLiteral/UseUtf8StringLiteralTests.cs index 5a36f81c5cd43..1cb5ba3943b6e 100644 --- a/src/Analyzers/CSharp/Tests/UseUtf8StringLiteral/UseUtf8StringLiteralTests.cs +++ b/src/Analyzers/CSharp/Tests/UseUtf8StringLiteral/UseUtf8StringLiteralTests.cs @@ -2,10 +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.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.CSharp.UseUtf8StringLiteral; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer.cs index 97702dfc3f137..9ff82c4829633 100644 --- a/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AbstractBuiltInCodeStyleDiagnosticAnalyzer.cs @@ -7,7 +7,6 @@ using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs index cf388d119420c..a04161d32b1d7 100644 --- a/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AbstractCodeQualityDiagnosticAnalyzer.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.CodeQuality; diff --git a/src/Analyzers/Core/Analyzers/AddRequiredParentheses/AbstractAddRequiredParenthesesDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/AddRequiredParentheses/AbstractAddRequiredParenthesesDiagnosticAnalyzer.cs index 7fd9f9a64c66e..75fe79115341b 100644 --- a/src/Analyzers/Core/Analyzers/AddRequiredParentheses/AbstractAddRequiredParenthesesDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/AddRequiredParentheses/AbstractAddRequiredParenthesesDiagnosticAnalyzer.cs @@ -4,13 +4,10 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Precedence; using Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddRequiredParentheses; diff --git a/src/Analyzers/Core/Analyzers/DiagnosticCustomTags.cs b/src/Analyzers/Core/Analyzers/DiagnosticCustomTags.cs index 05c5275fa1f7f..edf3612290edf 100644 --- a/src/Analyzers/Core/Analyzers/DiagnosticCustomTags.cs +++ b/src/Analyzers/Core/Analyzers/DiagnosticCustomTags.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.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs index 6dc7526db970f..45b60093d331d 100644 --- a/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs +++ b/src/Analyzers/Core/Analyzers/EnforceOnBuildValues.cs @@ -140,6 +140,7 @@ internal static class EnforceOnBuildValues public const EnforceOnBuild ConvertAnonymousTypeToTuple = /*IDE0050*/ EnforceOnBuild.Never; public const EnforceOnBuild RemoveUnreachableCode = /*IDE0035*/ EnforceOnBuild.Never; // Non-configurable fading diagnostic corresponding to CS0162. public const EnforceOnBuild RemoveUnnecessarySuppression = /*IDE0079*/ EnforceOnBuild.Never; // IDE-only analyzer. + public const EnforceOnBuild CopilotImplementNotImplementedException = /*IDE3000*/ EnforceOnBuild.Never; // IDE-only analyzer. // Pure IDE feature for lighting up editor features. Do not enforce on build. public const EnforceOnBuild DetectProbableJsonStrings = /*JSON002*/ EnforceOnBuild.Never; diff --git a/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs b/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs index 6429b6c763d3f..b04b43d75d3a6 100644 --- a/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs +++ b/src/Analyzers/Core/Analyzers/Helpers/DiagnosticHelper.cs @@ -11,7 +11,6 @@ using System.Runtime.Serialization.Json; using System.Text; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs index b657d4cd0f720..e3ae127f7728d 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIdToOptionMappingHelper.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; diff --git a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs index 66cbc65fec400..5d2a44f2f4121 100644 --- a/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs +++ b/src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs @@ -230,4 +230,7 @@ internal static class IDEDiagnosticIds public const string ConstructorInitializerPlacementDiagnosticId = "IDE2004"; public const string ConditionalExpressionPlacementDiagnosticId = "IDE2005"; public const string ArrowExpressionClausePlacementDiagnosticId = "IDE2006"; + + // 3000 range for copilot features. + public const string CopilotImplementNotImplementedExceptionDiagnosticId = "IDE3000"; } diff --git a/src/Analyzers/Core/Analyzers/NamingStyle/NamingStyleDiagnosticAnalyzerBase.cs b/src/Analyzers/Core/Analyzers/NamingStyle/NamingStyleDiagnosticAnalyzerBase.cs index 9f0a78a621d0a..2917357fb50a3 100644 --- a/src/Analyzers/Core/Analyzers/NamingStyle/NamingStyleDiagnosticAnalyzerBase.cs +++ b/src/Analyzers/Core/Analyzers/NamingStyle/NamingStyleDiagnosticAnalyzerBase.cs @@ -6,11 +6,9 @@ using System.Collections.Concurrent; using System.Collections.Immutable; using System.Linq; -using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.NamingStyles; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; diff --git a/src/Analyzers/Core/Analyzers/NewLines/ConsecutiveStatementPlacement/AbstractConsecutiveStatementPlacementDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/NewLines/ConsecutiveStatementPlacement/AbstractConsecutiveStatementPlacementDiagnosticAnalyzer.cs index ae9f026291946..d3bd4e24ba3e3 100644 --- a/src/Analyzers/Core/Analyzers/NewLines/ConsecutiveStatementPlacement/AbstractConsecutiveStatementPlacementDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/NewLines/ConsecutiveStatementPlacement/AbstractConsecutiveStatementPlacementDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Analyzers/Core/Analyzers/NewLines/MultipleBlankLines/AbstractMultipleBlankLinesDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/NewLines/MultipleBlankLines/AbstractMultipleBlankLinesDiagnosticAnalyzer.cs index 1c7006f6e007a..2dcadfc761b3c 100644 --- a/src/Analyzers/Core/Analyzers/NewLines/MultipleBlankLines/AbstractMultipleBlankLinesDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/NewLines/MultipleBlankLines/AbstractMultipleBlankLinesDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Analyzers/Core/Analyzers/ParenthesesDiagnosticAnalyzersHelper.cs b/src/Analyzers/Core/Analyzers/ParenthesesDiagnosticAnalyzersHelper.cs index 0e2a7ad3e5b88..bed0d607f92d0 100644 --- a/src/Analyzers/Core/Analyzers/ParenthesesDiagnosticAnalyzersHelper.cs +++ b/src/Analyzers/Core/Analyzers/ParenthesesDiagnosticAnalyzersHelper.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Precedence; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses; diff --git a/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs index 0e68fc6b49fdc..d83beb780833f 100644 --- a/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/PopulateSwitch/AbstractPopulateSwitchDiagnosticAnalyzer.cs @@ -2,10 +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.Generic; using System.Collections.Immutable; -using System.Diagnostics; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/Core/Analyzers/QualifyMemberAccess/AbstractQualifyMemberAccessDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/QualifyMemberAccess/AbstractQualifyMemberAccessDiagnosticAnalyzer.cs index 88d45c840dc76..931b836d09bcc 100644 --- a/src/Analyzers/Core/Analyzers/QualifyMemberAccess/AbstractQualifyMemberAccessDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/QualifyMemberAccess/AbstractQualifyMemberAccessDiagnosticAnalyzer.cs @@ -2,13 +2,10 @@ // 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.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.QualifyMemberAccess; diff --git a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs index 72af7310791af..7973a2f23d404 100644 --- a/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs +++ b/src/Analyzers/Core/Analyzers/RemoveUnnecessarySuppressions/SuppressMessageAttributeState.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Analyzers/Core/Analyzers/SimplifyBooleanExpression/AbstractSimplifyConditionalDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/SimplifyBooleanExpression/AbstractSimplifyConditionalDiagnosticAnalyzer.cs index 558c79e19bf31..ced7f82e05d10 100644 --- a/src/Analyzers/Core/Analyzers/SimplifyBooleanExpression/AbstractSimplifyConditionalDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/SimplifyBooleanExpression/AbstractSimplifyConditionalDiagnosticAnalyzer.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Threading; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Analyzers/Core/Analyzers/SimplifyLinqExpression/AbstractSimplifyLinqExpressionDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/SimplifyLinqExpression/AbstractSimplifyLinqExpressionDiagnosticAnalyzer.cs index 0ec7a513d5e1c..e57851f20ebba 100644 --- a/src/Analyzers/Core/Analyzers/SimplifyLinqExpression/AbstractSimplifyLinqExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/SimplifyLinqExpression/AbstractSimplifyLinqExpressionDiagnosticAnalyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/src/Analyzers/Core/Analyzers/UseAutoProperty/AnalysisResult.cs b/src/Analyzers/Core/Analyzers/UseAutoProperty/AnalysisResult.cs index e38aa38ee311e..98bd2523855dd 100644 --- a/src/Analyzers/Core/Analyzers/UseAutoProperty/AnalysisResult.cs +++ b/src/Analyzers/Core/Analyzers/UseAutoProperty/AnalysisResult.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.CodeStyle; namespace Microsoft.CodeAnalysis.UseAutoProperty; diff --git a/src/Analyzers/Core/Analyzers/UseAutoProperty/UseAutoPropertiesHelpers.cs b/src/Analyzers/Core/Analyzers/UseAutoProperty/UseAutoPropertiesHelpers.cs index 19679570faf23..d69fae6d30bfd 100644 --- a/src/Analyzers/Core/Analyzers/UseAutoProperty/UseAutoPropertiesHelpers.cs +++ b/src/Analyzers/Core/Analyzers/UseAutoProperty/UseAutoPropertiesHelpers.cs @@ -2,12 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Microsoft.CodeAnalysis.UseAutoProperty; internal static class UseAutoPropertiesHelpers diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs index 5dee730834d73..682ab67dc1160 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractObjectCreationExpressionAnalyzer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.UseCollectionExpression; namespace Microsoft.CodeAnalysis.UseCollectionInitializer; @@ -37,7 +36,8 @@ internal abstract class AbstractObjectCreationExpressionAnalyzer< { public readonly record struct AnalysisResult( ImmutableArray PreMatches, - ImmutableArray PostMatches); + ImmutableArray PostMatches, + bool ChangesSemantics); protected UpdateExpressionState State; @@ -48,7 +48,7 @@ public readonly record struct AnalysisResult( protected SemanticModel SemanticModel => this.State.SemanticModel; protected abstract bool ShouldAnalyze(CancellationToken cancellationToken); - protected abstract bool TryAddMatches(ArrayBuilder preMatches, ArrayBuilder postMatches, CancellationToken cancellationToken); + protected abstract bool TryAddMatches(ArrayBuilder preMatches, ArrayBuilder postMatches, out bool changesSemantics, CancellationToken cancellationToken); protected abstract bool IsInitializerOfLocalDeclarationStatement( TLocalDeclarationStatementSyntax localDeclarationStatement, TObjectCreationExpressionSyntax rootExpression, [NotNullWhen(true)] out TVariableDeclaratorSyntax? variableDeclarator); @@ -87,10 +87,10 @@ protected AnalysisResult AnalyzeWorker(CancellationToken cancellationToken) using var _1 = ArrayBuilder.GetInstance(out var preMatches); using var _2 = ArrayBuilder.GetInstance(out var postMatches); - if (!TryAddMatches(preMatches, postMatches, cancellationToken)) + if (!TryAddMatches(preMatches, postMatches, out var mayChangeSemantics, cancellationToken)) return default; - return new(preMatches.ToImmutableAndClear(), postMatches.ToImmutableAndClear()); + return new(preMatches.ToImmutableAndClear(), postMatches.ToImmutableAndClear(), mayChangeSemantics); } protected UpdateExpressionState? TryInitializeState( diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs index 1a8d7140e317b..e5093ced3782a 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerAnalyzer.cs @@ -50,7 +50,10 @@ internal abstract class AbstractUseCollectionInitializerAnalyzer< protected abstract bool IsComplexElementInitializer(SyntaxNode expression); protected abstract bool HasExistingInvalidInitializerForCollection(); protected abstract bool AnalyzeMatchesAndCollectionConstructorForCollectionExpression( - ArrayBuilder> preMatches, ArrayBuilder> postMatches, CancellationToken cancellationToken); + ArrayBuilder> preMatches, + ArrayBuilder> postMatches, + out bool mayChangeSemantics, + CancellationToken cancellationToken); protected abstract IUpdateExpressionSyntaxHelper SyntaxHelper { get; } @@ -66,7 +69,7 @@ public AnalysisResult Analyze( return default; this.Initialize(state.Value, objectCreationExpression, analyzeForCollectionExpression); - var (preMatches, postMatches) = this.AnalyzeWorker(cancellationToken); + var (preMatches, postMatches, mayChangeSemantics) = this.AnalyzeWorker(cancellationToken); // If analysis failed entirely, immediately bail out. if (preMatches.IsDefault || postMatches.IsDefault) @@ -81,15 +84,19 @@ public AnalysisResult Analyze( // other words, we don't want to suggest changing `new List()` to `new List() { }` as that's just // noise. So convert empty results to an invalid result here. if (analyzeForCollectionExpression) - return new(preMatches, postMatches); + return new(preMatches, postMatches, mayChangeSemantics); // Downgrade an empty result to a failure for the normal collection-initializer case. - return postMatches.IsEmpty ? default : new(preMatches, postMatches); + return postMatches.IsEmpty ? default : new(preMatches, postMatches, mayChangeSemantics); } protected sealed override bool TryAddMatches( - ArrayBuilder> preMatches, ArrayBuilder> postMatches, CancellationToken cancellationToken) + ArrayBuilder> preMatches, + ArrayBuilder> postMatches, + out bool mayChangeSemantics, + CancellationToken cancellationToken) { + mayChangeSemantics = false; var seenInvocation = false; var seenIndexAssignment = false; @@ -127,7 +134,10 @@ protected sealed override bool TryAddMatches( } if (_analyzeForCollectionExpression) - return AnalyzeMatchesAndCollectionConstructorForCollectionExpression(preMatches, postMatches, cancellationToken); + { + return AnalyzeMatchesAndCollectionConstructorForCollectionExpression( + preMatches, postMatches, out mayChangeSemantics, cancellationToken); + } return true; } diff --git a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs index 671b11f76ec94..d4410ebb3eaee 100644 --- a/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseCollectionInitializer/AbstractUseCollectionInitializerDiagnosticAnalyzer.cs @@ -68,10 +68,7 @@ public override DiagnosticAnalyzerCategory GetAnalyzerCategory() isUnnecessary: true); protected AbstractUseCollectionInitializerDiagnosticAnalyzer() - : base( - [ - (s_descriptor, CodeStyleOptions2.PreferCollectionInitializer) - ]) + : base([(s_descriptor, CodeStyleOptions2.PreferCollectionInitializer)]) { } @@ -117,9 +114,9 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) // as a non-local diagnostic and would not participate in lightbulb for computing code fixes. var expressionType = context.Compilation.ExpressionOfTType(); context.RegisterCodeBlockStartAction(blockStartContext => - blockStartContext.RegisterSyntaxNodeAction( - nodeContext => AnalyzeNode(nodeContext, ienumerableType, expressionType), - matchKindsArray)); + blockStartContext.RegisterSyntaxNodeAction( + nodeContext => AnalyzeNode(nodeContext, ienumerableType, expressionType), + matchKindsArray)); } private void AnalyzeNode( @@ -206,13 +203,13 @@ private void AnalyzeNode( if (!preferInitializerOption.Value) return null; - var (_, matches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: false, cancellationToken); + var (_, matches, changesSemantics) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: false, cancellationToken); // If analysis failed, we can't change this, no matter what. if (matches.IsDefault) return null; - return (matches, shouldUseCollectionExpression: false, changesSemantics: false); + return (matches, shouldUseCollectionExpression: false, changesSemantics); } (ImmutableArray> matches, bool shouldUseCollectionExpression, bool changesSemantics)? GetCollectionExpressionMatches() @@ -224,7 +221,7 @@ private void AnalyzeNode( if (!this.AreCollectionExpressionsSupported(context.Compilation)) return null; - var (preMatches, postMatches) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); + var (preMatches, postMatches, changesSemantics1) = analyzer.Analyze(semanticModel, syntaxFacts, objectCreationExpression, analyzeForCollectionExpression: true, cancellationToken); // If analysis failed, we can't change this, no matter what. if (preMatches.IsDefault || postMatches.IsDefault) @@ -232,10 +229,10 @@ private void AnalyzeNode( // Check if it would actually be legal to use a collection expression here though. var allowSemanticsChange = preferExpressionOption.Value == CollectionExpressionPreference.WhenTypesLooselyMatch; - if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, preMatches, allowSemanticsChange, cancellationToken, out var changesSemantics)) + if (!CanUseCollectionExpression(semanticModel, objectCreationExpression, expressionType, preMatches, allowSemanticsChange, cancellationToken, out var changesSemantics2)) return null; - return (preMatches.Concat(postMatches), shouldUseCollectionExpression: true, changesSemantics); + return (preMatches.Concat(postMatches), shouldUseCollectionExpression: true, changesSemantics1 || changesSemantics2); } } diff --git a/src/Analyzers/Core/Analyzers/UseConditionalExpression/AbstractUseConditionalExpressionDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseConditionalExpression/AbstractUseConditionalExpressionDiagnosticAnalyzer.cs index 63c14bc0cf351..0c8a9804428c1 100644 --- a/src/Analyzers/Core/Analyzers/UseConditionalExpression/AbstractUseConditionalExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseConditionalExpression/AbstractUseConditionalExpressionDiagnosticAnalyzer.cs @@ -11,26 +11,21 @@ namespace Microsoft.CodeAnalysis.UseConditionalExpression; -internal abstract class AbstractUseConditionalExpressionDiagnosticAnalyzer - : AbstractBuiltInCodeStyleDiagnosticAnalyzer +internal abstract class AbstractUseConditionalExpressionDiagnosticAnalyzer( + string descriptorId, + EnforceOnBuild enforceOnBuild, + LocalizableResourceString message, + PerLanguageOption2> option) + : AbstractBuiltInCodeStyleDiagnosticAnalyzer(descriptorId, + enforceOnBuild, + option, + new LocalizableResourceString(nameof(AnalyzersResources.Convert_to_conditional_expression), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), + message) where TIfStatementSyntax : SyntaxNode { public sealed override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; - protected AbstractUseConditionalExpressionDiagnosticAnalyzer( - string descriptorId, - EnforceOnBuild enforceOnBuild, - LocalizableResourceString message, - PerLanguageOption2> option) - : base(descriptorId, - enforceOnBuild, - option, - new LocalizableResourceString(nameof(AnalyzersResources.Convert_to_conditional_expression), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)), - message) - { - } - protected abstract ISyntaxFacts GetSyntaxFacts(); protected abstract (bool matched, bool canSimplify) TryMatchPattern( IConditionalOperation ifOperation, ISymbol containingSymbol, CancellationToken cancellationToken); diff --git a/src/Analyzers/Core/Analyzers/UseConditionalExpression/ForAssignment/UseConditionalExpressionForAssignmentHelpers.cs b/src/Analyzers/Core/Analyzers/UseConditionalExpression/ForAssignment/UseConditionalExpressionForAssignmentHelpers.cs index ef89d6ada6424..cb2916d775d49 100644 --- a/src/Analyzers/Core/Analyzers/UseConditionalExpression/ForAssignment/UseConditionalExpressionForAssignmentHelpers.cs +++ b/src/Analyzers/Core/Analyzers/UseConditionalExpression/ForAssignment/UseConditionalExpressionForAssignmentHelpers.cs @@ -209,12 +209,19 @@ private static bool TryGetAssignmentOrThrow( // Both the WhenTrue and WhenFalse statements must be of the form: // target = value; - if (statement is IExpressionStatementOperation exprStatement && - exprStatement.Operation is ISimpleAssignmentOperation assignmentOp && - assignmentOp.Target != null) + if (statement is IExpressionStatementOperation exprStatement) { - assignment = assignmentOp; - return true; + if (exprStatement.Operation is ISimpleAssignmentOperation { Target: not null } assignmentOp1) + { + assignment = assignmentOp1; + return true; + } + + if (exprStatement.Operation is IConditionalAccessOperation { WhenNotNull: ISimpleAssignmentOperation assignmentOp2 }) + { + assignment = assignmentOp2; + return true; + } } return false; diff --git a/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs index 51e3b7c980898..4bec6bacab283 100644 --- a/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseNullPropagation/AbstractUseNullPropagationDiagnosticAnalyzer.cs @@ -397,6 +397,12 @@ private static TExpressionSyntax RemoveObjectCastIfAny( if (node is TElementAccessExpressionSyntax elementAccess) return (TExpressionSyntax?)syntaxFacts.GetExpressionOfElementAccessExpression(elementAccess); + if (syntaxFacts.SyntaxKinds.SimpleAssignmentExpression == node.RawKind && syntaxFacts.SupportsNullConditionalAssignment(node.SyntaxTree.Options)) + { + syntaxFacts.GetPartsOfAssignmentExpressionOrStatement(node, out var left, out _, out _); + return (TExpressionSyntax)left; + } + return null; } } diff --git a/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs index 1564a3a2d687b..6760960332f27 100644 --- a/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseObjectInitializer/UseNamedMemberInitializerAnalyzer.cs @@ -69,8 +69,10 @@ protected sealed override bool ShouldAnalyze(CancellationToken cancellationToken protected sealed override bool TryAddMatches( ArrayBuilder> preMatches, ArrayBuilder> postMatches, + out bool changesSemantics, CancellationToken cancellationToken) { + changesSemantics = false; using var _1 = PooledHashSet.GetInstance(out var seenNames); var initializer = this.SyntaxFacts.GetInitializerOfBaseObjectCreationExpression(_objectCreationExpression); diff --git a/src/Analyzers/Core/Analyzers/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs index 2088447ffec98..7f5cdcd6cbfab 100644 --- a/src/Analyzers/Core/Analyzers/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseThrowExpression/AbstractUseThrowExpressionDiagnosticAnalyzer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseThrowExpression; diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs index feccbc95b2600..3253d23929b57 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AbstractAddAccessibilityModifiersCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs index e8bb74983cb44..1c9e20fbac9cb 100644 --- a/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs +++ b/src/Analyzers/Core/CodeFixes/AddAccessibilityModifiers/AddAccessibilityModifiersHelpers.cs @@ -2,10 +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.Threading; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddOrRemoveAccessibilityModifiers; diff --git a/src/Analyzers/Core/CodeFixes/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs index 29762c5ad3f12..db1c67659a107 100644 --- a/src/Analyzers/Core/CodeFixes/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddAnonymousTypeMemberName/AbstractAddAnonymousTypeMemberNameCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/Analyzers/Core/CodeFixes/AddObsoleteAttribute/AbstractAddObsoleteAttributeCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AddObsoleteAttribute/AbstractAddObsoleteAttributeCodeFixProvider.cs index 4c59eb92ce7ed..61a78a6f5ae33 100644 --- a/src/Analyzers/Core/CodeFixes/AddObsoleteAttribute/AbstractAddObsoleteAttributeCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AddObsoleteAttribute/AbstractAddObsoleteAttributeCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Analyzers/Core/CodeFixes/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs index 841a9a77dc4c9..a3f98de9b4f4d 100644 --- a/src/Analyzers/Core/CodeFixes/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/AliasAmbiguousType/AbstractAliasAmbiguousTypeCodeFixProvider.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractAddDocCommentNodesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractAddDocCommentNodesCodeFixProvider.cs index 41df8ea0b326a..39d5cba615d4a 100644 --- a/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractAddDocCommentNodesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractAddDocCommentNodesCodeFixProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractRemoveDocCommentNodeCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractRemoveDocCommentNodeCodeFixProvider.cs index ba25d7971bd2c..e992a7e6fc30f 100644 --- a/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractRemoveDocCommentNodeCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/DocumentationComments/AbstractRemoveDocCommentNodeCodeFixProvider.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Analyzers/Core/CodeFixes/FileHeaders/AbstractFileHeaderCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/FileHeaders/AbstractFileHeaderCodeFixProvider.cs index 40061d4fb71dd..07605e3783cdf 100644 --- a/src/Analyzers/Core/CodeFixes/FileHeaders/AbstractFileHeaderCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/FileHeaders/AbstractFileHeaderCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; diff --git a/src/Analyzers/Core/CodeFixes/ForEachCast/AbstractForEachCastCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/ForEachCast/AbstractForEachCastCodeFixProvider.cs index 87cff8def721e..cfd86259a4a42 100644 --- a/src/Analyzers/Core/CodeFixes/ForEachCast/AbstractForEachCastCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/ForEachCast/AbstractForEachCastCodeFixProvider.cs @@ -2,12 +2,10 @@ // 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.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs index 6a37a3b9d4ab7..e59af87ddedd5 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.State.cs @@ -15,10 +15,8 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; #if CODE_STYLE diff --git a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.cs b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.cs index c43f64ffbb3d0..cf267839ece5f 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateConstructor/AbstractGenerateConstructorService.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor; diff --git a/src/Analyzers/Core/CodeFixes/GenerateConstructor/IGenerateConstructorService.cs b/src/Analyzers/Core/CodeFixes/GenerateConstructor/IGenerateConstructorService.cs index e44d9b87d1ed2..6606e909d748b 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateConstructor/IGenerateConstructorService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateConstructor/IGenerateConstructorService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor; diff --git a/src/Analyzers/Core/CodeFixes/GenerateDefaultConstructors/GenerateDefaultConstructorsCodeAction.cs b/src/Analyzers/Core/CodeFixes/GenerateDefaultConstructors/GenerateDefaultConstructorsCodeAction.cs index 6aed37df43145..67ef2bbaac580 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateDefaultConstructors/GenerateDefaultConstructorsCodeAction.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateDefaultConstructors/GenerateDefaultConstructorsCodeAction.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; #if CODE_STYLE using DeclarationModifiers = Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers; diff --git a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs index c7912852061bb..7bc5f356d097d 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateEnumMember/AbstractGenerateEnumMemberService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberCodeFixProvider.cs index 81e779dad1590..c254ca042d3ac 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberService.cs b/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberService.cs index c804217770776..066b43781528e 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateMember/AbstractGenerateMemberService.cs @@ -64,8 +64,13 @@ protected static bool TryDetermineTypeToGenerateIn( TryDetermineTypeToGenerateInWorker( document, containingType, simpleNameOrMemberAccessExpression, cancellationToken, out typeToGenerateIn, out isStatic, out isColorColorCase); - typeToGenerateIn = typeToGenerateIn?.OriginalDefinition; + if (typeToGenerateIn.IsNullable(out var underlyingType) && + underlyingType is INamedTypeSymbol underlyingNamedType) + { + typeToGenerateIn = underlyingNamedType; + } + typeToGenerateIn = typeToGenerateIn?.OriginalDefinition; return typeToGenerateIn != null; } @@ -96,11 +101,8 @@ private static void TryDetermineTypeToGenerateInWorker( DetermineTypeToGenerateInWorker( semanticModel, beforeDotExpression, out typeToGenerateIn, out isStatic, out isColorColorCase, cancellationToken); } - - return; } - - if (syntaxFacts.IsConditionalAccessExpression(expression)) + else if (syntaxFacts.IsConditionalAccessExpression(expression)) { var beforeDotExpression = syntaxFacts.GetExpressionOfConditionalAccessExpression(expression); @@ -108,17 +110,9 @@ private static void TryDetermineTypeToGenerateInWorker( { DetermineTypeToGenerateInWorker( semanticModel, beforeDotExpression, out typeToGenerateIn, out isStatic, out isColorColorCase, cancellationToken); - if (typeToGenerateIn.IsNullable(out var underlyingType) && - underlyingType is INamedTypeSymbol underlyingNamedType) - { - typeToGenerateIn = underlyingNamedType; - } } - - return; } - - if (syntaxFacts.IsPointerMemberAccessExpression(expression)) + else if (syntaxFacts.IsPointerMemberAccessExpression(expression)) { var beforeArrowExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(expression); if (beforeArrowExpression != null) @@ -128,14 +122,10 @@ private static void TryDetermineTypeToGenerateInWorker( if (typeInfo.Type is IPointerTypeSymbol pointerType) { typeToGenerateIn = pointerType.PointedAtType as INamedTypeSymbol; - isStatic = false; } } - - return; } - - if (syntaxFacts.IsAttributeNamedArgumentIdentifier(expression)) + else if (syntaxFacts.IsAttributeNamedArgumentIdentifier(expression)) { var attributeNode = expression.GetAncestors().FirstOrDefault(syntaxFacts.IsAttribute); Contract.ThrowIfNull(attributeNode); @@ -144,16 +134,11 @@ private static void TryDetermineTypeToGenerateInWorker( var attributeType = semanticModel.GetTypeInfo(attributeName, cancellationToken); typeToGenerateIn = attributeType.Type as INamedTypeSymbol; - isStatic = false; - return; } - - if (syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier( + else if (syntaxFacts.IsMemberInitializerNamedAssignmentIdentifier( expression, out var initializedObject)) { typeToGenerateIn = semanticModel.GetTypeInfo(initializedObject, cancellationToken).Type as INamedTypeSymbol; - isStatic = false; - return; } else if (syntaxFacts.IsNameOfSubpattern(expression)) { @@ -164,15 +149,23 @@ private static void TryDetermineTypeToGenerateInWorker( // something like: { [|X|]: int i } or like: Blah { [|X|]: int i } var inferenceService = semanticDocument.Document.GetRequiredLanguageService(); typeToGenerateIn = inferenceService.InferType(semanticModel, propertyPatternClause, objectAsDefault: true, cancellationToken) as INamedTypeSymbol; - - isStatic = false; - return; } } + else if (syntaxFacts.IsMemberBindingExpression(expression)) + { + var target = syntaxFacts.GetTargetOfMemberBinding(expression); - // Generating into the containing type. - typeToGenerateIn = containingType; - isStatic = syntaxFacts.IsInStaticContext(expression); + if (target != null) + { + typeToGenerateIn = semanticModel.GetTypeInfo(target, cancellationToken).Type as INamedTypeSymbol; + } + } + else + { + // Generating into the containing type. + typeToGenerateIn = containingType; + isStatic = syntaxFacts.IsInStaticContext(expression); + } } private static void DetermineTypeToGenerateInWorker( diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateConversionService.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateConversionService.cs index 596ae892b8e81..6b7f6706f78ab 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateConversionService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateConversionService.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateDeconstructMethodService.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateDeconstructMethodService.cs index e5733af1c5217..fd41ad0aeed5d 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateDeconstructMethodService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateDeconstructMethodService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.State.cs index 80ce3f8f46692..161124c7c707b 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.State.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.cs index 466eff0317b09..ee4269d346b55 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateMethodService.cs @@ -2,13 +2,10 @@ // 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.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs index 163d6abfdb221..98c664fbbe944 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.AbstractInvocationInfo.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.CodeAction.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.CodeAction.cs index c97ab60e9897b..814681b4c53f1 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.CodeAction.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.CodeAction.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.MethodSignatureInfo.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.MethodSignatureInfo.cs index 8c1bcbe895d3e..8448595576acf 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.MethodSignatureInfo.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.MethodSignatureInfo.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.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.CodeGeneration; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs index 73576ea358ae1..2c87d938875cc 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/AbstractGenerateParameterizedMemberService.SignatureInfo.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; #if CODE_STYLE diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateDeconstructMemberService.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateDeconstructMemberService.cs index 7d4bb7a675e5f..70b78b797c788 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateDeconstructMemberService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateDeconstructMemberService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateParameterizedMemberService.cs b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateParameterizedMemberService.cs index 82dac9dd59a98..7a545f5da627e 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateParameterizedMemberService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateParameterizedMember/IGenerateParameterizedMemberService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember; diff --git a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs index 5bc4fc2132c35..4bef0b1c90b76 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateVariable/AbstractGenerateVariableService.State.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.FindSymbols; @@ -462,7 +461,9 @@ private bool TryDetermineFieldType(CancellationToken cancellationToken) // Substitute 'object' for all captured method type parameters. Note: we may need to // do this for things like anonymous types, as well as captured type parameters that // aren't in scope in the destination type. - var capturedMethodTypeParameters = inferredType.GetReferencedMethodTypeParameters(); + using var _1 = ArrayBuilder.GetInstance(out var capturedMethodTypeParameters); + inferredType.AddReferencedMethodTypeParameters(capturedMethodTypeParameters); + var mapping = capturedMethodTypeParameters.ToDictionary(tp => tp, tp => compilation.ObjectType); @@ -474,7 +475,7 @@ private bool TryDetermineFieldType(CancellationToken cancellationToken) var enclosingMethodSymbol = _document.SemanticModel.GetEnclosingSymbol(SimpleNameOrMemberAccessExpressionOpt.SpanStart, cancellationToken); if (enclosingMethodSymbol != null && enclosingMethodSymbol.TypeParameters != null && enclosingMethodSymbol.TypeParameters.Length != 0) { - using var _ = ArrayBuilder.GetInstance(out var combinedTypeParameters); + using var _2 = ArrayBuilder.GetInstance(out var combinedTypeParameters); combinedTypeParameters.AddRange(availableTypeParameters); combinedTypeParameters.AddRange(enclosingMethodSymbol.TypeParameters); LocalType = inferredType.RemoveUnavailableTypeParameters(compilation, combinedTypeParameters); diff --git a/src/Analyzers/Core/CodeFixes/GenerateVariable/IGenerateVariableService.cs b/src/Analyzers/Core/CodeFixes/GenerateVariable/IGenerateVariableService.cs index 742f3d0d3e369..fc649281aed02 100644 --- a/src/Analyzers/Core/CodeFixes/GenerateVariable/IGenerateVariableService.cs +++ b/src/Analyzers/Core/CodeFixes/GenerateVariable/IGenerateVariableService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Analyzers/Core/CodeFixes/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs index a512895b3c5b2..9687d3a3f6c10 100644 --- a/src/Analyzers/Core/CodeFixes/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/ImplementAbstractClass/AbstractImplementAbstractClassCodeFixProvider.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.ImplementAbstractClass; diff --git a/src/Analyzers/Core/CodeFixes/ImplementInterface/ImplementInterfaceGenerator_Property.cs b/src/Analyzers/Core/CodeFixes/ImplementInterface/ImplementInterfaceGenerator_Property.cs index 7707dc33fb3eb..9390f123877c1 100644 --- a/src/Analyzers/Core/CodeFixes/ImplementInterface/ImplementInterfaceGenerator_Property.cs +++ b/src/Analyzers/Core/CodeFixes/ImplementInterface/ImplementInterfaceGenerator_Property.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CodeGeneration; diff --git a/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs index 5db2af601d9f0..092139a4ba4a8 100644 --- a/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeFieldReadonly/AbstractMakeFieldReadonlyCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs index 1ee2dd22b664e..4dfca14b6c208 100644 --- a/src/Analyzers/Core/CodeFixes/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeMemberStatic/AbstractMakeMemberStaticCodeFixProvider.cs @@ -2,12 +2,10 @@ // 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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/Core/CodeFixes/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index 73af02c92ae82..8560a63ed58d3 100644 --- a/src/Analyzers/Core/CodeFixes/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Analyzers/Core/CodeFixes/MakeTypeAbstract/AbstractMakeTypeAbstractCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeTypeAbstract/AbstractMakeTypeAbstractCodeFixProvider.cs index 82d39e92f2fcd..024c04392cf5e 100644 --- a/src/Analyzers/Core/CodeFixes/MakeTypeAbstract/AbstractMakeTypeAbstractCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeTypeAbstract/AbstractMakeTypeAbstractCodeFixProvider.cs @@ -2,12 +2,10 @@ // 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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/Core/CodeFixes/MakeTypePartial/AbstractMakeTypePartialCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/MakeTypePartial/AbstractMakeTypePartialCodeFixProvider.cs index 37f501bdb90d5..4de179bac6305 100644 --- a/src/Analyzers/Core/CodeFixes/MakeTypePartial/AbstractMakeTypePartialCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/MakeTypePartial/AbstractMakeTypePartialCodeFixProvider.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/Core/CodeFixes/Naming/FallbackNamingRules.cs b/src/Analyzers/Core/CodeFixes/Naming/FallbackNamingRules.cs index 2e3518127d5e3..0ce937d7ba07d 100644 --- a/src/Analyzers/Core/CodeFixes/Naming/FallbackNamingRules.cs +++ b/src/Analyzers/Core/CodeFixes/Naming/FallbackNamingRules.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; diff --git a/src/Analyzers/Core/CodeFixes/Naming/NamingExtensions.cs b/src/Analyzers/Core/CodeFixes/Naming/NamingExtensions.cs index f9365fd6e1c45..01b3ab7184f7a 100644 --- a/src/Analyzers/Core/CodeFixes/Naming/NamingExtensions.cs +++ b/src/Analyzers/Core/CodeFixes/Naming/NamingExtensions.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Shared.Naming; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Analyzers/Core/CodeFixes/OrderModifiers/AbstractOrderModifiersCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/OrderModifiers/AbstractOrderModifiersCodeFixProvider.cs index 5715e8a08182c..b061646a01f88 100644 --- a/src/Analyzers/Core/CodeFixes/OrderModifiers/AbstractOrderModifiersCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/OrderModifiers/AbstractOrderModifiersCodeFixProvider.cs @@ -2,17 +2,14 @@ // 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.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchCodeFixProvider.cs index 145908f6c307d..5f7d4c204b58e 100644 --- a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs index 5b452e4e68a46..27f5502d3ecc8 100644 --- a/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/PopulateSwitch/AbstractPopulateSwitchExpressionCodeFixProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.PopulateSwitch; diff --git a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs index b25199c960ec8..13066ab750497 100644 --- a/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs +++ b/src/Analyzers/Core/CodeFixes/PredefinedCodeFixProviderNames.cs @@ -43,6 +43,7 @@ internal static class PredefinedCodeFixProviderNames public const string ConvertToRecord = nameof(ConvertToRecord); public const string ConvertToTopLevelStatements = nameof(ConvertToTopLevelStatements); public const string ConvertTypeOfToNameOf = nameof(ConvertTypeOfToNameOf); + public const string CopilotImplementNotImplementedException = nameof(CopilotImplementNotImplementedException); public const string CopilotSuggestions = nameof(CopilotSuggestions); public const string CorrectNextControlVariable = nameof(CorrectNextControlVariable); public const string DeclareAsNullable = nameof(DeclareAsNullable); diff --git a/src/Analyzers/Core/CodeFixes/QualifyMemberAccess/AbstractQualifyMemberAccessCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/QualifyMemberAccess/AbstractQualifyMemberAccessCodeFixProvider.cs index 4b0cb0f386fa3..1c7d70e0c4d0e 100644 --- a/src/Analyzers/Core/CodeFixes/QualifyMemberAccess/AbstractQualifyMemberAccessCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/QualifyMemberAccess/AbstractQualifyMemberAccessCodeFixProvider.cs @@ -2,11 +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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/RemoveRedundantEquality/RemoveRedundantEqualityCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveRedundantEquality/RemoveRedundantEqualityCodeFixProvider.cs index 56ec6abe94604..4a31e92a587f8 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveRedundantEquality/RemoveRedundantEqualityCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveRedundantEquality/RemoveRedundantEqualityCodeFixProvider.cs @@ -2,14 +2,12 @@ // 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.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryParentheses/AbstractRemoveUnnecessaryParenthesesCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryParentheses/AbstractRemoveUnnecessaryParenthesesCodeFixProvider.cs index c21ae53bb9a8c..41407b1e686b0 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryParentheses/AbstractRemoveUnnecessaryParenthesesCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessaryParentheses/AbstractRemoveUnnecessaryParenthesesCodeFixProvider.cs @@ -2,11 +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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryAttributeSuppressionsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryAttributeSuppressionsCodeFixProvider.cs index b5e2c50e00f1b..df44ed978de39 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryAttributeSuppressionsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryAttributeSuppressionsCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryPragmaSuppressionsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryPragmaSuppressionsCodeFixProvider.cs index 8d6418a13237f..fcb86a7ac9f86 100644 --- a/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryPragmaSuppressionsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/RemoveUnnecessarySuppressions/RemoveUnnecessaryPragmaSuppressionsCodeFixProvider.cs @@ -2,14 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/UpdateLegacySuppressions/UpdateLegacySuppressionsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UpdateLegacySuppressions/UpdateLegacySuppressionsCodeFixProvider.cs index 6733f37c256e8..1770445ee3502 100644 --- a/src/Analyzers/Core/CodeFixes/UpdateLegacySuppressions/UpdateLegacySuppressionsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UpdateLegacySuppressions/UpdateLegacySuppressionsCodeFixProvider.cs @@ -7,7 +7,6 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs index 6bcdd1ce88458..6c11e4cfffccb 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/AbstractUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -11,7 +10,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseCoalesceExpression; diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs index 208cdecb01d30..7a994f7fe81d2 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; diff --git a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider.cs index adba206a1f33d..2334bc175a664 100644 --- a/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCoalesceExpression/UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; diff --git a/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs index d1c1ed93b6077..3894a87ce428f 100644 --- a/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs @@ -76,7 +76,7 @@ protected sealed override async Task FixAsync( using var analyzer = GetAnalyzer(); var useCollectionExpression = properties.ContainsKey(UseCollectionInitializerHelpers.UseCollectionExpressionName) is true; - var (preMatches, postMatches) = analyzer.Analyze( + var (preMatches, postMatches, _) = analyzer.Analyze( semanticModel, syntaxFacts, objectCreation, useCollectionExpression, cancellationToken); if (preMatches.IsDefault || postMatches.IsDefault) diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index 20ec1d9dfe293..a5ae0ef3a13e9 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -2,11 +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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs index 2622d19c5ccf7..2b3ed157e4237 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForAssignment/AbstractUseConditionalExpressionForAssignmentCodeFixProvider.cs @@ -104,19 +104,25 @@ protected override async Task FixOneAsync( private void ConvertOnlyIfToConditionalExpression( SyntaxEditor editor, IConditionalOperation ifOperation, - ISimpleAssignmentOperation assignment, + ISimpleAssignmentOperation assignmentOperation, TExpressionSyntax conditionalExpression) { var generator = editor.Generator; var ifStatement = (TIfStatementSyntax)ifOperation.Syntax; - var expressionStatement = (TStatementSyntax)generator.ExpressionStatement( - generator.AssignmentStatement( - assignment.Target.Syntax, - conditionalExpression)).WithTriviaFrom(ifStatement); + var assignment = generator.AssignmentStatement(assignmentOperation.Target.Syntax, conditionalExpression); + + if (assignmentOperation.Parent is IConditionalAccessOperation conditionalAccess) + { + assignment = generator.ConditionalAccessExpression( + conditionalAccess.Operation.Syntax, + assignment); + } + + var expressionStatement = (TStatementSyntax)generator.ExpressionStatement(assignment); editor.ReplaceNode( ifOperation.Syntax, - WrapWithBlockIfAppropriate(ifStatement, expressionStatement)); + WrapWithBlockIfAppropriate(ifStatement, expressionStatement).WithTriviaFrom(ifStatement)); } private bool TryConvertWhenAssignmentToLocalDeclaredImmediateAbove( diff --git a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForReturn/AbstractUseConditionalExpressionForReturnCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForReturn/AbstractUseConditionalExpressionForReturnCodeFixProvider.cs index 525e9164b2126..61ab3eec1b0cd 100644 --- a/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForReturn/AbstractUseConditionalExpressionForReturnCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseConditionalExpression/ForReturn/AbstractUseConditionalExpressionForReturnCodeFixProvider.cs @@ -2,12 +2,10 @@ // 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.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/UseExplicitTupleName/UseExplicitTupleNameCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseExplicitTupleName/UseExplicitTupleNameCodeFixProvider.cs index 3e93da6cf6d98..6d57a354baa1b 100644 --- a/src/Analyzers/Core/CodeFixes/UseExplicitTupleName/UseExplicitTupleNameCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseExplicitTupleName/UseExplicitTupleNameCodeFixProvider.cs @@ -7,13 +7,11 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseExplicitTupleName; diff --git a/src/Analyzers/Core/CodeFixes/UseInferredMemberName/AbstractUseInferredMemberNameCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseInferredMemberName/AbstractUseInferredMemberNameCodeFixProvider.cs index c6cd72ba08ebd..8d95c70a6f730 100644 --- a/src/Analyzers/Core/CodeFixes/UseInferredMemberName/AbstractUseInferredMemberNameCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseInferredMemberName/AbstractUseInferredMemberNameCodeFixProvider.cs @@ -2,16 +2,12 @@ // 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.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseInferredMemberName; diff --git a/src/Analyzers/Core/CodeFixes/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs index 1c2bf9963d409..2919279a5a0ff 100644 --- a/src/Analyzers/Core/CodeFixes/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseIsNullCheck/AbstractUseIsNullForReferenceEqualsCodeFixProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs index 050540dbf5037..c1e6714f1e0ac 100644 --- a/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseNullPropagation/AbstractUseNullPropagationCodeFixProvider.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs index baec58480a98e..43b6681743bbb 100644 --- a/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UseObjectInitializer; diff --git a/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs index ff27c1ab4a616..38340365c24dd 100644 --- a/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseSystemHashCode/UseSystemHashCodeCodeFixProvider.cs @@ -8,7 +8,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; diff --git a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicCollectionInitializerAnalyzer.vb b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicCollectionInitializerAnalyzer.vb index f93006673927f..6d7b23598ee9f 100644 --- a/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicCollectionInitializerAnalyzer.vb +++ b/src/Analyzers/VisualBasic/Analyzers/UseCollectionInitializer/VisualBasicCollectionInitializerAnalyzer.vb @@ -41,6 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer Protected Overrides Function AnalyzeMatchesAndCollectionConstructorForCollectionExpression( preMatches As ArrayBuilder(Of CollectionMatch(Of SyntaxNode)), postMatches As ArrayBuilder(Of CollectionMatch(Of SyntaxNode)), + ByRef changesSemantics As Boolean, cancellationToken As CancellationToken) As Boolean ' Only called for collection expressions, which VB does not support Throw ExceptionUtilities.Unreachable() diff --git a/src/Analyzers/VisualBasic/CodeFixes/GenerateParameterizedMember/VisualBasicGenerateParameterizedMemberService.vb b/src/Analyzers/VisualBasic/CodeFixes/GenerateParameterizedMember/VisualBasicGenerateParameterizedMemberService.vb index 2a69484ac46c2..7ac08d83fd613 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/GenerateParameterizedMember/VisualBasicGenerateParameterizedMemberService.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/GenerateParameterizedMember/VisualBasicGenerateParameterizedMemberService.vb @@ -62,15 +62,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateMethod End Function Protected Overrides Function GetCapturedTypeParameters(cancellationToken As CancellationToken) As ImmutableArray(Of ITypeParameterSymbol) - Dim result = New List(Of ITypeParameterSymbol)() + Dim result = ArrayBuilder(Of ITypeParameterSymbol).GetInstance() If Me.InvocationExpression.ArgumentList IsNot Nothing Then For Each argument In Me.InvocationExpression.ArgumentList.Arguments Dim type = DetermineParameterType(argument, cancellationToken) - type.GetReferencedTypeParameters(result) + type.AddReferencedTypeParameters(result) Next End If - Return result.ToImmutableArray() + Return result.ToImmutableAndFree() End Function Protected Overrides Function GenerateTypeParameters(cancellationToken As CancellationToken) As ImmutableArray(Of ITypeParameterSymbol) diff --git a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs index d267b9abf3878..b422629ed39d0 100644 --- a/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs +++ b/src/Compilers/CSharp/CSharpAnalyzerDriver/CSharpDeclarationComputer.cs @@ -102,6 +102,7 @@ private static void ComputeDeclarations( case SyntaxKind.StructDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for analyzers { if (associatedSymbol is IMethodSymbol ctor) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 45caa7bd6b90d..1bf76dd56b127 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -591,6 +591,7 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind var methodGroup = (BoundMethodGroup)expr; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None); + Debug.Assert(!resolution.IsNonMethodExtensionMember(out _)); diagnostics.Add(expr.Syntax, useSiteInfo); Symbol otherSymbol = null; bool resolvedToMethodGroup = resolution.MethodGroup != null; @@ -2447,7 +2448,7 @@ static void getArgList( /// /// Returns the set of arguments to be considered for escape analysis of a method - /// invocation. Each argument is returned with the correponding parameter and + /// invocation. Each argument is returned with the corresponding parameter and /// whether analysis should consider value or ref escape. Not all method arguments /// are included, and some arguments may be included twice - once for value, once for ref. /// @@ -4071,7 +4072,7 @@ internal SafeContext GetValEscape(BoundExpression expr, SafeContext localScopeDe case BoundKind.MakeRefOperator: case BoundKind.RefValueOperator: // for compat reasons - // NB: it also means can`t assign stackalloc spans to a __refvalue + // NB: it also means can't assign stackalloc spans to a __refvalue // we are ok with that. return SafeContext.CallingMethod; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index 3805868f85f4f..4411b2b1e45ea 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -893,7 +893,7 @@ internal BoundStatement WrapWithVariablesAndLocalFunctionsIfAny(CSharpSyntaxNode return statement; } - return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null, + return new BoundBlock(statement.Syntax, locals, ImmutableArray.CastUp(localFunctions), hasUnsafeModifier: false, instrumentation: null, ImmutableArray.Create(statement)) { WasCompilerGenerated = true }; } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 84dd51c1cd3b0..2e82ab6e0a8b5 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -598,25 +598,16 @@ bool checkSymbol(Symbol sym, TextSpan memberSpan, SymbolKind kind, out Symbol re return false; } - if (kind is SymbolKind.Method or SymbolKind.Property) + if (kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event) { if (InSpan(sym.GetFirstLocation(), this.syntaxTree, memberSpan)) { return true; } - // If this is a partial member, the member represents the defining part, - // not the implementation (member.Locations includes both parts). If the - // span is in fact in the implementation, return that member instead. - if (sym switch -#pragma warning disable format - { - MethodSymbol method => (Symbol)method.PartialImplementationPart, - SourcePropertySymbol property => property.PartialImplementationPart, - _ => throw ExceptionUtilities.UnexpectedValue(sym) - } -#pragma warning restore format - is { } implementation) + // If this is a partial member, the member represents the defining part, not the implementation. + // If the span is in fact in the implementation, return that member instead. + if (sym.GetPartialImplementationPart() is { } implementation) { if (InSpan(implementation.GetFirstLocation(), this.syntaxTree, memberSpan)) { @@ -790,6 +781,11 @@ internal Binder VisitTypeDeclarationCore(TypeDeclarationSyntax parent, NodeUsage { resultBinder = new WithClassTypeParametersBinder(typeSymbol, resultBinder); } + + if (typeSymbol.IsExtension) + { + resultBinder = new WithExtensionParameterBinder(typeSymbol, resultBinder); + } } } @@ -819,6 +815,9 @@ public override Binder VisitInterfaceDeclaration(InterfaceDeclarationSyntax node public override Binder VisitRecordDeclaration(RecordDeclarationSyntax node) => VisitTypeDeclarationCore(node); + public override Binder VisitExtensionDeclaration(ExtensionDeclarationSyntax node) + => VisitTypeDeclarationCore(node); + public sealed override Binder VisitNamespaceDeclaration(NamespaceDeclarationSyntax parent) { if (!LookupPosition.IsInNamespaceDeclaration(_position, parent)) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 10d853d3ea371..ce13976db5419 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -376,14 +376,14 @@ private bool GetIsCompletedProperty(TypeSymbol awaiterType, SyntaxNode node, Typ return false; } - if (qualified.Kind != BoundKind.PropertyAccess) + if (qualified is not BoundPropertyAccess { PropertySymbol: { } propertySymbol } || propertySymbol.GetIsNewExtensionMember()) { Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.IsCompleted); isCompletedProperty = null; return false; } - isCompletedProperty = ((BoundPropertyAccess)qualified).PropertySymbol; + isCompletedProperty = propertySymbol; if (isCompletedProperty.IsWriteOnly) { Error(diagnostics, ErrorCode.ERR_PropertyLacksGet, node, isCompletedProperty); @@ -454,7 +454,7 @@ private bool GetGetResultMethod(BoundExpression awaiterExpression, SyntaxNode no var call = (BoundCall)getAwaiterGetResultCall; getResultMethod = call.Method; - if (getResultMethod.IsExtensionMethod) + if (getResultMethod.IsExtensionMethod || getResultMethod.GetIsNewExtensionMember()) { Error(diagnostics, ErrorCode.ERR_NoSuchMember, node, awaiterType, WellKnownMemberNames.GetResult); getResultMethod = null; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index bcb7c815a2a07..ee80c9724963b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1301,7 +1301,7 @@ static bool makeInvocationExpression( node, node, receiver, WellKnownMemberNames.CollectionInitializerAddMethodName, rightArity: 0, typeArgumentsSyntax: default(SeparatedSyntaxList), typeArgumentsWithAnnotations: default(ImmutableArray), - invoked: true, indexed: false, diagnostics, searchExtensionMethodsIfNecessary: true); + invoked: true, indexed: false, diagnostics, searchExtensionsIfNecessary: true); // require the target member to be a method. if (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess) @@ -1361,13 +1361,21 @@ static bool bindMethodGroupInvocation( var resolution = addMethodBinder.ResolveMethodGroup( methodGroup, expression, WellKnownMemberNames.CollectionInitializerAddMethodName, analyzedArguments, useSiteInfo: ref useSiteInfo, - options: OverloadResolution.Options.DynamicResolution | OverloadResolution.Options.DynamicConvertsToAnything); + options: OverloadResolution.Options.DynamicResolution | OverloadResolution.Options.DynamicConvertsToAnything, + acceptOnlyMethods: true); diagnostics.Add(expression, useSiteInfo); if (!methodGroup.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading. - if (resolution.HasAnyErrors) + if (resolution.IsNonMethodExtensionMember(out Symbol? extensionMember)) + { + Debug.Assert(false); // Should not get here given the 'acceptOnlyMethods' argument value used in 'ResolveMethodGroup' call above + ReportMakeInvocationExpressionBadMemberKind(syntax, WellKnownMemberNames.CollectionInitializerAddMethodName, methodGroup, diagnostics); + addMethods = []; + result = false; + } + else if (resolution.HasAnyErrors) { addMethods = []; result = false; @@ -1399,7 +1407,7 @@ static bool bindMethodGroupInvocation( var finalApplicableCandidates = addMethodBinder.GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult, methodGroup.ReceiverOpt, methodGroup.TypeArgumentsOpt, - invokedAsExtensionMethod: resolution.IsExtensionMethodGroup, + isExtensionMethodGroup: resolution.IsExtensionMethodGroup, diagnostics); Debug.Assert(finalApplicableCandidates.Length != 1 || finalApplicableCandidates[0].IsApplicable); @@ -1459,7 +1467,7 @@ static ImmutableArray filterOutBadGenericMethods( if (!typeParameters.IsEmpty) { - if (resolution.IsExtensionMethodGroup) + if (resolution.IsExtensionMethodGroup) // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we need to handle new extension methods { // We need to validate an ability to infer type arguments as well as check conversion to 'this' parameter. // Overload resolution doesn't check the conversion when 'this' type refers to a type parameter @@ -1510,7 +1518,7 @@ static ImmutableArray filterOutBadGenericMethods( parameterTypes, parameterRefKinds, ImmutableArray.Create(methodGroup.ReceiverOpt, new BoundValuePlaceholder(syntax, secondArgumentType) { WasCompilerGenerated = true }), - ref useSiteInfo); + ref useSiteInfo); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we may need to override ordinals here if (!inferenceResult.Success) { @@ -1593,6 +1601,10 @@ static bool bindInvocationExpressionContinued( var methodResult = result.ValidResult; var method = methodResult.Member; + // Tracked by https://github.com/dotnet/roslyn/issues/76130: It looks like we added a bunch of code in BindInvocationExpressionContinued at this position + // that specifically deals with new extension methods. It adjusts analyzedArguments, etc. + // It is very likely we need to do the same here. + // It is possible that overload resolution succeeded, but we have chosen an // instance method and we're in a static method. A careful reading of the // overload resolution spec shows that the "final validation" stage allows an @@ -1651,7 +1663,7 @@ internal static BoundExpression GetUnderlyingCollectionExpressionElement(BoundCo // Add methods. This case can be hit for spreads and non-spread elements. Debug.Assert(call.HasErrors); Debug.Assert(call.Method.Name == "Add"); - return call.Arguments[call.InvokedAsExtensionMethod ? 1 : 0]; + return call.Arguments[call.InvokedAsExtensionMethod ? 1 : 0]; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Add test coverage for new extensions case BoundBadExpression badExpression: Debug.Assert(false); // Add test if we hit this assert. return badExpression; @@ -1700,7 +1712,7 @@ internal bool TryGetCollectionIterationType(SyntaxNode syntax, TypeSymbol collec out iterationType, builder: out var builder); // Collection expression target types require instance method GetEnumerator. - if (result && builder.ViaExtensionMethod) + if (result && builder.ViaExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Add test coverage for new extensions { iterationType = default; return false; @@ -2223,7 +2235,7 @@ private BoundExpression CreateAnonymousFunctionConversion(SyntaxNode syntax, Bou diagnostics.AddRange(boundLambda.Diagnostics); CheckParameterModifierMismatchMethodConversion(syntax, boundLambda.Symbol, destination, invokedAsExtensionMethod: false, diagnostics); - CheckLambdaConversion(boundLambda.Symbol, destination, diagnostics); + CheckLambdaConversion((LambdaSymbol)boundLambda.Symbol, destination, diagnostics); return new BoundConversion( syntax, boundLambda, @@ -2826,20 +2838,14 @@ internal bool MethodIsCompatibleWithDelegateOrFunctionPointer(BoundExpression? r var methodParameters = method.Parameters; int numParams = delegateOrFuncPtrParameters.Length; - if (methodParameters.Length != numParams + (isExtensionMethod ? 1 : 0)) - { - // This can happen if "method" has optional parameters. - Debug.Assert(methodParameters.Length > numParams + (isExtensionMethod ? 1 : 0)); - Error(diagnostics, getMethodMismatchErrorCode(delegateType.TypeKind), errorLocation, method, delegateType); - return false; - } + Debug.Assert(methodParameters.Length == numParams + (isExtensionMethod ? 1 : 0)); CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); // If this is an extension method delegate, the caller should have verified the // receiver is compatible with the "this" parameter of the extension method. - Debug.Assert(!isExtensionMethod || - (Conversions.ConvertExtensionMethodThisArg(methodParameters[0].Type, receiverOpt!.Type, ref useSiteInfo, isMethodGroupConversion: true).Exists && useSiteInfo.Diagnostics.IsNullOrEmpty())); + Debug.Assert(!(isExtensionMethod || (method.GetIsNewExtensionMember() && !method.IsStatic)) || + (Conversions.ConvertExtensionMethodThisArg(GetReceiverParameter(method)!.Type, receiverOpt!.Type, ref useSiteInfo, isMethodGroupConversion: true).Exists && useSiteInfo.Diagnostics.IsNullOrEmpty())); useSiteInfo = new CompoundUseSiteInfo(useSiteInfo); @@ -2974,6 +2980,17 @@ static ErrorCode getRefMismatchErrorCode(TypeKind type) }; } + internal static ParameterSymbol? GetReceiverParameter(MethodSymbol method) + { + if (method.IsExtensionMethod) + { + return method.Parameters[0]; + } + + Debug.Assert(method.GetIsNewExtensionMember()); + return method.ContainingType.ExtensionParameter; + } + /// /// This method combines final validation (section 7.6.5.1) and delegate compatibility (section 15.2). /// diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs index b8ead0eef4f39..06a853b99f712 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs @@ -669,7 +669,8 @@ private BoundExpression MakeDeconstructInvocationExpression( // Those placeholders are also recorded in the outVar for easy access below, by the `SetInferredType` call on the outVar nodes. BoundExpression result = BindMethodGroupInvocation( rightSyntax, rightSyntax, methodName, (BoundMethodGroup)memberAccess, analyzedArguments, diagnostics, queryClause: null, - ignoreNormalFormIfHasValidParamsParameter: false, anyApplicableCandidates: out anyApplicableCandidates); + ignoreNormalFormIfHasValidParamsParameter: false, anyApplicableCandidates: out anyApplicableCandidates, + disallowExpandedNonArrayParams: false, acceptOnlyMethods: true); result.WasCompilerGenerated = true; @@ -682,7 +683,7 @@ private BoundExpression MakeDeconstructInvocationExpression( // This prevents, for example, an unused params parameter after the out parameters. var deconstructMethod = ((BoundCall)result).Method; var parameters = deconstructMethod.Parameters; - for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++) + for (int i = (deconstructMethod.IsExtensionMethod ? 1 : 0); i < parameters.Length; i++) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { if (parameters[i].RefKind != RefKind.Out) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index f4f3d1b54026a..feedd901d17fc 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -48,6 +48,11 @@ internal bool HasThis(bool isExplicit, out bool inStaticContext) return false; } + if (memberOpt?.GetIsNewExtensionMember() == true) + { + return false; + } + var containingType = memberOpt?.ContainingType; bool inTopLevelScriptMember = (object)containingType != null && containingType.IsScriptClass; @@ -497,7 +502,9 @@ internal BoundParameterEqualsValue BindParameterDefaultValue( out BoundExpression valueBeforeConversion) { Debug.Assert(this.InParameterDefaultValue); - Debug.Assert(this.ContainingMemberOrLambda.Kind == SymbolKind.Method || this.ContainingMemberOrLambda.Kind == SymbolKind.Property); + Debug.Assert(this.ContainingMemberOrLambda.Kind == SymbolKind.Method + || this.ContainingMemberOrLambda.Kind == SymbolKind.Property + || this.ContainingMemberOrLambda is TypeSymbol { IsExtension: true }); // UNDONE: The binding and conversion has to be executed in a checked context. Binder defaultValueBinder = this.GetBinder(defaultValueSyntax); @@ -2114,6 +2121,14 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind { Error(diagnostics, ErrorCode.ERR_InvalidPrimaryConstructorParameterReference, node, parameter); } + else if (parameter.ContainingSymbol is NamedTypeSymbol { IsExtension: true } && + (InParameterDefaultValue || InAttributeArgument || + this.ContainingMember() is not { Kind: not SymbolKind.NamedType, IsStatic: false } || // We are not in an instance member + (object)this.ContainingMember().ContainingSymbol != parameter.ContainingSymbol) && + !IsInsideNameof) + { + Error(diagnostics, ErrorCode.ERR_InvalidExtensionParameterReference, node, parameter); + } else { // Records never capture parameters within the type @@ -3435,75 +3450,8 @@ private void CheckAndCoerceArguments( continue; } - if (!argument.HasAnyErrors) - { - var argRefKind = analyzedArguments.RefKind(arg); - - if (!Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefReadonlyParameters)) - { - // Disallow using `ref readonly` parameters with no or `in` argument modifier, - // same as older versions of the compiler would (since they would see the parameter as `ref`). - if (argRefKind is RefKind.None or RefKind.In && - getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) - { - var available = CheckFeatureAvailability(argument.Syntax, MessageID.IDS_FeatureRefReadonlyParameters, diagnostics); - Debug.Assert(!available); - } - } - else - { - var argNumber = invokedAsExtensionMethod ? arg : arg + 1; - - // Warn for `ref`/`in` or None/`ref readonly` mismatch. - if (argRefKind == RefKind.Ref) - { - if (getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.In) - { - Debug.Assert(argNumber > 0); - // The 'ref' modifier for argument {0} corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. - diagnostics.Add( - ErrorCode.WRN_BadArgRef, - argument.Syntax, - argNumber); - } - } - else if (argRefKind == RefKind.None && - getCorrespondingParameter(in result, parameters, arg).RefKind == RefKind.RefReadOnlyParameter) - { - if (!this.CheckValueKind(argument.Syntax, argument, BindValueKind.RefersToLocation, checkingReceiver: false, BindingDiagnosticBag.Discarded)) - { - Debug.Assert(argNumber >= 0); // can be 0 for receiver of extension method - // Argument {0} should be a variable because it is passed to a 'ref readonly' parameter - diagnostics.Add( - ErrorCode.WRN_RefReadonlyNotVariable, - argument.Syntax, - argNumber); - } - else if (!invokedAsExtensionMethod || arg != 0) - { - Debug.Assert(argNumber > 0); - if (this.CheckValueKind(argument.Syntax, argument, BindValueKind.Assignable, checkingReceiver: false, BindingDiagnosticBag.Discarded)) - { - // Argument {0} should be passed with 'ref' or 'in' keyword - diagnostics.Add( - ErrorCode.WRN_ArgExpectedRefOrIn, - argument.Syntax, - argNumber); - } - else - { - // Argument {0} should be passed with the 'in' keyword - diagnostics.Add( - ErrorCode.WRN_ArgExpectedIn, - argument.Syntax, - argNumber); - } - } - } - } - } - int paramNum = result.ParameterFromArgument(arg); + CheckArgumentRefKind(analyzedArguments.RefKind(arg), argument, arg, parameters[paramNum], invokedAsExtensionMethod, diagnostics); if (expanded && paramNum == parameters.Length - 1) { @@ -3936,6 +3884,77 @@ BoundExpression bindInterpolatedStringHandlerInMemberCall( hasErrors || interpolatedString.HasErrors); } } + + private void CheckArgumentRefKind(RefKind argRefKind, BoundExpression argument, int arg, ParameterSymbol parameter, + bool invokedAsExtensionMethod, BindingDiagnosticBag diagnostics) + { + if (argument.HasAnyErrors) + { + return; + } + + if (!Compilation.IsFeatureEnabled(MessageID.IDS_FeatureRefReadonlyParameters)) + { + // Disallow using `ref readonly` parameters with no or `in` argument modifier, + // same as older versions of the compiler would (since they would see the parameter as `ref`). + if (argRefKind is RefKind.None or RefKind.In && parameter.RefKind == RefKind.RefReadOnlyParameter) + { + var available = CheckFeatureAvailability(argument.Syntax, MessageID.IDS_FeatureRefReadonlyParameters, diagnostics); + Debug.Assert(!available); + } + } + else + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider removing or adjusting the reported argument position + var argNumber = invokedAsExtensionMethod ? arg : arg + 1; + + // Warn for `ref`/`in` or None/`ref readonly` mismatch. + if (argRefKind == RefKind.Ref) + { + if (parameter.RefKind == RefKind.In) + { + Debug.Assert(argNumber > 0); + // The 'ref' modifier for argument {0} corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. + diagnostics.Add( + ErrorCode.WRN_BadArgRef, + argument.Syntax, + argNumber); + } + } + else if (argRefKind == RefKind.None && parameter.RefKind == RefKind.RefReadOnlyParameter) + { + if (!this.CheckValueKind(argument.Syntax, argument, BindValueKind.RefersToLocation, checkingReceiver: false, BindingDiagnosticBag.Discarded)) + { + Debug.Assert(argNumber >= 0); // can be 0 for receiver of extension method + // Argument {0} should be a variable because it is passed to a 'ref readonly' parameter + diagnostics.Add( + ErrorCode.WRN_RefReadonlyNotVariable, + argument.Syntax, + argNumber); + } + else if (!invokedAsExtensionMethod || arg != 0) + { + Debug.Assert(argNumber > 0); + if (this.CheckValueKind(argument.Syntax, argument, BindValueKind.Assignable, checkingReceiver: false, BindingDiagnosticBag.Discarded)) + { + // Argument {0} should be passed with 'ref' or 'in' keyword + diagnostics.Add( + ErrorCode.WRN_ArgExpectedRefOrIn, + argument.Syntax, + argNumber); + } + else + { + // Argument {0} should be passed with the 'in' keyword + diagnostics.Add( + ErrorCode.WRN_ArgExpectedIn, + argument.Syntax, + argNumber); + } + } + } + } + } #nullable disable private BoundExpression BindArrayCreationExpression(ArrayCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) @@ -5393,7 +5412,7 @@ private BoundExpression BindDelegateCreationExpression(SyntaxNode node, NamedTyp if (!hasErrors) { CheckParameterModifierMismatchMethodConversion(unboundLambda.Syntax, boundLambda.Symbol, type, invokedAsExtensionMethod: false, diagnostics); - CheckLambdaConversion(boundLambda.Symbol, type, diagnostics); + CheckLambdaConversion((LambdaSymbol)boundLambda.Symbol, type, diagnostics); } // Just stuff the bound lambda into the delegate creation expression. When we lower the lambda to @@ -6683,7 +6702,7 @@ protected BoundExpression BindClassCreationExpression( if (overloadResolutionResult.HasAnyApplicableMember) { - var finalApplicableCandidates = GetCandidatesPassingFinalValidation(node, overloadResolutionResult, receiverOpt: null, default(ImmutableArray), invokedAsExtensionMethod: false, diagnostics); + var finalApplicableCandidates = GetCandidatesPassingFinalValidation(node, overloadResolutionResult, receiverOpt: null, default(ImmutableArray), isExtensionMethodGroup: false, diagnostics); if (finalApplicableCandidates.Length == 1) { @@ -7835,7 +7854,7 @@ BoundExpression tryBindMemberAccessWithBoundNamespaceLeft( Error(diagnostics, lookupResult.Error, right); return new BoundTypeExpression(node, null, - new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity)); + new ExtendedErrorTypeSymbol(GetContainingNamespaceOrNonExtensionType(symbols[0]), symbols.ToImmutable(), lookupResult.Kind, lookupResult.Error, rightArity)); } else if (lookupResult.Kind == LookupResultKind.Empty) { @@ -7865,6 +7884,7 @@ BoundExpression tryBindMemberAccessWithBoundTypeLeft( string rightName, int rightArity) { + Debug.Assert(boundLeft is BoundTypeExpression); Debug.Assert((object)leftType != null); if (leftType.TypeKind == TypeKind.TypeParameter) { @@ -7892,10 +7912,25 @@ BoundExpression tryBindMemberAccessWithBoundTypeLeft( CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); this.LookupMembersWithFallback(lookupResult, leftType, rightName, rightArity, ref useSiteInfo, basesBeingResolved: null, options: options); diagnostics.Add(right, useSiteInfo); + if (lookupResult.IsMultiViable) { - return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArguments, lookupResult, BoundMethodGroupFlags.None, diagnostics: diagnostics); + return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArguments, lookupResult, BoundMethodGroupFlags.SearchExtensions, diagnostics: diagnostics); } + + if (!invoked) + { + var nonMethodExtensionMember = ResolveExtensionMemberAccessIfResultIsNonMethod(node, boundLeft, rightName, + typeArguments, diagnostics); + + if (nonMethodExtensionMember is not null) + { + return nonMethodExtensionMember; + } + } + + return MakeBoundMethodGroupAndCheckOmittedTypeArguments(boundLeft, rightName, typeArguments, lookupResult, + flags: BoundMethodGroupFlags.SearchExtensions, node, typeArgumentsSyntax, diagnostics); } return null; @@ -7925,6 +7960,7 @@ private BoundExpression MakeMemberAccessValue(BoundExpression expr, BindingDiagn var methodGroup = (BoundMethodGroup)expr; CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None); + Debug.Assert(!resolution.IsNonMethodExtensionMember(out _)); diagnostics.Add(expr.Syntax, useSiteInfo); if (!expr.HasAnyErrors) { @@ -7950,6 +7986,74 @@ private BoundExpression MakeMemberAccessValue(BoundExpression expr, BindingDiagn } } +#nullable enable + // When we're binding a member access that is not invoked and the member lookup yielded no result: + // - if an extension member lookup finds a non-method extension member, then that's the member being accessed. + // + // - if the extension member lookup finds a method (classic extension method compatible with the receiver or method in extension declaration; + // closer than any non-method extension member), then we return nothing and let the caller represent the failed member lookup with a BoundMethodGroup. + // Note: Such method group will be resolved specially in scenarios that can handle method groups + // (such as inferred local `var x = A.B;`, conversion to a delegate type `System.Action a = A.B;`). + // It will be an error in other scenarios. + // + // - if the extension member lookup is ambiguous, then we'll use an error symbol as the result of the member access. + // + // - if the extension member lookup finds nothing, then we return nothing and let the caller represent the failed member lookup with a BoundMethodGroup. + internal BoundExpression? ResolveExtensionMemberAccessIfResultIsNonMethod(SyntaxNode syntax, BoundExpression receiver, string name, + ImmutableArray typeArgumentsOpt, BindingDiagnosticBag diagnostics) + { + CompoundUseSiteInfo useSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); + + // Note: we're resolving without arguments, which means we're not treating the member access as invoked + var resolution = this.ResolveExtension( + syntax, name, analyzedArguments: null, receiver, typeArgumentsOpt, options: OverloadResolution.Options.None, + returnRefKind: default, returnType: null, ref useSiteInfo, acceptOnlyMethods: false); + + diagnostics.Add(syntax, useSiteInfo); + + if (resolution.IsNonMethodExtensionMember(out Symbol? extensionMember)) + { + Debug.Assert(typeArgumentsOpt.IsDefault); + if (!receiver.HasErrors) + { + diagnostics.AddRange(resolution.Diagnostics); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test dependencies/diagnostics + } + + resolution.Free(); + + return GetExtensionMemberAccess(syntax, receiver, extensionMember, diagnostics); + } + + resolution.Free(); + return null; + } + + private BoundExpression GetExtensionMemberAccess(SyntaxNode syntax, BoundExpression? receiver, Symbol extensionMember, BindingDiagnosticBag diagnostics) + { + receiver = ReplaceTypeOrValueReceiver(receiver, useType: extensionMember.IsStatic, diagnostics); + + switch (extensionMember) + { + case PropertySymbol propertySymbol: + Debug.Assert(propertySymbol.ContainingType.ExtensionParameter is not null); + + if (receiver is not BoundTypeExpression) + { + receiver = CheckAndConvertExtensionReceiver(receiver, propertySymbol.ContainingType.ExtensionParameter, diagnostics); + } + + return BindPropertyAccess(syntax, receiver, propertySymbol, diagnostics, LookupResultKind.Viable, hasErrors: false); + + case ExtendedErrorTypeSymbol errorTypeSymbol: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should likely reduce (ie. do type inference and substitute) the candidates (like ToBadExpression) + return new BoundBadExpression(syntax, LookupResultKind.Viable, errorTypeSymbol.CandidateSymbols!, [receiver], CreateErrorType()); + + default: + throw ExceptionUtilities.UnexpectedValue(extensionMember.Kind); + } + } +#nullable disable + private BoundExpression BindInstanceMemberAccess( SyntaxNode node, SyntaxNode right, @@ -7961,7 +8065,7 @@ private BoundExpression BindInstanceMemberAccess( bool invoked, bool indexed, BindingDiagnosticBag diagnostics, - bool searchExtensionMethodsIfNecessary = true) + bool searchExtensionsIfNecessary = true) { Debug.Assert(rightArity == (typeArgumentsWithAnnotations.IsDefault ? 0 : typeArgumentsWithAnnotations.Length)); var leftType = boundLeft.Type; @@ -7982,12 +8086,12 @@ private BoundExpression BindInstanceMemberAccess( // SPEC: Otherwise, an attempt is made to process E.I as an extension method invocation. // SPEC: If this fails, E.I is an invalid member reference, and a binding-time error occurs. - searchExtensionMethodsIfNecessary = searchExtensionMethodsIfNecessary && !leftIsBaseReference; + searchExtensionsIfNecessary = searchExtensionsIfNecessary && !leftIsBaseReference; - BoundMethodGroupFlags flags = 0; - if (searchExtensionMethodsIfNecessary) + BoundMethodGroupFlags flags = BoundMethodGroupFlags.None; + if (searchExtensionsIfNecessary) { - flags |= BoundMethodGroupFlags.SearchExtensionMethods; + flags |= BoundMethodGroupFlags.SearchExtensions; } if (lookupResult.IsMultiViable) @@ -7995,7 +8099,7 @@ private BoundExpression BindInstanceMemberAccess( return BindMemberOfType(node, right, rightName, rightArity, indexed, boundLeft, typeArgumentsSyntax, typeArgumentsWithAnnotations, lookupResult, flags, diagnostics); } - if (searchExtensionMethodsIfNecessary) + if (searchExtensionsIfNecessary) { BoundExpression colorColorValueReceiver = GetValueExpressionIfTypeOrValueReceiver(boundLeft); @@ -8004,22 +8108,19 @@ private BoundExpression BindInstanceMemberAccess( boundLeft = ReplaceTypeOrValueReceiver(boundLeft, useType: false, diagnostics); } - var boundMethodGroup = new BoundMethodGroup( - node, - typeArgumentsWithAnnotations, - boundLeft, - rightName, - lookupResult.Symbols.All(s => s.Kind == SymbolKind.Method) ? lookupResult.Symbols.SelectAsArray(s_toMethodSymbolFunc) : ImmutableArray.Empty, - lookupResult, - flags, - this); - - if (!boundMethodGroup.HasErrors && typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) + if (!invoked) { - Error(diagnostics, ErrorCode.ERR_OmittedTypeArgument, node); + var nonMethodExtensionMember = ResolveExtensionMemberAccessIfResultIsNonMethod(node, boundLeft, rightName, + typeArgumentsWithAnnotations, diagnostics); + + if (nonMethodExtensionMember is not null) + { + return nonMethodExtensionMember; + } } - return boundMethodGroup; + return MakeBoundMethodGroupAndCheckOmittedTypeArguments(boundLeft, rightName, typeArgumentsWithAnnotations, lookupResult, + flags, node, typeArgumentsSyntax, diagnostics); } this.BindMemberAccessReportError(node, right, rightName, boundLeft, lookupResult.Error, diagnostics); @@ -8031,6 +8132,28 @@ private BoundExpression BindInstanceMemberAccess( } } + private BoundMethodGroup MakeBoundMethodGroupAndCheckOmittedTypeArguments(BoundExpression boundLeft, string rightName, + ImmutableArray typeArgumentsWithAnnotations, LookupResult lookupResult, BoundMethodGroupFlags flags, SyntaxNode node, + SeparatedSyntaxList typeArgumentsSyntax, BindingDiagnosticBag diagnostics) + { + var boundMethodGroup = new BoundMethodGroup( + node, + typeArgumentsWithAnnotations, + boundLeft, + rightName, + lookupResult.Symbols.All(s => s.Kind == SymbolKind.Method) ? lookupResult.Symbols.SelectAsArray(s_toMethodSymbolFunc) : ImmutableArray.Empty, + lookupResult, + flags, + this); + + if (!boundMethodGroup.HasErrors && typeArgumentsSyntax.Any(SyntaxKind.OmittedTypeArgument)) + { + Error(diagnostics, ErrorCode.ERR_OmittedTypeArgument, node); + } + + return boundMethodGroup; + } + private void LookupInstanceMember(LookupResult lookupResult, TypeSymbol leftType, bool leftIsBaseReference, string rightName, int rightArity, bool invoked, ref CompoundUseSiteInfo useSiteInfo) { LookupOptions options = LookupOptions.AllMethodsOnArityZero; @@ -8083,7 +8206,6 @@ private void BindMemberAccessReportError( } else { - if ((object)boundLeft.Type == null) { Error(diagnostics, ErrorCode.ERR_NoSuchMember, name, boundLeft.Display, plainName); @@ -8227,7 +8349,7 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An Debug.Assert(extensionMethodArguments.Names.Count == 0); Debug.Assert(extensionMethodArguments.RefKinds.Count == 0); - extensionMethodArguments.IsExtensionMethodInvocation = true; + extensionMethodArguments.IncludesReceiverAsArgument = true; extensionMethodArguments.Arguments.Add(receiver); extensionMethodArguments.Arguments.AddRange(originalArguments.Arguments); @@ -8244,6 +8366,17 @@ private static void CombineExtensionMethodArguments(BoundExpression receiver, An } } + private static void InitializeExtensionPropertyArguments(BoundExpression receiver, AnalyzedArguments extensionPropertyArguments) + { + Debug.Assert(receiver != null); + Debug.Assert(extensionPropertyArguments.Arguments.Count == 0); + Debug.Assert(extensionPropertyArguments.Names.Count == 0); + Debug.Assert(extensionPropertyArguments.RefKinds.Count == 0); + + extensionPropertyArguments.IncludesReceiverAsArgument = true; + extensionPropertyArguments.Arguments.Add(receiver); + } + /// /// Binds a static or instance member access. /// @@ -8366,18 +8499,22 @@ private BoundExpression BindMemberOfType( return result; } - protected MethodGroupResolution BindExtensionMethod( +#nullable enable + protected MethodGroupResolution ResolveExtension( SyntaxNode expression, - string methodName, - AnalyzedArguments analyzedArguments, + string memberName, + AnalyzedArguments? analyzedArguments, BoundExpression left, ImmutableArray typeArgumentsWithAnnotations, OverloadResolution.Options options, RefKind returnRefKind, - TypeSymbol returnType, - bool withDependencies, + TypeSymbol? returnType, + ref CompoundUseSiteInfo useSiteInfo, + bool acceptOnlyMethods, in CallingConventionInfo callingConvention = default) { + Debug.Assert(left.Type is not null); + Debug.Assert(!left.Type.IsDynamic()); Debug.Assert((options & ~(OverloadResolution.Options.IsMethodGroupConversion | OverloadResolution.Options.IsFunctionPointerResolution | OverloadResolution.Options.InferWithDynamic | @@ -8387,107 +8524,316 @@ protected MethodGroupResolution BindExtensionMethod( OverloadResolution.Options.DynamicConvertsToAnything)) == 0); var firstResult = new MethodGroupResolution(); - AnalyzedArguments actualArguments = null; + var lookupResult = LookupResult.GetInstance(); + var classicExtensionLookupResult = LookupResult.GetInstance(); + var diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, useSiteInfo.AccumulatesDependencies); + AnalyzedArguments? actualMethodArguments = null; + AnalyzedArguments? actualReceiverArguments = null; - foreach (var scope in new ExtensionMethodScopes(this)) + int arity = typeArgumentsWithAnnotations.IsDefault ? 0 : typeArgumentsWithAnnotations.Length; + var lookupOptions = (arity == 0) ? LookupOptions.AllMethodsOnArityZero : LookupOptions.Default; + if (analyzedArguments is not null) { - var methodGroup = MethodGroup.GetInstance(); - var diagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies); + lookupOptions |= LookupOptions.MustBeInvocableIfMember; + } - this.PopulateExtensionMethodsFromSingleBinder(scope, methodGroup, expression, left, methodName, typeArgumentsWithAnnotations, diagnostics); + if (this.AllowRefOmittedArguments(left)) + { + options |= OverloadResolution.Options.AllowRefOmittedArguments; + } + + foreach (var scope in new ExtensionScopes(this)) + { + lookupResult.Clear(); + classicExtensionLookupResult.Clear(); + diagnostics.Clear(); + + if (tryResolveExtensionInScope( + expression, left, memberName, arity, + typeArgumentsWithAnnotations, returnType, returnRefKind, lookupResult, + analyzedArguments, ref actualMethodArguments, ref actualReceiverArguments, ref useSiteInfo, ref firstResult, + options, callingConvention, classicExtensionLookupResult, lookupOptions, binder: this, scope: scope, diagnostics: diagnostics, + acceptOnlyMethods: acceptOnlyMethods, + result: out MethodGroupResolution result)) + { + lookupResult.Free(); + classicExtensionLookupResult.Free(); + diagnostics.Free(); + actualReceiverArguments?.Free(); + + if (result.AnalyzedArguments != actualMethodArguments) + { + actualMethodArguments?.Free(); + } + + return result; + } + } + + lookupResult.Free(); + classicExtensionLookupResult.Free(); + diagnostics.Free(); + actualReceiverArguments?.Free(); + + if (firstResult.AnalyzedArguments != actualMethodArguments) + { + actualMethodArguments?.Free(); + } + + return firstResult; + + static bool tryResolveExtensionInScope( + SyntaxNode expression, + BoundExpression left, + string memberName, + int arity, + ImmutableArray typeArgumentsWithAnnotations, + TypeSymbol? returnType, + RefKind returnRefKind, + LookupResult lookupResult, + AnalyzedArguments? analyzedArguments, + ref AnalyzedArguments? actualMethodArguments, + ref AnalyzedArguments? actualReceiverArguments, + ref CompoundUseSiteInfo useSiteInfo, + ref MethodGroupResolution firstResult, + OverloadResolution.Options options, + CallingConventionInfo callingConvention, + LookupResult classicExtensionLookupResult, + LookupOptions lookupOptions, + Binder binder, + ExtensionScope scope, + bool acceptOnlyMethods, + BindingDiagnosticBag diagnostics, + out MethodGroupResolution result) + { + Debug.Assert(left.Type is not null); + result = default; + + // 1. gather candidates + CompoundUseSiteInfo classicExtensionUseSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics); + scope.Binder.LookupAllExtensionMembersInSingleBinder( + lookupResult, memberName, arity, lookupOptions, + originalBinder: binder, useSiteInfo: ref useSiteInfo, classicExtensionUseSiteInfo: ref classicExtensionUseSiteInfo); + + diagnostics.Add(expression, classicExtensionUseSiteInfo); + + if (!lookupResult.IsMultiViable) + { + return false; + } + + // 2. resolve methods + MethodGroupResolution methodResult = resolveMethods(expression, left, typeArgumentsWithAnnotations, returnType, returnRefKind, + lookupResult, analyzedArguments, ref actualMethodArguments, options, in callingConvention, binder, diagnostics); + + // 3. resolve properties + Debug.Assert(arity == 0 || lookupResult.Symbols.All(s => s.Kind != SymbolKind.Property)); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Regarding 'acceptOnlyMethods', consider if it would be better to add a special 'LookupOptions' value to filter out properties during lookup + OverloadResolutionResult? propertyResult = arity != 0 || acceptOnlyMethods ? null : resolveProperties(left, lookupResult, binder, ref actualReceiverArguments, ref useSiteInfo); + + // 4. determine member kind + if (!methodResult.HasAnyApplicableMethod && propertyResult?.HasAnyApplicableMember != true) + { + // Found nothing applicable. Store aside the first non-applicable result and continue searching for an applicable result. + if (firstResult.IsEmpty) + { + if (propertyResult != null) + { + methodResult.Free(keepArguments: true); + propertyResult.Free(); + firstResult = makeErrorResult(left.Type, memberName, arity, lookupResult, expression, diagnostics); + } + else + { + firstResult = methodResult; + } + } + + return false; + } + + if (methodResult.HasAnyApplicableMethod) + { + // If the search in the current scope resulted in any applicable method (regardless of whether a best + // applicable method could be determined) then our search is complete. + if (propertyResult?.HasAnyApplicableMember != true) + { + // methods win + propertyResult?.Free(); + result = methodResult; + return true; + } + + // ambiguous between methods and properties + methodResult.Free(keepArguments: true); + propertyResult?.Free(); + result = makeErrorResult(left.Type, memberName, arity, lookupResult, expression, diagnostics); + return true; + } + + // If the search in the current scope resulted in any applicable property (regardless of whether a best + // applicable property could be determined) then our search is complete. + Debug.Assert(propertyResult?.HasAnyApplicableMember == true); + methodResult.Free(keepArguments: true); + if (propertyResult.Succeeded && propertyResult.BestResult.Member is { } bestProperty) + { + // property wins + propertyResult.Free(); + result = new MethodGroupResolution(bestProperty, LookupResultKind.Viable, diagnostics.ToReadOnly()); + return true; + } + + // ambiguous between multiple applicable properties + propertyResult.Free(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider using the property overload resolution result in the result to improve reported diagnostics + result = makeErrorResult(left.Type, memberName, arity, lookupResult, expression, diagnostics); + return true; + } + + static MethodGroupResolution resolveMethods( + SyntaxNode expression, + BoundExpression left, + ImmutableArray typeArgumentsWithAnnotations, + TypeSymbol? returnType, + RefKind returnRefKind, + LookupResult lookupResult, + AnalyzedArguments? analyzedArguments, + ref AnalyzedArguments? actualMethodArguments, + OverloadResolution.Options options, + in CallingConventionInfo callingConvention, + Binder binder, + BindingDiagnosticBag diagnostics) + { + var methodGroup = MethodGroup.GetInstance(); + methodGroup.PopulateWithExtensionMethods(left, lookupResult.Symbols, typeArgumentsWithAnnotations, resultKind: lookupResult.Kind); - // analyzedArguments will be null if the caller is resolving for error recovery to the first method group - // that can accept that receiver, regardless of arguments, when the signature cannot be inferred. - // (In the error case of nameof(o.M) or the error case of o.M = null; for instance.) if (analyzedArguments == null) { - if (expression == EnclosingNameofArgument) + // Without arguments (for scenarios such as `nameof` or conversion to non-delegate/dynamic type) + // we can still prune the inapplicable extension methods using the receiver type + for (int i = methodGroup.Methods.Count - 1; i >= 0; i--) { - for (int i = methodGroup.Methods.Count - 1; i >= 0; i--) + MethodSymbol method = methodGroup.Methods[i]; + TypeSymbol? receiverType = left.Type; + Debug.Assert(receiverType is not null); + + bool inapplicable = false; + if (method.IsExtensionMethod + && (object)method.ReduceExtensionMethod(receiverType, binder.Compilation) == null) { - if ((object)methodGroup.Methods[i].ReduceExtensionMethod(left.Type, this.Compilation) == null) - methodGroup.Methods.RemoveAt(i); + inapplicable = true; + } + else if (method.GetIsNewExtensionMember() + && SourceNamedTypeSymbol.GetCompatibleSubstitutedMember(binder.Compilation, method, receiverType) == null) + { + inapplicable = true; + } + + if (inapplicable) + { + methodGroup.Methods.RemoveAt(i); } } if (methodGroup.Methods.Count != 0) { - return new MethodGroupResolution(methodGroup, diagnostics.ToReadOnlyAndFree()); + return new MethodGroupResolution(methodGroup, diagnostics.ToReadOnly()); } } if (methodGroup.Methods.Count == 0) { methodGroup.Free(); - diagnostics.Free(); - continue; + return default; } - if (actualArguments == null) + if (actualMethodArguments == null) { - // Create a set of arguments for overload resolution of the - // extension methods that includes the "this" parameter. - actualArguments = AnalyzedArguments.GetInstance(); - CombineExtensionMethodArguments(left, analyzedArguments, actualArguments); + // Create a set of arguments for overload resolution including the receiver. + actualMethodArguments = AnalyzedArguments.GetInstance(); + CombineExtensionMethodArguments(left, analyzedArguments, actualMethodArguments); } var overloadResolutionResult = OverloadResolutionResult.GetInstance(); - // If we're in a parameter default value or attribute argument, this is an error scenario, so avoid checking - // for COM imported types to avoid a binding cycle. - if (!InParameterDefaultValue && !InAttributeArgument && methodGroup.Receiver.IsExpressionOfComImportType()) - { - options |= OverloadResolution.Options.AllowRefOmittedArguments; - } - - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - OverloadResolution.MethodInvocationOverloadResolution( + CompoundUseSiteInfo overloadResolutionUseSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics); + binder.OverloadResolution.MethodInvocationOverloadResolution( methods: methodGroup.Methods, typeArguments: methodGroup.TypeArguments, receiver: methodGroup.Receiver, - arguments: actualArguments, + arguments: actualMethodArguments, result: overloadResolutionResult, - useSiteInfo: ref useSiteInfo, + ref overloadResolutionUseSiteInfo, options: options | OverloadResolution.Options.IsExtensionMethodResolution, returnRefKind: returnRefKind, returnType: returnType, in callingConvention); - diagnostics.Add(expression, useSiteInfo); - var sealedDiagnostics = diagnostics.ToReadOnlyAndFree(); - // Note: the MethodGroupResolution instance is responsible for freeing its copy of actual arguments - var result = new MethodGroupResolution(methodGroup, null, overloadResolutionResult, AnalyzedArguments.GetInstance(actualArguments), methodGroup.ResultKind, sealedDiagnostics); + diagnostics.Add(expression, overloadResolutionUseSiteInfo); + + // Note: the MethodGroupResolution instance is responsible for freeing the method group, + // the overload resolution result and the arguments + return new MethodGroupResolution(methodGroup, null, overloadResolutionResult, actualMethodArguments, methodGroup.ResultKind, diagnostics.ToReadOnly()); + } - // If the search in the current scope resulted in any applicable method (regardless of whether a best - // applicable method could be determined) then our search is complete. Otherwise, store aside the - // first non-applicable result and continue searching for an applicable result. - if (result.HasAnyApplicableMethod) + // The caller is responsible for freeing the result + static OverloadResolutionResult? resolveProperties( + BoundExpression left, + LookupResult lookupResult, + Binder binder, + ref AnalyzedArguments? actualReceiverArguments, + ref CompoundUseSiteInfo useSiteInfo) + { + ArrayBuilder? properties = null; + foreach (var member in lookupResult.Symbols) { - if (!firstResult.IsEmpty) + if (member is PropertySymbol property) { - firstResult.MethodGroup.Free(); - firstResult.OverloadResolutionResult.Free(); + properties ??= ArrayBuilder.GetInstance(); + properties.Add(property); } - return result; } - else if (firstResult.IsEmpty) + + if (properties is null) { - firstResult = result; + return null; } - else + + if (actualReceiverArguments == null) { - // Neither the first result, nor applicable. No need to save result. - overloadResolutionResult.Free(); - methodGroup.Free(); + actualReceiverArguments = AnalyzedArguments.GetInstance(); + InitializeExtensionPropertyArguments(left, actualReceiverArguments); } + + OverloadResolutionResult overloadResolutionResult = OverloadResolutionResult.GetInstance(); + + binder.OverloadResolution.PropertyOverloadResolution(properties, left, actualReceiverArguments, overloadResolutionResult, + allowRefOmittedArguments: binder.AllowRefOmittedArguments(left), dynamicResolution: actualReceiverArguments.HasDynamicArgument, ref useSiteInfo); + + properties.Free(); + return overloadResolutionResult; } - Debug.Assert((actualArguments == null) || !firstResult.IsEmpty); - actualArguments?.Free(); - return firstResult; + static MethodGroupResolution makeErrorResult(TypeSymbol receiverType, string memberName, int arity, LookupResult lookupResult, SyntaxNode expression, BindingDiagnosticBag diagnostics) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we'll want to describe what went wrong in a useful way (see OverloadResolutionResult.ReportDiagnostics) + var errorInfo = new CSDiagnosticInfo(ErrorCode.ERR_ExtensionResolutionFailed, receiverType, memberName); + diagnostics.Add(errorInfo, expression.Location); + var resultSymbol = new ExtendedErrorTypeSymbol(containingSymbol: null, lookupResult.Symbols.ToImmutable(), LookupResultKind.OverloadResolutionFailure, errorInfo, arity); + return new MethodGroupResolution(resultSymbol, LookupResultKind.Viable, diagnostics.ToReadOnly()); + } + } + + private bool AllowRefOmittedArguments(BoundExpression receiver) + { + // We don't consider when we're in default parameter values or attribute arguments so that we avoid cycles. This is an error scenario, + // so we don't care if we accidentally miss a parameter being applicable. + return !InParameterDefaultValue && !InAttributeArgument && receiver.IsExpressionOfComImportType(); } +#nullable disable private void PopulateExtensionMethodsFromSingleBinder( - ExtensionMethodScope scope, + ExtensionScope scope, MethodGroup methodGroup, SyntaxNode node, BoundExpression left, @@ -8525,7 +8871,7 @@ private void PopulateExtensionMethodsFromSingleBinder( lookupResult.Free(); } - private void LookupExtensionMethods(LookupResult lookupResult, ExtensionMethodScope scope, string rightName, int arity, ref CompoundUseSiteInfo useSiteInfo) + private void LookupExtensionMethods(LookupResult lookupResult, ExtensionScope scope, string rightName, int arity, ref CompoundUseSiteInfo useSiteInfo) { LookupOptions options; if (arity == 0) @@ -9790,7 +10136,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( // and an ambiguity error may be reported. Also additional checks are performed in runtime final validation // that are not performed at compile-time. // Only if the set of final applicable candidates is empty we know for sure the call will fail at runtime. - var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, overloadResolutionResult, receiver, default(ImmutableArray), invokedAsExtensionMethod: false, diagnostics); + var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, overloadResolutionResult, receiver, default(ImmutableArray), isExtensionMethodGroup: false, diagnostics); if (finalApplicableCandidates.Length == 1) { @@ -10153,7 +10499,9 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, { WasCompilerGenerated = true }; indexerOrSliceAccess = BindMethodGroupInvocation(syntax, syntax, method.Name, boundMethodGroup, analyzedArguments, - diagnostics, queryClause: null, ignoreNormalFormIfHasValidParamsParameter: true, anyApplicableCandidates: out bool _).MakeCompilerGenerated(); + diagnostics, queryClause: null, ignoreNormalFormIfHasValidParamsParameter: true, anyApplicableCandidates: out bool _, + disallowExpandedNonArrayParams: false, + acceptOnlyMethods: true).MakeCompilerGenerated(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test effect of acceptOnlyMethods value analyzedArguments.Free(); } @@ -10281,24 +10629,28 @@ internal MethodGroupResolution ResolveMethodGroup( return ResolveMethodGroup( node, node.Syntax, node.Name, analyzedArguments, ref useSiteInfo, - options, returnRefKind: returnRefKind, returnType: returnType, + options, + acceptOnlyMethods: true, // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Confirm this value is appropriate for all consumers of the enclosing method and test effect of this value for all of them + returnRefKind: returnRefKind, returnType: returnType, callingConventionInfo: callingConventionInfo); } internal MethodGroupResolution ResolveMethodGroup( BoundMethodGroup node, SyntaxNode expression, - string methodName, + string memberName, AnalyzedArguments analyzedArguments, ref CompoundUseSiteInfo useSiteInfo, OverloadResolution.Options options, + bool acceptOnlyMethods, RefKind returnRefKind = default, TypeSymbol returnType = null, in CallingConventionInfo callingConventionInfo = default) { var methodResolution = ResolveMethodGroupInternal( - node, expression, methodName, analyzedArguments, ref useSiteInfo, + node, expression, memberName, analyzedArguments, ref useSiteInfo, options, + acceptOnlyMethods: acceptOnlyMethods, returnRefKind: returnRefKind, returnType: returnType, callingConvention: callingConventionInfo); if (methodResolution.IsEmpty && !methodResolution.HasAnyErrors) @@ -10315,31 +10667,14 @@ internal MethodGroupResolution ResolveMethodGroup( return methodResolution; } - internal MethodGroupResolution ResolveMethodGroupForFunctionPointer( - BoundMethodGroup methodGroup, - AnalyzedArguments analyzedArguments, - TypeSymbol returnType, - RefKind returnRefKind, - in CallingConventionInfo callingConventionInfo, - ref CompoundUseSiteInfo useSiteInfo) - { - return ResolveDefaultMethodGroup( - methodGroup, - analyzedArguments, - ref useSiteInfo, - options: OverloadResolution.Options.IsMethodGroupConversion | OverloadResolution.Options.IsFunctionPointerResolution, - returnRefKind, - returnType, - callingConventionInfo); - } - private MethodGroupResolution ResolveMethodGroupInternal( BoundMethodGroup methodGroup, SyntaxNode expression, - string methodName, + string memberName, AnalyzedArguments analyzedArguments, ref CompoundUseSiteInfo useSiteInfo, OverloadResolution.Options options, + bool acceptOnlyMethods, RefKind returnRefKind = default, TypeSymbol returnType = null, in CallingConventionInfo callingConvention = default) @@ -10351,14 +10686,15 @@ private MethodGroupResolution ResolveMethodGroupInternal( // If the method group's receiver is dynamic then there is no point in looking for extension methods; // it's going to be a dynamic invocation. - if (!methodGroup.SearchExtensionMethods || methodResolution.HasAnyApplicableMethod || methodGroup.MethodGroupReceiverIsDynamic()) + if (!methodGroup.SearchExtensions || methodResolution.HasAnyApplicableMethod || methodGroup.MethodGroupReceiverIsDynamic()) { return methodResolution; } - var extensionMethodResolution = BindExtensionMethod( - expression, methodName, analyzedArguments, methodGroup.ReceiverOpt, methodGroup.TypeArgumentsOpt, options, - returnRefKind: returnRefKind, returnType: returnType, withDependencies: useSiteInfo.AccumulatesDependencies, + var extensionMethodResolution = ResolveExtension( + expression, memberName, analyzedArguments, methodGroup.ReceiverOpt, methodGroup.TypeArgumentsOpt, options, + returnRefKind: returnRefKind, returnType: returnType, ref useSiteInfo, + acceptOnlyMethods: acceptOnlyMethods, in callingConvention); bool preferExtensionMethodResolution = false; @@ -10464,10 +10800,7 @@ private MethodGroupResolution ResolveDefaultMethodGroup( else { var result = OverloadResolutionResult.GetInstance(); - // We check for being in a default parameter value or attribute to avoid a cycle. If we're binding these, - // this entire expression is bad, so we don't care whether this is a COM import type and we can safely - // just pass false here. - if (!InParameterDefaultValue && !InAttributeArgument && methodGroup.Receiver.IsExpressionOfComImportType()) + if (AllowRefOmittedArguments(methodGroup.Receiver)) { options |= OverloadResolution.Options.AllowRefOmittedArguments; } @@ -10548,10 +10881,10 @@ private MethodGroupResolution ResolveDefaultMethodGroup( } } - if (node.SearchExtensionMethods) + if (node.ReceiverOpt is not BoundTypeExpression && node.SearchExtensions) { var receiver = node.ReceiverOpt!; - foreach (var scope in new ExtensionMethodScopes(this)) + foreach (var scope in new ExtensionScopes(this)) { methods.Clear(); var methodGroup = MethodGroup.GetInstance(); @@ -10655,6 +10988,9 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) useParams = false; MethodSymbol? foundMethod = null; var typeArguments = node.TypeArgumentsOpt; + int arity = typeArguments.IsDefaultOrEmpty ? 0 : typeArguments.Length; + + // 1. instance methods if (node.ResultKind == LookupResultKind.Viable) { var methods = ArrayBuilder.GetInstance(capacity: node.Methods.Length); @@ -10662,6 +10998,8 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) { switch (node.ReceiverOpt) { + case BoundTypeOrValueExpression: + break; case BoundTypeExpression: case null: // if `using static Class` is in effect, the receiver is missing if (!memberMethod.IsStatic) continue; @@ -10673,7 +11011,6 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) break; } - int arity = typeArguments.IsDefaultOrEmpty ? 0 : typeArguments.Length; if (memberMethod.Arity != arity) { // We have no way of inferring type arguments, so if the given type arguments @@ -10714,52 +11051,63 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) } } - if (node.SearchExtensionMethods) + // 2. extensions + if (node.SearchExtensions) { - var receiver = node.ReceiverOpt!; - var methodGroup = MethodGroup.GetInstance(); - foreach (var scope in new ExtensionMethodScopes(this)) - { - methodGroup.Clear(); - PopulateExtensionMethodsFromSingleBinder(scope, methodGroup, node.Syntax, receiver, node.Name, typeArguments, BindingDiagnosticBag.Discarded); - var methods = ArrayBuilder.GetInstance(capacity: methodGroup.Methods.Count); - foreach (var extensionMethod in methodGroup.Methods) - { - var substituted = typeArguments.IsDefaultOrEmpty ? extensionMethod : extensionMethod.Construct(typeArguments); + Debug.Assert(node.ReceiverOpt!.Type is not null); // extensions are only considered on member access - var reduced = substituted.ReduceExtensionMethod(receiver.Type, Compilation, out bool wasFullyInferred); - if (reduced is null) - { - // Extension method was not applicable - continue; - } + BoundExpression receiver = node.ReceiverOpt; + LookupOptions options = arity == 0 ? LookupOptions.AllMethodsOnArityZero : LookupOptions.Default; + var singleLookupResults = ArrayBuilder.GetInstance(); + CompoundUseSiteInfo discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + + foreach (var scope in new ExtensionScopes(this)) + { + singleLookupResults.Clear(); + scope.Binder.EnumerateAllExtensionMembersInSingleBinder(singleLookupResults, node.Name, arity, options, originalBinder: this, ref discardedUseSiteInfo, ref discardedUseSiteInfo); - if (!wasFullyInferred) + var methods = ArrayBuilder.GetInstance(capacity: singleLookupResults.Count); + foreach (SingleLookupResult singleLookupResult in singleLookupResults) + { + // Remove static/instance mismatches + Symbol extensionMember = singleLookupResult.Symbol; + bool memberCountsAsStatic = extensionMember is MethodSymbol { IsExtensionMethod: true } ? false : extensionMember.IsStatic; + switch (node.ReceiverOpt) { - continue; + case BoundTypeOrValueExpression: + break; + case BoundTypeExpression: + if (!memberCountsAsStatic) continue; + break; + default: + if (memberCountsAsStatic) continue; + break; } - if (!satisfiesConstraintChecks(reduced)) + // Note: we only care about methods since we're already decided this is a method group (ie. not resolving to some other kind of extension member) + if (extensionMember is MethodSymbol method) { - continue; + var substituted = (MethodSymbol?)extensionMember.GetReducedAndFilteredSymbol(typeArguments, receiver.Type, Compilation, checkFullyInferred: true); + if (substituted is not null) + { + methods.Add(substituted); + } } - - methods.Add(reduced); } if (!OverloadResolution.FilterMethodsForUniqueSignature(methods, out useParams)) { + singleLookupResults.Free(); methods.Free(); - methodGroup.Free(); return null; } - foreach (var reduced in methods) + foreach (var method in methods) { - if (!isCandidateUnique(ref foundMethod, reduced)) + if (!isCandidateUnique(ref foundMethod, method)) { + singleLookupResults.Free(); methods.Free(); - methodGroup.Free(); useParams = false; return null; } @@ -10769,11 +11117,12 @@ static bool isCandidateUnique(ref MethodSymbol? method, MethodSymbol candidate) if (foundMethod is not null) { - methodGroup.Free(); + singleLookupResults.Free(); return foundMethod; } } - methodGroup.Free(); + + singleLookupResults.Free(); } useParams = false; @@ -10977,6 +11326,10 @@ private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccess var conditionalAccessBinder = new BinderWithConditionalReceiver(this, receiver); var access = conditionalAccessBinder.BindValue(node.WhenNotNull, diagnostics, BindValueKind.RValue); + if (access.Syntax is AssignmentExpressionSyntax assignment) + { + MessageID.IDS_FeatureNullConditionalAssignment.CheckFeatureAvailability(diagnostics, assignment.OperatorToken); + } if (receiver.HasAnyErrors || access.HasAnyErrors) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs index 2e1a9223a5d40..2fe795e8b504f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_InterpolatedString.cs @@ -972,7 +972,7 @@ private ImmutableArray BindInterpolatedStringParts(BoundUnconve parameterNamesAndLocationsBuilder.Clear(); } - var call = MakeInvocationExpression(part.Syntax, implicitBuilderReceiver, methodName, arguments, diagnostics, names: parameterNamesAndLocations, searchExtensionMethodsIfNecessary: false); + var call = MakeInvocationExpression(part.Syntax, implicitBuilderReceiver, methodName, arguments, diagnostics, names: parameterNamesAndLocations, searchExtensionsIfNecessary: false); builderAppendCalls.Add(call); positionInfo.Add((isLiteral, hasAlignment, hasFormat)); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index fcf522526898d..ba3fd176a2680 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -86,7 +86,7 @@ internal BoundExpression MakeInvocationExpression( CSharpSyntaxNode? queryClause = null, bool allowFieldsAndProperties = false, bool ignoreNormalFormIfHasValidParamsParameter = false, - bool searchExtensionMethodsIfNecessary = true, + bool searchExtensionsIfNecessary = true, bool disallowExpandedNonArrayParams = false) { // @@ -101,7 +101,7 @@ internal BoundExpression MakeInvocationExpression( Debug.Assert(names.IsDefault || names.Length == args.Length); receiver = BindToNaturalType(receiver, diagnostics); - var boundExpression = BindInstanceMemberAccess(node, node, receiver, methodName, typeArgs.NullToEmpty().Length, typeArgsSyntax, typeArgs, invoked: true, indexed: false, diagnostics, searchExtensionMethodsIfNecessary); + var boundExpression = BindInstanceMemberAccess(node, node, receiver, methodName, typeArgs.NullToEmpty().Length, typeArgsSyntax, typeArgs, invoked: true, indexed: false, diagnostics, searchExtensionsIfNecessary); // The other consumers of this helper (await and collection initializers) require the target member to be a method. if (!allowFieldsAndProperties && (boundExpression.Kind == BoundKind.FieldAccess || boundExpression.Kind == BoundKind.PropertyAccess)) @@ -355,7 +355,8 @@ private BoundExpression BindInvocationExpression( diagnostics, queryClause, ignoreNormalFormIfHasValidParamsParameter: ignoreNormalFormIfHasValidParamsParameter, disallowExpandedNonArrayParams: disallowExpandedNonArrayParams, - anyApplicableCandidates: out _); + anyApplicableCandidates: out _, + acceptOnlyMethods: false); } else if ((object)(delegateType = GetDelegateType(boundExpression)) != null) { @@ -696,7 +697,8 @@ private BoundExpression BindMethodGroupInvocation( CSharpSyntaxNode queryClause, bool ignoreNormalFormIfHasValidParamsParameter, out bool anyApplicableCandidates, - bool disallowExpandedNonArrayParams = false) + bool disallowExpandedNonArrayParams, + bool acceptOnlyMethods) // For example, do not accept extension property value invocations (delegates returned by a property can be invoked, etc.) { // // !!! ATTENTION !!! @@ -713,8 +715,23 @@ private BoundExpression BindMethodGroupInvocation( useSiteInfo: ref useSiteInfo, options: (ignoreNormalFormIfHasValidParamsParameter ? OverloadResolution.Options.IgnoreNormalFormIfHasValidParamsParameter : OverloadResolution.Options.None) | (disallowExpandedNonArrayParams ? OverloadResolution.Options.DisallowExpandedNonArrayParams : OverloadResolution.Options.None) | - (analyzedArguments.HasDynamicArgument ? OverloadResolution.Options.DynamicResolution : OverloadResolution.Options.None)); + (analyzedArguments.HasDynamicArgument ? OverloadResolution.Options.DynamicResolution : OverloadResolution.Options.None), + acceptOnlyMethods: acceptOnlyMethods); diagnostics.Add(expression, useSiteInfo); + + if (resolution.IsNonMethodExtensionMember(out Symbol extensionMember)) + { + diagnostics.AddRange(resolution.Diagnostics); + BoundExpression extensionMemberAccess = GetExtensionMemberAccess(expression, methodGroup.ReceiverOpt, extensionMember, diagnostics); + + Debug.Assert(extensionMemberAccess.Kind != BoundKind.MethodGroup); + + extensionMemberAccess = CheckValue(extensionMemberAccess, BindValueKind.RValue, diagnostics); + BoundExpression extensionMemberInvocation = BindInvocationExpression(syntax, expression, methodName: null, extensionMemberAccess, analyzedArguments, diagnostics); + anyApplicableCandidates = !extensionMemberInvocation.HasAnyErrors; + return extensionMemberInvocation; + } + anyApplicableCandidates = resolution.ResultKind == LookupResultKind.Viable && resolution.OverloadResolutionResult.HasAnyApplicableMember; if (!methodGroup.HasAnyErrors) diagnostics.AddRange(resolution.Diagnostics); // Suppress cascading. @@ -782,7 +799,7 @@ private BoundExpression BindMethodGroupInvocation( var finalApplicableCandidates = GetCandidatesPassingFinalValidation(syntax, resolution.OverloadResolutionResult, methodGroup.ReceiverOpt, methodGroup.TypeArgumentsOpt, - invokedAsExtensionMethod: resolution.IsExtensionMethodGroup, + isExtensionMethodGroup: resolution.IsExtensionMethodGroup, diagnostics); if (finalApplicableCandidates.Length == 0) @@ -812,11 +829,9 @@ private BoundExpression BindMethodGroupInvocation( // casting the dynamic arguments or calling the extension method without the extension method // syntax. - // We found an extension method, so the instance associated with the method group must have - // existed and had a type. - Debug.Assert(methodGroup.InstanceOpt != null && (object)methodGroup.InstanceOpt.Type != null); + Debug.Assert(methodGroup.ReceiverOpt != null && (object)methodGroup.ReceiverOpt.Type != null); - Error(diagnostics, ErrorCode.ERR_BadArgTypeDynamicExtension, syntax, methodGroup.InstanceOpt.Type, methodGroup.Name); + Error(diagnostics, ErrorCode.ERR_BadArgTypeDynamicExtension, syntax, methodGroup.ReceiverOpt.Type, methodGroup.Name); result = CreateBadCall(syntax, methodGroup, methodGroup.ResultKind, analyzedArguments); } else @@ -972,7 +987,7 @@ private ImmutableArray> GetCandi OverloadResolutionResult overloadResolutionResult, BoundExpression receiverOpt, ImmutableArray typeArgumentsOpt, - bool invokedAsExtensionMethod, + bool isExtensionMethodGroup, BindingDiagnosticBag diagnostics) where TMethodOrPropertySymbol : Symbol { Debug.Assert(overloadResolutionResult.HasAnyApplicableMember); @@ -994,7 +1009,8 @@ private ImmutableArray> GetCandi // * If F is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value, // or a member-access whose receiver can't be classified as a type or value until after overload resolution (see §7.6.4.1). - if (!MemberGroupFinalValidationAccessibilityChecks(receiverOpt, result.Member, syntax, candidateDiagnostics, invokedAsExtensionMethod: invokedAsExtensionMethod) && + TMethodOrPropertySymbol member = result.Member; + if (!MemberGroupFinalValidationAccessibilityChecks(receiverOpt, member, syntax, candidateDiagnostics, invokedAsExtensionMethod: isExtensionMethodGroup && !member.GetIsNewExtensionMember()) && (typeArgumentsOpt.IsDefault || ((MethodSymbol)(object)result.Member).CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, includeNullability: false, syntax.Location, candidateDiagnostics)))) { finalCandidates.Add(result); @@ -1042,7 +1058,7 @@ private void CheckRestrictedTypeReceiver(BoundExpression expression, CSharpCompi case BoundKind.Call: { var call = (BoundCall)expression; - if (!call.HasAnyErrors && call.ReceiverOpt != null && (object)call.ReceiverOpt.Type != null) + if (!call.HasAnyErrors && call.ReceiverOpt != null && (object)call.ReceiverOpt.Type != null && !call.Method.GetIsNewExtensionMember()) { // error CS0029: Cannot implicitly convert type 'A' to 'B' @@ -1120,7 +1136,7 @@ private BoundCall BindInvocationExpressionContinued( Debug.Assert(methodGroup.Methods.Count > 0); Debug.Assert(((object)delegateTypeOpt == null) || (methodGroup.Methods.Count == 1)); - var invokedAsExtensionMethod = methodGroup.IsExtensionMethodGroup; + bool invokedAsExtensionMethod = methodGroup.IsExtensionMethodGroup; // Delegate invocations should never be considered extension method // invocations (even though the delegate may refer to an extension method). @@ -1184,6 +1200,28 @@ private BoundCall BindInvocationExpressionContinued( var methodResult = result.ValidResult; var returnType = methodResult.Member.ReturnType; var method = methodResult.Member; + bool isNewExtensionMethod = method.GetIsNewExtensionMember(); + + if (isNewExtensionMethod) + { + // For new extension methods, we performed overload resolution giving the receiver as one of the arguments. + // We now restore the arguments to their original state and update the result accordingly. + invokedAsExtensionMethod = false; + analyzedArguments.Arguments.RemoveAt(0); + + if (analyzedArguments.Names is { Count: > 0 }) + { + analyzedArguments.Names.RemoveAt(0); + } + + if (analyzedArguments.RefKinds is { Count: > 0 }) + { + analyzedArguments.RefKinds.RemoveAt(0); + } + + Debug.Assert(methodResult.Result.ConversionForArg(0).Exists); + methodResult = methodResult.WithResult(methodResult.Result.WithoutReceiverArgument()); + } // It is possible that overload resolution succeeded, but we have chosen an // instance method and we're in a static method. A careful reading of the @@ -1248,9 +1286,13 @@ private BoundCall BindInvocationExpressionContinued( analyzedArguments.Arguments[0] = receiverArgument; } + else if (isNewExtensionMethod && receiver is not BoundTypeExpression) + { + receiver = CheckAndConvertExtensionReceiver(receiver, method.ContainingType.ExtensionParameter, diagnostics); + } // This will be the receiver of the BoundCall node that we create. - // For extension methods, there is no receiver because the receiver in source was actually the first argument. + // For classic extension methods, there is no receiver because the receiver in source was actually the first argument. // For instance methods, we may have synthesized an implicit this node. We'll keep it for the emitter. // For static methods, we may have synthesized a type expression. It serves no purpose, so we'll drop it. if (invokedAsExtensionMethod || (!method.RequiresInstanceReceiver && receiver != null && receiver.WasCompilerGenerated)) @@ -1317,6 +1359,20 @@ private BoundCall BindInvocationExpressionContinued( } #nullable enable + private BoundExpression CheckAndConvertExtensionReceiver(BoundExpression receiver, ParameterSymbol extensionParameter, BindingDiagnosticBag diagnostics) + { + CheckArgumentRefKind(RefKind.None, receiver, arg: 0, extensionParameter, invokedAsExtensionMethod: true, diagnostics); + + if (extensionParameter.RefKind == RefKind.Ref) + { + // If this was a ref extension method, the receiver must be checked for L-value constraints. + // This helper method will also replace it with a BoundBadExpression if it was invalid. + receiver = CheckValue(receiver, BindValueKind.RefOrOut, diagnostics); + } + + receiver = CreateConversion(receiver, extensionParameter.Type, diagnostics); + return receiver; + } internal ThreeState ReceiverIsSubjectToCloning(BoundExpression? receiver, PropertySymbol property) { @@ -1711,7 +1767,6 @@ static int getArgumentIndex(int parameterIndex, ImmutableArray argsToParams ? parameterIndex : argsToParamsOpt.IndexOf(parameterIndex); } - } private BoundExpression CreateParamsCollection(SyntaxNode node, ParameterSymbol paramsParameter, ImmutableArray collectionArgs, BindingDiagnosticBag diagnostics) @@ -2252,6 +2307,8 @@ private void EnsureNameofExpressionSymbols(BoundMethodGroup methodGroup, Binding // Check that the method group contains something applicable. Otherwise error. CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var resolution = ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None); + Debug.Assert(!resolution.IsNonMethodExtensionMember(out _)); + diagnostics.Add(methodGroup.Syntax, useSiteInfo); diagnostics.AddRange(resolution.Diagnostics); if (resolution.IsExtensionMethodGroup) @@ -2341,6 +2398,7 @@ internal bool InvocableNameofInScope() #nullable enable private BoundFunctionPointerInvocation BindFunctionPointerInvocation(SyntaxNode node, BoundExpression boundExpression, AnalyzedArguments analyzedArguments, BindingDiagnosticBag diagnostics) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : this function will probably need an adjustment to deal with static extensions boundExpression = BindToNaturalType(boundExpression, diagnostics); RoslynDebug.Assert(boundExpression.Type is FunctionPointerTypeSymbol); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 09a442e1d1869..da85570b65b95 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -9,6 +9,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using static Microsoft.CodeAnalysis.CSharp.Symbols.ParameterHelpers; namespace Microsoft.CodeAnalysis.CSharp { @@ -177,16 +178,14 @@ private UnboundLambda AnalyzeAnonymousFunction( var typeOpt = p.Type is not null ? BindType(p.Type, diagnostics) : default; - var refKind = ParameterHelpers.GetModifiers(p.Modifiers, out _, out var paramsKeyword, out _, out var scope); + var refKind = ParameterHelpers.GetModifiers(p.Modifiers, ignoreParams: false, out _, out var paramsKeyword, out _, out var scope); var isParams = paramsKeyword.Kind() != SyntaxKind.None; - ParameterHelpers.CheckParameterModifiers(p, diagnostics, parsingFunctionPointerParams: false, - parsingLambdaParams: !isAnonymousMethod, - parsingAnonymousMethodParams: isAnonymousMethod); + ParameterHelpers.CheckParameterModifiers(p, diagnostics, isAnonymousMethod ? ParameterContext.AnonymousMethod : ParameterContext.Lambda); ParameterHelpers.ReportParameterErrors( - owner: null, p, ordinal: i, lastParameterIndex: n - 1, isParams: isParams, typeOpt, - refKind, containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault, diagnostics); + owner: null, syntax: p, ordinal: i, lastParameterIndex: n - 1, isParams: isParams, typeWithAnnotations: typeOpt, + refKind: refKind, containingSymbol: null, thisKeyword: default, paramsKeyword: paramsKeyword, firstDefault: firstDefault, diagnostics: diagnostics); if (parameterCount == parameterSyntaxList.Count && paramsKeyword.Kind() != SyntaxKind.None && diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 764423d3d7b8a..7c0fede4e5119 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -44,13 +44,19 @@ internal void LookupSymbolsSimpleName( } } - internal void LookupExtensionMethods(LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo useSiteInfo) +#nullable enable + internal void LookupAllExtensions(LookupResult result, string? name, LookupOptions options) { - foreach (var scope in new ExtensionMethodScopes(this)) + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + + foreach (var scope in new ExtensionScopes(this)) { - this.LookupExtensionMethodsInSingleBinder(scope, result, name, arity, options, ref useSiteInfo); + scope.Binder.LookupAllExtensionMembersInSingleBinder( + result, name, arity: 0, options: options, + originalBinder: this, useSiteInfo: ref discardedUseSiteInfo, classicExtensionUseSiteInfo: ref discardedUseSiteInfo); } } +#nullable disable /// /// Look for any symbols in scope with the given name and arity. @@ -176,6 +182,71 @@ protected void LookupMembersInternal(LookupResult result, NamespaceOrTypeSymbol } } +#nullable enable + private void LookupAllExtensionMembersInSingleBinder(LookupResult result, string? name, + int arity, LookupOptions options, Binder originalBinder, ref CompoundUseSiteInfo useSiteInfo, ref CompoundUseSiteInfo classicExtensionUseSiteInfo) + { + var singleLookupResults = ArrayBuilder.GetInstance(); + EnumerateAllExtensionMembersInSingleBinder(singleLookupResults, name, arity, options, originalBinder, ref useSiteInfo, ref classicExtensionUseSiteInfo); + foreach (var singleLookupResult in singleLookupResults) + { + result.MergeEqual(singleLookupResult); + } + + singleLookupResults.Free(); + } + + internal void EnumerateAllExtensionMembersInSingleBinder(ArrayBuilder result, + string? name, int arity, LookupOptions options, Binder originalBinder, ref CompoundUseSiteInfo useSiteInfo, ref CompoundUseSiteInfo classicExtensionUseSiteInfo) + { + PooledHashSet? implementationsToShadow = null; + + // 1. Collect new extension members + if (this.Compilation.LanguageVersion.AllowNewExtensions()) + { + var extensionDeclarations = ArrayBuilder.GetInstance(); + this.GetExtensionDeclarations(extensionDeclarations, originalBinder); + + foreach (NamedTypeSymbol extensionDeclaration in extensionDeclarations) + { + var candidates = name is null ? extensionDeclaration.GetMembers() : extensionDeclaration.GetMembers(name); + + foreach (var candidate in candidates) + { + SingleLookupResult resultOfThisMember = originalBinder.CheckViability(candidate, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo); + result.Add(resultOfThisMember); + + if (candidate is MethodSymbol { IsStatic: false } shadows && + shadows.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is { } toShadow) + { + implementationsToShadow ??= PooledHashSet.GetInstance(); + implementationsToShadow.Add(toShadow); + } + } + } + + extensionDeclarations.Free(); + } + + // 2. Collect classic extension methods + var extensionMethods = ArrayBuilder.GetInstance(); + this.GetCandidateExtensionMethods(extensionMethods, name, arity, options, originalBinder: originalBinder); + + foreach (var method in extensionMethods) + { + // Prefer instance extension declarations vs. their implementations + if (implementationsToShadow is null || !implementationsToShadow.Remove(method.OriginalDefinition)) + { + SingleLookupResult resultOfThisMember = originalBinder.CheckViability(method, arity, options, null, diagnose: true, useSiteInfo: ref classicExtensionUseSiteInfo); + result.Add(resultOfThisMember); + } + } + + extensionMethods.Free(); + implementationsToShadow?.Free(); + } +#nullable disable + // Looks up a member of given name and arity in a particular type. protected void LookupMembersInType(LookupResult result, TypeSymbol type, string name, int arity, ConsList basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo useSiteInfo) { @@ -205,6 +276,7 @@ protected void LookupMembersInType(LookupResult result, TypeSymbol type, string case TypeKind.Pointer: case TypeKind.FunctionPointer: + case TypeKind.Extension: result.Clear(); break; @@ -451,13 +523,14 @@ private static void LookupMembersInNamespace(LookupResult result, NamespaceSymbo } } + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should be able to remove this method once all the callers are updated to account for new extension members /// /// Lookup extension methods by name and arity in the given binder and /// check viability in this binder. The lookup is performed on a single /// binder because extension method search stops at the first applicable /// method group from the nearest enclosing namespace. /// - private void LookupExtensionMethodsInSingleBinder(ExtensionMethodScope scope, LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo useSiteInfo) + private void LookupExtensionMethodsInSingleBinder(ExtensionScope scope, LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo useSiteInfo) { var methods = ArrayBuilder.GetInstance(); var binder = scope.Binder; @@ -738,7 +811,7 @@ private bool CheckAttributeTypeViability(Symbol symbol, bool diagnose, ref Diagn #endregion - internal virtual bool SupportsExtensionMethods + internal virtual bool SupportsExtensions { get { return false; } } @@ -759,6 +832,19 @@ internal virtual void GetCandidateExtensionMethods( { } +#nullable enable + /// + /// Return the extension declarations from this specific binding scope + /// Since the lookup of extension members is iterative, proceeding one binding scope at a time, + /// GetExtensionDeclarations should not defer to the next binding scope. Instead, the caller is + /// responsible for walking the nested binding scopes from innermost to outermost. This method is overridden + /// to search the available members list in binding types that represent types, namespaces, and usings. + /// + internal virtual void GetExtensionDeclarations(ArrayBuilder extensions, Binder originalBinder) + { + } +#nullable disable + // Does a member lookup in a single type, without considering inheritance. protected static void LookupMembersWithoutInheritance(LookupResult result, TypeSymbol type, string name, int arity, LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType, bool diagnose, ref CompoundUseSiteInfo useSiteInfo, ConsList basesBeingResolved) @@ -1793,7 +1879,7 @@ private static bool WrongArity(Symbol symbol, int arity, bool diagnose, LookupOp if (arity != 0 || (options & LookupOptions.AllMethodsOnArityZero) == 0) { MethodSymbol method = (MethodSymbol)symbol; - if (method.Arity != arity) + if (method.GetMemberArityIncludingExtension() != arity) { if (method.Arity == 0) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs index a4bb61a86fcff..26733eb3a9949 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs @@ -30,10 +30,10 @@ internal void ValidateParameterNameConflicts( bool allowShadowingNames, BindingDiagnosticBag diagnostics) { - PooledHashSet? tpNames = null; + PooledDictionary? tpNames = null; if (!typeParameters.IsDefaultOrEmpty) { - tpNames = PooledHashSet.GetInstance(); + tpNames = PooledDictionary.GetInstance(); foreach (var tp in typeParameters) { var name = tp.Name; @@ -42,7 +42,7 @@ internal void ValidateParameterNameConflicts( continue; } - if (!tpNames.Add(name)) + if (!tpNames.TryAdd(name, tp)) { // Type parameter declaration name conflicts are detected elsewhere } @@ -65,16 +65,37 @@ internal void ValidateParameterNameConflicts( continue; } - if (tpNames != null && tpNames.Contains(name)) + if (tpNames != null && tpNames.TryGetValue(name, out TypeParameterSymbol? tp)) { - // CS0412: 'X': a parameter or local variable cannot have the same name as a method type parameter - diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name); + if (tp.ContainingSymbol is NamedTypeSymbol { IsExtension: true }) + { + if (p.ContainingSymbol != (object)tp.ContainingSymbol) // Otherwise, SynthesizedExtensionMarker is going to report an error about this conflict + { + diagnostics.Add(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, GetLocation(p), name); + } + } + else if (p.ContainingSymbol is NamedTypeSymbol { IsExtension: true }) + { + diagnostics.Add(ErrorCode.ERR_TypeParameterSameNameAsExtensionParameter, tp.GetFirstLocationOrNone(), name); + } + else + { + // CS0412: 'X': a parameter or local variable cannot have the same name as a method type parameter + diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, GetLocation(p), name); + } } if (!pNames.Add(name)) { - // The parameter name '{0}' is a duplicate - diagnostics.Add(ErrorCode.ERR_DuplicateParamName, GetLocation(p), name); + if (parameters[0] is { ContainingSymbol: NamedTypeSymbol { IsExtension: true }, Name: var receiverName } && receiverName == name) + { + diagnostics.Add(ErrorCode.ERR_LocalSameNameAsExtensionParameter, GetLocation(p), name); + } + else + { + // The parameter name '{0}' is a duplicate + diagnostics.Add(ErrorCode.ERR_DuplicateParamName, GetLocation(p), name); + } } else if (!allowShadowingNames) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 860fbe3e56536..ce3304eef8da0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -1058,7 +1058,7 @@ deconstructMethod is null && if (deconstructMethod is null) hasErrors = true; - int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0; + int skippedExtensionParameters = deconstructMethod?.IsExtensionMethod == true ? 1 : 0; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions for (int i = 0; i < node.Subpatterns.Count; i++) { var subPattern = node.Subpatterns[i]; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 491a87033364c..d2cc0ca03080e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -676,7 +676,7 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind private static BoundExpression? ExtractCastInvocation(BoundCall invocation) { - int index = invocation.InvokedAsExtensionMethod ? 1 : 0; + int index = invocation.InvokedAsExtensionMethod ? 1 : 0; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Add test coverage for his code path var c1 = invocation.Arguments[index] as BoundConversion; var l1 = c1 != null ? c1.Operand as BoundLambda : null; var r1 = l1 != null ? l1.Body.Statements[0] as BoundReturnStatement : null; @@ -928,6 +928,8 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r Debug.Assert(receiver.Type is object || ultimateReceiver.Type is null); if ((object?)ultimateReceiver.Type == null) { + Debug.Assert(ultimateReceiver.Kind != BoundKind.MethodGroup || ultimateReceiver.HasAnyErrors); + if (ultimateReceiver.HasAnyErrors || node.HasErrors) { // report no additional errors @@ -953,24 +955,6 @@ protected BoundCall MakeQueryInvocation(CSharpSyntaxNode node, BoundExpression r // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_AnonMethod.Localize(), methodName); } - else if (ultimateReceiver.Kind == BoundKind.MethodGroup) - { - var methodGroup = (BoundMethodGroup)ultimateReceiver; - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - var resolution = this.ResolveMethodGroup(methodGroup, analyzedArguments: null, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.None); - diagnostics.Add(node, useSiteInfo); - diagnostics.AddRange(resolution.Diagnostics); - if (resolution.HasAnyErrors) - { - receiver = this.BindMemberAccessBadResult(methodGroup); - } - else - { - Debug.Assert(!resolution.IsEmpty); - diagnostics.Add(ErrorCode.ERR_QueryNoProvider, node.Location, MessageID.IDS_SK_METHOD.Localize(), methodName); - } - resolution.Free(); - } receiver = new BoundBadExpression(receiver.Syntax, LookupResultKind.NotAValue, ImmutableArray.Empty, ImmutableArray.Create(receiver), CreateErrorType()); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs b/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs index 9c61b375c3c41..f2b27b44c7a11 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs @@ -215,7 +215,7 @@ private static bool ReportQueryInferenceFailedSelectMany(FromClauseSyntax fromCl Debug.Assert(methodName == "SelectMany"); // Estimate the return type of Select's lambda argument - BoundExpression arg = arguments.Argument(arguments.IsExtensionMethodInvocation ? 1 : 0); + BoundExpression arg = arguments.Argument(arguments.IncludesReceiverAsArgument ? 1 : 0); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test this code path for new extensions TypeSymbol type = null; if (arg.Kind == BoundKind.UnboundLambda) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index cb1dbf25bbba8..535959ca12a0a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -766,7 +766,7 @@ internal MethodSymbol TryFindDisposePatternMethod(BoundExpression expr, SyntaxNo out var disposeMethod, out isExpanded); - if (disposeMethod?.IsExtensionMethod == true) + if (disposeMethod is not null && (disposeMethod.IsExtensionMethod || disposeMethod.GetIsNewExtensionMember())) { // Extension methods should just be ignored, rather than rejected after-the-fact // Tracked by https://github.com/dotnet/roslyn/issues/32767 @@ -1911,7 +1911,7 @@ private BoundBlock FinishBindBlockParts(CSharpSyntaxNode node, ImmutableArray.CastUp(GetDeclaredLocalFunctionsForScope(node)), hasUnsafeModifier: node.Parent?.Kind() == SyntaxKind.UnsafeStatement, instrumentation: null, boundStatements); @@ -3855,7 +3855,7 @@ internal virtual BoundExpressionStatement BindConstructorInitializer(Constructor } } - if (containingType.IsStructType() || containingType.IsEnumType()) + if (containingType.IsStructType() || containingType.IsEnumType() || containingType.IsExtension) { return null; } @@ -4153,7 +4153,9 @@ internal PatternLookupResult PerformPatternMethodLookup(BoundExpression receiver bindingDiagnostics, queryClause: null, ignoreNormalFormIfHasValidParamsParameter: true, - anyApplicableCandidates: out _); + anyApplicableCandidates: out _, + disallowExpandedNonArrayParams: false, + acceptOnlyMethods: true); analyzedArguments.Free(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 0961c7d0493c1..4dfb348e9845a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -338,16 +338,24 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindTypeOrAlias(ExpressionS } var diagnosticInfo = diagnostics.Add(ErrorCode.ERR_BadSKknown, syntax.Location, syntax, symbol.Symbol.GetKindText(), MessageID.IDS_SK_TYPE.Localize()); - return TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbol.Symbol), symbol.Symbol, LookupResultKind.NotATypeOrNamespace, diagnosticInfo)); + return TypeWithAnnotations.Create(new ExtendedErrorTypeSymbol(GetContainingNamespaceOrNonExtensionType(symbol.Symbol), symbol.Symbol, LookupResultKind.NotATypeOrNamespace, diagnosticInfo)); } /// /// The immediately containing namespace or named type, or the global /// namespace if containing symbol is neither a namespace or named type. + /// We don't want to use an extension declaration as the containing type + /// for error type symbols, as that causes cycles during symbol display. /// - private NamespaceOrTypeSymbol GetContainingNamespaceOrType(Symbol symbol) + private NamespaceOrTypeSymbol GetContainingNamespaceOrNonExtensionType(Symbol symbol) { - return symbol.ContainingNamespaceOrType() ?? this.Compilation.Assembly.GlobalNamespace; + if (symbol.ContainingNamespaceOrType() is { } containing + && containing is not TypeSymbol { IsExtension: true }) + { + return containing; + } + + return this.Compilation.Assembly.GlobalNamespace; } internal Symbol BindNamespaceAliasSymbol(IdentifierNameSyntax node, BindingDiagnosticBag diagnostics) @@ -1305,7 +1313,7 @@ private NamedTypeSymbol LookupGenericTypeName( // for us. Debug.Assert(lookupResult.Error != null); type = new ExtendedErrorTypeSymbol( - GetContainingNamespaceOrType(lookupResultSymbol), + GetContainingNamespaceOrNonExtensionType(lookupResultSymbol), ImmutableArray.Create(lookupResultSymbol), lookupResult.Kind, lookupResult.Error, @@ -1436,7 +1444,7 @@ private BoundMethodOrPropertyGroup ConstructBoundMemberGroupAndReportOmittedType BoundExpression colorColorValueReceiver = GetValueExpressionIfTypeOrValueReceiver(receiver); - Debug.Assert(colorColorValueReceiver is null || (methodGroupFlags & BoundMethodGroupFlags.SearchExtensionMethods) != 0); + Debug.Assert(colorColorValueReceiver is null || (methodGroupFlags & BoundMethodGroupFlags.SearchExtensions) != 0); if (IsPossiblyCapturingPrimaryConstructorParameterReference(colorColorValueReceiver, out ParameterSymbol parameter)) { @@ -1534,10 +1542,10 @@ private void CheckWhatCandidatesWeHave( if (!haveInstanceCandidates && members[0].Kind == SymbolKind.Method) { // See if there could be extension methods in scope - foreach (var scope in new ExtensionMethodScopes(this)) + foreach (var scope in new ExtensionScopes(this)) { lookupResult ??= LookupResult.GetInstance(); - LookupExtensionMethods(lookupResult, scope, plainName, arity, ref useSiteInfo); + LookupExtensionMethods(lookupResult, scope, plainName, arity, ref useSiteInfo); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : account for new extension members if (lookupResult.IsMultiViable) { @@ -2234,7 +2242,7 @@ Symbol resultSymbol( } return new ExtendedErrorTypeSymbol( - GetContainingNamespaceOrType(originalSymbols[0]), + GetContainingNamespaceOrNonExtensionType(originalSymbols[0]), originalSymbols, LookupResultKind.Ambiguous, info, @@ -2252,7 +2260,7 @@ Symbol resultSymbol( wasError = true; var errorInfo = new CSDiagnosticInfo(ErrorCode.ERR_SystemVoid); diagnostics.Add(errorInfo, where.Location); - singleResult = new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(singleResult), singleResult, LookupResultKind.NotReferencable, errorInfo); // UNDONE: Review resultkind. + singleResult = new ExtendedErrorTypeSymbol(GetContainingNamespaceOrNonExtensionType(singleResult), singleResult, LookupResultKind.NotReferencable, errorInfo); // UNDONE: Review resultkind. } // Check for bad symbol. else @@ -2285,7 +2293,7 @@ Symbol resultSymbol( { wasError = true; diagnostics.Add(errorInfo, where.Location); - singleResult = new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(errorType), errorType.Name, errorType.Arity, errorInfo, unreported: false); + singleResult = new ExtendedErrorTypeSymbol(GetContainingNamespaceOrNonExtensionType(errorType), errorType.Name, errorType.Arity, errorInfo, unreported: false); } } } @@ -2341,7 +2349,7 @@ Symbol resultSymbol( // Bad type or namespace (or things expected as types/namespaces) are packaged up as error types, preserving the symbols and the result kind. // We do this if there are multiple symbols too, because just returning one would be losing important information, and they might // be of different kinds. - return new ExtendedErrorTypeSymbol(GetContainingNamespaceOrType(symbols[0]), symbols.ToImmutable(), result.Kind, result.Error, arity); + return new ExtendedErrorTypeSymbol(GetContainingNamespaceOrNonExtensionType(symbols[0]), symbols.ToImmutable(), result.Kind, result.Error, arity); } else { diff --git a/src/Compilers/CSharp/Portable/Binder/ExtensionMethodScope.cs b/src/Compilers/CSharp/Portable/Binder/ExtensionScope.cs similarity index 60% rename from src/Compilers/CSharp/Portable/Binder/ExtensionMethodScope.cs rename to src/Compilers/CSharp/Portable/Binder/ExtensionScope.cs index 68057cd4b0406..8fa6adb3db599 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExtensionMethodScope.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExtensionScope.cs @@ -7,54 +7,54 @@ namespace Microsoft.CodeAnalysis.CSharp { /// - /// A distinct scope that may expose extension methods. For a particular Binder, there + /// A distinct scope that may expose extensions. For a particular Binder, there /// are two possible scopes: one for the namespace, and another for any using statements /// in the namespace. The namespace scope is searched before the using scope. /// - internal readonly struct ExtensionMethodScope + internal readonly struct ExtensionScope { public readonly Binder Binder; - public ExtensionMethodScope(Binder binder) + public ExtensionScope(Binder binder) { this.Binder = binder; } } /// - /// An enumerable collection of extension method scopes in search + /// An enumerable collection of extension scopes in search /// order, from the given Binder, out through containing Binders. /// - internal readonly struct ExtensionMethodScopes + internal readonly struct ExtensionScopes { private readonly Binder _binder; - public ExtensionMethodScopes(Binder binder) + public ExtensionScopes(Binder binder) { _binder = binder; } - public ExtensionMethodScopeEnumerator GetEnumerator() + public ExtensionScopeEnumerator GetEnumerator() { - return new ExtensionMethodScopeEnumerator(_binder); + return new ExtensionScopeEnumerator(_binder); } } /// - /// An enumerator over ExtensionMethodScopes. + /// An enumerator over ExtensionScopes. /// - internal struct ExtensionMethodScopeEnumerator + internal struct ExtensionScopeEnumerator { private readonly Binder _binder; - private ExtensionMethodScope _current; + private ExtensionScope _current; - public ExtensionMethodScopeEnumerator(Binder binder) + public ExtensionScopeEnumerator(Binder binder) { _binder = binder; - _current = new ExtensionMethodScope(); + _current = new ExtensionScope(); } - public ExtensionMethodScope Current + public ExtensionScope Current { get { return _current; } } @@ -68,24 +68,24 @@ public bool MoveNext() else { var binder = _current.Binder; - // Return a scope for the next Binder that supports extension methods. + // Return a scope for the next Binder that supports extensions. _current = GetNextScope(binder.Next); } return (_current.Binder != null); } - private static ExtensionMethodScope GetNextScope(Binder binder) + private static ExtensionScope GetNextScope(Binder binder) { for (var scope = binder; scope != null; scope = scope.Next) { - if (scope.SupportsExtensionMethods) + if (scope.SupportsExtensions) { - return new ExtensionMethodScope(scope); + return new ExtensionScope(scope); } } - return new ExtensionMethodScope(); + return new ExtensionScope(); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 368065f181463..aa73b0ff7ec40 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -236,12 +236,20 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno { originalBinder.CheckImplicitThisCopyInReadOnlyMember(collectionExpr, getEnumeratorMethod, diagnostics); - if (getEnumeratorMethod.IsExtensionMethod && !hasErrors) + if (!hasErrors) { - var messageId = IsAsync ? MessageID.IDS_FeatureExtensionGetAsyncEnumerator : MessageID.IDS_FeatureExtensionGetEnumerator; - messageId.CheckFeatureAvailability(diagnostics, Compilation, collectionExpr.Syntax.Location); + if (getEnumeratorMethod.IsExtensionMethod) + { + var messageId = IsAsync ? MessageID.IDS_FeatureExtensionGetAsyncEnumerator : MessageID.IDS_FeatureExtensionGetEnumerator; + messageId.CheckFeatureAvailability(diagnostics, Compilation, collectionExpr.Syntax.Location); - if (getEnumeratorMethod.ParameterRefKinds is { IsDefault: false } refKinds && refKinds[0] == RefKind.Ref) + if (getEnumeratorMethod.ParameterRefKinds is { IsDefault: false } refKinds && refKinds[0] == RefKind.Ref) + { + Error(diagnostics, ErrorCode.ERR_RefLvalueExpected, collectionExpr.Syntax); + hasErrors = true; + } + } + else if (getEnumeratorMethod.GetIsNewExtensionMember() && getEnumeratorMethod.ContainingType.ExtensionParameter.RefKind == RefKind.Ref) // Tracked by https://github.com/dotnet/roslyn/issues/76130: add test coverage for 'ref readonly' and 'in' { Error(diagnostics, ErrorCode.ERR_RefLvalueExpected, collectionExpr.Syntax); hasErrors = true; @@ -570,7 +578,8 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno (collectionConversionClassification.IsImplicit && (IsIEnumerable(builder.CollectionType) || IsIEnumerableT(builder.CollectionType.OriginalDefinition, IsAsync, Compilation) || - builder.GetEnumeratorInfo.Method.IsExtensionMethod)) || + builder.GetEnumeratorInfo.Method.IsExtensionMethod || + builder.GetEnumeratorInfo.Method.GetIsNewExtensionMember())) || // For compat behavior, we can enumerate over System.String even if it's not IEnumerable. That will // result in an explicit reference conversion in the bound nodes, but that conversion won't be emitted. (collectionConversionClassification.Kind == ConversionKind.ExplicitReference && collectionExpr.Type.SpecialType == SpecialType.System_String)); @@ -861,9 +870,9 @@ private EnumeratorResult GetEnumeratorInfoCore(SyntaxNode syntax, SyntaxNode col #if DEBUG Debug.Assert(span == originalSpan); - Debug.Assert(!builder.ViaExtensionMethod || builder.GetEnumeratorInfo.Method.IsExtensionMethod); + Debug.Assert(!builder.ViaExtensionMethod || builder.GetEnumeratorInfo.Method.IsExtensionMethod || builder.GetEnumeratorInfo.Method.GetIsNewExtensionMember()); #endif - if (!builder.ViaExtensionMethod && + if (!builder.ViaExtensionMethod && // Tracked by https://github.com/dotnet/roslyn/issues/76130: Add test coverage for new extensions ((result is EnumeratorResult.Succeeded && builder.ElementTypeWithAnnotations.Equals(elementField.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions) && builder.CurrentPropertyGetter?.RefKind == (wellKnownSpan == WellKnownType.System_ReadOnlySpan_T ? RefKind.RefReadOnly : RefKind.Ref)) || result is EnumeratorResult.FailedAndReported)) @@ -908,7 +917,7 @@ private EnumeratorResult GetEnumeratorInfoCore(SyntaxNode syntax, SyntaxNode col #if DEBUG Debug.Assert(collectionExpr == originalCollectionExpr || (originalCollectionExpr.Type?.IsNullableType() == true && originalCollectionExpr.Type.StrippedType().Equals(collectionExpr.Type, TypeCompareKind.AllIgnoreOptions))); - Debug.Assert(!builder.ViaExtensionMethod || builder.GetEnumeratorInfo.Method.IsExtensionMethod); + Debug.Assert(!builder.ViaExtensionMethod || builder.GetEnumeratorInfo.Method.IsExtensionMethod || builder.GetEnumeratorInfo.Method.GetIsNewExtensionMember()); #endif return result; @@ -1019,12 +1028,26 @@ EnumeratorResult createPatternBasedEnumeratorResult(ref ForEachEnumeratorInfo.Bu { Debug.Assert((object)builder.GetEnumeratorInfo != null); - Debug.Assert(!(viaExtensionMethod && builder.GetEnumeratorInfo.Method.Parameters.IsDefaultOrEmpty)); + Debug.Assert(!(viaExtensionMethod && builder.GetEnumeratorInfo.Method.IsExtensionMethod && builder.GetEnumeratorInfo.Method.Parameters.IsDefaultOrEmpty)); + Debug.Assert(!(viaExtensionMethod && !builder.GetEnumeratorInfo.Method.IsExtensionMethod && !builder.GetEnumeratorInfo.Method.GetIsNewExtensionMember())); builder.ViaExtensionMethod = viaExtensionMethod; - builder.CollectionType = viaExtensionMethod - ? builder.GetEnumeratorInfo.Method.Parameters[0].Type - : collectionExpr.Type; + + if (viaExtensionMethod) + { + if (builder.GetEnumeratorInfo.Method.IsExtensionMethod) + { + builder.CollectionType = builder.GetEnumeratorInfo.Method.Parameters[0].Type; + } + else + { + builder.CollectionType = builder.GetEnumeratorInfo.Method.ContainingType.ExtensionParameter.Type; + } + } + else + { + builder.CollectionType = collectionExpr.Type; + } if (SatisfiesForEachPattern(syntax, collectionSyntax, ref builder, isAsync, diagnostics)) { @@ -1200,7 +1223,7 @@ private void GetDisposalInfoForEnumerator(SyntaxNode syntax, ref ForEachEnumerat MethodSymbol patternDisposeMethod = TryFindDisposePatternMethod(receiver, syntax, isAsync, patternDiagnostics, out bool expanded); if (patternDisposeMethod is object) { - Debug.Assert(!patternDisposeMethod.IsExtensionMethod); + Debug.Assert(!patternDisposeMethod.IsExtensionMethod && !patternDisposeMethod.GetIsNewExtensionMember()); Debug.Assert(patternDisposeMethod.ParameterRefKinds.IsDefaultOrEmpty || patternDisposeMethod.ParameterRefKinds.All(static refKind => refKind is RefKind.None or RefKind.In or RefKind.RefReadOnlyParameter)); @@ -1500,7 +1523,9 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(SyntaxNode synta { var analyzedArguments = AnalyzedArguments.GetInstance(); - var methodGroupResolutionResult = this.BindExtensionMethod( + CompoundUseSiteInfo extensionUseSiteInfo = this.GetNewCompoundUseSiteInfo(diagnostics); + + var methodGroupResolutionResult = this.ResolveExtension( collectionSyntax, methodName, analyzedArguments, @@ -1509,8 +1534,10 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(SyntaxNode synta options: OverloadResolution.Options.None, returnRefKind: default, returnType: null, - withDependencies: diagnostics.AccumulatesDependencies); + ref extensionUseSiteInfo, + acceptOnlyMethods: true); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test effect of acceptOnlyMethods value + diagnostics.Add(syntax, extensionUseSiteInfo); diagnostics.AddRange(methodGroupResolutionResult.Diagnostics); var overloadResolutionResult = methodGroupResolutionResult.OverloadResolutionResult; @@ -1518,6 +1545,8 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(SyntaxNode synta { var result = overloadResolutionResult.ValidResult.Member; + Debug.Assert(result.IsExtensionMethod || result.GetIsNewExtensionMember()); + if (result.CallsAreOmitted(syntax.SyntaxTree)) { // Calls to this method are omitted in the current syntax tree, i.e it is either a partial method with no implementation part OR a conditional method whose condition is not true in this source file. @@ -1527,28 +1556,44 @@ private MethodArgumentInfo FindForEachPatternMethodViaExtension(SyntaxNode synta return null; } - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); - var collectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, result.Parameters[0].Type, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); - diagnostics.Add(syntax, useSiteInfo); + MethodArgumentInfo info; + bool expanded = overloadResolutionResult.ValidResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm; - // Unconditionally convert here, to match what we set the ConvertedExpression to in the main BoundForEachStatement node. - Debug.Assert(!collectionConversion.IsUserDefined); - collectionExpr = new BoundConversion( - collectionExpr.Syntax, - collectionExpr, - collectionConversion, - @checked: CheckOverflowAtRuntime, - explicitCastInCode: false, - conversionGroupOpt: null, - ConstantValue.NotAvailable, - result.Parameters[0].Type); + if (result.IsExtensionMethod) + { + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + var collectionConversion = this.Conversions.ClassifyConversionFromExpression(collectionExpr, result.Parameters[0].Type, isChecked: CheckOverflowAtRuntime, ref useSiteInfo); + diagnostics.Add(syntax, useSiteInfo); + + // Unconditionally convert here, to match what we set the ConvertedExpression to in the main BoundForEachStatement node. + Debug.Assert(!collectionConversion.IsUserDefined); + collectionExpr = new BoundConversion( + collectionExpr.Syntax, + collectionExpr, + collectionConversion, + @checked: CheckOverflowAtRuntime, + explicitCastInCode: false, + conversionGroupOpt: null, + ConstantValue.NotAvailable, + result.Parameters[0].Type); + + info = BindDefaultArguments( + result, + collectionExpr, + expanded: expanded, + collectionExpr.Syntax, + diagnostics); + } + else + { + info = BindDefaultArguments( + result, + extensionReceiverOpt: null, + expanded: expanded, + collectionExpr.Syntax, + diagnostics); + } - var info = BindDefaultArguments( - result, - collectionExpr, - expanded: overloadResolutionResult.ValidResult.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm, - collectionExpr.Syntax, - diagnostics); methodGroupResolutionResult.Free(); analyzedArguments.Free(); return info; diff --git a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs index d5f45168cd481..6e23054365498 100644 --- a/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InContainerBinder.cs @@ -67,7 +67,7 @@ internal override bool IsAccessibleHelper(Symbol symbol, TypeSymbol accessThroug } } - internal override bool SupportsExtensionMethods + internal override bool SupportsExtensions { get { return true; } } @@ -85,6 +85,14 @@ internal override void GetCandidateExtensionMethods( } } + internal override void GetExtensionDeclarations(ArrayBuilder extensions, Binder originalBinder) + { + if (_container is NamespaceSymbol ns) + { + ns.GetExtensionContainers(extensions); + } + } + internal override TypeWithAnnotations GetIteratorElementType() { if (IsScriptClass) diff --git a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs index c2e47a7daeadd..b835e700c620f 100644 --- a/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/InMethodBinder.cs @@ -292,8 +292,16 @@ private static bool ReportConflictWithParameter(Symbol parameter, Symbol newSymb { case SymbolKind.Parameter: case SymbolKind.Local: - // CS0412: '{0}': a parameter, local variable, or local function cannot have the same name as a method type parameter - diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, newLocation, name); + if (parameter.ContainingSymbol is NamedTypeSymbol { IsExtension: true }) + { + diagnostics.Add(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, newLocation, name); + } + else + { + // CS0412: '{0}': a parameter, local variable, or local function cannot have the same name as a method type parameter + diagnostics.Add(ErrorCode.ERR_LocalSameNameAsTypeParam, newLocation, name); + } + return true; case SymbolKind.Method: @@ -324,6 +332,16 @@ internal override bool EnsureSingleDefinition(Symbol symbol, string name, Locati var parameters = _methodSymbol.Parameters; var typeParameters = _methodSymbol.TypeParameters; + if (_methodSymbol.GetIsNewExtensionMember()) + { + typeParameters = _methodSymbol.ContainingType.TypeParameters.Concat(typeParameters); + + if (_methodSymbol.ContainingType.ExtensionParameter is { Name: not "" } receiver) + { + parameters = parameters.Insert(0, receiver); + } + } + if (parameters.IsEmpty && typeParameters.IsEmpty) { return false; diff --git a/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs b/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs index e85082a4068e2..1334d27dd953d 100644 --- a/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs +++ b/src/Compilers/CSharp/Portable/Binder/LookupOptions.cs @@ -75,9 +75,9 @@ internal enum LookupOptions UseBaseReferenceAccessibility = 1 << 9, /// - /// Include extension methods. + /// Include extension members. /// - IncludeExtensionMethods = 1 << 10, + IncludeExtensionMembers = 1 << 10, /// /// Consider only attribute types. diff --git a/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs b/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs index de0be66599870..4735b08633d50 100644 --- a/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/MethodGroupResolution.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; @@ -42,6 +43,7 @@ public MethodGroupResolution( LookupResultKind resultKind, ReadOnlyBindingDiagnostic diagnostics) { + Debug.Assert((methodGroup != null) || ((object)otherSymbol != null) || analyzedArguments == null); // analyzedArguments is only set if we have some result Debug.Assert((methodGroup == null) || (methodGroup.Methods.Count > 0)); Debug.Assert((methodGroup == null) || ((object)otherSymbol == null)); // Methods should be represented in the method group. @@ -83,13 +85,31 @@ public bool IsExtensionMethodGroup get { return (this.MethodGroup != null) && this.MethodGroup.IsExtensionMethodGroup; } } +#nullable enable + /// + /// Indicates that we have a viable result that is a non-method extension member. + /// + public bool IsNonMethodExtensionMember([NotNullWhen(true)] out Symbol? extensionMember) + { + bool isExtensionMember = ResultKind == LookupResultKind.Viable && MethodGroup is null; + extensionMember = isExtensionMember ? OtherSymbol : null; + Debug.Assert((extensionMember is not null) || !isExtensionMember); + + return isExtensionMember; + } +#nullable disable + public bool IsLocalFunctionInvocation => MethodGroup?.Methods.Count == 1 && // Local functions cannot be overloaded MethodGroup.Methods[0].MethodKind == MethodKind.LocalFunction; - public void Free() + public void Free(bool keepArguments = false) { - this.AnalyzedArguments?.Free(); + if (!keepArguments) + { + this.AnalyzedArguments?.Free(); + } + this.MethodGroup?.Free(); this.OverloadResolutionResult?.Free(); } diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 18f4e327cb9ea..e43b21e2619a0 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -308,7 +308,7 @@ private void AssertVisited(BoundExpression expr) public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { - var localFunction = node.Symbol; + var localFunction = (LocalFunctionSymbol)node.Symbol; var analysis = new RefSafetyAnalysis(_compilation, localFunction, _inUnsafeRegion || localFunction.IsUnsafe, _useUpdatedEscapeRules, _diagnostics); analysis.Visit(node.BlockBody); analysis.Visit(node.ExpressionBody); @@ -604,7 +604,7 @@ static SafeContext getDeclarationValEscape(BoundTypeExpression typeExpression, S static ParameterSymbol? tryGetThisParameter(MethodSymbol method) { - if (method.IsExtensionMethod) + if (method.IsExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { return method.Parameters is [{ } firstParameter, ..] ? firstParameter : null; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs index 2715fc9adefe4..1aaa9522d014d 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs @@ -327,8 +327,9 @@ private static bool IsMemberAccessible( return true; } + // For the purpose of accessibility checks, extension members are considered to be declared within the enclosing static type return IsNonPublicMemberAccessible( - containingType, + containingType.IsExtension && containingType.ContainingType is { } extensionEnclosingType ? extensionEnclosingType : containingType, declaredAccessibility, within, throughTypeOpt, diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs index abb608ffe3b20..54b21617e4c99 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversions.cs @@ -102,6 +102,8 @@ public override Conversion GetMethodGroupDelegateConversion(BoundMethodGroup sou } var resolution = ResolveDelegateOrFunctionPointerMethodGroup(_binder, source, methodSymbol, isFunctionPointer, callingConventionInfo, ref useSiteInfo); + Debug.Assert(!resolution.IsNonMethodExtensionMember(out _)); + var conversion = (resolution.IsEmpty || resolution.HasAnyErrors) ? Conversion.NoConversion : ToConversion(resolution.OverloadResolutionResult, resolution.MethodGroup, methodSymbol.ParameterCount); @@ -248,22 +250,25 @@ internal Conversion GetCollectionExpressionSpreadElementConversion( /// private static MethodGroupResolution ResolveDelegateOrFunctionPointerMethodGroup(Binder binder, BoundMethodGroup source, MethodSymbol delegateInvokeMethodOpt, bool isFunctionPointer, in CallingConventionInfo callingConventionInfo, ref CompoundUseSiteInfo useSiteInfo) { + MethodGroupResolution resolution; if ((object)delegateInvokeMethodOpt != null) { var analyzedArguments = AnalyzedArguments.GetInstance(); GetDelegateOrFunctionPointerArguments(source.Syntax, analyzedArguments, delegateInvokeMethodOpt.Parameters, binder.Compilation); - var resolution = binder.ResolveMethodGroup(source, analyzedArguments, useSiteInfo: ref useSiteInfo, + resolution = binder.ResolveMethodGroup(source, analyzedArguments, useSiteInfo: ref useSiteInfo, options: OverloadResolution.Options.InferWithDynamic | OverloadResolution.Options.IsMethodGroupConversion | (isFunctionPointer ? OverloadResolution.Options.IsFunctionPointerResolution : OverloadResolution.Options.None), returnRefKind: delegateInvokeMethodOpt.RefKind, returnType: delegateInvokeMethodOpt.ReturnType, callingConventionInfo: callingConventionInfo); analyzedArguments.Free(); - return resolution; } else { - return binder.ResolveMethodGroup(source, analyzedArguments: null, ref useSiteInfo, options: OverloadResolution.Options.IsMethodGroupConversion); + resolution = binder.ResolveMethodGroup(source, analyzedArguments: null, ref useSiteInfo, options: OverloadResolution.Options.IsMethodGroupConversion); } + + Debug.Assert(!resolution.IsNonMethodExtensionMember(out _)); + return resolution; } /// @@ -325,10 +330,24 @@ public static bool ReportDelegateOrFunctionPointerMethodGroupDiagnostics(Binder Debug.Assert((object)method != null); if (resolution.MethodGroup.IsExtensionMethodGroup) { - Debug.Assert(method.IsExtensionMethod); + Debug.Assert(method.IsExtensionMethod || method.GetIsNewExtensionMember()); + + ParameterSymbol thisParameter; + + if (method.IsExtensionMethod) + { + thisParameter = method.Parameters[0]; + } + else if (method.IsStatic) + { + thisParameter = null; + } + else + { + thisParameter = method.ContainingType.ExtensionParameter; + } - var thisParameter = method.Parameters[0]; - if (!thisParameter.Type.IsReferenceType) + if (thisParameter?.Type.IsReferenceType == false) { // Extension method '{0}' defined on value type '{1}' cannot be used to create delegates diagnostics.Add( @@ -449,9 +468,12 @@ private static Conversion ToConversion(OverloadResolutionResult re MethodSymbol method = result.BestResult.Member; - if (methodGroup.IsExtensionMethodGroup && !method.Parameters[0].Type.IsReferenceType) + if (methodGroup.IsExtensionMethodGroup) { - return Conversion.NoConversion; + if (!(method.GetIsNewExtensionMember() && method.IsStatic) && !Binder.GetReceiverParameter(method).Type.IsReferenceType) + { + return Conversion.NoConversion; + } } //cannot capture stack-only types. @@ -476,9 +498,10 @@ private static Conversion ToConversion(OverloadResolutionResult re // NOTE: Delegate type compatibility is important, but is not part of the existence check. - Debug.Assert(method.ParameterCount == parameterCount + (methodGroup.IsExtensionMethodGroup ? 1 : 0)); + bool isExtensionMethod = methodGroup.IsExtensionMethodGroup && !method.GetIsNewExtensionMember(); + Debug.Assert(method.ParameterCount == parameterCount + (isExtensionMethod ? 1 : 0)); - return new Conversion(ConversionKind.MethodGroup, method, methodGroup.IsExtensionMethodGroup); + return new Conversion(ConversionKind.MethodGroup, method, isExtensionMethod: isExtensionMethod); } public override Conversion GetStackAllocConversion(BoundStackAllocArrayCreation sourceExpression, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs index 84cfad7da7317..14d5796dfbdfb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/AnalyzedArguments.cs @@ -15,7 +15,7 @@ internal sealed class AnalyzedArguments public readonly ArrayBuilder Arguments; public readonly ArrayBuilder<(string Name, Location Location)?> Names; public readonly ArrayBuilder RefKinds; - public bool IsExtensionMethodInvocation; + public bool IncludesReceiverAsArgument; private ThreeState _lazyHasDynamicArgument; internal AnalyzedArguments() @@ -30,7 +30,7 @@ public void Clear() this.Arguments.Clear(); this.Names.Clear(); this.RefKinds.Clear(); - this.IsExtensionMethodInvocation = false; + this.IncludesReceiverAsArgument = false; _lazyHasDynamicArgument = ThreeState.Unknown; } @@ -78,9 +78,9 @@ public RefKind RefKind(int i) return RefKinds.Count > 0 ? RefKinds[i] : Microsoft.CodeAnalysis.RefKind.None; } - public bool IsExtensionMethodThisArgument(int i) + public bool IsExtensionMethodReceiverArgument(int i) { - return (i == 0) && this.IsExtensionMethodInvocation; + return (i == 0) && this.IncludesReceiverAsArgument; } public bool HasDynamicArgument @@ -139,7 +139,7 @@ public static AnalyzedArguments GetInstance(AnalyzedArguments original) instance.Arguments.AddRange(original.Arguments); instance.Names.AddRange(original.Names); instance.RefKinds.AddRange(original.RefKinds); - instance.IsExtensionMethodInvocation = original.IsExtensionMethodInvocation; + instance.IncludesReceiverAsArgument = original.IncludesReceiverAsArgument; instance._lazyHasDynamicArgument = original._lazyHasDynamicArgument; return instance; } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs index d60aa1ae31254..3ee43e4999231 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MemberAnalysisResult.cs @@ -4,11 +4,13 @@ #nullable disable +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -412,5 +414,54 @@ public void ArgumentsWereCoerced() _argumentsCoerced = true; #endif } + + internal MemberAnalysisResult WithoutReceiverArgument() + { + var badArguments = shift(BadArgumentsOpt); + var argsToParams = adjustArgsToParams(ArgsToParamsOpt); + var conversions = adjustConversions(ConversionsOpt); + + // We only need HasAnyRefOmittedArgument as part of overload resolution, so we don't need to adjust it post-overload resolution. + return new MemberAnalysisResult(Kind, badArguments, argsToParams, conversions, BadParameter - 1, HasAnyRefOmittedArgument, + ConstraintFailureDiagnostics, DefinitionParamsElementTypeOpt, ParamsElementTypeOpt); + + static BitVector shift(BitVector badArguments) + { + if (badArguments.IsNull) + { + return badArguments; + } + + var result = BitVector.Create(badArguments.Capacity); + foreach (int setIndex in badArguments.TrueBits()) + { + result[setIndex] = true; + } + + return result; + } + + static ImmutableArray adjustArgsToParams(ImmutableArray argsToParams) + { + if (argsToParams.IsDefault) + { + return argsToParams; + } + + var builder = ArrayBuilder.GetInstance(); + builder.AddRange(argsToParams, 1, argsToParams.Length - 1); + for (int i = 0; i < builder.Count; i++) + { + builder[i]--; + } + + return builder.ToImmutableAndFree(); + } + + static ImmutableArray adjustConversions(ImmutableArray conversions) + { + return conversions.IsDefaultOrEmpty ? conversions : conversions.RemoveAt(0); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodGroup.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodGroup.cs index 21be92c0c74ad..a174f27288b11 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodGroup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodGroup.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -47,10 +46,16 @@ internal void PopulateWithExtensionMethods( { this.PopulateHelper(receiverOpt, resultKind, error); this.IsExtensionMethodGroup = true; + foreach (var member in members) { - this.Methods.Add((MethodSymbol)member); + if (member is MethodSymbol method) + { + Debug.Assert(method.IsExtensionMethod || method.GetIsNewExtensionMember()); + this.Methods.Add(method); + } } + if (!typeArguments.IsDefault) { this.TypeArguments.AddRange(typeArguments); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs index 0d151943476d8..7d463db11cca4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs @@ -138,6 +138,10 @@ private enum Dependency private readonly ImmutableArray _arguments; private readonly Extensions _extensions; + // When doing type inference on a new extension method, we combine the type parameters + // from the extension declaration and from the method, so we cannot rely on the ordinals from the type parameters. + private readonly Dictionary _ordinals; + private readonly (TypeWithAnnotations Type, bool FromFunctionType)[] _fixedResults; private readonly HashSet[] _exactBounds; private readonly HashSet[] _upperBounds; @@ -272,7 +276,8 @@ public static MethodTypeInferenceResult Infer( ImmutableArray arguments,// Required; in scenarios like method group conversions where there are // no arguments per se we cons up some fake arguments. ref CompoundUseSiteInfo useSiteInfo, - Extensions extensions = null) + Extensions extensions = null, + Dictionary ordinals = null) { Debug.Assert(!methodTypeParameters.IsDefault); Debug.Assert(methodTypeParameters.Length > 0); @@ -298,7 +303,8 @@ public static MethodTypeInferenceResult Infer( formalParameterTypes, formalParameterRefKinds, arguments, - extensions); + extensions, + ordinals); return inferrer.InferTypeArgs(binder, ref useSiteInfo); } @@ -311,6 +317,7 @@ public static MethodTypeInferenceResult Infer( // SPEC: the bounds is of some type T. Initially each type parameter is unfixed // SPEC: with an empty set of bounds. +#nullable enable private MethodTypeInferrer( CSharpCompilation compilation, ConversionsBase conversions, @@ -319,7 +326,8 @@ private MethodTypeInferrer( ImmutableArray formalParameterTypes, ImmutableArray formalParameterRefKinds, ImmutableArray arguments, - Extensions extensions) + Extensions extensions, + Dictionary? ordinals) { _compilation = compilation; _conversions = conversions; @@ -329,6 +337,12 @@ private MethodTypeInferrer( _formalParameterRefKinds = formalParameterRefKinds; _arguments = arguments; _extensions = extensions ?? Extensions.Default; + + // For extension members, we do inference across all the type parameters (from the extension declaration and the member) + Debug.Assert(ordinals is null || ordinals.Values.Count() == ordinals.Values.Distinct().Count()); + Debug.Assert(ordinals is null || methodTypeParameters.All(tp => ordinals.ContainsKey(tp))); + + _ordinals = ordinals; _fixedResults = new (TypeWithAnnotations, bool)[methodTypeParameters.Length]; _exactBounds = new HashSet[methodTypeParameters.Length]; _upperBounds = new HashSet[methodTypeParameters.Length]; @@ -338,6 +352,7 @@ private MethodTypeInferrer( _dependencies = null; _dependenciesDirty = false; } +#nullable enable #if DEBUG @@ -488,12 +503,22 @@ private bool IsUnfixedTypeParameter(TypeWithAnnotations type) if (type.TypeKind != TypeKind.TypeParameter) return false; TypeParameterSymbol typeParameter = (TypeParameterSymbol)type.Type; - int ordinal = typeParameter.Ordinal; + int ordinal = GetOrdinal(typeParameter); return ValidIndex(ordinal) && TypeSymbol.Equals(typeParameter, _methodTypeParameters[ordinal], TypeCompareKind.ConsiderEverything2) && IsUnfixed(ordinal); } + private int GetOrdinal(TypeParameterSymbol typeParameter) + { + if (_ordinals != null) + { + return _ordinals[typeParameter]; + } + + return typeParameter.Ordinal; + } + private bool AllFixed() { for (int methodTypeParameterIndex = 0; methodTypeParameterIndex < _methodTypeParameters.Length; ++methodTypeParameterIndex) @@ -511,7 +536,7 @@ private void AddBound(TypeWithAnnotations addedBound, HashSet + /// We apply type inference to an extension type, using the receiver as argument against the + /// extension parameter. + /// This lets us infer the type arguments of the extension type given this receiver. + /// + public static ImmutableArray InferTypeArgumentsFromReceiverType( + NamedTypeSymbol extension, + BoundExpression receiver, + CSharpCompilation compilation, + ConversionsBase conversions, + ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(extension is not null); + Debug.Assert(extension.Arity > 0); + Debug.Assert(extension.ExtensionParameter is not null); + Debug.Assert(!extension.ExtensionParameter.Type.IsDynamic()); + Debug.Assert(extension.IsDefinition); + Debug.Assert(receiver is not null); + + var inferrer = new MethodTypeInferrer( + compilation, + conversions, + extension.TypeParameters, + extension.ContainingType, + [extension.ExtensionParameter.TypeWithAnnotations], + [extension.ExtensionParameter.RefKind], + [receiver], + extensions: null, + ordinals: null); + + if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteInfo)) + { + return default; + } + + return inferrer.GetInferredTypeArguments(out _); + } + //////////////////////////////////////////////////////////////////////////////// // // In error recovery and reporting scenarios we sometimes end up in a situation @@ -3216,7 +3281,8 @@ public static bool CanInferTypeArgumentsFromFirstArgument( constructedFromMethod.GetParameterTypes(), constructedFromMethod.ParameterRefKinds, arguments, - extensions: null); + extensions: null, + ordinals: null); if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteInfo)) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs index 0a9030c48f85d..bfb102af0fbe7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution.cs @@ -446,7 +446,7 @@ private void PerformMemberOverloadResolution( if (Compilation.LanguageVersion.AllowImprovedOverloadCandidates()) { - RemoveStaticInstanceMismatches(results, arguments, receiver); + RemoveStaticInstanceMismatches(results, receiver); RemoveConstraintViolations(results, template: new CompoundUseSiteInfo(useSiteInfo)); @@ -578,15 +578,13 @@ internal void FunctionPointerOverloadResolution( private void RemoveStaticInstanceMismatches( ArrayBuilder> results, - AnalyzedArguments arguments, BoundExpression receiverOpt) where TMember : Symbol { // When the feature 'ImprovedOverloadCandidates' is enabled, we do not include instance members when the receiver - // is a type, or static members when the receiver is an instance. This does not apply to extension method invocations, - // because extension methods are only considered when the receiver is an instance. It also does not apply when the + // is a type, or static members when the receiver is an instance. This does not apply when the // receiver is a TypeOrValueExpression, which is used to handle the receiver of a Color-Color ambiguity, where either // an instance or a static member would be acceptable. - if (arguments.IsExtensionMethodInvocation || Binder.IsTypeOrValueExpression(receiverOpt)) + if (Binder.IsTypeOrValueExpression(receiverOpt)) { return; } @@ -612,6 +610,11 @@ private static void RemoveStaticInstanceMismatches(ArrayBuilder(ArrayBuilder(ArrayBuilder> results, CompoundUseSiteInfo template) where TMember : Symbol { // When the feature 'ImprovedOverloadCandidates' is enabled, we do not include methods for which the type arguments - // violate the constraints of the method's type parameters. - - // Constraint violations apply to method in a method group, not to properties in a "property group". - if (typeof(TMember) != typeof(MethodSymbol)) - { - return; - } + // violate the constraints of the member's type parameters. for (int f = 0; f < results.Count; ++f) { var result = results[f]; - var member = (MethodSymbol)(Symbol)result.Member; + var member = result.Member; // a constraint failure on the method trumps (for reporting purposes) a previously-detected // constraint failure on the constructed type of a parameter if ((result.Result.IsValid || result.Result.Kind == MemberResolutionKind.ConstructedParameterFailedConstraintCheck) && @@ -791,9 +788,10 @@ static MemberResolutionResult makeWrongCallingConvention(MemberResoluti } #nullable disable - private bool FailsConstraintChecks(MethodSymbol method, out ArrayBuilder constraintFailureDiagnosticsOpt, CompoundUseSiteInfo template) + private bool FailsConstraintChecks(TMember member, out ArrayBuilder constraintFailureDiagnosticsOpt, CompoundUseSiteInfo template) where TMember : Symbol { - if (method.Arity == 0 || method.OriginalDefinition == (object)method) + int arity = member.GetMemberArityIncludingExtension(); + if (arity == 0 || member.OriginalDefinition == (object)member) { constraintFailureDiagnosticsOpt = null; return false; @@ -801,12 +799,25 @@ private bool FailsConstraintChecks(MethodSymbol method, out ArrayBuilder.GetInstance(); ArrayBuilder useSiteDiagnosticsBuilder = null; - bool constraintsSatisfied = ConstraintsHelper.CheckMethodConstraints( - method, - new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, includeNullability: false, location: NoLocation.Singleton, diagnostics: null, template), - diagnosticsBuilder, - nullabilityDiagnosticsBuilderOpt: null, - ref useSiteDiagnosticsBuilder); + var constraintsArgs = new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, includeNullability: false, location: NoLocation.Singleton, diagnostics: null, template); + + bool constraintsSatisfied = true; + if (member is MethodSymbol method && method.Arity > 0) + { + constraintsSatisfied &= ConstraintsHelper.CheckMethodConstraints( + method, + constraintsArgs, + diagnosticsBuilder, + nullabilityDiagnosticsBuilderOpt: null, + ref useSiteDiagnosticsBuilder); + } + + if (member.GetIsNewExtensionMember() && member.ContainingType is { } extension && ConstraintsHelper.RequiresChecking(extension)) + { + constraintsSatisfied &= ConstraintsHelper.CheckConstraints(extension, in constraintsArgs, + extension.TypeSubstitution, extension.TypeParameters, extension.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics, + diagnosticsBuilder, nullabilityDiagnosticsBuilderOpt: null, ref useSiteDiagnosticsBuilder); + } if (!constraintsSatisfied) { @@ -1157,7 +1168,7 @@ private void AddMemberToCandidateSet( // This is specifying an impossible condition; the member lookup algorithm has already filtered // out methods from the method group that have the wrong generic arity. - Debug.Assert(typeArguments.Count == 0 || typeArguments.Count == member.GetMemberArity()); + Debug.Assert(typeArguments.Count == 0 || typeArguments.Count == member.GetMemberArityIncludingExtension()); // Second, we need to determine if the method is applicable in its normal form or its expanded form. bool disallowExpandedNonArrayParams = (options & Options.DisallowExpandedNonArrayParams) != 0; @@ -1462,20 +1473,24 @@ private void RemoveInaccessibleTypeArguments(ArrayBuilder typeArguments, ref CompoundUseSiteInfo useSiteInfo) - { - foreach (TypeSymbol arg in typeArguments) + bool typeArgumentsAccessible(ImmutableArray typeArguments, ref CompoundUseSiteInfo useSiteInfo) { - if (!_binder.IsAccessible(arg, ref useSiteInfo)) return false; + foreach (TypeSymbol arg in typeArguments) + { + if (!_binder.IsAccessible(arg, ref useSiteInfo)) return false; + } + return true; } - return true; } private static void RemoveLessDerivedMembers(ArrayBuilder> results, ref CompoundUseSiteInfo useSiteInfo) @@ -1874,7 +1889,7 @@ private void RemoveLowerPriorityMembers(ArrayBuilder continue; } - var containingType = result.MemberWithPriority.ContainingType; + var containingType = result.MemberWithPriority.ContainingType; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : how should ORPA apply to new extension methods? if (resultsByContainingType.TryGetValue(containingType, out var previousResults)) { var previousPriority = previousResults.First().MemberWithPriority.GetOverloadResolutionPriority(); @@ -2065,16 +2080,29 @@ private void RemoveWorseMembers(ArrayBuilder /// Returns the parameter corresponding to the given argument index. /// - private static ParameterSymbol GetParameter(int argIndex, MemberAnalysisResult result, ImmutableArray parameters) + private static ParameterSymbol GetParameterOrExtensionParameter(int argIndex, MemberAnalysisResult result, ImmutableArray parameters, TMember member) + where TMember : Symbol { + if (member.GetIsNewExtensionMember()) + { + if (argIndex == 0) + { + ParameterSymbol? extensionParameter = member.ContainingType.ExtensionParameter; + Debug.Assert(extensionParameter is not null); + return extensionParameter; + } + + argIndex--; + } + int paramIndex = result.ParameterFromArgument(argIndex); return parameters[paramIndex]; } -#nullable enable private BetterResult BetterFunctionMember( MemberResolutionResult m1, MemberResolutionResult m2, @@ -2164,9 +2192,9 @@ private BetterResult BetterFunctionMember( continue; } - var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out RefKind parameter1RefKind); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, m1.LeastOverriddenMember, out RefKind parameter1RefKind); - var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out RefKind parameter2RefKind); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, m2.LeastOverriddenMember, out RefKind parameter2RefKind); bool okToDowngradeToNeither; BetterResult r; @@ -2306,9 +2334,9 @@ private BetterResult BetterFunctionMember( continue; } - var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, out _); + var type1 = getParameterTypeAndRefKind(i, m1.Result, m1LeastOverriddenParameters, m1.Result.ParamsElementTypeOpt, m1.LeastOverriddenMember, out _); - var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, out _); + var type2 = getParameterTypeAndRefKind(i, m2.Result, m2LeastOverriddenParameters, m2.Result.ParamsElementTypeOpt, m2.LeastOverriddenMember, out _); var type1Normalized = type1; var type2Normalized = type2; @@ -2376,14 +2404,14 @@ private BetterResult BetterFunctionMember( } // If MP is a non-generic method and MQ is a generic method, then MP is better than MQ. - if (m1.Member.GetMemberArity() == 0) + if (m1.Member.GetMemberArityIncludingExtension() == 0) { - if (m2.Member.GetMemberArity() > 0) + if (m2.Member.GetMemberArityIncludingExtension() > 0) { return BetterResult.Left; } } - else if (m2.Member.GetMemberArity() == 0) + else if (m2.Member.GetMemberArityIncludingExtension() == 0) { return BetterResult.Right; } @@ -2466,9 +2494,9 @@ private BetterResult BetterFunctionMember( continue; } - uninst1.Add(getParameterTypeAndRefKind(i, m1.Result, m1DefinitionParameters, m1.Result.DefinitionParamsElementTypeOpt, out _)); + uninst1.Add(getParameterTypeAndRefKind(i, m1.Result, m1DefinitionParameters, m1.Result.DefinitionParamsElementTypeOpt, (TMember)m1.LeastOverriddenMember.OriginalDefinition, out _)); - uninst2.Add(getParameterTypeAndRefKind(i, m2.Result, m2DefinitionParameters, m2.Result.DefinitionParamsElementTypeOpt, out _)); + uninst2.Add(getParameterTypeAndRefKind(i, m2.Result, m2DefinitionParameters, m2.Result.DefinitionParamsElementTypeOpt, (TMember)m2.LeastOverriddenMember.OriginalDefinition, out _)); } result = MoreSpecificType(ref uninst1.AsRef(), ref uninst2.AsRef(), ref useSiteInfo); @@ -2526,8 +2554,8 @@ private BetterResult BetterFunctionMember( for (i = 0; i < arguments.Count; ++i) { - var parameter1 = GetParameter(i, m1.Result, m1LeastOverriddenParameters); - var parameter2 = GetParameter(i, m2.Result, m2LeastOverriddenParameters); + var parameter1 = GetParameterOrExtensionParameter(i, m1.Result, m1LeastOverriddenParameters, m1.LeastOverriddenMember); + var parameter2 = GetParameterOrExtensionParameter(i, m2.Result, m2LeastOverriddenParameters, m2.LeastOverriddenMember); if ((parameter1.Ordinal == m1ParamsOrdinal) != (parameter2.Ordinal == m2ParamsOrdinal)) { @@ -2555,13 +2583,15 @@ private BetterResult BetterFunctionMember( return BetterResult.Neither; // Returns the parameter type (considering params). - static TypeSymbol getParameterTypeAndRefKind(int i, MemberAnalysisResult result, ImmutableArray parameters, TypeWithAnnotations paramsElementTypeOpt, out RefKind parameter1RefKind) + static TypeSymbol getParameterTypeAndRefKind(int i, MemberAnalysisResult memberResolutionResult, ImmutableArray parameters, + TypeWithAnnotations paramsElementTypeOpt, TMember member, out RefKind parameter1RefKind) { - var parameter = GetParameter(i, result, parameters); + ParameterSymbol parameter = GetParameterOrExtensionParameter(i, memberResolutionResult, parameters, member); + parameter1RefKind = parameter.RefKind; var type = parameter.Type; - if (result.Kind == MemberResolutionKind.ApplicableInExpandedForm && + if (memberResolutionResult.Kind == MemberResolutionKind.ApplicableInExpandedForm && parameter.Ordinal == parameters.Length - 1) { Debug.Assert(paramsElementTypeOpt.HasType); @@ -2612,8 +2642,8 @@ private static BetterResult PreferValOverInOrRefInterpolatedHandlerParameters(MemberResolutionResult m, ArrayBuilder arguments, out int declaredParameterCount, out int parametersUsedIncludingExpansionAndOptional) where TMember : Symbol { - declaredParameterCount = m.Member.GetParameterCount(); + declaredParameterCount = m.Member.GetParameterCount() + (m.Member.GetIsNewExtensionMember() ? 1 : 0); if (m.Result.Kind == MemberResolutionKind.ApplicableInExpandedForm) { @@ -3846,17 +3877,26 @@ private static EffectiveParameters GetEffectiveParametersInNormalForm( Debug.Assert(argumentRefKinds != null); hasAnyRefOmittedArgument = false; - ImmutableArray parameters = member.GetParameters(); - // We simulate an extra parameter for vararg methods - int parameterCount = member.GetParameterCount() + (member.GetIsVararg() ? 1 : 0); + bool isNewExtensionMember = member.GetIsNewExtensionMember(); + ImmutableArray parameters = isNewExtensionMember ? GetParametersIncludingReceiver(member) : member.GetParameters(); + + // We simulate an extra parameter for vararg methods + int parameterCount = parameters.Length + (member.GetIsVararg() ? 1 : 0); if (argumentCount == parameterCount && argToParamMap.IsDefaultOrEmpty) { - ImmutableArray parameterRefKinds = member.GetParameterRefKinds(); - if (parameterRefKinds.IsDefaultOrEmpty) + bool hasSomeRefKinds = !member.GetParameterRefKinds().IsDefaultOrEmpty; + if (member.GetIsNewExtensionMember()) + { + Debug.Assert(member.ContainingType.ExtensionParameter is not null); + hasSomeRefKinds |= member.ContainingType.ExtensionParameter.RefKind != RefKind.None; + } + + if (!hasSomeRefKinds) { - return new EffectiveParameters(member.GetParameterTypes(), parameterRefKinds, firstParamsElementIndex: -1); + var parameterTypes = isNewExtensionMember ? GetParameterTypesIncludingReceiver(member) : member.GetParameterTypes(); + return new EffectiveParameters(parameterTypes, refKinds: default, firstParamsElementIndex: -1); } } @@ -4000,7 +4040,7 @@ private static EffectiveParameters GetEffectiveParametersInExpandedForm var types = ArrayBuilder.GetInstance(); var refs = ArrayBuilder.GetInstance(); bool anyRef = false; - var parameters = member.GetParameters(); + var parameters = member.GetIsNewExtensionMember() ? GetParametersIncludingReceiver(member) : member.GetParameters(); bool hasAnyRefArg = argumentRefKinds.Any(); hasAnyRefOmittedArgument = false; TypeWithAnnotations paramsIterationType = default; @@ -4205,14 +4245,13 @@ private MemberResolutionResult IsApplicable( Debug.Assert(GetConstructedFrom(leastOverriddenMember) == (object)leastOverriddenMember); bool ignoreOpenTypes; - MethodSymbol method; EffectiveParameters constructedEffectiveParameters; bool hasTypeArgumentsInferredFromFunctionType = false; if ((options & Options.InferringUniqueMethodGroupSignature) == 0 && - member.Kind == SymbolKind.Method && (method = (MethodSymbol)(Symbol)member).Arity > 0) + member.GetMemberArityIncludingExtension() > 0) { - MethodSymbol leastOverriddenMethod = (MethodSymbol)(Symbol)leastOverriddenMember; ImmutableArray typeArguments; + bool isNewExtensionMember = member.GetIsNewExtensionMember(); if (typeArgumentsBuilder.Count == 0 && arguments.HasDynamicArgument && !inferWithDynamic) { @@ -4226,7 +4265,7 @@ private MemberResolutionResult IsApplicable( // We don't need to check constraints of types of the non-elided parameters since they // have no effect on applicability of this candidate. ignoreOpenTypes = true; - typeArguments = method.TypeArgumentsWithAnnotations; + typeArguments = getAllTypeArguments(member, isNewExtensionMember); } else { @@ -4239,8 +4278,10 @@ private MemberResolutionResult IsApplicable( { // infer generic type arguments: MemberAnalysisResult inferenceError; - typeArguments = InferMethodTypeArguments(method, - leastOverriddenMethod.ConstructedFrom.TypeParameters, + ImmutableArray typeParameters = GetTypeParametersIncludingExtension(leastOverriddenMember); + + typeArguments = InferMethodTypeArguments(member, + typeParameters, arguments, constructedFromEffectiveParameters, out hasTypeArgumentsInferredFromFunctionType, @@ -4252,8 +4293,8 @@ private MemberResolutionResult IsApplicable( } } - member = (TMember)(Symbol)method.Construct(typeArguments); - leastOverriddenMember = (TMember)(Symbol)leastOverriddenMethod.ConstructedFrom.Construct(typeArguments); + member = member.ConstructIncludingExtension(typeArguments); + leastOverriddenMember = GetConstructedFrom(leastOverriddenMember).ConstructIncludingExtension(typeArguments); // Spec (§7.6.5.1) // Once the (inferred) type arguments are substituted for the corresponding method type parameters, @@ -4283,7 +4324,10 @@ private MemberResolutionResult IsApplicable( // the generic method still needs to be discarded, even though type inference // never saw the second formal parameter. - var parameterTypes = leastOverriddenMember.GetParameterTypes(); + var parameterTypes = isNewExtensionMember + ? GetParameterTypesIncludingReceiver(leastOverriddenMember) + : leastOverriddenMember.GetParameterTypes(); + for (int i = 0; i < parameterTypes.Length; i++) { if (!parameterTypes[i].Type.CheckAllConstraints(Compilation, Conversions)) @@ -4295,7 +4339,7 @@ private MemberResolutionResult IsApplicable( ignoreOpenTypes = false; } - var map = new TypeMap(leastOverriddenMethod.TypeParameters, typeArguments, allowAlpha: true); + var map = new TypeMap(GetTypeParametersIncludingExtension(isNewExtensionMember ? leastOverriddenMember.OriginalDefinition : leastOverriddenMember), typeArguments, allowAlpha: true); constructedEffectiveParameters = new EffectiveParameters( map.SubstituteTypes(constructedFromEffectiveParameters.ParameterTypes), @@ -4323,16 +4367,54 @@ private MemberResolutionResult IsApplicable( isMethodGroupConversion: isMethodGroupConversion, useSiteInfo: ref useSiteInfo); return new MemberResolutionResult(member, leastOverriddenMember, applicableResult, hasTypeArgumentsInferredFromFunctionType); + + static ImmutableArray getAllTypeArguments(TMember member, bool isNewExtensionMember) + { + if (member is MethodSymbol method) + { + return isNewExtensionMember + ? method.ContainingType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Concat(method.TypeArgumentsWithAnnotations) + : method.TypeArgumentsWithAnnotations; + } + else if (member is PropertySymbol property) + { + Debug.Assert(isNewExtensionMember); + var result = property.ContainingType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + Debug.Assert(!result.IsDefaultOrEmpty); + return result; + } + + throw ExceptionUtilities.UnexpectedValue(member); + } } - private ImmutableArray InferMethodTypeArguments( - MethodSymbol method, + internal static ImmutableArray GetTypeParametersIncludingExtension(TMember member) where TMember : Symbol + { + if (member is MethodSymbol method) + { + return method.GetIsNewExtensionMember() + ? method.ContainingType.TypeParameters.Concat(method.TypeParameters) + : method.ConstructedFrom.TypeParameters; + } + + if (member is PropertySymbol property) + { + Debug.Assert(property.GetIsNewExtensionMember()); + return property.ContainingType.TypeParameters; + } + + throw ExceptionUtilities.UnexpectedValue(member); + } + + private ImmutableArray InferMethodTypeArguments( + TMember member, ImmutableArray originalTypeParameters, AnalyzedArguments arguments, EffectiveParameters originalEffectiveParameters, out bool hasTypeArgumentsInferredFromFunctionType, out MemberAnalysisResult error, ref CompoundUseSiteInfo useSiteInfo) + where TMember : Symbol { var args = arguments.Arguments.ToImmutable(); @@ -4341,15 +4423,20 @@ private ImmutableArray InferMethodTypeArguments( // a possibly constructed generic type, is exceedingly subtle. See the comments // in "Infer" for details. + PooledDictionary ordinals = makeOrdinalsIfNeeded(member, originalTypeParameters); + var inferenceResult = MethodTypeInferrer.Infer( _binder, _binder.Conversions, originalTypeParameters, - method.ContainingType, + member.ContainingType, originalEffectiveParameters.ParameterTypes, originalEffectiveParameters.ParameterRefKinds, args, - ref useSiteInfo); + ref useSiteInfo, + ordinals: ordinals); + + ordinals?.Free(); if (inferenceResult.Success) { @@ -4358,15 +4445,32 @@ private ImmutableArray InferMethodTypeArguments( return inferenceResult.InferredTypeArguments; } - if (arguments.IsExtensionMethodInvocation) + if (arguments.IncludesReceiverAsArgument) { - var canInfer = MethodTypeInferrer.CanInferTypeArgumentsFromFirstArgument( - _binder.Compilation, - _binder.Conversions, - method, - args, - useSiteInfo: ref useSiteInfo, - out _); + bool canInfer; + if (member.GetIsNewExtensionMember()) + { + if (member.ContainingType.Arity > 0) + { + var extensionTypeArguments = MethodTypeInferrer.InferTypeArgumentsFromReceiverType(member.ContainingType, args[0], _binder.Compilation, _binder.Conversions, ref useSiteInfo); + canInfer = !extensionTypeArguments.IsDefault && !extensionTypeArguments.Any(t => !t.HasType); + } + else + { + canInfer = true; + } + } + else + { + canInfer = MethodTypeInferrer.CanInferTypeArgumentsFromFirstArgument( + _binder.Compilation, + _binder.Conversions, + (MethodSymbol)(Symbol)member, + args, + useSiteInfo: ref useSiteInfo, + out _); + } + if (!canInfer) { hasTypeArgumentsInferredFromFunctionType = false; @@ -4378,7 +4482,38 @@ private ImmutableArray InferMethodTypeArguments( hasTypeArgumentsInferredFromFunctionType = false; error = MemberAnalysisResult.TypeInferenceFailed(); return default(ImmutableArray); + +#nullable enable + static PooledDictionary? makeOrdinalsIfNeeded(TMember member, ImmutableArray originalTypeParameters) + { + if (member is MethodSymbol method) + { + PooledDictionary? ordinals = null; + if (method.GetIsNewExtensionMember() && method.Arity > 0 && method.ContainingType.Arity > 0) + { + Debug.Assert(originalTypeParameters.Length == method.Arity + method.ContainingType.Arity); + + // Since we're concatenating type parameters from the extension and from the method together + // we need to control the ordinals that are used + ordinals = PooledDictionary.GetInstance(); + for (int i = 0; i < originalTypeParameters.Length; i++) + { + ordinals.Add(originalTypeParameters[i], i); + } + } + + return ordinals; + } + + if (member is PropertySymbol) + { + return null; + } + + throw ExceptionUtilities.UnexpectedValue(member); + } } +#nullable disable private MemberAnalysisResult IsApplicable( Symbol candidate, // method or property @@ -4467,7 +4602,7 @@ private MemberAnalysisResult IsApplicable( { RefKind argumentRefKind = arguments.RefKind(argumentPosition); RefKind parameterRefKind = parameters.ParameterRefKinds.IsDefault ? RefKind.None : parameters.ParameterRefKinds[argumentPosition]; - bool forExtensionMethodThisArg = arguments.IsExtensionMethodThisArgument(argumentPosition); + bool forExtensionMethodThisArg = arguments.IsExtensionMethodReceiverArgument(argumentPosition); if (forExtensionMethodThisArg) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs index d260b5ba8ebe3..945927818063a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs @@ -895,7 +895,7 @@ private static void ReportMissingRequiredParameter( // to required formal parameter 'y'. TMember badMember = bad.Member; - ImmutableArray parameters = badMember.GetParameters(); + ImmutableArray parameters = badMember.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(badMember) : badMember.GetParameters(); int badParamIndex = bad.Result.BadParameter; string badParamName; if (badParamIndex == parameters.Length) @@ -946,7 +946,7 @@ private static void ReportBadParameterCount( }; int argCount = arguments.Arguments.Count; - if (arguments.IsExtensionMethodInvocation) + if (arguments.IncludesReceiverAsArgument) { argCount--; } @@ -1114,8 +1114,8 @@ private bool HadBadArguments( // ErrorCode.ERR_BadArgTypesForCollectionAdd or ErrorCode.ERR_InitializerAddHasParamModifiers // as there is no explicit call to Add method. - int argumentOffset = arguments.IsExtensionMethodInvocation ? 1 : 0; - var parameters = method.GetParameters(); + int argumentOffset = arguments.IncludesReceiverAsArgument ? 1 : 0; + var parameters = method.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(method) : method.GetParameters(); for (int i = argumentOffset; i < parameters.Length; i++) { @@ -1156,6 +1156,7 @@ private static void ReportBadArgumentError( TMember method, int arg) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider adjusting or removing the argument index for displaying in diagnostic BoundExpression argument = arguments.Argument(arg); if (argument.HasAnyErrors) { @@ -1169,7 +1170,8 @@ private static void ReportBadArgumentError( // Early out: if the bad argument is an __arglist parameter then simply report that: - if (method.GetIsVararg() && parm == method.GetParameterCount()) + var parameters = method.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(method) : method.GetParameters(); + if (method.GetIsVararg() && parm == parameters.Length) { // NOTE: No SymbolDistinguisher required, since one of the arguments is "__arglist". @@ -1184,12 +1186,12 @@ private static void ReportBadArgumentError( return; } - ParameterSymbol parameter = method.GetParameters()[parm]; - bool isLastParameter = method.GetParameterCount() == parm + 1; // This is used to later decide if we need to try to unwrap a params collection + ParameterSymbol parameter = parameters[parm]; + bool isLastParameter = parameters.Length == parm + 1; // This is used to later decide if we need to try to unwrap a params collection RefKind refArg = arguments.RefKind(arg); RefKind refParameter = parameter.RefKind; - if (arguments.IsExtensionMethodThisArgument(arg)) + if (arguments.IsExtensionMethodReceiverArgument(arg)) { Debug.Assert(refArg == RefKind.None); if (refParameter == RefKind.Ref || refParameter == RefKind.In) @@ -1301,7 +1303,7 @@ private static void ReportBadArgumentError( Debug.Assert(argument.Kind != BoundKind.DiscardExpression || argument.HasExpressionType()); Debug.Assert(argument.Display != null); - if (arguments.IsExtensionMethodThisArgument(arg)) + if (arguments.IsExtensionMethodReceiverArgument(arg)) { Debug.Assert((arg == 0) && (parm == arg)); Debug.Assert(!badArg.Result.ConversionForArg(parm).IsImplicit); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs index 3da8c76c13371..15e4f55d5b914 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs @@ -55,6 +55,20 @@ public ImmutableArray ToImmutableArray() } } + internal static ImmutableArray GetParametersIncludingReceiver(Symbol symbol) + { + Debug.Assert(symbol.GetIsNewExtensionMember()); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider optimizing + return [symbol.ContainingType.ExtensionParameter, .. symbol.GetParameters()]; + } + + private static ImmutableArray GetParameterTypesIncludingReceiver(Symbol symbol) + { + Debug.Assert(symbol.GetIsNewExtensionMember()); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider optimizing + return [symbol.ContainingType.ExtensionParameter.TypeWithAnnotations, .. symbol.GetParameterTypes()]; + } + private static ArgumentAnalysisResult AnalyzeArguments( Symbol symbol, AnalyzedArguments arguments, @@ -64,7 +78,8 @@ private static ArgumentAnalysisResult AnalyzeArguments( Debug.Assert((object)symbol != null); Debug.Assert(arguments != null); - ImmutableArray parameters = symbol.GetParameters(); + bool isNewExtensionMember = symbol.GetIsNewExtensionMember(); + ImmutableArray parameters = isNewExtensionMember ? GetParametersIncludingReceiver(symbol) : symbol.GetParameters(); bool isVararg = symbol.GetIsVararg(); // The easy out is that we have no named arguments and are in normal form. diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index 3dcf824ff86db..c5dabbb02b759 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -62,7 +62,7 @@ internal override BoundStatement BindSwitchStatementCore(SwitchStatementSyntax n syntax: node, expression: boundSwitchGoverningExpression, innerLocals: locals, - innerLocalFunctions: functions, + innerLocalFunctions: ImmutableArray.CastUp(functions), switchSections: switchSections, defaultLabel: defaultLabel, breakLabel: this.BreakLabel, diff --git a/src/Compilers/CSharp/Portable/Binder/WithClassTypeParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithClassTypeParametersBinder.cs index a073d882d9a94..fd1a7d0f2c3aa 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithClassTypeParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithClassTypeParametersBinder.cs @@ -62,5 +62,19 @@ internal override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo resu } } } + + protected override LookupOptions LookupMask + { + get + { + if (_namedType.IsExtension) + { + // Extension type parameters should get the same treatment as method type parameters + return WithMethodTypeParametersBinder.MethodTypeParameterLookupMask; + } + + return base.LookupMask; + } + } } } diff --git a/src/Compilers/CSharp/Portable/Binder/WithExtensionParameterBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithExtensionParameterBinder.cs new file mode 100644 index 0000000000000..870e610d0f1b2 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Binder/WithExtensionParameterBinder.cs @@ -0,0 +1,52 @@ +// 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.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// Binder used to place extension parameter, if any, in scope. + /// + internal sealed class WithExtensionParameterBinder : Binder + { + private readonly NamedTypeSymbol _type; + + internal WithExtensionParameterBinder(NamedTypeSymbol type, Binder next) + : base(next) + { + _type = type; + } + + internal override void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) + { + if (options.CanConsiderMembers()) + { + if (_type.ExtensionParameter is { Name: not "" } parameter && + originalBinder.CanAddLookupSymbolInfo(parameter, options, result, null)) + { + result.AddSymbol(parameter, parameter.Name, 0); + } + } + } + + internal override void LookupSymbolsInSingleBinder( + LookupResult result, string name, int arity, ConsList basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo useSiteInfo) + { + Debug.Assert(result.IsClear); + + if ((options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) != 0) + { + return; + } + + if (_type.ExtensionParameter is { Name: not "" } parameter && parameter.Name == name) + { + result.MergeEqual(originalBinder.CheckViability(parameter, arity, options, null, diagnose, ref useSiteInfo)); + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Binder/WithMethodTypeParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithMethodTypeParametersBinder.cs index f72176050939d..5d3142da798a1 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithMethodTypeParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithMethodTypeParametersBinder.cs @@ -15,6 +15,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// internal sealed class WithMethodTypeParametersBinder : WithTypeParametersBinder { + internal const LookupOptions MethodTypeParameterLookupMask = LookupOptions.NamespaceAliasesOnly | LookupOptions.MustNotBeMethodTypeParameter; private readonly MethodSymbol _methodSymbol; private MultiDictionary _lazyTypeParameterMap; @@ -57,7 +58,7 @@ protected override LookupOptions LookupMask { get { - return LookupOptions.NamespaceAliasesOnly | LookupOptions.MustNotBeMethodTypeParameter; + return MethodTypeParameterLookupMask; } } diff --git a/src/Compilers/CSharp/Portable/Binder/WithTypeParametersBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithTypeParametersBinder.cs index f44ecf90aa85e..62e6cb17b378b 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithTypeParametersBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithTypeParametersBinder.cs @@ -21,7 +21,6 @@ internal WithTypeParametersBinder(Binder next) // TODO: Change this to a data structure that won't allocate enumerators protected abstract MultiDictionary TypeParameterMap { get; } - // This is only overridden by WithMethodTypeParametersBinder. protected virtual LookupOptions LookupMask { get diff --git a/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs b/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs index 05c22fab771c0..107d440659f1b 100644 --- a/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/WithUsingNamespacesAndTypesBinder.cs @@ -66,7 +66,7 @@ protected WithUsingNamespacesAndTypesBinder(Binder next, bool withImportChainEnt return base.GetForwardedToAssemblyInUsingNamespaces(name, ref qualifierOpt, diagnostics, location); } - internal override bool SupportsExtensionMethods + internal override bool SupportsExtensions { get { return true; } } @@ -129,6 +129,31 @@ internal override void GetCandidateExtensionMethods( } } + internal override void GetExtensionDeclarations(ArrayBuilder extensions, Binder originalBinder) + { + Debug.Assert(extensions.Count == 0); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test this flag (see TestUnusedExtensionMarksImportsAsUsed) + bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder; + + foreach (var nsOrType in this.GetUsings(basesBeingResolved: null)) + { + if (nsOrType.NamespaceOrType is NamespaceSymbol ns) + { + var count = extensions.Count; + ns.GetExtensionContainers(extensions); + // If we found any extension declarations, then consider this using as used. + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider refining this logic + if (extensions.Count != count) + { + MarkImportDirective(nsOrType.UsingDirectiveReference, callerIsSemanticModel); + } + } + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : clarify expected behavior for `using Extension;` or `using static Extension;`. + // If/when we do such a scenario, we have to remove duplicates (see GetCandidateExtensionMethods). + } + } + internal override void LookupSymbolsInSingleBinder( LookupResult result, string name, int arity, ConsList? basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo useSiteInfo) { @@ -170,7 +195,7 @@ private static bool IsValidLookupCandidateInUsings(Symbol symbol) // lookup via "using static" ignores extension methods and non-static methods case SymbolKind.Method: - if (!symbol.IsStatic || ((MethodSymbol)symbol).IsExtensionMethod) + if (!symbol.IsStatic || ((MethodSymbol)symbol).IsExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { return false; } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 9fff5020e041f..06204dd5e723a 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -477,7 +477,7 @@ public override bool SuppressVirtualCalls public BoundConversion UpdateOperand(BoundExpression operand) { - return this.Update(operand: operand, this.Conversion, this.IsBaseConversion, this.Checked, this.ExplicitCastInCode, this.ConstantValueOpt, this.ConversionGroupOpt, this.OriginalUserDefinedConversionsOpt, this.Type); + return this.Update(operand: operand, this.Conversion, this.IsBaseConversion, this.Checked, this.ExplicitCastInCode, this.ConstantValueOpt, this.ConversionGroupOpt, this.Type); } /// diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs index 01b94d33dbbef..f414e204313c2 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs @@ -69,11 +69,11 @@ public BoundExpression? InstanceOpt } } - public bool SearchExtensionMethods + public bool SearchExtensions { get { - return (this.Flags & BoundMethodGroupFlags.SearchExtensionMethods) != 0; + return (this.Flags & BoundMethodGroupFlags.SearchExtensions) != 0; } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroupFlags.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroupFlags.cs index 46727e00cee3f..ee1aa490768ca 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroupFlags.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroupFlags.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp internal enum BoundMethodGroupFlags { None = 0, - SearchExtensionMethods = 1, + SearchExtensions = 1, /// /// Set if the group has a receiver but one was not specified in syntax. diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 5a227e6297a93..663c321f9aada 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -902,10 +902,6 @@ Conversion is represented by a single BoundConversion. --> - - - + @@ -1121,7 +1117,8 @@ void Foo() { } --> - + + @@ -1165,11 +1162,11 @@ - + - + @@ -1177,11 +1174,11 @@ - + - + - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundTreeRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundTreeRewriter.cs index e55fc8af21250..bf47f1f5062bd 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundTreeRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundTreeRewriter.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.PooledObjects; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -59,6 +60,142 @@ private ImmutableArray DoVisitList(ImmutableArray list) where T : Bound return list; } + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual AliasSymbol? VisitAliasSymbol(AliasSymbol? symbol) => symbol; + + public virtual DiscardSymbol VisitDiscardSymbol(DiscardSymbol symbol) + { + Debug.Assert(symbol is not null); + return symbol; + } + + public virtual EventSymbol VisitEventSymbol(EventSymbol symbol) + { + Debug.Assert(symbol is not null); + return symbol; + } + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual LabelSymbol? VisitLabelSymbol(LabelSymbol? symbol) => symbol; + + public virtual LocalSymbol VisitLocalSymbol(LocalSymbol symbol) + { + Debug.Assert(symbol is not null); + return symbol; + } + + public virtual NamespaceSymbol VisitNamespaceSymbol(NamespaceSymbol symbol) + { + Debug.Assert(symbol is not null); + return symbol; + } + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual RangeVariableSymbol? VisitRangeVariableSymbol(RangeVariableSymbol? symbol) => symbol; + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual FieldSymbol? VisitFieldSymbol(FieldSymbol? symbol) => symbol; + + public virtual ParameterSymbol VisitParameterSymbol(ParameterSymbol symbol) + { + Debug.Assert(symbol is not null); + return symbol; + } + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual PropertySymbol? VisitPropertySymbol(PropertySymbol? symbol) => symbol; + + [return: NotNullIfNotNull(nameof(symbol))] + public virtual MethodSymbol? VisitMethodSymbol(MethodSymbol? symbol) => symbol; + + [return: NotNullIfNotNull(nameof(symbol))] + public Symbol? VisitSymbol(Symbol? symbol) + { + if (symbol is null) + { + return null; + } + + switch (symbol.Kind) + { + case SymbolKind.Alias: + return VisitAliasSymbol((AliasSymbol)symbol); + case SymbolKind.Discard: + return VisitDiscardSymbol((DiscardSymbol)symbol); + case SymbolKind.Event: + return VisitEventSymbol((EventSymbol)symbol); + case SymbolKind.Label: + return VisitLabelSymbol((LabelSymbol)symbol); + case SymbolKind.Local: + return VisitLocalSymbol((LocalSymbol)symbol); + case SymbolKind.Namespace: + return VisitNamespaceSymbol((NamespaceSymbol)symbol); + case SymbolKind.RangeVariable: + return VisitRangeVariableSymbol((RangeVariableSymbol)symbol); + case SymbolKind.Field: + return VisitFieldSymbol((FieldSymbol)symbol); + case SymbolKind.Parameter: + return VisitParameterSymbol((ParameterSymbol)symbol); + case SymbolKind.Property: + return VisitPropertySymbol((PropertySymbol)symbol); + case SymbolKind.Method: + return VisitMethodSymbol((MethodSymbol)symbol); + + default: + if (symbol is TypeSymbol type) + { + return VisitType(type); + } + + throw ExceptionUtilities.UnexpectedValue(symbol.Kind); + } + } + + [return: NotNullIfNotNull(nameof(symbol))] + protected FunctionTypeSymbol? VisitFunctionTypeSymbol(FunctionTypeSymbol? symbol) + { + return (FunctionTypeSymbol?)VisitType(symbol); + } + + public ImmutableArray VisitSymbols(ImmutableArray symbols) where T : Symbol? + { + if (symbols.IsDefault) + { + return symbols; + } + + ArrayBuilder? builder = null; + + for (int i = 0; i < symbols.Length; i++) + { + T symbol = symbols[i]; + + var newSymbol = (T?)VisitSymbol(symbol); + if (newSymbol != (object?)symbol) + { + Debug.Assert(newSymbol is not null); + + if (builder is null) + { + builder = ArrayBuilder.GetInstance(symbols.Length); + builder.AddRange(symbols, i); + } + + builder.Add(newSymbol); + } + else if (builder is not null) + { + builder.Add(symbol); + } + } + + return builder is null ? symbols : builder.ToImmutableAndFree(); + } + + protected virtual ImmutableArray VisitLocals(ImmutableArray locals) => locals; + + protected virtual ImmutableArray VisitDeclaredLocalFunctions(ImmutableArray localFunctions) => localFunctions; } internal abstract class BoundTreeRewriterWithStackGuard : BoundTreeRewriter @@ -112,7 +249,7 @@ protected BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperat if (child.Kind != BoundKind.BinaryOperator) { - return base.VisitBinaryOperator(node); + return node.Update(node.OperatorKind, VisitBinaryOperatorData(node), node.ResultKind, (BoundExpression)this.Visit(node.Left), (BoundExpression)this.Visit(node.Right), this.VisitType(node.Type)); } var stack = ArrayBuilder.GetInstance(); @@ -142,7 +279,7 @@ protected BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperat var right = (BoundExpression?)this.Visit(binary.Right); Debug.Assert(right is { }); var type = this.VisitType(binary.Type); - left = binary.Update(binary.OperatorKind, binary.Data, binary.ResultKind, left, right, type); + left = binary.Update(binary.OperatorKind, VisitBinaryOperatorData(binary), binary.ResultKind, left, right, type); } while (stack.Count > 0); @@ -152,6 +289,11 @@ protected BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperat return left; } + protected virtual BoundBinaryOperator.UncommonData? VisitBinaryOperatorData(BoundBinaryOperator node) + { + return node.Data; + } + public sealed override BoundNode? VisitIfStatement(BoundIfStatement node) { if (node.AlternativeOpt is not BoundIfStatement ifStatement) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index 259af1aa8fdf1..3aa692af65ad8 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -336,7 +336,6 @@ public static BoundConversion SynthesizedNonUserDefined(SyntaxNode syntax, Bound explicitCastInCode: false, conversionGroupOpt: null, constantValueOpt: constantValueOpt, - originalUserDefinedConversionsOpt: default, type: type) { WasCompilerGenerated = true }; } @@ -391,35 +390,9 @@ public BoundConversion( explicitCastInCode: explicitCastInCode, constantValueOpt: constantValueOpt, conversionGroupOpt, - conversion.OriginalUserDefinedConversions, type: type, hasErrors: hasErrors || !conversion.IsValid) { } - - public BoundConversion( - SyntaxNode syntax, - BoundExpression operand, - Conversion conversion, - bool isBaseConversion, - bool @checked, - bool explicitCastInCode, - ConstantValue? constantValueOpt, - ConversionGroup? conversionGroupOpt, - TypeSymbol type, - bool hasErrors = false) : - this(syntax, operand, conversion, isBaseConversion, @checked, explicitCastInCode, constantValueOpt, conversionGroupOpt, originalUserDefinedConversionsOpt: default, type, hasErrors) - { - } - - public BoundConversion Update(BoundExpression operand, - Conversion conversion, - bool isBaseConversion, - bool @checked, - bool explicitCastInCode, - ConstantValue? constantValueOpt, - ConversionGroup? conversionGroupOpt, - TypeSymbol type) - => Update(operand, conversion, isBaseConversion, @checked, explicitCastInCode, constantValueOpt, conversionGroupOpt, this.OriginalUserDefinedConversionsOpt, type); } internal sealed partial class BoundBinaryOperator @@ -512,17 +485,6 @@ public BoundUserDefinedConditionalLogicalOperator( { Debug.Assert(operatorKind.IsUserDefined() && operatorKind.IsLogical()); } - - public BoundUserDefinedConditionalLogicalOperator Update(BinaryOperatorKind operatorKind, - MethodSymbol logicalOperator, - MethodSymbol trueOperator, - MethodSymbol falseOperator, - TypeSymbol? constrainedToTypeOpt, - LookupResultKind resultKind, - BoundExpression left, - BoundExpression right, - TypeSymbol type) - => Update(operatorKind, logicalOperator, trueOperator, falseOperator, constrainedToTypeOpt, resultKind, this.OriginalUserDefinedOperatorsOpt, left, right, type); } internal sealed partial class BoundParameter @@ -653,7 +615,7 @@ public BoundGotoStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors = internal partial class BoundBlock { public BoundBlock(SyntaxNode syntax, ImmutableArray locals, ImmutableArray statements, bool hasErrors = false) - : this(syntax, locals, ImmutableArray.Empty, hasUnsafeModifier: false, instrumentation: null, statements, hasErrors) + : this(syntax, locals, ImmutableArray.Empty, hasUnsafeModifier: false, instrumentation: null, statements, hasErrors) { } diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 27d00fddb1c63..b1fba6ed796dc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -819,7 +819,7 @@ private BoundLambda ReallyBind(NamedTypeSymbol delegateType, bool inExpressionTr refKind == CodeAnalysis.RefKind.None && _returnInferenceCache!.TryGetValue(cacheKey, out BoundLambda? returnInferenceLambda) && GetLambdaExpressionBody(returnInferenceLambda.Body) is BoundExpression expression && - (lambdaSymbol = returnInferenceLambda.Symbol).RefKind == refKind && + (lambdaSymbol = (LambdaSymbol)returnInferenceLambda.Symbol).RefKind == refKind && (object)LambdaSymbol.InferenceFailureReturnType != lambdaSymbol.ReturnType && lambdaSymbol.ReturnTypeWithAnnotations.Equals(returnType, TypeCompareKind.ConsiderEverything)) { @@ -1273,7 +1273,7 @@ private BoundLambda ReallyBindForErrorRecovery( // If multiple candidates have the same number of diagnostics, order them by delegate type name. // It's not great, but it should be stable. return minDiagnosticsGroup - .OrderBy(lambda => GetLambdaSortString(lambda.Value.Symbol)) + .OrderBy(lambda => GetLambdaSortString((LambdaSymbol)lambda.Value.Symbol)) .FirstOrDefault() .Value; } diff --git a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs index 8ae7033ac2fd0..650f5b27cf0ad 100644 --- a/src/Compilers/CSharp/Portable/CSharpParseOptions.cs +++ b/src/Compilers/CSharp/Portable/CSharpParseOptions.cs @@ -230,6 +230,14 @@ static void addSingleNamespaceParts(ArrayBuilder> namespa } } + /// + /// Used for parsing .cs file-based programs. + /// + /// + /// In this mode, ignored directives #: are allowed. + /// + internal bool FileBasedProgram => Features.ContainsKey("FileBasedProgram"); + internal override void ValidateOptions(ArrayBuilder builder) { ValidateOptions(builder, MessageProvider.Instance); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index bdb7325e53256..611416fe327f8 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -1171,7 +1171,7 @@ Cannot implicitly convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?) - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. Imported type '{0}' is invalid. It contains a circular base type dependency. @@ -2543,7 +2543,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Extension methods must be defined in a top level static class; {0} is a nested class - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? Do not use 'System.Runtime.CompilerServices.ExtensionAttribute'. Use the 'this' keyword instead. @@ -2600,7 +2600,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep Invalid preprocessor expression - Invalid token '{0}' in class, record, struct, or interface member declaration + Invalid token '{0}' in a member declaration Method must have a return type @@ -6900,10 +6900,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ The loaded assembly references .NET Framework, which is not supported. - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. The type '{0}' may not be used for a field of a record. @@ -7483,7 +7483,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ file types - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. array access @@ -7983,14 +7983,14 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Property accessor '{0}' must be '{1}' to match the definition part - - Both partial property declarations must have the same type. + + Both partial member declarations must have the same type. - - Partial property declarations '{0}' and '{1}' have signature differences. + + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. + + Partial member declarations have signature differences. Both partial property declarations must be required or neither may be required @@ -8014,11 +8014,11 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Attribute 'System.Runtime.CompilerServices.InlineArray' cannot be applied to a record struct. - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. first-class Span types @@ -8050,4 +8050,88 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot emit this string literal into the data section because it has XXHash128 collision with another string literal: {0} + + partial events and constructors + + + Partial member '{0}' must have an implementation part. + + + Partial member '{0}' must have a definition part. + + + Partial member '{0}' may not have multiple defining declarations. + + + Partial member '{0}' may not have multiple implementing declarations. + + + '{0}': partial event cannot have initializer + + + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + null conditional assignment + + + extensions + + + Extension declarations may not have a name. + + + Extension declarations can include only methods or properties + + + Extensions must be declared in a top-level, non-generic, static class + + + The receiver parameter of an extension cannot have a default value + + + An extension container can have only one receiver parameter + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + + Type parameter '{0}' has the same name as an extension container type parameter + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + Type parameter '{0}' has the same name as an extension parameter + + + Cannot use extension parameter '{0}' in this context. + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + + An expression tree may not contain an extension property access + + + '#:' directives cannot be after first token in file + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + + '#:' directives cannot be after '#if' directive + diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 26f9586b15fc1..0b8cfbc2f54b7 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -596,7 +596,7 @@ private void EmitLoweredConditionalAccessExpression(BoundLoweredConditionalAcces /// /// We must use a temp when there is a chance that evaluation of the call arguments - /// could actually modify value of the reference type reciever. The call must use + /// could actually modify value of the reference type receiver. The call must use /// the original (unmodified) receiver. /// private sealed class IsConditionalConstrainedCallThatMustUseTempForReferenceTypeReceiverWalker : BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator @@ -3588,7 +3588,7 @@ private void EmitParameterIdExpression(BoundParameterId node) if (node.HoistedField is null) { - _builder.EmitIntConstant(node.Parameter.Ordinal); + _builder.EmitIntConstant(node.Parameter.Ordinal); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Follow up } else { diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 49c2ebda77ec0..4aa31399de20c 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1399,7 +1399,7 @@ private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax) /// scope around position is used. /// The name of the symbol to find. If null is specified then symbols /// with any names are returned. - /// Consider (reduced) extension methods. + /// Consider extension members. Classic extension methods will be returned in reduced form. /// A list of symbols that were found. If no symbols were found, an empty list is returned. /// /// The "position" is used to determine what variables are visible and accessible. Even if "container" is @@ -1408,15 +1408,15 @@ private void CheckModelAndSyntaxNodeToSpeculate(CSharpSyntaxNode syntax) /// /// Labels are not considered (see ). /// - /// Non-reduced extension methods are considered regardless of the value of . + /// Non-reduced extension methods are considered regardless of the value of . /// public ImmutableArray LookupSymbols( int position, NamespaceOrTypeSymbol container = null, string name = null, - bool includeReducedExtensionMethods = false) + bool includeExtensions = false) { - var options = includeReducedExtensionMethods ? LookupOptions.IncludeExtensionMethods : LookupOptions.Default; + var options = includeExtensions ? LookupOptions.IncludeExtensionMembers : LookupOptions.Default; return LookupSymbolsInternal(position, container, name, options, useBaseReferenceAccessibility: false); } @@ -1576,7 +1576,7 @@ private ImmutableArray LookupSymbolsInternal( if ((object)container == null || container.Kind == SymbolKind.Namespace) { - options &= ~LookupOptions.IncludeExtensionMethods; + options &= ~LookupOptions.IncludeExtensionMembers; } var binder = GetEnclosingBinder(position); @@ -1652,25 +1652,33 @@ private ImmutableArray LookupSymbolsInternal( info.Free(); - if ((options & LookupOptions.IncludeExtensionMethods) != 0) + if ((options & LookupOptions.IncludeExtensionMembers) != 0 && container is TypeSymbol receiverType) { var lookupResult = LookupResult.GetInstance(); options |= LookupOptions.AllMethodsOnArityZero; options &= ~LookupOptions.MustBeInstance; - var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - binder.LookupExtensionMethods(lookupResult, name, 0, options, ref discardedUseSiteInfo); + binder.LookupAllExtensions(lookupResult, name, options); if (lookupResult.IsMultiViable) { - TypeSymbol containingType = (TypeSymbol)container; - foreach (MethodSymbol extensionMethod in lookupResult.Symbols) + foreach (Symbol symbol in lookupResult.Symbols) { - var reduced = extensionMethod.ReduceExtensionMethod(containingType, Compilation); - if ((object)reduced != null) + if (symbol is MethodSymbol { IsExtensionMethod: true } extensionMethod) + { + if (extensionMethod.ReduceExtensionMethod(receiverType, Compilation) is { } reduced) + { + results.Add(reduced.GetPublicSymbol()); + } + } + else { - results.Add(reduced.GetPublicSymbol()); + Debug.Assert(symbol.GetIsNewExtensionMember()); + if (SourceNamedTypeSymbol.GetCompatibleSubstitutedMember(binder.Compilation, symbol, receiverType) is { } compatibleSubstitutedMember) + { + results.Add(compatibleSubstitutedMember.GetPublicSymbol()); + } } } } @@ -1739,7 +1747,7 @@ private void AppendSymbolsWithNameAndArity( name, arity, basesBeingResolved: null, - options: options & ~LookupOptions.IncludeExtensionMethods, + options: options & ~LookupOptions.IncludeExtensionMembers, diagnose: false, useSiteInfo: ref discardedUseSiteInfo); @@ -3361,6 +3369,7 @@ private OneOrMany GetSemanticSymbols( case BoundKind.PropertyGroup: symbols = GetPropertyGroupSemanticSymbols((BoundPropertyGroup)boundNode, boundNodeForSyntacticParent, binderOpt, out resultKind, out memberGroup); break; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle BoundPropertyAccess (which now may have a member group) case BoundKind.BadExpression: { @@ -3446,7 +3455,7 @@ private OneOrMany GetSemanticSymbols( } else { - symbols = StaticCast.From(CreateReducedExtensionMethodsFromOriginalsIfNecessary(call, Compilation)); + symbols = CreateReducedAndFilteredSymbolsFromOriginals(call, Compilation); resultKind = call.ResultKind; } } @@ -3470,7 +3479,7 @@ private OneOrMany GetSemanticSymbols( // group result in the call below. symbols = GetMethodGroupSemanticSymbols( ((BoundUnconvertedAddressOfOperator)boundNode).Operand, - boundNodeForSyntacticParent, binderOpt, out resultKind, out isDynamic, methodGroup: out _); + boundNodeForSyntacticParent, binderOpt, out resultKind, out isDynamic, memberGroup: out _); break; } @@ -3534,7 +3543,7 @@ boundNode.ExpressionSymbol is Symbol accessSymbol && } else if (conversion.ConversionKind.IsUserDefinedConversion()) { - GetSymbolsAndResultKind(conversion, conversion.SymbolOpt, conversion.OriginalUserDefinedConversionsOpt, out symbols, out resultKind); + GetSymbolsAndResultKind(conversion, conversion.SymbolOpt, conversion.Conversion.OriginalUserDefinedConversions, out symbols, out resultKind); } else { @@ -4235,7 +4244,7 @@ private OneOrMany GetMethodGroupSemanticSymbols( Binder binderOpt, out LookupResultKind resultKind, out bool isDynamic, - out ImmutableArray methodGroup) + out ImmutableArray memberGroup) { Debug.Assert(binderOpt != null || IsInTree(boundNode.Syntax)); @@ -4251,7 +4260,7 @@ private OneOrMany GetMethodGroupSemanticSymbols( // The method group needs filtering. Binder binder = binderOpt ?? GetEnclosingBinder(GetAdjustedNodePosition(boundNode.Syntax)); - methodGroup = GetReducedAndFilteredMethodGroupSymbols(binder, boundNode).Cast(); + memberGroup = GetReducedAndFilteredMethodGroupSymbols(binder, boundNode); // We want to get the actual node chosen by overload resolution, if possible. if (boundNodeForSyntacticParent != null) @@ -4274,7 +4283,7 @@ private OneOrMany GetMethodGroupSemanticSymbols( else { resultKind = call.ResultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); - symbols = StaticCast.From(CreateReducedExtensionMethodsFromOriginalsIfNecessary(call, Compilation)); + symbols = CreateReducedAndFilteredSymbolsFromOriginals(call, Compilation); } } break; @@ -4329,7 +4338,7 @@ private OneOrMany GetMethodGroupSemanticSymbols( case BoundKind.BadExpression: // If the bad expression has symbol(s) from this method group, it better indicates any problems. - ImmutableArray myMethodGroup = methodGroup; + ImmutableArray myMethodGroup = memberGroup; symbols = OneOrMany.Create(((BoundBadExpression)boundNodeForSyntacticParent).Symbols.WhereAsArray((sym, myMethodGroup) => myMethodGroup.Contains(sym), myMethodGroup)); if (symbols.Any()) @@ -4339,12 +4348,12 @@ private OneOrMany GetMethodGroupSemanticSymbols( break; case BoundKind.NameOfOperator: - symbols = OneOrMany.Create(methodGroup); + symbols = OneOrMany.Create(memberGroup); resultKind = resultKind.WorseResultKind(LookupResultKind.MemberGroup); break; default: - symbols = OneOrMany.Create(methodGroup); + symbols = OneOrMany.Create(memberGroup); if (symbols.Count > 0) { resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); @@ -4352,14 +4361,14 @@ private OneOrMany GetMethodGroupSemanticSymbols( break; } } - else if (methodGroup.Length == 1 && !boundNode.HasAnyErrors) + else if (memberGroup.Length == 1 && !boundNode.HasAnyErrors) { // During speculative binding, there won't be a parent bound node. The parent bound // node may also be absent if the syntactic parent has errors or if one is simply // not specified (see SemanticModel.GetSymbolInfoForNode). However, if there's exactly // one candidate, then we should probably succeed. - symbols = OneOrMany.Create(methodGroup); + symbols = OneOrMany.Create(memberGroup); if (symbols.Count > 0) { resultKind = resultKind.WorseResultKind(LookupResultKind.OverloadResolutionFailure); @@ -4371,7 +4380,7 @@ private OneOrMany GetMethodGroupSemanticSymbols( // If we didn't find a better set of symbols, then assume this is a method group that didn't // get resolved. Return all members of the method group, with a resultKind of OverloadResolutionFailure // (unless the method group already has a worse result kind). - symbols = OneOrMany.Create(methodGroup); + symbols = OneOrMany.Create(memberGroup); if (!isDynamic && resultKind > LookupResultKind.OverloadResolutionFailure) { resultKind = LookupResultKind.OverloadResolutionFailure; @@ -4558,10 +4567,10 @@ private static ParameterSymbol FindNamedParameter(ImmutableArray GetReducedAndFilteredMethodGroupSymbols(Binder binder, BoundMethodGroup node) + internal static ImmutableArray GetReducedAndFilteredMethodGroupSymbols(Binder binder, BoundMethodGroup node) { - var methods = ArrayBuilder.GetInstance(); - var filteredMethods = ArrayBuilder.GetInstance(); + var members = ArrayBuilder.GetInstance(); + var filteredMembers = ArrayBuilder.GetInstance(); var resultKind = LookupResultKind.Empty; var typeArguments = node.TypeArgumentsOpt; @@ -4576,12 +4585,12 @@ internal static ImmutableArray GetReducedAndFilteredMethodGroupSym foreach (var method in nonHiddenMethods) { - MergeReducedAndFilteredMethodGroupSymbol( - methods, - filteredMethods, + MergeReducedAndFilteredSymbol( + members, + filteredMembers, new SingleLookupResult(node.ResultKind, method, node.LookupError), typeArguments, - null, + receiverType: null, ref resultKind, binder.Compilation); } @@ -4591,12 +4600,12 @@ internal static ImmutableArray GetReducedAndFilteredMethodGroupSym var otherSymbol = node.LookupSymbolOpt; if (((object)otherSymbol != null) && (otherSymbol.Kind == SymbolKind.Method)) { - MergeReducedAndFilteredMethodGroupSymbol( - methods, - filteredMethods, + MergeReducedAndFilteredSymbol( + members, + filteredMembers, new SingleLookupResult(node.ResultKind, otherSymbol, node.LookupError), typeArguments, - null, + receiverType: null, ref resultKind, binder.Compilation); } @@ -4605,8 +4614,8 @@ internal static ImmutableArray GetReducedAndFilteredMethodGroupSym var receiver = node.ReceiverOpt; var name = node.Name; - // Extension methods, all scopes. - if (node.SearchExtensionMethods) + // Extension members, all scopes. + if (node.SearchExtensions && receiver.Type is { } receiverType) { Debug.Assert(receiver != null); int arity; @@ -4623,84 +4632,78 @@ internal static ImmutableArray GetReducedAndFilteredMethodGroupSym } binder = binder.WithAdditionalFlags(BinderFlags.SemanticModel); - foreach (var scope in new ExtensionMethodScopes(binder)) + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + + var singleLookupResults = ArrayBuilder.GetInstance(); + foreach (var scope in new ExtensionScopes(binder)) { - var extensionMethods = ArrayBuilder.GetInstance(); - var otherBinder = scope.Binder; - otherBinder.GetCandidateExtensionMethods(extensionMethods, - name, - arity, - options, - originalBinder: binder); - - foreach (var method in extensionMethods) + singleLookupResults.Clear(); + scope.Binder.EnumerateAllExtensionMembersInSingleBinder(singleLookupResults, name, arity, options, originalBinder: binder, useSiteInfo: ref discardedUseSiteInfo, classicExtensionUseSiteInfo: ref discardedUseSiteInfo); + + foreach (SingleLookupResult singleLookupResult in singleLookupResults) { - var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; - MergeReducedAndFilteredMethodGroupSymbol( - methods, - filteredMethods, - binder.CheckViability(method, arity, options, accessThroughType: null, diagnose: false, useSiteInfo: ref discardedUseSiteInfo), + if (singleLookupResult.Symbol is not (MethodSymbol or PropertySymbol)) + { + continue; + } + + MergeReducedAndFilteredSymbol( + members, + filteredMembers, + singleLookupResult, typeArguments, - receiver.Type, + receiverType, ref resultKind, binder.Compilation); } - - extensionMethods.Free(); } + + singleLookupResults.Free(); } - methods.Free(); - return filteredMethods.ToImmutableAndFree(); + members.Free(); + return filteredMembers.ToImmutableAndFree(); } - // Reduce extension methods to their reduced form, and remove: +#nullable enable + // Reduce classic extension methods to their reduced form, and remove: // a) Extension methods are aren't applicable to receiverType // including constraint checking. // b) Duplicate methods // c) Methods that are hidden or overridden by another method in the group. - private static bool AddReducedAndFilteredMethodGroupSymbol( - ArrayBuilder methods, - ArrayBuilder filteredMethods, - MethodSymbol method, + // For new extension members, infer type arguments for the extension declaration based on the receiver type, + // perform the substitution, and remove: + // a) Members that would break constraints + // b) Members that are not applicable to the receiver type. + private static bool AddReducedAndFilteredSymbol( + ArrayBuilder members, + ArrayBuilder filteredMembers, + Symbol member, ImmutableArray typeArguments, TypeSymbol receiverType, CSharpCompilation compilation) { - MethodSymbol constructedMethod; - if (!typeArguments.IsDefaultOrEmpty && method.Arity == typeArguments.Length) + Symbol? substitutedMember = member.GetReducedAndFilteredSymbol(typeArguments, receiverType, compilation, checkFullyInferred: false); + if (substitutedMember is null) { - constructedMethod = method.Construct(typeArguments); - Debug.Assert((object)constructedMethod != null); - } - else - { - constructedMethod = method; - } - - if ((object)receiverType != null) - { - constructedMethod = constructedMethod.ReduceExtensionMethod(receiverType, compilation); - if ((object)constructedMethod == null) - { - return false; - } + return false; } // Don't add exact duplicates. - if (filteredMethods.Contains(constructedMethod)) + if (filteredMembers.Contains(substitutedMember)) { return false; } - methods.Add(method); - filteredMethods.Add(constructedMethod); + members.Add(member); + filteredMembers.Add(substitutedMember); return true; } +#nullable disable - private static void MergeReducedAndFilteredMethodGroupSymbol( - ArrayBuilder methods, - ArrayBuilder filteredMethods, + private static void MergeReducedAndFilteredSymbol( + ArrayBuilder members, + ArrayBuilder filteredMembers, SingleLookupResult singleResult, ImmutableArray typeArguments, TypeSymbol receiverType, @@ -4712,65 +4715,63 @@ private static void MergeReducedAndFilteredMethodGroupSymbol( return; } - Debug.Assert(singleResult.Symbol.Kind == SymbolKind.Method); + Symbol member = singleResult.Symbol; - var singleKind = singleResult.Kind; + LookupResultKind singleKind = singleResult.Kind; if (resultKind > singleKind) { return; } else if (resultKind < singleKind) { - methods.Clear(); - filteredMethods.Clear(); + members.Clear(); + filteredMembers.Clear(); resultKind = LookupResultKind.Empty; } - var method = (MethodSymbol)singleResult.Symbol; - if (AddReducedAndFilteredMethodGroupSymbol(methods, filteredMethods, method, typeArguments, receiverType, compilation)) + if (AddReducedAndFilteredSymbol(members, filteredMembers, member, typeArguments, receiverType, compilation)) { - Debug.Assert(methods.Count > 0); + Debug.Assert(members.Count > 0); if (resultKind < singleKind) { resultKind = singleKind; } } - Debug.Assert((methods.Count == 0) == (resultKind == LookupResultKind.Empty)); - Debug.Assert(methods.Count == filteredMethods.Count); + Debug.Assert((members.Count == 0) == (resultKind == LookupResultKind.Empty)); + Debug.Assert(members.Count == filteredMembers.Count); } /// - /// If the call represents an extension method invocation with an explicit receiver, return the original + /// If the call represents a classic extension method invocation with an explicit receiver, return the original /// methods as ReducedExtensionMethodSymbols. Otherwise, return the original methods unchanged. /// - private static OneOrMany CreateReducedExtensionMethodsFromOriginalsIfNecessary(BoundCall call, CSharpCompilation compilation) + private static OneOrMany CreateReducedAndFilteredSymbolsFromOriginals(BoundCall call, CSharpCompilation compilation) { var methods = call.OriginalMethodsOpt; - TypeSymbol extensionThisType = null; + TypeSymbol receiverType = null; Debug.Assert(!methods.IsDefault); + // Note: A call including new extension members may be marked as InvokedAsExtensionMethod in error scenarios if (call.InvokedAsExtensionMethod) { - // If the call was invoked as an extension method, the receiver - // should be non-null and all methods should be extension methods. if (call.ReceiverOpt != null) { - extensionThisType = call.ReceiverOpt.Type; + receiverType = call.ReceiverOpt.Type; } else { - extensionThisType = call.Arguments[0].Type; + receiverType = call.Arguments[0].Type; } - Debug.Assert((object)extensionThisType != null); + Debug.Assert((object)receiverType != null); } - var methodBuilder = ArrayBuilder.GetInstance(); - var filteredMethodBuilder = ArrayBuilder.GetInstance(); + var methodBuilder = ArrayBuilder.GetInstance(); + var filteredMethodBuilder = ArrayBuilder.GetInstance(); foreach (var method in FilterOverriddenOrHiddenMethods(methods)) { - AddReducedAndFilteredMethodGroupSymbol(methodBuilder, filteredMethodBuilder, method, default(ImmutableArray), extensionThisType, compilation); + AddReducedAndFilteredSymbol(methodBuilder, filteredMethodBuilder, method, typeArguments: default, receiverType, compilation); } methodBuilder.Free(); return filteredMethodBuilder.ToOneOrManyAndFree(); diff --git a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs index 04d209c27c4be..65979e5f2a402 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MethodBodySemanticModel.cs @@ -98,6 +98,7 @@ internal override BoundNode Bind(Binder binder, CSharpSyntaxNode node, BindingDi case SyntaxKind.CompilationUnit: case SyntaxKind.RecordDeclaration: case SyntaxKind.ClassDeclaration: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for semantic model return binder.BindMethodBody(node, diagnostics); } diff --git a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs index 10b7050bbd0b3..412116cbb00d3 100644 --- a/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/SyntaxTreeSemanticModel.cs @@ -801,6 +801,7 @@ private MemberSemanticModel GetMemberModel(int position) break; case SyntaxKind.ClassDeclaration: case SyntaxKind.RecordDeclaration: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for semantic model { var typeDecl = (TypeDeclarationSyntax)memberDecl; @@ -871,6 +872,7 @@ internal override MemberSemanticModel GetMemberModel(SyntaxNode node) case SyntaxKind.ClassDeclaration: case SyntaxKind.RecordDeclaration: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for semantic model { var typeDecl = (TypeDeclarationSyntax)memberDecl; return typeDecl.ParameterList is object && @@ -1089,6 +1091,7 @@ private MemberSemanticModel CreateMemberModel(CSharpSyntaxNode node) case SyntaxKind.ClassDeclaration: case SyntaxKind.RecordDeclaration: + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : likely needs work for semantic model { SynthesizedPrimaryConstructor symbol = TryGetSynthesizedPrimaryConstructor((TypeDeclarationSyntax)node); @@ -1378,10 +1381,37 @@ private NamedTypeSymbol GetDeclaredType(BaseTypeDeclarationSyntax declarationSyn { Debug.Assert(declarationSyntax != null); + if (declarationSyntax is ExtensionDeclarationSyntax extensionDeclaration) + { + return GetDeclaredExtension(extensionDeclaration); + } + var name = declarationSyntax.Identifier.ValueText; return GetDeclaredNamedType(declarationSyntax, name); } + private NamedTypeSymbol GetDeclaredExtension(ExtensionDeclarationSyntax extensionDeclaration) + { + Debug.Assert(extensionDeclaration != null); + + var container = GetDeclaredTypeMemberContainer(extensionDeclaration); + Debug.Assert(container is not null); + + // look for any extension declaration with same declaration location + var collection = container.GetMembersUnordered(); + var declarationSpan = extensionDeclaration.Span; + foreach (var symbol in collection) + { + if (symbol is TypeSymbol { IsExtension: true } && symbol.HasLocationContainedWithin(this.SyntaxTree, declarationSpan, out var wasZeroWidthMatch)) + { + if (!wasZeroWidthMatch) + return (NamedTypeSymbol)symbol; + } + } + + return null; + } + private NamedTypeSymbol GetDeclaredType(DelegateDeclarationSyntax declarationSyntax) { Debug.Assert(declarationSyntax != null); @@ -1800,13 +1830,7 @@ private Symbol GetDeclaredMember(NamespaceOrTypeSymbol container, TextSpan decla } // Handle the case of the implementation of a partial member. - Symbol partial = symbol switch - { - MethodSymbol method => method.PartialImplementationPart, - SourcePropertySymbol property => property.PartialImplementationPart, - _ => null - }; - + Symbol partial = symbol.GetPartialImplementationPart(); if ((object)partial != null) { var loc = partial.GetFirstLocation(); @@ -2003,6 +2027,37 @@ public override IAliasSymbol GetDeclaredSymbol( return builder.ToImmutableAndFree(); } + private ParameterSymbol GetExtensionParameterSymbol( + ParameterSyntax parameter, + CancellationToken cancellationToken) + { + Debug.Assert(parameter != null); + + if (parameter.Parent is not ParameterListSyntax { Parent: ExtensionDeclarationSyntax extensionDecl }) + { + return null; + } + + INamedTypeSymbol extension = GetDeclaredSymbol(extensionDecl, cancellationToken); + if (extension is null) + { + return null; + } + + IParameterSymbol extensionParameter = extension.ExtensionParameter; + foreach (var location in extensionParameter.Locations) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (location.SourceTree == this.SyntaxTree && parameter.Span.Contains(location.SourceSpan)) + { + return extensionParameter.GetSymbol(); + } + } + + return null; + } + private ParameterSymbol GetMethodParameterSymbol( ParameterSyntax parameter, CancellationToken cancellationToken) @@ -2125,7 +2180,8 @@ private ParameterSymbol GetDelegateParameterSymbol( return GetMethodParameterSymbol(declarationSyntax, cancellationToken) ?? GetIndexerParameterSymbol(declarationSyntax, cancellationToken) ?? - GetDelegateParameterSymbol(declarationSyntax, cancellationToken); + GetDelegateParameterSymbol(declarationSyntax, cancellationToken) ?? + GetExtensionParameterSymbol(declarationSyntax, cancellationToken); } /// diff --git a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs index edd5c97d36375..ec6661e4541b2 100644 --- a/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/DocumentationCommentCompiler.cs @@ -258,13 +258,7 @@ public override void DefaultVisit(Symbol symbol) bool shouldSkipPartialDefinitionComments = false; if (symbol.IsPartialDefinition()) { - Symbol? implementationPart = symbol switch - { - MethodSymbol method => method.PartialImplementationPart, - SourcePropertySymbol property => property.PartialImplementationPart, - _ => null - }; - + Symbol? implementationPart = symbol.GetPartialImplementationPart(); if (implementationPart is not null) { Visit(implementationPart); diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs index 8c793b4eb6f45..7a4d807a82fcc 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.Lowered.cs @@ -259,7 +259,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, } } - internal sealed partial class SynthesizedSealedPropertyAccessor : SynthesizedInstanceMethodSymbol + internal sealed partial class SynthesizedSealedPropertyAccessor : SynthesizedMethodSymbol { internal override bool SynthesizesLoweredBoundBody { @@ -296,7 +296,7 @@ internal override void GenerateMethodBody(TypeCompilationState compilationState, internal abstract partial class MethodToClassRewriter { - private sealed partial class BaseMethodWrapperSymbol : SynthesizedMethodBaseSymbol + internal sealed partial class BaseMethodWrapperSymbol : SynthesizedMethodBaseSymbol { internal sealed override bool GenerateDebugInfo { diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index aa022352a842e..0036b26480517 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -271,6 +271,7 @@ internal static MethodSymbol GetEntryPoint(CSharpCompilation compilation, PEModu var loweredBody = LowerBodyOrInitializer( synthesizedEntryPoint, + extensionImplementationMethod: null, methodOrdinal, body, previousSubmissionFields: null, @@ -535,7 +536,17 @@ private void CompileNamedType(NamedTypeSymbol containingType) method.MethodKind == MethodKind.StaticConstructor ? processedStaticInitializers : default(Binder.ProcessedFieldInitializers); - CompileMethod(method, memberOrdinal, ref processedInitializers, synthesizedSubmissionFields, compilationState); + // Compile extension methods without implementation normally, to bind the body and get errors. + // Also, extension marker method should always be compiled normally. + if (containingType.IsExtension && + method.TryGetCorrespondingExtensionImplementationMethod() is not null) + { + EmitSkeletonMethodInExtension(method); + } + else + { + CompileMethod(method, memberOrdinal, ref processedInitializers, synthesizedSubmissionFields, compilationState); + } break; } @@ -893,6 +904,16 @@ private void CompileMethod( SynthesizedSubmissionFields previousSubmissionFields, TypeCompilationState compilationState) { + var extensionImplementation = methodSymbol as SourceExtensionImplementationMethodSymbol; + + if (extensionImplementation is not null) + { + // Binding and analysis is performed on the user declared method + methodSymbol = extensionImplementation.UnderlyingMethod; + + Debug.Assert(!methodSymbol.SynthesizesLoweredBoundBody); + } + _cancellationToken.ThrowIfCancellationRequested(); SourceMemberMethodSymbol sourceMethod = methodSymbol as SourceMemberMethodSymbol; @@ -1140,6 +1161,7 @@ forSemanticModel.Syntax is { } semanticModelSyntax && }); } + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Ensure we are not messing up relative order of events for extension members (with relation to events for enclosing types, etc.) _compilation.EventQueue.TryEnqueue(new SymbolDeclaredCompilationEvent( _compilation, methodSymbol, semanticModelWithCachedBoundNodes)); } @@ -1179,6 +1201,7 @@ forSemanticModel.Syntax is { } semanticModelSyntax && { loweredBodyOpt = LowerBodyOrInitializer( methodSymbol, + extensionImplementation, methodOrdinal, flowAnalyzedBody, previousSubmissionFields, @@ -1255,6 +1278,7 @@ forSemanticModel.Syntax is { } semanticModelSyntax && BoundStatement lowered = LowerBodyOrInitializer( methodSymbol, + extensionImplementation, methodOrdinal, analyzedInitializers, previousSubmissionFields, @@ -1325,7 +1349,7 @@ forSemanticModel.Syntax is { } semanticModelSyntax && var emittedBody = GenerateMethodBody( _moduleBeingBuiltOpt, - methodSymbol, + extensionImplementation ?? methodSymbol, methodOrdinal, boundBody, lambdaDebugInfoBuilder.ToImmutable(), @@ -1341,7 +1365,7 @@ forSemanticModel.Syntax is { } semanticModelSyntax && codeCoverageSpans, entryPointOpt: null); - _moduleBeingBuiltOpt.SetMethodBody(methodSymbol.PartialDefinitionPart ?? methodSymbol, emittedBody); + _moduleBeingBuiltOpt.SetMethodBody(GetSymbolForEmittedBody(extensionImplementation ?? methodSymbol), emittedBody); } } @@ -1362,9 +1386,63 @@ forSemanticModel.Syntax is { } semanticModelSyntax && } } + private void EmitSkeletonMethodInExtension(MethodSymbol methodSymbol) + { + if (!_emitMethodBodies) + { + return; + } + + ILBuilder builder = new ILBuilder(_moduleBeingBuiltOpt, new LocalSlotManager(slotAllocator: null), OptimizationLevel.Release, areLocalsZeroed: false); + + // Emit methods in extensions as skeletons: + // => throw null; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Should we throw NotSupportedException instead? + builder.EmitOpCode(System.Reflection.Metadata.ILOpCode.Ldnull); + builder.EmitThrow(isRethrow: false); + builder.Realize(); + + _moduleBeingBuiltOpt.TestData?.SetMethodILBuilder(methodSymbol, builder); + + _moduleBeingBuiltOpt.SetMethodBody( + methodSymbol, + new MethodBody( + builder.RealizedIL, + maxStack: 1, + methodSymbol.GetCciAdapter(), + new DebugId(ordinal: -1, _moduleBeingBuiltOpt.CurrentGenerationOrdinal), + locals: [], + SequencePointList.Empty, + debugDocumentProvider: null, + exceptionHandlers: ImmutableArray.Empty, + areLocalsZeroed: false, + hasStackalloc: false, + localScopes: ImmutableArray.Empty, + hasDynamicLocalVariables: false, + importScopeOpt: null, + lambdaDebugInfo: ImmutableArray.Empty, + orderedLambdaRuntimeRudeEdits: ImmutableArray.Empty, + closureDebugInfo: ImmutableArray.Empty, + stateMachineTypeNameOpt: null, + stateMachineHoistedLocalScopes: default(ImmutableArray), + stateMachineHoistedLocalSlots: default(ImmutableArray), + stateMachineAwaiterSlots: default(ImmutableArray), + StateMachineStatesDebugInfo.Create(variableSlotAllocator: null, ImmutableArray.Empty), + stateMachineMoveNextDebugInfoOpt: null, + codeCoverageSpans: ImmutableArray.Empty, + isPrimaryConstructor: false) + ); + } + + private static MethodSymbol GetSymbolForEmittedBody(MethodSymbol methodSymbol) + { + return methodSymbol.PartialDefinitionPart ?? methodSymbol; + } + // internal for testing internal static BoundStatement LowerBodyOrInitializer( MethodSymbol method, + SourceExtensionImplementationMethodSymbol extensionImplementationMethod, int methodOrdinal, BoundStatement body, SynthesizedSubmissionFields previousSubmissionFields, @@ -1413,6 +1491,22 @@ internal static BoundStatement LowerBodyOrInitializer( return loweredBody; } + if (extensionImplementationMethod is not null) + { + var extensionRewriter = new ExtensionMethodBodyRewriter(method, extensionImplementationMethod); + loweredBody = (BoundStatement)extensionRewriter.Visit(loweredBody); + method = extensionImplementationMethod; + } + else + { + loweredBody = ExtensionMethodReferenceRewriter.Rewrite(loweredBody); + } + + if (loweredBody.HasErrors) + { + return loweredBody; + } + if (sawAwaitInExceptionHandler) { // If we have awaits in handlers, we need to @@ -1601,9 +1695,6 @@ private static MethodBody GenerateMethodBody( return null; } - // We will only save the IL builders when running tests. - moduleBuilder.TestData?.SetMethodILBuilder(method, builder.GetSnapshot()); - var stateMachineHoistedLocalSlots = default(ImmutableArray); var stateMachineAwaiterSlots = default(ImmutableArray); if (optimizations == OptimizationLevel.Debug && (object)stateMachineTypeOpt != null) @@ -1613,10 +1704,15 @@ private static MethodBody GenerateMethodBody( Debug.Assert(!diagnostics.HasAnyErrors()); } + MethodSymbol methodBodyParentSymbol = GetSymbolForEmittedBody(method); + + // We will only save the IL builders when running tests. + moduleBuilder.TestData?.SetMethodILBuilder(methodBodyParentSymbol, builder.GetSnapshot()); + return new MethodBody( builder.RealizedIL, builder.MaxStack, - (method.PartialDefinitionPart ?? method).GetCciAdapter(), + methodBodyParentSymbol.GetCciAdapter(), variableSlotAllocatorOpt?.MethodId ?? new DebugId(methodOrdinal, moduleBuilder.CurrentGenerationOrdinal), localVariables, builder.RealizedSequencePoints, diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs index 4e24d98fdc81c..20d2c9b3156ee 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationKind.cs @@ -23,7 +23,8 @@ internal enum DeclarationKind : byte Submission, ImplicitClass, Record, - RecordStruct + RecordStruct, + Extension, } internal static partial class EnumConversions @@ -42,6 +43,7 @@ internal static DeclarationKind ToDeclarationKind(this SyntaxKind kind) case SyntaxKind.DelegateDeclaration: return DeclarationKind.Delegate; case SyntaxKind.RecordDeclaration: return DeclarationKind.Record; case SyntaxKind.RecordStructDeclaration: return DeclarationKind.RecordStruct; + case SyntaxKind.ExtensionDeclaration: return DeclarationKind.Extension; default: throw ExceptionUtilities.UnexpectedValue(kind); } diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index 966c576b4e75c..f880a1884ad12 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -115,6 +115,7 @@ DeclarationKind.Submission or DeclarationKind.ImplicitClass or DeclarationKind.Record or DeclarationKind.RecordStruct => true, + DeclarationKind.Extension => true, _ => throw ExceptionUtilities.UnexpectedValue(typeDeclaration.Kind) }; @@ -680,6 +681,11 @@ public override SingleNamespaceOrTypeDeclaration VisitRecordDeclaration(RecordDe return VisitTypeDeclaration(node, declarationKind); } + public override SingleNamespaceOrTypeDeclaration VisitExtensionDeclaration(ExtensionDeclarationSyntax node) + { + return VisitTypeDeclaration(node, DeclarationKind.Extension); + } + private SingleTypeDeclaration VisitTypeDeclaration(TypeDeclarationSyntax node, DeclarationKind kind) { var declFlags = node.AttributeLists.Any() @@ -766,14 +772,15 @@ private SingleTypeDeclaration VisitTypeDeclaration(TypeDeclarationSyntax node, D } } + bool isExtension = kind == DeclarationKind.Extension; return new SingleTypeDeclaration( kind: kind, - name: node.Identifier.ValueText, + name: isExtension ? "" : node.Identifier.ValueText, arity: node.Arity, modifiers: modifiers, declFlags: declFlags, syntaxReference: _syntaxTree.GetReference(node), - nameLocation: new SourceLocation(node.Identifier), + nameLocation: new SourceLocation(isExtension ? node.Keyword : node.Identifier), memberNames: memberNames, children: VisitTypeChildren(node), diagnostics: diagnostics.ToReadOnlyAndFree(), @@ -917,6 +924,7 @@ private BoxedMemberNames GetNonTypeMemberNames( bool hasPrimaryCtor = false) { bool anyMethodHadExtensionSyntax = false; + bool anyExtensionDeclarationSyntax = false; bool anyMemberHasAttributes = false; bool anyNonTypeMembers = false; bool anyRequiredMembers = false; @@ -936,6 +944,11 @@ private BoxedMemberNames GetNonTypeMemberNames( anyMethodHadExtensionSyntax = true; } + if (!anyExtensionDeclarationSyntax && member.Kind == SyntaxKind.ExtensionDeclaration) + { + anyExtensionDeclarationSyntax = true; + } + if (!anyMemberHasAttributes && CheckMemberForAttributes(member)) { anyMemberHasAttributes = true; @@ -947,7 +960,7 @@ private BoxedMemberNames GetNonTypeMemberNames( } // Break early if we've hit all sorts of members. - if (anyNonTypeMembers && anyMethodHadExtensionSyntax && anyMemberHasAttributes && anyRequiredMembers) + if (anyNonTypeMembers && anyMethodHadExtensionSyntax && anyExtensionDeclarationSyntax && anyMemberHasAttributes && anyRequiredMembers) { break; } @@ -958,6 +971,11 @@ private BoxedMemberNames GetNonTypeMemberNames( declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.AnyMemberHasExtensionMethodSyntax; } + if (anyExtensionDeclarationSyntax) + { + declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.AnyExtensionDeclarationSyntax; + } + if (anyMemberHasAttributes) { declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.AnyMemberHasAttributes; @@ -1089,6 +1107,7 @@ private static bool CheckMemberForAttributes(Syntax.InternalSyntax.CSharpSyntaxN case SyntaxKind.EnumDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: return (((Syntax.InternalSyntax.BaseTypeDeclarationSyntax)member).AttributeLists).Any(); case SyntaxKind.DelegateDeclaration: diff --git a/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs index 4521376e4b5d5..293c5765bde10 100644 --- a/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs @@ -78,6 +78,7 @@ public ImmutableArray> GetAttributeDeclarations( case SyntaxKind.InterfaceDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: attributesSyntaxList = ((TypeDeclarationSyntax)typeDecl).AttributeLists; break; @@ -129,6 +130,20 @@ public bool ContainsExtensionMethods } } + public bool ContainsExtensionDeclarations + { + get + { + foreach (var decl in this.Declarations) + { + if (decl.AnyExtensionDeclarationSyntax) + return true; + } + + return false; + } + } + public bool HasPrimaryConstructor { get @@ -253,7 +268,8 @@ public ICollection MemberNames internal string GetDebuggerDisplay() { - return $"{nameof(MergedTypeDeclaration)} {Name}"; + string identifier = (Kind is DeclarationKind.Extension) ? "extension" : Name; + return $"{nameof(MergedTypeDeclaration)} {identifier}"; } } } diff --git a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs index 3ef6b8c3ac27b..f51334bd3baf8 100644 --- a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs @@ -67,6 +67,11 @@ internal enum TypeDeclarationFlags : ushort HasRequiredMembers = 1 << 10, HasPrimaryConstructor = 1 << 11, + + /// + /// Set when is present. + /// + AnyExtensionDeclarationSyntax = 1 << 12, } internal SingleTypeDeclaration( @@ -136,6 +141,14 @@ public bool AnyMemberHasExtensionMethodSyntax } } + public bool AnyExtensionDeclarationSyntax + { + get + { + return (_flags & TypeDeclarationFlags.AnyExtensionDeclarationSyntax) != 0; + } + } + public bool HasAnyAttributes { get @@ -260,9 +273,9 @@ public bool Equals(TypeDeclarationIdentity other) return false; } - if (thisDecl._kind == DeclarationKind.Enum || thisDecl._kind == DeclarationKind.Delegate) + if (thisDecl._kind is DeclarationKind.Enum or DeclarationKind.Delegate or DeclarationKind.Extension) { - // oh, so close, but enums and delegates cannot be partial + // oh, so close, but enums, delegates and extensions cannot be partial return false; } diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs index 19a7bb6dc27da..9fb7c177d3854 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs @@ -287,7 +287,7 @@ Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context) Debug.Assert(((Cci.ITypeReference)this).AsTypeDefinition(context) != null); NamedTypeSymbol baseType = AdaptedNamedTypeSymbol.BaseTypeNoUseSiteDiagnostics; - if (AdaptedNamedTypeSymbol.IsScriptClass) + if (AdaptedNamedTypeSymbol.IsScriptClass || AdaptedNamedTypeSymbol.IsExtension) // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should have checked the presence of System.Object { // although submission and scripts semantically doesn't have a base we need to emit one into metadata: Debug.Assert((object)baseType == null); @@ -628,7 +628,7 @@ bool Cci.ITypeDefinition.IsSealed { Debug.Assert((object)method != null); - if ((alwaysIncludeConstructors && method.MethodKind == MethodKind.Constructor) || method.GetCciAdapter().ShouldInclude(context)) + if ((alwaysIncludeConstructors && method.MethodKind == MethodKind.Constructor) || method is SynthesizedExtensionMarker || method.GetCciAdapter().ShouldInclude(context)) { yield return method.GetCciAdapter(); } @@ -779,6 +779,11 @@ string Cci.INamedEntity.Name { get { + if (AdaptedNamedTypeSymbol.IsExtension) + { + return AdaptedNamedTypeSymbol.ExtensionName; + } + string unsuffixedName = AdaptedNamedTypeSymbol.Name; // CLR generally allows names with dots, however some APIs like IMetaDataImport diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index f19d7f8e9f6dd..eec2ef91da292 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -301,7 +301,7 @@ private void ValidateReferencedAssembly(AssemblySymbol assembly, AssemblyReferen /// private static void GetDocumentsForMethodsAndNestedTypes(PooledHashSet documentList, ArrayBuilder typesToProcess, EmitContext context) { - // Temporarily disable assert to unblock getting net8.0 teststing re-nabled on Unix. Will + // Temporarily disable assert to unblock getting net8.0 testing re-enabled on Unix. Will // remove this shortly. // https://github.com/dotnet/roslyn/issues/71571 // Debug.Assert(!context.MetadataOnly); @@ -2090,5 +2090,29 @@ internal bool TryGetTranslatedImports(ImportChain chain, out ImmutableArray false, }; #pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 38c4399efd622..f2a30c73742ed 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -294,8 +294,13 @@ internal enum MessageID IDS_FeatureOverloadResolutionPriority = MessageBase + 12848, IDS_FeatureFirstClassSpan = MessageBase + 12849, + IDS_FeatureUnboundGenericTypesInNameof = MessageBase + 12850, IDS_FeatureSimpleLambdaParameterModifiers = MessageBase + 12851, + + IDS_FeaturePartialEventsAndConstructors = MessageBase + 12852, + IDS_FeatureExtensions = MessageBase + 12853, + IDS_FeatureNullConditionalAssignment = MessageBase + 12854, } // Message IDs may refer to strings that need to be localized. @@ -480,6 +485,9 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureFirstClassSpan: case MessageID.IDS_FeatureUnboundGenericTypesInNameof: case MessageID.IDS_FeatureSimpleLambdaParameterModifiers: + case MessageID.IDS_FeaturePartialEventsAndConstructors: + case MessageID.IDS_FeatureExtensions: + case MessageID.IDS_FeatureNullConditionalAssignment: return LanguageVersion.Preview; // C# 13.0 features. diff --git a/src/Compilers/CSharp/Portable/Errors/SameDiagnosticComparer.cs b/src/Compilers/CSharp/Portable/Errors/SameDiagnosticComparer.cs new file mode 100644 index 0000000000000..2972d0ea70542 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Errors/SameDiagnosticComparer.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal sealed class SameDiagnosticComparer : EqualityComparer +{ + public static readonly SameDiagnosticComparer Instance = new SameDiagnosticComparer(); + public override bool Equals(Diagnostic? x, Diagnostic? y) => x is null ? y is null : x.Equals(y); + public override int GetHashCode(Diagnostic obj) => obj.GetHashCode(); +} diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 9331dab2b5c2e..43b5e7678aa68 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1129,7 +1129,7 @@ public override BoundNode VisitConvertedTupleLiteral(BoundConvertedTupleLiteral private BoundNode VisitTupleExpression(BoundTupleExpression node) { - VisitArguments(node.Arguments, default(ImmutableArray), null); + VisitArguments(node.Arguments, default(ImmutableArray), null, default, false); return null; } @@ -1142,7 +1142,7 @@ public override BoundNode VisitTupleBinaryOperator(BoundTupleBinaryOperator node public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node) { - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, node.ArgsToParamsOpt, node.Expanded); VisitRvalue(node.InitializerExpressionOpt); return null; } @@ -1150,7 +1150,7 @@ public override BoundNode VisitDynamicObjectCreationExpression(BoundDynamicObjec public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { VisitRvalue(node.Receiver); - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default, false); return null; } @@ -1163,7 +1163,7 @@ public override BoundNode VisitDynamicMemberAccess(BoundDynamicMemberAccess node public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node) { VisitRvalue(node.Expression); - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default, false); return null; } @@ -1246,7 +1246,7 @@ public override BoundNode VisitArgList(BoundArgList node) public override BoundNode VisitArgListOperator(BoundArgListOperator node) { // When we have M(__arglist(x, y, z)) we must visit x, y and z. - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, null, default, false); return null; } @@ -1387,7 +1387,7 @@ void visitArgumentsAndCompleteAnalysis(BoundCall node) VisitLocalFunctionUse(localFunc, node.Syntax, isCall: true); } - VisitArgumentsAfterCall(node.Arguments, node.ArgumentRefKindsOpt, node.Method); + VisitArgumentsAfterCall(node.Arguments, node.ArgumentRefKindsOpt, node.Method, node.ArgsToParamsOpt, node.Expanded); VisitReceiverAfterCall(node.ReceiverOpt, node.Method); } } @@ -1481,7 +1481,7 @@ public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) { var method = GetReadMethod(node.Indexer); VisitReceiverBeforeCall(node.ReceiverOpt, method); - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method, node.ArgsToParamsOpt, node.Expanded); if ((object)method != null) { VisitReceiverAfterCall(node.ReceiverOpt, method); @@ -1513,11 +1513,11 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera /// /// Do not call for a local function. /// - protected virtual void VisitArguments(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method) + protected virtual void VisitArguments(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method, ImmutableArray argsToParamsOpt, bool expanded) { Debug.Assert(method?.OriginalDefinition.MethodKind != MethodKind.LocalFunction); VisitArgumentsBeforeCall(arguments, refKindsOpt); - VisitArgumentsAfterCall(arguments, refKindsOpt, method); + VisitArgumentsAfterCall(arguments, refKindsOpt, method, argsToParamsOpt, expanded); } private void VisitArgumentsBeforeCall(ImmutableArray arguments, ImmutableArray refKindsOpt) @@ -1537,10 +1537,11 @@ private void VisitArgumentsBeforeCall(ImmutableArray arguments, } } +#nullable enable /// /// Writes ref and out parameters /// - private void VisitArgumentsAfterCall(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method) + private void VisitArgumentsAfterCall(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol? method, ImmutableArray argsToParamsOpt, bool expanded) { for (int i = 0; i < arguments.Length; i++) { @@ -1553,6 +1554,11 @@ private void VisitArgumentsAfterCall(ImmutableArray arguments, case RefKindExtensions.StrictIn: break; case RefKind.Ref: + if (method is null || Binder.GetCorrespondingParameter(i, method.Parameters, argsToParamsOpt, expanded)?.RefKind.IsWritableReference() != false) + { + goto case RefKind.Out; + } + break; case RefKind.Out: // passing as a byref argument is also a potential write WriteArgument(arguments[i], refKind, method); @@ -1562,6 +1568,7 @@ private void VisitArgumentsAfterCall(ImmutableArray arguments, } } } +#nullable disable protected static RefKind GetRefKind(ImmutableArray refKindsOpt, int index) { @@ -1635,7 +1642,7 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE static bool ignoreReceiver(MethodSymbol method) { // static methods that aren't extensions get an implicit `this` receiver that should be ignored - return method.IsStatic && !method.IsExtensionMethod; + return method.IsStatic && !method.IsExtensionMethod; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions } } @@ -2060,7 +2067,7 @@ protected virtual void VisitLvalueParameter(BoundParameter node) public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Constructor); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.Constructor, node.ArgsToParamsOpt, node.Expanded); VisitRvalue(node.InitializerExpressionOpt); return null; } @@ -3193,6 +3200,11 @@ public override BoundNode VisitSequencePointWithSpan(BoundSequencePointWithSpan return null; } + public override BoundNode VisitModuleCancellationTokenExpression(ModuleCancellationTokenExpression node) + { + return null; + } + public override BoundNode VisitStatementList(BoundStatementList node) { return VisitStatementListWorker(node); @@ -3530,7 +3542,7 @@ public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStack public override BoundNode VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node) { // visit arguments as r-values - VisitArguments(node.Arguments, default(ImmutableArray), node.Constructor); + VisitArguments(node.Arguments, default(ImmutableArray), node.Constructor, default, false); return null; } @@ -3592,7 +3604,7 @@ public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMem method = GetReadMethod(property); } - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, method, node.ArgsToParamsOpt, node.Expanded); } return null; @@ -3614,13 +3626,13 @@ public override BoundNode VisitCollectionElementInitializer(BoundCollectionEleme TLocalState savedState = savedState = this.State.Clone(); SetUnreachable(); - VisitArguments(node.Arguments, default(ImmutableArray), node.AddMethod); + VisitArguments(node.Arguments, default(ImmutableArray), node.AddMethod, node.ArgsToParamsOpt, node.Expanded); this.State = savedState; } else { - VisitArguments(node.Arguments, default(ImmutableArray), node.AddMethod); + VisitArguments(node.Arguments, default(ImmutableArray), node.AddMethod, node.ArgsToParamsOpt, node.Expanded); } return null; @@ -3628,7 +3640,7 @@ public override BoundNode VisitCollectionElementInitializer(BoundCollectionEleme public override BoundNode VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node) { - VisitArguments(node.Arguments, default(ImmutableArray), method: null); + VisitArguments(node.Arguments, default(ImmutableArray), method: null, default, false); return null; } @@ -3746,7 +3758,7 @@ public override BoundNode VisitReadOnlySpanFromArray(BoundReadOnlySpanFromArray public override BoundNode VisitFunctionPointerInvocation(BoundFunctionPointerInvocation node) { VisitRvalue(node.InvokedExpression); - VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.FunctionPointer.Signature); + VisitArguments(node.Arguments, node.ArgumentRefKindsOpt, node.FunctionPointer.Signature, default, false); return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_LocalFunctions.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_LocalFunctions.cs index ca468e82e76c2..9acb3c455cd77 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_LocalFunctions.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass_LocalFunctions.cs @@ -94,7 +94,7 @@ protected bool HasLocalFuncUsagesCreated(LocalFunctionSymbol localFunc) // transition the state of captured variables if the variables have state changes // across all branches leaving the local function - var localFunctionState = GetOrCreateLocalFuncUsages(localFuncSymbol); + var localFunctionState = GetOrCreateLocalFuncUsages((LocalFunctionSymbol)localFuncSymbol); var savedLocalFunctionState = LocalFunctionStart(localFunctionState); var oldPending2 = SavePending(); diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractRegionDataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractRegionDataFlowPass.cs index 153aa9b03f482..237b2ab5ad9e2 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractRegionDataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractRegionDataFlowPass.cs @@ -33,6 +33,10 @@ protected override ImmutableArray Scan(ref bool badRegion) { MakeSlots(MethodParameters); if ((object)MethodThisParameter != null) GetOrCreateSlot(MethodThisParameter); + + if (TryGetInstanceExtensionParameter(out ParameterSymbol extensionParameter)) + GetOrCreateSlot(extensionParameter); + var result = base.Scan(ref badRegion); return result; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs index 568b2c4d01aa6..3c29f693a0a7b 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DefiniteAssignment.cs @@ -368,6 +368,12 @@ protected override ImmutableArray Scan(ref bool badRegion) } } + ParameterSymbol extensionParameter = null; + if (TryGetInstanceExtensionParameter(out extensionParameter)) + { + EnterParameter(extensionParameter); + } + ImmutableArray pendingReturns = base.Scan(ref badRegion); // check that each out parameter is definitely assigned at the end of the method. If @@ -378,6 +384,7 @@ protected override ImmutableArray Scan(ref bool badRegion) { LeaveParameters(methodParameters, null, location); if ((object)methodThisParameter != null) LeaveParameter(methodThisParameter, null, location); + if ((object)extensionParameter != null) LeaveParameter(extensionParameter, null, location); var savedState = this.State; foreach (PendingBranch returnBranch in pendingReturns) @@ -385,6 +392,7 @@ protected override ImmutableArray Scan(ref bool badRegion) this.State = returnBranch.State; LeaveParameters(methodParameters, returnBranch.Branch.Syntax, null); if ((object)methodThisParameter != null) LeaveParameter(methodThisParameter, returnBranch.Branch.Syntax, null); + if ((object)extensionParameter != null) LeaveParameter(extensionParameter, returnBranch.Branch.Syntax, null); Join(ref savedState, ref this.State); } @@ -394,6 +402,21 @@ protected override ImmutableArray Scan(ref bool badRegion) return pendingReturns; } + protected bool TryGetInstanceExtensionParameter(out ParameterSymbol extensionParameter) + { + if (_symbol is not null + && _symbol.GetIsNewExtensionMember() + && !_symbol.IsStatic + && _symbol.ContainingType.ExtensionParameter is { } foundExtensionParameter) + { + extensionParameter = foundExtensionParameter; + return true; + } + + extensionParameter = null; + return false; + } + protected override ImmutableArray RemoveReturns() { var result = base.RemoveReturns(); @@ -666,14 +689,6 @@ public static void Analyze( } #nullable disable - private sealed class SameDiagnosticComparer : EqualityComparer - { - public static readonly SameDiagnosticComparer Instance = new SameDiagnosticComparer(); - public override bool Equals(Diagnostic x, Diagnostic y) => x.Equals(y); - public override int GetHashCode(Diagnostic obj) => - Hash.Combine(Hash.CombineValues(obj.Arguments), Hash.Combine(obj.Location.GetHashCode(), obj.Code)); - } - /// /// Analyze the body, reporting all necessary diagnostics. /// @@ -1832,6 +1847,18 @@ protected override LocalState TopState() } } } + + if (TryGetInstanceExtensionParameter(out ParameterSymbol extensionParameter)) + { + if (extensionParameter.RefKind != RefKind.Out) + { + int slot = GetOrCreateSlot(extensionParameter); + if (slot > 0) + { + SetSlotAssigned(slot, ref topState); + } + } + } } Symbol containing = current.ContainingSymbol; @@ -2181,7 +2208,7 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block) if (stmt is BoundLocalFunctionStatement localFunctionStatement) { // Mark attribute arguments as used. - VisitAttributes(localFunctionStatement.Symbol.BindMethodAttributes()); + VisitAttributes(((LocalFunctionSymbol)localFunctionStatement.Symbol).BindMethodAttributes()); VisitAlways(stmt); } @@ -2329,7 +2356,7 @@ private void ReportIfUnused(LocalSymbol symbol, bool assigned) } } - private void ReportUnusedVariables(ImmutableArray locals) + private void ReportUnusedVariables(ImmutableArray locals) { foreach (var symbol in locals) { @@ -2337,7 +2364,7 @@ private void ReportUnusedVariables(ImmutableArray locals) } } - private void ReportIfUnused(LocalFunctionSymbol symbol) + private void ReportIfUnused(MethodSymbol symbol) { if (!_usedLocalFunctions.Contains(symbol)) { @@ -2425,7 +2452,7 @@ public override BoundNode VisitLambda(BoundLambda node) this.CurrentSymbol = node.Symbol; // Mark attribute arguments as used. - VisitAttributes(node.Symbol.BindMethodAttributes()); + VisitAttributes(((LambdaSymbol)node.Symbol).BindMethodAttributes()); var oldPending = SavePending(); // we do not support branches into a lambda diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs index f8d3ae04207f2..7943c8c6579d8 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/FlowAnalysisPass.cs @@ -173,7 +173,7 @@ private static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol met builder.AddRange(statements, n - 1); builder.Add(AppendImplicitReturn((BoundBlock)statements[n - 1], method)); - return body.Update(body.Locals, ImmutableArray.Empty, body.HasUnsafeModifier, body.Instrumentation, builder.ToImmutableAndFree()); + return body.Update(body.Locals, ImmutableArray.Empty, body.HasUnsafeModifier, body.Instrumentation, builder.ToImmutableAndFree()); } else { diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index 88858dcaa497f..9ecb458367894 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -231,7 +231,7 @@ private void VerifyExpression(BoundExpression expression, bool overrideSkippedEx private void VisitForEachEnumeratorInfo(ForEachEnumeratorInfo enumeratorInfo) { Visit(enumeratorInfo.DisposeAwaitableInfo); - if (enumeratorInfo.GetEnumeratorInfo.Method.IsExtensionMethod) + if (enumeratorInfo.GetEnumeratorInfo.Method.IsExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { foreach (var arg in enumeratorInfo.GetEnumeratorInfo.Arguments) { diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 29140c1a41d71..6142a7c4e6a1c 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -180,6 +180,12 @@ internal string GetDebuggerDisplay() /// private readonly bool _useConstructorExitWarnings; + /// + /// 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; + /// /// If true, the parameter types and nullability from _delegateInvokeMethod is used for /// initial parameter state. If false, the signature of CurrentSymbol is used instead. @@ -448,6 +454,7 @@ private NullableWalker( CSharpCompilation compilation, Symbol? symbol, bool useConstructorExitWarnings, + (SynthesizedBackingFieldSymbol field, NullableAnnotation assumedAnnotation)? getterNullResilienceData, bool useDelegateInvokeParameterTypes, bool useDelegateInvokeReturnType, MethodSymbol? delegateInvokeMethodOpt, @@ -470,6 +477,7 @@ private NullableWalker( _binder = binder; _conversions = (Conversions)conversions.WithNullability(true); _useConstructorExitWarnings = useConstructorExitWarnings; + _getterNullResilienceData = getterNullResilienceData; _useDelegateInvokeParameterTypes = useDelegateInvokeParameterTypes; _useDelegateInvokeReturnType = useDelegateInvokeReturnType; _delegateInvokeMethod = delegateInvokeMethodOpt; @@ -592,6 +600,7 @@ protected override ImmutableArray Scan(ref bool badRegion) { EnterParameter(methodThisParameter, methodThisParameter.TypeWithAnnotations); } + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : should register the extension parameter makeNotNullMembersMaybeNull(); // We need to create a snapshot even of the first node, because we want to have the state of the initial parameters. @@ -724,7 +733,7 @@ void checkMemberStateOnConstructorExit(MethodSymbol constructor, Symbol member, switch (member) { case FieldSymbol f: - symbolType = f.TypeWithAnnotations; + symbolType = GetTypeOrReturnTypeWithAnnotations(f); field = f; symbol = (Symbol?)(f.AssociatedSymbol as PropertySymbol) ?? f; break; @@ -836,7 +845,7 @@ void makeNotNullMembersMaybeNull() var memberSlot = GetSlotForMemberPostCondition(memberToInitialize); if (memberSlot > 0) { - var type = memberToInitialize.GetTypeOrReturnType(); + var type = GetTypeOrReturnTypeWithAnnotations(memberToInitialize); if (!type.NullableAnnotation.IsOblivious()) { SetState(ref this.State, memberSlot, type.Type.IsPossiblyNullableReferenceTypeTypeParameter() ? NullableFlowState.MaybeDefault : NullableFlowState.MaybeNull); @@ -1450,6 +1459,7 @@ private static void Analyze( conversions, diagnostics, useConstructorExitWarnings, + getterNullResilienceData: null, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, @@ -1586,6 +1596,7 @@ private static (SnapshotManager?, ImmutableDictionary @@ -2773,7 +2793,7 @@ private void InheritDefaultState(TypeSymbol targetType, int targetSlot) Debug.Assert(targetSlot > 0); #if DEBUG - var actualType = _variables[targetSlot].Symbol.GetTypeOrReturnType().Type; + var actualType = GetTypeOrReturnType(_variables[targetSlot].Symbol); Debug.Assert(actualType is { }); if (!actualType.ContainsErrorType() && @@ -2793,13 +2813,46 @@ private void InheritDefaultState(TypeSymbol targetType, int targetSlot) { var symbol = AsMemberOfType(targetType, variable.Symbol); SetStateAndTrackForFinally(ref this.State, slot, GetDefaultState(symbol)); - InheritDefaultState(symbol.GetTypeOrReturnType().Type, slot); + InheritDefaultState(GetTypeOrReturnType(symbol), slot); } members.Free(); } + private static TypeSymbol GetTypeOrReturnType(Symbol symbol) => symbol.GetTypeOrReturnType().Type; + + /// Gets the TypeWithAnnotations of a symbol, possibly using the inferred nullable annotation for backing fields. + private TypeWithAnnotations GetTypeOrReturnTypeWithAnnotations(Symbol symbol) + { + var typeWithAnnotations = symbol.GetTypeOrReturnType(); + if (symbol is SynthesizedBackingFieldSymbol { InfersNullableAnnotation: true } backingField) + { + NullableAnnotation nullableAnnotation; + if (_getterNullResilienceData is var (analyzedField, assumedNullableAnnotation)) + { + // If we find a usage of a different backing field, than the one we are currently doing a null resilience analysis on, + // we should not proceed, in order to avoid cycles across inference of multiple fields. + if ((object)analyzedField != backingField) + throw ExceptionUtilities.UnexpectedValue(backingField); + + // Currently in the process of inferring the nullable annotation for 'backingField'. + // Therefore don't try to access the inferred nullable annotation, use a temporary assumedNullableAnnotation instead. + nullableAnnotation = assumedNullableAnnotation; + } + else + { + nullableAnnotation = backingField.GetInferredNullableAnnotation(); + } + + typeWithAnnotations = TypeWithAnnotations.Create(typeWithAnnotations.Type, nullableAnnotation); + } + + return typeWithAnnotations; + } + private NullableFlowState GetDefaultState(Symbol symbol) - => ApplyUnconditionalAnnotations(symbol.GetTypeOrReturnType().ToTypeWithState(), GetRValueAnnotations(symbol)).State; + { + return ApplyUnconditionalAnnotations(GetTypeOrReturnTypeWithAnnotations(symbol).ToTypeWithState(), GetRValueAnnotations(symbol)).State; + } private void InheritNullableStateOfTrackableType(int targetSlot, int valueSlot, int skipSlot) { @@ -3163,7 +3216,7 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block) // In the first pass of the nullable walker, existence of usages here means that a call site has been visited. // In subsequent nullable walker passes, the starting state is preserved from previous passes. // In any case, existence of usages means that we have a good starting state. - if (localFuncs[i] is { } localFunc && HasLocalFuncUsagesCreated(localFunc.Symbol)) + if (localFuncs[i] is { } localFunc && HasLocalFuncUsagesCreated((LocalFunctionSymbol)localFunc.Symbol)) { localFuncs[i] = null; unvisitedLocalFuncs--; @@ -3211,7 +3264,7 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block) public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { - var localFunc = node.Symbol; + var localFunc = (LocalFunctionSymbol)node.Symbol; // Usages state is created when we visit the function's call site or its body. // In the first pass of the nullable walker, existence of usages here means that a call site has been visited. @@ -4259,7 +4312,7 @@ void setAnalyzedNullabilityAsContinuation( if (symbol != null) { - Debug.Assert(TypeSymbol.Equals(objectInitializer.Type, symbol.GetTypeOrReturnType().Type, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); + Debug.Assert(TypeSymbol.Equals(objectInitializer.Type, GetTypeOrReturnType(symbol), TypeCompareKind.IgnoreNullableModifiersForReferenceTypes)); symbol = AsMemberOfType(containingType, symbol); } @@ -4276,7 +4329,7 @@ int getOrCreateSlot(int containingSlot, Symbol symbol) int slot = getOrCreateSlot(containingSlot, symbol); Debug.Assert(!delayCompletionForType || slot == -1); - Action? nestedCompletion = VisitObjectCreationInitializer(slot, symbol.GetTypeOrReturnType().Type, initializer, delayCompletionForType); + Action? nestedCompletion = VisitObjectCreationInitializer(slot, GetTypeOrReturnType(symbol), initializer, delayCompletionForType); return completeNestedInitializerAnalysis(symbol, initializer, slot, nestedCompletion, delayCompletionForType); } @@ -4309,7 +4362,7 @@ int getOrCreateSlot(int containingSlot, Symbol symbol) { int slot = getOrCreateSlot(containingSlot, symbol); completeNestedInitializerAnalysis(symbol, initializer, slot, nestedCompletion: null, delayCompletionForType: false); - nestedCompletion?.Invoke(slot, symbol.GetTypeOrReturnType().Type); + nestedCompletion?.Invoke(slot, GetTypeOrReturnType(symbol)); }; } @@ -4322,9 +4375,9 @@ int getOrCreateSlot(int containingSlot, Symbol symbol) TakeIncrementalSnapshot(node.Right); } - Debug.Assert(symbol.GetTypeOrReturnType().HasType); + Debug.Assert(GetTypeOrReturnTypeWithAnnotations(symbol).HasType); - var type = ApplyLValueAnnotations(symbol.GetTypeOrReturnType(), GetObjectInitializerMemberLValueAnnotations(symbol)); + var type = ApplyLValueAnnotations(GetTypeOrReturnTypeWithAnnotations(symbol), GetObjectInitializerMemberLValueAnnotations(symbol)); (TypeWithState resultType, conversionCompletion) = conversionCompletion is not null ? @@ -4415,7 +4468,7 @@ conversionCompletion is not null ? Debug.Assert(reinferredMethod is object); if (node.ImplicitReceiverOpt != null) { - Debug.Assert(node.ImplicitReceiverOpt.Kind == BoundKind.ObjectOrCollectionValuePlaceholder); + //Debug.Assert(node.ImplicitReceiverOpt.Kind == BoundKind.ObjectOrCollectionValuePlaceholder); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : the receiver may be converted now SetAnalyzedNullability(node.ImplicitReceiverOpt, new VisitResult(node.ImplicitReceiverOpt.Type, NullableAnnotation.NotAnnotated, NullableFlowState.NotNull)); } SetUnknownResultNullability(node); @@ -4464,6 +4517,7 @@ static MethodSymbol addMethodAsMemberOfContainingType(BoundCollectionElementInit } else { + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? method = (MethodSymbol)AsMemberOfType(containingType, method); } @@ -4771,6 +4825,7 @@ internal static TypeWithAnnotations BestTypeForLambdaReturns( var walker = new NullableWalker(binder.Compilation, symbol: null, useConstructorExitWarnings: false, + getterNullResilienceData: null, useDelegateInvokeParameterTypes: false, useDelegateInvokeReturnType: false, delegateInvokeMethodOpt: null, @@ -5337,7 +5392,7 @@ private void AfterLeftChildHasBeenVisited( operandComparedToNonNull = SkipReferenceConversions(operandComparedToNonNull); SplitAndLearnFromNonNullTest(operandComparedToNonNull, whenTrue: false); return; - }; + } } } @@ -5550,7 +5605,7 @@ private void MarkDependentSlotsNotNull(int slot, TypeSymbol expressionType, ref if (childSlot > 0) { SetState(ref state, childSlot, NullableFlowState.NotNull); - MarkDependentSlotsNotNull(childSlot, member.GetTypeOrReturnType().Type, ref state, depth - 1); + MarkDependentSlotsNotNull(childSlot, GetTypeOrReturnType(member), ref state, depth - 1); } } } @@ -6275,6 +6330,7 @@ private static BoundExpression CreatePlaceholderIfNecessary(BoundExpression expr // Only instance receivers go through VisitRvalue; arguments go through VisitArgumentEvaluate. if (node.ReceiverOpt is not null) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? VisitRvalueEpilogue(receiver); // VisitRvalue does this after visiting each node receiverType = ResultType; CheckCallReceiver(receiver, receiverType, node.Method); @@ -6340,6 +6396,7 @@ TypeWithState visitAndCheckReceiver(BoundCall node) void reinferMethodAndVisitArguments(BoundCall node, TypeWithState receiverType, VisitResult? firstArgumentResult = null) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? var method = node.Method; ImmutableArray refKindsOpt = node.ArgumentRefKindsOpt; if (!receiverType.HasNullType) @@ -6653,6 +6710,12 @@ private FlowAnalysisAnnotations GetRValueAnnotations(Symbol? symbol) private FlowAnalysisAnnotations GetParameterAnnotations(ParameterSymbol parameter) { + if (parameter.ContainingSymbol is TypeSymbol { IsExtension: true }) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit when doing nullability analysis + return FlowAnalysisAnnotations.None; + } + // Annotations are ignored when binding an attribute to avoid cycles. (Members used // in attributes are error scenarios, so missing warnings should not be important.) if (IsAnalyzingAttribute) @@ -6661,7 +6724,7 @@ private FlowAnalysisAnnotations GetParameterAnnotations(ParameterSymbol paramete var annotations = parameter.FlowAnalysisAnnotations; // Conditional annotations are ignored on parameters of non-boolean members. - if (parameter.ContainingSymbol.GetTypeOrReturnType().Type.SpecialType != SpecialType.System_Boolean) + if (GetTypeOrReturnType(parameter.ContainingSymbol).SpecialType != SpecialType.System_Boolean) { // NotNull = NotNullWhenTrue + NotNullWhenFalse bool hasNotNullWhenTrue = (annotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNullWhenTrue; @@ -6771,7 +6834,7 @@ private static bool HasImplicitTypeArguments(SyntaxNode syntax) return nameSyntax.Kind() != SyntaxKind.GenericName; } - protected override void VisitArguments(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method) + protected override void VisitArguments(ImmutableArray arguments, ImmutableArray refKindsOpt, MethodSymbol method, ImmutableArray argsToParamsOpt, bool expanded) { // Callers should be using VisitArguments overload below. throw ExceptionUtilities.Unreachable(); @@ -7930,7 +7993,7 @@ private MethodSymbol InferMethodTypeArguments( parameterRefKinds, arguments, ref discardedUseSiteInfo, - new MethodInferenceExtensions(this)); + new MethodInferenceExtensions(this)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we may need to override ordinals here if (!result.Success) { @@ -8309,7 +8372,9 @@ private static Symbol AsMemberOfType(TypeSymbol? type, Symbol symbol) } } } - Debug.Assert(false); // If this assert fails, add an appropriate test. + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : The assert below fails for an instance call on a generic extension. + //Debug.Assert(false); // If this assert fails, add an appropriate test. return symbol; bool tryAsMemberOfSingleType(NamedTypeSymbol singleType, [NotNullWhen(true)] out Symbol? result) @@ -8585,7 +8650,7 @@ private void TrackNullableStateOfNullableValue(int containingSlot, TypeSymbol co int targetSlot = GetNullableOfTValueSlot(containingType, containingSlot, out Symbol? symbol); if (targetSlot > 0) { - TrackNullableStateForAssignment(value, symbol!.GetTypeOrReturnType(), targetSlot, valueType, valueSlot); + TrackNullableStateForAssignment(value, GetTypeOrReturnTypeWithAnnotations(symbol!), targetSlot, valueType, valueSlot); } } @@ -8739,7 +8804,7 @@ void reportBadDelegateParameter(BindingDiagnosticBag bag, MethodSymbol sourceInv private void ReportNullabilityMismatchWithTargetDelegate(Location location, NamedTypeSymbol delegateType, BoundLambda lambda) { MethodSymbol? targetInvokeMethod = delegateType.DelegateInvokeMethod; - LambdaSymbol sourceMethod = lambda.Symbol; + LambdaSymbol sourceMethod = (LambdaSymbol)lambda.Symbol; UnboundLambda unboundLambda = lambda.UnboundLambda; if (targetInvokeMethod is null || @@ -8919,10 +8984,12 @@ private TypeWithState VisitConversion( { VisitLocalFunctionUse(localFunc); } + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? method = CheckMethodGroupReceiverNullability(group, parameters, method, conversion.IsExtensionMethod); } if (reportRemainingWarnings && invokeSignature != null) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), targetType, invokeSignature, method, conversion.IsExtensionMethod); } } @@ -9784,7 +9851,7 @@ void setAnalyzedNullabilityAsContinuation(BoundDelegateCreationExpression node, if (!lambda.IsSuppressed) { - ReportNullabilityMismatchWithTargetDelegate(lambda.Symbol.DiagnosticLocation, delegateType, lambda); + ReportNullabilityMismatchWithTargetDelegate(((LambdaSymbol)lambda.Symbol).DiagnosticLocation, delegateType, lambda); } return null; @@ -9879,6 +9946,7 @@ private MethodSymbol CheckMethodGroupReceiverNullability(BoundMethodGroup group, var receiverOpt = group.ReceiverOpt; if (TryGetMethodGroupReceiverNullability(receiverOpt, out TypeWithState receiverType)) { + // Tracked by https://github.com/dotnet/roslyn/issues/76130: Do we need to do anything special for new extensions here? var syntax = group.Syntax; if (!invokedAsExtensionMethod) { @@ -10010,9 +10078,23 @@ private void VisitThisOrBaseReference(BoundExpression node) // we may enter a conditional state for error scenarios on the LHS. Unsplit(); - FlowAnalysisAnnotations leftAnnotations = GetLValueAnnotations(left); - TypeWithAnnotations declaredType = LvalueResultType; - TypeWithAnnotations leftLValueType = ApplyLValueAnnotations(declaredType, leftAnnotations); + // When a getter-only prop is assigned in a constructor, it is bound as + // an assignment of the property even though it is really an assignment of the backing field. + // When such a property also uses the field keyword, we want the field's annotations+attributes + // to decide the validity of the assignment and the ones on the property itself to be ignored. + TypeWithAnnotations leftLValueType; + FlowAnalysisAnnotations leftAnnotations; + if (left is BoundPropertyAccess { PropertySymbol: SourcePropertySymbolBase { SetMethod: null, UsesFieldKeyword: true } property }) + { + var field = property.BackingField; + leftAnnotations = field.FlowAnalysisAnnotations; + leftLValueType = ApplyLValueAnnotations(GetTypeOrReturnTypeWithAnnotations(field), leftAnnotations); + } + else + { + leftAnnotations = GetLValueAnnotations(left); + leftLValueType = ApplyLValueAnnotations(LvalueResultType, leftAnnotations); + } if (left.Kind == BoundKind.EventAccess && ((BoundEventAccess)left).EventSymbol.IsWindowsRuntimeEvent) { @@ -10236,6 +10318,7 @@ private void VisitDeconstructMethodArguments(ArrayBuilder originalUserDefinedConversionsOpt, TypeSymbol type, bool hasErrors = false) + public BoundConversion(SyntaxNode syntax, BoundExpression operand, Conversion conversion, bool isBaseConversion, bool @checked, bool explicitCastInCode, ConstantValue? constantValueOpt, ConversionGroup? conversionGroupOpt, TypeSymbol type, bool hasErrors = false) : base(BoundKind.Conversion, syntax, type, hasErrors || operand.HasErrors()) { @@ -2963,7 +2963,6 @@ public BoundConversion(SyntaxNode syntax, BoundExpression operand, Conversion co this.ExplicitCastInCode = explicitCastInCode; this.ConstantValueOpt = constantValueOpt; this.ConversionGroupOpt = conversionGroupOpt; - this.OriginalUserDefinedConversionsOpt = originalUserDefinedConversionsOpt; } public new TypeSymbol Type => base.Type!; @@ -2974,16 +2973,15 @@ public BoundConversion(SyntaxNode syntax, BoundExpression operand, Conversion co public bool ExplicitCastInCode { get; } public override ConstantValue? ConstantValueOpt { get; } public ConversionGroup? ConversionGroupOpt { get; } - public ImmutableArray OriginalUserDefinedConversionsOpt { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitConversion(this); - public BoundConversion Update(BoundExpression operand, Conversion conversion, bool isBaseConversion, bool @checked, bool explicitCastInCode, ConstantValue? constantValueOpt, ConversionGroup? conversionGroupOpt, ImmutableArray originalUserDefinedConversionsOpt, TypeSymbol type) + public BoundConversion Update(BoundExpression operand, Conversion conversion, bool isBaseConversion, bool @checked, bool explicitCastInCode, ConstantValue? constantValueOpt, ConversionGroup? conversionGroupOpt, TypeSymbol type) { - if (operand != this.Operand || conversion != this.Conversion || isBaseConversion != this.IsBaseConversion || @checked != this.Checked || explicitCastInCode != this.ExplicitCastInCode || constantValueOpt != this.ConstantValueOpt || conversionGroupOpt != this.ConversionGroupOpt || originalUserDefinedConversionsOpt != this.OriginalUserDefinedConversionsOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (operand != this.Operand || conversion != this.Conversion || isBaseConversion != this.IsBaseConversion || @checked != this.Checked || explicitCastInCode != this.ExplicitCastInCode || constantValueOpt != this.ConstantValueOpt || conversionGroupOpt != this.ConversionGroupOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundConversion(this.Syntax, operand, conversion, isBaseConversion, @checked, explicitCastInCode, constantValueOpt, conversionGroupOpt, originalUserDefinedConversionsOpt, type, this.HasErrors); + var result = new BoundConversion(this.Syntax, operand, conversion, isBaseConversion, @checked, explicitCastInCode, constantValueOpt, conversionGroupOpt, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -3302,7 +3300,7 @@ public BoundStepThroughSequencePoint Update(TextSpan span) internal sealed partial class BoundBlock : BoundStatementList { - public BoundBlock(SyntaxNode syntax, ImmutableArray locals, ImmutableArray localFunctions, bool hasUnsafeModifier, BoundBlockInstrumentation? instrumentation, ImmutableArray statements, bool hasErrors = false) + public BoundBlock(SyntaxNode syntax, ImmutableArray locals, ImmutableArray localFunctions, bool hasUnsafeModifier, BoundBlockInstrumentation? instrumentation, ImmutableArray statements, bool hasErrors = false) : base(BoundKind.Block, syntax, statements, hasErrors || instrumentation.HasErrors() || statements.HasErrors()) { @@ -3317,14 +3315,14 @@ public BoundBlock(SyntaxNode syntax, ImmutableArray locals, Immutab } public ImmutableArray Locals { get; } - public ImmutableArray LocalFunctions { get; } + public ImmutableArray LocalFunctions { get; } public bool HasUnsafeModifier { get; } public BoundBlockInstrumentation? Instrumentation { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitBlock(this); - public BoundBlock Update(ImmutableArray locals, ImmutableArray localFunctions, bool hasUnsafeModifier, BoundBlockInstrumentation? instrumentation, ImmutableArray statements) + public BoundBlock Update(ImmutableArray locals, ImmutableArray localFunctions, bool hasUnsafeModifier, BoundBlockInstrumentation? instrumentation, ImmutableArray statements) { if (locals != this.Locals || localFunctions != this.LocalFunctions || hasUnsafeModifier != this.HasUnsafeModifier || instrumentation != this.Instrumentation || statements != this.Statements) { @@ -3504,7 +3502,7 @@ public BoundUsingLocalDeclarations Update(MethodArgumentInfo? patternDisposeInfo internal sealed partial class BoundLocalFunctionStatement : BoundStatement { - public BoundLocalFunctionStatement(SyntaxNode syntax, LocalFunctionSymbol symbol, BoundBlock? blockBody, BoundBlock? expressionBody, bool hasErrors = false) + public BoundLocalFunctionStatement(SyntaxNode syntax, MethodSymbol symbol, BoundBlock? blockBody, BoundBlock? expressionBody, bool hasErrors = false) : base(BoundKind.LocalFunctionStatement, syntax, hasErrors || blockBody.HasErrors() || expressionBody.HasErrors()) { @@ -3515,14 +3513,14 @@ public BoundLocalFunctionStatement(SyntaxNode syntax, LocalFunctionSymbol symbol this.ExpressionBody = expressionBody; } - public LocalFunctionSymbol Symbol { get; } + public MethodSymbol Symbol { get; } public BoundBlock? BlockBody { get; } public BoundBlock? ExpressionBody { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitLocalFunctionStatement(this); - public BoundLocalFunctionStatement Update(LocalFunctionSymbol symbol, BoundBlock? blockBody, BoundBlock? expressionBody) + public BoundLocalFunctionStatement Update(MethodSymbol symbol, BoundBlock? blockBody, BoundBlock? expressionBody) { if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(symbol, this.Symbol) || blockBody != this.BlockBody || expressionBody != this.ExpressionBody) { @@ -3694,7 +3692,7 @@ public BoundExpressionStatement Update(BoundExpression expression) internal sealed partial class BoundBreakStatement : BoundStatement { - public BoundBreakStatement(SyntaxNode syntax, GeneratedLabelSymbol label, bool hasErrors) + public BoundBreakStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors) : base(BoundKind.BreakStatement, syntax, hasErrors) { @@ -3703,7 +3701,7 @@ public BoundBreakStatement(SyntaxNode syntax, GeneratedLabelSymbol label, bool h this.Label = label; } - public BoundBreakStatement(SyntaxNode syntax, GeneratedLabelSymbol label) + public BoundBreakStatement(SyntaxNode syntax, LabelSymbol label) : base(BoundKind.BreakStatement, syntax) { @@ -3712,12 +3710,12 @@ public BoundBreakStatement(SyntaxNode syntax, GeneratedLabelSymbol label) this.Label = label; } - public GeneratedLabelSymbol Label { get; } + public LabelSymbol Label { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitBreakStatement(this); - public BoundBreakStatement Update(GeneratedLabelSymbol label) + public BoundBreakStatement Update(LabelSymbol label) { if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(label, this.Label)) { @@ -3731,7 +3729,7 @@ public BoundBreakStatement Update(GeneratedLabelSymbol label) internal sealed partial class BoundContinueStatement : BoundStatement { - public BoundContinueStatement(SyntaxNode syntax, GeneratedLabelSymbol label, bool hasErrors) + public BoundContinueStatement(SyntaxNode syntax, LabelSymbol label, bool hasErrors) : base(BoundKind.ContinueStatement, syntax, hasErrors) { @@ -3740,7 +3738,7 @@ public BoundContinueStatement(SyntaxNode syntax, GeneratedLabelSymbol label, boo this.Label = label; } - public BoundContinueStatement(SyntaxNode syntax, GeneratedLabelSymbol label) + public BoundContinueStatement(SyntaxNode syntax, LabelSymbol label) : base(BoundKind.ContinueStatement, syntax) { @@ -3749,12 +3747,12 @@ public BoundContinueStatement(SyntaxNode syntax, GeneratedLabelSymbol label) this.Label = label; } - public GeneratedLabelSymbol Label { get; } + public LabelSymbol Label { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitContinueStatement(this); - public BoundContinueStatement Update(GeneratedLabelSymbol label) + public BoundContinueStatement Update(LabelSymbol label) { if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(label, this.Label)) { @@ -3768,7 +3766,7 @@ public BoundContinueStatement Update(GeneratedLabelSymbol label) internal sealed partial class BoundSwitchStatement : BoundStatement { - public BoundSwitchStatement(SyntaxNode syntax, BoundExpression expression, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundDecisionDag reachabilityDecisionDag, BoundSwitchLabel? defaultLabel, GeneratedLabelSymbol breakLabel, bool hasErrors = false) + public BoundSwitchStatement(SyntaxNode syntax, BoundExpression expression, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundDecisionDag reachabilityDecisionDag, BoundSwitchLabel? defaultLabel, LabelSymbol breakLabel, bool hasErrors = false) : base(BoundKind.SwitchStatement, syntax, hasErrors || expression.HasErrors() || switchSections.HasErrors() || reachabilityDecisionDag.HasErrors() || defaultLabel.HasErrors()) { @@ -3790,16 +3788,16 @@ public BoundSwitchStatement(SyntaxNode syntax, BoundExpression expression, Immut public BoundExpression Expression { get; } public ImmutableArray InnerLocals { get; } - public ImmutableArray InnerLocalFunctions { get; } + public ImmutableArray InnerLocalFunctions { get; } public ImmutableArray SwitchSections { get; } public BoundDecisionDag ReachabilityDecisionDag { get; } public BoundSwitchLabel? DefaultLabel { get; } - public GeneratedLabelSymbol BreakLabel { get; } + public LabelSymbol BreakLabel { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitSwitchStatement(this); - public BoundSwitchStatement Update(BoundExpression expression, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundDecisionDag reachabilityDecisionDag, BoundSwitchLabel? defaultLabel, GeneratedLabelSymbol breakLabel) + public BoundSwitchStatement Update(BoundExpression expression, ImmutableArray innerLocals, ImmutableArray innerLocalFunctions, ImmutableArray switchSections, BoundDecisionDag reachabilityDecisionDag, BoundSwitchLabel? defaultLabel, LabelSymbol breakLabel) { if (expression != this.Expression || innerLocals != this.InnerLocals || innerLocalFunctions != this.InnerLocalFunctions || switchSections != this.SwitchSections || reachabilityDecisionDag != this.ReachabilityDecisionDag || defaultLabel != this.DefaultLabel || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(breakLabel, this.BreakLabel)) { @@ -3882,7 +3880,7 @@ public BoundIfStatement Update(BoundExpression condition, BoundStatement consequ internal abstract partial class BoundLoopStatement : BoundStatement { - protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors) + protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors) : base(kind, syntax, hasErrors) { @@ -3893,7 +3891,7 @@ protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, GeneratedLabelSy this.ContinueLabel = continueLabel; } - protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel) + protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, LabelSymbol breakLabel, LabelSymbol continueLabel) : base(kind, syntax) { @@ -3904,13 +3902,13 @@ protected BoundLoopStatement(BoundKind kind, SyntaxNode syntax, GeneratedLabelSy this.ContinueLabel = continueLabel; } - public GeneratedLabelSymbol BreakLabel { get; } - public GeneratedLabelSymbol ContinueLabel { get; } + public LabelSymbol BreakLabel { get; } + public LabelSymbol ContinueLabel { get; } } internal abstract partial class BoundConditionalLoopStatement : BoundLoopStatement { - protected BoundConditionalLoopStatement(BoundKind kind, SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) + protected BoundConditionalLoopStatement(BoundKind kind, SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors = false) : base(kind, syntax, breakLabel, continueLabel, hasErrors) { @@ -3932,7 +3930,7 @@ protected BoundConditionalLoopStatement(BoundKind kind, SyntaxNode syntax, Immut internal sealed partial class BoundDoStatement : BoundConditionalLoopStatement { - public BoundDoStatement(SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) + public BoundDoStatement(SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors = false) : base(BoundKind.DoStatement, syntax, locals, condition, body, breakLabel, continueLabel, hasErrors || condition.HasErrors() || body.HasErrors()) { @@ -3948,7 +3946,7 @@ public BoundDoStatement(SyntaxNode syntax, ImmutableArray locals, B [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitDoStatement(this); - public BoundDoStatement Update(ImmutableArray locals, BoundExpression condition, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel) + public BoundDoStatement Update(ImmutableArray locals, BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel) { if (locals != this.Locals || condition != this.Condition || body != this.Body || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(breakLabel, this.BreakLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(continueLabel, this.ContinueLabel)) { @@ -3962,7 +3960,7 @@ public BoundDoStatement Update(ImmutableArray locals, BoundExpressi internal sealed partial class BoundWhileStatement : BoundConditionalLoopStatement { - public BoundWhileStatement(SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) + public BoundWhileStatement(SyntaxNode syntax, ImmutableArray locals, BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors = false) : base(BoundKind.WhileStatement, syntax, locals, condition, body, breakLabel, continueLabel, hasErrors || condition.HasErrors() || body.HasErrors()) { @@ -3978,7 +3976,7 @@ public BoundWhileStatement(SyntaxNode syntax, ImmutableArray locals [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitWhileStatement(this); - public BoundWhileStatement Update(ImmutableArray locals, BoundExpression condition, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel) + public BoundWhileStatement Update(ImmutableArray locals, BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel) { if (locals != this.Locals || condition != this.Condition || body != this.Body || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(breakLabel, this.BreakLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(continueLabel, this.ContinueLabel)) { @@ -3992,7 +3990,7 @@ public BoundWhileStatement Update(ImmutableArray locals, BoundExpre internal sealed partial class BoundForStatement : BoundLoopStatement { - public BoundForStatement(SyntaxNode syntax, ImmutableArray outerLocals, BoundStatement? initializer, ImmutableArray innerLocals, BoundExpression? condition, BoundStatement? increment, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) + public BoundForStatement(SyntaxNode syntax, ImmutableArray outerLocals, BoundStatement? initializer, ImmutableArray innerLocals, BoundExpression? condition, BoundStatement? increment, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors = false) : base(BoundKind.ForStatement, syntax, breakLabel, continueLabel, hasErrors || initializer.HasErrors() || condition.HasErrors() || increment.HasErrors() || body.HasErrors()) { @@ -4020,7 +4018,7 @@ public BoundForStatement(SyntaxNode syntax, ImmutableArray outerLoc [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitForStatement(this); - public BoundForStatement Update(ImmutableArray outerLocals, BoundStatement? initializer, ImmutableArray innerLocals, BoundExpression? condition, BoundStatement? increment, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel) + public BoundForStatement Update(ImmutableArray outerLocals, BoundStatement? initializer, ImmutableArray innerLocals, BoundExpression? condition, BoundStatement? increment, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel) { if (outerLocals != this.OuterLocals || initializer != this.Initializer || innerLocals != this.InnerLocals || condition != this.Condition || increment != this.Increment || body != this.Body || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(breakLabel, this.BreakLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(continueLabel, this.ContinueLabel)) { @@ -4034,7 +4032,7 @@ public BoundForStatement Update(ImmutableArray outerLocals, BoundSt internal sealed partial class BoundForEachStatement : BoundLoopStatement { - public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo? enumeratorInfoOpt, BoundValuePlaceholder? elementPlaceholder, BoundExpression? elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray iterationVariables, BoundExpression? iterationErrorExpressionOpt, BoundExpression expression, BoundForEachDeconstructStep? deconstructionOpt, BoundAwaitableInfo? awaitOpt, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel, bool hasErrors = false) + public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo? enumeratorInfoOpt, BoundValuePlaceholder? elementPlaceholder, BoundExpression? elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray iterationVariables, BoundExpression? iterationErrorExpressionOpt, BoundExpression expression, BoundForEachDeconstructStep? deconstructionOpt, BoundAwaitableInfo? awaitOpt, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool hasErrors = false) : base(BoundKind.ForEachStatement, syntax, breakLabel, continueLabel, hasErrors || elementPlaceholder.HasErrors() || elementConversion.HasErrors() || iterationVariableType.HasErrors() || iterationErrorExpressionOpt.HasErrors() || expression.HasErrors() || deconstructionOpt.HasErrors() || awaitOpt.HasErrors() || body.HasErrors()) { @@ -4071,7 +4069,7 @@ public BoundForEachStatement(SyntaxNode syntax, ForEachEnumeratorInfo? enumerato [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitForEachStatement(this); - public BoundForEachStatement Update(ForEachEnumeratorInfo? enumeratorInfoOpt, BoundValuePlaceholder? elementPlaceholder, BoundExpression? elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray iterationVariables, BoundExpression? iterationErrorExpressionOpt, BoundExpression expression, BoundForEachDeconstructStep? deconstructionOpt, BoundAwaitableInfo? awaitOpt, BoundStatement body, GeneratedLabelSymbol breakLabel, GeneratedLabelSymbol continueLabel) + public BoundForEachStatement Update(ForEachEnumeratorInfo? enumeratorInfoOpt, BoundValuePlaceholder? elementPlaceholder, BoundExpression? elementConversion, BoundTypeExpression iterationVariableType, ImmutableArray iterationVariables, BoundExpression? iterationErrorExpressionOpt, BoundExpression expression, BoundForEachDeconstructStep? deconstructionOpt, BoundAwaitableInfo? awaitOpt, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel) { if (enumeratorInfoOpt != this.EnumeratorInfoOpt || elementPlaceholder != this.ElementPlaceholder || elementConversion != this.ElementConversion || iterationVariableType != this.IterationVariableType || iterationVariables != this.IterationVariables || iterationErrorExpressionOpt != this.IterationErrorExpressionOpt || expression != this.Expression || deconstructionOpt != this.DeconstructionOpt || awaitOpt != this.AwaitOpt || body != this.Body || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(breakLabel, this.BreakLabel) || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(continueLabel, this.ContinueLabel)) { @@ -7590,7 +7588,7 @@ public BoundDynamicIndexerAccess Update(BoundExpression receiver, ImmutableArray internal sealed partial class BoundLambda : BoundExpression { - public BoundLambda(SyntaxNode syntax, UnboundLambda unboundLambda, LambdaSymbol symbol, BoundBlock body, ReadOnlyBindingDiagnostic diagnostics, Binder binder, TypeSymbol? type, bool hasErrors = false) + public BoundLambda(SyntaxNode syntax, UnboundLambda unboundLambda, MethodSymbol symbol, BoundBlock body, ReadOnlyBindingDiagnostic diagnostics, Binder binder, TypeSymbol? type, bool hasErrors = false) : base(BoundKind.Lambda, syntax, type, hasErrors || unboundLambda.HasErrors() || body.HasErrors()) { @@ -7607,7 +7605,7 @@ public BoundLambda(SyntaxNode syntax, UnboundLambda unboundLambda, LambdaSymbol } public UnboundLambda UnboundLambda { get; } - public LambdaSymbol Symbol { get; } + public MethodSymbol Symbol { get; } public new TypeSymbol? Type => base.Type; public BoundBlock Body { get; } public ReadOnlyBindingDiagnostic Diagnostics { get; } @@ -7616,7 +7614,7 @@ public BoundLambda(SyntaxNode syntax, UnboundLambda unboundLambda, LambdaSymbol [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitLambda(this); - public BoundLambda Update(UnboundLambda unboundLambda, LambdaSymbol symbol, BoundBlock body, ReadOnlyBindingDiagnostic diagnostics, Binder binder, TypeSymbol? type) + public BoundLambda Update(UnboundLambda unboundLambda, MethodSymbol symbol, BoundBlock body, ReadOnlyBindingDiagnostic diagnostics, Binder binder, TypeSymbol? type) { if (unboundLambda != this.UnboundLambda || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(symbol, this.Symbol) || body != this.Body || diagnostics != this.Diagnostics || binder != this.Binder || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { @@ -10835,18 +10833,24 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { public override BoundNode? VisitFieldEqualsValue(BoundFieldEqualsValue node) { + FieldSymbol field = this.VisitFieldSymbol(node.Field); + ImmutableArray locals = this.VisitLocals(node.Locals); BoundExpression value = (BoundExpression)this.Visit(node.Value); - return node.Update(node.Field, node.Locals, value); + return node.Update(field, locals, value); } public override BoundNode? VisitPropertyEqualsValue(BoundPropertyEqualsValue node) { + PropertySymbol property = this.VisitPropertySymbol(node.Property); + ImmutableArray locals = this.VisitLocals(node.Locals); BoundExpression value = (BoundExpression)this.Visit(node.Value); - return node.Update(node.Property, node.Locals, value); + return node.Update(property, locals, value); } public override BoundNode? VisitParameterEqualsValue(BoundParameterEqualsValue node) { + ParameterSymbol parameter = this.VisitParameterSymbol(node.Parameter); + ImmutableArray locals = this.VisitLocals(node.Locals); BoundExpression value = (BoundExpression)this.Visit(node.Value); - return node.Update(node.Parameter, node.Locals, value); + return node.Update(parameter, locals, value); } public override BoundNode? VisitGlobalStatementInitializer(BoundGlobalStatementInitializer node) { @@ -10866,8 +10870,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) { + Symbol? variableSymbol = this.VisitSymbol(node.VariableSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.VariableSymbol, node.IsDiscardExpression, type); + return node.Update(variableSymbol, node.IsDiscardExpression, type); } public override BoundNode? VisitTupleOperandPlaceholder(BoundTupleOperandPlaceholder node) { @@ -10932,9 +10937,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitBadExpression(BoundBadExpression node) { + ImmutableArray symbols = this.VisitSymbols(node.Symbols); ImmutableArray childBoundNodes = this.VisitList(node.ChildBoundNodes); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ResultKind, node.Symbols, childBoundNodes, type); + return node.Update(node.ResultKind, symbols, childBoundNodes, type); } public override BoundNode? VisitBadStatement(BoundBadStatement node) { @@ -10948,10 +10954,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitTypeExpression(BoundTypeExpression node) { + AliasSymbol? aliasOpt = this.VisitAliasSymbol(node.AliasOpt); BoundTypeExpression? boundContainingTypeOpt = (BoundTypeExpression?)this.Visit(node.BoundContainingTypeOpt); ImmutableArray boundDimensionsOpt = this.VisitList(node.BoundDimensionsOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.AliasOpt, boundContainingTypeOpt, boundDimensionsOpt, node.TypeWithAnnotations, type); + return node.Update(aliasOpt, boundContainingTypeOpt, boundDimensionsOpt, node.TypeWithAnnotations, type); } public override BoundNode? VisitTypeOrValueExpression(BoundTypeOrValueExpression node) { @@ -10960,18 +10967,24 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitNamespaceExpression(BoundNamespaceExpression node) { + NamespaceSymbol namespaceSymbol = this.VisitNamespaceSymbol(node.NamespaceSymbol); + AliasSymbol? aliasOpt = this.VisitAliasSymbol(node.AliasOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.NamespaceSymbol, node.AliasOpt); + return node.Update(namespaceSymbol, aliasOpt); } public override BoundNode? VisitUnaryOperator(BoundUnaryOperator node) { + MethodSymbol? methodOpt = this.VisitMethodSymbol(node.MethodOpt); + ImmutableArray originalUserDefinedOperatorsOpt = this.VisitSymbols(node.OriginalUserDefinedOperatorsOpt); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol? constrainedToTypeOpt = this.VisitType(node.ConstrainedToTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, node.MethodOpt, constrainedToTypeOpt, node.ResultKind, node.OriginalUserDefinedOperatorsOpt, type); + return node.Update(node.OperatorKind, operand, node.ConstantValueOpt, methodOpt, constrainedToTypeOpt, node.ResultKind, originalUserDefinedOperatorsOpt, type); } public override BoundNode? VisitIncrementOperator(BoundIncrementOperator node) { + MethodSymbol? methodOpt = this.VisitMethodSymbol(node.MethodOpt); + ImmutableArray originalUserDefinedOperatorsOpt = this.VisitSymbols(node.OriginalUserDefinedOperatorsOpt); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); BoundValuePlaceholder? operandPlaceholder = node.OperandPlaceholder; BoundExpression? operandConversion = node.OperandConversion; @@ -10979,7 +10992,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? resultConversion = node.ResultConversion; TypeSymbol? constrainedToTypeOpt = this.VisitType(node.ConstrainedToTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.OperatorKind, operand, node.MethodOpt, constrainedToTypeOpt, operandPlaceholder, operandConversion, resultPlaceholder, resultConversion, node.ResultKind, node.OriginalUserDefinedOperatorsOpt, type); + return node.Update(node.OperatorKind, operand, methodOpt, constrainedToTypeOpt, operandPlaceholder, operandConversion, resultPlaceholder, resultConversion, node.ResultKind, originalUserDefinedOperatorsOpt, type); } public override BoundNode? VisitAddressOfOperator(BoundAddressOfOperator node) { @@ -10995,9 +11008,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitFunctionPointerLoad(BoundFunctionPointerLoad node) { + MethodSymbol targetMethod = this.VisitMethodSymbol(node.TargetMethod); TypeSymbol? constrainedToTypeOpt = this.VisitType(node.ConstrainedToTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.TargetMethod, constrainedToTypeOpt, type); + return node.Update(targetMethod, constrainedToTypeOpt, type); } public override BoundNode? VisitPointerIndirectionOperator(BoundPointerIndirectionOperator node) { @@ -11021,9 +11035,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitRefTypeOperator(BoundRefTypeOperator node) { + MethodSymbol? getTypeFromHandle = this.VisitMethodSymbol(node.GetTypeFromHandle); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(operand, node.GetTypeFromHandle, type); + return node.Update(operand, getTypeFromHandle, type); } public override BoundNode? VisitMakeRefOperator(BoundMakeRefOperator node) { @@ -11039,16 +11054,18 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitFromEndIndexExpression(BoundFromEndIndexExpression node) { + MethodSymbol? methodOpt = this.VisitMethodSymbol(node.MethodOpt); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(operand, node.MethodOpt, type); + return node.Update(operand, methodOpt, type); } public override BoundNode? VisitRangeExpression(BoundRangeExpression node) { + MethodSymbol? methodOpt = this.VisitMethodSymbol(node.MethodOpt); BoundExpression? leftOperandOpt = (BoundExpression?)this.Visit(node.LeftOperandOpt); BoundExpression? rightOperandOpt = (BoundExpression?)this.Visit(node.RightOperandOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(leftOperandOpt, rightOperandOpt, node.MethodOpt, type); + return node.Update(leftOperandOpt, rightOperandOpt, methodOpt, type); } public override BoundNode? VisitBinaryOperator(BoundBinaryOperator node) { @@ -11066,14 +11083,19 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node) { + MethodSymbol logicalOperator = this.VisitMethodSymbol(node.LogicalOperator); + MethodSymbol trueOperator = this.VisitMethodSymbol(node.TrueOperator); + MethodSymbol falseOperator = this.VisitMethodSymbol(node.FalseOperator); + ImmutableArray originalUserDefinedOperatorsOpt = this.VisitSymbols(node.OriginalUserDefinedOperatorsOpt); BoundExpression left = (BoundExpression)this.Visit(node.Left); BoundExpression right = (BoundExpression)this.Visit(node.Right); TypeSymbol? constrainedToTypeOpt = this.VisitType(node.ConstrainedToTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.OperatorKind, node.LogicalOperator, node.TrueOperator, node.FalseOperator, constrainedToTypeOpt, node.ResultKind, node.OriginalUserDefinedOperatorsOpt, left, right, type); + return node.Update(node.OperatorKind, logicalOperator, trueOperator, falseOperator, constrainedToTypeOpt, node.ResultKind, originalUserDefinedOperatorsOpt, left, right, type); } public override BoundNode? VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) { + ImmutableArray originalUserDefinedOperatorsOpt = this.VisitSymbols(node.OriginalUserDefinedOperatorsOpt); BoundExpression left = (BoundExpression)this.Visit(node.Left); BoundExpression right = (BoundExpression)this.Visit(node.Right); BoundValuePlaceholder? leftPlaceholder = node.LeftPlaceholder; @@ -11081,7 +11103,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundValuePlaceholder? finalPlaceholder = node.FinalPlaceholder; BoundExpression? finalConversion = node.FinalConversion; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Operator, left, right, leftPlaceholder, leftConversion, finalPlaceholder, finalConversion, node.ResultKind, node.OriginalUserDefinedOperatorsOpt, type); + return node.Update(node.Operator, left, right, leftPlaceholder, leftConversion, finalPlaceholder, finalConversion, node.ResultKind, originalUserDefinedOperatorsOpt, type); } public override BoundNode? VisitAssignmentOperator(BoundAssignmentOperator node) { @@ -11151,9 +11173,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitAwaitableInfo(BoundAwaitableInfo node) { + PropertySymbol? isCompleted = this.VisitPropertySymbol(node.IsCompleted); + MethodSymbol? getResult = this.VisitMethodSymbol(node.GetResult); BoundAwaitableValuePlaceholder? awaitableInstancePlaceholder = (BoundAwaitableValuePlaceholder?)this.Visit(node.AwaitableInstancePlaceholder); BoundExpression? getAwaiter = (BoundExpression?)this.Visit(node.GetAwaiter); - return node.Update(awaitableInstancePlaceholder, node.IsDynamic, getAwaiter, node.IsCompleted, node.GetResult); + return node.Update(awaitableInstancePlaceholder, node.IsDynamic, getAwaiter, isCompleted, getResult); } public override BoundNode? VisitAwaitExpression(BoundAwaitExpression node) { @@ -11164,9 +11188,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitTypeOfOperator(BoundTypeOfOperator node) { + MethodSymbol? getTypeFromHandle = this.VisitMethodSymbol(node.GetTypeFromHandle); BoundTypeExpression sourceType = (BoundTypeExpression)this.Visit(node.SourceType); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(sourceType, node.GetTypeFromHandle, type); + return node.Update(sourceType, getTypeFromHandle, type); } public override BoundNode? VisitBlockInstrumentation(BoundBlockInstrumentation node) { @@ -11176,18 +11201,23 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitMethodDefIndex(BoundMethodDefIndex node) { + MethodSymbol method = this.VisitMethodSymbol(node.Method); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Method, type); + return node.Update(method, type); } public override BoundNode? VisitLocalId(BoundLocalId node) { + LocalSymbol local = this.VisitLocalSymbol(node.Local); + FieldSymbol? hoistedField = this.VisitFieldSymbol(node.HoistedField); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Local, node.HoistedField, type); + return node.Update(local, hoistedField, type); } public override BoundNode? VisitParameterId(BoundParameterId node) { + ParameterSymbol parameter = this.VisitParameterSymbol(node.Parameter); + FieldSymbol? hoistedField = this.VisitFieldSymbol(node.HoistedField); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Parameter, node.HoistedField, type); + return node.Update(parameter, hoistedField, type); } public override BoundNode? VisitStateMachineInstanceId(BoundStateMachineInstanceId node) { @@ -11231,13 +11261,17 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitMethodInfo(BoundMethodInfo node) { + MethodSymbol method = this.VisitMethodSymbol(node.Method); + MethodSymbol? getMethodFromHandle = this.VisitMethodSymbol(node.GetMethodFromHandle); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Method, node.GetMethodFromHandle, type); + return node.Update(method, getMethodFromHandle, type); } public override BoundNode? VisitFieldInfo(BoundFieldInfo node) { + FieldSymbol field = this.VisitFieldSymbol(node.Field); + MethodSymbol? getFieldFromHandle = this.VisitMethodSymbol(node.GetFieldFromHandle); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Field, node.GetFieldFromHandle, type); + return node.Update(field, getFieldFromHandle, type); } public override BoundNode? VisitDefaultLiteral(BoundDefaultLiteral node) { @@ -11276,13 +11310,14 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, node.OriginalUserDefinedConversionsOpt, type); + return node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, type); } public override BoundNode? VisitReadOnlySpanFromArray(BoundReadOnlySpanFromArray node) { + MethodSymbol conversionMethod = this.VisitMethodSymbol(node.ConversionMethod); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(operand, node.ConversionMethod, type); + return node.Update(operand, conversionMethod, type); } public override BoundNode? VisitArgList(BoundArgList node) { @@ -11297,12 +11332,13 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitFixedLocalCollectionInitializer(BoundFixedLocalCollectionInitializer node) { + MethodSymbol? getPinnableOpt = this.VisitMethodSymbol(node.GetPinnableOpt); BoundValuePlaceholder? elementPointerPlaceholder = node.ElementPointerPlaceholder; BoundExpression? elementPointerConversion = node.ElementPointerConversion; BoundExpression expression = (BoundExpression)this.Visit(node.Expression); TypeSymbol? elementPointerType = this.VisitType(node.ElementPointerType); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(elementPointerType, elementPointerPlaceholder, elementPointerConversion, expression, node.GetPinnableOpt, type); + return node.Update(elementPointerType, elementPointerPlaceholder, elementPointerConversion, expression, getPinnableOpt, type); } public override BoundNode? VisitSequencePoint(BoundSequencePoint node) { @@ -11319,26 +11355,31 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitStepThroughSequencePoint(BoundStepThroughSequencePoint node) => node; public override BoundNode? VisitBlock(BoundBlock node) { + ImmutableArray locals = this.VisitLocals(node.Locals); + ImmutableArray localFunctions = this.VisitDeclaredLocalFunctions(node.LocalFunctions); BoundBlockInstrumentation? instrumentation = (BoundBlockInstrumentation?)this.Visit(node.Instrumentation); ImmutableArray statements = this.VisitList(node.Statements); - return node.Update(node.Locals, node.LocalFunctions, node.HasUnsafeModifier, instrumentation, statements); + return node.Update(locals, localFunctions, node.HasUnsafeModifier, instrumentation, statements); } public override BoundNode? VisitScope(BoundScope node) { + ImmutableArray locals = this.VisitLocals(node.Locals); ImmutableArray statements = this.VisitList(node.Statements); - return node.Update(node.Locals, statements); + return node.Update(locals, statements); } public override BoundNode? VisitStateMachineScope(BoundStateMachineScope node) { + ImmutableArray fields = this.VisitSymbols(node.Fields); BoundStatement statement = (BoundStatement)this.Visit(node.Statement); - return node.Update(node.Fields, statement); + return node.Update(fields, statement); } public override BoundNode? VisitLocalDeclaration(BoundLocalDeclaration node) { + LocalSymbol localSymbol = this.VisitLocalSymbol(node.LocalSymbol); BoundTypeExpression? declaredTypeOpt = (BoundTypeExpression?)this.Visit(node.DeclaredTypeOpt); BoundExpression? initializerOpt = (BoundExpression?)this.Visit(node.InitializerOpt); ImmutableArray argumentsOpt = this.VisitList(node.ArgumentsOpt); - return node.Update(node.LocalSymbol, declaredTypeOpt, initializerOpt, argumentsOpt, node.InferredType); + return node.Update(localSymbol, declaredTypeOpt, initializerOpt, argumentsOpt, node.InferredType); } public override BoundNode? VisitMultipleLocalDeclarations(BoundMultipleLocalDeclarations node) { @@ -11353,9 +11394,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { + MethodSymbol symbol = this.VisitMethodSymbol(node.Symbol); BoundBlock? blockBody = (BoundBlock?)this.Visit(node.BlockBody); BoundBlock? expressionBody = (BoundBlock?)this.Visit(node.ExpressionBody); - return node.Update(node.Symbol, blockBody, expressionBody); + return node.Update(symbol, blockBody, expressionBody); } public override BoundNode? VisitNoOpStatement(BoundNoOpStatement node) => node; public override BoundNode? VisitReturnStatement(BoundReturnStatement node) @@ -11379,20 +11421,32 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression expression = (BoundExpression)this.Visit(node.Expression); return node.Update(expression); } - public override BoundNode? VisitBreakStatement(BoundBreakStatement node) => node; - public override BoundNode? VisitContinueStatement(BoundContinueStatement node) => node; + public override BoundNode? VisitBreakStatement(BoundBreakStatement node) + { + LabelSymbol label = this.VisitLabelSymbol(node.Label); + return node.Update(label); + } + public override BoundNode? VisitContinueStatement(BoundContinueStatement node) + { + LabelSymbol label = this.VisitLabelSymbol(node.Label); + return node.Update(label); + } public override BoundNode? VisitSwitchStatement(BoundSwitchStatement node) { + ImmutableArray innerLocals = this.VisitLocals(node.InnerLocals); + ImmutableArray innerLocalFunctions = this.VisitDeclaredLocalFunctions(node.InnerLocalFunctions); + LabelSymbol breakLabel = this.VisitLabelSymbol(node.BreakLabel); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray switchSections = this.VisitList(node.SwitchSections); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; BoundSwitchLabel? defaultLabel = (BoundSwitchLabel?)this.Visit(node.DefaultLabel); - return node.Update(expression, node.InnerLocals, node.InnerLocalFunctions, switchSections, reachabilityDecisionDag, defaultLabel, node.BreakLabel); + return node.Update(expression, innerLocals, innerLocalFunctions, switchSections, reachabilityDecisionDag, defaultLabel, breakLabel); } public override BoundNode? VisitSwitchDispatch(BoundSwitchDispatch node) { + LabelSymbol defaultLabel = this.VisitLabelSymbol(node.DefaultLabel); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); - return node.Update(expression, node.Cases, node.DefaultLabel, node.LengthBasedStringSwitchDataOpt); + return node.Update(expression, node.Cases, defaultLabel, node.LengthBasedStringSwitchDataOpt); } public override BoundNode? VisitIfStatement(BoundIfStatement node) { @@ -11403,26 +11457,39 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDoStatement(BoundDoStatement node) { + ImmutableArray locals = this.VisitLocals(node.Locals); + LabelSymbol breakLabel = this.VisitLabelSymbol(node.BreakLabel); + LabelSymbol continueLabel = this.VisitLabelSymbol(node.ContinueLabel); BoundExpression condition = (BoundExpression)this.Visit(node.Condition); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.Locals, condition, body, node.BreakLabel, node.ContinueLabel); + return node.Update(locals, condition, body, breakLabel, continueLabel); } public override BoundNode? VisitWhileStatement(BoundWhileStatement node) { + ImmutableArray locals = this.VisitLocals(node.Locals); + LabelSymbol breakLabel = this.VisitLabelSymbol(node.BreakLabel); + LabelSymbol continueLabel = this.VisitLabelSymbol(node.ContinueLabel); BoundExpression condition = (BoundExpression)this.Visit(node.Condition); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.Locals, condition, body, node.BreakLabel, node.ContinueLabel); + return node.Update(locals, condition, body, breakLabel, continueLabel); } public override BoundNode? VisitForStatement(BoundForStatement node) { + ImmutableArray outerLocals = this.VisitLocals(node.OuterLocals); + ImmutableArray innerLocals = this.VisitLocals(node.InnerLocals); + LabelSymbol breakLabel = this.VisitLabelSymbol(node.BreakLabel); + LabelSymbol continueLabel = this.VisitLabelSymbol(node.ContinueLabel); BoundStatement? initializer = (BoundStatement?)this.Visit(node.Initializer); BoundExpression? condition = (BoundExpression?)this.Visit(node.Condition); BoundStatement? increment = (BoundStatement?)this.Visit(node.Increment); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.OuterLocals, initializer, node.InnerLocals, condition, increment, body, node.BreakLabel, node.ContinueLabel); + return node.Update(outerLocals, initializer, innerLocals, condition, increment, body, breakLabel, continueLabel); } public override BoundNode? VisitForEachStatement(BoundForEachStatement node) { + ImmutableArray iterationVariables = this.VisitLocals(node.IterationVariables); + LabelSymbol breakLabel = this.VisitLabelSymbol(node.BreakLabel); + LabelSymbol continueLabel = this.VisitLabelSymbol(node.ContinueLabel); BoundValuePlaceholder? elementPlaceholder = node.ElementPlaceholder; BoundExpression? elementConversion = node.ElementConversion; BoundTypeExpression iterationVariableType = (BoundTypeExpression)this.Visit(node.IterationVariableType); @@ -11431,7 +11498,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundForEachDeconstructStep? deconstructionOpt = (BoundForEachDeconstructStep?)this.Visit(node.DeconstructionOpt); BoundAwaitableInfo? awaitOpt = (BoundAwaitableInfo?)this.Visit(node.AwaitOpt); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.EnumeratorInfoOpt, elementPlaceholder, elementConversion, iterationVariableType, node.IterationVariables, iterationErrorExpressionOpt, expression, deconstructionOpt, awaitOpt, body, node.BreakLabel, node.ContinueLabel); + return node.Update(node.EnumeratorInfoOpt, elementPlaceholder, elementConversion, iterationVariableType, iterationVariables, iterationErrorExpressionOpt, expression, deconstructionOpt, awaitOpt, body, breakLabel, continueLabel); } public override BoundNode? VisitForEachDeconstructStep(BoundForEachDeconstructStep node) { @@ -11441,17 +11508,19 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitUsingStatement(BoundUsingStatement node) { + ImmutableArray locals = this.VisitLocals(node.Locals); BoundMultipleLocalDeclarations? declarationsOpt = (BoundMultipleLocalDeclarations?)this.Visit(node.DeclarationsOpt); BoundExpression? expressionOpt = (BoundExpression?)this.Visit(node.ExpressionOpt); BoundStatement body = (BoundStatement)this.Visit(node.Body); BoundAwaitableInfo? awaitOpt = (BoundAwaitableInfo?)this.Visit(node.AwaitOpt); - return node.Update(node.Locals, declarationsOpt, expressionOpt, body, awaitOpt, node.PatternDisposeInfoOpt); + return node.Update(locals, declarationsOpt, expressionOpt, body, awaitOpt, node.PatternDisposeInfoOpt); } public override BoundNode? VisitFixedStatement(BoundFixedStatement node) { + ImmutableArray locals = this.VisitLocals(node.Locals); BoundMultipleLocalDeclarations declarations = (BoundMultipleLocalDeclarations)this.Visit(node.Declarations); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.Locals, declarations, body); + return node.Update(locals, declarations, body); } public override BoundNode? VisitLockStatement(BoundLockStatement node) { @@ -11461,19 +11530,21 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitTryStatement(BoundTryStatement node) { + LabelSymbol? finallyLabelOpt = this.VisitLabelSymbol(node.FinallyLabelOpt); BoundBlock tryBlock = (BoundBlock)this.Visit(node.TryBlock); ImmutableArray catchBlocks = this.VisitList(node.CatchBlocks); BoundBlock? finallyBlockOpt = (BoundBlock?)this.Visit(node.FinallyBlockOpt); - return node.Update(tryBlock, catchBlocks, finallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler); + return node.Update(tryBlock, catchBlocks, finallyBlockOpt, finallyLabelOpt, node.PreferFaultHandler); } public override BoundNode? VisitCatchBlock(BoundCatchBlock node) { + ImmutableArray locals = this.VisitLocals(node.Locals); BoundExpression? exceptionSourceOpt = (BoundExpression?)this.Visit(node.ExceptionSourceOpt); BoundStatementList? exceptionFilterPrologueOpt = (BoundStatementList?)this.Visit(node.ExceptionFilterPrologueOpt); BoundExpression? exceptionFilterOpt = (BoundExpression?)this.Visit(node.ExceptionFilterOpt); BoundBlock body = (BoundBlock)this.Visit(node.Body); TypeSymbol? exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); - return node.Update(node.Locals, exceptionSourceOpt, exceptionTypeOpt, exceptionFilterPrologueOpt, exceptionFilterOpt, body, node.IsSynthesizedAsyncCatchAll); + return node.Update(locals, exceptionSourceOpt, exceptionTypeOpt, exceptionFilterPrologueOpt, exceptionFilterOpt, body, node.IsSynthesizedAsyncCatchAll); } public override BoundNode? VisitLiteral(BoundLiteral node) { @@ -11507,41 +11578,52 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitLocal(BoundLocal node) { + LocalSymbol localSymbol = this.VisitLocalSymbol(node.LocalSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.LocalSymbol, node.DeclarationKind, node.ConstantValueOpt, node.IsNullableUnknown, type); + return node.Update(localSymbol, node.DeclarationKind, node.ConstantValueOpt, node.IsNullableUnknown, type); } public override BoundNode? VisitPseudoVariable(BoundPseudoVariable node) { + LocalSymbol localSymbol = this.VisitLocalSymbol(node.LocalSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.LocalSymbol, node.EmitExpressions, type); + return node.Update(localSymbol, node.EmitExpressions, type); } public override BoundNode? VisitRangeVariable(BoundRangeVariable node) { + RangeVariableSymbol rangeVariableSymbol = this.VisitRangeVariableSymbol(node.RangeVariableSymbol); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.RangeVariableSymbol, value, type); + return node.Update(rangeVariableSymbol, value, type); } public override BoundNode? VisitParameter(BoundParameter node) { + ParameterSymbol parameterSymbol = this.VisitParameterSymbol(node.ParameterSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ParameterSymbol, type); + return node.Update(parameterSymbol, type); + } + public override BoundNode? VisitLabelStatement(BoundLabelStatement node) + { + LabelSymbol label = this.VisitLabelSymbol(node.Label); + return node.Update(label); } - public override BoundNode? VisitLabelStatement(BoundLabelStatement node) => node; public override BoundNode? VisitGotoStatement(BoundGotoStatement node) { + LabelSymbol label = this.VisitLabelSymbol(node.Label); BoundExpression? caseExpressionOpt = (BoundExpression?)this.Visit(node.CaseExpressionOpt); BoundLabel? labelExpressionOpt = (BoundLabel?)this.Visit(node.LabelExpressionOpt); - return node.Update(node.Label, caseExpressionOpt, labelExpressionOpt); + return node.Update(label, caseExpressionOpt, labelExpressionOpt); } public override BoundNode? VisitLabeledStatement(BoundLabeledStatement node) { + LabelSymbol label = this.VisitLabelSymbol(node.Label); BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(node.Label, body); + return node.Update(label, body); } public override BoundNode? VisitLabel(BoundLabel node) { + LabelSymbol label = this.VisitLabelSymbol(node.Label); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Label, type); + return node.Update(label, type); } public override BoundNode? VisitStatementList(BoundStatementList node) { @@ -11550,32 +11632,37 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitConditionalGoto(BoundConditionalGoto node) { + LabelSymbol label = this.VisitLabelSymbol(node.Label); BoundExpression condition = (BoundExpression)this.Visit(node.Condition); - return node.Update(condition, node.JumpIfTrue, node.Label); + return node.Update(condition, node.JumpIfTrue, label); } public override BoundNode? VisitSwitchExpressionArm(BoundSwitchExpressionArm node) { + ImmutableArray locals = this.VisitLocals(node.Locals); + LabelSymbol label = this.VisitLabelSymbol(node.Label); BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); BoundExpression? whenClause = (BoundExpression?)this.Visit(node.WhenClause); BoundExpression value = (BoundExpression)this.Visit(node.Value); - return node.Update(node.Locals, pattern, whenClause, value, node.Label); + return node.Update(locals, pattern, whenClause, value, label); } public override BoundNode? VisitUnconvertedSwitchExpression(BoundUnconvertedSwitchExpression node) { + LabelSymbol? defaultLabel = this.VisitLabelSymbol(node.DefaultLabel); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray switchArms = this.VisitList(node.SwitchArms); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, type); + return node.Update(expression, switchArms, reachabilityDecisionDag, defaultLabel, node.ReportedNotExhaustive, type); } public override BoundNode? VisitConvertedSwitchExpression(BoundConvertedSwitchExpression node) { + LabelSymbol? defaultLabel = this.VisitLabelSymbol(node.DefaultLabel); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray switchArms = this.VisitList(node.SwitchArms); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; TypeSymbol? naturalTypeOpt = this.VisitType(node.NaturalTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(naturalTypeOpt, node.WasTargetTyped, expression, switchArms, reachabilityDecisionDag, node.DefaultLabel, node.ReportedNotExhaustive, type); + return node.Update(naturalTypeOpt, node.WasTargetTyped, expression, switchArms, reachabilityDecisionDag, defaultLabel, node.ReportedNotExhaustive, type); } public override BoundNode? VisitDecisionDag(BoundDecisionDag node) { @@ -11602,7 +11689,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundDecisionDagNode? whenFalse = (BoundDecisionDagNode?)this.Visit(node.WhenFalse); return node.Update(node.Bindings, whenExpression, whenTrue, whenFalse); } - public override BoundNode? VisitLeafDecisionDagNode(BoundLeafDecisionDagNode node) => node; + public override BoundNode? VisitLeafDecisionDagNode(BoundLeafDecisionDagNode node) + { + LabelSymbol label = this.VisitLabelSymbol(node.Label); + return node.Update(label); + } public override BoundNode? VisitDagTemp(BoundDagTemp node) { BoundDagEvaluation? source = (BoundDagEvaluation?)this.Visit(node.Source); @@ -11637,8 +11728,9 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDagDeconstructEvaluation(BoundDagDeconstructEvaluation node) { + MethodSymbol deconstructMethod = this.VisitMethodSymbol(node.DeconstructMethod); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); - return node.Update(node.DeconstructMethod, input); + return node.Update(deconstructMethod, input); } public override BoundNode? VisitDagTypeEvaluation(BoundDagTypeEvaluation node) { @@ -11648,18 +11740,21 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDagFieldEvaluation(BoundDagFieldEvaluation node) { + FieldSymbol field = this.VisitFieldSymbol(node.Field); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); - return node.Update(node.Field, input); + return node.Update(field, input); } public override BoundNode? VisitDagPropertyEvaluation(BoundDagPropertyEvaluation node) { + PropertySymbol property = this.VisitPropertySymbol(node.Property); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); - return node.Update(node.Property, node.IsLengthOrCount, input); + return node.Update(property, node.IsLengthOrCount, input); } public override BoundNode? VisitDagIndexEvaluation(BoundDagIndexEvaluation node) { + PropertySymbol property = this.VisitPropertySymbol(node.Property); BoundDagTemp input = (BoundDagTemp)this.Visit(node.Input); - return node.Update(node.Property, node.Index, input); + return node.Update(property, node.Index, input); } public override BoundNode? VisitDagIndexerEvaluation(BoundDagIndexerEvaluation node) { @@ -11689,15 +11784,17 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitSwitchSection(BoundSwitchSection node) { + ImmutableArray locals = this.VisitLocals(node.Locals); ImmutableArray switchLabels = this.VisitList(node.SwitchLabels); ImmutableArray statements = this.VisitList(node.Statements); - return node.Update(node.Locals, switchLabels, statements); + return node.Update(locals, switchLabels, statements); } public override BoundNode? VisitSwitchLabel(BoundSwitchLabel node) { + LabelSymbol label = this.VisitLabelSymbol(node.Label); BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); BoundExpression? whenClause = (BoundExpression?)this.Visit(node.WhenClause); - return node.Update(node.Label, pattern, whenClause); + return node.Update(label, pattern, whenClause); } public override BoundNode? VisitSequencePointExpression(BoundSequencePointExpression node) { @@ -11707,17 +11804,19 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitSequence(BoundSequence node) { + ImmutableArray locals = this.VisitLocals(node.Locals); ImmutableArray sideEffects = this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Locals, sideEffects, value, type); + return node.Update(locals, sideEffects, value, type); } public override BoundNode? VisitSpillSequence(BoundSpillSequence node) { + ImmutableArray locals = this.VisitLocals(node.Locals); ImmutableArray sideEffects = this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Locals, sideEffects, value, type); + return node.Update(locals, sideEffects, value, type); } public override BoundNode? VisitDynamicMemberAccess(BoundDynamicMemberAccess node) { @@ -11727,10 +11826,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDynamicInvocation(BoundDynamicInvocation node) { + ImmutableArray applicableMethods = this.VisitSymbols(node.ApplicableMethods); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.ApplicableMethods, expression, arguments, type); + return node.Update(node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, applicableMethods, expression, arguments, type); } public override BoundNode? VisitConditionalAccess(BoundConditionalAccess node) { @@ -11741,11 +11841,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) { + MethodSymbol? hasValueMethodOpt = this.VisitMethodSymbol(node.HasValueMethodOpt); BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull); BoundExpression? whenNullOpt = (BoundExpression?)this.Visit(node.WhenNullOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.HasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.ForceCopyOfNullableValueType, type); + return node.Update(receiver, hasValueMethodOpt, whenNotNull, whenNullOpt, node.Id, node.ForceCopyOfNullableValueType, type); } public override BoundNode? VisitConditionalReceiver(BoundConditionalReceiver node) { @@ -11761,36 +11862,44 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitMethodGroup(BoundMethodGroup node) { + ImmutableArray methods = this.VisitSymbols(node.Methods); + Symbol? lookupSymbolOpt = this.VisitSymbol(node.LookupSymbolOpt); + FunctionTypeSymbol? functionType = this.VisitFunctionTypeSymbol(node.FunctionType); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.TypeArgumentsOpt, node.Name, node.Methods, node.LookupSymbolOpt, node.LookupError, node.Flags, node.FunctionType, receiverOpt, node.ResultKind); + return node.Update(node.TypeArgumentsOpt, node.Name, methods, lookupSymbolOpt, node.LookupError, node.Flags, functionType, receiverOpt, node.ResultKind); } public override BoundNode? VisitPropertyGroup(BoundPropertyGroup node) { + ImmutableArray properties = this.VisitSymbols(node.Properties); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Properties, receiverOpt, node.ResultKind); + return node.Update(properties, receiverOpt, node.ResultKind); } public override BoundNode? VisitCall(BoundCall node) { + MethodSymbol method = this.VisitMethodSymbol(node.Method); + ImmutableArray originalMethodsOpt = this.VisitSymbols(node.OriginalMethodsOpt); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, originalMethodsOpt, type); } public override BoundNode? VisitEventAssignmentOperator(BoundEventAssignmentOperator node) { + EventSymbol @event = this.VisitEventSymbol(node.Event); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Event, node.IsAddition, node.IsDynamic, receiverOpt, argument, type); + return node.Update(@event, node.IsAddition, node.IsDynamic, receiverOpt, argument, type); } public override BoundNode? VisitAttribute(BoundAttribute node) { + MethodSymbol? constructor = this.VisitMethodSymbol(node.Constructor); ImmutableArray constructorArguments = this.VisitList(node.ConstructorArguments); ImmutableArray namedArguments = this.VisitList(node.NamedArguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Constructor, constructorArguments, node.ConstructorArgumentNamesOpt, node.ConstructorArgumentsToParamsOpt, node.ConstructorExpanded, node.ConstructorDefaultArguments, namedArguments, node.ResultKind, type); + return node.Update(constructor, constructorArguments, node.ConstructorArgumentNamesOpt, node.ConstructorArgumentsToParamsOpt, node.ConstructorExpanded, node.ConstructorDefaultArguments, namedArguments, node.ResultKind, type); } public override BoundNode? VisitUnconvertedObjectCreationExpression(BoundUnconvertedObjectCreationExpression node) { @@ -11800,10 +11909,12 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitObjectCreationExpression(BoundObjectCreationExpression node) { + MethodSymbol constructor = this.VisitMethodSymbol(node.Constructor); + ImmutableArray constructorsGroup = this.VisitSymbols(node.ConstructorsGroup); ImmutableArray arguments = this.VisitList(node.Arguments); BoundObjectInitializerExpressionBase? initializerExpressionOpt = (BoundObjectInitializerExpressionBase?)this.Visit(node.InitializerExpressionOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Constructor, node.ConstructorsGroup, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ConstantValueOpt, initializerExpressionOpt, node.WasTargetTyped, type); + return node.Update(constructor, constructorsGroup, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ConstantValueOpt, initializerExpressionOpt, node.WasTargetTyped, type); } public override BoundNode? VisitUnconvertedCollectionExpression(BoundUnconvertedCollectionExpression node) { @@ -11813,6 +11924,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitCollectionExpression(BoundCollectionExpression node) { + MethodSymbol? collectionBuilderMethod = this.VisitMethodSymbol(node.CollectionBuilderMethod); BoundObjectOrCollectionValuePlaceholder? placeholder = node.Placeholder; BoundExpression? collectionCreation = node.CollectionCreation; BoundValuePlaceholder? collectionBuilderInvocationPlaceholder = node.CollectionBuilderInvocationPlaceholder; @@ -11820,7 +11932,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundUnconvertedCollectionExpression unconvertedCollectionExpression = node.UnconvertedCollectionExpression; ImmutableArray elements = this.VisitList(node.Elements); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.CollectionTypeKind, placeholder, collectionCreation, node.CollectionBuilderMethod, collectionBuilderInvocationPlaceholder, collectionBuilderInvocationConversion, node.WasTargetTyped, unconvertedCollectionExpression, elements, type); + return node.Update(node.CollectionTypeKind, placeholder, collectionCreation, collectionBuilderMethod, collectionBuilderInvocationPlaceholder, collectionBuilderInvocationConversion, node.WasTargetTyped, unconvertedCollectionExpression, elements, type); } public override BoundNode? VisitCollectionExpressionSpreadExpressionPlaceholder(BoundCollectionExpressionSpreadExpressionPlaceholder node) { @@ -11852,10 +11964,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDynamicObjectCreationExpression(BoundDynamicObjectCreationExpression node) { + ImmutableArray applicableMethods = this.VisitSymbols(node.ApplicableMethods); ImmutableArray arguments = this.VisitList(node.Arguments); BoundObjectInitializerExpressionBase? initializerExpressionOpt = (BoundObjectInitializerExpressionBase?)this.Visit(node.InitializerExpressionOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Name, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, initializerExpressionOpt, node.ApplicableMethods, node.WasTargetTyped, type); + return node.Update(node.Name, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, initializerExpressionOpt, applicableMethods, node.WasTargetTyped, type); } public override BoundNode? VisitNoPiaObjectCreationExpression(BoundNoPiaObjectCreationExpression node) { @@ -11872,10 +11985,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitObjectInitializerMember(BoundObjectInitializerMember node) { + Symbol? memberSymbol = this.VisitSymbol(node.MemberSymbol); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? receiverType = this.VisitType(node.ReceiverType); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.MemberSymbol, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.AccessorKind, receiverType, type); + return node.Update(memberSymbol, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.AccessorKind, receiverType, type); } public override BoundNode? VisitDynamicObjectInitializerMember(BoundDynamicObjectInitializerMember node) { @@ -11892,17 +12006,19 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitCollectionElementInitializer(BoundCollectionElementInitializer node) { + MethodSymbol addMethod = this.VisitMethodSymbol(node.AddMethod); ImmutableArray arguments = this.VisitList(node.Arguments); BoundExpression? implicitReceiverOpt = (BoundExpression?)this.Visit(node.ImplicitReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.AddMethod, arguments, implicitReceiverOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.InvokedAsExtensionMethod, node.ResultKind, type); + return node.Update(addMethod, arguments, implicitReceiverOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.InvokedAsExtensionMethod, node.ResultKind, type); } public override BoundNode? VisitDynamicCollectionElementInitializer(BoundDynamicCollectionElementInitializer node) { + ImmutableArray applicableMethods = this.VisitSymbols(node.ApplicableMethods); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ApplicableMethods, expression, arguments, type); + return node.Update(applicableMethods, expression, arguments, type); } public override BoundNode? VisitImplicitReceiver(BoundImplicitReceiver node) { @@ -11911,15 +12027,17 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitAnonymousObjectCreationExpression(BoundAnonymousObjectCreationExpression node) { + MethodSymbol constructor = this.VisitMethodSymbol(node.Constructor); ImmutableArray arguments = this.VisitList(node.Arguments); ImmutableArray declarations = this.VisitList(node.Declarations); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Constructor, arguments, declarations, type); + return node.Update(constructor, arguments, declarations, type); } public override BoundNode? VisitAnonymousPropertyDeclaration(BoundAnonymousPropertyDeclaration node) { + PropertySymbol property = this.VisitPropertySymbol(node.Property); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Property, type); + return node.Update(property, type); } public override BoundNode? VisitNewT(BoundNewT node) { @@ -11929,9 +12047,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node) { + MethodSymbol? methodOpt = this.VisitMethodSymbol(node.MethodOpt); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(argument, node.MethodOpt, node.IsExtensionMethod, node.WasTargetTyped, type); + return node.Update(argument, methodOpt, node.IsExtensionMethod, node.WasTargetTyped, type); } public override BoundNode? VisitArrayCreation(BoundArrayCreation node) { @@ -11964,33 +12083,39 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitFieldAccess(BoundFieldAccess node) { + FieldSymbol fieldSymbol = this.VisitFieldSymbol(node.FieldSymbol); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.FieldSymbol, node.ConstantValueOpt, node.ResultKind, node.IsByValue, node.IsDeclaration, type); + return node.Update(receiverOpt, fieldSymbol, node.ConstantValueOpt, node.ResultKind, node.IsByValue, node.IsDeclaration, type); } public override BoundNode? VisitHoistedFieldAccess(BoundHoistedFieldAccess node) { + FieldSymbol fieldSymbol = this.VisitFieldSymbol(node.FieldSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.FieldSymbol, type); + return node.Update(fieldSymbol, type); } public override BoundNode? VisitPropertyAccess(BoundPropertyAccess node) { + PropertySymbol propertySymbol = this.VisitPropertySymbol(node.PropertySymbol); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.PropertySymbol, node.AutoPropertyAccessorKind, node.ResultKind, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, propertySymbol, node.AutoPropertyAccessorKind, node.ResultKind, type); } public override BoundNode? VisitEventAccess(BoundEventAccess node) { + EventSymbol eventSymbol = this.VisitEventSymbol(node.EventSymbol); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.EventSymbol, node.IsUsableAsField, node.ResultKind, type); + return node.Update(receiverOpt, eventSymbol, node.IsUsableAsField, node.ResultKind, type); } public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node) { + PropertySymbol indexer = this.VisitPropertySymbol(node.Indexer); + ImmutableArray originalIndexersOpt = this.VisitSymbols(node.OriginalIndexersOpt); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.AccessorKind, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.AccessorKind, node.ArgsToParamsOpt, node.DefaultArguments, originalIndexersOpt, type); } public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { @@ -12012,31 +12137,35 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { + ImmutableArray applicableIndexers = this.VisitSymbols(node.ApplicableIndexers); BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.ApplicableIndexers, type); + return node.Update(receiver, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, applicableIndexers, type); } public override BoundNode? VisitLambda(BoundLambda node) { + MethodSymbol symbol = this.VisitMethodSymbol(node.Symbol); UnboundLambda unboundLambda = node.UnboundLambda; BoundBlock body = (BoundBlock)this.Visit(node.Body); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(unboundLambda, node.Symbol, body, node.Diagnostics, node.Binder, type); + return node.Update(unboundLambda, symbol, body, node.Diagnostics, node.Binder, type); } public override BoundNode? VisitUnboundLambda(UnboundLambda node) { + FunctionTypeSymbol? functionType = this.VisitFunctionTypeSymbol(node.FunctionType); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.Data, node.FunctionType, node.WithDependencies); + return node.Update(node.Data, functionType, node.WithDependencies); } public override BoundNode? VisitQueryClause(BoundQueryClause node) { + RangeVariableSymbol? definedSymbol = this.VisitRangeVariableSymbol(node.DefinedSymbol); BoundExpression value = (BoundExpression)this.Visit(node.Value); BoundExpression? operation = node.Operation; BoundExpression? cast = node.Cast; BoundExpression? unoptimizedForm = node.UnoptimizedForm; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(value, node.DefinedSymbol, operation, cast, node.Binder, unoptimizedForm, type); + return node.Update(value, definedSymbol, operation, cast, node.Binder, unoptimizedForm, type); } public override BoundNode? VisitTypeOrInstanceInitializers(BoundTypeOrInstanceInitializers node) { @@ -12081,11 +12210,13 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitIsPatternExpression(BoundIsPatternExpression node) { + LabelSymbol whenTrueLabel = this.VisitLabelSymbol(node.WhenTrueLabel); + LabelSymbol whenFalseLabel = this.VisitLabelSymbol(node.WhenFalseLabel); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(expression, pattern, node.IsNegated, reachabilityDecisionDag, node.WhenTrueLabel, node.WhenFalseLabel, type); + return node.Update(expression, pattern, node.IsNegated, reachabilityDecisionDag, whenTrueLabel, whenFalseLabel, type); } public override BoundNode? VisitConstantPattern(BoundConstantPattern node) { @@ -12102,24 +12233,28 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitDeclarationPattern(BoundDeclarationPattern node) { + Symbol? variable = this.VisitSymbol(node.Variable); BoundTypeExpression declaredType = (BoundTypeExpression)this.Visit(node.DeclaredType); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(declaredType, node.IsVar, node.Variable, variableAccess, inputType, narrowedType); + return node.Update(declaredType, node.IsVar, variable, variableAccess, inputType, narrowedType); } public override BoundNode? VisitRecursivePattern(BoundRecursivePattern node) { + MethodSymbol? deconstructMethod = this.VisitMethodSymbol(node.DeconstructMethod); + Symbol? variable = this.VisitSymbol(node.Variable); BoundTypeExpression? declaredType = (BoundTypeExpression?)this.Visit(node.DeclaredType); ImmutableArray deconstruction = this.VisitList(node.Deconstruction); ImmutableArray properties = this.VisitList(node.Properties); BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(declaredType, node.DeconstructMethod, deconstruction, properties, node.IsExplicitNotNullTest, node.Variable, variableAccess, inputType, narrowedType); + return node.Update(declaredType, deconstructMethod, deconstruction, properties, node.IsExplicitNotNullTest, variable, variableAccess, inputType, narrowedType); } public override BoundNode? VisitListPattern(BoundListPattern node) { + Symbol? variable = this.VisitSymbol(node.Variable); ImmutableArray subpatterns = this.VisitList(node.Subpatterns); BoundExpression? lengthAccess = node.LengthAccess; BoundExpression? indexerAccess = node.IndexerAccess; @@ -12128,7 +12263,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? variableAccess = (BoundExpression?)this.Visit(node.VariableAccess); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(subpatterns, node.HasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, node.Variable, variableAccess, inputType, narrowedType); + return node.Update(subpatterns, node.HasSlice, lengthAccess, indexerAccess, receiverPlaceholder, argumentPlaceholder, variable, variableAccess, inputType, narrowedType); } public override BoundNode? VisitSlicePattern(BoundSlicePattern node) { @@ -12142,15 +12277,18 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitITuplePattern(BoundITuplePattern node) { + MethodSymbol getLengthMethod = this.VisitMethodSymbol(node.GetLengthMethod); + MethodSymbol getItemMethod = this.VisitMethodSymbol(node.GetItemMethod); ImmutableArray subpatterns = this.VisitList(node.Subpatterns); TypeSymbol? inputType = this.VisitType(node.InputType); TypeSymbol? narrowedType = this.VisitType(node.NarrowedType); - return node.Update(node.GetLengthMethod, node.GetItemMethod, subpatterns, inputType, narrowedType); + return node.Update(getLengthMethod, getItemMethod, subpatterns, inputType, narrowedType); } public override BoundNode? VisitPositionalSubpattern(BoundPositionalSubpattern node) { + Symbol? symbol = this.VisitSymbol(node.Symbol); BoundPattern pattern = (BoundPattern)this.Visit(node.Pattern); - return node.Update(node.Symbol, pattern); + return node.Update(symbol, pattern); } public override BoundNode? VisitPropertySubpattern(BoundPropertySubpattern node) { @@ -12160,9 +12298,10 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitPropertySubpatternMember(BoundPropertySubpatternMember node) { + Symbol? symbol = this.VisitSymbol(node.Symbol); BoundPropertySubpatternMember? receiver = (BoundPropertySubpatternMember?)this.Visit(node.Receiver); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.Symbol, type); + return node.Update(receiver, symbol, type); } public override BoundNode? VisitTypePattern(BoundTypePattern node) { @@ -12206,20 +12345,23 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitOutVariablePendingInference(OutVariablePendingInference node) { + Symbol variableSymbol = this.VisitSymbol(node.VariableSymbol); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.VariableSymbol, receiverOpt); + return node.Update(variableSymbol, receiverOpt); } public override BoundNode? VisitDeconstructionVariablePendingInference(DeconstructionVariablePendingInference node) { + Symbol variableSymbol = this.VisitSymbol(node.VariableSymbol); BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.VariableSymbol, receiverOpt); + return node.Update(variableSymbol, receiverOpt); } public override BoundNode? VisitOutDeconstructVarPendingInference(OutDeconstructVarPendingInference node) { + Symbol? variableSymbol = this.VisitSymbol(node.VariableSymbol); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.VariableSymbol, node.IsDiscardExpression); + return node.Update(variableSymbol, node.IsDiscardExpression); } public override BoundNode? VisitNonConstructorMethodBody(BoundNonConstructorMethodBody node) { @@ -12229,10 +12371,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitConstructorMethodBody(BoundConstructorMethodBody node) { + ImmutableArray locals = this.VisitLocals(node.Locals); BoundStatement? initializer = (BoundStatement?)this.Visit(node.Initializer); BoundBlock? blockBody = (BoundBlock?)this.Visit(node.BlockBody); BoundBlock? expressionBody = (BoundBlock?)this.Visit(node.ExpressionBody); - return node.Update(node.Locals, initializer, blockBody, expressionBody); + return node.Update(locals, initializer, blockBody, expressionBody); } public override BoundNode? VisitExpressionWithNullability(BoundExpressionWithNullability node) { @@ -12242,10 +12385,11 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitWithExpression(BoundWithExpression node) { + MethodSymbol? cloneMethod = this.VisitMethodSymbol(node.CloneMethod); BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundObjectInitializerExpressionBase initializerExpression = (BoundObjectInitializerExpressionBase)this.Visit(node.InitializerExpression); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiver, node.CloneMethod, initializerExpression, type); + return node.Update(receiver, cloneMethod, initializerExpression, type); } } @@ -13301,18 +13445,17 @@ public NullabilityRewriter(ImmutableDictionary originalUserDefinedConversionsOpt = GetUpdatedArray(node, node.OriginalUserDefinedConversionsOpt); BoundExpression operand = (BoundExpression)this.Visit(node.Operand); BoundConversion updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { - updatedNode = node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, node.OriginalUserDefinedConversionsOpt, infoAndType.Type!); + updatedNode = node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, infoAndType.Type!); updatedNode.TopLevelNullability = infoAndType.Info; } else { - updatedNode = node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, node.OriginalUserDefinedConversionsOpt, node.Type); + updatedNode = node.Update(operand, node.Conversion, node.IsBaseConversion, node.Checked, node.ExplicitCastInCode, node.ConstantValueOpt, node.ConversionGroupOpt, node.Type); } return updatedNode; } @@ -13388,7 +13531,7 @@ public NullabilityRewriter(ImmutableDictionary locals = GetUpdatedArray(node, node.Locals); - ImmutableArray localFunctions = GetUpdatedArray(node, node.LocalFunctions); + ImmutableArray localFunctions = GetUpdatedArray(node, node.LocalFunctions); BoundBlockInstrumentation? instrumentation = (BoundBlockInstrumentation?)this.Visit(node.Instrumentation); ImmutableArray statements = this.VisitList(node.Statements); return node.Update(locals, localFunctions, node.HasUnsafeModifier, instrumentation, statements); @@ -13419,7 +13562,7 @@ public NullabilityRewriter(ImmutableDictionary innerLocals = GetUpdatedArray(node, node.InnerLocals); - ImmutableArray innerLocalFunctions = GetUpdatedArray(node, node.InnerLocalFunctions); + ImmutableArray innerLocalFunctions = GetUpdatedArray(node, node.InnerLocalFunctions); BoundExpression expression = (BoundExpression)this.Visit(node.Expression); ImmutableArray switchSections = this.VisitList(node.SwitchSections); BoundDecisionDag reachabilityDecisionDag = node.ReachabilityDecisionDag; @@ -14576,7 +14719,7 @@ public NullabilityRewriter(ImmutableDictionaryGets the identifier. - public abstract SyntaxToken Identifier { get; } + public abstract SyntaxToken? Identifier { get; } /// Gets the base type list. public abstract BaseListSyntax? BaseList { get; } @@ -16524,7 +16524,7 @@ internal BaseTypeDeclarationSyntax(SyntaxKind kind) public abstract SyntaxToken? SemicolonToken { get; } } -/// Base class for type declaration syntax (class, struct, interface, record). +/// Base class for type declaration syntax (class, struct, interface, record, extension). internal abstract partial class TypeDeclarationSyntax : BaseTypeDeclarationSyntax { internal TypeDeclarationSyntax(SyntaxKind kind, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) @@ -16537,7 +16537,7 @@ internal TypeDeclarationSyntax(SyntaxKind kind) { } - /// Gets the type keyword token ("class", "struct", "interface", "record"). + /// Gets the type keyword token ("class", "struct", "interface", "record", "extension"). public abstract SyntaxToken Keyword { get; } public abstract TypeParameterListSyntax? TypeParameterList { get; } @@ -18109,6 +18109,236 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) => new EnumMemberDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.identifier, this.equalsValue, GetDiagnostics(), annotations); } +/// Extension container syntax. +internal sealed partial class ExtensionDeclarationSyntax : TypeDeclarationSyntax +{ + internal readonly GreenNode? attributeLists; + internal readonly GreenNode? modifiers; + internal readonly SyntaxToken keyword; + internal readonly TypeParameterListSyntax? typeParameterList; + internal readonly ParameterListSyntax? parameterList; + internal readonly GreenNode? constraintClauses; + internal readonly SyntaxToken? openBraceToken; + internal readonly GreenNode? members; + internal readonly SyntaxToken? closeBraceToken; + internal readonly SyntaxToken? semicolonToken; + + internal ExtensionDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, GreenNode? constraintClauses, SyntaxToken? openBraceToken, GreenNode? members, SyntaxToken? closeBraceToken, SyntaxToken? semicolonToken, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 10; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(keyword); + this.keyword = keyword; + if (typeParameterList != null) + { + this.AdjustFlagsAndWidth(typeParameterList); + this.typeParameterList = typeParameterList; + } + if (parameterList != null) + { + this.AdjustFlagsAndWidth(parameterList); + this.parameterList = parameterList; + } + if (constraintClauses != null) + { + this.AdjustFlagsAndWidth(constraintClauses); + this.constraintClauses = constraintClauses; + } + if (openBraceToken != null) + { + this.AdjustFlagsAndWidth(openBraceToken); + this.openBraceToken = openBraceToken; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + if (closeBraceToken != null) + { + this.AdjustFlagsAndWidth(closeBraceToken); + this.closeBraceToken = closeBraceToken; + } + if (semicolonToken != null) + { + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + } + } + + internal ExtensionDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, GreenNode? constraintClauses, SyntaxToken? openBraceToken, GreenNode? members, SyntaxToken? closeBraceToken, SyntaxToken? semicolonToken, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 10; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(keyword); + this.keyword = keyword; + if (typeParameterList != null) + { + this.AdjustFlagsAndWidth(typeParameterList); + this.typeParameterList = typeParameterList; + } + if (parameterList != null) + { + this.AdjustFlagsAndWidth(parameterList); + this.parameterList = parameterList; + } + if (constraintClauses != null) + { + this.AdjustFlagsAndWidth(constraintClauses); + this.constraintClauses = constraintClauses; + } + if (openBraceToken != null) + { + this.AdjustFlagsAndWidth(openBraceToken); + this.openBraceToken = openBraceToken; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + if (closeBraceToken != null) + { + this.AdjustFlagsAndWidth(closeBraceToken); + this.closeBraceToken = closeBraceToken; + } + if (semicolonToken != null) + { + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + } + } + + internal ExtensionDeclarationSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, GreenNode? constraintClauses, SyntaxToken? openBraceToken, GreenNode? members, SyntaxToken? closeBraceToken, SyntaxToken? semicolonToken) + : base(kind) + { + this.SlotCount = 10; + if (attributeLists != null) + { + this.AdjustFlagsAndWidth(attributeLists); + this.attributeLists = attributeLists; + } + if (modifiers != null) + { + this.AdjustFlagsAndWidth(modifiers); + this.modifiers = modifiers; + } + this.AdjustFlagsAndWidth(keyword); + this.keyword = keyword; + if (typeParameterList != null) + { + this.AdjustFlagsAndWidth(typeParameterList); + this.typeParameterList = typeParameterList; + } + if (parameterList != null) + { + this.AdjustFlagsAndWidth(parameterList); + this.parameterList = parameterList; + } + if (constraintClauses != null) + { + this.AdjustFlagsAndWidth(constraintClauses); + this.constraintClauses = constraintClauses; + } + if (openBraceToken != null) + { + this.AdjustFlagsAndWidth(openBraceToken); + this.openBraceToken = openBraceToken; + } + if (members != null) + { + this.AdjustFlagsAndWidth(members); + this.members = members; + } + if (closeBraceToken != null) + { + this.AdjustFlagsAndWidth(closeBraceToken); + this.closeBraceToken = closeBraceToken; + } + if (semicolonToken != null) + { + this.AdjustFlagsAndWidth(semicolonToken); + this.semicolonToken = semicolonToken; + } + } + + public override CoreSyntax.SyntaxList AttributeLists => new CoreSyntax.SyntaxList(this.attributeLists); + public override CoreSyntax.SyntaxList Modifiers => new CoreSyntax.SyntaxList(this.modifiers); + public override SyntaxToken Keyword => this.keyword; + public override TypeParameterListSyntax? TypeParameterList => this.typeParameterList; + public override ParameterListSyntax? ParameterList => this.parameterList; + public override CoreSyntax.SyntaxList ConstraintClauses => new CoreSyntax.SyntaxList(this.constraintClauses); + public override SyntaxToken? OpenBraceToken => this.openBraceToken; + public override CoreSyntax.SyntaxList Members => new CoreSyntax.SyntaxList(this.members); + public override SyntaxToken? CloseBraceToken => this.closeBraceToken; + public override SyntaxToken? SemicolonToken => this.semicolonToken; + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.attributeLists, + 1 => this.modifiers, + 2 => this.keyword, + 3 => this.typeParameterList, + 4 => this.parameterList, + 5 => this.constraintClauses, + 6 => this.openBraceToken, + 7 => this.members, + 8 => this.closeBraceToken, + 9 => this.semicolonToken, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.ExtensionDeclarationSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionDeclaration(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionDeclaration(this); + + public ExtensionDeclarationSyntax Update(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, SyntaxToken keyword, TypeParameterListSyntax typeParameterList, ParameterListSyntax parameterList, CoreSyntax.SyntaxList constraintClauses, SyntaxToken openBraceToken, CoreSyntax.SyntaxList members, SyntaxToken closeBraceToken, SyntaxToken semicolonToken) + { + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || keyword != this.Keyword || typeParameterList != this.TypeParameterList || parameterList != this.ParameterList || constraintClauses != this.ConstraintClauses || openBraceToken != this.OpenBraceToken || members != this.Members || closeBraceToken != this.CloseBraceToken || semicolonToken != this.SemicolonToken) + { + var newNode = SyntaxFactory.ExtensionDeclaration(attributeLists, modifiers, keyword, typeParameterList, parameterList, constraintClauses, openBraceToken, members, closeBraceToken, semicolonToken); + var diags = GetDiagnostics(); + if (diags?.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations?.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) + => new ExtensionDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.keyword, this.typeParameterList, this.parameterList, this.constraintClauses, this.openBraceToken, this.members, this.closeBraceToken, this.semicolonToken, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new ExtensionDeclarationSyntax(this.Kind, this.attributeLists, this.modifiers, this.keyword, this.typeParameterList, this.parameterList, this.constraintClauses, this.openBraceToken, this.members, this.closeBraceToken, this.semicolonToken, GetDiagnostics(), annotations); +} + /// Base list syntax. internal sealed partial class BaseListSyntax : CSharpSyntaxNode { @@ -21618,10 +21848,10 @@ internal sealed partial class ParameterSyntax : BaseParameterSyntax internal readonly GreenNode? attributeLists; internal readonly GreenNode? modifiers; internal readonly TypeSyntax? type; - internal readonly SyntaxToken identifier; + internal readonly SyntaxToken? identifier; internal readonly EqualsValueClauseSyntax? @default; - internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken identifier, EqualsValueClauseSyntax? @default, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken? identifier, EqualsValueClauseSyntax? @default, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) : base(kind, diagnostics, annotations) { this.SlotCount = 5; @@ -21640,8 +21870,11 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? this.AdjustFlagsAndWidth(type); this.type = type; } - this.AdjustFlagsAndWidth(identifier); - this.identifier = identifier; + if (identifier != null) + { + this.AdjustFlagsAndWidth(identifier); + this.identifier = identifier; + } if (@default != null) { this.AdjustFlagsAndWidth(@default); @@ -21649,7 +21882,7 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? } } - internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken identifier, EqualsValueClauseSyntax? @default, SyntaxFactoryContext context) + internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken? identifier, EqualsValueClauseSyntax? @default, SyntaxFactoryContext context) : base(kind) { this.SetFactoryContext(context); @@ -21669,8 +21902,11 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? this.AdjustFlagsAndWidth(type); this.type = type; } - this.AdjustFlagsAndWidth(identifier); - this.identifier = identifier; + if (identifier != null) + { + this.AdjustFlagsAndWidth(identifier); + this.identifier = identifier; + } if (@default != null) { this.AdjustFlagsAndWidth(@default); @@ -21678,7 +21914,7 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? } } - internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken identifier, EqualsValueClauseSyntax? @default) + internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? modifiers, TypeSyntax? type, SyntaxToken? identifier, EqualsValueClauseSyntax? @default) : base(kind) { this.SlotCount = 5; @@ -21697,8 +21933,11 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? this.AdjustFlagsAndWidth(type); this.type = type; } - this.AdjustFlagsAndWidth(identifier); - this.identifier = identifier; + if (identifier != null) + { + this.AdjustFlagsAndWidth(identifier); + this.identifier = identifier; + } if (@default != null) { this.AdjustFlagsAndWidth(@default); @@ -21712,7 +21951,7 @@ internal ParameterSyntax(SyntaxKind kind, GreenNode? attributeLists, GreenNode? public override CoreSyntax.SyntaxList Modifiers => new CoreSyntax.SyntaxList(this.modifiers); public override TypeSyntax? Type => this.type; /// Gets the identifier. - public SyntaxToken Identifier => this.identifier; + public SyntaxToken? Identifier => this.identifier; public EqualsValueClauseSyntax? Default => this.@default; internal override GreenNode? GetSlot(int index) @@ -26365,6 +26604,96 @@ internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) => new ShebangDirectiveTriviaSyntax(this.Kind, this.hashToken, this.exclamationToken, this.endOfDirectiveToken, this.isActive, GetDiagnostics(), annotations); } +internal sealed partial class IgnoredDirectiveTriviaSyntax : DirectiveTriviaSyntax +{ + internal readonly SyntaxToken hashToken; + internal readonly SyntaxToken colonToken; + internal readonly SyntaxToken endOfDirectiveToken; + internal readonly bool isActive; + + internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive, DiagnosticInfo[]? diagnostics, SyntaxAnnotation[]? annotations) + : base(kind, diagnostics, annotations) + { + this.SlotCount = 3; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive, SyntaxFactoryContext context) + : base(kind) + { + this.SetFactoryContext(context); + this.SlotCount = 3; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + internal IgnoredDirectiveTriviaSyntax(SyntaxKind kind, SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + : base(kind) + { + this.SlotCount = 3; + this.AdjustFlagsAndWidth(hashToken); + this.hashToken = hashToken; + this.AdjustFlagsAndWidth(colonToken); + this.colonToken = colonToken; + this.AdjustFlagsAndWidth(endOfDirectiveToken); + this.endOfDirectiveToken = endOfDirectiveToken; + this.isActive = isActive; + } + + public override SyntaxToken HashToken => this.hashToken; + public SyntaxToken ColonToken => this.colonToken; + public override SyntaxToken EndOfDirectiveToken => this.endOfDirectiveToken; + public override bool IsActive => this.isActive; + + internal override GreenNode? GetSlot(int index) + => index switch + { + 0 => this.hashToken, + 1 => this.colonToken, + 2 => this.endOfDirectiveToken, + _ => null, + }; + + internal override SyntaxNode CreateRed(SyntaxNode? parent, int position) => new CSharp.Syntax.IgnoredDirectiveTriviaSyntax(this, parent, position); + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this); + public override TResult Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this); + + public IgnoredDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken != this.HashToken || colonToken != this.ColonToken || endOfDirectiveToken != this.EndOfDirectiveToken) + { + var newNode = SyntaxFactory.IgnoredDirectiveTrivia(hashToken, colonToken, endOfDirectiveToken, isActive); + var diags = GetDiagnostics(); + if (diags?.Length > 0) + newNode = newNode.WithDiagnosticsGreen(diags); + var annotations = GetAnnotations(); + if (annotations?.Length > 0) + newNode = newNode.WithAnnotationsGreen(annotations); + return newNode; + } + + return this; + } + + internal override GreenNode SetDiagnostics(DiagnosticInfo[]? diagnostics) + => new IgnoredDirectiveTriviaSyntax(this.Kind, this.hashToken, this.colonToken, this.endOfDirectiveToken, this.isActive, diagnostics, GetAnnotations()); + + internal override GreenNode SetAnnotations(SyntaxAnnotation[]? annotations) + => new IgnoredDirectiveTriviaSyntax(this.Kind, this.hashToken, this.colonToken, this.endOfDirectiveToken, this.isActive, GetDiagnostics(), annotations); +} + internal sealed partial class NullableDirectiveTriviaSyntax : DirectiveTriviaSyntax { internal readonly SyntaxToken hashToken; @@ -26654,6 +26983,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitEnumDeclaration(EnumDeclarationSyntax node) => this.DefaultVisit(node); public virtual TResult VisitDelegateDeclaration(DelegateDeclarationSyntax node) => this.DefaultVisit(node); public virtual TResult VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitExtensionDeclaration(ExtensionDeclarationSyntax node) => this.DefaultVisit(node); public virtual TResult VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); public virtual TResult VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); public virtual TResult VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); @@ -26727,6 +27057,7 @@ internal partial class CSharpSyntaxVisitor public virtual TResult VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node); + public virtual TResult VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual TResult VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node); } @@ -26902,6 +27233,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitEnumDeclaration(EnumDeclarationSyntax node) => this.DefaultVisit(node); public virtual void VisitDelegateDeclaration(DelegateDeclarationSyntax node) => this.DefaultVisit(node); public virtual void VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); + public virtual void VisitExtensionDeclaration(ExtensionDeclarationSyntax node) => this.DefaultVisit(node); public virtual void VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); public virtual void VisitSimpleBaseType(SimpleBaseTypeSyntax node) => this.DefaultVisit(node); public virtual void VisitPrimaryConstructorBaseType(PrimaryConstructorBaseTypeSyntax node) => this.DefaultVisit(node); @@ -26975,6 +27307,7 @@ internal partial class CSharpSyntaxVisitor public virtual void VisitReferenceDirectiveTrivia(ReferenceDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node); + public virtual void VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node); public virtual void VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node); } @@ -27490,6 +27823,9 @@ public override CSharpSyntaxNode VisitDelegateDeclaration(DelegateDeclarationSyn public override CSharpSyntaxNode VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.Identifier), (EqualsValueClauseSyntax)Visit(node.EqualsValue)); + public override CSharpSyntaxNode VisitExtensionDeclaration(ExtensionDeclarationSyntax node) + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), (SyntaxToken)Visit(node.Keyword), (TypeParameterListSyntax)Visit(node.TypeParameterList), (ParameterListSyntax)Visit(node.ParameterList), VisitList(node.ConstraintClauses), (SyntaxToken)Visit(node.OpenBraceToken), VisitList(node.Members), (SyntaxToken)Visit(node.CloseBraceToken), (SyntaxToken)Visit(node.SemicolonToken)); + public override CSharpSyntaxNode VisitBaseList(BaseListSyntax node) => node.Update((SyntaxToken)Visit(node.ColonToken), VisitList(node.Types)); @@ -27709,6 +28045,9 @@ public override CSharpSyntaxNode VisitLoadDirectiveTrivia(LoadDirectiveTriviaSyn public override CSharpSyntaxNode VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.ExclamationToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); + public override CSharpSyntaxNode VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) + => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.ColonToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); + public override CSharpSyntaxNode VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => node.Update((SyntaxToken)Visit(node.HashToken), (SyntaxToken)Visit(node.NullableKeyword), (SyntaxToken)Visit(node.SettingToken), (SyntaxToken)Visit(node.TargetToken), (SyntaxToken)Visit(node.EndOfDirectiveToken), node.IsActive); } @@ -31416,6 +31755,43 @@ public EnumMemberDeclarationSyntax EnumMemberDeclaration(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, CoreSyntax.SyntaxList constraintClauses, SyntaxToken? openBraceToken, CoreSyntax.SyntaxList members, SyntaxToken? closeBraceToken, SyntaxToken? semicolonToken) + { +#if DEBUG + if (keyword == null) throw new ArgumentNullException(nameof(keyword)); + if (keyword.Kind != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(keyword)); + if (openBraceToken != null) + { + switch (openBraceToken.Kind) + { + case SyntaxKind.OpenBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(openBraceToken)); + } + } + if (closeBraceToken != null) + { + switch (closeBraceToken.Kind) + { + case SyntaxKind.CloseBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(closeBraceToken)); + } + } + if (semicolonToken != null) + { + switch (semicolonToken.Kind) + { + case SyntaxKind.SemicolonToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(semicolonToken)); + } + } +#endif + + return new ExtensionDeclarationSyntax(SyntaxKind.ExtensionDeclaration, attributeLists.Node, modifiers.Node, keyword, typeParameterList, parameterList, constraintClauses.Node, openBraceToken, members.Node, closeBraceToken, semicolonToken, this.context); + } + public BaseListSyntax BaseList(SyntaxToken colonToken, CoreSyntax.SeparatedSyntaxList types) { #if DEBUG @@ -32059,15 +32435,18 @@ public BracketedParameterListSyntax BracketedParameterList(SyntaxToken openBrack return result; } - public ParameterSyntax Parameter(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, TypeSyntax? type, SyntaxToken identifier, EqualsValueClauseSyntax? @default) + public ParameterSyntax Parameter(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, TypeSyntax? type, SyntaxToken? identifier, EqualsValueClauseSyntax? @default) { #if DEBUG - if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - switch (identifier.Kind) + if (identifier != null) { - case SyntaxKind.IdentifierToken: - case SyntaxKind.ArgListKeyword: break; - default: throw new ArgumentException(nameof(identifier)); + switch (identifier.Kind) + { + case SyntaxKind.IdentifierToken: + case SyntaxKind.ArgListKeyword: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(identifier)); + } } #endif @@ -32953,6 +33332,20 @@ public ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken hashToken return new ShebangDirectiveTriviaSyntax(SyntaxKind.ShebangDirectiveTrivia, hashToken, exclamationToken, endOfDirectiveToken, isActive, this.context); } + public IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + { +#if DEBUG + if (hashToken == null) throw new ArgumentNullException(nameof(hashToken)); + if (hashToken.Kind != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken)); + if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); + if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); + if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken)); + if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); +#endif + + return new IgnoredDirectiveTriviaSyntax(SyntaxKind.IgnoredDirectiveTrivia, hashToken, colonToken, endOfDirectiveToken, isActive, this.context); + } + public NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken? targetToken, SyntaxToken endOfDirectiveToken, bool isActive) { #if DEBUG @@ -36684,6 +37077,43 @@ public static EnumMemberDeclarationSyntax EnumMemberDeclaration(CoreSyntax.Synta return new EnumMemberDeclarationSyntax(SyntaxKind.EnumMemberDeclaration, attributeLists.Node, modifiers.Node, identifier, equalsValue); } + public static ExtensionDeclarationSyntax ExtensionDeclaration(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, CoreSyntax.SyntaxList constraintClauses, SyntaxToken? openBraceToken, CoreSyntax.SyntaxList members, SyntaxToken? closeBraceToken, SyntaxToken? semicolonToken) + { +#if DEBUG + if (keyword == null) throw new ArgumentNullException(nameof(keyword)); + if (keyword.Kind != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(keyword)); + if (openBraceToken != null) + { + switch (openBraceToken.Kind) + { + case SyntaxKind.OpenBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(openBraceToken)); + } + } + if (closeBraceToken != null) + { + switch (closeBraceToken.Kind) + { + case SyntaxKind.CloseBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(closeBraceToken)); + } + } + if (semicolonToken != null) + { + switch (semicolonToken.Kind) + { + case SyntaxKind.SemicolonToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(semicolonToken)); + } + } +#endif + + return new ExtensionDeclarationSyntax(SyntaxKind.ExtensionDeclaration, attributeLists.Node, modifiers.Node, keyword, typeParameterList, parameterList, constraintClauses.Node, openBraceToken, members.Node, closeBraceToken, semicolonToken); + } + public static BaseListSyntax BaseList(SyntaxToken colonToken, CoreSyntax.SeparatedSyntaxList types) { #if DEBUG @@ -37327,15 +37757,18 @@ public static BracketedParameterListSyntax BracketedParameterList(SyntaxToken op return result; } - public static ParameterSyntax Parameter(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, TypeSyntax? type, SyntaxToken identifier, EqualsValueClauseSyntax? @default) + public static ParameterSyntax Parameter(CoreSyntax.SyntaxList attributeLists, CoreSyntax.SyntaxList modifiers, TypeSyntax? type, SyntaxToken? identifier, EqualsValueClauseSyntax? @default) { #if DEBUG - if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - switch (identifier.Kind) + if (identifier != null) { - case SyntaxKind.IdentifierToken: - case SyntaxKind.ArgListKeyword: break; - default: throw new ArgumentException(nameof(identifier)); + switch (identifier.Kind) + { + case SyntaxKind.IdentifierToken: + case SyntaxKind.ArgListKeyword: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(identifier)); + } } #endif @@ -38221,6 +38654,20 @@ public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken ha return new ShebangDirectiveTriviaSyntax(SyntaxKind.ShebangDirectiveTrivia, hashToken, exclamationToken, endOfDirectiveToken, isActive); } + public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + { +#if DEBUG + if (hashToken == null) throw new ArgumentNullException(nameof(hashToken)); + if (hashToken.Kind != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken)); + if (colonToken == null) throw new ArgumentNullException(nameof(colonToken)); + if (colonToken.Kind != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); + if (endOfDirectiveToken == null) throw new ArgumentNullException(nameof(endOfDirectiveToken)); + if (endOfDirectiveToken.Kind != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); +#endif + + return new IgnoredDirectiveTriviaSyntax(SyntaxKind.IgnoredDirectiveTrivia, hashToken, colonToken, endOfDirectiveToken, isActive); + } + public static NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken? targetToken, SyntaxToken endOfDirectiveToken, bool isActive) { #if DEBUG diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs index 0b31bde257994..6998b9cf5448b 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs @@ -525,6 +525,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a EnumMemberDeclarationSyntax node. public virtual TResult? VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExtensionDeclarationSyntax node. + public virtual TResult? VisitExtensionDeclaration(ExtensionDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a BaseListSyntax node. public virtual TResult? VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); @@ -744,6 +747,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a ShebangDirectiveTriviaSyntax node. public virtual TResult? VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a IgnoredDirectiveTriviaSyntax node. + public virtual TResult? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a NullableDirectiveTriviaSyntax node. public virtual TResult? VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node); } @@ -1260,6 +1266,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a EnumMemberDeclarationSyntax node. public virtual void VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a ExtensionDeclarationSyntax node. + public virtual void VisitExtensionDeclaration(ExtensionDeclarationSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a BaseListSyntax node. public virtual void VisitBaseList(BaseListSyntax node) => this.DefaultVisit(node); @@ -1479,6 +1488,9 @@ public partial class CSharpSyntaxVisitor /// Called when the visitor visits a ShebangDirectiveTriviaSyntax node. public virtual void VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a IgnoredDirectiveTriviaSyntax node. + public virtual void VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) => this.DefaultVisit(node); + /// Called when the visitor visits a NullableDirectiveTriviaSyntax node. public virtual void VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => this.DefaultVisit(node); } @@ -1995,6 +2007,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitEnumMemberDeclaration(EnumMemberDeclarationSyntax node) => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.Identifier), (EqualsValueClauseSyntax?)Visit(node.EqualsValue)); + public override SyntaxNode? VisitExtensionDeclaration(ExtensionDeclarationSyntax node) + => node.Update(VisitList(node.AttributeLists), VisitList(node.Modifiers), VisitToken(node.Keyword), (TypeParameterListSyntax?)Visit(node.TypeParameterList), (ParameterListSyntax?)Visit(node.ParameterList), VisitList(node.ConstraintClauses), VisitToken(node.OpenBraceToken), VisitList(node.Members), VisitToken(node.CloseBraceToken), VisitToken(node.SemicolonToken)); + public override SyntaxNode? VisitBaseList(BaseListSyntax node) => node.Update(VisitToken(node.ColonToken), VisitList(node.Types)); @@ -2214,6 +2229,9 @@ public partial class CSharpSyntaxRewriter : CSharpSyntaxVisitor public override SyntaxNode? VisitShebangDirectiveTrivia(ShebangDirectiveTriviaSyntax node) => node.Update(VisitToken(node.HashToken), VisitToken(node.ExclamationToken), VisitToken(node.EndOfDirectiveToken), node.IsActive); + public override SyntaxNode? VisitIgnoredDirectiveTrivia(IgnoredDirectiveTriviaSyntax node) + => node.Update(VisitToken(node.HashToken), VisitToken(node.ColonToken), VisitToken(node.EndOfDirectiveToken), node.IsActive); + public override SyntaxNode? VisitNullableDirectiveTrivia(NullableDirectiveTriviaSyntax node) => node.Update(VisitToken(node.HashToken), VisitToken(node.NullableKeyword), VisitToken(node.SettingToken), VisitToken(node.TargetToken), VisitToken(node.EndOfDirectiveToken), node.IsActive); } @@ -3196,14 +3214,6 @@ public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(SyntaxList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.ParameterSyntax)parameter.Green, (Syntax.InternalSyntax.SyntaxToken)arrowToken.Node!, block == null ? null : (Syntax.InternalSyntax.BlockSyntax)block.Green, expressionBody == null ? null : (Syntax.InternalSyntax.ExpressionSyntax)expressionBody.Green).CreateRed(); } - /// Creates a new SimpleLambdaExpressionSyntax instance. - public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(SyntaxList attributeLists, SyntaxTokenList modifiers, ParameterSyntax parameter, BlockSyntax? block, ExpressionSyntax? expressionBody) - => SyntaxFactory.SimpleLambdaExpression(attributeLists, modifiers, parameter, SyntaxFactory.Token(SyntaxKind.EqualsGreaterThanToken), block, expressionBody); - - /// Creates a new SimpleLambdaExpressionSyntax instance. - public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(ParameterSyntax parameter) - => SyntaxFactory.SimpleLambdaExpression(default, default(SyntaxTokenList), parameter, SyntaxFactory.Token(SyntaxKind.EqualsGreaterThanToken), default, default); - /// Creates a new RefExpressionSyntax instance. public static RefExpressionSyntax RefExpression(SyntaxToken refKeyword, ExpressionSyntax expression) { @@ -5146,6 +5156,39 @@ public static EnumMemberDeclarationSyntax EnumMemberDeclaration(SyntaxToken iden public static EnumMemberDeclarationSyntax EnumMemberDeclaration(string identifier) => SyntaxFactory.EnumMemberDeclaration(default, default(SyntaxTokenList), SyntaxFactory.Identifier(identifier), default); + /// Creates a new ExtensionDeclarationSyntax instance. + public static ExtensionDeclarationSyntax ExtensionDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, SyntaxList constraintClauses, SyntaxToken openBraceToken, SyntaxList members, SyntaxToken closeBraceToken, SyntaxToken semicolonToken) + { + if (keyword.Kind() != SyntaxKind.ExtensionKeyword) throw new ArgumentException(nameof(keyword)); + switch (openBraceToken.Kind()) + { + case SyntaxKind.OpenBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(openBraceToken)); + } + switch (closeBraceToken.Kind()) + { + case SyntaxKind.CloseBraceToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(closeBraceToken)); + } + switch (semicolonToken.Kind()) + { + case SyntaxKind.SemicolonToken: + case SyntaxKind.None: break; + default: throw new ArgumentException(nameof(semicolonToken)); + } + return (ExtensionDeclarationSyntax)Syntax.InternalSyntax.SyntaxFactory.ExtensionDeclaration(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken)keyword.Node!, typeParameterList == null ? null : (Syntax.InternalSyntax.TypeParameterListSyntax)typeParameterList.Green, parameterList == null ? null : (Syntax.InternalSyntax.ParameterListSyntax)parameterList.Green, constraintClauses.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken?)openBraceToken.Node, members.Node.ToGreenList(), (Syntax.InternalSyntax.SyntaxToken?)closeBraceToken.Node, (Syntax.InternalSyntax.SyntaxToken?)semicolonToken.Node).CreateRed(); + } + + /// Creates a new ExtensionDeclarationSyntax instance. + public static ExtensionDeclarationSyntax ExtensionDeclaration(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, SyntaxList constraintClauses, SyntaxList members) + => SyntaxFactory.ExtensionDeclaration(attributeLists, modifiers, SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), typeParameterList, parameterList, constraintClauses, default, members, default, default); + + /// Creates a new ExtensionDeclarationSyntax instance. + public static ExtensionDeclarationSyntax ExtensionDeclaration() + => SyntaxFactory.ExtensionDeclaration(default, default(SyntaxTokenList), SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), default, default, default, default, default, default, default); + /// Creates a new BaseListSyntax instance. public static BaseListSyntax BaseList(SyntaxToken colonToken, SeparatedSyntaxList types) { @@ -5718,16 +5761,13 @@ public static ParameterSyntax Parameter(SyntaxList attribut switch (identifier.Kind()) { case SyntaxKind.IdentifierToken: - case SyntaxKind.ArgListKeyword: break; + case SyntaxKind.ArgListKeyword: + case SyntaxKind.None: break; default: throw new ArgumentException(nameof(identifier)); } - return (ParameterSyntax)Syntax.InternalSyntax.SyntaxFactory.Parameter(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), type == null ? null : (Syntax.InternalSyntax.TypeSyntax)type.Green, (Syntax.InternalSyntax.SyntaxToken)identifier.Node!, @default == null ? null : (Syntax.InternalSyntax.EqualsValueClauseSyntax)@default.Green).CreateRed(); + return (ParameterSyntax)Syntax.InternalSyntax.SyntaxFactory.Parameter(attributeLists.Node.ToGreenList(), modifiers.Node.ToGreenList(), type == null ? null : (Syntax.InternalSyntax.TypeSyntax)type.Green, (Syntax.InternalSyntax.SyntaxToken?)identifier.Node, @default == null ? null : (Syntax.InternalSyntax.EqualsValueClauseSyntax)@default.Green).CreateRed(); } - /// Creates a new ParameterSyntax instance. - public static ParameterSyntax Parameter(SyntaxToken identifier) - => SyntaxFactory.Parameter(default, default(SyntaxTokenList), default, identifier, default); - /// Creates a new FunctionPointerParameterSyntax instance. public static FunctionPointerParameterSyntax FunctionPointerParameter(SyntaxList attributeLists, SyntaxTokenList modifiers, TypeSyntax type) { @@ -6480,6 +6520,19 @@ public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(SyntaxToken ha public static ShebangDirectiveTriviaSyntax ShebangDirectiveTrivia(bool isActive) => SyntaxFactory.ShebangDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ExclamationToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive); + /// Creates a new IgnoredDirectiveTriviaSyntax instance. + public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken.Kind() != SyntaxKind.HashToken) throw new ArgumentException(nameof(hashToken)); + if (colonToken.Kind() != SyntaxKind.ColonToken) throw new ArgumentException(nameof(colonToken)); + if (endOfDirectiveToken.Kind() != SyntaxKind.EndOfDirectiveToken) throw new ArgumentException(nameof(endOfDirectiveToken)); + return (IgnoredDirectiveTriviaSyntax)Syntax.InternalSyntax.SyntaxFactory.IgnoredDirectiveTrivia((Syntax.InternalSyntax.SyntaxToken)hashToken.Node!, (Syntax.InternalSyntax.SyntaxToken)colonToken.Node!, (Syntax.InternalSyntax.SyntaxToken)endOfDirectiveToken.Node!, isActive).CreateRed(); + } + + /// Creates a new IgnoredDirectiveTriviaSyntax instance. + public static IgnoredDirectiveTriviaSyntax IgnoredDirectiveTrivia(bool isActive) + => SyntaxFactory.IgnoredDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ColonToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), isActive); + /// Creates a new NullableDirectiveTriviaSyntax instance. public static NullableDirectiveTriviaSyntax NullableDirectiveTrivia(SyntaxToken hashToken, SyntaxToken nullableKeyword, SyntaxToken settingToken, SyntaxToken targetToken, SyntaxToken endOfDirectiveToken, bool isActive) { diff --git a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs index 0c97b7c25c0a7..4623b79d7ea54 100644 --- a/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs @@ -10261,7 +10261,7 @@ internal BaseTypeDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, Syntax public new BaseTypeDeclarationSyntax AddModifiers(params SyntaxToken[] items) => (BaseTypeDeclarationSyntax)AddModifiersCore(items); } -/// Base class for type declaration syntax (class, struct, interface, record). +/// Base class for type declaration syntax (class, struct, interface, record, extension). public abstract partial class TypeDeclarationSyntax : BaseTypeDeclarationSyntax { internal TypeDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) @@ -10269,7 +10269,7 @@ internal TypeDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode { } - /// Gets the type keyword token ("class", "struct", "interface", "record"). + /// Gets the type keyword token ("class", "struct", "interface", "record", "extension"). public abstract SyntaxToken Keyword { get; } public TypeDeclarationSyntax WithKeyword(SyntaxToken keyword) => WithKeywordCore(keyword); internal abstract TypeDeclarationSyntax WithKeywordCore(SyntaxToken keyword); @@ -11312,6 +11312,154 @@ public EnumMemberDeclarationSyntax Update(SyntaxList attrib public new EnumMemberDeclarationSyntax AddModifiers(params SyntaxToken[] items) => WithModifiers(this.Modifiers.AddRange(items)); } +/// Extension container syntax. +/// +/// This node is associated with the following syntax kinds: +/// +/// +/// +/// +public sealed partial class ExtensionDeclarationSyntax : TypeDeclarationSyntax +{ + private SyntaxNode? attributeLists; + private TypeParameterListSyntax? typeParameterList; + private ParameterListSyntax? parameterList; + private SyntaxNode? constraintClauses; + private SyntaxNode? members; + + internal ExtensionDeclarationSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxList AttributeLists => new SyntaxList(GetRed(ref this.attributeLists, 0)); + + public override SyntaxTokenList Modifiers + { + get + { + var slot = this.Green.GetSlot(1); + return slot != null ? new SyntaxTokenList(this, slot, GetChildPosition(1), GetChildIndex(1)) : default; + } + } + + public override SyntaxToken Keyword => new SyntaxToken(this, ((InternalSyntax.ExtensionDeclarationSyntax)this.Green).keyword, GetChildPosition(2), GetChildIndex(2)); + + public override TypeParameterListSyntax? TypeParameterList => GetRed(ref this.typeParameterList, 3); + + public override ParameterListSyntax? ParameterList => GetRed(ref this.parameterList, 4); + + public override SyntaxList ConstraintClauses => new SyntaxList(GetRed(ref this.constraintClauses, 5)); + + public override SyntaxToken OpenBraceToken + { + get + { + var slot = ((Syntax.InternalSyntax.ExtensionDeclarationSyntax)this.Green).openBraceToken; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(6), GetChildIndex(6)) : default; + } + } + + public override SyntaxList Members => new SyntaxList(GetRed(ref this.members, 7)); + + public override SyntaxToken CloseBraceToken + { + get + { + var slot = ((Syntax.InternalSyntax.ExtensionDeclarationSyntax)this.Green).closeBraceToken; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(8), GetChildIndex(8)) : default; + } + } + + public override SyntaxToken SemicolonToken + { + get + { + var slot = ((Syntax.InternalSyntax.ExtensionDeclarationSyntax)this.Green).semicolonToken; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(9), GetChildIndex(9)) : default; + } + } + + internal override SyntaxNode? GetNodeSlot(int index) + => index switch + { + 0 => GetRedAtZero(ref this.attributeLists)!, + 3 => GetRed(ref this.typeParameterList, 3), + 4 => GetRed(ref this.parameterList, 4), + 5 => GetRed(ref this.constraintClauses, 5)!, + 7 => GetRed(ref this.members, 7)!, + _ => null, + }; + + internal override SyntaxNode? GetCachedSlot(int index) + => index switch + { + 0 => this.attributeLists, + 3 => this.typeParameterList, + 4 => this.parameterList, + 5 => this.constraintClauses, + 7 => this.members, + _ => null, + }; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitExtensionDeclaration(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitExtensionDeclaration(this); + + public ExtensionDeclarationSyntax Update(SyntaxList attributeLists, SyntaxTokenList modifiers, SyntaxToken keyword, TypeParameterListSyntax? typeParameterList, ParameterListSyntax? parameterList, SyntaxList constraintClauses, SyntaxToken openBraceToken, SyntaxList members, SyntaxToken closeBraceToken, SyntaxToken semicolonToken) + { + if (attributeLists != this.AttributeLists || modifiers != this.Modifiers || keyword != this.Keyword || typeParameterList != this.TypeParameterList || parameterList != this.ParameterList || constraintClauses != this.ConstraintClauses || openBraceToken != this.OpenBraceToken || members != this.Members || closeBraceToken != this.CloseBraceToken || semicolonToken != this.SemicolonToken) + { + var newNode = SyntaxFactory.ExtensionDeclaration(attributeLists, modifiers, keyword, typeParameterList, parameterList, constraintClauses, openBraceToken, members, closeBraceToken, semicolonToken); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + internal override MemberDeclarationSyntax WithAttributeListsCore(SyntaxList attributeLists) => WithAttributeLists(attributeLists); + public new ExtensionDeclarationSyntax WithAttributeLists(SyntaxList attributeLists) => Update(attributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override MemberDeclarationSyntax WithModifiersCore(SyntaxTokenList modifiers) => WithModifiers(modifiers); + public new ExtensionDeclarationSyntax WithModifiers(SyntaxTokenList modifiers) => Update(this.AttributeLists, modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override TypeDeclarationSyntax WithKeywordCore(SyntaxToken keyword) => WithKeyword(keyword); + public new ExtensionDeclarationSyntax WithKeyword(SyntaxToken keyword) => Update(this.AttributeLists, this.Modifiers, keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override TypeDeclarationSyntax WithTypeParameterListCore(TypeParameterListSyntax? typeParameterList) => WithTypeParameterList(typeParameterList); + public new ExtensionDeclarationSyntax WithTypeParameterList(TypeParameterListSyntax? typeParameterList) => Update(this.AttributeLists, this.Modifiers, this.Keyword, typeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override TypeDeclarationSyntax WithParameterListCore(ParameterListSyntax? parameterList) => WithParameterList(parameterList); + public new ExtensionDeclarationSyntax WithParameterList(ParameterListSyntax? parameterList) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, parameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override TypeDeclarationSyntax WithConstraintClausesCore(SyntaxList constraintClauses) => WithConstraintClauses(constraintClauses); + public new ExtensionDeclarationSyntax WithConstraintClauses(SyntaxList constraintClauses) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, constraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseTypeDeclarationSyntax WithOpenBraceTokenCore(SyntaxToken openBraceToken) => WithOpenBraceToken(openBraceToken); + public new ExtensionDeclarationSyntax WithOpenBraceToken(SyntaxToken openBraceToken) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, openBraceToken, this.Members, this.CloseBraceToken, this.SemicolonToken); + internal override TypeDeclarationSyntax WithMembersCore(SyntaxList members) => WithMembers(members); + public new ExtensionDeclarationSyntax WithMembers(SyntaxList members) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, members, this.CloseBraceToken, this.SemicolonToken); + internal override BaseTypeDeclarationSyntax WithCloseBraceTokenCore(SyntaxToken closeBraceToken) => WithCloseBraceToken(closeBraceToken); + public new ExtensionDeclarationSyntax WithCloseBraceToken(SyntaxToken closeBraceToken) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, closeBraceToken, this.SemicolonToken); + internal override BaseTypeDeclarationSyntax WithSemicolonTokenCore(SyntaxToken semicolonToken) => WithSemicolonToken(semicolonToken); + public new ExtensionDeclarationSyntax WithSemicolonToken(SyntaxToken semicolonToken) => Update(this.AttributeLists, this.Modifiers, this.Keyword, this.TypeParameterList, this.ParameterList, this.ConstraintClauses, this.OpenBraceToken, this.Members, this.CloseBraceToken, semicolonToken); + + internal override MemberDeclarationSyntax AddAttributeListsCore(params AttributeListSyntax[] items) => AddAttributeLists(items); + public new ExtensionDeclarationSyntax AddAttributeLists(params AttributeListSyntax[] items) => WithAttributeLists(this.AttributeLists.AddRange(items)); + internal override MemberDeclarationSyntax AddModifiersCore(params SyntaxToken[] items) => AddModifiers(items); + public new ExtensionDeclarationSyntax AddModifiers(params SyntaxToken[] items) => WithModifiers(this.Modifiers.AddRange(items)); + internal override TypeDeclarationSyntax AddTypeParameterListParametersCore(params TypeParameterSyntax[] items) => AddTypeParameterListParameters(items); + public new ExtensionDeclarationSyntax AddTypeParameterListParameters(params TypeParameterSyntax[] items) + { + var typeParameterList = this.TypeParameterList ?? SyntaxFactory.TypeParameterList(); + return WithTypeParameterList(typeParameterList.WithParameters(typeParameterList.Parameters.AddRange(items))); + } + internal override TypeDeclarationSyntax AddParameterListParametersCore(params ParameterSyntax[] items) => AddParameterListParameters(items); + public new ExtensionDeclarationSyntax AddParameterListParameters(params ParameterSyntax[] items) + { + var parameterList = this.ParameterList ?? SyntaxFactory.ParameterList(); + return WithParameterList(parameterList.WithParameters(parameterList.Parameters.AddRange(items))); + } + internal override TypeDeclarationSyntax AddConstraintClausesCore(params TypeParameterConstraintClauseSyntax[] items) => AddConstraintClauses(items); + public new ExtensionDeclarationSyntax AddConstraintClauses(params TypeParameterConstraintClauseSyntax[] items) => WithConstraintClauses(this.ConstraintClauses.AddRange(items)); + internal override TypeDeclarationSyntax AddMembersCore(params MemberDeclarationSyntax[] items) => AddMembers(items); + public new ExtensionDeclarationSyntax AddMembers(params MemberDeclarationSyntax[] items) => WithMembers(this.Members.AddRange(items)); +} + /// Base list syntax. /// /// This node is associated with the following syntax kinds: @@ -13636,7 +13784,14 @@ public override SyntaxTokenList Modifiers public override TypeSyntax? Type => GetRed(ref this.type, 2); /// Gets the identifier. - public SyntaxToken Identifier => new SyntaxToken(this, ((InternalSyntax.ParameterSyntax)this.Green).identifier, GetChildPosition(3), GetChildIndex(3)); + public SyntaxToken Identifier + { + get + { + var slot = ((Syntax.InternalSyntax.ParameterSyntax)this.Green).identifier; + return slot != null ? new SyntaxToken(this, slot, GetChildPosition(3), GetChildIndex(3)) : default; + } + } public EqualsValueClauseSyntax? Default => GetRed(ref this.@default, 4); @@ -16417,6 +16572,55 @@ public ShebangDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken ex public ShebangDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.ExclamationToken, this.EndOfDirectiveToken, isActive); } +/// +/// This node is associated with the following syntax kinds: +/// +/// +/// +/// +public sealed partial class IgnoredDirectiveTriviaSyntax : DirectiveTriviaSyntax +{ + + internal IgnoredDirectiveTriviaSyntax(InternalSyntax.CSharpSyntaxNode green, SyntaxNode? parent, int position) + : base(green, parent, position) + { + } + + public override SyntaxToken HashToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).hashToken, Position, 0); + + public SyntaxToken ColonToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).colonToken, GetChildPosition(1), GetChildIndex(1)); + + public override SyntaxToken EndOfDirectiveToken => new SyntaxToken(this, ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).endOfDirectiveToken, GetChildPosition(2), GetChildIndex(2)); + + public override bool IsActive => ((InternalSyntax.IgnoredDirectiveTriviaSyntax)this.Green).IsActive; + + internal override SyntaxNode? GetNodeSlot(int index) => null; + + internal override SyntaxNode? GetCachedSlot(int index) => null; + + public override void Accept(CSharpSyntaxVisitor visitor) => visitor.VisitIgnoredDirectiveTrivia(this); + public override TResult? Accept(CSharpSyntaxVisitor visitor) where TResult : default => visitor.VisitIgnoredDirectiveTrivia(this); + + public IgnoredDirectiveTriviaSyntax Update(SyntaxToken hashToken, SyntaxToken colonToken, SyntaxToken endOfDirectiveToken, bool isActive) + { + if (hashToken != this.HashToken || colonToken != this.ColonToken || endOfDirectiveToken != this.EndOfDirectiveToken) + { + var newNode = SyntaxFactory.IgnoredDirectiveTrivia(hashToken, colonToken, endOfDirectiveToken, isActive); + var annotations = GetAnnotations(); + return annotations?.Length > 0 ? newNode.WithAnnotations(annotations) : newNode; + } + + return this; + } + + internal override DirectiveTriviaSyntax WithHashTokenCore(SyntaxToken hashToken) => WithHashToken(hashToken); + public new IgnoredDirectiveTriviaSyntax WithHashToken(SyntaxToken hashToken) => Update(hashToken, this.ColonToken, this.EndOfDirectiveToken, this.IsActive); + public IgnoredDirectiveTriviaSyntax WithColonToken(SyntaxToken colonToken) => Update(this.HashToken, colonToken, this.EndOfDirectiveToken, this.IsActive); + internal override DirectiveTriviaSyntax WithEndOfDirectiveTokenCore(SyntaxToken endOfDirectiveToken) => WithEndOfDirectiveToken(endOfDirectiveToken); + public new IgnoredDirectiveTriviaSyntax WithEndOfDirectiveToken(SyntaxToken endOfDirectiveToken) => Update(this.HashToken, this.ColonToken, endOfDirectiveToken, this.IsActive); + public IgnoredDirectiveTriviaSyntax WithIsActive(bool isActive) => Update(this.HashToken, this.ColonToken, this.EndOfDirectiveToken, isActive); +} + /// /// This node is associated with the following syntax kinds: /// diff --git a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs index 266ea28e686ca..35a3f0182d26b 100644 --- a/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/ErrorFacts.Generated.cs @@ -338,7 +338,7 @@ public static bool IsWarning(ErrorCode code) case ErrorCode.WRN_CollectionExpressionRefStructMayAllocate: case ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate: case ErrorCode.WRN_ConvertingLock: - case ErrorCode.WRN_PartialPropertySignatureDifference: + case ErrorCode.WRN_PartialMemberSignatureDifference: case ErrorCode.WRN_FieldIsAmbiguous: case ErrorCode.WRN_UninitializedNonNullableBackingField: case ErrorCode.WRN_UnassignedInternalRefField: diff --git a/src/Compilers/CSharp/Portable/LanguageVersion.cs b/src/Compilers/CSharp/Portable/LanguageVersion.cs index 4d429582030ef..ec5d56afa9904 100644 --- a/src/Compilers/CSharp/Portable/LanguageVersion.cs +++ b/src/Compilers/CSharp/Portable/LanguageVersion.cs @@ -572,5 +572,10 @@ internal static bool AllowImprovedOverloadCandidates(this LanguageVersion self) { return self >= MessageID.IDS_FeatureImprovedOverloadCandidates.RequiredVersion(); } + + internal static bool AllowNewExtensions(this LanguageVersion self) + { + return self >= MessageID.IDS_FeatureExtensions.RequiredVersion(); + } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs new file mode 100644 index 0000000000000..ae54fba8eca00 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/BoundTreeToDifferentEnclosingContextRewriter.cs @@ -0,0 +1,180 @@ +// 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 System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// a bound node rewriter that rewrites types properly (which in some cases the automatically-generated + /// base class does not). This is used in the lambda rewriter, the iterator rewriter, and the async rewriter. + /// + internal abstract class BoundTreeToDifferentEnclosingContextRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator + { + // A mapping from every local variable to its replacement local variable. Local variables are replaced when + // their types change due to being inside of a generic method. Otherwise we reuse the original local (even + // though its containing method is not correct because the code is moved into another method) + private readonly Dictionary localMap = new Dictionary(); + + // A mapping for types in the original method to types in its replacement. This is mainly necessary + // when the original method was generic, as type parameters in the original method are mapping into + // type parameters of the resulting class. + protected abstract TypeMap TypeMap { get; } + + protected abstract MethodSymbol CurrentMethod { get; } + + public override BoundNode DefaultVisit(BoundNode node) + { + Debug.Fail($"Override the visitor for {node.Kind}"); + return base.DefaultVisit(node); + } + + protected void RewriteLocals(ImmutableArray locals, ArrayBuilder newLocals) + { + foreach (var local in locals) + { + if (TryRewriteLocal(local, out LocalSymbol? newLocal)) + { + newLocals.Add(newLocal); + } + } + } + + protected virtual bool TryRewriteLocal(LocalSymbol local, [NotNullWhen(true)] out LocalSymbol? newLocal) + { + if (localMap.TryGetValue(local, out newLocal)) + { + return true; + } + + var newType = VisitType(local.Type); + if (TypeSymbol.Equals(newType, local.Type, TypeCompareKind.ConsiderEverything2)) + { + newLocal = local; + } + else + { + newLocal = new TypeSubstitutedLocalSymbol(local, TypeWithAnnotations.Create(newType), CurrentMethod); + localMap.Add(local, newLocal); + } + + return true; + } + + protected sealed override ImmutableArray VisitLocals(ImmutableArray locals) + { + if (locals.IsEmpty) return locals; + var newLocals = ArrayBuilder.GetInstance(); + RewriteLocals(locals, newLocals); + return newLocals.ToImmutableAndFree(); + } + + public sealed override LocalSymbol VisitLocalSymbol(LocalSymbol local) + { + if (!TryRewriteLocal(local, out var newLocal)) + { + throw ExceptionUtilities.UnexpectedValue(local); + } + + return newLocal; + } + + protected bool TryGetRewrittenLocal(LocalSymbol local, [NotNullWhen(true)] out LocalSymbol? localToUse) + { + return localMap.TryGetValue(local, out localToUse); + } + + public override BoundNode VisitBlock(BoundBlock node) + => VisitBlock(node, removeInstrumentation: false); + + protected BoundBlock VisitBlock(BoundBlock node, bool removeInstrumentation) + { + // Note: Instrumentation variable is intentionally not rewritten. It should never be lifted. + + var newLocals = this.VisitLocals(node.Locals); + var newLocalFunctions = this.VisitDeclaredLocalFunctions(node.LocalFunctions); + var newStatements = VisitList(node.Statements); + var newInstrumentation = removeInstrumentation ? null : (BoundBlockInstrumentation?)Visit(node.Instrumentation); + return node.Update(newLocals, newLocalFunctions, node.HasUnsafeModifier, newInstrumentation, newStatements); + } + + [return: NotNullIfNotNull(nameof(type))] + public sealed override TypeSymbol? VisitType(TypeSymbol? type) + { + return TypeMap.SubstituteType(type).Type; + } + + protected override BoundBinaryOperator.UncommonData? VisitBinaryOperatorData(BoundBinaryOperator node) + { + // Local rewriter should have already rewritten interpolated strings into their final form of calls and gotos + Debug.Assert(node.InterpolatedStringHandlerData is null); + + return BoundBinaryOperator.UncommonData.CreateIfNeeded(node.ConstantValueOpt, VisitMethodSymbol(node.Method), VisitType(node.ConstrainedToType), node.OriginalUserDefinedOperatorsOpt); + } + + public override BoundNode? VisitConversion(BoundConversion node) + { + var conversion = node.Conversion; + + if (conversion.Method is not null) + { + conversion = conversion.SetConversionMethod(VisitMethodSymbol(conversion.Method)); + } + + return node.Update( + (BoundExpression)Visit(node.Operand), + conversion, + node.IsBaseConversion, + node.Checked, + node.ExplicitCastInCode, + node.ConstantValueOpt, + node.ConversionGroupOpt, + VisitType(node.Type)); + } + + [return: NotNullIfNotNull(nameof(method))] + public override MethodSymbol? VisitMethodSymbol(MethodSymbol? method) + { + if (method is null) + { + return null; + } + + if (method.ContainingType.IsAnonymousType) + { + // Method of an anonymous type + var newType = (NamedTypeSymbol)TypeMap.SubstituteType(method.ContainingType).AsTypeSymbolOnly(); + if (ReferenceEquals(newType, method.ContainingType)) + { + // Anonymous type symbol was not rewritten + return method; + } + + // get a new method by name + foreach (var member in newType.GetMembers(method.Name)) + { + if (member.Kind == SymbolKind.Method) + { + return (MethodSymbol)member; + } + } + + throw ExceptionUtilities.Unreachable(); + } + else + { + // Method of a regular type + return ((MethodSymbol)method.OriginalDefinition) + .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(method.ContainingType).AsTypeSymbolOnly()) + .ConstructIfGeneric(TypeMap.SubstituteTypes(method.TypeArgumentsWithAnnotations)); + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 01e209bb2bb5b..a9460f35b945a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -768,7 +768,7 @@ private void InitVariableProxy(SyntaxNode syntax, Symbol symbol, LocalSymbol fra } LocalSymbol localToUse; - if (!localMap.TryGetValue(local, out localToUse)) + if (!TryGetRewrittenLocal(local, out localToUse)) { localToUse = local; } @@ -1198,17 +1198,15 @@ private BoundBlock RewriteBlock(BoundBlock node, ArrayBuilder p public override BoundNode VisitScope(BoundScope node) { Debug.Assert(!node.Locals.IsEmpty); - var newLocals = ArrayBuilder.GetInstance(); - RewriteLocals(node.Locals, newLocals); + var newLocals = VisitLocals(node.Locals); var statements = VisitList(node.Statements); - if (newLocals.Count == 0) + if (newLocals.Length == 0) { - newLocals.Free(); return new BoundStatementList(node.Syntax, statements); } - return node.Update(newLocals.ToImmutableAndFree(), statements); + return node.Update(newLocals, statements); } public override BoundNode VisitCatchBlock(BoundCatchBlock node) diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureEnvironment.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureEnvironment.cs index 49797ec0c86cd..b7754a5982068 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureEnvironment.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureEnvironment.cs @@ -50,7 +50,7 @@ internal SynthesizedClosureEnvironment( DebugId methodId, DebugId closureId, RuntimeRudeEdit? rudeEdit) - : base(MakeName(scopeSyntaxOpt, methodId, closureId), containingMethod) + : base(MakeName(scopeSyntaxOpt, methodId, closureId), containingMethod is null ? [] : TypeMap.ConcatMethodTypeParameters(containingMethod, stopAt: null)) { TypeKind = isStruct ? TypeKind.Struct : TypeKind.Class; TopLevelMethod = topLevelMethod; diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs index 259353e7ca066..000a43b663390 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/SynthesizedClosureMethod.cs @@ -57,22 +57,18 @@ originalMethod is LocalFunctionSymbol case ClosureKind.Singleton: // all type parameters on method (except the top level method's) case ClosureKind.General: // only lambda's type parameters on method (rest on class) RoslynDebug.Assert(!(lambdaFrame is null)); - typeMap = lambdaFrame.TypeMap.WithConcatAlphaRename( - originalMethod, + typeMap = lambdaFrame.TypeMap.WithAlphaRename( + TypeMap.ConcatMethodTypeParameters(originalMethod, stopAt: lambdaFrame.OriginalContainingMethodOpt), this, - out typeParameters, - out _, - lambdaFrame.OriginalContainingMethodOpt); + out typeParameters); break; case ClosureKind.ThisOnly: // all type parameters on method case ClosureKind.Static: RoslynDebug.Assert(lambdaFrame is null); - typeMap = TypeMap.Empty.WithConcatAlphaRename( - originalMethod, + typeMap = TypeMap.Empty.WithAlphaRename( + TypeMap.ConcatMethodTypeParameters(originalMethod, stopAt: null), this, - out typeParameters, - out _, - stopAt: null); + out typeParameters); break; default: throw ExceptionUtilities.UnexpectedValue(closureKind); diff --git a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs index d743981c20bcc..947a42ec2fe1f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs +++ b/src/Compilers/CSharp/Portable/Lowering/DiagnosticsPass_ExpressionTrees.cs @@ -155,7 +155,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen var outerLocalFunction = _staticLocalOrAnonymousFunction; if (node.Symbol.IsStatic) { - _staticLocalOrAnonymousFunction = node.Symbol; + _staticLocalOrAnonymousFunction = (SourceMethodSymbol)node.Symbol; } var result = base.VisitLocalFunctionStatement(node); _staticLocalOrAnonymousFunction = outerLocalFunction; @@ -441,7 +441,14 @@ public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMem if (node.MemberSymbol is PropertySymbol property) { - CheckRefReturningPropertyAccess(node, property); + if (_inExpressionLambda && property.GetIsNewExtensionMember()) + { + Error(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, node); + } + else + { + CheckRefReturningPropertyAccess(node, property); + } } return base.VisitObjectInitializerMember(node); @@ -508,7 +515,7 @@ private void CheckDiscard(BoundDiscardExpression argument) public override BoundNode VisitCollectionElementInitializer(BoundCollectionElementInitializer node) { - if (_inExpressionLambda && node.AddMethod.IsStatic) + if (_inExpressionLambda && (node.AddMethod.IsStatic || node.AddMethod.GetIsNewExtensionMember())) { Error(ErrorCode.ERR_ExtensionCollectionElementInitializerInExpressionTree, node); } @@ -549,9 +556,16 @@ public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) CheckRefReturningPropertyAccess(node, property); CheckReceiverIfField(node.ReceiverOpt); - if (_inExpressionLambda && (property.IsAbstract || property.IsVirtual) && property.IsStatic) + if (_inExpressionLambda) { - Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node); + if ((property.IsAbstract || property.IsVirtual) && property.IsStatic) + { + Error(ErrorCode.ERR_ExpressionTreeContainsAbstractStaticMemberAccess, node); + } + else if (property.GetIsNewExtensionMember()) + { + Error(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, node); + } } return base.VisitPropertyAccess(node); @@ -639,7 +653,7 @@ public override BoundNode VisitLambda(BoundLambda node) var outerLocalFunction = _staticLocalOrAnonymousFunction; if (node.Symbol.IsStatic) { - _staticLocalOrAnonymousFunction = node.Symbol; + _staticLocalOrAnonymousFunction = (SourceMethodSymbol)node.Symbol; } var result = base.VisitLambda(node); _staticLocalOrAnonymousFunction = outerLocalFunction; diff --git a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs new file mode 100644 index 0000000000000..c6b67cd5d3a42 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodBodyRewriter.cs @@ -0,0 +1,198 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Roslyn.Utilities; +using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// Rewrites method body lowered in context of an extension method to a body + /// that corresponds to its implementation form. + /// + internal sealed class ExtensionMethodBodyRewriter : BoundTreeToDifferentEnclosingContextRewriter + { + private readonly SourceExtensionImplementationMethodSymbol _implementationMethod; + + /// + /// Maps parameters and local functions from original enclosing context to corresponding rewritten symbols for rewritten context. + /// + private ImmutableDictionary _symbolMap; + + private RewrittenMethodSymbol _rewrittenContainingMethod; + + public ExtensionMethodBodyRewriter(MethodSymbol sourceMethod, SourceExtensionImplementationMethodSymbol implementationMethod) + { + Debug.Assert(sourceMethod is not null); + Debug.Assert(implementationMethod is not null); + Debug.Assert(sourceMethod == (object)implementationMethod.UnderlyingMethod); + + _implementationMethod = implementationMethod; + _symbolMap = ImmutableDictionary.Empty.WithComparers(ReferenceEqualityComparer.Instance, ReferenceEqualityComparer.Instance); + + bool haveExtraParameter = sourceMethod.ParameterCount != implementationMethod.ParameterCount; + if (haveExtraParameter) + { + Debug.Assert(implementationMethod.ParameterCount - 1 == sourceMethod.ParameterCount); + var receiverParameter = (WrappedParameterSymbol)implementationMethod.Parameters[0]; + _symbolMap = _symbolMap.Add(receiverParameter.UnderlyingParameter, receiverParameter); + } + EnterMethod(sourceMethod, implementationMethod, implementationMethod.Parameters.AsSpan()[(haveExtraParameter ? 1 : 0)..]); + Debug.Assert(_rewrittenContainingMethod is not null); + } + + private (RewrittenMethodSymbol, ImmutableDictionary) EnterMethod(MethodSymbol symbol, RewrittenMethodSymbol rewritten, ReadOnlySpan rewrittenParameters) + { + ImmutableDictionary saveSymbolMap = _symbolMap; + RewrittenMethodSymbol savedContainer = _rewrittenContainingMethod; + + Debug.Assert(symbol.Parameters.Length == rewrittenParameters.Length); + + if (!rewrittenParameters.IsEmpty) + { + var builder = _symbolMap.ToBuilder(); + foreach (var parameter in symbol.Parameters) + { + builder.Add(parameter, rewrittenParameters[parameter.Ordinal]); + } + _symbolMap = builder.ToImmutable(); + } + + _rewrittenContainingMethod = rewritten; + + return (savedContainer, saveSymbolMap); + } + + private (RewrittenMethodSymbol, ImmutableDictionary) EnterMethod(MethodSymbol symbol, RewrittenLambdaOrLocalFunctionSymbol rewritten) + { + return EnterMethod(symbol, rewritten, rewritten.Parameters.AsSpan()); + } + + protected override MethodSymbol CurrentMethod => _rewrittenContainingMethod; + + protected override TypeMap TypeMap => _rewrittenContainingMethod.TypeMap; + + public override BoundNode? VisitThisReference(BoundThisReference node) + { + throw ExceptionUtilities.Unreachable(); + } + + public override ParameterSymbol VisitParameterSymbol(ParameterSymbol symbol) + { + return (ParameterSymbol)_symbolMap[symbol]; + } + + public override BoundNode? VisitLambda(BoundLambda node) + { + var rewritten = new RewrittenLambdaOrLocalFunctionSymbol(node.Symbol, _rewrittenContainingMethod); + + var savedState = EnterMethod(node.Symbol, rewritten); + BoundBlock body = (BoundBlock)this.Visit(node.Body); + (_rewrittenContainingMethod, _symbolMap) = savedState; + + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(node.UnboundLambda, rewritten, body, node.Diagnostics, node.Binder, type); + } + + public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) + { + MethodSymbol symbol = this.VisitMethodSymbol(node.Symbol); + var savedState = EnterMethod(node.Symbol, (RewrittenLambdaOrLocalFunctionSymbol)symbol); + + BoundBlock? blockBody = (BoundBlock?)this.Visit(node.BlockBody); + BoundBlock? expressionBody = (BoundBlock?)this.Visit(node.ExpressionBody); + + (_rewrittenContainingMethod, _symbolMap) = savedState; + + return node.Update(symbol, blockBody, expressionBody); + } + + public override BoundNode VisitBlock(BoundBlock node) + { + ImmutableDictionary saveSymbolMap = _symbolMap; + + if (!node.LocalFunctions.IsEmpty) + { + var builder = _symbolMap.ToBuilder(); + + foreach (var localFunction in node.LocalFunctions) + { + builder.Add(localFunction, new RewrittenLambdaOrLocalFunctionSymbol(localFunction, _rewrittenContainingMethod)); + } + + _symbolMap = builder.ToImmutable(); + } + + var result = base.VisitBlock(node); + + _symbolMap = saveSymbolMap; + return result; + } + + protected override ImmutableArray VisitDeclaredLocalFunctions(ImmutableArray localFunctions) + { + return localFunctions.SelectAsArray(static (l, map) => (MethodSymbol)map[l], _symbolMap); + } + + [return: NotNullIfNotNull(nameof(symbol))] + public override MethodSymbol? VisitMethodSymbol(MethodSymbol? symbol) + { + switch (symbol?.MethodKind) + { + case MethodKind.LambdaMethod: + throw ExceptionUtilities.Unreachable(); + + case MethodKind.LocalFunction: + if (symbol.IsDefinition) + { + return (MethodSymbol)_symbolMap[symbol]; + } + + return ((MethodSymbol)_symbolMap[symbol.OriginalDefinition]).ConstructIfGeneric(TypeMap.SubstituteTypes(symbol.TypeArgumentsWithAnnotations)); + + default: + return base.VisitMethodSymbol(symbol); + } + } + + [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); + } + + public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node) + { + return ExtensionMethodReferenceRewriter.VisitDelegateCreationExpression(this, node); + } + + public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node) + { + return ExtensionMethodReferenceRewriter.VisitFunctionPointerLoad(this, node); + } + + [return: NotNullIfNotNull(nameof(symbol))] + public override PropertySymbol? VisitPropertySymbol(PropertySymbol? symbol) + { + Debug.Assert(symbol?.GetIsNewExtensionMember() != true); + return base.VisitPropertySymbol(symbol); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs new file mode 100644 index 0000000000000..1e6b2dbb0a649 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/ExtensionMethodReferenceRewriter.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp +{ + /// + /// Replaces references to extension methods with references to their implementation methods + /// + internal sealed class ExtensionMethodReferenceRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator + { + private ExtensionMethodReferenceRewriter() + { + } + + public static BoundStatement Rewrite(BoundStatement statement) + { + var rewriter = new ExtensionMethodReferenceRewriter(); + return (BoundStatement)rewriter.Visit(statement); + } + + public override BoundNode VisitCall(BoundCall node) + { + return VisitCall(this, node); + } + + public static BoundNode VisitCall(BoundTreeRewriter rewriter, BoundCall node) + { + Debug.Assert(node != null); + + BoundExpression rewrittenCall; + + if (LocalRewriter.TryGetReceiver(node, out BoundCall? receiver1)) + { + // Handle long call chain of both instance and extension method invocations. + var calls = ArrayBuilder.GetInstance(); + + calls.Push(node); + node = receiver1; + + while (LocalRewriter.TryGetReceiver(node, out BoundCall? receiver2)) + { + calls.Push(node); + node = receiver2; + } + + // Rewrite the receiver + BoundExpression? rewrittenReceiver = (BoundExpression?)rewriter.Visit(node.ReceiverOpt); + + do + { + rewrittenCall = visitArgumentsAndFinishRewrite(rewriter, node, rewrittenReceiver); + rewrittenReceiver = rewrittenCall; + } + while (calls.TryPop(out node!)); + + calls.Free(); + } + else + { + // Rewrite the receiver + BoundExpression? rewrittenReceiver = (BoundExpression?)rewriter.Visit(node.ReceiverOpt); + rewrittenCall = visitArgumentsAndFinishRewrite(rewriter, node, rewrittenReceiver); + } + + return rewrittenCall; + + static BoundExpression visitArgumentsAndFinishRewrite(BoundTreeRewriter rewriter, BoundCall node, BoundExpression? rewrittenReceiver) + { + return updateCall( + node, + VisitMethodSymbolWithExtensionRewrite(rewriter, node.Method), + rewriter.VisitSymbols(node.OriginalMethodsOpt), + rewrittenReceiver, + rewriter.VisitList(node.Arguments), + node.ArgumentRefKindsOpt, + node.InvokedAsExtensionMethod, + rewriter.VisitType(node.Type)); + } + + static BoundExpression updateCall( + BoundCall boundCall, + MethodSymbol method, + ImmutableArray originalMethodsOpt, + BoundExpression? receiverOpt, + ImmutableArray arguments, + ImmutableArray argumentRefKinds, + bool invokedAsExtensionMethod, + TypeSymbol type) + { + if (receiverOpt is not null && arguments.Length == method.ParameterCount - 1) + { + Debug.Assert(boundCall.Method.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() == (object)method.OriginalDefinition); + Debug.Assert(!boundCall.Method.IsStatic); + + var receiverRefKind = method.Parameters[0].RefKind; + + if (argumentRefKinds.IsDefault) + { + if (receiverRefKind != RefKind.None) + { + var builder = ArrayBuilder.GetInstance(method.ParameterCount, RefKind.None); + builder[0] = argumentRefKindFromReceiverRefKind(receiverRefKind); + argumentRefKinds = builder.ToImmutableAndFree(); + } + } + else + { + argumentRefKinds = argumentRefKinds.Insert(0, argumentRefKindFromReceiverRefKind(receiverRefKind)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + invokedAsExtensionMethod = true; + + Debug.Assert(receiverOpt.Type!.Equals(method.Parameters[0].Type, TypeCompareKind.ConsiderEverything)); + + arguments = arguments.Insert(0, receiverOpt); + receiverOpt = null; + } + + return boundCall.Update( + receiverOpt, + boundCall.InitialBindingReceiverIsSubjectToCloning, + method, + arguments, + default, + argumentRefKinds, + boundCall.IsDelegateCall, + boundCall.Expanded, + invokedAsExtensionMethod, + default, + default, + boundCall.ResultKind, + originalMethodsOpt, + type); + + static RefKind argumentRefKindFromReceiverRefKind(RefKind receiverRefKind) + { + return SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false); + } + } + } + + [return: NotNullIfNotNull(nameof(method))] + private static MethodSymbol? VisitMethodSymbolWithExtensionRewrite(BoundTreeRewriter rewriter, MethodSymbol? method) + { + if (method?.GetIsNewExtensionMember() == true && + method.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is MethodSymbol implementationMethod) + { + method = implementationMethod.AsMember(method.ContainingSymbol.ContainingType). + ConstructIfGeneric(method.ContainingType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.Concat(method.TypeArgumentsWithAnnotations)); + } + + return rewriter.VisitMethodSymbol(method); + } + + [return: NotNullIfNotNull(nameof(method))] + public override MethodSymbol? VisitMethodSymbol(MethodSymbol? method) + { + Debug.Assert(method?.GetIsNewExtensionMember() != true || + method.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() is null); + // All possibly interesting methods should go through VisitMethodSymbolWithExtensionRewrite first + Debug.Assert(method is null || + method.ContainingSymbol is not NamedTypeSymbol || + method.MethodKind is (MethodKind.Constructor or MethodKind.StaticConstructor) || + method.OriginalDefinition is ErrorMethodSymbol || + new StackTrace(fNeedFileInfo: false).GetFrame(1)?.GetMethod() switch + { + { Name: nameof(VisitTypeOfOperator) } => method is { Name: "GetTypeFromHandle", IsExtensionMethod: false }, // GetTypeFromHandle cannot be an extension method + { Name: nameof(VisitRefTypeOperator) } => method is { Name: "GetTypeFromHandle", IsExtensionMethod: false }, // GetTypeFromHandle cannot be an extension method + { Name: nameof(VisitReadOnlySpanFromArray) } => method is { Name: "op_Implicit", IsExtensionMethod: false }, // Conversion operator from array to span cannot be an extension method + { Name: nameof(VisitLoweredConditionalAccess) } => // Nullable.HasValue cannot be an extension method + method.ContainingAssembly.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_HasValue) == (object)method.OriginalDefinition, + { Name: nameof(VisitUnaryOperator) } => !method.IsExtensionMethod, // Expression tree context. At the moment an operator cannot be an extension method + { Name: nameof(VisitUserDefinedConditionalLogicalOperator) } => !method.IsExtensionMethod, // Expression tree context. At the moment an operator cannot be an extension method + { Name: nameof(VisitCollectionElementInitializer) } => !method.IsExtensionMethod, // Expression tree context. At the moment an extension method cannot be used in expression tree here. + { Name: nameof(VisitAwaitableInfo) } => method is { Name: "GetResult", IsExtensionMethod: false }, // Cannot be an extension method + { Name: nameof(VisitMethodSymbolWithExtensionRewrite), DeclaringType: { } declaringType } => declaringType == typeof(ExtensionMethodReferenceRewriter), + _ => false + }); + + return base.VisitMethodSymbol(method); + } + + public override BoundNode? VisitMethodDefIndex(BoundMethodDefIndex node) + { + MethodSymbol method = node.Method; + Debug.Assert(method.IsDefinition); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : From the code coverage and other instrumentations perspective, should we remap the index to the implementation symbol? + TypeSymbol? type = this.VisitType(node.Type); + return node.Update(method, type); + } + + public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node) + { + return VisitDelegateCreationExpression(this, node); + } + + public static BoundNode VisitDelegateCreationExpression(BoundTreeRewriter rewriter, BoundDelegateCreationExpression node) + { + var methodOpt = VisitMethodSymbolWithExtensionRewrite(rewriter, node.MethodOpt); + var argument = (BoundExpression)rewriter.Visit(node.Argument); + var type = rewriter.VisitType(node.Type); + bool isExtensionMethod = node.IsExtensionMethod; + + if (!isExtensionMethod && argument is not BoundTypeExpression && methodOpt?.IsStatic == true) + { + Debug.Assert(node.MethodOpt!.OriginalDefinition.TryGetCorrespondingExtensionImplementationMethod() == (object)methodOpt.OriginalDefinition); + isExtensionMethod = true; + } + + return node.Update(argument, methodOpt, isExtensionMethod, node.WasTargetTyped, type); + } + + public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node) + { + return VisitFunctionPointerLoad(this, node); + } + + public static BoundNode VisitFunctionPointerLoad(BoundTreeRewriter rewriter, BoundFunctionPointerLoad node) + { + MethodSymbol targetMethod = VisitMethodSymbolWithExtensionRewrite(rewriter, node.TargetMethod); + TypeSymbol? constrainedToTypeOpt = rewriter.VisitType(node.ConstrainedToTypeOpt); + TypeSymbol? type = rewriter.VisitType(node.Type); + return node.Update(targetMethod, constrainedToTypeOpt, type); + } + + protected override BoundBinaryOperator.UncommonData? VisitBinaryOperatorData(BoundBinaryOperator node) + { + Debug.Assert(node.Method is null || + (!node.Method.IsExtensionMethod && !node.Method.GetIsNewExtensionMember())); // Expression tree context. At the moment an operator cannot be an extension method + + return base.VisitBinaryOperatorData(node); + } + + [return: NotNullIfNotNull(nameof(symbol))] + public override PropertySymbol? VisitPropertySymbol(PropertySymbol? symbol) + { + Debug.Assert(symbol?.GetIsNewExtensionMember() != true); + return base.VisitPropertySymbol(symbol); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs index a8200be6deb4d..97aa7f30f3370 100644 --- a/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/IteratorRewriter/IteratorFinallyMethodSymbol.cs @@ -27,7 +27,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// NOTE: Finally is a private void nonvirtual instance method with no parameters. /// It is a valid JIT inlining target as long as JIT may consider inlining profitable. /// - internal sealed class IteratorFinallyMethodSymbol : SynthesizedInstanceMethodSymbol, ISynthesizedMethodBodyImplementationSymbol + internal sealed class IteratorFinallyMethodSymbol : SynthesizedMethodSymbol, ISynthesizedMethodBodyImplementationSymbol { private readonly IteratorStateMachine _stateMachineType; private readonly string _name; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 3472bb4419165..68c424eb9ffa1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -18,22 +18,27 @@ internal sealed class DelegateCacheContainer : SynthesizedContainer private readonly Dictionary<(TypeSymbol?, TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(CLRSignatureComparer.Instance); /// Creates a type-scope concrete delegate cache container. - internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) - : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal), containingMethod: null) + internal DelegateCacheContainer(NamedTypeSymbol containingType, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal)) { Debug.Assert(containingType.IsDefinition); _containingSymbol = containingType; } - /// Creates a method-scope generic delegate cache container. - internal DelegateCacheContainer(MethodSymbol ownerMethod, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) - : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, ownerMethod.Name, topLevelMethodOrdinal, ownerUniqueId), ownerMethod) + /// Creates a generic delegate cache container "scoped" to a specific . + internal DelegateCacheContainer(NamedTypeSymbol containingType, Symbol owner, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, owner.Name, topLevelMethodOrdinal, ownerUniqueId), + owner is NamedTypeSymbol type ? + type.TypeParameters : + (owner.ContainingType is { IsExtension: true } extensionType ? extensionType.TypeParameters : []).Concat( + TypeMap.ConcatMethodTypeParameters((MethodSymbol)owner, stopAt: null))) { - Debug.Assert(ownerMethod.IsDefinition); - Debug.Assert(ownerMethod.Arity > 0); + Debug.Assert(containingType.IsDefinition); + Debug.Assert(owner.IsDefinition); + Debug.Assert(owner is NamedTypeSymbol { Arity: > 0 } or MethodSymbol { Arity: > 0 }); - _containingSymbol = ownerMethod.ContainingType; + _containingSymbol = containingType; _constructedContainer = Construct(ConstructedFromTypeParameters); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 101f8a8a5800e..6e1c31d953cdb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -22,7 +22,7 @@ internal sealed class DelegateCacheRewriter private readonly SyntheticBoundNodeFactory _factory; private readonly int _topLevelMethodOrdinal; - private Dictionary? _genericCacheContainers; + private Dictionary? _genericCacheContainers; internal DelegateCacheRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) { @@ -90,7 +90,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(BoundDelegateCreationExpre // // In the above case, only one cached delegate is necessary, and it could be assigned to the container 'owned' by LF1. - if (!TryGetOwnerFunction(_factory.CurrentFunction, boundDelegateCreation, out var ownerFunction)) + if (!TryGetOwnerFunctionOrExtensionType(_factory.CurrentFunction, boundDelegateCreation, out Symbol? owner)) { var typeCompilationState = _factory.CompilationState; container = typeCompilationState.ConcreteDelegateCacheContainer; @@ -105,15 +105,15 @@ private DelegateCacheContainer GetOrAddCacheContainer(BoundDelegateCreationExpre } else { - var containers = _genericCacheContainers ??= new Dictionary(ReferenceEqualityComparer.Instance); + var containers = _genericCacheContainers ??= new Dictionary(ReferenceEqualityComparer.Instance); - if (containers.TryGetValue(ownerFunction, out container)) + if (containers.TryGetValue(owner, out container)) { return container; } - container = new DelegateCacheContainer(ownerFunction, _topLevelMethodOrdinal, containers.Count, generation); - containers.Add(ownerFunction, container); + container = new DelegateCacheContainer(_factory.CompilationState.Type, owner, _topLevelMethodOrdinal, ownerUniqueId: containers.Count, generation); + containers.Add(owner, container); } _factory.AddNestedType(container); @@ -121,7 +121,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(BoundDelegateCreationExpre return container; } - private static bool TryGetOwnerFunction(MethodSymbol currentFunction, BoundDelegateCreationExpression boundDelegateCreation, [NotNullWhen(true)] out MethodSymbol? ownerFunction) + private static bool TryGetOwnerFunctionOrExtensionType(MethodSymbol currentFunction, BoundDelegateCreationExpression boundDelegateCreation, [NotNullWhen(true)] out Symbol? owner) { var targetMethod = boundDelegateCreation.MethodOpt; Debug.Assert(targetMethod is { }); @@ -139,16 +139,23 @@ private static bool TryGetOwnerFunction(MethodSymbol currentFunction, BoundDeleg // // Therefore, without too much analysis, we select the closest generic enclosing function as the cache container owner. - for (Symbol? enclosingSymbol = currentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) + Symbol? enclosingSymbol = currentFunction; + for (; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { if (enclosingMethod.Arity > 0) { - ownerFunction = enclosingMethod; + owner = enclosingMethod; return true; } } - ownerFunction = null; + if (enclosingSymbol is NamedTypeSymbol { IsExtension: true, Arity: > 0 }) + { + owner = enclosingSymbol; + return true; + } + + owner = null; return false; } @@ -173,16 +180,24 @@ private static bool TryGetOwnerFunction(MethodSymbol currentFunction, BoundDeleg FindTypeParameters(delegateType, usedTypeParameters); FindTypeParameters(targetMethod, usedTypeParameters); - for (Symbol? enclosingSymbol = currentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) + Symbol? enclosingSymbol = currentFunction; + for (; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { if (usedTypeParametersContains(usedTypeParameters, enclosingMethod.TypeParameters)) { - ownerFunction = enclosingMethod; + owner = enclosingMethod; return true; } } - ownerFunction = null; + if (enclosingSymbol is NamedTypeSymbol { IsExtension: true, Arity: > 0 } extensionType && + usedTypeParametersContains(usedTypeParameters, extensionType.TypeParameters)) + { + owner = enclosingSymbol; + return true; + } + + owner = null; return false; } finally diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs index 70231c91adda7..a02f486447157 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DynamicSiteContainer.cs @@ -15,7 +15,9 @@ internal sealed class DynamicSiteContainer : SynthesizedContainer, ISynthesizedM private readonly MethodSymbol _topLevelMethod; internal DynamicSiteContainer(string name, MethodSymbol topLevelMethod, MethodSymbol containingMethod) - : base(name, containingMethod) + : base(name, + (topLevelMethod.ContainingSymbol is NamedTypeSymbol { IsExtension: true } extensionType ? extensionType.TypeParameters : []).Concat( + TypeMap.ConcatMethodTypeParameters(containingMethod, stopAt: null))) { Debug.Assert(topLevelMethod != null); _topLevelMethod = topLevelMethod; @@ -23,7 +25,16 @@ internal DynamicSiteContainer(string name, MethodSymbol topLevelMethod, MethodSy public override Symbol ContainingSymbol { - get { return _topLevelMethod.ContainingSymbol; } + get + { + var containingSymbol = _topLevelMethod.ContainingSymbol; + if (containingSymbol is NamedTypeSymbol { IsExtension: true }) + { + return containingSymbol.ContainingSymbol; + } + + return containingSymbol; + } } public override TypeKind TypeKind diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs index 9b1e482372253..56b944a4888b2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.PatternLocalRewriter.cs @@ -169,7 +169,7 @@ void addArg(RefKind refKind, BoundExpression expression) Debug.Assert(method.Name == WellKnownMemberNames.DeconstructMethodName); int extensionExtra; - if (method.IsStatic) + if (method.IsStatic) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { Debug.Assert(method.IsExtensionMethod); receiver = _factory.Type(method.ContainingType); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 8e73de6531706..6f8840715c10a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -412,7 +412,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen } } - static bool hasReturnTypeOrParameter(LocalFunctionSymbol localFunction, Func predicate) => + static bool hasReturnTypeOrParameter(MethodSymbol localFunction, Func predicate) => predicate(localFunction.ReturnTypeWithAnnotations) || localFunction.ParameterTypesWithAnnotations.Any(predicate); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 90283088cfd05..74264343a3cbd 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -77,7 +77,7 @@ public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDe if (_inExpressionLambda) { - return node.Update(operatorKind, node.LogicalOperator, node.TrueOperator, node.FalseOperator, node.ConstrainedToTypeOpt, node.ResultKind, loweredLeft, loweredRight, type); + return node.Update(operatorKind, node.LogicalOperator, node.TrueOperator, node.FalseOperator, node.ConstrainedToTypeOpt, node.ResultKind, originalUserDefinedOperatorsOpt: default, loweredLeft, loweredRight, type); } BoundAssignmentOperator tempAssignment; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index 021a5509ae2d7..913c4f7053771 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -54,7 +54,7 @@ public BoundExpression VisitDynamicInvocation(BoundDynamicInvocation node, bool { // Calling a static method defined on the current class via its simple name. Debug.Assert(_factory.CurrentType is { }); - loweredReceiver = new BoundTypeExpression(node.Syntax, null, _factory.CurrentType); + loweredReceiver = new BoundTypeExpression(node.Syntax, null, _factory.CurrentType); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Follow up (_factory.CompilationState.Type?) } else { @@ -199,8 +199,8 @@ private void InterceptCallAndAdjustArguments( // When the original call is to an instance method, and the interceptor is an extension method, // we need to take special care to intercept with the extension method as though it is being called in reduced form. Debug.Assert(receiverOpt is not BoundTypeExpression || method.IsStatic); - var needToReduce = receiverOpt is not (null or BoundTypeExpression) && interceptor.IsExtensionMethod; - var symbolForCompare = needToReduce ? ReducedExtensionMethodSymbol.Create(interceptor, receiverOpt!.Type, _compilation, out _) : interceptor; + var needToReduce = receiverOpt is not (null or BoundTypeExpression) && interceptor.IsExtensionMethod; // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions + var symbolForCompare = needToReduce ? ReducedExtensionMethodSymbol.Create(interceptor, receiverOpt!.Type, _compilation, out _) : interceptor; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : test interceptors if (!MemberSignatureComparer.InterceptorsComparer.Equals(method, symbolForCompare)) { @@ -245,7 +245,7 @@ private void InterceptCallAndAdjustArguments( break; } - if (invokedAsExtensionMethod && interceptor.IsStatic && !interceptor.IsExtensionMethod) + if (invokedAsExtensionMethod && interceptor.IsStatic && !interceptor.IsExtensionMethod) // Tracked by https://github.com/dotnet/roslyn/issues/76130: Test this code path with new extensions { // Special case when intercepting an extension method call in reduced form with a non-extension. this._diagnostics.Add(ErrorCode.ERR_InterceptorMustHaveMatchingThisParameter, attributeLocation, method.Parameters[0], method); @@ -323,7 +323,7 @@ public override BoundNode VisitCall(BoundCall node) BoundExpression rewrittenCall; - if (tryGetReceiver(node, out BoundCall? receiver1)) + if (TryGetReceiver(node, out BoundCall? receiver1)) { // Handle long call chain of both instance and extension method invocations. var calls = ArrayBuilder.GetInstance(); @@ -331,7 +331,7 @@ public override BoundNode VisitCall(BoundCall node) calls.Push(node); node = receiver1; - while (tryGetReceiver(node, out BoundCall? receiver2)) + while (TryGetReceiver(node, out BoundCall? receiver2)) { calls.Push(node); node = receiver2; @@ -358,26 +358,6 @@ public override BoundNode VisitCall(BoundCall node) return rewrittenCall; - // Gets the instance or extension invocation receiver if any. - static bool tryGetReceiver(BoundCall node, [MaybeNullWhen(returnValue: false)] out BoundCall receiver) - { - if (node.ReceiverOpt is BoundCall instanceReceiver) - { - receiver = instanceReceiver; - return true; - } - - if (node.InvokedAsExtensionMethod && node.Arguments is [BoundCall extensionReceiver, ..]) - { - Debug.Assert(node.ReceiverOpt is null); - receiver = extensionReceiver; - return true; - } - - receiver = null; - return false; - } - BoundExpression visitArgumentsAndFinishRewrite(BoundCall node, BoundExpression? rewrittenReceiver) { MethodSymbol method = node.Method; @@ -434,6 +414,28 @@ BoundExpression visitArgumentsAndFinishRewrite(BoundCall node, BoundExpression? } } + /// + /// Gets the instance or extension invocation receiver if any. + /// + internal static bool TryGetReceiver(BoundCall node, [MaybeNullWhen(returnValue: false)] out BoundCall receiver) + { + if (node.ReceiverOpt is BoundCall instanceReceiver) + { + receiver = instanceReceiver; + return true; + } + + if (node.InvokedAsExtensionMethod && node.Arguments is [BoundCall extensionReceiver, ..]) + { + Debug.Assert(node.ReceiverOpt is null); + receiver = extensionReceiver; + return true; + } + + receiver = null; + return false; + } + private BoundExpression MakeCall( BoundCall? node, SyntaxNode syntax, @@ -1229,15 +1231,29 @@ internal static bool CanSkipRewriting( if (!ignoreComReceiver) { - var receiverNamedType = invokedAsExtensionMethod ? - ((MethodSymbol)methodOrIndexer).Parameters[0].Type as NamedTypeSymbol : - methodOrIndexer.ContainingType; + NamedTypeSymbol? receiverNamedType = tryGetReceiverNamedType(methodOrIndexer, invokedAsExtensionMethod); isComReceiver = receiverNamedType is { IsComImport: true }; } return rewrittenArguments.Length == methodOrIndexer.GetParameterCount() && argsToParamsOpt.IsDefault && !isComReceiver; + + static NamedTypeSymbol? tryGetReceiverNamedType(Symbol methodOrIndexer, bool invokedAsExtensionMethod) + { + if (invokedAsExtensionMethod) + { + return ((MethodSymbol)methodOrIndexer).Parameters[0].Type as NamedTypeSymbol; + } + + if (methodOrIndexer.GetIsNewExtensionMember()) + { + Debug.Assert(methodOrIndexer.ContainingType.ExtensionParameter is not null); + return methodOrIndexer.ContainingType.ExtensionParameter.Type as NamedTypeSymbol; + } + + return (NamedTypeSymbol?)methodOrIndexer.ContainingType; + } } private static ImmutableArray GetRefKindsOrNull(ArrayBuilder refKinds) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index 4f4d5c20dfdb1..fe1967fcf02b7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -384,7 +384,7 @@ private BoundExpression VisitCollectionInitializerCollectionExpression(BoundColl { return expressionElement switch { - BoundCollectionElementInitializer collectionInitializer => MakeCollectionInitializer(rewrittenReceiver, collectionInitializer), + BoundCollectionElementInitializer collectionInitializer => MakeCollectionInitializer(collectionInitializer), BoundDynamicCollectionElementInitializer dynamicInitializer => MakeDynamicCollectionInitializer(rewrittenReceiver, dynamicInitializer), var e => throw ExceptionUtilities.UnexpectedValue(e) }; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index 306b42bad04f0..2b7c275bcc0a6 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -141,8 +141,8 @@ private BoundStatement RewriteForEachEnumerator( ImmutableArray iterationVariables, BoundForEachDeconstructStep? deconstruction, BoundAwaitableInfo? awaitableInfo, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, BoundStatement rewrittenBody) { var forEachSyntax = (CSharpSyntaxNode)node.Syntax; @@ -181,7 +181,7 @@ private BoundStatement RewriteForEachEnumerator( // ((C)(x)).GetEnumerator(); OR (x).GetEnumerator(); OR async variants (which fill-in arguments for optional parameters) BoundExpression enumeratorVarInitValue = SynthesizeCall(getEnumeratorInfo, forEachSyntax, receiver, - allowExtensionAndOptionalParameters: isAsync || getEnumeratorInfo.Method.IsExtensionMethod, firstRewrittenArgument: firstRewrittenArgument); + allowExtensionAndOptionalParameters: isAsync || getEnumeratorInfo.Method.IsExtensionMethod || getEnumeratorInfo.Method.GetIsNewExtensionMember(), firstRewrittenArgument: firstRewrittenArgument); // E e = ((C)(x)).GetEnumerator(); BoundStatement enumeratorVarDecl = MakeLocalDeclaration(forEachSyntax, enumeratorVar, enumeratorVarInitValue); @@ -602,8 +602,8 @@ private BoundStatement RewriteForEachStatementAsFor( BoundExpression? elementConversion, ImmutableArray iterationVariables, BoundForEachDeconstructStep? deconstructionOpt, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, BoundStatement rewrittenBody) { NamedTypeSymbol? collectionType = (NamedTypeSymbol?)collectionExpression.Type; @@ -898,8 +898,8 @@ private BoundStatement RewriteSingleDimensionalArrayForEachEnumerator( BoundExpression? elementConversion, ImmutableArray iterationVariables, BoundForEachDeconstructStep? deconstruction, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, BoundStatement rewrittenBody) { Debug.Assert(collectionExpression.Type is { TypeKind: TypeKind.Array }); @@ -1047,8 +1047,8 @@ private BoundStatement RewriteMultiDimensionalArrayForEachEnumerator( BoundExpression? elementConversion, ImmutableArray iterationVariables, BoundForEachDeconstructStep? deconstruction, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, BoundStatement rewrittenBody) { Debug.Assert(collectionExpression.Type is { TypeKind: TypeKind.Array }); @@ -1158,7 +1158,7 @@ private BoundStatement RewriteMultiDimensionalArrayForEachEnumerator( // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); - GeneratedLabelSymbol breakLabelInner = dimension == 0 // outermost for-loop + LabelSymbol breakLabelInner = dimension == 0 // outermost for-loop ? breakLabel // i.e. the one that break statements will jump to : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused @@ -1178,7 +1178,7 @@ private BoundStatement RewriteMultiDimensionalArrayForEachEnumerator( BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; - GeneratedLabelSymbol continueLabelInner; + LabelSymbol continueLabelInner; if (forLoop == null) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForStatement.cs index 1ac1cdd3d013b..6160089918162 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForStatement.cs @@ -44,8 +44,8 @@ private BoundStatement RewriteForStatementWithoutInnerLocals( BoundExpression? rewrittenCondition, BoundStatement? rewrittenIncrement, BoundStatement rewrittenBody, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, bool hasErrors) { Debug.Assert(original.Kind is BoundKind.ForStatement or BoundKind.ForEachStatement or BoundKind.CollectionExpressionSpreadElement); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs index 2141d021dd429..ec0cd11e141fe 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectOrCollectionInitializerExpression.cs @@ -74,18 +74,57 @@ private ImmutableArray MakeObjectOrCollectionInitializersForExp case BoundKind.CollectionInitializerExpression: var result = ArrayBuilder.GetInstance(); - AddCollectionInitializers(result, null, ((BoundCollectionInitializerExpression)initializerExpression).Initializers); + addCollectionInitializersForExpressionTree(result, ((BoundCollectionInitializerExpression)initializerExpression).Initializers); return result.ToImmutableAndFree(); default: throw ExceptionUtilities.UnexpectedValue(initializerExpression.Kind); } + + void addCollectionInitializersForExpressionTree(ArrayBuilder result, ImmutableArray initializers) + { + foreach (var initializer in initializers) + { + // In general bound initializers may contain bad expressions or element initializers. + // We don't lower them if they contain errors, so it's safe to assume an element initializer. + + if (initializer.Kind != BoundKind.CollectionElementInitializer) + { + throw ExceptionUtilities.UnexpectedValue(initializer.Kind); + } + + var elementInitializer = (BoundCollectionElementInitializer)initializer; + + // NOTE: Calls cannot be omitted within an expression tree (CS0765); this should already + // have been checked. + Debug.Assert(!elementInitializer.AddMethod.CallsAreOmitted(initializer.SyntaxTree)); + + Debug.Assert(!elementInitializer.InvokedAsExtensionMethod); + Debug.Assert(!elementInitializer.AddMethod.IsExtensionMethod); + Debug.Assert(!elementInitializer.AddMethod.GetIsNewExtensionMember()); + Debug.Assert(elementInitializer.Arguments.Length == elementInitializer.AddMethod.ParameterCount); + Debug.Assert(elementInitializer.ImplicitReceiverOpt is BoundObjectOrCollectionValuePlaceholder); + + result.Add( + VisitExpression( + elementInitializer.Update( + elementInitializer.AddMethod, + elementInitializer.Arguments, + implicitReceiverOpt: null, + elementInitializer.Expanded, + elementInitializer.ArgsToParamsOpt, + elementInitializer.DefaultArguments, + elementInitializer.InvokedAsExtensionMethod, + elementInitializer.ResultKind, + elementInitializer.Type))); + } + } } // Rewrite collection initializer add method calls: // 2) new List { 1 }; // ~ - private void AddCollectionInitializers(ArrayBuilder result, BoundExpression? rewrittenReceiver, ImmutableArray initializers) + private void AddCollectionInitializers(ArrayBuilder result, BoundExpression rewrittenReceiver, ImmutableArray initializers) { Debug.Assert(rewrittenReceiver is { } || _inExpressionLambda); @@ -97,7 +136,7 @@ private void AddCollectionInitializers(ArrayBuilder result, Bou BoundExpression? rewrittenInitializer; if (initializer.Kind == BoundKind.CollectionElementInitializer) { - rewrittenInitializer = MakeCollectionInitializer(rewrittenReceiver, (BoundCollectionElementInitializer)initializer); + rewrittenInitializer = MakeCollectionInitializer((BoundCollectionElementInitializer)initializer); } else { @@ -137,7 +176,7 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt // Rewrite collection initializer element Add method call: // new List { 1, 2, 3 }; OR new List { { 1, 2 }, 3 }; OR [1, 2, 3] // ~ ~~~~~~~~ - private BoundExpression? MakeCollectionInitializer(BoundExpression? rewrittenReceiver, BoundCollectionElementInitializer initializer) + private BoundExpression? MakeCollectionInitializer(BoundCollectionElementInitializer initializer) { MethodSymbol addMethod = initializer.AddMethod; @@ -146,22 +185,22 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt .Skip(addMethod.IsExtensionMethod ? 1 : 0) .All(p => p.RefKind is RefKind.None or RefKind.In or RefKind.RefReadOnlyParameter)); Debug.Assert(initializer.Arguments.Any()); - Debug.Assert(rewrittenReceiver != null || _inExpressionLambda); + Debug.Assert(!_inExpressionLambda); var syntax = initializer.Syntax; if (_allowOmissionOfConditionalCalls) { - // NOTE: Calls cannot be omitted within an expression tree (CS0765); this should already - // have been checked. if (addMethod.CallsAreOmitted(initializer.SyntaxTree)) { return null; } } + BoundExpression? rewrittenReceiver = VisitExpression(initializer.ImplicitReceiverOpt); + var argumentRefKindsOpt = default(ImmutableArray); - if (addMethod.Parameters[0].RefKind == RefKind.Ref) + if (initializer.InvokedAsExtensionMethod && addMethod.Parameters[0].RefKind == RefKind.Ref) { // If the Add method is an extension which takes a `ref this` as the first parameter, implicitly add a `ref` to the argument // Initializer element syntax cannot have `ref`, `in`, or `out` keywords. @@ -186,20 +225,14 @@ private BoundExpression MakeDynamicCollectionInitializer(BoundExpression rewritt var rewrittenType = VisitType(initializer.Type); +#if DEBUG if (initializer.InvokedAsExtensionMethod) { Debug.Assert(addMethod.IsStatic); Debug.Assert(addMethod.IsExtensionMethod); - Debug.Assert(!_inExpressionLambda, "Expression trees do not support extension Add"); - rewrittenReceiver = null; - } - - if (_inExpressionLambda) - { - Debug.Assert(temps.Count == 0); - temps.Free(); - return initializer.Update(addMethod, rewrittenArguments, rewrittenReceiver, expanded: false, argsToParamsOpt: default, defaultArguments: default, invokedAsExtensionMethod: false, initializer.ResultKind, rewrittenType); + Debug.Assert(rewrittenReceiver is null); } +#endif if (Instrument) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_WhileStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_WhileStatement.cs index 910a0c4e3f7f5..ba92006518868 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_WhileStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_WhileStatement.cs @@ -42,8 +42,8 @@ private BoundStatement RewriteWhileStatement( BoundNode loop, BoundExpression rewrittenCondition, BoundStatement rewrittenBody, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, bool hasErrors) { Debug.Assert(loop.Kind is BoundKind.WhileStatement or BoundKind.ForEachStatement or BoundKind.CollectionExpressionSpreadElement); @@ -108,8 +108,8 @@ private BoundStatement RewriteWhileStatement( ImmutableArray locals, BoundExpression rewrittenCondition, BoundStatement rewrittenBody, - GeneratedLabelSymbol breakLabel, - GeneratedLabelSymbol continueLabel, + LabelSymbol breakLabel, + LabelSymbol continueLabel, bool hasErrors) { if (locals.IsEmpty) diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index de13dc92deff2..a870c8678eb7f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -13,11 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - /// - /// a bound node rewriter that rewrites types properly (which in some cases the automatically-generated - /// base class does not). This is used in the lambda rewriter, the iterator rewriter, and the async rewriter. - /// - internal abstract partial class MethodToClassRewriter : BoundTreeRewriterWithStackGuard + internal abstract partial class MethodToClassRewriter : BoundTreeToDifferentEnclosingContextRewriter { // For each captured variable, information about its replacement. May be populated lazily (that is, not all // upfront) by subclasses. Specifically, the async rewriter produces captured symbols for temps, including @@ -27,21 +23,9 @@ internal abstract partial class MethodToClassRewriter : BoundTreeRewriterWithSta // created in the first pass. protected Dictionary proxies = new Dictionary(); - // A mapping from every local variable to its replacement local variable. Local variables are replaced when - // their types change due to being inside of a generic method. Otherwise we reuse the original local (even - // though its containing method is not correct because the code is moved into another method) - protected readonly Dictionary localMap = new Dictionary(); - - // A mapping for types in the original method to types in its replacement. This is mainly necessary - // when the original method was generic, as type parameters in the original method are mapping into - // type parameters of the resulting class. - protected abstract TypeMap TypeMap { get; } - // Subclasses override this method to fetch a frame pointer. protected abstract BoundExpression FramePointer(SyntaxNode syntax, NamedTypeSymbol frameClass); - protected abstract MethodSymbol CurrentMethod { get; } - // Containing type for any synthesized members. protected abstract NamedTypeSymbol ContainingType { get; } @@ -65,30 +49,13 @@ protected MethodToClassRewriter(VariableSlotAllocator? slotAllocator, TypeCompil this._placeholderMap = new Dictionary(); } - public override BoundNode DefaultVisit(BoundNode node) - { - Debug.Fail($"Override the visitor for {node.Kind}"); - return base.DefaultVisit(node); - } - /// /// Returns true if the specified local/parameter needs to be hoisted to a field. /// Variable may be hoisted even if it is not captured, to improve debugging experience. /// protected abstract bool NeedsProxy(Symbol localOrParameter); - protected void RewriteLocals(ImmutableArray locals, ArrayBuilder newLocals) - { - foreach (var local in locals) - { - if (TryRewriteLocal(local, out LocalSymbol? newLocal)) - { - newLocals.Add(newLocal); - } - } - } - - protected bool TryRewriteLocal(LocalSymbol local, [NotNullWhen(true)] out LocalSymbol? newLocal) + protected sealed override bool TryRewriteLocal(LocalSymbol local, [NotNullWhen(true)] out LocalSymbol? newLocal) { if (NeedsProxy(local)) { @@ -97,126 +64,29 @@ protected bool TryRewriteLocal(LocalSymbol local, [NotNullWhen(true)] out LocalS return false; } - if (localMap.TryGetValue(local, out newLocal)) - { - return true; - } - - var newType = VisitType(local.Type); - if (TypeSymbol.Equals(newType, local.Type, TypeCompareKind.ConsiderEverything2)) - { - newLocal = local; - } - else - { - newLocal = new TypeSubstitutedLocalSymbol(local, TypeWithAnnotations.Create(newType), CurrentMethod); - localMap.Add(local, newLocal); - } - - return true; - } - - private ImmutableArray RewriteLocals(ImmutableArray locals) - { - if (locals.IsEmpty) return locals; - var newLocals = ArrayBuilder.GetInstance(); - RewriteLocals(locals, newLocals); - return newLocals.ToImmutableAndFree(); - } - - public override BoundNode VisitCatchBlock(BoundCatchBlock node) - { - if (!node.Locals.IsDefaultOrEmpty) - { - // Yield/await aren't supported in catch block atm, but we need to rewrite the type - // of the variables owned by the catch block. Note that one of these variables might be a closure frame reference. - var newLocals = RewriteLocals(node.Locals); - - return node.Update( - newLocals, - (BoundExpression?)this.Visit(node.ExceptionSourceOpt), - this.VisitType(node.ExceptionTypeOpt), - (BoundStatementList?)this.Visit(node.ExceptionFilterPrologueOpt), - (BoundExpression?)this.Visit(node.ExceptionFilterOpt), - (BoundBlock?)this.Visit(node.Body)!, - node.IsSynthesizedAsyncCatchAll); - } - - return base.VisitCatchBlock(node)!; - } - - public override BoundNode VisitBlock(BoundBlock node) - => VisitBlock(node, removeInstrumentation: false); - - protected BoundBlock VisitBlock(BoundBlock node, bool removeInstrumentation) - { - // Note: Instrumentation variable is intentionally not rewritten. It should never be lifted. - - var newLocals = RewriteLocals(node.Locals); - var newLocalFunctions = node.LocalFunctions; - var newStatements = VisitList(node.Statements); - var newInstrumentation = removeInstrumentation ? null : (BoundBlockInstrumentation?)Visit(node.Instrumentation); - return node.Update(newLocals, newLocalFunctions, node.HasUnsafeModifier, newInstrumentation, newStatements); + return base.TryRewriteLocal(local, out newLocal); } public abstract override BoundNode VisitScope(BoundScope node); - public override BoundNode VisitSequence(BoundSequence node) - { - var newLocals = RewriteLocals(node.Locals); - var newSideEffects = VisitList(node.SideEffects); - var newValue = (BoundExpression)this.Visit(node.Value); - var newType = this.VisitType(node.Type); - return node.Update(newLocals, newSideEffects, newValue, newType); - } - public override BoundNode VisitForStatement(BoundForStatement node) { - var newOuterLocals = RewriteLocals(node.OuterLocals); - var initializer = (BoundStatement?)this.Visit(node.Initializer); - var newInnerLocals = RewriteLocals(node.InnerLocals); - var condition = (BoundExpression?)this.Visit(node.Condition); - var increment = (BoundStatement?)this.Visit(node.Increment); - var body = (BoundStatement)this.Visit(node.Body); - return node.Update(newOuterLocals, initializer, newInnerLocals, condition, increment, body, node.BreakLabel, node.ContinueLabel); + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitDoStatement(BoundDoStatement node) { - var newLocals = RewriteLocals(node.Locals); - BoundExpression condition = (BoundExpression)this.Visit(node.Condition); - BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(newLocals, condition, body, node.BreakLabel, node.ContinueLabel); + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitWhileStatement(BoundWhileStatement node) { - var newLocals = RewriteLocals(node.Locals); - BoundExpression condition = (BoundExpression)this.Visit(node.Condition); - BoundStatement body = (BoundStatement)this.Visit(node.Body); - return node.Update(newLocals, condition, body, node.BreakLabel, node.ContinueLabel); + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitUsingStatement(BoundUsingStatement node) { - var newLocals = RewriteLocals(node.Locals); - var declarations = (BoundMultipleLocalDeclarations?)this.Visit(node.DeclarationsOpt); - var expression = (BoundExpression?)this.Visit(node.ExpressionOpt); - var body = (BoundStatement)this.Visit(node.Body); - return node.Update(newLocals, declarations, expression, body, node.AwaitOpt, node.PatternDisposeInfoOpt); - } - - [return: NotNullIfNotNull(nameof(type))] - public sealed override TypeSymbol? VisitType(TypeSymbol? type) - { - return TypeMap.SubstituteType(type).Type; - } - - public override BoundNode VisitMethodInfo(BoundMethodInfo node) - { - var rewrittenMethod = VisitMethodSymbol(node.Method); - // No need to rewrite the node's type, because it is always System.Reflection.MethodInfo - return node.Update(rewrittenMethod, node.GetMethodFromHandle, node.Type); + throw ExceptionUtilities.Unreachable(); } public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) @@ -262,64 +132,6 @@ public override BoundNode VisitCall(BoundCall node) rewrittenType); } - public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) - { - // Local rewriter should have already rewritten interpolated strings into their final form of calls and gotos - Debug.Assert(node.InterpolatedStringHandlerData is null); - - return node.Update( - node.OperatorKind, - node.ConstantValueOpt, - VisitMethodSymbol(node.Method), - VisitType(node.ConstrainedToType), - node.ResultKind, - (BoundExpression)Visit(node.Left), - (BoundExpression)Visit(node.Right), - VisitType(node.Type)); - } - - public override BoundNode VisitUnaryOperator(BoundUnaryOperator node) - => node.Update( - node.OperatorKind, - (BoundExpression)Visit(node.Operand), - node.ConstantValueOpt, - VisitMethodSymbol(node.MethodOpt), - VisitType(node.ConstrainedToTypeOpt), - node.ResultKind, - VisitType(node.Type)); - - public override BoundNode? VisitConversion(BoundConversion node) - { - var conversion = node.Conversion; - - if (conversion.Method is not null) - { - conversion = conversion.SetConversionMethod(VisitMethodSymbol(conversion.Method)); - } - - return node.Update( - (BoundExpression)Visit(node.Operand), - conversion, - node.IsBaseConversion, - node.Checked, - node.ExplicitCastInCode, - node.ConstantValueOpt, - node.ConversionGroupOpt, - VisitType(node.Type)); - } - - public override BoundNode? VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node) - => node.Update( - node.OperatorKind, - VisitMethodSymbol(node.LogicalOperator), - VisitMethodSymbol(node.TrueOperator), - VisitMethodSymbol(node.FalseOperator), - VisitType(node.ConstrainedToTypeOpt), - node.ResultKind, - (BoundExpression)Visit(node.Left), - (BoundExpression)Visit(node.Right), - VisitType(node.Type)); - private MethodSymbol GetMethodWrapperForBaseNonVirtualCall(MethodSymbol methodBeingCalled, SyntaxNode syntax) { var newMethod = GetOrCreateBaseFunctionWrapper(methodBeingCalled, syntax); @@ -411,7 +223,7 @@ public sealed override BoundNode VisitLocal(BoundLocal node) // if a local needs a proxy it should have been allocated by its declaration node. Debug.Assert(!NeedsProxy(node.LocalSymbol)); - return VisitUnhoistedLocal(node); + return base.VisitLocal(node)!; } public override BoundNode? VisitLocalId(BoundLocalId node) @@ -442,16 +254,6 @@ private bool TryGetHoistedField(Symbol variable, [NotNullWhen(true)] out FieldSy return false; } - private BoundNode VisitUnhoistedLocal(BoundLocal node) - { - if (this.localMap.TryGetValue(node.LocalSymbol, out LocalSymbol? replacementLocal)) - { - return new BoundLocal(node.Syntax, replacementLocal, node.ConstantValueOpt, replacementLocal.Type, node.HasErrors); - } - - return base.VisitLocal(node)!; - } - public override BoundNode VisitAwaitableInfo(BoundAwaitableInfo node) { var awaitablePlaceholder = node.AwaitableInstancePlaceholder; @@ -558,29 +360,6 @@ public override BoundNode VisitFieldAccess(BoundFieldAccess node) return node.Update(receiverOpt, fieldSymbol, node.ConstantValueOpt, node.ResultKind, type); } - public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) - { - var rewritten = (BoundObjectCreationExpression?)base.VisitObjectCreationExpression(node); - Debug.Assert(rewritten != null); - if (!TypeSymbol.Equals(rewritten.Type, node.Type, TypeCompareKind.ConsiderEverything2) && (object)node.Constructor != null) - { - MethodSymbol ctor = VisitMethodSymbol(node.Constructor); - rewritten = rewritten.Update( - ctor, - rewritten.Arguments, - rewritten.ArgumentNamesOpt, - rewritten.ArgumentRefKindsOpt, - rewritten.Expanded, - rewritten.ArgsToParamsOpt, - rewritten.DefaultArguments, - rewritten.ConstantValueOpt, - rewritten.InitializerExpressionOpt, - rewritten.Type); - } - - return rewritten; - } - public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node) { BoundExpression originalArgument = node.Argument; @@ -598,60 +377,8 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE return node.Update(rewrittenArgument, method, node.IsExtensionMethod, node.WasTargetTyped, type); } - public override BoundNode VisitFunctionPointerLoad(BoundFunctionPointerLoad node) - { - return node.Update(VisitMethodSymbol(node.TargetMethod), VisitType(node.ConstrainedToTypeOpt), VisitType(node.Type)); - } - - public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalAccess node) - { - var receiver = (BoundExpression)this.Visit(node.Receiver); - var whenNotNull = (BoundExpression)this.Visit(node.WhenNotNull); - var whenNullOpt = (BoundExpression?)this.Visit(node.WhenNullOpt); - TypeSymbol type = this.VisitType(node.Type); - return node.Update(receiver, VisitMethodSymbol(node.HasValueMethodOpt), whenNotNull, whenNullOpt, node.Id, node.ForceCopyOfNullableValueType, type); - } - - [return: NotNullIfNotNull(nameof(method))] - protected MethodSymbol? VisitMethodSymbol(MethodSymbol? method) - { - if (method is null) - { - return null; - } - - if (method.ContainingType.IsAnonymousType) - { - // Method of an anonymous type - var newType = (NamedTypeSymbol)TypeMap.SubstituteType(method.ContainingType).AsTypeSymbolOnly(); - if (ReferenceEquals(newType, method.ContainingType)) - { - // Anonymous type symbol was not rewritten - return method; - } - - // get a new method by name - foreach (var member in newType.GetMembers(method.Name)) - { - if (member.Kind == SymbolKind.Method) - { - return (MethodSymbol)member; - } - } - - throw ExceptionUtilities.Unreachable(); - } - else - { - // Method of a regular type - return ((MethodSymbol)method.OriginalDefinition) - .AsMember((NamedTypeSymbol)TypeMap.SubstituteType(method.ContainingType).AsTypeSymbolOnly()) - .ConstructIfGeneric(TypeMap.SubstituteTypes(method.TypeArgumentsWithAnnotations)); - } - } - [return: NotNullIfNotNull(nameof(property))] - private PropertySymbol? VisitPropertySymbol(PropertySymbol? property) + private new PropertySymbol? VisitPropertySymbol(PropertySymbol? property) { if (property is null) { @@ -685,7 +412,7 @@ public override BoundNode VisitLoweredConditionalAccess(BoundLoweredConditionalA throw ExceptionUtilities.Unreachable(); } - private FieldSymbol VisitFieldSymbol(FieldSymbol field) + private new FieldSymbol VisitFieldSymbol(FieldSymbol field) { // Property of a regular type return ((FieldSymbol)field.OriginalDefinition) @@ -714,14 +441,6 @@ public override BoundNode VisitObjectInitializerMember(BoundObjectInitializerMem return node.Update(member, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.AccessorKind, receiverType, type); } - public override BoundNode VisitReadOnlySpanFromArray(BoundReadOnlySpanFromArray node) - { - BoundExpression operand = (BoundExpression)this.Visit(node.Operand); - MethodSymbol method = VisitMethodSymbol(node.ConversionMethod); - TypeSymbol type = this.VisitType(node.Type); - return node.Update(operand, method, type); - } - private static bool BaseReferenceInReceiverWasRewritten([NotNullWhen(true)] BoundExpression? originalReceiver, [NotNullWhen(true)] BoundExpression? rewrittenReceiver) { return originalReceiver is { Kind: BoundKind.BaseReference } && @@ -732,7 +451,7 @@ private static bool BaseReferenceInReceiverWasRewritten([NotNullWhen(true)] Boun /// A wrapper method that is created for non-virtually calling a base-class /// virtual method from other classes (like those created for lambdas...). /// - private sealed partial class BaseMethodWrapperSymbol : SynthesizedMethodBaseSymbol + internal sealed partial class BaseMethodWrapperSymbol : SynthesizedMethodBaseSymbol { internal BaseMethodWrapperSymbol(NamedTypeSymbol containingType, MethodSymbol methodBeingWrapped, SyntaxNode syntax, string name) : base(containingType, methodBeingWrapped, syntax.SyntaxTree.GetReference(syntax), syntax.GetLocation(), name, DeclarationModifiers.Private, @@ -756,13 +475,6 @@ internal BaseMethodWrapperSymbol(NamedTypeSymbol containingType, MethodSymbol me AssignTypeMapAndTypeParameters(typeMap, typeParameters); } - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - AddSynthesizedAttribute(ref attributes, this.DeclaringCompilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor)); - } } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs index d8ce97fa4088f..26b8010c98131 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/IteratorAndAsyncCaptureWalker.cs @@ -284,6 +284,8 @@ public override BoundNode VisitParameter(BoundParameter node) private void TryHoistTopLevelParameter(BoundParameter node) { + Debug.Assert(!topLevelMethod.GetIsNewExtensionMember()); // extension methods were replaced with implementation methods earlier in the pipeline + if (node.ParameterSymbol.ContainingSymbol == topLevelMethod) { CaptureVariable(node.ParameterSymbol, node.Syntax); diff --git a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineTypeSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineTypeSymbol.cs index 1dbc00cef4295..3756651f2dada 100644 --- a/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/StateMachineRewriter/StateMachineTypeSymbol.cs @@ -19,7 +19,7 @@ internal abstract class StateMachineTypeSymbol : SynthesizedContainer, ISynthesi public readonly MethodSymbol KickoffMethod; public StateMachineTypeSymbol(VariableSlotAllocator slotAllocatorOpt, TypeCompilationState compilationState, MethodSymbol kickoffMethod, int kickoffMethodOrdinal) - : base(MakeName(slotAllocatorOpt, compilationState, kickoffMethod, kickoffMethodOrdinal), kickoffMethod) + : base(MakeName(slotAllocatorOpt, compilationState, kickoffMethod, kickoffMethodOrdinal), TypeMap.ConcatMethodTypeParameters(kickoffMethod, stopAt: null)) { Debug.Assert(kickoffMethod != null); this.KickoffMethod = kickoffMethod; diff --git a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs index 20589154af9bc..a796c718606bb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SynthesizedMethodBaseSymbol.cs @@ -72,22 +72,6 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) // TODO: move more functionality into here, making these symbols more lazy } - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - // do not generate attributes for members of compiler-generated types: - if (ContainingType.IsImplicitlyDeclared) - { - return; - } - - var compilation = this.DeclaringCompilation; - - AddSynthesizedAttribute(ref attributes, - compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - } - public sealed override ImmutableArray TypeParameters { get { return _typeParameters; } diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 58eb9d9a7c2d9..15748f836eb9c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -148,7 +148,7 @@ public void AddNestedType(NamedTypeSymbol nestedType) { // It is only valid to call this on a bound node factory with a module builder. Debug.Assert(ModuleBuilderOpt is { }); - ModuleBuilderOpt.AddSynthesizedDefinition(CurrentType, nestedType.GetCciAdapter()); + ModuleBuilderOpt.AddSynthesizedDefinition(nestedType.ContainingType, nestedType.GetCciAdapter()); } public void OpenNestedType(NamedTypeSymbol nestedType) @@ -479,6 +479,11 @@ public BoundBlock Block(ImmutableArray locals, ImmutableArray locals, ImmutableArray localFunctions, ImmutableArray statements) + { + return Block(locals, ImmutableArray.CastUp(localFunctions), statements); + } + + public BoundBlock Block(ImmutableArray locals, ImmutableArray localFunctions, ImmutableArray statements) { return new BoundBlock(Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null, statements) { WasCompilerGenerated = true }; } @@ -858,35 +863,40 @@ public BoundCall Call(BoundExpression? receiver, MethodSymbol method, ImmutableA return new BoundCall( Syntax, receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, args, - argumentNamesOpt: default(ImmutableArray), argumentRefKindsOpt: getArgumentRefKinds(method, useStrictArgumentRefKinds), isDelegateCall: false, expanded: false, + argumentNamesOpt: default(ImmutableArray), argumentRefKindsOpt: ArgumentRefKindsFromParameterRefKinds(method, useStrictArgumentRefKinds), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray), defaultArguments: default(BitVector), resultKind: LookupResultKind.Viable, type: method.ReturnType, hasErrors: method.OriginalDefinition is ErrorMethodSymbol) { WasCompilerGenerated = true }; + } - static ImmutableArray getArgumentRefKinds(MethodSymbol method, bool useStrictArgumentRefKinds) + public static ImmutableArray ArgumentRefKindsFromParameterRefKinds(MethodSymbol method, bool useStrictArgumentRefKinds) + { + var result = method.ParameterRefKinds; + + if (!result.IsDefaultOrEmpty && (result.Contains(RefKind.RefReadOnlyParameter) || + (useStrictArgumentRefKinds && result.Contains(RefKind.In)))) { - var result = method.ParameterRefKinds; + var builder = ArrayBuilder.GetInstance(result.Length); - if (!result.IsDefaultOrEmpty && (result.Contains(RefKind.RefReadOnlyParameter) || - (useStrictArgumentRefKinds && result.Contains(RefKind.In)))) + foreach (var refKind in result) { - var builder = ArrayBuilder.GetInstance(result.Length); - - foreach (var refKind in result) - { - builder.Add(refKind switch - { - RefKind.In or RefKind.RefReadOnlyParameter when useStrictArgumentRefKinds => RefKindExtensions.StrictIn, - RefKind.RefReadOnlyParameter => RefKind.In, - _ => refKind - }); - } - - return builder.ToImmutableAndFree(); + builder.Add(ArgumentRefKindFromParameterRefKind(refKind, useStrictArgumentRefKinds)); } - return result; + return builder.ToImmutableAndFree(); } + + return result; + } + + public static RefKind ArgumentRefKindFromParameterRefKind(RefKind refKind, bool useStrictArgumentRefKinds) + { + return refKind switch + { + RefKind.In or RefKind.RefReadOnlyParameter when useStrictArgumentRefKinds => RefKindExtensions.StrictIn, + RefKind.RefReadOnlyParameter => RefKind.In, + _ => refKind + }; } public BoundCall Call(BoundExpression? receiver, MethodSymbol method, ImmutableArray refKinds, ImmutableArray args) diff --git a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs index 7c6641d1fd3a1..8ab5f8c772445 100644 --- a/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/DirectiveParser.cs @@ -108,10 +108,14 @@ public CSharpSyntaxNode ParseDirective( break; default: - if (lexer.Options.Kind == SourceCodeKind.Script && contextualKind == SyntaxKind.ExclamationToken && hashPosition == 0 && !hash.HasTrailingTrivia) + if (contextualKind == SyntaxKind.ExclamationToken && hashPosition == 0 && !hash.HasTrailingTrivia) { result = this.ParseShebangDirective(hash, this.EatToken(SyntaxKind.ExclamationToken), isActive); } + else if (contextualKind == SyntaxKind.ColonToken && !hash.HasTrailingTrivia) + { + result = this.ParseIgnoredDirective(hash, this.EatToken(SyntaxKind.ColonToken), isActive, isFollowingToken); + } else { var id = this.EatToken(SyntaxKind.IdentifierToken, false); @@ -681,6 +685,26 @@ private DirectiveTriviaSyntax ParseShebangDirective(SyntaxToken hash, SyntaxToke return SyntaxFactory.ShebangDirectiveTrivia(hash, exclamation, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive); } + private DirectiveTriviaSyntax ParseIgnoredDirective(SyntaxToken hash, SyntaxToken colon, bool isActive, bool isFollowingToken) + { + if (!lexer.Options.FileBasedProgram) + { + colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredNeedsFileBasedProgram); + } + + if (isFollowingToken) + { + colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredFollowsToken); + } + + if (_context.SeenAnyIfDirectives) + { + colon = this.AddError(colon, ErrorCode.ERR_PPIgnoredFollowsIf); + } + + return SyntaxFactory.IgnoredDirectiveTrivia(hash, colon, this.ParseEndOfDirectiveWithOptionalPreprocessingMessage(), isActive); + } + private SyntaxToken ParseEndOfDirectiveWithOptionalPreprocessingMessage() => this.lexer.LexEndOfDirectiveWithOptionalPreprocessingMessage(); diff --git a/src/Compilers/CSharp/Portable/Parser/Directives.cs b/src/Compilers/CSharp/Portable/Parser/Directives.cs index 30d0149a20605..101314b0b7cb0 100644 --- a/src/Compilers/CSharp/Portable/Parser/Directives.cs +++ b/src/Compilers/CSharp/Portable/Parser/Directives.cs @@ -117,13 +117,15 @@ internal enum DefineState [DebuggerDisplay("{GetDebuggerDisplay(), nq}")] internal readonly struct DirectiveStack { - public static readonly DirectiveStack Empty = new DirectiveStack(ConsList.Empty); + public static readonly DirectiveStack Empty = new DirectiveStack(ConsList.Empty, seenAnyIfDirectives: false); private readonly ConsList? _directives; + private readonly bool _seenAnyIfDirectives; - private DirectiveStack(ConsList? directives) + private DirectiveStack(ConsList? directives, bool seenAnyIfDirectives) { _directives = directives; + _seenAnyIfDirectives = seenAnyIfDirectives; } public static void InterlockedInitialize(ref DirectiveStack location, DirectiveStack value) @@ -145,6 +147,8 @@ public bool IsEmpty } } + public bool SeenAnyIfDirectives => _seenAnyIfDirectives; + public DefineState IsDefined(string id) { for (var current = _directives; current != null && current.Any(); current = current.Tail) @@ -235,7 +239,7 @@ public DirectiveStack Add(Directive directive) } RoslynDebug.AssertNotNull(_directives); // If 'prevIf' isn't null, then '_directives' wasn't null. - return new DirectiveStack(CompleteIf(_directives, out _)); + return new DirectiveStack(CompleteIf(_directives, out _), seenAnyIfDirectives: _seenAnyIfDirectives); case SyntaxKind.EndRegionDirectiveTrivia: var prevRegion = GetPreviousRegion(_directives); if (prevRegion == null || !prevRegion.Any()) @@ -244,9 +248,10 @@ public DirectiveStack Add(Directive directive) } RoslynDebug.AssertNotNull(_directives); // If 'prevRegion' isn't null, then '_directives' wasn't null. - return new DirectiveStack(CompleteRegion(_directives)); // remove region directives from stack but leave everything else + return new DirectiveStack(CompleteRegion(_directives), seenAnyIfDirectives: _seenAnyIfDirectives); // remove region directives from stack but leave everything else default: - return new DirectiveStack(new ConsList(directive, _directives ?? ConsList.Empty)); + return new DirectiveStack(new ConsList(directive, _directives ?? ConsList.Empty), + seenAnyIfDirectives: _seenAnyIfDirectives || directive.Kind is SyntaxKind.IfDirectiveTrivia); } } diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 078840f3dd553..692740c6eaa52 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -84,7 +84,7 @@ internal enum TerminatorState IsEndOfFunctionPointerParameterList = 1 << 23, IsEndOfFunctionPointerParameterListErrored = 1 << 24, IsEndOfFunctionPointerCallingConvention = 1 << 25, - IsEndOfRecordOrClassOrStructOrInterfaceSignature = 1 << 26, + IsEndOfTypeSignature = 1 << 26, IsExpressionOrPatternInCaseLabelOfSwitchStatement = 1 << 27, IsPatternInSwitchExpressionArm = 1 << 28, } @@ -128,7 +128,7 @@ private bool IsTerminator() case TerminatorState.IsEndOfFunctionPointerParameterList when this.IsEndOfFunctionPointerParameterList(errored: false): case TerminatorState.IsEndOfFunctionPointerParameterListErrored when this.IsEndOfFunctionPointerParameterList(errored: true): case TerminatorState.IsEndOfFunctionPointerCallingConvention when this.IsEndOfFunctionPointerCallingConvention(): - case TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature when this.IsEndOfRecordOrClassOrStructOrInterfaceSignature(): + case TerminatorState.IsEndOfTypeSignature when this.IsEndOfTypeSignature(): return true; } } @@ -1634,24 +1634,25 @@ private bool IsPartialType() private bool IsPartialMember() { - // note(cyrusn): this could have been written like so: - // - // return - // this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword && - // this.PeekToken(1).Kind == SyntaxKind.VoidKeyword; - // - // However, we want to be lenient and allow the user to write - // 'partial' in most modifier lists. We will then provide them with - // a more specific message later in binding that they are doing - // something wrong. - // - // Some might argue that the simple check would suffice. - // However, we'd like to maintain behavior with - // previously shipped versions, and so we're keeping this code. + Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword); + + // Check for: + // partial event + if (this.PeekToken(1).Kind == SyntaxKind.EventKeyword) + { + return true; + } + + // Check for constructor: + // partial Identifier( + if (this.PeekToken(1).Kind == SyntaxKind.IdentifierToken && + this.PeekToken(2).Kind == SyntaxKind.OpenParenToken) + { + return IsFeatureEnabled(MessageID.IDS_FeaturePartialEventsAndConstructors); + } - // Here we check for: + // Check for method/property: // partial ReturnType MemberName - Debug.Assert(this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword); using var _ = this.GetDisposableResetPoint(resetOnDispose: true); this.EatToken(); // partial @@ -1692,13 +1693,13 @@ private MemberDeclarationSyntax ParseTypeDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) + private TypeDeclarationSyntax ParseMainTypeDeclaration(SyntaxList attributes, SyntaxListBuilder modifiers) { Debug.Assert(this.CurrentToken.Kind is SyntaxKind.ClassKeyword or SyntaxKind.StructKeyword or SyntaxKind.InterfaceKeyword || - this.CurrentToken.ContextualKind == SyntaxKind.RecordKeyword); + this.CurrentToken.ContextualKind is SyntaxKind.RecordKeyword or SyntaxKind.ExtensionKeyword); // "top-level" expressions and statements should never occur inside an asynchronous context Debug.Assert(!IsInAsync); @@ -1728,19 +1729,34 @@ private TypeDeclarationSyntax ParseClassOrStructOrInterfaceDeclaration(SyntaxLis keyword = ConvertToKeyword(this.EatToken()); } + bool isExtension = keyword.Kind == SyntaxKind.ExtensionKeyword; var outerSaveTerm = _termState; - _termState |= TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature; + _termState |= TerminatorState.IsEndOfTypeSignature; var saveTerm = _termState; _termState |= TerminatorState.IsPossibleAggregateClauseStartOrStop; - var name = this.ParseIdentifierToken(); + SyntaxToken? name; + if (isExtension) + { + name = null; + if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) + { + keyword = AddTrailingSkippedSyntax(keyword, this.AddError(this.EatToken(), ErrorCode.ERR_ExtensionDisallowsName)); + } + } + else + { + name = this.ParseIdentifierToken(); + } + var typeParameters = this.ParseTypeParameterList(); - var paramList = CurrentToken.Kind == SyntaxKind.OpenParenToken - ? ParseParenthesizedParameterList() : null; + // For extension declarations, there must be a parameter list + var paramList = CurrentToken.Kind == SyntaxKind.OpenParenToken || isExtension + ? ParseParenthesizedParameterList(forExtension: isExtension) : null; - var baseList = this.ParseBaseList(); + var baseList = isExtension ? null : this.ParseBaseList(); _termState = saveTerm; // Parse class body @@ -1882,7 +1898,7 @@ bool tryScanRecordStart([NotNullWhen(true)] out SyntaxToken? keyword, out Syntax } static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxFactory, SyntaxList attributes, SyntaxListBuilder modifiers, SyntaxToken keyword, SyntaxToken? recordModifier, - SyntaxToken name, TypeParameterListSyntax typeParameters, ParameterListSyntax? paramList, BaseListSyntax baseList, SyntaxListBuilder constraints, + SyntaxToken? name, TypeParameterListSyntax typeParameters, ParameterListSyntax? paramList, BaseListSyntax? baseList, SyntaxListBuilder constraints, SyntaxToken? openBrace, SyntaxListBuilder members, SyntaxToken? closeBrace, SyntaxToken semicolon) { var modifiersList = (SyntaxList)modifiers.ToList(); @@ -1891,6 +1907,7 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF switch (keyword.Kind) { case SyntaxKind.ClassKeyword: + Debug.Assert(name is not null); return syntaxFactory.ClassDeclaration( attributes, modifiersList, @@ -1906,6 +1923,7 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF semicolon); case SyntaxKind.StructKeyword: + Debug.Assert(name is not null); return syntaxFactory.StructDeclaration( attributes, modifiersList, @@ -1921,6 +1939,7 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF semicolon); case SyntaxKind.InterfaceKeyword: + Debug.Assert(name is not null); return syntaxFactory.InterfaceDeclaration( attributes, modifiersList, @@ -1939,6 +1958,7 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF // record struct ... // record ... // record class ... + Debug.Assert(name is not null); SyntaxKind declarationKind = recordModifier?.Kind == SyntaxKind.StructKeyword ? SyntaxKind.RecordStructDeclaration : SyntaxKind.RecordDeclaration; return syntaxFactory.RecordDeclaration( declarationKind, @@ -1956,6 +1976,21 @@ static TypeDeclarationSyntax constructTypeDeclaration(ContextAwareSyntax syntaxF closeBrace, semicolon); + case SyntaxKind.ExtensionKeyword: + Debug.Assert(name is null); + Debug.Assert(baseList is null); + return syntaxFactory.ExtensionDeclaration( + attributes, + modifiers.ToList(), + keyword, + typeParameters, + paramList, + constraints, + openBrace, + members, + closeBrace, + semicolon); + default: throw ExceptionUtilities.UnexpectedValue(keyword.Kind); } @@ -2053,8 +2088,8 @@ private bool IsPossibleAggregateClauseStartOrStop() private BaseListSyntax ParseBaseList() { - // We are only called from ParseClassOrStructOrInterfaceDeclaration which unilaterally sets this. - Debug.Assert((_termState & TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature) != 0); + // We are only called from ParseMainTypeDeclaration which unilaterally sets this. + Debug.Assert((_termState & TerminatorState.IsEndOfTypeSignature) != 0); var colon = this.TryEatToken(SyntaxKind.ColonToken); if (colon == null) @@ -2163,7 +2198,7 @@ private TypeParameterConstraintClauseSyntax ParseTypeParameterConstraintClause() bool haveComma; if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken - || ((_termState & TerminatorState.IsEndOfRecordOrClassOrStructOrInterfaceSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) + || ((_termState & TerminatorState.IsEndOfTypeSignature) != 0 && this.CurrentToken.Kind == SyntaxKind.SemicolonToken) || this.CurrentToken.Kind == SyntaxKind.EqualsGreaterThanToken || this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword) { @@ -2389,6 +2424,12 @@ private bool IsTypeDeclarationStart() // older code that is not using C# 9 we conditionally parse based on langversion return IsFeatureEnabled(MessageID.IDS_FeatureRecords); } + + if (IsExtensionContainerStart()) + { + return true; + } + return false; default: @@ -3064,6 +3105,11 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind bool isPossibleTypeDeclaration; this.ParseModifiers(modifiers, forAccessors: false, forTopLevelStatements: false, out isPossibleTypeDeclaration); + if (IsExtensionContainerStart()) + { + return this.ParseMainTypeDeclaration(attributes, modifiers); + } + // Check for constructor form if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && this.PeekToken(1).Kind == SyntaxKind.OpenParenToken) { @@ -3193,6 +3239,11 @@ private MemberDeclarationSyntax ParseMemberDeclarationCore(SyntaxKind parentKind } } + private bool IsExtensionContainerStart() + { + return this.CurrentToken.ContextualKind == SyntaxKind.ExtensionKeyword && IsFeatureEnabled(MessageID.IDS_FeatureExtensions); + } + // if the modifiers do not contain async or replace and the type is the identifier "async" or "replace", then // add that identifier to the modifiers and assign a new type from the identifierOrThisOpt and the // type parameter list @@ -3307,7 +3358,7 @@ private ConstructorDeclarationSyntax ParseConstructorDeclaration( _termState |= TerminatorState.IsEndOfMethodSignature; try { - var paramList = this.ParseParenthesizedParameterList(); + var paramList = this.ParseParenthesizedParameterList(forExtension: false); var initializer = this.CurrentToken.Kind == SyntaxKind.ColonToken ? this.ParseConstructorInitializer() : null; @@ -3447,7 +3498,7 @@ private bool IsEndOfTypeParameterList() private bool IsEndOfMethodSignature() => this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.OpenBraceToken; - private bool IsEndOfRecordOrClassOrStructOrInterfaceSignature() + private bool IsEndOfTypeSignature() { return this.CurrentToken.Kind is SyntaxKind.SemicolonToken or SyntaxKind.OpenBraceToken; } @@ -3473,7 +3524,7 @@ private MethodDeclarationSyntax ParseMethodDeclaration( var saveTerm = _termState; _termState |= TerminatorState.IsEndOfMethodSignature; - var paramList = this.ParseParenthesizedParameterList(); + var paramList = this.ParseParenthesizedParameterList(forExtension: false); var constraints = default(SyntaxListBuilder); if (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword) @@ -3685,7 +3736,7 @@ private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaratio type = ParseIdentifierName(); } - var paramList = this.ParseParenthesizedParameterList(); + var paramList = this.ParseParenthesizedParameterList(forExtension: false); this.ParseBlockAndExpressionBodiesWithSemicolon(out var blockBody, out var expressionBody, out var semicolon); @@ -3889,7 +3940,7 @@ private MemberDeclarationSyntax ParseOperatorDeclaration( } } - var paramList = this.ParseParenthesizedParameterList(); + var paramList = this.ParseParenthesizedParameterList(forExtension: false); switch (paramList.Parameters.Count) { @@ -4502,14 +4553,14 @@ private static SyntaxKind GetAccessorKind(SyntaxToken accessorName) }; } - internal ParameterListSyntax ParseParenthesizedParameterList() + internal ParameterListSyntax ParseParenthesizedParameterList(bool forExtension) { - if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameterList(this.CurrentNode as CSharp.Syntax.ParameterListSyntax)) + if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameterList(this.CurrentNode as CSharp.Syntax.ParameterListSyntax, allowOptionalIdentifier: forExtension)) { return (ParameterListSyntax)this.EatNode(); } - var parameters = this.ParseParameterList(out var open, out var close, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); + var parameters = this.ParseParameterList(out var open, out var close, SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken, forExtension); return _syntaxFactory.ParameterList(open, parameters, close); } @@ -4520,11 +4571,11 @@ internal BracketedParameterListSyntax ParseBracketedParameterList() return (BracketedParameterListSyntax)this.EatNode(); } - var parameters = this.ParseParameterList(out var open, out var close, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); + var parameters = this.ParseParameterList(out var open, out var close, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken, forExtension: false); return _syntaxFactory.BracketedParameterList(open, parameters, close); } - private static bool CanReuseParameterList(CSharp.Syntax.ParameterListSyntax list) + private static bool CanReuseParameterList(Syntax.ParameterListSyntax list, bool allowOptionalIdentifier) { if (list == null) { @@ -4543,7 +4594,7 @@ private static bool CanReuseParameterList(CSharp.Syntax.ParameterListSyntax list foreach (var parameter in list.Parameters) { - if (!CanReuseParameter(parameter)) + if (!CanReuseParameter(parameter, allowOptionalIdentifier)) { return false; } @@ -4552,7 +4603,7 @@ private static bool CanReuseParameterList(CSharp.Syntax.ParameterListSyntax list return true; } - private static bool CanReuseBracketedParameterList(CSharp.Syntax.BracketedParameterListSyntax list) + private static bool CanReuseBracketedParameterList(Syntax.BracketedParameterListSyntax list) { if (list == null) { @@ -4571,7 +4622,7 @@ private static bool CanReuseBracketedParameterList(CSharp.Syntax.BracketedParame foreach (var parameter in list.Parameters) { - if (!CanReuseParameter(parameter)) + if (!CanReuseParameter(parameter, allowOptionalIdentifier: false)) { return false; } @@ -4584,22 +4635,28 @@ private SeparatedSyntaxList ParseParameterList( out SyntaxToken open, out SyntaxToken close, SyntaxKind openKind, - SyntaxKind closeKind) + SyntaxKind closeKind, + bool forExtension) { open = this.EatToken(openKind); var saveTerm = _termState; _termState |= TerminatorState.IsEndOfParameterList; + Func parseElement = forExtension + ? static @this => @this.ParseParameter(allowOptionalIdentifier: true) + : static @this => @this.ParseParameter(allowOptionalIdentifier: false); + var parameters = ParseCommaSeparatedSyntaxList( ref open, closeKind, static @this => @this.IsPossibleParameter(), - static @this => @this.ParseParameter(), + parseElement, skipBadParameterListTokens, allowTrailingSeparator: false, - requireOneElement: false, + requireOneElement: forExtension, // For extension declarations, we require at least one receiver parameter allowSemicolonAsSeparator: false); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider suppressing parsing diagnostics on extension parameters beyond the first one _termState = saveTerm; close = this.EatToken(closeKind); @@ -4639,7 +4696,7 @@ private bool IsPossibleParameter() } } - private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter) + private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter, bool allowOptionalIdentifier) { if (parameter == null) { @@ -4670,14 +4727,22 @@ private static bool CanReuseParameter(CSharp.Syntax.ParameterSyntax parameter) } } + // We can only reuse parameters without identifiers (found in extension declarations) in context that allow optional identifiers. + // The reverse is fine though. Normal parameters (from non extensions) can be re-used into an extension declaration + // as all normal parameters are legal extension parameters. + if (!allowOptionalIdentifier && parameter.Identifier.Kind() == SyntaxKind.None) + { + return false; + } + return true; } #nullable enable - private ParameterSyntax ParseParameter() + private ParameterSyntax ParseParameter(bool allowOptionalIdentifier) { - if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameter(this.CurrentNode as CSharp.Syntax.ParameterSyntax)) + if (this.IsIncrementalAndFactoryContextMatches && CanReuseParameter(this.CurrentNode as Syntax.ParameterSyntax, allowOptionalIdentifier)) { return (ParameterSyntax)this.EatNode(); } @@ -4696,19 +4761,22 @@ private ParameterSyntax ParseParameter() } var type = this.ParseType(mode: ParseTypeMode.Parameter); - SyntaxToken identifier; + SyntaxToken? identifier; if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken && IsCurrentTokenWhereOfConstraintClause()) { - identifier = this.AddError(CreateMissingIdentifierToken(), ErrorCode.ERR_IdentifierExpected); + identifier = allowOptionalIdentifier ? null : this.AddError(CreateMissingIdentifierToken(), ErrorCode.ERR_IdentifierExpected); } else { - identifier = this.ParseIdentifierToken(); + // The receiver parameter on an extension declaration may have a name or not + identifier = allowOptionalIdentifier && this.CurrentToken.Kind != SyntaxKind.IdentifierToken + ? null + : this.ParseIdentifierToken(); } // When the user type "int goo[]", give them a useful error - if (this.CurrentToken.Kind is SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind is SyntaxKind.CloseBracketToken) + if (identifier is not null && this.CurrentToken.Kind is SyntaxKind.OpenBracketToken && this.PeekToken(1).Kind is SyntaxKind.CloseBracketToken) { identifier = AddTrailingSkippedSyntax(identifier, SyntaxList.List( this.AddError(this.EatToken(), ErrorCode.ERR_BadArraySyntax), @@ -5443,7 +5511,7 @@ private bool IsLocalFunctionAfterIdentifier() using var _ = this.GetDisposableResetPoint(resetOnDispose: true); var typeParameterListOpt = this.ParseTypeParameterList(); - var paramList = ParseParenthesizedParameterList(); + var paramList = ParseParenthesizedParameterList(forExtension: false); if (!paramList.IsMissing && (this.CurrentToken.Kind is SyntaxKind.OpenBraceToken or SyntaxKind.EqualsGreaterThanToken || @@ -5503,7 +5571,7 @@ private DelegateDeclarationSyntax ParseDelegateDeclaration(SyntaxList); if (this.CurrentToken.ContextualKind == SyntaxKind.WhereKeyword) @@ -5681,7 +5749,7 @@ private bool IsTrueIdentifier() { if (this.CurrentToken.Kind == SyntaxKind.IdentifierToken) { - if (!IsCurrentTokenPartialKeywordOfPartialMethodOrType() && + if (!IsCurrentTokenPartialKeywordOfPartialMemberOrType() && !IsCurrentTokenQueryKeywordInQuery() && !IsCurrentTokenWhereOfConstraintClause()) { @@ -5728,7 +5796,7 @@ private SyntaxToken ParseIdentifierToken(ErrorCode code = ErrorCode.ERR_Identifi // show the correct parameter help in this case. So, when we see "partial" we check if it's being used // as an identifier or as a contextual keyword. If it's the latter then we bail out. See // Bug: vswhidbey/542125 - if (IsCurrentTokenPartialKeywordOfPartialMethodOrType() || IsCurrentTokenQueryKeywordInQuery()) + if (IsCurrentTokenPartialKeywordOfPartialMemberOrType() || IsCurrentTokenQueryKeywordInQuery()) { var result = CreateMissingIdentifierToken(); result = this.AddError(result, ErrorCode.ERR_InvalidExprTerm, this.CurrentToken.Text); @@ -5755,7 +5823,7 @@ private bool IsCurrentTokenQueryKeywordInQuery() return this.IsInQuery && this.IsCurrentTokenQueryContextualKeyword; } - private bool IsCurrentTokenPartialKeywordOfPartialMethodOrType() + private bool IsCurrentTokenPartialKeywordOfPartialMemberOrType() { if (this.CurrentToken.ContextualKind == SyntaxKind.PartialKeyword) { @@ -8430,7 +8498,7 @@ private bool IsPossibleMethodDeclarationFollowingNullableType() var saveTerm = _termState; _termState |= TerminatorState.IsEndOfMethodSignature; - var paramList = this.ParseParenthesizedParameterList(); + var paramList = this.ParseParenthesizedParameterList(forExtension: false); _termState = saveTerm; var separatedParameters = paramList.Parameters.GetWithSeparators(); @@ -10541,7 +10609,7 @@ private LocalFunctionStatementSyntax TryParseLocalFunctionStatementBody( TypeParameterListSyntax typeParameterListOpt = this.ParseTypeParameterList(); // "await f()" still makes sense, so don't force accept a local function if there's a type parameter list. - ParameterListSyntax paramList = this.ParseParenthesizedParameterList(); + ParameterListSyntax paramList = this.ParseParenthesizedParameterList(forExtension: false); // "await x()" is ambiguous (see note at start of this method), but we assume "await x(await y)" is meant to be a function if it's in a non-async context. if (!forceLocalFunc) { @@ -11115,7 +11183,7 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax unaryOrPrimar // precedence level. Examples include binary operator, assignment operators, range operators `..`, as // well as `switch` and `with` clauses. - var (operatorTokenKind, operatorExpressionKind) = getOperatorTokenAndExpressionKind(); + var (operatorTokenKind, operatorExpressionKind) = GetExpressionOperatorTokenKindAndExpressionKind(); if (operatorTokenKind == SyntaxKind.None) return null; @@ -11133,7 +11201,7 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax unaryOrPrimar return null; // Now consume the operator (including consuming multiple tokens in the case of merged operator tokens) - var operatorToken = eatOperatorToken(operatorTokenKind); + var operatorToken = EatExpressionOperatorToken(operatorTokenKind); if (newPrecedence > GetPrecedence(leftOperand.Kind)) { @@ -11180,25 +11248,7 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax unaryOrPrimar } if (IsExpectedAssignmentOperator(operatorToken.Kind)) - { - ExpressionSyntax rhs; - - if (operatorExpressionKind == SyntaxKind.SimpleAssignmentExpression && CurrentToken.Kind == SyntaxKind.RefKeyword && - // check for lambda expression with explicit ref return type: `ref int () => { ... }` - !this.IsPossibleLambdaExpression(newPrecedence)) - { - rhs = _syntaxFactory.RefExpression( - this.EatToken(), - this.ParseExpressionCore()); - } - else - { - rhs = this.ParseSubExpression(newPrecedence); - } - - return _syntaxFactory.AssignmentExpression( - operatorExpressionKind, leftOperand, operatorToken, rhs); - } + return ParseAssignmentExpression(operatorExpressionKind, leftOperand, operatorToken); if (IsExpectedBinaryOperator(operatorToken.Kind)) return _syntaxFactory.BinaryExpression(operatorExpressionKind, leftOperand, operatorToken, this.ParseSubExpression(newPrecedence)); @@ -11206,107 +11256,6 @@ private ExpressionSyntax ParseExpressionContinued(ExpressionSyntax unaryOrPrimar throw ExceptionUtilities.Unreachable(); } - (SyntaxKind operatorTokenKind, SyntaxKind operatorExpressionKind) getOperatorTokenAndExpressionKind() - { - // If the set of expression continuations is updated here, please review ParseStatementAttributeDeclarations - // to see if it may need a similar look-ahead check to determine if something is a collection expression versus - // an attribute. - - var token1 = this.CurrentToken; - var token1Kind = token1.ContextualKind; - - // Merge two consecutive dots into a DotDotToken - if (IsAtDotDotToken()) - return (SyntaxKind.DotDotToken, SyntaxKind.RangeExpression); - - // check for >>, >>=, >>> or >>>= - // - // In all those cases, update token1Kind to be the merged token kind. It will then be handled by the code below. - if (token1Kind == SyntaxKind.GreaterThanToken - && this.PeekToken(1) is { Kind: SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqualsToken } token2 - && NoTriviaBetween(token1, token2)) // check to see if they really are adjacent - { - if (token2.Kind == SyntaxKind.GreaterThanToken) - { - if (this.PeekToken(2) is { Kind: SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqualsToken } token3 - && NoTriviaBetween(token2, token3)) // check to see if they really are adjacent - { - // >>> or >>>= - token1Kind = token3.Kind == SyntaxKind.GreaterThanToken - ? SyntaxKind.GreaterThanGreaterThanGreaterThanToken - : SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; - } - else - { - // >> - token1Kind = SyntaxKind.GreaterThanGreaterThanToken; - } - } - else - { - // >>= - token1Kind = SyntaxKind.GreaterThanGreaterThanEqualsToken; - } - } - - if (IsExpectedBinaryOperator(token1Kind)) - return (token1Kind, SyntaxFacts.GetBinaryExpression(token1Kind)); - - if (IsExpectedAssignmentOperator(token1Kind)) - return (token1Kind, SyntaxFacts.GetAssignmentExpression(token1Kind)); - - if (token1Kind == SyntaxKind.SwitchKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken) - return (token1Kind, SyntaxKind.SwitchExpression); - - if (token1Kind == SyntaxKind.WithKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken) - return (token1Kind, SyntaxKind.WithExpression); - - // Something that doesn't expand the current expression we're looking at. Bail out and see if we - // can end with a conditional expression. - return (SyntaxKind.None, SyntaxKind.None); - } - - SyntaxToken eatOperatorToken(SyntaxKind operatorTokenKind) - { - // Combine tokens into a single token if needed - - if (operatorTokenKind is SyntaxKind.DotDotToken) - return EatDotDotToken(); - - if (operatorTokenKind is SyntaxKind.GreaterThanGreaterThanToken or SyntaxKind.GreaterThanGreaterThanEqualsToken) - { - // >> and >>= - // Two tokens need to be consumed here. - - var token1 = EatToken(); - var token2 = EatToken(); - - return SyntaxFactory.Token( - token1.GetLeadingTrivia(), - operatorTokenKind, - token2.GetTrailingTrivia()); - } - else if (operatorTokenKind is SyntaxKind.GreaterThanGreaterThanGreaterThanToken or SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) - { - // >>> and >>>= - // Three tokens need to be consumed here. - - var token1 = EatToken(); - _ = EatToken(); - var token3 = EatToken(); - - return SyntaxFactory.Token( - token1.GetLeadingTrivia(), - operatorTokenKind, - token3.GetTrailingTrivia()); - } - else - { - // Normal operator. Eat as a single token, converting contextual words cases (like 'with') to a keyword. - return this.EatContextualToken(operatorTokenKind); - } - } - ConditionalExpressionSyntax consumeConditionalExpression(ExpressionSyntax leftOperand) { // Complex ambiguity with `?` and collection-expressions. Specifically: b?[c]:d @@ -11401,6 +11350,131 @@ static bool containsTernaryCollectionToReinterpret(ExpressionSyntax expression) } } + private (SyntaxKind operatorTokenKind, SyntaxKind operatorExpressionKind) GetExpressionOperatorTokenKindAndExpressionKind() + { + // If the set of expression continuations is updated here, please review ParseStatementAttributeDeclarations + // to see if it may need a similar look-ahead check to determine if something is a collection expression versus + // an attribute. + + var token1 = this.CurrentToken; + var token1Kind = token1.ContextualKind; + + // Merge two consecutive dots into a DotDotToken + if (IsAtDotDotToken()) + return (SyntaxKind.DotDotToken, SyntaxKind.RangeExpression); + + // check for >>, >>=, >>> or >>>= + // + // In all those cases, update token1Kind to be the merged token kind. It will then be handled by the code below. + if (token1Kind == SyntaxKind.GreaterThanToken + && this.PeekToken(1) is { Kind: SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqualsToken } token2 + && NoTriviaBetween(token1, token2)) // check to see if they really are adjacent + { + if (token2.Kind == SyntaxKind.GreaterThanToken) + { + if (this.PeekToken(2) is { Kind: SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqualsToken } token3 + && NoTriviaBetween(token2, token3)) // check to see if they really are adjacent + { + // >>> or >>>= + token1Kind = token3.Kind == SyntaxKind.GreaterThanToken + ? SyntaxKind.GreaterThanGreaterThanGreaterThanToken + : SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; + } + else + { + // >> + token1Kind = SyntaxKind.GreaterThanGreaterThanToken; + } + } + else + { + // >>= + token1Kind = SyntaxKind.GreaterThanGreaterThanEqualsToken; + } + } + + if (IsExpectedBinaryOperator(token1Kind)) + return (token1Kind, SyntaxFacts.GetBinaryExpression(token1Kind)); + + if (IsExpectedAssignmentOperator(token1Kind)) + return (token1Kind, SyntaxFacts.GetAssignmentExpression(token1Kind)); + + if (token1Kind == SyntaxKind.SwitchKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken) + return (token1Kind, SyntaxKind.SwitchExpression); + + if (token1Kind == SyntaxKind.WithKeyword && this.PeekToken(1).Kind == SyntaxKind.OpenBraceToken) + return (token1Kind, SyntaxKind.WithExpression); + + // Something that doesn't expand the current expression we're looking at. Bail out and see if we + // can end with a conditional expression. + return (SyntaxKind.None, SyntaxKind.None); + } + + private SyntaxToken EatExpressionOperatorToken(SyntaxKind operatorTokenKind) + { + // Combine tokens into a single token if needed + + if (operatorTokenKind is SyntaxKind.DotDotToken) + return EatDotDotToken(); + + if (operatorTokenKind is SyntaxKind.GreaterThanGreaterThanToken or SyntaxKind.GreaterThanGreaterThanEqualsToken) + { + // >> and >>= + // Two tokens need to be consumed here. + + var token1 = EatToken(); + var token2 = EatToken(); + + return SyntaxFactory.Token( + token1.GetLeadingTrivia(), + operatorTokenKind, + token2.GetTrailingTrivia()); + } + else if (operatorTokenKind is SyntaxKind.GreaterThanGreaterThanGreaterThanToken or SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) + { + // >>> and >>>= + // Three tokens need to be consumed here. + + var token1 = EatToken(); + _ = EatToken(); + var token3 = EatToken(); + + return SyntaxFactory.Token( + token1.GetLeadingTrivia(), + operatorTokenKind, + token3.GetTrailingTrivia()); + } + else + { + // Normal operator. Eat as a single token, converting contextual words cases (like 'with') to a keyword. + return this.EatContextualToken(operatorTokenKind); + } + } + + private AssignmentExpressionSyntax ParseAssignmentExpression(SyntaxKind operatorExpressionKind, ExpressionSyntax leftOperand, SyntaxToken operatorToken) + { + Debug.Assert(IsExpectedAssignmentOperator(operatorToken.Kind)); + Debug.Assert(GetPrecedence(operatorExpressionKind) == Precedence.Assignment); + + ExpressionSyntax rhs; + + if (operatorExpressionKind == SyntaxKind.SimpleAssignmentExpression && CurrentToken.Kind == SyntaxKind.RefKeyword && + // check for lambda expression with explicit ref return type: `ref int () => { ... }` + !this.IsPossibleLambdaExpression(Precedence.Assignment)) + { + rhs = _syntaxFactory.RefExpression( + this.EatToken(), + this.ParseExpressionCore()); + } + else + { + rhs = this.ParseSubExpression(Precedence.Assignment); + } + + return _syntaxFactory.AssignmentExpression( + operatorExpressionKind, leftOperand, operatorToken, rhs); + } + /// Check if we're currently at a .. sequence that can then be parsed out as a . public bool IsAtDotDotToken() => IsAtDotDotToken(this.CurrentToken, this.PeekToken(1)); @@ -11717,12 +11791,9 @@ ExpressionSyntax parsePostFixExpression(ExpressionSyntax expr) continue; case SyntaxKind.QuestionToken: - if (CanStartConsequenceExpression()) + if (TryParseConditionalAccessExpression(expr, out var conditionalAccess)) { - expr = _syntaxFactory.ConditionalAccessExpression( - expr, - this.EatToken(), - ParseConsequenceSyntax()); + expr = conditionalAccess; continue; } @@ -11819,20 +11890,75 @@ private bool IsPossibleAnonymousMethodExpression() this.PeekToken(tokenIndex + 1).Kind != SyntaxKind.AsteriskToken; } - private bool CanStartConsequenceExpression() +#nullable enable + + /// + /// Called when we could be at a ? that could start a or + /// a . Returns if this succeeds at parsing the + /// former, and if we're not at the start of a conditional access expression. + /// + private bool TryParseConditionalAccessExpression( + ExpressionSyntax primaryExpression, + [NotNullWhen(true)] out ConditionalAccessExpressionSyntax? conditionalAccessExpression) { - Debug.Assert(this.CurrentToken.Kind == SyntaxKind.QuestionToken); - var nextToken = this.PeekToken(1); - var nextTokenKind = nextToken.Kind; + // From + // https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/expressions.md#1288-null-conditional-member-access + // https://github.com/dotnet/csharpstandard/blob/standard-v7/standard/expressions.md#12812-null-conditional-element-access - // ?. is always the start of of a consequence expression. + // null_conditional_member_access + // : primary_expression '?' '.' identifier type_argument_list? dependent_access* + // ; // - // ?.. is a ternary with a range expression as it's 'whenTrue' clause. - if (nextTokenKind == SyntaxKind.DotToken && !IsAtDotDotToken(nextToken, this.PeekToken(2))) - return true; + // null_conditional_element_access + // : primary_no_array_creation_expression '?' '[' argument_list ']' dependent_access* + // ; + // + // dependent_access + // : '.' identifier type_argument_list? // member access + // | '[' argument_list ']' // element access + // | '(' argument_list ? ')' // invocation + // ; + + // We get in here after parsing out the initial primary expression and seeing a `?` follow. + + var (questionToken, bindingExpression) = tryEatQuestionAndBindingExpression(); + if (questionToken is null || bindingExpression is null) + { + conditionalAccessExpression = null; + return false; + } + + conditionalAccessExpression = _syntaxFactory.ConditionalAccessExpression( + primaryExpression, questionToken, parseWhenNotNull(bindingExpression)); + return true; - if (nextTokenKind == SyntaxKind.OpenBracketToken) + (SyntaxToken? questionToken, ExpressionSyntax? bindingExpression) tryEatQuestionAndBindingExpression() { + if (this.CurrentToken.Kind == SyntaxKind.QuestionToken) + { + var nextToken = this.PeekToken(1); + var nextTokenKind = nextToken.Kind; + + // ?. is always the start of of a consequence expression. + // + // ?.. is a ternary with a range expression as it's 'whenTrue' clause. + if (nextTokenKind == SyntaxKind.DotToken && !IsAtDotDotToken(nextToken, this.PeekToken(2))) + return (questionToken: EatToken(), _syntaxFactory.MemberBindingExpression(this.EatToken(), this.ParseSimpleName(NameOptions.InExpression))); + + if (isStartOfElementBindingExpression(nextTokenKind)) + return (questionToken: EatToken(), _syntaxFactory.ElementBindingExpression(this.ParseBracketedArgumentList())); + } + + // Anything else is either not a `?` at all, or is just a `?` that starts a conditional expression (not + // a conditional access expression). + return default; + } + + bool isStartOfElementBindingExpression(SyntaxKind nextTokenKind) + { + if (nextTokenKind != SyntaxKind.OpenBracketToken) + return false; + // could simply be `x?[0]`, or could be `x ? [0] : [1]`. // Caller only wants us to parse ?[ how it was originally parsed before collection expressions. @@ -11850,71 +11976,66 @@ private bool CanStartConsequenceExpression() return this.CurrentToken.Kind != SyntaxKind.ColonToken; } - // Anything else is just a normal ? and indicates the start of a ternary expression. - return false; - } - - internal ExpressionSyntax ParseConsequenceSyntax() - { - Debug.Assert(this.CurrentToken.Kind is SyntaxKind.DotToken or SyntaxKind.OpenBracketToken); - ExpressionSyntax expr = this.CurrentToken.Kind switch - { - SyntaxKind.DotToken => _syntaxFactory.MemberBindingExpression(this.EatToken(), this.ParseSimpleName(NameOptions.InExpression)), - SyntaxKind.OpenBracketToken => _syntaxFactory.ElementBindingExpression(this.ParseBracketedArgumentList()), - _ => throw ExceptionUtilities.Unreachable(), - }; - - while (true) + ExpressionSyntax parseWhenNotNull(ExpressionSyntax expr) { - // Nullable suppression operators should only be consumed by a conditional access - // if there are further conditional operations performed after the suppression - if (isOptionalExclamationsFollowedByConditionalOperation()) + while (true) { + // We should consume suppression '!'s which are in the middle of the 'whenNotNull', but not at the end. + // For example, 'a?.b!.c' should be a cond-access whose RHS is '.b!.c', + // while 'a?.b!' should be a suppression-expr containing a cond-access 'a?.b'. + using var beforeSuppressionsResetPoint = GetDisposableResetPoint(resetOnDispose: false); + var expressionBeforeSuppressions = expr; + while (this.CurrentToken.Kind == SyntaxKind.ExclamationToken) expr = _syntaxFactory.PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, expr, EatToken()); - } - switch (this.CurrentToken.Kind) - { - case SyntaxKind.OpenParenToken: - expr = _syntaxFactory.InvocationExpression(expr, this.ParseParenthesizedArgumentList()); + // Expand to consume the `dependent_access*` continuations. + if (tryParseDependentAccess(expr) is ExpressionSyntax expandedExpression) + { + expr = expandedExpression; continue; + } - case SyntaxKind.OpenBracketToken: - expr = _syntaxFactory.ElementAccessExpression(expr, this.ParseBracketedArgumentList()); - continue; + // A trailing cond-access or assignment is effectively the "end" of the current cond-access node. + // Due to right-associativity, everything that follows will be included in the child node. + // e.g. 'a?.b?.c' parses as '(a) ? (.b?.c)' + // e.g. 'a?.b = c?.d = e?.f' parses as 'a?.b = (c?.d = e?.f)' - case SyntaxKind.DotToken: - expr = _syntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, this.EatToken(), this.ParseSimpleName(NameOptions.InExpression)); - continue; + // a?.b?.c + // a?.b!?.c + if (TryParseConditionalAccessExpression(expr, out var conditionalAccess)) + return conditionalAccess; - case SyntaxKind.QuestionToken: - return !CanStartConsequenceExpression() - ? expr - : _syntaxFactory.ConditionalAccessExpression( - expr, - operatorToken: this.EatToken(), - ParseConsequenceSyntax()); + // a?.b = c + // a?.b! = c + var (operatorTokenKind, operatorExpressionKind) = GetExpressionOperatorTokenKindAndExpressionKind(); + if (IsExpectedAssignmentOperator(operatorTokenKind)) + { + return ParseAssignmentExpression(operatorExpressionKind, expr, EatExpressionOperatorToken(operatorTokenKind)); + } - default: - return expr; + // End of the cond-access. + // Any '!' suppressions which followed this are a parent of the cond-access, not a child of it. + beforeSuppressionsResetPoint.Reset(); + return expressionBeforeSuppressions; } } - bool isOptionalExclamationsFollowedByConditionalOperation() - { - var index = 0; - while (this.PeekToken(index).Kind == SyntaxKind.ExclamationToken) - index++; - - return this.PeekToken(index).Kind - is SyntaxKind.OpenParenToken - or SyntaxKind.OpenBracketToken - or SyntaxKind.DotToken - or SyntaxKind.QuestionToken; - } + ExpressionSyntax? tryParseDependentAccess(ExpressionSyntax expr) + => this.CurrentToken.Kind switch + { + SyntaxKind.OpenParenToken + => _syntaxFactory.InvocationExpression(expr, this.ParseParenthesizedArgumentList()), + SyntaxKind.OpenBracketToken + => _syntaxFactory.ElementAccessExpression(expr, this.ParseBracketedArgumentList()), + SyntaxKind.DotToken + => _syntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expr, this.EatToken(), this.ParseSimpleName(NameOptions.InExpression)), + _ => null, + }; } +#nullable disable + internal ArgumentListSyntax ParseParenthesizedArgumentList() { if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.ArgumentList) @@ -13187,7 +13308,7 @@ AnonymousMethodExpressionSyntax parseAnonymousMethodExpressionWorker() ParameterListSyntax parameterList = null; if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken) { - parameterList = this.ParseParenthesizedParameterList(); + parameterList = this.ParseParenthesizedParameterList(forExtension: false); } // In mismatched braces cases (missing a }) it is possible for delegate declarations to be @@ -13262,7 +13383,7 @@ private bool IsAnonymousFunctionAsyncModifier() return true; case var kind: return IsPredefinedType(kind); - }; + } } /// diff --git a/src/Compilers/CSharp/Portable/Parser/Lexer.cs b/src/Compilers/CSharp/Portable/Parser/Lexer.cs index 22fbe4e66345a..719961f828e30 100644 --- a/src/Compilers/CSharp/Portable/Parser/Lexer.cs +++ b/src/Compilers/CSharp/Portable/Parser/Lexer.cs @@ -786,7 +786,7 @@ private bool TryScanInterpolatedString(ref TokenInfo info) if (TextWindow.PeekChar(1) is '$' or '@' or '"') { // $$ - definitely starts a raw interpolated string. - // $@ - definitely starts an interplated string. + // $@ - definitely starts an interpolated string. // // This will also match for $@@. This is an error case when the user thinks they can mix verbatim and raw // interpolations together. This will be properly handled in ScanInterpolatedStringLiteral @@ -2600,6 +2600,11 @@ private bool ScanDirectiveToken(ref TokenInfo info) info.Kind = SyntaxKind.MinusToken; break; + case ':': + TextWindow.AdvanceChar(); + info.Kind = SyntaxKind.ColonToken; + break; + case '!': TextWindow.AdvanceChar(); if (TextWindow.PeekChar() == '=') diff --git a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt index 3c53f4758c225..fd4173df29920 100644 --- a/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt @@ -1,2 +1,60 @@ abstract Microsoft.CodeAnalysis.CSharp.InterceptableLocation.Equals(Microsoft.CodeAnalysis.CSharp.InterceptableLocation? other) -> bool +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddAttributeLists(params Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddConstraintClauses(params Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddMembers(params Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddModifiers(params Microsoft.CodeAnalysis.SyntaxToken[]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddParameterListParameters(params Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddTypeParameterListParameters(params Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterSyntax![]! items) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxToken openBraceToken, Microsoft.CodeAnalysis.SyntaxList members, Microsoft.CodeAnalysis.SyntaxToken closeBraceToken, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList attributeLists) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithCloseBraceToken(Microsoft.CodeAnalysis.SyntaxToken closeBraceToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithConstraintClauses(Microsoft.CodeAnalysis.SyntaxList constraintClauses) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithKeyword(Microsoft.CodeAnalysis.SyntaxToken keyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithMembers(Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList modifiers) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithOpenBraceToken(Microsoft.CodeAnalysis.SyntaxToken openBraceToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithTypeParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionDeclaration = 9079 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionKeyword = 8451 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode? +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> TResult? +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AttributeLists.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.BaseList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.BaseListSyntax? +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.CloseBraceToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.ConstraintClauses.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Identifier.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Keyword.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Members.get -> Microsoft.CodeAnalysis.SyntaxList +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Modifiers.get -> Microsoft.CodeAnalysis.SyntaxTokenList +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.OpenBraceToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.ParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.SemicolonToken.get -> Microsoft.CodeAnalysis.SyntaxToken +override Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.TypeParameterList.get -> Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration() -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxList members) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration(Microsoft.CodeAnalysis.SyntaxList attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken keyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax? typeParameterList, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax? parameterList, Microsoft.CodeAnalysis.SyntaxList constraintClauses, Microsoft.CodeAnalysis.SyntaxToken openBraceToken, Microsoft.CodeAnalysis.SyntaxList members, Microsoft.CodeAnalysis.SyntaxToken closeBraceToken, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax! node) -> TResult? +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.ColonToken.get -> Microsoft.CodeAnalysis.SyntaxToken +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken colonToken, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken colonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken hashToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +[RSEXPERIMENTAL005]Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithIsActive(bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +Microsoft.CodeAnalysis.CSharp.SyntaxKind.IgnoredDirectiveTrivia = 9080 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind +override Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> Microsoft.CodeAnalysis.SyntaxNode? +[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> void +[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor! visitor) -> TResult? +[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.EndOfDirectiveToken.get -> Microsoft.CodeAnalysis.SyntaxToken +[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.HashToken.get -> Microsoft.CodeAnalysis.SyntaxToken +[RSEXPERIMENTAL005]override Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.IsActive.get -> bool +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken hashToken, Microsoft.CodeAnalysis.SyntaxToken colonToken, Microsoft.CodeAnalysis.SyntaxToken endOfDirectiveToken, bool isActive) -> Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> void +virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax! node) -> TResult? diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 0fa99aec99b2f..60211bf6c0b21 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -470,9 +470,10 @@ public override void VisitMethod(IMethodSymbol symbol) // Note: we are using the metadata name also in the case that // symbol.containingType is null (which should never be the case here) or is an // anonymous type (which 'does not have a name'). - var name = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType - ? symbol.Name - : symbol.ContainingType.Name; + bool useConstructorName = Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) + || symbol.ContainingType == null || symbol.ContainingType.IsAnonymousType || symbol.ContainingType.IsExtension; + + var name = useConstructorName ? symbol.Name : symbol.ContainingType!.Name; var partKind = GetPartKindForConstructorOrDestructor(symbol); @@ -752,7 +753,7 @@ void addUserDefinedConversionName(IMethodSymbol symbol, SyntaxKind conversionKin private static SymbolDisplayPartKind GetPartKindForConstructorOrDestructor(IMethodSymbol symbol) { // In the case that symbol.containingType is null (which should never be the case here) we will fallback to the MethodName symbol part - if (symbol.ContainingType is null) + if (symbol.ContainingType is null || symbol.ContainingType.IsExtension) { return SymbolDisplayPartKind.MethodName; } @@ -810,42 +811,7 @@ public override void VisitParameter(IParameterSymbol symbol) if (includeType) { - if (Format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) - { - // Add 'scoped' unless the parameter is an out parameter or - // 'this' since those cases are implicitly scoped. - if (symbol.ScopedKind == ScopedKind.ScopedRef && - symbol.RefKind != RefKind.Out && - !symbol.IsThis) - { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); - } - - AddParameterRefKind(symbol.RefKind); - } - - AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); - - if (Format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) - { - if (symbol.IsParams) - { - AddKeyword(SyntaxKind.ParamsKeyword); - AddSpace(); - } - - if (symbol.ScopedKind == ScopedKind.ScopedValue && - symbol.RefKind == RefKind.None && - !(symbol.IsParams && symbol.Type is { IsRefLikeType: true } or ITypeParameterSymbol { AllowsRefLikeType: true })) - { - AddKeyword(SyntaxKind.ScopedKeyword); - AddSpace(); - } - } - - symbol.Type.Accept(this.NotFirstVisitor); - AddCustomModifiersIfNeeded(symbol.CustomModifiers, leadingSpace: true, trailingSpace: false); + AddParameterModifiersAndType(symbol); } if (includeName) @@ -876,6 +842,46 @@ public override void VisitParameter(IParameterSymbol symbol) } } + private void AddParameterModifiersAndType(IParameterSymbol symbol) + { + if (Format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) + { + // Add 'scoped' unless the parameter is an out parameter or + // 'this' since those cases are implicitly scoped. + if (symbol.ScopedKind == ScopedKind.ScopedRef && + symbol.RefKind != RefKind.Out && + !symbol.IsThis) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } + + AddParameterRefKind(symbol.RefKind); + } + + AddCustomModifiersIfNeeded(symbol.RefCustomModifiers, leadingSpace: false, trailingSpace: true); + + if (Format.ParameterOptions.IncludesOption(SymbolDisplayParameterOptions.IncludeModifiers)) + { + if (symbol.IsParams) + { + AddKeyword(SyntaxKind.ParamsKeyword); + AddSpace(); + } + + if (symbol.ScopedKind == ScopedKind.ScopedValue && + symbol.RefKind == RefKind.None && + !(symbol.IsParams && symbol.Type is { IsRefLikeType: true } or ITypeParameterSymbol { AllowsRefLikeType: true })) + { + AddKeyword(SyntaxKind.ScopedKeyword); + AddSpace(); + } + } + + symbol.Type.Accept(this.NotFirstVisitor); + AddCustomModifiersIfNeeded(symbol.CustomModifiers, leadingSpace: true, trailingSpace: false); + } + private static bool CanAddConstant(ITypeSymbol type, object? value) { if (type.TypeKind == TypeKind.Enum) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index 71fac01c8ba6c..02a0b61258d34 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -332,54 +332,69 @@ private void AddNameAndTypeArgumentsOrParameters(INamedTypeSymbol symbol) return; } - string? symbolName = null; - - // It would be nice to handle VB NoPia symbols too, but it's not worth the effort. - NamedTypeSymbol? underlyingTypeSymbol = (symbol as Symbols.PublicModel.NamedTypeSymbol)?.UnderlyingNamedTypeSymbol; - var illegalGenericInstantiationSymbol = underlyingTypeSymbol as NoPiaIllegalGenericInstantiationSymbol; - - if (illegalGenericInstantiationSymbol is not null) + if (symbol.IsExtension) { - symbol = illegalGenericInstantiationSymbol.UnderlyingSymbol.GetPublicSymbol(); + if (Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames)) + { + var extensionIdentifier = underlyingTypeSymbol!.ExtensionName; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : use public API once it's available + Builder.Add(CreatePart(SymbolDisplayPartKind.ClassName, symbol, extensionIdentifier)); + } + else + { + AddKeyword(SyntaxKind.ExtensionKeyword); + } } else { - var ambiguousCanonicalTypeSymbol = underlyingTypeSymbol as NoPiaAmbiguousCanonicalTypeSymbol; - if (ambiguousCanonicalTypeSymbol is not null) + string? symbolName = null; + + // It would be nice to handle VB NoPia symbols too, but it's not worth the effort. + + var illegalGenericInstantiationSymbol = underlyingTypeSymbol as NoPiaIllegalGenericInstantiationSymbol; + + if (illegalGenericInstantiationSymbol is not null) { - symbol = ambiguousCanonicalTypeSymbol.FirstCandidate.GetPublicSymbol(); + symbol = illegalGenericInstantiationSymbol.UnderlyingSymbol.GetPublicSymbol(); } else { - var missingCanonicalTypeSymbol = underlyingTypeSymbol as NoPiaMissingCanonicalTypeSymbol; - - if (missingCanonicalTypeSymbol is not null) + var ambiguousCanonicalTypeSymbol = underlyingTypeSymbol as NoPiaAmbiguousCanonicalTypeSymbol; + if (ambiguousCanonicalTypeSymbol is not null) { - symbolName = missingCanonicalTypeSymbol.FullTypeName; + symbol = ambiguousCanonicalTypeSymbol.FirstCandidate.GetPublicSymbol(); + } + else + { + var missingCanonicalTypeSymbol = underlyingTypeSymbol as NoPiaMissingCanonicalTypeSymbol; + + if (missingCanonicalTypeSymbol is not null) + { + symbolName = missingCanonicalTypeSymbol.FullTypeName; + } } } - } - if (symbolName is null && symbol.IsAnonymousType && symbol.TypeKind == TypeKind.Delegate) - { - symbolName = ""; - } + if (symbolName is null && symbol.IsAnonymousType && symbol.TypeKind == TypeKind.Delegate) + { + symbolName = ""; + } - var partKind = GetPartKind(symbol); + var partKind = GetPartKind(symbol); - symbolName ??= symbol.Name; + symbolName ??= symbol.Name; - if (Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName) && - partKind == SymbolDisplayPartKind.ErrorTypeName && - string.IsNullOrEmpty(symbolName)) - { - Builder.Add(CreatePart(partKind, symbol, "?")); - } - else - { - symbolName = RemoveAttributeSuffixIfNecessary(symbol, symbolName); - Builder.Add(CreatePart(partKind, symbol, symbolName)); + if (Format.MiscellaneousOptions.IncludesOption(SymbolDisplayMiscellaneousOptions.UseErrorTypeSymbolName) && + partKind == SymbolDisplayPartKind.ErrorTypeName && + string.IsNullOrEmpty(symbolName)) + { + Builder.Add(CreatePart(partKind, symbol, "?")); + } + else + { + symbolName = RemoveAttributeSuffixIfNecessary(symbol, symbolName); + Builder.Add(CreatePart(partKind, symbol, symbolName)); + } } if (Format.CompilerInternalOptions.IncludesOption(SymbolDisplayCompilerInternalOptions.UseArityForGenericTypes)) @@ -410,10 +425,19 @@ private void AddNameAndTypeArgumentsOrParameters(INamedTypeSymbol symbol) AddTypeArguments(symbol, GetTypeArgumentsModifiers(underlyingTypeSymbol)); AddDelegateParameters(symbol); + if (symbol.IsExtension) + { + addExtensionParameter(symbol); + } + // TODO: do we want to skip these if we're being visited as a containing type? AddTypeParameterConstraints(symbol.TypeArguments); } } + else if (symbol.IsExtension) + { + addExtensionParameter(symbol); + } else { AddDelegateParameters(symbol); @@ -428,6 +452,19 @@ private void AddNameAndTypeArgumentsOrParameters(INamedTypeSymbol symbol) Builder.Add(CreatePart(InternalSymbolDisplayPartKind.Other, symbol, "missing")); AddPunctuation(SyntaxKind.CloseBracketToken); } + + return; + + void addExtensionParameter(INamedTypeSymbol symbol) + { + if (!Format.CompilerInternalOptions.HasFlag(SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames) + && symbol.ExtensionParameter is { } extensionParameter) + { + AddPunctuation(SyntaxKind.OpenParenToken); + AddParameterModifiersAndType(extensionParameter); + AddPunctuation(SyntaxKind.CloseParenToken); + } + } } private ImmutableArray> GetTypeArgumentsModifiers(NamedTypeSymbol? underlyingTypeSymbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs index e2ed766e3be0e..7a7f6e03314f4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/PublicSymbols/AnonymousManager.TypeOrDelegatePublicSymbol.cs @@ -46,6 +46,8 @@ internal sealed override IEnumerable GetFieldsToEmit() internal sealed override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override ImmutableArray GetEarlyAttributeDecodingMembers() { return this.GetMembersUnordered(); @@ -79,6 +81,9 @@ internal sealed override bool MangleName internal sealed override bool IsFileLocal => false; internal sealed override FileIdentifier? AssociatedFileIdentifier => null; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override int Arity { get { return 0; } diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs index 425d6a2a363cd..24eb91b6f5f64 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.SynthesizedMethodBase.cs @@ -17,7 +17,7 @@ internal sealed partial class AnonymousTypeManager /// /// Represents a base implementation for anonymous type synthesized methods. /// - private abstract class SynthesizedMethodBase : SynthesizedInstanceMethodSymbol + private abstract class SynthesizedMethodBase : SynthesizedMethodSymbol { private readonly NamedTypeSymbol _containingType; private readonly string _name; diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs index 9d601bda14ded..2d5944bc713b5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeOrDelegateTemplateSymbol.cs @@ -124,6 +124,8 @@ internal override bool GetGuidString(out string? guidString) internal sealed override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override ImmutableArray GetEarlyAttributeDecodingMembers() { return this.GetMembersUnordered(); @@ -164,6 +166,9 @@ public sealed override bool IsRefLikeType get { return false; } } + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override bool IsReadOnly { get { return false; } diff --git a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs index 3d4e09efc7be3..572ee0cc3442d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ArrayTypeSymbol.cs @@ -235,6 +235,8 @@ public override bool IsValueType } } + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Managed; public sealed override bool IsRefLikeType diff --git a/src/Compilers/CSharp/Portable/Symbols/ConversionSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/ConversionSignatureComparer.cs index 3281ec8457561..c3d66606f60d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ConversionSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ConversionSignatureComparer.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class ConversionSignatureComparer : IEqualityComparer + internal sealed class ConversionSignatureComparer : IEqualityComparer { private static readonly ConversionSignatureComparer s_comparer = new ConversionSignatureComparer(); public static ConversionSignatureComparer Comparer @@ -24,7 +24,7 @@ private ConversionSignatureComparer() { } - public bool Equals(SourceUserDefinedConversionSymbol member1, SourceUserDefinedConversionSymbol member2) + public bool Equals(MethodSymbol member1, MethodSymbol member2) { if (ReferenceEquals(member1, member2)) { @@ -53,7 +53,7 @@ public bool Equals(SourceUserDefinedConversionSymbol member1, SourceUserDefinedC && (member1.Name == WellKnownMemberNames.ImplicitConversionName || member2.Name == WellKnownMemberNames.ImplicitConversionName || member1.Name == member2.Name); } - public int GetHashCode(SourceUserDefinedConversionSymbol member) + public int GetHashCode(MethodSymbol member) { if ((object)member == null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs index 627e2a4ef0069..305545280cf9e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/DynamicTypeSymbol.cs @@ -106,6 +106,8 @@ public override bool IsValueType } } + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Managed; public sealed override bool IsRefLikeType diff --git a/src/Compilers/CSharp/Portable/Symbols/EnumConversions.cs b/src/Compilers/CSharp/Portable/Symbols/EnumConversions.cs index 4b439ae7fe27b..0d40d07e8360a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EnumConversions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EnumConversions.cs @@ -36,6 +36,8 @@ internal static TypeKind ToTypeKind(this DeclarationKind kind) case DeclarationKind.RecordStruct: return TypeKind.Struct; + case DeclarationKind.Extension: + return TypeKind.Extension; default: throw ExceptionUtilities.UnexpectedValue(kind); } diff --git a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs index c6abc0310eddc..e1ce775921fb1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ErrorTypeSymbol.cs @@ -105,6 +105,8 @@ public sealed override bool IsValueType get { return false; } } + internal sealed override ParameterSymbol? ExtensionParameter => null; + public sealed override bool IsRefLikeType { get @@ -113,6 +115,9 @@ public sealed override bool IsRefLikeType } } + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override bool IsReadOnly { get diff --git a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs index 42664a4724719..9d20059ffcd53 100644 --- a/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/EventSymbol.cs @@ -228,6 +228,10 @@ internal virtual bool IsExplicitInterfaceImplementation /// public abstract ImmutableArray ExplicitInterfaceImplementations { get; } + internal virtual EventSymbol? PartialImplementationPart => null; + internal virtual EventSymbol? PartialDefinitionPart => null; + internal virtual bool IsPartialDefinition => false; + /// /// Gets the kind of this symbol. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/ReceiverParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/ReceiverParameterSymbol.cs new file mode 100644 index 0000000000000..ced078cec4bdc --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/ReceiverParameterSymbol.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class ReceiverParameterSymbol : RewrittenParameterSymbol + { + private readonly NamedTypeSymbol _containingType; + + public ReceiverParameterSymbol(NamedTypeSymbol containingType, ParameterSymbol originalParameter) : + base(originalParameter) + { + _containingType = containingType; + } + + public override Symbol ContainingSymbol + { + get { return _containingType; } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenLambdaOrLocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenLambdaOrLocalFunctionSymbol.cs new file mode 100644 index 0000000000000..f20efc3f1f199 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenLambdaOrLocalFunctionSymbol.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class RewrittenLambdaOrLocalFunctionSymbol : RewrittenMethodSymbol + { + private readonly RewrittenMethodSymbol _containingMethod; + + public RewrittenLambdaOrLocalFunctionSymbol(MethodSymbol lambdaOrLocalFunctionSymbol, RewrittenMethodSymbol containingMethod) + : base(lambdaOrLocalFunctionSymbol, containingMethod.TypeMap, lambdaOrLocalFunctionSymbol.TypeParameters) + { + Debug.Assert(lambdaOrLocalFunctionSymbol.AssociatedSymbol is null); + Debug.Assert(lambdaOrLocalFunctionSymbol.TryGetThisParameter(out var thisParameter) && thisParameter is null); + _containingMethod = containingMethod; + } + + public override Symbol? AssociatedSymbol => null; + + public override Symbol ContainingSymbol => _containingMethod; + + internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter) + { + thisParameter = null; + return true; + } + + protected override ImmutableArray MakeParameters() + { + return ImmutableArray.CastUp(_originalMethod.Parameters.SelectAsArray(static (p, @this) => new RewrittenMethodParameterSymbol(@this, p), this)); + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs new file mode 100644 index 0000000000000..fd634a9e9edc3 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenMethodSymbol.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal abstract class RewrittenMethodSymbol : WrappedMethodSymbol + { + protected readonly MethodSymbol _originalMethod; + private readonly TypeMap _typeMap; + private readonly ImmutableArray _typeParameters; + private ImmutableArray _lazyParameters; + + protected RewrittenMethodSymbol(MethodSymbol originalMethod, TypeMap typeMap, ImmutableArray typeParametersToAlphaRename) + { + Debug.Assert(originalMethod.IsDefinition); + Debug.Assert(originalMethod.ExplicitInterfaceImplementations.IsEmpty); + + _originalMethod = originalMethod; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we creating type parameters with the right emit behavior? Attributes, etc. + _typeMap = typeMap.WithAlphaRename(typeParametersToAlphaRename, this, out _typeParameters); + } + + public TypeMap TypeMap => _typeMap; + + public sealed override MethodSymbol UnderlyingMethod => _originalMethod; + + public sealed override ImmutableArray TypeParameters + { + get { return _typeParameters; } + } + + public sealed override ImmutableArray TypeArgumentsWithAnnotations + { + get { return GetTypeParametersAsTypeArguments(); } + } + + public sealed override TypeWithAnnotations ReturnTypeWithAnnotations + { + get { return _typeMap.SubstituteType(_originalMethod.ReturnTypeWithAnnotations); } + } + + internal override TypeWithAnnotations IteratorElementTypeWithAnnotations + { + get + { + TypeWithAnnotations iteratorElementTypeWithAnnotations = _originalMethod.IteratorElementTypeWithAnnotations; + + if (iteratorElementTypeWithAnnotations.HasType) + { + return _typeMap.SubstituteType(iteratorElementTypeWithAnnotations); + } + + return iteratorElementTypeWithAnnotations; + } + } + + internal override bool IsIterator + { + get { return _originalMethod.IsIterator; } + } + + internal sealed override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree) + { + return _originalMethod.CalculateLocalSyntaxOffset(localPosition, localTree); + } + + internal sealed override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable(); + + public sealed override ImmutableArray ExplicitInterfaceImplementations => ImmutableArray.Empty; + + internal sealed override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) + => _originalMethod.GetUnmanagedCallersOnlyAttributeData(forceComplete); + + public sealed override ImmutableArray RefCustomModifiers + { + get { return _typeMap.SubstituteCustomModifiers(_originalMethod.RefCustomModifiers); } + } + + public sealed override ImmutableArray GetAttributes() + { + return _originalMethod.GetAttributes(); + } + + internal sealed override UseSiteInfo GetUseSiteInfo() + { + return _originalMethod.GetUseSiteInfo(); + } + + public sealed override ImmutableArray Parameters + { + get + { + if (_lazyParameters.IsDefault) + { + ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, this.MakeParameters()); + } + return _lazyParameters; + } + } + + protected abstract ImmutableArray MakeParameters(); + + internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + return _originalMethod.HasAsyncMethodBuilderAttribute(out builderArgument); + } + + protected class RewrittenMethodParameterSymbol : RewrittenParameterSymbol + { + protected readonly RewrittenMethodSymbol _containingMethod; + + public RewrittenMethodParameterSymbol(RewrittenMethodSymbol containingMethod, ParameterSymbol originalParameter) : + base(originalParameter) + { + _containingMethod = containingMethod; + } + + public sealed override Symbol ContainingSymbol + { + get { return _containingMethod; } + } + + public override TypeWithAnnotations TypeWithAnnotations + { + get { return _containingMethod._typeMap.SubstituteType(this._underlyingParameter.TypeWithAnnotations); } + } + + public override ImmutableArray RefCustomModifiers + { + get + { + return _containingMethod._typeMap.SubstituteCustomModifiers(this._underlyingParameter.RefCustomModifiers); + } + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenParameterSymbol.cs new file mode 100644 index 0000000000000..ccf32fb12c6d3 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/RewrittenParameterSymbol.cs @@ -0,0 +1,29 @@ +// 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 Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal abstract class RewrittenParameterSymbol : WrappedParameterSymbol + { + public RewrittenParameterSymbol(ParameterSymbol originalParameter) : + base(originalParameter) + { + } + + internal sealed override bool IsCallerLineNumber => _underlyingParameter.IsCallerLineNumber; + + internal sealed override bool IsCallerFilePath => _underlyingParameter.IsCallerFilePath; + + internal sealed override bool IsCallerMemberName => _underlyingParameter.IsCallerMemberName; + + internal sealed override int CallerArgumentExpressionParameterIndex => _underlyingParameter.CallerArgumentExpressionParameterIndex; + + internal sealed override ImmutableArray InterpolatedStringHandlerArgumentIndexes => throw ExceptionUtilities.Unreachable(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Follow up + + internal sealed override bool HasInterpolatedStringHandlerArgumentError => throw ExceptionUtilities.Unreachable(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Follow up + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/SourceExtensionImplementationMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/SourceExtensionImplementationMethodSymbol.cs new file mode 100644 index 0000000000000..574b60f151077 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/SourceExtensionImplementationMethodSymbol.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal sealed class SourceExtensionImplementationMethodSymbol : RewrittenMethodSymbol // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Do we need to implement ISynthesizedMethodBodyImplementationSymbol? + { + public SourceExtensionImplementationMethodSymbol(MethodSymbol sourceMethod) + : base(sourceMethod, TypeMap.Empty, sourceMethod.ContainingType.TypeParameters.Concat(sourceMethod.TypeParameters)) + { + Debug.Assert(sourceMethod.GetIsNewExtensionMember()); + Debug.Assert(sourceMethod.IsStatic || sourceMethod.ContainingType.ExtensionParameter is not null); + Debug.Assert(!sourceMethod.IsExtern); + Debug.Assert(!sourceMethod.IsExternal); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we creating type parameters with the right emit behavior? Attributes, etc. + // Also, they should be IsImplicitlyDeclared + } + + public override int Arity => TypeParameters.Length; + + public override bool IsGenericMethod => Arity != 0; + + public override MethodKind MethodKind => MethodKind.Ordinary; + public override bool IsImplicitlyDeclared => true; + + internal override bool HasSpecialName => true; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : reconcile with spec + + internal override int ParameterCount + { + get + { + return _originalMethod.ParameterCount + (_originalMethod.IsStatic ? 0 : 1); + } + } + + public sealed override bool IsExtensionMethod => !_originalMethod.IsStatic && _originalMethod.MethodKind is MethodKind.Ordinary; + public sealed override bool IsVirtual => false; + + public sealed override bool IsOverride => false; + public sealed override bool IsAbstract => false; + public sealed override bool IsSealed => false; + + internal sealed override bool IsMetadataVirtual(IsMetadataVirtualOption option = IsMetadataVirtualOption.None) => false; + internal sealed override bool IsMetadataFinal => false; + internal sealed override bool IsMetadataNewSlot(bool ignoreInterfaceImplementationChanges = false) => false; + + internal sealed override bool IsAccessCheckedOnOverride => false; + + public sealed override bool IsExtern => false; + public sealed override DllImportData? GetDllImportData() => null; + internal sealed override bool IsExternal => false; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : How doc comments are supposed to work? GetDocumentationCommentXml + + internal sealed override bool IsDeclaredReadOnly => false; + + public sealed override Symbol ContainingSymbol => _originalMethod.ContainingType.ContainingSymbol; + + internal sealed override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + SourceMethodSymbol.AddSynthesizedAttributes(this, moduleBuilder, ref attributes); + } + + public override bool IsStatic => true; + public override bool RequiresInstanceReceiver => false; + + internal override Microsoft.Cci.CallingConvention CallingConvention + { + get + { + return (_originalMethod.CallingConvention & (~Cci.CallingConvention.HasThis)) | + (Arity != 0 ? Cci.CallingConvention.Generic : 0); + } + } + + public override Symbol? AssociatedSymbol => null; + + protected override ImmutableArray MakeParameters() + { + var sourceParameters = _originalMethod.Parameters; + var parameters = ArrayBuilder.GetInstance(ParameterCount); + + if (!_originalMethod.IsStatic) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Need to confirm if this rewrite going to break LocalStateTracingInstrumenter + // Specifically BoundParameterId, etc. + parameters.Add(new ExtensionMetadataMethodParameterSymbol(this, ((SourceNamedTypeSymbol)_originalMethod.ContainingType).ExtensionParameter!)); + } + + foreach (var parameter in sourceParameters) + { + parameters.Add(new ExtensionMetadataMethodParameterSymbol(this, parameter)); + } + + Debug.Assert(parameters.Count == ParameterCount); + return parameters.ToImmutableAndFree(); + } + + internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter) + { + thisParameter = null; + return true; + } + + private sealed class ExtensionMetadataMethodParameterSymbol : RewrittenMethodParameterSymbol + { + public ExtensionMetadataMethodParameterSymbol(SourceExtensionImplementationMethodSymbol containingMethod, ParameterSymbol sourceParameter) : + base(containingMethod, sourceParameter) + { + } + + public override bool IsImplicitlyDeclared => true; + + public override int Ordinal + { + get + { + if (this._underlyingParameter.ContainingSymbol is NamedTypeSymbol) + { + return 0; + } + + var ordinal = this._underlyingParameter.Ordinal; + + if (this._underlyingParameter.ContainingSymbol.IsStatic) + { + return ordinal; + } + + return ordinal + 1; + } + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Extensions/SynthesizedExtensionMarker.cs b/src/Compilers/CSharp/Portable/Symbols/Extensions/SynthesizedExtensionMarker.cs new file mode 100644 index 0000000000000..b342e1410381c --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Extensions/SynthesizedExtensionMarker.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// This method encodes the information needed to round-trip extension types + /// through metadata. + /// + /// It encodes receiver parameter - the only parameter of the method + /// + internal sealed class SynthesizedExtensionMarker : SynthesizedSourceOrdinaryMethodSymbol + { + internal SynthesizedExtensionMarker(SourceMemberContainerTypeSymbol extensionType, ParameterListSyntax parameterList) + : base(extensionType, WellKnownMemberNames.ExtensionMarkerMethodName, parameterList.OpenParenToken.GetLocation(), parameterList, + (GetDeclarationModifiers(), MakeFlags( + MethodKind.Ordinary, RefKind.None, GetDeclarationModifiers(), returnsVoid: false, returnsVoidIsSet: false, + isExpressionBodied: false, isExtensionMethod: false, isNullableAnalysisEnabled: false, isVarArg: false, + isExplicitInterfaceImplementation: false, hasThisInitializer: false))) + { + Debug.Assert(extensionType.IsDefinition); + Debug.Assert(extensionType.IsExtension); + Debug.Assert(parameterList is not null); + + return; + } + + private static DeclarationModifiers GetDeclarationModifiers() => DeclarationModifiers.Private | DeclarationModifiers.Static; + + internal override bool HasSpecialName => true; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : reconcile with spec + + internal override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics) + { + var F = new SyntheticBoundNodeFactory(this, ContainingType.GetNonNullSyntaxNode(), compilationState, diagnostics); + F.CloseMethod(F.Return()); + } + + protected override int GetParameterCountFromSyntax() + { + ParameterListSyntax parameterList = (ParameterListSyntax)syntaxReferenceOpt.GetSyntax(); + return parameterList.Parameters is [{ IsArgList: false }, ..] ? 1 : 0; + } + + protected override (TypeWithAnnotations ReturnType, ImmutableArray Parameters) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics) + { + return (TypeWithAnnotations.Create(Binder.GetSpecialType(DeclaringCompilation, SpecialType.System_Void, GetFirstLocation(), diagnostics)), + makeExtensionParameter(diagnostics) is { } parameter ? [parameter] : []); + + ParameterSymbol? makeExtensionParameter(BindingDiagnosticBag diagnostics) + { + ParameterListSyntax parameterList = (ParameterListSyntax)syntaxReferenceOpt.GetSyntax(); + int count = parameterList.Parameters.Count; + + if (count == 0) + { + return null; + } + + BinderFactory binderFactory = this.DeclaringCompilation.GetBinderFactory(parameterList.SyntaxTree); + var withTypeParamsBinder = binderFactory.GetBinder(parameterList); + + // Constraints are checked later + var signatureBinder = withTypeParamsBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); + + for (int parameterIndex = 1; parameterIndex < count; parameterIndex++) + { + diagnostics.Add(ErrorCode.ERR_ReceiverParameterOnlyOne, parameterList.Parameters[parameterIndex].GetLocation()); + } + + ParameterSymbol? parameter = ParameterHelpers.MakeExtensionReceiverParameter(withTypeParametersBinder: signatureBinder, owner: this, parameterList, diagnostics); + + if (parameter is { }) + { + checkUnderspecifiedGenericExtension(parameter, ContainingType.TypeParameters, diagnostics); + } + + if (parameter is { Name: var name } && name != "" && + ContainingType.TypeParameters.Any(static (p, name) => p.Name == name, name)) + { + diagnostics.Add(ErrorCode.ERR_ReceiverParameterSameNameAsTypeParameter, parameter.GetFirstLocation(), name); + } + + return parameter; + } + + static void checkUnderspecifiedGenericExtension(ParameterSymbol parameter, ImmutableArray typeParameters, BindingDiagnosticBag diagnostics) + { + var underlyingType = parameter.Type; + var usedTypeParameters = PooledHashSet.GetInstance(); + underlyingType.VisitType(collectTypeParameters, arg: usedTypeParameters); + + foreach (var typeParameter in typeParameters) + { + if (!usedTypeParameters.Contains(typeParameter)) + { + diagnostics.Add(ErrorCode.ERR_UnderspecifiedExtension, parameter.GetFirstLocation(), underlyingType, typeParameter); + } + } + + usedTypeParameters.Free(); + } + + static bool collectTypeParameters(TypeSymbol type, PooledHashSet typeParameters, bool ignored) + { + if (type is TypeParameterSymbol typeParameter) + { + typeParameters.Add(typeParameter); + } + + return false; + } + + } + } +} + diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs index 5db3fd3bd3a4e..0af58ff20b8fe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionPointers/FunctionPointerTypeSymbol.cs @@ -72,6 +72,7 @@ private FunctionPointerTypeSymbol(FunctionPointerMethodSymbol signature) public override bool IsReferenceType => false; public override bool IsValueType => true; + internal sealed override ParameterSymbol? ExtensionParameter => null; public override TypeKind TypeKind => TypeKind.FunctionPointer; public override bool IsRefLikeType => false; public override bool IsReadOnly => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs index 0cbd87e3b03e4..1058dcf3aaa55 100644 --- a/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/FunctionTypeSymbol.cs @@ -99,6 +99,8 @@ internal void SetExpression(BoundExpression expression) public override bool IsValueType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + public override TypeKind TypeKind => TypeKindInternal.FunctionType; public override bool IsRefLikeType => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs index 2ee050ccc2797..d70ecd4d63436 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSignatureComparer.cs @@ -771,45 +771,59 @@ internal static bool HaveSameParameterTypes( for (int i = 0; i < numParams; i++) { - var param1 = params1[i]; - var param2 = params2[i]; - - var type1 = SubstituteType(typeMap1, param1.TypeWithAnnotations); - var type2 = SubstituteType(typeMap2, param2.TypeWithAnnotations); - - if (!type1.Equals(type2, typeComparison)) + if (!HaveSameParameterType(params1[i], typeMap1, params2[i], typeMap2, refKindCompareMode, considerDefaultValues, typeComparison)) { return false; } + } - if (considerDefaultValues && param1.ExplicitDefaultConstantValue != param2.ExplicitDefaultConstantValue) - { - return false; - } + return true; + } - if ((typeComparison & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) == 0 && - !HaveSameCustomModifiers(param1.RefCustomModifiers, typeMap1, param2.RefCustomModifiers, typeMap2)) - { - return false; - } + internal static bool HaveSameParameterType( + ParameterSymbol param1, + TypeMap? typeMap1, + ParameterSymbol param2, + TypeMap? typeMap2, + RefKindCompareMode refKindCompareMode, + bool considerDefaultValues, + TypeCompareKind typeComparison) + { + var type1 = SubstituteType(typeMap1, param1.TypeWithAnnotations); + var type2 = SubstituteType(typeMap2, param2.TypeWithAnnotations); - var refKind1 = param1.RefKind; - var refKind2 = param2.RefKind; + if (!type1.Equals(type2, typeComparison)) + { + return false; + } - // Metadata signatures don't distinguish ref/out, but C# does - even when comparing metadata method signatures. - if ((refKindCompareMode & RefKindCompareMode.ConsiderDifferences) != 0) + if (considerDefaultValues && param1.ExplicitDefaultConstantValue != param2.ExplicitDefaultConstantValue) + { + return false; + } + + if ((typeComparison & TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds) == 0 && + !HaveSameCustomModifiers(param1.RefCustomModifiers, typeMap1, param2.RefCustomModifiers, typeMap2)) + { + return false; + } + + var refKind1 = param1.RefKind; + var refKind2 = param2.RefKind; + + // Metadata signatures don't distinguish ref/out, but C# does - even when comparing metadata method signatures. + if ((refKindCompareMode & RefKindCompareMode.ConsiderDifferences) != 0) + { + if (!areRefKindsCompatible(refKindCompareMode, refKind1, refKind2)) { - if (!areRefKindsCompatible(refKindCompareMode, refKind1, refKind2)) - { - return false; - } + return false; } - else + } + else + { + if ((refKind1 == RefKind.None) != (refKind2 == RefKind.None)) { - if ((refKind1 == RefKind.None) != (refKind2 == RefKind.None)) - { - return false; - } + return false; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs index 638734bf2cdf4..775b8e8a39edd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MemberSymbolExtensions.cs @@ -11,6 +11,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -77,6 +78,166 @@ internal static bool GetIsVararg(this Symbol member) } } +#nullable enable + internal static bool GetIsNewExtensionMember(this Symbol member) + { + switch (member.Kind) + { + case SymbolKind.Method: + case SymbolKind.Property: + return member.ContainingSymbol is TypeSymbol { IsExtension: true }; + default: + return false; + } + } + + internal static bool GetIsNewExtensionMember(this MethodSymbol member) + { + return member.ContainingSymbol is TypeSymbol { IsExtension: true }; + } + + internal static bool GetIsNewExtensionMember(this PropertySymbol member) + { + return member.ContainingSymbol is TypeSymbol { IsExtension: true }; + } + + internal static int GetMemberArityIncludingExtension(this Symbol member) + { + if (member.GetIsNewExtensionMember()) + { + return member.ContainingType.Arity + member.GetMemberArity(); + } + + return member.GetMemberArity(); + } + + /// + /// For an extension member, we distribute the type arguments between the extension declaration and the member. + /// Otherwise, we just construct the member with the type arguments. + /// + internal static TMember ConstructIncludingExtension(this TMember member, ImmutableArray typeArguments) where TMember : Symbol + { + if (member is MethodSymbol method) + { + if (method.GetIsNewExtensionMember()) + { + NamedTypeSymbol extension = method.ContainingType; + if (extension.Arity > 0) + { + extension = extension.Construct(typeArguments[..extension.Arity]); + method = method.AsMember(extension); + } + + if (method.Arity > 0) + { + return (TMember)(Symbol)method.Construct(typeArguments[extension.Arity..]); + } + + return (TMember)(Symbol)method; + } + + return (TMember)(Symbol)method.Construct(typeArguments); + } + + if (member is PropertySymbol property) + { + Debug.Assert(property.GetIsNewExtensionMember()); + NamedTypeSymbol extension = property.ContainingType; + Debug.Assert(extension.Arity > 0); + Debug.Assert(extension.Arity == typeArguments.Length); + + extension = extension.Construct(typeArguments); + property = property.AsMember(extension); + + return (TMember)(Symbol)property; + } + + throw ExceptionUtilities.UnexpectedValue(member); + } + + // For lookup APIs in the semantic model, we can return symbols that aren't fully inferred. + // But for function type inference, if the symbol isn't fully inferred with the information we have (the receiver and any explicit type arguments) + // then we won't return it. + internal static Symbol? GetReducedAndFilteredSymbol(this Symbol member, ImmutableArray typeArguments, TypeSymbol receiverType, CSharpCompilation compilation, bool checkFullyInferred) + { + if (member is MethodSymbol method) + { + // 1. construct with explicit type arguments if provided + MethodSymbol? constructed; + if (!typeArguments.IsDefaultOrEmpty && method.GetMemberArityIncludingExtension() == typeArguments.Length) + { + constructed = method.ConstructIncludingExtension(typeArguments); + Debug.Assert((object)constructed != null); + + if (!checkConstraintsIncludingExtension(constructed, compilation, method.ContainingAssembly.CorLibrary.TypeConversions)) + { + return null; + } + } + else + { + constructed = method; + } + + // 2. infer type arguments based on the receiver type if needed, check applicability, reduce symbol (for classic extension methods), check whether fully inferred + if ((object)receiverType != null) + { + if (method.IsExtensionMethod) + { + constructed = constructed.ReduceExtensionMethod(receiverType, compilation, out bool wasFullyInferred); + + if (checkFullyInferred && !wasFullyInferred) + { + return null; + } + } + else + { + Debug.Assert(method.GetIsNewExtensionMember()); + constructed = (MethodSymbol?)SourceNamedTypeSymbol.GetCompatibleSubstitutedMember(compilation, constructed, receiverType); + + if (checkFullyInferred && constructed?.IsGenericMethod == true && typeArguments.IsDefaultOrEmpty) + { + return null; + } + } + } + + return constructed; + } + else if (member is PropertySymbol property) + { + // infer type arguments based off the receiver type if needed, check applicability + Debug.Assert(receiverType is not null); + Debug.Assert(property.GetIsNewExtensionMember()); + return (PropertySymbol?)SourceNamedTypeSymbol.GetCompatibleSubstitutedMember(compilation, property, receiverType); + } + + throw ExceptionUtilities.UnexpectedValue(member.Kind); + + static bool checkConstraintsIncludingExtension(MethodSymbol symbol, CSharpCompilation compilation, TypeConversions conversions) + { + var constraintArgs = new ConstraintsHelper.CheckConstraintsArgs(compilation, conversions, includeNullability: false, + NoLocation.Singleton, diagnostics: BindingDiagnosticBag.Discarded, template: CompoundUseSiteInfo.Discarded); + + bool success = true; + + if (symbol.GetIsNewExtensionMember()) + { + NamedTypeSymbol extensionDeclaration = symbol.ContainingType; + success = extensionDeclaration.CheckConstraints(constraintArgs); + } + + if (success) + { + success = symbol.CheckConstraints(constraintArgs); + } + + return success; + } + } +#nullable disable + /// /// Get the ref kinds of the parameters of a member symbol. Should be a method, property, or event. /// @@ -551,7 +712,10 @@ internal static bool IsPartialMember(this Symbol member) return member is SourceOrdinaryMethodSymbol { IsPartial: true } or SourcePropertySymbol { IsPartial: true } - or SourcePropertyAccessorSymbol { IsPartial: true }; + or SourcePropertyAccessorSymbol { IsPartial: true } + or SourceConstructorSymbol { IsPartial: true } + or SourceEventSymbol { IsPartial: true } + or SourceEventAccessorSymbol { IsPartial: true }; } internal static bool IsPartialImplementation(this Symbol member) @@ -560,7 +724,10 @@ internal static bool IsPartialImplementation(this Symbol member) return member is SourceOrdinaryMethodSymbol { IsPartialImplementation: true } or SourcePropertySymbol { IsPartialImplementation: true } - or SourcePropertyAccessorSymbol { IsPartialImplementation: true }; + or SourcePropertyAccessorSymbol { IsPartialImplementation: true } + or SourceConstructorSymbol { IsPartialImplementation: true } + or SourceEventSymbol { IsPartialImplementation: true } + or SourceEventAccessorSymbol { IsPartialImplementation: true }; } internal static bool IsPartialDefinition(this Symbol member) @@ -569,9 +736,38 @@ internal static bool IsPartialDefinition(this Symbol member) return member is SourceOrdinaryMethodSymbol { IsPartialDefinition: true } or SourcePropertySymbol { IsPartialDefinition: true } - or SourcePropertyAccessorSymbol { IsPartialDefinition: true }; + or SourcePropertyAccessorSymbol { IsPartialDefinition: true } + or SourceConstructorSymbol { IsPartialDefinition: true } + or SourceEventSymbol { IsPartialDefinition: true } + or SourceEventAccessorSymbol { IsPartialDefinition: true }; } +#nullable enable + internal static Symbol? GetPartialImplementationPart(this Symbol member) + { + Debug.Assert(member.IsDefinition); + return member switch + { + MethodSymbol method => method.PartialImplementationPart, + SourcePropertySymbol property => property.PartialImplementationPart, + SourceEventSymbol ev => ev.PartialImplementationPart, + _ => null, + }; + } + + internal static Symbol? GetPartialDefinitionPart(this Symbol member) + { + Debug.Assert(member.IsDefinition); + return member switch + { + MethodSymbol method => method.PartialDefinitionPart, + SourcePropertySymbol property => property.PartialDefinitionPart, + SourceEventSymbol ev => ev.PartialDefinitionPart, + _ => null, + }; + } +#nullable disable + internal static bool ContainsTupleNames(this Symbol member) { switch (member.Kind) diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs index 7594549773a9b..1be0a208a237a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEMethodSymbol.cs @@ -1434,6 +1434,13 @@ internal override UseSiteInfo GetUseSiteInfo() MergeUseSiteDiagnostics(ref diagnosticInfo, DeriveCompilerFeatureRequiredDiagnostic()); EnsureTypeParametersAreLoaded(ref diagnosticInfo); + if (diagnosticInfo == null && _containingType.IsExtension && + TryGetCorrespondingExtensionImplementationMethod() is null) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + diagnosticInfo = new CSDiagnosticInfo(ErrorCode.ERR_BindToBogus, this); + } + if (diagnosticInfo == null && _containingType.ContainingPEModule.RefSafetyRulesVersion == PEModuleSymbol.RefSafetyRulesAttributeVersion.UnrecognizedAttribute) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs index 60d770d5ad6b8..6d4d4880c52b7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs @@ -5,6 +5,7 @@ #nullable disable using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -13,6 +14,7 @@ using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis.Collections; @@ -117,7 +119,7 @@ private bool IsUncommon() return true; } - if (this.TypeKind == TypeKind.Enum) + if (this.TypeKind is TypeKind.Enum or TypeKind.Extension) { return true; } @@ -151,6 +153,7 @@ private sealed class UncommonProperties internal ImmutableArray lazyFilePathChecksum = default; internal string lazyDisplayFileName; + internal ExtensionInfo lazyExtensionInfo; #if DEBUG internal bool IsDefaultValue() @@ -169,11 +172,23 @@ internal bool IsDefaultValue() !lazyHasRequiredMembers.HasValue() && (object)lazyCollectionBuilderAttributeData == CollectionBuilderAttributeData.Uninitialized && lazyFilePathChecksum.IsDefault && - lazyDisplayFileName == null; + lazyDisplayFileName == null && + lazyExtensionInfo is null; } #endif } +#nullable enable + + private class ExtensionInfo(MethodDefinitionHandle markerMethod) + { + public readonly MethodDefinitionHandle MarkerMethod = markerMethod; + public StrongBox? LazyExtensionParameter; + public ConcurrentDictionary? LazyImplementationMap; + } + +#nullable disable + #endregion // Uncommon properties internal static PENamedTypeSymbol Create( @@ -366,6 +381,166 @@ public override ExtendedSpecialType ExtendedSpecialType } } +#nullable enable + + internal sealed override ParameterSymbol? ExtensionParameter + { + get + { + if (!IsExtension) + { + return null; + } + + var uncommon = GetUncommonProperties().lazyExtensionInfo; + + if (uncommon.LazyExtensionParameter is null) + { + var extensionParameter = makeExtensionParameter(this, uncommon); + Interlocked.CompareExchange(ref uncommon.LazyExtensionParameter, new StrongBox(extensionParameter), null); + } + + return uncommon.LazyExtensionParameter.Value; + + static ParameterSymbol? makeExtensionParameter(PENamedTypeSymbol @this, ExtensionInfo uncommon) + { + var methodSymbol = getMarkerMethodSymbol(@this, uncommon); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : do we want to tighten the flags check further? (require that type be sealed?) + if (methodSymbol.DeclaredAccessibility != Accessibility.Private || + methodSymbol.IsGenericMethod || + !methodSymbol.IsStatic || + !methodSymbol.ReturnsVoid || + methodSymbol.ParameterCount != 1) // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Should we accept more than one parameter (in case new versions add more info)? + { + return null; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + return new ReceiverParameterSymbol(@this, methodSymbol.Parameters[0]); + } + + static MethodSymbol getMarkerMethodSymbol(PENamedTypeSymbol @this, ExtensionInfo uncommon) + { + Debug.Assert(!uncommon.MarkerMethod.IsNil); + + foreach (var member in @this.GetMembers(WellKnownMemberNames.ExtensionMarkerMethodName)) + { + if (member is PEMethodSymbol candidate && candidate.Handle == uncommon.MarkerMethod) + { + return candidate; + } + } + + throw ExceptionUtilities.Unreachable(); + } + } + } + + public sealed override MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method) + { + Debug.Assert(this.IsExtension); + Debug.Assert(method.IsDefinition); + Debug.Assert(method.ContainingType == (object)this); + + if (this.ContainingType is null) + { + return null; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (!method.IsStatic && ExtensionParameter is null) + { + return null; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + var uncommon = GetUncommonProperties().lazyExtensionInfo; + + if (uncommon.LazyImplementationMap is null) + { + Interlocked.CompareExchange(ref uncommon.LazyImplementationMap, new ConcurrentDictionary(Roslyn.Utilities.ReferenceEqualityComparer.Instance), null); + } + + return uncommon.LazyImplementationMap.GetOrAdd(method, findCorrespondingExtensionImplementationMethod, this); + + static MethodSymbol? findCorrespondingExtensionImplementationMethod(MethodSymbol method, PENamedTypeSymbol @this) + { + Debug.Assert(@this.ExtensionParameter is not null); + + foreach (var member in @this.ContainingType.GetMembers(method.Name)) + { + if (member is not MethodSymbol { HasSpecialName: true, IsStatic: true } candidate) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider comparing accessibility as well + + if (candidate.Arity != @this.Arity + method.Arity) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + int additionalParameterCount = method.IsStatic ? 0 : 1; + if (additionalParameterCount + method.ParameterCount != candidate.ParameterCount) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + ImmutableArray combinedTypeParameters = @this.TypeParameters.Concat(method.TypeParameters); + var typeMap = combinedTypeParameters.IsEmpty ? null : new TypeMap(combinedTypeParameters, candidate.TypeParameters); + + if (!MemberSignatureComparer.HaveSameReturnTypes( + candidate, + typeMap1: null, + method, + typeMap, + TypeCompareKind.CLRSignatureCompareOptions)) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (!method.IsStatic && + !MemberSignatureComparer.HaveSameParameterType( + candidate.Parameters[0], + typeMap1: null, + @this.ExtensionParameter, + typeMap, + MemberSignatureComparer.RefKindCompareMode.ConsiderDifferences, + considerDefaultValues: false, + TypeCompareKind.CLRSignatureCompareOptions)) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (!MemberSignatureComparer.HaveSameParameterTypes( + candidate.Parameters.AsSpan(additionalParameterCount, candidate.ParameterCount - additionalParameterCount), + typeMap1: null, + method.Parameters.AsSpan(), + typeMap, + MemberSignatureComparer.RefKindCompareMode.ConsiderDifferences, + considerDefaultValues: false, + TypeCompareKind.CLRSignatureCompareOptions)) + { + continue; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (MemberSignatureComparer.HaveSameConstraints( + candidate.TypeParameters, + typeMap1: null, + combinedTypeParameters, + typeMap)) + { + return candidate; + } + + break; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + return null; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + } + +#nullable disable + internal PEModuleSymbol ContainingPEModule { get @@ -1777,7 +1952,7 @@ public override TypeKind TypeKind { get { - TypeKind result = _lazyKind; + var result = (TypeKind)Volatile.Read(ref Unsafe.As(ref _lazyKind)); if (result == TypeKind.Unknown) { @@ -1815,6 +1990,21 @@ public override TypeKind TypeKind result = TypeKind.Struct; } break; + + default: + if (TryGetExtensionMarkerMethod() is { IsNil: false } markerHandle) + { + // Extension + result = TypeKind.Extension; + + if (_lazyUncommonProperties is null) + { + Interlocked.CompareExchange(ref _lazyUncommonProperties, new UncommonProperties(), null); + } + + Interlocked.CompareExchange(ref _lazyUncommonProperties.lazyExtensionInfo, new ExtensionInfo(markerHandle), null); + } + break; } } } @@ -1826,6 +2016,46 @@ public override TypeKind TypeKind } } + /// + /// Superficially checks whether this is a valid extension type + /// and returns the extension marker method (to be validated later) + /// if it is. + /// + private MethodDefinitionHandle TryGetExtensionMarkerMethod() + { + var moduleSymbol = this.ContainingPEModule; + var module = moduleSymbol.Module; + + try + { + // They must have a single marker method (to be validated later) + MethodDefinitionHandle foundMarkerMethod = default; + foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle)) + { + string methodName; + MethodAttributes flags; + module.GetMethodDefPropsOrThrow(methodHandle, out methodName, out _, out flags, out _); + + if ((flags & MethodAttributes.SpecialName) != 0 && methodName is WellKnownMemberNames.ExtensionMarkerMethodName) + { + if (!foundMarkerMethod.IsNil) + { + return default; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + foundMarkerMethod = methodHandle; + } + } + + return foundMarkerMethod; + } + catch (BadImageFormatException) + { + } + + return default; + } + internal sealed override bool IsInterface { get @@ -2008,11 +2238,15 @@ private PooledDictionary CreateMethods(A // for ordinary embeddable struct types we import private members so that we can report appropriate errors if the structure is used var isOrdinaryEmbeddableStruct = (this.TypeKind == TypeKind.Struct) && (this.SpecialType == Microsoft.CodeAnalysis.SpecialType.None) && this.ContainingAssembly.IsLinked; + MethodDefinitionHandle? extensionMarkerMethod = _lazyUncommonProperties?.lazyExtensionInfo?.MarkerMethod; + Debug.Assert(extensionMarkerMethod is not null || this.TypeKind is not TypeKind.Extension); + try { foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle)) { - if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions)) + if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions) || + extensionMarkerMethod == methodHandle) { var method = new PEMethodSymbol(moduleSymbol, this, methodHandle); members.Add(method); @@ -2323,6 +2557,9 @@ public override bool IsRefLikeType } } + internal override string ExtensionName + => Name; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Confirm implementation + public override bool IsReadOnly { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamespaceSymbol.cs index 8b7133a7843bf..9ec821ac724f4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamespaceSymbol.cs @@ -2,12 +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. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Metadata; using System.Threading; @@ -27,20 +26,20 @@ internal abstract class PENamespaceSymbol /// A map of namespaces immediately contained within this namespace /// mapped by their name (case-sensitively). /// - protected Dictionary, PENestedNamespaceSymbol> lazyNamespaces; + protected Dictionary, PENestedNamespaceSymbol>? lazyNamespaces; /// /// A map of types immediately contained within this namespace /// grouped by their name (case-sensitively). /// - protected Dictionary, ImmutableArray> lazyTypes; + protected Dictionary, ImmutableArray>? lazyTypes; /// /// A map of NoPia local types immediately contained in this assembly. /// Maps type name (non-qualified) to the row id. Note, for VB we should use /// full name. /// - private Dictionary _lazyNoPiaLocalTypes; + private Dictionary? _lazyNoPiaLocalTypes; /// /// All type members in a flat array @@ -98,6 +97,7 @@ private ImmutableArray GetMemberTypesPrivate() //assume that EnsureAllMembersLoaded() has initialize lazyTypes if (_lazyFlattenedTypes.IsDefault) { + Debug.Assert(lazyTypes != null); var flattened = lazyTypes.Flatten(); ImmutableInterlocked.InterlockedExchange(ref _lazyFlattenedTypes, flattened); } @@ -105,7 +105,7 @@ private ImmutableArray GetMemberTypesPrivate() return StaticCast.From(_lazyFlattenedTypes); } - internal override NamespaceSymbol GetNestedNamespace(ReadOnlyMemory name) + internal override NamespaceSymbol? GetNestedNamespace(ReadOnlyMemory name) { EnsureAllMembersLoaded(); @@ -121,7 +121,7 @@ public sealed override ImmutableArray GetMembers(ReadOnlyMemory na { EnsureAllMembersLoaded(); - PENestedNamespaceSymbol ns = null; + PENestedNamespaceSymbol? ns = null; ImmutableArray t; if (lazyNamespaces.TryGetValue(name, out ns)) @@ -189,6 +189,8 @@ public override ImmutableArray DeclaringSyntaxReferences /// PEModuleSymbol containing the namespace. internal abstract PEModuleSymbol ContainingPEModule { get; } + [MemberNotNull(nameof(lazyTypes))] + [MemberNotNull(nameof(lazyNamespaces))] protected abstract void EnsureAllMembersLoaded(); /// @@ -204,19 +206,21 @@ public override ImmutableArray DeclaringSyntaxReferences /// immediately contained within Global namespace. Therefore, all types in this namespace, if any, /// must be in several first IGroupings. /// + [MemberNotNull(nameof(lazyTypes))] + [MemberNotNull(nameof(lazyNamespaces))] protected void LoadAllMembers(IEnumerable> typesByNS) { Debug.Assert(typesByNS != null); // A sequence of groups of TypeDef row ids for types immediately contained within this namespace. - IEnumerable> nestedTypes = null; + IEnumerable>? nestedTypes = null; // A sequence with information about namespaces immediately contained within this namespace. // For each pair: // Key - contains simple name of a child namespace. // Value - contains a sequence similar to the one passed to this function, but // calculated for the child namespace. - IEnumerable>>> nestedNamespaces = null; + IEnumerable>>>? nestedNamespaces = null; bool isGlobalNamespace = this.IsGlobalNamespace; MetadataHelpers.GetInfoForImmediateNamespaceMembers( @@ -249,6 +253,7 @@ private int GetQualifiedNameLength() /// /// Create symbols for nested namespaces and initialize namespaces map. /// + [MemberNotNull(nameof(lazyNamespaces))] private void LazyInitializeNamespaces( IEnumerable>>> childNamespaces) { @@ -269,6 +274,7 @@ private void LazyInitializeNamespaces( /// /// Create symbols for nested types and initialize types map. /// + [MemberNotNull(nameof(lazyTypes))] private void LazyInitializeTypes(IEnumerable> typeGroups) { if (this.lazyTypes == null) @@ -277,7 +283,7 @@ private void LazyInitializeTypes(IEnumerable.GetInstance(); var skipCheckForPiaType = !moduleSymbol.Module.ContainsNoPiaLocalTypes(); - Dictionary noPiaLocalTypes = null; + Dictionary? noPiaLocalTypes = null; foreach (var g in typeGroups) { @@ -325,8 +331,6 @@ private void LazyInitializeTypes(IEnumerable methods, string n { var thisParam = method.Parameters.First(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should use similar logic when looking up new extension members if ((thisParam.RefKind == RefKind.Ref && !thisParam.Type.IsValueType) || (thisParam.RefKind is RefKind.In or RefKind.RefReadOnlyParameter && thisParam.Type.TypeKind != TypeKind.Struct)) { @@ -369,6 +370,15 @@ internal void DoGetExtensionMethods(ArrayBuilder methods, string n } } +#nullable enable + + public virtual MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method) + { + throw ExceptionUtilities.Unreachable(); + } + +#nullable disable + // TODO: Probably should provide similar accessors for static constructor, destructor, // TODO: operators, conversions. @@ -494,6 +504,10 @@ public override string MetadataName /// internal abstract FileIdentifier? AssociatedFileIdentifier { get; } + /// + /// For extensions, returns the synthesized identifier for the type: "<E>__N". + /// + internal abstract string ExtensionName { get; } #nullable disable /// diff --git a/src/Compilers/CSharp/Portable/Symbols/NamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NamespaceSymbol.cs index e64a8c90a12e3..c5825a11647e6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NamespaceSymbol.cs @@ -236,6 +236,8 @@ internal NamedTypeSymbol ImplicitType } } +#nullable enable + /// /// Lookup a nested namespace. /// @@ -246,9 +248,9 @@ internal NamedTypeSymbol ImplicitType /// Symbol for the most nested namespace, if found. Nothing /// if namespace or any part of it can not be found. /// - internal NamespaceSymbol LookupNestedNamespace(ImmutableArray> names) + internal NamespaceSymbol? LookupNestedNamespace(ImmutableArray> names) { - NamespaceSymbol scope = this; + NamespaceSymbol? scope = this; foreach (ReadOnlyMemory name in names) { scope = scope.GetNestedNamespace(name); @@ -259,10 +261,10 @@ internal NamespaceSymbol LookupNestedNamespace(ImmutableArray GetNestedNamespace(name.AsMemory()); - internal virtual NamespaceSymbol GetNestedNamespace(ReadOnlyMemory name) + internal virtual NamespaceSymbol? GetNestedNamespace(ReadOnlyMemory name) { foreach (var sym in this.GetMembers(name)) { @@ -275,6 +277,8 @@ internal virtual NamespaceSymbol GetNestedNamespace(ReadOnlyMemory name) return null; } +#nullable disable + public abstract ImmutableArray GetMembers(ReadOnlyMemory name); public sealed override ImmutableArray GetMembers(string name) @@ -351,6 +355,22 @@ internal virtual void GetExtensionMethods(ArrayBuilder methods, st } } + internal void GetExtensionContainers(ArrayBuilder extensions) + { + foreach (var type in this.GetTypeMembersUnordered()) + { + if (!type.IsReferenceType || !type.IsStatic || type.IsGenericType || !type.MightContainExtensionMethods) continue; + + foreach (var nestedType in type.GetTypeMembersUnordered()) + { + if (nestedType.IsExtension) + { + extensions.Add(nestedType); + } + } + } + } + internal string QualifiedName { get diff --git a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs index b805e7b0319fb..a7b187f9a54da 100644 --- a/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/NativeIntegerTypeSymbol.cs @@ -184,6 +184,8 @@ internal override UseSiteInfo GetUseSiteInfo() internal sealed override bool IsRecordStruct => false; internal sealed override bool HasPossibleWellKnownCloneMethod() => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal override bool Equals(TypeSymbol? other, TypeCompareKind comparison) { if (other is null) diff --git a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs index aaff1d36f2138..11cd05b0834c3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PointerTypeSymbol.cs @@ -106,6 +106,8 @@ public override bool IsValueType } } + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) => ManagedKind.Unmanaged; public sealed override bool IsRefLikeType diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/EventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/EventSymbol.cs index 9bf557e085674..957ed5fe60608 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/EventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/EventSymbol.cs @@ -88,6 +88,12 @@ ImmutableArray IEventSymbol.ExplicitInterfaceImplementations bool IEventSymbol.IsWindowsRuntimeEvent => _underlying.IsWindowsRuntimeEvent; + IEventSymbol? IEventSymbol.PartialDefinitionPart => _underlying.PartialDefinitionPart.GetPublicSymbol(); + + IEventSymbol? IEventSymbol.PartialImplementationPart => _underlying.PartialImplementationPart.GetPublicSymbol(); + + bool IEventSymbol.IsPartialDefinition => _underlying.IsPartialDefinition; + #region ISymbol Members protected override void Accept(SymbolVisitor visitor) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs index 476a5444dc0df..57d245f2ef045 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/TypeSymbol.cs @@ -26,6 +26,11 @@ protected TypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) ITypeSymbol ITypeSymbol.WithNullableAnnotation(CodeAnalysis.NullableAnnotation nullableAnnotation) { + if (UnderlyingTypeSymbol.IsExtension) + { + throw new System.NotSupportedException(); + } + if (NullableAnnotation == nullableAnnotation) { return this; @@ -144,6 +149,12 @@ TypeKind ITypeSymbol.TypeKind bool ITypeSymbol.IsNativeIntegerType => UnderlyingTypeSymbol.IsNativeIntegerType; +#nullable enable + bool ITypeSymbol.IsExtension => UnderlyingTypeSymbol.IsExtension; + + IParameterSymbol? ITypeSymbol.ExtensionParameter => UnderlyingTypeSymbol.ExtensionParameter?.GetPublicSymbol(); +#nullable disable + string ITypeSymbol.ToDisplayString(CodeAnalysis.NullableFlowState topLevelNullability, SymbolDisplayFormat format) { return SymbolDisplay.ToDisplayString(this, topLevelNullability, format); diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs index 32a375a6e2eed..daf7f50ddcbde 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingNamedTypeSymbol.cs @@ -8,6 +8,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; using Roslyn.Utilities; @@ -38,6 +39,8 @@ internal sealed class RetargetingNamedTypeSymbol : WrappedNamedTypeSymbol private CachedUseSiteInfo _lazyCachedUseSiteInfo = CachedUseSiteInfo.Uninitialized; + private StrongBox _lazyExtensionParameter; + public RetargetingNamedTypeSymbol(RetargetingModuleSymbol retargetingModule, NamedTypeSymbol underlyingType, TupleExtraData tupleData = null) : base(underlyingType, tupleData) { @@ -90,6 +93,40 @@ internal override ImmutableArray TypeArgumentsWithAnnotatio } } + internal sealed override ParameterSymbol ExtensionParameter + { + get + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this method + + if (_lazyExtensionParameter is null) + { + var extensionParameter = _underlyingType.ExtensionParameter is { } receiverParameter ? new RetargetingExtensionReceiverParameterSymbol(this, receiverParameter) : null; + Interlocked.CompareExchange(ref _lazyExtensionParameter, new StrongBox(extensionParameter), null); + } + + return _lazyExtensionParameter.Value; + } + } + + public override MethodSymbol TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this method + + Debug.Assert(this.IsExtension); + Debug.Assert(method.IsDefinition); + Debug.Assert(method.ContainingType == (object)this); + + var underlyingImplementation = _underlyingType.TryGetCorrespondingExtensionImplementationMethod(((RetargetingMethodSymbol)method).UnderlyingMethod); + + if (underlyingImplementation is null) + { + return null; + } + + return RetargetingTranslator.Retarget(underlyingImplementation); + } + public override NamedTypeSymbol ConstructedFrom { get diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs index 36b791724d1b6..a175d4d218c30 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingParameterSymbol.cs @@ -60,13 +60,7 @@ public sealed override ImmutableArray RefCustomModifiers } } - public sealed override Symbol ContainingSymbol - { - get - { - return this.RetargetingModule.RetargetingTranslator.Retarget(_underlyingParameter.ContainingSymbol); - } - } + public abstract override Symbol ContainingSymbol { get; } public sealed override ImmutableArray GetAttributes() { @@ -142,7 +136,27 @@ internal sealed override CSharpCompilation? DeclaringCompilation internal sealed override ImmutableArray InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes; - internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; + internal sealed override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; + + internal sealed override bool IsCallerLineNumber + { + get { return _underlyingParameter.IsCallerLineNumber; } + } + + internal sealed override bool IsCallerFilePath + { + get { return _underlyingParameter.IsCallerFilePath; } + } + + internal sealed override bool IsCallerMemberName + { + get { return _underlyingParameter.IsCallerMemberName; } + } + + internal sealed override int CallerArgumentExpressionParameterIndex + { + get { return _underlyingParameter.CallerArgumentExpressionParameterIndex; } + } } internal sealed class RetargetingMethodParameterSymbol : RetargetingParameterSymbol @@ -164,25 +178,7 @@ protected override RetargetingModuleSymbol RetargetingModule get { return _retargetingMethod.RetargetingModule; } } - internal override bool IsCallerLineNumber - { - get { return _underlyingParameter.IsCallerLineNumber; } - } - - internal override bool IsCallerFilePath - { - get { return _underlyingParameter.IsCallerFilePath; } - } - - internal override bool IsCallerMemberName - { - get { return _underlyingParameter.IsCallerMemberName; } - } - - internal override int CallerArgumentExpressionParameterIndex - { - get { return _underlyingParameter.CallerArgumentExpressionParameterIndex; } - } + public override Symbol ContainingSymbol => _retargetingMethod; } internal sealed class RetargetingPropertyParameterSymbol : RetargetingParameterSymbol @@ -204,24 +200,28 @@ protected override RetargetingModuleSymbol RetargetingModule get { return _retargetingProperty.RetargetingModule; } } - internal override bool IsCallerLineNumber - { - get { return _underlyingParameter.IsCallerLineNumber; } - } + public override Symbol ContainingSymbol => _retargetingProperty; + } - internal override bool IsCallerFilePath - { - get { return _underlyingParameter.IsCallerFilePath; } - } + internal sealed class RetargetingExtensionReceiverParameterSymbol : RetargetingParameterSymbol + { + /// + /// Owning RetargetingNamedTypeSymbol. + /// + private readonly RetargetingNamedTypeSymbol _retargetingType; - internal override bool IsCallerMemberName + public RetargetingExtensionReceiverParameterSymbol(RetargetingNamedTypeSymbol retargetingType, ParameterSymbol underlyingParameter) + : base(underlyingParameter) { - get { return _underlyingParameter.IsCallerMemberName; } + Debug.Assert((object)retargetingType != null); + _retargetingType = retargetingType; } - internal override int CallerArgumentExpressionParameterIndex + protected override RetargetingModuleSymbol RetargetingModule { - get { return _underlyingParameter.CallerArgumentExpressionParameterIndex; } + get { return (RetargetingModuleSymbol)_retargetingType.ContainingModule; } } + + public override Symbol ContainingSymbol => _retargetingType; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs index b370e6423052a..c60c6ac95843e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ImplicitNamedTypeSymbol.cs @@ -175,6 +175,8 @@ internal override ObsoleteAttributeData ObsoleteAttributeData internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol ExtensionParameter => null; + internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable(); internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => null; @@ -192,5 +194,8 @@ internal sealed override bool HasCollectionBuilderAttribute(out TypeSymbol? buil methodName = null; return false; } + + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index efbe13b19594e..175403675a328 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -20,7 +20,8 @@ internal static DeclarationModifiers MakeAndCheckNonTypeMemberModifiers( DeclarationModifiers allowedModifiers, Location errorLocation, BindingDiagnosticBag diagnostics, - out bool modifierErrors) + out bool modifierErrors, + out bool hasExplicitAccessModifier) { var result = modifiers.ToDeclarationModifiers(isForTypeDeclaration: false, diagnostics.DiagnosticBag ?? new DiagnosticBag(), isOrdinaryMethod: isOrdinaryMethod); result = CheckModifiers(isForTypeDeclaration: false, isForInterfaceMember, result, allowedModifiers, errorLocation, diagnostics, modifiers, out modifierErrors); @@ -29,7 +30,8 @@ internal static DeclarationModifiers MakeAndCheckNonTypeMemberModifiers( if (readonlyToken.Parent is MethodDeclarationSyntax or AccessorDeclarationSyntax or BasePropertyDeclarationSyntax or EventDeclarationSyntax) modifierErrors |= !MessageID.IDS_FeatureReadOnlyMembers.CheckFeatureAvailability(diagnostics, readonlyToken); - if ((result & DeclarationModifiers.AccessibilityMask) == 0) + hasExplicitAccessModifier = (result & DeclarationModifiers.AccessibilityMask) != 0; + if (!hasExplicitAccessModifier) result |= defaultAccess; return result; @@ -223,6 +225,26 @@ internal static void CheckFeatureAvailabilityForStaticAbstractMembersInInterface } } +#nullable enable + internal static void CheckFeatureAvailabilityForPartialEventsAndConstructors(Location location, BindingDiagnosticBag diagnostics) + { + Debug.Assert(location.SourceTree is not null); + + LanguageVersion availableVersion = ((CSharpParseOptions)location.SourceTree.Options).LanguageVersion; + LanguageVersion requiredVersion = MessageID.IDS_FeaturePartialEventsAndConstructors.RequiredVersion(); + if (availableVersion < requiredVersion) + { + ReportUnsupportedModifiersForLanguageVersion( + DeclarationModifiers.Partial, + DeclarationModifiers.Partial, + location, + diagnostics, + availableVersion, + requiredVersion); + } + } +#nullable disable + internal static DeclarationModifiers AdjustModifiersForAnInterfaceMember(DeclarationModifiers mods, bool hasBody, bool isExplicitInterfaceImplementation) { if (isExplicitInterfaceImplementation) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 06f45033425c7..79b0a9bc5c6d2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -38,7 +38,7 @@ public static ImmutableArray MakeParameters( addRefReadOnlyModifier, suppressUseSiteDiagnostics: false, lastIndex: syntax.Parameters.Count - 1, - parameterCreationFunc: (Binder context, Symbol owner, TypeWithAnnotations parameterType, + parameterCreationFunc: static (Binder context, Symbol owner, TypeWithAnnotations parameterType, ParameterSyntax syntax, RefKind refKind, int ordinal, SyntaxToken paramsKeyword, SyntaxToken thisKeyword, bool addRefReadOnlyModifier, ScopedKind scope, @@ -60,6 +60,58 @@ public static ImmutableArray MakeParameters( }); } +#nullable enable + public static SourceParameterSymbol? MakeExtensionReceiverParameter( + Binder withTypeParametersBinder, + Symbol owner, + ParameterListSyntax syntax, + BindingDiagnosticBag diagnostics) + { + var parameterCreationFunc = static (Binder context, Symbol owner, TypeWithAnnotations parameterType, + ParameterSyntax syntax, RefKind refKind, int ordinal, + SyntaxToken paramsKeyword, SyntaxToken thisKeyword, bool addRefReadOnlyModifier, + ScopedKind scope, + BindingDiagnosticBag declarationDiagnostics) => + { + Debug.Assert(ordinal == 0); + Debug.Assert(paramsKeyword.Kind() == SyntaxKind.None); + + return SourceParameterSymbol.Create( + context, + owner, + parameterType, + syntax, + refKind, + syntax.Identifier, + ordinal, + hasParamsModifier: false, + isExtensionMethodThis: false, + addRefReadOnlyModifier, + scope, + declarationDiagnostics); + }; + + SyntaxToken arglistToken = default; + int firstDefault = -1; + + return MakeParameter( + withTypeParametersBinder, + owner, + syntax.Parameters[0], + ref arglistToken, + diagnostics, + allowRefOrOut: true, + allowThis: false, + addRefReadOnlyModifier: false, + suppressUseSiteDiagnostics: false, + lastIndex: syntax.Parameters.Count - 1, + parameterCreationFunc: parameterCreationFunc, + parameterIndex: 0, + firstDefault: ref firstDefault, + ParameterContext.ExtensionReceiverParameter); + } +#nullable disable + public static ImmutableArray MakeFunctionPointerParameters( Binder binder, FunctionPointerMethodSymbol owner, @@ -78,7 +130,7 @@ public static ImmutableArray MakeFunctionPointer addRefReadOnlyModifier: true, suppressUseSiteDiagnostics, parametersList.Count - 2, - parameterCreationFunc: (Binder binder, FunctionPointerMethodSymbol owner, TypeWithAnnotations parameterType, + parameterCreationFunc: static (Binder binder, FunctionPointerMethodSymbol owner, TypeWithAnnotations parameterType, FunctionPointerParameterSyntax syntax, RefKind refKind, int ordinal, SyntaxToken paramsKeyword, SyntaxToken thisKeyword, bool addRefReadOnlyModifier, ScopedKind scope, @@ -110,6 +162,7 @@ public static ImmutableArray MakeFunctionPointer parsingFunctionPointer: true); } +#nullable enable private static ImmutableArray MakeParameters( Binder withTypeParametersBinder, TOwningSymbol owner, @@ -134,91 +187,143 @@ private static ImmutableArray MakeParameters.GetInstance(); + var parameterContext = parsingFunctionPointer ? ParameterContext.FunctionPointer : ParameterContext.Default; foreach (var parameterSyntax in parametersList) { if (parameterIndex > lastIndex) break; - CheckParameterModifiers(parameterSyntax, diagnostics, parsingFunctionPointer, parsingLambdaParams: false, parsingAnonymousMethodParams: false); + TParameterSymbol? parameter = MakeParameter(withTypeParametersBinder, owner, parameterSyntax, ref arglistToken, diagnostics, + allowRefOrOut, allowThis, addRefReadOnlyModifier, suppressUseSiteDiagnostics, lastIndex, parameterCreationFunc, + parameterIndex, ref firstDefault, parameterContext); - var refKind = GetModifiers(parameterSyntax.Modifiers, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out ScopedKind scope); - if (thisKeyword.Kind() != SyntaxKind.None && !allowThis) - { - diagnostics.Add(ErrorCode.ERR_ThisInBadContext, thisKeyword.GetLocation()); - } + if (parameter is null) continue; - if (parameterSyntax is ParameterSyntax concreteParam) - { - if (concreteParam.IsArgList) - { - arglistToken = concreteParam.Identifier; - // The native compiler produces "Expected type" here, in the parser. Roslyn produces - // the somewhat more informative "arglist not valid" error. - if (paramsKeyword.Kind() != SyntaxKind.None - || refnessKeyword.Kind() != SyntaxKind.None - || thisKeyword.Kind() != SyntaxKind.None) - { - // CS1669: __arglist is not valid in this context - diagnostics.Add(ErrorCode.ERR_IllegalVarArgs, arglistToken.GetLocation()); - } + builder.Add(parameter); + ++parameterIndex; + } - if (parameterIndex != lastIndex) - { - // CS0257: An __arglist parameter must be the last parameter in a parameter list - diagnostics.Add(ErrorCode.ERR_VarargsLast, concreteParam.GetLocation()); - } + ImmutableArray parameters = builder.ToImmutableAndFree(); - continue; - } + if (!parsingFunctionPointer) + { + var methodOwner = owner as MethodSymbol; + var typeParameters = (object?)methodOwner != null ? + methodOwner.TypeParameters : + []; + + ImmutableArray parametersForNameConflict = parameters.Cast(); - if (concreteParam.Default != null && firstDefault == -1) + if (owner.GetIsNewExtensionMember()) + { + typeParameters = owner.ContainingType.TypeParameters.Concat(typeParameters); + + if (owner.ContainingType.ExtensionParameter is { Name: not "" } receiver) { - firstDefault = parameterIndex; + parametersForNameConflict = parametersForNameConflict.Insert(0, receiver); } } - Debug.Assert(parameterSyntax.Type != null); - var parameterType = withTypeParametersBinder.BindType(parameterSyntax.Type, diagnostics, suppressUseSiteDiagnostics: suppressUseSiteDiagnostics); + Debug.Assert(methodOwner?.MethodKind != MethodKind.LambdaMethod); + bool allowShadowingNames = withTypeParametersBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions) && + methodOwner?.MethodKind == MethodKind.LocalFunction; - if (!allowRefOrOut && (refKind == RefKind.Ref || refKind == RefKind.Out)) - { - Debug.Assert(refnessKeyword.Kind() != SyntaxKind.None); + withTypeParametersBinder.ValidateParameterNameConflicts(typeParameters, parametersForNameConflict, allowShadowingNames, diagnostics); + } - // error CS0631: ref and out are not valid in this context - diagnostics.Add(ErrorCode.ERR_IllegalRefParam, refnessKeyword.GetLocation()); - } + return parameters; + } + + internal enum ParameterContext + { + Default, + FunctionPointer, + Lambda, + AnonymousMethod, + ExtensionReceiverParameter + } - TParameterSymbol parameter = parameterCreationFunc(withTypeParametersBinder, owner, parameterType, parameterSyntax, refKind, parameterIndex, paramsKeyword, thisKeyword, addRefReadOnlyModifier, scope, diagnostics); + private static TParameterSymbol? MakeParameter( + Binder withTypeParametersBinder, + TOwningSymbol owner, + TParameterSyntax parameterSyntax, + ref SyntaxToken arglistToken, + BindingDiagnosticBag diagnostics, + bool allowRefOrOut, + bool allowThis, + bool addRefReadOnlyModifier, + bool suppressUseSiteDiagnostics, + int lastIndex, + Func parameterCreationFunc, + int parameterIndex, + ref int firstDefault, + ParameterContext parameterContext) + where TParameterSyntax : BaseParameterSyntax + where TParameterSymbol : ParameterSymbol + where TOwningSymbol : Symbol + { + Debug.Assert(parameterContext is ParameterContext.Default or ParameterContext.FunctionPointer or ParameterContext.ExtensionReceiverParameter); + CheckParameterModifiers(parameterSyntax, diagnostics, parameterContext); - Debug.Assert(parameter is SourceComplexParameterSymbolBase || !parameter.IsParams); // Only SourceComplexParameterSymbolBase validates 'params' type. - Debug.Assert(parameter is SourceComplexParameterSymbolBase || parameter is not SourceParameterSymbol s || s.DeclaredScope == ScopedKind.None); // Only SourceComplexParameterSymbolBase validates 'scope'. - ReportParameterErrors(owner, parameterSyntax, parameter.Ordinal, lastParameterIndex: lastIndex, parameter.IsParams, parameter.TypeWithAnnotations, - parameter.RefKind, parameter.ContainingSymbol, thisKeyword, paramsKeyword, firstDefault, diagnostics); + bool inExtension = parameterContext is ParameterContext.ExtensionReceiverParameter; + var refKind = GetModifiers(parameterSyntax.Modifiers, ignoreParams: inExtension, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out ScopedKind scope); - builder.Add(parameter); - ++parameterIndex; + if (thisKeyword.Kind() != SyntaxKind.None && !allowThis) + { + diagnostics.Add(ErrorCode.ERR_ThisInBadContext, thisKeyword.GetLocation()); } - ImmutableArray parameters = builder.ToImmutableAndFree(); - - if (!parsingFunctionPointer) + if (parameterSyntax is ParameterSyntax concreteParam) { - var methodOwner = owner as MethodSymbol; - var typeParameters = (object)methodOwner != null ? - methodOwner.TypeParameters : - default(ImmutableArray); + if (concreteParam.IsArgList) + { + arglistToken = concreteParam.Identifier; + // The native compiler produces "Expected type" here, in the parser. Roslyn produces + // the somewhat more informative "arglist not valid" error. + if (paramsKeyword.Kind() != SyntaxKind.None + || refnessKeyword.Kind() != SyntaxKind.None + || thisKeyword.Kind() != SyntaxKind.None + || inExtension) + { + // CS1669: __arglist is not valid in this context + diagnostics.Add(ErrorCode.ERR_IllegalVarArgs, arglistToken.GetLocation()); + } - Debug.Assert(methodOwner?.MethodKind != MethodKind.LambdaMethod); - bool allowShadowingNames = withTypeParametersBinder.Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNameShadowingInNestedFunctions) && - methodOwner?.MethodKind == MethodKind.LocalFunction; + if (parameterIndex != lastIndex && !inExtension) + { + // CS0257: An __arglist parameter must be the last parameter in a parameter list + diagnostics.Add(ErrorCode.ERR_VarargsLast, concreteParam.GetLocation()); + } + + return null; + } - withTypeParametersBinder.ValidateParameterNameConflicts(typeParameters, parameters.Cast(), allowShadowingNames, diagnostics); + if (concreteParam.Default != null && firstDefault == -1) + { + firstDefault = parameterIndex; + } } - return parameters; + Debug.Assert(parameterSyntax.Type != null); + var parameterType = withTypeParametersBinder.BindType(parameterSyntax.Type, diagnostics, suppressUseSiteDiagnostics: suppressUseSiteDiagnostics); + + if (!allowRefOrOut && (refKind == RefKind.Ref || refKind == RefKind.Out)) + { + Debug.Assert(refnessKeyword.Kind() != SyntaxKind.None); + + // error CS0631: ref and out are not valid in this context + diagnostics.Add(ErrorCode.ERR_IllegalRefParam, refnessKeyword.GetLocation()); + } + + TParameterSymbol parameter = parameterCreationFunc(withTypeParametersBinder, owner, parameterType, parameterSyntax, refKind, parameterIndex, paramsKeyword, thisKeyword, addRefReadOnlyModifier, scope, diagnostics); + + Debug.Assert(parameter is SourceComplexParameterSymbolBase || !parameter.IsParams); // Only SourceComplexParameterSymbolBase validates 'params' type. + Debug.Assert(parameter is SourceComplexParameterSymbolBase || parameter is not SourceParameterSymbol s || s.DeclaredScope == ScopedKind.None); // Only SourceComplexParameterSymbolBase validates 'scope'. + ReportParameterErrors(owner, parameterSyntax, parameter.Ordinal, lastParameterIndex: lastIndex, isParams: parameter.IsParams, typeWithAnnotations: parameter.TypeWithAnnotations, + refKind: parameter.RefKind, containingSymbol: parameter.ContainingSymbol, thisKeyword: thisKeyword, paramsKeyword: paramsKeyword, firstDefault: firstDefault, diagnostics: diagnostics); + return parameter; } -#nullable enable internal static void EnsureRefKindAttributesExist(PEModuleBuilder moduleBuilder, ImmutableArray parameters) { EnsureRefKindAttributesExist(moduleBuilder.Compilation, parameters, diagnostics: null, modifyCompilation: false, moduleBuilder); @@ -419,12 +524,8 @@ private static void EnsureNullableAttributeExists(CSharpCompilation compilation, internal static void CheckParameterModifiers( BaseParameterSyntax parameter, BindingDiagnosticBag diagnostics, - bool parsingFunctionPointerParams, - bool parsingLambdaParams, - bool parsingAnonymousMethodParams) + ParameterContext parameterContext) { - Debug.Assert(!parsingLambdaParams || !parsingAnonymousMethodParams); - var seenThis = false; var seenRef = false; var seenOut = false; @@ -446,7 +547,8 @@ internal static void CheckParameterModifiers( Binder.CheckFeatureAvailability(modifier, MessageID.IDS_FeatureRefExtensionMethods, diagnostics); } - if (parsingLambdaParams || parsingAnonymousMethodParams) + // `this` on extension parameters was already reported elsewhere + if (parameterContext is ParameterContext.Lambda or ParameterContext.AnonymousMethod) { diagnostics.Add(ErrorCode.ERR_ThisInBadContext, modifier.GetLocation()); } @@ -505,6 +607,10 @@ internal static void CheckParameterModifiers( { addERR_BadParameterModifiers(diagnostics, modifier, SyntaxKind.ThisKeyword); } + else if (parameterContext is ParameterContext.ExtensionReceiverParameter) + { + addERR_BadParameterModifiers(diagnostics, modifier, SyntaxKind.ExtensionKeyword); + } else if (seenParams) { addERR_ParamsCantBeWithModifier(diagnostics, modifier, SyntaxKind.OutKeyword); @@ -523,8 +629,8 @@ internal static void CheckParameterModifiers( } break; - case SyntaxKind.ParamsKeyword when !parsingFunctionPointerParams: - if (parsingAnonymousMethodParams) + case SyntaxKind.ParamsKeyword when parameterContext is not ParameterContext.FunctionPointer: + if (parameterContext is ParameterContext.AnonymousMethod or ParameterContext.ExtensionReceiverParameter) { diagnostics.Add(ErrorCode.ERR_IllegalParams, modifier.GetLocation()); } @@ -553,7 +659,7 @@ internal static void CheckParameterModifiers( seenParams = true; } - if (parsingLambdaParams) + if (parameterContext is ParameterContext.Lambda) { MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, modifier); @@ -594,7 +700,7 @@ internal static void CheckParameterModifiers( } break; - case SyntaxKind.ScopedKeyword when !parsingFunctionPointerParams: + case SyntaxKind.ScopedKeyword when parameterContext is not ParameterContext.FunctionPointer: ModifierUtils.CheckScopedModifierAvailability(parameter, modifier, diagnostics); Debug.Assert(!seenIn); Debug.Assert(!seenOut); @@ -620,8 +726,8 @@ internal static void CheckParameterModifiers( } break; - case SyntaxKind.ParamsKeyword when parsingFunctionPointerParams: - case SyntaxKind.ScopedKeyword when parsingFunctionPointerParams: + case SyntaxKind.ParamsKeyword when parameterContext is ParameterContext.FunctionPointer: + case SyntaxKind.ScopedKeyword when parameterContext is ParameterContext.FunctionPointer: diagnostics.Add(ErrorCode.ERR_BadFuncPointerParamModifier, modifier.GetLocation(), SyntaxFacts.GetText(modifier.Kind())); break; @@ -682,12 +788,21 @@ public static void ReportParameterErrors( } else if (!typeWithAnnotations.IsDefault && typeWithAnnotations.IsStatic) { - Debug.Assert(containingSymbol is null || (containingSymbol is FunctionPointerMethodSymbol or { ContainingType: not null })); - // error CS0721: '{0}': static types cannot be used as parameters - diagnostics.Add( - ErrorFacts.GetStaticClassParameterCode(containingSymbol?.ContainingType?.IsInterfaceType() ?? false), - syntax.Type?.Location ?? syntax.GetLocation(), - typeWithAnnotations.Type); + bool inExtension = owner is SynthesizedExtensionMarker; + bool isNamed = syntax is ParameterSyntax parameterSyntax && parameterSyntax.Identifier.Kind() != SyntaxKind.None; + + Debug.Assert(containingSymbol is null + || (containingSymbol is FunctionPointerMethodSymbol or { ContainingType: not null }) + || inExtension); + + if (!inExtension || isNamed) + { + // error CS0721: '{0}': static types cannot be used as parameters + diagnostics.Add( + ErrorFacts.GetStaticClassParameterCode(containingSymbol?.ContainingType?.IsInterfaceType() ?? false), + syntax.Type?.Location ?? syntax.GetLocation(), + typeWithAnnotations.Type); + } } else if (firstDefault != -1 && parameterIndex > firstDefault && !isDefault && !isParams) { @@ -740,12 +855,19 @@ internal static bool ReportDefaultParameterErrors( // stick it in the parameter because we want to be able to analyze it for // IntelliSense purposes. + bool inExtension = parameter.ContainingSymbol is SynthesizedExtensionMarker; + if (inExtension) + { + diagnostics.Add(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, parameterSyntax.GetLocation()); + return true; + } + TypeSymbol parameterType = parameter.Type; CompoundUseSiteInfo useSiteInfo = binder.GetNewCompoundUseSiteInfo(diagnostics); Conversion conversion = binder.Conversions.ClassifyImplicitConversionFromExpression(defaultExpression, parameterType, ref useSiteInfo); diagnostics.Add(defaultExpression.Syntax, useSiteInfo); - var refKind = GetModifiers(parameterSyntax.Modifiers, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out _); + var refKind = GetModifiers(parameterSyntax.Modifiers, ignoreParams: inExtension, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out _); // CONSIDER: We are inconsistent here regarding where the error is reported; is it // CONSIDER: reported on the parameter name, or on the value of the initializer? @@ -926,7 +1048,7 @@ internal static MethodSymbol FindContainingGenericMethod(Symbol symbol) return null; } - internal static RefKind GetModifiers(SyntaxTokenList modifiers, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out ScopedKind scope) + internal static RefKind GetModifiers(SyntaxTokenList modifiers, bool ignoreParams, out SyntaxToken refnessKeyword, out SyntaxToken paramsKeyword, out SyntaxToken thisKeyword, out ScopedKind scope) { var refKind = RefKind.None; bool isScoped = false; @@ -961,7 +1083,10 @@ internal static RefKind GetModifiers(SyntaxTokenList modifiers, out SyntaxToken } break; case SyntaxKind.ParamsKeyword: - paramsKeyword = modifier; + if (!ignoreParams) + { + paramsKeyword = modifier; + } break; case SyntaxKind.ThisKeyword: thisKeyword = modifier; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs b/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs index 96aeb91c91d15..de44539ca6d5e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/QuickAttributeChecker.cs @@ -48,6 +48,7 @@ private static QuickAttributeChecker CreatePredefinedQuickAttributeChecker() var result = new QuickAttributeChecker(); result.AddName(AttributeDescription.TypeIdentifierAttribute.Name, QuickAttributes.TypeIdentifier); result.AddName(AttributeDescription.TypeForwardedToAttribute.Name, QuickAttributes.TypeForwardedTo); + result.AddName(AttributeDescription.IndexerNameAttribute.Name, QuickAttributes.IndexerName); result.AddName(AttributeDescription.AssemblyKeyNameAttribute.Name, QuickAttributes.AssemblyKeyName); result.AddName(AttributeDescription.AssemblyKeyFileAttribute.Name, QuickAttributes.AssemblyKeyFile); result.AddName(AttributeDescription.AssemblySignatureKeyAttribute.Name, QuickAttributes.AssemblySignatureKey); @@ -144,9 +145,10 @@ internal enum QuickAttributes : byte None = 0, TypeIdentifier = 1 << 0, TypeForwardedTo = 1 << 1, - AssemblyKeyName = 1 << 2, - AssemblyKeyFile = 1 << 3, - AssemblySignatureKey = 1 << 4, + IndexerName = 1 << 2, + AssemblyKeyName = 1 << 3, + AssemblyKeyFile = 1 << 4, + AssemblySignatureKey = 1 << 5, Last = AssemblySignatureKey, } @@ -171,6 +173,10 @@ public static QuickAttributes GetQuickAttributes(string name, bool inAttribute) { result |= QuickAttributes.TypeForwardedTo; } + else if (matches(AttributeDescription.IndexerNameAttribute)) + { + result |= QuickAttributes.IndexerName; + } else if (matches(AttributeDescription.AssemblyKeyNameAttribute)) { result |= QuickAttributes.AssemblyKeyName; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index b6c467e7338a2..90d064f042a7f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -1666,7 +1666,7 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute } attributesBag = null; - Func attributeMatches = attribute switch + Func attributeMatches = attribute switch { QuickAttributes.AssemblySignatureKey => isPossibleAssemblySignatureKeyAttribute, QuickAttributes.AssemblyKeyName => isPossibleAssemblyKeyNameAttribute, @@ -1678,19 +1678,19 @@ internal CommonAssemblyWellKnownAttributeData GetSourceDecodedWellKnownAttribute return (CommonAssemblyWellKnownAttributeData?)attributesBag?.DecodedWellKnownAttributeData; - bool isPossibleAssemblySignatureKeyAttribute(AttributeSyntax node) + bool isPossibleAssemblySignatureKeyAttribute(AttributeSyntax node, Binder? rootBinderOpt) { QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker; return checker.IsPossibleMatch(node, QuickAttributes.AssemblySignatureKey); } - bool isPossibleAssemblyKeyNameAttribute(AttributeSyntax node) + bool isPossibleAssemblyKeyNameAttribute(AttributeSyntax node, Binder? rootBinderOpt) { QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker; return checker.IsPossibleMatch(node, QuickAttributes.AssemblyKeyName); } - bool isPossibleAssemblyKeyFileAttribute(AttributeSyntax node) + bool isPossibleAssemblyKeyFileAttribute(AttributeSyntax node, Binder? rootBinderOpt) { QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker; return checker.IsPossibleMatch(node, QuickAttributes.AssemblyKeyFile); @@ -1756,7 +1756,7 @@ private static void AfterPossibleForwardedTypesAttributePartBound(AttributeSynta Debug.Assert(removed); } - private bool IsPossibleForwardedTypesAttribute(AttributeSyntax node) + private bool IsPossibleForwardedTypesAttribute(AttributeSyntax node, Binder rootBinderOpt) { QuickAttributeChecker checker = this.DeclaringCompilation.GetBinderFactory(node.SyntaxTree).GetBinder(node).QuickAttributeChecker; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index cb35f330c2699..6bbab27a68ff2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -471,12 +471,7 @@ protected SourceParameterSymbol? PartialImplementationPart { get { - ImmutableArray implParameters = this.ContainingSymbol switch - { - SourceMemberMethodSymbol { PartialImplementationPart.Parameters: { } parameters } => parameters, - SourcePropertySymbol { PartialImplementationPart.Parameters: { } parameters } => parameters, - _ => default - }; + ImmutableArray implParameters = this.ContainingSymbol.GetPartialImplementationPart()?.GetParameters() ?? default; if (implParameters.IsDefault) { @@ -492,12 +487,7 @@ protected SourceParameterSymbol? PartialDefinitionPart { get { - ImmutableArray defParameters = this.ContainingSymbol switch - { - SourceMemberMethodSymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters, - SourcePropertySymbol { PartialDefinitionPart.Parameters: { } parameters } => parameters, - _ => default - }; + ImmutableArray defParameters = this.ContainingSymbol.GetPartialDefinitionPart()?.GetParameters() ?? default; if (defParameters.IsDefault) { @@ -1598,9 +1588,8 @@ void validateParamsType(BindingDiagnosticBag diagnostics) Debug.Assert(!addMethods.IsDefaultOrEmpty); - if (addMethods[0].IsStatic) // No need to check other methods, extensions are never mixed with instance methods + if (addMethods[0].IsExtensionMethod || addMethods[0].GetIsNewExtensionMember()) // No need to check other methods, extensions are never mixed with instance methods { - Debug.Assert(addMethods[0].IsExtensionMethod); diagnostics.Add(ErrorCode.ERR_ParamsCollectionExtensionAddMethod, syntax, Type); return; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs index 512755dd22f60..a570212d8250e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbol.cs @@ -2,9 +2,10 @@ // 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.Diagnostics; +using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; @@ -12,6 +13,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { internal sealed class SourceConstructorSymbol : SourceConstructorSymbolBase { + private SourceConstructorSymbol? _otherPartOfPartial; + +#nullable disable public static SourceConstructorSymbol CreateConstructorSymbol( SourceMemberContainerTypeSymbol containingType, ConstructorDeclarationSyntax syntax, @@ -61,6 +65,11 @@ private SourceConstructorSymbol( } } + if (IsPartialDefinition && syntax.Initializer is { } initializer) + { + diagnostics.Add(ErrorCode.ERR_PartialConstructorInitializer, initializer, this); + } + if (methodKind == MethodKind.StaticConstructor) { CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasAnyBody, diagnostics); @@ -88,12 +97,13 @@ private static (DeclarationModifiers, Flags) MakeModifiersAndFlags( out bool modifierErrors, out bool report_ERR_StaticConstructorWithAccessModifiers) { - DeclarationModifiers declarationModifiers = MakeModifiers(containingType, syntax, methodKind, syntax.HasAnyBody(), location, diagnostics, out modifierErrors, out report_ERR_StaticConstructorWithAccessModifiers); - Flags flags = MakeFlags( - methodKind, RefKind.None, declarationModifiers, returnsVoid: true, returnsVoidIsSet: true, - isExpressionBodied: syntax.IsExpressionBodied(), isExtensionMethod: false, isVarArg: syntax.IsVarArg(), + bool hasAnyBody = syntax.HasAnyBody(); + DeclarationModifiers declarationModifiers = MakeModifiers(containingType, syntax, methodKind, hasAnyBody, location, diagnostics, out modifierErrors, out bool hasExplicitAccessModifier, out report_ERR_StaticConstructorWithAccessModifiers); + Flags flags = new Flags( + methodKind, RefKind.None, declarationModifiers, returnsVoid: true, returnsVoidIsSet: true, hasAnyBody: hasAnyBody, + isExpressionBodied: syntax.IsExpressionBodied(), isExtensionMethod: false, isVararg: syntax.IsVarArg(), isNullableAnalysisEnabled: isNullableAnalysisEnabled, isExplicitInterfaceImplementation: false, - hasThisInitializer: hasThisInitializer); + hasThisInitializer: hasThisInitializer, hasExplicitAccessModifier: hasExplicitAccessModifier); return (declarationModifiers, flags); } @@ -121,19 +131,24 @@ protected override CSharpSyntaxNode GetInitializer() private static DeclarationModifiers MakeModifiers( NamedTypeSymbol containingType, ConstructorDeclarationSyntax syntax, MethodKind methodKind, bool hasBody, Location location, BindingDiagnosticBag diagnostics, - out bool modifierErrors, out bool report_ERR_StaticConstructorWithAccessModifiers) + out bool modifierErrors, out bool hasExplicitAccessModifier, out bool report_ERR_StaticConstructorWithAccessModifiers) { var defaultAccess = (methodKind == MethodKind.StaticConstructor) ? DeclarationModifiers.None : DeclarationModifiers.Private; // Check that the set of modifiers is allowed - const DeclarationModifiers allowedModifiers = + DeclarationModifiers allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.Static | DeclarationModifiers.Extern | DeclarationModifiers.Unsafe; + if (methodKind == MethodKind.Constructor) + { + allowedModifiers |= DeclarationModifiers.Partial; + } + bool isInterface = containingType.IsInterface; - var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors, out hasExplicitAccessModifier); report_ERR_StaticConstructorWithAccessModifiers = false; if (methodKind == MethodKind.StaticConstructor) @@ -164,7 +179,7 @@ private static DeclarationModifiers MakeModifiers( private void CheckModifiers(MethodKind methodKind, bool hasBody, Location location, BindingDiagnosticBag diagnostics) { - if (!hasBody && !IsExtern) + if (!hasBody && !IsExtern && !IsPartial) { diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this); } @@ -176,13 +191,45 @@ private void CheckModifiers(MethodKind methodKind, bool hasBody, Location locati { diagnostics.Add(ErrorCode.ERR_ConstructorInStaticClass, location); } + else if (IsPartial && !ContainingType.IsPartial()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberOnlyInPartialClass, location); + } } +#nullable enable internal override OneOrMany> GetAttributeDeclarations() { - return OneOrMany.Create(((ConstructorDeclarationSyntax)this.SyntaxNode).AttributeLists); + // Attributes on partial constructors are owned by the definition part. + // If this symbol has a non-null PartialDefinitionPart, we should have accessed this method through that definition symbol instead. + Debug.Assert(PartialDefinitionPart is null); + + if (SourcePartialImplementationPart is { } implementationPart) + { + return OneOrMany.Create( + this.AttributeDeclarationSyntaxList, + implementationPart.AttributeDeclarationSyntaxList); + } + + return OneOrMany.Create(this.AttributeDeclarationSyntaxList); } + private SyntaxList AttributeDeclarationSyntaxList + { + get + { + if (this.ContainingType is SourceMemberContainerTypeSymbol { AnyMemberHasAttributes: true }) + { + return this.GetSyntax().AttributeLists; + } + + return default; + } + } + + protected override SourceMemberMethodSymbol? BoundAttributesSource => SourcePartialDefinitionPart; +#nullable disable + internal override bool IsNullableAnalysisEnabled() => flags.HasThisInitializer ? flags.IsNullableAnalysisEnabled @@ -213,5 +260,103 @@ protected override bool IsWithinExpressionOrBlockBody(int position, out int offs offset = -1; return false; } + +#nullable enable + internal sealed override void ForceComplete(SourceLocation? locationOpt, Predicate? filter, CancellationToken cancellationToken) + { + SourcePartialImplementationPart?.ForceComplete(locationOpt, filter, cancellationToken); + base.ForceComplete(locationOpt, filter, cancellationToken); + } + + protected override void PartialConstructorChecks(BindingDiagnosticBag diagnostics) + { + if (SourcePartialImplementationPart is { } implementation) + { + PartialConstructorChecks(implementation, diagnostics); + } + } + + private void PartialConstructorChecks(SourceConstructorSymbol implementation, BindingDiagnosticBag diagnostics) + { + Debug.Assert(this.IsPartialDefinition); + Debug.Assert(!ReferenceEquals(this, implementation)); + Debug.Assert(ReferenceEquals(this.OtherPartOfPartial, implementation)); + + if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(this, implementation)) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberInconsistentTupleNames, implementation.GetFirstLocation(), this, implementation); + } + else if (!MemberSignatureComparer.PartialMethodsStrictComparer.Equals(this, implementation) + || !Parameters.SequenceEqual(implementation.Parameters, static (a, b) => a.Name == b.Name)) + { + diagnostics.Add(ErrorCode.WRN_PartialMemberSignatureDifference, implementation.GetFirstLocation(), + new FormattedSymbol(this, SymbolDisplayFormat.MinimallyQualifiedFormat), + new FormattedSymbol(implementation, SymbolDisplayFormat.MinimallyQualifiedFormat)); + } + + if (IsUnsafe != implementation.IsUnsafe && this.CompilationAllowsUnsafe()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberUnsafeDifference, implementation.GetFirstLocation()); + } + + if (this.IsParams() != implementation.IsParams()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberParamsDifference, implementation.GetFirstLocation()); + } + + if (DeclaredAccessibility != implementation.DeclaredAccessibility + || HasExplicitAccessModifier != implementation.HasExplicitAccessModifier) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementation.GetFirstLocation()); + } + + Debug.Assert(this.ParameterCount == implementation.ParameterCount); + for (var i = 0; i < this.ParameterCount; i++) + { + // An error is only reported for a modifier difference here, regardless of whether the difference is safe or not. + // Presence of UnscopedRefAttribute is also not considered when checking partial signatures, because when the attribute is used, it will affect both parts the same way. + var definitionParameter = (SourceParameterSymbol)this.Parameters[i]; + var implementationParameter = (SourceParameterSymbol)implementation.Parameters[i]; + if (definitionParameter.DeclaredScope != implementationParameter.DeclaredScope) + { + diagnostics.Add(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, implementation.GetFirstLocation(), new FormattedSymbol(implementation.Parameters[i], SymbolDisplayFormat.ShortFormat)); + } + } + } + + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier; + + private bool HasAnyBody => flags.HasAnyBody; + + private bool HasExplicitAccessModifier => flags.HasExplicitAccessModifier; + + internal bool IsPartialDefinition => IsPartial && !HasAnyBody && !HasExternModifier; + + internal bool IsPartialImplementation => IsPartial && (HasAnyBody || HasExternModifier); + + internal SourceConstructorSymbol? OtherPartOfPartial => _otherPartOfPartial; + + internal SourceConstructorSymbol? SourcePartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null; + + internal SourceConstructorSymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null; + + public sealed override MethodSymbol? PartialDefinitionPart => SourcePartialDefinitionPart; + + public sealed override MethodSymbol? PartialImplementationPart => SourcePartialImplementationPart; + + internal static void InitializePartialConstructorParts(SourceConstructorSymbol definition, SourceConstructorSymbol implementation) + { + Debug.Assert(definition.IsPartialDefinition); + Debug.Assert(implementation.IsPartialImplementation); + + Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || ReferenceEquals(alreadySetImplPart, implementation)); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || ReferenceEquals(alreadySetDefPart, definition)); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + + Debug.Assert(ReferenceEquals(definition._otherPartOfPartial, implementation)); + Debug.Assert(ReferenceEquals(implementation._otherPartOfPartial, definition)); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index 246b307c70ef8..8b2f328bc6ae7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -97,6 +97,13 @@ internal sealed override void AfterAddingTypeMembersChecks(ConversionsBase conve { parameter.Type.CheckAllConstraints(compilation, conversions, parameter.GetFirstLocation(), diagnostics); } + + PartialConstructorChecks(diagnostics); + } + + protected virtual void PartialConstructorChecks(BindingDiagnosticBag diagnostics) + { + Debug.Assert(!IsPartial); } public sealed override bool IsImplicitlyDeclared @@ -259,11 +266,5 @@ internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAt return base.EarlyDecodeWellKnownAttribute(ref arguments); } - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - AddRequiredMembersMarkerAttributes(ref attributes, this); - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs index 7ffd478df9134..8ffcd5b794056 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventAccessorSymbol.cs @@ -77,9 +77,32 @@ public override Accessibility DeclaredAccessibility } } +#nullable enable + protected override SourceMemberMethodSymbol? BoundAttributesSource => (SourceMemberMethodSymbol?)PartialDefinitionPart; + internal override OneOrMany> GetAttributeDeclarations() { - return OneOrMany.Create(GetSyntax().AttributeLists); + Debug.Assert(PartialImplementationPart is null); + + // If this is a partial event, the corresponding partial definition cannot have any accessor attributes + // (there are no explicit accessors in source on the definition part - it has a field-like syntax). + Debug.Assert(PartialDefinitionPart is null + or SourceEventAccessorSymbol { AssociatedEvent.MemberSyntax: EventFieldDeclarationSyntax }); + + return OneOrMany.Create(this.AttributeDeclarationSyntaxList); + } + + internal SyntaxList AttributeDeclarationSyntaxList + { + get + { + if (this.AssociatedEvent.containingType.AnyMemberHasAttributes) + { + return this.GetSyntax().AttributeLists; + } + + return default; + } } public override bool IsImplicitlyDeclared diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs index cc0b0475d0b68..b5faa9b49e15d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceCustomEventSymbol.cs @@ -170,6 +170,8 @@ internal SourceCustomEventSymbol(SourceMemberContainerTypeSymbol containingType, ImmutableArray.Create(explicitlyImplementedEvent); } + protected override bool AccessorsHaveImplementation => true; + public override TypeWithAnnotations TypeWithAnnotations { get { return _type; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs index 5e6846f3883b4..1252f9f3d5e83 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceDestructorSymbol.cs @@ -28,7 +28,7 @@ internal SourceDestructorSymbol( bool hasBlockBody = syntax.Body != null; bool isExpressionBodied = IsExpressionBodied; - if (syntax.Identifier.ValueText != containingType.Name) + if (syntax.Identifier.ValueText != containingType.Name && !containingType.IsExtension) { diagnostics.Add(ErrorCode.ERR_BadDestructorName, syntax.Identifier.GetLocation()); } @@ -130,7 +130,7 @@ private static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingType { // Check that the set of modifiers is allowed const DeclarationModifiers allowedModifiers = DeclarationModifiers.Extern | DeclarationModifiers.Unsafe; - var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: containingType.IsInterface, modifiers, DeclarationModifiers.None, allowedModifiers, location, diagnostics, out modifierErrors); + var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: containingType.IsInterface, modifiers, DeclarationModifiers.None, allowedModifiers, location, diagnostics, out modifierErrors, out _); mods = (mods & ~DeclarationModifiers.AccessibilityMask) | DeclarationModifiers.Protected; // we mark destructors protected in the symbol table diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs index 7a3aa3b89b479..8cc0635837f60 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventAccessorSymbol.cs @@ -233,5 +233,22 @@ internal sealed override int TryGetOverloadResolutionPriority() { return 0; } + +#nullable enable + protected abstract override SourceMemberMethodSymbol? BoundAttributesSource { get; } + + public sealed override MethodSymbol? PartialImplementationPart => _event is { IsPartialDefinition: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.EventAdd ? other.AddMethod : other.RemoveMethod) + : null; + + public sealed override MethodSymbol? PartialDefinitionPart => _event is { IsPartialImplementation: true, OtherPartOfPartial: { } other } + ? (MethodKind == MethodKind.EventAdd ? other.AddMethod : other.RemoveMethod) + : null; + + internal bool IsPartialDefinition => _event.IsPartialDefinition; + + internal bool IsPartialImplementation => _event.IsPartialImplementation; + + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : base.IsExtern; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs index f59aa15ba20fc..7487788040b59 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEventSymbol.cs @@ -22,9 +22,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// internal abstract class SourceEventSymbol : EventSymbol, IAttributeTargetSymbol { + private SourceEventSymbol? _otherPartOfPartial; + private readonly Location _location; private readonly SyntaxReference _syntaxRef; private readonly DeclarationModifiers _modifiers; + private readonly bool _hasExplicitAccessModifier; internal readonly SourceMemberContainerTypeSymbol containingType; private SymbolCompletionState _state; @@ -52,8 +55,7 @@ internal SourceEventSymbol( _syntaxRef = syntax.GetReference(); var isExplicitInterfaceImplementation = interfaceSpecifierSyntaxOpt != null; - bool modifierErrors; - _modifiers = MakeModifiers(modifiers, isExplicitInterfaceImplementation, isFieldLike, _location, diagnostics, out modifierErrors); + _modifiers = MakeModifiers(modifiers, isExplicitInterfaceImplementation, isFieldLike, _location, diagnostics, out _, out _hasExplicitAccessModifier); this.CheckAccessibility(_location, diagnostics, isExplicitInterfaceImplementation); } @@ -72,6 +74,8 @@ internal sealed override bool HasComplete(CompletionPart part) internal override void ForceComplete(SourceLocation? locationOpt, Predicate? filter, CancellationToken cancellationToken) { + SourcePartialImplementationPart?.ForceComplete(locationOpt, filter, cancellationToken); + if (filter?.Invoke(this) == false) { return; @@ -137,25 +141,34 @@ internal SyntaxList AttributeDeclarationSyntaxList { get { - if (this.containingType.AnyMemberHasAttributes) + if (this.containingType.AnyMemberHasAttributes && MemberSyntax is { } memberSyntax) + { + return memberSyntax.AttributeLists; + } + + return default; + } + } + + internal MemberDeclarationSyntax? MemberSyntax + { + get + { + if (this.CSharpSyntaxNode is { } syntax) { - var syntax = this.CSharpSyntaxNode; - if (syntax != null) + switch (syntax.Kind()) { - switch (syntax.Kind()) - { - case SyntaxKind.EventDeclaration: - return ((EventDeclarationSyntax)syntax).AttributeLists; - case SyntaxKind.VariableDeclarator: - Debug.Assert(syntax.Parent!.Parent is object); - return ((EventFieldDeclarationSyntax)syntax.Parent.Parent).AttributeLists; - default: - throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); - } + case SyntaxKind.EventDeclaration: + return (EventDeclarationSyntax)syntax; + case SyntaxKind.VariableDeclarator: + Debug.Assert(syntax.Parent?.Parent is not null); + return (EventFieldDeclarationSyntax)syntax.Parent.Parent; + default: + throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); } } - return default; + return null; } } @@ -190,8 +203,26 @@ protected abstract AttributeLocation AllowedAttributeLocations /// private CustomAttributesBag GetAttributesBag() { - if ((_lazyCustomAttributesBag == null || !_lazyCustomAttributesBag.IsSealed) && - LoadAndValidateAttributes(OneOrMany.Create(this.AttributeDeclarationSyntaxList), ref _lazyCustomAttributesBag)) + var bag = _lazyCustomAttributesBag; + if (bag != null && bag.IsSealed) + { + return bag; + } + + bool bagCreatedOnThisThread; + + if (SourcePartialDefinitionPart is { } definitionPart) + { + Debug.Assert(!ReferenceEquals(definitionPart, this)); + bag = definitionPart.GetAttributesBag(); + bagCreatedOnThisThread = Interlocked.CompareExchange(ref _lazyCustomAttributesBag, bag, null) == null; + } + else + { + bagCreatedOnThisThread = LoadAndValidateAttributes(this.GetAttributeDeclarations(), ref _lazyCustomAttributesBag); + } + + if (bagCreatedOnThisThread) { DeclaringCompilation.SymbolDeclaredEvent(this); var wasCompletedThisThread = _state.NotePartComplete(CompletionPart.Attributes); @@ -202,6 +233,20 @@ private CustomAttributesBag GetAttributesBag() return _lazyCustomAttributesBag; } + private OneOrMany> GetAttributeDeclarations() + { + // Attributes on partial events are owned by the definition part. + // If this symbol has a non-null PartialDefinitionPart, we should have accessed this method through that definition symbol instead. + Debug.Assert(PartialDefinitionPart is null); + + if (SourcePartialImplementationPart is { } implementationPart) + { + return OneOrMany.Create(this.AttributeDeclarationSyntaxList, implementationPart.AttributeDeclarationSyntaxList); + } + + return OneOrMany.Create(this.AttributeDeclarationSyntaxList); + } + /// /// Gets the attributes applied on this symbol. /// Returns an empty array if there are no attributes. @@ -370,11 +415,13 @@ public sealed override bool IsAbstract get { return (_modifiers & DeclarationModifiers.Abstract) != 0; } } - public sealed override bool IsExtern + private bool HasExternModifier { get { return (_modifiers & DeclarationModifiers.Extern) != 0; } } + public sealed override bool IsExtern => PartialImplementationPart is { } implementation ? implementation.IsExtern : HasExternModifier; + public sealed override bool IsStatic { get { return (_modifiers & DeclarationModifiers.Static) != 0; } @@ -400,6 +447,11 @@ internal bool IsReadOnly get { return (_modifiers & DeclarationModifiers.ReadOnly) != 0; } } + private bool IsUnsafe + { + get { return (_modifiers & DeclarationModifiers.Unsafe) != 0; } + } + public sealed override Accessibility DeclaredAccessibility { get { return ModifierUtils.EffectiveAccessibility(_modifiers); } @@ -442,14 +494,15 @@ private void CheckAccessibility(Location location, BindingDiagnosticBag diagnost private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool explicitInterfaceImplementation, bool isFieldLike, Location location, - BindingDiagnosticBag diagnostics, out bool modifierErrors) + BindingDiagnosticBag diagnostics, out bool modifierErrors, + out bool hasExplicitAccessModifier) { bool isInterface = this.ContainingType.IsInterface; var defaultAccess = isInterface && !explicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; // Check that the set of modifiers is allowed - var allowedModifiers = DeclarationModifiers.Unsafe; + var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; if (!explicitInterfaceImplementation) { allowedModifiers |= DeclarationModifiers.New | @@ -501,7 +554,8 @@ private DeclarationModifiers MakeModifiers(SyntaxTokenList modifiers, bool expli } var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, - modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors, + out hasExplicitAccessModifier); ModifierUtils.CheckFeatureAvailabilityForStaticAbstractMembersInInterfacesIfNeeded(mods, explicitInterfaceImplementation, location, diagnostics); @@ -554,6 +608,18 @@ protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics) // '{0}' cannot be sealed because it is not an override diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this); } + else if (IsPartial && !ContainingType.IsPartial()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberOnlyInPartialClass, location); + } + else if (IsPartial && IsExplicitInterfaceImplementation) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberNotExplicit, location); + } + else if (IsPartial && IsAbstract) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberCannotBeAbstract, location); + } else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct) { // The modifier '{0}' is not valid for this item @@ -611,6 +677,11 @@ protected void CheckModifiersAndType(BindingDiagnosticBag diagnostics) diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); } + if (IsPartial) + { + ModifierUtils.CheckFeatureAvailabilityForPartialEventsAndConstructors(_location, diagnostics); + } + diagnostics.Add(location, useSiteInfo); } @@ -771,6 +842,11 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, CheckExplicitImplementationAccessor(AddMethod, explicitlyImplementedEvent.AddMethod, explicitlyImplementedEvent, diagnostics); CheckExplicitImplementationAccessor(RemoveMethod, explicitlyImplementedEvent.RemoveMethod, explicitlyImplementedEvent, diagnostics); } + + if (IsPartialDefinition && OtherPartOfPartial is { } implementation) + { + PartialEventChecks(implementation, diagnostics); + } } private void CheckExplicitImplementationAccessor(MethodSymbol? thisAccessor, MethodSymbol? otherAccessor, EventSymbol explicitlyImplementedEvent, BindingDiagnosticBag diagnostics) @@ -780,5 +856,88 @@ private void CheckExplicitImplementationAccessor(MethodSymbol? thisAccessor, Met diagnostics.Add(ErrorCode.ERR_ExplicitPropertyAddingAccessor, thisAccessor.GetFirstLocation(), thisAccessor, explicitlyImplementedEvent); } } + + private void PartialEventChecks(SourceEventSymbol implementation, BindingDiagnosticBag diagnostics) + { + Debug.Assert(this.IsPartialDefinition); + Debug.Assert(!ReferenceEquals(this, implementation)); + Debug.Assert(ReferenceEquals(this.OtherPartOfPartial, implementation)); + + if (!TypeWithAnnotations.Equals(implementation.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions)) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberTypeDifference, implementation.GetFirstLocation()); + } + else if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(this, implementation)) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberInconsistentTupleNames, implementation.GetFirstLocation(), this, implementation); + } + else if (!MemberSignatureComparer.PartialMethodsStrictComparer.Equals(this, implementation)) + { + diagnostics.Add(ErrorCode.WRN_PartialMemberSignatureDifference, implementation.GetFirstLocation(), + new FormattedSymbol(this, SymbolDisplayFormat.MinimallyQualifiedFormat), + new FormattedSymbol(implementation, SymbolDisplayFormat.MinimallyQualifiedFormat)); + } + + if (IsStatic != implementation.IsStatic) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberStaticDifference, implementation.GetFirstLocation()); + } + + if (IsUnsafe != implementation.IsUnsafe && this.CompilationAllowsUnsafe()) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberUnsafeDifference, implementation.GetFirstLocation()); + } + + if (DeclaredAccessibility != implementation.DeclaredAccessibility + || _hasExplicitAccessModifier != implementation._hasExplicitAccessModifier) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberAccessibilityDifference, implementation.GetFirstLocation()); + } + + if (IsVirtual != implementation.IsVirtual + || IsOverride != implementation.IsOverride + || IsSealed != implementation.IsSealed + || IsNew != implementation.IsNew) + { + diagnostics.Add(ErrorCode.ERR_PartialMemberExtendedModDifference, implementation.GetFirstLocation()); + } + } + + internal bool IsPartial => (this.Modifiers & DeclarationModifiers.Partial) != 0; + + /// + /// if this symbol corresponds to a semi-colon body declaration. + /// if this symbol corresponds to a declaration with custom and accessors. + /// + protected abstract bool AccessorsHaveImplementation { get; } + + internal sealed override bool IsPartialDefinition => IsPartial && !AccessorsHaveImplementation && !HasExternModifier; + + internal bool IsPartialImplementation => IsPartial && (AccessorsHaveImplementation || HasExternModifier); + + internal SourceEventSymbol? OtherPartOfPartial => _otherPartOfPartial; + + internal SourceEventSymbol? SourcePartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null; + + internal SourceEventSymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null; + + internal sealed override EventSymbol? PartialDefinitionPart => SourcePartialDefinitionPart; + + internal sealed override EventSymbol? PartialImplementationPart => SourcePartialImplementationPart; + + internal static void InitializePartialEventParts(SourceEventSymbol definition, SourceEventSymbol implementation) + { + Debug.Assert(definition.IsPartialDefinition); + Debug.Assert(implementation.IsPartialImplementation); + + Debug.Assert(definition._otherPartOfPartial is not { } alreadySetImplPart || ReferenceEquals(alreadySetImplPart, implementation)); + Debug.Assert(implementation._otherPartOfPartial is not { } alreadySetDefPart || ReferenceEquals(alreadySetDefPart, definition)); + + definition._otherPartOfPartial = implementation; + implementation._otherPartOfPartial = definition; + + Debug.Assert(ReferenceEquals(definition._otherPartOfPartial, implementation)); + Debug.Assert(ReferenceEquals(implementation._otherPartOfPartial, definition)); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs index be6a53393884a..3148d00a9ad3e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldLikeEventSymbol.cs @@ -5,25 +5,27 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Reflection; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// /// This class represents an event declared in source without explicit accessors. /// It implicitly has thread safe accessors and an associated field (of the same - /// name), unless it does not have an initializer and is either extern or inside + /// name), unless it does not have an initializer and is extern, partial, or inside /// an interface, in which case it only has accessors. /// internal sealed class SourceFieldLikeEventSymbol : SourceEventSymbol { private readonly string _name; private readonly TypeWithAnnotations _type; - private readonly SynthesizedEventAccessorSymbol _addMethod; - private readonly SynthesizedEventAccessorSymbol _removeMethod; + private readonly SourceEventAccessorSymbol _addMethod; + private readonly SourceEventAccessorSymbol _removeMethod; internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingType, Binder binder, SyntaxTokenList modifiers, VariableDeclaratorSyntax declaratorSyntax, BindingDiagnosticBag diagnostics) : base(containingType, declaratorSyntax, modifiers, isFieldLike: true, interfaceSpecifierSyntaxOpt: null, @@ -77,11 +79,15 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy { diagnostics.Add(ErrorCode.ERR_ExternEventInitializer, this.GetFirstLocation(), this); } + else if (this.IsPartial) + { + diagnostics.Add(ErrorCode.ERR_PartialEventInitializer, this.GetFirstLocation(), this); + } } // NOTE: if there's an initializer in source, we'd better create a backing field, regardless of // whether or not the initializer is legal. - if (hasInitializer || !(this.IsExtern || this.IsAbstract)) + if (hasInitializer || !(this.IsExtern || this.IsAbstract || this.IsPartial)) { AssociatedEventField = MakeAssociatedField(declaratorSyntax); // Don't initialize this.type - we'll just use the type of the field (which is lazy and handles var) @@ -108,14 +114,22 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy diagnostics.Add(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, this.GetFirstLocation()); } } - else if (!this.IsAbstract) + else if (!this.IsAbstract && !this.IsPartialDefinition) { diagnostics.Add(ErrorCode.ERR_EventNeedsBothAccessors, this.GetFirstLocation(), this); } } - _addMethod = new SynthesizedEventAccessorSymbol(this, isAdder: true, isExpressionBodied: false); - _removeMethod = new SynthesizedEventAccessorSymbol(this, isAdder: false, isExpressionBodied: false); + if (this.IsPartialDefinition) + { + _addMethod = new SourceEventDefinitionAccessorSymbol(this, isAdder: true, diagnostics); + _removeMethod = new SourceEventDefinitionAccessorSymbol(this, isAdder: false, diagnostics); + } + else + { + _addMethod = new SynthesizedEventAccessorSymbol(this, isAdder: true, isExpressionBodied: false); + _removeMethod = new SynthesizedEventAccessorSymbol(this, isAdder: false, isExpressionBodied: false); + } if (declarationSyntax.Variables[0] == declaratorSyntax) { @@ -126,6 +140,8 @@ internal SourceFieldLikeEventSymbol(SourceMemberContainerTypeSymbol containingTy declaratorDiagnostics.Free(); } + protected override bool AccessorsHaveImplementation => false; + /// /// Backing field for field-like event. Will be null if the event /// has no initializer and is either extern or inside an interface. @@ -163,9 +179,19 @@ protected override AttributeLocation AllowedAttributeLocations { get { - return (object?)AssociatedEventField != null ? - AttributeLocation.Event | AttributeLocation.Method | AttributeLocation.Field : - AttributeLocation.Event | AttributeLocation.Method; + var result = AttributeLocation.Event; + + if (!IsPartial || IsExtern) + { + result |= AttributeLocation.Method; + } + + if (AssociatedEventField is not null) + { + result |= AttributeLocation.Field; + } + + return result; } } @@ -191,5 +217,122 @@ internal override void ForceComplete(SourceLocation? locationOpt, Predicate + /// Accessor of a which is a partial definition. + /// + internal sealed class SourceEventDefinitionAccessorSymbol : SourceEventAccessorSymbol + { + internal SourceEventDefinitionAccessorSymbol( + SourceFieldLikeEventSymbol ev, + bool isAdder, + BindingDiagnosticBag diagnostics) + : base( + @event: ev, + syntaxReference: ev.SyntaxReference, + location: ev.Location, + explicitlyImplementedEventOpt: null, + aliasQualifierOpt: null, + isAdder: isAdder, + isIterator: false, + isNullableAnalysisEnabled: ev.DeclaringCompilation.IsNullableAnalysisEnabledIn(ev.CSharpSyntaxNode), + isExpressionBodied: false) + { + Debug.Assert(ev.IsPartialDefinition); + + CheckFeatureAvailabilityAndRuntimeSupport(ev.CSharpSyntaxNode, ev.Location, hasBody: false, diagnostics: diagnostics); + } + + public override Accessibility DeclaredAccessibility => AssociatedEvent.DeclaredAccessibility; + + public override bool IsImplicitlyDeclared => true; + + internal override bool GenerateDebugInfo => true; + + internal override ExecutableCodeBinder? TryGetBodyBinder(BinderFactory? binderFactoryOpt = null, bool ignoreAccessibility = false) + { + return null; + } + + protected override SourceMemberMethodSymbol? BoundAttributesSource + { + get + { + return IsExtern && this.MethodKind == MethodKind.EventAdd + ? (SourceMemberMethodSymbol?)this.AssociatedEvent.RemoveMethod + : null; + } + } + + protected override IAttributeTargetSymbol AttributeOwner + { + get + { + Debug.Assert(IsPartialDefinition); + + switch (PartialImplementationPart) + { + case SourceCustomEventAccessorSymbol: + return this; + + case SynthesizedEventAccessorSymbol: + Debug.Assert(IsExtern); + return AssociatedEvent; + + case null: + // Might happen in error scenarios. + return this; + + default: + Debug.Assert(false); + return this; + } + } + } + + internal override OneOrMany> GetAttributeDeclarations() + { + Debug.Assert(IsPartialDefinition); + + switch (PartialImplementationPart) + { + case SourceCustomEventAccessorSymbol customImplementationPart: + return OneOrMany.Create(customImplementationPart.AttributeDeclarationSyntaxList); + + case SynthesizedEventAccessorSymbol synthesizedImplementationPart: + Debug.Assert(IsExtern); + return OneOrMany.Create( + AssociatedEvent.AttributeDeclarationSyntaxList, + synthesizedImplementationPart.AssociatedEvent.AttributeDeclarationSyntaxList); + + case null: + // Might happen in error scenarios. + return OneOrMany>.Empty; + + default: + Debug.Assert(false); + return OneOrMany>.Empty; + } + } + + internal override MethodImplAttributes ImplementationAttributes + { + get + { + Debug.Assert(IsPartialDefinition); + + if (PartialImplementationPart is { } implementationPart) + { + return implementationPart.ImplementationAttributes; + } + + // This could happen in error scenarios (when the implementation part of a partial event is missing), + // but then we should not get to the emit stage and call this property. + Debug.Assert(false); + + return base.ImplementationAttributes; + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index abb58e2d51de5..621a0c877a50d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -292,54 +292,63 @@ private DeclarationModifiers MakeModifiers(TypeKind typeKind, BindingDiagnosticB DeclarationModifiers defaultAccess; // note: we give a specific diagnostic when a file-local type is nested - var allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.File; - - if (containingSymbol.Kind == SymbolKind.Namespace) + DeclarationModifiers allowedModifiers; + if (typeKind == TypeKind.Extension) { - defaultAccess = DeclarationModifiers.Internal; + allowedModifiers = DeclarationModifiers.None; + defaultAccess = DeclarationModifiers.Public; } else { - allowedModifiers |= DeclarationModifiers.New; + allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.File; - if (((NamedTypeSymbol)containingSymbol).IsInterface) + if (containingSymbol.Kind == SymbolKind.Namespace) { - defaultAccess = DeclarationModifiers.Public; + defaultAccess = DeclarationModifiers.Internal; } else { - defaultAccess = DeclarationModifiers.Private; - } - } + allowedModifiers |= DeclarationModifiers.New; - switch (typeKind) - { - case TypeKind.Class: - case TypeKind.Submission: - allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Abstract - | DeclarationModifiers.Unsafe; - - if (!this.IsRecord) + if (((NamedTypeSymbol)containingSymbol).IsInterface) { - allowedModifiers |= DeclarationModifiers.Static; + defaultAccess = DeclarationModifiers.Public; } - - break; - case TypeKind.Struct: - allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Unsafe; - - if (!this.IsRecordStruct) + else { - allowedModifiers |= DeclarationModifiers.Ref; + defaultAccess = DeclarationModifiers.Private; } + } - break; - case TypeKind.Interface: - allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; - break; - case TypeKind.Delegate: - allowedModifiers |= DeclarationModifiers.Unsafe; - break; + switch (typeKind) + { + case TypeKind.Class: + case TypeKind.Submission: + allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Abstract + | DeclarationModifiers.Unsafe; + + if (!this.IsRecord) + { + allowedModifiers |= DeclarationModifiers.Static; + } + + break; + case TypeKind.Struct: + allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Unsafe; + + if (!this.IsRecordStruct) + { + allowedModifiers |= DeclarationModifiers.Ref; + } + + break; + case TypeKind.Interface: + allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; + break; + case TypeKind.Delegate: + allowedModifiers |= DeclarationModifiers.Unsafe; + break; + } } bool modifierErrors; @@ -371,9 +380,8 @@ private DeclarationModifiers MakeModifiers(TypeKind typeKind, BindingDiagnosticB break; case TypeKind.Struct: case TypeKind.Enum: - mods |= DeclarationModifiers.Sealed; - break; case TypeKind.Delegate: + case TypeKind.Extension: mods |= DeclarationModifiers.Sealed; break; } @@ -1267,7 +1275,7 @@ public override IEnumerable MemberNames { get { - return (IsTupleType || IsRecord || IsRecordStruct) ? GetMembers().Select(m => m.Name) : this.declaration.MemberNames; + return (IsTupleType || IsRecord || IsRecordStruct || this.declaration.ContainsExtensionDeclarations) ? GetMembers().Select(m => m.Name) : this.declaration.MemberNames; } } @@ -1329,27 +1337,30 @@ private Dictionary, ImmutableArray> MakeTy foreach (var childDeclaration in declaration.Children) { var t = new SourceNamedTypeSymbol(this, childDeclaration, diagnostics); - this.CheckMemberNameDistinctFromType(t, diagnostics); - - var key = (t.Name, t.Arity, t.AssociatedSyntaxTree); - SourceNamedTypeSymbol? other; - if (conflictDict.TryGetValue(key, out other)) + if (!t.IsExtension) { - if (Locations.Length == 1 || IsPartial) + this.CheckMemberNameDistinctFromType(t, diagnostics); + + var key = (t.Name, t.Arity, t.AssociatedSyntaxTree); + SourceNamedTypeSymbol? other; + if (conflictDict.TryGetValue(key, out other)) { - if (t.IsPartial && other.IsPartial) + if (Locations.Length == 1 || IsPartial) { - diagnostics.Add(ErrorCode.ERR_PartialTypeKindConflict, t.GetFirstLocation(), t); - } - else - { - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, t.GetFirstLocation(), this, t.Name); + if (t.IsPartial && other.IsPartial) + { + diagnostics.Add(ErrorCode.ERR_PartialTypeKindConflict, t.GetFirstLocation(), t); + } + else + { + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, t.GetFirstLocation(), this, t.Name); + } } } - } - else - { - conflictDict.Add(key, t); + else + { + conflictDict.Add(key, t); + } } symbols.Add(t); @@ -1497,7 +1508,7 @@ internal override bool HasPossibleWellKnownCloneMethod() internal override ImmutableArray GetSimpleNonTypeMembers(string name) { - if (_lazyMembersDictionary != null || declaration.MemberNames.Contains(name) || declaration.Kind is DeclarationKind.Record or DeclarationKind.RecordStruct) + if (_lazyMembersDictionary != null || declaration.ContainsExtensionDeclarations || declaration.MemberNames.Contains(name) || declaration.Kind is DeclarationKind.Record or DeclarationKind.RecordStruct) { return GetMembers(name); } @@ -1700,6 +1711,10 @@ internal void AssertMemberExposure(Symbol member, bool forDiagnostics = false) { member = implementation; // This is a workaround for https://github.com/dotnet/roslyn/issues/76870, remove once the issue is addressed. } + else if (member is SynthesizedExtensionMarker) + { + return; + } var membersAndInitializers = Volatile.Read(ref _lazyMembersAndInitializers); @@ -1743,16 +1758,7 @@ internal void AssertMemberExposure(Symbol member, bool forDiagnostics = false) static bool isMemberInCompleteMemberList(MembersAndInitializers? membersAndInitializers, Symbol member) { - switch (member) - { - case MethodSymbol method: - member = method.PartialDefinitionPart ?? method; - break; - case PropertySymbol property: - member = property.PartialDefinitionPart ?? property; - break; - } - + member = member.GetPartialDefinitionPart() ?? member; return membersAndInitializers?.NonTypeMembers.Contains(m => m == (object)member) == true; } } @@ -1796,17 +1802,21 @@ internal override IEnumerable GetInstanceFieldsAndEvents() protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) { + var compilation = DeclaringCompilation; if (IsInterface) { CheckInterfaceMembers(this.GetMembersAndInitializers().NonTypeMembers, diagnostics); } + else if (IsExtension) + { + CheckExtensionMembers(this.GetMembers(), diagnostics); + } - CheckMemberNamesDistinctFromType(diagnostics); + CheckMemberNamesDistinctFromType(diagnostics); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Should this check "see through" extensions? CheckMemberNameConflicts(diagnostics); CheckRecordMemberNames(diagnostics); CheckSpecialMemberErrors(diagnostics); CheckTypeParameterNameConflicts(diagnostics); - CheckAccessorNameConflicts(diagnostics); bool unused = KnownCircularStruct; @@ -1821,7 +1831,6 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) } var location = GetFirstLocation(); - var compilation = DeclaringCompilation; if (this.IsRefLikeType) { @@ -1900,6 +1909,12 @@ protected void AfterMembersChecks(BindingDiagnosticBag diagnostics) } } + if (IsExtension) + { + // Verify ExtensionAttribute is available. + SourceOrdinaryMethodSymbol.CheckExtensionAttributeAvailability(DeclaringCompilation, location, diagnostics); + } + return; bool hasBaseTypeOrInterface(Func predicate) @@ -1948,17 +1963,21 @@ private void CheckRecordMemberNames(BindingDiagnosticBag diagnostics) } } - private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) + private static void CheckMemberNameConflicts( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, + Dictionary, ImmutableArray>? typesByName, + Dictionary, ImmutableArray> membersByName, + BindingDiagnosticBag diagnostics) { - Dictionary, ImmutableArray> membersByName = GetMembersByName(); // Collisions involving indexers are handled specially. - CheckIndexerNameConflicts(diagnostics, membersByName); + CheckIndexerNameConflicts(containerForDiagnostics, mightHaveMembersFromDistinctNonPartialDeclarations, diagnostics, membersByName); // key and value will be the same object in these dictionaries. - var methodsBySignature = new Dictionary(MemberSignatureComparer.DuplicateSourceComparer); - var conversionsAsMethods = new Dictionary(MemberSignatureComparer.DuplicateSourceComparer); - var conversionsAsConversions = new HashSet(ConversionSignatureComparer.Comparer); + var methodsBySignature = new Dictionary(MemberSignatureComparer.DuplicateSourceComparer); + var conversionsAsMethods = new Dictionary(MemberSignatureComparer.DuplicateSourceComparer); + var conversionsAsConversions = new HashSet(ConversionSignatureComparer.Comparer); // SPEC: The signature of an operator must differ from the signatures of all other // SPEC: operators declared in the same class. @@ -1997,7 +2016,7 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) foreach (var pair in membersByName) { var name = pair.Key; - Symbol? lastSym = GetTypeMembers(name).FirstOrDefault(); + Symbol? lastSym = typesByName?.TryGetValue(name, out var types) == true ? types.FirstOrDefault() : null; methodsBySignature.Clear(); // Conversion collisions do not consider the name of the conversion, // so do not clear that dictionary. @@ -2005,7 +2024,8 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) { if (symbol.Kind == SymbolKind.NamedType || symbol.IsAccessor() || - symbol.IsIndexer()) + symbol.IsIndexer() || + symbol.OriginalDefinition is SynthesizedExtensionMarker) { continue; } @@ -2051,9 +2071,9 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) if (symbol.Kind != SymbolKind.Field || !symbol.IsImplicitlyDeclared) { // The type '{0}' already contains a definition for '{1}' - if (Locations.Length == 1 || IsPartial) + if (!mightHaveMembersFromDistinctNonPartialDeclarations) { - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, symbol.GetFirstLocation(), this, symbol.Name); + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, symbol.GetFirstLocation(), containerForDiagnostics, symbol.Name); } } @@ -2071,11 +2091,8 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) // That takes care of the first category of conflict; we detect the // second and third categories as follows: - var conversion = symbol as SourceUserDefinedConversionSymbol; - var method = symbol as SourceMemberMethodSymbol; - // We don't want to consider explicit interface implementations - if (conversion is { MethodKind: MethodKind.Conversion }) + if (symbol is MethodSymbol { MethodKind: MethodKind.Conversion } conversion) { // Does this conversion collide *as a conversion* with any previously-seen // conversion? @@ -2083,7 +2100,7 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) if (!conversionsAsConversions.Add(conversion)) { // CS0557: Duplicate user-defined conversion in type 'C' - diagnostics.Add(ErrorCode.ERR_DuplicateConversionInClass, conversion.GetFirstLocation(), this); + diagnostics.Add(ErrorCode.ERR_DuplicateConversionInClass, conversion.GetFirstLocation(), containerForDiagnostics); } else { @@ -2100,19 +2117,19 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) if (methodsBySignature.TryGetValue(conversion, out var previousMethod)) { - ReportMethodSignatureCollision(diagnostics, conversion, previousMethod); + ReportMethodSignatureCollision(containerForDiagnostics, diagnostics, conversion, previousMethod); } // Do not add the conversion to the set of previously-seen methods; that set // is only non-conversion methods. } - else if (!(method is null)) + else if (symbol is MethodSymbol method) { // Does this method collide *as a method* with any previously-seen // conversion? if (conversionsAsMethods.TryGetValue(method, out var previousConversion)) { - ReportMethodSignatureCollision(diagnostics, method, previousConversion); + ReportMethodSignatureCollision(containerForDiagnostics, diagnostics, method, previousConversion); } // Do not add the method to the set of previously-seen conversions. @@ -2121,7 +2138,7 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) if (methodsBySignature.TryGetValue(method, out var previousMethod)) { - ReportMethodSignatureCollision(diagnostics, method, previousMethod); + ReportMethodSignatureCollision(containerForDiagnostics, diagnostics, method, previousMethod); } else { @@ -2136,7 +2153,7 @@ private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) // Report a name conflict; the error is reported on the location of method1. // UNDONE: Consider adding a secondary location pointing to the second method. - private void ReportMethodSignatureCollision(BindingDiagnosticBag diagnostics, SourceMemberMethodSymbol method1, SourceMemberMethodSymbol method2) + private static void ReportMethodSignatureCollision(SourceMemberContainerTypeSymbol containerForDiagnostics, BindingDiagnosticBag diagnostics, MethodSymbol method1, MethodSymbol method2) { switch (method1, method2) { @@ -2152,12 +2169,28 @@ private void ReportMethodSignatureCollision(BindingDiagnosticBag diagnostics, So // If method1 is a constructor only because its return type is missing, then // we've already produced a diagnostic for the missing return type and we suppress the // diagnostic about duplicate signature. - if (method1.MethodKind == MethodKind.Constructor && - ((ConstructorDeclarationSyntax)method1.SyntaxRef.GetSyntax()).Identifier.ValueText != this.Name) + if (method1.OriginalDefinition is SourceMemberMethodSymbol { MethodKind: MethodKind.Constructor } constructor && + ((ConstructorDeclarationSyntax)constructor.SyntaxRef.GetSyntax()).Identifier.ValueText != method1.ContainingType.Name) { return; } + if (method1 is SourceExtensionImplementationMethodSymbol { UnderlyingMethod: var underlying1 } && + method2 is SourceExtensionImplementationMethodSymbol { UnderlyingMethod: var underlying2 } && + underlying1.IsStatic == underlying2.IsStatic && + ((object)underlying1.ContainingType == underlying2.ContainingType || + new ExtensionGroupingKey(underlying1.ContainingType).Equals(new ExtensionGroupingKey(underlying2.ContainingType))) && + diagnostics.DiagnosticBag?.AsEnumerableWithoutResolution().Any( + static (d, arg) => + (d.Code is (int)ErrorCode.ERR_OverloadRefKind or (int)ErrorCode.ERR_MemberAlreadyExists or + (int)ErrorCode.ERR_DuplicateNameInClass or (int)ErrorCode.ERR_MemberReserved) && + (d.Location == arg.method1.GetFirstLocation() || d.Location == arg.underlying1.AssociatedSymbol?.TryGetFirstLocation() || + d.Location == arg.method2.GetFirstLocation() || d.Location == arg.underlying2.AssociatedSymbol?.TryGetFirstLocation()), + (method1, underlying1, method2, underlying2)) == true) + { + return; // The conflict is reported in context of extension declaration + } + Debug.Assert(method1.ParameterCount == method2.ParameterCount); for (int i = 0; i < method1.ParameterCount; i++) @@ -2169,32 +2202,33 @@ private void ReportMethodSignatureCollision(BindingDiagnosticBag diagnostics, So { // '{0}' cannot define an overloaded {1} that differs only on parameter modifiers '{2}' and '{3}' var methodKind = method1.MethodKind == MethodKind.Constructor ? MessageID.IDS_SK_CONSTRUCTOR : MessageID.IDS_SK_METHOD; - diagnostics.Add(ErrorCode.ERR_OverloadRefKind, method1.GetFirstLocation(), this, methodKind.Localize(), refKind1.ToParameterDisplayString(), refKind2.ToParameterDisplayString()); + diagnostics.Add(ErrorCode.ERR_OverloadRefKind, method1.GetFirstLocation(), containerForDiagnostics, methodKind.Localize(), refKind1.ToParameterDisplayString(), refKind2.ToParameterDisplayString()); return; } } + if (method1 is SourceExtensionImplementationMethodSymbol extensionImplementation) + { + method1 = extensionImplementation.UnderlyingMethod; + } + // Special case: if there are two destructors, use the destructor syntax instead of "Finalize" var methodName = (method1.MethodKind == MethodKind.Destructor && method2.MethodKind == MethodKind.Destructor) ? - "~" + this.Name : - (method1.IsConstructor() ? this.Name : method1.Name); + "~" + method1.ContainingType.Name : + (method1.IsConstructor() ? method1.ContainingType.Name : method1.Name); // Type '{1}' already defines a member called '{0}' with the same parameter types - diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, method1.GetFirstLocation(), methodName, this); + diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, method1.GetFirstLocation(), methodName, containerForDiagnostics); } - private void CheckIndexerNameConflicts(BindingDiagnosticBag diagnostics, Dictionary, ImmutableArray> membersByName) + private static void CheckIndexerNameConflicts( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, + BindingDiagnosticBag diagnostics, Dictionary, ImmutableArray> membersByName) { PooledHashSet? typeParameterNames = null; - if (this.Arity > 0) - { - typeParameterNames = PooledHashSet.GetInstance(); - foreach (TypeParameterSymbol typeParameter in this.TypeParameters) - { - typeParameterNames.Add(typeParameter.Name); - } - } + bool checkCollisionWithTypeParameters = true; var indexersBySignature = new Dictionary(MemberSignatureComparer.DuplicateSourceComparer); @@ -2210,6 +2244,8 @@ private void CheckIndexerNameConflicts(BindingDiagnosticBag diagnostics, Diction { PropertySymbol indexer = (PropertySymbol)symbol; CheckIndexerSignatureCollisions( + containerForDiagnostics, + mightHaveMembersFromDistinctNonPartialDeclarations, indexer, diagnostics, membersByName, @@ -2218,12 +2254,31 @@ private void CheckIndexerNameConflicts(BindingDiagnosticBag diagnostics, Diction // Also check for collisions with type parameters, which aren't in the member map. // NOTE: Accessors have normal names and are handled in CheckTypeParameterNameConflicts. + + if (checkCollisionWithTypeParameters && typeParameterNames == null) + { + if (!indexer.GetIsNewExtensionMember() && indexer.ContainingType.Arity > 0) + { + typeParameterNames = PooledHashSet.GetInstance(); + foreach (TypeParameterSymbol typeParameter in indexer.ContainingType.TypeParameters) + { + typeParameterNames.Add(typeParameter.Name); + } + } + else + { + checkCollisionWithTypeParameters = false; + } + } + + Debug.Assert(checkCollisionWithTypeParameters || typeParameterNames == null); + if (typeParameterNames != null) { string indexerName = indexer.MetadataName; if (typeParameterNames.Contains(indexerName)) { - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), this, indexerName); + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), containerForDiagnostics, indexerName); continue; } } @@ -2234,7 +2289,9 @@ private void CheckIndexerNameConflicts(BindingDiagnosticBag diagnostics, Diction typeParameterNames?.Free(); } - private void CheckIndexerSignatureCollisions( + private static void CheckIndexerSignatureCollisions( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, PropertySymbol indexer, BindingDiagnosticBag diagnostics, Dictionary, ImmutableArray> membersByName, @@ -2259,7 +2316,7 @@ private void CheckIndexerSignatureCollisions( lastIndexerName = indexerName; - if (Locations.Length == 1 || IsPartial) + if (!mightHaveMembersFromDistinctNonPartialDeclarations) { #pragma warning disable CA1854 //Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup if (membersByName.ContainsKey(indexerName.AsMemory())) @@ -2267,7 +2324,7 @@ private void CheckIndexerSignatureCollisions( { // The name of the indexer is reserved - it can only be used by other indexers. Debug.Assert(!membersByName[indexerName.AsMemory()].Any(SymbolExtensions.IsIndexer)); - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), this, indexerName); + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), containerForDiagnostics, indexerName); } } } @@ -2276,7 +2333,7 @@ private void CheckIndexerSignatureCollisions( { // Type '{1}' already defines a member called '{0}' with the same parameter types // NOTE: Dev10 prints "this" as the name of the indexer. - diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, indexer.GetFirstLocation(), SyntaxFacts.GetText(SyntaxKind.ThisKeyword), this); + diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, indexer.GetFirstLocation(), SyntaxFacts.GetText(SyntaxKind.ThisKeyword), containerForDiagnostics); } else { @@ -2284,6 +2341,181 @@ private void CheckIndexerSignatureCollisions( } } + private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics) + { + if (IsExtension) + { + return; // Conflicts are checked in context of the enclosing type + } + + if (this.declaration.ContainsExtensionDeclarations) + { + checkMemberNameConflictsInExtensions(diagnostics); + } + + checkMemberNameConflicts(GetMembersByName(), GetTypeMembersDictionary(), GetMembersUnordered(), diagnostics); + return; + + void checkMemberNameConflicts( + Dictionary, ImmutableArray> membersByName, + Dictionary, ImmutableArray>? typesByName, + ImmutableArray membersUnordered, + BindingDiagnosticBag diagnostics) + { + bool mightHaveMembersFromDistinctNonPartialDeclarations = !(Locations.Length == 1 || IsPartial); + CheckMemberNameConflicts(this, mightHaveMembersFromDistinctNonPartialDeclarations, typesByName, membersByName, diagnostics); + CheckAccessorNameConflicts(this, mightHaveMembersFromDistinctNonPartialDeclarations, membersByName, membersUnordered, diagnostics); + } + + void checkMemberNameConflictsInExtensions(BindingDiagnosticBag diagnostics) + { + IEnumerable> extensionsByReceiverType = GetTypeMembers("").Where(static t => t.IsExtension).GroupBy(static t => new ExtensionGroupingKey(t)); + + foreach (var grouping in extensionsByReceiverType) + { + Dictionary, ImmutableArray>? membersByName; + ImmutableArray membersUnordered; + + (membersByName, membersUnordered) = mergeMembersInGroup(grouping); + + if (membersByName is not null) + { + checkMemberNameConflicts(membersByName, typesByName: null /* nested types not supported */, membersUnordered, diagnostics); + } + } + } + + static (Dictionary, ImmutableArray>? membersByName, ImmutableArray membersUnordered) mergeMembersInGroup(IGrouping grouping) + { + Dictionary, ImmutableArray>? membersByName = null; + ImmutableArray membersUnordered = []; + NamedTypeSymbol? masterExtension = null; + bool cloneMembersByName = true; + + foreach (NamedTypeSymbol item in grouping) + { + var extension = item; + Dictionary, ImmutableArray> membersByNameToMerge = ((SourceMemberContainerTypeSymbol)extension).GetMembersByName(); + + if (membersByNameToMerge.Count == 0 || + (membersByNameToMerge.Count == 1 && membersByNameToMerge.Values.Single() is [SynthesizedExtensionMarker])) + { + continue; // This is an optimization + } + + if (membersByName == null) + { + membersByName = membersByNameToMerge; + membersUnordered = extension.GetMembersUnordered(); + masterExtension = extension; + Debug.Assert(cloneMembersByName); + } + else + { + Debug.Assert(masterExtension is not null); + + if (cloneMembersByName) + { + membersByName = new Dictionary, ImmutableArray>(membersByName, ReadOnlyMemoryOfCharComparer.Instance); + cloneMembersByName = false; + } + + if (extension.Arity != 0) + { + extension = extension.Construct(masterExtension.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics); + } + else + { + membersUnordered = membersUnordered.Concat(extension.GetMembersUnordered()); + } + + foreach (var pair in membersByNameToMerge) + { + if (membersByName.TryGetValue(pair.Key, out var members)) + { + membersByName[pair.Key] = concatMembers(members, extension, pair.Value, ref membersUnordered); + } + else + { + membersByName.Add(pair.Key, concatMembers([], extension, pair.Value, ref membersUnordered)); + } + } + } + } + + return (membersByName, membersUnordered); + } + + static ImmutableArray concatMembers(ImmutableArray existingMembers, NamedTypeSymbol extension, ImmutableArray newMembers, ref ImmutableArray membersUnordered) + { + Debug.Assert(!newMembers.IsEmpty); + + if (extension.IsDefinition) + { + Debug.Assert(newMembers.All(static (m, membersUnordered) => membersUnordered.Contains(m), membersUnordered)); + return existingMembers.Concat(newMembers); + } + + var membersBuilder = ArrayBuilder.GetInstance(existingMembers.Length + newMembers.Length); + var membersUnorderedBuilder = ArrayBuilder.GetInstance(membersUnordered.Length + newMembers.Length); + + membersBuilder.AddRange(existingMembers); + membersUnorderedBuilder.AddRange(membersUnordered); + + foreach (var member in newMembers) + { + Symbol toAdd = member.SymbolAsMember(extension); + membersBuilder.Add(toAdd); + membersUnorderedBuilder.Add(toAdd); + } + + membersUnordered = membersUnorderedBuilder.ToImmutableAndFree(); + return membersBuilder.ToImmutableAndFree(); + } + } + + private readonly struct ExtensionGroupingKey : IEquatable + { + public readonly int ExtensionArity; + public readonly TypeSymbol ReceiverType; + + public ExtensionGroupingKey(NamedTypeSymbol extension) + { + ExtensionArity = extension.Arity; + + if (extension.Arity != 0) + { + extension = extension.Construct(IndexedTypeParameterSymbol.Take(extension.Arity)); + } + + if (extension.ExtensionParameter is { } receiverParameter) + { + ReceiverType = receiverParameter.Type; + } + else + { + ReceiverType = ErrorTypeSymbol.UnknownResultType; + } + } + + public bool Equals(ExtensionGroupingKey other) + { + return ExtensionArity == other.ExtensionArity && + ReceiverType.Equals(other.ReceiverType, TypeCompareKind.AllIgnoreOptions); + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + Debug.Assert(false); // Usage of this method is unexpected + return Equals((ExtensionGroupingKey)obj!); + } + + public override int GetHashCode() + { + return ReceiverType.GetHashCode(); + } + } + private void CheckSpecialMemberErrors(BindingDiagnosticBag diagnostics) { var conversions = this.ContainingAssembly.CorLibrary.TypeConversions; @@ -2295,9 +2527,9 @@ private void CheckSpecialMemberErrors(BindingDiagnosticBag diagnostics) private void CheckTypeParameterNameConflicts(BindingDiagnosticBag diagnostics) { - if (this.TypeKind == TypeKind.Delegate) + if (this.TypeKind is TypeKind.Delegate or TypeKind.Extension) { - // Delegates do not have conflicts between their type parameter + // Delegates and extensions do not have conflicts between their type parameter // names and their methods; it is legal (though odd) to say // delegate void D(Invoke x); @@ -2316,11 +2548,16 @@ private void CheckTypeParameterNameConflicts(BindingDiagnosticBag diagnostics) } } - private void CheckAccessorNameConflicts(BindingDiagnosticBag diagnostics) + private static void CheckAccessorNameConflicts( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, + Dictionary, ImmutableArray> membersByName, + ImmutableArray membersUnordered, + BindingDiagnosticBag diagnostics) { // Report errors where property and event accessors // conflict with other members of the same name. - foreach (Symbol symbol in this.GetMembersUnordered()) + foreach (Symbol symbol in membersUnordered) { if (symbol.IsExplicitInterfaceImplementation()) { @@ -2333,15 +2570,15 @@ private void CheckAccessorNameConflicts(BindingDiagnosticBag diagnostics) case SymbolKind.Property: { var propertySymbol = (PropertySymbol)symbol; - this.CheckForMemberConflictWithPropertyAccessor(propertySymbol, getNotSet: true, diagnostics: diagnostics); - this.CheckForMemberConflictWithPropertyAccessor(propertySymbol, getNotSet: false, diagnostics: diagnostics); + CheckForMemberConflictWithPropertyAccessor(containerForDiagnostics, mightHaveMembersFromDistinctNonPartialDeclarations, membersByName, propertySymbol, getNotSet: true, diagnostics: diagnostics); + CheckForMemberConflictWithPropertyAccessor(containerForDiagnostics, mightHaveMembersFromDistinctNonPartialDeclarations, membersByName, propertySymbol, getNotSet: false, diagnostics: diagnostics); break; } case SymbolKind.Event: { var eventSymbol = (EventSymbol)symbol; - this.CheckForMemberConflictWithEventAccessor(eventSymbol, isAdder: true, diagnostics: diagnostics); - this.CheckForMemberConflictWithEventAccessor(eventSymbol, isAdder: false, diagnostics: diagnostics); + CheckForMemberConflictWithEventAccessor(containerForDiagnostics, mightHaveMembersFromDistinctNonPartialDeclarations, membersByName, eventSymbol, isAdder: true, diagnostics: diagnostics); + CheckForMemberConflictWithEventAccessor(containerForDiagnostics, mightHaveMembersFromDistinctNonPartialDeclarations, membersByName, eventSymbol, isAdder: false, diagnostics: diagnostics); break; } } @@ -2454,7 +2691,7 @@ private void CheckForProtectedInStaticClass(BindingDiagnosticBag diagnostics) continue; } - if (member.DeclaredAccessibility.HasProtected()) + if (member.DeclaredAccessibility.HasProtected() && member is not SourceExtensionImplementationMethodSymbol) { if (member.Kind != SymbolKind.Method || ((MethodSymbol)member).MethodKind != MethodKind.Destructor) { @@ -2841,13 +3078,13 @@ static bool hasInstanceData(MemberDeclarationSyntax m) private static bool All(SyntaxList list, Func predicate) where T : CSharpSyntaxNode { - foreach (var t in list) { if (predicate(t)) return true; }; + foreach (var t in list) { if (predicate(t)) return true; } return false; } private static bool ContainsModifier(SyntaxTokenList modifiers, SyntaxKind modifier) { - foreach (var m in modifiers) { if (m.IsKind(modifier)) return true; }; + foreach (var m in modifiers) { if (m.IsKind(modifier)) return true; } return false; } @@ -3439,9 +3676,9 @@ internal IEnumerable GetMethodsPossiblyCapturingPrimar } else { - var membersAndInituializers = GetMembersAndInitializers(); - nonTypeMembersToCheck = membersAndInituializers.NonTypeMembers; - primaryConstructor = membersAndInituializers.PrimaryConstructor; + var membersAndInitializers = GetMembersAndInitializers(); + nonTypeMembersToCheck = membersAndInitializers.NonTypeMembers; + primaryConstructor = membersAndInitializers.PrimaryConstructor; } Debug.Assert(primaryConstructor is not null); @@ -3484,9 +3721,9 @@ internal ImmutableArray GetMembersToMatchAgainstDeclarationSpan() } else { - var membersAndInituializers = GetMembersAndInitializers(); - Debug.Assert(membersAndInituializers.PrimaryConstructor is not null); - return membersAndInituializers.NonTypeMembers; + var membersAndInitializers = GetMembersAndInitializers(); + Debug.Assert(membersAndInitializers.PrimaryConstructor is not null); + return membersAndInitializers.NonTypeMembers; } } @@ -3509,9 +3746,9 @@ internal ImmutableArray GetCandidateMembersForLookup(string name) } else { - var membersAndInituializers = GetMembersAndInitializers(); - nonTypeMembersToCheck = membersAndInituializers.NonTypeMembers; - primaryConstructor = membersAndInituializers.PrimaryConstructor; + var membersAndInitializers = GetMembersAndInitializers(); + nonTypeMembersToCheck = membersAndInitializers.NonTypeMembers; + primaryConstructor = membersAndInitializers.PrimaryConstructor; } Debug.Assert(primaryConstructor is not null); @@ -3563,6 +3800,15 @@ private void AddSynthesizedMembers(MembersAndInitializersBuilder builder, Declar case TypeKind.Submission: AddSynthesizedTypeMembersIfNecessary(builder, declaredMembersAndInitializers, diagnostics); AddSynthesizedConstructorsIfNecessary(builder, declaredMembersAndInitializers, diagnostics); + + if (TypeKind == TypeKind.Class) // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider tightening this check to only top-level non-generic static classes, however optimizing for error scenarios is usually not a goal. + { + AddSynthesizedExtensionImplementationsIfNecessary(builder, declaredMembersAndInitializers); + } + break; + + case TypeKind.Extension: + AddSynthesizedExtensionMarker(builder, declaredMembersAndInitializers); break; default: @@ -3572,6 +3818,38 @@ private void AddSynthesizedMembers(MembersAndInitializersBuilder builder, Declar AddSynthesizedTupleMembersIfNecessary(builder, declaredMembersAndInitializers); } + private void AddSynthesizedExtensionImplementationsIfNecessary(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers) + { + foreach (var type in GetTypeMembers("")) + { + if (type.TypeKind == TypeKind.Extension) + { + foreach (var member in type.GetMembers()) + { + if (member is MethodSymbol { IsImplicitlyDeclared: false, MethodKind: not (MethodKind.Constructor or MethodKind.StaticConstructor or MethodKind.Destructor or MethodKind.ExplicitInterfaceImplementation) } method && + (method.IsStatic || type.ExtensionParameter is not null)) + { + builder.AddNonTypeMember(this, new SourceExtensionImplementationMethodSymbol(method), declaredMembersAndInitializers); + } + } + } + } + } + + private void AddSynthesizedExtensionMarker(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers) + { + var marker = CreateSynthesizedExtensionMarker(); + if (marker is not null) + { + builder.AddNonTypeMember(this, marker, declaredMembersAndInitializers); + } + } + + protected virtual MethodSymbol? CreateSynthesizedExtensionMarker() + { + throw ExceptionUtilities.Unreachable(); + } + private void AddDeclaredNontypeMembers(DeclaredMembersAndInitializersBuilder builder, BindingDiagnosticBag diagnostics) { foreach (var decl in this.declaration.Declarations) @@ -3617,6 +3895,7 @@ private void AddDeclaredNontypeMembers(DeclaredMembersAndInitializersBuilder bui case SyntaxKind.StructDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: var typeDecl = (TypeDeclarationSyntax)syntax; noteTypeParameters(typeDecl, builder, diagnostics); AddNonTypeMembers(builder, typeDecl.Members, diagnostics); @@ -3636,29 +3915,32 @@ void noteTypeParameters(TypeDeclarationSyntax syntax, DeclaredMembersAndInitiali return; } - if (builder.DeclarationWithParameters is null) + if (!this.IsExtension) { - builder.DeclarationWithParameters = syntax; - var ctor = new SynthesizedPrimaryConstructor(this, syntax); - - if (this.IsStatic) + if (builder.DeclarationWithParameters is null) { - diagnostics.Add(ErrorCode.ERR_ConstructorInStaticClass, syntax.Identifier.GetLocation()); - } + builder.DeclarationWithParameters = syntax; + var ctor = new SynthesizedPrimaryConstructor(this, syntax); - builder.PrimaryConstructor = ctor; + if (this.IsStatic) + { + diagnostics.Add(ErrorCode.ERR_ConstructorInStaticClass, syntax.Identifier.GetLocation()); + } - var compilation = DeclaringCompilation; - builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, parameterList); - if (syntax is { PrimaryConstructorBaseTypeIfClass: { ArgumentList: { } baseParamList } }) + builder.PrimaryConstructor = ctor; + + var compilation = DeclaringCompilation; + builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, parameterList); + if (syntax is { PrimaryConstructorBaseTypeIfClass: { ArgumentList: { } baseParamList } }) + { + builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, baseParamList); + } + } + else { - builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, baseParamList); + diagnostics.Add(ErrorCode.ERR_MultipleRecordParameterLists, parameterList.Location); } } - else - { - diagnostics.Add(ErrorCode.ERR_MultipleRecordParameterLists, parameterList.Location); - } } } @@ -3701,14 +3983,24 @@ private static void MergePartialMembers( mergePartialProperties(nonTypeMembers, currentProperty, prevProperty, diagnostics); break; + case (SourceConstructorSymbol { IsStatic: false } currentConstructor, SourceConstructorSymbol { IsStatic: false } prevConstructor): + Debug.Assert(pair.Key.Equals(WellKnownMemberNames.InstanceConstructorName.AsMemory())); + mergePartialConstructors(nonTypeMembers, currentConstructor, prevConstructor, diagnostics); + break; + + case (SourceEventSymbol currentEvent, SourceEventSymbol prevEvent): + mergePartialEvents(nonTypeMembers, currentEvent, prevEvent, diagnostics); + break; + case (SourcePropertyAccessorSymbol, SourcePropertyAccessorSymbol): - break; // accessor symbols and their diagnostics are handled by processing the associated property + case (SourceEventAccessorSymbol, SourceEventAccessorSymbol): + break; // accessor symbols and their diagnostics are handled by processing the associated member default: // This is an error scenario. We simply don't merge the symbols in this case and a duplicate name diagnostic is reported separately. - // One way this case can be reached is if type contains both `public partial int P { get; }` and `public partial int P_get();`. - Debug.Assert(symbol is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol); - Debug.Assert(prev is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol); + // One way this case can be reached is if type contains both `public partial int P { get; }` and `public partial int get_P();`. + Debug.Assert(symbol is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol or SourceEventAccessorSymbol); + Debug.Assert(prev is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol or SourceEventAccessorSymbol); break; } } @@ -3746,8 +4038,29 @@ private static void MergePartialMembers( } break; + case SourceConstructorSymbol constructor: + if (constructor.OtherPartOfPartial is null) + { + diagnostics.Add( + constructor.IsPartialDefinition ? ErrorCode.ERR_PartialMemberMissingImplementation : ErrorCode.ERR_PartialMemberMissingDefinition, + constructor.GetFirstLocation(), + constructor); + } + break; + + case SourceEventSymbol ev: + if (ev.OtherPartOfPartial is null) + { + diagnostics.Add( + ev.IsPartialDefinition ? ErrorCode.ERR_PartialMemberMissingImplementation : ErrorCode.ERR_PartialMemberMissingDefinition, + ev.GetFirstLocation(), + ev); + } + break; + + case SourceEventAccessorSymbol: case SourcePropertyAccessorSymbol: - break; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'. + break; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'/'mergePartialEvents'. default: throw ExceptionUtilities.UnexpectedValue(symbol); @@ -3847,6 +4160,60 @@ static bool hasInitializer(SourcePropertySymbol property) return property.DeclaredBackingField?.HasInitializer == true; } } + + static void mergePartialConstructors(ArrayBuilder nonTypeMembers, SourceConstructorSymbol currentConstructor, SourceConstructorSymbol prevConstructor, BindingDiagnosticBag diagnostics) + { + if (currentConstructor.IsPartialImplementation && + (prevConstructor.IsPartialImplementation || (prevConstructor.OtherPartOfPartial is { } otherImplementation && !ReferenceEquals(otherImplementation, currentConstructor)))) + { + // A partial constructor may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateImplementation, currentConstructor.GetFirstLocation(), currentConstructor); + } + else if (currentConstructor.IsPartialDefinition && + (prevConstructor.IsPartialDefinition || (prevConstructor.OtherPartOfPartial is { } otherDefinition && !ReferenceEquals(otherDefinition, currentConstructor)))) + { + // A partial constructor may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateDefinition, currentConstructor.GetFirstLocation(), currentConstructor); + } + else + { + FixPartialConstructor(nonTypeMembers, prevConstructor, currentConstructor); + } + } + + static void mergePartialEvents(ArrayBuilder nonTypeMembers, SourceEventSymbol currentEvent, SourceEventSymbol prevEvent, BindingDiagnosticBag diagnostics) + { + if (currentEvent.IsPartialImplementation && + (prevEvent.IsPartialImplementation || (prevEvent.OtherPartOfPartial is { } otherImplementation && !ReferenceEquals(otherImplementation, currentEvent)))) + { + // A partial event may not have multiple implementing declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateImplementation, currentEvent.GetFirstLocation(), currentEvent); + } + else if (currentEvent.IsPartialDefinition && + (prevEvent.IsPartialDefinition || (prevEvent.OtherPartOfPartial is { } otherDefinition && !ReferenceEquals(otherDefinition, currentEvent)))) + { + // A partial event may not have multiple defining declarations + diagnostics.Add(ErrorCode.ERR_PartialMemberDuplicateDefinition, currentEvent.GetFirstLocation(), currentEvent); + } + else + { + mergeAccessors(nonTypeMembers, (SourceEventAccessorSymbol?)currentEvent.AddMethod, (SourceEventAccessorSymbol?)prevEvent.AddMethod); + mergeAccessors(nonTypeMembers, (SourceEventAccessorSymbol?)currentEvent.RemoveMethod, (SourceEventAccessorSymbol?)prevEvent.RemoveMethod); + FixPartialEvent(nonTypeMembers, prevEvent, currentEvent); + } + + static void mergeAccessors(ArrayBuilder nonTypeMembers, SourceEventAccessorSymbol? currentAccessor, SourceEventAccessorSymbol? prevAccessor) + { + if (currentAccessor?.IsPartialImplementation == true) + { + Remove(nonTypeMembers, currentAccessor); + } + else if (prevAccessor?.IsPartialImplementation == true) + { + Remove(nonTypeMembers, prevAccessor); + } + } + } } /// Links together the definition and implementation parts of a partial method. Removes implementation part from . @@ -3899,6 +4266,50 @@ private static void FixPartialProperty(ArrayBuilder nonTypeMembers, Sour Remove(nonTypeMembers, implementation); } + /// Links together the definition and implementation parts of a partial constructor. Removes implementation part from . + private static void FixPartialConstructor(ArrayBuilder nonTypeMembers, SourceConstructorSymbol part1, SourceConstructorSymbol part2) + { + SourceConstructorSymbol definition; + SourceConstructorSymbol implementation; + if (part1.IsPartialDefinition) + { + definition = part1; + implementation = part2; + } + else + { + definition = part2; + implementation = part1; + } + + SourceConstructorSymbol.InitializePartialConstructorParts(definition, implementation); + + // a partial constructor is represented in the member list by its definition part: + Remove(nonTypeMembers, implementation); + } + + /// Links together the definition and implementation parts of a partial event. Removes implementation part from . + private static void FixPartialEvent(ArrayBuilder nonTypeMembers, SourceEventSymbol part1, SourceEventSymbol part2) + { + SourceEventSymbol definition; + SourceEventSymbol implementation; + if (part1.IsPartialDefinition) + { + definition = part1; + implementation = part2; + } + else + { + definition = part2; + implementation = part1; + } + + SourceEventSymbol.InitializePartialEventParts(definition, implementation); + + // a partial event is represented in the member list by its definition part: + Remove(nonTypeMembers, implementation); + } + private static void Remove(ArrayBuilder symbols, Symbol symbol) { for (int i = 0; i < symbols.Count; i++) @@ -3918,7 +4329,10 @@ private static void Remove(ArrayBuilder symbols, Symbol symbol) /// Report an error if a member (other than a method) exists with the same name /// as the property accessor, or if a method exists with the same name and signature. /// - private void CheckForMemberConflictWithPropertyAccessor( + private static void CheckForMemberConflictWithPropertyAccessor( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, + Dictionary, ImmutableArray> membersByName, PropertySymbol propertySymbol, bool getNotSet, BindingDiagnosticBag diagnostics) @@ -3939,13 +4353,13 @@ private void CheckForMemberConflictWithPropertyAccessor( propertySymbol.IsCompilationOutputWinMdObj()); } - foreach (var symbol in GetMembers(accessorName)) + foreach (var symbol in membersByName.TryGetValue(accessorName.AsMemory(), out var members) ? members : []) { if (symbol.Kind != SymbolKind.Method) { // The type '{0}' already contains a definition for '{1}' - if (Locations.Length == 1 || IsPartial) - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), this, accessorName); + if (!mightHaveMembersFromDistinctNonPartialDeclarations) + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), containerForDiagnostics, accessorName); return; } else @@ -3955,7 +4369,7 @@ private void CheckForMemberConflictWithPropertyAccessor( ParametersMatchPropertyAccessor(propertySymbol, getNotSet, methodSymbol.Parameters)) { // Type '{1}' already reserves a member called '{0}' with the same parameter types - diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), accessorName, this); + diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), accessorName, containerForDiagnostics); return; } } @@ -3966,7 +4380,10 @@ private void CheckForMemberConflictWithPropertyAccessor( /// Report an error if a member (other than a method) exists with the same name /// as the event accessor, or if a method exists with the same name and signature. /// - private void CheckForMemberConflictWithEventAccessor( + private static void CheckForMemberConflictWithEventAccessor( + SourceMemberContainerTypeSymbol containerForDiagnostics, + bool mightHaveMembersFromDistinctNonPartialDeclarations, + Dictionary, ImmutableArray> membersByName, EventSymbol eventSymbol, bool isAdder, BindingDiagnosticBag diagnostics) @@ -3975,13 +4392,13 @@ private void CheckForMemberConflictWithEventAccessor( string accessorName = SourceEventSymbol.GetAccessorName(eventSymbol.Name, isAdder); - foreach (var symbol in GetMembers(accessorName)) + foreach (var symbol in membersByName.TryGetValue(accessorName.AsMemory(), out var members) ? members : []) { if (symbol.Kind != SymbolKind.Method) { // The type '{0}' already contains a definition for '{1}' - if (Locations.Length == 1 || IsPartial) - diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrEventLocation(eventSymbol, isAdder), this, accessorName); + if (!mightHaveMembersFromDistinctNonPartialDeclarations) + diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrEventLocation(eventSymbol, isAdder), containerForDiagnostics, accessorName); return; } else @@ -3991,7 +4408,7 @@ private void CheckForMemberConflictWithEventAccessor( ParametersMatchEventAccessor(eventSymbol, methodSymbol.Parameters)) { // Type '{1}' already reserves a member called '{0}' with the same parameter types - diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrEventLocation(eventSymbol, isAdder), accessorName, this); + diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrEventLocation(eventSymbol, isAdder), accessorName, containerForDiagnostics); return; } } @@ -4178,6 +4595,59 @@ private static void CheckInterfaceMember(Symbol member, BindingDiagnosticBag dia } } + private static void CheckExtensionMembers(ImmutableArray members, BindingDiagnosticBag diagnostics) + { + foreach (var member in members) + { + checkExtensionMember(member, diagnostics); + } + + return; + + static void checkExtensionMember(Symbol member, BindingDiagnosticBag diagnostics) + { + switch (member.Kind) + { + case SymbolKind.Method: + var meth = (MethodSymbol)member; + switch (meth.MethodKind) + { + case MethodKind.Constructor: + case MethodKind.Conversion: + case MethodKind.UserDefinedOperator: + case MethodKind.Destructor: + case MethodKind.EventAdd: + case MethodKind.EventRemove: + case MethodKind.StaticConstructor: + break; + case MethodKind.ExplicitInterfaceImplementation: + // error, but reported elsewhere + return; + case MethodKind.Ordinary: + case MethodKind.PropertyGet: + case MethodKind.PropertySet: + return; + default: + throw ExceptionUtilities.UnexpectedValue(meth.MethodKind); + } + break; + + case SymbolKind.Property: + return; + + case SymbolKind.Field: + case SymbolKind.Event: + case SymbolKind.NamedType: + break; + + default: + throw ExceptionUtilities.UnexpectedValue(member.Kind); + } + + diagnostics.Add(ErrorCode.ERR_ExtensionDisallowsMember, member.GetFirstLocation()); + } + } + private static void CheckForStructDefaultConstructors( ArrayBuilder members, bool isEnum, @@ -4312,6 +4782,11 @@ private void AddSynthesizedTypeMembersIfNecessary(MembersAndInitializersBuilder Debug.Assert(ctor is object); members.Add(ctor); + if (!memberSignatures.ContainsKey(ctor)) + { + memberSignatures.Add(ctor, ctor); + } + if (ctor.ParameterCount != 0) { // properties and Deconstruct @@ -4327,7 +4802,7 @@ private void AddSynthesizedTypeMembersIfNecessary(MembersAndInitializersBuilder if (isRecordClass) { - addCopyCtor(primaryAndCopyCtorAmbiguity); + addCopyCtor(primaryAndCopyCtorAmbiguity, declaredMembersAndInitializers); addCloneMethod(); } @@ -4414,7 +4889,7 @@ void addDeconstruct(SynthesizedPrimaryConstructor ctor, ImmutableArray p } } - void addCopyCtor(bool primaryAndCopyCtorAmbiguity) + void addCopyCtor(bool primaryAndCopyCtorAmbiguity, DeclaredMembersAndInitializers declaredMembersAndInitializers) { Debug.Assert(isRecordClass); var targetMethod = new SignatureOnlyMethodSymbol( @@ -4451,7 +4926,11 @@ void addCopyCtor(bool primaryAndCopyCtorAmbiguity) { var constructor = (MethodSymbol)existingConstructor; - if (!this.IsSealed && (constructor.DeclaredAccessibility != Accessibility.Public && constructor.DeclaredAccessibility != Accessibility.Protected)) + if ((object)constructor == declaredMembersAndInitializers.PrimaryConstructor && primaryAndCopyCtorAmbiguity) + { + diagnostics.Add(ErrorCode.ERR_RecordAmbigCtor, this.GetFirstLocation()); + } + else if (!this.IsSealed && (constructor.DeclaredAccessibility != Accessibility.Public && constructor.DeclaredAccessibility != Accessibility.Protected)) { diagnostics.Add(ErrorCode.ERR_CopyConstructorWrongAccessibility, constructor.GetFirstLocation(), constructor); } @@ -5195,8 +5674,8 @@ private void AddNonTypeMembers( } } - Debug.Assert((object)@event.AddMethod != null); - Debug.Assert((object)@event.RemoveMethod != null); + Debug.Assert(@event.IsPartial || @event.AddMethod is not null); + Debug.Assert(@event.IsPartial || @event.RemoveMethod is not null); AddAccessorIfAvailable(builder.NonTypeMembersWithPartialImplementations, @event.AddMethod); AddAccessorIfAvailable(builder.NonTypeMembersWithPartialImplementations, @event.RemoveMethod); @@ -5451,7 +5930,8 @@ internal bool ContainsExtensionMethods { if (!_lazyContainsExtensionMethods.HasValue()) { - bool containsExtensionMethods = ((this.IsStatic && !this.IsGenericType) || this.IsScriptClass) && this.declaration.ContainsExtensionMethods; + bool containsExtensionMethods = ((this.IsStatic && !this.IsGenericType) || this.IsScriptClass) && + (this.declaration.ContainsExtensionMethods || this.declaration.ContainsExtensionDeclarations); _lazyContainsExtensionMethods = containsExtensionMethods.ToThreeState(); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs index 31ad801a4f1c6..ae0b132a125ee 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol_ImplementationChecks.cs @@ -509,9 +509,10 @@ private void CheckMembersAgainstBaseType( switch (this.TypeKind) { - // These checks don't make sense for enums and delegates: + // These checks don't make sense for enums, delegates or extensions: case TypeKind.Enum: case TypeKind.Delegate: + case TypeKind.Extension: return; case TypeKind.Class: diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 792385eafbda8..9f179a98f89be 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -193,7 +193,7 @@ internal static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingTyp var errorLocation = new SourceLocation(firstIdentifier); DeclarationModifiers result = ModifierUtils.MakeAndCheckNonTypeMemberModifiers( isOrdinaryMethod: false, isForInterfaceMember: isInterface, - modifiers, defaultAccess, allowedModifiers, errorLocation, diagnostics, out modifierErrors); + modifiers, defaultAccess, allowedModifiers, errorLocation, diagnostics, out modifierErrors, out _); if ((result & DeclarationModifiers.Abstract) != 0) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index fc43f0601c28c..94ffe236219c3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -24,7 +24,7 @@ protected struct Flags { // We currently pack everything into a 32 bit int with the following layout: // - // | |t|a|b|e|n|vvv|yy|s|r|q|z|kkk|wwwww| + // | |m|t|a|b|e|n|vvv|yy|s|r|q|z|kkk|wwwww| // // w = method kind. 5 bits. // k = ref kind. 3 bits. @@ -39,6 +39,7 @@ protected struct Flags // b = HasAnyBody. 1 bit. // a = IsVararg. 1 bit. // t = HasThisInitializer. 1 bit. + // m = HasExplicitAccessModifier. 1 bit. private int _flags; private const int MethodKindOffset = 0; @@ -81,8 +82,11 @@ protected struct Flags private const int IsVarargSize = 1; private const int HasThisInitializerOffset = IsVarargOffset + IsVarargSize; -#pragma warning disable IDE0051 // Remove unused private members private const int HasThisInitializerSize = 1; + + private const int HasExplicitAccessModifierOffset = HasThisInitializerOffset + HasThisInitializerSize; +#pragma warning disable IDE0051 // Remove unused private members + private const int HasExplicitAccessModifierSize = 1; #pragma warning restore IDE0051 // Remove unused private members private const int HasAnyBodyBit = 1 << HasAnyBodyOffset; @@ -93,6 +97,7 @@ protected struct Flags private const int IsMetadataVirtualLockedBit = 1 << IsMetadataVirtualLockedOffset; private const int IsVarargBit = 1 << IsVarargOffset; private const int HasThisInitializerBit = 1 << HasThisInitializerOffset; + private const int HasExplicitAccessModifierBit = 1 << HasExplicitAccessModifierOffset; private const int ReturnsVoidBit = 1 << ReturnsVoidOffset; private const int ReturnsVoidIsSetBit = 1 << ReturnsVoidOffset + 1; @@ -161,6 +166,9 @@ public bool IsVararg public readonly bool HasThisInitializer => (_flags & HasThisInitializerBit) != 0; + public readonly bool HasExplicitAccessModifier + => (_flags & HasExplicitAccessModifierBit) != 0; + #if DEBUG static Flags() { @@ -188,7 +196,8 @@ public Flags( bool isNullableAnalysisEnabled, bool isVararg, bool isExplicitInterfaceImplementation, - bool hasThisInitializer) + bool hasThisInitializer, + bool hasExplicitAccessModifier) { Debug.Assert(!returnsVoid || returnsVoidIsSet); @@ -204,6 +213,7 @@ public Flags( int isMetadataVirtualIgnoringInterfaceImplementationChangesInt = isMetadataVirtual ? IsMetadataVirtualIgnoringInterfaceChangesBit : 0; int isMetadataVirtualInt = isMetadataVirtual ? IsMetadataVirtualBit : 0; int hasThisInitializerInt = hasThisInitializer ? HasThisInitializerBit : 0; + int hasExplicitAccessModifierInt = hasExplicitAccessModifier ? HasExplicitAccessModifierBit : 0; _flags = methodKindInt | refKindInt @@ -215,6 +225,7 @@ public Flags( | isMetadataVirtualIgnoringInterfaceImplementationChangesInt | isMetadataVirtualInt | hasThisInitializerInt + | hasExplicitAccessModifierInt | (returnsVoid ? ReturnsVoidBit : 0) | (returnsVoidIsSet ? ReturnsVoidIsSetBit : 0); } @@ -242,7 +253,8 @@ public Flags( isNullableAnalysisEnabled: isNullableAnalysisEnabled, isVararg: isVararg, isExplicitInterfaceImplementation: isExplicitInterfaceImplementation, - hasThisInitializer: hasThisInitializer) + hasThisInitializer: hasThisInitializer, + hasExplicitAccessModifier: false) { } @@ -366,26 +378,42 @@ protected void CheckEffectiveAccessibility(TypeWithAnnotations returnType, Immut } } + if (!IsStatic && this.GetIsNewExtensionMember() && ContainingType.ExtensionParameter is { } extensionParameter) + { + if (!extensionParameter.TypeWithAnnotations.IsAtLeastAsVisibleAs(this, ref useSiteInfo)) + { + // Inconsistent accessibility: parameter type '{1}' is less accessible than method '{0}' + diagnostics.Add(code, GetFirstLocation(), this, extensionParameter.Type); + } + } + diagnostics.Add(GetFirstLocation(), useSiteInfo); } protected void CheckFileTypeUsage(TypeWithAnnotations returnType, ImmutableArray parameters, BindingDiagnosticBag diagnostics) { - if (ContainingType.HasFileLocalTypes()) + NamedTypeSymbol containingType = ContainingType; + + if (containingType is { IsExtension: true, ContainingType: { } enclosing }) + { + containingType = enclosing; + } + + if (containingType.HasFileLocalTypes()) { return; } if (returnType.Type.HasFileLocalTypes()) { - diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), returnType.Type, ContainingType); + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), returnType.Type, containingType); } foreach (var param in parameters) { if (param.Type.HasFileLocalTypes()) { - diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), param.Type, ContainingType); + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), param.Type, containingType); } } } @@ -531,7 +559,7 @@ public sealed override MethodKind MethodKind } } - public override bool IsExtensionMethod + public sealed override bool IsExtensionMethod { get { @@ -1005,74 +1033,6 @@ internal override bool IsNullableAnalysisEnabled() return flags.IsNullableAnalysisEnabled; } - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - if (IsDeclaredReadOnly && !ContainingType.IsReadOnly) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this)); - } - - var compilation = this.DeclaringCompilation; - - if (compilation.ShouldEmitNullableAttributes(this) && - ShouldEmitNullableContextValue(out byte nullableContextValue)) - { - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(this, nullableContextValue)); - } - - if (this.RequiresExplicitOverride(out _)) - { - // On platforms where it is present, add PreserveBaseOverridesAttribute when a methodimpl is used to override a class method. - AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizePreserveBaseOverridesAttribute()); - } - - bool isAsync = this.IsAsync; - bool isIterator = this.IsIterator; - - if (!isAsync && !isIterator) - { - return; - } - - // The async state machine type is not synthesized until the async method body is rewritten. If we are - // only emitting metadata the method body will not have been rewritten, and the async state machine - // type will not have been created. In this case, omit the attribute. - if (moduleBuilder.CompilationState.TryGetStateMachineType(this, out NamedTypeSymbol stateMachineType)) - { - var arg = new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), - TypedConstantKind.Type, stateMachineType.GetUnboundGenericTypeOrSelf()); - - if (isAsync && isIterator) - { - AddSynthesizedAttribute(ref attributes, - compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncIteratorStateMachineAttribute__ctor, - ImmutableArray.Create(arg))); - } - else if (isAsync) - { - AddSynthesizedAttribute(ref attributes, - compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor, - ImmutableArray.Create(arg))); - } - else if (isIterator) - { - AddSynthesizedAttribute(ref attributes, - compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor, - ImmutableArray.Create(arg))); - } - } - - if (isAsync && !isIterator) - { - // Regular async (not async-iterator) kick-off method calls MoveNext, which contains user code. - // This means we need to emit DebuggerStepThroughAttribute in order - // to have correct stepping behavior during debugging. - AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerStepThroughAttribute()); - } - } - /// /// Checks to see if a body is legal given the current modifiers. /// If it is not, a diagnostic is added with the current type. diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs index 3f0597c402e65..63f85869f0c4d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbol.cs @@ -3,8 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -88,5 +91,131 @@ internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArg { return SourceMemberContainerTypeSymbol.HasAsyncMethodBuilderAttribute(this, out builderArgument); } + + internal sealed override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + AddSynthesizedAttributes(this, moduleBuilder, ref attributes); + } + + internal static void AddSynthesizedAttributes(MethodSymbol target, PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + Debug.Assert(target is not (LambdaSymbol or LocalFunctionSymbol)); + + if (target.IsDeclaredReadOnly && !target.ContainingType.IsReadOnly) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(target)); + } + + var compilation = target.DeclaringCompilation; + + if (compilation.ShouldEmitNullableAttributes(target) && + target.ShouldEmitNullableContextValue(out byte nullableContextValue)) + { + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(target, nullableContextValue)); + } + + if (target.RequiresExplicitOverride(out _)) + { + // On platforms where it is present, add PreserveBaseOverridesAttribute when a methodimpl is used to override a class method. + AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizePreserveBaseOverridesAttribute()); + } + + bool isAsync = target.IsAsync; + bool isIterator = target.IsIterator; + + if ((isAsync || isIterator) && !target.GetIsNewExtensionMember()) + { + // The async state machine type is not synthesized until the async method body is rewritten. If we are + // only emitting metadata the method body will not have been rewritten, and the async state machine + // type will not have been created. In this case, omit the attribute. + + if (moduleBuilder.CompilationState.TryGetStateMachineType(target, out NamedTypeSymbol? stateMachineType)) + { + var arg = new TypedConstant(compilation.GetWellKnownType(WellKnownType.System_Type), + TypedConstantKind.Type, stateMachineType.GetUnboundGenericTypeOrSelf()); + + if (isAsync && isIterator) + { + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncIteratorStateMachineAttribute__ctor, + ImmutableArray.Create(arg))); + } + else if (isAsync) + { + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_AsyncStateMachineAttribute__ctor, + ImmutableArray.Create(arg))); + } + else if (isIterator) + { + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IteratorStateMachineAttribute__ctor, + ImmutableArray.Create(arg))); + } + } + + if (isAsync && !isIterator) + { + // Regular async (not async-iterator) kick-off method calls MoveNext, which contains user code. + // This means we need to emit DebuggerStepThroughAttribute in order + // to have correct stepping behavior during debugging. + AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDebuggerStepThroughAttribute()); + } + } + + // Do not generate CompilerGeneratedAttribute for members of compiler-generated types: + if (((target.IsImplicitlyDeclared && target is not SourceFieldLikeEventSymbol.SourceEventDefinitionAccessorSymbol { PartialImplementationPart.IsImplicitlyDeclared: false }) || + target is SourcePropertyAccessorSymbol { IsAutoPropertyAccessor: true }) && + !target.ContainingType.IsImplicitlyDeclared && + target is SynthesizedMethodBaseSymbol or + SourcePropertyAccessorSymbol or + SynthesizedSourceOrdinaryMethodSymbol or + SynthesizedRecordEqualityOperatorBase or + SynthesizedEventAccessorSymbol or + SourceFieldLikeEventSymbol.SourceEventDefinitionAccessorSymbol) + { + Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); + AddSynthesizedAttribute(ref attributes, + compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); + } + + if (target is SourceConstructorSymbolBase) + { + AddRequiredMembersMarkerAttributes(ref attributes, target); + } + + if (target.IsExtensionMethod) + { + // No need to check if [Extension] attribute was explicitly set since + // we'll issue CS1112 error in those cases and won't generate IL. + AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute( + WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor)); + } + + if (target is SourcePropertyAccessorSymbol { AssociatedSymbol: SourcePropertySymbolBase property }) + { + if (!target.NotNullMembers.IsEmpty) + { + foreach (var attributeData in property.MemberNotNullAttributeIfExists) + { + AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData)); + } + } + + if (!target.NotNullWhenTrueMembers.IsEmpty || !target.NotNullWhenFalseMembers.IsEmpty) + { + foreach (var attributeData in property.MemberNotNullWhenAttributeIfExists) + { + AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData)); + } + } + } + + if (target is MethodToClassRewriter.BaseMethodWrapperSymbol) + { + AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor)); + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index 7fa6d895a89b5..ddd951ad2b8d4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -83,6 +83,7 @@ internal SourceNamedTypeSymbol(NamespaceOrTypeSymbol containingSymbol, MergedTyp case DeclarationKind.Class: case DeclarationKind.Record: case DeclarationKind.RecordStruct: + case DeclarationKind.Extension: break; default: Debug.Assert(false, "bad declaration kind"); @@ -162,6 +163,7 @@ private ImmutableArray MakeTypeParameters(BindingDiagnostic case SyntaxKind.InterfaceDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: tpl = ((TypeDeclarationSyntax)typeDecl).TypeParameterList; break; @@ -471,6 +473,7 @@ private static SyntaxList GetConstraintClau case SyntaxKind.InterfaceDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: var typeDeclaration = (TypeDeclarationSyntax)node; typeParameterList = typeDeclaration.TypeParameterList; return typeDeclaration.ConstraintClauses; @@ -1812,6 +1815,16 @@ internal bool IsSimpleProgram } } + public override string MetadataName + { + get + { + return IsExtension + ? MetadataHelpers.ComposeAritySuffixedMetadataName(this.ExtensionName, Arity, associatedFileIdentifier: null) + : base.MetadataName; + } + } + protected override void AfterMembersCompletedChecks(BindingDiagnosticBag diagnostics) { base.AfterMembersCompletedChecks(diagnostics); @@ -1964,6 +1977,16 @@ protected override void AfterMembersCompletedChecks(BindingDiagnosticBag diagnos } } + + if (IsExtension && ContainingType?.IsExtension != true) + { + // If the containing type is an extension, we'll have already reported an error + if (ContainingType is null || !ContainingType.IsStatic || ContainingType.Arity != 0 || ContainingType.ContainingType is not null) + { + var syntax = (ExtensionDeclarationSyntax)this.GetNonNullSyntaxNode(); + diagnostics.Add(ErrorCode.ERR_BadExtensionContainingType, syntax.Keyword); + } + } } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs index c6aef2c70456b..c4ba746250eeb 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Bases.cs @@ -716,6 +716,10 @@ private NamedTypeSymbol MakeAcyclicBaseType(BindingDiagnosticBag diagnostics) Debug.Assert((object)GetDeclaredBaseType(basesBeingResolved: null) == null, "Computation skipped for enums"); declaredBase = compilation.GetSpecialType(SpecialType.System_Enum); } + else if (typeKind == TypeKind.Extension) + { + return null; + } else { declaredBase = GetDeclaredBaseType(basesBeingResolved: null); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs new file mode 100644 index 0000000000000..03182aa0738b8 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol_Extension.cs @@ -0,0 +1,216 @@ +// 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 System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal partial class SourceNamedTypeSymbol + { + private ExtensionInfo _lazyExtensionInfo; + + private class ExtensionInfo + { + public MethodSymbol? LazyExtensionMarker = ErrorMethodSymbol.UnknownMethod; + public ParameterSymbol? LazyExtensionParameter; + public ImmutableDictionary? LazyImplementationMap; + } + + internal override string ExtensionName + { + get + { + if (!IsExtension) + { + throw ExceptionUtilities.Unreachable(); + } + + MergedNamespaceOrTypeDeclaration declaration; + if (ContainingType is not null) + { + declaration = ((SourceNamedTypeSymbol)this.ContainingType).declaration; + } + else + { + declaration = ((SourceNamespaceSymbol)this.ContainingSymbol).MergedDeclaration; + } + + var index = declaration.Children.IndexOf(this.declaration); + return GeneratedNames.MakeExtensionName(index); + } + } + + internal sealed override ParameterSymbol? ExtensionParameter + { + get + { + if (!IsExtension) + { + return null; + } + + var markerMethod = TryGetOrCreateExtensionMarker(); + + if (_lazyExtensionInfo.LazyExtensionParameter == null && markerMethod is { Parameters: [var parameter, ..] }) + { + Interlocked.CompareExchange(ref _lazyExtensionInfo.LazyExtensionParameter, new ReceiverParameterSymbol(this, parameter), null); + } + + return _lazyExtensionInfo.LazyExtensionParameter; + } + } + + public sealed override MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method) + { + Debug.Assert(this.IsExtension); + Debug.Assert(method.IsDefinition); + Debug.Assert(method.ContainingType == (object)this); + + var containingType = this.ContainingType; + + if (containingType is null) + { + return null; // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (_lazyExtensionInfo is null) + { + Interlocked.CompareExchange(ref _lazyExtensionInfo, new ExtensionInfo(), null); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test this code path + } + + if (_lazyExtensionInfo.LazyImplementationMap is null) + { + var builder = ImmutableDictionary.CreateBuilder(Roslyn.Utilities.ReferenceEqualityComparer.Instance); + + builder.AddRange( + containingType.GetMembersUnordered().OfType(). + Select(static m => new KeyValuePair(m.UnderlyingMethod, m))); + + Interlocked.CompareExchange(ref _lazyExtensionInfo.LazyImplementationMap, builder.ToImmutable(), null); + } + + return _lazyExtensionInfo.LazyImplementationMap.GetValueOrDefault(method); + } + + protected sealed override MethodSymbol? CreateSynthesizedExtensionMarker() + { + return TryGetOrCreateExtensionMarker(); + } + + [MemberNotNull(nameof(_lazyExtensionInfo))] + private MethodSymbol? TryGetOrCreateExtensionMarker() + { + Debug.Assert(IsExtension); + + if (_lazyExtensionInfo is null) + { + Interlocked.CompareExchange(ref _lazyExtensionInfo, new ExtensionInfo(), null); + } + + if (_lazyExtensionInfo.LazyExtensionMarker == (object)ErrorMethodSymbol.UnknownMethod) + { + Interlocked.CompareExchange(ref _lazyExtensionInfo.LazyExtensionMarker, tryCreateExtensionMarker(), ErrorMethodSymbol.UnknownMethod); + } + + return _lazyExtensionInfo.LazyExtensionMarker; + + MethodSymbol? tryCreateExtensionMarker() + { + var syntax = (ExtensionDeclarationSyntax)this.GetNonNullSyntaxNode(); + var parameterList = syntax.ParameterList; + Debug.Assert(parameterList is not null); + + if (parameterList is null) + { + return null; + } + + int count = parameterList.Parameters.Count; + Debug.Assert(count > 0); + + return new SynthesizedExtensionMarker(this, parameterList); + } + } + + internal static Symbol? GetCompatibleSubstitutedMember(CSharpCompilation compilation, Symbol extensionMember, TypeSymbol receiverType) + { + Debug.Assert(extensionMember.GetIsNewExtensionMember()); + + NamedTypeSymbol extension = extensionMember.ContainingType; + if (extension.ExtensionParameter is null) + { + return null; + } + + Symbol result; + if (extensionMember.IsDefinition) + { + NamedTypeSymbol? constructedExtension = inferExtensionTypeArguments(extension, receiverType, compilation); + if (constructedExtension is null) + { + return null; + } + + result = extensionMember.SymbolAsMember(constructedExtension); + } + else + { + result = extensionMember; + } + + Debug.Assert(result.ContainingType.ExtensionParameter is not null); + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + Conversion conversion = compilation.Conversions.ConvertExtensionMethodThisArg(parameterType: result.ContainingType.ExtensionParameter.Type, receiverType, ref discardedUseSiteInfo, isMethodGroupConversion: false); + if (!conversion.Exists) + { + return null; + } + + return result; + + static NamedTypeSymbol? inferExtensionTypeArguments(NamedTypeSymbol extension, TypeSymbol receiverType, CSharpCompilation compilation) + { + if (extension.Arity == 0) + { + return extension; + } + + TypeConversions conversions = extension.ContainingAssembly.CorLibrary.TypeConversions; + + // Note: we create a value for purpose of inferring type arguments even when the receiver type is static + var syntax = (CSharpSyntaxNode)CSharpSyntaxTree.Dummy.GetRoot(); + var receiverValue = new BoundLiteral(syntax, ConstantValue.Bad, receiverType) { WasCompilerGenerated = true }; + + var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded; + ImmutableArray typeArguments = MethodTypeInferrer.InferTypeArgumentsFromReceiverType(extension, receiverValue, compilation, conversions, ref discardedUseSiteInfo); + if (typeArguments.IsDefault || typeArguments.Any(t => !t.HasType)) + { + return null; + } + + var result = extension.Construct(typeArguments); + + var constraintArgs = new ConstraintsHelper.CheckConstraintsArgs(compilation, conversions, includeNullability: false, + NoLocation.Singleton, diagnostics: BindingDiagnosticBag.Discarded, template: CompoundUseSiteInfo.Discarded); + + bool success = result.CheckConstraints(constraintArgs); + if (!success) + { + return null; + } + + return result; + } + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs index a02eb45cc149c..ee1bea85c7f90 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamespaceSymbol.cs @@ -313,6 +313,8 @@ private static void CheckMembers(NamespaceSymbol @namespace, Dictionary GetSyntax().ReturnType.Location; internal MethodDeclarationSyntax GetSyntax() @@ -424,7 +429,7 @@ private SyntaxList AttributeDeclarationSyntaxList private static DeclarationModifiers MakeDeclarationModifiers(MethodDeclarationSyntax syntax, NamedTypeSymbol containingType, Location location, DeclarationModifiers allowedModifiers, BindingDiagnosticBag diagnostics) { return ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: true, isForInterfaceMember: containingType.IsInterface, - syntax.Modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out _); + syntax.Modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out _, out _); } internal sealed override void ForceComplete(SourceLocation locationOpt, Predicate filter, CancellationToken cancellationToken) @@ -679,10 +684,20 @@ protected override void MethodChecks(BindingDiagnosticBag diagnostics) CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, _location, diagnostics); } + + internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, BindingDiagnosticBag diagnostics) + { + base.AfterAddingTypeMembersChecks(conversions, diagnostics); + + if (this.ReturnType?.IsErrorType() == true && GetSyntax().ReturnType is IdentifierNameSyntax { Identifier.RawContextualKind: (int)SyntaxKind.PartialKeyword }) + { + var available = MessageID.IDS_FeaturePartialEventsAndConstructors.CheckFeatureAvailability(diagnostics, DeclaringCompilation, ReturnTypeLocation); + Debug.Assert(!available, "Should have been parsed as partial constructor."); + } + } #nullable disable - // Consider moving this to flags to save space - internal bool HasExplicitAccessModifier { get; } + internal bool HasExplicitAccessModifier => flags.HasExplicitAccessModifier; private static (DeclarationModifiers mods, bool hasExplicitAccessMod) MakeModifiers(MethodDeclarationSyntax syntax, NamedTypeSymbol containingType, MethodKind methodKind, bool hasBody, Location location, BindingDiagnosticBag diagnostics) { @@ -1126,24 +1141,37 @@ private ImmutableArray MakeTypeParameters(MethodDeclaration // Note: It is not an error to have a type parameter named the same as its enclosing method: void M() {} - for (int i = 0; i < result.Count; i++) + var tpEnclosing = ContainingType.FindEnclosingTypeParameter(name); + bool checkForDuplicates = true; + + if ((object)tpEnclosing != null) { - if (name == result[i].Name) + if (tpEnclosing.ContainingSymbol is NamedTypeSymbol { IsExtension: true }) { - diagnostics.Add(ErrorCode.ERR_DuplicateTypeParameter, location, name); - break; + diagnostics.Add(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, location, name); + checkForDuplicates = false; + } + else + { + // Type parameter '{0}' has the same name as the type parameter from outer type '{1}' + diagnostics.Add(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, location, name, tpEnclosing.ContainingType); } } - SourceMemberContainerTypeSymbol.ReportReservedTypeName(identifier.Text, this.DeclaringCompilation, diagnostics.DiagnosticBag, location); - - var tpEnclosing = ContainingType.FindEnclosingTypeParameter(name); - if ((object)tpEnclosing != null) + if (checkForDuplicates) { - // Type parameter '{0}' has the same name as the type parameter from outer type '{1}' - diagnostics.Add(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, location, name, tpEnclosing.ContainingType); + for (int i = 0; i < result.Count; i++) + { + if (name == result[i].Name) + { + diagnostics.Add(ErrorCode.ERR_DuplicateTypeParameter, location, name); + break; + } + } } + SourceMemberContainerTypeSymbol.ReportReservedTypeName(identifier.Text, this.DeclaringCompilation, diagnostics.DiagnosticBag, location); + var syntaxRefs = ImmutableArray.Create(parameter.GetReference()); var locations = ImmutableArray.Create(location); var typeParameter = (typeMap != null) ? diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs index ade24d4dcfefe..769d31c34ca28 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -80,7 +80,7 @@ private void CompleteAsyncMethodChecks(BindingDiagnosticBag diagnosticsOpt, Canc public abstract override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)); - public override string Name + public sealed override string Name { get { @@ -91,20 +91,5 @@ public override string Name protected abstract override SourceMemberMethodSymbol BoundAttributesSource { get; } internal abstract override OneOrMany> GetAttributeDeclarations(); - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - if (this.IsExtensionMethod) - { - // No need to check if [Extension] attribute was explicitly set since - // we'll issue CS1112 error in those cases and won't generate IL. - var compilation = this.DeclaringCompilation; - - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute( - WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor)); - } - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs index 15c514be372ef..dfe4b4f419760 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceParameterSymbol.cs @@ -44,7 +44,7 @@ public static SourceParameterSymbol Create( Debug.Assert(owner is not LambdaSymbol); // therefore we don't need to deal with discard parameters var name = identifier.ValueText; - var location = new SourceLocation(identifier); + var location = new SourceLocation(name == "" ? syntax.Type : identifier); if (hasParamsModifier && parameterType.IsSZArray()) { @@ -108,7 +108,7 @@ protected SourceParameterSymbol( Location location) : base(owner, ordinal) { - Debug.Assert((owner.Kind == SymbolKind.Method) || (owner.Kind == SymbolKind.Property)); + Debug.Assert((owner.Kind == SymbolKind.Method) || (owner.Kind == SymbolKind.Property) || owner is TypeSymbol { IsExtension: true }); _refKind = refKind; _scope = scope; _name = name; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 4c7a1dd16a416..c67c5fc3d1f61 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -512,7 +512,7 @@ private static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingType } var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, - modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors); + modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors, out _); ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods, defaultInterfaceImplementationModifiers, @@ -654,13 +654,16 @@ private SyntaxList AttributeDeclarationList { get { - var syntax = this.GetSyntax(); - switch (syntax.Kind()) + if (this._property.ContainingType is SourceMemberContainerTypeSymbol { AnyMemberHasAttributes: true }) { - case SyntaxKind.GetAccessorDeclaration: - case SyntaxKind.SetAccessorDeclaration: - case SyntaxKind.InitAccessorDeclaration: - return ((AccessorDeclarationSyntax)syntax).AttributeLists; + var syntax = this.GetSyntax(); + switch (syntax.Kind()) + { + case SyntaxKind.GetAccessorDeclaration: + case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: + return ((AccessorDeclarationSyntax)syntax).AttributeLists; + } } return default; @@ -721,7 +724,7 @@ public sealed override string Name } #nullable disable - public sealed override bool IsImplicitlyDeclared + public override bool IsImplicitlyDeclared { get { @@ -801,33 +804,6 @@ internal sealed override void AddSynthesizedReturnTypeAttributes(PEModuleBuilder } } - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - if (_isAutoPropertyAccessor) - { - var compilation = this.DeclaringCompilation; - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - } - - if (!NotNullMembers.IsEmpty) - { - foreach (var attributeData in _property.MemberNotNullAttributeIfExists) - { - AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData)); - } - } - - if (!NotNullWhenTrueMembers.IsEmpty || !NotNullWhenFalseMembers.IsEmpty) - { - foreach (var attributeData in _property.MemberNotNullWhenAttributeIfExists) - { - AddSynthesizedAttribute(ref attributes, SynthesizedAttributeData.Create(attributeData)); - } - } - } - #nullable enable protected sealed override SourceMemberMethodSymbol? BoundAttributesSource => (SourceMemberMethodSymbol?)PartialDefinitionPart; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index 9c716dd1fa2bf..b5cd129376f0b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -199,12 +199,25 @@ public override OneOrMany> GetAttributeDeclarati if (SourcePartialImplementationPart is { } implementationPart) { return OneOrMany.Create( - ((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists, - ((BasePropertyDeclarationSyntax)implementationPart.CSharpSyntaxNode).AttributeLists); + this.AttributeDeclarationSyntaxList, + implementationPart.AttributeDeclarationSyntaxList); } else { - return OneOrMany.Create(((BasePropertyDeclarationSyntax)CSharpSyntaxNode).AttributeLists); + return OneOrMany.Create(this.AttributeDeclarationSyntaxList); + } + } + + private SyntaxList AttributeDeclarationSyntaxList + { + get + { + if (this.ContainingType is SourceMemberContainerTypeSymbol { AnyMemberHasAttributes: true }) + { + return ((BasePropertyDeclarationSyntax)this.CSharpSyntaxNode).AttributeLists; + } + + return default; } } @@ -430,20 +443,9 @@ private static (DeclarationModifiers modifiers, bool hasExplicitAccessMod) MakeM allowedModifiers |= DeclarationModifiers.Extern; - // In order to detect whether explicit accessibility mods were provided, we pass the default value - // for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call. bool hasExplicitAccessMod; var mods = ModifierUtils.MakeAndCheckNonTypeMemberModifiers(isOrdinaryMethod: false, isForInterfaceMember: isInterface, - modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out modifierErrors); - if ((mods & DeclarationModifiers.AccessibilityMask) == 0) - { - hasExplicitAccessMod = false; - mods |= defaultAccess; - } - else - { - hasExplicitAccessMod = true; - } + modifiers, defaultAccess, allowedModifiers, location, diagnostics, out modifierErrors, out hasExplicitAccessMod); if ((mods & DeclarationModifiers.Partial) != 0) { @@ -574,9 +576,18 @@ private TypeWithAnnotations ComputeType(Binder binder, SyntaxNode syntax, Bindin diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type); } - if (type.Type.HasFileLocalTypes() && !ContainingType.HasFileLocalTypes()) + if (type.Type.HasFileLocalTypes()) { - diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, type.Type, ContainingType); + NamedTypeSymbol containingType = ContainingType; + if (containingType is { IsExtension: true, ContainingType: { } enclosing }) + { + containingType = enclosing; + } + + if (!containingType.HasFileLocalTypes()) + { + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, type.Type, containingType); + } } diagnostics.Add(Location, useSiteInfo); @@ -650,15 +661,21 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, var useSiteInfo = new CompoundUseSiteInfo(diagnostics, ContainingAssembly); + var containingTypeForFileTypeCheck = this.ContainingType; + if (containingTypeForFileTypeCheck is { IsExtension: true, ContainingType: { } enclosing }) + { + containingTypeForFileTypeCheck = enclosing; + } + foreach (ParameterSymbol param in Parameters) { if (!IsExplicitInterfaceImplementation && !this.IsNoMoreVisibleThan(param.Type, ref useSiteInfo)) { diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type); } - else if (param.Type.HasFileLocalTypes() && !this.ContainingType.HasFileLocalTypes()) + else if (param.Type.HasFileLocalTypes() && !containingTypeForFileTypeCheck.HasFileLocalTypes()) { - diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, param.Type, this.ContainingType); + diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, param.Type, containingTypeForFileTypeCheck); } else if (SetMethod is object && param.Name == ParameterSymbol.ValueParameterName) { @@ -666,6 +683,25 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, } } + if (SetMethod is { } setter && this.GetIsNewExtensionMember()) + { + if (ContainingType.TypeParameters.Any(static tp => tp.Name == ParameterSymbol.ValueParameterName)) + { + diagnostics.Add(ErrorCode.ERR_ValueParameterSameNameAsExtensionTypeParameter, setter.GetFirstLocationOrNone()); + } + + if (ContainingType.ExtensionParameter is { Name: ParameterSymbol.ValueParameterName }) + { + diagnostics.Add(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, setter.GetFirstLocationOrNone()); + } + } + + if (!IsStatic && this.GetIsNewExtensionMember() && ContainingType.ExtensionParameter is { } extensionParameter && + !this.IsNoMoreVisibleThan(extensionParameter.Type, ref useSiteInfo)) + { + diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, extensionParameter.Type); + } + diagnostics.Add(Location, useSiteInfo); if (IsPartialDefinition && OtherPartOfPartial is { } implementation) @@ -690,7 +726,7 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD if (hasTypeDifferences) { - diagnostics.Add(ErrorCode.ERR_PartialPropertyTypeDifference, implementation.GetFirstLocation()); + diagnostics.Add(ErrorCode.ERR_PartialMemberTypeDifference, implementation.GetFirstLocation()); } else if (MemberSignatureComparer.ConsideringTupleNamesCreatesDifference(this, implementation)) { @@ -707,7 +743,7 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD if ((!hasTypeDifferences && !MemberSignatureComparer.PartialMethodsStrictComparer.Equals(this, implementation)) || !Parameters.SequenceEqual(implementation.Parameters, (a, b) => a.Name == b.Name)) { - diagnostics.Add(ErrorCode.WRN_PartialPropertySignatureDifference, implementation.GetFirstLocation(), + diagnostics.Add(ErrorCode.WRN_PartialMemberSignatureDifference, implementation.GetFirstLocation(), new FormattedSymbol(this, SymbolDisplayFormat.MinimallyQualifiedFormat), new FormattedSymbol(implementation, SymbolDisplayFormat.MinimallyQualifiedFormat)); } @@ -789,8 +825,8 @@ private void PartialPropertyChecks(SourcePropertySymbol implementation, BindingD internal SourcePropertySymbol? SourcePartialDefinitionPart => IsPartialImplementation ? OtherPartOfPartial : null; internal SourcePropertySymbol? SourcePartialImplementationPart => IsPartialDefinition ? OtherPartOfPartial : null; - internal override PropertySymbol? PartialDefinitionPart => SourcePartialDefinitionPart; - internal override PropertySymbol? PartialImplementationPart => SourcePartialImplementationPart; + internal sealed override PropertySymbol? PartialDefinitionPart => SourcePartialDefinitionPart; + internal sealed override PropertySymbol? PartialImplementationPart => SourcePartialImplementationPart; internal static void InitializePartialPropertyParts(SourcePropertySymbol definition, SourcePropertySymbol implementation) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs index d3fa761b0baee..a2062e129739a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs @@ -470,7 +470,11 @@ internal string SourceName // always use the real attribute bag of this symbol and modify LoadAndValidateAttributes to // handle partially filled bags. CustomAttributesBag? temp = null; - LoadAndValidateAttributes(OneOrMany.Create(indexerNameAttributeLists), ref temp, earlyDecodingOnly: true); + Binder rootBinder = GetAttributeBinder(indexerNameAttributeLists, DeclaringCompilation); + LoadAndValidateAttributes( + OneOrMany.Create(indexerNameAttributeLists), ref temp, earlyDecodingOnly: true, + binderOpt: rootBinder, + attributeMatchesOpt: this.GetIsNewExtensionMember() ? isPossibleIndexerNameAttributeInExtension : isPossibleIndexerNameAttribute); if (temp != null) { Debug.Assert(temp.IsEarlyDecodedWellKnownAttributeDataComputed); @@ -487,6 +491,24 @@ internal string SourceName } return _lazySourceName; + + static bool isPossibleIndexerNameAttribute(AttributeSyntax node, Binder? rootBinderOpt) + { + Debug.Assert(rootBinderOpt is not null); + QuickAttributeChecker checker = rootBinderOpt.QuickAttributeChecker; + return checker.IsPossibleMatch(node, QuickAttributes.IndexerName); + } + + static bool isPossibleIndexerNameAttributeInExtension(AttributeSyntax node, Binder? rootBinderOpt) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Temporarily limit binding to a string literal argument in order to avoid a binding cycle. + if (node.ArgumentList?.Arguments is not [{ NameColon: null, NameEquals: null, Expression: LiteralExpressionSyntax { RawKind: (int)SyntaxKind.StringLiteralExpression } }]) + { + return false; + } + + return isPossibleIndexerNameAttribute(node, rootBinderOpt); + } } } #nullable disable @@ -1696,6 +1718,12 @@ private void ValidateIndexerNameAttribute(CSharpAttributeData attribute, Attribu { diagnostics.Add(ErrorCode.ERR_BadArgumentToAttribute, node.ArgumentList.Arguments[0].Location, node.GetErrorDisplayName()); } + else if (this.GetIsNewExtensionMember() && SourceName != indexerName) + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Report more descriptive error + // error CS8078: An expression is too long or complex to compile + diagnostics.Add(ErrorCode.ERR_InsufficientStack, node.ArgumentList.Arguments[0].Location); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs index b1c4daff40be3..bbb1b18035d39 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbolBase.cs @@ -153,7 +153,7 @@ protected static DeclarationModifiers MakeDeclarationModifiers(MethodKind method var result = ModifierUtils.MakeAndCheckNonTypeMemberModifiers( isOrdinaryMethod: false, isForInterfaceMember: inInterface, - syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, modifierErrors: out _); + syntax.Modifiers, defaultAccess, allowedModifiers, location, diagnostics, modifierErrors: out _, hasExplicitAccessModifier: out _); if (inInterface) { @@ -766,14 +766,6 @@ public sealed override string Name } } - public sealed override bool IsExtensionMethod - { - get - { - return false; - } - } - public sealed override ImmutableArray TypeParameters { get { return ImmutableArray.Empty; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SynthesizedSourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SynthesizedSourceOrdinaryMethodSymbol.cs new file mode 100644 index 0000000000000..5b642de3e2371 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SynthesizedSourceOrdinaryMethodSymbol.cs @@ -0,0 +1,77 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// + /// Common base for ordinary methods synthesized by compiler and added to the result. + /// + internal abstract class SynthesizedSourceOrdinaryMethodSymbol : SourceOrdinaryMethodSymbolBase + { + protected SynthesizedSourceOrdinaryMethodSymbol(SourceMemberContainerTypeSymbol containingType, string name, Location location, CSharpSyntaxNode syntax, (DeclarationModifiers declarationModifiers, Flags flags) modifiersAndFlags) + : base(containingType, name, location, syntax, isIterator: false, modifiersAndFlags) + { + } + + protected override void MethodChecks(BindingDiagnosticBag diagnostics) + { + Debug.Assert(Arity == 0); + var (returnType, parameters) = MakeParametersAndBindReturnType(diagnostics); + MethodChecks(returnType, parameters, diagnostics); + } + + protected abstract (TypeWithAnnotations ReturnType, ImmutableArray Parameters) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics); + + public sealed override bool IsImplicitlyDeclared => true; + + protected sealed override Location ReturnTypeLocation => GetFirstLocation(); + + protected sealed override MethodSymbol? FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) => null; + + public sealed override ImmutableArray TypeParameters => ImmutableArray.Empty; + + public sealed override ImmutableArray> GetTypeParameterConstraintTypes() => ImmutableArray>.Empty; + + public sealed override ImmutableArray GetTypeParameterConstraintKinds() => ImmutableArray.Empty; + + protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnostics) + { + } + + protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) + { + } + + protected sealed override void CompleteAsyncMethodChecksBetweenStartAndFinish() + { + } + + protected sealed override TypeSymbol? ExplicitInterfaceType => null; + + protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics) + { + } + + protected sealed override SourceMemberMethodSymbol? BoundAttributesSource => null; + + internal sealed override OneOrMany> GetAttributeDeclarations() => OneOrMany.Create(default(SyntaxList)); + + public sealed override string? GetDocumentationCommentXml(CultureInfo? preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default) => null; + + internal sealed override bool GenerateDebugInfo => false; + + internal sealed override bool SynthesizesLoweredBoundBody => true; + internal sealed override ExecutableCodeBinder? TryGetBodyBinder(BinderFactory? binderFactoryOpt = null, bool ignoreAccessibility = false) => throw ExceptionUtilities.Unreachable(); + internal abstract override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics); + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs index 379aae474eb7e..f83a89bd604e5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedNamedTypeSymbol.cs @@ -9,6 +9,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Emit; @@ -40,6 +41,10 @@ internal abstract class SubstitutedNamedTypeSymbol : WrappedNamedTypeSymbol private TypeMap _lazyMap; private ImmutableArray _lazyTypeParameters; +#nullable enable + private StrongBox _lazyExtensionParameter; +#nullable disable + private NamedTypeSymbol _lazyBaseType = ErrorTypeSymbol.UnknownResultType; // computed on demand @@ -487,5 +492,35 @@ internal sealed override bool HasInlineArrayAttribute(out int length) } internal sealed override bool HasCompilerLoweringPreserveAttribute => _underlyingType.HasCompilerLoweringPreserveAttribute; + +#nullable enable + internal sealed override ParameterSymbol? ExtensionParameter + { + get + { + if (_lazyExtensionParameter is null) + { + Interlocked.CompareExchange(ref _lazyExtensionParameter, new StrongBox(substituteParameter()), null); + } + + return _lazyExtensionParameter.Value; + + ParameterSymbol? substituteParameter() + { + if (!this.IsExtension) + { + return null; + } + + var unsubstitutedParameter = OriginalDefinition.ExtensionParameter; + if (unsubstitutedParameter is null) + { + return null; + } + + return new SubstitutedParameterSymbol(containingSymbol: this, Map, unsubstitutedParameter); + } + } + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs index 383aacb6b7583..d8b6c80c504c5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs @@ -24,6 +24,11 @@ internal SubstitutedParameterSymbol(PropertySymbol containingSymbol, TypeMap map { } + internal SubstitutedParameterSymbol(NamedTypeSymbol containingSymbol, TypeMap map, ParameterSymbol originalParameter) : + this((Symbol)containingSymbol, map, originalParameter) + { + } + private SubstitutedParameterSymbol(Symbol containingSymbol, TypeMap map, ParameterSymbol originalParameter) : base(originalParameter) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 216b61f6d68ae..1b843d8c13d89 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -9,7 +9,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; -using System.Reflection.Metadata; using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -648,10 +647,12 @@ public bool CanBeReferencedByName break; case SymbolKind.NamedType: - if (((NamedTypeSymbol)this).IsSubmissionClass) + var namedType = (NamedTypeSymbol)this; + if (namedType.IsSubmissionClass || namedType.IsExtension) { return false; } + break; case SymbolKind.Property: @@ -1740,6 +1741,13 @@ internal static bool IsCaptured(Symbol variable, SourceMethodSymbol containingSy throw ExceptionUtilities.UnexpectedValue(variable.Kind); } + if (containingSymbol.GetIsNewExtensionMember() + && variable is ParameterSymbol { ContainingSymbol: TypeSymbol { IsExtension: true } }) + { + // An extension member doesn't capture the extension parameter + return false; + } + // Walk up the containing symbols until we find the target function, in which // case the variable is not captured by the target function, or null, in which // case it is. diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index 752624007b7e6..0850691a84d82 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -302,7 +302,7 @@ internal bool LoadAndValidateAttributes( AttributeLocation symbolPart = AttributeLocation.None, bool earlyDecodingOnly = false, Binder? binderOpt = null, - Func? attributeMatchesOpt = null, + Func? attributeMatchesOpt = null, Action? beforeAttributePartBound = null, Action? afterAttributePartBound = null) { @@ -586,7 +586,7 @@ private ImmutableArray GetAttributesToBind( AttributeLocation symbolPart, BindingDiagnosticBag diagnostics, CSharpCompilation compilation, - Func attributeMatchesOpt, + Func attributeMatchesOpt, Binder rootBinderOpt, out ImmutableArray binders) { @@ -624,7 +624,7 @@ private ImmutableArray GetAttributesToBind( { foreach (var attribute in attributesToBind) { - if (attributeMatchesOpt(attribute)) + if (attributeMatchesOpt(attribute, rootBinderOpt)) { syntaxBuilder.Add(attribute); attributesToBindCount++; @@ -666,7 +666,7 @@ protected virtual bool ShouldBindAttributes(AttributeListSyntax attributeDeclara } #nullable enable - private Binder GetAttributeBinder(SyntaxList attributeDeclarationSyntaxList, CSharpCompilation compilation, Binder? rootBinder = null) + protected Binder GetAttributeBinder(SyntaxList attributeDeclarationSyntaxList, CSharpCompilation compilation, Binder? rootBinder = null) { var binder = rootBinder ?? compilation.GetBinderFactory(attributeDeclarationSyntaxList.Node!.SyntaxTree).GetBinder(attributeDeclarationSyntaxList.Node); binder = new ContextualAttributeBinder(binder, this); diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs index 1ce4cbea2afe8..81e158c5e9331 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNameKind.cs @@ -30,6 +30,7 @@ internal enum GeneratedNameKind ReusableHoistedLocalField = '7', LambdaCacheField = '9', FixedBufferField = 'e', + Extension = 'E', FileType = 'F', AnonymousType = 'f', TransparentIdentifier = 'h', diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 2430ad6d920c0..e55d1f8eca160 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -70,6 +70,12 @@ internal static string MakeAnonymousTypeBackingFieldName(string propertyName) return "<" + propertyName + ">i__Field"; } + internal static string MakeExtensionName(int index) + { + Debug.Assert((char)GeneratedNameKind.Extension == 'E'); + return "<>E__" + StringExtensions.GetNumeral(index); + } + internal static string MakeAnonymousTypeParameterName(string propertyName) { return "<" + propertyName + ">j__TPar"; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListEnumeratorTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListEnumeratorTypeSymbol.cs index 927c1d23d5931..0d6a1dd0e4773 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListEnumeratorTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListEnumeratorTypeSymbol.cs @@ -139,6 +139,9 @@ static void addProperty(ArrayBuilder builder, PropertySymbol property) public override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => false; public override Symbol ContainingSymbol => _containingType; @@ -173,6 +176,8 @@ static void addProperty(ArrayBuilder builder, PropertySymbol property) internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal override bool HasSpecialName => false; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs index 43b5765ed7076..7d1c4d80cfd6b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/ReadOnlyListType/SynthesizedReadOnlyListTypeSymbol.cs @@ -816,6 +816,9 @@ public static bool CanCreateSingleElement(CSharpCompilation compilation) public override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => false; public override Symbol? ContainingSymbol => _containingModule.GlobalNamespace; @@ -850,6 +853,8 @@ public static bool CanCreateSingleElement(CSharpCompilation compilation) internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal override bool HasSpecialName => false; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs index b99b4283207fc..bf26af38968aa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityContractProperty.cs @@ -155,14 +155,7 @@ internal GetAccessorSymbol( { } - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - var compilation = this.DeclaringCompilation; - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - } + public override bool IsImplicitlyDeclared => true; public override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs index 9b1f67f53bcd3..ec959a2e84c7b 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordEqualityOperatorBase.cs @@ -77,14 +77,5 @@ protected sealed override (TypeWithAnnotations ReturnType, ImmutableArray 2; - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - Debug.Assert(IsImplicitlyDeclared); - var compilation = this.DeclaringCompilation; - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs index 404ed77cadab1..6811e4cd49ea6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedRecordOrdinaryMethod.cs @@ -2,27 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; -using System.Diagnostics; -using System.Globalization; -using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// /// Common base for ordinary methods synthesized by compiler for records. /// - internal abstract class SynthesizedRecordOrdinaryMethod : SourceOrdinaryMethodSymbolBase + internal abstract class SynthesizedRecordOrdinaryMethod : SynthesizedSourceOrdinaryMethodSymbol { private readonly int _memberOffset; protected SynthesizedRecordOrdinaryMethod(SourceMemberContainerTypeSymbol containingType, string name, int memberOffset, DeclarationModifiers declarationModifiers) : base(containingType, name, containingType.GetFirstLocation(), (CSharpSyntaxNode)containingType.SyntaxReferences[0].GetSyntax(), - isIterator: false, (declarationModifiers, MakeFlags( MethodKind.Ordinary, RefKind.None, declarationModifiers, returnsVoid: false, returnsVoidIsSet: false, isExpressionBodied: false, isExtensionMethod: false, isNullableAnalysisEnabled: false, isVarArg: false, @@ -31,66 +21,6 @@ protected SynthesizedRecordOrdinaryMethod(SourceMemberContainerTypeSymbol contai _memberOffset = memberOffset; } - protected override void MethodChecks(BindingDiagnosticBag diagnostics) - { - Debug.Assert(Arity == 0); - var (returnType, parameters) = MakeParametersAndBindReturnType(diagnostics); - MethodChecks(returnType, parameters, diagnostics); - } - - protected abstract (TypeWithAnnotations ReturnType, ImmutableArray Parameters) MakeParametersAndBindReturnType(BindingDiagnosticBag diagnostics); - - public sealed override bool IsImplicitlyDeclared => true; - - protected sealed override Location ReturnTypeLocation => GetFirstLocation(); - - protected sealed override MethodSymbol? FindExplicitlyImplementedMethod(BindingDiagnosticBag diagnostics) => null; - internal sealed override LexicalSortKey GetLexicalSortKey() => LexicalSortKey.GetSynthesizedMemberKey(_memberOffset); - - public sealed override ImmutableArray TypeParameters => ImmutableArray.Empty; - - public sealed override ImmutableArray> GetTypeParameterConstraintTypes() => ImmutableArray>.Empty; - - public sealed override ImmutableArray GetTypeParameterConstraintKinds() => ImmutableArray.Empty; - - protected sealed override void PartialMethodChecks(BindingDiagnosticBag diagnostics) - { - } - - protected sealed override void ExtensionMethodChecks(BindingDiagnosticBag diagnostics) - { - } - - protected sealed override void CompleteAsyncMethodChecksBetweenStartAndFinish() - { - } - - protected sealed override TypeSymbol? ExplicitInterfaceType => null; - - protected sealed override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics) - { - } - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - Debug.Assert(IsImplicitlyDeclared); - var compilation = this.DeclaringCompilation; - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - Debug.Assert(WellKnownMembers.IsSynthesizedAttributeOptional(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); - } - - protected sealed override SourceMemberMethodSymbol? BoundAttributesSource => null; - - internal sealed override OneOrMany> GetAttributeDeclarations() => OneOrMany.Create(default(SyntaxList)); - - public sealed override string? GetDocumentationCommentXml(CultureInfo? preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default) => null; - - internal sealed override bool GenerateDebugInfo => false; - - internal sealed override bool SynthesizesLoweredBoundBody => true; - internal sealed override ExecutableCodeBinder? TryGetBodyBinder(BinderFactory? binderFactoryOpt = null, bool ignoreAccessibility = false) => throw ExceptionUtilities.Unreachable(); - internal abstract override void GenerateMethodBody(TypeCompilationState compilationState, BindingDiagnosticBag diagnostics); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs index 851b7d1f214d2..b7290cbe61ecc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedBackingFieldSymbol.cs @@ -4,8 +4,10 @@ #nullable disable +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; @@ -78,6 +80,7 @@ public override bool IsImplicitlyDeclared internal sealed class SynthesizedBackingFieldSymbol : SynthesizedBackingFieldSymbolBase { private readonly SourcePropertySymbolBase _property; + private int _inferredNullableAnnotation = (int)NullableAnnotation.Ignored; internal override bool HasInitializer { get; } public SynthesizedBackingFieldSymbol( @@ -122,6 +125,110 @@ public override ImmutableArray Locations internal override TypeWithAnnotations GetFieldType(ConsList fieldsBeingBound) => _property.TypeWithAnnotations; +#nullable enable + internal bool InfersNullableAnnotation + { + get + { + if (FlowAnalysisAnnotations != FlowAnalysisAnnotations.None) + { + return false; + } + + var propertyType = _property.TypeWithAnnotations; + if (propertyType.NullableAnnotation != NullableAnnotation.NotAnnotated + || !_property.UsesFieldKeyword) + { + return false; + } + + return true; + } + } + + internal NullableAnnotation GetInferredNullableAnnotation() + { + if (_inferredNullableAnnotation == (int)NullableAnnotation.Ignored) + { + var inferredAnnotation = ComputeInferredNullableAnnotation(); + Debug.Assert(inferredAnnotation is not NullableAnnotation.Ignored); + Interlocked.CompareExchange(ref _inferredNullableAnnotation, (int)inferredAnnotation, (int)NullableAnnotation.Ignored); + } + Debug.Assert((NullableAnnotation)_inferredNullableAnnotation is NullableAnnotation.NotAnnotated or NullableAnnotation.Annotated); + return (NullableAnnotation)_inferredNullableAnnotation; + } + + private NullableAnnotation ComputeInferredNullableAnnotation() + { + // https://github.com/dotnet/csharplang/blob/94205582d0f5c73e5765cb5888311c2f14890b95/proposals/field-keyword.md#nullability-of-the-backing-field + Debug.Assert(InfersNullableAnnotation); + + // If the property does not have a get accessor, it is (vacuously) null-resilient. + if (_property.GetMethod is not SourcePropertyAccessorSymbol getAccessor) + { + Debug.Assert(_property.GetMethod is null); + return NullableAnnotation.Annotated; + } + + // If the get accessor is auto-implemented, the property is not null-resilient. + if (getAccessor.IsAutoPropertyAccessor) + return NullableAnnotation.NotAnnotated; + + getAccessor = (SourcePropertyAccessorSymbol?)getAccessor.PartialImplementationPart ?? getAccessor; + var binder = getAccessor.TryGetBodyBinder() ?? throw ExceptionUtilities.UnexpectedValue(getAccessor); + var boundGetAccessor = binder.BindMethodBody(getAccessor.SyntaxNode, BindingDiagnosticBag.Discarded); + + var annotatedDiagnostics = nullableAnalyzeAndFilterDiagnostics(assumedNullableAnnotation: NullableAnnotation.Annotated); + if (annotatedDiagnostics.IsEmptyWithoutResolution) + { + // If the pass where the field was annotated results in no diagnostics at all, then the property is null-resilient and the not-annotated pass can be skipped. + annotatedDiagnostics.Free(); + return NullableAnnotation.Annotated; + } + + var notAnnotatedDiagnostics = nullableAnalyzeAndFilterDiagnostics(assumedNullableAnnotation: NullableAnnotation.NotAnnotated); + if (notAnnotatedDiagnostics.IsEmptyWithoutResolution) + { + // annotated pass had diagnostics, and not-annotated pass had no diagnostics. + annotatedDiagnostics.Free(); + notAnnotatedDiagnostics.Free(); + return NullableAnnotation.NotAnnotated; + } + + // Both annotated and not-annotated cases had nullable warnings. + var notAnnotatedDiagnosticsSet = new HashSet(notAnnotatedDiagnostics.AsEnumerable(), SameDiagnosticComparer.Instance); + notAnnotatedDiagnostics.Free(); + + foreach (var diagnostic in annotatedDiagnostics.AsEnumerable()) + { + if (!notAnnotatedDiagnosticsSet.Contains(diagnostic)) + { + // There is a nullable diagnostic in the pass where the field was *annotated*, + // which was not present in the pass where the field is *not-annotated*. The property is not null-resilient. + annotatedDiagnostics.Free(); + return NullableAnnotation.NotAnnotated; + } + } + + annotatedDiagnostics.Free(); + return NullableAnnotation.Annotated; + + DiagnosticBag nullableAnalyzeAndFilterDiagnostics(NullableAnnotation assumedNullableAnnotation) + { + var diagnostics = DiagnosticBag.GetInstance(); + NullableWalker.AnalyzeIfNeeded(binder, boundGetAccessor, boundGetAccessor.Syntax, diagnostics, getterNullResilienceData: (getAccessor, _property.BackingField, assumedNullableAnnotation)); + if (diagnostics.IsEmptyWithoutResolution) + { + return diagnostics; + } + + var filteredDiagnostics = DiagnosticBag.GetInstance(); + _ = DeclaringCompilation.FilterAndAppendAndFreeDiagnostics(filteredDiagnostics, ref diagnostics, cancellationToken: default); + return filteredDiagnostics; + } + } +#nullable disable + internal override bool HasPointerType => _property.HasPointerType; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs index f6e73c27ebbe0..f4582d2a201ad 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedContainer.cs @@ -23,19 +23,12 @@ internal abstract class SynthesizedContainer : NamedTypeSymbol private readonly ImmutableArray _typeParameters; private readonly ImmutableArray _constructedFromTypeParameters; - protected SynthesizedContainer(string name, MethodSymbol containingMethod) + protected SynthesizedContainer(string name, ImmutableArray typeParametersToAlphaRename) { Debug.Assert(name != null); Name = name; - if (containingMethod == null) - { - TypeMap = TypeMap.Empty; - _typeParameters = ImmutableArray.Empty; - } - else - { - TypeMap = TypeMap.Empty.WithConcatAlphaRename(containingMethod, this, out _typeParameters, out _constructedFromTypeParameters); - } + _constructedFromTypeParameters = typeParametersToAlphaRename; + TypeMap = TypeMap.Empty.WithAlphaRename(typeParametersToAlphaRename, this, out _typeParameters); } protected SynthesizedContainer(string name) @@ -53,6 +46,8 @@ protected SynthesizedContainer(string name) internal sealed override bool IsInterface => this.TypeKind == TypeKind.Interface; + internal sealed override ParameterSymbol ExtensionParameter => null; + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) { base.AddSynthesizedAttributes(moduleBuilder, ref attributes); @@ -153,6 +148,9 @@ internal override IEnumerable GetFieldsToEmit() public sealed override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override bool IsReadOnly => false; internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList basesBeingResolved) => ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs index 33d3253985712..27527193735ea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedDelegateSymbol.cs @@ -28,7 +28,7 @@ public override ImmutableArray Parameters } } - internal sealed class SynthesizedDelegateInvokeMethod : SynthesizedInstanceMethodSymbol + internal sealed class SynthesizedDelegateInvokeMethod : SynthesizedMethodSymbol { internal readonly struct ParameterDescription { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs index b3a57c3c7e1c7..c57954b8425c3 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEmbeddedAttributeSymbol.cs @@ -89,6 +89,9 @@ public SynthesizedEmbeddedAttributeSymbolBase( public override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => false; public override bool IsAbstract => false; @@ -114,6 +117,8 @@ internal override bool GetGuidString(out string guidString) internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol ExtensionParameter => null; + internal override bool HasSpecialName => false; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs index 9c37841206071..3fafab5d05194 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEventAccessorSymbol.cs @@ -50,6 +50,13 @@ protected override SourceMemberMethodSymbol BoundAttributesSource { get { + Debug.Assert(PartialImplementationPart is null); + + if (PartialDefinitionPart is { } definitionPart) + { + return (SourceMemberMethodSymbol)definitionPart; + } + return this.MethodKind == MethodKind.EventAdd ? (SourceMemberMethodSymbol)this.AssociatedEvent.RemoveMethod : null; @@ -67,15 +74,24 @@ protected override IAttributeTargetSymbol AttributeOwner internal override OneOrMany> GetAttributeDeclarations() { - return OneOrMany.Create(this.AssociatedEvent.AttributeDeclarationSyntaxList); - } + // If we are asking this question on a partial implementation symbol, + // it must be from a context which prefers to order implementation attributes before definition attributes. + // For example, the 'value' parameter of an add or remove accessor. + if (PartialDefinitionPart is { } definitionPart) + { + return OneOrMany.Create( + this.AssociatedEvent.AttributeDeclarationSyntaxList, + ((SourceEventAccessorSymbol)definitionPart).AssociatedEvent.AttributeDeclarationSyntaxList); + } - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + if (PartialImplementationPart is { } implementationPart) + { + return OneOrMany.Create( + this.AssociatedEvent.AttributeDeclarationSyntaxList, + ((SourceEventAccessorSymbol)implementationPart).AssociatedEvent.AttributeDeclarationSyntaxList); + } - var compilation = this.DeclaringCompilation; - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_CompilerGeneratedAttribute__ctor)); + return OneOrMany.Create(this.AssociatedEvent.AttributeDeclarationSyntaxList); } protected override object MethodChecksLockObject diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs index 30bcbb683b475..adbb03ff0a6d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedHotReloadExceptionSymbol.cs @@ -96,6 +96,10 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r public override ImmutableArray DeclaringSyntaxReferences => []; public override bool IsStatic => false; public override bool IsRefLikeType => false; + + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => false; public override bool IsAbstract => false; public override bool IsSealed => true; @@ -106,6 +110,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r internal override bool HasCodeAnalysisEmbeddedAttribute => true; internal override bool HasCompilerLoweringPreserveAttribute => false; internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; internal override bool HasSpecialName => false; internal override bool IsComImport => false; internal override bool IsWindowsRuntimeImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs index cdde99030bca4..16d8fb98bb4ba 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedImplementationMethod.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal abstract class SynthesizedImplementationMethod : SynthesizedInstanceMethodSymbol + internal abstract class SynthesizedImplementationMethod : SynthesizedMethodSymbol { //inputs protected readonly MethodSymbol _interfaceMethod; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs index 014d0e9005941..317f919c9ba5d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInlineArrayTypeSymbol.cs @@ -64,6 +64,9 @@ internal SynthesizedInlineArrayTypeSymbol(SourceModuleSymbol containingModule, s public override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => true; public override Symbol? ContainingSymbol => _containingModule.GlobalNamespace; @@ -104,6 +107,8 @@ internal override bool GetGuidString(out string? guidString) internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal override bool HasSpecialName => false; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs index 70ea99af74714..248c22bffc6e9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceConstructor.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal class SynthesizedInstanceConstructor : SynthesizedInstanceMethodSymbol + internal class SynthesizedInstanceConstructor : SynthesizedMethodSymbol { private readonly NamedTypeSymbol _containingType; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs index 19e7b4476dcc7..7a27eaeeac500 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInteractiveInitializerMethod.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class SynthesizedInteractiveInitializerMethod : SynthesizedInstanceMethodSymbol + internal sealed class SynthesizedInteractiveInitializerMethod : SynthesizedMethodSymbol { internal const string InitializerName = ""; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs similarity index 89% rename from src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs rename to src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs index a3f190bfafa9d..de0966d0f8ac4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedInstanceMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedMethodSymbol.cs @@ -12,9 +12,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// - /// A base class for synthesized methods that want a this parameter. + /// A base class for synthesized methods that might want a this parameter. /// - internal abstract class SynthesizedInstanceMethodSymbol : MethodSymbol + internal abstract class SynthesizedMethodSymbol : MethodSymbol { private ParameterSymbol _lazyThisParameter; @@ -42,9 +42,15 @@ public sealed override bool AreLocalsZeroed } } + public abstract override bool IsStatic { get; } + internal override bool TryGetThisParameter(out ParameterSymbol thisParameter) { - Debug.Assert(!IsStatic); + if (IsStatic) + { + thisParameter = null; + return true; + } if ((object)_lazyThisParameter == null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs index 7436082c596fe..e207f611093d2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedPrivateImplementationDetailsType.cs @@ -57,6 +57,9 @@ public SynthesizedPrivateImplementationDetailsType(PrivateImplementationDetails public override bool IsRefLikeType => false; + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public override bool IsReadOnly => false; public override Symbol ContainingSymbol => _globalNamespace; @@ -87,6 +90,8 @@ public SynthesizedPrivateImplementationDetailsType(PrivateImplementationDetails internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol? ExtensionParameter => null; + internal override bool HasSpecialName => _privateImplementationDetails.IsSpecialName; internal override bool IsComImport => false; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs index 3896f75f165f4..aa17a287362b4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedSealedPropertyAccessor.cs @@ -20,7 +20,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols /// original virtual property, it is necessary to synthesize a sealed /// accessor so that the accessor will not be overridable from metadata. /// - internal sealed partial class SynthesizedSealedPropertyAccessor : SynthesizedInstanceMethodSymbol + internal sealed partial class SynthesizedSealedPropertyAccessor : SynthesizedMethodSymbol { private readonly PropertySymbol _property; private readonly MethodSymbol _overriddenAccessor; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeMap.cs b/src/Compilers/CSharp/Portable/Symbols/TypeMap.cs index 483f075e3e9fd..e37396f1c9685 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeMap.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeMap.cs @@ -43,7 +43,7 @@ internal TypeMap(ImmutableArray from, ImmutableArray tp is SubstitutedTypeParameterSymbol)); + Debug.Assert(allowAlpha || !from.Any(static tp => tp is SubstitutedTypeParameterSymbol && tp.ContainingSymbol is not SourceExtensionImplementationMethodSymbol)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Follow up, will the SourceExtensionImplementationMethodSymbol type check still be necessary at the end } // Only when the caller passes allowAlpha=true do we tolerate substituted (alpha-renamed) type parameters as keys @@ -100,7 +100,7 @@ public static TypeMap Empty } } - private TypeMap WithAlphaRename(ImmutableArray oldTypeParameters, Symbol newOwner, out ImmutableArray newTypeParameters) + internal TypeMap WithAlphaRename(ImmutableArray oldTypeParameters, Symbol newOwner, out ImmutableArray newTypeParameters) { if (oldTypeParameters.Length == 0) { @@ -148,12 +148,7 @@ internal TypeMap WithAlphaRename(MethodSymbol oldOwner, Symbol newOwner, out Imm return WithAlphaRename(oldOwner.OriginalDefinition.TypeParameters, newOwner, out newTypeParameters); } - internal TypeMap WithConcatAlphaRename( - MethodSymbol oldOwner, - Symbol newOwner, - out ImmutableArray newTypeParameters, - out ImmutableArray oldTypeParameters, - MethodSymbol stopAt = null) + internal static ImmutableArray ConcatMethodTypeParameters(MethodSymbol oldOwner, MethodSymbol stopAt) { Debug.Assert(oldOwner.ConstructedFrom == oldOwner); Debug.Assert(stopAt == null || stopAt.ConstructedFrom == stopAt); @@ -193,8 +188,7 @@ internal TypeMap WithConcatAlphaRename( stopAt?.MethodKind == MethodKind.StaticConstructor || stopAt?.MethodKind == MethodKind.Constructor); - oldTypeParameters = parameters.ToImmutableAndFree(); - return WithAlphaRename(oldTypeParameters, newOwner, out newTypeParameters); + return parameters.ToImmutableAndFree(); } private static SmallDictionary ConstructMapping(ImmutableArray from, ImmutableArray to) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs index ccab2679def85..57ed680f45364 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs @@ -591,6 +591,8 @@ public sealed override bool IsValueType } } + internal sealed override ParameterSymbol ExtensionParameter => null; + internal sealed override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) { return HasUnmanagedTypeConstraint ? ManagedKind.Unmanaged : ManagedKind.Managed; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs index 61fd5fa1bc95e..38e42656dee4a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbol.cs @@ -555,6 +555,16 @@ public virtual bool IsAnonymousType /// internal virtual bool IsNativeIntegerWrapperType => false; +#nullable enable + public bool IsExtension + => TypeKind == TypeKind.Extension; + + /// + /// For the type representing an extension declaration, returns the receiver parameter symbol. + /// + internal abstract ParameterSymbol? ExtensionParameter { get; } +#nullable disable + internal bool IsNativeIntegerType => IsNativeIntegerWrapperType || (SpecialType is SpecialType.System_IntPtr or SpecialType.System_UIntPtr && this.ContainingAssembly.RuntimeSupportsNumericIntPtr); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 3940f6f3e229e..9c31d578b65cc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -851,6 +851,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Struct: case TypeKind.Interface: case TypeKind.Delegate: + case TypeKind.Extension: if (current.IsAnonymousType) { @@ -1715,22 +1716,21 @@ internal static void GetAllTypeParameters(this NamedTypeSymbol type, ArrayBuilde /// internal static TypeParameterSymbol? FindEnclosingTypeParameter(this NamedTypeSymbol type, string name) { - var allTypeParameters = ArrayBuilder.GetInstance(); - type.GetAllTypeParameters(allTypeParameters); - - TypeParameterSymbol? result = null; - - foreach (TypeParameterSymbol tpEnclosing in allTypeParameters) + do { - if (name == tpEnclosing.Name) + foreach (TypeParameterSymbol tpEnclosing in type.TypeParameters) { - result = tpEnclosing; - break; + if (name == tpEnclosing.Name) + { + return tpEnclosing; + } } + + type = type.ContainingType; } + while (type is not null); - allTypeParameters.Free(); - return result; + return null; } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs index 48a0389a946be..63a931375bbaa 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedNamedTypeSymbol.cs @@ -95,6 +95,14 @@ internal override bool MangleName } } + internal override string ExtensionName + { + get + { + return _underlyingType.ExtensionName; + } + } + public override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { return _underlyingType.GetDocumentationCommentXml(preferredCulture, expandIncludes, cancellationToken); diff --git a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs index 704298132f7a4..17880ee6cdf7b 100644 --- a/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/DirectiveTriviaSyntax.cs @@ -56,6 +56,8 @@ public SyntaxToken DirectiveNameToken return ((LoadDirectiveTriviaSyntax)this).LoadKeyword; case SyntaxKind.ShebangDirectiveTrivia: return ((ShebangDirectiveTriviaSyntax)this).ExclamationToken; + case SyntaxKind.IgnoredDirectiveTrivia: + return ((IgnoredDirectiveTriviaSyntax)this).ColonToken; case SyntaxKind.NullableDirectiveTrivia: return ((NullableDirectiveTriviaSyntax)this).NullableKeyword; default: diff --git a/src/Compilers/CSharp/Portable/Syntax/ExtensionDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ExtensionDeclarationSyntax.cs new file mode 100644 index 0000000000000..b6b919098d81b --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/ExtensionDeclarationSyntax.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.CSharp.Syntax +{ + public partial class ExtensionDeclarationSyntax + { + public override SyntaxToken Identifier => default; + + internal override BaseTypeDeclarationSyntax WithIdentifierCore(SyntaxToken identifier) + => throw new System.NotSupportedException(); + + public override BaseListSyntax? BaseList => null; + + internal override BaseTypeDeclarationSyntax AddBaseListTypesCore(params BaseTypeSyntax[] items) + => throw new System.NotSupportedException(); + + internal override BaseTypeDeclarationSyntax WithBaseListCore(BaseListSyntax? baseList) + => throw new System.NotSupportedException(); + } +} diff --git a/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs new file mode 100644 index 0000000000000..584eb1aef7c14 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/IgnoredDirectiveTriviaSyntax.cs @@ -0,0 +1,13 @@ +// 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.Diagnostics.CodeAnalysis; + +// NOTE: This whole file should be removed once the experimental attribute is not needed +// (the source generator should be used to emit this code instead). + +namespace Microsoft.CodeAnalysis.CSharp.Syntax; + +[Experimental(RoslynExperiments.IgnoredDirectives, UrlFormat = RoslynExperiments.IgnoredDirectives_Url)] +partial class IgnoredDirectiveTriviaSyntax; diff --git a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeDeclarationSyntax.cs index b41530c1f7a5d..6750b331260e9 100644 --- a/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeDeclarationSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/InternalSyntax/TypeDeclarationSyntax.cs @@ -4,6 +4,7 @@ #nullable disable +using Roslyn.Utilities; using CoreSyntax = Microsoft.CodeAnalysis.Syntax.InternalSyntax; namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax @@ -153,4 +154,47 @@ public override TypeDeclarationSyntax UpdateCore( semicolonToken); } } + + internal partial class ExtensionDeclarationSyntax + { + public override TypeDeclarationSyntax UpdateCore( + CoreSyntax.SyntaxList attributeLists, + CoreSyntax.SyntaxList modifiers, + SyntaxToken keyword, + SyntaxToken identifier, + TypeParameterListSyntax typeParameterList, + ParameterListSyntax parameterList, + BaseListSyntax baseList, + CoreSyntax.SyntaxList constraintClauses, + SyntaxToken openBraceToken, + CoreSyntax.SyntaxList members, + SyntaxToken closeBraceToken, + SyntaxToken semicolonToken) + { + if (identifier is not null) + { + throw ExceptionUtilities.Unreachable(); + } + + if (baseList is not null) + { + throw ExceptionUtilities.Unreachable(); + } + + return this.Update( + attributeLists, + modifiers, + keyword, + typeParameterList, + parameterList, + constraintClauses, + openBraceToken, + members, + closeBraceToken, + semicolonToken); + } + + public override SyntaxToken Identifier => null; + public override BaseListSyntax BaseList => null; + } } diff --git a/src/Compilers/CSharp/Portable/Syntax/ParameterDeclarationSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/ParameterDeclarationSyntax.cs new file mode 100644 index 0000000000000..44e699e832c3f --- /dev/null +++ b/src/Compilers/CSharp/Portable/Syntax/ParameterDeclarationSyntax.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.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp +{ + public partial class SyntaxFactory + { + /// Creates a new ParameterSyntax instance. + public static ParameterSyntax Parameter(SyntaxToken identifier) + => SyntaxFactory.Parameter(default, default(SyntaxTokenList), null, identifier, null); + } +} + diff --git a/src/Compilers/CSharp/Portable/Syntax/SimpleLambdaExpressionSyntax.cs b/src/Compilers/CSharp/Portable/Syntax/SimpleLambdaExpressionSyntax.cs index 7e070e3fe7c8b..dbd917f946cc9 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SimpleLambdaExpressionSyntax.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SimpleLambdaExpressionSyntax.cs @@ -39,6 +39,14 @@ namespace Microsoft.CodeAnalysis.CSharp { public partial class SyntaxFactory { + /// Creates a new SimpleLambdaExpressionSyntax instance. + public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(SyntaxList attributeLists, SyntaxTokenList modifiers, ParameterSyntax parameter, BlockSyntax? block, ExpressionSyntax? expressionBody) + => SyntaxFactory.SimpleLambdaExpression(attributeLists, modifiers, parameter, SyntaxFactory.Token(SyntaxKind.EqualsGreaterThanToken), block, expressionBody); + + /// Creates a new SimpleLambdaExpressionSyntax instance. + public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(ParameterSyntax parameter) + => SyntaxFactory.SimpleLambdaExpression(default, default(SyntaxTokenList), parameter, SyntaxFactory.Token(SyntaxKind.EqualsGreaterThanToken), null, null); + public static SimpleLambdaExpressionSyntax SimpleLambdaExpression(SyntaxToken asyncKeyword, ParameterSyntax parameter, SyntaxToken arrowToken, BlockSyntax? block, ExpressionSyntax? expressionBody) => SimpleLambdaExpression(attributeLists: default, TokenList(asyncKeyword), parameter, arrowToken, block, expressionBody); diff --git a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml index 8e05ae9fee8b7..369768857720f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/Syntax.xml +++ b/src/Compilers/CSharp/Portable/Syntax/Syntax.xml @@ -1440,7 +1440,7 @@ - + @@ -3352,7 +3352,7 @@ Base class for type declaration syntax. - + Gets the identifier. @@ -3384,11 +3384,11 @@ - Base class for type declaration syntax (class, struct, interface, record). + Base class for type declaration syntax (class, struct, interface, record, extension). - Gets the type keyword token ("class", "struct", "interface", "record"). + Gets the type keyword token ("class", "struct", "interface", "record", "extension"). @@ -3617,6 +3617,30 @@ + + + Extension container syntax. + + + + + + + + + + + + + + + + + + + + + Base list syntax. @@ -4307,7 +4331,7 @@ - + Parameter syntax. @@ -4323,7 +4347,8 @@ - + + Gets the identifier. @@ -5075,6 +5100,7 @@ + @@ -5088,6 +5114,20 @@ + + + + + + + + + + + + + + diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs index 5c7b02cdd3254..a1cbe33a1b30f 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxFactory.cs @@ -1817,7 +1817,7 @@ public static ParameterListSyntax ParseParameterList(string text, int offset = 0 using (var lexer = MakeLexer(text, offset, (CSharpParseOptions?)options)) using (var parser = MakeParser(lexer)) { - var node = parser.ParseParenthesizedParameterList(); + var node = parser.ParseParenthesizedParameterList(forExtension: false); if (consumeFullText) node = parser.ConsumeUnexpectedTokens(node); return (ParameterListSyntax)node.CreateRed(); } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs index d697c4c2202be..6ba23a9cfc285 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs @@ -421,6 +421,8 @@ public enum SyntaxKind : ushort FileKeyword = 8449, /// Represents . AllowsKeyword = 8450, + /// Represents . + ExtensionKeyword = 8451, // when adding a contextual keyword following functions must be adapted: // @@ -925,5 +927,9 @@ public enum SyntaxKind : ushort CollectionExpression = 9076, ExpressionElement = 9077, SpreadElement = 9078, + + ExtensionDeclaration = 9079, + + IgnoredDirectiveTrivia = 9080, } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs index 17382780cb90e..9cd9333a54557 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs @@ -291,6 +291,7 @@ public static bool IsPreprocessorDirective(SyntaxKind kind) case SyntaxKind.LoadDirectiveTrivia: case SyntaxKind.BadDirectiveTrivia: case SyntaxKind.ShebangDirectiveTrivia: + case SyntaxKind.IgnoredDirectiveTrivia: case SyntaxKind.NullableDirectiveTrivia: return true; default: @@ -383,6 +384,7 @@ public static bool IsTypeDeclaration(SyntaxKind kind) case SyntaxKind.EnumDeclaration: case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.ExtensionDeclaration: return true; default: @@ -828,6 +830,7 @@ public static SyntaxKind GetBaseTypeDeclarationKind(SyntaxKind kind) return kind == SyntaxKind.EnumKeyword ? SyntaxKind.EnumDeclaration : GetTypeDeclarationKind(kind); } + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : decide what we want for extension declaration public static SyntaxKind GetTypeDeclarationKind(SyntaxKind kind) { switch (kind) @@ -1166,7 +1169,7 @@ public static SyntaxKind GetPreprocessorKeywordKind(string text) public static IEnumerable GetContextualKeywordKinds() { - for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.AllowsKeyword; i++) + for (int i = (int)SyntaxKind.YieldKeyword; i <= (int)SyntaxKind.ExtensionKeyword; i++) { // 8441 corresponds to a deleted kind (DataKeyword) that was previously shipped. if (i != 8441) @@ -1228,6 +1231,7 @@ public static bool IsContextualKeyword(SyntaxKind kind) case SyntaxKind.ScopedKeyword: case SyntaxKind.FileKeyword: case SyntaxKind.AllowsKeyword: + case SyntaxKind.ExtensionKeyword: return true; default: return false; @@ -1355,6 +1359,8 @@ public static SyntaxKind GetContextualKeywordKind(string text) return SyntaxKind.FileKeyword; case "allows": return SyntaxKind.AllowsKeyword; + case "extension": + return SyntaxKind.ExtensionKeyword; default: return SyntaxKind.None; } @@ -1802,6 +1808,8 @@ public static string GetText(SyntaxKind kind) return "file"; case SyntaxKind.AllowsKeyword: return "allows"; + case SyntaxKind.ExtensionKeyword: + return "extension"; default: return string.Empty; } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs index 0471674501472..4376ad61d88f2 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNormalizer.cs @@ -100,7 +100,7 @@ public override SyntaxToken VisitToken(SyntaxToken token) isTrailing: false, indentAfterLineBreak: NeedsIndentAfterLineBreak(token), mustHaveSeparator: false, - lineBreaksAfter: 0)); + lineBreaksAfter: lineBreaksAfterLeading(token))); var nextToken = this.GetNextRelevantToken(token); @@ -118,6 +118,22 @@ public override SyntaxToken VisitToken(SyntaxToken token) lineBreaksAfter: lineBreaksAfter)); return tk; + + static int lineBreaksAfterLeading(SyntaxToken syntaxToken) + { + if (syntaxToken.LeadingTrivia.Count < 2) + { + return 0; + } + + if (syntaxToken.LeadingTrivia[^2].IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia) && + syntaxToken.LeadingTrivia[^1].IsKind(SyntaxKind.EndOfLineTrivia)) + { + return 1; + } + + return 0; + } } finally { @@ -1247,7 +1263,7 @@ private static bool EndsInLineBreak(SyntaxTrivia trivia) } else { - return IsLineBreak(node.GetLastToken()); + return !node.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia) && IsLineBreak(node.GetLastToken()); } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs index 5be08a6030e18..2cf64f0ab9d58 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxReplacer.cs @@ -6,9 +6,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Syntax { @@ -74,9 +72,9 @@ private class Replacer : CSharpSyntaxRewriter where TNode : SyntaxNode private readonly HashSet _triviaSet; private readonly HashSet _spanSet; - private readonly TextSpan _totalSpan; - private readonly bool _visitIntoStructuredTrivia; - private readonly bool _shouldVisitTrivia; + private TextSpan _totalSpan; + private bool _visitIntoStructuredTrivia; + private bool _shouldVisitTrivia; public Replacer( IEnumerable? nodes, @@ -94,19 +92,9 @@ public Replacer( _tokenSet = tokens != null ? new HashSet(tokens) : s_noTokens; _triviaSet = trivia != null ? new HashSet(trivia) : s_noTrivia; - _spanSet = new HashSet( - _nodeSet.Select(n => n.FullSpan).Concat( - _tokenSet.Select(t => t.FullSpan).Concat( - _triviaSet.Select(t => t.FullSpan)))); - - _totalSpan = ComputeTotalSpan(_spanSet); - - _visitIntoStructuredTrivia = - _nodeSet.Any(n => n.IsPartOfStructuredTrivia()) || - _tokenSet.Any(t => t.IsPartOfStructuredTrivia()) || - _triviaSet.Any(t => t.IsPartOfStructuredTrivia()); + _spanSet = new HashSet(); - _shouldVisitTrivia = _triviaSet.Count > 0 || _visitIntoStructuredTrivia; + CalculateVisitationCriteria(); } private static readonly HashSet s_noNodes = new HashSet(); @@ -129,13 +117,29 @@ public bool HasWork } } - private static TextSpan ComputeTotalSpan(IEnumerable spans) + private void CalculateVisitationCriteria() { + _spanSet.Clear(); + foreach (var node in _nodeSet) + { + _spanSet.Add(node.FullSpan); + } + + foreach (var token in _tokenSet) + { + _spanSet.Add(token.FullSpan); + } + + foreach (var trivia in _triviaSet) + { + _spanSet.Add(trivia.FullSpan); + } + bool first = true; int start = 0; int end = 0; - foreach (var span in spans) + foreach (var span in _spanSet) { if (first) { @@ -150,7 +154,14 @@ private static TextSpan ComputeTotalSpan(IEnumerable spans) } } - return new TextSpan(start, end - start); + _totalSpan = new TextSpan(start, end - start); + + _visitIntoStructuredTrivia = + _nodeSet.Any(static n => n.IsPartOfStructuredTrivia()) || + _tokenSet.Any(static t => t.IsPartOfStructuredTrivia()) || + _triviaSet.Any(static t => t.IsPartOfStructuredTrivia()); + + _shouldVisitTrivia = _triviaSet.Count > 0 || _visitIntoStructuredTrivia; } private bool ShouldVisit(TextSpan span) @@ -179,16 +190,28 @@ private bool ShouldVisit(TextSpan span) [return: NotNullIfNotNull(nameof(node))] public override SyntaxNode? Visit(SyntaxNode? node) { - SyntaxNode? rewritten = node; + var rewritten = node; if (node != null) { + bool isReplacedNode = _nodeSet.Remove(node); + + if (isReplacedNode) + { + // If node is in _nodeSet, then it contributed to the calculation of _spanSet. + // We are currently processing that node, so it no longer needs to contribute + // to _spanSet and affect determination of inward visitation. This is done before + // calling ShouldVisit to avoid walking into the node if there aren't any remaining + // spans inside it representing items to replace. + CalculateVisitationCriteria(); + } + if (this.ShouldVisit(node.FullSpan)) { rewritten = base.Visit(node); } - if (_nodeSet.Contains(node) && _computeReplacementNode != null) + if (isReplacedNode && _computeReplacementNode != null) { rewritten = _computeReplacementNode((TNode)node, (TNode)rewritten!); } @@ -200,13 +223,24 @@ private bool ShouldVisit(TextSpan span) public override SyntaxToken VisitToken(SyntaxToken token) { var rewritten = token; + bool isReplacedToken = _tokenSet.Remove(token); + + if (isReplacedToken) + { + // If token is in _tokenSet, then it contributed to the calculation of _spanSet. + // We are currently processing that token, so it no longer needs to contribute + // to _spanSet and affect determination of inward visitation. This is done before + // calling ShouldVisit to avoid walking into the token if there aren't any remaining + // spans inside it representing items to replace. + CalculateVisitationCriteria(); + } if (_shouldVisitTrivia && this.ShouldVisit(token.FullSpan)) { rewritten = base.VisitToken(token); } - if (_tokenSet.Contains(token) && _computeReplacementToken != null) + if (isReplacedToken && _computeReplacementToken != null) { rewritten = _computeReplacementToken(token, rewritten); } @@ -217,13 +251,24 @@ public override SyntaxToken VisitToken(SyntaxToken token) public override SyntaxTrivia VisitListElement(SyntaxTrivia trivia) { var rewritten = trivia; + bool isReplacedTrivia = _triviaSet.Remove(trivia); + + if (isReplacedTrivia) + { + // If trivia is in _triviaSet, then it contributed to the calculation of _spanSet. + // We are currently processing that trivia, so it no longer needs to contribute + // to _spanSet and affect determination of inward visitation. This is done before + // calling ShouldVisit to avoid walking into the trivia if there aren't any remaining + // spans inside it representing items to replace. + CalculateVisitationCriteria(); + } if (this.VisitIntoStructuredTrivia && trivia.HasStructure && this.ShouldVisit(trivia.FullSpan)) { rewritten = this.VisitTrivia(trivia); } - if (_triviaSet.Contains(trivia) && _computeReplacementTrivia != null) + if (isReplacedTrivia && _computeReplacementTrivia != null) { rewritten = _computeReplacementTrivia(trivia, rewritten); } diff --git a/src/Compilers/CSharp/Portable/Utilities/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Utilities/TypeSymbolExtensions.cs index 75434c8f292c4..d0d250a0be2e4 100644 --- a/src/Compilers/CSharp/Portable/Utilities/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Utilities/TypeSymbolExtensions.cs @@ -196,7 +196,8 @@ internal static TypeSymbol GetNextBaseTypeNoUseSiteDiagnostics(this TypeSymbol t case TypeKind.Submission: case TypeKind.Pointer: case TypeKind.FunctionPointer: - // Enums, arrays, submissions and delegates know their own base types + case TypeKind.Extension: + // Enums, arrays, submissions, delegates and extensions know their own base types // intrinsically (and do not include interface lists) // so there is no possibility of a cycle. return type.BaseTypeNoUseSiteDiagnostics; diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 5720459cb38ce..344d4b128b789 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -222,6 +222,11 @@ V asynchronním příkazu foreach nejde použít kolekce dynamického typu. + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Typ {0} se nedá použít pro pole záznamu. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Nelze použít číselnou konstantu ani relační vzor pro „{0}“, protože dědí z nebo rozšiřuje INumberBase<T>. Zvažte použití vzoru typu k zúžení na konkrétní číselný typ. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Nelze použít číselnou konstantu ani relační vzor pro „{0}“, protože dědí z nebo rozšiřuje INumberBase<T>. Zvažte použití vzoru typu k zúžení na konkrétní číselný typ. @@ -717,6 +722,11 @@ Strom výrazů nesmí obsahovat výraz kolekce. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Strom výrazů nesmí obsahovat výraz indexu od-do (^). @@ -762,6 +772,26 @@ Strom výrazů nesmí obsahovat výraz with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer {0}: Externí událost nemůže mít inicializátor. @@ -1247,6 +1277,11 @@ Argument diagnosticId atributu Experimental musí být platný identifikátor + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. {0} není platný modifikátor návratového typu ukazatele na funkci. Platné modifikátory jsou ref a ref readonly. @@ -1387,6 +1422,16 @@ Vzory seznamů nelze použít pro hodnotu typu {0}. Nenašla se žádná vhodná vlastnost Length nebo Count. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Žádná přetížená metoda {0} neodpovídá ukazateli na funkci {1}. @@ -1637,6 +1682,21 @@ Metoda {0} určuje omezení struct pro parametr typu {1}, ale odpovídající parametr typu {2} přepsané nebo explicitně implementované metody {3} není typ, který nemůže mít hodnotu null. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Konstruktor {0} ponechá požadovaný člen {1} neinicializovaný. @@ -1672,6 +1732,16 @@ Parametr params musí mít platný typ kolekce. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Obě deklarace částečných členů musí mít shodné modifikátory přístupnosti. @@ -1682,11 +1752,31 @@ Částečný člen nemůže mít modifikátor abstract. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. V deklaracích částečných členů, {0} a {1} se musí používat stejné názvy prvků řazené kolekce členů. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Částečný člen musí být deklarován v rámci částečného typu. @@ -1712,6 +1802,11 @@ Deklarace částečných členů musí mít odpovídající referenční návratové hodnoty. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Obě deklarace částečného člena musí být nezabezpečené, nebo nesmí být nezabezpečená žádná z nich. @@ -1782,11 +1877,6 @@ Obě deklarace částečných vlastností musí být vyžadovány, nebo nesmí být vyžadována žádná z nich. - - Both partial property declarations must have the same type. - Obě deklarace částečných vlastností musí mít stejný typ. - - Property accessor '{0}' does not implement any accessor declared on the definition part Přístupový objekt vlastnosti {0} neimplementuje žádný přístupový objekt deklarovaný v definiční části. @@ -1847,6 +1937,16 @@ {0}: U přístupových objektů se modifikátor readonly může použít jenom v případě, že vlastnost nebo indexer má přístupový objekt get i set. + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Primární konstruktor je v konfliktu se syntetizovaně zkopírovaným konstruktorem. @@ -2257,6 +2357,16 @@ Aby se typ {0} dal použít jako konvence volání, musí být veřejný. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Před vrácením řízení volajícímu se musí plně přiřadit automaticky implementovaná vlastnost {0}. Zvažte aktualizaci jazykové verze {1} na automatické výchozí nastavení vlastnosti. @@ -2267,6 +2377,11 @@ Před vrácením řízení volajícímu se musí plně přiřadit pole {0}. Zvažte aktualizaci jazykové verze {1} na automatické výchozí nastavení pole. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Neočekávaný seznam parametrů. @@ -2357,6 +2472,16 @@ Objekt this nelze použít před přiřazením všech jeho polí. Zvažte aktualizaci na jazykovou verzi {0} na automatické výchozí nastavení nepřiřazených polí. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ vzory rozšířených vlastností + + extensions + extensions + + field keyword klíčové slovo pole @@ -2522,6 +2652,11 @@ nové řádky v interpolacích + + null conditional assignment + null conditional assignment + + overload resolution priority priorita řešení přetížení @@ -2537,6 +2672,11 @@ kolekce parametrů + + partial events and constructors + partial events and constructors + + positional fields in records pozice polí v záznamech @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. - The analyzer assembly references a newer version of the compiler than the currently running version. - Sestavení analyzátoru odkazuje na novější verzi kompilátoru, než je aktuálně spuštěná verze. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Sestavení analyzátoru odkazuje na novější verzi kompilátoru, než je aktuálně spuštěná verze. @@ -3192,14 +3332,14 @@ Parametr má modifikátor params ve výrazu lambda, ale ne v cílovém typu delegáta. - - Partial property declarations '{0}' and '{1}' have signature differences. - Deklarace částečných vlastností {0} a {1} mají rozdíly v signaturách. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Deklarace částečných vlastností mají rozdíly v signaturách. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Proměnná {0} {1}, která nemůže být null, musí při ukončování konstruktoru obsahovat hodnotu, která není null. Zvažte přidání modifikátoru required, deklaraci {0} jako s možnou hodnotou null nebo přidání atributů [field: MaybeNull, AllowNull]. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Při ukončovacím konstruktoru musí vlastnost, která nemůže mít hodnotu null, obsahovat hodnotu, která není null. Zvažte přidání modifikátoru required, deklaraci vlastnosti jako s možnou hodnotou null nebo přidání atributů [field: MaybeNull, AllowNull]. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Modifikátor partial se může objevit jen bezprostředně před klíčovými slovy class, record, struct, interface nebo návratovým typem metody nebo vlastnosti. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Modifikátor partial se může objevit jen bezprostředně před klíčovými slovy class, record, struct, interface nebo návratovým typem metody nebo vlastnosti. @@ -8817,8 +8957,8 @@ Blok catch() po bloku catch (System.Exception e) může zachytit výjimky, kter - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Nejde definovat novou metodu rozšíření, protože se nenašel vyžadovaný typ kompilátoru {0}. Nechybí odkaz na System.Core.dll? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Nejde definovat novou metodu rozšíření, protože se nenašel vyžadovaný typ kompilátoru {0}. Nechybí odkaz na System.Core.dll? @@ -8912,8 +9052,8 @@ Blok catch() po bloku catch (System.Exception e) může zachytit výjimky, kter - Invalid token '{0}' in class, record, struct, or interface member declaration - Neplatný token {0} v deklaraci člena rozhraní, třídy, záznamu nebo struktury + Invalid token '{0}' in a member declaration + Neplatný token {0} v deklaraci člena rozhraní, třídy, záznamu nebo struktury diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 2ac6b42fb054d..d8cc687591ed3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -222,6 +222,11 @@ Eine Sammlung des dynamic-Typs kann in einem asynchronen foreach nicht verwendet werden. + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Der Typ "{0}" darf nicht für ein Feld eines Datensatzes verwendet werden. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Für "{0}" kann keine numerische Konstante oder ein relationales Muster verwendet werden, da es von "INumberBase<T>" vererbt oder erweitert wird. Erwägen Sie die Verwendung eines Typmusters, um auf einen bestimmten numerischen Typ einzugrenzen. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Für "{0}" kann keine numerische Konstante oder ein relationales Muster verwendet werden, da es von "INumberBase<T>" vererbt oder erweitert wird. Erwägen Sie die Verwendung eines Typmusters, um auf einen bestimmten numerischen Typ einzugrenzen. @@ -717,6 +722,11 @@ Eine Ausdrucksbaumstruktur darf keinen Sammlungsausdruck enthalten. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Eine Ausdrucksbaumstruktur darf keinen vom Ende ausgehenden Indexausdruck ("^") enthalten. @@ -762,6 +772,26 @@ Eine Ausdrucksbaumstruktur darf keinen with-Ausdruck enthalten. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer "{0}": Externes Ereignis darf keinen Initialisierer aufweisen. @@ -1247,6 +1277,11 @@ Das diagnosticId-Argument für das Experimental-Attribut muss ein gültiger Bezeichner sein. + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" ist kein gültiger Rückgabetyp-Modifizierer für Funktionszeiger. Gültige Modifizierer sind "ref" und "ref readonly". @@ -1387,6 +1422,16 @@ Listenmuster dürfen nicht für einen Wert vom Typ „{0}“ verwendet werden. Es wurde keine geeignete Längen- oder Anzahl-Eigenschaft gefunden. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Keine Überladung für "{0}" stimmt mit dem Funktionszeiger "{1}" überein. @@ -1637,6 +1682,21 @@ Die Methode "{0}" gibt eine struct-Einschränkung für den Typparameter "{1}" an, aber der zugehörige Typparameter "{2}" der außer Kraft gesetzten oder explizit implementierten Methode "{3}" ist kein Non-Nullable-Werttyp. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Der Konstruktor "{0}" lässt den erforderlichen Member "{1}" deinitialisiert. @@ -1672,6 +1732,16 @@ Der Params-Parameter muss einen gültigen Sammlungstyp aufweisen + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Beide Deklarationen des partiellen Mitglieds müssen identische Zugriffsmodifizierer aufweisen. @@ -1682,11 +1752,31 @@ Ein partielles Mitglied darf nicht den Modifizierer "abstract" aufweisen. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Beide Deklarationen des partiellen Mitglieds ("{0}" und "{1}") müssen die gleichen Tupelelementnamen verwenden. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Ein partielles Mitglied muss innerhalb eines partiellen Typs deklariert sein. @@ -1712,6 +1802,11 @@ Deklarationen partieller Mitglieder müssen übereinstimmende Ref-Rückgabewerte aufweisen. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Beide Deklarationen des partiellen Mitglieds müssen unsicher sein, oder keine von beiden darf unsicher sein. @@ -1782,11 +1877,6 @@ Entweder beide oder keine der Deklarationen der partiellen Eigenschaft müssen als "required" festgelegt werden. - - Both partial property declarations must have the same type. - Beide Deklarationen der partiellen Eigenschaft müssen den gleichen Typ aufweisen. - - Property accessor '{0}' does not implement any accessor declared on the definition part Der Eigenschaftenaccessor "{0}" implementiert keinen Accessor, der für den Definitionsteil deklariert wurde. @@ -1847,6 +1937,16 @@ {0}: "readonly" kann für Accessoren nur verwendet werden, wenn die Eigenschaft oder der Indexer sowohl einen get- als auch einen set-Accessor aufweist. + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Der primäre Konstruktor verursacht einen Konflikt mit dem synthetisierten Kopierkonstruktor. @@ -2257,6 +2357,16 @@ Der Typ "{0}" muss öffentlich sein, damit er als Aufrufkonvention verwendet werden kann. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Die automatisch implementierte Eigenschaft '{0}' muss vollständig zugewiesen werden, bevor das Steuerelement an den Aufrufer zurückgegeben wird. Erwägen Sie eine Aktualisierung auf die Sprachversion '{1}', um die Eigenschaft automatisch als Standard zu verwenden. @@ -2267,6 +2377,11 @@ Das Feld "{0}" muss vollständig zugewiesen werden, bevor das Steuerelement an den Aufrufer zurückgegeben wird. Aktualisieren Sie ggf. auf die Sprachversion "{1}", um das Feld automatisch als Standard zu verwenden. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Unerwartete Parameterliste. @@ -2357,6 +2472,16 @@ Das „this“-Objekt kann nicht verwendet werden, bevor alle zugehörigen Felder zugewiesen wurden. Aktualisieren Sie ggf. auf die Sprachversion „{0}“, um die nicht zugewiesenen Felder automatisch als Standard zu verwenden. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ Muster für erweiterte Eigenschaften + + extensions + extensions + + field keyword Feld Schlüsselwort @@ -2522,6 +2652,11 @@ Zeilenumbrüche in Interpolationen + + null conditional assignment + null conditional assignment + + overload resolution priority Priorität der Überladungsauflösung @@ -2537,6 +2672,11 @@ Params-Sammlungen + + partial events and constructors + partial events and constructors + + positional fields in records Positionsfelder in Datensätzen @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. - The analyzer assembly references a newer version of the compiler than the currently running version. - Die Analyzerassembly verweist auf eine neuere Version des Compilers als die derzeit ausgeführte Version. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Die Analyzerassembly verweist auf eine neuere Version des Compilers als die derzeit ausgeführte Version. @@ -3192,14 +3332,14 @@ Der Parameter verfügt über den Modifizierer „params“ in der Lambdafunktion, aber nicht im Delegattyp des Ziels. - - Partial property declarations '{0}' and '{1}' have signature differences. - Die Deklarationen der partiellen Eigenschaft "{0}" und "{1}" weisen Signaturunterschiede auf. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Die Deklarationen der partiellen Eigenschaft "" und "" weisen Signaturunterschiede auf. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Non-Nullable-{0} "{1}" muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den Modifizierer "required" hinzu, oder deklarieren Sie die {0} als Nullwerte zulassend, oder fügen Sie "[field: MaybeNull, AllowNull] "-Attribute hinzu. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Non-Nullable-Eigenschaft muss beim Beenden des Konstruktors einen Wert ungleich NULL enthalten. Fügen Sie ggf. den Modifizierer "required" hinzu, oder deklarieren Sie die Eigenschaft als Nullwerte zulassend, oder fügen Sie "[field: MaybeNull, AllowNull] "-Attribute hinzu. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Der partial-Modifizierer kann nur unmittelbar vor "class", "record", "struct", "interface" oder einem Methoden- Eigenschaftsrückgabetyp verwendet werden. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Der partial-Modifizierer kann nur unmittelbar vor "class", "record", "struct", "interface" oder einem Methoden- Eigenschaftsrückgabetyp verwendet werden. @@ -8817,8 +8957,8 @@ Ein catch()-Block nach einem catch (System.Exception e)-Block kann nicht-CLS-Aus - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Es kann keine neue Erweiterungsmethode definiert werden, weil der für den Compiler erforderliche Typ "{0}" nicht gefunden werden kann. Fehlt möglicherweise ein Verweis auf "System.Core.dll"? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Es kann keine neue Erweiterungsmethode definiert werden, weil der für den Compiler erforderliche Typ "{0}" nicht gefunden werden kann. Fehlt möglicherweise ein Verweis auf "System.Core.dll"? @@ -8912,8 +9052,8 @@ Ein catch()-Block nach einem catch (System.Exception e)-Block kann nicht-CLS-Aus - Invalid token '{0}' in class, record, struct, or interface member declaration - Ungültiges Token "{0}" in Klassen-, Datensatz-, Struktur- oder Schnittstellenmemberdeklaration + Invalid token '{0}' in a member declaration + Ungültiges Token "{0}" in Klassen-, Datensatz-, Struktur- oder Schnittstellenmemberdeklaration diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index e776b15f4336c..1bbebe82943bb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -222,6 +222,11 @@ No se puede usar una colección de tipo dinámico en una instrucción foreach asincrónica. + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. No se puede usar el tipo "{0}" para un campo de un registro. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - No se puede usar una constante numérica o un patrón relacional en '{0}' porque hereda o extiende 'INumberBase<T>'. Considere la posibilidad de usar un patrón de tipo para restringir a un tipo numérico específico. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + No se puede usar una constante numérica o un patrón relacional en '{0}' porque hereda o extiende 'INumberBase<T>'. Considere la posibilidad de usar un patrón de tipo para restringir a un tipo numérico específico. @@ -717,6 +722,11 @@ Un árbol de expresión no puede contener una expresión de colección. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Un árbol de expresión no puede contener una expresión de índice del otro extremo ("^"). @@ -762,6 +772,26 @@ Un árbol de expresión no puede contener una expresión with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer "{0}": un evento externo no puede tener un inicializador @@ -1247,6 +1277,11 @@ El argumento diagnosticId del atributo "Experimental" debe ser un identificador válido. + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" no es un modificador de tipo de valor devuelto de puntero de función válido. Los modificadores válidos son "ref" y "ref readonly". @@ -1387,6 +1422,16 @@ No se pueden usar patrones de lista para un valor de tipo \"{0}\". No se encontró ninguna propiedad \"Length\" o \"Count\" adecuada. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Ninguna sobrecarga correspondiente a "{0}" coincide con el puntero de función "{1}". @@ -1637,6 +1682,21 @@ El método "{0}" especifica una restricción "struct" para el parámetro de tipo "{1}", pero el parámetro de tipo correspondiente "{2}" de los métodos invalidados o implementados explícitamente "{3}" no es un tipo de valor que acepta valores NULL. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. El constructor "{0}" deja el miembro requerido "{1}" sin inicializar. @@ -1672,6 +1732,16 @@ El parámetro params debe tener un tipo de colección válido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Ambas declaraciones de miembro parcial deben tener modificadores de accesibilidad idénticos. @@ -1682,11 +1752,31 @@ Un miembro parcial no puede tener el modificador 'abstract' + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Ambas declaraciones de miembro parcial, '{0}' y '{1}', deben usar los mismos nombres de elemento de tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un miembro parcial debe declararse dentro de un tipo parcial @@ -1712,6 +1802,11 @@ Las declaraciones de miembros parciales deben tener valores devueltos de referencia coincidentes. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Ambas declaraciones de miembros parciales deben ser no seguras o ninguna de ellas puede no ser segura @@ -1782,11 +1877,6 @@ Ambas declaraciones de propiedades parciales deben ser obligatorias o ninguna de ellas podrá serlo - - Both partial property declarations must have the same type. - Ambas declaraciones de propiedad parcial deben tener el mismo tipo. - - Property accessor '{0}' does not implement any accessor declared on the definition part El descriptor de acceso de propiedad '{0}' no implementa ningún descriptor de acceso declarado en la parte de definición @@ -1847,6 +1937,16 @@ "{0}": "readonly" solo se puede usar en los descriptores de acceso si la propiedad o el indexador tienen un descriptor de acceso get y set + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. El constructor principal está en conflicto con el constructor de copia sintetizado. @@ -2257,6 +2357,16 @@ El tipo "{0}" debe ser público para poder usarlo como convención de llamada. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. La propiedad implementada automáticamente '{0}' debe estar totalmente asignada antes de que el control se devuelva al autor de la llamada. Considere la posibilidad de actualizar a la versión de idioma "{1}" para establecer automáticamente el valor predeterminado de la propiedad. @@ -2267,6 +2377,11 @@ El campo '{0}' debe estar totalmente asignado antes de que el control se devuelva al autor de la llamada. Considere la posibilidad de actualizar a la versión de idioma "{1}" para establecer automáticamente el valor predeterminado del campo. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Lista de parámetros inesperada. @@ -2357,6 +2472,16 @@ No se puede usar el objeto 'this' antes de que se hayan asignado todos sus campos. Considere la posibilidad de actualizar a la versión de idioma "{0}" para establecer automáticamente el valor predeterminado de los campos sin asignar. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ patrones de propiedad extendidos + + extensions + extensions + + field keyword palabra clave field @@ -2522,6 +2652,11 @@ Nuevas líneas en interpolaciones + + null conditional assignment + null conditional assignment + + overload resolution priority prioridad de resolución de sobrecarga @@ -2537,6 +2672,11 @@ colecciones params + + partial events and constructors + partial events and constructors + + positional fields in records campos posicionales en registros @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. - The analyzer assembly references a newer version of the compiler than the currently running version. - El ensamblado del analizador hace referencia a una versión más reciente del compilador que la versión que se está ejecutando actualmente. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + El ensamblado del analizador hace referencia a una versión más reciente del compilador que la versión que se está ejecutando actualmente. @@ -3192,14 +3332,14 @@ El parámetro tiene modificador de parámetros en lambda, pero no en el tipo delegado de destino. - - Partial property declarations '{0}' and '{1}' have signature differences. - Las declaraciones de propiedades parciales '{0}' y '{1}' tienen diferencias de firma. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Las declaraciones de propiedades parciales tienen diferencias de firma. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. El elemento {0} "{1}" que no acepta valores NULL debe contener un valor distinto de NULL al salir del constructor. Considere agregar el modificador "requerido", o declarar {0} como anulable, o agregar atributos "[campo: MaybeNull, AllowNull]". Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. La propiedad no anulable debe contener un valor no nulo al salir del constructor. Considere agregar el modificador "requerido", o declarar la propiedad como anulable, o agregar atributos "[campo: MaybeNull, AllowNull]". @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - El modificador 'partial' solo puede aparecer inmediatamente antes de 'class', 'record', 'struct', 'interface' o un método o tipo de valor devuelto de propiedad. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + El modificador 'partial' solo puede aparecer inmediatamente antes de 'class', 'record', 'struct', 'interface' o un método o tipo de valor devuelto de propiedad. @@ -8817,8 +8957,8 @@ Un bloque catch() después de un bloque catch (System.Exception e) puede abarcar - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - No se puede definir un nuevo método de extensión porque no se encontró el tipo '{0}' requerido por el compilador. ¿Falta alguna referencia a System.Core.dll? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + No se puede definir un nuevo método de extensión porque no se encontró el tipo '{0}' requerido por el compilador. ¿Falta alguna referencia a System.Core.dll? @@ -8912,8 +9052,8 @@ Un bloque catch() después de un bloque catch (System.Exception e) puede abarcar - Invalid token '{0}' in class, record, struct, or interface member declaration - El token "{0}" no es válido en una clase, un registro, una estructura o una declaración de miembro de interfaz + Invalid token '{0}' in a member declaration + El token "{0}" no es válido en una clase, un registro, una estructura o una declaración de miembro de interfaz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index ddceebdb38625..cafe17844a0b4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -222,6 +222,11 @@ Impossible d'utiliser une collection de type dynamique dans un foreach asynchrone + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Le type '{0}' ne doit pas être utilisé pour un champ d'enregistrement. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Nous n’avons pas pu utiliser une constante numérique ou un modèle relationnel sur '{0}', car il hérite ou étend 'INumberBase<T>'. Utilisez un modèle de type pour vous limiter à un type numérique spécifié. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Nous n’avons pas pu utiliser une constante numérique ou un modèle relationnel sur '{0}', car il hérite ou étend 'INumberBase<T>'. Utilisez un modèle de type pour vous limiter à un type numérique spécifié. @@ -717,6 +722,11 @@ Une arborescence de l’expression ne peut pas contenir une expression de collection. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Une arborescence de l'expression ne peut pas contenir d'expression d'index partant de la fin ('^'). @@ -762,6 +772,26 @@ Une arborescence de l'expression ne peut pas contenir d'expression with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}' : un événement extern ne peut pas avoir d'initialiseur @@ -1247,6 +1277,11 @@ L’argument diagnosticId de l’attribut « Experimental » doit être un identificateur valide + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' n'est pas un modificateur de type de retour de pointeur de fonction valide. Les modificateurs valides sont 'ref' et 'ref readonly'. @@ -1387,6 +1422,16 @@ Les modèles de liste ne peuvent pas être utilisés pour une valeur de type '{0}'. Aucune propriété 'Longueur' ou 'Compte' appropriée n’a été trouvée. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Aucune surcharge pour '{0}' ne correspond au pointeur de fonction '{1}' @@ -1637,6 +1682,21 @@ La méthode '{0}' spécifie une contrainte 'struct' pour le paramètre de type '{1}', mais le paramètre de type '{2}' correspondant de la méthode substituée ou explicitement implémentée '{3}' n'est pas un type valeur non-nullable. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Le constructeur « {0} » laisse le membre « {1} » requis non initialisé. @@ -1672,6 +1732,16 @@ Le paramètre params doit avoir un type de collection valide + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Les deux déclarations de membres partiels doivent avoir des modificateurs d'accessibilité identiques. @@ -1682,11 +1752,31 @@ Un membre partiel ne peut pas avoir le modificateur « abstract » + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Les deux déclarations de membres partiels, « {0} » et « {1} », doivent utiliser les mêmes noms d'éléments tuple. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un membre partiel doit être déclaré dans un type partiel @@ -1712,6 +1802,11 @@ Les déclarations de membres partiels doivent avoir des valeurs de retour ref correspondantes. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Les deux déclarations partielles de membres doivent être peu sûres ou aucune ne peut l'être. @@ -1782,11 +1877,6 @@ Les deux déclarations de propriété partielle doivent être obligatoires ou aucune ne peut être requise - - Both partial property declarations must have the same type. - Les deux déclarations de propriété partielle doivent avoir le même type. - - Property accessor '{0}' does not implement any accessor declared on the definition part L’accesseur de propriété « {0} » n’implémente aucun accesseur déclaré sur la partie définition @@ -1847,6 +1937,16 @@ '{0}' : 'readonly' peut uniquement être utilisé sur des accesseurs si la propriété ou l'indexeur a un accesseur get et un accesseur set + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Le constructeur principal est en conflit avec le constructeur de copie synthétisée. @@ -2257,6 +2357,16 @@ Le type '{0}' doit être public pour être utilisé comme convention d'appel. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. La propriété implémentée automatiquement '{0}' doit être entièrement affectée avant que le contrôle soit retourné à l’appelant. Envisagez de mettre à jour la version de langue «{1}» pour définir automatiquement la propriété par défaut. @@ -2267,6 +2377,11 @@ Le champ '{0}' doit être entièrement attribué avant que le contrôle soit retourné à l’appelant. Envisagez de mettre à jour la version de langue «{1}» pour définir automatiquement le champ par défaut. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Liste de paramètres inattendue. @@ -2357,6 +2472,16 @@ Impossible d’utiliser l’objet 'this' avant l’affectation de tous ses champs. Envisagez de mettre à jour vers la version de langage '{0}' pour définir automatiquement les champs non attribués par défaut. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ modèles de propriétés étendues + + extensions + extensions + + field keyword mot-clé du champ @@ -2522,6 +2652,11 @@ sauts de ligne dans les interpolations + + null conditional assignment + null conditional assignment + + overload resolution priority priorité de résolution de surcharge @@ -2537,6 +2672,11 @@ collections de params + + partial events and constructors + partial events and constructors + + positional fields in records champs positionnels dans les enregistrements @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - L'assembly de l'analyseur fait référence à une version plus récente du compilateur que la version en cours d'exécution. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + L'assembly de l'analyseur fait référence à une version plus récente du compilateur que la version en cours d'exécution. @@ -3192,14 +3332,14 @@ Le paramètre a un modificateur de paramètres dans l’expression lambda mais pas dans le type délégué cible. - - Partial property declarations '{0}' and '{1}' have signature differences. - Les déclarations de propriétés partielles « {0} » et « {1} » ont des différences de signature. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Les déclarations de propriétés partielles présentent des différences de signature. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Non nullable {0} « {1} » doit contenir une valeur non nulle lors de la sortie du constructeur. Pensez à ajouter le modificateur « obligatoire », ou à déclarer {0} comme nullable, ou à ajouter les attributs « [field : MaybeNull, AllowNull] ». Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Une propriété non nullable doit contenir une valeur non nulle lors de la sortie du constructeur. Pensez à ajouter le modificateur « obligatoire », ou à déclarer la propriété comme nullable, ou à ajouter les attributs « [field : MaybeNull, AllowNull] ». @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Le modificateur 'partial' peut apparaître uniquement juste avant 'class', 'record', 'struct', 'interface' ou un type de retour de méthode ou de propriété. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Le modificateur 'partial' peut apparaître uniquement juste avant 'class', 'record', 'struct', 'interface' ou un type de retour de méthode ou de propriété. @@ -8817,8 +8957,8 @@ Un bloc catch() après un bloc catch (System.Exception e) peut intercepter des e - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Impossible de définir une nouvelle méthode d'extension, car le type requis par le compilateur '{0}' est introuvable. Vous manque-t-il une référence à System.Core.dll ? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Impossible de définir une nouvelle méthode d'extension, car le type requis par le compilateur '{0}' est introuvable. Vous manque-t-il une référence à System.Core.dll ? @@ -8912,8 +9052,8 @@ Un bloc catch() après un bloc catch (System.Exception e) peut intercepter des e - Invalid token '{0}' in class, record, struct, or interface member declaration - Jeton '{0}' non valide dans la déclaration de membre de classe, d'enregistrement, de struct ou d'interface + Invalid token '{0}' in a member declaration + Jeton '{0}' non valide dans la déclaration de membre de classe, d'enregistrement, de struct ou d'interface diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index fd94a9ee799ce..db97ace0ae56d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -222,6 +222,11 @@ Non è possibile usare una raccolta di tipo dinamico in un'istruzione foreach asincrona + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Il tipo '{0}' non può essere usato per un campo di un record. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Non è possibile usare una costante numerica o un modello relazionale su '{0}' perché eredita da o estende 'INumberBase<T>'. Provare a usare un modello di tipo per limitare a un tipo numerico specifico. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Non è possibile usare una costante numerica o un modello relazionale su '{0}' perché eredita da o estende 'INumberBase<T>'. Provare a usare un modello di tipo per limitare a un tipo numerico specifico. @@ -717,6 +722,11 @@ Un albero delle espressioni non può contenere un'espressione di raccolta. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Un albero delle espressioni non può contenere un'espressione di indice from end ('^'). @@ -762,6 +772,26 @@ Un albero delle espressioni non può contenere un'espressione with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': l'evento extern non può avere inizializzatori @@ -1247,6 +1277,11 @@ L'argomento diagnosticId dell'attributo 'Experimental' deve essere un identificatore valido + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' non è un modificatore di tipo restituito di puntatore a funzione valido. I modificatori validi sono 'ref' e 'ref readonly'. @@ -1387,6 +1422,16 @@ I criteri di elenco non possono essere utilizzati per un valore di tipo '{0}'. Non è stata trovata alcuna proprietà 'Length' o 'Count' appropriata. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Nessun overload per '{0}' corrisponde al puntatore a funzione '{1}' @@ -1637,6 +1682,21 @@ Il metodo '{0}' specifica un vincolo 'struct' per il parametro di tipo '{1}', ma il parametro di tipo corrispondente '{2}' del metodo '{3}' sottoposto a override o implementato in modo esplicito non è un tipo valore che non ammette valori Null. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Il costruttore '{0}' lascia non inizializzato il membro richiesto '{1}'. @@ -1672,6 +1732,16 @@ Il parametro params deve avere un tipo di raccolta valido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. I modificatori di accessibilità devono essere identici in entrambe le dichiarazioni di membro parziale. @@ -1682,11 +1752,31 @@ Un membro parziale non può contenere il modificatore 'abstract' + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Entrambe le dichiarazioni di membro parziale '{0}' e '{1}' devono usare gli stessi nomi di elementi di tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Un membro parziale deve essere dichiarato in un tipo parziale @@ -1712,6 +1802,11 @@ Le dichiarazioni di membro parziale devono contenere valori restituiti di riferimento corrispondenti. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Entrambe le dichiarazioni di membro parziale devono essere di tipo unsafe, altrimenti nessuna delle due potrà esserlo @@ -1782,11 +1877,6 @@ Entrambe le dichiarazioni di proprietà parziale devono essere richieste, altrimenti nessuna delle due potrà esserlo - - Both partial property declarations must have the same type. - Il tipo deve essere identico in entrambe le dichiarazioni di proprietà parziale. - - Property accessor '{0}' does not implement any accessor declared on the definition part La funzione di accesso alle proprietà '{0}' non implementa alcuna funzione di accesso dichiarata nella parte della definizione @@ -1847,6 +1937,16 @@ '{0}': 'readonly' può essere usato su funzioni di accesso solo se la proprietà o l'indicizzatore include entrambi le funzioni di accesso get e set + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Il costruttore primario è in conflitto con il costruttore di copia sintetizzato. @@ -2257,6 +2357,16 @@ Il tipo '{0}' deve essere pubblico per poterlo usare come convenzione di chiamata. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. La proprietà implementata automaticamente '{0}' deve essere assegnata completamente prima che il controllo sia restituito al chiamante. Provare a eseguire l'aggiornamento alla versione del linguaggio '{1}' per impostare automaticamente la proprietà come predefinita. @@ -2267,6 +2377,11 @@ Il campo '{0}' deve essere assegnato completamente prima che il controllo sia restituito al chiamante. Provare a eseguire l'aggiornamento alla versione del linguaggio '{1}' per impostare automaticamente il campo come predefinito. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Elenco parametri imprevisto. @@ -2357,6 +2472,16 @@ Impossibile utilizzare l'oggetto 'this' prima dell'assegnazione di tutti i relativi campi. Provare a eseguire l'aggiornamento alla versione del linguaggio '{0}' per impostare come predefiniti automaticamente i campi non assegnati. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ criteri di proprietà estesa + + extensions + extensions + + field keyword parola chiave campo @@ -2522,6 +2652,11 @@ nuove linee nelle interpolazioni + + null conditional assignment + null conditional assignment + + overload resolution priority priorità di risoluzione dell'overload @@ -2537,6 +2672,11 @@ raccolte parametri + + partial events and constructors + partial events and constructors + + positional fields in records campi posizionali nei record @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - L'assembly dell'analizzatore fa riferimento alla versione del compilatore, che è più recente della versione attualmente in esecuzione. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + L'assembly dell'analizzatore fa riferimento alla versione del compilatore, che è più recente della versione attualmente in esecuzione. @@ -3192,14 +3332,14 @@ Il parametro contiene un modificatore di parametri in lambda ma non nel tipo delegato di destinazione. - - Partial property declarations '{0}' and '{1}' have signature differences. - Le dichiarazioni di proprietà parziale '{0}' e '{1}' presentano differenze di firma. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Le dichiarazioni di proprietà parziale presentano differenze di firma. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ target:module Compila un modulo che può essere aggiunto ad altro - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. L'elemento {0} '{1}' che non ammette i valori Null deve contenere un valore non Null all'uscita dal costruttore. Prendere in considerazione l'aggiunta del modificatore 'required' o la dichiarazione come {0} che ammette i valori Null, oppure l'aggiunta di attributi '[field: MaybeNull, AllowNull]'. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. La proprietà che non ammette i valori Null deve contenere un valore non Null all'uscita dal costruttore. Provare ad aggiungere il modificatore 'required', a dichiarare la proprietà come che ammette i valori Null, oppure ad aggiungere gli attributi '[field: MaybeNull, AllowNull]'. @@ -6709,8 +6849,8 @@ target:module Compila un modulo che può essere aggiunto ad altro - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Il modificatore 'partial' può trovarsi solo immediatamente prima di 'class', 'record', 'struct', 'interface' o del tipo restituito di un metodo o di una proprietà. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Il modificatore 'partial' può trovarsi solo immediatamente prima di 'class', 'record', 'struct', 'interface' o del tipo restituito di un metodo o di una proprietà. @@ -8817,8 +8957,8 @@ Un blocco catch() dopo un blocco catch (System.Exception e) può rilevare eccezi - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Non è possibile definire un nuovo metodo di estensione perché non è stato trovato il tipo '{0}' richiesto dal compilatore. Probabilmente manca un riferimento. + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Non è possibile definire un nuovo metodo di estensione perché non è stato trovato il tipo '{0}' richiesto dal compilatore. Probabilmente manca un riferimento. @@ -8912,8 +9052,8 @@ Un blocco catch() dopo un blocco catch (System.Exception e) può rilevare eccezi - Invalid token '{0}' in class, record, struct, or interface member declaration - Il token '{0}' nella dichiarazione del membro di classe, record, struct o interfaccia non è valido + Invalid token '{0}' in a member declaration + Il token '{0}' nella dichiarazione del membro di classe, record, struct o interfaccia non è valido diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 65647430f1f04..a1ba411fc0134 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -222,6 +222,11 @@ 非同期 foreach では動的な型のコレクションを使用できません + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. レコードのフィールドに対して型 '{0}' を使用することはできません。 @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - 'INumberBase<T>' を継承または拡張しているため、'{0}' で数値定数またはリレーショナル パターンを使用することはできません。指定された数値型に絞り込むために、型パターンを使用することを検討してください。 + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + 'INumberBase<T>' を継承または拡張しているため、'{0}' で数値定数またはリレーショナル パターンを使用することはできません。指定された数値型に絞り込むために、型パターンを使用することを検討してください。 @@ -717,6 +722,11 @@ 式ツリーにコレクション式を含めることはできません。 + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. 式ツリーに、from-end インデックス ('^') 式を含めることはできません。 @@ -762,6 +772,26 @@ 式ツリーは、with 式を含むことはできません + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': extern イベントは初期化子を持つことができません @@ -1247,6 +1277,11 @@ 'Experimental' 属性への diagnosticId 引数は有効な識別子である必要があります + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' は有効な関数ポインターの戻り値の型修飾子ではありません。有効な修飾子は 'ref ' および 'ref readonly' です。 @@ -1387,6 +1422,16 @@ リスト パターンは、型 '{0}' の値には使用できません。適切な 'Length' または 'Count' プロパティが見つかりませんでした。 + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' 関数ポインター '{1}' に一致する '{0}' のオーバーロードはありません @@ -1637,6 +1682,21 @@ メソッド '{0}' は、型パラメーター '{1}' に対して 'struct' 制約を指定していますが、オーバーライドされた、または明示的に実装されたメソッド '{3}' の対応する型パラメーター '{2}' は NULL 非許容の値型ではありません。 + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. コンストラクター '{0}' は、必要なメンバー '{1}' を初期化しないままにします。 @@ -1672,6 +1732,16 @@ params パラメーターには有効なコレクションの種類が必要です + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 両方の部分メンバー宣言には、同じアクセシビリティ修飾子を指定する必要があります。 @@ -1682,11 +1752,31 @@ 部分メンバーに 'abstract' 修飾子を指定することはできません + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 部分メンバー宣言 '{0}' および '{1}' は、どちらも同じタプル要素名を使用する必要があります。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 部分メンバーは、部分型内で宣言される必要があります @@ -1712,6 +1802,11 @@ 部分メンバーの宣言には、一致する ref 戻り値が必要です。 + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe 部分メンバー宣言は、両方とも unsafe であるか、両方とも unsafe でないかのいずれかである必要があります @@ -1782,11 +1877,6 @@ 両方の部分プロパティ宣言を必須にするか、どちらも必須にしないようにする必要があります - - Both partial property declarations must have the same type. - 両方の部分プロパティ宣言の型が同じである必要があります。 - - Property accessor '{0}' does not implement any accessor declared on the definition part プロパティ アクセサー '{0}' は、定義パーツで宣言されたアクセサーを実装しません @@ -1847,6 +1937,16 @@ '{0}': 'readonly' は、プロパティまたはインデクサーが get および set の両方のアクセサーを含む場合にのみ、アクセサーで使用できます + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. プライマリ コンストラクターが、合成されたコピー コンストラクターと競合しています。 @@ -2257,6 +2357,16 @@ 呼び出し規則として使用する型 '{0}' はパブリックでなければなりません。 + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. コントロールを呼び出し元に返す前に、自動実装プロパティ '{0}' を完全に割り当てる必要があります。プロパティを自動既定値にするため '{1}' 言語バージョンに更新することを検討してください。 @@ -2267,6 +2377,11 @@ コントロールを呼び出し元に返す前に、フィールド '{0}' を完全に割り当てる必要があります。プロパティを自動既定値にするため '{1}' 言語バージョンに更新することを検討してください。 + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. 予期しないパラメーター リストです。 @@ -2357,6 +2472,16 @@ すべてのフィールドが割り当てられる前に、'this' オブジェクトを使用することはできません。割り当てられていないフィールドを自動既定値にするため '{0}' 言語バージョンに更新することを検討してください。 + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ 拡張プロパティ パターン + + extensions + extensions + + field keyword field キーワード @@ -2522,6 +2652,11 @@ 補間における改行 + + null conditional assignment + null conditional assignment + + overload resolution priority オーバーロード解決の優先順位 @@ -2537,6 +2672,11 @@ params コレクション + + partial events and constructors + partial events and constructors + + positional fields in records レコード内の位置指定フィールド @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 - The analyzer assembly references a newer version of the compiler than the currently running version. - アナライザー アセンブリが参照しるコンパイラのバージョンは、現在実行中のバージョンよりも新しいです。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + アナライザー アセンブリが参照しるコンパイラのバージョンは、現在実行中のバージョンよりも新しいです。 @@ -3192,14 +3332,14 @@ パラメーターにラムダの params 修飾子がありますが、ターゲット デリゲート型にはありません。 - - Partial property declarations '{0}' and '{1}' have signature differences. - 部分プロパティ宣言 '{0}' と '{1}' には、シグネチャの違いがあります。 + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - 部分プロパティの宣言には、シグネチャの違いがあります。 + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. null 非許容の {0} '{1}' には、コンストラクターの終了時に null 以外の値が入っていなければなりません。'required' 修飾子を追加するか、{0} を null 許容として宣言するか、'[field: MaybeNull, AllowNull]' 属性を追加することを検討してください。 Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. null 非許容プロパティには、コンストラクターの終了時に null 以外の値が入っていなければなりません。'required' 修飾子を追加するか、プロパティを null 許容として宣言するか、'[field: MaybeNull, AllowNull]' 属性を追加することを検討してください。 @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドまだはプロパティの戻り値の型の直前にのみ指定できます。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 修飾子は、'class'、'record'、'struct'、'interface'、またはメソッドまだはプロパティの戻り値の型の直前にのみ指定できます。 @@ -8817,8 +8957,8 @@ AssemblyInfo.cs ファイルで RuntimeCompatibilityAttribute が false に設 - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - コンパイラで必要とされる型 '{0}' が見つからないため、新しい拡張メソッドを定義できません。System.Core.dll への参照が指定されていることを確認してください。 + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + コンパイラで必要とされる型 '{0}' が見つからないため、新しい拡張メソッドを定義できません。System.Core.dll への参照が指定されていることを確認してください。 @@ -8912,8 +9052,8 @@ AssemblyInfo.cs ファイルで RuntimeCompatibilityAttribute が false に設 - Invalid token '{0}' in class, record, struct, or interface member declaration - クラス、レコード、構造体、またはインターフェイス メンバーの宣言でトークン '{0}' が無効です + Invalid token '{0}' in a member declaration + クラス、レコード、構造体、またはインターフェイス メンバーの宣言でトークン '{0}' が無効です diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index b01d67981c2c0..dea7078e009e4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -222,6 +222,11 @@ 비동기 foreach에는 동적 형식 컬렉션을 사용할 수 없습니다. + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. '{0}' 형식은 레코드의 필드에 사용할 수 없습니다. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - 'INumberBase<T>'에서 상속되거나 확장되므로 '{0}' 숫자 상수 또는 관계형 패턴을 사용할 수 없습니다. 형식 패턴을 사용하여 특정 숫자 형식으로 좁히는 것이 좋습니다. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + 'INumberBase<T>'에서 상속되거나 확장되므로 '{0}' 숫자 상수 또는 관계형 패턴을 사용할 수 없습니다. 형식 패턴을 사용하여 특정 숫자 형식으로 좁히는 것이 좋습니다. @@ -717,6 +722,11 @@ 식 트리에는 컬렉션 식이 포함될 수 없습니다. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. 식 트리에는 내림차순 인덱스('^') 식을 포함할 수 없습니다. @@ -762,6 +772,26 @@ 식 트리에는 with 식이 포함될 수 없습니다. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': extern 이벤트에는 이니셜라이저를 사용할 수 없습니다. @@ -1247,6 +1277,11 @@ 'Experimental' 특성에 대한 diagnosticId 인수는 유효한 식별자여야 합니다. + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}'은(는) 유효한 함수 포인터 반환 형식 한정자가 아닙니다. 유효한 한정자는 'ref' 및 'ref readonly'입니다. @@ -1387,6 +1422,16 @@ 목록 패턴은 '{0}' 형식의 값에 사용할 수 없습니다. 적합한 'Length' 또는 'Count' 속성을 찾을 수 없습니다. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' 함수 포인터 '{1}'과(와) 일치하는 '{0}'에 대한 오버로드가 없습니다. @@ -1637,6 +1682,21 @@ '{0}' 메서드는 형식 매개 변수 '{1}'의 'struct' 제약 조건을 지정하지만 재정의되었거나 명시적으로 구현된 '{3}' 메서드의 해당 형식 매개 변수 '{2}'이(가) null을 허용하지 않는 값 형식이 아닙니다. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. 생성자 '{0}'은(는) 필수 멤버 '{1}'을(를) 초기화되지 않은 상태로 둡니다. @@ -1672,6 +1732,16 @@ params 매개 변수는 유효한 컬렉션 형식이어야 합니다. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 두 부분 멤버 선언에는 동일한 접근성 한정자를 갖어야 합니다. @@ -1682,11 +1752,31 @@ 부분 멤버는 'abstract' 한정자를 갖을 수 없습니다. + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 두 부분 멤버 선언은 '{0}' 및 '{1}' 모두에서 동일한 튜플 요소 이름을 사용해야 합니다. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 부분 멤버 내에 부분 메서드가 선언되어야 함 @@ -1712,6 +1802,11 @@ 부분 멤버 선언에는 일치하는 참조 반환 값이 있어야 합니다. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe 두 부분 멤버 선언은 모두 unsafe이거나 unsafe가 아니어야 함 @@ -1782,11 +1877,6 @@ 두 부분 속성 선언이 모두 필요하거나 둘 다 필요하지 않을 수 있습니다. - - Both partial property declarations must have the same type. - 두 부분 속성 선언의 형식이 같아야 합니다. - - Property accessor '{0}' does not implement any accessor declared on the definition part 속성 접근자 '{0}'은(는) 정의 부분에 선언된 접근자를 구현하지 않음 @@ -1847,6 +1937,16 @@ '{0}': 속성 또는 인덱서에 get 접근자와 set 접근자가 둘 다 있는 경우에만 접근자에 'readonly'를 사용할 수 있습니다. + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. 기본 생성자가 합성된 복사 생성자와 충돌합니다. @@ -2257,6 +2357,16 @@ 호출 규칙으로 사용하려면 '{0}' 형식이 public이어야 합니다. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. 제어가 호출자에게 반환되기 전에 자동 구현 속성 '{0}'이(가) 완전히 할당되어야 합니다. 속성을 자동으로 기본 설정하려면 언어 버전 '{1}'(으)로 업데이트하는 것이 좋습니다. @@ -2267,6 +2377,11 @@ 제어가 호출자에게 반환되기 전에 필드 '{0}'이(가) 완전히 할당되어야 합니다. 필드를 자동으로 기본 설정하려면 언어 버전 '{1}'으로 업데이트하는 것이 좋습니다. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. 예기치 않은 매개 변수 목록입니다. @@ -2357,6 +2472,16 @@ 모든 필드가 할당되기 전에는 'this' 개체를 사용할 수 없습니다. 할당되지 않은 필드의 기본값을 자동으로 설정하려면 언어 버전 '{0}'(으)로 업데이트하는 것이 좋습니다. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ 확장 속성 패턴 + + extensions + extensions + + field keyword 필드 키워드 @@ -2522,6 +2652,11 @@ 보간에서 줄 바꿈 + + null conditional assignment + null conditional assignment + + overload resolution priority 오버로드 해결 우선 순위 @@ -2537,6 +2672,11 @@ params 컬렉션 + + partial events and constructors + partial events and constructors + + positional fields in records 레코드의 위치 필드 @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. - The analyzer assembly references a newer version of the compiler than the currently running version. - 분석기 어셈블리는 현재 실행 중인 버전보다 최신 버전의 컴파일러를 참조합니다. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 분석기 어셈블리는 현재 실행 중인 버전보다 최신 버전의 컴파일러를 참조합니다. @@ -3192,14 +3332,14 @@ 매개 변수에 람다의 매개 변수 한정자가 있지만 대상 대리자 형식에는 없습니다. - - Partial property declarations '{0}' and '{1}' have signature differences. - 부분 속성 선언 '{0}' 및 '{1}'에는 서명 차이가 있습니다. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - 부분 속성 선언에는 서명 차이가 있습니다. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. null을 허용하지 않는 {0} '{1}'은(는) 생성자를 종료할 때 null이 아닌 값을 포함해야 합니다. 'required' 한정자를 추가하거나 {0}을(를) nullable로 선언하거나 '[field: MaybeNull, AllowNull]' 특성을 추가하는 것이 좋습니다. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. null을 허용하지 않는 속성은 생성자를 종료할 때 null이 아닌 값을 포함해야 합니다. 'required' 한정자를 추가하거나 속성을 nullable로 선언하거나 '[field: MaybeNull, AllowNull]' 특성을 추가하는 것이 좋습니다. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 또는 속성 반환 형식 바로 앞에만 올 수 있습니다. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 한정자는 'class', 'record', 'struct', 'interface' 또는 메서드 또는 속성 반환 형식 바로 앞에만 올 수 있습니다. @@ -8817,8 +8957,8 @@ catch (System.Exception e) 블록 뒤의 catch() 블록은 RuntimeCompatibilityA - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - 컴파일러에 필요한 '{0}' 형식을 찾을 수 없으므로 새 확장 메서드를 정의할 수 없습니다. System.Core.dll의 참조가 있는지 확인하세요. + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + 컴파일러에 필요한 '{0}' 형식을 찾을 수 없으므로 새 확장 메서드를 정의할 수 없습니다. System.Core.dll의 참조가 있는지 확인하세요. @@ -8912,8 +9052,8 @@ catch (System.Exception e) 블록 뒤의 catch() 블록은 RuntimeCompatibilityA - Invalid token '{0}' in class, record, struct, or interface member declaration - 클래스, 레코드, 구조체 또는 인터페이스 멤버 선언에 잘못된 토큰 '{0}'이(가) 있습니다. + Invalid token '{0}' in a member declaration + 클래스, 레코드, 구조체 또는 인터페이스 멤버 선언에 잘못된 토큰 '{0}'이(가) 있습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 57dbceb0ec44e..434cd933d2262 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -222,6 +222,11 @@ Nie można użyć kolekcji typu dynamicznego w asynchronicznej instrukcji foreach + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Typ „{0}” nie może być używany dla pola rekordu. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Nie można użyć stałej liczbowej lub wzorca relacyjnego w „{0}”, ponieważ dziedziczy on lub rozszerza element "INumberBase<T>". Rozważ użycie wzorca typu w celu zawężenia do określonego typu liczbowego. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Nie można użyć stałej liczbowej lub wzorca relacyjnego w „{0}”, ponieważ dziedziczy on lub rozszerza element "INumberBase<T>". Rozważ użycie wzorca typu w celu zawężenia do określonego typu liczbowego. @@ -717,6 +722,11 @@ Drzewo wyrażenia nie może zawierać wyrażenia kolekcji. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Drzewo wyrażeń nie może zawierać wyrażenia „od końca indeksu” („^”). @@ -762,6 +772,26 @@ Drzewo wyrażeń nie może zawierać wyrażenia with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer „{0}”: zdarzenie extern nie może mieć inicjatora @@ -1247,6 +1277,11 @@ Argument diagnosticId atrybutu „Experimental” musi być prawidłowym identyfikatorem + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. Element „{0}” nie jest prawidłowym modyfikatorem zwracanego typu wskaźnikowego funkcji. Prawidłowe modyfikatory to „ref” i „ref readonly”. @@ -1387,6 +1422,16 @@ Wzorce listy nie mogą być używane dla wartości typu „{0}”. Nie znaleziono odpowiedniej właściwości „Długość” ani „Liczba”. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Żadne z przeciążeń dla elementu „{0}” nie pasuje do wskaźnika funkcji „{1}” @@ -1637,6 +1682,21 @@ Metoda „{0}” określa ograniczenie „struct” dla parametru typu „{1}”, lecz odpowiadający parametr typu „{2}” przesłoniętej lub jawnie zaimplementowanej metody „{3}” nie jest nienullowalnym typem wartości. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Konstruktor „{0}” pozostawia wymaganą składową "{1}". @@ -1672,6 +1732,16 @@ Parametr params musi mieć prawidłowy typ kolekcji + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Obie częściowe deklaracje elementów członkowskich muszą mieć identyczne modyfikatory dostępności. @@ -1682,11 +1752,31 @@ Częściowy element członkowski nie może mieć modyfikatora „abstract” + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Obie deklaracje częściowych elementów członkowskich, „{0}” i „{1}”, muszą korzystać z tych samych nazw elementów krotki. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Częściowy element członkowski musi być zadeklarowany w ramach częściowego typu @@ -1712,6 +1802,11 @@ Deklaracje częściowych elementów członkowskich muszą mieć pasujące wartości zwracane ref. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Obie częściowe deklaracje elementów członkowskich muszą być niebezpieczne lub żadna z nich nie może być niebezpieczna @@ -1782,11 +1877,6 @@ Obie deklaracje właściwości częściowych muszą być wymagane lub żadna z nich nie może być wymagana - - Both partial property declarations must have the same type. - Obie deklaracje właściwości częściowych muszą mieć ten sam typ. - - Property accessor '{0}' does not implement any accessor declared on the definition part Właściwość accessor „{0}” nie implementuje żadnego accessora zadeklarowanego w części definiującej @@ -1847,6 +1937,16 @@ „{0}”: modyfikatora „readonly” można użyć dla metod dostępu tylko wtedy, gdy właściwość lub indeksator mają metody dostępu get i set + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Konstruktor podstawowy powoduje konflikt z konstruktorem syntetyzowanej kopii. @@ -2257,6 +2357,16 @@ Typ „{0}” musi być publiczny, aby można go było używać jako konwencji wywoływania. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Automatycznie implementowana właściwość „{0}” musi być w pełni przypisana, zanim kontrolka zostanie zwrócona do obiektu wywołującego. Rozważ zaktualizowanie do wersji językowej „{1}”, aby automatycznie ustawić domyślną właściwość. @@ -2267,6 +2377,11 @@ Pole „{0}” musi być w pełni przypisane, zanim kontrolka zostanie zwrócona do obiektu wywołującego. Rozważ zaktualizowanie pola do wersji językowej „{1}”, aby automatycznie ustawić domyślne pole. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Nieoczekiwana lista parametrów. @@ -2357,6 +2472,16 @@ Nie można użyć obiektu „this” przed przypisaniem wszystkich jego pól. Rozważ zaktualizowanie nieprzypisanych pól do wersji językowej „{0}”, aby automatycznie ustawić domyślne pola niezaznaczone. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ wzorce właściwości rozszerzonych + + extensions + extensions + + field keyword słowo kluczowe pola @@ -2522,6 +2652,11 @@ nowe wiersze w interpolacjach + + null conditional assignment + null conditional assignment + + overload resolution priority priorytet rozwiązywania przeciążenia @@ -2537,6 +2672,11 @@ kolekcje params + + partial events and constructors + partial events and constructors + + positional fields in records pola pozycyjne w rekordach @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. - The analyzer assembly references a newer version of the compiler than the currently running version. - Zestaw analizatora odwołuje się do nowszej wersji kompilatora niż obecnie uruchomiona wersja. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Zestaw analizatora odwołuje się do nowszej wersji kompilatora niż obecnie uruchomiona wersja. @@ -3192,14 +3332,14 @@ Parametr ma modyfikator params w wyrażeniu lambda, ale nie ma w docelowym typie delegata. - - Partial property declarations '{0}' and '{1}' have signature differences. - Deklaracje właściwości częściowych „{0}” i „{1}” mają różnice w sygnaturach. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Deklaracje właściwości częściowych różnią się sygnaturami. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Niedopuszczający wartości null element {0} „{1}” musi zawierać wartość inną niż null podczas kończenia działania konstruktora. Rozważ dodanie modyfikatora „required”, zadeklarowanie {0} jako dopuszczającej wartość null lub dodanie atrybutów „[field: MaybeNull, AllowNull]”. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Właściwość niedopuszczająca wartości null musi zawierać wartość inną niż null podczas zamykania konstruktora. Rozważ dodanie modyfikatora „required” lub zadeklarowanie właściwości dopuszczającej wartość null lub dodanie atrybutów „[field: MaybeNull, AllowNull]”. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Modyfikator „partial” może pojawić się tylko bezpośrednio przed słowem kluczowym „class”, „record” „struct”, „interface” lub zwracanym typem metody lub właściwości. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Modyfikator „partial” może pojawić się tylko bezpośrednio przed słowem kluczowym „class”, „record” „struct”, „interface” lub zwracanym typem metody lub właściwości. @@ -8817,8 +8957,8 @@ Blok catch() po bloku catch (System.Exception e) może przechwytywać wyjątki n - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Nie można zdefiniować nowej metody rozszerzenia, ponieważ nie można odnaleźć wymaganego przez kompilator typu „{0}”. Czy brakuje odwołania do System.Core.dll? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Nie można zdefiniować nowej metody rozszerzenia, ponieważ nie można odnaleźć wymaganego przez kompilator typu „{0}”. Czy brakuje odwołania do System.Core.dll? @@ -8912,8 +9052,8 @@ Blok catch() po bloku catch (System.Exception e) może przechwytywać wyjątki n - Invalid token '{0}' in class, record, struct, or interface member declaration - Nieprawidłowy token „{0}” w deklaracji składowej klasy, rekordu, struktury lub interfejsu + Invalid token '{0}' in a member declaration + Nieprawidłowy token „{0}” w deklaracji składowej klasy, rekordu, struktury lub interfejsu diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index d0d94f1330ea7..7c7e74a0eb44b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -222,6 +222,11 @@ Não é possível usar uma coleção do tipo dinâmico em uma foreach assíncrona + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. O tipo '{0}' não pode ser usado para um campo de um registro. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Não é possível usar uma constante numérica ou padrão relacional em '{0}' porque herda ou estende 'INumberBase<T>'. Considere usar um padrão de tipo para restringir a um tipo numérico específico. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Não é possível usar uma constante numérica ou padrão relacional em '{0}' porque herda ou estende 'INumberBase<T>'. Considere usar um padrão de tipo para restringir a um tipo numérico específico. @@ -717,6 +722,11 @@ Uma árvore de expressão não pode conter uma expressão de coleção. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Uma árvore de expressão não pode conter uma expressão de índice de front-end ('^'). @@ -762,6 +772,26 @@ Uma árvore de expressão não pode conter uma expressão with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': o evento externo não pode ter inicializador @@ -1247,6 +1277,11 @@ O argumento diagnosticId para o atributo 'Experimental' deve ser um identificador válido + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}' não é um modificador de tipo de retorno de ponteiro de função válido. Os modificadores válidos são 'ref' e 'ref readonly'. @@ -1387,6 +1422,16 @@ Padrões de lista não podem ser usados para um valor do tipo “{0}”. Nenhuma propriedade “Length” ou “Count” adequada foi encontrada. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Nenhuma sobrecarga de '{0}' corresponde ao ponteiro de função '{1}' @@ -1637,6 +1682,21 @@ O método '{0}' especifica uma restrição 'struct' para o parâmetro de tipo '{1}', mas o parâmetro de tipo correspondente '{2}' do método substituído ou implementado explicitamente '{3}' não é um tipo de valor não anulável. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. O construtor '{0}' deixa o membro obrigatório '{1}' não inicializado. @@ -1672,6 +1732,16 @@ O parâmetro params deve ter um tipo de coleção válido + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. As duas declarações de membros parciais precisam ter modificadores de acessibilidade idênticos. @@ -1682,11 +1752,31 @@ Um membro parcial não pode ter o modificador "abstract" + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. As duas declarações de membros parciais, "{0}" e "{1}", precisam usar os mesmos nomes de elementos de tupla. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Um membro parcial precisa ser declarado dentro de um tipo parcial @@ -1712,6 +1802,11 @@ As declarações de membros parciais precisam ter valores de retorno de referência correspondentes. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe As duas declarações de membros parciais precisam ser inseguras ou nenhuma pode ser insegura @@ -1782,11 +1877,6 @@ As duas declarações de propriedades parciais precisam ser obrigatórias ou nenhuma delas pode ser obrigatória - - Both partial property declarations must have the same type. - As duas declarações de propriedades parciais precisam ter o mesmo tipo. - - Property accessor '{0}' does not implement any accessor declared on the definition part O acessador de propriedade "{0}" não implementa nenhum acessador declarado na parte de definição @@ -1847,6 +1937,16 @@ '{0}': 'readonly' somente pode ser usado em acessadores quando a propriedade ou o indexador tem um acessador get e um set + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. O construtor primário entra em conflito com o construtor de cópia sintetizado. @@ -2257,6 +2357,16 @@ O tipo '{0}' precisa ser público para ser usado como uma convenção de chamada. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. A propriedade auto-implementada '{0}' deve ser totalmente atribuída antes que o controle seja devolvido ao chamador. Considere atualizar para a versão de linguagem '{1}' para auto-padrão da propriedade. @@ -2267,6 +2377,11 @@ O campo '{0}' deve ser totalmente atribuída antes que o controle seja devolvido ao chamador. Considere atualizar para a versão de linguagem '{1}' para auto-padrão do campo. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Lista de parâmetros inesperada. @@ -2357,6 +2472,16 @@ O objeto 'this' não pode ser usado antes que todos os seus campos serem atribuídos. Considere atualizar para a versão de linguagem '{0}' para auto-padrão dos campos não atribuídos. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ padrões de propriedade estendida + + extensions + extensions + + field keyword campo palavra-chave @@ -2522,6 +2652,11 @@ novas linhas em interpolações + + null conditional assignment + null conditional assignment + + overload resolution priority Prioridade de resolução de sobrecarga @@ -2537,6 +2672,11 @@ coleções de params + + partial events and constructors + partial events and constructors + + positional fields in records campos posicionais nos registros @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - O assembly do analisador faz referência a uma versão mais recente do compilador do que a versão em execução no momento. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + O assembly do analisador faz referência a uma versão mais recente do compilador do que a versão em execução no momento. @@ -3192,14 +3332,14 @@ O parâmetro tem modificador de parâmetros em lambda, mas não no tipo delegado de destino. - - Partial property declarations '{0}' and '{1}' have signature differences. - As declarações de propriedades parciais "{0}" e "{1}" têm diferenças de assinatura. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - As declarações de propriedade parcial têm diferenças de assinatura. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. A {0} não anulável ''{1}'' precisa conter um valor não nulo ao sair do construtor. Considere adicionar o modificador ''necessário'' ou declarar a {0} como anulável ou adicionar os atributos ''[field: MaybeNull, AllowNull]''. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. A propriedade não anulável deve conter um valor não nulo ao sair do construtor. Considere adicionar o modificador ''required'' ou declarar a propriedade como anulável ou adicionar os atributos ''[field: MaybeNull, AllowNull]''. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - O modificador "partial" só pode aparecer imediatamente antes de "class", de "record", de "struct", de "interface" ou de um tipo de retorno de método ou propriedade. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + O modificador "partial" só pode aparecer imediatamente antes de "class", de "record", de "struct", de "interface" ou de um tipo de retorno de método ou propriedade. @@ -8817,8 +8957,8 @@ Um bloco catch() depois de um bloco catch (System.Exception e) poderá capturar - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Não é possível definir um novo método de extensão porque o tipo necessário de compilador "{0}" não pode ser encontrado. Está faltando uma referência a System.Core.dll? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Não é possível definir um novo método de extensão porque o tipo necessário de compilador "{0}" não pode ser encontrado. Está faltando uma referência a System.Core.dll? @@ -8912,8 +9052,8 @@ Um bloco catch() depois de um bloco catch (System.Exception e) poderá capturar - Invalid token '{0}' in class, record, struct, or interface member declaration - Token inválido '{0}' na declaração de membro de classe, de registro, de struct ou de interface + Invalid token '{0}' in a member declaration + Token inválido '{0}' na declaração de membro de classe, de registro, de struct ou de interface diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 2c1f011baab3d..96459e55e0274 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -222,6 +222,11 @@ Не удается использовать коллекцию динамического типа в асинхронном операторе foreach + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. Тип "{0}" не может быть использован для поля записи. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - Невозможно использовать числовую константу или реляционный шаблон для "{0}", поскольку он наследует от "INumberBase<T>" или расширяет его. Рассмотрите возможность использовать шаблон типа, чтобы указать конкретный числовой тип. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + Невозможно использовать числовую константу или реляционный шаблон для "{0}", поскольку он наследует от "INumberBase<T>" или расширяет его. Рассмотрите возможность использовать шаблон типа, чтобы указать конкретный числовой тип. @@ -717,6 +722,11 @@ Дерево выражения не может содержать выражение коллекции. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. Дерево выражений не может содержать выражение индекса, отсчитываемого с конца ("^"). @@ -762,6 +772,26 @@ Дерево выражения не может содержать выражение with. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer "{0}": внешнее событие не может иметь инициализатор @@ -1247,6 +1277,11 @@ Аргумент diagnosticId атрибута "Experimental" должен быть допустимым идентификатором + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. "{0}" не является допустимым модификатором типа для возвращаемого значения указателя на функцию. Допустимые модификаторы: ref и ref readonly. @@ -1387,6 +1422,16 @@ Шаблоны списка не могут использоваться для значения типа {0}. Подходящее свойство \"Length\" или \"Count\" не найдено. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' Нет перегруженного метода для "{0}", который соответствует указателю на функцию "{1}". @@ -1637,6 +1682,21 @@ Метод "{0}" задает ограничение struct для параметра типа "{1}", но соответствующий параметр типа "{2}" переопределенного или явно реализованного метода "{3}" не является типом значения, не допускающим значение NULL. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Конструктор "{0}" оставляет необходимый элемент "{1}" не инициализированным. @@ -1672,6 +1732,16 @@ Параметр params должен иметь допустимый тип коллекции. + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. У обоих объявлений частичного члена должны быть идентичные модификаторы доступа. @@ -1682,11 +1752,31 @@ У частичного члена не может быть модификатора "abstract" + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Оба определения частичного члена "{0}" и "{1}" должны использовать одинаковые имена элементов кортежа. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Частичный член должен быть объявлен из частичного типа @@ -1712,6 +1802,11 @@ У объявлений частичного члена должны совпадать возвращаемые значения "ref". + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Оба объявления частичного члена должны быть небезопасными или оба не должны быть небезопасными @@ -1782,11 +1877,6 @@ Оба объявления частичного члена должны быть обязательными или оба не должны быть обязательными - - Both partial property declarations must have the same type. - Оба объявления частичного свойства должны быть одинакового типа. - - Property accessor '{0}' does not implement any accessor declared on the definition part Метод доступа "{0}" не реализует методы доступа, объявленные для части определения @@ -1847,6 +1937,16 @@ "{0}": readonly можно использовать для методов доступа, только если свойство или индексатор имеет оба метода доступа, get и set. + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Первичный конструктор конфликтует с синтезированным конструктором копий. @@ -2257,6 +2357,16 @@ Тип "{0}" должен быть открытым для использования в качестве соглашения о вызовах. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Автоматически реализуемое свойство "{0}" должно быть полностью назначено перед возвратом контроля вызывающему элементу. Попробуйте обновить до языковой версии "{1}", чтобы автоматически применить значение по умолчанию к этому свойству. @@ -2267,6 +2377,11 @@ Поле '{0}' должно быть полностью назначенным перед возвратом контроля вызывающему элементу. Попробуйте обновить поле до языковой версии "{1}", чтобы автоматически применить значение по умолчанию. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Неожиданный список параметров. @@ -2357,6 +2472,16 @@ Нельзя использовать объект "this", пока не будут назначены все его поля. Попробуйте обновить до языковой версии "{0}", чтобы автоматически применить значения по умолчанию к неназначенным полям. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ шаблоны расширенных свойств + + extensions + extensions + + field keyword ключевое слово поля @@ -2522,6 +2652,11 @@ новые линии в интерполяции + + null conditional assignment + null conditional assignment + + overload resolution priority приоритет разрешения перегрузки @@ -2537,6 +2672,11 @@ коллекции params + + partial events and constructors + partial events and constructors + + positional fields in records позиционные поля в записях @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". - The analyzer assembly references a newer version of the compiler than the currently running version. - Сборка анализатора ссылается на более новую версию компилятора, чем установленная сейчас. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Сборка анализатора ссылается на более новую версию компилятора, чем установленная сейчас. @@ -3192,14 +3332,14 @@ У параметра есть модификатор params в лямбде, но не в типе целевого делегата. - - Partial property declarations '{0}' and '{1}' have signature differences. - У объявлений частичного свойства "{0}" и "{1}" разные сигнатуры. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - У объявлений частичного свойств и разные сигнатуры. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5360,12 +5500,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. {0} "{1}", не допускающий значения NULL, должен содержать значение, отличное от NULL, при выходе из конструктора. Рассмотрите возможность добавить модификатор "required", объявить {0} как допускающее значение NULL или добавить атрибуты "[field: MaybeNull, AllowNull]". Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Свойство, не допускающее значения NULL, должно содержать значение, отличное от NULL, при выходе из конструктора. Рассмотрите возможность добавить модификатор "required", объявить свойство как допускающее значение NULL или добавить атрибуты "[field: MaybeNull, AllowNull]". @@ -6710,8 +6850,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface", а также перед возвращаемым типом метода или свойства. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + Модификатор "partial" может использоваться только перед ключевыми словами "class", "record", "struct" и "interface", а также перед возвращаемым типом метода или свойства. @@ -8818,8 +8958,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Не удается определить новый метод расширения, так как не найден требуемый компилятором тип "{0}". Возможно, отсутствует ссылка на System.Core.dll + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Не удается определить новый метод расширения, так как не найден требуемый компилятором тип "{0}". Возможно, отсутствует ссылка на System.Core.dll @@ -8913,8 +9053,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Invalid token '{0}' in class, record, struct, or interface member declaration - Недопустимый токен "{0}" в объявлении класса, записи, структуры или элемента интерфейса + Invalid token '{0}' in a member declaration + Недопустимый токен "{0}" в объявлении класса, записи, структуры или элемента интерфейса diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 4e5bd6417ef0e..85efa757102a7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -222,6 +222,11 @@ Zaman uyumsuz bir foreach içinde dinamik tür koleksiyonu oluşturulamaz + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. '{0}' türü, kayıt alanı için kullanılamaz. @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - 'INumberBase<T>' öğesinden devraldığı veya genişlediği için '{0}' öğesinde sayısal bir sabit veya ilişkisel desen kullanılamaz. Belirli bir sayısal türe daraltmak için bir tür deseni kullanmayı düşünün. + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + 'INumberBase<T>' öğesinden devraldığı veya genişlediği için '{0}' öğesinde sayısal bir sabit veya ilişkisel desen kullanılamaz. Belirli bir sayısal türe daraltmak için bir tür deseni kullanmayı düşünün. @@ -717,6 +722,11 @@ İfade ağacı bir koleksiyon ifadesi içermeyebilir. + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. İfade ağacı, sondan dizin ('^') ifadesi içeremez. @@ -762,6 +772,26 @@ İfade ağacı, with ifadesi içeremez. + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': dış etkinliğin başlatıcısı olamaz @@ -1247,6 +1277,11 @@ 'Experimental' özniteliğinin diagnosticId bağımsız değişkeni geçerli bir tanımlayıcı olmalıdır + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}', geçerli bir işlev işaretçisi dönüş türü değiştiricisi değil. Geçerli değiştiriciler: 'ref' ve 'ref readonly'. @@ -1387,6 +1422,16 @@ Liste desenleri, '{0}' türündeki bir değer için kullanılamaz. Uygun 'Length' veya 'Count' özelliği bulunamadı. + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' '{0}' için aşırı yüklemelerin hiçbiri '{1}' işlev işaretçisiyle eşleşmiyor @@ -1637,6 +1682,21 @@ '{0}' yöntemi, '{1}' tür parametresi için bir 'struct' kısıtlaması belirtiyor, ancak geçersiz kılınan veya açıkça uygulanan '{3}' yönteminin karşılık gelen '{2}' tür parametresi boş değer atanamaz bir tip değil. + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. Oluşturucu '{0}', gerekli '{1}' üyesinin başlatmasını geri alıyor. @@ -1672,6 +1732,16 @@ Params parametresi geçerli bir koleksiyon türüne sahip olmalıdır + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. Kısmi üye bildirimlerinin ikisi de aynı erişilebilirlik değiştiricilerine sahip olmalıdır. @@ -1682,11 +1752,31 @@ Kısmi üyede 'abstract' değiştiricisi olamaz + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. Kısmi üye bildirimlerinin ikisi de ('{0}' ve '{1}') aynı tanımlama grubu adını kullanmalıdır. + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type Kısmi metot, kısmi tür içinde bildirilmelidir @@ -1712,6 +1802,11 @@ Kısmi üye bildirimlerinde eşleşen başvuru dönüş değerleri olmalıdır. + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe Kısmi üye bildirimlerinin ikisi de güvensiz olmalı veya hiçbiri güvensiz olmamalıdır @@ -1782,11 +1877,6 @@ Kısmi özellik bildirimlerinin ikisi de gerekli olmalı veya hiçbiri gerekli olmamalıdır - - Both partial property declarations must have the same type. - Kısmi özellik bildirimlerini ikisi de aynı dönüş türüne sahip olmalıdır. - - Property accessor '{0}' does not implement any accessor declared on the definition part '{0}' özellik erişimcisi, tanım bölümünde bildirilen hiçbir erişimciyi uygulamaz @@ -1847,6 +1937,16 @@ '{0}': 'readonly' erişimcilerde yalnızca özellik veya dizin oluşturucusu hem alma hem ayarlama erişimcisine sahipse kullanılabilir + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. Birincil oluşturucu, sentezlenmiş kopya oluşturucusuyla çakışıyor. @@ -2257,6 +2357,16 @@ '{0}' türünün bir çağırma kuralı olarak kullanılabilmesi için genel olması gerekir. + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. Denetim çağırana döndürülmeden önce otomatik uygulanan '{0}' özelliği tam olarak atanmalıdır. Özelliği otomatik olarak varsayılan durumuna getirmek için '{1}' dil sürümüne güncelleştirmeyi düşünün. @@ -2267,6 +2377,11 @@ Denetim çağırana döndürülmeden önce '{0}' alanı tam olarak atanmalıdır. Alanı otomatik olarak varsayılan durumuna getirmek için '{1}' dil sürümüne güncelleştirmeyi düşünün. + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. Beklenmeyen parametre listesi. @@ -2357,6 +2472,16 @@ 'this' nesnesi, tüm alanları atanmadan önce kullanılamaz. Atanmamış alanları otomatik olarak varsayılan durumuna getirmek için '{0}' dil sürümüne güncelleştirmeyi düşünün. + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ genişletilmiş özellik desenleri + + extensions + extensions + + field keyword alan anahtar sözcüğü @@ -2522,6 +2652,11 @@ ilişkilendirmedeki yeni satırlar + + null conditional assignment + null conditional assignment + + overload resolution priority aşırı yükleme çözümlemesi önceliği @@ -2537,6 +2672,11 @@ params koleksiyonları + + partial events and constructors + partial events and constructors + + positional fields in records kayıtlardaki konumsal alanlar @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. - The analyzer assembly references a newer version of the compiler than the currently running version. - Çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışandan daha yeni bir sürümüne başvurur. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışandan daha yeni bir sürümüne başvurur. @@ -3192,14 +3332,14 @@ Parametre, lambda içinde parametre değiştiricisi içeriyor ancak hedef temsilci türünde içermiyor. - - Partial property declarations '{0}' and '{1}' have signature differences. - '{0}' ve '{1}' kısmi özellik bildirimlerinde imza farklılıkları var. + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - Kısmi özellik bildirimlerinde imza farklılıkları var. + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. Null atanamaz {0} '{1}', oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. 'required' tanımlayıcısını eklemeyi, {0} null atanabilir olarak bildirmeyi veya '[field: MaybeNull, AllowNull]' özniteliklerini eklemeyi düşünün. Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. Null atanamaz özelliği, oluşturucudan çıkış yaparken null olmayan bir değer içermelidir. 'required' tanımlayıcısını eklemeyi, özelliği null atanabilir olarak bildirmeyi veya '[field: MaybeNull, AllowNull]' özniteliklerini eklemeyi düşünün. @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' değiştiricisi yalnızca 'class', 'record', 'struct', 'interface' ifadelerinden veya metot ya da özellik dönüş türünden hemen önce gelebilir. + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' değiştiricisi yalnızca 'class', 'record', 'struct', 'interface' ifadelerinden veya metot ya da özellik dönüş türünden hemen önce gelebilir. @@ -8817,8 +8957,8 @@ RuntimeCompatibilityAttribute AssemblyInfo.cs dosyasında false olarak ayarlanm - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - Derleyicinin gerektirdiği '{0}' türü bulunamadığından yeni bir genişletme yöntemi tanımlanamıyor. Bir System.Core.dll başvurusu eksik olabilir mi? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + Derleyicinin gerektirdiği '{0}' türü bulunamadığından yeni bir genişletme yöntemi tanımlanamıyor. Bir System.Core.dll başvurusu eksik olabilir mi? @@ -8912,8 +9052,8 @@ RuntimeCompatibilityAttribute AssemblyInfo.cs dosyasında false olarak ayarlanm - Invalid token '{0}' in class, record, struct, or interface member declaration - Sınıf, kayıt, yapı veya arabirim üye bildiriminde '{0}' belirteci geçersiz + Invalid token '{0}' in a member declaration + Sınıf, kayıt, yapı veya arabirim üye bildiriminde '{0}' belirteci geçersiz diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 2ba44fcc0e1bd..d05cbeb6be7e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -222,6 +222,11 @@ 无法在异步 foreach 中使用动态类型集合 + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. 类型“{0}”不能用于记录的字段。 @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - 无法对 "{0}" 使用数值常量或关系模式,因为它继承自或扩展了 "INumberBase<T>"。请考虑使用类型模式缩小到具体的数值类型。 + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + 无法对 "{0}" 使用数值常量或关系模式,因为它继承自或扩展了 "INumberBase<T>"。请考虑使用类型模式缩小到具体的数值类型。 @@ -717,6 +722,11 @@ 表达式树不能包含集合表达式。 + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. 表达式树不能包含 from-end 索引("^")表达式。 @@ -762,6 +772,26 @@ 表达式树不能包含 with 表达式。 + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer “{0}”: 外部事件不能有初始值设定项 @@ -1247,6 +1277,11 @@ “Experimental” 属性的 diagnosticId 参数必须是有效的标识符 + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. “{0}”不是有效的函数指针返回类型修饰符。有效的修饰符为 "ref" 和 "ref readonly"。 @@ -1387,6 +1422,16 @@ 列表模式不能用于 '{0}' 类型的值。找不到合适的 \"Length\" 或 \"Count\" 属性。 + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' “{0}”没有与函数指针“{1}”匹配的重载 @@ -1637,6 +1682,21 @@ 方法 "{0}" 为类型参数 "{1}" 指定了 "struct" 约束,但重写的或显式实现的方法 "{3}" 的相应类型参数 "{2}" 不是不可为 null 的值类型。 + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. 构造函数“{0}”的必需成员“{1}”未初始化。 @@ -1672,6 +1732,16 @@ Params 参数必须具有有效的集合类型 + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 这两个分部成员声明必须具有相同的可访问性修饰符。 @@ -1682,11 +1752,31 @@ 分部成员不能具有 "abstract" 修饰符 + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 这两个分部成员声明(“{0}”和“{1}”)都必须使用相同的元组元素名称。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 分部成员必须在分部类型内声明 @@ -1712,6 +1802,11 @@ 分部成员声明必须具有匹配的引用返回值。 + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe 这两个分部成员声明必须都是或者都不是不安全声明 @@ -1782,11 +1877,6 @@ 这两个分部属性声明必须都是或者都不是必需声明 - - Both partial property declarations must have the same type. - 这两个分部属性声明必须具有相同的类型。 - - Property accessor '{0}' does not implement any accessor declared on the definition part 属性访问器“{0}”未实现在定义部分上声明的任何访问器 @@ -1847,6 +1937,16 @@ “{0}”: 仅当属性或索引器同时具有 get 访问器和 set 访问器时,才能对访问器使用 "readonly" + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. 主构造函数与合成的复制构造函数冲突。 @@ -2257,6 +2357,16 @@ 类型“{0}”必须是公共的,才能用作调用约定。 + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. 必须先完全分配自动实现的属性'{0}',然后才能将控件返回到调用方。请考虑更新到语言版本'{1}'以自动默认属性。 @@ -2267,6 +2377,11 @@ 必须先完全分配字段 '{0}' ,然后才能将控件返回给调用方。请考虑更新到语言版本 '{1}' 以自动默认字段。 + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. 意外的参数列表。 @@ -2357,6 +2472,16 @@ 在分配“this”对象的所有字段之前,无法使用该对象。请考虑更新到语言版本 '{0}' 以自动默认未分配的字段。 + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ 扩展的属性模式 + + extensions + extensions + + field keyword 字段关键字 @@ -2522,6 +2652,11 @@ 内插中的换行符 + + null conditional assignment + null conditional assignment + + overload resolution priority 重载解析优先级 @@ -2537,6 +2672,11 @@ Params 集合 + + partial events and constructors + partial events and constructors + + positional fields in records 记录中的位置字段 @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 - The analyzer assembly references a newer version of the compiler than the currently running version. - 分析器程序集引用的编译器版本高于当前正在运行的版本。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 分析器程序集引用的编译器版本高于当前正在运行的版本。 @@ -3192,14 +3332,14 @@ 参数在 lambda 中具有参数修饰符,但在目标委托类型中没有参数修饰符。 - - Partial property declarations '{0}' and '{1}' have signature differences. - 分部属性声明“{0}”和“{1}”具有签名差异。 + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - 分部属性声明具有签名差异。 + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. 在退出构造函数时,不可为 null 的 {0} ‘{1}’ 必须包含非 null 值。请考虑添加 ‘required’ 修饰符,或将 {0} 声明为可为 null,或添加 ‘[field: MaybeNull, AllowNull]’ 特性。 Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. 退出构造函数时,不可为 null 的属性必须包含非 null 值。请考虑添加 ‘required’ 修饰符,或将属性声明为可为 null,或添加 ‘[field: MaybeNull, AllowNull]’ 特性。 @@ -6709,8 +6849,8 @@ - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或者方法或属性返回类型。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + "partial" 修饰符的后面只能紧跟 "class"、"record"、"struct"、"interface" 或者方法或属性返回类型。 @@ -8817,8 +8957,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - 无法定义新的扩展方法,因为找不到编译器需要的类型“{0}”。是否缺少对 System.Core.dll 的引用? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + 无法定义新的扩展方法,因为找不到编译器需要的类型“{0}”。是否缺少对 System.Core.dll 的引用? @@ -8912,8 +9052,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Invalid token '{0}' in class, record, struct, or interface member declaration - 类、记录、结构或接口成员声明中的标记“{0}”无效 + Invalid token '{0}' in a member declaration + 类、记录、结构或接口成员声明中的标记“{0}”无效 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 4ae88a2fecd1e..b30ef2cfe42ab 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -222,6 +222,11 @@ 無法在非同步 foreach 中使用動態類型的集合 + + Extensions must be declared in a top-level, non-generic, static class + Extensions must be declared in a top-level, non-generic, static class + + The type '{0}' may not be used for a field of a record. 類型 '{0}' 不可用於記錄的欄位。 @@ -368,8 +373,8 @@ - Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specifc numeric type. - 無法在 '{0}' 上使用數值常數或關聯式模式,因為它繼承自或延伸 'INumberBase<T>'。請考慮使用類型模式來縮小為特定數數值型別。 + Cannot use a numeric constant or relational pattern on '{0}' because it inherits from or extends 'INumberBase<T>'. Consider using a type pattern to narrow to a specific numeric type. + 無法在 '{0}' 上使用數值常數或關聯式模式,因為它繼承自或延伸 'INumberBase<T>'。請考慮使用類型模式來縮小為特定數數值型別。 @@ -717,6 +722,11 @@ 運算式樹狀架構不得包含集合運算式。 + + An expression tree may not contain an extension property access + An expression tree may not contain an extension property access + + An expression tree may not contain a from-end index ('^') expression. 運算式樹狀架構不可包含 from-end index ('^') 運算式。 @@ -762,6 +772,26 @@ 運算式樹狀架構不得包含 with 運算式。 + + Extension declarations can include only methods or properties + Extension declarations can include only methods or properties + + + + Extension declarations may not have a name. + Extension declarations may not have a name. + + + + The receiver parameter of an extension cannot have a default value + The receiver parameter of an extension cannot have a default value + + + + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + '{0}' does not contain a definition for '{1}' and no accessible extension member '{1}' for receiver of type '{0}' could be found (are you missing a using directive or an assembly reference?) + + '{0}': extern event cannot have initializer '{0}': 外部事件不可有初始設定式 @@ -1247,6 +1277,11 @@ 'Experimental' 屬性的 diagnosticId 引數必須是有效的識別碼 + + Cannot use extension parameter '{0}' in this context. + Cannot use extension parameter '{0}' in this context. + + '{0}' is not a valid function pointer return type modifier. Valid modifiers are 'ref' and 'ref readonly'. '{0}'不是有效的函式指標傳回型別修飾元。有效的修飾元為 'ref' 與 'ref readonly'。 @@ -1387,6 +1422,16 @@ 清單模式不能用於型別 '{0}' 的值。找不到適當的 'Length' 或 'Count' 屬性。 + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension parameter + + + + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + '{0}': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + + No overload for '{0}' matches function pointer '{1}' '{0}' 沒有任何多載符合函式指標 '{1}' @@ -1637,6 +1682,21 @@ 方法 '{0}' 會為型別參數 '{1}' 指定 'struct' 條件約束,但覆寫或明確實作的方法 '{3}' 對應型別參數 '{2}' 是不可為 Null 實值型別。 + + '#:' directives cannot be after '#if' directive + '#:' directives cannot be after '#if' directive + + + + '#:' directives cannot be after first token in file + '#:' directives cannot be after first token in file + + + + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + '#:' directives can be only used in file-based programs ('/feature:FileBasedProgram') + + Constructor '{0}' leaves required member '{1}' uninitialized. 建構函式 '{0}' 未初始化必要的成員 '{1}'。 @@ -1672,6 +1732,16 @@ params 參數必須具有有效的集合型別 + + '{0}': only the implementing declaration of a partial constructor can have an initializer + '{0}': only the implementing declaration of a partial constructor can have an initializer + + + + '{0}': partial event cannot have initializer + '{0}': partial event cannot have initializer + + Both partial member declarations must have identical accessibility modifiers. 兩個部分成員宣告都必須具有完全相同的協助工具修飾元。 @@ -1682,11 +1752,31 @@ 部分成員不能有 'abstract' 修飾元 + + Partial member '{0}' may not have multiple defining declarations. + Partial member '{0}' may not have multiple defining declarations. + + + + Partial member '{0}' may not have multiple implementing declarations. + Partial member '{0}' may not have multiple implementing declarations. + + Both partial member declarations, '{0}' and '{1}', must use the same tuple element names. 兩個部份成員宣告 '{0}' 和 '{1}' 都必須使用相同的 Tuple 元素名稱。 + + Partial member '{0}' must have a definition part. + Partial member '{0}' must have a definition part. + + + + Partial member '{0}' must have an implementation part. + Partial member '{0}' must have an implementation part. + + A partial member must be declared within a partial type 部分成員必須在部分型別內宣告 @@ -1712,6 +1802,11 @@ 部分成員宣告必須有相符的參考傳回值。 + + Both partial member declarations must have the same type. + Both partial member declarations must have the same type. + + Both partial member declarations must be unsafe or neither may be unsafe 兩個部分成員宣告皆必須為非受控,或者皆不為非受控 @@ -1782,11 +1877,6 @@ 兩個部分屬性宣告都必須是必要項,或者都不是必要項 - - Both partial property declarations must have the same type. - 兩個部分屬性宣告都必須有相同型別。 - - Property accessor '{0}' does not implement any accessor declared on the definition part 屬性存取子 '{0}' 未實作定義部分上宣告的任何存取子 @@ -1847,6 +1937,16 @@ '{0}': 只有在屬性或索引子同時具有 get 和 set 存取子時,才能在存取子上使用 'readonly' + + An extension container can have only one receiver parameter + An extension container can have only one receiver parameter + + + + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + '{0}': a receiver parameter cannot have the same name as an extension container type parameter + + The primary constructor conflicts with the synthesized copy constructor. 主要建構函式與合成的複製建構函式相衝突。 @@ -2257,6 +2357,16 @@ 類型 '{0}' 必須是公用,才能用為呼叫慣例。 + + Type parameter '{0}' has the same name as an extension parameter + Type parameter '{0}' has the same name as an extension parameter + + + + Type parameter '{0}' has the same name as an extension container type parameter + Type parameter '{0}' has the same name as an extension container type parameter + + Auto-implemented property '{0}' must be fully assigned before control is returned to the caller. Consider updating to language version '{1}' to auto-default the property. 在控制項傳回呼叫者之前,必須先完全指派自動實作屬性 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設屬性。 @@ -2267,6 +2377,11 @@ 在控制項傳回呼叫者之前,必須先完全指派欄位 '{0}'。請考慮更新至語言版本 '{1}' 以自動預設欄位。 + + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + The extended type '{0}' must reference all the type parameters declared by the extension, but type parameter '{1}' is not referenced. + + Unexpected parameter list. 未預期的參數清單。 @@ -2357,6 +2472,16 @@ 在指派 'this' 物件的所有欄位之前,無法使用該物件。請考慮更新語言版本 '{0}',以自動預設未指派的欄位。 + + 'value': an automatically-generated parameter name conflicts with an extension parameter name + 'value': an automatically-generated parameter name conflicts with an extension parameter name + + + + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + 'value': an automatically-generated parameter name conflicts with an extension type parameter name + + In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. In language version {0}, 'field' is a keyword within a property accessor. Rename the variable or use the identifier '@field' instead. @@ -2427,6 +2552,11 @@ 擴充屬性模式 + + extensions + extensions + + field keyword 欄位關鍵字 @@ -2522,6 +2652,11 @@ 插補中的新行 + + null conditional assignment + null conditional assignment + + overload resolution priority 多載解析優先順序 @@ -2537,6 +2672,11 @@ 參數集合 + + partial events and constructors + partial events and constructors + + positional fields in records 記錄中的位置欄位 @@ -2723,13 +2863,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 - The analyzer assembly references a newer version of the compiler than the currently running version. - 分析程式組件參考的編譯器版本比目前執行的版本新。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 分析程式組件參考的編譯器版本比目前執行的版本新。 @@ -3192,14 +3332,14 @@ 參數在 Lambda 中具有參數修飾元,但不在目標委派類型中。 - - Partial property declarations '{0}' and '{1}' have signature differences. - 部分屬性宣告 '{0}' 和 '{1}' 有簽章差異。 + + Partial member declarations '{0}' and '{1}' have signature differences. + Partial member declarations '{0}' and '{1}' have signature differences. - - Partial property declarations have signature differences. - 部分屬性宣告有簽章差異。 + + Partial member declarations have signature differences. + Partial member declarations have signature differences. @@ -5359,12 +5499,12 @@ strument:TestCoverage 產生檢測要收集 - Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable {0} '{1}' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the {0} as nullable, or safely handling the case where 'field' is null in the 'get' accessor. 退出建構函式時,不可為 Null {0} '{1}' 必須包含不可為 Null 值。請考慮新增 'required' 修飾元,或將 {0} 宣告為可為 Null,或新增 '[field: MaybeNull, AllowNull]' 屬性。 Similar diagnostic message as 'WRN_UninitializedNonNullableField' - Non-nullable property must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + Non-nullable property 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. 退出建構函式時,不可為 Null 屬性必須包含不可為 Null 值。請考慮新增 'required' 修飾元,或將屬性宣告為可為 Null,或新增 '[field: MaybeNull, AllowNull]' 屬性。 @@ -6709,8 +6849,8 @@ strument:TestCoverage 產生檢測要收集 - The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method or property return type. - 'partial' 修飾元只能直接出現在 'class'、'record'、'struct'、'interface' 或方法或屬性傳回型別之前。 + The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + 'partial' 修飾元只能直接出現在 'class'、'record'、'struct'、'interface' 或方法或屬性傳回型別之前。 @@ -8817,8 +8957,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Cannot define a new extension method because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? - 無法定義新的擴充方法,因為找不到編譯器的必要類型 '{0}'。是否遺漏了 System.Core.dll 的參考? + Cannot define a new extension because the compiler required type '{0}' cannot be found. Are you missing a reference to System.Core.dll? + 無法定義新的擴充方法,因為找不到編譯器的必要類型 '{0}'。是否遺漏了 System.Core.dll 的參考? @@ -8912,8 +9052,8 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep - Invalid token '{0}' in class, record, struct, or interface member declaration - 類別、記錄、結構或介面成員宣告中的語彙基元 '{0}' 無效 + Invalid token '{0}' in a member declaration + 類別、記錄、結構或介面成員宣告中的語彙基元 '{0}' 無效 diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 31372989a3f2c..bd8a95b57f441 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -15497,11 +15497,11 @@ class C {} "); var notAnalyzer = dir.CreateFile("random.txt"); - // not suppresssed + // not suppressed var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false); Assert.Contains("warning CS8034", output, StringComparison.Ordinal); - // supressed + // suppressed VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path, "/nowarn:CS8034" }, expectedWarningCount: 0, includeCurrentAssemblyAsAnalyzerReference: false); // elevated @@ -15521,11 +15521,11 @@ class C {} "); var notAnalyzer = dir.CreateFile("random.txt"); - // not suppresssed + // not suppressed var output = VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path }, expectedWarningCount: 1, includeCurrentAssemblyAsAnalyzerReference: false); Assert.Contains("warning CS8034", output, StringComparison.Ordinal); - // suppresssed via global analyzer config + // suppressed via global analyzer config VerifyOutput(dir, src, additionalFlags: new[] { "/analyzer:" + notAnalyzer.Path, "/analyzerConfig:" + globalconfig.Path }, includeCurrentAssemblyAsAnalyzerReference: false); } diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs index 26e0d88f23509..e65f4aca1a281 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs @@ -31078,7 +31078,7 @@ .locals init (int V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Class() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Class() { var source = @" using System; @@ -31219,7 +31219,7 @@ .locals init (T& V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Struct() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Struct() { var source = @" using System; @@ -31319,7 +31319,7 @@ .locals init (T& V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Class_Ref() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Class_Ref() { var source = @" using System; @@ -31461,7 +31461,7 @@ .locals init (T& V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Struct_Ref() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Struct_Ref() { var source = @" using System; @@ -31561,7 +31561,7 @@ .locals init (T& V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Class_Async_01() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Class_Async_01() { var source = @" using System; @@ -31856,7 +31856,7 @@ .locals init (int V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeExpression_Struct_Async_01() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeExpression_Struct_Async_01() { var source = @" using System; @@ -32026,7 +32026,7 @@ .locals init (int V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Class() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Class() { var source = @" using System; @@ -32201,7 +32201,7 @@ .locals init (T V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Struct() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Struct() { var source = @" using System; @@ -32318,7 +32318,7 @@ .locals init (System.Range V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Class_Ref() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Class_Ref() { var source = @" using System; @@ -32494,7 +32494,7 @@ .locals init (T V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Struct_Ref() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Struct_Ref() { var source = @" using System; @@ -32611,7 +32611,7 @@ .locals init (System.Range V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Class_Async_01() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Class_Async_01() { var source = @" using System; @@ -32946,7 +32946,7 @@ .locals init (int V_0, [ConditionalFact(typeof(CoreClrOnly))] [WorkItem(63221, "https://github.com/dotnet/roslyn/issues/63221")] - public void GenericTypeParameterAsReceiver_ImpicitRangeIndexer_RangeValue_Struct_Async_01() + public void GenericTypeParameterAsReceiver_ImplicitRangeIndexer_RangeValue_Struct_Async_01() { var source = @" using System; diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs index ce58d22354c8a..c96a7e29d4da4 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs @@ -1083,29 +1083,14 @@ static void G() {} Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default),// add new row ]); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), - ]); - } - else - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), - ]); - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(8, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), + ]); }) .Verify(); } @@ -1484,32 +1469,15 @@ [A7]void H() { } Handle(9, TableIndex.CustomAttribute), ]); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete - new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] - ]); - } - else - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete - new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 - ]); - - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // F [A2] delete + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // H [A5] delete + new CustomAttributeRow(Handle(9, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), // F [A1] -> [A2] + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)),// G [A3] -> [A4] + new CustomAttributeRow(Handle(10, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)),// G [A3] add with RowId 9 + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)),// H [A6] -> [A7] + ]); }) .AddGeneration( source: common + """ @@ -1551,32 +1519,15 @@ void G() { } Handle(11, TableIndex.CustomAttribute), ]); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 - ]); - } - else - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] - new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 - new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 - ]); - - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A4] delete + new CustomAttributeRow(Handle(0, TableIndex.MethodDef), Handle(0, TableIndex.MemberRef)), // G [A3] delete + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(5, TableIndex.MethodDef)), // H [A5] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(6, TableIndex.MethodDef)), // H [A6] + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(7, TableIndex.MethodDef)), // H [A7] add with RowId 10 + new CustomAttributeRow(Handle(11, TableIndex.MethodDef), Handle(8, TableIndex.MethodDef)), // H [A8] add with RowId 11 + ]); }) .AddGeneration( source: common + """ @@ -1744,23 +1695,11 @@ [A4] void G() { } Handle(5, TableIndex.CustomAttribute), ]); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] - new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] - ]); - } - else - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] - new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] - ]); - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(5, TableIndex.MethodDef), Handle(3, TableIndex.MethodDef)), // G: [A2] -> [A4] + new CustomAttributeRow(Handle(6, TableIndex.MethodDef), Handle(4, TableIndex.MethodDef)), // F: [A1] -> [A3] + ]); }) .Verify(); } @@ -3251,23 +3190,15 @@ class C g.VerifyTypeDefNames("E", "C", "D"); g.VerifyMethodDefNames(); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(5, TableIndex.MethodDef)), // X - new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(2, TableIndex.MethodDef)), // E.A - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(4, TableIndex.MethodDef)), // _x - new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(1, TableIndex.MethodDef)), // E - new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(3, TableIndex.MethodDef)), // C - new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(6, TableIndex.MethodDef)), // D - ]); - } - else - { - - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(5, TableIndex.MethodDef)), // X + new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(2, TableIndex.MethodDef)), // E.A + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(4, TableIndex.MethodDef)), // _x + new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(1, TableIndex.MethodDef)), // E + new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(3, TableIndex.MethodDef)), // C + new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(6, TableIndex.MethodDef)), // D + ]); g.VerifyEncLogDefinitions( [ @@ -3340,31 +3271,15 @@ class C g.VerifyTypeDefNames("E", "C", "D"); g.VerifyMethodDefNames(); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)),// X - new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x - new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E - new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C - new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D - ]); - } - else - { - g.VerifyCustomAttributes( - [ - new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E - new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C - new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D - new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x - new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)),// X - ]); - } + g.VerifyCustomAttributes( + [ + new CustomAttributeRow(Handle(1, TableIndex.Property), Handle(11, TableIndex.MethodDef)),// X + new CustomAttributeRow(Handle(2, TableIndex.Field), Handle(8, TableIndex.MethodDef)), // E.A + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(10, TableIndex.MethodDef)), // _x + new CustomAttributeRow(Handle(14, TableIndex.TypeDef), Handle(7, TableIndex.MethodDef)), // E + new CustomAttributeRow(Handle(15, TableIndex.TypeDef), Handle(9, TableIndex.MethodDef)), // C + new CustomAttributeRow(Handle(16, TableIndex.TypeDef), Handle(12, TableIndex.MethodDef)),// D + ]); g.VerifyEncLogDefinitions(new[] { @@ -6193,7 +6108,7 @@ void ValidateReplacedType(CompilationDifference diff, MetadataReader[] readers) Assert.Equal(generation, parentGeneration); Assert.Equal(newTypeDefHandle, parentHandle); - // attribute contructor should match + // attribute constructor should match var ctorHandle = aggregator.GetGenerationHandle(attribute.Constructor, out var ctorGeneration); Assert.Equal(0, ctorGeneration); Assert.Equal(attributeCtorDefHandle, ctorHandle); @@ -7459,33 +7374,16 @@ [B] static void M2<[A]T>() { } Handle(6, TableIndex.MethodSemantics), Handle(2, TableIndex.GenericParam)); - // https://github.com/dotnet/roslyn/issues/73513 - if (RuntimeUtilities.IsCoreClr9OrHigherRuntime) - { - CheckAttributes(reader1, - new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef))); - } - else - { - CheckAttributes(reader1, - new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), - new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), - new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef))); - } + CheckAttributes(reader1, + new CustomAttributeRow(Handle(1, TableIndex.GenericParam), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(2, TableIndex.Property), Handle(2, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(2, TableIndex.Event), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(3, TableIndex.Field), Handle(1, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(4, TableIndex.Field), Handle(12, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(12, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef)), + new CustomAttributeRow(Handle(14, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef)), + new CustomAttributeRow(Handle(15, TableIndex.MethodDef), Handle(11, TableIndex.MemberRef))); } /// diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs index 899fc155bff31..861f246abcc58 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/LocalStateTracing/LocalStateTracingTests.cs @@ -3207,7 +3207,7 @@ .locals init (Microsoft.CodeAnalysis.Runtime.LocalStoreTracker V_0, } [Fact] - public void Initializers_NoContructorBody() + public void Initializers_NoConstructorBody() { var source = WithHelpers(@" var _ = new C(); diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs index 26c01cb91bcdd..6771a4eb4c05c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/RuntimeProbing/ModuleCancellationTests.cs @@ -2783,4 +2783,28 @@ void G(T a, S b) {} var verifier = CompileAndVerify(source); AssertNotInstrumentedWithTokenLoad(verifier, "C.G(T, S, System.Threading.CancellationToken)"); } + + [Fact] + public void FlowPass() + { + var source = """ + using System.Threading; + using System.Collections.Generic; + + class C + { + IEnumerable F() + { + var x = G() as string; + yield return (x != null) ? 1 : 0; + } + + object G(CancellationToken token = default) + => ""; + } + """; + + // definite assignment flow pass doesn't fail + CompileAndVerify(source).VerifyDiagnostics(); + } } diff --git a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_WellKnownAttributes.cs b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_WellKnownAttributes.cs index d731b5cf5b410..cb4ef10c97def 100644 --- a/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_WellKnownAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit3/Attributes/AttributeTests_WellKnownAttributes.cs @@ -11311,6 +11311,45 @@ void local1() Assert.True(verifier.HasLocalsInit("C.g__local1|4_0")); } + [Theory] + [InlineData("[SkipLocalsInit]", "")] + [InlineData("", "[SkipLocalsInit]")] + public void SkipLocalsInit_PartialEventAccessor_ContainsLocalFunction(string defAttrs, string implAttrs) + { + // SkipLocalsInit applied to either part affects the event and nested functions + var source = $$""" + using System; + using System.Runtime.CompilerServices; + + public partial class C + { + {{defAttrs}} + partial event Action EventWithAttribute; + + {{implAttrs}} + partial event Action EventWithAttribute + { + add + { + int w = 1; + w = w + w + w + w; + + void local1() + { + int x = 1; + x = x + x + x + x; + } + } + remove { } + } + } + """; + + var verifier = CompileAndVerifyWithSkipLocalsInit(source); + Assert.False(verifier.HasLocalsInit("C.EventWithAttribute.add")); + Assert.False(verifier.HasLocalsInit("C.g__local1|0_0")); + } + [Fact] public void SkipLocalsInit_Class_ContainsLocalFunction() { diff --git a/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs b/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs index 971a72c210829..77f2542f3d244 100644 --- a/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs +++ b/src/Compilers/CSharp/Test/Emit3/Diagnostics/DiagnosticAnalyzerTests.AllInOne.cs @@ -36,6 +36,8 @@ public void DiagnosticAnalyzerAllInOne() missingSyntaxKinds.Add(SyntaxKind.CollectionExpression); missingSyntaxKinds.Add(SyntaxKind.ExpressionElement); missingSyntaxKinds.Add(SyntaxKind.SpreadElement); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Add to all-in-one + missingSyntaxKinds.Add(SyntaxKind.ExtensionDeclaration); var analyzer = new CSharpTrackingDiagnosticAnalyzer(); var options = new AnalyzerOptions(new[] { new TestAdditionalText() }.ToImmutableArray()); diff --git a/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs b/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs index da6e2ff2751c5..3a7a679a1ab01 100644 --- a/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Diagnostics/GetDiagnosticsTests.cs @@ -175,7 +175,7 @@ private void NonPartialMethod2() { } Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -224,7 +224,7 @@ partial void PartialMethod() { } Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -276,7 +276,7 @@ partial class Class Assert.True(eventQueue.Count > 0); bool compilationStartedFired; HashSet declaredSymbolNames, completedCompilationUnits; - Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out completedCompilationUnits)); + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); // Verify symbol declared events fired for all symbols declared in the first source file. Assert.True(compilationStartedFired); @@ -303,6 +303,130 @@ partial class Class AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); } + [Fact] + public void TestCompilationEventsForPartialEvent() + { + var source1 = @" +namespace N1 +{ + partial class Class + { + event System.Action NonPartialEvent1 { add { } remove { } } + partial event System.Action DefOnlyPartialEvent; + partial event System.Action ImplOnlyPartialEvent { add { } remove { } } + partial event System.Action PartialEvent1; + partial event System.Action PartialEvent2 { add { } remove { } } + } +} +"; + var source2 = @" +namespace N1 +{ + partial class Class + { + event System.Action NonPartialEvent2 { add { } remove { } } + partial event System.Action PartialEvent1 { add { } remove { } } + partial event System.Action PartialEvent2; + } +} +"; + + var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1", options: TestOptions.RegularPreview); + var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2", options: TestOptions.RegularPreview); + var eventQueue = new AsyncQueue(); + var compilation = CreateCompilationWithMscorlib461(new[] { tree1, tree2 }).WithEventQueue(eventQueue); + + // Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file. + var model = compilation.GetSemanticModel(tree1); + model.GetDiagnostics(tree1.GetRoot().FullSpan); + + Assert.True(eventQueue.Count > 0); + bool compilationStartedFired; + HashSet declaredSymbolNames, completedCompilationUnits; + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out declaredSymbolNames, out _, out completedCompilationUnits)); + + // Verify symbol declared events fired for all symbols declared in the first source file. + Assert.True(compilationStartedFired); + + // NB: NonPartialEvent2 is missing here because we only asked for diagnostics in tree1. + // PartialEvent2 is missing because it is the implementation part and that is removed (only the definition part is kept). + AssertEx.Equal([ + "", + "add_ImplOnlyPartialEvent", + "add_NonPartialEvent1", + "add_PartialEvent1", + "Class", + "DefOnlyPartialEvent", + "ImplOnlyPartialEvent", + "N1", + "NonPartialEvent1", + "PartialEvent1", + "remove_ImplOnlyPartialEvent", + "remove_NonPartialEvent1", + "remove_PartialEvent1", + ], declaredSymbolNames.OrderBy(name => name)); + + AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); + } + + [Fact] + public void TestCompilationEventsForPartialConstructor() + { + var source1 = @" +namespace N1 +{ + partial class Class + { + partial C(int a) { } // not partial 1 + partial C(int a, int b); // def only + partial C(int a, int b, int c) { } // impl only + partial C(int a, int b, int c, int d); // full partial with def first + partial C(int a, int b, int c, int d, int e) { } // full partial with impl first + } +} +"; + var source2 = @" +namespace N1 +{ + partial class Class + { + partial C(string a) { } // not partial 2 + partial C(int a, int b, int c, int d) { } // full partial with def first + partial C(int a, int b, int c, int d, int e); // full partial with impl first + } +} +"; + + var tree1 = CSharpSyntaxTree.ParseText(source1, path: "file1", options: TestOptions.RegularPreview); + var tree2 = CSharpSyntaxTree.ParseText(source2, path: "file2", options: TestOptions.RegularPreview); + var eventQueue = new AsyncQueue(); + var compilation = CreateCompilationWithMscorlib461(new[] { tree1, tree2 }).WithEventQueue(eventQueue); + + // Invoke SemanticModel.GetDiagnostics to force populate the event queue for symbols in the first source file. + var model = compilation.GetSemanticModel(tree1); + model.GetDiagnostics(tree1.GetRoot().FullSpan); + + Assert.True(eventQueue.Count > 0); + bool compilationStartedFired; + HashSet declaredSymbols, completedCompilationUnits; + Assert.True(DequeueCompilationEvents(eventQueue, out compilationStartedFired, out _, out declaredSymbols, out completedCompilationUnits)); + + // Verify symbol declared events fired for all symbols declared in the first source file. + Assert.True(compilationStartedFired); + + // NB: non partial 2 is missing here because we only asked for diagnostics in tree1 + AssertEx.Equal([ + "", + "N1", + "N1.Class", + "N1.Class..ctor(System.Int32 a)", + "N1.Class..ctor(System.Int32 a, System.Int32 b, System.Int32 c)", + "N1.Class..ctor(System.Int32 a, System.Int32 b, System.Int32 c, System.Int32 d)", + ], declaredSymbols.OrderBy(name => name, StringComparer.Ordinal)); + + AssertEx.Equal(["file1"], completedCompilationUnits.OrderBy(name => name)); + } + [Fact, WorkItem(8178, "https://github.com/dotnet/roslyn/issues/8178")] public void TestEarlyCancellation() { @@ -324,10 +448,11 @@ private void NonPartialMethod1() { } model.GetDiagnostics(tree.GetRoot().FullSpan); } - private static bool DequeueCompilationEvents(AsyncQueue eventQueue, out bool compilationStartedFired, out HashSet declaredSymbolNames, out HashSet completedCompilationUnits) + private static bool DequeueCompilationEvents(AsyncQueue eventQueue, out bool compilationStartedFired, out HashSet declaredSymbolNames, out HashSet declaredSymbols, out HashSet completedCompilationUnits) { compilationStartedFired = false; declaredSymbolNames = new HashSet(); + declaredSymbols = new HashSet(); completedCompilationUnits = new HashSet(); if (eventQueue.Count == 0) { @@ -348,7 +473,7 @@ private static bool DequeueCompilationEvents(AsyncQueue eventQ if (symbolDeclaredEvent != null) { var symbol = symbolDeclaredEvent.Symbol; - var added = declaredSymbolNames.Add(symbol.Name); + var added = declaredSymbolNames.Add(symbol.Name) & declaredSymbols.Add(symbol.ToTestDisplayString()); if (!added) { Assert.True(symbol.GetSymbol().IsPartialMember(), "Unexpected multiple symbol declared events for symbol " + symbol); diff --git a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs index d95d92200b540..4cf1db623c279 100644 --- a/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FieldKeywordTests.cs @@ -7,6 +7,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using System.Collections.Immutable; @@ -5604,16 +5605,13 @@ class C else { comp.VerifyEmitDiagnostics( - // (4,13): warning CS9264: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (4,13): warning CS9264: Non-nullable property 'P1' 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. // object P1 => field; Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "P1").WithArguments("property", "P1").WithLocation(4, 13), - // (5,13): warning CS9264: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (5,13): warning CS9264: Non-nullable property 'P2' 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. // object P2 { get => field; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "P2").WithArguments("property", "P2").WithLocation(5, 13), - // (6,13): warning CS9264: Non-nullable property 'P3' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. - // object P3 { set { field = value; } } - Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "P3").WithArguments("property", "P3").WithLocation(6, 13), - // (7,13): warning CS9264: Non-nullable property 'P4' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (7,13): warning CS9264: Non-nullable property 'P4' 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. // object P4 { get => field; set { field = value; } } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "P4").WithArguments("property", "P4").WithLocation(7, 13)); } @@ -5635,7 +5633,7 @@ class C // (4,19): warning CS8602: Dereference of a possibly null reference. // string? P1 => field.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(4, 19), - // (5,12): warning CS9264: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (5,12): warning CS9264: Non-nullable property 'P2' 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. // string P2 => field.ToString(); Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "P2").WithArguments("property", "P2").WithLocation(5, 12)); } @@ -5770,9 +5768,6 @@ class C // (8,51): warning CS8601: Possible null reference assignment. // object P5 { get; set { field = value; } } = MaybeNull(); Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "MaybeNull()").WithLocation(8, 51), - // (9,46): warning CS8601: Possible null reference assignment. - // object P6 { set { field = value; } } = MaybeNull(); - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "MaybeNull()").WithLocation(9, 46), // (14,9): warning CS8602: Dereference of a possibly null reference. // P1.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "P1").WithLocation(14, 9), @@ -5848,7 +5843,7 @@ public C() { } var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (6,12): 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 C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(6, 12)); } @@ -5868,7 +5863,7 @@ public C() { } var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (6,12): 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 C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(6, 12)); } @@ -5930,7 +5925,7 @@ static C() { } var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (6,12): 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. // static C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(6, 12)); } @@ -5950,7 +5945,7 @@ static C() { } var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (6,12): 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. // static C() { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(6, 12)); } @@ -6196,22 +6191,16 @@ class C public string Prop { get => field; - set => field = null; // 1 + set => field = null; } - public C() // 2 + public C() { } } """; var comp = CreateCompilation([source, MaybeNullAttributeDefinition, AllowNullAttributeDefinition]); - comp.VerifyEmitDiagnostics( - // (10,24): warning CS8625: Cannot convert null literal to non-nullable reference type. - // set => field = null; // 1 - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 24), - // (12,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. - // public C() // 2 - Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(12, 12)); + comp.VerifyEmitDiagnostics(); } [Fact] @@ -6242,7 +6231,7 @@ public C() // 2 // (10,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // set => field = null; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 24), - // (12,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (12,12): 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 C() // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(12, 12)); } @@ -6304,7 +6293,7 @@ public C() // 2 // (10,24): warning CS8601: Possible null reference assignment. // set => field = value; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "value").WithLocation(10, 24), - // (12,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (12,12): 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 C() // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(12, 12)); } @@ -6333,7 +6322,7 @@ public C() // 1 var comp = CreateCompilation([source, AllowNullAttributeDefinition]); comp.VerifyEmitDiagnostics( - // (11,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (11,12): 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 C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(11, 12)); } @@ -6361,7 +6350,7 @@ public C() // 1 var comp = CreateCompilation([source, AllowNullAttributeDefinition]); comp.VerifyEmitDiagnostics( - // (11,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (11,12): 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 C() // 1 Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(11, 12)); } @@ -6460,7 +6449,7 @@ public C() // 2 // (10,24): warning CS8601: Possible null reference assignment. // set => field = value; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "value").WithLocation(10, 24), - // (12,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (12,12): 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 C() // 2 Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(12, 12)); } @@ -6624,7 +6613,7 @@ class C var comp = CreateCompilation([source, NotNullAttributeDefinition]); comp.VerifyEmitDiagnostics( - // (7,20): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (7,20): 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 string? Prop { get; set => field = value; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 20)); } @@ -6646,7 +6635,7 @@ class C var comp = CreateCompilation([source, NotNullAttributeDefinition]); comp.VerifyEmitDiagnostics( - // (7,20): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (7,20): 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 string? Prop { get; set => field = value; } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(7, 20)); } @@ -6958,7 +6947,7 @@ public C() var comp = CreateCompilation([source, RequiredMemberAttribute, CompilerFeatureRequiredAttribute, SetsRequiredMembersAttribute]); comp.VerifyEmitDiagnostics( - // (9,12): 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 adding '[field: MaybeNull, AllowNull]' attributes. + // (9,12): 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 C() Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "Prop").WithLocation(9, 12)); } @@ -7194,10 +7183,7 @@ class C """; var comp = CreateCompilation([source, MaybeNullAttributeDefinition, AllowNullAttributeDefinition]); - comp.VerifyEmitDiagnostics( - // (6,19): warning CS9264: Non-nullable property 'Prop1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. - // public string Prop1 => field ??= "a"; // 1 - Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop1").WithArguments("property", "Prop1").WithLocation(6, 19)); + comp.VerifyEmitDiagnostics(); } // Based on NullableReferenceTypesTests.NotNull_Property_WithAssignment @@ -7347,83 +7333,83 @@ class C T P1 { get => field; - } = default; // 1 + } = default; [AllowNull] T P2 { get => field; - } = default; // 2 + } = default; // 1 [MaybeNull, AllowNull] T P3 { get => field; - } = default; // 3 + } = default; [MaybeNull] T P4 { get => field; set => field = value; - } = default; // 4 + } = default; [AllowNull] T P5 { get => field; - set => field = value; // 5 - } = default; // 6 + set => field = value; // 2 + } = default; // 3 [MaybeNull, AllowNull] T P6 { get => field; - set => field = value; // 7 - } = default; // 8 + set => field = value; + } = default; C([AllowNull]T t) { - P1 = t; // 9 - P2 = t; + P1 = t; + P2 = t; // 4 P3 = t; - P4 = t; // 10 + P4 = t; // 5 P5 = t; P6 = t; } }"; var comp = CreateCompilation(new[] { source, AllowNullAttributeDefinition, MaybeNullAttributeDefinition }); comp.VerifyDiagnostics( - // 0.cs(9,9): warning CS8601: Possible null reference assignment. - // } = default; // 1 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(9, 9), // 0.cs(15,9): warning CS8601: Possible null reference assignment. - // } = default; // 2 + // } = default; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(15, 9), - // 0.cs(21,9): warning CS8601: Possible null reference assignment. - // } = default; // 3 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(21, 9), - // 0.cs(28,9): warning CS8601: Possible null reference assignment. - // } = default; // 4 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(28, 9), // 0.cs(34,24): warning CS8601: Possible null reference assignment. - // set => field = value; // 5 + // set => field = value; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "value").WithLocation(34, 24), // 0.cs(35,9): warning CS8601: Possible null reference assignment. - // } = default; // 6 + // } = default; // 3 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(35, 9), - // 0.cs(41,24): warning CS8601: Possible null reference assignment. - // set => field = value; // 7 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "value").WithLocation(41, 24), - // 0.cs(42,9): warning CS8601: Possible null reference assignment. - // } = default; // 8 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(42, 9), - // 0.cs(46,14): warning CS8601: Possible null reference assignment. - // P1 = t; // 9 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(46, 14), + // 0.cs(47,14): warning CS8601: Possible null reference assignment. + // P2 = t; // 4 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(47, 14), // 0.cs(49,14): warning CS8601: Possible null reference assignment. - // P4 = t; // 10 - Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(49, 14)); + // P4 = t; // 5 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(49, 14) + ); + + var classC = comp.GetMember("C"); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.Annotated); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.NotAnnotated); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.Annotated); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.Annotated); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.NotAnnotated); + verify(classC.GetMember("k__BackingField"), NullableAnnotation.Annotated); + + void verify(SynthesizedBackingFieldSymbol field, NullableAnnotation expectedInferredAnnotation) + { + Assert.Equal(NullableAnnotation.NotAnnotated, field.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(expectedInferredAnnotation, field.GetInferredNullableAnnotation()); + } } // Based on RequiredMembersTests.RequiredMemberSuppressesNullabilityWarnings_ChainedConstructor_01. @@ -7476,7 +7462,7 @@ class C else { comp.VerifyEmitDiagnostics( - // (8,5): warning CS9264: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (8,5): warning CS9264: Non-nullable property 'P2' 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. // C(bool unused) { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "P2").WithLocation(8, 5), // (8,5): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. @@ -7531,10 +7517,10 @@ class C else { comp.VerifyEmitDiagnostics( - // (9,5): warning CS9264: Non-nullable property 'P5' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (9,5): warning CS9264: Non-nullable property 'P5' 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. // C(bool unused) { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "P5").WithLocation(9, 5), - // (9,5): warning CS9264: Non-nullable property 'P6' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier, or declaring the property as nullable, or adding '[field: MaybeNull, AllowNull]' attributes. + // (9,5): warning CS9264: Non-nullable property 'P6' 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. // C(bool unused) { } Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "C").WithArguments("property", "P6").WithLocation(9, 5), // (9,5): warning CS8618: Non-nullable property 'P4' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable. @@ -10737,5 +10723,1413 @@ struct S var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } + + [Fact] + public void Nullable_NotResilient_NotInitialized_01() + { + var source = """ + #nullable enable + class C + { + public string Prop { get => field; set => field = value; } // 1 + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,19): 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 string Prop { get => field; set => field = value; } // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(4, 19)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_NotInitialized_02() + { + var source = """ + #nullable enable + class C + { + public string Prop => field.ToString() ?? "a"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,19): 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 string Prop => field.ToString() ?? "a"; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(4, 19)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_NotInitialized_03() + { + var source = """ + #nullable enable + class C + { + public string Prop + { + get + { + string unrelated = null; + return field ??= "a"; + } + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,20): warning CS0219: The variable 'unrelated' is assigned but its value is never used + // string unrelated = null; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "unrelated").WithArguments("unrelated").WithLocation(8, 20), + // (8,32): warning CS8600: Converting null literal or possible null value to non-nullable type. + // string unrelated = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 32)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Getter_DifferentWarningsOccurWithEitherNullability() + { + var source = """ + #nullable enable + using System.Collections.Generic; + + class C + { + public string Prop + { + get + { + var list = M(field); + List li1 = list; + List li2 = list; + return field; + } + } + + static List M(T elem) => [elem]; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,19): 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 string Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(6, 19), + // (12,33): warning CS8619: Nullability of reference types in value of type 'List' doesn't match target type 'List'. + // List li2 = list; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "list").WithArguments("System.Collections.Generic.List", "System.Collections.Generic.List").WithLocation(12, 33)); + } + + [Fact] + public void NullResilience_GetterDoesNotUseField() + { + var source = """ + #nullable enable + + class C + { + public string Prop { get => "a"; set => field = value; } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,26): warning CS9266: The 'get' accessor of property 'C.Prop' should use 'field' because the other accessor is using it. + // public string Prop { get => "a"; set => field = value; } + Diagnostic(ErrorCode.WRN_AccessorDoesNotUseBackingField, "get").WithArguments("get", "C.Prop").WithLocation(5, 26)); + + var field = comp.GetMember("C.k__BackingField"); + + // We could further scope this by saying: do not infer if the getter specifically doesn't use 'field', + // regardless of whether the setter uses it, and perhaps skip some additional work. + // However, this is considered a pathological case. + Assert.True(field.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, field.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, field.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_Initialized_01() + { + var source = """ + #nullable enable + class C + { + public string Prop { get => field; set => field = value; } = "a"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_Initialized_02() + { + var source = """ + #nullable enable + class C + { + public C() { Prop = "a"; } + public string Prop { get => field; set => field = value; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Theory] + [InlineData("??=")] + [InlineData("??")] + public void Nullable_Resilient_NotInitialized_01(string op) + { + var source = $$""" + #nullable enable + class C + { + public string Prop => field {{op}} "a"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Initialized_01() + { + var source = """ + #nullable enable + class C + { + public string Prop { get => field ??= "a"; } = "b"; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Initialized_02() + { + var source = """ + #nullable enable + class C + { + public C() { Prop = "b"; } + public string Prop { get => field ?? "a"; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Initialized_03() + { + var source = """ + #nullable enable + class C + { + public C() + { + Prop = null; + } + + public string Prop { get => field ??= "a"; } = null; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Initialized_04() + { + // This case is a bit funky. The inference does not affect signature of the setter. + // In general, we don't want an inference to affect shape of a public API. + // But, it's conceivable to want to assign a possible null value here, which gets further coalesced into a non-null in the getter. + var source = """ + #nullable enable + class C + { + public C() + { + Prop = null; // 1 + } + + public string Prop { get => field ??= "a"; set; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (6,16): warning CS8625: Cannot convert null literal to non-nullable reference type. + // Prop = null; // 1 + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(6, 16)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Initialized_05() + { + // Suggested fix for the nullable warning in Nullable_Resilient_Initialized_04 + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + public C() + { + Prop = null; + } + + [AllowNull] + public string Prop { get => field ??= "a"; set; } + } + """; + + var comp = CreateCompilation([source, AllowNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_UseInSetter_01() + { + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field ??= "a"; + set => field = field.ToString(); + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,24): warning CS8602: Dereference of a possibly null reference. + // set => field = field.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(8, 24)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_UseInSetter_02() + { + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field ??= "a"; + set => field = null; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_NotNullProperty_01() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + [NotNull] + public string? Prop + { + get => field ??= "a"; + set => field = null; + } + } + """; + + var comp = CreateCompilation([source, NotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.False(prop.BackingField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_NotNullProperty_02() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + [NotNull] + public T Prop + { + get => field ??= default!; + set => field = default; + } + } + """; + + var comp = CreateCompilation([source, NotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_NotNullProperty_03() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C(T fallback) + { + [NotNull] + public T Prop + { + get => field ??= fallback; + set => field = default; + } + } + """; + + var comp = CreateCompilation([source, NotNullAttributeDefinition]); + comp.VerifyEmitDiagnostics( + // (9,16): warning CS8607: A possible null value may not be used for a type marked with [NotNull] or [DisallowNull] + // get => field ??= fallback; + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "field ??= fallback").WithLocation(9, 16)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_Generic_01() + { + var source = """ + #nullable enable + + class C(T fallback) + { + public T Prop + { + get => field ??= fallback; + set => field = default; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_Generic_01() + { + var source = """ + #nullable enable + + class C + { + public T Prop + { + get => field; + set => field = value; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,14): 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 T Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 14)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_UseInSetter_01() + { + // Setter analysis is not yet implemented. + // https://github.com/dotnet/roslyn/issues/77215 + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field; + set => field = field.ToString(); + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,19): 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 string Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_UseInSetter_02() + { + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field; + set => field = null; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,19): 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 string Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19), + // (8,24): warning CS8625: Cannot convert null literal to non-nullable reference type. + // set => field = null; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 24)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Theory] + [InlineData("#nullable disable")] + [InlineData("#nullable disable warnings")] + [InlineData("#pragma warning disable 8603 // Possible null reference return.")] + public void Nullable_NotResilient_AccessorWarningsAreDisabled_01(string directive) + { + var source = $$""" + #nullable enable + + class C + { + public string Prop + { + {{directive}} + get => field; + set => field = null; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Theory] + [InlineData("#nullable disable")] + [InlineData("#nullable disable warnings")] + [InlineData("#pragma warning disable")] + [InlineData("#pragma warning disable 8603 // Possible null reference return.")] + public void Nullable_Resilient_ChainedConstructor_01(string directive) + { + // Since the backing field is nullable, a chained constructor isn't expected to put it into non-null state. + // The property has maybe-null state in "caller" constructor since it shares a slot with the field. + var source = $$""" + #nullable enable + + class C + { + public C(bool ignored) { Prop = "a"; } + public C() : this(false) + { + Prop.ToString(); // 1 + } + + public string Prop + { + {{directive}} + get => field; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,9): warning CS8602: Dereference of a possibly null reference. + // Prop.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(8, 9)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_ChainedConstructor_02() + { + var source = $$""" + #nullable enable + + class C + { + public C(bool ignored) { Prop = "a"; } + public C() : this(false) + { + Prop.ToString(); // 1 + } + + public string Prop + { + get => field!; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (8,9): warning CS8602: Dereference of a possibly null reference. + // Prop.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(8, 9)); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Explicit_ChainedConstructor_01() + { + // Behavior is consistent with inferred nullable backing field compared with explicitly attributed backing field. + var source = $$""" + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + public C(bool ignored) { Prop = "a"; } + public C() : this(false) + { + Prop.ToString(); // 1 + } + + [field: MaybeNull] + public string Prop + { + get => field!; + } + } + """; + + var comp = CreateCompilation([source, MaybeNullAttributeDefinition]); + comp.VerifyEmitDiagnostics( + // (9,9): warning CS8602: Dereference of a possibly null reference. + // Prop.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(9, 9)); + + var prop = comp.GetMember("C.Prop"); + Assert.False(prop.BackingField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_NotResilient_ChainedConstructor_01() + { + // Backing field is non-nullable, so chaining a constructor puts it into non-null state. + var source = $$""" + #nullable enable + + class C + { + public C(bool ignored) { Prop = "a"; } + public C() : this(false) + { + Prop.ToString(); + } + + public string Prop + { + get => field; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_NullForgivingOperator() + { + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field!; + set => field = null; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.Annotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Theory] + [CombinatorialData] + public void Nullable_NotResilient_DiagnosticSuppressor(bool useSuppressor) + { + // DiagnosticSuppressors don't affect the inferred nullability of the backing field. + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field; + set => field = null; + } + } + """; + + var comp = CreateCompilation(source); + if (useSuppressor) + { + // The 8603 is a "hypothetical" diagnostic which occurs only internally in the compiler. + // We don't run DiagnosticSuppressors in that context. + // So, it's not clear that this suppressor would ever do anything. + // Still, it seems useful to express our intent with this test, that DiagnosticSuppressors don't have an effect. + comp = comp.VerifySuppressedDiagnostics( + [new CommonDiagnosticAnalyzers.DiagnosticSuppressorForId("CS8603"), new CommonDiagnosticAnalyzers.DiagnosticSuppressorForId("CS8625")], + expected: [Diagnostic("CS8625", "null", isSuppressed: true).WithLocation(8, 24)]); + } + + comp.VerifyEmitDiagnostics( + // (5,19): 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 string Prop + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19), + // (8,24): warning CS8625: Cannot convert null literal to non-nullable reference type. + // set => field = null; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 24)); + var prop = comp.GetMember("C.Prop"); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, prop.BackingField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_Resilient_AnnotationInMetadata() + { + // Inferred nullability is not used in metadata. + var source = """ + #nullable enable + + class C + { + public string Prop + { + get => field ??= "a"; + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + + var comp1 = CreateCompilation("", references: [comp0.EmitToImageReference()], options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + var metadataField = comp1.GetMember("C.k__BackingField"); + Assert.Equal(NullableAnnotation.NotAnnotated, metadataField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Nullable_DisallowNullField_NullableValueType() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + public C() + { + Prop = null; // 1 + Prop = 0; + } + + [field: DisallowNull] + public int? Prop + { + get => field; + } + } + """; + + var comp0 = CreateCompilation([source, DisallowNullAttributeDefinition]); + comp0.VerifyEmitDiagnostics( + // (8,16): warning CS8607: A possible null value may not be used for a type marked with [NotNull] or [DisallowNull] + // Prop = null; // 1 + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "null").WithLocation(8, 16)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.False(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.TypeWithAnnotations.NullableAnnotation); + Assert.Equal("System.Int32?", sourceField.TypeWithAnnotations.ToTestDisplayString()); + } + + [Fact] + public void NullableWarningOnFieldInBothCases() + { + var source = """ + #nullable enable + + class C + { + public C() + { + Prop = null; + } + + public string Prop + { + get + { + field = null; + field.ToString(); // 1 + return "a"; + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (15,13): warning CS8602: Dereference of a possibly null reference. + // field.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(15, 13)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void StaticPropAndConstructor_Resilient_01() + { + var source = """ + #nullable enable + + class C + { + public static string Prop => field ?? "a"; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void StaticPropAndConstructor_NotResilient_01() + { + var source = """ + #nullable enable + + class C + { + public static string Prop => field; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (5,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 static string Prop => field; + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 26)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void StaticPropAndConstructor_NotResilient_02() + { + var source = """ + #nullable enable + + class C + { + static C() + { + Prop = "a"; + } + public static string Prop => field; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void StaticPropAndConstructor_NotResilient_03() + { + var source = """ + #nullable enable + + class C + { + public static string Prop { get => field; } = "a"; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Ref_NotResilient_01() + { + var source = """ + #nullable enable + + class C + { + public static void M(ref string s) { } + public string Prop // 1 + { + get + { + M(ref field); + return field; + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (6,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(6, 19)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Ref_Resilient_01() + { + // "Resilient" in the sense that same warnings occur whether field is nullable or not. + var source = """ + #nullable enable + + class C + { + public static void M(ref string? s) { } + public string Prop + { + get + { + M(ref field); + return field; // 1 + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (11,20): warning CS8603: Possible null reference return. + // return field; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "field").WithLocation(11, 20)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Ref_Resilient_02() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + public static void M([NotNull] ref string? s) { s = "a"; } + public string Prop + { + get + { + M(ref field); + return field; + } + } + } + """; + + var comp0 = CreateCompilation([source, NotNullAttributeDefinition]); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Ref_Resilient_03() + { + // This is an interesting case. + // Diagnostic (1) is reported the same way in both cases, but they actually refer to different things. + // For the 'string? field' case, it refers to the assignment of 'field' to 'ref string s' on the way in. + // For the 'string field' case, it refers to the '[MaybeNull]' assignment to 'field' on the way out. + + // This is something that might be simplified by saying null-resilience decides initial flow state rather than annotation. + // Then, maybe-null going into the field would not produce a warning in both cases. + // We would then conclude that the property is not-null-resilient, due to the 'M(ref field)' call resulting in a warning only when initial state is maybe-null. + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + class C + { + public static void M([MaybeNull] ref string s) { } + public string Prop + { + get + { + M(ref field); // 1 + return field; // 2 + } + } + } + """; + var comp0 = CreateCompilation([source, MaybeNullAttributeDefinition]); + comp0.VerifyEmitDiagnostics( + // (11,19): warning CS8601: Possible null reference assignment. + // M(ref field); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "field").WithLocation(11, 19), + // (12,20): warning CS8603: Possible null reference return. + // return field; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "field").WithLocation(12, 20)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void LocalFunction_NullResilience_01() + { + // Is null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop + { + get + { + return local(); + string local() => field ?? "a"; + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void LocalFunction_NullResilient_02() + { + // Not null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop // 1 + { + get + { + return local(); + string local() => field; + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (5,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void LocalFunction_NullResilient_03() + { + // Local function is not called, but, the warning in the local function only occurs when field is nullable. + var source = """ + #nullable enable + + class C + { + public string Prop // 1 + { + get + { + return field ?? "a"; + void local() => field.ToString(); // 2 + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (5,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19), + // (10,18): warning CS8321: The local function 'local' is declared but never used + // void local() => field.ToString(); // 2 + Diagnostic(ErrorCode.WRN_UnreferencedLocalFunction, "local").WithArguments("local").WithLocation(10, 18)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void LocalFunction_NullResilience_04() + { + // Is null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop + { + get + { + field ??= "a"; + local(); + return field; + void local() => field.ToString(); + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void LocalFunction_NullResilience_05() + { + // Not null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop // 1 + { + get + { + local(); + field ??= "a"; + return field; + void local() => field.ToString(); + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (5,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Lambda_NullResilience_01() + { + // Null-resilient + var source = """ + #nullable enable + using System; + + class C + { + public string Prop // 1 + { + get + { + Func a = () => field ?? "a"; + return a(); + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Lambda_NullResilience_02() + { + // Not null-resilient + var source = """ + #nullable enable + using System; + + class C + { + public string Prop // 1 + { + get + { + Func a = () => field; + return a(); + } + } + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (6,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(6, 19)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void AsTargetTypedOperand_01() + { + // Not null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop // 1 + { + get + { + var arr = M([field]); + return arr[0]; + } + } + + public static T[] M(T[] arr) => arr; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics( + // (5,19): 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 string Prop // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableBackingField, "Prop").WithArguments("property", "Prop").WithLocation(5, 19)); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void AsTargetTypedOperand_02() + { + // Null-resilient + var source = """ + #nullable enable + + class C + { + public string Prop + { + get + { + var arr = M([field ?? "a"]); + return arr[0]; + } + } + + public static T[] M(T[] arr) => arr; + } + """; + + var comp0 = CreateCompilation(source); + comp0.VerifyEmitDiagnostics(); + + var sourceField = comp0.GetMember("C.k__BackingField"); + Assert.True(sourceField.InfersNullableAnnotation); + Assert.Equal(NullableAnnotation.Annotated, sourceField.GetInferredNullableAnnotation()); + Assert.Equal(NullableAnnotation.NotAnnotated, sourceField.TypeWithAnnotations.NullableAnnotation); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs index f4004a3170e99..65462a8c3034a 100644 --- a/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/FirstClassSpanTests.cs @@ -4937,9 +4937,9 @@ static class C """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,5): error CS1503: Argument 1: cannot convert from 'method group' to 'System.Func' + // (4,7): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // C.R(a.M); - Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(4, 5), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(4, 7), // (5,10): error CS1929: 'int[]' does not contain a definition for 'M' and the best extension method overload 'C.M(Span, int)' requires a receiver of type 'System.Span' // C.R(x => a.M(x)); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(5, 10)); @@ -4955,6 +4955,37 @@ static class C CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); } + [Fact] + public void Nameof_MethodGroup() + { + var source = """ + using System; + + var a = new[] { 1, 2, 3 }; + _ = nameof(a.M); + + static class C + { + public static int M(this Span x, int y) => x[y]; + } + """; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (4,14): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // _ = nameof(a.M); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(4, 14)); + + var expectedDiagnostics = new[] + { + // (4,12): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // _ = nameof(a.M); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "a.M").WithLocation(4, 12) + }; + + CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilationWithSpanAndMemoryExtensions(source).VerifyDiagnostics(expectedDiagnostics); + } + [Fact] public void Conversion_Array_Span_Implicit_MethodGroup_ExtensionMethodReceiver_Inferred() { @@ -4973,9 +5004,9 @@ static class C """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,10): error CS8917: The delegate type could not be inferred. + // (4,12): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // var d1 = a.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "a.M").WithLocation(4, 10), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(4, 12), // (5,10): error CS8917: The delegate type could not be inferred. // var d2 = x => a.M(x); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(5, 10), @@ -5027,12 +5058,12 @@ static class C """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,5): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // (4,7): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // C.R(a.M); - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "a.M").WithArguments("int[]", "M").WithLocation(4, 5), - // (5,5): error CS1503: Argument 1: cannot convert from 'method group' to 'System.Func' + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(4, 7), + // (5,7): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // C.R(a.M); - Diagnostic(ErrorCode.ERR_BadArgType, "a.M").WithArguments("1", "method group", "System.Func").WithLocation(5, 5), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(5, 7), // (6,12): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // C.R(x => a.M(x)); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(6, 12), @@ -5077,27 +5108,27 @@ static class C """; CreateCompilationWithSpanAndMemoryExtensions(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( - // (4,10): error CS8917: The delegate type could not be inferred. + // (4,12): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // var d1 = a.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "a.M").WithLocation(4, 10), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(4, 12), // (5,10): error CS8917: The delegate type could not be inferred. // var d2 = x => a.M(x); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(5, 10), // (6,23): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // var d3 = (int x) => a.M(x); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(6, 23), - // (7,10): error CS8917: The delegate type could not be inferred. + // (7,12): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // var d4 = a.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "a.M").WithLocation(7, 10), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(7, 12), // (8,10): error CS8917: The delegate type could not be inferred. // var d5 = x => a.M(x); Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "x => a.M(x)").WithLocation(8, 10), // (9,21): error CS1929: 'int[]' does not contain a definition for 'M' and the best extension method overload 'C.M(Span, int)' requires a receiver of type 'System.Span' // var d6 = (int x) => a.M(x); Diagnostic(ErrorCode.ERR_BadInstanceArgType, "a").WithArguments("int[]", "M", "C.M(System.Span, int)", "System.Span").WithLocation(9, 21), - // (10,21): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) + // (10,23): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // Func d7 = a.M; - Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "a.M").WithArguments("int[]", "M").WithLocation(10, 21), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(10, 23), // (11,28): error CS1061: 'int[]' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int[]' could be found (are you missing a using directive or an assembly reference?) // Func d8 = x => a.M(x); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int[]", "M").WithLocation(11, 28)); diff --git a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTestBase.cs b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTestBase.cs index 928ebdceec7b5..064bbfa34fd2b 100644 --- a/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTestBase.cs +++ b/src/Compilers/CSharp/Test/Emit3/FlowAnalysis/FlowTestBase.cs @@ -44,6 +44,26 @@ internal ImmutableArray FlowDiagnostics(CSharpCompilation compilatio return flowDiagnostics.ToReadOnlyAndFree().Diagnostics; } + protected static void VerifyDataFlowAnalysis(string expected, DataFlowAnalysis result) + { + var actual = $$""" +VariablesDeclared: {{GetSymbolNamesJoined(result.VariablesDeclared)}} +AlwaysAssigned: {{GetSymbolNamesJoined(result.AlwaysAssigned)}} +Captured: {{GetSymbolNamesJoined(result.Captured)}} +CapturedInside: {{GetSymbolNamesJoined(result.CapturedInside)}} +CapturedOutside: {{GetSymbolNamesJoined(result.CapturedOutside)}} +DataFlowsIn: {{GetSymbolNamesJoined(result.DataFlowsIn)}} +DataFlowsOut: {{GetSymbolNamesJoined(result.DataFlowsOut)}} +DefinitelyAssignedOnEntry: {{GetSymbolNamesJoined(result.DefinitelyAssignedOnEntry)}} +DefinitelyAssignedOnExit: {{GetSymbolNamesJoined(result.DefinitelyAssignedOnExit)}} +ReadInside: {{GetSymbolNamesJoined(result.ReadInside)}} +ReadOutside: {{GetSymbolNamesJoined(result.ReadOutside)}} +WrittenInside: {{GetSymbolNamesJoined(result.WrittenInside)}} +WrittenOutside: {{GetSymbolNamesJoined(result.WrittenOutside)}} +"""; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, actual); + } + private IEnumerable AllMethods(Symbol symbol) { switch (symbol.Kind) diff --git a/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs new file mode 100644 index 0000000000000..cbe87ebb75e9e --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/PartialEventsAndConstructorsTests.cs @@ -0,0 +1,3434 @@ +// 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.Reflection; +using System.Runtime.InteropServices; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public sealed class PartialEventsAndConstructorsTests : CSharpTestBase +{ + [Fact] + public void ReturningPartialType_LocalFunction_InMethod() + { + var source = """ + class @partial + { + static void Main() + { + System.Console.Write(F().GetType().Name); + partial F() => new(); + } + } + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: "partial").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (5,30): error CS0103: The name 'F' does not exist in the current context + // System.Console.Write(F().GetType().Name); + Diagnostic(ErrorCode.ERR_NameNotInContext, "F").WithArguments("F").WithLocation(5, 30), + // (5,50): error CS1513: } expected + // System.Console.Write(F().GetType().Name); + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 50), + // (6,17): error CS1520: Method must have a return type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_MemberNeedsType, "F").WithLocation(6, 17), + // (6,17): error CS0751: A partial member must be declared within a partial type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(6, 17), + // (6,17): error CS9276: Partial member 'partial.partial()' must have a definition part. + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("partial.partial()").WithLocation(6, 17), + // (6,24): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // partial F() => new(); + Diagnostic(ErrorCode.ERR_IllegalStatement, "new()").WithLocation(6, 24), + // (8,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(8, 1) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void ReturningPartialType_LocalFunction_TopLevel() + { + var source = """ + System.Console.Write(F().GetType().Name); + partial F() => new(); + class @partial; + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: "partial").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (1,22): error CS0103: The name 'F' does not exist in the current context + // System.Console.Write(F().GetType().Name); + Diagnostic(ErrorCode.ERR_NameNotInContext, "F").WithArguments("F").WithLocation(1, 22), + // (2,9): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // partial F() => new(); + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "F").WithLocation(2, 9), + // (2,10): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // partial F() => new(); + Diagnostic(ErrorCode.ERR_IllegalStatement, "() => new()").WithLocation(2, 10) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void ReturningPartialType_Method() + { + var source = """ + class C + { + partial F() => new(); + static void Main() + { + System.Console.Write(new C().F().GetType().Name); + } + } + class @partial; + """; + CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: "partial").VerifyDiagnostics(); + + var expectedDiagnostics = new[] + { + // (3,13): error CS1520: Method must have a return type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_MemberNeedsType, "F").WithLocation(3, 13), + // (3,13): error CS0751: A partial member must be declared within a partial type + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(3, 13), + // (3,13): error CS9276: Partial member 'C.C()' must have a definition part. + // partial F() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("C.C()").WithLocation(3, 13), + // (3,20): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // partial F() => new(); + Diagnostic(ErrorCode.ERR_IllegalStatement, "new()").WithLocation(3, 20), + // (6,38): error CS1061: 'C' does not contain a definition for 'F' and no accessible extension method 'F' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(new C().F().GetType().Name); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "F").WithArguments("C", "F").WithLocation(6, 38) + }; + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void ReturningPartialType_Method_CouldBePartialConstructor() + { + var source = """ + class C + { + partial F() { } + partial C() { } + } + """; + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial F() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 5), + // (3,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial F() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(3, 5), + // (3,13): error CS0161: 'C.F()': not all code paths return a value + // partial F() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "F").WithArguments("C.F()").WithLocation(3, 13), + // (4,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(4, 5), + // (4,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(4, 5), + // (4,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(4, 13), + // (4,13): error CS0161: 'C.C()': not all code paths return a value + // partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void ReturningPartialType_Method_Escaped() + { + var source = """ + class C + { + @partial F() { } + @partial C() { } + } + """; + + var expectedDiagnostics = new[] + { + // (3,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // @partial F() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@partial").WithArguments("partial").WithLocation(3, 5), + // (3,14): error CS0161: 'C.F()': not all code paths return a value + // @partial F() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "F").WithArguments("C.F()").WithLocation(3, 14), + // (4,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // @partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "@partial").WithArguments("partial").WithLocation(4, 5), + // (4,14): error CS0542: 'C': member names cannot be the same as their enclosing type + // @partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(4, 14), + // (4,14): error CS0161: 'C.C()': not all code paths return a value + // @partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(4, 14) + }; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void LangVersion() + { + var source = """ + partial class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial C(); + partial C() { } + } + """; + + CreateCompilation(source, parseOptions: TestOptions.Regular13).VerifyDiagnostics( + // (3,33): error CS8703: The modifier 'partial' is not valid for this item in C# 13.0. Please use language version 'preview' or greater. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "E").WithArguments("partial", "13.0", "preview").WithLocation(3, 33), + // (5,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C(); + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(5, 5), + // (5,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(5, 5), + // (5,13): error CS0501: 'C.C()' must declare a body because it is not marked abstract, extern, or partial + // partial C(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "C").WithArguments("C.C()").WithLocation(5, 13), + // (5,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C(); + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(5, 13), + // (6,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // partial C() { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(6, 5), + // (6,5): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // partial C() { } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(6, 5), + // (6,13): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(6, 13), + // (6,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(6, 13), + // (6,13): error CS0161: 'C.C()': not all code paths return a value + // partial C() { } + Diagnostic(ErrorCode.ERR_ReturnExpected, "C").WithArguments("C.C()").WithLocation(6, 13)); + + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(); + CreateCompilation(source).VerifyDiagnostics(); + } + + [Theory, CombinatorialData] + public void PartialLast([CombinatorialValues("", "public")] string modifier) + { + var source = $$""" + partial class C + { + {{modifier}} + partial event System.Action E; + {{modifier}} + partial event System.Action E { add { } remove { } } + {{modifier}} + partial C(); + {{modifier}} + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact] + public void PartialNotLast() + { + var source = """ + partial class C + { + partial public event System.Action E; + partial public event System.Action E { add { } remove { } } + partial public C(); + partial public C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5), + // (5,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(5, 5), + // (6,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial public C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(6, 5)); + } + + [Fact] + public void PartialAsType() + { + var source = """ + partial class C + { + partial C() => new partial(); + } + + class @partial; + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9276: Partial member 'C.C()' must have a definition part. + // partial C() => new partial(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(3, 13)); + } + + [Fact] + public void MissingImplementation() + { + var source = """ + partial class C + { + partial event System.Action E; + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9275: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,13): error CS9275: Partial member 'C.C()' must have an implementation part. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void MissingDefinition() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9276: Partial member 'C.E' must have a definition part. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,13): error CS9276: Partial member 'C.C()' must have a definition part. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(4, 13)); + } + + [Fact] + public void DuplicateDefinition() + { + var source = """ + partial class C + { + partial event System.Action E, F; + partial event System.Action E; + partial event System.Action F; + partial C(); + partial C(); + + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9277: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (5,33): error CS9277: Partial member 'C.F' may not have multiple defining declarations. + // partial event System.Action F; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "F").WithArguments("C.F").WithLocation(5, 33), + // (5,33): error CS0102: The type 'C' already contains a definition for 'F' + // partial event System.Action F; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "F").WithArguments("C", "F").WithLocation(5, 33), + // (7,13): error CS9277: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(7, 13), + // (7,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 13)); + } + + [Fact] + public void DuplicateImplementation() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + + partial event System.Action E; + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9278: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (6,13): error CS9278: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (8,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(8, 33), + // (9,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(9, 13)); + } + + [Fact] + public void DuplicateDeclarations_01() + { + var source = """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + + partial event System.Action E; + partial event System.Action E; + partial C(); + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9278: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (6,13): error CS9278: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (8,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(8, 33), + // (9,33): error CS9277: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(9, 33), + // (9,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(9, 33), + // (10,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(10, 13), + // (11,13): error CS9277: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(11, 13), + // (11,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(11, 13)); + } + + [Fact] + public void DuplicateDeclarations_02() + { + var source = """ + partial class C + { + partial event System.Action E; + partial void add_E(System.Action value); + partial void remove_E(System.Action value); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9275: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): error CS0082: Type 'C' already reserves a member called 'add_E' with the same parameter types + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_MemberReserved, "E").WithArguments("add_E", "C").WithLocation(3, 33), + // (3,33): error CS0082: Type 'C' already reserves a member called 'remove_E' with the same parameter types + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_MemberReserved, "E").WithArguments("remove_E", "C").WithLocation(3, 33)); + } + + [Fact] + public void DuplicateDeclarations_03() + { + var source = """ + partial class C + { + partial event System.Action E; + partial event System.Action E; + partial C(); + partial C(); + + partial event System.Action E { add { } remove { } } + partial event System.Action E { add { } remove { } } + partial C() { } + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS9277: Partial member 'C.E' may not have multiple defining declarations. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (6,13): error CS9277: Partial member 'C.C()' may not have multiple defining declarations. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateDefinition, "C").WithArguments("C.C()").WithLocation(6, 13), + // (6,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(6, 13), + // (9,33): error CS9278: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(9, 33), + // (9,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(9, 33), + // (11,13): error CS9278: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(11, 13), + // (11,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(11, 13)); + } + + [Fact] + public void DuplicateDeclarations_04() + { + var source = """ + using System; + partial class C + { + partial event Action E; + partial event Action E { add { } } + partial event Action E { remove { } } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics( + // (5,26): error CS0065: 'C.E': event property must have both add and remove accessors + // partial event Action E { add { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E").WithLocation(5, 26), + // (6,26): error CS0065: 'C.E': event property must have both add and remove accessors + // partial event Action E { remove { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E").WithLocation(6, 26), + // (6,26): error CS9278: Partial member 'C.E' may not have multiple implementing declarations. + // partial event Action E { remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(6, 26), + // (6,26): error CS0102: The type 'C' already contains a definition for 'E' + // partial event Action E { remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(6, 26)); + + var events = comp.GetMembers("C.E"); + Assert.Equal(2, events.Length); + + var e1 = (SourceEventSymbol)events[0]; + Assert.True(e1.IsPartialDefinition); + AssertEx.Equal("event System.Action C.E", e1.ToTestDisplayString()); + AssertEx.Equal("event System.Action C.E", e1.PartialImplementationPart.ToTestDisplayString()); + + var e2 = (SourceEventSymbol)events[1]; + Assert.True(e2.IsPartialImplementation); + AssertEx.Equal("event System.Action C.E", e2.ToTestDisplayString()); + Assert.Null(e2.PartialDefinitionPart); + } + + [Fact] + public void EventInitializer_Single() + { + var source = """ + partial class C + { + partial event System.Action E = null; + partial event System.Action E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9279: 'C.E': partial event cannot have initializer + // partial event System.Action E = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): warning CS0414: The field 'C.E' is assigned but its value is never used + // partial event System.Action E = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "E").WithArguments("C.E").WithLocation(3, 33)); + } + + [Fact] + public void EventInitializer_Multiple_01() + { + var source = """ + partial class C + { + partial event System.Action E, F = null; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,36): error CS9279: 'C.F': partial event cannot have initializer + // partial event System.Action E, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "F").WithArguments("C.F").WithLocation(3, 36), + // (3,36): warning CS0414: The field 'C.F' is assigned but its value is never used + // partial event System.Action E, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "F").WithArguments("C.F").WithLocation(3, 36)); + } + + [Fact] + public void EventInitializer_Multiple_02() + { + var source = """ + partial class C + { + partial event System.Action E = null, F = null; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9279: 'C.E': partial event cannot have initializer + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,33): warning CS0414: The field 'C.E' is assigned but its value is never used + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "E").WithArguments("C.E").WithLocation(3, 33), + // (3,43): error CS9279: 'C.F': partial event cannot have initializer + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.ERR_PartialEventInitializer, "F").WithArguments("C.F").WithLocation(3, 43), + // (3,43): warning CS0414: The field 'C.F' is assigned but its value is never used + // partial event System.Action E = null, F = null; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "F").WithArguments("C.F").WithLocation(3, 43)); + } + + [Fact] + public void EventAccessorMissing() + { + var source = """ + partial class C + { + partial event System.Action E, F; + partial event System.Action E { add { } } + partial event System.Action F { remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,33): error CS0065: 'C.E': event property must have both add and remove accessors + // partial event System.Action E { add { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E").WithLocation(4, 33), + // (5,33): error CS0065: 'C.F': event property must have both add and remove accessors + // partial event System.Action F { remove { } } + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "F").WithArguments("C.F").WithLocation(5, 33)); + } + + [Fact] + public void StaticPartialConstructor_01() + { + var source = """ + partial class C + { + static partial C(); + static partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // static partial C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), + // (4,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // static partial C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 12), + // (4,20): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // static partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(4, 20)); + } + + [Fact] + public void StaticPartialConstructor_02() + { + var source = """ + partial class C + { + partial static C(); + partial static C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static C(); + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5), + // (4,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static C() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 5), + // (4,20): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial static C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(4, 20)); + } + + [Fact] + public void Finalizer() + { + var source = """ + partial class C + { + partial ~C(); + partial ~C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS1519: Invalid token '~' in class, record, struct, or interface member declaration + // partial ~C(); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "~").WithArguments("~").WithLocation(3, 13), + // (4,13): error CS1519: Invalid token '~' in class, record, struct, or interface member declaration + // partial ~C() { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "~").WithArguments("~").WithLocation(4, 13), + // (3,14): error CS0501: 'C.~C()' must declare a body because it is not marked abstract, extern, or partial + // partial ~C(); + Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "C").WithArguments("C.~C()").WithLocation(3, 14), + // (4,14): error CS0111: Type 'C' already defines a member called '~C' with the same parameter types + // partial ~C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("~C", "C").WithLocation(4, 14)); + } + + [Fact] + public void PrimaryConstructor_Duplicate_WithoutInitializer() + { + var source = """ + partial class C() + { + partial C(); + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(3, 13), + // (4,13): error CS8862: A constructor declared in a type with parameter list must have 'this' constructor initializer. + // partial C() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "C").WithLocation(4, 13)); + } + + [Fact] + public void PrimaryConstructor_Duplicate_WithInitializer() + { + var source = """ + partial class C() + { + partial C(); + partial C() : this() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(3, 13), + // (4,19): error CS0121: The call is ambiguous between the following methods or properties: 'C.C()' and 'C.C()' + // partial C() : this() { } + Diagnostic(ErrorCode.ERR_AmbigCall, "this").WithArguments("C.C()", "C.C()").WithLocation(4, 19)); + } + + [Fact] + public void PrimaryConstructor_DefinitionOnly() + { + var source = """ + partial class C() + { + partial C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9275: Partial member 'C.C()' must have an implementation part. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(3, 13), + // (3,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(3, 13)); + } + + [Fact] + public void PrimaryConstructor_ImplementationOnly_WithoutInitializer() + { + var source = """ + partial class C() + { + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9276: Partial member 'C.C()' must have a definition part. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(3, 13), + // (3,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(3, 13), + // (3,13): error CS8862: A constructor declared in a type with parameter list must have 'this' constructor initializer. + // partial C() { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "C").WithLocation(3, 13)); + } + + [Fact] + public void PrimaryConstructor_ImplementationOnly_WithInitializer() + { + var source = """ + partial class C() + { + partial C() : this() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9276: Partial member 'C.C()' must have a definition part. + // partial C() : this() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(3, 13), + // (3,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() : this() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(3, 13), + // (3,19): error CS0121: The call is ambiguous between the following methods or properties: 'C.C()' and 'C.C()' + // partial C() : this() { } + Diagnostic(ErrorCode.ERR_AmbigCall, "this").WithArguments("C.C()", "C.C()").WithLocation(3, 19)); + } + + [Fact] + public void PrimaryConstructor_Different_WithoutInitializer() + { + var source = """ + partial class C() + { + partial C(int x); + partial C(int x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,13): error CS8862: A constructor declared in a type with parameter list must have 'this' constructor initializer. + // partial C(int x) { } + Diagnostic(ErrorCode.ERR_UnexpectedOrMissingConstructorInitializerInRecord, "C").WithLocation(4, 13)); + } + + [Fact] + public void PrimaryConstructor_Different_WithInitializer() + { + var source = """ + partial class C() + { + partial C(int x); + partial C(int x) : this() { } + } + """; + CreateCompilation(source).VerifyDiagnostics(); + } + + [Fact] + public void PrimaryConstructor_Twice() + { + var source = """ + partial class C(); + partial class C() { } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,16): error CS8863: Only a single partial type declaration may have a parameter list + // partial class C() { } + Diagnostic(ErrorCode.ERR_MultipleRecordParameterLists, "()").WithLocation(2, 16)); + } + + [Fact] + public void NotInPartialType() + { + var source = """ + class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial C(); + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS0751: A partial member must be declared within a partial type + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "E").WithLocation(3, 33), + // (5,33): error CS9276: Partial event 'C.F' must have a definition part. + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "F").WithArguments("C.F").WithLocation(5, 33), + // (5,33): error CS0751: A partial member must be declared within a partial type + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "F").WithLocation(5, 33), + // (6,13): error CS0751: A partial member must be declared within a partial type + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "C").WithLocation(6, 13), + // (7,13): error CS0751: A partial member must be declared within a partial type + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "C").WithLocation(7, 13)); + } + + [Fact] + public void InInterface() + { + var source = """ + partial interface I + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,37): error CS8701: Target runtime doesn't support default interface implementation. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, "add").WithLocation(4, 37), + // (4,45): error CS8701: Target runtime doesn't support default interface implementation. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_RuntimeDoesNotSupportDefaultInterfaceImplementation, "remove").WithLocation(4, 45)); + + CreateCompilation(source, targetFramework: TargetFramework.Net60).VerifyDiagnostics(); + + CreateCompilation(source, targetFramework: TargetFramework.Net60, parseOptions: TestOptions.Regular7).VerifyDiagnostics( + // (3,33): error CS8703: The modifier 'partial' is not valid for this item in C# 7.0. Please use language version 'preview' or greater. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_InvalidModifierForLanguageVersion, "E").WithArguments("partial", "7.0", "preview").WithLocation(3, 33), + // (4,37): error CS8107: Feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "add").WithArguments("default interface implementation", "8.0").WithLocation(4, 37), + // (4,45): error CS8107: Feature 'default interface implementation' is not available in C# 7.0. Please use language version 8.0 or greater. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion7, "remove").WithArguments("default interface implementation", "8.0").WithLocation(4, 45)); + } + + [Fact] + public void InInterface_DefinitionOnly() + { + var source = """ + partial interface I + { + partial event System.Action E; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9275: Partial member 'I.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("I.E").WithLocation(3, 33)); + } + + [Fact] + public void Abstract() + { + var source = """ + abstract partial class C + { + protected abstract partial event System.Action E; + protected abstract partial event System.Action E { add { } remove { } } + protected abstract partial C(); + protected abstract partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,52): error CS0750: A partial member cannot have the 'abstract' modifier + // protected abstract partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberCannotBeAbstract, "E").WithLocation(3, 52), + // (4,54): error CS8712: 'C.E': abstract event cannot use event accessor syntax + // protected abstract partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_AbstractEventHasAccessors, "{").WithArguments("C.E").WithLocation(4, 54), + // (5,32): error CS0106: The modifier 'abstract' is not valid for this item + // protected abstract partial C(); + Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("abstract").WithLocation(5, 32), + // (6,32): error CS0106: The modifier 'abstract' is not valid for this item + // protected abstract partial C() { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "C").WithArguments("abstract").WithLocation(6, 32)); + } + + [Fact] + public void Required() + { + var source = """ + partial class C + { + public required partial event System.Action E; + public required partial event System.Action E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,49): error CS0106: The modifier 'required' is not valid for this item + // public required partial event System.Action E; + Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("required").WithLocation(3, 49), + // (4,49): error CS0106: The modifier 'required' is not valid for this item + // public required partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "E").WithArguments("required").WithLocation(4, 49)); + } + + [Fact] + public void ExplicitInterfaceImplementation() + { + var source = """ + interface I + { + event System.Action E; + } + partial class C : I + { + partial event System.Action I.E; + partial event System.Action I.E { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,15): error CS8646: 'I.E' is explicitly implemented more than once. + // partial class C : I + Diagnostic(ErrorCode.ERR_DuplicateExplicitImpl, "C").WithArguments("I.E").WithLocation(5, 15), + // (7,35): error CS0071: An explicit interface implementation of an event must use event accessor syntax + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_ExplicitEventFieldImpl, "E").WithLocation(7, 35), + // (7,35): error CS9276: Partial member 'C.I.E' must have a definition part. + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.I.E").WithLocation(7, 35), + // (7,35): error CS0754: A partial member may not explicitly implement an interface member + // partial event System.Action I.E; + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "E").WithLocation(7, 35), + // (8,35): error CS9278: Partial member 'C.I.E' may not have multiple implementing declarations. + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.I.E").WithLocation(8, 35), + // (8,35): error CS0102: The type 'C' already contains a definition for 'I.E' + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "I.E").WithLocation(8, 35), + // (8,35): error CS0754: A partial member may not explicitly implement an interface member + // partial event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberNotExplicit, "E").WithLocation(8, 35)); + } + + [Fact] + public void ConstructorInitializers_This_Duplicate() + { + var source = """ + partial class C + { + partial C() : this(1) { } + partial C() : this(2); + + C(int x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,17): error CS9280: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : this(2); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": this(2)").WithArguments("C.C()").WithLocation(4, 17)); + } + + [Fact] + public void ConstructorInitializers_This_OnDefinition() + { + var source = """ + partial class C + { + partial C() { } + partial C() : this(1); + + C(int x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,17): error CS9280: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : this(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": this(1)").WithArguments("C.C()").WithLocation(4, 17)); + } + + [Fact] + public void ConstructorInitializers_This_OnImplementation() + { + var source = """ + var c = new C(); + + partial class C + { + public partial C() : this(1) { } + public partial C(); + + C(int x) { System.Console.Write(x); } + } + """; + CompileAndVerify(source, expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void ConstructorInitializers_Base_Duplicate() + { + var source = """ + abstract class B + { + protected B(int x) { } + } + + partial class C : B + { + partial C() : base(1) { } + partial C() : base(2); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (9,17): error CS9280: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(2); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(2)").WithArguments("C.C()").WithLocation(9, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnDefinition_01() + { + var source = """ + abstract class B + { + protected B(int x) { } + } + + partial class C : B + { + partial C() { } + partial C() : base(1); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (8,13): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'B.B(int)' + // partial C() { } + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "C").WithArguments("x", "B.B(int)").WithLocation(8, 13), + // (9,17): error CS9280: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(1)").WithArguments("C.C()").WithLocation(9, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnDefinition_02() + { + var source = """ + abstract class B + { + protected B(int x) { } + protected B() { } + } + + partial class C : B + { + partial C() { } + partial C() : base(1); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (10,17): error CS9280: 'C.C()': only the implementing declaration of a partial constructor can have an initializer + // partial C() : base(1); + Diagnostic(ErrorCode.ERR_PartialConstructorInitializer, ": base(1)").WithArguments("C.C()").WithLocation(10, 17)); + } + + [Fact] + public void ConstructorInitializers_Base_OnImplementation() + { + var source = """ + var c = new C(); + + abstract class B + { + protected B(int x) { System.Console.Write(x); } + } + + partial class C : B + { + public partial C() : base(1) { } + public partial C(); + } + """; + CompileAndVerify(source, expectedOutput: "1").VerifyDiagnostics(); + } + + [Fact] + public void VariableInitializer() + { + var source = """ + var c = new C(); + + partial class C + { + int x = 5; + + public partial C() { System.Console.Write(x); } + public partial C(); + } + """; + CompileAndVerify(source, expectedOutput: "5").VerifyDiagnostics(); + } + + [Fact] + public void Extern_01() + { + var source = """ + partial class C + { + partial event System.Action E; + extern partial event System.Action E; + + partial C(); + extern partial C(); + } + """; + CompileAndVerifyWithMscorlib46(source, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics() + .VerifyTypeIL("C", """ + .class private auto ansi beforefieldinit C + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname + instance void add_E ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + } // end of method C::add_E + .method private hidebysig specialname + instance void remove_E ( + class [mscorlib]System.Action 'value' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + } // end of method C::remove_E + .method private hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + } // end of method C::.ctor + // Events + .event [mscorlib]System.Action E + { + .addon instance void C::add_E(class [mscorlib]System.Action) + .removeon instance void C::remove_E(class [mscorlib]System.Action) + } + } // end of class C + """); + + static void verifySource(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.True(ev.IsPartialDefinition); + Assert.True(ev.GetPublicSymbol().IsExtern); + Assert.True(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.AddMethod!.GetPublicSymbol().IsExtern); + Assert.True(ev.PartialImplementationPart!.RemoveMethod!.GetPublicSymbol().IsExtern); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.IsPartialDefinition); + Assert.True(c.GetPublicSymbol().IsExtern); + Assert.True(c.PartialImplementationPart!.GetPublicSymbol().IsExtern); + + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + event System.Action C.E + C..ctor() + """, members); + } + + static void verifyMetadata(ModuleSymbol module) + { + // IsExtern doesn't round trip from metadata when DllImportAttribute is missing. + // This is consistent with the behavior of partial methods and properties. + + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.False(ev.GetPublicSymbol().IsExtern); + Assert.False(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.False(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.False(c.GetPublicSymbol().IsExtern); + + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + C..ctor() + event System.Action C.E + """, members); + } + } + + [Fact] + public void Extern_02() + { + var source = """ + partial class C + { + partial event System.Action E; + extern event System.Action E; + + partial C(); + extern C(); + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,33): error CS9275: Partial member 'C.E' must have an implementation part. + // partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(3, 33), + // (4,32): error CS0102: The type 'C' already contains a definition for 'E' + // extern event System.Action E; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 32), + // (4,32): warning CS0626: Method, operator, or accessor 'C.E.remove' is marked external and has no attributes on it. Consider adding a DllImport attribute to specify the external implementation. + // extern event System.Action E; + Diagnostic(ErrorCode.WRN_ExternMethodNoImplementation, "E").WithArguments("C.E.remove").WithLocation(4, 32), + // (6,13): error CS9275: Partial member 'C.C()' must have an implementation part. + // partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(6, 13), + // (7,12): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // extern C(); + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 12), + // (7,12): warning CS0824: Constructor 'C.C()' is marked external + // extern C(); + Diagnostic(ErrorCode.WRN_ExternCtorNoImplementation, "C").WithArguments("C.C()").WithLocation(7, 12)); + } + + [Fact] + public void Extern_03() + { + var source = """ + partial class C + { + extern partial event System.Action E; + partial event System.Action E { add { } remove { } } + + extern partial C(); + partial C() { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,40): error CS9276: Partial member 'C.E' must have a definition part. + // extern partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "E").WithArguments("C.E").WithLocation(3, 40), + // (4,33): error CS9278: Partial member 'C.E' may not have multiple implementing declarations. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "E").WithArguments("C.E").WithLocation(4, 33), + // (4,33): error CS0102: The type 'C' already contains a definition for 'E' + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "E").WithArguments("C", "E").WithLocation(4, 33), + // (6,20): error CS9276: Partial member 'C.C()' must have a definition part. + // extern partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C").WithArguments("C.C()").WithLocation(6, 20), + // (7,13): error CS9278: Partial member 'C.C()' may not have multiple implementing declarations. + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberDuplicateImplementation, "C").WithArguments("C.C()").WithLocation(7, 13), + // (7,13): error CS0111: Type 'C' already defines a member called 'C' with the same parameter types + // partial C() { } + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments("C", "C").WithLocation(7, 13)); + } + + [Fact] + public void Extern_DllImport() + { + var source = """ + using System; + using System.Runtime.InteropServices; + public partial class C + { + public static partial event Action E; + [method: DllImport("something.dll")] + public static extern partial event Action E; + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verify, + symbolValidator: verify) + .VerifyDiagnostics(); + + static void verify(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.GetPublicSymbol().IsExtern); + // unexpected mismatch between metadata and entrypoint name: https://github.com/dotnet/roslyn/issues/76882 + verifyAccessor(e.AddMethod!, "add_E", "remove_E"); + verifyAccessor(e.RemoveMethod!, "remove_E", "remove_E"); + + if (module is SourceModuleSymbol) + { + var eImpl = ((SourceEventSymbol)e).PartialImplementationPart!; + Assert.True(eImpl.GetPublicSymbol().IsExtern); + // unexpected mismatch between metadata and entrypoint name: https://github.com/dotnet/roslyn/issues/76882 + verifyAccessor(eImpl.AddMethod!, "add_E", "remove_E"); + verifyAccessor(eImpl.RemoveMethod!, "remove_E", "remove_E"); + } + } + + static void verifyAccessor(MethodSymbol accessor, string expectedMetadataName, string expectedEntryPointName) + { + Assert.True(accessor.GetPublicSymbol().IsExtern); + Assert.Equal(expectedMetadataName, accessor.MetadataName); + Assert.False(accessor.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + + var importData = accessor.GetDllImportData()!; + Assert.Equal("something.dll", importData.ModuleName); + Assert.Equal(expectedEntryPointName, importData.EntryPointName); + Assert.Equal(CharSet.None, importData.CharacterSet); + Assert.False(importData.SetLastError); + Assert.False(importData.ExactSpelling); + Assert.Equal(MethodImplAttributes.PreserveSig, accessor.ImplementationAttributes); + Assert.Equal(CallingConvention.Winapi, importData.CallingConvention); + Assert.Null(importData.BestFitMapping); + Assert.Null(importData.ThrowOnUnmappableCharacter); + } + } + + [Fact] + public void Extern_InternalCall() + { + var source = """ + using System; + using System.Runtime.CompilerServices; + public partial class C + { + public partial C(); + [MethodImpl(MethodImplOptions.InternalCall)] + public extern partial C(); + + public partial event Action E; + [method: MethodImpl(MethodImplOptions.InternalCall)] + public extern partial event Action E; + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata) + .VerifyDiagnostics(); + + static void verifySource(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.True(ev.GetPublicSymbol().IsExtern); + Assert.True(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.AddMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.AddMethod.ImplementationAttributes); + Assert.False(ev.AddMethod.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + Assert.True(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.RemoveMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.RemoveMethod.ImplementationAttributes); + Assert.False(ev.RemoveMethod.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.GetPublicSymbol().IsExtern); + Assert.Null(c.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, c.ImplementationAttributes); + } + + static void verifyMetadata(ModuleSymbol module) + { + var ev = module.GlobalNamespace.GetMember("C.E"); + Assert.False(ev.GetPublicSymbol().IsExtern); + Assert.False(ev.AddMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.AddMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.AddMethod.ImplementationAttributes); + Assert.False(ev.AddMethod.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + Assert.False(ev.RemoveMethod!.GetPublicSymbol().IsExtern); + Assert.Null(ev.RemoveMethod!.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, ev.RemoveMethod.ImplementationAttributes); + Assert.False(ev.RemoveMethod.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.False(c.GetPublicSymbol().IsExtern); + Assert.Null(c.GetDllImportData()); + Assert.Equal(MethodImplAttributes.InternalCall, c.ImplementationAttributes); + } + } + + [Fact] + public void WinRtEvent() + { + var source = """ + partial class C + { + public partial event System.Action E; + public partial event System.Action E { add { return default; } remove { } } + } + """; + CompileAndVerifyWithWinRt(source, + sourceSymbolValidator: validate, + symbolValidator: validate, + options: TestOptions.ReleaseWinMD) + .VerifyDiagnostics() + .VerifyTypeIL("C", """ + .class private auto ansi beforefieldinit C + extends [mscorlib]System.Object + { + // Methods + .method public hidebysig specialname + instance valuetype [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_E ( + class [mscorlib]System.Action 'value' + ) cil managed + { + // Method begins at RVA 0x2068 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] valuetype [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken + ) + IL_0000: ldloca.s 0 + IL_0002: initobj [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken + IL_0008: ldloc.0 + IL_0009: ret + } // end of method C::add_E + .method public hidebysig specialname + instance void remove_E ( + valuetype [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken 'value' + ) cil managed + { + // Method begins at RVA 0x207e + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method C::remove_E + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2080 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method C::.ctor + // Events + .event [mscorlib]System.Action E + { + .addon instance valuetype [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C::add_E(class [mscorlib]System.Action) + .removeon instance void C::remove_E(valuetype [mscorlib]System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken) + } + } // end of class C + """); + + static void validate(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.IsWindowsRuntimeEvent); + + if (module is SourceModuleSymbol) + { + Assert.True(((SourceEventSymbol)e).PartialImplementationPart!.IsWindowsRuntimeEvent); + } + } + } + + [Fact] + public void Extern_MissingCompareExchange() + { + var source = """ + using System; + partial class C + { + partial event Action E; + extern partial event Action E; + } + """; + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All)); + comp.MakeMemberMissing(WellKnownMember.System_Threading_Interlocked__CompareExchange_T); + CompileAndVerify(comp, + sourceSymbolValidator: validate, + symbolValidator: validate, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.AddMethod!.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + Assert.True(e.RemoveMethod!.ImplementationAttributes.HasFlag(MethodImplAttributes.Synchronized)); + } + } + + [Fact] + public void Metadata() + { + var source = """ + public partial class C + { + public partial event System.Action E; + public partial event System.Action E { add { } remove { } } + public partial C(); + public partial C() { } + } + """; + CompileAndVerify(source, + sourceSymbolValidator: verifySource, + symbolValidator: verifyMetadata) + .VerifyDiagnostics(); + + static void verifySource(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.True(e.IsPartialDefinition); + Assert.False(e.IsPartialImplementation); + Assert.False(e.HasAssociatedField); + Assert.False(e.IsWindowsRuntimeEvent); + Assert.Null(e.PartialDefinitionPart); + Assert.True(e.SourcePartialImplementationPart!.IsPartialImplementation); + Assert.False(e.SourcePartialImplementationPart.IsPartialDefinition); + Assert.False(e.SourcePartialImplementationPart.HasAssociatedField); + Assert.False(e.SourcePartialImplementationPart.IsWindowsRuntimeEvent); + + var addMethod = e.AddMethod!; + Assert.Equal("add_E", addMethod.Name); + Assert.NotSame(addMethod, e.SourcePartialImplementationPart.AddMethod); + Assert.Same(e, addMethod.AssociatedSymbol); + Assert.Same(e.PartialImplementationPart, addMethod.PartialImplementationPart.AssociatedSymbol); + var removeMethod = e.RemoveMethod!; + Assert.Equal("remove_E", removeMethod.Name); + Assert.NotSame(removeMethod, e.SourcePartialImplementationPart.RemoveMethod); + Assert.Same(e, removeMethod.AssociatedSymbol); + Assert.Same(e.PartialImplementationPart, removeMethod.PartialImplementationPart.AssociatedSymbol); + + var c = module.GlobalNamespace.GetMember("C..ctor"); + Assert.True(c.IsPartialDefinition); + Assert.False(c.IsPartialImplementation); + Assert.Null(c.PartialDefinitionPart); + var cImpl = (SourceConstructorSymbol)c.PartialImplementationPart!; + Assert.True(cImpl.IsPartialImplementation); + Assert.False(cImpl.IsPartialDefinition); + } + + static void verifyMetadata(ModuleSymbol module) + { + var e = module.GlobalNamespace.GetMember("C.E"); + Assert.False(e.HasAssociatedField); + + var addMethod = e.AddMethod!; + Assert.Equal("add_E", addMethod.Name); + var removeMethod = e.RemoveMethod!; + Assert.Equal("remove_E", removeMethod.Name); + } + } + + [Fact] + public void GetDeclaredSymbol() + { + var source = (""" + partial class C + { + public partial event System.Action E, F; + public partial event System.Action E { add { } remove { } } + public partial event System.Action F { add { } remove { } } + + public partial C(); + public partial C() { } + } + """, "Program.cs"); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var eventDefs = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(2, eventDefs.Length); + + var eventImpls = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(2, eventImpls.Length); + + { + var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[0])!; + Assert.Equal("event System.Action C.E", defSymbol.ToTestDisplayString()); + + IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[0])!; + Assert.Equal("event System.Action C.E", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.Null(implSymbol.PartialImplementationPart); + Assert.Null(defSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + } + + { + var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(eventDefs[1])!; + Assert.Equal("event System.Action C.F", defSymbol.ToTestDisplayString()); + + IEventSymbol implSymbol = model.GetDeclaredSymbol(eventImpls[1])!; + Assert.Equal("event System.Action C.F", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.Null(implSymbol.PartialImplementationPart); + Assert.Null(defSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + } + + { + var ctors = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(2, ctors.Length); + + IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!; + Assert.Equal("C..ctor()", defSymbol.ToTestDisplayString()); + + IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!; + Assert.Equal("C..ctor()", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.Null(implSymbol.PartialImplementationPart); + Assert.Null(defSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + } + } + + [Fact] + public void GetDeclaredSymbol_GenericContainer() + { + var source = (""" + partial class C + { + public partial event System.Action E; + public partial event System.Action E { add { } remove { } } + + public partial C(); + public partial C() { } + } + """, "Program.cs"); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + { + var defSymbol = (IEventSymbol)model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single())!; + Assert.Equal("event System.Action C.E", defSymbol.ToTestDisplayString()); + + IEventSymbol implSymbol = model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType().Single())!; + Assert.Equal("event System.Action C.E", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.Null(implSymbol.PartialImplementationPart); + Assert.Null(defSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + + var intSymbol = comp.GetSpecialType(SpecialType.System_Int32); + var cOfTSymbol = defSymbol.ContainingType!; + var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]); + + var defOfIntSymbol = (IEventSymbol)cOfIntSymbol.GetMember("E"); + Assert.Equal("event System.Action C.E", defOfIntSymbol.ToTestDisplayString()); + Assert.Null(defOfIntSymbol.PartialImplementationPart); + Assert.Null(defOfIntSymbol.PartialDefinitionPart); + Assert.False(defOfIntSymbol.IsPartialDefinition); + } + + { + var ctors = tree.GetRoot().DescendantNodes().OfType().ToImmutableArray(); + Assert.Equal(2, ctors.Length); + + IMethodSymbol defSymbol = model.GetDeclaredSymbol(ctors[0])!; + Assert.Equal("C..ctor()", defSymbol.ToTestDisplayString()); + + IMethodSymbol implSymbol = model.GetDeclaredSymbol(ctors[1])!; + Assert.Equal("C..ctor()", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, defSymbol.PartialImplementationPart); + Assert.Same(defSymbol, implSymbol.PartialDefinitionPart); + Assert.Null(implSymbol.PartialImplementationPart); + Assert.Null(defSymbol.PartialDefinitionPart); + Assert.True(defSymbol.IsPartialDefinition); + Assert.False(implSymbol.IsPartialDefinition); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + + var intSymbol = comp.GetSpecialType(SpecialType.System_Int32); + var cOfTSymbol = defSymbol.ContainingType!; + var cOfIntSymbol = cOfTSymbol.Construct([intSymbol]); + + var defOfIntSymbol = (IMethodSymbol)cOfIntSymbol.GetMember(".ctor"); + Assert.Equal("C..ctor()", defOfIntSymbol.ToTestDisplayString()); + Assert.Null(defOfIntSymbol.PartialImplementationPart); + Assert.Null(defOfIntSymbol.PartialDefinitionPart); + Assert.False(defOfIntSymbol.IsPartialDefinition); + } + } + + [Fact] + public void GetDeclaredSymbol_ConstructorParameter() + { + var source = (""" + partial class C + { + public partial C(int i); + public partial C(int i) { } + } + """, "Program.cs"); + + var comp = CreateCompilation(source).VerifyDiagnostics(); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var parameters = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(2, parameters.Length); + + IParameterSymbol defSymbol = model.GetDeclaredSymbol(parameters[0])!; + Assert.Equal("System.Int32 i", defSymbol.ToTestDisplayString()); + + IParameterSymbol implSymbol = model.GetDeclaredSymbol(parameters[1])!; + Assert.Equal("System.Int32 i", implSymbol.ToTestDisplayString()); + + Assert.NotEqual(defSymbol, implSymbol); + Assert.Same(implSymbol, ((IMethodSymbol)defSymbol.ContainingSymbol).PartialImplementationPart!.Parameters.Single()); + Assert.Same(defSymbol, ((IMethodSymbol)implSymbol.ContainingSymbol).PartialDefinitionPart!.Parameters.Single()); + + Assert.NotEqual(defSymbol.Locations.Single(), implSymbol.Locations.Single()); + } + + [Fact] + public void SequencePoints() + { + var source = """ + partial class C + { + partial C(int i); + partial C(int i) + { + System.Console.Write(i); + } + partial event System.Action E; + partial event System.Action E + { + add + { + System.Console.Write(value); + } + remove + { + value(); + } + } + } + """; + CompileAndVerify(source) + .VerifyDiagnostics() + .VerifyMethodBody("C..ctor", """ + { + // Code size 13 (0xd) + .maxstack 1 + // sequence point: partial C(int i) + IL_0000: ldarg.0 + IL_0001: call "object..ctor()" + // sequence point: System.Console.Write(i); + IL_0006: ldarg.1 + IL_0007: call "void System.Console.Write(int)" + // sequence point: } + IL_000c: ret + } + """) + .VerifyMethodBody("C.E.add", """ + { + // Code size 7 (0x7) + .maxstack 1 + // sequence point: System.Console.Write(value); + IL_0000: ldarg.1 + IL_0001: call "void System.Console.Write(object)" + // sequence point: } + IL_0006: ret + } + """) + .VerifyMethodBody("C.E.remove", """ + { + // Code size 7 (0x7) + .maxstack 1 + // sequence point: value(); + IL_0000: ldarg.1 + IL_0001: callvirt "void System.Action.Invoke()" + // sequence point: } + IL_0006: ret + } + """); + } + + [Fact] + public void EmitOrder_01() + { + verify(""" + partial class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + partial C(); + partial C() { } + } + """); + + verify(""" + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action E; + partial C() { } + partial C(); + } + """); + + verify(""" + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """, """ + partial class C + { + partial event System.Action E; + partial C(); + } + """); + + verify(""" + partial class C + { + partial event System.Action E; + partial C(); + } + """, """ + partial class C + { + partial event System.Action E { add { } remove { } } + partial C() { } + } + """); + + void verify(params CSharpTestSource sources) + { + CompileAndVerify(sources, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate) + .VerifyDiagnostics(); + } + + static void validate(ModuleSymbol module) + { + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + void C.E.add + void C.E.remove + C..ctor() + event System.Action C.E + """, members); + } + } + + [Fact] + public void EmitOrder_02() + { + verify(""" + partial class C + { + partial C(); + partial C() { } + partial event System.Action E; + partial event System.Action E { add { } remove { } } + } + """); + + verify(""" + partial class C + { + partial C() { } + partial C(); + partial event System.Action E { add { } remove { } } + partial event System.Action E; + } + """); + + verify(""" + partial class C + { + partial C(); + partial event System.Action E; + } + """, """ + partial class C + { + partial C() { } + partial event System.Action E { add { } remove { } } + } + """); + + verify(""" + partial class C + { + partial C() { } + partial event System.Action E { add { } remove { } } + } + """, """ + partial class C + { + partial C(); + partial event System.Action E; + } + """); + + void verify(params CSharpTestSource sources) + { + CompileAndVerify(sources, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate) + .VerifyDiagnostics(); + } + + static void validate(ModuleSymbol module) + { + var members = module.GlobalNamespace.GetTypeMember("C").GetMembers().Select(s => s.ToTestDisplayString()).Join("\n"); + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + C..ctor() + void C.E.add + void C.E.remove + event System.Action C.E + """, members); + } + } + + [Fact] + public void Use_Valid() + { + var source = """ + using System; + + var c = new C(); + c.E += () => Console.Write(1); + c.E -= () => Console.Write(2); + + partial class C + { + public partial event Action E; + public partial event Action E + { + add { Console.Write(3); value(); } + remove { Console.Write(4); value(); } + } + public partial C(); + public partial C() { Console.Write(5); } + } + """; + CompileAndVerify(source, expectedOutput: "53142").VerifyDiagnostics(); + } + + [Fact] + public void Use_EventAsValue() + { + var source = """ + using System; + + var c = new C(); + Action a = c.E; + c.E(); + + partial class C + { + public partial event Action E; + public partial event Action E { add { } remove { } } + + void M() + { + Action a = this.E; + this.E(); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,14): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // Action a = c.E; + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(4, 14), + // (5,3): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // c.E(); + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(5, 3), + // (14,25): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // Action a = this.E; + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(14, 25), + // (15,14): error CS0079: The event 'C.E' can only appear on the left hand side of += or -= + // this.E(); + Diagnostic(ErrorCode.ERR_BadEventUsageNoField, "E").WithArguments("C.E").WithLocation(15, 14)); + } + + [Fact] + public void Use_EventAccessorsInaccessible() + { + var source = """ + using System; + + var c = new C(); + c.E += () => { }; + c.E -= () => { }; + + partial class C + { + partial event Action E; + partial event Action E { add { } remove { } } + + void M() + { + this.E += () => { }; + this.E -= () => { }; + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,3): error CS0122: 'C.E' is inaccessible due to its protection level + // c.E += () => { }; + Diagnostic(ErrorCode.ERR_BadAccess, "E").WithArguments("C.E").WithLocation(4, 3), + // (5,3): error CS0122: 'C.E' is inaccessible due to its protection level + // c.E -= () => { }; + Diagnostic(ErrorCode.ERR_BadAccess, "E").WithArguments("C.E").WithLocation(5, 3)); + } + + [Fact] + public void Use_Static() + { + var source = """ + var c = new C(); + c.E += () => { }; + C.E += () => { }; // 1 + c.F += () => { }; // 2 + C.F += () => { }; + + partial class C + { + public partial event System.Action E; + public static partial event System.Action F; + } + partial class C + { + public partial event System.Action E + { + add + { + this.E += null; + E += null; + C.E += null; // 3 + } + remove + { + this.F += null; // 4 + F += null; + C.F += null; + } + } + public static partial event System.Action F + { + add + { + this.E += null; // 5 + E += null; // 6 + C.E += null; // 7 + } + remove + { + this.F += null; // 8 + F += null; + C.F += null; + } + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,1): error CS0120: An object reference is required for the non-static field, method, or property 'C.E' + // C.E += () => { }; // 1 + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.E").WithArguments("C.E").WithLocation(3, 1), + // (4,1): error CS0176: Member 'C.F' cannot be accessed with an instance reference; qualify it with a type name instead + // c.F += () => { }; // 2 + Diagnostic(ErrorCode.ERR_ObjectProhibited, "c.F").WithArguments("C.F").WithLocation(4, 1), + // (20,13): error CS0120: An object reference is required for the non-static field, method, or property 'C.E' + // C.E += null; // 3 + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.E").WithArguments("C.E").WithLocation(20, 13), + // (24,13): error CS0176: Member 'C.F' cannot be accessed with an instance reference; qualify it with a type name instead + // this.F += null; // 4 + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.F").WithArguments("C.F").WithLocation(24, 13), + // (33,13): error CS0026: Keyword 'this' is not valid in a static property, static method, or static field initializer + // this.E += null; // 5 + Diagnostic(ErrorCode.ERR_ThisInStaticMeth, "this").WithLocation(33, 13), + // (34,13): error CS0120: An object reference is required for the non-static field, method, or property 'C.E' + // E += null; // 6 + Diagnostic(ErrorCode.ERR_ObjectRequired, "E").WithArguments("C.E").WithLocation(34, 13), + // (35,13): error CS0120: An object reference is required for the non-static field, method, or property 'C.E' + // C.E += null; // 7 + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.E").WithArguments("C.E").WithLocation(35, 13), + // (39,13): error CS0026: Keyword 'this' is not valid in a static property, static method, or static field initializer + // this.F += null; // 8 + Diagnostic(ErrorCode.ERR_ThisInStaticMeth, "this").WithLocation(39, 13), + // (39,13): error CS0176: Member 'C.F' cannot be accessed with an instance reference; qualify it with a type name instead + // this.F += null; // 8 + Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.F").WithArguments("C.F").WithLocation(39, 13)); + } + + [Fact] + public void Use_Inheritance() + { + var source = """ + using System; + + var c = new C1(); + c.E += () => { }; + c = new C2(); + c.E += () => { }; + + partial class C1 + { + public virtual partial event Action E; + public virtual partial event Action E { add { Console.Write(1); } remove { } } + } + partial class C2 : C1 + { + public override partial event Action E; + public override partial event Action E { add { Console.Write(2); } remove { } } + } + """; + CompileAndVerify(source, expectedOutput: "12").VerifyDiagnostics(); + } + + [Fact] + public void Difference_Accessibility() + { + var source = """ + partial class C + { + partial C(); + internal partial C(int x); + partial event System.Action E, F; + public partial event System.Action G; + } + partial class C + { + public partial C() { } + private partial C(int x) { } + private partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + internal partial event System.Action G { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (10,20): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // public partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "C").WithLocation(10, 20), + // (11,21): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // private partial C(int x) { } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "C").WithLocation(11, 21), + // (12,41): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // private partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "E").WithLocation(12, 41), + // (14,42): error CS8799: Both partial member declarations must have identical accessibility modifiers. + // internal partial event System.Action G { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberAccessibilityDifference, "G").WithLocation(14, 42)); + } + + [Fact] + public void Difference_Type() + { + var source = """ + using A = System.Action; + partial class C + { + partial event System.Action E, F; + partial event System.Action<(int X, int Y)> G; + partial event System.Action<(string X, string Y)> H; + partial event System.Action I; + partial event A J; + } + partial class C + { + partial event System.Func E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial event System.Action<(int A, int B)> G { add { } remove { } } + partial event System.Action<(int A, int B)> H { add { } remove { } } + partial event System.Action I { add { } remove { } } + partial event System.Action J { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (12,36): error CS9255: Both partial member declarations must have the same type. + // partial event System.Func E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "E").WithLocation(12, 36), + // (14,49): error CS8142: Both partial member declarations, 'C.G' and 'C.G', must use the same tuple element names. + // partial event System.Action<(int A, int B)> G { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "G").WithArguments("C.G", "C.G").WithLocation(14, 49), + // (15,49): error CS9255: Both partial member declarations must have the same type. + // partial event System.Action<(int A, int B)> H { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "H").WithLocation(15, 49), + // (16,41): warning CS9256: Partial member declarations 'event Action C.I' and 'event Action C.I' have signature differences. + // partial event System.Action I { add { } remove { } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "I").WithArguments("event Action C.I", "event Action C.I").WithLocation(16, 41)); + } + + [Fact] + public void Difference_ParameterType() + { + var source = """ + partial class C1 + { + partial C1(string x); + partial C1(int x) { } + } + partial class C2 + { + partial C2(dynamic x); + partial C2(object x) { } + } + partial class C3 + { + partial C3((int X, int Y) x); + partial C3((int A, int B) x) { } + } + partial class C4 + { + partial C4((int X, int Y) x); + partial C4((string A, string B) x) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9275: Partial member 'C1.C1(string)' must have an implementation part. + // partial C1(string x); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C1").WithArguments("C1.C1(string)").WithLocation(3, 13), + // (4,13): error CS9276: Partial member 'C1.C1(int)' must have a definition part. + // partial C1(int x) { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C1").WithArguments("C1.C1(int)").WithLocation(4, 13), + // (9,13): warning CS9256: Partial member declarations 'C2.C2(dynamic x)' and 'C2.C2(object x)' have signature differences. + // partial C2(object x) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C2").WithArguments("C2.C2(dynamic x)", "C2.C2(object x)").WithLocation(9, 13), + // (14,13): error CS8142: Both partial member declarations, 'C3.C3((int X, int Y))' and 'C3.C3((int A, int B))', must use the same tuple element names. + // partial C3((int A, int B) x) { } + Diagnostic(ErrorCode.ERR_PartialMemberInconsistentTupleNames, "C3").WithArguments("C3.C3((int X, int Y))", "C3.C3((int A, int B))").WithLocation(14, 13), + // (18,13): error CS9275: Partial member 'C4.C4((int X, int Y))' must have an implementation part. + // partial C4((int X, int Y) x); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C4").WithArguments("C4.C4((int X, int Y))").WithLocation(18, 13), + // (19,13): error CS9276: Partial member 'C4.C4((string A, string B))' must have a definition part. + // partial C4((string A, string B) x) { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C4").WithArguments("C4.C4((string A, string B))").WithLocation(19, 13)); + } + + [Fact] + public void Difference_Nullability() + { + var source = """ + partial class C + { + partial event System.Action? E; + partial event System.Action F; + partial event System.Action G; + } + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + partial event System.Action? G { add { } remove { } } + } + """; + + var expectedDiagnostics = new[] + { + // (9,33): warning CS9256: Partial member declarations 'event Action? C.E' and 'event Action C.E' have signature differences. + // partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "E").WithArguments("event Action? C.E", "event Action C.E").WithLocation(9, 33), + // (10,41): warning CS9256: Partial member declarations 'event Action C.F' and 'event Action C.F' have signature differences. + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "F").WithArguments("event Action C.F", "event Action C.F").WithLocation(10, 41), + // (11,34): warning CS9256: Partial member declarations 'event Action C.G' and 'event Action? C.G' have signature differences. + // partial event System.Action? G { add { } remove { } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "G").WithArguments("event Action C.G", "event Action? C.G").WithLocation(11, 34) + }; + + CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Enable)).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Annotations)).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Difference_ParameterNullability() + { + var source = """ + partial class C1 + { + partial C1(string x); + partial C1(string? x) { } + } + partial class C2 + { + partial C2(string? x); + partial C2(string x) { } + } + """; + + var expectedDiagnostics = new[] + { + // (4,13): warning CS9256: Partial member declarations 'C1.C1(string x)' and 'C1.C1(string? x)' have signature differences. + // partial C1(string? x) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C1").WithArguments("C1.C1(string x)", "C1.C1(string? x)").WithLocation(4, 13), + // (9,13): warning CS9256: Partial member declarations 'C2.C2(string? x)' and 'C2.C2(string x)' have signature differences. + // partial C2(string x) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C2").WithArguments("C2.C2(string? x)", "C2.C2(string x)").WithLocation(9, 13) + }; + + CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Enable)).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Annotations)).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact] + public void Difference_NullabilityContext() + { + var source = """ + #nullable enable + partial class C + { + partial event System.Action E; + partial event System.Action? F; + #nullable disable + partial event System.Action G; + } + + #nullable disable + partial class C + { + partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + #nullable enable + partial event System.Action G { add { } remove { } } + } + """; + + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var e = comp.GetMember("C.E"); + Assert.True(e.IsPartialDefinition); + Assert.Equal(NullableAnnotation.NotAnnotated, e.TypeWithAnnotations.NullableAnnotation); + + var f = comp.GetMember("C.F"); + Assert.True(f.IsPartialDefinition); + Assert.Equal(NullableAnnotation.Annotated, f.TypeWithAnnotations.NullableAnnotation); + + var g = comp.GetMember("C.G"); + Assert.True(g.IsPartialDefinition); + Assert.Equal(NullableAnnotation.Oblivious, g.TypeWithAnnotations.NullableAnnotation); + } + + [Fact] + public void Difference_NullabilityAnalysis() + { + // The implementation part signature is used to analyze the implementation part bodies. + // The definition part signature is used to analyze use sites. + // Note that event assignments are not checked for nullability: https://github.com/dotnet/roslyn/issues/31018 + var source = """ + #nullable enable + + var c = new C(0, null); + c = new C(null, 0); + c.E += null; + c.E -= null; + c.F += null; + c.F -= null; + + partial class C + { + public partial C(int x, string? y); + public partial C(string x, int y); + public partial event System.Action E; + public partial event System.Action? F; + } + + partial class C + { + public partial C(int x, string y) + { + y.ToString(); + } + public partial C(string? x, int y) + { + x.ToString(); + } + public partial event System.Action? E { add { value(); } remove { value(); } } + public partial event System.Action F { add { value(); } remove { value(); } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,11): warning CS8625: Cannot convert null literal to non-nullable reference type. + // c = new C(null, 0); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 11), + // (20,20): warning CS9256: Partial member declarations 'C.C(int x, string? y)' and 'C.C(int x, string y)' have signature differences. + // public partial C(int x, string y) + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int x, string? y)", "C.C(int x, string y)").WithLocation(20, 20), + // (24,20): warning CS9256: Partial member declarations 'C.C(string x, int y)' and 'C.C(string? x, int y)' have signature differences. + // public partial C(string? x, int y) + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(string x, int y)", "C.C(string? x, int y)").WithLocation(24, 20), + // (26,9): warning CS8602: Dereference of a possibly null reference. + // x.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(26, 9), + // (28,41): warning CS9256: Partial member declarations 'event Action C.E' and 'event Action? C.E' have signature differences. + // public partial event System.Action? E { add { value(); } remove { value(); } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "E").WithArguments("event Action C.E", "event Action? C.E").WithLocation(28, 41), + // (28,51): warning CS8602: Dereference of a possibly null reference. + // public partial event System.Action? E { add { value(); } remove { value(); } } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "value").WithLocation(28, 51), + // (28,71): warning CS8602: Dereference of a possibly null reference. + // public partial event System.Action? E { add { value(); } remove { value(); } } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "value").WithLocation(28, 71), + // (29,40): warning CS9256: Partial member declarations 'event Action? C.F' and 'event Action C.F' have signature differences. + // public partial event System.Action F { add { value(); } remove { value(); } } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "F").WithArguments("event Action? C.F", "event Action C.F").WithLocation(29, 40)); + } + + [Fact] + public void Difference_Static() + { + var source = """ + partial class C + { + partial event System.Action E, F; + } + partial class C + { + static partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (7,40): error CS0763: Both partial member declarations must be static or neither may be static + // static partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberStaticDifference, "E").WithLocation(7, 40)); + } + + [Fact] + public void Difference_Unsafe_01() + { + var source = """ + partial class C + { + unsafe partial C(); + unsafe partial event System.Action E, F; + } + partial class C + { + partial C() { } + unsafe partial event System.Action E { add { } remove { } } + partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (8,13): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + // partial C() { } + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "C").WithLocation(8, 13), + // (10,33): error CS0764: Both partial member declarations must be unsafe or neither may be unsafe + // partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberUnsafeDifference, "F").WithLocation(10, 33)); + } + + [Fact] + public void Difference_Unsafe_02() + { + var source = """ + unsafe partial class C + { + partial C(int* p); + partial event System.Action E, F; + } + partial class C + { + partial C(int* p) { } + partial event System.Action E { add { int* p = null; } remove { } } + partial event System.Action F { add { int* p = null; } remove { } } + } + """; + CreateCompilation(source, options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics( + // (8,15): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // partial C(int* p) { } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(8, 15), + // (9,43): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // partial event System.Action E { add { int* p = null; } remove { } } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(9, 43), + // (10,43): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // partial event System.Action F { add { int* p = null; } remove { } } + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "int*").WithLocation(10, 43)); + } + + [Fact] + public void Difference_ExtendedModifiers() + { + var source = """ + partial class C1 + { + protected virtual partial event System.Action E, F; + protected partial event System.Action E { add { } remove { } } + protected virtual partial event System.Action F { add { } remove { } } + } + partial class C2 : C1 + { + protected override partial event System.Action E; + protected sealed partial event System.Action E { add { } remove { } } + protected new partial event System.Action F; + protected partial event System.Action F { add { } remove { } } + } + partial class C3 : C1 + { + protected sealed partial event System.Action E; + protected partial event System.Action E { add { } remove { } } + protected override partial event System.Action F; + protected override sealed partial event System.Action F { add { } remove { } } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (4,43): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // protected partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "E").WithLocation(4, 43), + // (10,50): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // protected sealed partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "E").WithLocation(10, 50), + // (12,43): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // protected partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "F").WithLocation(12, 43), + // (16,50): warning CS0114: 'C3.E' hides inherited member 'C1.E'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword. + // protected sealed partial event System.Action E; + Diagnostic(ErrorCode.WRN_NewOrOverrideExpected, "E").WithArguments("C3.E", "C1.E").WithLocation(16, 50), + // (16,50): error CS0238: 'C3.E' cannot be sealed because it is not an override + // protected sealed partial event System.Action E; + Diagnostic(ErrorCode.ERR_SealedNonOverride, "E").WithArguments("C3.E").WithLocation(16, 50), + // (17,43): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // protected partial event System.Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "E").WithLocation(17, 43), + // (19,59): error CS8800: Both partial member declarations must have identical combinations of 'virtual', 'override', 'sealed', and 'new' modifiers. + // protected override sealed partial event System.Action F { add { } remove { } } + Diagnostic(ErrorCode.ERR_PartialMemberExtendedModDifference, "F").WithLocation(19, 59)); + } + + [Fact] + public void Difference_RefKind() + { + var source = """ + partial class C1 + { + partial C1(int x); + partial C1(ref int x) { } + } + partial class C2 + { + partial C2(in int x); + partial C2(ref readonly int x) { } + } + partial class C3 + { + partial C3(ref int x); + partial C3(out int x) => throw null; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,13): error CS9275: Partial member 'C1.C1(int)' must have an implementation part. + // partial C1(int x); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C1").WithArguments("C1.C1(int)").WithLocation(3, 13), + // (4,13): error CS9276: Partial member 'C1.C1(ref int)' must have a definition part. + // partial C1(ref int x) { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C1").WithArguments("C1.C1(ref int)").WithLocation(4, 13), + // (8,13): error CS9275: Partial member 'C2.C2(in int)' must have an implementation part. + // partial C2(in int x); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C2").WithArguments("C2.C2(in int)").WithLocation(8, 13), + // (9,13): error CS9276: Partial member 'C2.C2(ref readonly int)' must have a definition part. + // partial C2(ref readonly int x) { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C2").WithArguments("C2.C2(ref readonly int)").WithLocation(9, 13), + // (9,13): error CS0663: 'C2' cannot define an overloaded constructor that differs only on parameter modifiers 'ref readonly' and 'in' + // partial C2(ref readonly int x) { } + Diagnostic(ErrorCode.ERR_OverloadRefKind, "C2").WithArguments("C2", "constructor", "ref readonly", "in").WithLocation(9, 13), + // (13,13): error CS9275: Partial member 'C3.C3(ref int)' must have an implementation part. + // partial C3(ref int x); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C3").WithArguments("C3.C3(ref int)").WithLocation(13, 13), + // (14,13): error CS9276: Partial member 'C3.C3(out int)' must have a definition part. + // partial C3(out int x) => throw null; + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "C3").WithArguments("C3.C3(out int)").WithLocation(14, 13), + // (14,13): error CS0663: 'C3' cannot define an overloaded constructor that differs only on parameter modifiers 'out' and 'ref' + // partial C3(out int x) => throw null; + Diagnostic(ErrorCode.ERR_OverloadRefKind, "C3").WithArguments("C3", "constructor", "out", "ref").WithLocation(14, 13)); + } + + [Fact] + public void Difference_Params() + { + var source = """ + using System.Collections.Generic; + partial class C1 + { + partial C1(object[] x); + partial C1(params object[] x) { } + } + partial class C2 + { + partial C2(int x, params IEnumerable y); + partial C2(int x, IEnumerable y) { } + } + partial class C3 + { + partial C3(params IEnumerable x, int y); + partial C3(IEnumerable x, int y) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (5,13): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter + // partial C1(params object[] x) { } + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "C1").WithLocation(5, 13), + // (10,13): error CS0758: Both partial member declarations must use a params parameter or neither may use a params parameter + // partial C2(int x, IEnumerable y) { } + Diagnostic(ErrorCode.ERR_PartialMemberParamsDifference, "C2").WithLocation(10, 13), + // (14,16): error CS0231: A params parameter must be the last parameter in a parameter list + // partial C3(params IEnumerable x, int y); + Diagnostic(ErrorCode.ERR_ParamsLast, "params IEnumerable x").WithLocation(14, 16)); + } + + [Fact] + public void Difference_ParameterNames() + { + var source = """ + var c = new C(x: 123); + + partial class C + { + public partial C(int x); + public partial C(int y) { System.Console.Write(y); } + } + """; + CompileAndVerify(source, + sourceSymbolValidator: validate, + symbolValidator: validate, + expectedOutput: "123") + .VerifyDiagnostics( + // (6,20): warning CS9256: Partial member declarations 'C.C(int x)' and 'C.C(int y)' have signature differences. + // public partial C(int y) { System.Console.Write(y); } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int x)", "C.C(int y)").WithLocation(6, 20)); + + static void validate(ModuleSymbol module) + { + var indexer = module.GlobalNamespace.GetMember("C..ctor"); + AssertEx.Equal("x", indexer.Parameters.Single().Name); + } + } + + [Fact] + public void Difference_ParameterNames_NameOf() + { + var source = """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A : Attribute { public A(string s) { } } + + partial class C + { + [A(nameof(p1))] + [A(nameof(p2))] // 1 + partial C( + [A(nameof(p1))] // 2 + [A(nameof(p2))] + int p1); + + [A(nameof(p1))] + [A(nameof(p2))] // 3 + partial C( + [A(nameof(p1))] // 4 + [A(nameof(p2))] + int p2) + { + Console.WriteLine(nameof(p1)); // 5 + Console.WriteLine(nameof(p2)); + } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (17,13): warning CS9256: Partial member declarations 'C.C(int p1)' and 'C.C(int p2)' have signature differences. + // partial C( + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int p1)", "C.C(int p2)").WithLocation(17, 13), + // (18,19): error CS0103: The name 'p1' does not exist in the current context + // [A(nameof(p1))] // 4 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(18, 19), + // (11,19): error CS0103: The name 'p1' does not exist in the current context + // [A(nameof(p1))] // 2 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(11, 19), + // (9,15): error CS0103: The name 'p2' does not exist in the current context + // [A(nameof(p2))] // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(9, 15), + // (16,15): error CS0103: The name 'p2' does not exist in the current context + // [A(nameof(p2))] // 3 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(16, 15), + // (22,34): error CS0103: The name 'p1' does not exist in the current context + // Console.WriteLine(nameof(p1)); // 5 + Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(22, 34)); + } + + [Fact] + public void Difference_OptionalParameters() + { + var source = """ + var c1 = new C1(); + var c2 = new C2(); + + partial class C1 + { + public partial C1(int x = 1); + public partial C1(int x) { } + } + partial class C2 + { + public partial C2(int x); + public partial C2(int x = 1) { } + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (2,14): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'C2.C2(int)' + // var c2 = new C2(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "C2").WithArguments("x", "C2.C2(int)").WithLocation(2, 14), + // (12,27): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial C2(int x = 1) { } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(12, 27)); + } + + [Fact] + public void Difference_DefaultParameterValues() + { + var source = """ + var c = new C(); + + partial class C + { + public partial C(int x = 1); + public partial C(int x = 2) { System.Console.Write(x); } + } + """; + CompileAndVerify(source, expectedOutput: "1").VerifyDiagnostics( + // (6,26): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // public partial C(int x = 2) { System.Console.Write(x); } + Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(6, 26)); + } + + [Fact] + public void Difference_Scoped() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + partial class C1 + { + partial C1(scoped ref int x); + partial C1(ref int x) { } + } + partial class C2 + { + partial C2(ref int x); + partial C2(scoped ref int x) { } + } + partial class C3 + { + partial C3([UnscopedRef] ref int x); + partial C3(ref int x) { } + } + partial class C4 + { + partial C4(ref int x); + partial C4([UnscopedRef] ref int x) { } + } + """; + CreateCompilation([source, UnscopedRefAttributeDefinition]).VerifyDiagnostics( + // (5,13): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition. + // partial C1(ref int x) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "C1").WithArguments("x").WithLocation(5, 13), + // (10,13): error CS8988: The 'scoped' modifier of parameter 'x' doesn't match partial definition. + // partial C2(scoped ref int x) { } + Diagnostic(ErrorCode.ERR_ScopedMismatchInParameterOfPartial, "C2").WithArguments("x").WithLocation(10, 13)); + } + + [Theory] + [InlineData("A(1)", "B(2)")] + [InlineData("B(2)", "A(1)")] + [InlineData("A(1), B(1)", "A(2), B(2)")] + public void Attributes(string declAttributes, string implAttributes) + { + var source1 = """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class A : Attribute { public A(int i) { } } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + class B : Attribute { public B(int i) { } } + """; + + var source2 = $$""" + public partial class C + { + [{{declAttributes}}] public partial C([{{declAttributes}}] int x); + [{{declAttributes}}] public partial event System.Action E; + } + """; + + var source3 = $$""" + public partial class C + { + [{{implAttributes}}] public partial C([{{implAttributes}}] int x) { } + [{{implAttributes}}] public partial event System.Action E { add { } remove { } } + } + """; + + CompileAndVerify([source1, source2, source3], + symbolValidator: validate, + sourceSymbolValidator: validate) + .VerifyDiagnostics(); + + CompileAndVerify([source1, source3, source2], + symbolValidator: validate, + sourceSymbolValidator: validate) + .VerifyDiagnostics(); + + void validate(ModuleSymbol module) + { + var ctor = module.GlobalNamespace.GetMember("C..ctor"); + assertEqual([declAttributes, implAttributes], ctor.GetAttributes()); + + var ctorParam = ctor.GetParameters().Single(); + assertEqual([implAttributes, declAttributes], ctorParam.GetAttributes()); + + var ev = module.GlobalNamespace.GetMember("C.E"); + assertEqual([declAttributes, implAttributes], ev.GetAttributes()); + + if (module is SourceModuleSymbol) + { + assertEqual([declAttributes, implAttributes], ((SourceConstructorSymbol)ctor).PartialImplementationPart!.GetAttributes()); + assertEqual([declAttributes, implAttributes], ((SourceEventSymbol)ev).PartialImplementationPart!.GetAttributes()); + } + } + + static void assertEqual(IEnumerable expected, ImmutableArray actual) + { + AssertEx.Equal(string.Join(", ", expected), actual.ToStrings().Join(", ")); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77254")] + public void Attributes_Locations() + { + // Note that [method:] is not allowed on partial events. + // Therefore users have no way to specify accessor attributes on the definition part. + // Ideally, this would be possible and we would join attributes from accessors with [method:] attributes from the event. + // However, current implementation of attribute matching is not strong enough to do that (there can be only one attribute owner symbol kind). + + var source = """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class A : Attribute { public A(int i) { } } + + partial class C + { + [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action E; + [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public partial event Action E + { + [A(3)] [method: A(13)] [param: A(23)] [return: A(33)] [event: A(43)] [field: A(53)] add { } + [A(4)] [method: A(14)] [param: A(24)] [return: A(34)] [event: A(44)] [field: A(54)] remove { } + } + + [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action F; + [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public extern partial event Action F; + } + """; + CompileAndVerify(source, + symbolValidator: validate, + sourceSymbolValidator: validate, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics( + // (8,13): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "event").WithLocation(8, 13), + // (8,29): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "event").WithLocation(8, 29), + // (8,44): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "event").WithLocation(8, 44), + // (8,75): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "event").WithLocation(8, 75), + // (9,13): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public partial event Action E + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "event").WithLocation(9, 13), + // (9,29): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public partial event Action E + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "event").WithLocation(9, 29), + // (9,44): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public partial event Action E + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "event").WithLocation(9, 44), + // (9,75): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public partial event Action E + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "event").WithLocation(9, 75), + // (11,64): warning CS0657: 'event' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(3)] [method: A(13)] [param: A(23)] [return: A(33)] [event: A(43)] [field: A(53)] add { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "event").WithArguments("event", "method, param, return").WithLocation(11, 64), + // (11,79): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(3)] [method: A(13)] [param: A(23)] [return: A(33)] [event: A(43)] [field: A(53)] add { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, param, return").WithLocation(11, 79), + // (12,64): warning CS0657: 'event' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(4)] [method: A(14)] [param: A(24)] [return: A(34)] [event: A(44)] [field: A(54)] remove { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "event").WithArguments("event", "method, param, return").WithLocation(12, 64), + // (12,79): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(4)] [method: A(14)] [param: A(24)] [return: A(34)] [event: A(44)] [field: A(54)] remove { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, param, return").WithLocation(12, 79), + // (15,29): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, event").WithLocation(15, 29), + // (15,44): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "method, event").WithLocation(15, 44), + // (15,75): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(11)] [param: A(21)] [return: A(31)] [event: A(41)] [field: A(51)] public partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, event").WithLocation(15, 75), + // (16,29): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public extern partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, event").WithLocation(16, 29), + // (16,44): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public extern partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "method, event").WithLocation(16, 44), + // (16,75): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(2)] [method: A(12)] [param: A(22)] [return: A(32)] [event: A(42)] [field: A(52)] public extern partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, event").WithLocation(16, 75)); + + static void validate(ModuleSymbol module) + { + var isSource = module is SourceModuleSymbol; + ReadOnlySpan compiledGeneratedAttr = isSource ? [] : ["System.Runtime.CompilerServices.CompilerGeneratedAttribute"]; + + var e = module.GlobalNamespace.GetMember("C.E"); + AssertEx.Equal(["A(1)", "A(41)", "A(2)", "A(42)"], e.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)", "A(13)"], e.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(23)"], e.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(33)"], e.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal(["A(4)", "A(14)"], e.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(24)"], e.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(34)"], e.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + + if (isSource) + { + var eImpl = ((SourceEventSymbol)e).PartialImplementationPart!; + AssertEx.Equal(["A(1)", "A(41)", "A(2)", "A(42)"], eImpl.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)", "A(13)"], eImpl.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(23)"], eImpl.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(33)"], eImpl.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal(["A(4)", "A(14)"], eImpl.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(24)"], eImpl.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(34)"], eImpl.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + } + + var f = module.GlobalNamespace.GetMember("C.F"); + AssertEx.Equal(["A(1)", "A(41)", "A(2)", "A(42)"], f.GetAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(11)", "A(12)"], f.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(22)", "A(21)"], f.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], f.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(11)", "A(12)"], f.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(22)", "A(21)"], f.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], f.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + + if (isSource) + { + var fImpl = ((SourceEventSymbol)f).PartialImplementationPart!; + AssertEx.Equal(["A(1)", "A(41)", "A(2)", "A(42)"], fImpl.GetAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(11)", "A(12)"], fImpl.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(22)", "A(21)"], fImpl.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], fImpl.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(11)", "A(12)"], fImpl.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(22)", "A(21)"], fImpl.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], fImpl.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + } + } + } + + [Fact] + public void Attributes_Duplicates() + { + var source = """ + using System; + + class A1 : Attribute; + class A2 : Attribute; + class A3 : Attribute; + + partial class C + { + [A1] [method: A2] partial C( // def + [A1] [param: A2] int x); + [A1] [method: A2] partial C( // impl + [A1] [param: A2] int x) { } + + [A1] [method: A2] partial event Action E; + [A1] [method: A2] partial event Action E + { + [A1] [method: A1] [param: A2] add { } + [A1] [method: A1] [param: A2] remove { } + } + + [A1] [method: A2] [param: A3] partial event Action F; + [A1] [method: A2] [param: A3] extern partial event Action F; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (10,10): error CS0579: Duplicate 'A1' attribute + // [A1] [param: A2] int x); + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(10, 10), + // (10,22): error CS0579: Duplicate 'A2' attribute + // [A1] [param: A2] int x); + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A2").WithArguments("A2").WithLocation(10, 22), + // (11,6): error CS0579: Duplicate 'A1' attribute + // [A1] [method: A2] partial C( // impl + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(11, 6), + // (11,19): error CS0579: Duplicate 'A2' attribute + // [A1] [method: A2] partial C( // impl + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A2").WithArguments("A2").WithLocation(11, 19), + // (14,11): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A1] [method: A2] partial event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "event").WithLocation(14, 11), + // (15,6): error CS0579: Duplicate 'A1' attribute + // [A1] [method: A2] partial event Action E + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(15, 6), + // (15,11): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A1] [method: A2] partial event Action E + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "event").WithLocation(15, 11), + // (17,23): error CS0579: Duplicate 'A1' attribute + // [A1] [method: A1] [param: A2] add { } + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(17, 23), + // (18,23): error CS0579: Duplicate 'A1' attribute + // [A1] [method: A1] [param: A2] remove { } + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(18, 23), + // (21,24): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A1] [method: A2] [param: A3] partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, event").WithLocation(21, 24), + // (21,31): error CS0579: Duplicate 'A3' attribute + // [A1] [method: A2] [param: A3] partial event Action F; + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A3").WithArguments("A3").WithLocation(21, 31), + // (21,31): error CS0579: Duplicate 'A3' attribute + // [A1] [method: A2] [param: A3] partial event Action F; + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A3").WithArguments("A3").WithLocation(21, 31), + // (22,6): error CS0579: Duplicate 'A1' attribute + // [A1] [method: A2] [param: A3] extern partial event Action F; + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A1").WithArguments("A1").WithLocation(22, 6), + // (22,19): error CS0579: Duplicate 'A2' attribute + // [A1] [method: A2] [param: A3] extern partial event Action F; + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "A2").WithArguments("A2").WithLocation(22, 19), + // (22,24): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A1] [method: A2] [param: A3] extern partial event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, event").WithLocation(22, 24)); + } + + [Fact] + public void Attributes_CallerInfo() + { + var source1 = """ + using System; + using System.Runtime.CompilerServices; + + public partial class C + { + public partial C(int x, + [CallerLineNumber] int a = -1, + [CallerFilePath] string b = "f", + [CallerMemberName] string c = "m", + [CallerArgumentExpression(nameof(x))] string d = "e"); + public partial C(int x, int a, string b, string c, string d) + { + Console.WriteLine($"x='{x}' a='{a}' b='{b}' c='{c}' d='{d}'"); + } + + public partial C(string x, + int a = -1, + string b = "f", + string c = "m", + string d = "e"); + public partial C(string x, + [CallerLineNumber] int a, + [CallerFilePath] string b, + [CallerMemberName] string c, + [CallerArgumentExpression(nameof(x))] string d) + { + Console.WriteLine($"x='{x}' a='{a}' b='{b}' c='{c}' d='{d}'"); + } + } + """; + + var source2 = (""" + var c1 = new C(40 + 2); + var c2 = new C("s"); + """, "file.cs"); + + var expectedDiagnostics = new[] + { + // (22,10): warning CS4024: The CallerLineNumberAttribute applied to parameter 'a' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // [CallerLineNumber] int a, + Diagnostic(ErrorCode.WRN_CallerLineNumberParamForUnconsumedLocation, "CallerLineNumber").WithArguments("a").WithLocation(22, 10), + // (23,10): warning CS4025: The CallerFilePathAttribute applied to parameter 'b' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // [CallerFilePath] string b, + Diagnostic(ErrorCode.WRN_CallerFilePathParamForUnconsumedLocation, "CallerFilePath").WithArguments("b").WithLocation(23, 10), + // (24,10): warning CS4026: The CallerMemberNameAttribute applied to parameter 'c' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // [CallerMemberName] string c, + Diagnostic(ErrorCode.WRN_CallerMemberNameParamForUnconsumedLocation, "CallerMemberName").WithArguments("c").WithLocation(24, 10), + // (25,10): warning CS8966: The CallerArgumentExpressionAttribute applied to parameter 'd' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments + // [CallerArgumentExpression(nameof(x))] string d) + Diagnostic(ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, "CallerArgumentExpression").WithArguments("d").WithLocation(25, 10) + }; + + CompileAndVerify([source1, source2, CallerArgumentExpressionAttributeDefinition], + expectedOutput: """ + x='42' a='1' b='file.cs' c='
$' d='40 + 2' + x='s' a='-1' b='f' c='m' d='e' + """) + .VerifyDiagnostics(expectedDiagnostics); + + // Although caller info attributes on the implementation part have no effect in source, + // they are written to metadata as is demonstrated below. + // See https://github.com/dotnet/roslyn/issues/73482. + + var lib = CreateCompilation([source1, CallerArgumentExpressionAttributeDefinition]) + .VerifyDiagnostics(expectedDiagnostics) + .EmitToPortableExecutableReference(); + + CompileAndVerify(CreateCompilation(source2, references: [lib]), + expectedOutput: """ + x='42' a='1' b='file.cs' c='
$' d='40 + 2' + x='s' a='2' b='file.cs' c='
$' d='"s"' + """) + .VerifyDiagnostics(); + } + + [Fact] + public void Attributes_Nullable() + { + var source = """ + #nullable enable + using System.Diagnostics.CodeAnalysis; + + new C(default(I1)!, null); + new C(default(I2)!, null); + new C(default(I3)!, null); + new C(default(I4)!, null); + new C(default(I5)!, null); + new C(default(I6)!, null); + new C(default(I7)!, null); + new C(default(I8)!, null); + + interface I1; + interface I2; + interface I3; + interface I4; + interface I5; + interface I6; + interface I7; + interface I8; + + partial class C + { + public partial C(I1 i, [AllowNull] object x); + public partial C(I1 i, object x) { x.ToString(); } + + public partial C(I2 i, object x); + public partial C(I2 i, [AllowNull] object x) { x.ToString(); } + + public partial C(I3 i, [AllowNull] object x); + public partial C(I3 i, object? x) { x.ToString(); } + + public partial C(I4 i, object? x); + public partial C(I4 i, [AllowNull] object x) { x.ToString(); } + + public partial C(I5 i, [DisallowNull] object? x); + public partial C(I5 i, object? x) { x.ToString(); } + + public partial C(I6 i, object? x); + public partial C(I6 i, [DisallowNull] object? x) { x.ToString(); } + + public partial C(I7 i, [DisallowNull] object? x); + public partial C(I7 i, object x) { x.ToString(); } + + public partial C(I8 i, object x); + public partial C(I8 i, [DisallowNull] object? x) { x.ToString(); } + } + """; + CreateCompilation([source, AllowNullAttributeDefinition, DisallowNullAttributeDefinition]).VerifyDiagnostics( + // (8,21): warning CS8625: Cannot convert null literal to non-nullable reference type. + // new C(default(I5)!, null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 21), + // (9,21): warning CS8625: Cannot convert null literal to non-nullable reference type. + // new C(default(I6)!, null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 21), + // (10,21): warning CS8625: Cannot convert null literal to non-nullable reference type. + // new C(default(I7)!, null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 21), + // (11,21): warning CS8625: Cannot convert null literal to non-nullable reference type. + // new C(default(I8)!, null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 21), + // (25,40): warning CS8602: Dereference of a possibly null reference. + // public partial C(I1 i, object x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(25, 40), + // (28,52): warning CS8602: Dereference of a possibly null reference. + // public partial C(I2 i, [AllowNull] object x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(28, 52), + // (31,20): warning CS9256: Partial member declarations 'C.C(I3 i, object x)' and 'C.C(I3 i, object? x)' have signature differences. + // public partial C(I3 i, object? x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(I3 i, object x)", "C.C(I3 i, object? x)").WithLocation(31, 20), + // (31,41): warning CS8602: Dereference of a possibly null reference. + // public partial C(I3 i, object? x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(31, 41), + // (34,20): warning CS9256: Partial member declarations 'C.C(I4 i, object? x)' and 'C.C(I4 i, object x)' have signature differences. + // public partial C(I4 i, [AllowNull] object x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(I4 i, object? x)", "C.C(I4 i, object x)").WithLocation(34, 20), + // (34,52): warning CS8602: Dereference of a possibly null reference. + // public partial C(I4 i, [AllowNull] object x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(34, 52), + // (43,20): warning CS9256: Partial member declarations 'C.C(I7 i, object? x)' and 'C.C(I7 i, object x)' have signature differences. + // public partial C(I7 i, object x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(I7 i, object? x)", "C.C(I7 i, object x)").WithLocation(43, 20), + // (46,20): warning CS9256: Partial member declarations 'C.C(I8 i, object x)' and 'C.C(I8 i, object? x)' have signature differences. + // public partial C(I8 i, [DisallowNull] object? x) { x.ToString(); } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(I8 i, object x)", "C.C(I8 i, object? x)").WithLocation(46, 20)); + } + + [Fact] + public void Attributes_Obsolete() + { + var source = """ + using System; + + var c = new C(); + c = new C(2); + c.E += null; + c.E -= null; + c.F += null; + c.F -= null; + c.G += null; + c.G -= null; + c.H += null; + c.H -= null; + + partial class C + { + [Obsolete] public partial C(); + public partial C() { M(); } + + public partial C(int x = X); + [Obsolete] public partial C(int x) { M(); } + + [Obsolete] public partial event Action E; + public partial event Action E { add { M(); } remove { M(); } } + + public partial event Action F; + public partial event Action F + { + [Obsolete] add { M(); } + remove { M(); } + } + + [Obsolete] public partial event Action G; + public extern partial event Action G; + + [method: Obsolete] public partial event Action H; + public extern partial event Action H; + + [Obsolete] void M() { } + [Obsolete] const int X = 1; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (3,9): warning CS0612: 'C.C()' is obsolete + // var c = new C(); + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C()").WithArguments("C.C()").WithLocation(3, 9), + // (4,5): warning CS0612: 'C.C(int)' is obsolete + // c = new C(2); + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "new C(2)").WithArguments("C.C(int)").WithLocation(4, 5), + // (5,1): warning CS0612: 'C.E' is obsolete + // c.E += null; + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c.E").WithArguments("C.E").WithLocation(5, 1), + // (6,1): warning CS0612: 'C.E' is obsolete + // c.E -= null; + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c.E").WithArguments("C.E").WithLocation(6, 1), + // (9,1): warning CS0612: 'C.G' is obsolete + // c.G += null; + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c.G").WithArguments("C.G").WithLocation(9, 1), + // (10,1): warning CS0612: 'C.G' is obsolete + // c.G -= null; + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "c.G").WithArguments("C.G").WithLocation(10, 1), + // (28,10): error CS8423: Attribute 'System.ObsoleteAttribute' is not valid on event accessors. It is only valid on 'class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [Obsolete] add { M(); } + Diagnostic(ErrorCode.ERR_AttributeNotOnEventAccessor, "Obsolete").WithArguments("System.ObsoleteAttribute", "class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(28, 10), + // (29,18): warning CS0612: 'C.M()' is obsolete + // remove { M(); } + Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "M()").WithArguments("C.M()").WithLocation(29, 18), + // (35,14): error CS8423: Attribute 'System.ObsoleteAttribute' is not valid on event accessors. It is only valid on 'class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [method: Obsolete] public partial event Action H; + Diagnostic(ErrorCode.ERR_AttributeNotOnEventAccessor, "Obsolete").WithArguments("System.ObsoleteAttribute", "class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(35, 14)); + } +} diff --git a/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs b/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs index 55808b02151ef..12482bd5997b1 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefReadonlyParameterTests.cs @@ -8664,4 +8664,51 @@ static void Main() // d = (ref int x) => { x = 42; }; // should be an error Diagnostic(ErrorCode.ERR_BadParamRef, "x").WithArguments("1", "in").WithLocation(7, 22)); } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/77528")] + public void UnassignedField( + [CombinatorialValues("in", "ref")] string arg, + [CombinatorialValues("ref readonly", "in")] string param) + { + var source = $$""" + #pragma warning disable CS9191 // The 'ref' modifier for argument corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. + + M({{arg}} C.F); + + static void M({{param}} int i) { } + + static class C + { + public static int F; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (9,23): warning CS0649: Field 'C.F' is never assigned to, and will always have its default value 0 + // public static int F; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("C.F", "0").WithLocation(9, 23)); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/77528")] + public void UnassignedField_NamedArguments( + [CombinatorialValues("in", "ref")] string arg, + [CombinatorialValues("ref readonly", "in")] string param) + { + var source = $$""" + #pragma warning disable CS9191 // The 'ref' modifier for argument corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. + + M(j: ref C.F1, i: {{arg}} C.F2); + + static void M({{param}} int i, ref int j) { } + + static class C + { + public static int F1; + public static int F2; + } + """; + CreateCompilation(source).VerifyDiagnostics( + // (10,23): warning CS0649: Field 'C.F2' is never assigned to, and will always have its default value 0 + // public static int F2; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F2").WithArguments("C.F2", "0").WithLocation(10, 23)); + } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs new file mode 100644 index 0000000000000..49b55402cfecc --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs @@ -0,0 +1,31213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#nullable disable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.VisualBasic; +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Sdk; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; + +[CompilerTrait(CompilerFeature.Extensions)] +public class ExtensionTests : CompilingTestBase +{ + private static string ExpectedOutput(string output) + { + return ExecutionConditionUtil.IsMonoOrCoreClr ? output : null; + } + + private static void VerifyTypeIL(CompilationVerifier compilation, string typeName, string expected) + { + // .Net Core has different assemblies for the same standard library types as .Net Framework, meaning that that the emitted output will be different to the expected if we run them .Net Core + // Since we do not expect there to be any meaningful differences between output for .Net Core and .Net Framework, we will skip these tests on .Net Framework + if (ExecutionConditionUtil.IsCoreClr) + { + compilation.VerifyTypeIL(typeName, expected); + } + } + + private static void AssertEqualAndNoDuplicates(string[] expected, string[] actual) + { + Assert.True(expected.All(new HashSet().Add), $"Duplicates were found in '{nameof(expected)}'"); + Assert.True(actual.All(new HashSet().Add), $"Duplicates were found in '{nameof(actual)}'"); + AssertEx.SetEqual(expected, actual); + } + + private static void AssertExtensionDeclaration(INamedTypeSymbol symbol) + { + // Verify things that are common for all extension types + Assert.Equal(TypeKind.Extension, symbol.TypeKind); + Assert.True(symbol.IsExtension); + Assert.Null(symbol.BaseType); + Assert.Empty(symbol.Interfaces); + Assert.Empty(symbol.AllInterfaces); + Assert.True(symbol.IsReferenceType); + Assert.False(symbol.IsValueType); + Assert.False(symbol.IsAnonymousType); + Assert.False(symbol.IsTupleType); + Assert.False(symbol.IsNativeIntegerType); + Assert.Equal(SpecialType.None, symbol.SpecialType); + Assert.False(symbol.IsRefLikeType); + Assert.False(symbol.IsUnmanagedType); + Assert.False(symbol.IsReadOnly); + Assert.False(symbol.IsRecord); + Assert.Equal(CodeAnalysis.NullableAnnotation.None, symbol.NullableAnnotation); + Assert.Throws(() => { symbol.WithNullableAnnotation(CodeAnalysis.NullableAnnotation.Annotated); }); + + Assert.False(symbol.IsScriptClass); + Assert.False(symbol.IsImplicitClass); + Assert.False(symbol.IsComImport); + Assert.False(symbol.IsFileLocal); + Assert.Null(symbol.DelegateInvokeMethod); + Assert.Null(symbol.EnumUnderlyingType); + Assert.Null(symbol.AssociatedSymbol); + Assert.False(symbol.MightContainExtensionMethods); + Assert.Null(symbol.TupleUnderlyingType); + Assert.True(symbol.TupleElements.IsDefault); + Assert.False(symbol.IsSerializable); + Assert.Null(symbol.NativeIntegerUnderlyingType); + + Assert.Equal(SymbolKind.NamedType, symbol.Kind); + Assert.Equal("", symbol.Name); + Assert.Equal(SpecialType.None, symbol.SpecialType); + Assert.True(symbol.IsDefinition); + Assert.False(symbol.IsStatic); + Assert.False(symbol.IsVirtual); + Assert.False(symbol.IsOverride); + Assert.False(symbol.IsAbstract); + Assert.True(symbol.IsSealed); + Assert.False(symbol.IsExtern); + Assert.False(symbol.IsImplicitlyDeclared); + Assert.False(symbol.CanBeReferencedByName); + Assert.Equal(Accessibility.Public, symbol.DeclaredAccessibility); + + var namedTypeSymbol = symbol.GetSymbol(); + Assert.False(namedTypeSymbol.HasSpecialName); + Assert.False(namedTypeSymbol.IsImplicitlyDeclared); + } + + [Fact] + public void EmptyExtension() + { + var src = """ +public static class Extensions +{ + extension(object) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + } // end of class <>E__0 +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Empty(symbol.MemberNames); + Assert.Empty(symbol.InstanceConstructors); + Assert.Empty(symbol.StaticConstructors); + Assert.Empty(symbol.Constructors); + + Assert.Equal(0, symbol.Arity); + Assert.False(symbol.IsGenericType); + Assert.False(symbol.IsUnboundGenericType); + Assert.Empty(symbol.TypeParameters); + Assert.Empty(symbol.TypeArguments); + Assert.Same(symbol, symbol.OriginalDefinition); + Assert.Same(symbol, symbol.ConstructedFrom); + Assert.Equal("Extensions", symbol.ContainingSymbol.Name); + Assert.Equal("Extensions", symbol.ContainingType.Name); + Assert.Equal("<>E__0", symbol.MetadataName); + + var member = symbol.ContainingType.GetMembers().Single(); + Assert.Equal("Extensions.<>E__0", member.ToTestDisplayString()); + + var format = new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); + Assert.Equal("Extensions.extension(System.Object)", symbol.ToDisplayString(format)); + + format = new SymbolDisplayFormat(kindOptions: SymbolDisplayKindOptions.IncludeTypeKeyword); + Assert.Equal("extension(Object)", symbol.ToDisplayString(format)); + + format = new SymbolDisplayFormat(); + Assert.Equal("extension(Object)", symbol.ToDisplayString(format)); + + format = new SymbolDisplayFormat(compilerInternalOptions: SymbolDisplayCompilerInternalOptions.UseMetadataMemberNames); + Assert.Equal("<>E__0", symbol.ToDisplayString(format)); + + var comp5 = CreateCompilation(src); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object) { } + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact] + public void TypeParameters_01() + { + // Unconstrained type parameter + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + } // end of class <>E__0`1 +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Equal(1, symbol.Arity); + Assert.True(symbol.IsGenericType); + Assert.False(symbol.IsUnboundGenericType); + Assert.Equal(["T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["T"], symbol.TypeArguments.ToTestDisplayStrings()); + Assert.Same(symbol, symbol.OriginalDefinition); + Assert.Same(symbol, symbol.ConstructedFrom); + Assert.Equal("Extensions", symbol.ContainingSymbol.Name); + Assert.Equal("Extensions", symbol.ContainingType.Name); + Assert.Equal("<>E__0`1", symbol.MetadataName); + + var member = symbol.ContainingType.GetMembers().Single(); + Assert.Equal("Extensions.<>E__0", member.ToTestDisplayString()); + + var constructed = symbol.Construct(comp.GetSpecialType(SpecialType.System_Int32)); + Assert.True(constructed.IsExtension); + Assert.Equal("Extensions.<>E__0", constructed.ToTestDisplayString()); + Assert.Equal("<>E__0`1", constructed.MetadataName); + Assert.NotSame(symbol, constructed); + Assert.Same(symbol, constructed.OriginalDefinition); + Assert.Same(symbol, constructed.ConstructedFrom); + + var unbound = symbol.ConstructUnboundGenericType(); + Assert.Equal("Extensions.<>E__0<>", unbound.ToTestDisplayString()); + Assert.True(unbound.IsUnboundGenericType); + Assert.NotSame(symbol, unbound); + Assert.Same(symbol, unbound.OriginalDefinition); + Assert.Same(symbol, unbound.ConstructedFrom); + } + + [Fact] + public void TypeParameters_02() + { + // Constrained type parameter + var src = """ +public static class Extensions +{ + extension(T) where T : struct { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + } // end of class <>E__0`1 +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + + Assert.Equal(1, symbol.Arity); + Assert.True(symbol.IsGenericType); + Assert.Equal(["T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["T"], symbol.TypeArguments.ToTestDisplayStrings()); + Assert.True(symbol.TypeParameters.Single().IsValueType); + Assert.False(symbol.TypeParameters.Single().IsReferenceType); + Assert.Empty(symbol.TypeParameters.Single().ConstraintTypes); + + var format = new SymbolDisplayFormat(genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints); + Assert.Equal("extension(T) where T : struct", symbol.ToDisplayString(format)); + } + + [Fact] + public void TypeParameters_03() + { + // Constraint on undefined type parameter + var src = """ +public static class Extensions +{ + extension(object) where T : struct { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,23): error CS0080: Constraints are not allowed on non-generic declarations + // extension(object) where T : struct { } + Diagnostic(ErrorCode.ERR_ConstraintOnlyAllowedOnGenericDecl, "where").WithLocation(3, 23)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(0, symbol.Arity); + Assert.False(symbol.IsGenericType); + Assert.Empty(symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Empty(symbol.TypeArguments.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_04() + { + // Type parameter variance + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1960: Invalid variance modifier. Only interface and delegate type parameters can be specified as variant. + // extension(object) { } + Diagnostic(ErrorCode.ERR_IllegalVarianceSyntax, "out").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(1, symbol.Arity); + Assert.True(symbol.IsGenericType); + Assert.Equal(["out T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["out T"], symbol.TypeArguments.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_05() + { + // Duplicate type parameter + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,18): error CS0692: Duplicate type parameter 'T' + // extension(T) { } + Diagnostic(ErrorCode.ERR_DuplicateTypeParameter, "T").WithArguments("T").WithLocation(3, 18), + // (3,21): error CS0229: Ambiguity between 'T' and 'T' + // extension(T) { } + Diagnostic(ErrorCode.ERR_AmbigMember, "T").WithArguments("T", "T").WithLocation(3, 21), + // (3,21): error CS9295: The extended type 'T' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(T) { } + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T").WithArguments("T", "T").WithLocation(3, 21), + // (3,21): error CS9295: The extended type 'T' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(T) { } + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T").WithArguments("T", "T").WithLocation(3, 21)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(2, symbol.Arity); + Assert.Equal(["T", "T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["T", "T"], symbol.TypeArguments.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_05_Nested() + { + // Duplicate type parameter + var src = """ +public static class Extensions +{ + extension(C) { } +} +class C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,18): error CS0692: Duplicate type parameter 'T' + // extension(C) { } + Diagnostic(ErrorCode.ERR_DuplicateTypeParameter, "T").WithArguments("T").WithLocation(3, 18), + // (3,21): error CS9295: The extended type 'C' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(C) { } + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "C").WithArguments("C", "T").WithLocation(3, 21), + // (3,21): error CS9295: The extended type 'C' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(C) { } + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "C").WithArguments("C", "T").WithLocation(3, 21), + // (3,23): error CS0229: Ambiguity between 'T' and 'T' + // extension(C) { } + Diagnostic(ErrorCode.ERR_AmbigMember, "T").WithArguments("T", "T").WithLocation(3, 23)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(2, symbol.Arity); + Assert.Equal(["T", "T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["T", "T"], symbol.TypeArguments.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_06() + { + // Type parameter same as outer type parameter + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 5), + // (3,15): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Extensions' + // extension(object) { } + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Extensions").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(1, symbol.Arity); + Assert.Equal(["T"], symbol.TypeParameters.ToTestDisplayStrings()); + Assert.Equal(["T"], symbol.TypeArguments.ToTestDisplayStrings()); + + var container = symbol.ContainingType; + var substitutedExtension = (INamedTypeSymbol)container.Construct(comp.GetSpecialType(SpecialType.System_Int32)).GetMembers().Single(); + Assert.Equal("Extensions.<>E__0", substitutedExtension.ToTestDisplayString()); + Assert.True(substitutedExtension.IsExtension); + } + + [Fact] + public void TypeParameters_07() + { + // Reserved type name for type parameter + var src = $$""" +public static class Extensions +{ + extension(record) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): warning CS8860: Types and aliases should not be named 'record'. + // extension(object) { } + Diagnostic(ErrorCode.WRN_RecordNamedDisallowed, "record").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["record"], symbol.TypeParameters.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_08() + { + // Reserved type name for type parameter + var src = $$""" +public static class Extensions +{ + extension(file) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9056: Types and aliases cannot be named 'file'. + // extension(object) { } + Diagnostic(ErrorCode.ERR_FileTypeNameDisallowed, "file").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["file"], symbol.TypeParameters.ToTestDisplayStrings()); + } + + [Fact] + public void TypeParameters_09() + { + // Member name same as type parameter + var src = $$""" +public static class Extensions +{ + extension(T) + { + void T() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void TypeParameters_10() + { + var src = $$""" +#nullable enable +public static class Extensions +{ + extension(T) where T : notnull + { + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp); + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [netstandard]System.Object + { + .custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Methods + .method private hidebysig specialname static + void '$' ( + !T '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x209d + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + } // end of class <>E__0`1 +} // end of class Extensions +"""); + } + + [Fact] + public void BadContainer_Generic() + { + var src = """ +object.M(); + +public static class Extensions +{ + extension(object) { public static void M() { } } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8), + // (5,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { public static void M() { } } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 5)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + Assert.True(symbol.IsGenericType); + var members = symbol.ContainingType.GetMembers(); + Assert.Equal(["Extensions.<>E__0", "void Extensions.M()"], members.ToTestDisplayStrings()); + } + + [Fact] + public void BadContainer_TopLevel() + { + var src = """ +object.M(); + +extension(object) { public static void M() { } } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8), + // (3,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { public static void M() { } } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 1)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + Assert.Null(symbol.ContainingType); + Assert.Equal("<>E__0", symbol.ToTestDisplayString()); + } + + [Fact] + public void BadContainer_Nested() + { + var src = """ +object.M(); + +public static class Extensions +{ + static void Method() + { + object.M(); + } + + public static class Extensions2 + { + extension(object) { public static void M() { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8), + // (7,16): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(7, 16), + // (12,9): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { public static void M() { } } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(12, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var nestedExtension = tree.GetRoot().DescendantNodes().OfType().Last(); + + var nestedExtensionSymbol = model.GetDeclaredSymbol(nestedExtension); + AssertExtensionDeclaration(nestedExtensionSymbol); + Assert.Equal("Extensions.Extensions2", nestedExtensionSymbol.ContainingType.ToTestDisplayString()); + var members = nestedExtensionSymbol.ContainingType.GetMembers(); + Assert.Equal(["Extensions.Extensions2.<>E__0", "void Extensions.Extensions2.M()"], members.ToTestDisplayStrings()); + } + + [Fact] + public void BadContainer_NestedInExtension() + { + var src = """ +string.M(); + +public static class Extensions +{ + static void Method2() + { + string.M(); + } + + extension(object) + { + static void Method() + { + string.M(); + } + + extension(string) { public static void M() { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'string' does not contain a definition for 'M' + // string.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("string", "M").WithLocation(1, 8), + // (7,16): error CS0117: 'string' does not contain a definition for 'M' + // string.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("string", "M").WithLocation(7, 16), + // (14,20): error CS0117: 'string' does not contain a definition for 'M' + // string.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("string", "M").WithLocation(14, 20), + // (17,9): error CS9282: Extension declarations can include only methods or properties + // extension(string) { public static void M() { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(17, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var nestedExtension = tree.GetRoot().DescendantNodes().OfType().Last(); + + var nestedExtensionSymbol = model.GetDeclaredSymbol(nestedExtension); + AssertExtensionDeclaration(nestedExtensionSymbol); + Assert.Equal("Extensions.<>E__0", nestedExtensionSymbol.ContainingType.ToTestDisplayString()); + Assert.Equal(["void Extensions.<>E__0.$(System.Object)", "void Extensions.<>E__0.Method()", "Extensions.<>E__0.<>E__0"], nestedExtensionSymbol.ContainingType.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void BadContainer_TypeKind() + { + var src = """ +object.M(); + +public static struct Extensions +{ + extension(object) { public static void M() { } } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8), + // (3,22): error CS0106: The modifier 'static' is not valid for this item + // public static struct Extensions + Diagnostic(ErrorCode.ERR_BadMemberFlag, "Extensions").WithArguments("static").WithLocation(3, 22), + // (5,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { public static void M() { } } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 5)); + } + + [Fact] + public void BadContainer_NotStatic() + { + var src = """ +object.M(); + +public class Extensions +{ + extension(object) { public static void M() { } } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8), + // (5,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { public static void M() { } } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 5)); + } + + [Theory, CombinatorialData] + public void ExtensionIndex_InPartial(bool reverseOrder) + { + var src1 = """ +public static partial class Extensions +{ + extension(object) { } +} +"""; + var src2 = """ +public static partial class Extensions +{ +} +"""; + + var src = reverseOrder ? new[] { src2, src1 } : new[] { src1, src2 }; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + } // end of class <>E__0 +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[reverseOrder ? 1 : 0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + Assert.Equal("<>E__0", symbol.MetadataName); + } + + [Fact] + public void ExtensionIndex_InPartial_TwoExtension() + { + var src1 = """ +public static partial class Extensions +{ + extension(T t) { } +} +"""; + var src2 = """ +public static partial class Extensions +{ + extension((T1, T2) t) { } +} +"""; + + var comp = CreateCompilation([src1, src2]); + comp.VerifyEmitDiagnostics(); + + var tree1 = comp.SyntaxTrees[0]; + var model1 = comp.GetSemanticModel(tree1); + var extension1 = tree1.GetRoot().DescendantNodes().OfType().Single(); + var symbol1 = model1.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", sourceExtension1.ToTestDisplayString()); + + var tree2 = comp.SyntaxTrees[1]; + var extension2 = tree2.GetRoot().DescendantNodes().OfType().Single(); + var model2 = comp.GetSemanticModel(tree2); + var symbol2 = model2.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__1`2", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__1", sourceExtension2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_InPartial_TwoExtensions() + { + var src = """ +public static partial class Extensions +{ + extension(T) { } +} +public static partial class Extensions +{ + extension((T1, T2)) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + Assert.Equal("<>E__1`2", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__1", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_TwoExtensions_SameSignatures_01() + { + var src = """ +public static class Extensions +{ + extension(T) { } + extension(T) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__1`1", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__1", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_TwoExtensions_SameSignatures_02() + { + var src = """ +public static class Extensions +{ + extension(T) { } + class C { } + extension(T) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__2`1", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__2", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_TwoExtensions_SameSignatures_03() + { + var src = """ +extension(T) { } +class C { } +extension(T) { } +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(1, 1), + // (3,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 1)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__2`1", symbol2.MetadataName); + Assert.Equal("<>E__2", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_TwoExtensions_DifferentSignatures_01() + { + var src = """ +public static class Extensions +{ + extension(T) { } + extension((T1, T2) t) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__1`2", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__1", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_TwoExtensions_DifferentSignatures_02() + { + var src = """ +public static class Extensions +{ + extension(T) { } + extension(T1) where T1 : struct { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(extension1); + var sourceExtension1 = symbol1.GetSymbol(); + Assert.Equal("<>E__0`1", symbol1.MetadataName); + Assert.Equal("Extensions.<>E__0", symbol1.ToTestDisplayString()); + + var extension2 = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol2 = model.GetDeclaredSymbol(extension2); + var sourceExtension2 = symbol2.GetSymbol(); + Assert.Equal("<>E__1`1", symbol2.MetadataName); + Assert.Equal("Extensions.<>E__1", symbol2.ToTestDisplayString()); + } + + [Fact] + public void ExtensionIndex_ElevenExtensions() + { + var src = """ +public static class Extensions +{ + extension(T1 o1) { } + extension(T2 o2) { } + extension(T3 o3) { } + extension(T4 o4) { } + extension(T5 o5) { } + extension(T6 o6) { } + extension(T7 o7) { } + extension(T8 o8) { } + extension(T9 o9) { } + extension(T10 o10) { } + extension(T11 o11) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Last(); + var symbol = model.GetDeclaredSymbol(extension); + var sourceExtension = symbol.GetSymbol(); + Assert.Equal("<>E__10`1", symbol.MetadataName); + Assert.Equal("Extensions.<>E__10", symbol.ToTestDisplayString()); + } + + [Fact] + public void Member_InstanceMethod() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig + instance void M () cil managed + { + // Method begins at RVA 0x2069 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + void M ( + object o + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["M"], symbol.MemberNames); + Assert.Equal(["", "M"], symbol.ContainingType.MemberNames); + Assert.Equal("void Extensions.<>E__0.M()", symbol.GetMember("M").ToTestDisplayString()); + } + + [Fact] + public void Member_ExtensionMethod() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + void M(this int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,14): error CS1109: Extension methods must be defined in a top level static class; is a nested class + // void M(this int i) { } + Diagnostic(ErrorCode.ERR_ExtensionMethodsDecl, "M").WithArguments("").WithLocation(5, 14)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var method = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(method); + Assert.Equal("void Extensions.<>E__0.M(this System.Int32 i)", symbol.ToTestDisplayString()); + Assert.True(symbol.IsExtensionMethod); + } + + [Fact] + public void Member_StaticMethod() + { + var src = """ +public static class Extensions +{ + extension(object) + { + static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig static + void M () cil managed + { + // Method begins at RVA 0x2069 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + void M () cil managed + { + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["M"], symbol.MemberNames); + Assert.Equal("void Extensions.<>E__0.M()", symbol.GetMember("M").ToTestDisplayString()); + } + + [Fact] + public void Member_InstanceMethod_ExplicitInterfaceImplementation() + { + var src = """ +public interface I +{ + void M(); +} + +public static class Extensions +{ + extension(object o) + { + void I.M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (10,16): error CS0541: 'Extensions.extension(object).M()': explicit interface declaration can only be declared in a class, record, struct or interface + // void I.M() { } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "M").WithArguments("Extensions.extension(object).M()").WithLocation(10, 16)); + } + + [Fact] + public void Member_InstanceMethod_ShadowingTypeParameter() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + void M() { } + void M2(int T) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,16): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // void M() { } + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(5, 16), + // (6,21): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // void M2(int T) { } + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(6, 21) + ); + } + + [Fact] + public void Member_InstanceProperty() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + int Property { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig specialname + instance int32 get_Property () cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_Property + .method private hidebysig specialname + instance void set_Property ( + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::set_Property + // Properties + .property instance int32 Property() + { + .get instance int32 Extensions/'<>E__0'::get_Property() + .set instance void Extensions/'<>E__0'::set_Property(int32) + } + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + int32 get_Property ( + object o + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 3 (0x3) + .maxstack 8 + IL_0000: ldc.i4.s 42 + IL_0002: ret + } // end of method Extensions::get_Property + .method private hidebysig specialname static + void set_Property ( + object o, + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::set_Property +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["Property"], symbol.MemberNames); + Assert.Equal("System.Int32 Extensions.<>E__0.Property { get; set; }", symbol.GetMember("Property").ToTestDisplayString()); + + AssertEx.Equal([ + "void Extensions.<>E__0.$(System.Object o)", + "System.Int32 Extensions.<>E__0.Property { get; set; }", + "System.Int32 Extensions.<>E__0.Property.get", + "void Extensions.<>E__0.Property.set"], + symbol.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Member_InstanceProperty_Auto() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + int Property { get; set; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,13): error CS9282: Extension declarations can include only methods or properties + // int Property { get; set; } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Property").WithLocation(5, 13)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["Property"], symbol.MemberNames); + Assert.Equal("System.Int32 Extensions.<>E__0.Property { get; set; }", symbol.GetMember("Property").ToTestDisplayString()); + + AssertEx.Equal([ + "void Extensions.<>E__0.$(System.Object o)", + "System.Int32 Extensions.<>E__0.k__BackingField", + "System.Int32 Extensions.<>E__0.Property { get; set; }", + "System.Int32 Extensions.<>E__0.Property.get", + "void Extensions.<>E__0.Property.set"], + symbol.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Member_InstanceProperty_ExplicitInterfaceImplementation() + { + var src = """ +public interface I +{ + int Property { get; set; } +} +public static class Extensions +{ + extension(object o) + { + int I.Property { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (9,15): error CS0541: 'Extensions.extension(object).Property': explicit interface declaration can only be declared in a class, record, struct or interface + // int I.Property { get => 42; set { } } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "Property").WithArguments("Extensions.extension(object).Property").WithLocation(9, 15)); + } + + [Fact] + public void Member_StaticProperty() + { + var src = """ +public static class Extensions +{ + extension(object) + { + static int Property { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions +extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object '' + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig specialname static + int32 get_Property () cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_Property + .method private hidebysig specialname static + void set_Property ( + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::set_Property + // Properties + .property int32 Property() + { + .get int32 Extensions/'<>E__0'::get_Property() + .set void Extensions/'<>E__0'::set_Property(int32) + } + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + int32 get_Property () cil managed + { + // Method begins at RVA 0x2067 + // Code size 3 (0x3) + .maxstack 8 + IL_0000: ldc.i4.s 42 + IL_0002: ret + } // end of method Extensions::get_Property + .method private hidebysig specialname static + void set_Property ( + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::set_Property +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["Property"], symbol.MemberNames); + Assert.Equal("System.Int32 Extensions.<>E__0.Property { get; set; }", symbol.GetMember("Property").ToTestDisplayString()); + } + + [Fact] + public void Member_StaticProperty_Auto() + { + var src = """ +public static class Extensions +{ + extension(object) + { + static int Property { get; set; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,20): error CS9282: Extension declarations can include only methods or properties + // static int Property { get; set; } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Property").WithLocation(5, 20)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["Property"], symbol.MemberNames); + AssertEx.Equal([ + "void Extensions.<>E__0.$(System.Object)", + "System.Int32 Extensions.<>E__0.k__BackingField", + "System.Int32 Extensions.<>E__0.Property { get; set; }", + "System.Int32 Extensions.<>E__0.Property.get", + "void Extensions.<>E__0.Property.set"], + symbol.GetMembers().ToTestDisplayStrings()); + + Assert.Equal("System.Int32 Extensions.<>E__0.Property { get; set; }", symbol.GetMember("Property").ToTestDisplayString()); + } + + [Fact] + public void Member_InstanceIndexer() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + int this[int i] { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + + VerifyTypeIL(verifier, "Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [netstandard]System.Object +{ + .custom instance void [netstandard]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [netstandard]System.Object + { + .custom instance void [netstandard]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [netstandard]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig specialname + instance int32 get_Item ( + int32 i + ) cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_Item + .method private hidebysig specialname + instance void set_Item ( + int32 i, + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::set_Item + // Properties + .property instance int32 Item( + int32 i + ) + { + .get instance int32 Extensions/'<>E__0'::get_Item(int32) + .set instance void Extensions/'<>E__0'::set_Item(int32, int32) + } + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + int32 get_Item ( + object o, + int32 i + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 3 (0x3) + .maxstack 8 + IL_0000: ldc.i4.s 42 + IL_0002: ret + } // end of method Extensions::get_Item + .method private hidebysig specialname static + void set_Item ( + object o, + int32 i, + int32 'value' + ) cil managed + { + // Method begins at RVA 0x206b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::set_Item +} // end of class Extensions +"""); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Equal(["this[]"], symbol.MemberNames); + Assert.Equal("System.Int32 Extensions.<>E__0.this[System.Int32 i] { get; set; }", symbol.GetMember("this[]").ToTestDisplayString()); + + AssertEx.Equal([ + "void Extensions.<>E__0.$(System.Object o)", + "System.Int32 Extensions.<>E__0.this[System.Int32 i] { get; set; }", + "System.Int32 Extensions.<>E__0.this[System.Int32 i].get", + "void Extensions.<>E__0.this[System.Int32 i].set"], + symbol.GetMembers().ToTestDisplayStrings()); + + var comp5 = CreateCompilation(src); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object o) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact] + public void Member_StaticIndexer() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + static int this[int i] { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object o) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5), + // (5,20): error CS0106: The modifier 'static' is not valid for this item + // static int this[int i] { get => 42; set { } } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "this").WithArguments("static").WithLocation(5, 20) + ); + } + + [Fact] + public void Member_Type() + { + var src = """ +public static class Extensions +{ + extension(object) + { + class Nested { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,15): error CS9282: Extension declarations can include only methods or properties + // class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(5, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + Assert.Empty(symbol.MemberNames); + Assert.Equal(["void Extensions.<>E__0.$(System.Object)", "Extensions.<>E__0.Nested"], symbol.GetMembers().ToTestDisplayStrings()); + Assert.Equal(["Extensions.<>E__0.Nested"], symbol.GetTypeMembers().ToTestDisplayStrings()); + Assert.Equal("Extensions.<>E__0.Nested", symbol.GetTypeMember("Nested").ToTestDisplayString()); + } + + [Fact] + public void Member_Constructor() + { + var src = """ +public static class Extensions +{ + extension(object) { Extensions() { } } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,25): error CS1520: Method must have a return type + // extension(object) { Extensions() { } } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "Extensions").WithLocation(3, 25), + // (3,25): error CS9282: Extension declarations can include only methods or properties + // extension(object) { Extensions() { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Extensions").WithLocation(3, 25)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Equal([".ctor"], symbol.MemberNames); + Assert.Equal(["Extensions.<>E__0..ctor()"], symbol.InstanceConstructors.ToTestDisplayStrings()); + Assert.Empty(symbol.StaticConstructors); + Assert.Equal(["Extensions.<>E__0..ctor()"], symbol.Constructors.ToTestDisplayStrings()); + } + + [Fact] + public void Member_Finalizer() + { + var src = """ +public static class Extensions +{ + extension(object) { ~Extensions() { } } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,26): error CS9282: Extension declarations can include only methods or properties + // extension(object) { ~Extensions() { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Extensions").WithLocation(3, 26)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Equal(["Finalize"], symbol.MemberNames); + Assert.Equal(["void Extensions.<>E__0.$(System.Object)", "void Extensions.<>E__0.Finalize()"], symbol.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Member_Field() + { + var src = """ +_ = new object().field; + +public static class Extensions +{ + extension(object o) { int field = 0; } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,18): error CS1061: 'object' does not contain a definition for 'field' and no accessible extension method 'field' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // _ = new object().field; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "field").WithArguments("object", "field").WithLocation(1, 18), + // (5,31): error CS9282: Extension declarations can include only methods or properties + // extension(object o) { int field = 0; } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 31), + // (5,31): warning CS0169: The field 'Extensions.extension(object).field' is never used + // extension(object o) { int field = 0; } + Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("Extensions.extension(object).field").WithLocation(5, 31)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Equal(["field"], symbol.MemberNames); + Assert.Equal(["void Extensions.<>E__0.$(System.Object o)", "System.Int32 Extensions.<>E__0.field"], symbol.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Member_Const() + { + var src = """ +public static class Extensions +{ + extension(object) { const int i = 0; } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,35): error CS9282: Extension declarations can include only methods or properties + // extension(object) { const int i = 0; } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "i").WithLocation(3, 35)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extension); + AssertExtensionDeclaration(symbol); + + Assert.Equal(["i"], symbol.MemberNames); + Assert.Equal(["void Extensions.<>E__0.$(System.Object)", "System.Int32 Extensions.<>E__0.i"], symbol.GetMembers().ToTestDisplayStrings()); + } + + [Fact] + public void Member_InstanceEvent_ExplicitInterfaceImplementation() + { + var src = """ +public interface I +{ + event System.Action E; +} +public static class Extensions +{ + extension(object o) + { + event System.Action I.E { add { } remove { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (9,31): error CS0541: 'Extensions.extension(object).E': explicit interface declaration can only be declared in a class, record, struct or interface + // event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExplicitInterfaceImplementationInNonClassOrStruct, "E").WithArguments("Extensions.extension(object).E").WithLocation(9, 31), + // (9,31): error CS9282: Extension declarations can include only methods or properties + // event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "E").WithLocation(9, 31), + // (9,35): error CS9282: Extension declarations can include only methods or properties + // event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "add").WithLocation(9, 35), + // (9,43): error CS9282: Extension declarations can include only methods or properties + // event System.Action I.E { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "remove").WithLocation(9, 43)); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + [InlineData("record")] + [InlineData("record struct")] + public void IsExtension_MiscTypeKinds(string typeKind) + { + var src = $$""" +{{typeKind}} C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(type); + Assert.False(symbol.IsExtension); + } + + [Fact] + public void IsExtension_Delegate() + { + var src = $$""" +delegate void C(); +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.False(symbol.IsExtension); + } + + [Fact] + public void IsExtension_Enum() + { + var src = $$""" +enum E { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.False(symbol.IsExtension); + } + + [Fact] + public void Attributes_01() + { + var src = """ +public static class Extensions +{ + [System.Obsolete] + extension(object) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,6): error CS0592: Attribute 'System.Obsolete' is not valid on this declaration type. It is only valid on 'class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate' declarations. + // [System.Obsolete] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "System.Obsolete").WithArguments("System.Obsolete", "class, struct, enum, constructor, method, property, indexer, field, event, interface, delegate").WithLocation(3, 6)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + AssertEx.SetEqual(["System.ObsoleteAttribute"], symbol.GetAttributes().Select(a => a.ToString())); + } + + [Fact] + public void Attributes_02() + { + var src = """ +public static class Extensions +{ + [My(nameof(o)), My(nameof(Extensions))] + extension(object o) { } +} + +public class MyAttribute : System.Attribute +{ + public MyAttribute(string s) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,6): error CS0592: Attribute 'My' is not valid on this declaration type. It is only valid on 'assembly, module, class, struct, enum, constructor, method, property, indexer, field, event, interface, parameter, delegate, return, type parameter' declarations. + // [My(nameof(o)), My(nameof(Extensions))] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "My").WithArguments("My", "assembly, module, class, struct, enum, constructor, method, property, indexer, field, event, interface, parameter, delegate, return, type parameter").WithLocation(3, 6), + // (3,21): error CS0579: Duplicate 'My' attribute + // [My(nameof(o)), My(nameof(Extensions))] + Diagnostic(ErrorCode.ERR_DuplicateAttribute, "My").WithArguments("My").WithLocation(3, 21) + ); + } + + [Fact] + public void ReceiverParameter() + { + var src = """ +public static class Extensions +{ + extension(object) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.Equal("System.Object", symbol.ExtensionParameter.ToTestDisplayString()); + } + + [Fact] + public void ReceiverParameter_WithIdentifier() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + public object M() { return o; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.Equal("System.Object o", symbol.ExtensionParameter.ToTestDisplayString()); + + var returnStatement = GetSyntax(tree, "return o;"); + Assert.Equal("System.Object o", model.GetSymbolInfo(returnStatement.Expression).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ReceiverParameter_Multiple() + { + var src = """ +public static class Extensions +{ + extension(int i, int j, C c) { } +} +class C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, int j, C c) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int j").WithLocation(3, 22), + // (3,29): error CS9285: An extension container can have only one receiver parameter + // extension(int i, int j, C c) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "C c").WithLocation(3, 29)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + var extensionParameter = symbol.ExtensionParameter; + + Assert.Equal("System.Int32 i", extensionParameter.ToTestDisplayString()); + Assert.True(extensionParameter.Equals(extensionParameter)); + + var parameterSyntaxes = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal("System.Int32 i", model.GetDeclaredSymbol(parameterSyntaxes[0]).ToTestDisplayString()); + Assert.Same(extensionParameter, model.GetDeclaredSymbol(parameterSyntaxes[0])); + + Assert.Equal("System.Int32", model.GetTypeInfo(parameterSyntaxes[1].Type).Type.ToTestDisplayString()); + Assert.Null(model.GetDeclaredSymbol(parameterSyntaxes[1])); + + Assert.Equal("C", model.GetTypeInfo(parameterSyntaxes[2].Type).Type.ToTestDisplayString()); + Assert.Null(model.GetDeclaredSymbol(parameterSyntaxes[2])); + } + + [Fact] + public void ReceiverParameter_Multiple_MissingType() + { + var src = """ +public static class Extensions +{ + extension(int i, Type) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, Type) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "Type").WithLocation(3, 22)); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Found() + { + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + var extensionParameter = symbol.ExtensionParameter; + Assert.Equal("T", extensionParameter.ToTestDisplayString()); + Assert.Same(extensionParameter.Type, symbol.TypeParameters[0]); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Found_FromContainingType() + { + var src = """ +public static class Extensions +{ + extension(T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(T) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 5)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + var extensionParameter = symbol.ExtensionParameter; + Assert.Equal("T", extensionParameter.ToTestDisplayString()); + Assert.Same(extensionParameter.Type, symbol.ContainingType.TypeParameters[0]); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Missing() + { + var src = """ +public static class Extensions +{ + extension(T) + { + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // extension(T) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + var parameter = symbol.ExtensionParameter; + Assert.Equal("T", parameter.ToTestDisplayString()); + Assert.True(parameter.Type.IsErrorType()); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Unreferenced_01() + { + var src = """ +int.M(); + +public static class Extensions +{ + extension(int) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS1061: 'int' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // int.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int", "M").WithLocation(1, 5), + // (5,18): error CS9295: The extended type 'int' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(int) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "int").WithArguments("int", "T").WithLocation(5, 18)); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Unreferenced_02() + { + var src = """ +int.M(); + +public static class Extensions +{ + extension(T1) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS1061: 'int' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // int.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int", "M").WithLocation(1, 5), + // (5,23): error CS9295: The extended type 'T1' must reference all the type parameters declared by the extension, but type parameter 'T2' is not referenced. + // extension(T1) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T1").WithArguments("T1", "T2").WithLocation(5, 23)); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Unreferenced_03() + { + var src = """ +int.M(); + +public static class Extensions +{ + extension(T1) where T1 : class + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS1061: 'int' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // int.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("int", "M").WithLocation(1, 5), + // (5,23): error CS9295: The extended type 'T1' must reference all the type parameters declared by the extension, but type parameter 'T2' is not referenced. + // extension(T1) where T1 : class + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T1").WithArguments("T1", "T2").WithLocation(5, 23)); + } + + [Fact] + public void ReceiverParameter_TypeParameter_Missing_Local() + { + var src = """ +public static class Extensions +{ + extension(T) + { + void T() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS0246: The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?) + // extension(T) + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "T").WithArguments("T").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Params() + { + var src = """ +public static class Extensions +{ + extension(params int[] i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1670: params is not valid in this context + // extension(params int[] i) { } + Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(type1); + var parameter = symbol1.ExtensionParameter; + Assert.Equal("System.Int32[] i", parameter.ToTestDisplayString()); + Assert.False(parameter.IsParams); + } + + [Fact] + public void ReceiverParameter_Params_BadType() + { + var src = """ +public static class Extensions +{ + extension(params int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1670: params is not valid in this context + // extension(params int i) { } + Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Params_NotLast() + { + var src = """ +public static class Extensions +{ + extension(params int[] i, int j) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1670: params is not valid in this context + // extension(params int[] i, int j) { } + Diagnostic(ErrorCode.ERR_IllegalParams, "params").WithLocation(3, 15), + // (3,31): error CS9285: An extension container can have only one receiver parameter + // extension(params int[] i, int j) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int j").WithLocation(3, 31)); + } + + [Fact] + public void ReceiverParameter_ParameterTypeViolatesConstraint() + { + var src = """ +public static class Extensions +{ + extension(C) { } + extension(C) where T2 : struct { } +} + +public class C where T : struct { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,18): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'C' + // extension(C) { } + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "C").WithArguments("C", "T", "T").WithLocation(3, 18)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(type1); + Assert.Equal("C", symbol1.ExtensionParameter.ToTestDisplayString()); + } + + [Fact] + public void ReceiverParameter_DefaultValue() + { + var src = """ +public static class Extensions +{ + extension(int i = 0) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(int i = 0) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "int i = 0").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.True(symbol.ExtensionParameter.HasExplicitDefaultValue); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider not recognizing the default value entirely + } + + [Fact] + public void ReceiverParameter_DefaultValue_BeforeAnotherParameter() + { + var src = """ +public static class Extensions +{ + extension(int i = 0, object) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(int i = 0, object) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "int i = 0").WithLocation(3, 15), + // (3,26): error CS9285: An extension container can have only one receiver parameter + // extension(int i = 0, object) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "object").WithLocation(3, 26)); + } + + [Fact] + public void ReceiverParameter_DefaultValue_BadValue() + { + var src = """ +public static class Extensions +{ + extension(int i = null) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(int i = null) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "int i = null").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_DefaultValue_RefReadonly() + { + var src = """ +public static class Extensions +{ + extension(ref readonly int x = 2) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(ref readonly int x = 2) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "ref readonly int x = 2").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Attributes_01() + { + var src = """ +public static class Extensions +{ + extension([System.Runtime.InteropServices.DefaultParameterValue(1)] int o = 2) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension([System.Runtime.InteropServices.DefaultParameterValue(1)] int o = 2) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "[System.Runtime.InteropServices.DefaultParameterValue(1)] int o = 2").WithLocation(3, 15), + // (3,16): error CS1745: Cannot specify default parameter value in conjunction with DefaultParameterAttribute or OptionalAttribute + // extension([System.Runtime.InteropServices.DefaultParameterValue(1)] int o = 2) { } + Diagnostic(ErrorCode.ERR_DefaultValueUsedWithAttributes, "System.Runtime.InteropServices.DefaultParameterValue").WithLocation(3, 16)); + } + + [Fact] + public void ReceiverParameter_Attributes_02() + { + var src = """ +public static class Extensions +{ + extension([System.Runtime.InteropServices.Optional, System.Runtime.InteropServices.DefaultParameterValue(1)] ref readonly int i) { } + extension([System.Runtime.InteropServices.Optional, System.Runtime.InteropServices.DefaultParameterValue(2)] ref readonly int) { } +} +"""; + var comp = CreateCompilation(src); + // Note: we use "" name in the diagnostic for the second parameter + // Note: these attributes are allowed on the receiver parameter of an extension method + comp.VerifyEmitDiagnostics( + // (3,57): warning CS9200: A default value is specified for 'ref readonly' parameter 'i', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // extension([System.Runtime.InteropServices.Optional, System.Runtime.InteropServices.DefaultParameterValue(1)] ref readonly int i) { } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "System.Runtime.InteropServices.DefaultParameterValue(1)").WithArguments("i").WithLocation(3, 57), + // (4,57): warning CS9200: A default value is specified for 'ref readonly' parameter '', but 'ref readonly' should be used only for references. Consider declaring the parameter as 'in'. + // extension([System.Runtime.InteropServices.Optional, System.Runtime.InteropServices.DefaultParameterValue(2)] ref readonly int) { } + Diagnostic(ErrorCode.WRN_RefReadonlyParameterDefaultValue, "System.Runtime.InteropServices.DefaultParameterValue(2)").WithArguments("").WithLocation(4, 57)); + } + + [Fact] + public void ReceiverParameter_Attributes_03() + { + var src = """ +public static class Extensions +{ + extension([System.Runtime.CompilerServices.ParamCollectionAttribute] int[] xs) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (3,16): error CS0674: Do not use 'System.ParamArrayAttribute'/'System.Runtime.CompilerServices.ParamCollectionAttribute'. Use the 'params' keyword instead. + // extension([System.Runtime.CompilerServices.ParamCollectionAttribute] int[] xs) { } + Diagnostic(ErrorCode.ERR_ExplicitParamArrayOrCollection, "System.Runtime.CompilerServices.ParamCollectionAttribute").WithLocation(3, 16)); + } + + [Fact] + public void ReceiverParameter_Attributes_04() + { + var src = """ +public static class Extensions +{ + extension([System.Runtime.CompilerServices.CallerLineNumber] int x = 2) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension([System.Runtime.CompilerServices.CallerLineNumber] int x = 2) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "[System.Runtime.CompilerServices.CallerLineNumber] int x = 2").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Attributes_54() + { + var src = """ +public static class Extensions +{ + extension([System.Runtime.CompilerServices.CallerLineNumber] int x) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,16): error CS4020: The CallerLineNumberAttribute may only be applied to parameters with default values + // extension([System.Runtime.CompilerServices.CallerLineNumber] int x) { } + Diagnostic(ErrorCode.ERR_BadCallerLineNumberParamWithoutDefaultValue, "System.Runtime.CompilerServices.CallerLineNumber").WithLocation(3, 16)); + } + + [Fact] + public void ReceiverParameter_Attributes_06() + { + var src = """ +public static class Extensions +{ + extension([My] int x) { } +} +public class MyAttribute : System.Attribute { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var parameter = tree.GetRoot().DescendantNodes().OfType().Single(); + var parameterSymbol = model.GetDeclaredSymbol(parameter); + Assert.Equal("System.Int32 x", parameterSymbol.ToTestDisplayString()); + AssertEx.SetEqual(["MyAttribute"], parameterSymbol.GetAttributes().Select(a => a.ToString())); + + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var extensionSymbol = model.GetDeclaredSymbol(type); + AssertEx.SetEqual(["MyAttribute"], extensionSymbol.ExtensionParameter.GetAttributes().Select(a => a.ToString())); + } + + [Fact] + public void ReceiverParameter_This_01() + { + var src = """ +public static class Extensions +{ + extension(this int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS0027: Keyword 'this' is not available in the current context + // extension(this int i) { } + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_This_02() + { + var src = """ +public static class Extensions +{ + extension(int i, this int j) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, this int j) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "this int j").WithLocation(3, 22)); + } + + [Fact] + public void ReceiverParameter_This_03() + { + var src = """ +public static class Extensions +{ + extension(this this int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,20): error CS1107: A parameter can only have one 'this' modifier + // extension(this this int i) { } + Diagnostic(ErrorCode.ERR_DupParamMod, "this").WithArguments("this").WithLocation(3, 20), + // (3,20): error CS0027: Keyword 'this' is not available in the current context + // extension(this this int i) { } + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(3, 20)); + } + + [Fact] + public void ReceiverParameter_Ref_01() + { + var src = """ +int i = 42; +i.M(); +System.Console.Write(i); + +public static class Extensions +{ + extension(ref int i) + { + public void M() { System.Console.Write(i); i = 43; } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "4243").VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Ref_02() + { + var src = """ +public static class Extensions +{ + extension(ref ref int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,19): error CS1107: A parameter can only have one 'ref' modifier + // extension(ref ref int i) { } + Diagnostic(ErrorCode.ERR_DupParamMod, "ref").WithArguments("ref").WithLocation(3, 19)); + } + + [Fact] + public void ReceiverParameter_Out_01() + { + var src = """ +public static class Extensions +{ + extension(out int i) + { + void M2() { } + static void M3() { } + } + static void M(this out int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS8328: The parameter modifier 'out' cannot be used with 'extension' + // extension(out int i) + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "extension").WithLocation(3, 15), + // (5,14): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // void M2() { } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "M2").WithArguments("i").WithLocation(5, 14), + // (8,17): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // static void M(this out int i) { } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "M").WithArguments("i").WithLocation(8, 17), + // (8,24): error CS8328: The parameter modifier 'out' cannot be used with 'this' + // static void M(this out int i) { } + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "this").WithLocation(8, 24)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type1 = tree.GetRoot().DescendantNodes().OfType().First(); + var symbol1 = model.GetDeclaredSymbol(type1); + var parameter = symbol1.ExtensionParameter; + Assert.Equal("out System.Int32 i", parameter.ToTestDisplayString()); + Assert.Equal(RefKind.Out, parameter.RefKind); + } + + [Fact] + public void ReceiverParameter_Out_02() + { + var src = """ +public static class Extensions +{ + extension(int i, out int j) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, out int j) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "out int j").WithLocation(3, 22)); + } + + [Fact] + public void ReceiverParameter_Out_03() + { + var src = """ +public static class Extensions +{ + extension(out out int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS8328: The parameter modifier 'out' cannot be used with 'extension' + // extension(out out int i) { } + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "extension").WithLocation(3, 15), + // (3,19): error CS8328: The parameter modifier 'out' cannot be used with 'extension' + // extension(out out int i) { } + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "extension").WithLocation(3, 19)); + } + + [Fact] + public void ReceiverParameter_Out_04() + { + var src = """ +public static class Extensions +{ + extension(out int i) + { + void M2(bool b) { if (b) return; else return; } + } + static void M(this out int i, bool b) { if (b) return; else return; } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS8328: The parameter modifier 'out' cannot be used with 'extension' + // extension(out int i) + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "extension").WithLocation(3, 15), + // (5,34): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // void M2(bool b) { if (b) return; else return; } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "return;").WithArguments("i").WithLocation(5, 34), + // (5,47): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // void M2(bool b) { if (b) return; else return; } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "return;").WithArguments("i").WithLocation(5, 47), + // (7,24): error CS8328: The parameter modifier 'out' cannot be used with 'this' + // static void M(this out int i, bool b) { if (b) return; else return; } + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "this").WithLocation(7, 24), + // (7,52): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // static void M(this out int i, bool b) { if (b) return; else return; } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "return;").WithArguments("i").WithLocation(7, 52), + // (7,65): error CS0177: The out parameter 'i' must be assigned to before control leaves the current method + // static void M(this out int i, bool b) { if (b) return; else return; } + Diagnostic(ErrorCode.ERR_ParamUnassigned, "return;").WithArguments("i").WithLocation(7, 65)); + } + + [Fact] + public void ReceiverParameter_Out_05() + { + var src = """ +public static class Extensions +{ + extension(out int i) + { + void M2(bool b) { i = 0; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS8328: The parameter modifier 'out' cannot be used with 'extension' + // extension(out int i) + Diagnostic(ErrorCode.ERR_BadParameterModifiers, "out").WithArguments("out", "extension").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_In_01() + { + var src = """ +public static class Extensions +{ + extension(in int i) { } + static void M(this in int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_In_02() + { + var src = """ +public static class Extensions +{ + extension(in in int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,18): error CS1107: A parameter can only have one 'in' modifier + // extension(in in int i) { } + Diagnostic(ErrorCode.ERR_DupParamMod, "in").WithArguments("in").WithLocation(3, 18)); + } + + [Fact] + public void ReceiverParameter_RefReadonly() + { + var src = """ +int i = 42; +i.M(); + +public static class Extensions +{ + extension(ref readonly int i) + { + public void M() { System.Console.Write(i); } + } + extension(ref readonly int) { } + static void M2(this ref readonly int i) { } +} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp, symbolValidator: (m) => + { + Assert.Equal(RefKind.RefReadOnlyParameter, m.GlobalNamespace.GetMember("Extensions.<>E__0.$").Parameters[0].RefKind); + Assert.Equal(RefKind.RefReadOnlyParameter, m.GlobalNamespace.GetMember("Extensions.<>E__1.$").Parameters[0].RefKind); + }, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_ReadonlyRef() + { + var src = """ +public static class Extensions +{ + extension(readonly ref int i) { } + static void M(this readonly ref int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9190: 'readonly' modifier must be specified after 'ref'. + // extension(readonly ref int i) { } + Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(3, 15), + // (4,24): error CS9190: 'readonly' modifier must be specified after 'ref'. + // static void M(this readonly ref int i) { } + Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(4, 24)); + } + + [Fact] + public void ReceiverParameter_ArgList_01() + { + var src = """ +_ = object.M(); + +public static class Extensions +{ + extension(__arglist) + { + void M(){} + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,12): error CS0117: 'object' does not contain a definition for 'M' + // _ = object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 12), + // (5,15): error CS1669: __arglist is not valid in this context + // extension(__arglist) + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(5, 15)); + + Assert.Empty(comp.GetTypeByMetadataName("Extensions").GetMembers().OfType()); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates(_objectMembers, model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void ReceiverParameter_ArgList_02() + { + var src = """ +public static class Extensions +{ + extension(__arglist, int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1669: __arglist is not valid in this context + // extension(__arglist, int i) { } + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(3, 15), + // (3,26): error CS9285: An extension container can have only one receiver parameter + // extension(__arglist, int i) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "int i").WithLocation(3, 26)); + } + + [Fact] + public void ReceiverParameter_StaticType_01() + { + var src = """ +public static class Extensions +{ + extension(Extensions) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_StaticType_02() + { + var src = """ +public static class Extensions +{ + extension(object o, Extensions e) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,25): error CS9285: An extension container can have only one receiver parameter + // extension(object o, Extensions e) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "Extensions e").WithLocation(3, 25)); + } + + [Fact] + public void ReceiverParameter_StaticType_03() + { + var src = """ +public static class Extensions +{ + extension(Extensions e) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS0721: 'Extensions': static types cannot be used as parameters + // extension(Extensions) { } + Diagnostic(ErrorCode.ERR_ParameterIsStaticClass, "Extensions").WithArguments("Extensions").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_StaticType_04() + { + var src = """ +extension(C) { } +extension(C c) { } +public static class C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(C) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(1, 1), + // (2,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(C c) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(2, 1), + // (2,11): error CS0721: 'C': static types cannot be used as parameters + // extension(C c) { } + Diagnostic(ErrorCode.ERR_ParameterIsStaticClass, "C").WithArguments("C").WithLocation(2, 11)); + } + + [Fact] + public void ReceiverParameter_StaticType_05() + { + var src = """ +static class Extensions +{ + extension(C) + { + } +} + +static class C {} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_InstanceType_01() + { + var src = """ +public static class Extensions +{ + extension(C c) { } + extension(C) { } +} +public class C { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_ShadowingTypeParameter() + { + var src = """ +public static class Extensions +{ + extension(object T) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object T) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(3, 5)); + } + + [Fact] + public void ReceiverParameter_Ref() + { + var src = """ +int i = 42; +i.M(43); + +public static class Extensions +{ + extension(ref int i) + { + public void M(int j) { System.Console.Write((i, j)); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Scoped_01() + { + var src = """ +public static class Extensions +{ + extension(scoped int i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9048: The 'scoped' modifier can be used for refs and ref struct values only. + // extension(scoped int i) { } + Diagnostic(ErrorCode.ERR_ScopedRefAndRefStructOnly, "scoped int i").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Scoped_02() + { + var src = """ +public static class Extensions +{ + extension(int i, scoped int j) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,22): error CS9285: An extension container can have only one receiver parameter + // extension(int i, scoped int j) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "scoped int j").WithLocation(3, 22)); + } + + [Fact] + public void ReceiverParameter_Scoped_03() + { + var src = """ +public static class Extensions +{ + extension(scoped System.Span i) { } + public static void M(this scoped System.Span i) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Nullable_01() + { + var src = """ +public static class Extensions +{ + extension(string?) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,21): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. + // extension(string?) { } + Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(3, 21)); + } + + [Fact] + public void ReceiverParameter_Nullable_02() + { + var src = """ +#nullable enable +public static class Extensions +{ + extension(string?) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_RestrictedType() + { + var src = """ +public static class Extensions +{ + extension(ref System.ArgIterator) { } + extension(ref System.Span) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1601: Cannot make reference to variable of type 'ArgIterator' + // extension(ref System.ArgIterator) { } + Diagnostic(ErrorCode.ERR_MethodArgCantBeRefAny, "ref System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(3, 15)); + } + + [Fact] + public void ReceiverParameter_Empty() + { + var src = """ +public static class Extensions +{ + extension() { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS1031: Type expected + // extension() { } + Diagnostic(ErrorCode.ERR_TypeExpected, ")").WithLocation(3, 15)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var type = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(type); + Assert.Equal("?", symbol.ExtensionParameter.ToTestDisplayString()); + } + + [Fact] + public void ReceiverParameter_ConstraintsCheck() + { + var src = """ +static class Extensions +{ + extension(System.Nullable receiver) + { + } + + extension(System.Nullable) + { + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (3,39): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' + // extension(System.Nullable receiver) + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "receiver").WithArguments("System.Nullable", "T", "string").WithLocation(3, 39), + // (7,15): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Nullable' + // extension(System.Nullable) + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "System.Nullable").WithArguments("System.Nullable", "T", "string").WithLocation(7, 15) + ); + } + + [Fact] + public void ReceiverParameter_RefScope() + { + var src = """ +static class Extensions +{ + extension(scoped ref int receiver) + { + } + + extension(scoped ref int) + { + } +} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp, symbolValidator: (m) => + { + AssertEx.Equal(ScopedKind.ScopedRef, m.GlobalNamespace.GetMember("Extensions.<>E__0.$").Parameters[0].EffectiveScope); + AssertEx.Equal(ScopedKind.ScopedRef, m.GlobalNamespace.GetMember("Extensions.<>E__1.$").Parameters[0].EffectiveScope); + }).VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Nullability() + { + var src = """ +#nullable enable + +static class Extensions +{ + extension(string? receiver) + { + } + + extension(string?) + { + } +} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp, symbolValidator: (m) => + { + AssertEx.Equal("System.String?", m.GlobalNamespace.GetMember("Extensions.<>E__0.$").Parameters[0].TypeWithAnnotations.ToTestDisplayString()); + AssertEx.Equal("System.String?", m.GlobalNamespace.GetMember("Extensions.<>E__1.$").Parameters[0].TypeWithAnnotations.ToTestDisplayString()); + }).VerifyDiagnostics(); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : verify nullability in GetDeclaredSymbol and GetSymbolInfo + } + + [Fact] + public void ReceiverParameter_NativeInteger() + { + var src = """ +#nullable enable + +static class Extensions +{ + extension(nint receiver) + { + } + + extension(nint) + { + } +} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp, symbolValidator: (m) => + { + Assert.True(m.GlobalNamespace.GetMember("Extensions.<>E__0.$").Parameters[0].Type.IsNativeIntegerType); + Assert.True(m.GlobalNamespace.GetMember("Extensions.<>E__1.$").Parameters[0].Type.IsNativeIntegerType); + }).VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_InconsistentTypeAccessibility_01() + { + var src = """ +public static class Extensions +{ + extension(C x) + { + public void M() {} + public int P { get => 0; set {}} + public int this[int i] { get => 0; set {}} + + private void M1() {} + private int P1 { get => 0; set {}} + private int this[long i] { get => 0; set {}} + + internal void M2() {} + internal int P2 { get => 0; set {}} + internal int this[byte i] { get => 0; set {}} + } +} + +class C {} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,21): error CS0051: Inconsistent accessibility: parameter type 'C' is less accessible than method 'Extensions.extension(C).M()' + // public void M() {} + Diagnostic(ErrorCode.ERR_BadVisParamType, "M").WithArguments("Extensions.extension(C).M()", "C").WithLocation(5, 21), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Error wording, this isn't an indexer + // (6,20): error CS0055: Inconsistent accessibility: parameter type 'C' is less accessible than indexer 'Extensions.extension(C).P' + // public int P { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P").WithArguments("Extensions.extension(C).P", "C").WithLocation(6, 20), + // (7,20): error CS0055: Inconsistent accessibility: parameter type 'C' is less accessible than indexer 'Extensions.extension(C).this[int]' + // public int this[int i] { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("Extensions.extension(C).this[int]", "C").WithLocation(7, 20) + ); + } + + [Fact] + public void ReceiverParameter_InconsistentTypeAccessibility_02() + { + var src = """ +public static class Extensions +{ + extension(C x) + { + } + + private class C {} +} +"""; + var comp = CreateCompilation(src); + + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void ReceiverParameter_InconsistentTypeAccessibility_03() + { + var src = """ +public static class Extensions +{ + extension(C x) + { + public void M() {} + public int P { get => 0; set {}} + public int this[int i] { get => 0; set {}} + + private void M1() {} + private int P1 { get => 0; set {}} + private int this[long i] { get => 0; set {}} + + internal void M2() {} + internal int P2 { get => 0; set {}} + internal int this[byte i] { get => 0; set {}} + } + + private class C {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,21): error CS0051: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than method 'Extensions.extension(Extensions.C).M()' + // public void M() {} + Diagnostic(ErrorCode.ERR_BadVisParamType, "M").WithArguments("Extensions.extension(Extensions.C).M()", "Extensions.C").WithLocation(5, 21), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Error wording, this isn't an indexer + + // (6,20): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer 'Extensions.extension(Extensions.C).P' + // public int P { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P").WithArguments("Extensions.extension(Extensions.C).P", "Extensions.C").WithLocation(6, 20), + // (7,20): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer 'Extensions.extension(Extensions.C).this[int]' + // public int this[int i] { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("Extensions.extension(Extensions.C).this[int]", "Extensions.C").WithLocation(7, 20), + // (13,23): error CS0051: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than method 'Extensions.extension(Extensions.C).M2()' + // internal void M2() {} + Diagnostic(ErrorCode.ERR_BadVisParamType, "M2").WithArguments("Extensions.extension(Extensions.C).M2()", "Extensions.C").WithLocation(13, 23), + // (14,22): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer 'Extensions.extension(Extensions.C).P2' + // internal int P2 { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "P2").WithArguments("Extensions.extension(Extensions.C).P2", "Extensions.C").WithLocation(14, 22), + // (15,22): error CS0055: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than indexer 'Extensions.extension(Extensions.C).this[byte]' + // internal int this[byte i] { get => 0; set {}} + Diagnostic(ErrorCode.ERR_BadVisIndexerParam, "this").WithArguments("Extensions.extension(Extensions.C).this[byte]", "Extensions.C").WithLocation(15, 22) + ); + } + + [Fact] + public void InconsistentTypeAccessibility_01() + { + var src = """ +public static class Extensions +{ + extension(int x) + { + public void M1(C c) {} + private void M2(C c) {} + } + + extension(long x) + { + public static void M3(int y) + { + y.M2(new C()); + } + } + + public static void M4(int x) + { + x.M2(new C()); + } + + private class C {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,21): error CS0051: Inconsistent accessibility: parameter type 'Extensions.C' is less accessible than method 'Extensions.extension(int).M1(Extensions.C)' + // public void M1(C c) {} + Diagnostic(ErrorCode.ERR_BadVisParamType, "M1").WithArguments("Extensions.extension(int).M1(Extensions.C)", "Extensions.C").WithLocation(5, 21) + ); + } + + [Fact] + public void InconsistentTypeAccessibility_02() + { + var src = """ +public static class Extensions +{ + extension(int x) + { + public C M1 => null; + private C M2 => null; + } + + extension(long x) + { + public static void M3(int y) + { + _ = y.M2; + } + } + + public static void M4(int x) + { + _ = x.M2; + } + + private class C {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,18): error CS0053: Inconsistent accessibility: property type 'Extensions.C' is less accessible than property 'Extensions.extension(int).M1' + // public C M1 => null; + Diagnostic(ErrorCode.ERR_BadVisPropertyType, "M1").WithArguments("Extensions.extension(int).M1", "Extensions.C").WithLocation(5, 18) + ); + } + + [Fact] + public void Inaccessible_01() + { + var src = """ +static class Extensions +{ + extension(int x) + { + private void M2() {} + } + + private static void M3(this int x) {} +} + +class C +{ + void Test(int x) + { + x.M2(); + x.M3(); + Extensions.M2(x); + Extensions.M3(x); + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (15,11): error CS1061: 'int' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // x.M2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("int", "M2").WithLocation(15, 11), + // (16,11): error CS1061: 'int' does not contain a definition for 'M3' and no accessible extension method 'M3' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // x.M3(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M3").WithArguments("int", "M3").WithLocation(16, 11), + // (17,20): error CS0122: 'Extensions.M2(int)' is inaccessible due to its protection level + // Extensions.M2(x); + Diagnostic(ErrorCode.ERR_BadAccess, "M2").WithArguments("Extensions.M2(int)").WithLocation(17, 20), + // (18,20): error CS0122: 'Extensions.M3(int)' is inaccessible due to its protection level + // Extensions.M3(x); + Diagnostic(ErrorCode.ERR_BadAccess, "M3").WithArguments("Extensions.M3(int)").WithLocation(18, 20) + ); + } + + [Fact] + public void ReceiverParameter_FileType_01() + { + var src = """ +file class C {} + +static class Extensions +{ + extension(C x) + { + } + + private static void M3(this C x) {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,14): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'Extensions'. + // extension(C x) + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "(").WithArguments("C", "Extensions").WithLocation(5, 14), + // (9,25): error CS9051: File-local type 'C' cannot be used in a member signature in non-file-local type 'Extensions'. + // private static void M3(this C x) {} + Diagnostic(ErrorCode.ERR_FileTypeDisallowedInSignature, "M3").WithArguments("C", "Extensions").WithLocation(9, 25) + ); + } + + [Fact] + public void ReceiverParameter_FileType_02() + { + var src = """ +file class C {} + +file static class Extensions +{ + extension(C x) + { + public void M1() {} + public int P => 0; + public int this[int i] => 0; + } + + public static void M2(this C x) {} + + private static void M3(this C x) {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void FileType_01() + { + var src = """ +file class C {} + +file static class Extensions +{ + extension(int x) + { + public void M1(C c) {} + public C P => null; + public C this[int y] => null; + public int this[C y] => 0; + } + + public static void M2(this int x, C c) {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_PointerType() + { + string source = """ +unsafe static class E +{ + extension(int* i) + { + public static void M() { } + } + public static void M2(this int* i) { } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing validation + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (7,32): error CS1103: The first parameter of an extension method cannot be of type 'int*' + // public static void M2(this int* i) { } + Diagnostic(ErrorCode.ERR_BadTypeforThis, "int*").WithArguments("int*").WithLocation(7, 32)); + } + + [Fact] + public void Skeleton() + { + var src = """ +public static class Extensions +{ + extension(object) + { + void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var verifier = CompileAndVerify(comp); + verifier.VerifyIL("Extensions.<>E__0.M()", """ +{ + // Code size 2 (0x2) + .maxstack 1 + IL_0000: ldnull + IL_0001: throw +} +"""); + + verifier.VerifyIL("Extensions.M", """ +{ + // Code size 11 (0xb) + .maxstack 1 + IL_0000: ldstr "ran" + IL_0005: call "void System.Console.Write(string)" + IL_000a: ret +} +"""); + } + + [Fact] + public void GetDiagnosticsForSpan_NoReceiverParameter() + { + var src = """ +public static class Extensions +{ + extension(__arglist) + { + public int M() + { + return ""; + } + } +} +"""; + var comp = CreateCompilation(src); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var ext = tree.GetRoot().DescendantNodes().OfType().Single(); + var m = ext.DescendantNodes().OfType().Single(); + + model.GetDiagnostics(ext.ParameterList.Span).Verify( + // (3,15): error CS1669: __arglist is not valid in this context + // extension(__arglist) + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(3, 15) + ); + + model.GetDiagnostics(m.Body.Span).Verify( + // (7,20): error CS0029: Cannot implicitly convert type 'string' to 'int' + // return ""; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""""").WithArguments("string", "int").WithLocation(7, 20) + ); + + comp.VerifyDiagnostics( + // (7,20): error CS0029: Cannot implicitly convert type 'string' to 'int' + // return ""; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""""").WithArguments("string", "int").WithLocation(7, 20), + // (3,15): error CS1669: __arglist is not valid in this context + // extension(__arglist) + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(3, 15) + ); + } + + [Fact] + public void GetDiagnosticsForSpan_WithReceiverParameter() + { + var src = """ +public static class Extensions +{ + extension(object o = null) + { + public int M() + { + return ""; + } + } +} +"""; + var comp = CreateCompilation(src); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var ext = tree.GetRoot().DescendantNodes().OfType().Single(); + var m = ext.DescendantNodes().OfType().Single(); + + model.GetDiagnostics(ext.ParameterList.Span).Verify( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(object o = null) + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "object o = null").WithLocation(3, 15) + ); + + model.GetDiagnostics(m.Body.Span).Verify( + // (7,20): error CS0029: Cannot implicitly convert type 'string' to 'int' + // return ""; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""""").WithArguments("string", "int").WithLocation(7, 20) + ); + + comp.VerifyDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(object o = null) + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "object o = null").WithLocation(3, 15), + // (7,20): error CS0029: Cannot implicitly convert type 'string' to 'int' + // return ""; + Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""""").WithArguments("string", "int").WithLocation(7, 20) + ); + + Assert.Equal("[System.Object o = null]", model.GetDeclaredSymbol(ext.ParameterList.Parameters[0]).ToTestDisplayString()); + } + + [Fact] + public void ReceiverInScopeButIllegalInStaticMember() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + static object M1() => o; + static object M2() { return o; } + static object P1 => o; + static object P2 { get { return o; } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,31): error CS9293: Cannot use extension parameter 'object o' in this context. + // static object M1() => o; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "o").WithArguments("object o").WithLocation(5, 31), + // (6,37): error CS9293: Cannot use extension parameter 'object o' in this context. + // static object M2() { return o; } + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "o").WithArguments("object o").WithLocation(6, 37), + // (7,29): error CS9293: Cannot use extension parameter 'object o' in this context. + // static object P1 => o; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "o").WithArguments("object o").WithLocation(7, 29), + // (8,41): error CS9293: Cannot use extension parameter 'object o' in this context. + // static object P2 { get { return o; } } + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "o").WithArguments("object o").WithLocation(8, 41) + ); + } + + [Fact] + public void PassingValueForARefReceiver_01() + { + var src = """ +public class C +{ + static void Main() + { + GetInt().M1(); + GetInt().M2(); + _ = GetInt().P; + } + + static int GetInt() => 0; +} + +static class Extensions +{ + extension(ref int receiver) + { + public void M1() {} + public int P => 0; + } + + public static void M2 (this ref int receiver) + { + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,9): error CS1510: A ref or out value must be an assignable variable + // GetInt().M1(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetInt()").WithLocation(5, 9), + // (6,9): error CS1510: A ref or out value must be an assignable variable + // GetInt().M2(); + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetInt()").WithLocation(6, 9), + // (7,13): error CS1510: A ref or out value must be an assignable variable + // _ = GetInt().P; + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "GetInt()").WithLocation(7, 13) + ); + } + + [Fact] + public void PassingValueForARefReceiver_02() + { + var src = """ +public class C +{ + static void Main() + { + GetInt().M1(); + GetInt().M2(); + _ = GetInt().P; + } + + static int GetInt() => 0; +} + +static class Extensions +{ + extension(ref readonly int receiver) + { + public void M1() {} + public int P => 0; + } + + public static void M2 (this ref readonly int receiver) + { + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (5,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // GetInt().M1(); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetInt()").WithArguments("0").WithLocation(5, 9), + // (6,9): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // GetInt().M2(); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetInt()").WithArguments("0").WithLocation(6, 9), + // (7,13): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // _ = GetInt().P; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "GetInt()").WithArguments("0").WithLocation(7, 13) + ); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test emit and execution for a scenario like this + } + + [Fact] + public void PassingValueForARefReceiver_03() + { + var src = """ +public class C +{ + static void Main() + { + GetInt().M1(); + GetInt().M2(); + _ = GetInt().P; + } + + static int GetInt() => 0; +} + +static class Extensions +{ + extension(in int receiver) + { + public void M1() {} + public int P => 0; + } + + public static void M2 (this in int receiver) + { + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Test emit and execution for a scenario like this + } + + [Fact] + public void Implementation_InstanceMethod_01() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + void M(string s) + { + o.ToString(); + _ = s.Length; + } + } +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2077 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig + instance void M ( + string s + ) cil managed + { + // Method begins at RVA 0x2079 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + // Methods + .method private hidebysig specialname static + void M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 15 (0xf) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: callvirt instance string [mscorlib]System.Object::ToString() + IL_0006: pop + IL_0007: ldarg.1 + IL_0008: callvirt instance int32 [mscorlib]System.String::get_Length() + IL_000d: pop + IL_000e: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + CompileAndVerify( + comp, + emitOptions: EmitOptions.Default.WithEmitMetadataOnly(true).WithIncludePrivateMembers(false), + symbolValidator: (m) => + { + AssertEx.Equal("void Extensions.<>E__0.$(System.Object o)", m.GlobalNamespace.GetMember("Extensions.<>E__0.$").ToTestDisplayString()); + } + ).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_02() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public string M(string s) => o + s; + } +} +"""; + var comp1 = CreateCompilation(src1); + + var verifier1 = CompileAndVerify(comp1, sourceSymbolValidator: verifySymbols, symbolValidator: verifySymbols).VerifyDiagnostics(); + + static void verifySymbols(ModuleSymbol m) + { + NamedTypeSymbol extensions = m.ContainingAssembly.GetTypeByMetadataName("Extensions"); + MethodSymbol implementation = extensions.GetMembers().OfType().Single(); + Assert.True(implementation.IsStatic); + Assert.Equal(MethodKind.Ordinary, implementation.MethodKind); + Assert.Equal(2, implementation.ParameterCount); + AssertEx.Equal("System.String Extensions.M(this System.Object o, System.String s)", implementation.ToTestDisplayString()); + Assert.Equal(m is not PEModuleSymbol, implementation.IsImplicitlyDeclared); + Assert.True(implementation.IsExtensionMethod); + Assert.True(implementation.HasSpecialName); + Assert.False(implementation.HasRuntimeSpecialName); + + Assert.True(implementation.ContainingType.MightContainExtensionMethods); + + Assert.Contains("M", extensions.MemberNames); + Assert.NotEmpty(extensions.GetSimpleNonTypeMembers("M")); + + if (m is PEModuleSymbol peModuleSymbol) + { + Assert.True(peModuleSymbol.Module.HasExtensionAttribute(((PEAssemblySymbol)peModuleSymbol.ContainingAssembly).Assembly.Handle, ignoreCase: false)); + } + } + + comp1 = CreateCompilation(src1); + NamedTypeSymbol extensions = comp1.GetTypeByMetadataName("Extensions"); + Assert.Contains("M", extensions.MemberNames); + + comp1 = CreateCompilation(src1); + extensions = comp1.GetTypeByMetadataName("Extensions"); + Assert.NotEmpty(extensions.GetSimpleNonTypeMembers("M")); + + var expectedTypeIL = """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x207b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig + instance string M ( + string s + ) cil managed + { + // Method begins at RVA 0x207d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 19 (0x13) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldnull + IL_0004: br.s IL_000c + IL_0006: ldarg.0 + IL_0007: callvirt instance string [mscorlib]System.Object::ToString() + IL_000c: ldarg.1 + IL_000d: call string [mscorlib]System.String::Concat(string, string) + IL_0012: ret + } // end of method Extensions::M +} // end of class Extensions +"""; + + verifier1.VerifyTypeIL("Extensions", expectedTypeIL.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("3".M2("4")); + } + + static string Test(object o) + { + return o.M("2"); + } +} + +static class Extensions +{ + extension(object o) + { + public string M2(string s) => o.M(s); + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 17 (0x11) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr ""2"" + IL_0007: call ""string Extensions.M(object, string)"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"; + verifier2.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""string Extensions.M(object, string)"" + IL_0007: ret +} +"; + verifier2.VerifyIL("Extensions.M2", m2IL); + + var comp1ImageReference = comp1.EmitToImageReference(); + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions.M2", m2IL); + + comp2 = CreateCompilationWithIL(src2, expectedTypeIL, options: TestOptions.DebugExe); + CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + var remove = """ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) +"""; + + comp2 = CreateCompilationWithIL(src2, expectedTypeIL.Remove(expectedTypeIL.IndexOf(remove), remove.Length)); + comp2.VerifyDiagnostics( + // (11,18): error CS1061: 'object' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // return o.M("2"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("object", "M").WithLocation(11, 18), + // (19,41): error CS1061: 'object' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // public string M2(string s) => o.M(s); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("object", "M").WithLocation(19, 41) + ); + + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("3".M2("4")); + } + + static string Test(object o) + { + return Extensions.M(o, "2"); + } +} + +static class Extensions_ +{ + extension(object o) + { + public string M2(string s) => Extensions.M(o, s); + } + + public static void NotUsed(this object o) {} +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + var vbComp = CreateVisualBasicCompilation(""" +Class Program + Shared Sub Main() + System.Console.Write(Test1("1")) + System.Console.Write(Test2("3")) + End Sub + + Shared Function Test1(o As String) As String + return o.M("2") + End Function + Shared Function Test2(o As String) As String + return Extensions.M(o, "4") + End Function +End Class +""", + referencedAssemblies: comp2.References, compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); + + CompileAndVerify(vbComp, expectedOutput: "1234").VerifyDiagnostics(); + + if (!CompilationExtensions.EnableVerifyUsedAssemblies) // Tracked by https://github.com/dotnet/roslyn/issues/77542 + { + var src4 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("3".M2("4")); + } + + static string Test(object o) + { + System.Func d = o.M; + return d("2"); + } +} + +static class Extensions +{ + extension(object o) + { + public string M2(string s) => new System.Func(o.M)(s); + } +} +"""; + + var comp4 = CreateCompilation(src4, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier4 = CompileAndVerify(comp4, expectedOutput: "1234").VerifyDiagnostics(); + + testIL = + @" +{ + // Code size 30 (0x1e) + .maxstack 2 + .locals init (System.Func V_0, //d + string V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldftn ""string Extensions.M(object, string)"" + IL_0008: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: ldstr ""2"" + IL_0014: callvirt ""string System.Func.Invoke(string)"" + IL_0019: stloc.1 + IL_001a: br.s IL_001c + IL_001c: ldloc.1 + IL_001d: ret +} +"; + verifier4.VerifyIL("Program.Test", testIL); + + m2IL = + @" +{ + // Code size 19 (0x13) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldftn ""string Extensions.M(object, string)"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: ldarg.1 + IL_000d: callvirt ""string System.Func.Invoke(string)"" + IL_0012: ret +} +"; + verifier4.VerifyIL("Extensions.M2", m2IL); + + comp4 = CreateCompilation(src4, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier4 = CompileAndVerify(comp4, expectedOutput: "1234").VerifyDiagnostics(); + + verifier4.VerifyIL("Program.Test", testIL); + verifier4.VerifyIL("Extensions.M2", m2IL); + } + + var comp5 = CreateCompilation(src1); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object o) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/77542")] + public void UseSiteInfoTracking_01() + { + var src1 = """ +public static class Extensions +{ + public static string M(this object o, string s) => o + s; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + var src4 = """ +class Program +{ + static string Test(object o) + { + System.Func d = o.M; + return d("2"); + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp4 = CreateCompilation(src4, references: [comp1MetadataReference]); + comp4.VerifyEmitDiagnostics(); + var refs = comp4.GetUsedAssemblyReferences(); + Assert.Contains(comp1MetadataReference, refs); + } + + [Fact] + public void UseSiteInfoTracking_02() + { + var src1 = """ +public static class Extensions +{ + public static string M(this object o, string s) => o + s; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + var src4 = """ +class Program +{ + static string Test(object o) + { + return o.M("2"); + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp4 = CreateCompilation(src4, references: [comp1MetadataReference]); + comp4.VerifyEmitDiagnostics(); + var refs = comp4.GetUsedAssemblyReferences(); + Assert.Contains(comp1MetadataReference, refs); + } + + [Fact] + public void Implementation_InstanceMethod_03_WithLocalFunction() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public string M(string s) + { + string local() => o + s; + return local(); + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20ab + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig + instance string M ( + string s + ) cil managed + { + // Method begins at RVA 0x20ad + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0' + extends [mscorlib]System.ValueType + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public object o + .field public string s + } // end of class <>c__DisplayClass1_0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 24 (0x18) + .maxstack 2 + .locals init ( + [0] valuetype Extensions/'<>c__DisplayClass1_0' + ) + IL_0000: ldloca.s 0 + IL_0002: ldarg.0 + IL_0003: stfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0008: ldloca.s 0 + IL_000a: ldarg.1 + IL_000b: stfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0010: ldloca.s 0 + IL_0012: call string Extensions::'b__1_0'(valuetype Extensions/'<>c__DisplayClass1_0'&) + IL_0017: ret + } // end of method Extensions::M + .method assembly hidebysig static + string 'b__1_0' ( + valuetype Extensions/'<>c__DisplayClass1_0'& '' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x208c + // Code size 30 (0x1e) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0006: dup + IL_0007: brtrue.s IL_000d + IL_0009: pop + IL_000a: ldnull + IL_000b: br.s IL_0012 + IL_000d: callvirt instance string [mscorlib]System.Object::ToString() + IL_0012: ldarg.0 + IL_0013: ldfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0018: call string [mscorlib]System.String::Concat(string, string) + IL_001d: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + string M(string s) + { + string local() => o + s; + return local(); + } + } + + extension(object o) + { + string M(string s, int x) + { + string local() => o + s; + return local(); + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("3".M2("4")); + } + + static string Test(object o) + { + string local() => o.M("2"); + return local(); + } +} + +static class Extensions +{ + extension(object o) + { + public string M2(string s) + { + string local() => o.M(s); + return local(); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_04_WithLambda() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public string M(string s) + { + System.Func local = () => o + s; + return local(); + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x208c + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig + instance string M ( + string s + ) cil managed + { + // Method begins at RVA 0x208e + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public object o + .field public string s + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2091 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method '<>c__DisplayClass1_0'::.ctor + .method assembly hidebysig + instance string 'b__0' () cil managed + { + // Method begins at RVA 0x2099 + // Code size 30 (0x1e) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0006: dup + IL_0007: brtrue.s IL_000d + IL_0009: pop + IL_000a: ldnull + IL_000b: br.s IL_0012 + IL_000d: callvirt instance string [mscorlib]System.Object::ToString() + IL_0012: ldarg.0 + IL_0013: ldfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0018: call string [mscorlib]System.String::Concat(string, string) + IL_001d: ret + } // end of method '<>c__DisplayClass1_0'::'b__0' + } // end of class <>c__DisplayClass1_0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 36 (0x24) + .maxstack 8 + IL_0000: newobj instance void Extensions/'<>c__DisplayClass1_0'::.ctor() + IL_0005: dup + IL_0006: ldarg.0 + IL_0007: stfld object Extensions/'<>c__DisplayClass1_0'::o + IL_000c: dup + IL_000d: ldarg.1 + IL_000e: stfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0013: ldftn instance string Extensions/'<>c__DisplayClass1_0'::'b__0'() + IL_0019: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) + IL_001e: callvirt instance !0 class [mscorlib]System.Func`1::Invoke() + IL_0023: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + string M(string s) + { + System.Func local = () => o + s; + return local(); + } + } + + extension(object o) + { + string M(string s, int x) + { + System.Func local = () => o + s; + return local(); + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("3".M2("4")); + } + + static string Test(object o) + { + System.Func local = () => o.M("2"); + return local(); + } +} + +static class Extensions +{ + extension(object o) + { + public string M2(string s) + { + System.Func local = () => o.M(s); + return local(); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_05_Iterator() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public System.Collections.Generic.IEnumerable M(string s) + { + yield return o + s; + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.IteratorStateMachineAttribute(typeof(Extensions.d__1))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", (""" +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x207e + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig + instance class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + string s + ) cil managed + { + // Method begins at RVA 0x2080 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit 'd__1' + extends [mscorlib]System.Object + implements class [mscorlib]System.Collections.Generic.IEnumerable`1, + [mscorlib]System.Collections.IEnumerable, + class [mscorlib]System.Collections.Generic.IEnumerator`1, + +""" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? +""" + [mscorlib]System.Collections.IEnumerator, + [mscorlib]System.IDisposable + +""" : +""" + [mscorlib]System.IDisposable, + [mscorlib]System.Collections.IEnumerator + +""") + +""" + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private int32 '<>1__state' + .field private string '<>2__current' + .field private int32 '<>l__initialThreadId' + .field private object o + .field public object '<>3__o' + .field private string s + .field public string '<>3__s' + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 '<>1__state' + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2083 + // Code size 25 (0x19) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld int32 Extensions/'d__1'::'<>1__state' + IL_000d: ldarg.0 + IL_000e: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0013: stfld int32 Extensions/'d__1'::'<>l__initialThreadId' + IL_0018: ret + } // end of method 'd__1'::.ctor + .method private final hidebysig newslot virtual + instance void System.IDisposable.Dispose () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.IDisposable::Dispose() + // Method begins at RVA 0x209d + // Code size 9 (0x9) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s -2 + IL_0003: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0008: ret + } // end of method 'd__1'::System.IDisposable.Dispose + .method private final hidebysig newslot virtual + instance bool MoveNext () cil managed + { + .override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + // Method begins at RVA 0x20a8 + // Code size 76 (0x4c) + .maxstack 3 + .locals init ( + [0] int32 + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0043 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: ldfld object Extensions/'d__1'::o + IL_001e: dup + IL_001f: brtrue.s IL_0025 + IL_0021: pop + IL_0022: ldnull + IL_0023: br.s IL_002a + IL_0025: callvirt instance string [mscorlib]System.Object::ToString() + IL_002a: ldarg.0 + IL_002b: ldfld string Extensions/'d__1'::s + IL_0030: call string [mscorlib]System.String::Concat(string, string) + IL_0035: stfld string Extensions/'d__1'::'<>2__current' + IL_003a: ldarg.0 + IL_003b: ldc.i4.1 + IL_003c: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0041: ldc.i4.1 + IL_0042: ret + IL_0043: ldarg.0 + IL_0044: ldc.i4.m1 + IL_0045: stfld int32 Extensions/'d__1'::'<>1__state' + IL_004a: ldc.i4.0 + IL_004b: ret + } // end of method 'd__1'::MoveNext + .method private final hidebysig specialname newslot virtual + instance string 'System.Collections.Generic.IEnumerator.get_Current' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + // Method begins at RVA 0x2100 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string Extensions/'d__1'::'<>2__current' + IL_0006: ret + } // end of method 'd__1'::'System.Collections.Generic.IEnumerator.get_Current' + .method private final hidebysig newslot virtual + instance void System.Collections.IEnumerator.Reset () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Collections.IEnumerator::Reset() + // Method begins at RVA 0x2108 + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() + IL_0005: throw + } // end of method 'd__1'::System.Collections.IEnumerator.Reset + .method private final hidebysig specialname newslot virtual + instance object System.Collections.IEnumerator.get_Current () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance object [mscorlib]System.Collections.IEnumerator::get_Current() + // Method begins at RVA 0x2100 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string Extensions/'d__1'::'<>2__current' + IL_0006: ret + } // end of method 'd__1'::System.Collections.IEnumerator.get_Current + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.Generic.IEnumerator`1 'System.Collections.Generic.IEnumerable.GetEnumerator' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + // Method begins at RVA 0x2110 + // Code size 67 (0x43) + .maxstack 2 + .locals init ( + [0] class Extensions/'d__1' + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: ldc.i4.s -2 + IL_0008: bne.un.s IL_0022 + IL_000a: ldarg.0 + IL_000b: ldfld int32 Extensions/'d__1'::'<>l__initialThreadId' + IL_0010: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0015: bne.un.s IL_0022 + IL_0017: ldarg.0 + IL_0018: ldc.i4.0 + IL_0019: stfld int32 Extensions/'d__1'::'<>1__state' + IL_001e: ldarg.0 + IL_001f: stloc.0 + IL_0020: br.s IL_0029 + IL_0022: ldc.i4.0 + IL_0023: newobj instance void Extensions/'d__1'::.ctor(int32) + IL_0028: stloc.0 + IL_0029: ldloc.0 + IL_002a: ldarg.0 + IL_002b: ldfld object Extensions/'d__1'::'<>3__o' + IL_0030: stfld object Extensions/'d__1'::o + IL_0035: ldloc.0 + IL_0036: ldarg.0 + IL_0037: ldfld string Extensions/'d__1'::'<>3__s' + IL_003c: stfld string Extensions/'d__1'::s + IL_0041: ldloc.0 + IL_0042: ret + } // end of method 'd__1'::'System.Collections.Generic.IEnumerable.GetEnumerator' + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() + // Method begins at RVA 0x215f + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class [mscorlib]System.Collections.Generic.IEnumerator`1 Extensions/'d__1'::'System.Collections.Generic.IEnumerable.GetEnumerator'() + IL_0006: ret + } // end of method 'd__1'::System.Collections.IEnumerable.GetEnumerator + // Properties + .property instance string 'System.Collections.Generic.IEnumerator.Current'() + { + .get instance string Extensions/'d__1'::'System.Collections.Generic.IEnumerator.get_Current'() + } + .property instance object System.Collections.IEnumerator.Current() + { + .get instance object Extensions/'d__1'::System.Collections.IEnumerator.get_Current() + } + } // end of class d__1 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.IteratorStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 12 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 00 00 + ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 22 (0x16) + .maxstack 8 + IL_0000: ldc.i4.s -2 + IL_0002: newobj instance void Extensions/'d__1'::.ctor(int32) + IL_0007: dup + IL_0008: ldarg.0 + IL_0009: stfld object Extensions/'d__1'::'<>3__o' + IL_000e: dup + IL_000f: ldarg.1 + IL_0010: stfld string Extensions/'d__1'::'<>3__s' + IL_0015: ret + } // end of method Extensions::M +} // end of class Extensions +""").Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + System.Collections.Generic.IEnumerable M(string s) + { + yield return o + s; + } + } + + extension(object o) + { + System.Collections.Generic.IEnumerable M(string s, int x) + { + yield return o + s; + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + foreach (var s in Test("1")) + System.Console.Write(s); + foreach (var s in "3".M2("4")) + System.Console.Write(s); + } + + static System.Collections.Generic.IEnumerable Test(object o) + { + return o.M("2"); + } +} + +static class Extensions +{ + extension(object o) + { + public System.Collections.Generic.IEnumerable M2(string s) => o.M(s); + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + var verifier3 = CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 17 (0x11) + .maxstack 2 + .locals init (System.Collections.Generic.IEnumerable V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr ""2"" + IL_0007: call ""System.Collections.Generic.IEnumerable Extensions.M(object, string)"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"; + verifier3.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 8 (0x8) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: call ""System.Collections.Generic.IEnumerable Extensions.M(object, string)"" + IL_0007: ret +} +"; + verifier3.VerifyIL("Extensions.M2", m2IL); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions.M2", m2IL); + } + + [Fact] + public void Implementation_InstanceMethod_06_Async() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public async System.Threading.Tasks.Task M(string s) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Extensions.d__1))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20b3 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig + instance class [mscorlib]System.Threading.Tasks.Task`1 M ( + string s + ) cil managed + { + // Method begins at RVA 0x20b5 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit 'd__1' + extends [mscorlib]System.ValueType + implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public int32 '<>1__state' + .field public valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 '<>t__builder' + .field public object o + .field public string s + .field private valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter '<>u__1' + // Methods + .method private final hidebysig newslot virtual + instance void MoveNext () cil managed + { + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext() + // Method begins at RVA 0x20b8 + // Code size 178 (0xb2) + .maxstack 3 + .locals init ( + [0] int32, + [1] string, + [2] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter, + [3] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable, + [4] class [mscorlib]System.Exception + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0041 + IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable [mscorlib]System.Threading.Tasks.Task::Yield() + IL_000f: stloc.3 + IL_0010: ldloca.s 3 + IL_0012: call instance valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [mscorlib]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter() + IL_0017: stloc.2 + IL_0018: ldloca.s 2 + IL_001a: call instance bool [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted() + IL_001f: brtrue.s IL_005d + IL_0021: ldarg.0 + IL_0022: ldc.i4.0 + IL_0023: dup + IL_0024: stloc.0 + IL_0025: stfld int32 Extensions/'d__1'::'<>1__state' + IL_002a: ldarg.0 + IL_002b: ldloc.2 + IL_002c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_0031: ldarg.0 + IL_0032: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0037: ldloca.s 2 + IL_0039: ldarg.0 + IL_003a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::AwaitUnsafeOnCompletedd__1'>(!!0&, !!1&) + IL_003f: leave.s IL_00b1 + IL_0041: ldarg.0 + IL_0042: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_0047: stloc.2 + IL_0048: ldarg.0 + IL_0049: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_004e: initobj [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter + IL_0054: ldarg.0 + IL_0055: ldc.i4.m1 + IL_0056: dup + IL_0057: stloc.0 + IL_0058: stfld int32 Extensions/'d__1'::'<>1__state' + IL_005d: ldloca.s 2 + IL_005f: call instance void [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult() + IL_0064: ldarg.0 + IL_0065: ldfld object Extensions/'d__1'::o + IL_006a: dup + IL_006b: brtrue.s IL_0071 + IL_006d: pop + IL_006e: ldnull + IL_006f: br.s IL_0076 + IL_0071: callvirt instance string [mscorlib]System.Object::ToString() + IL_0076: ldarg.0 + IL_0077: ldfld string Extensions/'d__1'::s + IL_007c: call string [mscorlib]System.String::Concat(string, string) + IL_0081: stloc.1 + IL_0082: leave.s IL_009d + } // end .try + catch [mscorlib]System.Exception + { + IL_0084: stloc.s 4 + IL_0086: ldarg.0 + IL_0087: ldc.i4.s -2 + IL_0089: stfld int32 Extensions/'d__1'::'<>1__state' + IL_008e: ldarg.0 + IL_008f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0094: ldloc.s 4 + IL_0096: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetException(class [mscorlib]System.Exception) + IL_009b: leave.s IL_00b1 + } // end handler + IL_009d: ldarg.0 + IL_009e: ldc.i4.s -2 + IL_00a0: stfld int32 Extensions/'d__1'::'<>1__state' + IL_00a5: ldarg.0 + IL_00a6: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_00ab: ldloc.1 + IL_00ac: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetResult(!0) + IL_00b1: ret + } // end of method 'd__1'::MoveNext + .method private final hidebysig newslot virtual + instance void SetStateMachine ( + class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + // Method begins at RVA 0x2188 + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0006: ldarg.1 + IL_0007: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + IL_000c: ret + } // end of method 'd__1'::SetStateMachine + } // end of class d__1 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Threading.Tasks.Task`1 M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 12 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 00 00 + ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 63 (0x3f) + .maxstack 2 + .locals init ( + [0] valuetype Extensions/'d__1' + ) + IL_0000: ldloca.s 0 + IL_0002: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Create() + IL_0007: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_000c: ldloca.s 0 + IL_000e: ldarg.0 + IL_000f: stfld object Extensions/'d__1'::o + IL_0014: ldloca.s 0 + IL_0016: ldarg.1 + IL_0017: stfld string Extensions/'d__1'::s + IL_001c: ldloca.s 0 + IL_001e: ldc.i4.m1 + IL_001f: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0024: ldloca.s 0 + IL_0026: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_002b: ldloca.s 0 + IL_002d: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Startd__1'>(!!0&) + IL_0032: ldloca.s 0 + IL_0034: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0039: call instance class [mscorlib]System.Threading.Tasks.Task`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::get_Task() + IL_003e: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + async System.Threading.Tasks.Task M(string s) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } + + extension(object o) + { + async System.Threading.Tasks.Task M(string s, int x) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1").Result); + System.Console.Write("3".M2("4").Result); + } + + async static System.Threading.Tasks.Task Test(object o) + { + await System.Threading.Tasks.Task.Yield(); + return await o.M("2"); + } +} + +static class Extensions +{ + extension(object o) + { + async public System.Threading.Tasks.Task M2(string s) + { + await System.Threading.Tasks.Task.Yield(); + return await o.M(s); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_07_Generic() + { + var src1 = """ +public static class Extensions +{ + extension(C o) + { + public string M(T t, U u) + { + return o.GetString() + u.ToString() + t.ToString(); + } + } +} + +public class C(string v) +{ + public string GetString() => v; +} +"""; + var comp1 = CreateCompilation(src1); + + MethodSymbol implementation = comp1.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + Assert.True(implementation.IsStatic); + Assert.Equal(MethodKind.Ordinary, implementation.MethodKind); + Assert.Equal(3, implementation.ParameterCount); + AssertEx.Equal("System.String Extensions.M(this C o, T t, U u)", implementation.ToTestDisplayString()); + Assert.True(implementation.IsImplicitlyDeclared); + Assert.True(implementation.IsExtensionMethod); + Assert.True(implementation.HasSpecialName); + Assert.False(implementation.HasRuntimeSpecialName); + + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + class C`1 o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20a5 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method public hidebysig + instance string M ( + !T t, + !!U u + ) cil managed + { + // Method begins at RVA 0x20a7 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M + } // end of class <>E__0`1 + // Methods + .method public hidebysig specialname static + string M ( + class C`1 o, + !!T t, + !!U u + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 38 (0x26) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: callvirt instance string class C`1::GetString() + IL_0006: ldarga.s u + IL_0008: constrained. !!U + IL_000e: callvirt instance string [mscorlib]System.Object::ToString() + IL_0013: ldarga.s t + IL_0015: constrained. !!T + IL_001b: callvirt instance string [mscorlib]System.Object::ToString() + IL_0020: call string [mscorlib]System.String::Concat(string, string, string) + IL_0025: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test(new C("1"), "2", 3)); + System.Console.Write(new C("4").M2("5", 6)); + } + + static string Test(C o, T t, U u) + { + return o.M(t, u); + } +} + +static class Extensions +{ + extension(C o) + { + public string M2(T t, U u) => o.M(t, u); + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier3 = CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 14 (0xe) + .maxstack 3 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: ldarg.2 + IL_0004: call ""string Extensions.M(C, T, U)"" + IL_0009: stloc.0 + IL_000a: br.s IL_000c + IL_000c: ldloc.0 + IL_000d: ret +} +"; + verifier3.VerifyIL("Program.Test(C, T, U)", testIL); + + var m2IL = +@" +{ + // Code size 9 (0x9) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: ldarg.2 + IL_0003: call ""string Extensions.M(C, T, U)"" + IL_0008: ret +} +"; + verifier3.VerifyIL("Extensions.M2(this C, T, U)", m2IL); + + var comp1ImageReference = comp1.EmitToImageReference(); + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test(C, T, U)", testIL); + verifier3.VerifyIL("Extensions.M2(this C, T, U)", m2IL); + + if (!CompilationExtensions.EnableVerifyUsedAssemblies) // Tracked by https://github.com/dotnet/roslyn/issues/77542 + { + src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test(new C("1"), "2", 3)); + System.Console.Write(new C("4").M2("5", 6)); + } + + static string Test(C o, T t, U u) + { + System.Func d = o.M; + return d(t, u); + } +} + +static class Extensions +{ + extension(C o) + { + public string M2(T t, U u) => new System.Func(o.M)(t, u); + } +} +"""; + + comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + testIL = +@" +{ + // Code size 27 (0x1b) + .maxstack 3 + .locals init (System.Func V_0, //d + string V_1) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldftn ""string Extensions.M(C, T, U)"" + IL_0008: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: ldarg.1 + IL_0010: ldarg.2 + IL_0011: callvirt ""string System.Func.Invoke(T, U)"" + IL_0016: stloc.1 + IL_0017: br.s IL_0019 + IL_0019: ldloc.1 + IL_001a: ret +} +"; + verifier3.VerifyIL("Program.Test(C, T, U)", testIL); + + m2IL = +@" +{ + // Code size 20 (0x14) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldftn ""string Extensions.M(C, T, U)"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: ldarg.1 + IL_000d: ldarg.2 + IL_000e: callvirt ""string System.Func.Invoke(T, U)"" + IL_0013: ret +} +"; + verifier3.VerifyIL("Extensions.M2(this C, T, U)", m2IL); + + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + + verifier3 = CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test(C, T, U)", testIL); + verifier3.VerifyIL("Extensions.M2(this C, T, U)", m2IL); + } + } + + [Fact] + public void Implementation_InstanceMethod_08_WithLocalFunction_Generic() + { + var src1 = """ +public static class Extensions +{ + extension(C o) + { + public C M(T t1, U u1) + { + C local(T t2, U u2, X x2, Y y2, Z z2) + { + return new C(o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString() + x2.ToString() + y2.ToString() + z2.ToString()); + }; + + return local(t1, u1, 0, t1, u1); + } + } +} + +public class C(string val) +{ + public string GetString() => val; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + class C`1 o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x216d + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method public hidebysig + instance class C`1 M ( + !T t1, + !!U u1 + ) cil managed + { + // Method begins at RVA 0x216f + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M + } // end of class <>E__0`1 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0`2' + extends [mscorlib]System.ValueType + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public class C`1 o + .field public !U u1 + .field public !T t1 + } // end of class <>c__DisplayClass1_0`2 + // Methods + .method public hidebysig specialname static + class C`1 M ( + class C`1 o, + !!T t1, + !!U u1 + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 57 (0x39) + .maxstack 6 + .locals init ( + [0] valuetype Extensions/'<>c__DisplayClass1_0`2' + ) + IL_0000: ldloca.s 0 + IL_0002: ldarg.0 + IL_0003: stfld class C`1 valuetype Extensions/'<>c__DisplayClass1_0`2'::o + IL_0008: ldloca.s 0 + IL_000a: ldarg.2 + IL_000b: stfld !1 valuetype Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_0010: ldloca.s 0 + IL_0012: ldarg.1 + IL_0013: stfld !0 valuetype Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_0018: ldloc.0 + IL_0019: ldfld !0 valuetype Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_001e: ldloc.0 + IL_001f: ldfld !1 valuetype Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_0024: ldc.i4.0 + IL_0025: ldloc.0 + IL_0026: ldfld !0 valuetype Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_002b: ldloc.0 + IL_002c: ldfld !1 valuetype Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_0031: ldloca.s 0 + IL_0033: call class C`1 Extensions::'b__1_0'(!!0, !!1, !!2, !!3, !!4, valuetype Extensions/'<>c__DisplayClass1_0`2'&) + IL_0038: ret + } // end of method Extensions::M + .method assembly hidebysig static + class C`1 'b__1_0' ( + !!T t2, + !!U u2, + !!X x2, + !!Y y2, + !!Z z2, + valuetype Extensions/'<>c__DisplayClass1_0`2'& '' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20b0 + // Code size 154 (0x9a) + .maxstack 4 + IL_0000: ldc.i4.8 + IL_0001: newarr [mscorlib]System.String + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldarg.s 5 + IL_000a: ldfld class C`1 valuetype Extensions/'<>c__DisplayClass1_0`2'::o + IL_000f: callvirt instance string class C`1::GetString() + IL_0014: stelem.ref + IL_0015: dup + IL_0016: ldc.i4.1 + IL_0017: ldarg.s 5 + IL_0019: ldflda !1 valuetype Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_001e: constrained. !!U + IL_0024: callvirt instance string [mscorlib]System.Object::ToString() + IL_0029: stelem.ref + IL_002a: dup + IL_002b: ldc.i4.2 + IL_002c: ldarg.s 5 + IL_002e: ldflda !0 valuetype Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_0033: constrained. !!T + IL_0039: callvirt instance string [mscorlib]System.Object::ToString() + IL_003e: stelem.ref + IL_003f: dup + IL_0040: ldc.i4.3 + IL_0041: ldarga.s u2 + IL_0043: constrained. !!U + IL_0049: callvirt instance string [mscorlib]System.Object::ToString() + IL_004e: stelem.ref + IL_004f: dup + IL_0050: ldc.i4.4 + IL_0051: ldarga.s t2 + IL_0053: constrained. !!T + IL_0059: callvirt instance string [mscorlib]System.Object::ToString() + IL_005e: stelem.ref + IL_005f: dup + IL_0060: ldc.i4.5 + IL_0061: ldarga.s x2 + IL_0063: constrained. !!X + IL_0069: callvirt instance string [mscorlib]System.Object::ToString() + IL_006e: stelem.ref + IL_006f: dup + IL_0070: ldc.i4.6 + IL_0071: ldarga.s y2 + IL_0073: constrained. !!Y + IL_0079: callvirt instance string [mscorlib]System.Object::ToString() + IL_007e: stelem.ref + IL_007f: dup + IL_0080: ldc.i4.7 + IL_0081: ldarga.s z2 + IL_0083: constrained. !!Z + IL_0089: callvirt instance string [mscorlib]System.Object::ToString() + IL_008e: stelem.ref + IL_008f: call string [mscorlib]System.String::Concat(string[]) + IL_0094: newobj instance void class C`1::.ctor(string) + IL_0099: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(C o) + { + string M(T t1, U u1) + { + U local(T t2, U u2, X x2, Y y2, Z z2) + { + _ = o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString() + x2.ToString() + y2.ToString() + z2.ToString(); + return u2; + }; + + return local(t1, u1, 0, t1, u1).ToString(); + } + } + + extension(C o) + { + string M(T t1, U u1, int x) + { + U local(T t2, U u2, X x2, Y y2, Z z2) + { + _ = o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString() + x2.ToString() + y2.ToString() + z2.ToString(); + return u2; + }; + + return local(t1, u1, 0, t1, u1).ToString(); + } + } +} + +public class C +{ + public string GetString() => null; +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test(new C("1"), 2, "3").GetString()); + System.Console.Write(new C("4").M2(5, "6").GetString()); + } + + static C Test(C o, T t1, U u1) + { + C local(T t2, X x2) + { + return o.M(t2, x2); + }; + + return local(t1, u1); + } +} + +static class Extensions +{ + extension(C o) + { + public C M2(T t1, U u1) + { + C local(T t2, X x2) + { + return o.M(t2, x2); + }; + + return local(t1, u1); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1323202346565056").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1323202346565056").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_09_WithLambda_Generic() + { + var src1 = """ +public static class Extensions +{ + extension(C o) + { + public C M(T t1, U u1) + { + System.Func> local = (T t2, U u2) => + { + return new C(o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString()); + }; + + return local(t1, u1); + } + } +} + +public class C(string val) +{ + public string GetString() => val; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + class C`1 o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20c4 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method public hidebysig + instance class C`1 M ( + !T t1, + !!U u1 + ) cil managed + { + // Method begins at RVA 0x20c6 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M + } // end of class <>E__0`1 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0`2' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public class C`1 o + .field public !U u1 + .field public !T t1 + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x20c9 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method '<>c__DisplayClass1_0`2'::.ctor + .method assembly hidebysig + instance class C`1 'b__0' ( + !T t2, + !U u2 + ) cil managed + { + // Method begins at RVA 0x20d4 + // Code size 103 (0x67) + .maxstack 4 + IL_0000: ldc.i4.5 + IL_0001: newarr [mscorlib]System.String + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldarg.0 + IL_0009: ldfld class C`1 class Extensions/'<>c__DisplayClass1_0`2'::o + IL_000e: callvirt instance string class C`1::GetString() + IL_0013: stelem.ref + IL_0014: dup + IL_0015: ldc.i4.1 + IL_0016: ldarg.0 + IL_0017: ldflda !1 class Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_001c: constrained. !U + IL_0022: callvirt instance string [mscorlib]System.Object::ToString() + IL_0027: stelem.ref + IL_0028: dup + IL_0029: ldc.i4.2 + IL_002a: ldarg.0 + IL_002b: ldflda !0 class Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_0030: constrained. !T + IL_0036: callvirt instance string [mscorlib]System.Object::ToString() + IL_003b: stelem.ref + IL_003c: dup + IL_003d: ldc.i4.3 + IL_003e: ldarga.s u2 + IL_0040: constrained. !U + IL_0046: callvirt instance string [mscorlib]System.Object::ToString() + IL_004b: stelem.ref + IL_004c: dup + IL_004d: ldc.i4.4 + IL_004e: ldarga.s t2 + IL_0050: constrained. !T + IL_0056: callvirt instance string [mscorlib]System.Object::ToString() + IL_005b: stelem.ref + IL_005c: call string [mscorlib]System.String::Concat(string[]) + IL_0061: newobj instance void class C`1::.ctor(string) + IL_0066: ret + } // end of method '<>c__DisplayClass1_0`2'::'b__0' + } // end of class <>c__DisplayClass1_0`2 + // Methods + .method public hidebysig specialname static + class C`1 M ( + class C`1 o, + !!T t1, + !!U u1 + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 57 (0x39) + .maxstack 3 + .locals init ( + [0] class Extensions/'<>c__DisplayClass1_0`2' + ) + IL_0000: newobj instance void class Extensions/'<>c__DisplayClass1_0`2'::.ctor() + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldarg.0 + IL_0008: stfld class C`1 class Extensions/'<>c__DisplayClass1_0`2'::o + IL_000d: ldloc.0 + IL_000e: ldarg.2 + IL_000f: stfld !1 class Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_0014: ldloc.0 + IL_0015: ldarg.1 + IL_0016: stfld !0 class Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_001b: ldloc.0 + IL_001c: ldftn instance class C`1 class Extensions/'<>c__DisplayClass1_0`2'::'b__0'(!0, !1) + IL_0022: newobj instance void class [mscorlib]System.Func`3>::.ctor(object, native int) + IL_0027: ldloc.0 + IL_0028: ldfld !0 class Extensions/'<>c__DisplayClass1_0`2'::t1 + IL_002d: ldloc.0 + IL_002e: ldfld !1 class Extensions/'<>c__DisplayClass1_0`2'::u1 + IL_0033: callvirt instance !2 class [mscorlib]System.Func`3>::Invoke(!0, !1) + IL_0038: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(C o) + { + string M(T t1, U u1) + { + System.Func local = (T t2, U u2) => + { + _ = o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString(); + return u2; + }; + + return local(t1, u1).ToString(); + } + } + + extension(C o) + { + string M(T t1, U u1, int x) + { + System.Func local = (T t2, U u2) => + { + _ = o.GetString() + u1.ToString() + t1.ToString() + u2.ToString() + t2.ToString(); + return u2; + }; + + return local(t1, u1).ToString(); + } + } +} + +public class C +{ + public string GetString() => null; +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test(new C("1"), 2, "3").GetString()); + System.Console.Write(new C("4").M2(5, "6").GetString()); + } + + static C Test(C o, T t1, U u1) + { + System.Func> local = (T t2, U u2) => + { + return o.M(t2, u2); + }; + + return local(t1, u1); + } +} + +static class Extensions +{ + extension(C o) + { + public C M2(T t1, U u1) + { + System.Func> local = (T t2, U u2) => + { + return o.M(t2, u2); + }; + + return local(t1, u1); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1323246565").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1323246565").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_10_Iterator_Generic() + { + var src1 = """ +public static class Extensions +{ + extension(C o) + { + public System.Collections.Generic.IEnumerable M(T t1, U u1) + { + yield return o.GetString() + u1.ToString() + t1.ToString(); + } + } +} + +public class C(string val) +{ + public string GetString() => val; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.IteratorStateMachineAttribute(typeof(Extensions.d__1<,>))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", (""" +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + class C`1 o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x209c + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method public hidebysig + instance class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + !T t1, + !!U u1 + ) cil managed + { + // Method begins at RVA 0x209e + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M + } // end of class <>E__0`1 + .class nested private auto ansi sealed beforefieldinit 'd__1`2' + extends [mscorlib]System.Object + implements class [mscorlib]System.Collections.Generic.IEnumerable`1, + [mscorlib]System.Collections.IEnumerable, + class [mscorlib]System.Collections.Generic.IEnumerator`1, + +""" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? +""" + [mscorlib]System.Collections.IEnumerator, + [mscorlib]System.IDisposable + +""" : +""" + [mscorlib]System.IDisposable, + [mscorlib]System.Collections.IEnumerator + +""") + +""" + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private int32 '<>1__state' + .field private string '<>2__current' + .field private int32 '<>l__initialThreadId' + .field private class C`1 o + .field public class C`1 '<>3__o' + .field private !U u1 + .field public !U '<>3__u1' + .field private !T t1 + .field public !T '<>3__t1' + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 '<>1__state' + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20a1 + // Code size 25 (0x19) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_000d: ldarg.0 + IL_000e: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0013: stfld int32 class Extensions/'d__1`2'::'<>l__initialThreadId' + IL_0018: ret + } // end of method 'd__1`2'::.ctor + .method private final hidebysig newslot virtual + instance void System.IDisposable.Dispose () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.IDisposable::Dispose() + // Method begins at RVA 0x20bb + // Code size 9 (0x9) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s -2 + IL_0003: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_0008: ret + } // end of method 'd__1`2'::System.IDisposable.Dispose + .method private final hidebysig newslot virtual + instance bool MoveNext () cil managed + { + .override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + // Method begins at RVA 0x20c8 + // Code size 97 (0x61) + .maxstack 4 + .locals init ( + [0] int32 + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0058 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: ldfld class C`1 class Extensions/'d__1`2'::o + IL_001e: callvirt instance string class C`1::GetString() + IL_0023: ldarg.0 + IL_0024: ldflda !1 class Extensions/'d__1`2'::u1 + IL_0029: constrained. !U + IL_002f: callvirt instance string [mscorlib]System.Object::ToString() + IL_0034: ldarg.0 + IL_0035: ldflda !0 class Extensions/'d__1`2'::t1 + IL_003a: constrained. !T + IL_0040: callvirt instance string [mscorlib]System.Object::ToString() + IL_0045: call string [mscorlib]System.String::Concat(string, string, string) + IL_004a: stfld string class Extensions/'d__1`2'::'<>2__current' + IL_004f: ldarg.0 + IL_0050: ldc.i4.1 + IL_0051: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_0056: ldc.i4.1 + IL_0057: ret + IL_0058: ldarg.0 + IL_0059: ldc.i4.m1 + IL_005a: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_005f: ldc.i4.0 + IL_0060: ret + } // end of method 'd__1`2'::MoveNext + .method private final hidebysig specialname newslot virtual + instance string 'System.Collections.Generic.IEnumerator.get_Current' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + // Method begins at RVA 0x2135 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string class Extensions/'d__1`2'::'<>2__current' + IL_0006: ret + } // end of method 'd__1`2'::'System.Collections.Generic.IEnumerator.get_Current' + .method private final hidebysig newslot virtual + instance void System.Collections.IEnumerator.Reset () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Collections.IEnumerator::Reset() + // Method begins at RVA 0x213d + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() + IL_0005: throw + } // end of method 'd__1`2'::System.Collections.IEnumerator.Reset + .method private final hidebysig specialname newslot virtual + instance object System.Collections.IEnumerator.get_Current () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance object [mscorlib]System.Collections.IEnumerator::get_Current() + // Method begins at RVA 0x2135 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string class Extensions/'d__1`2'::'<>2__current' + IL_0006: ret + } // end of method 'd__1`2'::System.Collections.IEnumerator.get_Current + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.Generic.IEnumerator`1 'System.Collections.Generic.IEnumerable.GetEnumerator' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + // Method begins at RVA 0x2144 + // Code size 79 (0x4f) + .maxstack 2 + .locals init ( + [0] class Extensions/'d__1`2' + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_0006: ldc.i4.s -2 + IL_0008: bne.un.s IL_0022 + IL_000a: ldarg.0 + IL_000b: ldfld int32 class Extensions/'d__1`2'::'<>l__initialThreadId' + IL_0010: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0015: bne.un.s IL_0022 + IL_0017: ldarg.0 + IL_0018: ldc.i4.0 + IL_0019: stfld int32 class Extensions/'d__1`2'::'<>1__state' + IL_001e: ldarg.0 + IL_001f: stloc.0 + IL_0020: br.s IL_0029 + IL_0022: ldc.i4.0 + IL_0023: newobj instance void class Extensions/'d__1`2'::.ctor(int32) + IL_0028: stloc.0 + IL_0029: ldloc.0 + IL_002a: ldarg.0 + IL_002b: ldfld class C`1 class Extensions/'d__1`2'::'<>3__o' + IL_0030: stfld class C`1 class Extensions/'d__1`2'::o + IL_0035: ldloc.0 + IL_0036: ldarg.0 + IL_0037: ldfld !0 class Extensions/'d__1`2'::'<>3__t1' + IL_003c: stfld !0 class Extensions/'d__1`2'::t1 + IL_0041: ldloc.0 + IL_0042: ldarg.0 + IL_0043: ldfld !1 class Extensions/'d__1`2'::'<>3__u1' + IL_0048: stfld !1 class Extensions/'d__1`2'::u1 + IL_004d: ldloc.0 + IL_004e: ret + } // end of method 'd__1`2'::'System.Collections.Generic.IEnumerable.GetEnumerator' + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() + // Method begins at RVA 0x219f + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class Extensions/'d__1`2'::'System.Collections.Generic.IEnumerable.GetEnumerator'() + IL_0006: ret + } // end of method 'd__1`2'::System.Collections.IEnumerable.GetEnumerator + // Properties + .property instance string 'System.Collections.Generic.IEnumerator.Current'() + { + .get instance string Extensions/'d__1`2'::'System.Collections.Generic.IEnumerator.get_Current'() + } + .property instance object System.Collections.IEnumerator.Current() + { + .get instance object Extensions/'d__1`2'::System.Collections.IEnumerator.get_Current() + } + } // end of class d__1`2 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + class C`1 o, + !!T t1, + !!U u1 + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.IteratorStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 14 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 60 32 00 00 + ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 29 (0x1d) + .maxstack 8 + IL_0000: ldc.i4.s -2 + IL_0002: newobj instance void class Extensions/'d__1`2'::.ctor(int32) + IL_0007: dup + IL_0008: ldarg.0 + IL_0009: stfld class C`1 class Extensions/'d__1`2'::'<>3__o' + IL_000e: dup + IL_000f: ldarg.1 + IL_0010: stfld !0 class Extensions/'d__1`2'::'<>3__t1' + IL_0015: dup + IL_0016: ldarg.2 + IL_0017: stfld !1 class Extensions/'d__1`2'::'<>3__u1' + IL_001c: ret + } // end of method Extensions::M +} // end of class Extensions +""").Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(C o) + { + System.Collections.Generic.IEnumerable M(T t1, U u1) + { + yield return o.GetString() + u1.ToString() + t1.ToString(); + } + } + + extension(C o) + { + System.Collections.Generic.IEnumerable M(T t1, U u1, int x) + { + yield return o.GetString() + u1.ToString() + t1.ToString(); + } + } +} + +public class C +{ + public string GetString() => null; +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + foreach (var s in Test(new C("1"), 2, "3")) + System.Console.Write(s); + foreach (var s in new C("4").M2(5, "6")) + System.Console.Write(s); + } + + static System.Collections.Generic.IEnumerable Test(C o, T t1, U u1) + { + return o.M(t1, u1); + } +} + +static class Extensions +{ + extension(C o) + { + public System.Collections.Generic.IEnumerable M2(T t1, U u1) => o.M(t1, u1); + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceMethod_11_Async_Generic() + { + var src1 = """ +public static class Extensions +{ + extension(C o) + { + public async System.Threading.Tasks.Task M(T t1, U u1) + { + await System.Threading.Tasks.Task.Yield(); + return o.GetString() + u1.ToString() + t1.ToString(); + } + } +} + +public class C(string val) +{ + public string GetString() => val; +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Extensions.d__1<,>))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + class C`1 o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20d2 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method public hidebysig + instance class [mscorlib]System.Threading.Tasks.Task`1 M ( + !T t1, + !!U u1 + ) cil managed + { + // Method begins at RVA 0x20d4 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M + } // end of class <>E__0`1 + .class nested private auto ansi sealed beforefieldinit 'd__1`2' + extends [mscorlib]System.ValueType + implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public int32 '<>1__state' + .field public valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 '<>t__builder' + .field public class C`1 o + .field public !U u1 + .field public !T t1 + .field private valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter '<>u__1' + // Methods + .method private final hidebysig newslot virtual + instance void MoveNext () cil managed + { + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext() + // Method begins at RVA 0x20d8 + // Code size 202 (0xca) + .maxstack 3 + .locals init ( + [0] int32, + [1] string, + [2] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter, + [3] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable, + [4] class [mscorlib]System.Exception + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0044 + IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable [mscorlib]System.Threading.Tasks.Task::Yield() + IL_000f: stloc.3 + IL_0010: ldloca.s 3 + IL_0012: call instance valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [mscorlib]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter() + IL_0017: stloc.2 + IL_0018: ldloca.s 2 + IL_001a: call instance bool [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted() + IL_001f: brtrue.s IL_0060 + IL_0021: ldarg.0 + IL_0022: ldc.i4.0 + IL_0023: dup + IL_0024: stloc.0 + IL_0025: stfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_002a: ldarg.0 + IL_002b: ldloc.2 + IL_002c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter valuetype Extensions/'d__1`2'::'<>u__1' + IL_0031: ldarg.0 + IL_0032: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_0037: ldloca.s 2 + IL_0039: ldarg.0 + IL_003a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::AwaitUnsafeOnCompletedd__1`2'>(!!0&, !!1&) + IL_003f: leave IL_00c9 + IL_0044: ldarg.0 + IL_0045: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter valuetype Extensions/'d__1`2'::'<>u__1' + IL_004a: stloc.2 + IL_004b: ldarg.0 + IL_004c: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter valuetype Extensions/'d__1`2'::'<>u__1' + IL_0051: initobj [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter + IL_0057: ldarg.0 + IL_0058: ldc.i4.m1 + IL_0059: dup + IL_005a: stloc.0 + IL_005b: stfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_0060: ldloca.s 2 + IL_0062: call instance void [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult() + IL_0067: ldarg.0 + IL_0068: ldfld class C`1 valuetype Extensions/'d__1`2'::o + IL_006d: callvirt instance string class C`1::GetString() + IL_0072: ldarg.0 + IL_0073: ldflda !1 valuetype Extensions/'d__1`2'::u1 + IL_0078: constrained. !U + IL_007e: callvirt instance string [mscorlib]System.Object::ToString() + IL_0083: ldarg.0 + IL_0084: ldflda !0 valuetype Extensions/'d__1`2'::t1 + IL_0089: constrained. !T + IL_008f: callvirt instance string [mscorlib]System.Object::ToString() + IL_0094: call string [mscorlib]System.String::Concat(string, string, string) + IL_0099: stloc.1 + IL_009a: leave.s IL_00b5 + } // end .try + catch [mscorlib]System.Exception + { + IL_009c: stloc.s 4 + IL_009e: ldarg.0 + IL_009f: ldc.i4.s -2 + IL_00a1: stfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_00a6: ldarg.0 + IL_00a7: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_00ac: ldloc.s 4 + IL_00ae: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetException(class [mscorlib]System.Exception) + IL_00b3: leave.s IL_00c9 + } // end handler + IL_00b5: ldarg.0 + IL_00b6: ldc.i4.s -2 + IL_00b8: stfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_00bd: ldarg.0 + IL_00be: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_00c3: ldloc.1 + IL_00c4: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetResult(!0) + IL_00c9: ret + } // end of method 'd__1`2'::MoveNext + .method private final hidebysig newslot virtual + instance void SetStateMachine ( + class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + // Method begins at RVA 0x21c0 + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_0006: ldarg.1 + IL_0007: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + IL_000c: ret + } // end of method 'd__1`2'::SetStateMachine + } // end of class d__1`2 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Threading.Tasks.Task`1 M ( + class C`1 o, + !!T t1, + !!U u1 + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 14 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 60 32 00 00 + ) + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 71 (0x47) + .maxstack 2 + .locals init ( + [0] valuetype Extensions/'d__1`2' + ) + IL_0000: ldloca.s 0 + IL_0002: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Create() + IL_0007: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_000c: ldloca.s 0 + IL_000e: ldarg.0 + IL_000f: stfld class C`1 valuetype Extensions/'d__1`2'::o + IL_0014: ldloca.s 0 + IL_0016: ldarg.1 + IL_0017: stfld !0 valuetype Extensions/'d__1`2'::t1 + IL_001c: ldloca.s 0 + IL_001e: ldarg.2 + IL_001f: stfld !1 valuetype Extensions/'d__1`2'::u1 + IL_0024: ldloca.s 0 + IL_0026: ldc.i4.m1 + IL_0027: stfld int32 valuetype Extensions/'d__1`2'::'<>1__state' + IL_002c: ldloca.s 0 + IL_002e: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_0033: ldloca.s 0 + IL_0035: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Startd__1`2'>(!!0&) + IL_003a: ldloca.s 0 + IL_003c: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype Extensions/'d__1`2'::'<>t__builder' + IL_0041: call instance class [mscorlib]System.Threading.Tasks.Task`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::get_Task() + IL_0046: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(C o) + { + async System.Threading.Tasks.Task M(T t1, U u1) + { + await System.Threading.Tasks.Task.Yield(); + return o.GetString() + u1.ToString() + t1.ToString(); + } + } + + extension(C o) + { + async System.Threading.Tasks.Task M(T t1, U u1, int x) + { + await System.Threading.Tasks.Task.Yield(); + return o.GetString() + u1.ToString() + t1.ToString(); + } + } +} + +public class C +{ + public string GetString() => null; +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test(new C("1"), 2, "3").Result); + System.Console.Write(new C("4").M2(5, "6").Result); + } + + async static System.Threading.Tasks.Task Test(C o, T t1, U u1) + { + await System.Threading.Tasks.Task.Yield(); + return await o.M(t1, u1); + } +} + +static class Extensions +{ + extension(C o) + { + async public System.Threading.Tasks.Task M2(T t1, U u1) + { + await System.Threading.Tasks.Task.Yield(); + return await o.M(t1, u1); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "132465").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_StaticMethod_01() + { + var src1 = """ +public static class Extensions +{ + extension(object _) + { + public static string M(object o, string s) + { + return o + s; + } + } +} +"""; + var comp1 = CreateCompilation(src1); + + var verifier1 = CompileAndVerify(comp1, sourceSymbolValidator: verifySymbols, symbolValidator: verifySymbols).VerifyDiagnostics(); + + static void verifySymbols(ModuleSymbol m) + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + Assert.True(implementation.IsStatic); + Assert.Equal(MethodKind.Ordinary, implementation.MethodKind); + Assert.Equal(2, implementation.ParameterCount); + AssertEx.Equal("System.String Extensions.M(System.Object o, System.String s)", implementation.ToTestDisplayString()); + Assert.Equal(m is not PEModuleSymbol, implementation.IsImplicitlyDeclared); + Assert.False(implementation.IsExtensionMethod); + Assert.True(implementation.HasSpecialName); + Assert.False(implementation.HasRuntimeSpecialName); + + Assert.True(implementation.ContainingType.MightContainExtensionMethods); + + if (m is PEModuleSymbol peModuleSymbol) + { + Assert.True(peModuleSymbol.Module.HasExtensionAttribute(((PEAssemblySymbol)peModuleSymbol.ContainingAssembly).Assembly.Handle, ignoreCase: false)); + } + } + + var expectedTypeIL = """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object _ + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x207b + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x207d + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 19 (0x13) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldnull + IL_0004: br.s IL_000c + IL_0006: ldarg.0 + IL_0007: callvirt instance string [mscorlib]System.Object::ToString() + IL_000c: ldarg.1 + IL_000d: call string [mscorlib]System.String::Concat(string, string) + IL_0012: ret + } // end of method Extensions::M +} // end of class Extensions +"""; + + verifier1.VerifyTypeIL("Extensions", expectedTypeIL.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + static string Test(object o) + { + return object.M(o, "2"); + } +} + +static class Extensions +{ + extension(object o) + { + public static string M2(object o1, string s) + { + return object.M(o1, s); + } + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 17 (0x11) + .maxstack 2 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldstr ""2"" + IL_0007: call ""string Extensions.M(object, string)"" + IL_000c: stloc.0 + IL_000d: br.s IL_000f + IL_000f: ldloc.0 + IL_0010: ret +} +"; + verifier2.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 9 (0x9) + .maxstack 2 + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldarg.1 + IL_0003: call ""string Extensions.M(object, string)"" + IL_0008: ret +} +"; + verifier2.VerifyIL("Extensions.M2", m2IL); + + var comp1ImageReference = comp1.EmitToImageReference(); + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions.M2", m2IL); + + comp2 = CreateCompilationWithIL(src2, expectedTypeIL, options: TestOptions.DebugExe); + CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + var remove = """ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) +"""; + + comp2 = CreateCompilationWithIL(src2, expectedTypeIL.Remove(expectedTypeIL.IndexOf(remove), remove.Length)); + comp2.VerifyDiagnostics( + // (11,23): error CS0117: 'object' does not contain a definition for 'M' + // return object.M(o, "2"); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(11, 23), + // (21,27): error CS0117: 'object' does not contain a definition for 'M' + // return object.M(o, s); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(21, 27) + ); + + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + static string Test(object o) + { + return Extensions.M(o, "2"); + } +} + +static class Extensions_ +{ + extension(object o) + { + public static string M2(object o1, string s) + { + return Extensions.M(o1, s); + } + } +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + var vbComp = CreateVisualBasicCompilation(""" +Class Program + Shared Sub Main() + System.Console.Write(Test2("3")) + End Sub + + Shared Function Test2(o As String) As String + return Extensions.M(o, "4") + End Function +End Class +""", + referencedAssemblies: comp2.References, compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); + + CompileAndVerify(vbComp, expectedOutput: "34").VerifyDiagnostics(); + + if (!CompilationExtensions.EnableVerifyUsedAssemblies) // Tracked by https://github.com/dotnet/roslyn/issues/77542 + { + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + static string Test(object o) + { + System.Func d = object.M; + return d(o, "2"); + } +} + +static class Extensions +{ + extension(object o) + { + public static string M2(object o1, string s) + { + return new System.Func(object.M)(o1, s); + } + } +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + testIL = + @" +{ + // Code size 46 (0x2e) + .maxstack 3 + .locals init (System.Func V_0, //d + string V_1) + IL_0000: nop + IL_0001: ldsfld ""System.Func Program.<>O.<0>__M"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""string Extensions.M(object, string)"" + IL_0011: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld ""System.Func Program.<>O.<0>__M"" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: ldarg.0 + IL_001f: ldstr ""2"" + IL_0024: callvirt ""string System.Func.Invoke(object, string)"" + IL_0029: stloc.1 + IL_002a: br.s IL_002c + IL_002c: ldloc.1 + IL_002d: ret +} +"; + verifier2.VerifyIL("Program.Test", testIL); + + m2IL = + @" +{ + // Code size 21 (0x15) + .maxstack 3 + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn ""string Extensions.M(object, string)"" + IL_0008: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000d: ldarg.0 + IL_000e: ldarg.1 + IL_000f: callvirt ""string System.Func.Invoke(object, string)"" + IL_0014: ret +} +"; + verifier2.VerifyIL("Extensions.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions.M2", m2IL); + + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + static string Test(object o) + { + System.Func d = Extensions.M; + return d(o, "2"); + } +} + +static class Extensions_ +{ + extension(object o) + { + public static string M2(object o1, string s) + { + return new System.Func(Extensions.M)(o1, s); + } + } +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234").VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + unsafe static string Test(object o) + { + delegate* d = &object.M; + return d(o, "2"); + } +} + +static class Extensions +{ + extension(object o) + { + unsafe public static string M2(object o1, string s) + { + return ((delegate*)&object.M)(o1, s); + } + } +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234", verify: Verification.Skipped).VerifyDiagnostics(); + + testIL = +@" +{ + // Code size 27 (0x1b) + .maxstack 3 + .locals init (delegate* V_0, //d + delegate* V_1, + string V_2) + IL_0000: nop + IL_0001: ldftn ""string Extensions.M(object, string)"" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: stloc.1 + IL_000a: ldarg.0 + IL_000b: ldstr ""2"" + IL_0010: ldloc.1 + IL_0011: calli ""delegate*"" + IL_0016: stloc.2 + IL_0017: br.s IL_0019 + IL_0019: ldloc.2 + IL_001a: ret +} +"; + verifier2.VerifyIL("Program.Test", testIL); + + m2IL = +@" +{ + // Code size 17 (0x11) + .maxstack 3 + .locals init (delegate* V_0) + IL_0000: nop + IL_0001: ldftn ""string Extensions.M(object, string)"" + IL_0007: stloc.0 + IL_0008: ldarg.0 + IL_0009: ldarg.1 + IL_000a: ldloc.0 + IL_000b: calli ""delegate*"" + IL_0010: ret +} +"; + verifier2.VerifyIL("Extensions.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions.M2", m2IL); + + src2 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3", "4")); + } + + unsafe static string Test(object o) + { + delegate* d = &Extensions.M; + return d(o, "2"); + } +} + +static class Extensions_ +{ + extension(object o) + { + unsafe public static string M2(object o1, string s) + { + return ((delegate*)&Extensions.M)(o1, s); + } + } +} +"""; + + comp2 = CreateCompilation(src2, references: [comp1MetadataReference], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + + comp2 = CreateCompilation(src2, references: [comp1ImageReference], options: TestOptions.DebugExe.WithAllowUnsafe(true)); + verifier2 = CompileAndVerify(comp2, expectedOutput: "1234", verify: Verification.Skipped).VerifyDiagnostics(); + + verifier2.VerifyIL("Program.Test", testIL); + verifier2.VerifyIL("Extensions_.M2", m2IL); + } + + var comp5 = CreateCompilation(src1); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object _) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact] + public void Implementation_StaticMethod_02_WithLocalFunction() + { + var src1 = """ +public static class Extensions +{ + extension(object _) + { + public static string M(object o, string s) + { + string local() => o + s; + return local(); + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object _ + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20ab + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x20ad + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0' + extends [mscorlib]System.ValueType + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public object o + .field public string s + } // end of class <>c__DisplayClass1_0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x2068 + // Code size 24 (0x18) + .maxstack 2 + .locals init ( + [0] valuetype Extensions/'<>c__DisplayClass1_0' + ) + IL_0000: ldloca.s 0 + IL_0002: ldarg.0 + IL_0003: stfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0008: ldloca.s 0 + IL_000a: ldarg.1 + IL_000b: stfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0010: ldloca.s 0 + IL_0012: call string Extensions::'b__1_0'(valuetype Extensions/'<>c__DisplayClass1_0'&) + IL_0017: ret + } // end of method Extensions::M + .method assembly hidebysig static + string 'b__1_0' ( + valuetype Extensions/'<>c__DisplayClass1_0'& '' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x208c + // Code size 30 (0x1e) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0006: dup + IL_0007: brtrue.s IL_000d + IL_0009: pop + IL_000a: ldnull + IL_000b: br.s IL_0012 + IL_000d: callvirt instance string [mscorlib]System.Object::ToString() + IL_0012: ldarg.0 + IL_0013: ldfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0018: call string [mscorlib]System.String::Concat(string, string) + IL_001d: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object _) + { + static string M(object o, string s) + { + string local() => o + s; + return local(); + } + } + + extension(object) + { + static string M(object o, string s, int x) + { + string local() => o + s; + return local(); + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3","4")); + } + + static string Test(object o) + { + string local() => object.M(o, "2"); + return local(); + } +} + +static class Extensions +{ + extension(object _) + { + public static string M2(object o, string s) + { + string local() => object.M(o, s); + return local(); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_StaticMethod_03_WithLambda() + { + var src1 = """ +public static class Extensions +{ + extension(object _) + { + public static string M(object o, string s) + { + System.Func local = () => o + s; + return local(); + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object _ + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x208c + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x208e + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass1_0' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public object o + .field public string s + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2091 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ret + } // end of method '<>c__DisplayClass1_0'::.ctor + .method assembly hidebysig + instance string 'b__0' () cil managed + { + // Method begins at RVA 0x2099 + // Code size 30 (0x1e) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld object Extensions/'<>c__DisplayClass1_0'::o + IL_0006: dup + IL_0007: brtrue.s IL_000d + IL_0009: pop + IL_000a: ldnull + IL_000b: br.s IL_0012 + IL_000d: callvirt instance string [mscorlib]System.Object::ToString() + IL_0012: ldarg.0 + IL_0013: ldfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0018: call string [mscorlib]System.String::Concat(string, string) + IL_001d: ret + } // end of method '<>c__DisplayClass1_0'::'b__0' + } // end of class <>c__DisplayClass1_0 + // Methods + .method public hidebysig specialname static + string M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 36 (0x24) + .maxstack 8 + IL_0000: newobj instance void Extensions/'<>c__DisplayClass1_0'::.ctor() + IL_0005: dup + IL_0006: ldarg.0 + IL_0007: stfld object Extensions/'<>c__DisplayClass1_0'::o + IL_000c: dup + IL_000d: ldarg.1 + IL_000e: stfld string Extensions/'<>c__DisplayClass1_0'::s + IL_0013: ldftn instance string Extensions/'<>c__DisplayClass1_0'::'b__0'() + IL_0019: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) + IL_001e: callvirt instance !0 class [mscorlib]System.Func`1::Invoke() + IL_0023: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object _) + { + static string M(object o, string s) + { + System.Func local = () => o + s; + return local(); + } + } + + extension(object) + { + static string M(object o, string s, int x) + { + System.Func local = () => o + s; + return local(); + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write(object.M2("3","4")); + } + + static string Test(object o) + { + System.Func local = () => object.M(o, "2"); + return local(); + } +} + +static class Extensions +{ + extension(object _) + { + public static string M2(object o, string s) + { + System.Func local = () => object.M(o, s); + return local(); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_StaticMethod_04_Iterator() + { + var src1 = """ +public static class Extensions +{ + extension(object _) + { + public static System.Collections.Generic.IEnumerable M(object o, string s) + { + yield return o + s; + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.IteratorStateMachineAttribute(typeof(Extensions.d__1))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", (""" +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object _ + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x207e + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig static + class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x2080 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit 'd__1' + extends [mscorlib]System.Object + implements class [mscorlib]System.Collections.Generic.IEnumerable`1, + [mscorlib]System.Collections.IEnumerable, + class [mscorlib]System.Collections.Generic.IEnumerator`1, + +""" + + (ExecutionConditionUtil.IsMonoOrCoreClr ? +""" + [mscorlib]System.Collections.IEnumerator, + [mscorlib]System.IDisposable + +""" : +""" + [mscorlib]System.IDisposable, + [mscorlib]System.Collections.IEnumerator + +""") + +""" + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field private int32 '<>1__state' + .field private string '<>2__current' + .field private int32 '<>l__initialThreadId' + .field private object o + .field public object '<>3__o' + .field private string s + .field public string '<>3__s' + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + int32 '<>1__state' + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2083 + // Code size 25 (0x19) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: ldarg.0 + IL_0007: ldarg.1 + IL_0008: stfld int32 Extensions/'d__1'::'<>1__state' + IL_000d: ldarg.0 + IL_000e: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0013: stfld int32 Extensions/'d__1'::'<>l__initialThreadId' + IL_0018: ret + } // end of method 'd__1'::.ctor + .method private final hidebysig newslot virtual + instance void System.IDisposable.Dispose () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.IDisposable::Dispose() + // Method begins at RVA 0x209d + // Code size 9 (0x9) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldc.i4.s -2 + IL_0003: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0008: ret + } // end of method 'd__1'::System.IDisposable.Dispose + .method private final hidebysig newslot virtual + instance bool MoveNext () cil managed + { + .override method instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() + // Method begins at RVA 0x20a8 + // Code size 76 (0x4c) + .maxstack 3 + .locals init ( + [0] int32 + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0010 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: beq.s IL_0043 + IL_000e: ldc.i4.0 + IL_000f: ret + IL_0010: ldarg.0 + IL_0011: ldc.i4.m1 + IL_0012: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0017: ldarg.0 + IL_0018: ldarg.0 + IL_0019: ldfld object Extensions/'d__1'::o + IL_001e: dup + IL_001f: brtrue.s IL_0025 + IL_0021: pop + IL_0022: ldnull + IL_0023: br.s IL_002a + IL_0025: callvirt instance string [mscorlib]System.Object::ToString() + IL_002a: ldarg.0 + IL_002b: ldfld string Extensions/'d__1'::s + IL_0030: call string [mscorlib]System.String::Concat(string, string) + IL_0035: stfld string Extensions/'d__1'::'<>2__current' + IL_003a: ldarg.0 + IL_003b: ldc.i4.1 + IL_003c: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0041: ldc.i4.1 + IL_0042: ret + IL_0043: ldarg.0 + IL_0044: ldc.i4.m1 + IL_0045: stfld int32 Extensions/'d__1'::'<>1__state' + IL_004a: ldc.i4.0 + IL_004b: ret + } // end of method 'd__1'::MoveNext + .method private final hidebysig specialname newslot virtual + instance string 'System.Collections.Generic.IEnumerator.get_Current' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance !0 class [mscorlib]System.Collections.Generic.IEnumerator`1::get_Current() + // Method begins at RVA 0x2100 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string Extensions/'d__1'::'<>2__current' + IL_0006: ret + } // end of method 'd__1'::'System.Collections.Generic.IEnumerator.get_Current' + .method private final hidebysig newslot virtual + instance void System.Collections.IEnumerator.Reset () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Collections.IEnumerator::Reset() + // Method begins at RVA 0x2108 + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj instance void [mscorlib]System.NotSupportedException::.ctor() + IL_0005: throw + } // end of method 'd__1'::System.Collections.IEnumerator.Reset + .method private final hidebysig specialname newslot virtual + instance object System.Collections.IEnumerator.get_Current () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance object [mscorlib]System.Collections.IEnumerator::get_Current() + // Method begins at RVA 0x2100 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld string Extensions/'d__1'::'<>2__current' + IL_0006: ret + } // end of method 'd__1'::System.Collections.IEnumerator.get_Current + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.Generic.IEnumerator`1 'System.Collections.Generic.IEnumerable.GetEnumerator' () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.Generic.IEnumerator`1 class [mscorlib]System.Collections.Generic.IEnumerable`1::GetEnumerator() + // Method begins at RVA 0x2110 + // Code size 67 (0x43) + .maxstack 2 + .locals init ( + [0] class Extensions/'d__1' + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: ldc.i4.s -2 + IL_0008: bne.un.s IL_0022 + IL_000a: ldarg.0 + IL_000b: ldfld int32 Extensions/'d__1'::'<>l__initialThreadId' + IL_0010: call int32 [mscorlib]System.Environment::get_CurrentManagedThreadId() + IL_0015: bne.un.s IL_0022 + IL_0017: ldarg.0 + IL_0018: ldc.i4.0 + IL_0019: stfld int32 Extensions/'d__1'::'<>1__state' + IL_001e: ldarg.0 + IL_001f: stloc.0 + IL_0020: br.s IL_0029 + IL_0022: ldc.i4.0 + IL_0023: newobj instance void Extensions/'d__1'::.ctor(int32) + IL_0028: stloc.0 + IL_0029: ldloc.0 + IL_002a: ldarg.0 + IL_002b: ldfld object Extensions/'d__1'::'<>3__o' + IL_0030: stfld object Extensions/'d__1'::o + IL_0035: ldloc.0 + IL_0036: ldarg.0 + IL_0037: ldfld string Extensions/'d__1'::'<>3__s' + IL_003c: stfld string Extensions/'d__1'::s + IL_0041: ldloc.0 + IL_0042: ret + } // end of method 'd__1'::'System.Collections.Generic.IEnumerable.GetEnumerator' + .method private final hidebysig newslot virtual + instance class [mscorlib]System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator () cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.IEnumerable::GetEnumerator() + // Method begins at RVA 0x215f + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance class [mscorlib]System.Collections.Generic.IEnumerator`1 Extensions/'d__1'::'System.Collections.Generic.IEnumerable.GetEnumerator'() + IL_0006: ret + } // end of method 'd__1'::System.Collections.IEnumerable.GetEnumerator + // Properties + .property instance string 'System.Collections.Generic.IEnumerator.Current'() + { + .get instance string Extensions/'d__1'::'System.Collections.Generic.IEnumerator.get_Current'() + } + .property instance object System.Collections.IEnumerator.Current() + { + .get instance object Extensions/'d__1'::System.Collections.IEnumerator.get_Current() + } + } // end of class d__1 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Collections.Generic.IEnumerable`1 M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.IteratorStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 12 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 22 (0x16) + .maxstack 8 + IL_0000: ldc.i4.s -2 + IL_0002: newobj instance void Extensions/'d__1'::.ctor(int32) + IL_0007: dup + IL_0008: ldarg.0 + IL_0009: stfld object Extensions/'d__1'::'<>3__o' + IL_000e: dup + IL_000f: ldarg.1 + IL_0010: stfld string Extensions/'d__1'::'<>3__s' + IL_0015: ret + } // end of method Extensions::M +} // end of class Extensions +""").Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object _) + { + static System.Collections.Generic.IEnumerable M(object o, string s) + { + yield return o + s; + } + } + + extension(object) + { + static System.Collections.Generic.IEnumerable M(object o, string s, int x) + { + yield return o + s; + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + foreach (var s in Test("1")) + System.Console.Write(s); + foreach (var s in object.M2("3", "4")) + System.Console.Write(s); + } + + static System.Collections.Generic.IEnumerable Test(object o) + { + return object.M(o, "2"); + } +} + +static class Extensions +{ + extension(object _) + { + public static System.Collections.Generic.IEnumerable M2(object o, string s) => object.M(o, s); + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_StaticMethod_05_Async() + { + var src1 = """ +public static class Extensions +{ + extension(object _) + { + public static async System.Threading.Tasks.Task M(object o, string s) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1, symbolValidator: (m) => + { + MethodSymbol implementation = m.ContainingAssembly.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + AssertEx.Equal("System.Runtime.CompilerServices.AsyncStateMachineAttribute(typeof(Extensions.d__1))", implementation.GetAttributes().Single().ToString()); + }).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object _ + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x20b3 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig static + class [mscorlib]System.Threading.Tasks.Task`1 M ( + object o, + string s + ) cil managed + { + // Method begins at RVA 0x20b5 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M + } // end of class <>E__0 + .class nested private auto ansi sealed beforefieldinit 'd__1' + extends [mscorlib]System.ValueType + implements [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public int32 '<>1__state' + .field public valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 '<>t__builder' + .field public object o + .field public string s + .field private valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter '<>u__1' + // Methods + .method private final hidebysig newslot virtual + instance void MoveNext () cil managed + { + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::MoveNext() + // Method begins at RVA 0x20b8 + // Code size 178 (0xb2) + .maxstack 3 + .locals init ( + [0] int32, + [1] string, + [2] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter, + [3] valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable, + [4] class [mscorlib]System.Exception + ) + IL_0000: ldarg.0 + IL_0001: ldfld int32 Extensions/'d__1'::'<>1__state' + IL_0006: stloc.0 + .try + { + IL_0007: ldloc.0 + IL_0008: brfalse.s IL_0041 + IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable [mscorlib]System.Threading.Tasks.Task::Yield() + IL_000f: stloc.3 + IL_0010: ldloca.s 3 + IL_0012: call instance valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter [mscorlib]System.Runtime.CompilerServices.YieldAwaitable::GetAwaiter() + IL_0017: stloc.2 + IL_0018: ldloca.s 2 + IL_001a: call instance bool [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::get_IsCompleted() + IL_001f: brtrue.s IL_005d + IL_0021: ldarg.0 + IL_0022: ldc.i4.0 + IL_0023: dup + IL_0024: stloc.0 + IL_0025: stfld int32 Extensions/'d__1'::'<>1__state' + IL_002a: ldarg.0 + IL_002b: ldloc.2 + IL_002c: stfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_0031: ldarg.0 + IL_0032: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0037: ldloca.s 2 + IL_0039: ldarg.0 + IL_003a: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::AwaitUnsafeOnCompletedd__1'>(!!0&, !!1&) + IL_003f: leave.s IL_00b1 + IL_0041: ldarg.0 + IL_0042: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_0047: stloc.2 + IL_0048: ldarg.0 + IL_0049: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter Extensions/'d__1'::'<>u__1' + IL_004e: initobj [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter + IL_0054: ldarg.0 + IL_0055: ldc.i4.m1 + IL_0056: dup + IL_0057: stloc.0 + IL_0058: stfld int32 Extensions/'d__1'::'<>1__state' + IL_005d: ldloca.s 2 + IL_005f: call instance void [mscorlib]System.Runtime.CompilerServices.YieldAwaitable/YieldAwaiter::GetResult() + IL_0064: ldarg.0 + IL_0065: ldfld object Extensions/'d__1'::o + IL_006a: dup + IL_006b: brtrue.s IL_0071 + IL_006d: pop + IL_006e: ldnull + IL_006f: br.s IL_0076 + IL_0071: callvirt instance string [mscorlib]System.Object::ToString() + IL_0076: ldarg.0 + IL_0077: ldfld string Extensions/'d__1'::s + IL_007c: call string [mscorlib]System.String::Concat(string, string) + IL_0081: stloc.1 + IL_0082: leave.s IL_009d + } // end .try + catch [mscorlib]System.Exception + { + IL_0084: stloc.s 4 + IL_0086: ldarg.0 + IL_0087: ldc.i4.s -2 + IL_0089: stfld int32 Extensions/'d__1'::'<>1__state' + IL_008e: ldarg.0 + IL_008f: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0094: ldloc.s 4 + IL_0096: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetException(class [mscorlib]System.Exception) + IL_009b: leave.s IL_00b1 + } // end handler + IL_009d: ldarg.0 + IL_009e: ldc.i4.s -2 + IL_00a0: stfld int32 Extensions/'d__1'::'<>1__state' + IL_00a5: ldarg.0 + IL_00a6: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_00ab: ldloc.1 + IL_00ac: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetResult(!0) + IL_00b1: ret + } // end of method 'd__1'::MoveNext + .method private final hidebysig newslot virtual + instance void SetStateMachine ( + class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine stateMachine + ) cil managed + { + .custom instance void [mscorlib]System.Diagnostics.DebuggerHiddenAttribute::.ctor() = ( + 01 00 00 00 + ) + .override method instance void [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + // Method begins at RVA 0x2188 + // Code size 13 (0xd) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0006: ldarg.1 + IL_0007: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::SetStateMachine(class [mscorlib]System.Runtime.CompilerServices.IAsyncStateMachine) + IL_000c: ret + } // end of method 'd__1'::SetStateMachine + } // end of class d__1 + // Methods + .method public hidebysig specialname static + class [mscorlib]System.Threading.Tasks.Task`1 M ( + object o, + string s + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( + 01 00 12 45 78 74 65 6e 73 69 6f 6e 73 2b 3c 4d + 3e 64 5f 5f 31 00 00 + ) + // Method begins at RVA 0x2068 + // Code size 63 (0x3f) + .maxstack 2 + .locals init ( + [0] valuetype Extensions/'d__1' + ) + IL_0000: ldloca.s 0 + IL_0002: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Create() + IL_0007: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_000c: ldloca.s 0 + IL_000e: ldarg.0 + IL_000f: stfld object Extensions/'d__1'::o + IL_0014: ldloca.s 0 + IL_0016: ldarg.1 + IL_0017: stfld string Extensions/'d__1'::s + IL_001c: ldloca.s 0 + IL_001e: ldc.i4.m1 + IL_001f: stfld int32 Extensions/'d__1'::'<>1__state' + IL_0024: ldloca.s 0 + IL_0026: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_002b: ldloca.s 0 + IL_002d: call instance void valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::Startd__1'>(!!0&) + IL_0032: ldloca.s 0 + IL_0034: ldflda valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1 Extensions/'d__1'::'<>t__builder' + IL_0039: call instance class [mscorlib]System.Threading.Tasks.Task`1 valuetype [mscorlib]System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1::get_Task() + IL_003e: ret + } // end of method Extensions::M +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object _) + { + static async System.Threading.Tasks.Task M(object o, string s) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } + + extension(object) + { + static async System.Threading.Tasks.Task M(object o, string s, int x) + { + await System.Threading.Tasks.Task.Yield(); + return o + s; + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1").Result); + System.Console.Write(object.M2("3", "4").Result); + } + + async static System.Threading.Tasks.Task Test(object o) + { + await System.Threading.Tasks.Task.Yield(); + return await object.M(o, "2"); + } +} + +static class Extensions +{ + extension(object _) + { + async public static System.Threading.Tasks.Task M2(object o, string s) + { + await System.Threading.Tasks.Task.Yield(); + return await object.M(o, s); + } + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "1234").VerifyDiagnostics(); + } + + [Fact] + public void Implementation_InstanceProperty_01() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public string P => o.ToString(); + } +} +"""; + var comp1 = CreateCompilation(src1); + + MethodSymbol implementation = comp1.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + Assert.True(implementation.IsStatic); + Assert.Equal(MethodKind.Ordinary, implementation.MethodKind); + Assert.Equal(1, implementation.ParameterCount); + AssertEx.Equal("System.String Extensions.get_P(System.Object o)", implementation.ToTestDisplayString()); + Assert.True(implementation.IsImplicitlyDeclared); + Assert.False(implementation.IsExtensionMethod); + Assert.True(implementation.HasSpecialName); + Assert.False(implementation.HasRuntimeSpecialName); + + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + var expectedTypeIL = """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206f + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig specialname + instance string get_P () cil managed + { + // Method begins at RVA 0x2071 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_P + // Properties + .property instance string P() + { + .get instance string Extensions/'<>E__0'::get_P() + } + } // end of class <>E__0 + // Methods + .method public hidebysig specialname static + string get_P ( + object o + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: callvirt instance string [mscorlib]System.Object::ToString() + IL_0006: ret + } // end of method Extensions::get_P +} // end of class Extensions +"""; + + verifier1.VerifyTypeIL("Extensions", expectedTypeIL.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("2".P2); + } + + static string Test(object o) + { + return o.P; + } +} + +static class Extensions +{ + extension(object o) + { + public string P2 => o.P; + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 12 (0xc) + .maxstack 1 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""string Extensions.get_P(object)"" + IL_0007: stloc.0 + IL_0008: br.s IL_000a + IL_000a: ldloc.0 + IL_000b: ret +} +"; + verifier3.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""string Extensions.get_P(object)"" + IL_0006: ret +} +"; + verifier3.VerifyIL("Extensions.get_P2(object)", m2IL); + + var comp1ImageReference = comp1.EmitToImageReference(); + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions.get_P2(object)", m2IL); + + comp3 = CreateCompilationWithIL(src3, expectedTypeIL, options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + var remove = """ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) +"""; + + comp3 = CreateCompilationWithIL(src3, expectedTypeIL.Remove(expectedTypeIL.IndexOf(remove), remove.Length)); + comp3.VerifyDiagnostics( + // (11,18): error CS1061: 'object' does not contain a definition for 'P' and no accessible extension method 'P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // return o.P; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "P").WithArguments("object", "P").WithLocation(11, 18), + // (19,31): error CS1061: 'object' does not contain a definition for 'P' and no accessible extension method 'P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // public string P2 => o.P; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "P").WithArguments("object", "P").WithLocation(19, 31) + ); + + src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("2".P2); + } + + static string Test(object o) + { + return o.get_P(); + } +} + +static class Extensions +{ + extension(object o) + { + public string P2 => o.get_P(); + } +} +"""; + + comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + comp3.VerifyDiagnostics( + // (11,18): error CS1061: 'object' does not contain a definition for 'get_P' and no accessible extension method 'get_P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // return o.get_P(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "get_P").WithArguments("object", "get_P").WithLocation(11, 18), + // (19,31): error CS1061: 'object' does not contain a definition for 'get_P' and no accessible extension method 'get_P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // public string P2 => o.get_P(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "get_P").WithArguments("object", "get_P").WithLocation(19, 31) + ); + + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + comp3.VerifyDiagnostics( + // (11,18): error CS1061: 'object' does not contain a definition for 'get_P' and no accessible extension method 'get_P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // return o.get_P(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "get_P").WithArguments("object", "get_P").WithLocation(11, 18), + // (19,31): error CS1061: 'object' does not contain a definition for 'get_P' and no accessible extension method 'get_P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // public string P2 => o.get_P(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "get_P").WithArguments("object", "get_P").WithLocation(19, 31) + ); + + src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("2".P2); + } + + static string Test(object o) + { + return Extensions.get_P(o); + } +} + +static class Extensions_ +{ + extension(object o) + { + public string P2 => Extensions.get_P(o); + } +} +"""; + + comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions_.get_P2(object)", m2IL); + + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions_.get_P2(object)", m2IL); + + var vbComp = CreateVisualBasicCompilation(""" +Class Program + Shared Sub Main() + System.Console.Write(Test2("3")) + End Sub + + Shared Function Test2(o As String) As String + return Extensions.get_P(o) + End Function +End Class +""", + referencedAssemblies: comp3.References, compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); + + CompileAndVerify(vbComp, expectedOutput: "3").VerifyDiagnostics(); + + vbComp = CreateVisualBasicCompilation(""" +Class Program + Shared Sub Main() + System.Console.Write(Test1("1")) + End Sub + + Shared Function Test1(o As String) As String + return o.get_P() + End Function +End Class +""", + referencedAssemblies: comp3.References, compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); + + vbComp.VerifyDiagnostics( + // error BC30456: 'get_P' is not a member of 'String'. + Diagnostic(30456 /*ERRID.ERR_NameNotMember2*/, "o.get_P").WithArguments("get_P", "String").WithLocation(7, 16) + ); + + var comp5 = CreateCompilation(src1); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object o) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact] + public void Implementation_StaticProperty_01() + { + var src1 = """ +public static class Extensions +{ + extension(object) + { + public static string P => "P"; + } +} +"""; + var comp1 = CreateCompilation(src1); + + MethodSymbol implementation = comp1.GetTypeByMetadataName("Extensions").GetMembers().OfType().Single(); + Assert.True(implementation.IsStatic); + Assert.Equal(MethodKind.Ordinary, implementation.MethodKind); + Assert.Equal(0, implementation.ParameterCount); + AssertEx.Equal("System.String Extensions.get_P()", implementation.ToTestDisplayString()); + Assert.True(implementation.IsImplicitlyDeclared); + Assert.False(implementation.IsExtensionMethod); + Assert.True(implementation.HasSpecialName); + Assert.False(implementation.HasRuntimeSpecialName); + + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + var expectedTypeIL = """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object '' + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206e + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig specialname static + string get_P () cil managed + { + // Method begins at RVA 0x2070 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_P + // Properties + .property string P() + { + .get string Extensions/'<>E__0'::get_P() + } + } // end of class <>E__0 + // Methods + .method public hidebysig specialname static + string get_P () cil managed + { + // Method begins at RVA 0x2067 + // Code size 6 (0x6) + .maxstack 8 + IL_0000: ldstr "P" + IL_0005: ret + } // end of method Extensions::get_P +} // end of class Extensions +"""; + + verifier1.VerifyTypeIL("Extensions", expectedTypeIL.Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test()); + System.Console.Write(object.P2); + } + + static string Test() + { + return object.P; + } +} + +static class Extensions +{ + extension(object o) + { + public static string P2 => object.P; + } +} +"""; + + var comp1MetadataReference = comp1.ToMetadataReference(); + var comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + var verifier3 = CompileAndVerify(comp3, expectedOutput: "PP").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 11 (0xb) + .maxstack 1 + .locals init (string V_0) + IL_0000: nop + IL_0001: call ""string Extensions.get_P()"" + IL_0006: stloc.0 + IL_0007: br.s IL_0009 + IL_0009: ldloc.0 + IL_000a: ret +} +"; + verifier3.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 6 (0x6) + .maxstack 1 + IL_0000: call ""string Extensions.get_P()"" + IL_0005: ret +} +"; + verifier3.VerifyIL("Extensions.get_P2()", m2IL); + + var comp1ImageReference = comp1.EmitToImageReference(); + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "PP").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions.get_P2()", m2IL); + + comp3 = CreateCompilationWithIL(src3, expectedTypeIL, options: TestOptions.DebugExe); + CompileAndVerify(comp3, expectedOutput: "PP").VerifyDiagnostics(); + + var remove = """ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) +"""; + + comp3 = CreateCompilationWithIL(src3, expectedTypeIL.Remove(expectedTypeIL.IndexOf(remove), remove.Length)); + comp3.VerifyDiagnostics( + // (11,23): error CS0117: 'object' does not contain a definition for 'P' + // return object.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("object", "P").WithLocation(11, 23), + // (19,43): error CS0117: 'object' does not contain a definition for 'P' + // public static string P2 => object.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("object", "P").WithLocation(19, 43) + ); + + src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test()); + System.Console.Write(object.P2); + } + + static string Test() + { + return Extensions.get_P(); + } +} + +static class Extensions_ +{ + extension(object o) + { + public static string P2 => Extensions.get_P(); + } +} +"""; + + comp3 = CreateCompilation(src3, references: [comp1MetadataReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "PP").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions_.get_P2()", m2IL); + + comp3 = CreateCompilation(src3, references: [comp1ImageReference], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "PP").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions_.get_P2()", m2IL); + + var vbComp = CreateVisualBasicCompilation(""" +Class Program + Shared Sub Main() + System.Console.Write(Test2()) + End Sub + + Shared Function Test2() As String + return Extensions.get_P() + End Function +End Class +""", + referencedAssemblies: comp3.References, compilationOptions: new VisualBasicCompilationOptions(OutputKind.ConsoleApplication)); + + CompileAndVerify(vbComp, expectedOutput: "P").VerifyDiagnostics(); + + var comp5 = CreateCompilation(src1); + comp5.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + comp5.VerifyEmitDiagnostics( + // (3,5): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // extension(object) + Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "extension").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 5) + ); + } + + [Fact] + public void Implementation_InstanceProperty_02() + { + var src1 = """ +public static class Extensions +{ + extension(object o) + { + public string P { get { return o.ToString(); } } + } +} +"""; + var comp1 = CreateCompilation(src1); + var verifier1 = CompileAndVerify(comp1).VerifyDiagnostics(); + + verifier1.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206f + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method public hidebysig specialname + instance string get_P () cil managed + { + // Method begins at RVA 0x2071 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::get_P + // Properties + .property instance string P() + { + .get instance string Extensions/'<>E__0'::get_P() + } + } // end of class <>E__0 + // Methods + .method public hidebysig specialname static + string get_P ( + object o + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: callvirt instance string [mscorlib]System.Object::ToString() + IL_0006: ret + } // end of method Extensions::get_P +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src3 = """ +class Program +{ + static void Main() + { + System.Console.Write(Test("1")); + System.Console.Write("2".P2); + } + + static string Test(object o) + { + return o.P; + } +} + +static class Extensions +{ + extension(object o) + { + public string P2 => o.P; + } +} +"""; + + var comp3 = CreateCompilation(src3, references: [comp1.ToMetadataReference()], options: TestOptions.DebugExe); + var verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + var testIL = +@" +{ + // Code size 12 (0xc) + .maxstack 1 + .locals init (string V_0) + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: call ""string Extensions.get_P(object)"" + IL_0007: stloc.0 + IL_0008: br.s IL_000a + IL_000a: ldloc.0 + IL_000b: ret +} +"; + verifier3.VerifyIL("Program.Test", testIL); + + var m2IL = +@" +{ + // Code size 7 (0x7) + .maxstack 1 + IL_0000: ldarg.0 + IL_0001: call ""string Extensions.get_P(object)"" + IL_0006: ret +} +"; + verifier3.VerifyIL("Extensions.get_P2(object)", m2IL); + + comp3 = CreateCompilation(src3, references: [comp1.EmitToImageReference()], options: TestOptions.DebugExe); + verifier3 = CompileAndVerify(comp3, expectedOutput: "12").VerifyDiagnostics(); + + verifier3.VerifyIL("Program.Test", testIL); + verifier3.VerifyIL("Extensions.get_P2(object)", m2IL); + } + + [Fact] + public void Implementation_DelegateCaching_01() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Func local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public V M1() => default; +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x20a6 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M2 + } // end of class <>E__0`1 + .class nested private auto ansi abstract sealed beforefieldinit 'O__1_0`3' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [mscorlib]System.Func`1 '<0>__M1' + } // end of class O__1_0`3 + // Methods + .method private hidebysig specialname static + void M2 ( + !!T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + class [mscorlib]System.Func`1 'b__1_0' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2069 + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldsfld class [mscorlib]System.Func`1 class Extensions/'O__1_0`3'::'<0>__M1' + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn !!2 C1::M1() + IL_0010: newobj instance void class [mscorlib]System.Func`1::.ctor(object, native int) + IL_0015: dup + IL_0016: stsfld class [mscorlib]System.Func`1 class Extensions/'O__1_0`3'::'<0>__M1' + IL_001b: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Func local() + { + return C1.M1; + } + } + } + + extension(T o) + { + void M2(int x) + { + System.Func local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public V M1() => default; +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DelegateCaching_02() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Action local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public void M1() {} +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x208e + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M2 + } // end of class <>E__0`1 + .class nested private auto ansi abstract sealed beforefieldinit '<>O__1_0`1' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [mscorlib]System.Action '<0>__M1' + } // end of class <>O__1_0`1 + // Methods + .method private hidebysig specialname static + void M2 ( + !!T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + class [mscorlib]System.Action 'b__1_0' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2069 + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldsfld class [mscorlib]System.Action class Extensions/'<>O__1_0`1'::'<0>__M1' + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn void C1::M1() + IL_0010: newobj instance void [mscorlib]System.Action::.ctor(object, native int) + IL_0015: dup + IL_0016: stsfld class [mscorlib]System.Action class Extensions/'<>O__1_0`1'::'<0>__M1' + IL_001b: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Action local() + { + return C1.M1; + } + } + } + + extension(T o) + { + void M2(int x) + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Action local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public void M1() {} +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DelegateCaching_03() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Action local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public void M1() {} +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x208e + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M2 + } // end of class <>E__0 + .class nested private auto ansi abstract sealed beforefieldinit '<>O' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [mscorlib]System.Action '<0>__M1' + } // end of class <>O + // Methods + .method private hidebysig specialname static + void M2 ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + class [mscorlib]System.Action 'b__1_0' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2069 + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldsfld class [mscorlib]System.Action Extensions/'<>O'::'<0>__M1' + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn void C1::M1() + IL_0010: newobj instance void [mscorlib]System.Action::.ctor(object, native int) + IL_0015: dup + IL_0016: stsfld class [mscorlib]System.Action Extensions/'<>O'::'<0>__M1' + IL_001b: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + System.Action local() + { + return C1.M1; + } + } + } + + extension(object o) + { + void M2(int x) + { + System.Action local() + { + return C1.M1; + } + } + } +} + +class C1 +{ + static public void M1() {} +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DelegateCaching_04() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + System.Action M2() + { + return local; + + static void local() + { + typeof(T).ToString(); + } + } + } +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2096 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method private hidebysig + instance class [mscorlib]System.Action M2 () cil managed + { + // Method begins at RVA 0x2098 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M2 + } // end of class <>E__0`1 + .class nested private auto ansi abstract sealed beforefieldinit '<>O__1_0`1' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [mscorlib]System.Action '<0>__local' + } // end of class <>O__1_0`1 + // Methods + .method private hidebysig specialname static + class [mscorlib]System.Action M2 ( + !!T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldsfld class [mscorlib]System.Action class Extensions/'<>O__1_0`1'::'<0>__local' + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn void Extensions::'b__1_0'() + IL_0010: newobj instance void [mscorlib]System.Action::.ctor(object, native int) + IL_0015: dup + IL_0016: stsfld class [mscorlib]System.Action class Extensions/'<>O__1_0`1'::'<0>__local' + IL_001b: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + void 'b__1_0' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2084 + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldtoken !!T + IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_000a: callvirt instance string [mscorlib]System.Object::ToString() + IL_000f: pop + IL_0010: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(T o) + { + System.Action M2() + { + return local; + + static void local() + { + typeof(T).ToString(); + } + } + } + + extension(T o) + { + System.Action M2(int x) + { + return local; + + static void local() + { + typeof(T).ToString(); + } + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DelegateCaching_05() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + System.Action M2() + { + return local; + + static void local() + { + typeof(object).ToString(); + } + } + } +} +"""; + var comp = CreateCompilation(src); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2096 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig + instance class [mscorlib]System.Action M2 () cil managed + { + // Method begins at RVA 0x2098 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M2 + } // end of class <>E__0 + .class nested private auto ansi abstract sealed beforefieldinit '<>O' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [mscorlib]System.Action '<0>__local' + } // end of class <>O + // Methods + .method private hidebysig specialname static + class [mscorlib]System.Action M2 ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 28 (0x1c) + .maxstack 8 + IL_0000: ldsfld class [mscorlib]System.Action Extensions/'<>O'::'<0>__local' + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn void Extensions::'b__1_0'() + IL_0010: newobj instance void [mscorlib]System.Action::.ctor(object, native int) + IL_0015: dup + IL_0016: stsfld class [mscorlib]System.Action Extensions/'<>O'::'<0>__local' + IL_001b: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + void 'b__1_0' () cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2084 + // Code size 17 (0x11) + .maxstack 8 + IL_0000: ldtoken [mscorlib]System.Object + IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_000a: callvirt instance string [mscorlib]System.Object::ToString() + IL_000f: pop + IL_0010: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + System.Action M2() + { + return local; + + static void local() + { + typeof(object).ToString(); + } + } + } + + extension(object o) + { + System.Action M2(int x) + { + return local; + + static void local() + { + typeof(object).ToString(); + } + } + } +} +"""; + var comp2 = CreateCompilation(src2); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DynamicCallSite_01() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d, T t, U u, V v) + { + d.M1(t, u, v); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x20ea + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M2 + } // end of class <>E__0`1 + .class nested private auto ansi abstract sealed beforefieldinit '<>o__0|1`3' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1> '<>p__0' + } // end of class <>o__0|1`3 + // Methods + .method private hidebysig specialname static + void M2 ( + !!T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + void 'b__1_0' ( + object d, + !!T t, + !!U u, + !!V v + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .param [1] + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206c + // Code size 114 (0x72) + .maxstack 9 + IL_0000: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__0|1`3'::'<>p__0' + IL_0005: brtrue.s IL_0059 + IL_0007: ldc.i4 256 + IL_000c: ldstr "M1" + IL_0011: ldnull + IL_0012: ldtoken Extensions + IL_0017: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001c: ldc.i4.4 + IL_001d: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo + IL_0022: dup + IL_0023: ldc.i4.0 + IL_0024: ldc.i4.0 + IL_0025: ldnull + IL_0026: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_002b: stelem.ref + IL_002c: dup + IL_002d: ldc.i4.1 + IL_002e: ldc.i4.1 + IL_002f: ldnull + IL_0030: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_0035: stelem.ref + IL_0036: dup + IL_0037: ldc.i4.2 + IL_0038: ldc.i4.1 + IL_0039: ldnull + IL_003a: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_003f: stelem.ref + IL_0040: dup + IL_0041: ldc.i4.3 + IL_0042: ldc.i4.1 + IL_0043: ldnull + IL_0044: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_0049: stelem.ref + IL_004a: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [mscorlib]System.Collections.Generic.IEnumerable`1, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_004f: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder) + IL_0054: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__0|1`3'::'<>p__0' + IL_0059: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__0|1`3'::'<>p__0' + IL_005e: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Target + IL_0063: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__0|1`3'::'<>p__0' + IL_0068: ldarg.0 + IL_0069: ldarg.1 + IL_006a: ldarg.2 + IL_006b: ldarg.3 + IL_006c: callvirt instance void class [mscorlib]System.Action`5::Invoke(!0, !1, !2, !3, !4) + IL_0071: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]"). + Replace("[System.Core]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[System.Core]")); + + var src2 = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d, T t, U u, V v) + { + d.M1(t, u, v); + } + } + } + + extension(T o) + { + void M2(int x) + { + void local(dynamic d, T t, U u, V v) + { + d.M1(t, u, v); + } + } + } +} +"""; + var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DynamicCallSite_02() + { + var src = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d, T t) + { + d.M1(t); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0`1' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + !T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0`1'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x20d4 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0`1'::M2 + } // end of class <>E__0`1 + .class nested private auto ansi abstract sealed beforefieldinit '<>o__1`1' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1> '<>p__0' + } // end of class <>o__1`1 + // Methods + .method private hidebysig specialname static + void M2 ( + !!T o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + void 'b__1_0' ( + object d, + !!T t + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .param [1] + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206c + // Code size 92 (0x5c) + .maxstack 9 + IL_0000: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__1`1'::'<>p__0' + IL_0005: brtrue.s IL_0045 + IL_0007: ldc.i4 256 + IL_000c: ldstr "M1" + IL_0011: ldnull + IL_0012: ldtoken Extensions + IL_0017: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001c: ldc.i4.2 + IL_001d: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo + IL_0022: dup + IL_0023: ldc.i4.0 + IL_0024: ldc.i4.0 + IL_0025: ldnull + IL_0026: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_002b: stelem.ref + IL_002c: dup + IL_002d: ldc.i4.1 + IL_002e: ldc.i4.1 + IL_002f: ldnull + IL_0030: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_0035: stelem.ref + IL_0036: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [mscorlib]System.Collections.Generic.IEnumerable`1, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_003b: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder) + IL_0040: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__1`1'::'<>p__0' + IL_0045: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__1`1'::'<>p__0' + IL_004a: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Target + IL_004f: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> class Extensions/'<>o__1`1'::'<>p__0' + IL_0054: ldarg.0 + IL_0055: ldarg.1 + IL_0056: callvirt instance void class [mscorlib]System.Action`3::Invoke(!0, !1, !2) + IL_005b: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]"). + Replace("[System.Core]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[System.Core]")); + + var src2 = """ +public static class Extensions +{ + extension(T o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d, T t) + { + d.M1(t); + } + } + } + + extension(T o) + { + void M2(int x) + { + void local(dynamic d, T t) + { + d.M1(t); + } + } + } +} +"""; + var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void Implementation_DynamicCallSite_03() + { + var src = """ +public static class Extensions +{ + extension(object o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d) + { + d.M1(); + } + } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp); + var verifier = CompileAndVerify(comp).VerifyDiagnostics(); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Consider executing and verifying behavior + + verifier.VerifyTypeIL("Extensions", """ +.class public auto ansi abstract sealed beforefieldinit Extensions + extends [mscorlib]System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Nested Types + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends [mscorlib]System.Object + { + // Methods + .method private hidebysig specialname static + void '$' ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method '<>E__0'::'$' + .method private hidebysig + instance void M2 () cil managed + { + // Method begins at RVA 0x20c9 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } // end of method '<>E__0'::M2 + } // end of class <>E__0 + .class nested private auto ansi abstract sealed beforefieldinit '<>o__1' + extends [mscorlib]System.Object + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + // Fields + .field public static class [System.Core]System.Runtime.CompilerServices.CallSite`1> '<>p__0' + } // end of class <>o__1 + // Methods + .method private hidebysig specialname static + void M2 ( + object o + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x2067 + // Code size 1 (0x1) + .maxstack 8 + IL_0000: ret + } // end of method Extensions::M2 + .method assembly hidebysig static + void 'b__1_0' ( + object d + ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( + 01 00 00 00 + ) + .param [1] + .custom instance void [System.Core]System.Runtime.CompilerServices.DynamicAttribute::.ctor() = ( + 01 00 00 00 + ) + // Method begins at RVA 0x206c + // Code size 81 (0x51) + .maxstack 9 + IL_0000: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> Extensions/'<>o__1'::'<>p__0' + IL_0005: brtrue.s IL_003b + IL_0007: ldc.i4 256 + IL_000c: ldstr "M1" + IL_0011: ldnull + IL_0012: ldtoken Extensions + IL_0017: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) + IL_001c: ldc.i4.1 + IL_001d: newarr [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo + IL_0022: dup + IL_0023: ldc.i4.0 + IL_0024: ldc.i4.0 + IL_0025: ldnull + IL_0026: call class [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo::Create(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string) + IL_002b: stelem.ref + IL_002c: call class [System.Core]System.Runtime.CompilerServices.CallSiteBinder [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.Binder::InvokeMember(valuetype [Microsoft.CSharp]Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, class [mscorlib]System.Collections.Generic.IEnumerable`1, class [mscorlib]System.Type, class [mscorlib]System.Collections.Generic.IEnumerable`1) + IL_0031: call class [System.Core]System.Runtime.CompilerServices.CallSite`1 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Create(class [System.Core]System.Runtime.CompilerServices.CallSiteBinder) + IL_0036: stsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> Extensions/'<>o__1'::'<>p__0' + IL_003b: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> Extensions/'<>o__1'::'<>p__0' + IL_0040: ldfld !0 class [System.Core]System.Runtime.CompilerServices.CallSite`1>::Target + IL_0045: ldsfld class [System.Core]System.Runtime.CompilerServices.CallSite`1> Extensions/'<>o__1'::'<>p__0' + IL_004a: ldarg.0 + IL_004b: callvirt instance void class [mscorlib]System.Action`2::Invoke(!0, !1) + IL_0050: ret + } // end of method Extensions::'b__1_0' +} // end of class Extensions +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]"). + Replace("[System.Core]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[System.Core]")); + + var src2 = """ +public static class Extensions +{ + extension(object o) + { + void M2() + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d) + { + d.M1(); + } + } + } + + extension(object o) + { + void M2(int x) + { + #pragma warning disable CS8321 // The local function 'local' is declared but never used + void local(dynamic d) + { + d.M1(); + } + } + } +} +"""; + var comp2 = CreateCompilation(src2, targetFramework: TargetFramework.StandardAndCSharp); + CompileAndVerify(comp2).VerifyDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_Simple() + { + var src = """ +new object().M(); + +public static class Extensions +{ + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new object().M()"); + Assert.Equal("void Extensions.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Inaccessible() + { + var src = """ +new object().M(); + +public static class Extensions +{ + extension(object o) + { + void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'object' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("object", "M").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new object().M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetSymbolInfo(invocation).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void Extensions.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void Extensions.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + src = """ +new object().M(); + +public static class Extensions +{ + private static void M(this object o) { } +} +"""; + comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'object' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("object", "M").WithLocation(1, 14)); + + tree = comp.SyntaxTrees[0]; + model = comp.GetSemanticModel(tree); + invocation = GetSyntax(tree, "new object().M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetSymbolInfo(invocation).CandidateSymbols.ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void System.Object.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void System.Object.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Inaccessible_02() + { + var src = """ +new object().M(); + +public static class E1 +{ + extension(object o) + { + void M() { } + } +} +public static class E2 +{ + extension(object o) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new object().M()"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + src = """ +new object().M(); + +public static class E1 +{ + private static void M(this object o) { } +} +public static class E2 +{ + public static void M(this object o) { } +} +"""; + comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + tree = comp.SyntaxTrees[0]; + model = comp.GetSemanticModel(tree); + invocation = GetSyntax(tree, "new object().M()"); + Assert.Equal("void System.Object.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void System.Object.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void System.Object.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_GenericReceiverParameter() + { + var src = """ +new object().M(); + +public static class Extensions +{ + extension(T t) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new object().M()"); + Assert.Equal("void Extensions.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal(["void Extensions.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void StaticMethodInvocation_GenericReceiverParameter_Constrained() + { + var src = """ +object.M(); +int.M(); +new object().M2(); + +public static class Extensions +{ + extension(T) where T : struct + { + public static void M() { } + } + public static void M2(this T t) where T : struct { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Extensions.extension(T)' + // object.M(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("Extensions.extension(T)", "T", "object").WithLocation(1, 8), + // (3,14): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'Extensions.M2(T)' + // new object().M2(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M2").WithArguments("Extensions.M2(T)", "T", "object").WithLocation(3, 14)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "object.M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + + invocation = GetSyntax(tree, "int.M()"); + Assert.Equal("void Extensions.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal(["void Extensions.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_GenericReceiverParameter_Constrained() + { + var src = """ +_ = object.P; +_ = int.P; + +public static class E +{ + extension(T) where T : struct + { + public static int P => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS9286: 'object' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // _ = object.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.P").WithArguments("object", "P").WithLocation(1, 5)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.P"); + Assert.Equal("System.Int32 E.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + + memberAccess = GetSyntax(tree, "int.P"); + Assert.Equal("System.Int32 E.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ReceiverParameter_TypeWithUseSiteError() + { + var lib1_cs = "public class MissingBase { }"; + var comp1 = CreateCompilation(lib1_cs, assemblyName: "missing"); + comp1.VerifyDiagnostics(); + + var lib2_cs = "public class UseSiteError : MissingBase { }"; + var comp2 = CreateCompilation(lib2_cs, [comp1.EmitToImageReference()]); + comp2.VerifyDiagnostics(); + + var src = """ +class C { } +static class Extensions +{ + extension(UseSiteError) { } + extension(C) { } +} + +class C1 +{ + void M(UseSiteError x) { } + void M(C x) { } +} +"""; + var comp = CreateCompilation(src, [comp2.EmitToImageReference()]); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_SuppressConstraintChecksInitially() + { + var text = @" +public class C1 where T : struct { } + +public static class Extensions +{ + extension(C1) { } +} +"; + var comp = CreateCompilation(text); + comp.VerifyEmitDiagnostics( + // (6,18): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'C1' + // extension(C1) { } + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "C1").WithArguments("C1", "T", "T").WithLocation(6, 18)); + } + + [Fact] + public void ReceiverParameter_SuppressConstraintChecksInitially_PointerAsTypeArgument() + { + var text = @" +public class C { } + +unsafe static class Extensions +{ + extension(C) { } +} +"; + var comp = CreateCompilation(text, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (6,15): error CS0306: The type 'int*' may not be used as a type argument + // extension(C) { } + Diagnostic(ErrorCode.ERR_BadTypeArgument, "C").WithArguments("int*").WithLocation(6, 15)); + } + + [Fact] + public void InstanceMethodInvocation_VariousScopes_Errors() + { + var cSrc = """ +class C +{ + public static void Main() + { + new object().Method(); + _ = new object().Property; + } +} +"""; + + var eSrc = """ +static class Extensions +{ + extension(object o) + { + public void Method() => throw null; + public int Property => throw null; + } +} +"""; + + var src1 = $$""" +namespace N +{ + {{cSrc}} + namespace N2 + { + {{eSrc}} + } +} +"""; + + verify(src1, + // (7,22): error CS1061: 'object' does not contain a definition for 'Method' and no accessible extension method 'Method' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().Method(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Method").WithArguments("object", "Method").WithLocation(7, 22), + // (8,26): error CS1061: 'object' does not contain a definition for 'Property' and no accessible extension method 'Property' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // _ = new object().Property; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Property").WithArguments("object", "Property").WithLocation(8, 26)); + + var src2 = $$""" +file {{eSrc}} +"""; + + verify(new[] { cSrc, src2 }, + // 0.cs(5,22): error CS1061: 'object' does not contain a definition for 'Method' and no accessible extension method 'Method' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().Method(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Method").WithArguments("object", "Method").WithLocation(5, 22), + // 0.cs(6,26): error CS1061: 'object' does not contain a definition for 'Property' and no accessible extension method 'Property' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // _ = new object().Property; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Property").WithArguments("object", "Property").WithLocation(6, 26)); + + static void verify(CSharpTestSource src, params DiagnosticDescription[] expected) + { + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(expected); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var method = GetSyntax(tree, "new object().Method"); + Assert.Null(model.GetSymbolInfo(method).Symbol); + Assert.Empty(model.GetMemberGroup(method)); + + var property = GetSyntax(tree, "new object().Property"); + Assert.Null(model.GetSymbolInfo(property).Symbol); + Assert.Empty(model.GetMemberGroup(property)); + } + } + + [Fact] + public void InstanceMethodInvocation_FromUsingNamespace() + { + var cSrc = """ +class C +{ + public static void Main() + { + new object().Method(); + } +} +"""; + + var eSrc = """ +namespace N2 +{ + static class E + { + extension(object o) + { + public void Method() => throw null; + } + } +} +"""; + + var src1 = $$""" +using N2; +{{cSrc}} + +{{eSrc}} +"""; + verify(src1, "N2.E.<>E__0"); + + var src2 = $$""" +using N2; +using N2; // 1, 2 +{{cSrc}} + +{{eSrc}} +"""; + + var comp = CreateCompilation(src2, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (2,1): hidden CS8019: Unnecessary using directive. + // using N2; // 1, 2 + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N2;").WithLocation(2, 1), + // (2,7): warning CS0105: The using directive for 'N2' appeared previously in this namespace + // using N2; // 1, 2 + Diagnostic(ErrorCode.WRN_DuplicateUsing, "N2").WithArguments("N2").WithLocation(2, 7) + ); + + var src3 = $$""" +namespace N3 +{ + using N2; + + namespace N4 + { + {{cSrc}} + } + + {{eSrc}} +} +"""; + verify(src3, "N3.N2.E.<>E__0"); + + void verify(string src, string extensionName) + { + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var invocation = GetSyntax(tree, "new object().Method"); + Assert.Equal($$"""void {{extensionName}}.Method()""", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([$$"""void {{extensionName}}.Method()"""], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + } + } + + [Fact] + public void InstanceMethodInvocation_UsingNamespaceNecessity() + { + var src = """ +using N; + +class C +{ + public static void Main() + { + new object().Method(); + } +} + +"""; + var eSrc = """ +namespace N +{ + public static class E + { + extension(object o) + { + public void Method() { System.Console.Write("method"); } + } + } +} +"""; + + var comp = CreateCompilation([src, eSrc], options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "method"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var invocation = GetSyntax(tree, "new object().Method"); + Assert.Equal("void N.E.<>E__0.Method()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal(["void N.E.<>E__0.Method()"], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + src = """ +using N; + +class C +{ + public static void Main() { } +} + +namespace N +{ + public static class Extensions + { + extension(object o) + { + public void Method() { } + } + } +} +"""; + + comp = CreateCompilation([src, eSrc]); + comp.VerifyEmitDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // using N; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(1, 1)); + } + + [Theory, CombinatorialData] + public void InstanceMethodInvocation_Ambiguity(bool e1BeforeE2) + { + var e1 = """ +static class E1 +{ + extension(object o) + { + public void Method() => throw null; + } +} +"""; + + var e2 = """ +static class E2 +{ + extension(object o) + { + public void Method() => throw null; + } +} +"""; + + var src = $$""" +new object().Method(); + +{{(e1BeforeE2 ? e1 : e2)}} +{{(e1BeforeE2 ? e2 : e1)}} +"""; + var comp = CreateCompilation(src); + if (!e1BeforeE2) + { + comp.VerifyEmitDiagnostics( + // (1,14): error CS0121: The call is ambiguous between the following methods or properties: 'E2.extension(object).Method()' and 'E1.extension(object).Method()' + // new object().Method(); + Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("E2.extension(object).Method()", "E1.extension(object).Method()").WithLocation(1, 14)); + } + else + { + comp.VerifyEmitDiagnostics( + // (1,14): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(object).Method()' and 'E2.extension(object).Method()' + // new object().Method(); + Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("E1.extension(object).Method()", "E2.extension(object).Method()").WithLocation(1, 14)); + } + } + + [Fact] + public void InstanceMethodInvocation_Overloads() + { + var src = """ +new object().Method(42); +new object().Method("hello"); + +static class E1 +{ + extension(object o) + { + public void Method(int i) { System.Console.Write($"E1.Method({i}) "); } + } +} + +static class E2 +{ + extension(object o) + { + public void Method(string s) { System.Console.Write($"E2.Method({s}) "); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "E1.Method(42) E2.Method(hello)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var invocation1 = GetSyntax(tree, "new object().Method(42)"); + Assert.Equal("void E1.<>E__0.Method(System.Int32 i)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation1)); + + var invocation2 = GetSyntax(tree, """new object().Method("hello")"""); + Assert.Equal("void E2.<>E__0.Method(System.String s)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation2)); + + var memberAccess1 = GetSyntaxes(tree, "new object().Method").First(); + Assert.Equal(["void E1.<>E__0.Method(System.Int32 i)", "void E2.<>E__0.Method(System.String s)"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "new object().Method").Last(); + Assert.Equal(["void E1.<>E__0.Method(System.Int32 i)", "void E2.<>E__0.Method(System.String s)"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Overloads_DifferentScopes_NestedNamespace() + { + var src = """ +namespace N1 +{ + static class E1 + { + extension(object o) + { + public void Method(int i) { System.Console.Write($"E1.Method({i}) "); } + } + } + + namespace N2 + { + static class E2 + { + extension(object o) + { + public void Method(string s) { System.Console.Write($"E2.Method({s}) "); } + } + } + + class C + { + public static void Main() + { + new object().Method(42); + new object().Method("hello"); + } + } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "E1.Method(42) E2.Method(hello)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var invocation1 = GetSyntax(tree, "new object().Method(42)"); + Assert.Equal("void N1.E1.<>E__0.Method(System.Int32 i)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation1)); + + var invocation2 = GetSyntax(tree, """new object().Method("hello")"""); + Assert.Equal("void N1.N2.E2.<>E__0.Method(System.String s)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation2)); + + var memberAccess1 = GetSyntaxes(tree, "new object().Method").First(); + Assert.Equal(["void N1.N2.E2.<>E__0.Method(System.String s)", "void N1.E1.<>E__0.Method(System.Int32 i)"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "new object().Method").Last(); + Assert.Equal(["void N1.N2.E2.<>E__0.Method(System.String s)", "void N1.E1.<>E__0.Method(System.Int32 i)"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_NamespaceVsUsing_FromNamespace() + { + var src = """ +using N2; + +new object().Method(42); +new object().Method("hello"); +new object().Method(default); + +static class E1 +{ + extension(object o) + { + public void Method(int i) { System.Console.Write("E1.Method "); } + } +} + +namespace N2 +{ + static class E2 + { + extension(object o) + { + public void Method(string s) { System.Console.Write("E2.Method "); } + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "E1.Method E2.Method E1.Method").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var invocation1 = GetSyntax(tree, "new object().Method(42)"); + Assert.Equal("void E1.<>E__0.Method(System.Int32 i)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation1)); + Assert.Equal(["void E1.<>E__0.Method(System.Int32 i)", "void N2.E2.<>E__0.Method(System.String s)"], model.GetMemberGroup(invocation1.Expression).ToTestDisplayStrings()); + + var invocation2 = GetSyntax(tree, """new object().Method("hello")"""); + Assert.Equal("void N2.E2.<>E__0.Method(System.String s)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation2)); + Assert.Equal(["void E1.<>E__0.Method(System.Int32 i)", "void N2.E2.<>E__0.Method(System.String s)"], model.GetMemberGroup(invocation2.Expression).ToTestDisplayStrings()); + + var invocation3 = GetSyntax(tree, "new object().Method(default)"); + Assert.Equal("void E1.<>E__0.Method(System.Int32 i)", model.GetSymbolInfo(invocation3).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation3)); + Assert.Equal(["void E1.<>E__0.Method(System.Int32 i)", "void N2.E2.<>E__0.Method(System.String s)"], model.GetMemberGroup(invocation3.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_DerivedDerivedType() + { + var src = """ +new Derived().M(); + +class Base { } +class Derived : Base { } + +static class E +{ + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new Derived().M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation)); + + var memberAccess = GetSyntax(tree, "new Derived().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_ImplementedInterface() + { + var src = """ +new C().M(); + +interface I { } +class C : I { } + +static class E +{ + extension(I i) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation)); + + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_IndirectlyImplementedInterface() + { + var src = """ +new C().M(); + +interface I { } +interface Indirect : I { } +class C : Indirect { } + +static class E +{ + extension(I i) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation)); + + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_TypeParameterImplementedInterface() + { + var src = """ +class C +{ + void M(T t) where T : I + { + t.M(); + } +} + +interface I { } + +static class E +{ + extension(I i) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "t.M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "t.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void StaticMethodInvocation_MatchingExtendedType_TypeParameterImplementedInterface() + { + var src = """ +class C +{ + void M() where T : I + { + T.M(); + } +} + +interface I { } + +static class E +{ + extension(I) + { + public static void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,9): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter + // T.M(); + Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(5, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "T.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_TypeParameterWithBaseClass() + { + var src = $$""" +class C { } + +class D +{ + void M(T t) where T : C + { + t.M2(); + } +} + +static class E +{ + extension(C c) + { + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "t.M2()"); + Assert.Equal("void E.<>E__0.M2()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "t.M2"); + Assert.Equal(["void E.<>E__0.M2()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_ConstrainedTypeParameter() + { + var src = $$""" +D.M(""); + +class D +{ + public static void M(T t) where T : class + { + t.M2(); + } +} + +static class E1 +{ + extension(T t) where T : struct + { + public void M2() { } + } +} + +static class E2 +{ + extension(T t) where T : class + { + public void M2() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "t.M2()"); + Assert.Equal("void E2.<>E__0.M2()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "t.M2"); + Assert.Equal(["void E2.<>E__0.M2()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_BaseType() + { + var src = """ +new object().M(); +new object().M2(); + +static class E +{ + extension(string s) + { + public void M() => throw null; + } + public static void M2(this string s) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(string).M()' requires a receiver of type 'string' + // new object().M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object()").WithArguments("object", "M", "E.extension(string).M()", "string").WithLocation(1, 1), + // (2,1): error CS1929: 'object' does not contain a definition for 'M2' and the best extension method overload 'E.M2(string)' requires a receiver of type 'string' + // new object().M2(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object()").WithArguments("object", "M2", "E.M2(string)", "string").WithLocation(2, 1) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType() + { + var src = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType_GenericMember_01() + { + var src = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType_GenericMember_02() + { + var src = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType_GenericMember_OmittedTypeArgument_01() + { + var src = """ +new C().M<,>(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8389: Omitting the type argument is not allowed in the current context + // new C().M<,>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "new C().M<,>").WithLocation(1, 1), + // (1,14): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M<,>(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M<,>").WithArguments("C", "M").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M<,>()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType_GenericMember_OmittedTypeArgument_02() + { + var src = """ +new C().M<,,>(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8389: Omitting the type argument is not allowed in the current context + // new C().M<,,>(); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "new C().M<,,>").WithLocation(1, 1), + // (1,1): error CS1929: 'C' does not contain a definition for 'M' and the best extension method overload 'E.extension(C).M()' requires a receiver of type 'C' + // new C().M<,,>(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new C()").WithArguments("C", "M", "E.extension(C).M()", "C").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M<,,>()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + Assert.Equal([], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_GenericType_GenericMember_BrokenConstraint() + { + var src = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() where U : struct => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'U' in the generic type or method 'E.extension(C).M()' + // new C().M(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("E.extension(C).M()", "U", "string").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new C().M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_BrokenConstraint() + { + var source = """ +new object().Method(); +new object().Method2(); + +static class E +{ + extension(T t) where T : struct + { + public void Method() { } + } + public static void Method2(this T t) where T : struct { } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.extension(T)' + // new object().Method(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Method").WithArguments("E.extension(T)", "T", "object").WithLocation(1, 14), + // (2,14): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.Method2(T)' + // new object().Method2(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "Method2").WithArguments("E.Method2(T)", "T", "object").WithLocation(2, 14) + ); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_BrokenConstraint_Nullability() + { + var source = """ +#nullable enable +bool b = true; +var o = b ? null : new object(); +o.Method(); + +static class E +{ + extension(T t) where T : notnull + { + public void Method() { System.Console.Write(t is null); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,1): warning CS8602: Dereference of a possibly null reference. + // o.Method(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(4, 1)); + CompileAndVerify(comp, expectedOutput: "True"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "o.Method"); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Nullability is undone + Assert.Equal("void E.extension(System.Object!).Method()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString(includeNonNullable: true)); + } + + [Fact] + public void ReceiverParameter_AliasType() + { + var source = """ +using Alias = C; + +new Alias().M(); + +class C { } + +static class E +{ + extension(Alias a) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new Alias().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_DynamicArgument() + { + // No extension members in dynamic invocation + var src = """ +dynamic d = null; +new object().M(d); +new object().M2(d); + +static class E +{ + extension(object o) + { + public void M(object o1) => throw null; + } + public static void M2(this object o, object o2) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS1973: 'object' has no applicable method named 'M' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // new object().M(d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new object().M(d)").WithArguments("object", "M").WithLocation(2, 1), + // (3,1): error CS1973: 'object' has no applicable method named 'M2' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // new object().M2(d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new object().M2(d)").WithArguments("object", "M2").WithLocation(3, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal(["void E.<>E__0.M(System.Object o1)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_DynamicDifference_Nested() + { + var src = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() + { + System.Console.Write("M"); + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "M").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_DynamicDifference_InBase() + { + var src = """ +new D().M(); + +class C { } +class D : C { } + +static class E +{ + extension(C c) + { + public void M() + { + System.Console.Write("M"); + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "M").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new D().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_DynamicDifference_InInterface() + { + var src = """ +new D().M(); + +interface I { } +class D : I { } + +static class E +{ + extension(I i) + { + public void M() { System.Console.Write("M"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (4,11): error CS1966: 'D': cannot implement a dynamic interface 'I' + // class D : I { } + Diagnostic(ErrorCode.ERR_DeriveFromConstructedDynamic, "I").WithArguments("D", "I").WithLocation(4, 11)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new D().M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_TupleNamesDifference() + { + var src = """ +new C<(int a, int b)>().M(); +new C<(int, int)>().M(); +new C<(int other, int)>().M(); + +class C { } + +static class E +{ + extension(C<(int a, int b)> c) + { + public void M() { System.Console.Write("M"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "MMM").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C<(int a, int b)>().M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "new C<(int, int)>().M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "new C<(int other, int)>().M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + src = """ +new C<(int a, int b)>().M(); +new C<(int, int)>().M(); +new C<(int other, int)>().M(); + +class C { } + +static class E +{ + public static void M(this C<(int a, int b)> c) { System.Console.Write("M"); } +} +"""; + comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_TupleNamesDifference_InBase() + { + var src = """ +new D1().M(); +new D2().M(); +new D3().M(); + +class C { } +class D1 : C<(int a, int b)> { } +class D2 : C<(int, int)> { } +class D3 : C<(int other, int)> { } + +static class E +{ + extension(C<(int a, int b)> c) + { + public void M() { System.Console.Write("M"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "MMM").VerifyDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_MatchingExtendedType_TupleNamesDifference_InInterface() + { + var src = """ +new D1().M(); +new D2().M(); +new D3().M(); + +class I { } +class D1 : I<(int a, int b)> { } +class D2 : I<(int, int)> { } +class D3 : I<(int other, int)> { } + +static class E +{ + extension(I<(int a, int b)> i) + { + public void M() { System.Console.Write("M"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "MMM").VerifyDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_Nameof() + { + var src = """ +object o = null; +System.Console.Write($"{nameof(o.M)} "); + +static class E +{ + extension(object) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,32): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write($"{nameof(o.M)} "); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "o.M").WithLocation(2, 32)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "o.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Nameof_ViaType() + { + var src = """ +System.Console.Write($"{nameof(E.M)} "); + +static class E +{ + extension(object) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "M").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "E.M"); + Assert.Equal(["void E.M(this System.Object)", "void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Nameof_Overloads() + { + var src = """ +object o = null; +System.Console.Write($"{nameof(o.M)} "); + +static class E +{ + extension(object o) + { + public void M() { } + public void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,32): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write($"{nameof(o.M)} "); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "o.M").WithLocation(2, 32)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "o.M"); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Nameof_SimpleName() + { + var src = """ +class C +{ + void M() + { + _ = nameof(Method); + } +} + +static class E +{ + extension(object o) + { + public void Method() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,20): error CS0103: The name 'Method' does not exist in the current context + // _ = nameof(Method); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Method").WithArguments("Method").WithLocation(5, 20)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var identifier = GetSyntax(tree, "Method"); + Assert.Equal([], model.GetMemberGroup(identifier).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_Null_Method() + { + var src = """ +#nullable enable + +object? o = null; +o.Method(); + +static class E +{ + extension(object o) + { + public void Method() { System.Console.Write("Method"); } + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : nullability is undone + comp.VerifyEmitDiagnostics( + // (4,1): warning CS8602: Dereference of a possibly null reference. + // o.Method(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(4, 1)); + + CompileAndVerify(comp, expectedOutput: "Method"); + } + + [Fact] + public void InstanceMethodInvocation_ColorColor_Method() + { + var src = """ +C.M(new C()); + +class C +{ + public static void M(C C) + { + C.Method(); + } +} + +static class E +{ + extension(C c) + { + public void Method() { System.Console.Write("Method "); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "Method").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Method"); + Assert.Equal("void E.<>E__0.Method()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.Method()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_ColorColor_Static_Method() + { + var src = """ +C.M(null); + +class C +{ + public static void M(C C) + { + C.Method(); + } +} + +static class E +{ + extension(C c) + { + public void Method() { System.Console.Write("Method"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "Method").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Method"); + Assert.Equal("void E.<>E__0.Method()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.Method()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_PatternBased_ForEach_NoMethod() + { + var src = """ +foreach (var x in new C()) +{ + System.Console.Write(x); + break; +} + +class C { } +class D { } + +static class E +{ + extension(C c) + { + public D GetEnumerator() => new D(); + } + + extension(D d) + { + public bool MoveNext() => true; + public int Current => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,19): error CS0117: 'D' does not contain a definition for 'Current' + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_NoSuchMember, "new C()").WithArguments("D", "Current").WithLocation(1, 19), + // (1,19): error CS0202: foreach requires that the return type 'D' of 'E.extension(C).GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new C()").WithArguments("D", "E.extension(C).GetEnumerator()").WithLocation(1, 19) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var loop = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetForEachStatementInfo(loop).GetEnumeratorMethod); + Assert.Null(model.GetForEachStatementInfo(loop).MoveNextMethod); + Assert.Null(model.GetForEachStatementInfo(loop).CurrentProperty); + } + + [Fact] + public void InstanceMethodInvocation_NameOf_SingleParameter() + { + var src = """ +class C +{ + public static void Main() + { + string x = ""; + System.Console.Write(nameof(x)); + } +} + + +static class E +{ + extension(C c) + { + public string nameof(string s) => throw null; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "x").VerifyDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_Simple_ExpressionTree() + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : decide whether to allow expression tree scenarios. Verify shape of the tree if we decide to allow + var source = """ +using System.Linq.Expressions; +Expression x = () => new C().M(42); + +class C +{ + public void M() => throw null; +} + +static class E +{ + extension(C c) + { + public void M(int i) { System.Console.Write("E.M"); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_NextScope() + { + // If overload resolution on extension type methods yields no applicable candidates, + // we look in the next scope. + var source = """ +using N; + +new C().M(42); + +class C +{ + public void M() => throw null; +} + +static class E1 +{ + extension(C c) + { + public void M(string s) => throw null; + } +} + +namespace N +{ + static class E2 + { + extension(C c) + { + public void M(int i) { System.Console.Write($"E2.M({i})"); } + } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E2.M(42)"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void N.E2.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()", "void E1.<>E__0.M(System.String s)", "void N.E2.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_NewExtensionPriority() + { + var source = """ +new C().M(42); + +class C +{ + public void M() => throw null; +} + +static class E1 +{ + extension(C c) + { + public void M(int i) => throw null; + } +} + +static class E2 +{ + public static void M(this C c, int i) => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,9): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(C).M(int)' and 'E2.M(C, int)' + // new C().M(42); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E1.extension(C).M(int)", "E2.M(C, int)").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void C.M()", "void E1.<>E__0.M(System.Int32 i)", "void C.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_NewExtensionPriority_02() + { + var source = """ +new C().M(42); + +class C +{ + public void M() => throw null; +} + +static class E +{ + extension(C c) + { + public void M(int i) => throw null; + } + public static void M(this C c, int i) => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,9): error CS0121: The call is ambiguous between the following methods or properties: 'E.extension(C).M(int)' and 'E.M(C, int)' + // new C().M(42); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E.extension(C).M(int)", "E.M(C, int)").WithLocation(1, 9), + // (12,21): error CS0111: Type 'E' already defines a member called 'M' with the same parameter types + // public void M(int i) => throw null; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M").WithArguments("M", "E").WithLocation(12, 21) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void C.M()", "void E.<>E__0.M(System.Int32 i)", "void C.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_FallbackToExtensionMethod() + { + // The extension method is picked up if extension declaration candidates were not applicable + var source = """ +new C().M(42); + +class C +{ + public static void M() => throw null; +} + +static class E1 +{ + extension(C c) + { + public void M(string s) => throw null; + public void M(char c1) => throw null; + } +} + +static class E2 +{ + public static void M(this C c, int i) { System.Console.Write($"E2.M({i})"); } +} +"""; + var comp = CreateCompilation(source); + + CompileAndVerify(comp, expectedOutput: "E2.M(42)"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void C.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()", "void E1.<>E__0.M(System.String s)", "void E1.<>E__0.M(System.Char c1)", "void C.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_SimpleName() + { + // Extension invocation comes into play on an invocation on a member access but not an invocation on a simple name + var source = """ +class C +{ + public void M() => throw null; + + void M2() + { + M(42); // 1 + } +} + +static class E +{ + extension(C c) + { + public void M(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // 0.cs(7,9): error CS1501: No overload for method 'M' takes 1 arguments + // M(42); // 1 + Diagnostic(ErrorCode.ERR_BadArgCount, "M").WithArguments("M", "1").WithLocation(7, 9) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "M(42)"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Empty(model.GetMemberGroup(invocation)); + Assert.Equal(["void C.M()"], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_ArgumentName() + { + // Instance method with incompatible parameter name is skipped in favor of extension declaration method + var source = """ +new C().M(b: 42); + +class C +{ + public void M(int a) => throw null; +} + +static class E1 +{ + extension(C c) + { + public void M(int b) { System.Console.Write($"E1.M({b})"); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "E1.M(42)"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E1.<>E__0.M(System.Int32 b)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M(System.Int32 a)", "void E1.<>E__0.M(System.Int32 b)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_ArgumentName_02() + { + // Extension declaration method with incompatible parameter name is skipped in favor of extension method + var source = """ +new C().M(c: 42); + +public class C +{ + public static void M(int a) => throw null; +} + +static class E1 +{ + extension(C c) + { + public void M(int b) => throw null; + } +} + +public static class E2 +{ + public static void M(this C self, int c) + { + System.Console.Write($"E2.M({c})"); + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E2.M(42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void C.M(System.Int32 c)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + Assert.Equal(["void C.M(System.Int32 a)", "void E1.<>E__0.M(System.Int32 b)", "void C.M(System.Int32 c)"], + model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_ArgumentName_03() + { + var source = """ +new object().M(c: 43, b: 42); + +static class E +{ + extension(object o) + { + public void M(int b, int c) { System.Console.Write($"E.M({b}, {c})"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E.M(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E.<>E__0.M(System.Int32 b, System.Int32 c)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_ArgumentName_04() + { + var source = """ +new object().M(o: new object()); + +static class E +{ + extension(object o) + { + public void M(object o2) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,16): error CS1744: Named argument 'o' specifies a parameter for which a positional argument has already been given + // new object().M(o: new object()); + Diagnostic(ErrorCode.ERR_NamedArgumentUsedInPositional, "o").WithArguments("o").WithLocation(1, 16)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void InstanceMethodInvocation_RefKind() + { + var source = """ +int i = 42; +int j; + +new object().M(ref i, out j); + +static class E +{ + extension(object o) + { + public void M(ref int b, out int c) { c = 43; System.Console.Write($"E.M({b}, {c})"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E.M(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E.<>E__0.M(ref System.Int32 b, out System.Int32 c)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_AmbiguityWithExtensionOnBaseType_PreferMoreSpecific() + { + var source = """ +System.Console.Write(new C().M(42)); + +class Base { } + +class C : Base { } + +static class E1 +{ + extension(Base b) + { + public int M(int i) => throw null; + } +} + +static class E2 +{ + extension(C c) + { + public int M(int i) => i; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("System.Int32 E2.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["System.Int32 E1.<>E__0.M(System.Int32 i)", "System.Int32 E2.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + source = """ +System.Console.Write(new C().M(42)); + +public class Base { } + +public class C : Base { } + +public static class E1 +{ + public static int M(this Base b, int i) => throw null; +} + +public static class E2 +{ + public static int M(this C c, int i) => i; +} +"""; + comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void InstanceMethodInvocation_TypeArguments() + { + var source = """ +new C().M(42); + +class C { } + +static class E +{ + extension(C c) + { + public void M(int i) => throw null; + public void M(int i) + { + System.Console.Write("ran"); + } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_TypeArguments_WrongNumber() + { + var source = """ +new C().M(42); + +class C { } + +static class E +{ + extension(C c) + { + public void M(int i) => throw null; + public void M(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // 0.cs(1,9): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M(42); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 9) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)", "void E.<>E__0.M(System.Int32 i)"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_TypeArguments_Omitted() + { + var source = """ +new C().M<>(42); + +class C { } + +static class E +{ + extension(C c) + { + public void M(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8389: Omitting the type argument is not allowed in the current context + // new C().M<>(42); + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "new C().M<>").WithLocation(1, 1) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M<>"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_TypeArguments_Inferred() + { + // No type arguments passed, but the extension declaration method is found and the type parameter inferred + var source = """ +new C().M(42); + +class C { } + +static class E +{ + extension(C c) + { + public void M(T t) + { + System.Console.Write($"M({t})"); + } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "M(42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M(System.Int32 t)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(T t)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void StaticMethodInvocation_InstanceExtensionMethod() + { + // The extension method is not static, but the receiver is a type + var source = """ +C.M(); + +class C { } + +static class E +{ + extension(C c) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0120: An object reference is required for the non-static field, method, or property 'E.extension(C).M()' + // C.M(); + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.M").WithArguments("E.extension(C).M()").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + source = """ +C.Method(); + +public class C { } + +public static class E +{ + public static void Method(this C c) { } +} +"""; + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // 0.cs(1,1): error CS0120: An object reference is required for the non-static field, method, or property 'E.Method(C)' + // C.Method(); + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.Method").WithArguments("E.Method(C)").WithLocation(1, 1)); + } + + [Fact] + public void InstanceMethodInvocation_StaticExtensionMethod() + { + // The extension method is static but the receiver is a value + var source = """ +new C().M(); + +class C { } + +static class E +{ + extension(C c) + { + public static void M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0176: Member 'E.extension(C).M()' cannot be accessed with an instance reference; qualify it with a type name instead + // new C().M(); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "new C().M").WithArguments("E.extension(C).M()").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_GenericType() + { + var src = """ +new C().StaticType(); + +class C { } + +static class E +{ + extension(C c) + { + public static class StaticType { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'C' does not contain a definition for 'StaticType' and no accessible extension method 'StaticType' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().StaticType(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "StaticType").WithArguments("C", "StaticType").WithLocation(1, 14), + // (9,29): error CS9282: Extension declarations can include only methods or properties + // public static class StaticType { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "StaticType").WithLocation(9, 29)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().StaticType"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess).CandidateReason); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_RefOmittedComCall() + { + // For COM import type, omitting the ref is allowed + string source = @" +using System; +using System.Runtime.InteropServices; + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +class C { } + +static class E +{ + extension(C c) + { + public void M(ref short p) { } + public void M(sbyte p) { } + public void I(ref int p) { } + } +} + +class X +{ + public static void Goo() + { + short x = 123; + C c = new C(); + c.M(x); + c.I(123); + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + } + + [Fact] + public void ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_ExtensionDeclarationMethods() + { + // See ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_Method + var source = """ +struct S1(Color Color) +{ + public void Test() + { + Color.M1(this); + } +} + +class Color { } + +static class E +{ + extension(Color c) + { + public void M1(S1 x, int y = 0) { System.Console.WriteLine("instance"); } + + public static void M1(T x) where T : unmanaged { System.Console.WriteLine("static"); } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics( + //// (5,9): error CS9106: Identifier 'Color' is ambiguous between type 'Color' and parameter 'Color Color' in this context. + //// Color.M1(this); + //Diagnostic(ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver, "Color").WithArguments("Color", "Color", "Color Color").WithLocation(5, 9) + ); + + Assert.NotEmpty(comp.GetTypeByMetadataName("S1").InstanceConstructors.OfType().Single().GetCapturedParameters()); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M1"); + Assert.Equal("void E.<>E__0.M1(S1 x, [System.Int32 y = 0])", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_ExtensionDeclarationMembersVsExtensionMethod() + { + var source = """ +public struct S1(Color Color) +{ + public void Test() + { + Color.M1(this); + } +} + +public class Color { } + +public static class E1 +{ + public static void M1(this Color c, S1 x, int y = 0) { System.Console.WriteLine("instance"); } +} + +static class E2 +{ + extension(Color c) + { + public static void M1(T x) where T : unmanaged { System.Console.WriteLine("static"); } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics( + //// (5,9): error CS9106: Identifier 'Color' is ambiguous between type 'Color' and parameter 'Color Color' in this context. + //// Color.M1(this); + //Diagnostic(ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver, "Color").WithArguments("Color", "Color", "Color Color").WithLocation(5, 9) + ); + + Assert.NotEmpty(comp.GetTypeByMetadataName("S1").InstanceConstructors.OfType().Single().GetCapturedParameters()); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M1"); + Assert.Equal("void Color.M1(S1 x, [System.Int32 y = 0])", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_NotOnBase() + { + // Unlike `this`, `base` is not an expression in itself. + // "Extension invocation" and "extension member lookup" do not apply to `base_access` syntax. + var src = """ +class Base { } + +class Derived : Base +{ + void Main() + { + M(); // 1 + base.M(); // 2 + } +} + +static class E +{ + extension(Base b) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,9): error CS0103: The name 'M' does not exist in the current context + // M(); // 1 + Diagnostic(ErrorCode.ERR_NameNotInContext, "M").WithArguments("M").WithLocation(7, 9), + // (8,14): error CS0117: 'Base' does not contain a definition for 'M' + // base.M(); // 2 + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("Base", "M").WithLocation(8, 14)); + } + + [Fact] + public void LookupKind_Invocation() + { + // Non-invocable extension member in inner scope is skipped in favor of invocable one from outer scope + var src = """ +using N; + +new object().Member(); + +static class E +{ + extension(object o) + { + public int Member => 0; + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public void Member() { System.Console.Write("ran "); } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().Member"); + Assert.Equal("void N.E2.<>E__0.Member()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_NotUnique() + { + var src = """ +new C().M(); +new C().M(); + +new C().M2(); +new C().M2(); + +class C { } + +static class E +{ + extension(C c) + { + public string M() => "hi"; + } + public static string M2(this C c) => "hi"; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,26): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 26), + // (2,26): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(2, 26), + // (4,26): error CS1061: 'C' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("C", "M2").WithLocation(4, 26), + // (5,26): error CS1061: 'C' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("C", "M2").WithLocation(5, 26)); + } + + [Fact] + public void InstanceMethodInvocation_Generic_NestedTuples() + { + var src = """ +var s = new C<(string, string)>.Nested<(int, int)>().M(); +System.Console.Write(s); + +class C +{ + internal class Nested { } +} + +static class E +{ + extension(C<(T1, T1)>.Nested<(T2, T2)> cn) + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "new C<(string, string)>.Nested<(int, int)>().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_PointerArray() + { + var src = """ +unsafe +{ + string s = new C.Nested().M(); + System.Console.Write(s); +} + +unsafe class C +{ + internal class Nested { } +} + +unsafe static class E +{ + extension(C.Nested cn) + where T1 : unmanaged + where T2 : unmanaged + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C.Nested().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_Pointer() + { + var src = """ +unsafe +{ + new C.Nested().M(); + new C.Nested().M2(); +} + +unsafe class C +{ + internal class Nested { } +} + +static class E +{ + extension(C.Nested cn) + { + public string M() => null; + } + public static string M2(this C.Nested cn) => null; +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe); + comp.VerifyEmitDiagnostics( + // (3,37): error CS0306: The type 'long*' may not be used as a type argument + // new C.Nested().M(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("long*").WithLocation(3, 37), + // (3,37): error CS0306: The type 'int*' may not be used as a type argument + // new C.Nested().M(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M").WithArguments("int*").WithLocation(3, 37), + // (4,37): error CS0306: The type 'long*' may not be used as a type argument + // new C.Nested().M2(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("long*").WithLocation(4, 37), + // (4,37): error CS0306: The type 'int*' may not be used as a type argument + // new C.Nested().M2(); + Diagnostic(ErrorCode.ERR_BadTypeArgument, "M2").WithArguments("int*").WithLocation(4, 37) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C.Nested().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void InstanceMethodInvocation_Generic_FunctionPointer() + { + var src = """ +unsafe +{ + string s = new C[]>.Nested[]>().M(); + System.Console.Write(s); +} + +unsafe class C +{ + internal class Nested { } +} + +unsafe static class E +{ + extension(C[]>.Nested[]> cn) + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C[]>.Nested[]>().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_ForInterface() + { + var src = """ +string s = new C().M(); +System.Console.Write(s); + +class C : I { } +interface I { } + +static class E +{ + extension(I i) + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_ForBaseInterface() + { + var src = """ +string s = new C().M(); +System.Console.Write(s); + +class C : I { } +interface I : I2 { } +interface I2 { } + +static class E +{ + extension(I2 i) + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Generic_ForBase() + { + var src = """ +string s = new C().M(); +System.Console.Write(s); + +class Base { } +class C : Base { } + +static class E +{ + extension(Base b) + { + public string M() => "hi"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethodInvocation_Obsolete() + { + var src = """ +new object().Method(); + +static class E +{ + extension(object o) + { + [System.Obsolete("Method is obsolete", true)] + public void Method() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0619: 'E.extension(object).Method()' is obsolete: 'Method is obsolete' + // new object().Method(); + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new object().Method()").WithArguments("E.extension(object).Method()", "Method is obsolete").WithLocation(1, 1)); + } + + [Fact] + public void InstanceMethodInvocation_BrokenConstraintMethodOuterExtension() + { + var src = """ +static class E2 +{ + extension(object o) + { + public void M() => throw null; + } +} + +namespace Inner +{ + class C + { + public static void Main() + { + new C().M(); + } + } + + static class E1 + { + extension(C c) + { + public string M() where T : struct => throw null; + } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethodInvocation_MultipleSubstitutions() + { + var src = """ +new C().M(); + +interface I { } +class C : I, I { } + +static class E +{ + extension(I i) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Theory, CombinatorialData] + public void InstanceMethodInvocation_MultipleExtensions(bool e1BeforeE2) + { + var e1 = """ +static class E1 +{ + extension(object o) + { + public string M() => throw null; + } +} +"""; + + var e2 = """ +static class E2 +{ + extension(object o) + { + public string M() => throw null; + } +} +"""; + + var src = $$""" +new object().M(); +{{(e1BeforeE2 ? e1 : e2)}} +{{(e1BeforeE2 ? e2 : e1)}} +"""; + var comp = CreateCompilation(src); + if (!e1BeforeE2) + { + comp.VerifyEmitDiagnostics( + // (1,14): error CS0121: The call is ambiguous between the following methods or properties: 'E2.extension(object).M()' and 'E1.extension(object).M()' + // new object().M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E2.extension(object).M()", "E1.extension(object).M()").WithLocation(1, 14)); + } + else + { + comp.VerifyEmitDiagnostics( + // (1,14): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(object).M()' and 'E2.extension(object).M()' + // new object().M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E1.extension(object).M()", "E2.extension(object).M()").WithLocation(1, 14)); + } + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + if (e1BeforeE2) + { + Assert.Equal(["System.String E1.<>E__0.M()", "System.String E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + else + { + Assert.Equal(["System.String E2.<>E__0.M()", "System.String E1.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + } + + public class ThreePermutationGenerator : IEnumerable + { + private readonly List _data = [ + [0, 1, 2], + [0, 2, 1], + [1, 0, 2], + [1, 2, 0], + [2, 0, 1], + [2, 1, 0]]; + + public IEnumerator GetEnumerator() => _data.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } + + [Theory, ClassData(typeof(ThreePermutationGenerator))] + public void InstanceMethodInvocation_InterfaceAppearsTwice(int first, int second, int third) + { + string[] segments = [ + """ + static class E1 + { + extension(I1 i) + { + public string M() => null; + } + } + """, + """ + static class E2 + { + extension(I2 i) { } + } + """, + """ + static class E3 + { + extension(C c) { } + } + """]; + + var src = $$""" +System.Console.Write(new C().M()); + +interface I1 { } +interface I2 : I1 { } + +class C : I1, I2 { } + +{{segments[first]}} + +{{segments[second]}} + +{{segments[third]}} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,30): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(new C().M()); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 30)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void InstanceMethodInvocation_SingleStageInference() + { + var src = """ +public class C +{ + public void M(I i, out object o) + { + i.M(out o); + i.M2(out o); + } +} + +public static class E +{ + public static void M(this I i, out T t) { t = default; } +} + +static class E2 +{ + extension(I i) + { + public void M2(out T t) { t = default; } + } +} + +public interface I { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "i.M"); + Assert.Equal("void I.M(out System.Object t)", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void I.M(out System.String t)"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "i.M2"); + Assert.Equal("void E2.<>E__0.M2(out System.Object t)", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E2.<>E__0.M2(out System.String t)"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void GetCompatibleExtension_Conversion_01() + { + var src = """ +using System.Collections.Generic; + +IEnumerable i = null; +i.M(); +_ = i.P; + +static class E +{ + extension(IEnumerable o) + { + public void M() { System.Console.Write(o is null); } + public int P { get { System.Console.Write(o is null); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "TrueTrue").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "i.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "i.P"); + Assert.Equal("System.Int32 E.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + src = """ +using System.Collections.Generic; + +IEnumerable i = null; +i.M(); +_ = i.P; + +static class E +{ + extension(IEnumerable o) + { + public static void M() { } + public static int P => throw null; + } +} +"""; + comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (4,1): error CS1929: 'IEnumerable' does not contain a definition for 'M' and the best extension method overload 'E.extension(IEnumerable).M()' requires a receiver of type 'System.Collections.Generic.IEnumerable' + // i.M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "i").WithArguments("System.Collections.Generic.IEnumerable", "M", "E.extension(System.Collections.Generic.IEnumerable).M()", "System.Collections.Generic.IEnumerable").WithLocation(4, 1), + // (5,5): error CS9286: 'IEnumerable' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?) + // _ = i.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "i.P").WithArguments("System.Collections.Generic.IEnumerable", "P").WithLocation(5, 5) + ); + } + + [Fact] + public void GetCompatibleExtension_Conversion_02() + { + var src = """ +string.M(); +_ = string.P; + +static class E +{ + extension(object) + { + public static void M() { System.Console.Write("ran "); } + public static int P { get { System.Console.Write("ran2"); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran ran2").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "string.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetCompatibleExtension_Conversion_03() + { + var src = """ +int.M(); +_ = int.P; + +static class E +{ + extension(object) + { + public static void M() { System.Console.Write("ran "); } + public static int P { get { System.Console.Write("ran2"); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran ran2").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetCompatibleExtension_Conversion_04() + { + var src = """ +int.M(); +42.M2(); + +_ = int.P; +_ = 42.P2; + +static class E +{ + extension(int? i) + { + public static void M() { } + public static int P => 0; + public int P2 => 0; + } + public static void M2(this int? i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1929: 'int' does not contain a definition for 'M' and the best extension method overload 'E.extension(int?).M()' requires a receiver of type 'int?' + // int.M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "int").WithArguments("int", "M", "E.extension(int?).M()", "int?").WithLocation(1, 1), + // (2,1): error CS1929: 'int' does not contain a definition for 'M2' and the best extension method overload 'E.M2(int?)' requires a receiver of type 'int?' + // 42.M2(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "42").WithArguments("int", "M2", "E.M2(int?)", "int?").WithLocation(2, 1), + // (4,5): error CS9286: 'int' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'int' could be found (are you missing a using directive or an assembly reference?) + // _ = int.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "int.P").WithArguments("int", "P").WithLocation(4, 5), + // (5,5): error CS9286: 'int' does not contain a definition for 'P2' and no accessible extension member 'P2' for receiver of type 'int' could be found (are you missing a using directive or an assembly reference?) + // _ = 42.P2; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "42.P2").WithArguments("int", "P2").WithLocation(5, 5)); + } + + [Fact] + public void GetCompatibleExtension_Conversion_05() + { + var src = """ +MyEnum.Zero.M(); + +enum MyEnum { Zero } + +static class E +{ + extension(System.Enum e) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void GetCompatibleExtension_Conversion_06() + { + var src = """ +dynamic d = new C(); +d.M(); +d.M2(); + +static class E +{ + extension(object o) + { + public void M() => throw null; + } + + public static void M2(this object o) => throw null; +} + +class C +{ + public void M() { System.Console.Write("ran "); } + public void M2() { System.Console.Write("ran2"); } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran ran2"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + public void GetCompatibleExtension_Conversion_07() + { + var src = """ +object o = null; +o.M(); +o.M2(); + +static class E +{ + extension(dynamic d) + { + public void M() { } + } + + public static void M2(this dynamic d) { } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : validate extension parameter + comp.VerifyEmitDiagnostics( + // (12,32): error CS1103: The first parameter of an extension method cannot be of type 'dynamic' + // public static void M2(this dynamic d) { } + Diagnostic(ErrorCode.ERR_BadTypeforThis, "dynamic").WithArguments("dynamic").WithLocation(12, 32)); + } + + [Fact] + public void GetCompatibleExtension_Conversion_08() + { + var src = """ +(int a, int b) t = default; +t.M(); +t.M2(); + +static class E +{ + extension((int c, int d) t) + { + public void M() { } + } + + public static void M2(this (int c, int d) t) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void GetCompatibleExtension_Conversion_09() + { + var src = """ +int[] i = default; +i.M(); +i.M2(); + +static class E +{ + extension(System.ReadOnlySpan ros) + { + public void M() { } + } + + public static void M2(this System.ReadOnlySpan ros) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void GetCompatibleExtension_Conversion_10() + { + var missingSrc = """ +public class Missing { } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var derivedSrc = """ +public class Derived : Missing { } +"""; + var derivedRef = CreateCompilation(derivedSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +new Derived().M(); +new Derived().M2(); + +class Other { } + +static class E +{ + extension(Other o) + { + public void M() { } + } + + public static void M2(this Other o) { } +} +"""; + var comp = CreateCompilation(src, references: [derivedRef]); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "new Derived().M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 1), + // (1,15): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 15), + // (2,1): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M2(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "new Derived().M2").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 1), + // (2,15): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M2(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M2").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 15)); + } + + [Fact] + public void GetCompatibleExtension_Conversion_11() + { + var missingSrc = """ +public class Missing { } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var derivedSrc = """ +public class Derived : Missing { } +"""; + var derivedRef = CreateCompilation(derivedSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +new Derived().M(); +new Derived().M2(); + +static class E +{ + extension(Derived d) + { + public void M() { } + } + + public static void M2(this Derived d) { } +} +"""; + var comp = CreateCompilation(src, references: [derivedRef]); + comp.VerifyEmitDiagnostics( + // (1,15): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 15), + // (2,15): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Derived().M2(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M2").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 15)); + } + + [Fact] + public void GetCompatibleExtension_Conversion_12() + { + var missingSrc = """ +public class Missing { } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var derivedSrc = """ +public class I { } +public class Derived : I { } +"""; + var derivedRef = CreateCompilation(derivedSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +new Derived().M(); +new Derived().M2(); + +static class E +{ + extension(I i) + { + public void M() { } + } + + public static void M2(this I i) { } +} +"""; + var comp = CreateCompilation(src, references: [derivedRef]); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1929: 'Derived' does not contain a definition for 'M' and the best extension method overload 'E.extension(I).M()' requires a receiver of type 'I' + // new Derived().M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new Derived()").WithArguments("Derived", "M", "E.extension(I).M()", "I").WithLocation(1, 1), + // (2,1): error CS1929: 'Derived' does not contain a definition for 'M2' and the best extension method overload 'E.M2(I)' requires a receiver of type 'I' + // new Derived().M2(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new Derived()").WithArguments("Derived", "M2", "E.M2(I)", "I").WithLocation(2, 1)); + } + + [Fact] + public void GetCompatibleExtension_TypeInference_01() + { + var src = """ +I.M(); + +interface I { } + +static class E +{ + extension(I) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "I.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void GetCompatibleExtension_TypeInference_02() + { + var src = """ +I.M(); + +interface I { } + +static class E +{ + extension(I) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "I.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void GetCompatibleExtension_TypeInference_03() + { + var src = """ +I.M(); + +interface I { } + +static class E +{ + extension(I) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,19): error CS1061: 'I' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'I' could be found (are you missing a using directive or an assembly reference?) + // I.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("I", "M").WithLocation(1, 19)); + } + + [Fact] + public void GetCompatibleExtension_Constraint_UseSiteInfo_01() + { + var missingSrc = """ +public struct Missing { public int i; } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var containerSrc = """ +public struct Container { public Missing field; } +"""; + var containerRef = CreateCompilation(containerSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +Container.M(); + +static class E +{ + extension(T t) where T : unmanaged + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics( + // (1,11): error CS8377: The type 'Container' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'E.extension(T)' + // Container.M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("E.extension(T)", "T", "Container").WithLocation(1, 11), + // (1,11): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // Container.M(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 11)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Container.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + + src = """ +new Container().M(); + +static class E +{ + public static void M(this T t) where T : unmanaged { } +} +"""; + comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics( + // (1,17): error CS8377: The type 'Container' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'E.M(T)' + // new Container().M(); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("E.M(T)", "T", "Container").WithLocation(1, 17), + // (1,17): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new Container().M(); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 17)); + + src = """ +new object().M(new Container()); + +static class E +{ + public static void M(this object o, T t) where T : unmanaged { } +} +"""; + comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics( + // (1,14): error CS8377: The type 'Container' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'E.M(object, T)' + // new object().M(new Container()); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("E.M(object, T)", "T", "Container").WithLocation(1, 14), + // (1,14): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // new object().M(new Container()); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 14)); + } + + [Fact] + public void GetCompatibleExtension_Constraint_UseSiteInfo_02() + { + var missingSrc = """ +public struct Missing { public int i; } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var containerSrc = """ +public struct Container { public Missing field; } +"""; + var containerRef = CreateCompilation(containerSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +using N; + +Container.M(); + +static class E +{ + extension(T t) where T : unmanaged + { + public static void M(int inapplicable) => throw null; + } +} + +namespace N +{ + static class E2 + { + extension(T t) + { + public static void M() { } + } + } +} +"""; + var comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics(); // The inapplicable candidate gets rejected before we get to check its constraints + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Container.M"); + Assert.Equal("void N.E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + + src = """ +Container.M(); + +static class E +{ + extension(T t) where T : unmanaged + { + public static void M(int inapplicable) => throw null; + } +} +"""; + comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics( + // (1,11): error CS7036: There is no argument given that corresponds to the required parameter 'inapplicable' of 'E.extension(T).M(int)' + // Container.M(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("inapplicable", "E.extension(T).M(int)").WithLocation(1, 11)); + + src = """ +using N; + +Container.M(); + +static class E +{ + extension(T t) where T : unmanaged + { + public static void M() => throw null; // applicable to arguments + } +} + +namespace N +{ + static class E2 + { + extension(T t) + { + public static void M() => throw null; + } + } +} +"""; + comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics(); + + tree = comp.SyntaxTrees.Single(); + model = comp.GetSemanticModel(tree); + memberAccess = GetSyntax(tree, "Container.M"); + Assert.Equal("void N.E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + + src = """ +using N; + +new Container().M(); + +static class E +{ + public static void M(this T t) where T : unmanaged { } +} + +namespace N +{ + static class E2 + { + public static void M(this T t) { } + } +} +"""; + // Expecting an error to be reported since we're not able to check whether the constraint is violated + // Tracked by https://github.com/dotnet/roslyn/issues/77407 + comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void GetCompatibleExtension_Constraint_UseSiteInfo_03() + { + var missingSrc = """ +public struct Missing { public int i; } +"""; + var missingRef = CreateCompilation(missingSrc, assemblyName: "missing").EmitToImageReference(); + + var containerSrc = """ +public struct Container { public Missing field; } +"""; + var containerRef = CreateCompilation(containerSrc, references: [missingRef]).EmitToImageReference(); + + var src = """ +int.M(new Container()); + +static class E +{ + extension(int) + { + public static void M(T t) where T : unmanaged { } + } +} +"""; + var comp = CreateCompilation(src, references: [containerRef]); + comp.VerifyEmitDiagnostics( + // (1,5): error CS8377: The type 'Container' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'E.extension(int).M(T)' + // int.M(new Container()); + Diagnostic(ErrorCode.ERR_UnmanagedConstraintNotSatisfied, "M").WithArguments("E.extension(int).M(T)", "T", "Container").WithLocation(1, 5), + // (1,5): error CS0012: The type 'Missing' is defined in an assembly that is not referenced. You must add a reference to assembly 'missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. + // int.M(new Container()); + Diagnostic(ErrorCode.ERR_NoTypeDef, "M").WithArguments("Missing", "missing, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(1, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void InstancePropertyAccess_Simple() + { + var src = """ +System.Console.Write(new object().P); + +public static class Extensions +{ + extension(object o) + { + public int P => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().P"); + Assert.Equal("System.Int32 Extensions.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void InstancePropertyAccess_StaticExtensionProperty() + { + var src = """ +System.Console.Write(new object().P); + +public static class Extensions +{ + extension(object o) + { + public static int P => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'object' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(new object().P); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "new object().P").WithArguments("object", "P").WithLocation(1, 22)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().P"); + Assert.Equal("System.Int32 Extensions.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void InstancePropertyAccess_Invoked() + { + var src = """ +new object().P(); + +public static class Extensions +{ + extension(object o) + { + public int P => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'object' does not contain a definition for 'P' and no accessible extension method 'P' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().P(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "P").WithArguments("object", "P").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "new object().P()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetSymbolInfo(invocation).CandidateSymbols.ToTestDisplayStrings()); + + Assert.Equal(["System.Int32 Extensions.<>E__0.P { get; }"], model.GetMemberGroup(invocation.Expression).ToTestDisplayStrings()); + } + + [Fact] + public void InstancePropertyAccess_Invoked_Invocable() + { + var src = """ +new object().P(); + +public static class Extensions +{ + extension(object o) + { + public System.Action P { get { return () => { System.Console.Write("ran"); }; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().P"); + Assert.Equal("System.Action Extensions.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void StaticMethodInvocation_Simple() + { + var src = """ +object.M(); + +public static class Extensions +{ + extension(object) + { + public static int M() => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "object.M()"); + Assert.Equal("System.Int32 Extensions.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(invocation).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void StaticMethodInvocation_TypeArguments() + { + var source = """ +C.M(42); + +class C { } + +static class E +{ + extension(C) + { + public static void M(int i) => throw null; + public static void M(int i) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Theory, CombinatorialData] + public void StaticMethodInvocation_Ambiguity_Method(bool e1BeforeE2) + { + var e1 = """ +static class E1 +{ + extension(object) + { + public static void Method() => throw null; + } +} +"""; + + var e2 = """ +static class E2 +{ + extension(object) + { + public static void Method() => throw null; + } +} +"""; + + var src = $$""" +object.Method(); + +{{(e1BeforeE2 ? e1 : e2)}} +{{(e1BeforeE2 ? e2 : e1)}} +"""; + var comp = CreateCompilation(src); + if (!e1BeforeE2) + { + comp.VerifyEmitDiagnostics( + // (1,8): error CS0121: The call is ambiguous between the following methods or properties: 'E2.extension(object).Method()' and 'E1.extension(object).Method()' + // object.Method(); + Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("E2.extension(object).Method()", "E1.extension(object).Method()").WithLocation(1, 8)); + } + else + { + comp.VerifyEmitDiagnostics( + // (1,8): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(object).Method()' and 'E2.extension(object).Method()' + // object.Method(); + Diagnostic(ErrorCode.ERR_AmbigCall, "Method").WithArguments("E1.extension(object).Method()", "E2.extension(object).Method()").WithLocation(1, 8)); + } + } + + [Fact] + public void StaticPropertyAccess_InstanceExtensionProperty() + { + var src = """ +System.Console.Write(new object().P); + +public static class Extensions +{ + extension(object o) + { + public static int P => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'object' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(new object().P); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "new object().P").WithArguments("object", "P").WithLocation(1, 22)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().P"); + Assert.Equal("System.Int32 Extensions.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_ExtensionMethod() + { + var source = """ +bool b = true; +var x = b ? object.M : object.M; + +static class E +{ + extension(object o) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,9): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'method group' and 'method group' + // var x = b ? object.M : object.M; + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? object.M : object.M").WithArguments("method group", "method group").WithLocation(2, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").ToArray(); + Assert.Null(model.GetSymbolInfo(memberAccess[0]).Symbol); + Assert.Null(model.GetSymbolInfo(memberAccess[1]).Symbol); + + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess[0]).ToTestDisplayStrings()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess[1]).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_ExtensionProperty() + { + var source = """ +bool b = true; +var x = b ? object.StaticProperty : object.StaticProperty; +System.Console.Write(x); + +static class E +{ + extension(object o) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.StaticProperty").ToArray(); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_DifferentTypes() + { + var source = """ +bool b = true; +var x = b ? object.StaticProperty : object.StaticProperty2; +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public static int StaticProperty => 42; + public static long StaticProperty2 => 43; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.StaticProperty"); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess).Type.ToTestDisplayString()); + Assert.Equal("System.Int64", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_WithTargetType() + { + var source = """ +bool b = true; +long x = b ? object.StaticProperty : object.StaticProperty; +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.StaticProperty").ToArray(); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess[0]).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess[0]).ConvertedType.ToTestDisplayString()); + + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess[1]).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess[1]).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_TwoExtensions_WithTargetType() + { + var source = """ +bool b = true; +string x = b ? D.f : D.f; +System.Console.Write(x); + +class D { } + +static class E1 +{ + extension(D) + { + public static string f => "ran"; + } +} + +static class E2 +{ + extension(object o) + { + public static void f() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,16): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // string x = b ? D.f : D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(2, 16), + // (2,22): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // string x = b ? D.f : D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(2, 22)); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_TwoExtensions_WithTargetDelegateType() + { + var source = """ +bool b = true; +System.Action x = b ? D.f : D.f; +System.Console.Write(x); + +class D { } + +static class E +{ + extension(D) + { + public static string f => null; + } +} + +static class E2 +{ + extension(object o) + { + public static void f() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,23): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // System.Action x = b ? D.f : D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(2, 23), + // (2,29): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // System.Action x = b ? D.f : D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(2, 29)); + } + + [Fact] + public void ResolveAll_Cast_Static_Operand() + { + var source = """ +var x = (long)object.StaticProperty; +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.StaticProperty"); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Cast_Static_Operand_TwoExtensions() + { + var source = """ +var x = (string)D.f; +System.Console.Write(x); + +class D { } + +static class E1 +{ + extension(D) + { + public static string f => "ran"; + } +} + +static class E2 +{ + extension(object) + { + public static void f() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,17): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // var x = (string)D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 17)); + } + + [Fact] + public void ResolveAll_Cast_Static_Operand_TwoExtensions_DelegateType() + { + var source = """ +var x = (System.Action)D.f; +System.Action a = D.f; + +class D { } + +static class E1 +{ + extension(D) + { + public static string f => null; + } +} + +static class E2 +{ + extension(object) + { + public static void f() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,24): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // var x = (System.Action)D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 24), + // (2,19): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // System.Action a = D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(2, 19)); + + // Note: a conversion to a delegate type does not provide invocation context for resolving the member access + source = """ +var x = (System.Action)D.f; +System.Action a = D.f; + +class C +{ + public static void f() { } +} + +class D : C +{ + public static new string f => null!; +} +"""; + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,9): error CS0030: Cannot convert type 'string' to 'System.Action' + // var x = (System.Action)D.f; + Diagnostic(ErrorCode.ERR_NoExplicitConv, "(System.Action)D.f").WithArguments("string", "System.Action").WithLocation(1, 9), + // (2,19): error CS0029: Cannot implicitly convert type 'string' to 'System.Action' + // System.Action a = D.f; + Diagnostic(ErrorCode.ERR_NoImplicitConv, "D.f").WithArguments("string", "System.Action").WithLocation(2, 19)); + } + + [Fact] + public void ResolveAll_MethodTypeInference() + { + var source = """ +write(object.M); +void write(T t) { System.Console.Write(t.ToString()); } + +static class E +{ + extension(object) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess).Type.ToTestDisplayString()); + Assert.Equal("System.Int32", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ArrayCreation_Initializer_Static() + { + var source = """ +var x = new[] { object.StaticProperty, object.StaticProperty }; +System.Console.Write((x[0], x[1])); + +static class E +{ + extension(object) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "(42, 42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.StaticProperty").ToArray(); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ArrayCreation_Rank() + { + var source = """ +var x = new object[object.StaticProperty]; +System.Console.Write(x.Length.ToString()); + +static class E +{ + extension(object o) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.StaticProperty"); + Assert.Equal("System.Int32 E.<>E__0.StaticProperty { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Deconstruction_Declaration() + { + var source = """ +var (x, y) = object.M; +System.Console.Write((x, y)); + +static class E +{ + extension(object) + { + public static (int, int) M => (42, 43); + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("(System.Int32, System.Int32) E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Deconstruction_Assignment() + { + var source = """ +int x, y; +(x, y) = object.M; +System.Console.Write((x, y)); + +static class E +{ + extension(object o) + { + public static (int, int) M => (42, 43); + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("(System.Int32, System.Int32) E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_TupleExpression() + { + var source = """ +System.Console.Write((object.M, object.M)); + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "(42, 42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").ToArray(); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_CollectionExpression() + { + var source = """ +int[] x = [object.M]; +System.Console.Write(x[0].ToString()); + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_CollectionExpression_ExtensionAddMethod() + { + var source = """ +using System.Collections; +using System.Collections.Generic; + +MyCollection c = [42]; + +static class E +{ + extension(MyCollection c) + { + public void Add(int i) { System.Console.Write("ran"); } + } +} + +public class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void ResolveAll_CollectionExpression_ExtensionAddMethod_RefReceiverParameter() + { + // The receiver argument gets an implicit `ref` when the parameter is `ref` + var source = """ +using System.Collections; +using System.Collections.Generic; + +MyCollection c = [42]; +System.Console.Write(c is null); + +static class E +{ + extension(ref MyCollection c) + { + public void Add(int i) { System.Console.Write("ran "); c = null; } + } +} + +public class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran True").VerifyDiagnostics(); + } + + [Fact] + public void ResolveAll_CollectionExpression_ExtensionAddMethod_InReceiverParameter() + { + var source = """ +using System.Collections; +using System.Collections.Generic; + +MyCollection c = [42]; + +static class E +{ + extension(in MyCollection c) + { + public void Add(int i) { System.Console.Write("ran"); } + } +} + +public class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void ResolveAll_CollectionExpression_ExtensionAddMethod_RefParameter() + { + var source = """ +using System.Collections; +using System.Collections.Generic; + +MyCollection c = [42]; + +static class E +{ + extension(MyCollection c) + { + public void Add(ref int i) { System.Console.Write("ran"); } + } +} + +public class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,18): error CS1954: The best overloaded method match 'E.extension(MyCollection).Add(ref int)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. + // MyCollection c = [42]; + Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[42]").WithArguments("E.extension(MyCollection).Add(ref int)").WithLocation(4, 18)); + } + + [Fact] + public void ResolveAll_CollectionExpression_ExtensionAddDelegateTypeProperty() + { + var source = """ +using System.Collections; +using System.Collections.Generic; + +MyCollection c = [42]; + +static class E +{ + extension(MyCollection c) + { + public System.Action Add => (int i) => { }; + } +} + +public class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,18): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) + // MyCollection c = [42]; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[42]").WithArguments("MyCollection", "Add").WithLocation(4, 18) + ); + } + + [Fact] + public void ResolveAll_Initializer_Property() + { + var source = """ +var x = new System.Collections.Generic.List() { object.M }; +System.Console.Write(x[0]); + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Initializer_Method() + { + var source = """ +var x = new System.Collections.Generic.List() { object.M }; + +static class E +{ + extension(object o) + { + public static void M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,54): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // var x = new System.Collections.Generic.List() { object.M }; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "object.M").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(1, 54), + // (1,54): error CS1503: Argument 1: cannot convert from 'method group' to 'int' + // var x = new System.Collections.Generic.List() { object.M }; + Diagnostic(ErrorCode.ERR_BadArgType, "object.M").WithArguments("1", "method group", "int").WithLocation(1, 54)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.OverloadResolutionFailure, model.GetSymbolInfo(memberAccess).CandidateReason); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Initializer_ObjectInitializer() + { + var source = """ +var x = new C() { f = object.M }; +System.Console.Write(x.f.ToString()); + +class C +{ + public int f; +} + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalAccess_Receiver() + { + var source = """ +System.Console.Write(object.M?.ToString()); + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalAccess_WhenNotNull_Property() + { + var source = """ +var x = new object()?.M; +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public string M => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberBinding = GetSyntax(tree, ".M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ConditionalAccess_WhenNotNull_Invocation() + { + var source = """ +var x = new object()?.M(); +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public string M() => "ran"; + public string M(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberBinding = GetSyntax(tree, ".M"); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_CompoundAssignment_Left() + { + var source = """ +object.M += 41; +System.Console.Write(E.M.ToString()); + +static class E +{ + extension(object o) + { + public static int M { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; set; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_CompoundAssignment_Right() + { + var source = """ +int x = 1; +x += object.M; +System.Console.Write(x.ToString()); + +static class E +{ + extension(object o) + { + public static int M => 41; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_BinaryOperator_UserDefinedOperator() + { + var source = """ +var x = object.M + object.M; +System.Console.Write(x.ToString()); + +public class C +{ + public static int operator+(C c1, C c2) => 42; +} + +static class E +{ + extension(object o) + { + public static C M => new C(); + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").ToArray(); + Assert.Equal("C E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("C E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + + var binaryOp = GetSyntax(tree, "object.M + object.M"); + Assert.Equal("System.Int32 C.op_Addition(C c1, C c2)", model.GetSymbolInfo(binaryOp).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_BinaryOperator_NoUserDefinedOperator() + { + var source = """ +var x = object.M + object.M; +System.Console.Write(x.ToString()); + +public class C { } + +static class E +{ + extension(object o) + { + public static C M => new C(); + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,9): error CS0019: Operator '+' cannot be applied to operands of type 'C' and 'C' + // var x = object.M + object.M; + Diagnostic(ErrorCode.ERR_BadBinaryOps, "object.M + object.M").WithArguments("+", "C", "C").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").ToArray(); + Assert.Equal("C E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[0]).Symbol.ToTestDisplayString()); + Assert.Equal("C E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess[1]).Symbol.ToTestDisplayString()); + + var binaryOp = GetSyntax(tree, "object.M + object.M"); + Assert.Null(model.GetSymbolInfo(binaryOp).Symbol); + } + + [Fact] + public void ResolveAll_IncrementOperator() + { + var source = """ +object.M++; + +public class C { } + +static class E +{ + extension(object o) + { + public static int M { get { System.Console.Write("get "); return 41; } set { System.Console.Write($"set({value}) "); } } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "get set(42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; set; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + var unaryOp = GetSyntax(tree, "object.M++"); + Assert.Equal("System.Int32 System.Int32.op_Increment(System.Int32 value)", model.GetSymbolInfo(unaryOp).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_UnaryOperator() + { + var source = """ +_ = !object.M; + +public class C { } + +static class E +{ + extension(object o) + { + public static bool M { get { System.Console.Write("ran"); return true; } } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.Boolean E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + var unaryOp = GetSyntax(tree, "!object.M"); + Assert.Equal("System.Boolean System.Boolean.op_LogicalNot(System.Boolean value)", + model.GetSymbolInfo(unaryOp).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_NullCoalescingOperator() + { + var source = """ +var x = object.M ?? object.M2; +System.Console.Write(x); + +static class E +{ + extension(object o) + { + public static string M => null; + public static string M2 => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.String E.<>E__0.M2 { get; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_NullCoalescingAssignmentOperator() + { + var source = """ +object.M ??= object.M2; + +static class E +{ + extension(object o) + { + public static string M { get { System.Console.Write("get "); return null; } set { System.Console.Write($"set({value}) "); } } + public static string M2 => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "get set(ran)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.String E.<>E__0.M2 { get; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Query_Select() + { + var source = """ +using System.Linq; + +int[] array = [1]; +var r = from int i in array select object.M; +foreach (var x in r) +{ + System.Console.Write(x.ToString()); +} + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact(Skip = "Tracked by https://github.com/dotnet/roslyn/issues/76130 : WasPropertyBackingFieldAccessChecked asserts that we're setting twice")] + public void ResolveAll_Query_Cast() + { + var source = """ +using System.Linq; + +var r = from string s in object.M from string s2 in object.M2 select s.ToString(); +foreach (var x in r) +{ + System.Console.Write(x.ToString()); +} + +static class E +{ + extension(object o) + { + public static object[] M => ["ran"]; + public static object[] M2 => [""]; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Object[] E.M", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.Object[] E.M2", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Return_Lambda() + { + var source = """ +var x = () => + { + bool b = true; + if (b) + return object.M; + else + return object.M2; + }; +System.Console.Write(x().ToString()); + +static class E +{ + extension(object o) + { + public static int M => 42; + public static int M2 => 0; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.Int32 E.<>E__0.M2 { get; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_ExpressionBodiedLambda() + { + var source = """ +var x = () => object.M; +System.Console.Write(x().ToString()); + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_YieldReturn() + { + var source = """ +foreach (var y in local()) +{ + System.Console.Write(y.ToString()); +} + +System.Collections.Generic.IEnumerable local() +{ + bool b = true; + if (b) + yield return object.M; + else + yield return object.M2; +} + +static class E +{ + extension(object o) + { + public static int M => 42; + public static int M2 => 0; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.Int32 E.<>E__0.M2 { get; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_YieldReturn_Lambda() + { + var source = """ +var x = System.Collections.Generic.IEnumerable () => + { + bool b = true; + if (b) + yield return object.M; + else + yield return object.M2; + }; + +foreach (var y in x()) +{ + System.Console.Write(y.ToString()); +} + +static class E +{ + extension(object o) + { + public static int M => 42; + public static int M2 => 0; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,56): error CS1643: Not all code paths return a value in lambda expression of type 'Func>' + // var x = System.Collections.Generic.IEnumerable () => + Diagnostic(ErrorCode.ERR_AnonymousReturnExpected, "=>").WithArguments("lambda expression", "System.Func>").WithLocation(1, 56), + // (5,13): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return object.M; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(5, 13), + // (7,13): error CS1621: The yield statement cannot be used inside an anonymous method or lambda expression + // yield return object.M2; + Diagnostic(ErrorCode.ERR_YieldInAnonMeth, "yield").WithLocation(7, 13)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal("System.Int32 E.<>E__0.M2 { get; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Throw() + { + var source = """ +try +{ + throw object.M; +} +catch (System.Exception e) +{ + System.Console.Write(e.Message); +} + +static class E +{ + extension(object o) + { + public static System.Exception M => new System.Exception("ran"); + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.Exception E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_FieldInitializer() + { + var source = """ +System.Console.Write(C.field.ToString()); + +class C +{ + public static string field = object.M; +} + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Invocation_Static() + { + var src = """ +local(object.M); + +void local(string s) +{ + System.Console.Write(s); +} + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").First(); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Invocation_Static_DelegateTypeParameter() + { + var src = """ +local(object.M); + +void local(System.Func d) +{ + System.Console.Write(d()); +} + +static class E +{ + extension(object o) + { + public static string M() => "ran"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").First(); + Assert.Equal("System.String E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Invocation_Static_Inferred() + { + var src = """ +System.Console.Write(local(object.M)); + +T local(T t) +{ + return t; +} + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.M").First(); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Invocation_Static_DelegateTypeParameter_InapplicableInstanceMember() + { + var src = """ +local(object.ToString); + +void local(System.Func d) +{ + System.Console.Write(d(42)); +} + +static class E +{ + extension(object o) + { + public static string ToString(int i) => "ran"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.ToString").First(); + Assert.Equal("System.String E.<>E__0.ToString(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Invocation_Static_DelegateTypeParameter_PropertyAndMethod() + { + var src = """ +var o = new object(); +C.M(o.Member); + +class C +{ + public static void M(System.Action a) { a(); } +} + +static class E1 +{ + extension(object o) + { + public string Member => throw null; + } +} + +public static class E2 +{ + public static void Member(this object o) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,5): error CS9286: 'object' does not contain a definition for 'Member' and no accessible extension member 'Member' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // C.M(o.Member); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "o.Member").WithArguments("object", "Member").WithLocation(2, 5)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "o.Member"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal(["System.String E1.<>E__0.Member { get; }", "void E2.Member(this System.Object o)"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_ObjectCreation_Static() + { + var source = """ +new C(object.M); + +class C +{ + public C(string s) { System.Console.Write(s); } +} + +static class E +{ + extension(object o) + { + public static string M => "ran"; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_BinaryOperator_Static_TwoExtensions() + { + var src = """ +bool b = D.f + D.f; + +class C +{ + public static bool operator +(C c, System.Action a) => true; +} + +class D { } + +static class E1 +{ + extension(D d) + { + public static C f => null; + } +} + +static class E2 +{ + extension(object o) + { + public static void f() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,10): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // bool b = D.f + D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 10), + // (1,16): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // bool b = D.f + D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 16)); + } + + [Fact] + public void ResolveAll_Lambda_Static_TwoAsGoodExtensions_LambdaConverted() + { + var src = """ +System.Func l = () => object.f; + +static class E1 +{ + extension(object o) + { + public static string f => null; + } +} + +static class E2 +{ + extension(object o) + { + public static void f() { System.Console.Write("ran"); } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : the diagnostic should describe what went wrong + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,38): error CS9286: 'object' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Func l = () => object.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.f").WithArguments("object", "f").WithLocation(1, 38) + ); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.f").First(); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.String E1.<>E__0.f { get; }", "void E2.<>E__0.f()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Lambda_Instance_ExtensionMethodVsExtensionMember() + { + var src = """ +System.Func lambda = () => new object().Member; + +static class E +{ + extension(object o) + { + public string Member => throw null; + } + + public static void Member(this object o) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,43): error CS9286: 'object' does not contain a definition for 'Member' and no accessible extension member 'Member' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Func lambda = () => new object().Member; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "new object().Member").WithArguments("object", "Member").WithLocation(1, 43)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().Member"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void ResolveAll_Lambda_Instance_MethodGroupWithMultipleOverloads() + { + var src = """ +System.Func lambda = () => new object().Member; +lambda()(); + +static class E +{ + extension(object o) + { + public void Member() { System.Console.Write("ran"); } + public void Member(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().Member"); + Assert.Equal("void E.<>E__0.Member()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Lambda_Static_TwoExtensions_ConversionToDelegateType_ExplicitReturnType() + { + var src = """ +var l = System.Action () => D.f; + +class D { } + +static class E1 +{ + extension(object o) + { + public static string f => null; + } +} +static class E2 +{ + extension(object o) + { + public static void f() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,29): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // var l = System.Action () => D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 29) + ); + } + + [Fact] + public void ResolveAll_Lambda_Static_TwoExtensions_ConversionToDelegateType() + { + var src = """ +System.Func l = () => D.f; + +class D { } + +static class E1 +{ + extension(D) + { + public static string f => null; + } +} + +static class E2 +{ + extension(object) + { + public static void f() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,38): error CS9286: 'D' does not contain a definition for 'f' and no accessible extension member 'f' for receiver of type 'D' could be found (are you missing a using directive or an assembly reference?) + // System.Func l = () => D.f; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "D.f").WithArguments("D", "f").WithLocation(1, 38) + ); + } + + [Fact] + public void ResolveAll_SwitchExpression_Static_Default() + { + var src = """ +bool b = true; +var s = b switch { true => object.f, false => default }; +System.Console.Write(s); + +static class E +{ + extension(object) + { + public static string f => "hi"; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hi").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.f").First(); + Assert.Equal("System.String E.<>E__0.f { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + + var defaultExpr = GetSyntax(tree, "default"); + Assert.Equal("System.String", model.GetTypeInfo(defaultExpr).Type.ToTestDisplayString()); + Assert.Equal("System.String", model.GetTypeInfo(defaultExpr).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_RefTernary() + { + var src = """ +bool b = true; +string s1 = "ran"; +string s2 = null; + +var x = b ? ref s1.f : ref s2.f; +System.Console.Write(x); + +static class E +{ + extension(ref string s) + { + public ref string f => ref s; + } + + public static ref string M(this ref string s) => ref s; +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing error on extension parameter + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (15,30): error CS8337: The first parameter of a 'ref' extension method 'M' must be a value type or a generic type constrained to struct. + // public static ref string M(this ref string s) => ref s; + Diagnostic(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, "M").WithArguments("M").WithLocation(15, 30)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s1.f"); + Assert.Equal("ref System.String E.<>E__0.f { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ResolveAll_Query_Static_InstanceMethodGroup() + { + var src = """ +string query = from x in object.ToString select x; + +static class E +{ + extension(object) + { + public static string ToString() => null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,33): error CS0119: 'object.ToString()' is a method, which is not valid in the given context + // string query = from x in object.ToString select x; + Diagnostic(ErrorCode.ERR_BadSKunknown, "ToString").WithArguments("object.ToString()", "method").WithLocation(1, 33)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.ToString"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.String System.Object.ToString()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Query_Static_ExtensionMethodGroup() + { + var src = """ +string query = from x in object.M select x; + +static class E +{ + extension(object o) + { + public static string M() => null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,33): error CS0119: 'E.extension(object).M()' is a method, which is not valid in the given context + // string query = from x in object.M select x; + Diagnostic(ErrorCode.ERR_BadSKunknown, "M").WithArguments("E.extension(object).M()", "method").WithLocation(1, 33)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Empty(model.GetMemberGroup(memberAccess)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider handling BoundBadExpression better + } + + [Fact] + public void ResolveAll_Instance_Invocation_InnerInapplicableExtensionMethodVsOuterInvocableExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + new object().M(); + } + } + + public static class Extension + { + public static void M(this object o, int i) { } // not applicable because of second parameter + } +} + +static class E +{ + extension(object o) + { + public System.Action M => () => { System.Console.Write("ran"); }; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("System.Action E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ResolveAll_Instance_Invocation_InnerIrrelevantExtensionMethodVsOuterInvocableExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + new object().M(); + } + } + + public static class Extension + { + public static void M(this string o, int i) { } // not eligible because of `this` parameter + } +} + +static class E +{ + extension(object o) + { + public System.Action M => () => { System.Console.Write("ran"); }; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("System.Action E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ResolveAll_Instance_InferredVariable_InnerExtensionMethodVsOuterInvocableExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + var x = new object().M; + x(42); + } + } + + public static class Extension + { + public static void M(this object o, int i) { System.Console.Write("ran"); } + } +} + +static class E +{ + extension(object o) + { + public System.Action M => () => throw null; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void System.Object.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void System.Object.M(System.Int32 i)", "System.Action E.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Instance_LocalDeclaration_InnerExtensionMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + int x = new object().M; + } + } + + public static class Extension + { + public static void M(this object o, int i) { } + } +} + +static class E +{ + extension(object o) + { + public int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0428: Cannot convert method group 'M' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = new object().M; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "M").WithArguments("M", "int").WithLocation(7, 34)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void System.Object.M(System.Int32 i)", "System.Int32 E.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Static_LocalDeclaration_InnerExtensionMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + int x = object.M; + } + } + + public static class Extension + { + public static void M(this object o, int i) { } + } +} + +static class E +{ + extension(object o) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (7,28): error CS0428: Cannot convert method group 'M' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = object.M; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "M").WithArguments("M", "int").WithLocation(7, 28)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void System.Object.M(System.Int32 i)", "System.Int32 E.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Static_LocalDeclaration_InstanceInnerExtensionTypeMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + int x = object.M; + } + } + + static class E1 + { + extension(object o) + { + public void M(int i) { } + } + } +} + +static class E2 +{ + extension(object) + { + public static int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (7,28): error CS0428: Cannot convert method group 'M' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = object.M; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "M").WithArguments("M", "int").WithLocation(7, 28)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void N.E1.<>E__0.M(System.Int32 i)", "System.Int32 E2.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Instance_LocalDeclaration_StaticInnerExtensionTypeMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + int x = new object().M; + } + } + + static class E1 + { + extension(object) + { + public static void M(int i) => throw null; + } + } +} + +static class E2 +{ + extension(object o) + { + public int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (7,34): error CS0428: Cannot convert method group 'M' to non-delegate type 'int'. Did you intend to invoke the method? + // int x = new object().M; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "M").WithArguments("M", "int").WithLocation(7, 34)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void N.E1.<>E__0.M(System.Int32 i)", "System.Int32 E2.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Instance_LocalDeclaration_InnerIrrelevantExtensionMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + int x = new object().M; + System.Console.Write(x); + } + } + + public static class Extension + { + public static void M(this string o, int i) { } // not eligible because of `this` parameter + } +} + +static class E +{ + extension(object o) + { + public int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("System.Int32 E.<>E__0.M { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ResolveAll_Instance_LocalDeclaration_DelegateType_InnerInapplicableExtensionMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + System.Action x = new object().M; + x(); + } + } + + public static class Extension + { + public static void M(this object o, int i) { } + } +} + +static class E +{ + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void System.Object.M(System.Int32 i)", "void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_Instance_LocalDeclaration_DelegateType_InnerIrrelevantExtensionMethodVsOuterExtensionProperty() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + System.Action x = new object().M; + x(); + } + } + + public static class Extension + { + public static void M(this string o, int i) { } // not eligible because of `this` parameter + } +} + +static class E +{ + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ResolveAll_ConditionalOperator_Static_TwoAsGoodExtensions_Property() + { + var source = """ +bool b = true; +var x = b ? object.StaticProperty : object.StaticProperty; + +static class E1 +{ + extension(object) + { + public static int StaticProperty => 42; + } +} +static class E2 +{ + extension(object) + { + public static int StaticProperty => 42; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (2,13): error CS9286: 'object' does not contain a definition for 'StaticProperty' and no accessible extension member 'StaticProperty' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // var x = b ? object.StaticProperty : object.StaticProperty; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.StaticProperty").WithArguments("object", "StaticProperty").WithLocation(2, 13), + // (2,37): error CS9286: 'object' does not contain a definition for 'StaticProperty' and no accessible extension member 'StaticProperty' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // var x = b ? object.StaticProperty : object.StaticProperty; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.StaticProperty").WithArguments("object", "StaticProperty").WithLocation(2, 37)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "object.StaticProperty").ToArray(); + Assert.Null(model.GetSymbolInfo(memberAccess[0]).Symbol); + Assert.Null(model.GetSymbolInfo(memberAccess[1]).Symbol); + } + + [Fact] + public void DelegateConversion_TypeReceiver() + { + var source = """ +D d = C.M; +d(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C) + { + public static void M(int i) { System.Console.Write($"E.M({i})"); } + } +} +"""; + var comp = CreateCompilation(source); + var verifier = CompileAndVerify(comp, expectedOutput: "E.M(42)").VerifyDiagnostics(); + verifier.VerifyIL("", """ +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld "D Program.<>O.<0>__M" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn "void E.M(int)" + IL_0010: newobj "D..ctor(object, System.IntPtr)" + IL_0015: dup + IL_0016: stsfld "D Program.<>O.<0>__M" + IL_001b: ldc.i4.s 42 + IL_001d: callvirt "void D.Invoke(int)" + IL_0022: ret +} +"""); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Null(model.GetTypeInfo(memberAccess).Type); + Assert.Equal("D", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal(ConversionKind.MethodGroup, model.GetConversion(memberAccess).Kind); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Creation() + { + var source = """ +D d = new D(C.M); +d(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C) + { + public static void M(int i) { System.Console.Write($"E.M({i})"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E.M(42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Null(model.GetTypeInfo(memberAccess).Type); + Assert.Equal("D", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal(ConversionKind.MethodGroup, model.GetConversion(memberAccess).Kind); + } + + [Fact] + public void DelegateConversion_InstanceReceiver_Creation() + { + var source = """ +D d = new D(new C().M); +d(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C) + { + public void M(int i) { System.Console.Write($"E.M({i})"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "E.M(42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C().M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Null(model.GetTypeInfo(memberAccess).Type); + Assert.Equal("D", model.GetTypeInfo(memberAccess).ConvertedType.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal(ConversionKind.MethodGroup, model.GetConversion(memberAccess).Kind); + } + + [Fact] + public void DelegateConversion_InstanceReceiver() + { + var source = """ +D d = new C(42).M; +d(43); + +delegate void D(int i); + +class C(int x) +{ + public int x = x; + public void M() => throw null; +} + +static class E +{ + extension(C c) + { + public void M(int i) { System.Console.Write($"{c.x}.M({i})"); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "42.M(43)").VerifyDiagnostics(); + verifier.VerifyIL("", """ +{ + // Code size 26 (0x1a) + .maxstack 2 + IL_0000: ldc.i4.s 42 + IL_0002: newobj "C..ctor(int)" + IL_0007: ldftn "void E.M(C, int)" + IL_000d: newobj "D..ctor(object, System.IntPtr)" + IL_0012: ldc.i4.s 43 + IL_0014: callvirt "void D.Invoke(int)" + IL_0019: ret +} +"""); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new C(42).M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal(ConversionKind.MethodGroup, model.GetConversion(memberAccess).Kind); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Overloads() + { + var source = """ +D d = C.M; +d(42); + +C.M(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C) + { + public static void M(int i) { System.Console.Write("ran "); } + + public static void M(string s) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "C.M").First(); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)", "void E.<>E__0.M(System.String s)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_ValueReceiver_Overloads() + { + var source = """ +D d = new C().M; +d(42); + +new C().M(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C c) + { + public void M(int i) { System.Console.Write("ran "); } + public void M(string s) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "new C().M").First(); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)", "void E.<>E__0.M(System.String s)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Overloads_DifferentExtensions() + { + var source = """ +D d = C.M; +d(42); + +C.M(42); + +delegate void D(int i); + +class C { } + +static class E1 +{ + extension(C) + { + public static void M(int i) { System.Console.Write("ran "); } + } +} +static class E2 +{ + extension(C) + { + public static void M(string s) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "C.M").First(); + Assert.Equal("void E1.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E1.<>E__0.M(System.Int32 i)", "void E2.<>E__0.M(System.String s)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_WrongSignature() + { + var source = """ +D d = new C().M; +d(42); + +new C().M(42); + +delegate void D(int i); + +class C { } + +static class E +{ + extension(C c) + { + public void M(string s) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,15): error CS0123: No overload for 'M' matches delegate 'D' + // D d = new C().M; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "D").WithLocation(1, 15), + // (4,11): error CS1503: Argument 2: cannot convert from 'int' to 'string' + // new C().M(42); + Diagnostic(ErrorCode.ERR_BadArgType, "42").WithArguments("2", "int", "string").WithLocation(4, 11)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "new C().M").First(); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M(System.String s)"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E.<>E__0.M(System.String s)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_ZeroArityMatchesAny() + { + var source = """ +D d = object.Method; +d(""); + +d = object.Method; +d(""); + +delegate void D(string s); + +static class E +{ + extension(object) + { + public static void Method(int i) => throw null; + public static void Method(T t) { System.Console.Write("Method "); } + public static void Method(T1 t1, T2 t2) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "Method Method").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.Method"); + Assert.Equal("void E.<>E__0.Method(System.String t)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.Method(System.Int32 i)", "void E.<>E__0.Method(T t)", "void E.<>E__0.Method(T1 t1, T2 t2)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_ValueReceiver_Overloads_OuterScope_WithInapplicableInstanceMember() + { + var source = """ +using N; + +D d = new C().M; +d(42); + +new C().M(42); + +delegate void D(int i); + +class C +{ + public void M(char c) { } +} + +namespace N +{ + static class E1 + { + extension(C c) + { + public void M(int i) + { + System.Console.Write("ran "); + } + } + } +} + +static class E2 +{ + extension(C c) + { + public void M(string s) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "new C().M").First(); + Assert.Equal("void N.E1.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M(System.Char c)", "void E2.<>E__0.M(System.String s)", "void N.E1.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Overloads_InnerScope() + { + var source = """ +using N; + +D d = C.M; +d(42); + +delegate void D(int i); + +class C { } + +static class E1 +{ + extension(C) + { + public static void M(int i) { System.Console.Write("ran"); } + } +} + +namespace N +{ + static class E2 + { + extension(C) + { + public static void M(int i) => throw null; + } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // 0.cs(1,1): hidden CS8019: Unnecessary using directive. + // using N; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(1, 1)); + + CompileAndVerify(comp, expectedOutput: "ran"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal("void E1.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E1.<>E__0.M(System.Int32 i)", "void N.E2.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_TypeArguments_01() + { + var source = """ +D d = object.M; +d(42); + +delegate void D(int i); + +static class E +{ + extension(object) + { + public static void M(int i) => throw null; + public static void M(int i) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_TypeArguments_02() + { + var source = """ +D d = object.M; +d(42); + +delegate void D(int i); + +static class E +{ + extension(object) + { + public static void M(T t) { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0117: 'object' does not contain a definition for 'M' + // D d = object.M; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void DelegateConversion_TypeReceiver_TypeArguments_03() + { + var source = """ +D d = object.M; +d(42); + +delegate void D(int i); + +static class E +{ + extension(T t) + { + public static void M(U u) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 u)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 u)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_TypeArguments_04() + { + var source = """ +D d = object.M; +d(42); + +delegate void D(int i); + +static class E +{ + extension(T) + { + public static void M(int i) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_OptionalParameter() + { + var source = """ +System.Action a = object.M; +System.Action a2 = E.M2; + +static class E +{ + extension(object) + { + public static void M(int i = 0) => throw null; + } + + public static void M2(int i = 0) => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,26): error CS0123: No overload for 'M' matches delegate 'Action' + // System.Action a = object.M; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(1, 26), + // (2,22): error CS0123: No overload for 'M2' matches delegate 'Action' + // System.Action a2 = E.M2; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M2").WithArguments("M2", "System.Action").WithLocation(2, 22)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M([System.Int32 i = 0])"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + source = """ +static class E +{ + static void Main() + { + System.Action a2 = E.M2; + } + + public static void M2(int i = 0) => throw null; +} +"""; + comp = CreateCompilation(source, parseOptions: TestOptions.Regular7); + comp.VerifyEmitDiagnostics( + // (5,30): error CS0123: No overload for 'M2' matches delegate 'Action' + // System.Action a2 = E.M2; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M2").WithArguments("M2", "System.Action").WithLocation(5, 30)); + } + + [Fact] + public void DelegateConversion_TypeReceiver_ReturnRefKindMismatch() + { + var source = """ +D d = object.M; + +delegate int D(); + +static class E +{ + extension(object) + { + public static ref int M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,7): error CS8189: Ref mismatch between 'E.extension(object).M()' and delegate 'D' + // D d = object.M; + Diagnostic(ErrorCode.ERR_DelegateRefMismatch, "object.M").WithArguments("E.extension(object).M()", "D").WithLocation(1, 7)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["ref System.Int32 E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_ReturnTypeMismatch() + { + var source = """ +D d = object.M; + +delegate int D(); + +static class E +{ + extension(object) + { + public static string M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,7): error CS0407: 'string E.extension(object).M()' has the wrong return type + // D d = object.M; + Diagnostic(ErrorCode.ERR_BadRetType, "object.M").WithArguments("E.extension(object).M()", "string").WithLocation(1, 7)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["System.String E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_BadParameterConversion() + { + var source = """ +D d = object.M; +D2 d2 = object.M2; + +delegate void D(long l); +delegate void D2(int i); + +static class E +{ + extension(object) + { + public static void M(int i) => throw null; + public static void M2(long l) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0123: No overload for 'M' matches delegate 'D' + // D d = object.M; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "D").WithLocation(1, 14), + // (2,9): error CS0123: No overload for 'E.extension(object).M2(long)' matches delegate 'D2' + // D2 d2 = object.M2; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "object.M2").WithArguments("E.extension(object).M2(long)", "D2").WithLocation(2, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal(["void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "object.M2"); + Assert.Equal(["void E.<>E__0.M2(System.Int64 l)"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Conditional() + { + var source = """ +System.Action a = object.M; + +static class E +{ + extension(object) + { + [System.Diagnostics.Conditional("DEBUG")] + public static void M() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,19): error CS1618: Cannot create delegate with 'E.extension(object).M()' because it or a method it overrides has a Conditional attribute + // System.Action a = object.M; + Diagnostic(ErrorCode.ERR_DelegateOnConditional, "object.M").WithArguments("E.extension(object).M()").WithLocation(1, 19)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Partial() + { + var source = """ +System.Action a = object.M; + +static partial class E +{ + extension(object) + { + static partial void M(); + } +} +"""; + var comp = CreateCompilation(source); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : the error on the partial method seems misleading + comp.VerifyEmitDiagnostics( + // (1,26): error CS0117: 'object' does not contain a definition for 'M' + // System.Action a = object.M; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 26), + // (7,29): error CS0751: A partial member must be declared within a partial type + // static partial void M(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(7, 29)); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Pointer() + { + var source = """ +D d = object.M; + +unsafe delegate int* D(); + +unsafe static class E +{ + extension(object) + { + public static int* M() => throw null; + } +} +"""; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugExe); + comp.VerifyEmitDiagnostics( + // (1,7): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // D d = object.M; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "object.M").WithLocation(1, 7)); + } + + [Fact] + public void DelegateConversion_TypeReceiver_RefReadonlyMismatch() + { + var source = """ +D d = object.M; +D2 d2 = object.M2; + +D d3 = E.M3; +D2 d4 = E.M4; + +delegate void D(ref int i); +delegate void D2(ref readonly int i); + +static class E +{ + extension(object) + { + public static void M(ref readonly int i) => throw null; + public static void M2(ref int i) => throw null; + } + + public static void M3(ref readonly int i) => throw null; + public static void M4(ref int i) => throw null; +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,7): warning CS9198: Reference kind modifier of parameter 'ref readonly int i' doesn't match the corresponding parameter 'ref int i' in target. + // D d = object.M; + Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "object.M").WithArguments("ref readonly int i", "ref int i").WithLocation(1, 7), + // (2,16): error CS0123: No overload for 'M2' matches delegate 'D2' + // D2 d2 = object.M2; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M2").WithArguments("M2", "D2").WithLocation(2, 16), + // (4,8): warning CS9198: Reference kind modifier of parameter 'ref readonly int i' doesn't match the corresponding parameter 'ref int i' in target. + // D d3 = E.M3; + Diagnostic(ErrorCode.WRN_TargetDifferentRefness, "E.M3").WithArguments("ref readonly int i", "ref int i").WithLocation(4, 8), + // (5,11): error CS0123: No overload for 'M4' matches delegate 'D2' + // D2 d4 = E.M4; + Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M4").WithArguments("M4", "D2").WithLocation(5, 11)); + } + + [Fact] + public void DelegateConversion_TypeReceiver_Obsolete() + { + var source = """ +System.Action a = object.M; + +static class E +{ + extension(object) + { + [System.Obsolete("obsolete", true)] + public static void M() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,19): error CS0619: 'E.extension(object).M()' is obsolete: 'obsolete' + // System.Action a = object.M; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "object.M").WithArguments("E.extension(object).M()", "obsolete").WithLocation(1, 19)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_ReceiverTypeKind_01() + { + var source = """ +System.Action a = object.M; +System.Action a2 = int.M; +a(); +a2(); + +static class E +{ + extension(object) + { + public static void M() { System.Console.Write("ran "); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + } + + [Fact] + public void DelegateConversion_TypeReceiver_ReceiverTypeKind_02() + { + var source = """ +System.Action a2 = int.M; +a2(); + +static class E +{ + extension(int) + { + public static void M() { System.Console.Write("ran"); } + } +} +"""; + + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void DelegateConversion_InstanceReceiver_ReceiverTypeKind() + { + var source = """ +object o = null; +int i = 0; + +System.Action a = o.M; +System.Action a2 = i.M; + +static class E +{ + extension(object o) + { + public void M() => throw null; + } + + extension(int i) + { + public void M() => throw null; + } +} +"""; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,20): error CS1113: Extension method 'E.extension(int).M()' defined on value type 'int' cannot be used to create delegates + // System.Action a2 = i.M; + Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "i.M").WithArguments("E.extension(int).M()", "int").WithLocation(5, 20)); + } + + [Fact] + public void DelegateConversion_InstanceReceiver_RefStructReceiver() + { + var source = """ +System.Span s = default; + +System.Action a = s.M; + +static class E +{ + extension(object o) + { + public void M() => throw null; + } +} +"""; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (3,21): error CS1061: 'Span' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'Span' could be found (are you missing a using directive or an assembly reference?) + // System.Action a = s.M; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("System.Span", "M").WithLocation(3, 21)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void DelegateConversion_TypeReceiver_RefStructReceiver() + { + var source = """ +System.Action a = System.Span.M; + +static class E +{ + extension(object) + { + public static void M() => throw null; + } +} +"""; + + // Note: we apply the same conversion requirements even though no conversion on the receiver + // is needed in a static scenario. + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (1,36): error CS0117: 'Span' does not contain a definition for 'M' + // System.Action a = System.Span.M; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("System.Span", "M").WithLocation(1, 36)); + } + + [Fact] + public void InstancePropertyAccess_Obsolete() + { + var src = """ +_ = new object().Property; +new object().Property = 43; + +static class E +{ + extension(object o) + { + [System.Obsolete("Property is obsolete", true)] + public int Property { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0619: 'E.extension(object).Property' is obsolete: 'Property is obsolete' + // _ = new object().Property; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new object().Property").WithArguments("E.extension(object).Property", "Property is obsolete").WithLocation(1, 5), + // (2,1): error CS0619: 'E.extension(object).Property' is obsolete: 'Property is obsolete' + // new object().Property = 43; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new object().Property").WithArguments("E.extension(object).Property", "Property is obsolete").WithLocation(2, 1)); + } + + [Fact] + public void StaticPropertyAccess_Obsolete() + { + var src = """ +_ = object.Property; +object.Property = 43; + +static class E +{ + extension(object) + { + [System.Obsolete("Property is obsolete", true)] + public static int Property { get => 42; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0619: 'E.extension(object).Property' is obsolete: 'Property is obsolete' + // _ = object.Property; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "object.Property").WithArguments("E.extension(object).Property", "Property is obsolete").WithLocation(1, 5), + // (2,1): error CS0619: 'E.extension(object).Property' is obsolete: 'Property is obsolete' + // object.Property = 43; + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "object.Property").WithArguments("E.extension(object).Property", "Property is obsolete").WithLocation(2, 1)); + } + + [Fact] + public void InstancePropertyAccess_Obsolete_InInvocation() + { + var src = """ +new object().Property(); + +static class E +{ + extension(object o) + { + [System.Obsolete("Property is obsolete", true)] + public System.Action Property { get => throw null; set { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0619: 'E.extension(object).Property' is obsolete: 'Property is obsolete' + // new object().Property(); + Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "new object().Property").WithArguments("E.extension(object).Property", "Property is obsolete").WithLocation(1, 1)); + } + + [Fact] + public void InstancePropertyAccess_ColorColor() + { + var src = """ +C.M(new C()); + +class C +{ + public static void M(C C) + { + C.Property = 42; + } +} + +static class E +{ + extension(C c) + { + public int Property { set { System.Console.Write(value); } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var property = GetSyntax(tree, "C.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { set; }", model.GetSymbolInfo(property).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(property)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void StaticPropertyAccess_ColorColor() + { + var src = """ +C.M(null); + +class C +{ + public static void M(C C) + { + C.Property = 42; + } +} + +static class E +{ + extension(C c) + { + public static int Property { set { System.Console.Write("Property"); } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "Property").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var property = GetSyntax(tree, "C.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { set; }", model.GetSymbolInfo(property).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(property)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ConditionalReceiver_Property_MemberAccess() + { + var src = """ +bool b = true; +System.Console.Write((b ? "" : null).Property.ToString()); + +static class E +{ + extension(string s) + { + public int Property => 42; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, """(b ? "" : null).Property"""); + Assert.Equal("System.Int32 E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void PropertyAccess_ReturnNotLValue() + { + var src = """ +object.Property.field = 1; + +public struct S +{ + public int field; +} +static class E +{ + extension(object) + { + public static S Property { get => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1612: Cannot modify the return value of 'E.extension(object).Property' because it is not a variable + // object.Property.field = 1; + Diagnostic(ErrorCode.ERR_ReturnNotLValue, "object.Property").WithArguments("E.extension(object).Property").WithLocation(1, 1)); + } + + [Fact] + public void StaticPropertyAccess_RefProperty_01() + { + var src = """ +localFuncRef(ref object.Property); +localFuncOut(out object.Property); + +void localFuncRef(ref int i) => throw null; +void localFuncOut(out int i) => throw null; + +static class E +{ + extension(object) + { + public static int Property { get => throw null; set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,18): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // localFuncRef(ref object.Property); + Diagnostic(ErrorCode.ERR_RefProperty, "object.Property").WithLocation(1, 18), + // (2,18): error CS0206: A non ref-returning property or indexer may not be used as an out or ref value + // localFuncOut(out object.Property); + Diagnostic(ErrorCode.ERR_RefProperty, "object.Property").WithLocation(2, 18)); + } + + [Fact] + public void StaticPropertyAccess_RefProperty_02() + { + var src = """ +localFuncRef(ref object.Property); + +void localFuncRef(ref int i) => throw null; + +static class E +{ + extension(object) + { + public static ref int Property { get => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + } + + [Fact] + public void StaticPropertyAccess_AssignReadonlyNotField() + { + var src = """ +object.Property = 1; + +static class E +{ + extension(object) + { + public static ref readonly int Property { get => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8331: Cannot assign to property 'Property' or use it as the right hand side of a ref assignment because it is a readonly variable + // object.Property = 1; + Diagnostic(ErrorCode.ERR_AssignReadonlyNotField, "object.Property").WithArguments("property", "Property").WithLocation(1, 1)); + } + + [Fact] + public void StaticPropertyAccess_AssgReadonlyProp() + { + var src = """ +object.Property = 1; + +static class E +{ + extension(object) + { + public static int Property { get => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0200: Property or indexer 'E.extension(object).Property' cannot be assigned to -- it is read only + // object.Property = 1; + Diagnostic(ErrorCode.ERR_AssgReadonlyProp, "object.Property").WithArguments("E.extension(object).Property").WithLocation(1, 1)); + } + + [Fact] + public void StaticPropertyAccess_InitOnlyProperty() + { + var src = """ +object.Property = 1; + +static class E +{ + extension(object) + { + public static int Property { init => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,38): error CS8856: The 'init' accessor is not valid on static members + // public static int Property { init => throw null; } + Diagnostic(ErrorCode.ERR_BadInitAccessor, "init").WithLocation(7, 38)); + } + + [Fact] + public void InstancePropertyAccess_InitOnlyProperty() + { + var src = """ +new object().Property = 1; + +static class E +{ + extension(object o) + { + public int Property { init => throw null; } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 :(instance) confirm whether init-only accessors should be allowed in extensions + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8852: Init-only property or indexer 'E.extension(object).Property' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // new object().Property = 1; + Diagnostic(ErrorCode.ERR_AssignmentInitOnly, "new object().Property").WithArguments("E.extension(object).Property").WithLocation(1, 1)); + } + + [ConditionalFact(typeof(NoUsedAssembliesValidation))] // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + public void InstancePropertyAccess_InitOnlyProperty_ObjectInitializer() + { + var src = """ +_ = new object() { Property = 1 }; + +static class E +{ + extension(object o) + { + public int Property { init => throw null; } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm whether init-only accessors should be allowed in extensions + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var assignment = GetSyntax(tree, "Property = 1"); + Assert.Equal("System.Int32 E.<>E__0.Property { init; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString()); + } + + [Fact] + public void StaticPropertyAccess_InaccessibleSetter() + { + var src = """ +object.Property = 1; + +static class E +{ + extension(object) + { + public static int Property { get => throw null; private set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0272: The property or indexer 'E.extension(object).Property' cannot be used in this context because the set accessor is inaccessible + // object.Property = 1; + Diagnostic(ErrorCode.ERR_InaccessibleSetter, "object.Property").WithArguments("E.extension(object).Property").WithLocation(1, 1)); + } + + [Fact] + public void ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_ExtensionTypeMethods() + { + // See ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_Method + var source = """ +struct S1(Color Color) +{ + public void Test() + { + Color.M1(this); + } +} + +class Color { } + +static class E +{ + extension(Color c) + { + public void M1(S1 x, int y = 0) + { + System.Console.WriteLine("instance"); + } + + public static void M1(T x) where T : unmanaged + { + System.Console.WriteLine("static"); + } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics( + //// (5,9): error CS9106: Identifier 'Color' is ambiguous between type 'Color' and parameter 'Color Color' in this context. + //// Color.M1(this); + //Diagnostic(ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver, "Color").WithArguments("Color", "Color", "Color Color").WithLocation(5, 9) + ); + + Assert.NotEmpty(comp.GetTypeByMetadataName("S1").InstanceConstructors.OfType().Single().GetCapturedParameters()); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M1"); + Assert.Equal("void E.<>E__0.M1(S1 x, [System.Int32 y = 0])", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_ExtensionTypeProperties() + { + var source = """ +struct S1(Color Color) +{ + public void Test() + { + _ = Color.P1; + } +} + +class Color { } + +static class E1 +{ + extension(Color c) + { + public int P1 => 0; + } +} + +static class E2 +{ + extension(Color) + { + public static int P1 => 0; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.P1"); + Assert.Equal("System.Int32 E1.<>E__0.P1 { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ParameterCapturing_023_ColorColor_MemberAccess_InstanceAndStatic_ExtensionTypeMembersVsExtensionMethod() + { + var source = """ +public struct S1(Color Color) +{ + public void Test() + { + Color.M1(this); + } +} + +public class Color { } + +public static class E1 +{ + public static void M1(this Color c, S1 x, int y = 0) + { + System.Console.WriteLine("instance"); + } +} + +static class E +{ + extension(Color) + { + public static void M1(T x) where T : unmanaged + { + System.Console.WriteLine("static"); + } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver + var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); + comp.VerifyEmitDiagnostics( + //// (5,9): error CS9106: Identifier 'Color' is ambiguous between type 'Color' and parameter 'Color Color' in this context. + //// Color.M1(this); + //Diagnostic(ErrorCode.ERR_AmbiguousPrimaryConstructorParameterAsColorColorReceiver, "Color").WithArguments("Color", "Color", "Color Color").WithLocation(5, 9) + ); + + Assert.NotEmpty(comp.GetTypeByMetadataName("S1").InstanceConstructors.OfType().Single().GetCapturedParameters()); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M1"); + Assert.Equal("void Color.M1(S1 x, [System.Int32 y = 0])", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void InstanceMethod_MemberAccess() + { + var src = """ +new object().M.ToString(); + +static class E +{ + extension(object o) + { + public int M() => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0119: 'E.extension(object).M()' is a method, which is not valid in the given context + // new object().M.ToString(); + Diagnostic(ErrorCode.ERR_BadSKunknown, "M").WithArguments("E.extension(object).M()", "method").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + src = """ +new object().M.ToString(); + +static class E +{ + public static int M(this object o) => 42; +} +"""; + comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS0119: 'E.M(object)' is a method, which is not valid in the given context + // new object().M.ToString(); + Diagnostic(ErrorCode.ERR_BadSKunknown, "M").WithArguments("E.M(object)", "method").WithLocation(1, 14)); + + tree = comp.SyntaxTrees.Single(); + model = comp.GetSemanticModel(tree); + memberAccess = GetSyntax(tree, "new object().M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void InstanceMethod_MemberAccess_Missing() + { + var src = """ +new object().M.ToString(); +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,14): error CS1061: 'object' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) + // new object().M.ToString(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("object", "M").WithLocation(1, 14)); + } + + [Fact] + public void CheckValueKind_AssignToMethodGroup() + { + var src = """ +object.M = null; + +static class E +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1656: Cannot assign to 'M' because it is a 'method group' + // object.M = null; + Diagnostic(ErrorCode.ERR_AssgReadonlyLocalCause, "object.M").WithArguments("M", "method group").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.NotAVariable, model.GetSymbolInfo(memberAccess).CandidateReason); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void AccessOnVoid_Invocation() + { + var src = """ +object.M().ToString(); + +static class E +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,11): error CS0023: Operator '.' cannot be applied to operand of type 'void' + // object.M().ToString(); + Diagnostic(ErrorCode.ERR_BadUnaryOp, ".").WithArguments(".", "void").WithLocation(1, 11)); + } + + [Fact] + public void ExtensionMemberLookup_InaccessibleMembers() + { + var src = """ +object.Method(); +_ = object.Property; + +static class E +{ + extension(object o) + { + private static void Method() => throw null; + private static int Property => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'Method' + // object.Method(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "Method").WithArguments("object", "Method").WithLocation(1, 8), + // (2,12): error CS0117: 'object' does not contain a definition for 'Property' + // _ = object.Property; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Property").WithArguments("object", "Property").WithLocation(2, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "object.Method"); + Assert.Equal(["void E.<>E__0.Method()"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "object.Property"); + Assert.Equal([], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void ExtensionMemberLookup_InterpolationHandler_Simple() + { + var src = """ +_ = f($"{(object)1} {f2()}"); + +static int f(InterpolationHandler s) => 0; +static string f2() => "hello"; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + public InterpolationHandler(int literalLength, int formattedCount) => throw null; + public void AppendLiteral(string value) { } + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} +"""; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ExtensionMemberLookup_InterpolationHandler_AppendLiteralExtensionMethod() + { + var src = """ +_ = f($"{(object)1} {f2()}"); + +static int f(InterpolationHandler s) => 0; +static string f2() => "hello"; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + public InterpolationHandler(int literalLength, int formattedCount) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +public static class Extensions +{ + public static void AppendLiteral(this InterpolationHandler ih, string value) { } +} +"""; + + // Interpolation handlers don't allow extension methods + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,20): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendLiteral' and no accessible extension method 'AppendLiteral' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, " ").WithArguments("InterpolationHandler", "AppendLiteral").WithLocation(1, 20), + // (1,20): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, " ").WithArguments("?.()").WithLocation(1, 20)); + } + + [Fact] + public void ExtensionMemberLookup_InterpolationHandler_AppendLiteralExtensionDeclarationMethod() + { + var src = """ +_ = f($"{(object)1} {f2()}"); + +static int f(InterpolationHandler s) => 0; +static string f2() => "hello"; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + public InterpolationHandler(int literalLength, int formattedCount) => throw null; + public void AppendFormatted(T hole, int alignment = 0, string format = null) => throw null; +} + +static class E +{ + extension(InterpolationHandler i) + { + public void AppendLiteral(string value) { } + } +} +"""; + + // Interpolation handlers don't allow extension methods + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,20): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendLiteral' and no accessible extension method 'AppendLiteral' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, " ").WithArguments("InterpolationHandler", "AppendLiteral").WithLocation(1, 20), + // (1,20): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, " ").WithArguments("?.()").WithLocation(1, 20)); + } + + [Fact] + public void ExtensionMemberLookup_InterpolationHandler_AppendFormattedExtensionMethod() + { + var src = """ +/**/ +_ = f($"{(object)1} {f2()}"); +/**/ + +static int f(InterpolationHandler s) => 0; +static string f2() => "hello"; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + public InterpolationHandler(int literalLength, int formattedCount) => throw null; + public void AppendLiteral(string value) { } +} + +public static class Extensions +{ + public static void AppendFormatted(this InterpolationHandler ih, T hole, int alignment = 0, string format = null) { } +} +"""; + + // Interpolation handlers don't allow extension methods + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (2,9): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{(object)1}").WithArguments("InterpolationHandler", "AppendFormatted").WithLocation(2, 9), + // (2,9): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{(object)1}").WithArguments("?.()").WithLocation(2, 9), + // (2,21): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{f2()}").WithArguments("InterpolationHandler", "AppendFormatted").WithLocation(2, 21), + // (2,21): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{f2()}").WithArguments("?.()").WithLocation(2, 21) + ); + } + + [Fact] + public void ExtensionMemberLookup_InterpolationHandler_AppendFormattedExtensionTypeMethod() + { + var src = """ +_ = f($"{(object)1} {f2()}"); + +static int f(InterpolationHandler s) => 0; +static string f2() => "hello"; + +[System.Runtime.CompilerServices.InterpolatedStringHandler] +public struct InterpolationHandler +{ + public InterpolationHandler(int literalLength, int formattedCount) => throw null; + public void AppendLiteral(string value) { } +} + +static class E +{ + extension(InterpolationHandler i) + { + public void AppendFormatted(T hole, int alignment = 0, string format = null) { } + } +} +"""; + + // Interpolation handlers don't allow extension methods + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,9): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{(object)1}").WithArguments("InterpolationHandler", "AppendFormatted").WithLocation(1, 9), + // (1,9): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{(object)1}").WithArguments("?.()").WithLocation(1, 9), + // (1,21): error CS1061: 'InterpolationHandler' does not contain a definition for 'AppendFormatted' and no accessible extension method 'AppendFormatted' accepting a first argument of type 'InterpolationHandler' could be found (are you missing a using directive or an assembly reference?) + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "{f2()}").WithArguments("InterpolationHandler", "AppendFormatted").WithLocation(1, 21), + // (1,21): error CS8941: Interpolated string handler method '?.()' is malformed. It does not return 'void' or 'bool'. + // _ = f($"{(object)1} {f2()}"); + Diagnostic(ErrorCode.ERR_InterpolatedStringHandlerMethodReturnMalformed, "{f2()}").WithArguments("?.()").WithLocation(1, 21)); + } + + [Fact] + public void LiteralReceiver_Property_Enum_Set() + { + var src = """ +Enum.Zero.Property = 1; + +enum Enum +{ + Zero +} + +static class E +{ + extension(Enum e) + { + public int Property { set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + // Consider improving the error message + comp.VerifyEmitDiagnostics( + // (1,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // Enum.Zero.Property = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "Enum.Zero.Property").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Enum.Zero.Property"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Int32 E.<>E__0.Property { set; }"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.NotAVariable, model.GetSymbolInfo(memberAccess).CandidateReason); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void LiteralReceiver_Property_Integer_ForLong() + { + var src = """ +1.Property = 42; +_ = 2.Property; + +static class E +{ + extension(long l) + { + public int Property { get { System.Console.Write("get "); return 42; } set { System.Console.Write("set "); } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9286: 'int' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type 'int' could be found (are you missing a using directive or an assembly reference?) + // 1.Property = 42; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "1.Property").WithArguments("int", "Property").WithLocation(1, 1), + // (2,5): error CS9286: 'int' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type 'int' could be found (are you missing a using directive or an assembly reference?) + // _ = 2.Property; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "2.Property").WithArguments("int", "Property").WithLocation(2, 5)); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : review the behavior of the semantic model APIs + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "1.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess1).CandidateReason); + + var memberAccess2 = GetSyntax(tree, "2.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess2).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_String() + { + var src = """ +"".Property = 42; +_ = "".Property; + +static class E +{ + extension(string s) + { + public int Property { get { System.Console.Write("get "); return 42; } set { System.Console.Write("set "); } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "set get").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "\"\".Property").First(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntaxes(tree, "\"\".Property").Last(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void SwitchReceiver_Property_String() + { + var src = """ +bool b = true; +(b switch { true => "", _ => "" }).Property = 42; +_ = (b switch { true => "", _ => "" }).Property; + +static class E +{ + extension(string s) + { + public int Property { get { System.Console.Write("get "); return 42; } set { System.Console.Write("set "); } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "set get").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, """(b switch { true => "", _ => "" }).Property""").First(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntaxes(tree, """(b switch { true => "", _ => "" }).Property""").Last(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ConditionalReceiver_Property_String() + { + var src = """ +bool b = true; +(b ? "" : null).Property = 42; +_ = (b ? "" : null).Property; + +static class E +{ + extension(string s) + { + public int Property { get { System.Console.Write("get "); return 42; } set { System.Console.Write("set "); } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "set get").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, """(b ? "" : null).Property""").First(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + + var memberAccess2 = GetSyntaxes(tree, """(b ? "" : null).Property""").Last(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + } + + [Fact] + public void LiteralReceiver_Property_Integer_Get() + { + var src = """ +_ = 1.Property; + +static class E +{ + extension(int i) + { + public int Property { get { System.Console.Write("get"); return 42; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "get").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "1.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void LiteralReceiver_Property_Integer_Set() + { + var src = """ +1.Property = 1; + +static class E +{ + extension(int i) + { + public int Property { set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + // Consider improving the error message + comp.VerifyEmitDiagnostics( + // (1,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // 1.Property = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "1.Property").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "1.Property"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Int32 E.<>E__0.Property { set; }"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.NotAVariable, model.GetSymbolInfo(memberAccess).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_Null() + { + var src = """ +null.Property = 1; +_ = null.Property; + +static class E +{ + extension(object o) + { + public int Property { get => throw null; set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0023: Operator '.' cannot be applied to operand of type '' + // null.Property = 1; + Diagnostic(ErrorCode.ERR_BadUnaryOp, "null.Property").WithArguments(".", "").WithLocation(1, 1), + // (2,5): error CS0023: Operator '.' cannot be applied to operand of type '' + // _ = null.Property; + Diagnostic(ErrorCode.ERR_BadUnaryOp, "null.Property").WithArguments(".", "").WithLocation(2, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "null.Property").First(); + Assert.Null(model.GetSymbolInfo(memberAccess1).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess1).CandidateReason); + + var memberAccess2 = GetSyntaxes(tree, "null.Property").Last(); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess2).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_Default() + { + var src = """ +default.Property = 1; +_ = default.Property; + +static class E +{ + extension(object o) + { + public int Property { get => throw null; set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS8716: There is no target type for the default literal. + // default.Property = 1; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(1, 1), + // (2,5): error CS8716: There is no target type for the default literal. + // _ = default.Property; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(2, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "default.Property").First(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess1).CandidateReason); + + var memberAccess2 = GetSyntaxes(tree, "default.Property").Last(); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess2).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_Tuple_Get() + { + var src = """ +_ = (1, 2).Property; + +static class E +{ + extension((int, int) t) + { + public int Property { get { System.Console.Write("get "); return 42; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "get").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "(1, 2).Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void LiteralReceiver_Property_Tuple_Set() + { + var src = """ +(1, 2).Property = 1; + +static class E +{ + extension((int, int) t) + { + public int Property { set { System.Console.Write($"set(value)"); }} + } +} +"""; + var comp = CreateCompilation(src); + // Consider improving the error message + comp.VerifyEmitDiagnostics( + // (1,1): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (1, 2).Property = 1; + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "(1, 2).Property").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "(1, 2).Property"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Int32 E.<>E__0.Property { set; }"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.NotAVariable, model.GetSymbolInfo(memberAccess).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_Tuple_Default() + { + var src = """ +(default, default).Property = 1; +_ = (default, default).Property; + +static class E +{ + extension((object, object) t) + { + public int Property { get => throw null; set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,2): error CS8716: There is no target type for the default literal. + // (default, default).Property = 1; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(1, 2), + // (1,11): error CS8716: There is no target type for the default literal. + // (default, default).Property = 1; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(1, 11), + // (2,6): error CS8716: There is no target type for the default literal. + // _ = (default, default).Property; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(2, 6), + // (2,15): error CS8716: There is no target type for the default literal. + // _ = (default, default).Property; + Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(2, 15)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "(default, default).Property").First(); + Assert.Null(model.GetSymbolInfo(memberAccess1).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess1).CandidateReason); + + var memberAccess2 = GetSyntaxes(tree, "(default, default).Property").Last(); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess2).CandidateReason); + } + + [Fact] + public void LiteralReceiver_Property_Tuple_Integer_ForLong() + { + var src = """ +(1, 1).Property = 1; +_ = (2, 2).Property; + +static class E +{ + extension((long, long) t) + { + public int Property { get => throw null; set => throw null; } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9286: '(int, int)' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type '(int, int)' could be found (are you missing a using directive or an assembly reference?) + // (1, 1).Property = 1; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "(1, 1).Property").WithArguments("(int, int)", "Property").WithLocation(1, 1), + // (2,5): error CS9286: '(int, int)' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type '(int, int)' could be found (are you missing a using directive or an assembly reference?) + // _ = (2, 2).Property; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "(2, 2).Property").WithArguments("(int, int)", "Property").WithLocation(2, 5)); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : review the behavior of the semantic model APIs + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntax(tree, "(1, 1).Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess1).CandidateReason); + + var memberAccess2 = GetSyntax(tree, "(2, 2).Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; set; }", model.GetSymbolInfo(memberAccess2).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(CandidateReason.None, model.GetSymbolInfo(memberAccess2).CandidateReason); + } + + [Fact] + public void PreferMoreSpecific_Static_MethodAndProperty() + { + var src = """ +System.Console.Write(object.M); + +static class E1 +{ + extension(object) + { + public static string M() => throw null; + } +} + +static class E2 +{ + extension(object) + { + public static string M => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'object' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(object.M); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.M").WithArguments("object", "M").WithLocation(1, 22)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.String E1.<>E__0.M()", "System.String E2.<>E__0.M { get; }"], + model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider handling BoundBadExpression better + } + + [Fact] + public void PreferMoreSpecific_Static_MethodAndProperty_Generic() + { + var src = """ +System.Console.Write(object.M); + +static class E1 +{ + extension(T) + { + public static string M() => throw null; + } +} + +static class E2 +{ + extension(T) + { + public static string M => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'object' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(object.M); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.M").WithArguments("object", "M").WithLocation(1, 22)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider handling BoundBadExpression better + Assert.Equal(["System.String E1.<>E__0.M()", "System.String E2.<>E__0.M { get; }"], + model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void PreferMoreSpecific_Static_MethodAndProperty_Invocation() + { + var src = """ +System.Console.Write(object.M()); + +static class E1 +{ + extension(object) + { + public static string M() => "ran"; + } +} + +static class E2 +{ + extension(object) + { + public static string M => throw null; // not invocable + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("System.String E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["System.String E1.<>E__0.M()", "System.String E2.<>E__0.M { get; }"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetCompatibleExtensions_TwoSubstitutions() + { + var src = """ +C.M(); +new C().M2(); + +interface I { } +class C : I, I { } + +static class E +{ + extension(I) + { + public static void M() { } + } + + public static void M2(this I i) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,3): error CS1061: 'C' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // C.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("C", "M").WithLocation(1, 3), + // (2,9): error CS1061: 'C' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // new C().M2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("C", "M2").WithLocation(2, 9)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "C.M"); + Assert.Null(model.GetSymbolInfo(memberAccess1).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess1).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess1)); + + var memberAccess2 = GetSyntax(tree, "new C().M2"); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal([], model.GetSymbolInfo(memberAccess2).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess2)); + } + + [Theory, ClassData(typeof(ThreePermutationGenerator))] + public void PreferMoreSpecific_Static_MethodAndMoreSpecificInvocablePropertyAndMoreSpecificMethod(int first, int second, int third) + { + string[] segments = [ + """ + static class E1 + { + extension(object) + { + public static string M() => throw null; + } + } + """, + """ + static class E2 + { + extension(C) + { + public static System.Func M => null; + } + } + """, + """ + static class E3 + { + extension(C) + { + public static string M() => throw null; + } + } + """]; + + var src = $$""" +System.Console.Write(C.M()); + +class C { } + +{{segments[first]}} + +{{segments[second]}} + +{{segments[third]}} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'C' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'C' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(C.M()); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "C.M").WithArguments("C", "M").WithLocation(1, 22)); + } + + [Fact] + public void AmbiguousCallOnInterface() + { + var src = """ +I2.M(); + +interface I +{ + public static void M() { } +} + +interface I2 : I, I { } +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider improving the symbols in this error message + comp.VerifyEmitDiagnostics( + // (1,4): error CS0121: The call is ambiguous between the following methods or properties: 'I.M()' and 'I.M()' + // I2.M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("I.M()", "I.M()").WithLocation(1, 4)); + } + + [Fact] + public void AmbiguousCallOnInterface_Generic() + { + var src = """ +I2.M(); + +interface I +{ + public static void M() { } +} + +interface I2 : I, I { } +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0121: The call is ambiguous between the following methods or properties: 'I.M()' and 'I.M()' + // I2.M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("I.M()", "I.M()").WithLocation(1, 4)); + } + + [Fact] + public void OmittedTypeArguments() + { + var src = """ +object.P; +object.P<>; + +static class E +{ + extension(object) + { + public static int P => 42; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (1,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // object.P; + Diagnostic(ErrorCode.ERR_IllegalStatement, "object.P").WithLocation(1, 1), + // (1,8): error CS0117: 'object' does not contain a definition for 'P' + // object.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("object", "P").WithLocation(1, 8), + // (2,1): error CS8389: Omitting the type argument is not allowed in the current context + // object.P<>; + Diagnostic(ErrorCode.ERR_OmittedTypeArgument, "object.P<>").WithLocation(2, 1), + // (2,1): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // object.P<>; + Diagnostic(ErrorCode.ERR_IllegalStatement, "object.P<>").WithLocation(2, 1), + // (2,8): error CS0117: 'object' does not contain a definition for 'P' + // object.P<>; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P<>").WithArguments("object", "P").WithLocation(2, 8)); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_ForEach_NoMethod() + { + var src = """ +foreach (var x in new C()) +{ + System.Console.Write(x); + break; +} + +class C { } +class D { } + +static class E +{ + extension(C c) + { + public D GetEnumerator() => new D(); + } + extension(D d) + { + public bool MoveNext() => true; + public int Current => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,19): error CS0117: 'D' does not contain a definition for 'Current' + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_NoSuchMember, "new C()").WithArguments("D", "Current").WithLocation(1, 19), + // (1,19): error CS0202: foreach requires that the return type 'D' of 'E.extension(C).GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new C()").WithArguments("D", "E.extension(C).GetEnumerator()").WithLocation(1, 19) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var loop = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetForEachStatementInfo(loop).GetEnumeratorMethod); + Assert.Null(model.GetForEachStatementInfo(loop).MoveNextMethod); + Assert.Null(model.GetForEachStatementInfo(loop).CurrentProperty); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_ForEach_NoApplicableMethod() + { + var src = """ +foreach (var x in new C()) +{ + System.Console.Write(x); + break; +} + +class C +{ + public void GetEnumerator(int notApplicable) { } // not applicable +} +class D { } + +static class E +{ + extension(C c) + { + public D GetEnumerator() => new D(); + } + extension(D d) + { + public bool MoveNext() => true; + public int Current => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,19): error CS0117: 'D' does not contain a definition for 'Current' + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_NoSuchMember, "new C()").WithArguments("D", "Current").WithLocation(1, 19), + // (1,19): error CS0202: foreach requires that the return type 'D' of 'E.extension(C).GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property + // foreach (var x in new C()) + Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new C()").WithArguments("D", "E.extension(C).GetEnumerator()").WithLocation(1, 19) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var loop = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetForEachStatementInfo(loop).GetEnumeratorMethod); + Assert.Null(model.GetForEachStatementInfo(loop).MoveNextMethod); + Assert.Null(model.GetForEachStatementInfo(loop).CurrentProperty); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_ForEach_WrongArity() + { + var src = """ +using System.Collections; + +foreach (var x in new C()) { } + +class C { } + +static class E +{ + extension(C c) + { + public IEnumerator GetEnumerator() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,19): error CS0411: The type arguments for method 'E.extension(C).GetEnumerator()' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // foreach (var x in new C()) { } + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "new C()").WithArguments("E.extension(C).GetEnumerator()").WithLocation(3, 19), + // (3,19): error CS1579: foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public instance or extension definition for 'GetEnumerator' + // foreach (var x in new C()) { } + Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new C()").WithArguments("C", "GetEnumerator").WithLocation(3, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var loop = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetForEachStatementInfo(loop).GetEnumeratorMethod); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_ForEach_NonInvocable() + { + var src = """ +using System.Collections; + +foreach (var x in new C()) { } + +class C { } + +static class E +{ + extension(C c) + { + public IEnumerator GetEnumerator => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,19): error CS1579: foreach statement cannot operate on variables of type 'C' because 'C' does not contain a public instance or extension definition for 'GetEnumerator' + // foreach (var x in new C()) { } + Diagnostic(ErrorCode.ERR_ForEachMissingMember, "new C()").WithArguments("C", "GetEnumerator").WithLocation(3, 19)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var loop = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Null(model.GetForEachStatementInfo(loop).GetEnumeratorMethod); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Deconstruct_NoMethod() + { + var src = """ +var (x, y) = new C(); +System.Console.Write((x, y)); + +class C { } + +static class E +{ + extension(C c) + { + public void Deconstruct(out int i, out int j) { i = 42; j = 43; } + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based deconstruction + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var deconstruction = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("void E.<>E__0.Deconstruct(out System.Int32 i, out System.Int32 j)", + model.GetDeconstructionInfo(deconstruction).Method.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Deconstruct_FallbackToExtensionMethod() + { + // If the method from the extension type is not applicable, we fall back + // to a Deconstruct extension method + var src = """ +var (x, y) = new C(); +System.Console.Write((x, y)); + +public class C { } + +static class E +{ + extension(C c) + { + public void Deconstruct(int inapplicable) => throw null; + } +} + +public static class E2 +{ + public static void Deconstruct(this C c, out int i, out int j) { i = 42; j = 43; } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based deconstruction + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var deconstruction = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("void E2.Deconstruct(this C c, out System.Int32 i, out System.Int32 j)", + model.GetDeconstructionInfo(deconstruction).Method.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Deconstruct_DelegateTypeProperty() + { + var src = """ +var (x1, y1) = new C1(); + +var (x2, y2) = new C2(); + +class C1 { } + +class C2 +{ + public D Deconstruct => (out int i, out int j) => { i = 42; j = 43; }; +} + +delegate void D(out int i, out int j); + +static class E +{ + extension(C1 c) + { + public D Deconstruct => (out int i, out int j) => { i = 42; j = 43; }; + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit pattern-based deconstruction + comp.VerifyDiagnostics( + // (1,6): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x1'. + // var (x1, y1) = new C1(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x1").WithArguments("x1").WithLocation(1, 6), + // (1,10): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y1'. + // var (x1, y1) = new C1(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y1").WithArguments("y1").WithLocation(1, 10), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : It looks like the following error is not reported for instance scenario. Noise? + + // (1,16): error CS1061: 'C1' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'C1' could be found (are you missing a using directive or an assembly reference?) + // var (x1, y1) = new C1(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "new C1()").WithArguments("C1", "Deconstruct").WithLocation(1, 16), + + // (1,16): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'C1', with 2 out parameters and a void return type. + // var (x1, y1) = new C1(); + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C1()").WithArguments("C1", "2").WithLocation(1, 16), + // (3,6): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x2'. + // var (x2, y2) = new C2(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x2").WithArguments("x2").WithLocation(3, 6), + // (3,10): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y2'. + // var (x2, y2) = new C2(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y2").WithArguments("y2").WithLocation(3, 10), + // (3,16): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'C2', with 2 out parameters and a void return type. + // var (x2, y2) = new C2(); + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C2()").WithArguments("C2", "2").WithLocation(3, 16) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var deconstruction = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Null(model.GetDeconstructionInfo(deconstruction).Method); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Deconstruct_DynamicProperty() + { + var src = """ +var (x, y) = new C(); + +class C { } + +static class E +{ + extension(C c) + { + public dynamic Deconstruct => throw null; + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit pattern-based deconstruction + comp.VerifyEmitDiagnostics( + // (1,6): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'x'. + // var (x, y) = new C(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "x").WithArguments("x").WithLocation(1, 6), + // (1,9): error CS8130: Cannot infer the type of implicitly-typed deconstruction variable 'y'. + // var (x, y) = new C(); + Diagnostic(ErrorCode.ERR_TypeInferenceFailedForImplicitlyTypedDeconstructionVariable, "y").WithArguments("y").WithLocation(1, 9), + // (1,14): error CS1061: 'C' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // var (x, y) = new C(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "new C()").WithArguments("C", "Deconstruct").WithLocation(1, 14), + // (1,14): error CS8129: No suitable 'Deconstruct' instance or extension method was found for type 'C', with 2 out parameters and a void return type. + // var (x, y) = new C(); + Diagnostic(ErrorCode.ERR_MissingDeconstruct, "new C()").WithArguments("C", "2").WithLocation(1, 14) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var deconstruction = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Null(model.GetDeconstructionInfo(deconstruction).Method); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Deconstruct_NoApplicableMethod() + { + var src = """ +var (x, y) = new C(); +System.Console.Write((x, y)); + +class C +{ + public void Deconstruct() { } // not applicable +} + +static class E +{ + extension(C c) + { + public void Deconstruct(out int i, out int j) { i = 42; j = 43; } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based deconstruction + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var deconstruction = tree.GetRoot().DescendantNodes().OfType().First(); + + Assert.Equal("void E.<>E__0.Deconstruct(out System.Int32 i, out System.Int32 j)", + model.GetDeconstructionInfo(deconstruction).Method.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Dispose_Async_NoMethod() + { + var src = """ +using System.Threading.Tasks; + +/**/ +await using var x1 = new C1(); +/**/ + +await using var x2 = new C2(); + +class C1 { } +class C2 { } + +static class E +{ + extension(C1 c) + { + public async Task DisposeAsync() + { + System.Console.Write("RAN"); + await Task.Yield(); + } + } + + public static async Task DisposeAsync(this C2 c) + { + System.Console.Write("RAN"); + await Task.Yield(); + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based disposal + + var expectedDiagnostics = new[] { + // (4,1): error CS8410: 'C1': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using var x1 = new C1(); + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "await using var x1 = new C1();").WithArguments("C1").WithLocation(4, 1), + // (7,1): error CS8410: 'C2': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using var x2 = new C2(); + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "await using var x2 = new C2();").WithArguments("C2").WithLocation(7, 1) + }; + + comp.VerifyDiagnostics(expectedDiagnostics); + + string expectedOperationTree = """ +IUsingDeclarationOperation(IsAsynchronous: True) (OperationKind.UsingDeclaration, Type: null, IsInvalid) (Syntax: 'await using ... = new C1();') + DeclarationGroup: + IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid, IsImplicit) (Syntax: 'await using ... = new C1();') + IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'var x1 = new C1()') + Declarators: + IVariableDeclaratorOperation (Symbol: C1 x1) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x1 = new C1()') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= new C1()') + IObjectCreationOperation (Constructor: C1..ctor()) (OperationKind.ObjectCreation, Type: C1, IsInvalid) (Syntax: 'new C1()') + Arguments(0) + Initializer: + null + Initializer: + null +"""; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void TestPatternBasedDisposal_ExtensionMethod() + { + string source = @" +public class C +{ + public static async System.Threading.Tasks.Task Main() + { + await using (var x = new C()) + { + } + + return 1; + } +} +public static class Extensions +{ + extension (C c) + { + public System.Threading.Tasks.ValueTask DisposeAsync() + => throw null; + } +} +"; + // extension methods do not contribute to pattern-based disposal + var comp = CreateCompilationWithTasksExtensions(new[] { source, IAsyncDisposableDefinition }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // 0.cs(6,22): error CS8410: 'C': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using (var x = new C()) + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "var x = new C()").WithArguments("C").WithLocation(6, 22) + ); + } + + [Fact] + public void PatternBased_Dispose_Async_DelegateTypeProperty() + { + var src = """ +using System.Threading.Tasks; + +await using var x = new C(); + +class C +{ + public System.Func DisposeAsync => async () => { System.Console.Write("ran2"); await Task.Yield(); }; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,1): error CS8410: 'C': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using var x = new C(); + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "await using var x = new C();").WithArguments("C").WithLocation(3, 1) + ); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Dispose_Async_DelegateTypeProperty() + { + var src = """ +using System.Threading.Tasks; + +await using var x = new C(); + +class C { } + +static class E +{ + extension(C c) + { + public System.Func DisposeAsync => async () => { System.Console.Write("ran2"); await Task.Yield(); }; + } +} +"""; + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 :(instance) confirm when spec'ing pattern-based disposal + comp.VerifyEmitDiagnostics( + // (3,1): error CS8410: 'C': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using var x = new C(); + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "await using var x = new C();").WithArguments("C").WithLocation(3, 1) + ); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Dispose_Async_NoApplicableMethod() + { + var src = """ +using System.Threading.Tasks; + +/**/ +await using var x = new C(); +/**/ + +class C +{ + public Task DisposeAsync(int notApplicable) => throw null; // not applicable +} + +static class E +{ + extension(C c) + { + public async Task DisposeAsync() + { + System.Console.Write("RAN"); + await Task.Yield(); + } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based disposal + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (4,1): error CS8410: 'C': type used in an asynchronous using statement must implement 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. + // await using var x = new C(); + Diagnostic(ErrorCode.ERR_NoConvToIAsyncDisp, "await using var x = new C();").WithArguments("C").WithLocation(4, 1) + ); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : verify IOperation + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Dispose_RefStruct() + { + var src = """ +using var x1 = new S1(); +using var x2 = new S2(); + +ref struct S1 { } + +ref struct S2 +{ +} + +static class E +{ + extension(S1 s) + { + public void Dispose() { } + } + + public static void Dispose(this S2 s) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,1): error CS1674: 'S1': type used in a using statement must implement 'System.IDisposable'. + // using var x1 = new S1(); + Diagnostic(ErrorCode.ERR_NoConvToIDisp, "using var x1 = new S1();").WithArguments("S1").WithLocation(1, 1), + // (2,1): error CS1674: 'S2': type used in a using statement must implement 'System.IDisposable'. + // using var x2 = new S2(); + Diagnostic(ErrorCode.ERR_NoConvToIDisp, "using var x2 = new S2();").WithArguments("S2").WithLocation(2, 1) + ); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Fixed_NoMethod() + { + var text = """ +unsafe class C +{ + public static void Main() + { + fixed (int* p = new Fixable()) + { + System.Console.WriteLine(p[1]); + } + } +} + +class Fixable { } + +static class E +{ + extension(Fixable f) + { + public ref int GetPinnableReference() { System.Console.Write("pin "); return ref (new int[] { 1, 2, 3 })[0]; } + } +} +"""; + var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseExe); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based fixed + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Fixed_NoMethod_DelegateTypeProperty() + { + var text = @" +unsafe class C +{ + public static void Main() + { + fixed (int* p = new Fixable1()) + { + System.Console.WriteLine(p[1]); + } + + fixed (int* p = new Fixable2()) + { + System.Console.WriteLine(p[1]); + } + } +} + +class Fixable1 { } + +class Fixable2 +{ + public MyDelegate GetPinnableReference => throw null; +} + +delegate ref int MyDelegate(); + +static class E +{ + extension(Fixable1 f) + { + public MyDelegate GetPinnableReference => throw null; + } +} +"; + var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseExe); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based fixed + comp.VerifyEmitDiagnostics( + // (6,25): error CS8385: The given expression cannot be used in a fixed statement + // fixed (int* p = new Fixable1()) + Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable1()").WithLocation(6, 25), + // (11,25): error CS8385: The given expression cannot be used in a fixed statement + // fixed (int* p = new Fixable2()) + Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable2()").WithLocation(11, 25) + ); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Fixed_NoApplicableMethod() + { + var src = """ +unsafe class C +{ + public static void Main() + { + fixed (int* p = new Fixable()) + { + System.Console.WriteLine(p[1]); + } + } +} + +class Fixable +{ + public ref int GetPinnableReference(int notApplicable) => throw null; // not applicable +} + +static class E +{ + extension(Fixable f) + { + public ref int GetPinnableReference() { return ref (new int[] { 1, 2, 3 })[0]; } + } +} +"""; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based fixed + var comp = CreateCompilation(src, options: TestOptions.UnsafeReleaseExe); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : verify IOperation + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Fixed_Static() + { + var text = @" +unsafe class C +{ + public static void Main() + { + fixed (int* p = new Fixable()) + { + } + } +} + +class Fixable { } + +static class E +{ + extension(Fixable f) + { + public static ref int GetPinnableReference() => throw null; + } +} +"; + + var comp = CreateCompilation(text, options: TestOptions.UnsafeReleaseExe); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based fixed + comp.VerifyEmitDiagnostics( + // (6,25): error CS0176: Member 'E.extension(Fixable).GetPinnableReference()' cannot be accessed with an instance reference; qualify it with a type name instead + // fixed (int* p = new Fixable()) + Diagnostic(ErrorCode.ERR_ObjectProhibited, "new Fixable()").WithArguments("E.extension(Fixable).GetPinnableReference()").WithLocation(6, 25), + // (6,25): error CS8385: The given expression cannot be used in a fixed statement + // fixed (int* p = new Fixable()) + Diagnostic(ErrorCode.ERR_ExprCannotBeFixed, "new Fixable()").WithLocation(6, 25)); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Await_ExtensionIsCompleted() + { + var text = @" +using System; +using System.Runtime.CompilerServices; + +int i = await new C(); +System.Console.Write(i); + +class C +{ + public D GetAwaiter() => new D(); +} + +class D : INotifyCompletion +{ + public int GetResult() => 42; + public void OnCompleted(Action continuation) => throw null; +} + +static class E +{ + extension(D d) + { + public bool IsCompleted => true; + } +} +"; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based await + var comp = CreateCompilation(text); + comp.VerifyEmitDiagnostics( + // (5,9): error CS0117: 'D' does not contain a definition for 'IsCompleted' + // int i = await new C(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "await new C()").WithArguments("D", "IsCompleted").WithLocation(5, 9) + ); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Await_ExtensionGetAwaiter() + { + var text = @" +using System; +using System.Runtime.CompilerServices; + +int i = await new C(); +System.Console.Write(i); + +class C +{ +} + +class D : INotifyCompletion +{ + public int GetResult() => 42; + public void OnCompleted(Action continuation) => throw null; + public bool IsCompleted => true; +} + +static class E +{ + extension(C c) + { + public D GetAwaiter() => new D(); + } +} +"; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based await + var comp = CreateCompilation(text); + comp.VerifyEmitDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_Await_ExtensionGetResult() + { + var text = @" +using System; +using System.Runtime.CompilerServices; + +int i = await new C(); +System.Console.Write(i); + +class C +{ + public D GetAwaiter() => new D(); +} + +class D : INotifyCompletion +{ + public void OnCompleted(Action continuation) => throw null; + public bool IsCompleted => true; +} + +static class E +{ + extension(D d) + { + public int GetResult() => 42; + } +} +"; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based await + var comp = CreateCompilation(text); + + // The error is consistent with classic extension methods + comp.VerifyEmitDiagnostics( + // (5,9): error CS0117: 'D' does not contain a definition for 'GetResult' + // int i = await new C(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "await new C()").WithArguments("D", "GetResult").WithLocation(5, 9) + ); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_IndexIndexer_NoLength() + { + var src = """ +var c = new C(); + +/**/ +_ = c[^1]; +/**/ + +class C +{ + public int this[int i] + { + get { System.Console.Write("indexer "); return 0; } + } +} + +static class E +{ + extension(C c) + { + public int Length + { + get { System.Console.Write("length "); return 42; } + } + } +} +"""; + DiagnosticDescription[] expectedDiagnostics = [ + // (4,7): error CS1503: Argument 1: cannot convert from 'System.Index' to 'int' + // _ = c[^1]; + Diagnostic(ErrorCode.ERR_BadArgType, "^1").WithArguments("1", "System.Index", "int").WithLocation(4, 7)]; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit as part of "implicit indexer access" section + comp.VerifyEmitDiagnostics(expectedDiagnostics); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "length indexer"); + + string expectedOperationTree = """ +ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: '_ = c[^1]') +Left: + IDiscardOperation (Symbol: System.Int32 _) (OperationKind.Discard, Type: System.Int32) (Syntax: '_') +Right: + IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid) (Syntax: 'c[^1]') + Children(2): + ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C) (Syntax: 'c') + IUnaryOperation (UnaryOperatorKind.Hat) (OperationKind.Unary, Type: System.Index, IsInvalid) (Syntax: '^1') + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') +"""; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, expectedDiagnostics, targetFramework: TargetFramework.Net70); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_RangeIndexer_NoMethod() + { + var src = """ +var c = new C(); + +/**/ +_ = c[1..^1]; +/**/ + +class C { } + +static class E +{ + extension(C c) + { + public int Slice(int i, int j) { System.Console.Write("slice "); return 0; } + + public int Length + { + get { System.Console.Write("length "); return 42; } + } + } +} +"""; + + DiagnosticDescription[] expectedDiagnostics = [ + // (4,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = c[1..^1]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(4, 5)]; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit as part of "implicit indexer access" section + comp.VerifyEmitDiagnostics(expectedDiagnostics); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "length slice"); + + string expectedOperationTree = """ +ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: ?, IsInvalid) (Syntax: '_ = c[1..^1]') +Left: + IDiscardOperation (Symbol: ? _) (OperationKind.Discard, Type: ?) (Syntax: '_') +Right: + IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'c[1..^1]') + Children(2): + IRangeOperation (OperationKind.Range, Type: System.Range, IsInvalid) (Syntax: '1..^1') + LeftOperand: + IConversionOperation (TryCast: False, Unchecked) (OperatorMethod: System.Index System.Index.op_Implicit(System.Int32 value)) (OperationKind.Conversion, Type: System.Index, IsInvalid, IsImplicit) (Syntax: '1') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: True) (MethodSymbol: System.Index System.Index.op_Implicit(System.Int32 value)) + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + RightOperand: + IUnaryOperation (UnaryOperatorKind.Hat) (OperationKind.Unary, Type: System.Index, IsInvalid) (Syntax: '^1') + Operand: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsInvalid) (Syntax: '1') + ILocalReferenceOperation: c (OperationKind.LocalReference, Type: C, IsInvalid) (Syntax: 'c') +"""; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, expectedDiagnostics, targetFramework: TargetFramework.Net70); + } + + [Fact] + public void ExtensionMemberLookup_PatternBased_RangeIndexer_NoApplicableMethod() + { + var src = """ +var c = new C(); + +/**/ +_ = c[1..^1]; +/**/ + +class C +{ + public int Slice(int notApplicable) => throw null; // not applicable +} + +static class E +{ + extension(C c) + { + public int Slice(int i, int j) { System.Console.Write("slice "); return 0; } + + public int Length + { + get { System.Console.Write("length "); return 42; } + } + } +} +"""; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : revisit as part of "implicit indexer access" section + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (4,5): error CS0021: Cannot apply indexing with [] to an expression of type 'C' + // _ = c[1..^1]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "c[1..^1]").WithArguments("C").WithLocation(4, 5)); + } + + [Fact] + public void ExtensionMemberLookup_Patterns() + { + var src = """ +var c = new C(); + +_ = c is { Property: 42 }; + +class C { } + +static class E +{ + extension(C c) + { + public int Property + { + get { System.Console.Write("property"); return 42; } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "property").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var nameColon = GetSyntax(tree, "Property:"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; }", model.GetSymbolInfo(nameColon.Name).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_Patterns_ExtendedPropertyPattern() + { + var src = """ +var c = new C(); + +_ = c is { Property.Property2: 43 }; + +class C { } + +static class E1 +{ + extension(C c) + { + public int Property { get { System.Console.Write("property "); return 42; } } + } +} + +static class E2 +{ + extension(int i) + { + public int Property2 { get { System.Console.Write("property2"); return 43; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "property property2").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var expressionColon = GetSyntax(tree, "Property.Property2:"); + Assert.Equal("System.Int32 E2.<>E__0.Property2 { get; }", model.GetSymbolInfo(expressionColon.Expression).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_Patterns_ListPattern_NoInstanceLength() + { + var src = """ +System.Console.Write(new C() is ["hi"]); + +class C +{ + public string this[System.Index i] + { + get { System.Console.Write("indexer "); return "hi"; } + } +} + +static class E +{ + extension(C c) + { + public int Length + { + get { System.Console.Write("length "); return 42; } + } + } +} +"""; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net70); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm that we want extensions to contribute to list-patterns + comp.VerifyEmitDiagnostics( + // (1,33): error CS8985: List patterns may not be used for a value of type 'C'. No suitable 'Length' or 'Count' property was found. + // System.Console.Write(new C() is ["hi"]); + Diagnostic(ErrorCode.ERR_ListPatternRequiresLength, @"[""hi""]").WithArguments("C").WithLocation(1, 33) + ); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "length indexer"); + } + + [ConditionalFact(typeof(NoUsedAssembliesValidation))] // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + public void ExtensionMemberLookup_ObjectInitializer() + { + var src = """ +/**/ +_ = new C() { Property = 42 }; +/**/ + +class C { } + +static class E +{ + extension(C c) + { + public int Property { set { System.Console.Write("property"); } } + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "property"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var assignment = GetSyntax(tree, "Property = 42"); + Assert.Equal("System.Int32 E.<>E__0.Property { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString()); + } + + [ConditionalFact(typeof(NoUsedAssembliesValidation))] // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + public void ExtensionMemberLookup_With() + { + var src = """ +/**/ +_ = new S() with { Property = 42 }; +/**/ + +struct S { } + +static class E +{ + extension(S s) + { + public int Property { set { System.Console.Write("property"); } } + } +} +"""; + + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : need to decide whether extensions apply here + comp.VerifyDiagnostics(); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : metadata is undone + //CompileAndVerify(comp, expectedOutput: "property"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var assignment = GetSyntax(tree, "Property = 42"); + Assert.Equal("System.Int32 E.<>E__0.Property { set; }", model.GetSymbolInfo(assignment.Left).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ExtensionMemberLookup_CollectionInitializer_NoMethod() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +/**/ +_ = new C() { 42 }; +/**/ + +class C : IEnumerable, IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +static class E +{ + extension(C c) + { + public void Add(int i) { System.Console.Write("add"); } + } +} +"""; + + var comp = CreateCompilation(src); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based collection initializer + CompileAndVerify(comp, expectedOutput: "add").VerifyDiagnostics(); + + string expectedOperationTree = """ +ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() { 42 }') +Left: + IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') +Right: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { 42 }') + Arguments(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C) (Syntax: '{ 42 }') + Initializers(1): + IInvocationOperation ( void E.<>E__0.Add(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '42') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'C') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) +"""; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, expectedDiagnostics); + } + + [Fact] + public void ExtensionMemberLookup_CollectionInitializer_NoApplicableMethod() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class Program +{ + static void Main() + { + /**/ + _ = new C() { 42 }; + /**/ + } +} + +class C : IEnumerable, IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(string notApplicable) => throw null; +} + +static class E +{ + extension(object o) + { + public void Add(int i) { System.Console.Write("add"); } + } +} +"""; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based collection initializer + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "add").VerifyDiagnostics(); + + string expectedOperationTree = """ +ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() { 42 }') + Left: + IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') + Right: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { 42 }') + Arguments(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: C) (Syntax: '{ 42 }') + Initializers(1): + IInvocationOperation ( void E.<>E__0.Add(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '42') + Instance Receiver: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'C') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + Operand: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: C, IsImplicit) (Syntax: 'C') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) +"""; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, expectedDiagnostics); + + VerifyFlowGraph(comp, comp.SyntaxTrees.Single().GetRoot().DescendantNodes().OfType().First(), """ +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (3) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C() { 42 }') + Value: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C() { 42 }') + Arguments(0) + Initializer: + null + IInvocationOperation ( void E.<>E__0.Add(System.Int32 i)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '42') + Instance Receiver: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'C') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) + (ImplicitReference) + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { 42 }') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: i) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '42') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '_ = new C() { 42 };') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: C) (Syntax: '_ = new C() { 42 }') + Left: + IDiscardOperation (Symbol: C _) (OperationKind.Discard, Type: C) (Syntax: '_') + Right: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'new C() { 42 }') + Next (Regular) Block[B2] + Leaving: {R1} +} +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"""); + } + + [Fact] + public void ExtensionMemberLookup_CollectionInitializer_NoApplicableMethod_ExpressionTree() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +try +{ + System.Linq.Expressions.Expression> e = () => new C() { 42 }; + System.Console.Write(e); +} +catch (System.ArgumentException ae) +{ + System.Console.Write(ae.Message); +} + +class C : IEnumerable, IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(string notApplicable) => throw null; +} + +static class E +{ + extension(object o) + { + public void Add(int i) { System.Console.Write("add"); } + } +} +"""; + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm when spec'ing pattern-based collection initializer + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : expression trees + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (6,76): error CS8075: An extension Add method is not supported for a collection initializer in an expression lambda. + // System.Linq.Expressions.Expression> e = () => new C() { 42 }; + Diagnostic(ErrorCode.ERR_ExtensionCollectionElementInitializerInExpressionTree, "42").WithLocation(6, 76) + ); + } + + [Fact] + public void ExtensionMemberLookup_Query_NoMethod() + { + var src = """ +/**/ +string query = from x in new C() select x; +/**/ + +System.Console.Write(query); + +class C { } + +static class E +{ + extension(C c) + { + public string Select(System.Func selector) => "hello"; + } +} +"""; + + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hello").VerifyDiagnostics(); + + string expectedOperationTree = """ +IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null) (Syntax: 'string quer ... ) select x;') + IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'string quer ... () select x') + Declarators: + IVariableDeclaratorOperation (Symbol: System.String query) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'query = fro ... () select x') + Initializer: + IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= from x in ... () select x') + ITranslatedQueryOperation (OperationKind.TranslatedQuery, Type: System.String) (Syntax: 'from x in n ... () select x') + Expression: + IInvocationOperation ( System.String E.<>E__0.Select(System.Func selector)) (OperationKind.Invocation, Type: System.String, IsImplicit) (Syntax: 'select x') + Instance Receiver: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') + Arguments(0) + Initializer: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: selector) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'x') + IDelegateCreationOperation (OperationKind.DelegateCreation, Type: System.Func, IsImplicit) (Syntax: 'x') + Target: + IAnonymousFunctionOperation (Symbol: lambda expression) (OperationKind.AnonymousFunction, Type: null, IsImplicit) (Syntax: 'x') + IBlockOperation (1 statements) (OperationKind.Block, Type: null, IsImplicit) (Syntax: 'x') + IReturnOperation (OperationKind.Return, Type: null, IsImplicit) (Syntax: 'x') + ReturnedValue: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C) (Syntax: 'x') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Initializer: + null +"""; + + VerifyOperationTreeAndDiagnosticsForTest(src, expectedOperationTree, DiagnosticDescription.None); + } + + [Fact] + public void ExtensionMemberLookup_Query_NoApplicableMethod() + { + var src = """ +/**/ +string query = from x in new C() select x; +/**/ + +System.Console.Write(query); + +class C +{ + public string Select(int notApplicable) => throw null; // not applicable +} + +static class E +{ + extension(C c) + { + public string Select(System.Func selector) => "hello"; + } +} +"""; + + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "hello").VerifyDiagnostics(); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : verify IOperation + } + + [Fact] + public void ExtensionMemberLookup_Invocation_ZeroArityMatchesAny() + { + var source = $$""" +object.Method(""); +object.Method(""); + +static class E +{ + extension(object) + { + public static void Method(int i) => throw null; + public static void Method(T t) { System.Console.Write("Method "); } + public static void Method(T1 t1, T2 t2) => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "Method Method").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, """object.Method("")"""); + Assert.Equal("void E.<>E__0.Method(System.String t)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Empty(model.GetMemberGroup(invocation)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : need to fix the semantic model + } + + [Fact] + public void StaticPropertyAccess_ZeroArityMatchesAny() + { + var source = """ +int i = object.P; + +static class E1 +{ + extension(object) + { + public static int P => 42; + } +} + +static class E2 +{ + extension(object) + { + public static void P() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,9): error CS9286: 'object' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // int i = object.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.P").WithArguments("object", "P").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.P"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Int32 E1.<>E__0.P { get; }", "void E2.<>E__0.P()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void StaticPropertyAccess_NonZeroArity() + { + var source = """ +int i = object.P; + +static class E1 +{ + extension(object) + { + public static int P => 42; + } +} + +static class E2 +{ + extension(object) + { + public static void P() => throw null; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,16): error CS0428: Cannot convert method group 'P' to non-delegate type 'int'. Did you intend to invoke the method? + // int i = object.P; + Diagnostic(ErrorCode.ERR_MethGrpToNonDel, "P").WithArguments("P", "int").WithLocation(1, 16)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.P"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E2.<>E__0.P()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void StaticMethodAccess_NonZeroArity() + { + var source = """ +object.M(); + +static class E1 +{ + extension(object) + { + public static void M() { } + public static void M() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M' + // object.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("object", "M").WithLocation(1, 8)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E1.<>E__0.M()", "void E1.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E1.<>E__0.M()", "void E1.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void AddressOf_TypeReceiver() + { + var src = """ +unsafe class C +{ + static void Main() + { + delegate* ptr = &C.M; + ptr("ran", null); + } +} + +static class E +{ + extension(C) + { + public static void M(string s, object o) { System.Console.Write(s); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp, expectedOutput: "ran", verify: Verification.Fails with { ILVerifyMessage = "[Main]: ImportCalli not implemented" }); + verifier.VerifyIL("C.Main", """ +{ + // Code size 24 (0x18) + .maxstack 3 + .locals init (delegate* V_0, //ptr + delegate* V_1) + IL_0000: nop + IL_0001: ldftn "void E.M(string, object)" + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: stloc.1 + IL_000a: ldstr "ran" + IL_000f: ldnull + IL_0010: ldloc.1 + IL_0011: calli "delegate*" + IL_0016: nop + IL_0017: ret +} +"""); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal("void E.<>E__0.M(System.String s, System.Object o)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void AddressOf_InstanceReceiver() + { + var src = """ +unsafe class C +{ + static void Main() + { + C c = new C(); + delegate* ptr = &c.M; + ptr("ran", null); + + delegate* ptr2 = &c.M2; + } +} + +static class E +{ + extension(C) + { + public void M(string s, object o) { System.Console.Write(s); } + } + public static void M2(this C c, string s, object o) { System.Console.Write(s); } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe); + comp.VerifyEmitDiagnostics( + // (6,48): error CS8759: Cannot create a function pointer for 'E.extension(C).M(string, object)' because it is not a static method + // delegate* ptr = &c.M; + Diagnostic(ErrorCode.ERR_FuncPtrMethMustBeStatic, "c.M").WithArguments("E.extension(C).M(string, object)").WithLocation(6, 48), + // (9,48): error CS8788: Cannot use an extension method with a receiver as the target of a '&' operator. + // delegate* ptr2 = &c.M2; + Diagnostic(ErrorCode.ERR_CannotUseReducedExtensionMethodInAddressOf, "&c.M2").WithLocation(9, 48)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "c.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void AddressOf_AmbiguousBestMethod() + { + var src = """ +unsafe class C +{ + static void M1() + { + delegate* ptr = &C.M; + } +} + +static class E +{ + extension(C) + { + public static void M(string s, object o) {} + public static void M(object o, string s) {} + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (5,48): error CS0121: The call is ambiguous between the following methods or properties: 'E.extension(C).M(string, object)' and 'E.extension(C).M(object, string)' + // delegate* ptr = &C.M; + Diagnostic(ErrorCode.ERR_AmbigCall, "C.M").WithArguments("E.extension(C).M(string, object)", "E.extension(C).M(object, string)").WithLocation(5, 48)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E.<>E__0.M(System.String s, System.Object o)", "void E.<>E__0.M(System.Object o, System.String s)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void AddressOf_TypeReceiver_UnmanagedCallersOnly_01() + { + var src = """ +unsafe class C +{ + static void Main() + { + delegate* ptr = &C.M; + delegate* ptr2 = &E.M; + } +} + +static class E +{ + extension(C) + { + [System.Runtime.InteropServices.UnmanagedCallersOnly] + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (5,32): error CS8786: Calling convention of 'E.extension(C).M()' is not compatible with 'Default'. + // delegate* ptr = &C.M; + Diagnostic(ErrorCode.ERR_WrongFuncPtrCallingConvention, "C.M").WithArguments("E.extension(C).M()", "Default").WithLocation(5, 32), + // (6,33): error CS8786: Calling convention of 'E.M()' is not compatible with 'Default'. + // delegate* ptr2 = &E.M; + Diagnostic(ErrorCode.ERR_WrongFuncPtrCallingConvention, "E.M").WithArguments("E.M()", "Default").WithLocation(6, 33)); + } + + [Fact] + public void AddressOf_TypeReceiver_UnmanagedCallersOnly_02() + { + var src = """ +unsafe class C +{ + static void Main() + { + delegate* unmanaged ptr = &C.M; + delegate* unmanaged ptr2 = &E.M; + } +} + +static class E +{ + extension(C) + { + [System.Runtime.InteropServices.UnmanagedCallersOnly] + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugExe, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void TwoExtensions_MethodAndProperty() + { + var src = """ +System.Console.Write(object.M()); + +static class E1 +{ + extension(object) + { + public static string M() => throw null; + } +} + +static class E2 +{ + extension(object) + { + public static System.Func M => null; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (1,22): error CS9286: 'object' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(object.M()); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.M").WithArguments("object", "M").WithLocation(1, 22)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + + Assert.Equal(["System.String E1.<>E__0.M()", "System.Func E2.<>E__0.M { get; }"], + model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + + Assert.Empty(model.GetMemberGroup(memberAccess)); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider handling BoundBadExpression better + } + + [Fact] + public void Nameof_Static_Method() + { + var src = """ +System.Console.Write(nameof(C.Method)); + +class C { } + +static class E +{ + extension(C) + { + public static string Method() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,29): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write(nameof(C.Method)); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "C.Method").WithLocation(1, 29)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Method"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.String E.<>E__0.Method()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void Nameof_Instance_Method() + { + var src = """ +C c = null; +_ = nameof(c.Method); + +class C { } + +static class E +{ + extension(C) + { + public string Method() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,12): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // _ = nameof(c.Method); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "c.Method").WithLocation(2, 12)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "c.Method"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + } + + [Fact] + public void Nameof_Static_Property() + { + var src = """ +System.Console.Write(nameof(C.Property)); + +class C { } + +static class E +{ + extension(C) + { + public static string Property => throw null; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "Property").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Property"); + Assert.Equal("System.String E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void Nameof_Static_WrongArityMethod() + { + var src = """ +System.Console.Write(nameof(C.Method)); + +class C { } + +static class E +{ + extension(C) + { + public static string Method() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,29): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write(nameof(C.Method)); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "C.Method").WithLocation(1, 29)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Method"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.String E.<>E__0.Method()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void Nameof_Instance_Property() + { + var src = """ +C c = null; +System.Console.Write(nameof(c.Property)); + +class C { } + +static class E +{ + extension(C) + { + public string Property => "Property"; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : should we get an error as with methods? + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "Property").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "c.Property"); + Assert.Equal("System.String E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void Nameof_Static_Property_Generic_01() + { + var src = """ +System.Console.Write(nameof(C.Property)); + +class C : I { } +interface I { } + +static class E +{ + extension(I i) + { + public static string Property => throw null; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "Property").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Property"); + Assert.Equal("System.String E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void Nameof_Static_Property_Generic_02() + { + var src = """ +System.Console.Write(nameof(C.Property)); + +class C : I, I { } +interface I { } + +static class E +{ + extension(I i) + { + public static string Property => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,29): error CS9286: 'C' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type 'C' could be found (are you missing a using directive or an assembly reference?) + // System.Console.Write(nameof(C.Property)); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "C.Property").WithArguments("C", "Property").WithLocation(1, 29)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.Property"); + Assert.Equal("System.String E.<>E__0.Property { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void Nameof_Overloads_01() + { + var src = """ +System.Console.Write($"{nameof(object.M)} "); + +static class E +{ + extension(object) + { + public static void M() { } + public static void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,32): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write($"{nameof(object.M)} "); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "object.M").WithLocation(1, 32)); + } + + [Fact] + public void Nameof_Overloads_02() + { + var src = """ +System.Console.Write($"{nameof(object.M)} "); + +static class E1 +{ + extension(T) where T : class + { + public static void M() { } + } +} + +static class E2 +{ + extension(T) where T : struct + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,32): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // System.Console.Write($"{nameof(object.M)} "); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "object.M").WithLocation(1, 32)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E1.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E1.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void Nameof_SimpleName() + { + var src = """ +class C +{ + void M() + { + _ = nameof(Method); + _ = nameof(Property); + } +} + +static class E +{ + extension(object) + { + public static void Method() { } + public static int Property => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (5,20): error CS0103: The name 'Method' does not exist in the current context + // _ = nameof(Method); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Method").WithArguments("Method").WithLocation(5, 20), + // (6,20): error CS0103: The name 'Property' does not exist in the current context + // _ = nameof(Property); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Property").WithArguments("Property").WithLocation(6, 20)); + } + + [Fact] + public void Nameof_NoParameter() + { + var src = """ +class C +{ + void M() + { + System.Console.Write(nameof()); + } +} + +static class E +{ + extension(C c) + { + public string nameof() => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (5,30): error CS0103: The name 'nameof' does not exist in the current context + // System.Console.Write(nameof()); + Diagnostic(ErrorCode.ERR_NameNotInContext, "nameof").WithArguments("nameof").WithLocation(5, 30)); + } + + [Fact] + public void Nameof_SingleParameter() + { + var src = """ +class C +{ + public static void Main() + { + string x = ""; + System.Console.Write(nameof(x)); + } +} + +static class E +{ + extension(C c) + { + public string nameof(string s) => throw null; + } +} +"""; + + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "x").VerifyDiagnostics(); + } + + [Fact] + public void StaticMethodInvocation_TypeParameter_InNameof() + { + var source = """ +public static class C +{ + static void M() + { + _ = nameof(T.Method); + } + + extension(T) + { + public static void Method() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // 0.cs(5,20): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter + // _ = nameof(T.Method); + Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(5, 20)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "T.Method"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void SymbolInfoForMethodGroup03() + { + var source = """ +public class A { } + +static class E +{ + extension(A a) + { + public string Extension() { return null; } + } +} +public class Program +{ + public static void Main(string[] args) + { + A a = null; + _ = nameof(a.Extension); + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (15,20): error CS8093: Extension method groups are not allowed as an argument to 'nameof'. + // _ = nameof(a.Extension); + Diagnostic(ErrorCode.ERR_NameofExtensionMethod, "a.Extension").WithLocation(15, 20)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "a.Extension"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void StaticMethodInvocation_PartialStaticClass() + { + var source = """ +object.M(); +object.M2(); + +public static partial class C +{ + extension(object) + { + public static void M() { System.Console.Write("ran "); } + } +} + +public static partial class C +{ + extension(object) + { + public static void M2() { System.Console.Write("ran2"); } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran ran2").VerifyDiagnostics(); + } + + [Fact] + public void StaticMethodInvocation_TupleTypeReceiver() + { + var src = """ +(string, string).M(); +(int a, int b).M(); +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider parsing this + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,2): error CS1525: Invalid expression term 'string' + // (string, string).M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(1, 2), + // (1,10): error CS1525: Invalid expression term 'string' + // (string, string).M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(1, 10), + // (2,2): error CS8185: A declaration is not allowed in this context. + // (int a, int b).M(); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(2, 2), + // (2,2): error CS0165: Use of unassigned local variable 'a' + // (int a, int b).M(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int a").WithArguments("a").WithLocation(2, 2), + // (2,9): error CS8185: A declaration is not allowed in this context. + // (int a, int b).M(); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(2, 9), + // (2,9): error CS0165: Use of unassigned local variable 'b' + // (int a, int b).M(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int b").WithArguments("b").WithLocation(2, 9), + // (2,16): error CS1061: '(int a, int b)' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type '(int a, int b)' could be found (are you missing a using directive or an assembly reference?) + // (int a, int b).M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("(int a, int b)", "M").WithLocation(2, 16)); + } + + [Fact] + public void StaticMethodInvocation_TupleTypeReceiver_02() + { + var src = """ +((string, string)).M(); +((int a, int b)).M(); +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider parsing this + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,3): error CS1525: Invalid expression term 'string' + // ((string, string)).M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(1, 3), + // (1,11): error CS1525: Invalid expression term 'string' + // ((string, string)).M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "string").WithArguments("string").WithLocation(1, 11), + // (2,3): error CS8185: A declaration is not allowed in this context. + // ((int a, int b)).M(); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int a").WithLocation(2, 3), + // (2,3): error CS0165: Use of unassigned local variable 'a' + // ((int a, int b)).M(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int a").WithArguments("a").WithLocation(2, 3), + // (2,10): error CS8185: A declaration is not allowed in this context. + // ((int a, int b)).M(); + Diagnostic(ErrorCode.ERR_DeclarationExpressionNotPermitted, "int b").WithLocation(2, 10), + // (2,10): error CS0165: Use of unassigned local variable 'b' + // ((int a, int b)).M(); + Diagnostic(ErrorCode.ERR_UseDefViolation, "int b").WithArguments("b").WithLocation(2, 10), + // (2,18): error CS1061: '(int a, int b)' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type '(int a, int b)' could be found (are you missing a using directive or an assembly reference?) + // ((int a, int b)).M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("(int a, int b)", "M").WithLocation(2, 18)); + } + + [Fact] + public void StaticMethodInvocation_PointerTypeReceiver() + { + var src = """ +unsafe class C +{ + void M() + { + int*.M(); + delegate*.M(); + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider parsing this + var comp = CreateCompilation(src, options: TestOptions.UnsafeDebugDll); + comp.VerifyDiagnostics( + // (5,13): error CS1001: Identifier expected + // int*.M(); + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(5, 13), + // (5,13): error CS1003: Syntax error, ',' expected + // int*.M(); + Diagnostic(ErrorCode.ERR_SyntaxError, ".").WithArguments(",").WithLocation(5, 13), + // (5,14): error CS1002: ; expected + // int*.M(); + Diagnostic(ErrorCode.ERR_SemicolonExpected, "M").WithLocation(5, 14), + // (6,17): error CS1514: { expected + // delegate*.M(); + Diagnostic(ErrorCode.ERR_LbraceExpected, "*").WithLocation(6, 17), + // (6,17): warning CS8848: Operator '*' cannot be used here due to precedence. Use parentheses to disambiguate. + // delegate*.M(); + Diagnostic(ErrorCode.WRN_PrecedenceInversion, "*").WithArguments("*").WithLocation(6, 17), + // (6,18): error CS1525: Invalid expression term '<' + // delegate*.M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<").WithArguments("<").WithLocation(6, 18), + // (6,19): error CS1525: Invalid expression term 'void' + // delegate*.M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "void").WithArguments("void").WithLocation(6, 19), + // (6,24): error CS1525: Invalid expression term '.' + // delegate*.M(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ".").WithArguments(".").WithLocation(6, 24)); + } + + [Fact] + public void StaticMethodInvocation_DynamicTypeReceiver() + { + var src = """ +dynamic.M(); +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics( + // (1,1): error CS0103: The name 'dynamic' does not exist in the current context + // dynamic.M(); + Diagnostic(ErrorCode.ERR_NameNotInContext, "dynamic").WithArguments("dynamic").WithLocation(1, 1)); + } + + [Fact] + public void DisplayString_Constraint() + { + var source = """ +static class E +{ + extension(T) where T : struct + { + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(extension); + + var format = new SymbolDisplayFormat(genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeTypeConstraints); + Assert.Equal("extension(T) where T : struct", symbol.ToDisplayString(format)); + } + + [Fact] + public void DisplayString_Modifier() + { + var source = """ +static class E +{ + extension(ref readonly int) + { + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + var symbol = model.GetDeclaredSymbol(extension); + + var format = new SymbolDisplayFormat(parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeModifiers); + Assert.Equal("extension(ref readonly Int32)", symbol.ToDisplayString(format)); + } + + [Fact] + public void NameConflict_01_EnclosingStaticTypeNameWithExtensionTypeParameterName() + { + var src = """ +static class Extensions +{ + extension(Extensions) + { + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NameConflict_02_EnclosingStaticTypeNameWithReceiverParameterName() + { + var src = """ +static class Extensions +{ + extension(int Extensions) + { + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NameConflict_03_ExtensionTypeParameterNameWithReceiverParameterName() + { + var src = """ +static class Extensions +{ +#line 7 + extension(T[] T) + { + void M1(){} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (7,22): error CS9287: 'T': a receiver parameter cannot have the same name as an extension container type parameter + // extension(T[] T) + Diagnostic(ErrorCode.ERR_ReceiverParameterSameNameAsTypeParameter, "T").WithArguments("T").WithLocation(7, 22) + ); + } + + [Fact] + public void NameConflict_04_ExtensionTypeParameterNameWithMemberParameterName() + { + var src = """ +static class Extensions +{ + extension(T[] p) + { +#line 14 + void M2(int T){} + static void M3(int T){} + int this[int T] => 0; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (14,21): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // void M2(int T){} + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(14, 21), + // (15,28): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // static void M3(int T){} + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(15, 28), + // (16,22): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // int this[int T] => 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(16, 22) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_05_ExtensionTypeParameterNameWithSetterValueParameter(bool isStatic) + { + var src = @" +static class Extensions +{ + extension(value[] p) + { + " + (isStatic ? "static" : "") + @" + int P11 {set{}} + + " + (isStatic ? "static" : "") + @" + int P12 => 0; + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (4,15): warning CS8981: The type name 'value' only contains lower-cased ascii characters. Such names may become reserved for the language. + // extension(value[] p) + Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "value").WithArguments("value").WithLocation(4, 15), + // (7,18): error CS9294: 'value': an automatically-generated parameter name conflicts with an extension type parameter name + // int P11 {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionTypeParameter, "set").WithLocation(7, 18) + ); + } + + [Fact] + public void NameConflict_06_ExtensionTypeParameterNameWithSetterValueParameter() + { + var src = @" +static class Extensions +{ + extension(value[] p) + { + int this[int i] {set{}} + int this[long i] => 0; + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (4,15): warning CS8981: The type name 'value' only contains lower-cased ascii characters. Such names may become reserved for the language. + // extension(value[] p) + Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "value").WithArguments("value").WithLocation(4, 15), + // (6,26): error CS9294: 'value': an automatically-generated parameter name conflicts with an extension type parameter name + // int this[int i] {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionTypeParameter, "set").WithLocation(6, 26) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_07_ExtensionTypeParameterNameWithLocalFunctionParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(T[] p) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @"int local(int T) + { + return T; + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @"int local(int T) + { + return T; + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_08_ExtensionTypeParameterNameWithLambdaParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(T[] p) + { + " + modifier1 + @"void M4() + { + System.Func l = " + modifier2 + @"(int T) => + { + return T; + }; + } + " + modifier1 + @"int P7 + { + set + { + System.Func l = " + modifier2 + @"(int T) => + { + return T; + }; + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_09_ExtensionTypeParameterNameWithLocalName(bool isStatic) + { + var modifier = isStatic ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(T[] p) + { + " + modifier + @"int M4() + { +#line 19 + int T = 0; + return T; + } + " + modifier + @"int M5() + { + int T() => 0; + return T(); + } + " + modifier + @"int P7 + { + get + { + int T = 0; + return T; + } + } + " + modifier + @"int P8 + { + get + { + int T() => 0; + return T(); + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (19,17): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // int T = 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(19, 17), + // (24,17): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // int T() => 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(24, 17), + // (31,21): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // int T = 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(31, 21), + // (39,21): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // int T() => 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(39, 21) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_10_ExtensionTypeParameterNameWithLocalNameInLocalFunction(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(T[] p) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @"int local() + { + int T = 0; + return T; + } + } + " + modifier1 + @"void M5() + { + " + modifier2 + @"int local() + { + int T() => 0; + return T(); + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @"int local() + { + int T = 0; + return T; + } + } + } + " + modifier1 + @"int P8 + { + set + { + " + modifier2 + @"int local() + { + int T() => 0; + return T(); + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_11_ExtensionTypeParameterNameWithLocalNameInLambda(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(T[] p) + { + " + modifier1 + @"void M4() + { + System.Func l = " + modifier2 + @"() => + { + int T = 0; + return T; + }; + } + " + modifier1 + @"void M5() + { + System.Func l = " + modifier2 + @"() => + { + int T() => 0; + return T(); + }; + } + " + modifier1 + @"int P7 + { + set + { + System.Func l = " + modifier2 + @"() => + { + int T = 0; + return T; + }; + } + } + " + modifier1 + @"int P8 + { + set + { + System.Func l = " + modifier2 + @"() => + { + int T() => 0; + return T(); + }; + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NameConflict_12_ExtensionTypeParameterNameWithAnotherExtensionTypeParameterName() + { + var src = """ +static class Extensions +{ +#line 55 + extension(T[] p) + {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (55,18): error CS0692: Duplicate type parameter 'T' + // extension(T[] p) + Diagnostic(ErrorCode.ERR_DuplicateTypeParameter, "T").WithArguments("T").WithLocation(55, 18), + // (55,21): error CS0229: Ambiguity between 'T' and 'T' + // extension(T[] p) + Diagnostic(ErrorCode.ERR_AmbigMember, "T").WithArguments("T", "T").WithLocation(55, 21), + // (55,25): error CS9295: The extended type 'T[]' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(T[] p) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "p").WithArguments("T[]", "T").WithLocation(55, 25), + // (55,25): error CS9295: The extended type 'T[]' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(T[] p) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "p").WithArguments("T[]", "T").WithLocation(55, 25) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_13_ExtensionTypeParameterNameWithMemberTypeParameterName(bool isStatic) + { + var modifier = isStatic ? "static" : ""; + + var src = @" +static class Extensions +{ + extension(T[] p) + { + " + modifier + @" +#line 60 + void M9(){} + + " + modifier + @" +#line 61 + void M10(){} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (60,17): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // void M9(){} + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(60, 17), + // (61,18): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // void M10(){} + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(61, 18), + // (61,21): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // void M10(){} + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(61, 21) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_14_ExtensionTypeParameterNameWithLocalFunctionTypeParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static" : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(T[] p) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + " + modifier1 + @"void M5() + { + void local2() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + " + modifier1 + @"int P8 + { + set + { + void local2() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : We might need to add a new warning if we don't want to refer to extension as a type in diagnostics + + comp.VerifyEmitDiagnostics( + // (11,21): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Extensions.extension(T[])' + // T local(T p1) + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Extensions.extension(T[])").WithLocation(11, 21), + // (21,25): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Extensions.extension(T[])' + // T local(T p1) + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Extensions.extension(T[])").WithLocation(21, 25), + // (32,25): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Extensions.extension(T[])' + // T local(T p1) + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Extensions.extension(T[])").WithLocation(32, 25), + // (45,29): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Extensions.extension(T[])' + // T local(T p1) + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Extensions.extension(T[])").WithLocation(45, 29) + ); + } + + [Fact] + public void NameConflict_15_ExtensionTypeParameterNameWithMemberName() + { + var src = """ +static class Extensions +{ + extension(C1 p) + { + int T() + { + return T; + } + } + + extension(C2 p) + { + int T => T; + } + + extension(C3 p) + { + [System.Runtime.CompilerServices.IndexerName("T")] + int this[int x] => T; + } + + extension(C4 p) + { + static int T() + { + return T; + } + } + + extension(C5 p) + { + static int T => T; + } + + extension(C6 p) + { + int P => 0; + } + + extension(C7 p) + { + [System.Runtime.CompilerServices.IndexerName("Indexer")] + int this[int x] => 0; + } + + extension(C8 p) + { + int this[int x] => 0; + } +} + +class C1 {} +class C2 {} +class C3 {} +class C4 {} +class C5 {} +class C6 {} +class C7 {} +class C8 {} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,20): error CS0119: 'T' is a type, which is not valid in the given context + // return T; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(7, 20), + // (13,18): error CS0119: 'T' is a type, which is not valid in the given context + // int T => T; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(13, 18), + // (19,28): error CS0119: 'T' is a type, which is not valid in the given context + // int this[int x] => T; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(19, 28), + // (26,20): error CS0119: 'T' is a type, which is not valid in the given context + // return T; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(26, 20), + // (32,25): error CS0119: 'T' is a type, which is not valid in the given context + // static int T => T; + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(32, 25) + ); + } + + [Fact] + public void NameConflict_16_ReceiverParameterNameWithMemberName() + { + var src = """ +static class Extensions +{ + extension(int M1) + { + void M1() + { + int x = M1; + x++; + } + } + + extension(long P1) + { + int P1 + { + get + { + P1 = long.MaxValue; + return 0; + } + } + } + + extension(byte Indexer) + { + [System.Runtime.CompilerServices.IndexerName("Indexer")] + int this[int y] + { + get + { + byte x = Indexer; + x++; + return 0; + } + } + } + + extension(short M1) + { + static void M1() + { + short x = M1; + x++; + } + } + + extension(string P1) + { + static int P1 + { + get + { + P1 = "val"; + return 0; + } + } + } + + extension(int[] get_P) + { + int P => 0; + } + + extension(long[] get_Indexer) + { + [System.Runtime.CompilerServices.IndexerName("Indexer")] + int this[int x] => 0; + } + + extension(byte[] get_Item) + { + int this[int x] => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (42,23): error CS9293: Cannot use extension parameter 'short M1' in this context. + // short x = M1; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "M1").WithArguments("short M1").WithLocation(42, 23), + // (53,17): error CS9293: Cannot use extension parameter 'string P1' in this context. + // P1 = "val"; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "P1").WithArguments("string P1").WithLocation(53, 17) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_17_ReceiverParameterNameWithMemberTypeParameterName(bool isStatic) + { + var modifier = isStatic ? "static" : ""; + + var src = @" +static class Extensions +{ + extension(int T) + { + " + modifier + @" +#line 5 + void M1(){} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,17): error CS9292: Type parameter 'T' has the same name as an extension parameter + // void M1(){} + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionParameter, "T").WithArguments("T").WithLocation(5, 17) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_18_ReceiverParameterNameWithLocalFunctionTypeParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static" : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(int T) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + " + modifier1 + @"void M5() + { + void local2() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + " + modifier1 + @"int P8 + { + set + { + void local2() + { + " + modifier2 + @" + T local(T p1) + { + return p1; + } + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NameConflict_19_ReceiverParameterNameWithMemberParameterName() + { + var src = """ +static class Extensions +{ + extension(int p) + { + void M2(int p){} + static void M3(int p){} + int this[int p] => 0; + void M3(int p2, int p2) {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,21): error CS9290: 'p': a parameter, local variable, or local function cannot have the same name as an extension parameter + // void M2(int p){} + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "p").WithArguments("p").WithLocation(5, 21), + // (6,28): error CS9290: 'p': a parameter, local variable, or local function cannot have the same name as an extension parameter + // static void M3(int p){} + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "p").WithArguments("p").WithLocation(6, 28), + // (7,22): error CS9290: 'p': a parameter, local variable, or local function cannot have the same name as an extension parameter + // int this[int p] => 0; + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "p").WithArguments("p").WithLocation(7, 22), + // (8,29): error CS0100: The parameter name 'p2' is a duplicate + // void M3(int p2, int p2) {} + Diagnostic(ErrorCode.ERR_DuplicateParamName, "p2").WithArguments("p2").WithLocation(8, 29) + ); + } + + [Fact] + public void NameConflict_20_ReceiverParameterNameWithSetterValueParameter() + { + var src = """ +static class Extensions +{ + extension(int value) + { + int P1 {get=>0;} + int P2 {set{}} + int this[int x] {get=>0;} + int this[long x] {set{}} + int this[long x, int value] {set{}} + static int P6 {get=>0;} + static int P7 {set{}} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (6,17): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name + // int P2 {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(6, 17), + // (8,27): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name + // int this[long x] {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(8, 27), + // (9,30): error CS9290: 'value': a parameter, local variable, or local function cannot have the same name as an extension parameter + // int this[long x, int value] {set{}} + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "value").WithArguments("value").WithLocation(9, 30), + // (9,30): error CS0316: The parameter name 'value' conflicts with an automatically-generated parameter name + // int this[long x, int value] {set{}} + Diagnostic(ErrorCode.ERR_DuplicateGeneratedName, "value").WithArguments("value").WithLocation(9, 30), + // (9,38): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name + // int this[long x, int value] {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(9, 38), + // (11,24): error CS9291: 'value': an automatically-generated parameter name conflicts with an extension parameter name + // static int P7 {set{}} + Diagnostic(ErrorCode.ERR_ValueParameterSameNameAsExtensionParameter, "set").WithLocation(11, 24) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_21_ReceiverParameterNameWithLocalFunctionParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(string p) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @"int local(int p) + { + return p; + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @"int local(int p) + { + return p; + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_22_ReceiverParameterNameWithLambdaParameterName(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(string p) + { + " + modifier1 + @"void M4() + { + System.Func l = " + modifier2 + @"(int p) => + { + return p; + }; + } + " + modifier1 + @"int P7 + { + set + { + System.Func l = " + modifier2 + @"(int p) => + { + return p; + }; + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_23_ReceiverParameterNameWithLocalName(bool isStatic) + { + var modifier = isStatic ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + " + modifier + @"int M4() + { +#line 7 + int p = 0; + return p; + } + " + modifier + @"int M5() + { + int p() => 0; + return p(); + } + " + modifier + @"int P7 + { + get + { + int p = 0; + return p; + } + } + " + modifier + @"int P8 + { + get + { + int p() => 0; + return p(); + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (7,17): error CS0136: A local or parameter named 'p' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // int p = 0; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "p").WithArguments("p").WithLocation(7, 17), + // (12,17): error CS0136: A local or parameter named 'p' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // int p() => 0; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "p").WithArguments("p").WithLocation(12, 17), + // (19,21): error CS0136: A local or parameter named 'p' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // int p = 0; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "p").WithArguments("p").WithLocation(19, 21), + // (27,21): error CS0136: A local or parameter named 'p' cannot be declared in this scope because that name is used in an enclosing local scope to define a local or parameter + // int p() => 0; + Diagnostic(ErrorCode.ERR_LocalIllegallyOverrides, "p").WithArguments("p").WithLocation(27, 21) + ); + } + + [Theory] + [CombinatorialData] + public void NameConflict_24_ReceiverParameterNameWithLocalNameInLocalFunction(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +#pragma warning disable CS8321 // The local function 'local' is declared but never used + +static class Extensions +{ + extension(string p) + { + " + modifier1 + @"void M4() + { + " + modifier2 + @"int local() + { + int p = 0; + return p; + } + } + " + modifier1 + @"void M5() + { + " + modifier2 + @"int local() + { + int p() => 0; + return p(); + } + } + " + modifier1 + @"int P7 + { + set + { + " + modifier2 + @"int local() + { + int p = 0; + return p; + } + } + } + " + modifier1 + @"int P8 + { + set + { + " + modifier2 + @"int local() + { + int p() => 0; + return p(); + } + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void NameConflict_25_ReceiverParameterNameWithLocalNameInLambda(bool isStatic1, bool isStatic2) + { + var modifier1 = isStatic1 ? "static " : ""; + var modifier2 = isStatic2 ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(string p) + { + " + modifier1 + @"void M4() + { + System.Func l = " + modifier2 + @"() => + { + int p = 0; + return p; + }; + } + " + modifier1 + @"void M5() + { + System.Func l = " + modifier2 + @"() => + { + int p() => 0; + return p(); + }; + } + " + modifier1 + @"int P7 + { + set + { + System.Func l = " + modifier2 + @"() => + { + int p = 0; + return p; + }; + } + } + " + modifier1 + @"int P8 + { + set + { + System.Func l = " + modifier2 + @"() => + { + int p() => 0; + return p(); + }; + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NameConflict_26_ExampleFromSpec() + { + var src = @" +using System.Linq; + +public static class E +{ + extension(T[] ts) + { + public bool M1(T t) => ts.Contains(t); // `T` and `ts` are in scope + public static bool M2(T t) => ts.Contains(t); // Error: Cannot refer to `ts` from static context + public void M3(int T, string ts) { } // Error: Cannot reuse names `T` and `ts` + public void M4(string s) { } // Error: Cannot reuse names `T` and `ts` + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (9,39): error CS9293: Cannot use extension parameter 'T[] ts' in this context. + // public static bool M2(T t) => ts.Contains(t); // Error: Cannot refer to `ts` from static context + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "ts").WithArguments("T[] ts").WithLocation(9, 39), + // (10,28): error CS9288: 'T': a parameter, local variable, or local function cannot have the same name as an extension container type parameter + // public void M3(int T, string ts) { } // Error: Cannot reuse names `T` and `ts` + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(10, 28), + // (10,38): error CS9290: 'ts': a parameter, local variable, or local function cannot have the same name as an extension parameter + // public void M3(int T, string ts) { } // Error: Cannot reuse names `T` and `ts` + Diagnostic(ErrorCode.ERR_LocalSameNameAsExtensionParameter, "ts").WithArguments("ts").WithLocation(10, 38), + // (11,24): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // public void M4(string s) { } // Error: Cannot reuse names `T` and `ts` + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(11, 24), + // (11,27): warning CS8981: The type name 'ts' only contains lower-cased ascii characters. Such names may become reserved for the language. + // public void M4(string s) { } // Error: Cannot reuse names `T` and `ts` + Diagnostic(ErrorCode.WRN_LowerCaseTypeName, "ts").WithArguments("ts").WithLocation(11, 27), + // (11,27): error CS9292: Type parameter 'ts' has the same name as an extension parameter + // public void M4(string s) { } // Error: Cannot reuse names `T` and `ts` + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionParameter, "ts").WithArguments("ts").WithLocation(11, 27) + ); + } + + [Fact] + public void NameConflict_27_ExampleFromSpec() + { + var src = @" +public static class E +{ + extension(T[] ts) + { + public int T() { return M(ts); } // Generated static method M(T[]) is found + public string M() { return T(ts); } // Error: T is a type parameter + } +} + +class CTest +{ + static int M(T[] ts) + { + return T(ts); + } + + static int T(U[] ts) => 0; +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (6,33): error CS0029: Cannot implicitly convert type 'string' to 'int' + // public int T() { return M(ts); } // Generated static method M(T[]) is found + Diagnostic(ErrorCode.ERR_NoImplicitConv, "M(ts)").WithArguments("string", "int").WithLocation(6, 33), + // (7,36): error CS0119: 'T' is a type, which is not valid in the given context + // public string M() { return T(ts); } // Error: T is a type parameter + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(7, 36), + // (15,16): error CS0119: 'T' is a type, which is not valid in the given context + // return T(ts); + Diagnostic(ErrorCode.ERR_BadSKunknown, "T").WithArguments("T", "type").WithLocation(15, 16) + ); + } + + [Fact] + public void NameConflict_28_ExampleFromSpec() + { + var src = @" +public static class E +{ + extension(int P) + { + public int P() { return M(P); } // Generated static method M(T[]) is found + public string M() { return P(P); } // Error: P is a parameter + } +} + +class CTest +{ + static int M(int P) + { + return P(P); + } + + static int P(int P) => 0; +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (6,33): error CS0029: Cannot implicitly convert type 'string' to 'int' + // public int P() { return M(P); } // Generated static method M(T[]) is found + Diagnostic(ErrorCode.ERR_NoImplicitConv, "M(P)").WithArguments("string", "int").WithLocation(6, 33), + // (7,36): error CS0149: Method name expected + // public string M() { return P(P); } // Error: P is a parameter + Diagnostic(ErrorCode.ERR_MethodNameExpected, "P").WithLocation(7, 36), + // (15,16): error CS0149: Method name expected + // return P(P); + Diagnostic(ErrorCode.ERR_MethodNameExpected, "P").WithLocation(15, 16) + ); + } + + [Fact] + public void NameConflict_29_WithStaticTypeTypeParameter() + { + var src = @" +public static class E +{ + extension(int p) + { + public void M1() {} + } + + extension(T[] p) + { + public void M2() {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (4,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(int p) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(4, 5), + // (6,24): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'E' + // public void M1() {} + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "E").WithLocation(6, 24), + // (9,5): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(T[] p) + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(9, 5), + // (9,15): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'E' + // extension(T[] p) + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "E").WithLocation(9, 15), + // (11,24): error CS9289: Type parameter 'T' has the same name as an extension container type parameter + // public void M2() {} + Diagnostic(ErrorCode.ERR_TypeParameterSameNameAsExtensionTypeParameter, "T").WithArguments("T").WithLocation(11, 24) + ); + } + + [Fact] + public void ReceiverParameterScope_01_InStaticMember() + { + var src = """ +static class Extensions +{ + extension(int p) + { + static int P1 { get => p; } + static int M2() + { + return p; + } + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,32): error CS9293: Cannot use extension parameter 'int p' in this context. + // static int P1 { get => p; } + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(5, 32), + // (8,20): error CS9293: Cannot use extension parameter 'int p' in this context. + // return p; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(8, 20) + ); + } + + [Fact] + public void ReceiverParameterScope_02_InStaticMember() + { + var src = """ +static class Extensions +{ + extension(int p) + { + static int P1 + { + get + { + int local() => p; + return local(); + } + } + static int M2() + { + int local() => p; + return local(); + } + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (9,32): error CS9293: Cannot use extension parameter 'int p' in this context. + // int local() => p; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(9, 32), + // (15,28): error CS9293: Cannot use extension parameter 'int p' in this context. + // int local() => p; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(15, 28) + ); + } + + [Fact] + public void ReceiverParameterScope_03_InStaticMember() + { + var src = """ +static class Extensions +{ + extension(int p) + { + static string P1 { get => nameof(p); } + static string M2() + { + return nameof(p); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameterScope_04_InStaticLocalFunction() + { + var src = """ +static class Extensions +{ + extension(int p) + { + int P1 + { + get + { + static int local() => p; + return local(); + } + } + int M2() + { + static int local() => p; + return local(); + } + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (9,39): error CS8421: A static local function cannot contain a reference to 'p'. + // static int local() => p; + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable, "p").WithArguments("p").WithLocation(9, 39), + // (15,35): error CS8421: A static local function cannot contain a reference to 'p'. + // static int local() => p; + Diagnostic(ErrorCode.ERR_StaticLocalFunctionCannotCaptureVariable, "p").WithArguments("p").WithLocation(15, 35) + ); + } + + [Theory] + [CombinatorialData] + public void ReceiverParameterScope_05_InAttribute(bool isStatic) + { + var modifier = isStatic ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + [MyAttr(nameof(p))] + " + modifier + @"int P1 { get => 0; } + + [MyAttr(nameof(p))] + " + modifier + @"int M2() + { + return 0; + } + } +} + +class MyAttr : System.Attribute +{ + public MyAttr(string s) {} +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void ReceiverParameterScope_06_InAttribute(bool isStatic) + { + var modifier = isStatic ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + [MyAttr(p)] + " + modifier + @"int P1 { get => 0; } + + [MyAttr(p)] + " + modifier + @"int M2() + { + return 0; + } + } +} + +class MyAttr : System.Attribute +{ + public MyAttr(int p) {} +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (6,17): error CS9293: Cannot use extension parameter 'int p' in this context. + // [MyAttr(p)] + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(6, 17), + // (6,17): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [MyAttr(p)] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "p").WithLocation(6, 17), + // (9,17): error CS9293: Cannot use extension parameter 'int p' in this context. + // [MyAttr(p)] + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(9, 17), + // (9,17): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [MyAttr(p)] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "p").WithLocation(9, 17) + ); + } + + [Fact] + public void ReceiverParameterScope_07_InAttribute() + { + var src = @" +static class Extensions +{ + extension(int p) + { + [MyAttr(nameof(p))] + int this[int y] + { + get + { + return 0; + } + } + } +} + +class MyAttr : System.Attribute +{ + public MyAttr(string p) {} +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameterScope_08_InAttribute() + { + var src = @" +static class Extensions +{ + extension(string p) + { + [System.Runtime.CompilerServices.IndexerName(p)] + int this[int y] + { + get + { + return 0; + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (6,54): error CS9293: Cannot use extension parameter 'string p' in this context. + // [System.Runtime.CompilerServices.IndexerName(p)] + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("string p").WithLocation(6, 54), + // (6,54): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type + // [System.Runtime.CompilerServices.IndexerName(p)] + Diagnostic(ErrorCode.ERR_BadAttributeArgument, "p").WithLocation(6, 54) + ); + } + + [Theory] + [CombinatorialData] + public void ReceiverParameterScope_09_InDefaultValue(bool isStatic) + { + var modifier = isStatic ? "static " : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + " + modifier + @"int M2(string x = nameof(p)) + { + return 0; + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void ReceiverParameterScope_10_InDefaultValue(bool isStatic) + { + var modifier = isStatic ? "static" : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + " + modifier + @" +#line 6 + int M2(int x = p) + { + return 0; + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (6,24): error CS9293: Cannot use extension parameter 'int p' in this context. + // int M2(int x = p) + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(6, 24), + // (6,24): error CS1736: Default parameter value for 'x' must be a compile-time constant + // int M2(int x = p) + Diagnostic(ErrorCode.ERR_DefaultValueMustBeConstant, "p").WithArguments("x").WithLocation(6, 24) + ); + } + + [Theory] + [CombinatorialData] + public void ReceiverParameterScope_11_InNestedType(bool isStatic) + { + var modifier = isStatic ? "static" : ""; + + var src = @" +static class Extensions +{ + extension(int p) + { + class Nested + { + " + modifier + @" + int M2() + { + return p; + } + + " + modifier + @" + string M3() + { + return nameof(p); + } + } + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics( + // (6,15): error CS9282: Extension declarations can include only methods or properties + // class Nested + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 15), + // (11,24): error CS9293: Cannot use extension parameter 'int p' in this context. + // return p; + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "p").WithArguments("int p").WithLocation(11, 24) + ); + } + + [Fact] + public void CycleInAttribute_01() + { + var src = @" +static class Extensions +{ + const string Str = ""val""; + extension(string p) + { + [System.Runtime.CompilerServices.IndexerName(Str)] + int this[int y] + { + get + { + return 0; + } + } + } +} +"; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : We do not allow complex forms of IndexerName attribute due to a possible binding cycle + comp.VerifyEmitDiagnostics( + // (7,54): error CS8078: An expression is too long or complex to compile + // [System.Runtime.CompilerServices.IndexerName(Str)] + Diagnostic(ErrorCode.ERR_InsufficientStack, "Str").WithLocation(7, 54) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_01() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public void M1() {} + } + + extension(object receiver) + { + public void M1() {} + } + + extension(int receiver) + { + public void M2() {} + } + + extension(ref int receiver) + { + public void M2() {} + } + + extension(in int receiver) + { + public void M3() {} + } + + extension(ref int receiver) + { + public void M3() {} + } + + extension(ref readonly int receiver) + { + public void M4() {} + } + + extension(ref int receiver) + { + public void M4() {} + } + + extension(ref readonly int receiver) + { + public void M5() {} + } + + extension(in int receiver) + { + public void M5() {} + } + + static public void M6(this int receiver) {} + + static public void M6(this ref int receiver) {} + + static public void M7(this in int receiver) {} + + static public void M7(this ref int receiver) {} + + static public void M8(this ref readonly int receiver) {} + + static public void M8(this ref int receiver) {} + + static public void M9(this ref readonly int receiver) {} + + static public void M9(this in int receiver) {} + + extension(object receiver) + { + public void M10() {} + public void M10() {} + } + + extension(object receiver1) + { + public void M13() {} + } + + extension(object receiver2) + { + public void M13() {} + } +} +"""; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Despite the fact that we do not complain about M6, should we report an error for M2 (the only difference is receiver ref-ness)? + comp.VerifyDiagnostics( + // (10,21): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(10, 21), + // (20,21): error CS0111: Type 'Extensions' already defines a member called 'M2' with the same parameter types + // public void M2() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "Extensions").WithLocation(20, 21), + // (30,21): error CS0111: Type 'Extensions' already defines a member called 'M3' with the same parameter types + // public void M3() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M3").WithArguments("M3", "Extensions").WithLocation(30, 21), + // (40,21): error CS0111: Type 'Extensions' already defines a member called 'M4' with the same parameter types + // public void M4() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M4").WithArguments("M4", "Extensions").WithLocation(40, 21), + // (50,21): error CS0111: Type 'Extensions' already defines a member called 'M5' with the same parameter types + // public void M5() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M5").WithArguments("M5", "Extensions").WithLocation(50, 21), + // (59,24): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'in' + // static public void M7(this ref int receiver) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M7").WithArguments("Extensions", "method", "ref", "in").WithLocation(59, 24), + // (63,24): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'ref readonly' + // static public void M8(this ref int receiver) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M8").WithArguments("Extensions", "method", "ref", "ref readonly").WithLocation(63, 24), + // (67,24): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref readonly' + // static public void M9(this in int receiver) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M9").WithArguments("Extensions", "method", "in", "ref readonly").WithLocation(67, 24), + // (72,21): error CS0111: Type 'Extensions' already defines a member called 'M10' with the same parameter types + // public void M10() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M10").WithArguments("M10", "Extensions").WithLocation(72, 21), + // (82,21): error CS0111: Type 'Extensions' already defines a member called 'M13' with the same parameter types + // public void M13() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M13").WithArguments("M13", "Extensions").WithLocation(82, 21) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + int i = 0; + i.M11(); + "".M11(); + + ((long)i).M12(i); + ((long)i).M12(ref i); + } + + extension(int receiver) + { + public void M11() {} + } + + extension(string receiver) + { + public void M11() {} + } + + extension(long receiver) + { + public void M12(int x) {} + } + + extension(long receiver) + { + public void M12(ref int x) {} + } +} +"""); + + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void MemberNameAndSignatureConflict_02() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public void M1() {} + } + + extension(object receiver) + { + static public void M1() {} + } + + extension(object receiver) + { + static public void M2(ref int x) {} + } + + extension(object receiver) + { + static public void M2(ref readonly int x) {} + } + + extension(object receiver) + { + static public void M3(in int x) {} + } + + extension(object receiver) + { + static public void M3(ref readonly int x) {} + } + + extension(object receiver) + { + static public void M4(ref int x) {} + } + + extension(object receiver) + { + static public void M4(in int x) {} + } + + extension(object receiver) + { + static public void M5() {} + static public void M5() {} + } + + extension(int receiver) + { + static public void M6() {} + } + + extension(string receiver) + { + static public void M6() {} + } + + extension(int receiver) + { + static public int M7() => 0; + } + + extension(long receiver) + { + static public long M7() => 0; + } + + extension(int receiver) + { + public static void M8() {} + } + + extension(ref int receiver) + { + public static void M8() {} + } + + extension(int receiver) + { + public void M9() {} + } + + extension(int receiver) + { + public static void M9(int x) {} + } + + extension(ref int receiver) + { + public void M10() {} + } + + extension(int receiver) + { + public static void M10(in int x) {} + } + + extension(int receiver) + { + public void M11() {} + public static void M11(int x) {} + } + + extension(ref int receiver) + { + public void M12() {} + public static void M12(in int x) {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,28): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // static public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(10, 28), + // (20,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'ref' + // static public void M2(ref readonly int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M2").WithArguments("Extensions", "method", "ref readonly", "ref").WithLocation(20, 28), + // (30,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'in' + // static public void M3(ref readonly int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M3").WithArguments("Extensions", "method", "ref readonly", "in").WithLocation(30, 28), + // (40,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // static public void M4(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M4").WithArguments("Extensions", "method", "in", "ref").WithLocation(40, 28), + // (46,28): error CS0111: Type 'Extensions' already defines a member called 'M5' with the same parameter types + // static public void M5() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M5").WithArguments("M5", "Extensions").WithLocation(46, 28), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : It feels unfortunate that we generate conflicting signatures, the methods extend different types (refer to M6 and M7 cases) + + // (56,28): error CS0111: Type 'Extensions' already defines a member called 'M6' with the same parameter types + // static public void M6() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M6").WithArguments("M6", "Extensions").WithLocation(56, 28), + // (66,28): error CS0111: Type 'Extensions' already defines a member called 'M7' with the same parameter types + // static public long M7() => 0; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M7").WithArguments("M7", "Extensions").WithLocation(66, 28), // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Signatures in metadata are different in this case (return type is different), consider if we want to enable this specific case + + // (76,28): error CS0111: Type 'Extensions' already defines a member called 'M8' with the same parameter types + // public static void M8() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M8").WithArguments("M8", "Extensions").WithLocation(76, 28), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we comfortable with these four conflicts? + + // (86,28): error CS0111: Type 'Extensions' already defines a member called 'M9' with the same parameter types + // public static void M9(int x) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M9").WithArguments("M9", "Extensions").WithLocation(86, 28), + // (96,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public static void M10(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M10").WithArguments("Extensions", "method", "in", "ref").WithLocation(96, 28), + // (102,28): error CS0111: Type 'Extensions' already defines a member called 'M11' with the same parameter types + // public static void M11(int x) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M11").WithArguments("M11", "Extensions").WithLocation(102, 28), + // (108,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public static void M12(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M12").WithArguments("Extensions", "method", "in", "ref").WithLocation(108, 28) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + int i = 0; + i.M13(); + int.M13(ref i); + M13(i); + M13(ref i); + + i.M14(); + int.M14(i); + M14(ref i); + M14(i); + } + + extension(int receiver) + { + public void M13() => System.Console.Write(1); + } + + extension(int receiver) + { + public static void M13(ref int x) => System.Console.Write(2); + } + + extension(ref int receiver) + { + public void M14() => System.Console.Write(3); + } + + extension(int receiver) + { + public static void M14(int x) => System.Console.Write(4); + } +} +""", options: TestOptions.DebugExe); + + CompileAndVerify(comp, expectedOutput: "12123434").VerifyDiagnostics(); + } + + [Fact] + public void MemberNameAndSignatureConflict_03() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public int P1 => 1; + } + + extension(object receiver) + { + public int P1 => 4; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,20): error CS0102: The type 'Extensions' already contains a definition for 'P1' + // public int P1 => 4; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("Extensions", "P1").WithLocation(10, 20) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_04() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public int P1 => 1; + } + + extension(object receiver) + { + static public int P1 => 4; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,27): error CS0102: The type 'Extensions' already contains a definition for 'P1' + // static public int P1 => 4; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("Extensions", "P1").WithLocation(10, 27) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_05() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public int P1 => 1; + public int get_P2() => 2; + } + + extension(object receiver) + { + public int get_P1() => 3; + public int P2 => 4; + } +} + +static class Extensions2 +{ + extension(object receiver) + { + public int P3 => 1; + public int get_P4() => 2; + public int get_P3() => 3; + public int P4 => 4; + } +} + +static class Extensions3 +{ + extension(object receiver) + { + public int P1 => 1; + public int set_P2(int x) => 2; + } + + extension(object receiver) + { + public int set_P1(int y) => 3; + public int P2 => 4; + } +} + +static class Extensions4 +{ + extension(object receiver) + { + public int P3 => 1; + public int set_P4(int z) => 2; + public int set_P3(int z) => 3; + public int P4 => 4; + } +} + +static class Extensions5 +{ + extension(object receiver) + { + public int this[int x] => 1; + public int get_Item(long y) => 2; + } + + extension(object receiver) + { + public int get_Item(int a) => 3; + public int this[long b] => 4; + } +} + +static class Extensions6 +{ + extension(object receiver) + { + [System.Runtime.CompilerServices.IndexerName("Indexer")] + public int this[int x] => 1; + } + + extension(object receiver) + { + public int get_Indexer(int a) => 3; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,26): error CS0082: Type 'Extensions' already reserves a member called 'get_P1' with the same parameter types + // public int P1 => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "1").WithArguments("get_P1", "Extensions").WithLocation(5, 26), + // (12,26): error CS0082: Type 'Extensions' already reserves a member called 'get_P2' with the same parameter types + // public int P2 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P2", "Extensions").WithLocation(12, 26), + // (20,26): error CS0082: Type 'Extensions2' already reserves a member called 'get_P3' with the same parameter types + // public int P3 => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "1").WithArguments("get_P3", "Extensions2").WithLocation(20, 26), + // (23,26): error CS0082: Type 'Extensions2' already reserves a member called 'get_P4' with the same parameter types + // public int P4 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P4", "Extensions2").WithLocation(23, 26), + // (31,20): error CS0082: Type 'Extensions3' already reserves a member called 'set_P1' with the same parameter types + // public int P1 => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "P1").WithArguments("set_P1", "Extensions3").WithLocation(31, 20), + // (38,20): error CS0082: Type 'Extensions3' already reserves a member called 'set_P2' with the same parameter types + // public int P2 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "P2").WithArguments("set_P2", "Extensions3").WithLocation(38, 20), + // (46,20): error CS0082: Type 'Extensions4' already reserves a member called 'set_P3' with the same parameter types + // public int P3 => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "P3").WithArguments("set_P3", "Extensions4").WithLocation(46, 20), + // (49,20): error CS0082: Type 'Extensions4' already reserves a member called 'set_P4' with the same parameter types + // public int P4 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "P4").WithArguments("set_P4", "Extensions4").WithLocation(49, 20), + // (57,35): error CS0082: Type 'Extensions5' already reserves a member called 'get_Item' with the same parameter types + // public int this[int x] => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "1").WithArguments("get_Item", "Extensions5").WithLocation(57, 35), + // (64,36): error CS0082: Type 'Extensions5' already reserves a member called 'get_Item' with the same parameter types + // public int this[long b] => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_Item", "Extensions5").WithLocation(64, 36), + // (73,35): error CS0082: Type 'Extensions6' already reserves a member called 'get_Indexer' with the same parameter types + // public int this[int x] => 1; + Diagnostic(ErrorCode.ERR_MemberReserved, "1").WithArguments("get_Indexer", "Extensions6").WithLocation(73, 35) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_06() + { + var src = """ +static class Extensions1 +{ + extension(object receiver) + { + public int P1 => 1; + } + + extension(object receiver) + { + public int P1 {set{}} + } +} + +static class Extensions2 +{ + extension(object receiver1) + { + public int this[int x] => 1; + } + + extension(object receiver2) + { + public int this[int y] {set{}} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,20): error CS0102: The type 'Extensions1' already contains a definition for 'P1' + // public int P1 {set{}} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P1").WithArguments("Extensions1", "P1").WithLocation(10, 20), + // (23,20): error CS0111: Type 'Extensions2' already defines a member called 'this' with the same parameter types + // public int this[int y] {set{}} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "Extensions2").WithLocation(23, 20) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_07() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public void M(){} + } + + extension(__arglist) + { + public void M(object x){} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,15): error CS1669: __arglist is not valid in this context + // extension(__arglist) + Diagnostic(ErrorCode.ERR_IllegalVarArgs, "__arglist").WithLocation(8, 15) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_08() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public void M1() {} + } + + public static void M1(object receiver) {} + + extension(int receiver) + { + public void M2() {} + } + + public static void M2(ref int receiver) {} + + extension(in int receiver) + { + public void M3() {} + } + + public static void M3(ref int receiver) {} + + extension(ref readonly int receiver) + { + public void M4() {} + } + + public static void M4(ref int receiver) {} + + extension(ref readonly int receiver) + { + public void M5() {} + } + + public static void M5(in int receiver) {} + + extension(object receiver1) + { + public void M13() {} + } + + public static void M13(object receiver2) {} +} +"""; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Should we report an error for M2 (the only difference is receiver ref-ness)? + comp.VerifyDiagnostics( + // (5,21): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(5, 21), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : The error might be somewhat confusing in this scenario because there are no parameters and we complain about ref-ness of the receiver. + + // (19,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public void M3() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M3").WithArguments("Extensions", "method", "in", "ref").WithLocation(19, 21), + // (26,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'ref' + // public void M4() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M4").WithArguments("Extensions", "method", "ref readonly", "ref").WithLocation(26, 21), + // (33,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'in' + // public void M5() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M5").WithArguments("Extensions", "method", "ref readonly", "in").WithLocation(33, 21), + // (40,21): error CS0111: Type 'Extensions' already defines a member called 'M13' with the same parameter types + // public void M13() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M13").WithArguments("M13", "Extensions").WithLocation(40, 21) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + 1.M2(); + } + + extension(int receiver) + { + public void M2() {} + } + + public static void M2(this ref int receiver) {} +} +"""); + + comp.VerifyDiagnostics( + // (5,11): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions.extension(int).M2()' and 'Extensions.M2(ref int)' + // 1.M2(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments("Extensions.extension(int).M2()", "Extensions.M2(ref int)").WithLocation(5, 11) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + int i = 0; + 1.M2(); + M2(i); + M2(ref i); + + ((long)i).M12(i); + ((long)i).M12(ref i); + } + + extension(int receiver) + { + public void M2() {} + } + + public static void M2(ref int receiver) {} + + extension(long receiver) + { + public void M12(int x) {} + } + + public static void M12(this long receiver, ref int x) {} +} +"""); + + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Fact] + public void MemberNameAndSignatureConflict_09() + { + var src = """ +static class Extensions +{ + public static void M1(object receiver) {} + + extension(object receiver) + { + public void M1() {} + } + + public static void M2(ref int receiver) {} + + extension(int receiver) + { + public void M2() {} + } + + public static void M3(ref int receiver) {} + + extension(in int receiver) + { + public void M3() {} + } + + public static void M4(ref int receiver) {} + + extension(ref readonly int receiver) + { + public void M4() {} + } + + public static void M5(in int receiver) {} + + extension(ref readonly int receiver) + { + public void M5() {} + } + + public static void M13(object receiver2) {} + + extension(object receiver1) + { + public void M13() {} + } +} +"""; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Should we report an error for M2 (the only difference is receiver ref-ness)? + comp.VerifyDiagnostics( + // (7,21): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(7, 21), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : The error might be somewhat confusing in this scenario because there are no parameters and we complain about ref-ness of the receiver. + + // (21,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public void M3() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M3").WithArguments("Extensions", "method", "in", "ref").WithLocation(21, 21), + // (28,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'ref' + // public void M4() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M4").WithArguments("Extensions", "method", "ref readonly", "ref").WithLocation(28, 21), + // (35,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref readonly' and 'in' + // public void M5() {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M5").WithArguments("Extensions", "method", "ref readonly", "in").WithLocation(35, 21), + // (42,21): error CS0111: Type 'Extensions' already defines a member called 'M13' with the same parameter types + // public void M13() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M13").WithArguments("M13", "Extensions").WithLocation(42, 21) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + 1.M2(); + } + + public static void M2(this ref int receiver) {} + + extension(int receiver) + { + public void M2() {} + } +} +"""); + + comp.VerifyDiagnostics( + // (5,11): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions.extension(int).M2()' and 'Extensions.M2(ref int)' + // 1.M2(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments("Extensions.extension(int).M2()", "Extensions.M2(ref int)").WithLocation(5, 11) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_10() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public void M1() {} + } + + static public void M1() {} + + extension(object receiver) + { + static public void M2(ref int x) {} + } + + static public void M2(ref readonly int x) {} + + extension(object receiver) + { + static public void M3(in int x) {} + } + + static public void M3(ref readonly int x) {} + + extension(object receiver) + { + static public void M4(ref int x) {} + } + + static public void M4(in int x) {} + + + extension(int receiver) + { + static public int M7() => 0; + } + + static public long M7() => 0; + + + public static void M9(int receiver) {} + + extension(int receiver) + { + public static void M9(int x) {} + } + + public static void M10(ref int receiver) {} + + extension(int receiver) + { + public static void M10(in int x) {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,28): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // static public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(5, 28), + // (12,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'ref readonly' + // static public void M2(ref int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M2").WithArguments("Extensions", "method", "ref", "ref readonly").WithLocation(12, 28), + // (19,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref readonly' + // static public void M3(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M3").WithArguments("Extensions", "method", "in", "ref readonly").WithLocation(19, 28), + // (26,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'in' + // static public void M4(ref int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M4").WithArguments("Extensions", "method", "ref", "in").WithLocation(26, 28), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : It feels unfortunate that we generate conflicting signatures + + // (34,27): error CS0111: Type 'Extensions' already defines a member called 'M7' with the same parameter types + // static public int M7() => 0; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M7").WithArguments("M7", "Extensions").WithLocation(34, 27), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we comfortable with these two conflicts? + + // (44,28): error CS0111: Type 'Extensions' already defines a member called 'M9' with the same parameter types + // public static void M9(int x) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M9").WithArguments("M9", "Extensions").WithLocation(44, 28), + // (51,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public static void M10(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M10").WithArguments("Extensions", "method", "in", "ref").WithLocation(51, 28) + ); + + comp = CreateCompilation(""" +static class Extensions +{ + static void Main() + { + int i = 0; + i.M13(); + int.M13(ref i); + M13(i); + M13(ref i); + + i.M14(); + int.M14(i); + M14(ref i); + M14(i); + } + + public static void M13(this int receiver) => System.Console.Write(1); + + extension(int receiver) + { + public static void M13(ref int x) => System.Console.Write(2); + } + + public static void M14(this ref int receiver) => System.Console.Write(3); + + extension(int receiver) + { + public static void M14(int x) => System.Console.Write(4); + } +} +""", options: TestOptions.DebugExe); + + CompileAndVerify(comp, expectedOutput: "12123434").VerifyDiagnostics(); + } + + [Fact] + public void MemberNameAndSignatureConflict_11() + { + var src = """ +static class Extensions +{ + static public void M1() {} + + extension(object receiver) + { + static public void M1() {} + } + + static public void M2(ref readonly int x) {} + + extension(object receiver) + { + static public void M2(ref int x) {} + } + + static public void M3(ref readonly int x) {} + + extension(object receiver) + { + static public void M3(in int x) {} + } + + static public void M4(in int x) {} + + extension(object receiver) + { + static public void M4(ref int x) {} + } + + static public long M7() => 0; + + extension(int receiver) + { + static public int M7() => 0; + } + + extension(int receiver) + { + public static void M9(int x) {} + } + + public static void M9(int receiver) {} + + extension(int receiver) + { + public static void M10(in int x) {} + } + + public static void M10(ref int receiver) {} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (7,28): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // static public void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(7, 28), + // (14,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'ref readonly' + // static public void M2(ref int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M2").WithArguments("Extensions", "method", "ref", "ref readonly").WithLocation(14, 28), + // (21,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref readonly' + // static public void M3(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M3").WithArguments("Extensions", "method", "in", "ref readonly").WithLocation(21, 28), + // (28,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'ref' and 'in' + // static public void M4(ref int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M4").WithArguments("Extensions", "method", "ref", "in").WithLocation(28, 28), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : It feels unfortunate that we generate conflicting signatures + + // (35,27): error CS0111: Type 'Extensions' already defines a member called 'M7' with the same parameter types + // static public int M7() => 0; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M7").WithArguments("M7", "Extensions").WithLocation(35, 27), + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we comfortable with these two conflicts? + + // (40,28): error CS0111: Type 'Extensions' already defines a member called 'M9' with the same parameter types + // public static void M9(int x) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M9").WithArguments("M9", "Extensions").WithLocation(40, 28), + // (47,28): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // public static void M10(in int x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M10").WithArguments("Extensions", "method", "in", "ref").WithLocation(47, 28) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_12() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public int P1 => 1; + } + + public static int get_P1(object receiver) => 4; + + public static int get_P2(object receiver) => 4; + + extension(object receiver) + { + public int P2 => 1; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,26): error CS0111: Type 'Extensions' already defines a member called 'get_P1' with the same parameter types + // public int P1 => 1; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "1").WithArguments("get_P1", "Extensions").WithLocation(5, 26), + // (14,26): error CS0111: Type 'Extensions' already defines a member called 'get_P2' with the same parameter types + // public int P2 => 1; + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "1").WithArguments("get_P2", "Extensions").WithLocation(14, 26) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_13() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public int P1 => 1; + } + + public static int P1 => 4; + + public static int P2 => 4; + + extension(object receiver) + { + static public int P2 => 1; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,29): error CS0082: Type 'Extensions' already reserves a member called 'get_P1' with the same parameter types + // public static int P1 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P1", "Extensions").WithLocation(8, 29), + // (10,29): error CS0082: Type 'Extensions' already reserves a member called 'get_P2' with the same parameter types + // public static int P2 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P2", "Extensions").WithLocation(10, 29) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_14() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public int get_P1() => 1; + } + + public static int P1 => 4; + + public static int P2 => 4; + + extension(object receiver) + { + static public int get_P2() => 1; + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,29): error CS0082: Type 'Extensions' already reserves a member called 'get_P1' with the same parameter types + // public static int P1 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P1", "Extensions").WithLocation(8, 29), + // (10,29): error CS0082: Type 'Extensions' already reserves a member called 'get_P2' with the same parameter types + // public static int P2 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "4").WithArguments("get_P2", "Extensions").WithLocation(10, 29) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_15() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + public int P1 => 1; + } + + public static int set_P1(object receiver, int x) => 4; +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void MemberNameAndSignatureConflict_16() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public int P1 => 1; + } + + public static int P1 {set{}} + + static public int get_P2() => 1; + public static int P2 {set{}} +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we comfortable reporting an error like this? + + // (8,23): error CS0082: Type 'Extensions' already reserves a member called 'get_P1' with the same parameter types + // public static int P1 {set{}} + Diagnostic(ErrorCode.ERR_MemberReserved, "P1").WithArguments("get_P1", "Extensions").WithLocation(8, 23), + + // (11,23): error CS0082: Type 'Extensions' already reserves a member called 'get_P2' with the same parameter types + // public static int P2 {set{}} + Diagnostic(ErrorCode.ERR_MemberReserved, "P2").WithArguments("get_P2", "Extensions").WithLocation(11, 23) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_17() + { + var src = """ +static class Extensions +{ + extension(object receiver) + { + static public int set_P1(int x) => 1; + } + + public static int P1 => 4; + + static public int set_P2(int x) => 1; + public static int P2 => 4; +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Are we comfortable reporting an error like this? + + // (8,23): error CS0082: Type 'Extensions' already reserves a member called 'set_P1' with the same parameter types + // public static int P1 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "P1").WithArguments("set_P1", "Extensions").WithLocation(8, 23), + + // (11,23): error CS0082: Type 'Extensions' already reserves a member called 'set_P2' with the same parameter types + // public static int P2 => 4; + Diagnostic(ErrorCode.ERR_MemberReserved, "P2").WithArguments("set_P2", "Extensions").WithLocation(11, 23) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_18() + { + var src = """ +public static class Extensions +{ + extension(object receiver) + { + static public void M1(int x) {} + } + + public static int M1 => 4; + + extension(object receiver) + { + static public void M2(int x) {} + } + + public static int M2 = 4; + + extension(object receiver) + { + static public void M3(int x) {} + } + +#pragma warning disable CS0067 // The event 'Extensions.M3' is never used + public static event System.Action M3; +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (5,28): error CS0102: The type 'Extensions' already contains a definition for 'M1' + // static public void M1(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M1").WithArguments("Extensions", "M1").WithLocation(5, 28), + // (12,28): error CS0102: The type 'Extensions' already contains a definition for 'M2' + // static public void M2(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M2").WithArguments("Extensions", "M2").WithLocation(12, 28), + // (19,28): error CS0102: The type 'Extensions' already contains a definition for 'M3' + // static public void M3(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M3").WithArguments("Extensions", "M3").WithLocation(19, 28) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_19() + { + var src = """ +public static class Extensions +{ + public static int M1 => 4; + + extension(object receiver) + { + static public void M1(int x) {} + } + + public static int M2 = 4; + + extension(object receiver) + { + static public void M2(int x) {} + } + +#pragma warning disable CS0067 // The event 'Extensions.M3' is never used + public static event System.Action M3; + + extension(object receiver) + { + static public void M3(int x) {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (7,28): error CS0102: The type 'Extensions' already contains a definition for 'M1' + // static public void M1(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M1").WithArguments("Extensions", "M1").WithLocation(7, 28), + // (14,28): error CS0102: The type 'Extensions' already contains a definition for 'M2' + // static public void M2(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M2").WithArguments("Extensions", "M2").WithLocation(14, 28), + // (22,28): error CS0102: The type 'Extensions' already contains a definition for 'M3' + // static public void M3(int x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "M3").WithArguments("Extensions", "M3").WithLocation(22, 28) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_20_ReceiverType_TupleNameDifference() + { + var src = """ +public static class Extensions +{ + extension((int a, int b) receiver) + { + void M1() {} + } + + extension((int c, int d)) + { + static void M1() {} + } + + extension(int receiver) + { + void M1() {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,21): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // static void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(10, 21) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_21_ReceiverType_NativeIntDifference() + { + var src = """ +public static class Extensions +{ + extension(System.IntPtr receiver) + { + void M1() {} + } + + extension(nint) + { + static void M1() {} + } + + extension(int receiver) + { + void M1() {} + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + + comp.VerifyDiagnostics( + // (10,21): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // static void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(10, 21) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_22_ReceiverType_NullabilityDifference() + { + var src = """ +#nullable enable + +public static class Extensions +{ + extension(string[]) + { + static void M1() {} + } + + extension(string?[] receiver) + { + void M1() {} + } + + extension(int receiver) + { + void M1() {} + } +} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (12,14): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(12, 14) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_23_Receiver_TypeDifference() + { + var src = """ +public static class Extensions +{ + extension(int receiver) + { + void M1() {} + } + + extension(long) + { + static void M1() {} + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_24_Genericity(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) where T : class + { + static void M1(T x) {} + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) where U : struct + { +#line 17 + void M1(U y) {} + void M2(U y) {} + static void M2(U x) {} + } + + extension(int[] receiver) + { + void M1(int a) {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (17,14): error CS0111: Type 'Extensions' already defines a member called 'M1' with the same parameter types + // void M1(U y) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions").WithLocation(17, 14), + // (19,21): error CS0111: Type 'Extensions' already defines a member called 'M2' with the same parameter types + // static void M2(U x) {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M2").WithArguments("M2", "Extensions").WithLocation(19, 21) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_25_Genericity_DifferentArity() + { + var src = """ +public static class Extensions1 +{ + extension(T1) + { + static void M1(T1 x) {} + } + + extension(T1) + { + static void M1(T1 x) {} + } +} + +public static class Extensions2 +{ + extension(T2) + { + static void M1(T2 x) {} + } + + extension(T2) + { + static void M1(T2 x) {} + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (8,23): error CS9295: The extended type 'T1' must reference all the type parameters declared by the extension, but type parameter 'U1' is not referenced. + // extension(T1) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T1").WithArguments("T1", "U1").WithLocation(8, 23), + // (16,23): error CS9295: The extended type 'T2' must reference all the type parameters declared by the extension, but type parameter 'U2' is not referenced. + // extension(T2) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "T2").WithArguments("T2", "U2").WithLocation(16, 23)); + } + + [Fact] + public void MemberNameAndSignatureConflict_26_Genericity() + { + var src = """ +public static class Extensions1 +{ + extension(C) + { + static void M1() {} + } + + extension(C) + { + static void M1() {} + } +} + +class C {} +"""; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (10,21): error CS0111: Type 'Extensions1' already defines a member called 'M1' with the same parameter types + // static void M1() {} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "M1").WithArguments("M1", "Extensions1").WithLocation(10, 21) + ); + } + + [Fact] + public void MemberNameAndSignatureConflict_27_Genericity() + { + var src = """ +public static class Extensions1 +{ + extension(C) + { + void M1() {} + } + + extension(C) + { + void M1() {} + } +} + +class C {} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp).VerifyDiagnostics(); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_28_Genericity(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(ref int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { + static void M1(ref T x) {} + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { +#line 17 + void M1(in U y) {} + void M2(ref U y) {} + static void M2(in U x) {} + } + + extension(int[] receiver) + { + void M1(ref int a) {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (17,14): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // void M1(in U y) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M1").WithArguments("Extensions", "method", "in", "ref").WithLocation(17, 14), + // (19,21): error CS0663: 'Extensions' cannot define an overloaded method that differs only on parameter modifiers 'in' and 'ref' + // static void M2(in U x) {} + Diagnostic(ErrorCode.ERR_OverloadRefKind, "M2").WithArguments("Extensions", "method", "in", "ref").WithLocation(19, 21) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_29_Indexers(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { +#line 8 + int this[T x] => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { + void Item(U x) {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,13): error CS0102: The type 'Extensions' already contains a definition for 'Item' + // int this[T x] => default; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "this").WithArguments("Extensions", "Item").WithLocation(8, 13) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_30_Indexers(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { + int this[T x] => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { +#line 18 + int this[U x] { set{}} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (18,13): error CS0111: Type 'Extensions' already defines a member called 'this' with the same parameter types + // int this[U x] { set{}} + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "this").WithArguments("this", "Extensions").WithLocation(18, 13) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_31_Indexers(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { + int this[T x] => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { +#line 18 + [System.Runtime.CompilerServices.IndexerName(""NotItem"")] + int this[int x] { set{}} + } +} +"; + var comp = CreateCompilation(src); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : The "within a type" part of the message might be somewhat misleading + comp.VerifyDiagnostics( + // (19,13): error CS0668: Two indexers have different names; the IndexerName attribute must be used with the same name on every indexer within a type + // int this[int x] { set{}} + Diagnostic(ErrorCode.ERR_InconsistentIndexerNames, "this").WithLocation(19, 13) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_32_Properties(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { + T P => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { +#line 18 + void P(U x) {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (18,14): error CS0102: The type 'Extensions' already contains a definition for 'P' + // void P(U x) {} + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "P").WithArguments("Extensions", "P").WithLocation(18, 14) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_33_Properties(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { +#line 8 + T P => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { + static void set_P(U x) {} + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,11): error CS0082: Type 'Extensions' already reserves a member called 'set_P' with the same parameter types + // T P => default; + Diagnostic(ErrorCode.ERR_MemberReserved, "P").WithArguments("set_P", "Extensions").WithLocation(8, 11) + ); + } + + [Theory] + [CombinatorialData] + public void MemberNameAndSignatureConflict_34_Properties(bool insertAtTheBeginning) + { + var insert = """ + extension(S[]) + { + static void M1(int z) {} + } +"""; + + var src = @" +public static class Extensions +{ +" + (insertAtTheBeginning ? insert : "") + @" + + extension(T[]) + { +#line 8 + T P => default; + } + +" + (!insertAtTheBeginning ? insert : "") + @" + + extension(U[] receiver) + { + U[] get_P => null; + } +} +"; + var comp = CreateCompilation(src); + + comp.VerifyDiagnostics( + // (8,16): error CS0102: The type 'Extensions' already contains a definition for 'get_P' + // T P => default; + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "default").WithArguments("Extensions", "get_P").WithLocation(8, 16) + ); + } + + [Fact] + public void MethodInvocation_01() + { + var source = """ +new object().M(); + +static class E1 +{ + extension(object o) + { + public void M() { } + } +} +static class E2 +{ + public static void M(this object o) { } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (1,14): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(object).M()' and 'E2.M(object)' + // new object().M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E1.extension(object).M()", "E2.M(object)").WithLocation(1, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal(["void E1.<>E__0.M()", "void System.Object.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void MethodInvocation_02() + { + var source = """ +new object().M(42); + +static class E1 +{ + extension(object o) + { + public void M(int i) { System.Console.Write("ran"); } + } +} +static class E2 +{ + public static void M(this object o, string s) => throw null; +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void E1.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_03() + { + var source = """ +new object().M(""); + +static class E1 +{ + extension(object o) + { + public void M(int i) => throw null; + } +} +static class E2 +{ + public static void M(this object o, string s) { System.Console.Write("ran"); } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "new object().M"); + Assert.Equal("void System.Object.M(System.String s)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_04() + { + var source = """ +42.M(); + +static class E1 +{ + extension(int i) + { + public void M() { System.Console.Write("ran"); } + } +} +static class E2 +{ + public static void M(this int? i) => throw null; +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_05() + { + var source = """ +42.M(); + +static class E1 +{ + extension(int? i) + { + public void M() => throw null; + } +} +static class E2 +{ + public static void M(this int i) { System.Console.Write("ran"); } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void System.Int32.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_06() + { + var src = """ +object.M(); + +public static class E +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0411: The type arguments for method 'E.extension(object).M()' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // object.M(); + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "M").WithArguments("E.extension(object).M()").WithLocation(1, 8)); + } + + [Fact] + public void MethodInvocation_RemoveLowerPriorityMembers_01() + { + var source = """ +42.M(43); + +static class E1 +{ + extension(int i) + { + public void M(int j) => throw null; + + [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + public void M(long l) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M(System.Int64 l)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveLowerPriorityMembers_02() + { + var source = """ +42.M(43); + +static class E1 +{ + extension(int i) + { + public void M(int j) { System.Console.Write("ran"); } + } + extension(int i) + { + [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + public void M(long l) => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : ORPA should look at the containing static class (rather than look at the extension declaration) as the "containing type" + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M(System.Int32 j)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveLowerPriorityMembers_03() + { + var source = """ +42.M(43); + +static class E1 +{ + extension(int i) + { + public void M(int j) { System.Console.Write("ran"); } + } + [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + public static void M(this int i, long l) => throw null; +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : ORPA should look at the containing static class (rather than look at the extension declaration) as the "containing type" + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M(System.Int32 j)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveLowerPriorityMembers_04() + { + var source = """ +42.M(43); + +static class E1 +{ + public static void M(this int i, int j) { System.Console.Write("ran"); } + extension(int i) + { + [System.Runtime.CompilerServices.OverloadResolutionPriority(1)] + public void M(long l) => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : ORPA should look at the containing static class (rather than look at the extension declaration) as the "containing type" + var comp = CreateCompilation([source, OverloadResolutionPriorityAttributeDefinition]); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void System.Int32.M(System.Int32 j)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveStaticInstanceMismatches_01() + { + var src = """ +using N; + +42.M(); + +static class E1 +{ + extension(object o) + { + public static void M() => throw null; // skipped + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void N.E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveStaticInstanceMismatches_02() + { + var src = """ +using N; + +42.M(); + +static class E1 +{ + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public void M() => throw null; + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // using N; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(1, 1)); + CompileAndVerify(comp, expectedOutput: "ran"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveStaticInstanceMismatches_03() + { + var src = """ +using N; + +int.M(); + +static class E1 +{ + extension(object o) + { + public void M() => throw null; + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public static void M() { System.Console.Write("ran"); } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal("void N.E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveStaticInstanceMismatches_ColorColor_01() + { + var src = """ +using N; + +Color.M2(null); + +class Color +{ + public static void M2(Color Color) + { + Color.M(); + } +} + +static class E1 +{ + extension(Color) + { + public static void M() { System.Console.Write("ran"); } + } +} + +namespace N +{ + static class E2 + { + extension(Color c) + { + public void M() => throw null; + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // using N; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(1, 1)); + + CompileAndVerify(comp, expectedOutput: "ran"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(memberAccess.Expression).Symbol.Kind); + } + + [Fact] + public void MethodInvocation_RemoveStaticInstanceMismatches_ColorColor_02() + { + var src = """ +using N; + +Color.M2(new Color()); + +class Color +{ + public static void M2(Color Color) + { + Color.M(); + } +} + +static class E1 +{ + extension(Color c) + { + public void M() { System.Console.Write("ran"); } + } +} + +namespace N +{ + static class E2 + { + extension(Color) + { + public static void M() => throw null; + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): hidden CS8019: Unnecessary using directive. + // using N; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N;").WithLocation(1, 1)); + CompileAndVerify(comp, expectedOutput: "ran"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Parameter, model.GetSymbolInfo(memberAccess.Expression).Symbol.Kind); + } + + [Fact] + public void MethodInvocation_RemoveInaccessibleTypeArguments() + { + var src = """ +int.M(new A.C()); + +static class E1 +{ + extension(int) + { + public static void M(I x) { } + } +} + +interface I { } + +class A +{ + private class B { } + public class C : I { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0122: 'E1.extension(int).M(I)' is inaccessible due to its protection level + // int.M(new A.C()); + Diagnostic(ErrorCode.ERR_BadAccess, "M").WithArguments("E1.extension(int).M(I)").WithLocation(1, 5)); + } + + [Fact] + public void MethodInvocation_RemoveLessDerivedMembers() + { + var src = """ +"".M(""); + +static class E +{ + extension(object o) + { + public void M(string s) { } + } + extension(string s) + { + public void M(object o) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0121: The call is ambiguous between the following methods or properties: 'E.extension(object).M(string)' and 'E.extension(string).M(object)' + // "".M(""); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E.extension(object).M(string)", "E.extension(string).M(object)").WithLocation(1, 4)); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_01() + { + var src = """ +I i = null; +i.M(); + +static class E1 +{ + extension(I i) + { + public void M() { } + } +} +static class E2 +{ + extension(I i) + { + public void M() { } + } +} +interface I { } +class C1 { } +class C2 : C1 { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "i.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_02() + { + var src = """ +I.M(); + +static class E1 +{ + extension(I) + { + public static void M() { } + } +} +static class E2 +{ + extension(I) + { + public static void M() { } + } +} +interface I { } +class C1 { } +class C2 : C1 { } +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm whether we want this betterness behavior (for methods and/or properties) + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "I.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_03() + { + var src = """ +42.M(); + +static class E1 +{ + extension(T t) + { + public void M() { } + } +} +static class E2 +{ + extension(int i) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_04() + { + var src = """ +int.M(); + +static class E1 +{ + extension(T) + { + public static void M() { } + } +} +static class E2 +{ + extension(int) + { + public static void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_05() + { + var src = """ +42.M(); + +static class E1 +{ + extension(int i) + { + public void M() { } + } +} +static class E2 +{ + extension(in int i) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_06() + { + var src = """ +int.M(); + +static class E1 +{ + extension(int) + { + public static void M() { } + } +} +static class E2 +{ + extension(in int i) + { + public static void M() => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm whether we want this betterness behavior (for methods and/or properties) + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal("void E1.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_07() + { + var src = """ +int.M(42); + +static class E1 +{ + extension(int) + { + public static void M(T t) { } + } +} +static class E2 +{ + extension(T t) + { + public static void M(int i) => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm whether we want this betterness behavior for methods + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(int).M(T)' and 'E2.extension(T).M(int)' + // int.M(42); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E1.extension(int).M(T)", "E2.extension(T).M(int)").WithLocation(1, 5)); + } + + [Fact] + public void MethodInvocation_RemoveWorseMembers_08() + { + var src = """ +int.M(42); +0.M2(42); + +static class E1 +{ + extension(T t) + { + public static void M(U u) => throw null; + public void M2(U u) => throw null; + } +} +static class E2 +{ + extension(T t) + { + public static void M(int i) { System.Console.Write("ran "); } + public void M2(int i) { System.Console.Write("ran2"); } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm whether we want this betterness behavior for methods when the receiver is a type + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran ran2").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.M"); + Assert.Equal("void E2.<>E__0.M(System.Int32 i)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_ByValueArgumentForRefReadonlyParameter_01() + { + var src = """ +int.M(42); + +static class E +{ + extension(int) + { + public static void M(ref readonly int i) { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,7): warning CS9193: Argument 1 should be a variable because it is passed to a 'ref readonly' parameter + // int.M(42); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "42").WithArguments("1").WithLocation(1, 7)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void MethodInvocation_ByValueArgumentForRefReadonlyParameter_02() + { + var src = """ +int i = 42; +int.M(i); + +static class E +{ + extension(int) + { + public static void M(ref readonly int i) { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,7): warning CS9192: Argument 1 should be passed with 'ref' or 'in' keyword + // int.M(i); + Diagnostic(ErrorCode.WRN_ArgExpectedRefOrIn, "i").WithArguments("1").WithLocation(2, 7)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void MethodInvocation_ByValueArgumentForRefReadonlyParameter_03() + { + var src = """ +var f = (ref readonly int i) => int.M(i); +int i = 42; +f(ref i); + + +static class E +{ + extension(int) + { + public static void M(ref readonly int i) { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,39): warning CS9195: Argument 1 should be passed with the 'in' keyword + // var f = (ref readonly int i) => int.M(i); + Diagnostic(ErrorCode.WRN_ArgExpectedIn, "i").WithArguments("1").WithLocation(1, 39)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void MethodInvocation_RefArgumentForInParameter() + { + var src = """ +int i = 42; +int.M(ref i); + +static class E +{ + extension(int) + { + public static void M(in int i) { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,11): warning CS9191: The 'ref' modifier for argument 1 corresponding to 'in' parameter is equivalent to 'in'. Consider using 'in' instead. + // int.M(ref i); + Diagnostic(ErrorCode.WRN_BadArgRef, "i").WithArguments("1").WithLocation(2, 11)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void MethodInvocation_ByValueReceiverForRefReadonlyParameter_01() + { + var src = """ +42.M(); + +static class E +{ + extension(ref readonly int i) + { + public void M() { System.Console.Write(42); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // 42.M(); + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "42").WithArguments("0").WithLocation(1, 1)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void MethodInvocation_ByValueReceiverForRefReadonlyParameter_02() + { + var src = """ +int i = 42; +i.M(); + +static class E +{ + extension(ref readonly int i) + { + public void M() { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_ByValueReceiverForRefReadonlyParameter_03() + { + var src = """ +var f = (ref readonly int i) => i.M(); +int i = 42; +f(ref i); + +static class E +{ + extension(ref readonly int i) + { + public void M() { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_Params() + { + var src = """ +int.M(42, 43); + +static class E +{ + extension(int) + { + public static void M(params int[] i) { System.Console.Write((i[0], i[1])); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_Params_02() + { + var src = """ +int.M([42, 43]); + +static class E +{ + extension(int) + { + public static void M(params int[] i) { System.Console.Write((i[0], i[1])); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_Params_03() + { + var src = """ +int.M([42, 43]); + +static class E +{ + extension(int) + { + public static void M(params long[] l) { System.Console.Write((l[0], l[1])); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "(42, 43)").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_DefaultValue() + { + var src = """ +int.M(); + +static class E +{ + extension(int) + { + public static void M(int i = 42) { System.Console.Write(i); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void MethodInvocation_DefaultValue_02() + { + var src = """ +42.M(); + +static class E +{ + extension(int i = 0) + { + public void M(int j = 1) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(int i = 0) + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "int i = 0").WithLocation(5, 15)); + } + + [Fact] + public void MethodInvocation_InaccessibleTypeArguments() + { + var src = """ +new A.C().M(); +new A.C().M2(); + +static class E1 +{ + extension(I i) + { + public void M() { } + } + public static void M2(this I i) { } +} + +interface I { } + +class A +{ + private class B { } + public class C : I { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,11): error CS0122: 'E1.extension(I).M()' is inaccessible due to its protection level + // new A.C().M(); + Diagnostic(ErrorCode.ERR_BadAccess, "M").WithArguments("E1.extension(I).M()").WithLocation(1, 11), + // (2,11): error CS0122: 'E1.M2(I)' is inaccessible due to its protection level + // new A.C().M2(); + Diagnostic(ErrorCode.ERR_BadAccess, "M2").WithArguments("E1.M2(I)").WithLocation(2, 11)); + } + + [Fact] + public void PropertyAccess_InaccessibleTypeArguments() + { + var src = """ +_ = new A.C().P; + +static class E1 +{ + extension(I i) + { + public int P => 0; + } +} + +interface I { } + +class A +{ + private class B { } + public class C : I { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS9286: 'A.C' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'A.C' could be found (are you missing a using directive or an assembly reference?) + // _ = new A.C().P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "new A.C().P").WithArguments("A.C", "P").WithLocation(1, 5)); + } + + [Fact] + public void MethodInvocation_ReceiverConversion() + { + var src = """ +42.M(); + +static class E +{ + extension(object o) + { + public void M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var literal = GetSyntax(tree, "42"); + Assert.Equal("System.Int32", model.GetTypeInfo(literal).Type.ToTestDisplayString()); + Assert.Equal("System.Object", model.GetTypeInfo(literal).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_ReceiverConversion_ColorColor() + { + var src = """ +Color.M2(new Color(42)); + +class Color(int i) : Base(i) +{ + public static void M2(Color Color) + { + Color.M(); + } +} + +class Base(int i) { public int value = i; } + +static class E +{ + extension(Base b) + { + public void M() { System.Console.Write(b.value); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var color = GetSyntax(tree, "Color.M").Expression; + Assert.Equal("Color", model.GetTypeInfo(color).Type.ToTestDisplayString()); + Assert.Equal("Base", model.GetTypeInfo(color).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void PropertyAccess_ReceiverConversion() + { + var src = """ +_ = 42.P; + +static class E +{ + extension(object o) + { + public int P => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var literal = GetSyntax(tree, "42"); + Assert.Equal("System.Int32", model.GetTypeInfo(literal).Type.ToTestDisplayString()); + Assert.Equal("System.Object", model.GetTypeInfo(literal).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void PropertyAccess_ReceiverConversion_ColorColor() + { + var src = """ +Color.M2(new Color(42)); + +class Color(int i) : Base(i) +{ + public static void M2(Color Color) + { + _ = Color.P; + } +} + +class Base(int i) { public int value = i; } + +static class E +{ + extension(Base b) + { + public int P { get { System.Console.Write(b.value); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var color = GetSyntax(tree, "Color.P").Expression; + Assert.Equal("Color", model.GetTypeInfo(color).Type.ToTestDisplayString()); + Assert.Equal("Base", model.GetTypeInfo(color).ConvertedType.ToTestDisplayString()); + } + + [Fact] + public void MethodInvocation_BrokenConstraint_01() + { + var src = """ +42.M("", null); + +static class E +{ + extension(object o) + { + public void M(T t, C c) where T : struct => throw null; + } +} + +class C where U : struct { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.extension(object).M(T, C)' + // 42.M("", null); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("E.extension(object).M(T, C)", "T", "string").WithLocation(1, 4)); + } + + [Fact] + public void MethodInvocation_BrokenConstraint_02() + { + var src = """ +42.M(null, ""); + +static class E +{ + extension(object o) + { + public void M(C c, T t) where T : struct => throw null; + } +} + +class C where U : struct { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0453: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.extension(object).M(C, T)' + // 42.M(null, ""); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("E.extension(object).M(C, T)", "T", "string").WithLocation(1, 4)); + } + + [Fact] + public void MethodInvocation_ExtraRef() + { + var src = """ +int i = 0; +int.M(ref i); + +static class E +{ + extension(int) + { + public static void M(int i) => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,11): error CS1615: Argument 2 may not be passed with the 'ref' keyword + // int.M(ref i); + Diagnostic(ErrorCode.ERR_BadArgExtraRef, "i").WithArguments("2", "ref").WithLocation(2, 11)); + } + + [Fact] + public void SingleCandidate_Extension() + { + string src = """ +public class C +{ + static void Main() + { + dynamic d = 1; + var result = new C().Test("name", d); + System.Console.Write(result); + } +} + +static class Extensions +{ + extension(C c) + { + public int Test(string name, object value) => 123; + } +} +"""; + + var comp = CreateCompilation(src, targetFramework: TargetFramework.StandardAndCSharp); + + comp.VerifyDiagnostics( + // (6,22): error CS1973: 'C' has no applicable method named 'Test' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // var result = new C().Test("name", d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, @"new C().Test(""name"", d)").WithArguments("C", "Test").WithLocation(6, 22)); + } + + [Fact] + public void ArgList_Error() + { + string source = """ +dynamic d = 1; +object.M(d); + +static class Extensions +{ + extension(object) + { + public static int M(__arglist) => 123; + } +} +"""; + CreateCompilation(source, targetFramework: TargetFramework.Net90).VerifyDiagnostics( + // (2,10): error CS1503: Argument 2: cannot convert from 'dynamic' to '__arglist' + // object.M(d); + Diagnostic(ErrorCode.ERR_BadArgType, "d").WithArguments("2", "dynamic", "__arglist").WithLocation(2, 10)); + } + + [Fact] + public void ArgList() + { + string source = """ +int i = 1; +object.M(__arglist(i)); + +static class Extensions +{ + extension(object) + { + public static void M(__arglist) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void MethodInvocation_ReceiverWithTupleDifferences() + { + string source = """ +C<(int, int other)>.M(); + +static class Extensions +{ + extension(C<(int a, int b)>) + { + public static void M() { System.Console.Write("ran"); } + } +} + +class C { } +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccess_RemoveStaticInstanceMismatches_InstanceReceiver() + { + var source = """ +System.Console.Write(42.P); + +static class E1 +{ + extension(int i) + { + public int P => 43; + } +} +static class E2 +{ + extension(int) + { + public static int P => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "43").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void PropertyAccess_RemoveStaticInstanceMismatches_StaticReceiver() + { + var source = """ +System.Console.Write(int.P); + +static class E1 +{ + extension(int i) + { + public int P => throw null; + } +} +static class E2 +{ + extension(int) + { + public static int P => 42; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.P"); + Assert.Equal("System.Int32 E2.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveStaticInstanceMismatches_ColorColor_01() + { + var source = """ +class Color +{ + static void M(Color Color) + { + _ = Color.P; + } +} + +static class E1 +{ + extension(Color c) + { + public int P => 0; + } +} +static class E2 +{ + extension(Color) + { + public static int P => 0; + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,13): error CS9286: 'Color' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'Color' could be found (are you missing a using directive or an assembly reference?) + // _ = Color.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "Color.P").WithArguments("Color", "P").WithLocation(5, 13)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.P"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Int32 E1.<>E__0.P { get; }", "System.Int32 E2.<>E__0.P { get; }"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveStaticInstanceMismatches_ColorColor_02() + { + var source = """ +Color.M(new Color()); + +class Color +{ + public static void M(Color Color) + { + _ = Color.P; + } +} + +static class E1 +{ + extension(Color c) + { + public int P { get { System.Console.Write("ran"); return 0; } } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal("Color Color", model.GetSymbolInfo(memberAccess.Expression).Symbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.Parameter, model.GetSymbolInfo(memberAccess.Expression).Symbol.Kind); + } + + [Fact] + public void PropertyAccess_RemoveStaticInstanceMismatches_ColorColor_03() + { + var source = """ +Color.M(null); + +class Color +{ + public static void M(Color Color) + { + _ = Color.P; + } +} + +static class E1 +{ + extension(Color c) + { + public static int P { get { System.Console.Write("ran"); return 0; } } + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "Color.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal("Color", model.GetSymbolInfo(memberAccess.Expression).Symbol.ToTestDisplayString()); + Assert.Equal(SymbolKind.NamedType, model.GetSymbolInfo(memberAccess.Expression).Symbol.Kind); + } + + [Fact] + public void RefOmittedComCall() + { + // For COM import type, omitting the ref is allowed + string source = @" +using System; +using System.Runtime.InteropServices; + +short x = 123; +C c = new C(); +c.M(x); +c.I(123); + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +class C { } + +static class E +{ + extension(C c) + { + public void M(ref short p) { } + public void M(sbyte p) { } + public void I(ref int p) { } + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void RefOmittedComCall_02() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +C c = default; +c.M(); +c.M2(); + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +class C { } + +static class E +{ + extension(ref C c) + { + public void M() { } + } + public static void M2(this ref C c) { } +} +"; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : missing ERR_RefExtensionMustBeValueTypeOrConstrainedToOne on the extension parameter + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,3): error CS1061: 'C' does not contain a definition for 'M2' and no accessible extension method 'M2' accepting a first argument of type 'C' could be found (are you missing a using directive or an assembly reference?) + // c.M2(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M2").WithArguments("C", "M2").WithLocation(7, 3), + // (18,24): error CS8337: The first parameter of a 'ref' extension method 'M2' must be a value type or a generic type constrained to struct. + // public static void M2(this ref C c) { } + Diagnostic(ErrorCode.ERR_RefExtensionMustBeValueTypeOrConstrainedToOne, "M2").WithArguments("M2").WithLocation(18, 24)); + } + + [Fact] + public void RefOmittedComCall_03() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +C c = default; +c.M(); +c.M2(); + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +struct C { } + +static class E +{ + extension(ref C c) + { + public void M() { } + } + public static void M2(this ref C c) { } +} +"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,2): error CS0592: Attribute 'ComImport' is not valid on this declaration type. It is only valid on 'class, interface' declarations. + // [ComImport, Guid("1234C65D-1234-447A-B786-64682CBEF136")] + Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "ComImport").WithArguments("ComImport", "class, interface").WithLocation(9, 2)); + } + + [Fact] + public void RefOmittedComCall_04() + { + // For COM import type, omitting the ref is allowed (even in static scenarios) + string source = @" +using System; +using System.Runtime.InteropServices; + +short x = 42; +short y = 43; +C.M(x.ToString(), y.ToString()); + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +class C { } + +static class E +{ + extension(C) + { + public static void M(ref string p, ref string p2) { } + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp); + verifier.VerifyIL("", """ +{ + // Code size 32 (0x20) + .maxstack 2 + .locals init (short V_0, //x + short V_1, //y + string V_2, + string V_3) + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldc.i4.s 43 + IL_0005: stloc.1 + IL_0006: ldloca.s V_0 + IL_0008: call "string short.ToString()" + IL_000d: stloc.2 + IL_000e: ldloca.s V_2 + IL_0010: ldloca.s V_1 + IL_0012: call "string short.ToString()" + IL_0017: stloc.3 + IL_0018: ldloca.s V_3 + IL_001a: call "void E.M(ref string, ref string)" + IL_001f: ret +} +"""); + + source = """ +using System; +using System.Runtime.InteropServices; + +short x = 42; +C.M(x.ToString()); + +[ComImport, Guid("1234C65D-1234-447A-B786-64682CBEF136")] +class C +{ + public extern static void M(ref string p); +} +"""; + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); + verifier.VerifyIL("", """ +{ + // Code size 19 (0x13) + .maxstack 1 + .locals init (short V_0, //x + string V_1) + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call "string short.ToString()" + IL_000a: stloc.1 + IL_000b: ldloca.s V_1 + IL_000d: call "void C.M(ref string)" + IL_0012: ret +} +"""); + } + + [Fact] + public void RefOmittedComCall_05() + { + string source = @" +using System; +using System.Runtime.InteropServices; + +short x = 42; +C.M(x.ToString()); + +[ComImport, Guid(""1234C65D-1234-447A-B786-64682CBEF136"")] +class C { } + +static class E +{ + extension(C) + { + public static void M(string p) { } + } +} +"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + var verifier = CompileAndVerify(comp); + verifier.VerifyIL("", """ +{ + // Code size 16 (0x10) + .maxstack 1 + .locals init (short V_0) //x + IL_0000: ldc.i4.s 42 + IL_0002: stloc.0 + IL_0003: ldloca.s V_0 + IL_0005: call "string short.ToString()" + IL_000a: call "void E.M(string)" + IL_000f: ret +} +"""); + } + + [Fact] + public void Nullability_Method_01() + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Nullability is undone + string source = """ +#nullable enable + +string? s = null; +s.M(); + +static class E +{ + extension(T t) + { + public void M() { System.Console.Write(t is null); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,1): warning CS8602: Dereference of a possibly null reference. + // s.M(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(4, 1)); + CompileAndVerify(comp, expectedOutput: "True"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void Nullability_Method_02() + { + string source = """ +#nullable enable + +string s = ""; +s.M(null); + +static class E +{ + extension(T t) + { + public void M(T t2) { System.Console.Write(t2 is null); } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "True"); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : Nullability is undone + Assert.Equal("void E.<>E__0.M(System.String t2)", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_01() + { + string source = """ +string s = ""; +System.Console.Write(s.P); + +static class E +{ + extension(string s) + { + public int P => 42; + } + extension(object o) + { + public int P => throw null; + } +} +"""; + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.P"); + Assert.Equal("System.Int32 E.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_02() + { + string source = """ +System.Console.Write(string.P); + +static class E1 +{ + extension(string s) + { + public static int P => 42; + } +} +static class E2 +{ + extension(object o) + { + public static int P => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm what betterness behavior we want for static properties + var comp = CreateCompilation(source); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "string.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_03() + { + var src = """ +I i = null; +System.Console.Write(i.P); + +interface I { } + +static class E +{ + extension(I i) + { + public int P => 42; + } + extension(I i) + { + public int P => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm what betterness behavior we want for properties + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "i.P"); + Assert.Equal("System.Int32 E.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_04() + { + var src = """ +I i = null; +System.Console.Write(i.P); + +interface I { } + +static class E +{ + extension(I i) + { + public int P => throw null; + } + extension(I i) + { + public int P => 42; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm what betterness behavior we want for properties + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "i.P"); + Assert.Equal("System.Int32 E.<>E__1.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_05() + { + var src = """ +System.Console.Write(42.P); + +static class E1 +{ + extension(int i) + { + public int P => 42; + } +} +static class E2 +{ + extension(in int i) + { + public int P => throw null; + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : confirm what betterness behavior we want for properties + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_06() + { + var src = """ +System.Console.Write(int.P); + +static class E1 +{ + extension(int i) + { + public static int P => 42; + } +} + +static class E2 +{ + extension(in int i) + { + public static int P => throw null; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_RemoveWorseMembers_07() + { + var src = """ +System.Console.Write(int.P); + +static class E1 +{ + extension(int i) + { + public static int P => 42; + } +} + +static class E2 +{ + extension(T t) + { + public static int P => throw null; + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "int.P"); + Assert.Equal("System.Int32 E1.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyOrMethod_RemoveWorseMembers_01() + { + var src = """ +string s = null; +s.M(); + +static class E +{ + extension(string s) + { + public System.Action M => throw null; + } + extension(object o) + { + public string M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS9286: 'string' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'string' could be found (are you missing a using directive or an assembly reference?) + // s.M(); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "s.M").WithArguments("string", "M").WithLocation(2, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Action E.<>E__0.M { get; }", "System.String E.<>E__1.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyOrMethod_RemoveWorseMembers_02() + { + var src = """ +string.M(); + +static class E +{ + extension(object) + { + public static System.Action M => throw null; + } + extension(string) + { + public static string M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9286: 'string' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'string' could be found (are you missing a using directive or an assembly reference?) + // string.M(); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "string.M").WithArguments("string", "M").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "string.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Action E.<>E__0.M { get; }", "System.String E.<>E__1.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyOrMethod_RemoveWorseMembers_03() + { + var src = """ +I i = null; +i.M(); + +interface I { } + +static class E +{ + extension(I i) + { + public System.Action M => throw null; + } + extension(I i) + { + public string M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS9286: 'I' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'I' could be found (are you missing a using directive or an assembly reference?) + // i.M(); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "i.M").WithArguments("I", "M").WithLocation(2, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "i.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Action E.<>E__0.M { get; }", "System.String E.<>E__1.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + } + + [Fact] + public void PropertyOrMethod_RemoveWorseMembers_04() + { + var src = """ +I i = null; +i.M(); + +interface I { } + +static class E1 +{ + extension(I i) + { + public System.Action M => throw null; + } +} + +static class E2 +{ + extension(I i) + { + public string M() => throw null; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS9286: 'I' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'I' could be found (are you missing a using directive or an assembly reference?) + // i.M(); + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "i.M").WithArguments("I", "M").WithLocation(2, 1)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "i.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["System.Action E1.<>E__0.M { get; }", "System.String E2.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Empty(model.GetMemberGroup(memberAccess)); + } + + [Fact] + public void Dynamic_01() + { + var src = """ +dynamic d = new object(); +object.M(d); +new object().M(d); + +static class E +{ + extension(U) + { + public static int M(T t) => throw null; + } + public static int M2(this U u, T t) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(U).M(T)' requires a receiver of type 'U' + // object.M(d); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "object").WithArguments("object", "M", "E.extension(U).M(T)", "U").WithLocation(2, 1), + // (3,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(U).M(T)' requires a receiver of type 'U' + // new object().M(d); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "new object()").WithArguments("object", "M", "E.extension(U).M(T)", "U").WithLocation(3, 1)); + } + + [Fact] + public void Dynamic_02() + { + var src = """ +dynamic d = new object(); +object.M(d); +new object().M2(d); + +static class E +{ + extension(object) + { + public static int M(T t) => throw null; + } + public static int M2(this object o, T t) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS1973: 'object' has no applicable method named 'M' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // object.M(d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "object.M(d)").WithArguments("object", "M").WithLocation(2, 1), + // (3,1): error CS1973: 'object' has no applicable method named 'M2' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // new object().M2(d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new object().M2(d)").WithArguments("object", "M2").WithLocation(3, 1)); + } + + [Fact] + public void Dynamic_03() + { + var src = """ +dynamic d = new object(); +object.M(d); +new object().M2(d); + +static class E +{ + extension(U) + { + public static int M(object o) => throw null; + } + public static int M2(this U u, object o) => throw null; +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(U).M(object)' requires a receiver of type 'U' + // object.M(d); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "object").WithArguments("object", "M", "E.extension(U).M(object)", "U").WithLocation(2, 1), + // (3,1): error CS1973: 'object' has no applicable method named 'M2' but appears to have an extension method by that name. Extension methods cannot be dynamically dispatched. Consider casting the dynamic arguments or calling the extension method without the extension method syntax. + // new object().M2(d); + Diagnostic(ErrorCode.ERR_BadArgTypeDynamicExtension, "new object().M2(d)").WithArguments("object", "M2").WithLocation(3, 1)); + } + + [Fact] + public void Dynamic_04() + { + var src = """ +int i = 42; +i.M(i); +i.M2(i); + +static class E +{ + extension(object o) + { + public void M(dynamic d) { System.Console.Write(d); } + } + + public static void M2(this object o, dynamic d) { } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("42"), verify: Verification.FailsPEVerify).VerifyDiagnostics(); + } + + [Fact] + public void Dynamic_05() + { + var src = """ +try +{ + dynamic d = 42; + _ = d.P; +} +catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e) +{ + System.Console.Write(e.Message); +} + +static class E +{ + extension(object o) + { + public int P => 0; + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("'int' does not contain a definition for 'P'"), verify: Verification.FailsPEVerify); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "d.P"); + var dynamicType = model.GetTypeInfo(memberAccess.Expression).Type; + Assert.True(dynamicType.IsDynamic()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.P { get; }"], model.LookupSymbols(position: 0, dynamicType, name: "P", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.P { get; }"], model.LookupSymbols(position: 0, dynamicType, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void Dynamic_06() + { + var src = """ +try +{ + dynamic d = 42; + d.M(); +} +catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException e) +{ + System.Console.Write(e.Message); +} + +static class E +{ + extension(object o) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("'int' does not contain a definition for 'M'"), verify: Verification.FailsPEVerify); + } + + [Fact] + public void ResolveExtension_01() + { + var src = """ +using N; + +System.Console.Write(object.M()); + +static class E1 +{ + extension(string) // inapplicable + { + public static System.Action M => null; + } +} + +namespace N +{ + static class E2 + { + extension(object) + { + public static int M() => 42; + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("42")).VerifyDiagnostics(); + } + + [Fact] + public void ResolveExtension_02() + { + var src = """ +using N; + +object.M(); + +static class E1 +{ + extension(string) // inapplicable + { + public static System.Action M => null; + } +} + +namespace N +{ + static class E2 + { + extension(object) + { + public static System.Action M => () => { System.Console.Write("ran"); }; + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran")).VerifyDiagnostics(); + } + + [Fact] + public void ResolveExtension_03() + { + var src = """ +using N; + +new object().M(); + +static class E1 +{ + public static void M(this string s) { } // inapplicable +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public System.Action M => () => { System.Console.Write("ran"); }; + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran")).VerifyDiagnostics(); + } + + [Fact] + public void ResolveExtension_04() + { + var src = """ +using N; + +new object().M(); + +static class E1 +{ + public static void M(this string s) { } // inapplicable +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran")).VerifyDiagnostics(); + } + + [Fact] + public void ResolveExtension_05() + { + var src = """ +using N; + +new object().M(); + +static class E1 +{ + extension(string s) // inapplicable + { + public void M() { } + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public System.Action M => () => { System.Console.Write("ran"); }; + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran")).VerifyDiagnostics(); + } + + [Fact] + public void ResolveExtension_06() + { + var src = """ +using N; + +new object().M(); + +static class E1 +{ + extension(string s) // inapplicable + { + public void M() { } + } +} + +namespace N +{ + static class E2 + { + extension(object o) + { + public void M() { System.Console.Write("ran"); } + } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: ExpectedOutput("ran")).VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccess_ByValueReceiverForRefReadonlyParameter_01() + { + var src = """ +_ = 42.P; + +static class E +{ + extension(ref readonly int i) + { + public int P { get { System.Console.Write(i); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): warning CS9193: Argument 0 should be a variable because it is passed to a 'ref readonly' parameter + // _ = 42.P; + Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "42").WithArguments("0").WithLocation(1, 5)); + + CompileAndVerify(comp, expectedOutput: "42"); + } + + [Fact] + public void PropertyAccess_ByValueReceiverForRefReadonlyParameter_02() + { + var src = """ +int i = 42; +_ = i.P; + +static class E +{ + extension(ref readonly int i) + { + public int P { get { System.Console.Write(i); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccess_ByValueReceiverForRefReadonlyParameter_03() + { + var src = """ +var f = (ref readonly int i) => i.P; +int i = 42; +f(ref i); + +static class E +{ + extension(ref readonly int i) + { + public int P { get { System.Console.Write(i); return 0; } } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "42").VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccess_AmbiguityWithNoArguments() + { + var src = """ +int x = new object().M; + +public static class E1 +{ + extension(object o) + { + public int M => 42; + } +} + +public static class E2 +{ + public static void M(this object o) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS9286: 'object' does not contain a definition for 'M' and no accessible extension member 'M' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // int x = new object().M; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "new object().M").WithArguments("object", "M").WithLocation(1, 9)); + } + + [Fact] + public void ConstAccess() + { + var src = """ +_ = int.Const; + +static class E +{ + extension(int) + { + public const int Const = 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS0117: 'int' does not contain a definition for 'Const' + // _ = int.Const; + Diagnostic(ErrorCode.ERR_NoSuchMember, "Const").WithArguments("int", "Const").WithLocation(1, 9), + // (7,26): error CS9282: Extension declarations can include only methods or properties + // public const int Const = 42; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Const").WithLocation(7, 26)); + } + + [Fact] + public void ParamsReceiver_01() + { + // extension(params int[] i) + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends System.Object + { + // Methods + .method private hidebysig specialname static void '$' ( int32[] i ) cil managed + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00) + + IL_0000: ret + } + .method public hidebysig instance void M () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + } + .method public hidebysig specialname static void 'M' ( int32[] i ) cil managed + { + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00) + + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ret + } +} +"""; + var src = """ +int[] i = null; +i.M(); +"""; + var comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran"); + + src = """ +int i = 0; +i.M(); +"""; + comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics( + // (2,1): error CS1929: 'int' does not contain a definition for 'M' and the best extension method overload 'E.extension(params int[]).M()' requires a receiver of type 'params int[]' + // i.M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "i").WithArguments("int", "M", "E.extension(params int[]).M()", "params int[]").WithLocation(2, 1)); + + src = """ +int i = 0; +i.M(2); +"""; + comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics( + // (2,3): error CS1501: No overload for method 'M' takes 1 arguments + // i.M(2); + Diagnostic(ErrorCode.ERR_BadArgCount, "M").WithArguments("M", "1").WithLocation(2, 3)); + } + + [Fact] + public void ParamsReceiver_02() + { + // extension(params int[] i) + var ilSrc = """ +.class public auto ansi abstract sealed beforefieldinit E + extends System.Object +{ + .custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( + 01 00 00 00 + ) + .class nested public auto ansi sealed beforefieldinit '<>E__0' + extends System.Object + { + .method private hidebysig specialname static void '$' ( int32[] i ) cil managed + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00) + + IL_0000: ret + } + .method public hidebysig specialname instance int32 get_P () cil managed + { + IL_0000: ldnull + IL_0001: throw + } + .property instance int32 P() + { + .get instance int32 E/'<>E__0'::get_P() + } + } + .method public hidebysig specialname static int32 'get_P' ( int32[] i ) cil managed + { + .param [1] + .custom instance void [mscorlib]System.ParamArrayAttribute::.ctor() = ( 01 00 00 00) + + IL_0000: ldstr "ran" + IL_0005: call void [mscorlib]System.Console::Write(string) + IL_000a: ldc.i4.0 + IL_000b: ret + } +} +"""; + var src = """ +int[] i = null; +_ = i.P; +"""; + var comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "ran"); + + src = """ +int i = 0; +_ = i.P; +"""; + comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics( + // (2,5): error CS9286: 'int' does not contain a definition for 'P' and no accessible extension member 'P' for receiver of type 'int' could be found (are you missing a using directive or an assembly reference?) + // _ = i.P; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "i.P").WithArguments("int", "P").WithLocation(2, 5)); + + src = """ +int i = 0; +_ = i.P(1); +"""; + comp = CreateCompilationWithIL(src, ilSrc); + comp.VerifyEmitDiagnostics( + // (2,7): error CS1061: 'int' does not contain a definition for 'P' and no accessible extension method 'P' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) + // _ = i.P(1); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "P").WithArguments("int", "P").WithLocation(2, 7)); + } + + [Fact] + public void ExplicitTypeArguments_01() + { + var src = """ +string s = "ran"; +s.M(); + +static class E +{ + extension(T t) + { + public void M() { System.Console.Write(t); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "s.M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_02() + { + var src = """ +42.M(); + +static class E +{ + extension(T t) where T : struct + { + public void M() { System.Console.Write(t); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.extension(T)' + // 42.M(); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("E.extension(T)", "T", "object").WithLocation(1, 4)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "42.M()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + Assert.Equal([], model.GetMemberGroup(invocation).ToTestDisplayStrings()); + + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_03() + { + var src = """ +string s = "ran"; +_ = s.P; + +static class E +{ + extension(T t) + { + public int P => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,7): error CS1061: 'string' does not contain a definition for 'P' and no accessible extension method 'P' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // _ = s.P; + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "P").WithArguments("string", "P").WithLocation(2, 7)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.P"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_04() + { + var src = """ +string s = "ran"; +s.M(); + +static class E +{ + extension(T t) + { + public void M() => throw null; + } + extension(string s) + { + public void M() { System.Console.Write(s); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__1.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_05() + { + var src = """ +string s = null; +s.M(); + +static class E +{ + extension(T t) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (2,3): error CS1061: 'string' does not contain a definition for 'M' and no accessible extension method 'M' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) + // s.M(); + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "M").WithArguments("string", "M").WithLocation(2, 3)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "s.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_06() + { + var src = """ +C.M(); + +class C { } +static class E +{ + extension(C t) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,19): error CS0117: 'C' does not contain a definition for 'M' + // C.M(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M").WithArguments("C", "M").WithLocation(1, 19)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_07() + { + var src = """ +string.M(42); + +static class E +{ + extension(T t) + { + public static void M(U u) { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "string.M(42)"); + Assert.Equal("void E.<>E__0.M(System.Int64 u)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var memberAccess = GetSyntax(tree, "string.M"); + Assert.Equal(["void E.<>E__0.M(System.Int64 u)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_08() + { + var src = """ +object.M(42); + +static class E +{ + extension(T t) + { + public static void M(U u) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(string).M(long)' requires a receiver of type 'string' + // object.M(42); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "object").WithArguments("object", "M", "E.extension(string).M(long)", "string").WithLocation(1, 1)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void ExplicitTypeArguments_09() + { + var src = """ +42.M(42); +42.M2(42); + +static class E +{ + extension(int i) + { + public void M(T t) where T : struct { } + } + + public static void M2(this int i, T t) where T : struct { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,4): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.extension(int).M(T)' + // 42.M(42); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M").WithArguments("E.extension(int).M(T)", "T", "object").WithLocation(1, 4), + // (2,4): error CS0453: The type 'object' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'E.M2(int, T)' + // 42.M2(42); + Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "M2").WithArguments("E.M2(int, T)", "T", "object").WithLocation(2, 4)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "42.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + memberAccess = GetSyntax(tree, "42.M2"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetDeclaredSymbol_01() + { + var src = """ +static class E +{ + extension(int i) + { + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var extensionParameter = tree.GetRoot().DescendantNodes().OfType().Single(); + + var symbol = model.GetDeclaredSymbol(extensionParameter); + Assert.Equal(SymbolKind.Parameter, symbol.Kind); + Assert.Equal("System.Int32 i", symbol.ToTestDisplayString()); + } + + readonly string[] _objectMembers = [ + "System.String System.Object.ToString()", + "System.Boolean System.Object.Equals(System.Object obj)", + "System.Boolean System.Object.Equals(System.Object objA, System.Object objB)", + "System.Int32 System.Object.GetHashCode()", + "System.Type System.Object.GetType()", + "System.Boolean System.Object.ReferenceEquals(System.Object objA, System.Object objB)"]; + + [Fact] + public void LookupSymbols_Simple() + { + var src = """ +object.M(); +_ = object.Property; + +public static class E +{ + extension(object) + { + public static void M() => throw null; + public static int Property => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "object.M()"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + var property = GetSyntax(tree, "object.Property"); + Assert.Equal("System.Int32 E.<>E__0.Property { get; }", model.GetSymbolInfo(property).Symbol.ToTestDisplayString()); + + var e = ((Compilation)comp).GlobalNamespace.GetTypeMember("E"); + AssertEqualAndNoDuplicates(["void E.M()"], model.LookupSymbols(position: 0, e, name: "M").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.M()", "void E.<>E__0.M()"], model.LookupSymbols(position: 0, e, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, e, name: "Property").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.Property { get; }"], model.LookupSymbols(position: 0, e, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["System.Int32 E.get_Property()"], model.LookupSymbols(position: 0, e, name: "get_Property").ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["System.Int32 E.get_Property()"], + model.LookupSymbols(position: 0, e, name: "get_Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.<>E__0.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Property").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.Property { get; }"], model.LookupSymbols(position: 0, o, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void E.<>E__0.M()", "System.Int32 E.<>E__0.Property { get; }", .. _objectMembers], + model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + Assert.Equal([ + "System.Boolean System.Object.Equals(System.Object objA, System.Object objB)", + "System.Boolean System.Object.ReferenceEquals(System.Object objA, System.Object objB)"], + model.LookupStaticMembers(position: 0, o, name: null).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : should we include extension static members? + + Assert.Empty(model.LookupNamespacesAndTypes(position: 0, o, name: null)); + } + + [Fact] + public void LookupSymbols_Inapplicable() + { + var src = """ +object.M(); +_ = object.Property; + +public static class E +{ + extension(string) + { + public static void M() => throw null; + public static int Property => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS1929: 'object' does not contain a definition for 'M' and the best extension method overload 'E.extension(string).M()' requires a receiver of type 'string' + // object.M(); + Diagnostic(ErrorCode.ERR_BadInstanceArgType, "object").WithArguments("object", "M", "E.extension(string).M()", "string").WithLocation(1, 1), + // (2,5): error CS9286: 'object' does not contain a definition for 'Property' and no accessible extension member 'Property' for receiver of type 'object' could be found (are you missing a using directive or an assembly reference?) + // _ = object.Property; + Diagnostic(ErrorCode.ERR_ExtensionResolutionFailed, "object.Property").WithArguments("object", "Property").WithLocation(2, 5)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Property").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(_objectMembers, model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_Substituted_01() + { + var src = """ +string.M(); +_ = string.Property; + +public static class E +{ + extension(T) + { + public static void M() => throw null; + public static int Property => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.<>E__0.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Property").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.Property { get; }"], model.LookupSymbols(position: 0, o, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void E.<>E__0.M()", "System.Int32 E.<>E__0.Property { get; }", .. _objectMembers], + model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + var s = ((Compilation)comp).GetSpecialType(SpecialType.System_String); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, s, name: "M").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.<>E__0.M()"], model.LookupSymbols(position: 0, s, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, s, name: "Property").ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.Property { get; }"], model.LookupSymbols(position: 0, s, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_Substituted_02() + { + var src = """ +string.M(42); + +public static class E +{ + extension(T) + { + public static void M(U u) => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var s = ((Compilation)comp).GetSpecialType(SpecialType.System_String); + AssertEqualAndNoDuplicates(["void E.<>E__0.M(U u)"], model.LookupSymbols(position: 0, s, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_StaticAndInstance() + { + var src = """ +public static class E1 +{ + extension(object) + { + public static void M() => throw null; + public static int Property => throw null; + } +} +public static class E2 +{ + extension(object) + { + public void M() => throw null; + public int Property => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates(["void E1.<>E__0.M()", "void E2.<>E__0.M()"], + model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["System.Int32 E1.<>E__0.Property { get; }", "System.Int32 E2.<>E__0.Property { get; }"], + model.LookupSymbols(position: 0, o, name: "Property", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates([ + "void E1.<>E__0.M()", + "System.Int32 E1.<>E__0.Property { get; }", + "void E2.<>E__0.M()", + "System.Int32 E2.<>E__0.Property { get; }", + .. _objectMembers], model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_MethodAndProperty() + { + var src = """ +public static class E1 +{ + extension(object) + { + public static void MP() => throw null; + } +} +public static class E2 +{ + extension(object) + { + public int MP => throw null; + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates(["void E1.<>E__0.MP()", "System.Int32 E2.<>E__0.MP { get; }"], + model.LookupSymbols(position: 0, o, name: "MP", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void E1.<>E__0.MP()", "System.Int32 E2.<>E__0.MP { get; }", .. _objectMembers], + model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_ClassicAndNew() + { + var src = """ + +public static class E +{ + extension(object) + { + public static void M() => throw null; + public void M(string s) => throw null; + } + + public static void M(this object o, int i) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates(["void E.<>E__0.M()", "void E.<>E__0.M(System.String s)", "void System.Object.M(System.Int32 i)"], + model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void E.<>E__0.M()", "void E.<>E__0.M(System.String s)", "void System.Object.M(System.Int32 i)", .. _objectMembers], + model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + + [Fact] + public void LookupSymbols_NestedType() + { + var src = """ + +public static class E +{ + extension(object) + { + static class Nested { } + } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (6,22): error CS9282: Extension declarations can include only methods or properties + // static class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(6, 22)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "Nested", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([.. _objectMembers], model.LookupSymbols(position: 0, o, name: null, includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + Assert.Empty(model.LookupNamespacesAndTypes(position: 0, o, name: null)); + } + + [Fact] + public void LookupSymbols_ExtensionParameter() + { + var src = """ +public static class E +{ + extension(object o) + { + } +} +"""; + + var comp = CreateCompilation(src); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var extension = tree.GetRoot().DescendantNodes().OfType().Single(); + + int position = extension.OpenBraceToken.EndPosition; + AssertEqualAndNoDuplicates(["System.Object o"], model.LookupSymbols(position, null, name: "o").ToTestDisplayStrings()); + AssertEx.Equal("System.Object o", model.LookupSymbols(position, null, name: null).OfType().Single().ToTestDisplayString()); + Assert.Empty(model.LookupNamespacesAndTypes(position, null, name: "o")); + + position = extension.OpenBraceToken.Position; + Assert.Empty(model.LookupSymbols(position, null, name: "o")); + Assert.Empty(model.LookupSymbols(position, null, name: null).OfType()); + } + + [Fact] + public void GetMemberGroup_01() + { + var src = """ +object.M(); +new object().M2(); + +static class E +{ + extension(object) + { + public static void M() where U : class { } + } + + public static void M2(this object o) where U : class { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'U' in the generic type or method 'E.extension(object).M()' + // object.M(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("E.extension(object).M()", "U", "int").WithLocation(1, 8), + // (2,14): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'U' in the generic type or method 'E.M2(object)' + // new object().M2(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M2").WithArguments("E.M2(object)", "U", "int").WithLocation(2, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "new object().M2"); + Assert.Equal([], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_02() + { + var src = """ +object.M(42); +new object().M2(42); + +static class E +{ + extension(object) + { + public static void M(U u) where U : class { } + } + + public static void M2(this object o, U u) where U : class { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'U' in the generic type or method 'E.extension(object).M(U)' + // object.M(42); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("E.extension(object).M(U)", "U", "int").WithLocation(1, 8), + // (2,14): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'U' in the generic type or method 'E.M2(object, U)' + // new object().M2(42); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M2").WithArguments("E.M2(object, U)", "U", "int").WithLocation(2, 14)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "object.M"); + Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "new object().M2"); + Assert.Equal(["void System.Object.M2(U u)"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_03() + { + var src = """ +int.M(); +42.M2(); + +static class E +{ + extension(T) where T : class + { + public static void M() { } + } + + public static void M2(this T t) where T : class { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'E.extension(T)' + // int.M(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("E.extension(T)", "T", "int").WithLocation(1, 5), + // (2,4): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'E.M2(T)' + // 42.M2(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M2").WithArguments("E.M2(T)", "T", "int").WithLocation(2, 4)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "int.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "42.M2"); + Assert.Equal([], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_04() + { + var src = """ +int.M(); +42.M2(); + +static class E +{ + extension(T) where T : class + { + public static void M() { } + } + + public static void M2(this T t) where T : class { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,5): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'E.extension(T)' + // int.M(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("E.extension(T)", "T", "int").WithLocation(1, 5), + // (2,4): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'E.M2(T)' + // 42.M2(); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M2").WithArguments("E.M2(T)", "T", "int").WithLocation(2, 4)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess1 = GetSyntax(tree, "int.M"); + Assert.Equal([], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntax(tree, "42.M2"); + Assert.Equal([], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_05() + { + var src = """ +object.M(); + +static class E +{ + extension(object) + { + public static void M() { } + public static void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_06() + { + var src = """ +_ = object.P; + +static class E +{ + extension(object) + { + public static int P => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "object.P"); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void GetMemberGroup_07() + { + var src = """ +object.M(); + +static class E1 +{ + extension(object) + { + public static int M => 0; + } +} +static class E2 +{ + extension(object) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal(["System.Int32 E1.<>E__0.M { get; }", "void E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_08() + { + var src = """ +string.M(); + +static class E1 +{ + extension(T) + { + public static int M => 0; + } +} +static class E2 +{ + extension(T) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "string.M"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["System.Int32 E1.<>E__0.M { get; }", "void E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + var invocation = GetSyntax(tree, "string.M()"); + Assert.Equal("void E2.<>E__0.M()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void GetMemberGroup_09() + { + var src = """ +string.M(); + +static class E1 +{ + extension(T) + { + public static void M() { } + } +} +static class E2 +{ + extension(T) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(T).M()' and 'E2.extension(T).M()' + // string.M(); + Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("E1.extension(T).M()", "E2.extension(T).M()").WithLocation(1, 8)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "string.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void E1.<>E__0.M()", "void E2.<>E__0.M()"], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E1.<>E__0.M()", "void E2.<>E__0.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_10() + { + var src = """ +System.Action a = new System.Action(object.M); + +static class E +{ + extension(object) + { + public static void M() { } + public static void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_11() + { + var src = """ +System.Action a = new System.Action(object.M); + +static class E +{ + extension(T) + { + public static void M() { } + public static void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetMemberGroup_12() + { + var src = """ +System.Action a = (System.Action)object.M; + +static class E +{ + extension(T) + { + public static void M() { } + public static void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void E.<>E__0.M()", "void E.<>E__0.M(System.Int32 i)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + + var cast = GetSyntax(tree, "(System.Action)object.M"); + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(cast).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(cast).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(cast).ToTestDisplayStrings()); + } + + [Fact] + public void ToBadExpression_01() + { + var source = """ +class A +{ + void F() { } +} + +class C +{ + static void M(A a) + { + a.F(); + M(a.F); + } + static void M(System.Action a) { } +} + +static class E1 +{ + static void F(this T t) { } +} + +static class E2 +{ + extension(T) + { + static void F() { } + } +} +"""; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,11): error CS0122: 'A.F()' is inaccessible due to its protection level + // a.F(); + Diagnostic(ErrorCode.ERR_BadAccess, "F").WithArguments("A.F()").WithLocation(10, 11), + // (11,13): error CS0122: 'A.F()' is inaccessible due to its protection level + // M(a.F); + Diagnostic(ErrorCode.ERR_BadAccess, "F").WithArguments("A.F()").WithLocation(11, 13)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntaxes(tree, "a.F").ToArray(); + Assert.Null(model.GetSymbolInfo(memberAccess[0]).Symbol); + Assert.Equal(["void A.F()"], model.GetSymbolInfo(memberAccess[0]).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void A.F()", "void E2.<>E__0.F()", "void A.F()"], model.GetMemberGroup(memberAccess[0]).ToTestDisplayStrings()); + + Assert.Null(model.GetSymbolInfo(memberAccess[1]).Symbol); + Assert.Equal(["void A.F()", "void E2.<>E__0.F()", "void A.F()"], model.GetSymbolInfo(memberAccess[1]).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal(["void A.F()", "void E2.<>E__0.F()", "void A.F()"], model.GetMemberGroup(memberAccess[1]).ToTestDisplayStrings()); + } + + [Fact] + public void PropertyAccess_NotAmbiguousWithInapplicableMethod() + { + var src = """ +int i = object.P; +System.Console.Write(i); + +static class E1 +{ + extension(T) where T : struct + { + public static void P() { } + } +} + +static class E2 +{ + extension(T) where T : class + { + public static int P => 42; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "object.P"); + Assert.Equal("System.Int32 E2.<>E__0.P { get; }", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); + Assert.Equal([], model.GetSymbolInfo(memberAccess).CandidateSymbols.ToTestDisplayStrings()); + Assert.Equal([], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); // Tracked by https://github.com/dotnet/roslyn/issues/76130 : handle GetMemberGroup on a property access + } + + [Fact] + public void GetSymbolInfo_GenericMethodInGenericType_01() + { + var src = """ +class C +{ + public static void M(T2 x) { } + public static void M(int x) { } +} + +class D +{ + public void Test() + { + C.M(1); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var genericName = GetSyntax(tree, "M"); + Assert.Equal("void C.M(System.Int32 x)", model.GetSymbolInfo(genericName).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M(System.Int32 x)"], model.GetMemberGroup(genericName).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_GenericMethodInGenericType_02() + { + var src = """ +class C +{ + public static void M(T2 x) where T2 : class { } +} + +class D +{ + public void Test() + { + C.M(1); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (10,16): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T2' in the generic type or method 'C.M(T2)' + // C.M(1); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("C.M(T2)", "T2", "int").WithLocation(10, 16)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var genericName = GetSyntax(tree, "M"); + Assert.Null(model.GetSymbolInfo(genericName).Symbol); + Assert.Equal([], model.GetMemberGroup(genericName).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_GenericMethodInGenericType_03() + { + var src = """ +class C +{ + public static void M(T2 x) where T2 : class { } +} + +class D +{ + public void Test() + { + C.M(1); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (10,16): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T2' in the generic type or method 'C.M(T2)' + // C.M(1); + Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "M").WithArguments("C.M(T2)", "T2", "int").WithLocation(10, 16)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var memberAccess = GetSyntax(tree, "C.M"); + Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); + Assert.Equal(["void C.M(T2 x)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_04() + { + var src = """ +class C +{ + private static void M() + { + M(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var genericName = GetSyntax(tree, "M()").Expression; + Assert.Equal("void C.M()", model.GetSymbolInfo(genericName).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()"], model.GetMemberGroup(genericName).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_05() + { + var src = """ +public static class E +{ + extension(T t) + { + static void M() + { + T.M(); + T.M(); + E.M(); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter + // T.M(); + Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(7, 13), + // (8,13): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter + // T.M(); + Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(8, 13)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var expr = GetSyntax(tree, "T.M()").Expression; + Assert.Null(model.GetSymbolInfo(expr).Symbol); + Assert.Equal([], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "T.M()").Expression; + Assert.Null(model.GetSymbolInfo(expr).Symbol); + Assert.Equal([], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "E.M()").Expression; + Assert.Equal("void E.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_06() + { + var src = """ +public static class E +{ + extension(T t) + { + void M() + { + t.M(); + t.M(); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void E.<>E__0.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_07() + { + var src = """ +public static class E +{ + extension(T t) + { + void M(U u) + { + t.M(u); + t.M(u); + + t.M(42); + 42.M(u); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var extensionParameterSyntax = tree.GetRoot().DescendantNodes().OfType().First(); + IParameterSymbol extensionParameter = model.GetDeclaredSymbol(extensionParameterSyntax); + Assert.Equal("T t", extensionParameter.ToTestDisplayString()); + var t = extensionParameter.Type; + + var expr = GetSyntax(tree, "t.M(u)").Expression; + Assert.Equal("void E.<>E__0.M(U u)", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void E.<>E__0.M(U u)"], model.LookupSymbols(position: expr.SpanStart, t, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "t.M(u)").Expression; + Assert.Equal("void E.<>E__0.M(U u)", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "t.M(42)").Expression; + Assert.Equal("void E.<>E__0.M(System.Int32 u)", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "42.M(u)").Expression; + Assert.Equal("void E.<>E__0.M(U u)", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void E.<>E__0.M(U u)"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + } + + [Fact] + public void GetSymbolInfo_08() + { + var src = """ +public static class E +{ + public static void M(this T t) + { + t.M(); + t.M(); + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + + var extensionParameterSyntax = tree.GetRoot().DescendantNodes().OfType().First(); + IParameterSymbol extensionParameter = model.GetDeclaredSymbol(extensionParameterSyntax); + Assert.Equal("T t", extensionParameter.ToTestDisplayString()); + var t = extensionParameter.Type; + + var expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void T.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void T.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + + AssertEqualAndNoDuplicates(["void T.M()"], model.LookupSymbols(position: expr.SpanStart, t, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + + expr = GetSyntax(tree, "t.M()").Expression; + Assert.Equal("void T.M()", model.GetSymbolInfo(expr).Symbol.ToTestDisplayString()); + Assert.Equal(["void T.M()"], model.GetMemberGroup(expr).ToTestDisplayStrings()); + } + + [Fact] + public void LangVer_01() + { + var libSrc = """ +public static class E +{ + extension(object) + { + public void M() { } + public static void M2() { } + public static int P => 0; + } +} + +"""; + var libComp = CreateCompilation(libSrc, parseOptions: TestOptions.RegularNext); + libComp.VerifyEmitDiagnostics(); + var libRef = libComp.EmitToImageReference(); + + var srcCompat = """ +new object().M(); +System.Action a = new object().M; +var x = new object().M; + +E.M(new object()); +E.get_P(); +E.M2(); +"""; + var comp = CreateCompilation(srcCompat, references: [libRef], parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(srcCompat, references: [libRef], parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + + comp = CreateCompilation(srcCompat, references: [libRef]); + comp.VerifyEmitDiagnostics(); + + var src = """ +object.M2(); +System.Action a = object.M2; +var x = object.M2; + +_ = object.P; +"""; + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (1,8): error CS0117: 'object' does not contain a definition for 'M2' + // object.M2(); + Diagnostic(ErrorCode.ERR_NoSuchMember, "M2").WithArguments("object", "M2").WithLocation(1, 8), + // (2,26): error CS0117: 'object' does not contain a definition for 'M2' + // System.Action a = object.M2; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M2").WithArguments("object", "M2").WithLocation(2, 26), + // (3,16): error CS0117: 'object' does not contain a definition for 'M2' + // var x = object.M2; + Diagnostic(ErrorCode.ERR_NoSuchMember, "M2").WithArguments("object", "M2").WithLocation(3, 16), + // (5,12): error CS0117: 'object' does not contain a definition for 'P' + // _ = object.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("object", "P").WithLocation(5, 12)); + verifySymbolInfo(comp, newLangVer: false); + + comp = CreateCompilation(src, references: [libRef], parseOptions: TestOptions.RegularNext); + comp.VerifyEmitDiagnostics(); + verifySymbolInfo(comp, newLangVer: true); + + comp = CreateCompilation(src, references: [libRef]); + comp.VerifyEmitDiagnostics(); + verifySymbolInfo(comp, newLangVer: true); + + static void verifySymbolInfo(CSharpCompilation comp, bool newLangVer) + { + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var o = ((Compilation)comp).GetSpecialType(SpecialType.System_Object); + + if (newLangVer) + { + AssertEqualAndNoDuplicates(["void E.<>E__0.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["void E.<>E__0.M2()"], model.LookupSymbols(position: 0, o, name: "M2", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates(["System.Int32 E.<>E__0.P { get; }"], model.LookupSymbols(position: 0, o, name: "P", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + else + { + AssertEqualAndNoDuplicates(["void System.Object.M()"], model.LookupSymbols(position: 0, o, name: "M", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "M2", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + AssertEqualAndNoDuplicates([], model.LookupSymbols(position: 0, o, name: "P", includeReducedExtensionMethods: true).ToTestDisplayStrings()); + } + } + } + + [Fact] + public void This_01() + { + var src = """ +static class E +{ + extension(object) + { + public void M() + { + var x = this; + this.ToString(); + local(); + + void local() + { + var y = this; + } + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,21): error CS0027: Keyword 'this' is not available in the current context + // var x = this; + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(7, 21), + // (8,13): error CS0027: Keyword 'this' is not available in the current context + // this.ToString(); + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(8, 13), + // (13,25): error CS0027: Keyword 'this' is not available in the current context + // var y = this; + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(13, 25)); + } + + [Fact] + public void This_02() + { + var src = """ +static class E +{ + extension(object o) + { + public void M() + { + M2(); + this.M2(); + M2(new object()); + } + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS7036: There is no argument given that corresponds to the required parameter 'o' of 'E.M2(object)' + // M2(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M2").WithArguments("o", "E.M2(object)").WithLocation(7, 13), + // (8,13): error CS0027: Keyword 'this' is not available in the current context + // this.M2(); + Diagnostic(ErrorCode.ERR_ThisInBadContext, "this").WithLocation(8, 13)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "M2()"); + Assert.Null(model.GetSymbolInfo(invocation).Symbol); + + invocation = GetSyntax(tree, "this.M2()"); + Assert.Equal("void E.<>E__0.M2()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + + invocation = GetSyntax(tree, "M2(new object())"); + Assert.Equal("void E.M2(this System.Object o)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void This_03() + { + var src = """ +static class E +{ + extension(object o) + { + public static void M() + { + M2(); + } + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS7036: There is no argument given that corresponds to the required parameter 'o' of 'E.M2(object)' + // M2(); + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M2").WithArguments("o", "E.M2(object)").WithLocation(7, 13)); + } + + [Fact] + public void This_04() + { + var src = """ +static class E +{ + extension(object o) + { + public void M() + { + M2(); + } + public static void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var invocation = GetSyntax(tree, "M2()"); + Assert.Equal("void E.M2()", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); + } + + [Fact] + public void ReceiverParameter_Access_01() + { + var src = """ +static class E +{ + extension(object o) + { + public void M() + { + o.M2(); + } + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Access_02() + { + var src = """ +static class E +{ + extension(object o) + { + public void M() + { + o.M2(); + } + public static void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS0176: Member 'E.extension(object).M2()' cannot be accessed with an instance reference; qualify it with a type name instead + // o.M2(); + Diagnostic(ErrorCode.ERR_ObjectProhibited, "o.M2").WithArguments("E.extension(object).M2()").WithLocation(7, 13)); + } + + [Fact] + public void ReceiverParameter_Access_03() + { + var src = """ +static class E +{ + extension(object o) + { + public static void M() + { + o.M2(); + } + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS9293: Cannot use extension parameter 'object o' in this context. + // o.M2(); + Diagnostic(ErrorCode.ERR_InvalidExtensionParameterReference, "o").WithArguments("object o").WithLocation(7, 13)); + } + + [Fact] + public void ReceiverParameter_Access_04() + { + var src = """ +static class E +{ + extension(object o) + { + public void M() + { + local(); + void local() + { + o.M2(); + } + } + public void M2() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void ReceiverParameter_Access_05() + { + var src = """ +static class E +{ + extension(object o, object o2) + { + public void M() + { + o2.ToString(); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,25): error CS9285: An extension container can have only one receiver parameter + // extension(object o, object o2) + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "object o2").WithLocation(3, 25), + // (7,13): error CS0103: The name 'o2' does not exist in the current context + // o2.ToString(); + Diagnostic(ErrorCode.ERR_NameNotInContext, "o2").WithArguments("o2").WithLocation(7, 13)); + } + + [Fact] + public void ReceiverParameter_Access_06() + { + var src = """ +static class E +{ + extension(System.Span s) + { + public async void M() + { + s.ToString(); + await System.Threading.Tasks.Task.Yield(); + s.ToString(); + } + } +} +"""; + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : we should produce this error instead: + // error CS4012: Parameters of type 'Span' cannot be declared in async methods or async lambda expressions. + var comp = CreateCompilation(src, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (7,13): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // s.ToString(); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s").WithArguments("System.Span").WithLocation(7, 13), + // (9,13): error CS4007: Instance of type 'System.Span' cannot be preserved across 'await' or 'yield' boundary. + // s.ToString(); + Diagnostic(ErrorCode.ERR_ByRefTypeAndAwait, "s").WithArguments("System.Span").WithLocation(9, 13)); + } + + [Fact] + public void ReceiverParameter_TypeParametersMustBeUsed() + { + var src = """ +int i = C.P; + +class C { } + +static class E +{ + extension(C) + { + static int P => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,11): error CS0117: 'C' does not contain a definition for 'P' + // int i = C.P; + Diagnostic(ErrorCode.ERR_NoSuchMember, "P").WithArguments("C", "P").WithLocation(1, 11), + // (7,21): error CS9295: The extended type 'C' must reference all the type parameters declared by the extension, but type parameter 'T' is not referenced. + // extension(C) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "C").WithArguments("C", "T").WithLocation(7, 21), + // (7,21): error CS9295: The extended type 'C' must reference all the type parameters declared by the extension, but type parameter 'U' is not referenced. + // extension(C) + Diagnostic(ErrorCode.ERR_UnderspecifiedExtension, "C").WithArguments("C", "U").WithLocation(7, 21)); + } + + public partial class RegionAnalysisTests : FlowTestBase + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider removing `this` from the region analysis tests + [Fact] + public void RegionAnalysis_01() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(int i) + { + public void M(int i2) + { + _ = /**/i + i2/**/; + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: i, i2 +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2 +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: i, i2 +ReadOutside: +WrittenInside: +WrittenOutside: i, this, i2 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_02() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(int i) + { + public void M(int i2) + { + /**/this.ToString()/**/; + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: this +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2 +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: this +ReadOutside: +WrittenInside: +WrittenOutside: i, this, i2 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_03() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(int i) + { + public void M(int i2) + { + int i3 = 0; +/**/ + System.Action = () => i + i2 + i3; +/**/ + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: +Captured: i, i2, i3 +CapturedInside: i, i2, i3 +CapturedOutside: +DataFlowsIn: i, i2, i3 +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2, i3 +DefinitelyAssignedOnExit: i, this, i2, i3 +ReadInside: i, i2, i3 +ReadOutside: +WrittenInside: +WrittenOutside: i, this, i2, i3 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_04() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(ref int i) + { + public void M(ref int i2) + { + _ = /**/i + i2/**/; + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: i, i2 +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2 +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: i, i2 +ReadOutside: i, i2 +WrittenInside: +WrittenOutside: i, this, i2 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_05() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(out int i) + { + public void M(out int i2) + { + /**/i = i2 = 0;/**/ + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: i, i2 +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: +DataFlowsOut: i, i2 +DefinitelyAssignedOnEntry: this +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: +ReadOutside: i, i2 +WrittenInside: i, i2 +WrittenOutside: this +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_06() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(int i) + { + public void M(int i2) + { + /**/i = i2 = 0;/**/ + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: i, i2 +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2 +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: +ReadOutside: +WrittenInside: i, i2 +WrittenOutside: i, this, i2 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_07() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(ref int i) + { + public void M(ref int i2) + { + /**/i = ref i2;/**/ + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: i +Captured: +CapturedInside: +CapturedOutside: +DataFlowsIn: i2 +DataFlowsOut: i, i2 +DefinitelyAssignedOnEntry: i, this, i2 +DefinitelyAssignedOnExit: i, this, i2 +ReadInside: i2 +ReadOutside: i, i2 +WrittenInside: i, i2 +WrittenOutside: i, this, i2 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_08() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(int i) + { + public void M(int i2) + { + void local(int i3) + { + _ = /**/i + i2 + i3/**/; + } + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: +Captured: i, i2 +CapturedInside: i, i2 +CapturedOutside: +DataFlowsIn: i3 +DataFlowsOut: +DefinitelyAssignedOnEntry: i, this, i2, i3 +DefinitelyAssignedOnExit: i, this, i2, i3 +ReadInside: i, i2, i3 +ReadOutside: +WrittenInside: +WrittenOutside: i, this, i2, i3 +""", analysisResults); + } + + [Fact] + public void RegionAnalysis_09() + { + var analysisResults = CompileAndAnalyzeDataFlowExpression(""" +static class E +{ + extension(out int i) + { + public void M(out int i2) + { + void local(out int i3) + { + /**/i = i2 = i3 = 0;/**/ + } + } + } +} +"""); + VerifyDataFlowAnalysis(""" +VariablesDeclared: +AlwaysAssigned: i, i2, i3 +Captured: i, i2 +CapturedInside: i, i2 +CapturedOutside: +DataFlowsIn: +DataFlowsOut: i, i2, i3 +DefinitelyAssignedOnEntry: this +DefinitelyAssignedOnExit: i, this, i2, i3 +ReadInside: +ReadOutside: i, i2, i3 +WrittenInside: i, i2, i3 +WrittenOutside: this +""", analysisResults); + } + } + + [Fact] + public void Base_01() + { + var src = """ +static class E +{ + extension(object) + { + public void M() + { + base.ToString(); + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (7,13): error CS1512: Keyword 'base' is not available in the current context + // base.ToString(); + Diagnostic(ErrorCode.ERR_BaseInBadContext, "base").WithLocation(7, 13)); + } + + [Fact] + public void FunctionType_TypeReceiver_01() + { + var src = """ +var x = int.M; +x(); + +public static class E +{ + extension(T t) + { + public static void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = int.M"); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_TypeReceiver_02() + { + var src = """ +var x = int.M; + +public static class E +{ + extension(T t) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var x = int.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "int.M").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = int.M"); + Assert.True(model.GetTypeInfo(localDeclaration.Type).Type.IsErrorType()); + } + + [Fact] + public void FunctionType_TypeReceiver_03() + { + var src = """ +var x = int.M; + +public static class E +{ + public static void M(this T t) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var x = int.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "int.M").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = int.M"); + Assert.True(model.GetTypeInfo(localDeclaration.Type).Type.IsErrorType()); + } + + [Fact] + public void FunctionType_InstanceReceiver_01() + { + var src = """ +var x = 42.M; + +public static class E +{ + extension(T t) + { + public void M() { System.Console.Write(t); } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS1113: Extension method 'E.extension(int).M()' defined on value type 'int' cannot be used to create delegates + // var x = 42.M; + Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "42.M").WithArguments("E.extension(int).M()", "int").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = 42.M"); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_InstanceReceiver_02() + { + var src = """ +var x = "ran".M; +x(); + +public static class E +{ + extension(T t) + { + public void M() { System.Console.Write(t); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, """var x = "ran".M"""); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_InstanceReceiver_03() + { + var src = """ +var x = 42.M; + +public static class E +{ + extension(T t) + { + public static void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var x = 42.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "42.M").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = 42.M"); + Assert.True(model.GetTypeInfo(localDeclaration.Type).Type.IsErrorType()); + } + + [Fact] + public void FunctionType_InstanceReceiver_04() + { + var src = """ +var x = 42.M; + +public static class E +{ + extension(int) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var x = 42.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "42.M").WithLocation(1, 9)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, "var x = 42.M"); + Assert.True(model.GetTypeInfo(localDeclaration.Type).Type.IsErrorType()); + } + + [Fact] + public void FunctionType_InstanceReceiver_05() + { + var src = """ +var x = "ran".M; +x(); + +public static class E +{ + extension(string s) + { + public void M() { System.Console.Write(s); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, """var x = "ran".M"""); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_InstanceReceiver_06() + { + var src = """ +var x = "ran".M; +x(); + +public static class E +{ + extension(string s) + { + public void M() { System.Console.Write(s); } + public void M(T t) { } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, """var x = "ran".M"""); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_InstanceReceiver_07() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + var x = "".M; + System.Console.Write(x); + } + } + + public static class E1 + { + extension(string s) + { + public void M() { } + } + } +} + +public static class E2 +{ + extension(string s) + { + public int M => 42; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (7,21): error CS8917: The delegate type could not be inferred. + // var x = "".M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, @""""".M").WithLocation(7, 21)); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, """var x = "".M"""); + Assert.True(model.GetTypeInfo(localDeclaration.Type).Type.IsErrorType()); + } + + [Fact] + public void FunctionType_InstanceReceiver_08() + { + var src = """ +namespace N +{ + public class C + { + public static void Main() + { + var x = "ran".M; + x(42); + } + } + + public static class E1 + { + extension(string s) + { + public void M() { } + } + } +} + +public static class E2 +{ + extension(string s) + { + public void M(int i) { System.Console.Write((s, i)); } + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: "(ran, 42)").VerifyDiagnostics(); + + var tree = comp.SyntaxTrees.First(); + var model = comp.GetSemanticModel(tree); + var localDeclaration = GetSyntax(tree, """var x = "ran".M"""); + Assert.Equal("System.Action", model.GetTypeInfo(localDeclaration.Type).Type.ToTestDisplayString()); + } + + [Fact] + public void FunctionType_InstanceReceiver_09() + { + var src = """ +var x = "ran".M; + +public static class E1 +{ + extension(string s) + { + public void M() { } + } +} + +public static class E2 +{ + extension(string s) + { + public void M(int i) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,9): error CS8917: The delegate type could not be inferred. + // var x = "ran".M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, @"""ran"".M").WithLocation(1, 9)); + } + + [Fact] + public void FunctionType_InstanceReceiver_10() + { + var src = """ +System.Delegate x = "ran".M; +x.DynamicInvoke(); + +id("ran".M)(); + +T id(T t) => t; + +public static class E +{ + extension(string s) + { + public void M() { System.Console.Write("ran "); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran ran").VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_InstanceReceiver_11() + { + var src = """ +using N; + +var x = "ran".M; + +public static class E1 +{ + extension(T t) where T : struct + { + public void M() { } + } +} + +namespace N +{ + public static class E2 + { + extension(T t) + { + public void M() { } + } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_ColorColorReceiver_01() + { + var src = """ +Color.M2(new Color()); + +public class Color +{ + public static void M2(Color Color) + { + var x = Color.M; + x(); + } +} + +public static class E +{ + extension(T t) + { + public void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_ColorColorReceiver_02() + { + var src = """ +Color.M2(null); + +public class Color +{ + public static void M2(Color Color) + { + var x = Color.M; + x(); + } +} + +public static class E +{ + extension(T) + { + public static void M() { System.Console.Write("ran"); } + } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_ColorColorReceiver_03() + { + var src = """ +Color.M2(new Color()); + +public class Color +{ + public static void M2(Color Color) + { + var x = Color.M; + x(); + } + public void M() { System.Console.Write("ran"); } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_ColorColorReceiver_04() + { + var src = """ +Color.M2(null); + +public class Color +{ + public static void M2(Color Color) + { + var x = Color.M; + x(); + } + public static void M() { System.Console.Write("ran"); } +} +"""; + var comp = CreateCompilation(src); + CompileAndVerify(comp, expectedOutput: "ran").VerifyDiagnostics(); + } + + [Fact] + public void FunctionType_ColorColorReceiver_05() + { + var src = """ +public class Color +{ + public static void M2(Color Color) + { + var x = Color.M; + } +} + +public static class E1 +{ + extension(T) + { + public static void M() { } + } +} + +public static class E2 +{ + extension(T) + { + public void M() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,17): error CS0121: The call is ambiguous between the following methods or properties: 'E1.extension(T).M()' and 'E2.extension(T).M()' + // var x = Color.M; + Diagnostic(ErrorCode.ERR_AmbigCall, "Color.M").WithArguments("E1.extension(T).M()", "E2.extension(T).M()").WithLocation(5, 17)); + } + + [Fact] + public void Params_ExtensionScopes_01() + { + var source = """ +static class E1 +{ + extension(N.C c) + { + public void M(int[] x) => System.Console.Write(3); + } +} + +namespace N +{ + static class E2 + { + extension(C c) + { + public void M(params int[] x) => System.Console.Write(2); + } + } + + class C + { + public void M(params int[] x) => System.Console.Write(1); + public static void Main() + { + var d = new C().M; + System.Console.WriteLine(d.GetType()); + d(); + } + } +} +"""; + + var expectedOutput = """ +<>f__AnonymousDelegate0`1[System.Int32] +1 +"""; + + CompileAndVerify(source, symbolValidator: validateSymbols, expectedOutput: expectedOutput).VerifyDiagnostics(); + + static void validateSymbols(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + Assert.Equal("void <>f__AnonymousDelegate0.Invoke(params T1[] arg)", m.ToTestDisplayString()); + } + } + + [Fact] + public void Params_ExtensionScopes_02() + { + var source = """ +static class E1 +{ + extension(N.C c) + { + public void M(params int[] x) => System.Console.Write(3); + } +} + +namespace N +{ + static class E2 + { + extension(C c) + { + public void M(params int[] x) => System.Console.Write(2); + } + } + + class C + { + public void M(params int[] x) => System.Console.Write(1); + public static void Main() + { + var d = new C().M; + System.Console.WriteLine(d.GetType()); + d(); + } + } +} +"""; + + var expectedOutput = """ +<>f__AnonymousDelegate0`1[System.Int32] +1 +"""; + + CompileAndVerify(source, symbolValidator: validateSymbols, expectedOutput: expectedOutput).VerifyDiagnostics(); + + static void validateSymbols(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + Assert.Equal("void <>f__AnonymousDelegate0.Invoke(params T1[] arg)", m.ToTestDisplayString()); + } + } + [Fact] + public void Params_ExtensionScopes_07() + { + var source = """ +static class E1 +{ + extension(N.C c) + { + public void M(params int[] x) => System.Console.Write(1); + } +} + +namespace N +{ + static class E2 + { + extension(C) + { + public void M(int[] x) => System.Console.Write(2); + } + } + + class C + { + public static void Main() + { + var d = new C().M; + System.Console.WriteLine(d.GetType()); + d(default); + } + } +} +"""; + + var expectedOutput = """ +System.Action`1[System.Int32[]] +2 +"""; + + CompileAndVerify(source, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact] + public void Params_ExtensionScopes_08() + { + var source = """ +static class E1 +{ + extension(N.C c) + { + public void M(int[] x) => System.Console.Write(1); + } +} + +namespace N +{ + static class E2 + { + extension(C c) + { + public void M(params int[] x) => System.Console.Write(2); + } + } + + class C + { + public static void Main() + { + var d = new C().M; + System.Console.WriteLine(d.GetType()); + d(); + } + } +} +"""; + + var expectedOutput = """ +<>f__AnonymousDelegate0`1[System.Int32] +2 +"""; + + CreateCompilation(source).VerifyDiagnostics(); + CompileAndVerify(source, symbolValidator: validateSymbols, expectedOutput: expectedOutput).VerifyDiagnostics(); + + static void validateSymbols(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + Assert.Equal("void <>f__AnonymousDelegate0.Invoke(params T1[] arg)", m.ToTestDisplayString()); + } + } + + [Fact] + public void ImplementsIEnumerableT_21_AddIsNotAnExtension() + { + var src = """ +using System.Collections; +using System.Collections.Generic; + +class MyCollection : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; +} + +static class Ext +{ + extension(MyCollection c) + { + public void Add(long l) {} + } +} + +class Program +{ + static void Main() + { + Test(); + Test(1); + Test(2, 3); + } +#line 24 + static void Test(params MyCollection a) + { + } + + static void Test2() + { + Test([2, 3]); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.ReleaseExe); + + comp.VerifyDiagnostics( + // (24,22): error CS9227: 'MyCollection' does not contain a definition for a suitable instance 'Add' method + // static void Test(params MyCollection a) + Diagnostic(ErrorCode.ERR_ParamsCollectionExtensionAddMethod, "params MyCollection a").WithArguments("MyCollection").WithLocation(24, 22) + ); + } + + [Fact] + public void TestGetEnumeratorPatternViaExtensionWithOptionalParameter() + { + var source = @" +using System; +public class C +{ + public static void Main() + /**/ + { + foreach (var i in new C()) + { + Console.Write(i); + } + } + /**/ + + public sealed class Enumerator + { + public Enumerator(int start) => Current = start; + public int Current { get; private set; } + public bool MoveNext() => Current++ != 3; + } +} +public static class Extensions +{ + extension(C self) + { + public C.Enumerator GetEnumerator(int x = 1) => new C.Enumerator(x); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: "23"); + + VerifyFlowGraphAndDiagnosticsForTest((CSharpCompilation)verifier.Compilation, +@" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C()') + Value: + IInvocationOperation ( C.Enumerator Extensions.<>E__0.GetEnumerator([System.Int32 x = 1])) (OperationKind.Invocation, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + Instance Receiver: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: C, IsImplicit) (Syntax: 'new C()') + Conversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Identity) + Operand: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') + Arguments(0) + Initializer: + null + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'new C()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'new C()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] [B3] + Statements (0) + Jump if False (Regular) to Block[B4] + IInvocationOperation ( System.Boolean C.Enumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new C()') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + Arguments(0) + Leaving: {R1} + Next (Regular) Block[B3] + Entering: {R2} + .locals {R2} + { + Locals: [System.Int32 i] + Block[B3] - Block + Predecessors: [B2] + Statements (2) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var') + Left: + ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var') + Right: + IPropertyReferenceOperation: System.Int32 C.Enumerator.Current { get; private set; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write(i);') + Expression: + IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write(i)') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i') + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B2] + Leaving: {R2} + } +} +Block[B4] - Exit + Predecessors: [B2] + Statements (0) +", []); + } + + [Fact] + public void TestGetEnumeratorPatternViaExtensionWithOptionalParameter_02() + { + var source = @" +using System; +public struct C +{ + public static void Main() + /**/ + { + foreach (var i in new C()) + { + Console.Write(i); + } + } + /**/ + + public sealed class Enumerator + { + public Enumerator(int start) => Current = start; + public int Current { get; private set; } + public bool MoveNext() => Current++ != 3; + } +} +public static class Extensions +{ + extension(object self) + { + public C.Enumerator GetEnumerator(int x = 1) => new C.Enumerator(x); + } +}"; + var verifier = CompileAndVerify(source, expectedOutput: "23", parseOptions: TestOptions.RegularPreview.WithFeature("run-nullable-analysis", "never")); // Tracked by https://github.com/dotnet/roslyn/issues/76130: Nullable analysis asserts + + VerifyFlowGraphAndDiagnosticsForTest((CSharpCompilation)verifier.Compilation, +@" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'new C()') + Value: + IInvocationOperation ( C.Enumerator Extensions.<>E__0.GetEnumerator([System.Int32 x = 1])) (OperationKind.Invocation, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + Instance Receiver: + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new C()') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (Boxing) + Operand: + IObjectCreationOperation (Constructor: C..ctor()) (OperationKind.ObjectCreation, Type: C) (Syntax: 'new C()') + Arguments(0) + Initializer: + null + Arguments(1): + IArgumentOperation (ArgumentKind.DefaultValue, Matching Parameter: x) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'new C()') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1, IsImplicit) (Syntax: 'new C()') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] [B3] + Statements (0) + Jump if False (Regular) to Block[B4] + IInvocationOperation ( System.Boolean C.Enumerator.MoveNext()) (OperationKind.Invocation, Type: System.Boolean, IsImplicit) (Syntax: 'new C()') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + Arguments(0) + Leaving: {R1} + Next (Regular) Block[B3] + Entering: {R2} + .locals {R2} + { + Locals: [System.Int32 i] + Block[B3] - Block + Predecessors: [B2] + Statements (2) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: null, IsImplicit) (Syntax: 'var') + Left: + ILocalReferenceOperation: i (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Int32, IsImplicit) (Syntax: 'var') + Right: + IPropertyReferenceOperation: System.Int32 C.Enumerator.Current { get; private set; } (OperationKind.PropertyReference, Type: System.Int32, IsImplicit) (Syntax: 'var') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C.Enumerator, IsImplicit) (Syntax: 'new C()') + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.Write(i);') + Expression: + IInvocationOperation (void System.Console.Write(System.Int32 value)) (OperationKind.Invocation, Type: System.Void) (Syntax: 'Console.Write(i)') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: 'i') + ILocalReferenceOperation: i (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'i') + InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + Next (Regular) Block[B2] + Leaving: {R2} + } +} +Block[B4] - Exit + Predecessors: [B2] + Statements (0) +", []); + } + + [Fact] + public void TestMoveNextPatternViaExtensions_OnInstanceGetEnumerator() + { + var source = @" +using System; +public class C +{ + public static void Main() + { + foreach (var i in new C()) + { + Console.Write(i); + } + } + public sealed class Enumerator + { + public int Current { get; private set; } + } + + public C.Enumerator GetEnumerator() => new C.Enumerator(); +} +public static class Extensions +{ + extension(C.Enumerator e) + { + public bool MoveNext() => false; + } +}"; + CreateCompilation(source) + .VerifyDiagnostics( + // (7,27): error CS0117: 'C.Enumerator' does not contain a definition for 'MoveNext' + // foreach (var i in new C()) + Diagnostic(ErrorCode.ERR_NoSuchMember, "new C()").WithArguments("C.Enumerator", "MoveNext").WithLocation(7, 27), + // (7,27): error CS0202: foreach requires that the return type 'C.Enumerator' of 'C.GetEnumerator()' must have a suitable public 'MoveNext' method and public 'Current' property + // foreach (var i in new C()) + Diagnostic(ErrorCode.ERR_BadGetEnumerator, "new C()").WithArguments("C.Enumerator", "C.GetEnumerator()").WithLocation(7, 27) + ); + } + + [Fact] + public void TestGetEnumeratorPatternViaRefExtensionOnNonAssignableVariable() + { + var source = @" +using System; +public struct C +{ + public static void Main() + { + foreach (var i in new C()) + { + Console.Write(i); + } + } + public struct Enumerator + { + public int Current { get; private set; } + public bool MoveNext() => Current++ != 3; + } +} +public static class Extensions +{ + extension(ref C self) + { + public C.Enumerator GetEnumerator() => new C.Enumerator(); + } +}"; + CreateCompilation(source) + .VerifyDiagnostics( + // (7,27): error CS1510: A ref or out value must be an assignable variable + // foreach (var i in new C()) + Diagnostic(ErrorCode.ERR_RefLvalueExpected, "new C()").WithLocation(7, 27)); + } + + [Fact] + public void ExpressionTrees_01_InstanceMethod() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string M(string s) => o + s; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1", "2")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (o, s) => o.M(s); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: @"12: (o, s) => o.M(s)").VerifyDiagnostics(); + } + + [Fact] + public void ExpressionTrees_02_StaticMethod() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public static string M(string s) => s; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (s) => object.M(s); + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: @"1: s => M(s)").VerifyDiagnostics(); + } + + [Fact] + public void ExpressionTrees_03_InstanceIndexer() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string this[string s] => o + s; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1", "2")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (o, s) => o[s]; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (24,26): error CS0021: Cannot apply indexing with [] to an expression of type 'object' + // return (o, s) => o[s]; + Diagnostic(ErrorCode.ERR_BadIndexLHS, "o[s]").WithArguments("object").WithLocation(24, 26) + ); + } + + [Fact] + public void ExpressionTrees_04_InstanceProperty() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string P => (string)o; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (o) => o.P; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (24,23): error CS9296: An expression tree may not contain an extension property access + // return (o) => o.P; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, "o.P").WithLocation(24, 23) + ); + } + + [Fact] + public void ExpressionTrees_05_InstanceProperty() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string P { get => (string)o; set {}} + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (s) => new object() { P = s }; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (24,38): error CS9296: An expression tree may not contain an extension property access + // return (s) => new object() { P = s }; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, "P").WithLocation(24, 38) + ); + } + + [Fact] + public void ExpressionTrees_06_InstanceProperty() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public C P { get => default; } + } +} + +public class C +{ + public string F; +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return (s) => new object() { P = { F = s } }; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (29,38): error CS9296: An expression tree may not contain an extension property access + // return (s) => new object() { P = { F = s } }; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, "P").WithLocation(29, 38) + ); + } + + [Fact] + public void ExpressionTrees_07_InstanceProperty() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string P { get => default; } + } +} + +class Program +{ + static void Main() + { + } + + static Expression> Test() + { + return (o) => o is object { P: "s" }; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (20,23): error CS8122: An expression tree may not contain an 'is' pattern-matching operator. + // return (o) => o is object { P: "s" }; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsIsMatch, @"o is object { P: ""s"" }").WithLocation(20, 23) + ); + } + + [Fact] + public void ExpressionTrees_08_StaticProperty() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public static string P => "o"; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke()); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression> Test() + { + return () => object.P; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + comp.VerifyDiagnostics( + // (24,22): error CS9296: An expression tree may not contain an extension property access + // return () => object.P; + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsExtensionPropertyAccess, "object.P").WithLocation(24, 22) + ); + } + + [Fact] + public void ExpressionTrees_09_DelegateCreation_InstanceMethod() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public string M(string s) => o + s; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke("1").Invoke("2")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression>> Test() + { + return (o) => o.M; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: @"12: o => Convert(System.String M(System.Object, System.String).CreateDelegate(System.Func`2[System.String,System.String], o)" + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Func`2)" : ")")).VerifyDiagnostics(); + } + + [Fact] + public void ExpressionTrees_10_DelegateCreation_StaticMethod() + { + var src = """ +using System; +using System.Linq.Expressions; + +public static class Extensions +{ + extension(object o) + { + public static string M(string s) => s; + } +} + +class Program +{ + static void Main() + { + var e = Test(); + System.Console.Write(e.Compile().Invoke().Invoke("1")); + System.Console.Write(": "); + System.Console.Write(e); + } + + static Expression>> Test() + { + return () => object.M; + } +} +"""; + var comp = CreateCompilation(src, options: TestOptions.DebugExe); + CompileAndVerify(comp, expectedOutput: @"1: () => Convert(System.String M(System.String).CreateDelegate(System.Func`2[System.String,System.String], null)" + (ExecutionConditionUtil.IsMonoOrCoreClr ? ", Func`2)" : ")")).VerifyDiagnostics(); + } +} diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs index 795ef8e73fb65..027e23174b049 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/InlineArrayTests.cs @@ -23479,5 +23479,26 @@ .locals init (int V_0, """); } } + + [Fact] + public void NullConditionalAssignment_01() + { + var src = @" +class Program +{ + static void Test(Buffer4 x) + { + x[0] = 1; + Buffer4? nx = x; + nx?[2] = 3; // 1 + } +} +"; + var comp = CreateCompilation(src + Buffer4Definition, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseDll); + comp.VerifyDiagnostics( + // (8,12): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // nx?[2] = 3; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[2]").WithLocation(8, 12)); + } } } diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs index e6efe39d327e3..2a80d5a0d1968 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/ParamsCollectionTests.cs @@ -3193,7 +3193,7 @@ static int GetC() // Note, the collection is created after the lexically previous argument is evaluated, // but before the lexically following argument is evaluated. This differs from params - // array case, which is created right before the target methos is invoked, after all + // array case, which is created right before the target method is invoked, after all // arguments are evaluated in their lexical order, which can be observed in a unit-test // Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen.CodeGenTests.NamedParamsOptimizationAndParams002​ verifier.VerifyIL("Program.Main", @" diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs index 9b892c7ea73c6..6ba97279d29d0 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests.cs @@ -12284,7 +12284,7 @@ public void Test2(G x2) [Fact] [WorkItem(63476, "https://github.com/dotnet/roslyn/issues/63476")] - public void PatternNonConstant_UserDefinedImplicit_ConvertionToInputType() + public void PatternNonConstant_UserDefinedImplicit_ConversionToInputType() { var source = @" @@ -12306,7 +12306,7 @@ class C [Fact] [WorkItem(63476, "https://github.com/dotnet/roslyn/issues/63476")] - public void PatternNonConstant_UserDefinedExplicit_ConvertionToInputType() + public void PatternNonConstant_UserDefinedExplicit_ConversionToInputType() { var source = @" @@ -12327,7 +12327,7 @@ class C } [Fact] - public void PatternReadOnlySpan_ImplicitBuiltInConvertion_ToString() + public void PatternReadOnlySpan_ImplicitBuiltInConversion_ToString() { var source = @" @@ -12341,7 +12341,7 @@ class C } [Fact] - public void PatternNoImplicitConvertionToInputType() + public void PatternNoImplicitConversionToInputType() { // Cannot implicitly cast long to byte.. var source = diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests5.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests5.cs index 0120dc6e33d29..1d4e2f1a64c85 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests5.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests5.cs @@ -1981,10 +1981,10 @@ void M(T t) where T : INumberBase var comp = CreateCompilation(new[] { source, INumberBaseDefinition }); comp.VerifyDiagnostics( - // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // 1 => 1, // 1 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("T").WithLocation(8, 9), - // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // > 1 => 2, // 2 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("T").WithLocation(9, 9), // (11,9): error CS8985: List patterns may not be used for a value of type 'T'. No suitable 'Length' or 'Count' property was found. @@ -2025,10 +2025,10 @@ void M(T t) where T : struct, INumberBase var comp = CreateCompilation(new[] { source, INumberBaseDefinition }); comp.VerifyDiagnostics( - // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // 1 => 1, // 1 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("T").WithLocation(8, 9), - // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // > 1 => 2, // 2 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("T").WithLocation(9, 9), // (11,9): error CS8985: List patterns may not be used for a value of type 'T'. No suitable 'Length' or 'Count' property was found. @@ -2122,10 +2122,10 @@ void M(T t) where T : struct, A::System.Numerics.INumberBase var comp = CreateCompilation(new[] { source }, references: new[] { ref1, ref2 }); comp.VerifyDiagnostics( - // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (8,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // 1 => 1, // 1 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("T").WithLocation(8, 9), - // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (9,9): error CS9060: Cannot use a numeric constant or relational pattern on 'T' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // > 1 => 2, // 2 Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("T").WithLocation(9, 9), // (11,9): error CS8985: List patterns may not be used for a value of type 'T'. No suitable 'Length' or 'Count' property was found. @@ -2431,10 +2431,10 @@ public void MatchingOnConstantConversionsWithINumberBaseIsDisallowed_TypePattern var comp = CreateEmptyCompilation(new[] { source, INumberBaseBCL, INumberBaseDefinition }); comp.VerifyDiagnostics( - // (5,5): error CS9060: Cannot use a numeric constant or relational pattern on 'INumberBase' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (5,5): error CS9060: Cannot use a numeric constant or relational pattern on 'INumberBase' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // 1 => 1, Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "1").WithArguments("System.Numerics.INumberBase").WithLocation(5, 5), - // (6,5): error CS9060: Cannot use a numeric constant or relational pattern on 'INumberBase' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specifc numeric type. + // (6,5): error CS9060: Cannot use a numeric constant or relational pattern on 'INumberBase' because it inherits from or extends 'INumberBase'. Consider using a type pattern to narrow to a specific numeric type. // > 1 => 2, Diagnostic(ErrorCode.ERR_CannotMatchOnINumberBase, "> 1").WithArguments("System.Numerics.INumberBase").WithLocation(6, 5) ); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs index 0ab3cc6b650cf..0b8fa5baa41b0 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/RecordTests.cs @@ -306,6 +306,8 @@ record R(R x); record R2(R2? x) { } record R3([System.Diagnostics.CodeAnalysis.NotNull] R3 x); + +record R4(in R4 x); "; var comp = CreateCompilation(new[] { src, NotNullAttributeDefinition }); comp.VerifyEmitDiagnostics( @@ -317,11 +319,14 @@ record R3([System.Diagnostics.CodeAnalysis.NotNull] R3 x); Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "R2").WithLocation(5, 8), // (7,8): error CS8909: The primary constructor conflicts with the synthesized copy constructor. // record R3([System.Diagnostics.CodeAnalysis.NotNull] R3 x); - Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "R3").WithLocation(7, 8) + Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "R3").WithLocation(7, 8), + // 0.cs(9,8): error CS8910: The primary constructor conflicts with the synthesized copy constructor. + // record R4(in R4 x); + Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "R4").WithLocation(9, 8) ); var r = comp.GlobalNamespace.GetTypeMember("R"); - Assert.Equal(new[] { "R..ctor(R x)", "R..ctor(R original)" }, r.GetMembers(".ctor").ToTestDisplayStrings()); + Assert.Equal(new[] { "R..ctor(R x)" }, r.GetMembers(".ctor").ToTestDisplayStrings()); } [Fact, WorkItem(49628, "https://github.com/dotnet/roslyn/issues/49628")] @@ -394,27 +399,12 @@ record R3(R3 x) : Base // (4,8): error CS8909: The primary constructor conflicts with the synthesized copy constructor. // record R(R x) : Base; // 1 Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "R").WithLocation(4, 8), - // (6,30): error CS0121: The call is ambiguous between the following methods or properties: 'R.R(R)' and 'R.R(R)' - // record Derived(Derived y) : R(y) // 2 - Diagnostic(ErrorCode.ERR_AmbigCall, "(y)").WithArguments("R.R(R)", "R.R(R)").WithLocation(6, 30), // (8,12): error CS0111: Type 'Derived' already defines a member called 'Derived' with the same parameter types // public Derived(Derived y) : base(y) => throw null; // 3, 4, 5 Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "Derived").WithArguments("Derived", "Derived").WithLocation(8, 12), - // (8,33): error CS0121: The call is ambiguous between the following methods or properties: 'R.R(R)' and 'R.R(R)' - // public Derived(Derived y) : base(y) => throw null; // 3, 4, 5 - Diagnostic(ErrorCode.ERR_AmbigCall, "base").WithArguments("R.R(R)", "R.R(R)").WithLocation(8, 33), - // (8,33): error CS8868: A copy constructor in a record must call a copy constructor of the base, or a parameterless object constructor if the record inherits from object. - // public Derived(Derived y) : base(y) => throw null; // 3, 4, 5 - Diagnostic(ErrorCode.ERR_CopyConstructorMustInvokeBaseCopyConstructor, "base").WithLocation(8, 33), // (11,8): error CS8909: The primary constructor conflicts with the synthesized copy constructor. // record Derived2(Derived2 y) : R(y); // 6, 7, 8 Diagnostic(ErrorCode.ERR_RecordAmbigCtor, "Derived2").WithLocation(11, 8), - // (11,8): error CS8867: No accessible copy constructor found in base type 'R'. - // record Derived2(Derived2 y) : R(y); // 6, 7, 8 - Diagnostic(ErrorCode.ERR_NoCopyConstructorInBaseType, "Derived2").WithArguments("R").WithLocation(11, 8), - // (11,32): error CS0121: The call is ambiguous between the following methods or properties: 'R.R(R)' and 'R.R(R)' - // record Derived2(Derived2 y) : R(y); // 6, 7, 8 - Diagnostic(ErrorCode.ERR_AmbigCall, "(y)").WithArguments("R.R(R)", "R.R(R)").WithLocation(11, 32), // (15,12): error CS0111: Type 'R2' already defines a member called 'R2' with the same parameter types // public R2(R2 x) => throw null; // 9, 10 Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "R2").WithArguments("R2", "R2").WithLocation(15, 12), @@ -601,7 +591,7 @@ public record A(int i, int ) { } var ctor = comp.GetMember("A").Constructors[0]; Assert.Equal("A..ctor(System.Int32 i, System.Int32)", ctor.ToTestDisplayString()); Assert.IsType(ctor.Parameters[1].DeclaringSyntaxReferences.Single().GetSyntax()); - Assert.Equal(0, ctor.Parameters[1].Locations.Single().SourceSpan.Length); + Assert.Equal(3, ctor.Parameters[1].Locations.Single().SourceSpan.Length); } [Fact, WorkItem(46123, "https://github.com/dotnet/roslyn/issues/46123")] @@ -615,12 +605,12 @@ public record A(int, string ) { } // (2,20): error CS1001: Identifier expected // public record A(int, string ) { } Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(2, 20), - // (2,29): error CS1001: Identifier expected + // (2,22): error CS0102: The type 'A' already contains a definition for '' // public record A(int, string ) { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(2, 29), - // (2,29): error CS0102: The type 'A' already contains a definition for '' + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "string").WithArguments("A", "").WithLocation(2, 22), + // (2,29): error CS1001: Identifier expected // public record A(int, string ) { } - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "").WithArguments("A", "").WithLocation(2, 29) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(2, 29) ); var expectedMembers = new[] @@ -649,12 +639,12 @@ public record A(int, int ) { } // (2,20): error CS1001: Identifier expected // public record A(int, int ) { } Diagnostic(ErrorCode.ERR_IdentifierExpected, ",").WithLocation(2, 20), - // (2,26): error CS1001: Identifier expected + // (2,22): error CS0102: The type 'A' already contains a definition for '' // public record A(int, int ) { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(2, 26), - // (2,26): error CS0102: The type 'A' already contains a definition for '' + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "int").WithArguments("A", "").WithLocation(2, 22), + // (2,26): error CS1001: Identifier expected // public record A(int, int ) { } - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "").WithArguments("A", "").WithLocation(2, 26) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(2, 26) ); var expectedMembers = new[] @@ -685,12 +675,12 @@ public record A(int // A // (2,20): error CS1001: Identifier expected // public record A(int // A Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(2, 20), - // (4,18): error CS1001: Identifier expected + // (4,7): error CS0102: The type 'A' already contains a definition for '' // , int /* C */) { } - Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 18), - // (4,18): error CS0102: The type 'A' already contains a definition for '' + Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "int").WithArguments("A", "").WithLocation(4, 7), + // (4,18): error CS1001: Identifier expected // , int /* C */) { } - Diagnostic(ErrorCode.ERR_DuplicateNameInClass, "").WithArguments("A", "").WithLocation(4, 18) + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 18) ); var ctor = comp.GetMember("A").Constructors[0]; @@ -30379,7 +30369,7 @@ class C3 } [Fact, WorkItem(62051, "https://github.com/dotnet/roslyn/issues/62051")] - public void MisingBaseType() + public void MissingBaseType() { var src = @" record R : // 1 diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs index 7d9c663c2218e..a02802136180a 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IConditionalAccessExpression.cs @@ -989,80 +989,54 @@ public struct S1 public int P1 { get; set; } }"; string expectedGraph = @" + Block[B0] - Entry Statements (0) Next (Regular) Block[B1] - Entering: {R1} {R2} + Entering: {R1} .locals {R1} { CaptureIds: [0] - .locals {R2} - { - CaptureIds: [1] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: - IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x') - Jump if True (Regular) to Block[B3] - IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x') - Operand: - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') - Leaving: {R2} - Next (Regular) Block[B2] - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1') - Value: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - Operand: - IPropertyReferenceOperation: System.Int32 S1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') - Instance Receiver: - IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x') - Instance Receiver: - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') - Arguments(0) - Next (Regular) Block[B4] - Leaving: {R2} - } - Block[B3] - Block - Predecessors: [B1] + Block[B1] - Block + Predecessors: [B0] Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x') Value: - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x') - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B2] [B3] + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?) (Syntax: 'x') + Jump if True (Regular) to Block[B3] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'x') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1?, IsImplicit) (Syntax: 'x') + Leaving: {R1} + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: '.P1 = 0') Left: - IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') + IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: '.P1') Children(1): - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') + IPropertyReferenceOperation: System.Int32 S1.P1 { get; set; } (OperationKind.PropertyReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') + Instance Receiver: + IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsImplicit) (Syntax: 'x') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1?, IsImplicit) (Syntax: 'x') + Arguments(0) Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - Operand: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Next (Regular) Block[B5] + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B3] Leaving: {R1} } -Block[B5] - Exit - Predecessors: [B4] +Block[B3] - Exit + Predecessors: [B1] [B2] Statements (0) "; var expectedDiagnostics = new[] { - // file.cs(6,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (6,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // x?.P1 = 0; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "x?.P1").WithLocation(6, 9) + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".P1").WithLocation(6, 11) }; VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); @@ -1089,77 +1063,50 @@ public struct S1 Block[B0] - Entry Statements (0) Next (Regular) Block[B1] - Entering: {R1} {R2} + Entering: {R1} .locals {R1} { CaptureIds: [0] - .locals {R2} - { - CaptureIds: [1] - Block[B1] - Block - Predecessors: [B0] - Statements (1) - IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') - Value: - IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?, IsInvalid) (Syntax: 'x') - Jump if True (Regular) to Block[B3] - IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x') - Operand: - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') - Leaving: {R2} - Next (Regular) Block[B2] - Block[B2] - Block - Predecessors: [B1] - Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: '.P1') - Value: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: '.P1') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - Operand: - IFieldReferenceOperation: System.Int32 S1.P1 (OperationKind.FieldReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') - Instance Receiver: - IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsInvalid, IsImplicit) (Syntax: 'x') - Instance Receiver: - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: S1?, IsInvalid, IsImplicit) (Syntax: 'x') - Arguments(0) - Next (Regular) Block[B4] - Leaving: {R2} - } - Block[B3] - Block - Predecessors: [B1] + Block[B1] - Block + Predecessors: [B0] Statements (1) - IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsInvalid, IsImplicit) (Syntax: 'x') + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x') Value: - IDefaultValueOperation (OperationKind.DefaultValue, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x') - Next (Regular) Block[B4] - Block[B4] - Block - Predecessors: [B2] [B3] + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S1?) (Syntax: 'x') + Jump if True (Regular) to Block[B3] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'x') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1?, IsImplicit) (Syntax: 'x') + Leaving: {R1} + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] Statements (1) IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid) (Syntax: 'x?.P1 = 0;') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32?, IsInvalid) (Syntax: 'x?.P1 = 0') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Int32, IsInvalid) (Syntax: '.P1 = 0') Left: - IInvalidOperation (OperationKind.Invalid, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') + IInvalidOperation (OperationKind.Invalid, Type: System.Int32, IsInvalid, IsImplicit) (Syntax: '.P1') Children(1): - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsInvalid, IsImplicit) (Syntax: 'x?.P1') + IFieldReferenceOperation: System.Int32 S1.P1 (OperationKind.FieldReference, Type: System.Int32, IsInvalid) (Syntax: '.P1') + Instance Receiver: + IInvocationOperation ( S1 S1?.GetValueOrDefault()) (OperationKind.Invocation, Type: S1, IsImplicit) (Syntax: 'x') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S1?, IsImplicit) (Syntax: 'x') + Arguments(0) Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32?, IsImplicit) (Syntax: '0') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (ImplicitNullable) - Operand: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') - Next (Regular) Block[B5] + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + Next (Regular) Block[B3] Leaving: {R1} } -Block[B5] - Exit - Predecessors: [B4] +Block[B3] - Exit + Predecessors: [B1] [B2] Statements (0) "; var expectedDiagnostics = new[] { - // file.cs(6,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (6,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // x?.P1 = 0; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "x?.P1").WithLocation(6, 9) + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".P1").WithLocation(6, 11) }; VerifyFlowGraphAndDiagnosticsForTest(source, expectedGraph, expectedDiagnostics); diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_InvalidExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_InvalidExpression.cs index 4e10cee0ee03b..0192b30fbbcae 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_InvalidExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_InvalidExpression.cs @@ -33,9 +33,7 @@ static void Main(string[] args) string expectedOperationTree = @" IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'Console.WriteLine2()') Children(1): - IInvalidOperation (OperationKind.Invalid, Type: ?, IsInvalid) (Syntax: 'Console.WriteLine2') - Children(1): - IOperation: (OperationKind.None, Type: System.Console) (Syntax: 'Console') + IOperation: (OperationKind.None, Type: System.Console) (Syntax: 'Console') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0117: 'Console' does not contain a definition for 'WriteLine2' diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ConditionalOperatorTests.cs index 678dfc5989b01..8559efc7a739b 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ConditionalOperatorTests.cs @@ -1438,23 +1438,25 @@ static void Main() var compilation = CreateCompilation(source, options: TestOptions.DebugDll); compilation.VerifyDiagnostics( - // (10,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer - // receiver?.test += Main; - Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "receiver?.test").WithLocation(10, 9) + // (6,18): warning CS0067: The event 'TestClass.test' is never used + // event Action test; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "test").WithArguments("TestClass.test").WithLocation(6, 18) ); var tree = compilation.SyntaxTrees.Single(); var memberBinding = tree.GetRoot().DescendantNodes().OfType().Single(); - var access = (ConditionalAccessExpressionSyntax)memberBinding.Parent!; + var assignment = (AssignmentExpressionSyntax)memberBinding.Parent!; + var access = (ConditionalAccessExpressionSyntax)assignment.Parent!; Assert.Equal(".test", memberBinding.ToString()); - Assert.Equal("receiver?.test", access.ToString()); + Assert.Equal(".test += Main", assignment.ToString()); + Assert.Equal("receiver?.test += Main", access.ToString()); var model = compilation.GetSemanticModel(tree); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding).Symbol.ToTestDisplayString()); Assert.Equal("event System.Action TestClass.test", model.GetSymbolInfo(memberBinding.Name).Symbol.ToTestDisplayString()); - + Assert.Equal("void TestClass.test.add", model.GetSymbolInfo(assignment).Symbol.ToTestDisplayString()); Assert.Null(model.GetSymbolInfo(access).Symbol); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index d8e9be5af9aa0..c9eff6dfe013d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -2400,7 +2400,7 @@ public static void M(this C c) var typeInfo = model.GetTypeInfo(memberAccess); Assert.Null(typeInfo.Type); Assert.Equal("System.Action", typeInfo.ConvertedType!.ToTestDisplayString()); - // https://github.com/dotnet/roslyn/issues/52870: GetSymbolInfo() should return resolved method from method group. + // https://github.com/dotnet/roslyn/issues/52870 GetSymbolInfo() should return resolved method from method group. Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); AssertEx.Equal(["void C.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } @@ -2423,37 +2423,139 @@ public void M(C c) { } // ignored public static class E { - public static void M(this C c) { } + public static void M(this C c) { } // ignored } """; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular12); - comp.VerifyDiagnostics( - // (1,21): error CS0123: No overload for 'M' matches delegate 'Action' - // System.Action x = C.M; - Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(1, 21), - // (4,9): error CS8917: The delegate type could not be inferred. - // var z = C.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9) - ); + { + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (1,19): error CS0120: An object reference is required for the non-static field, method, or property 'E.M(C)' + // System.Action x = C.M; + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.M").WithArguments("E.M(C)").WithLocation(1, 19), + // (4,9): error CS8917: The delegate type could not be inferred. + // var z = C.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "C.M").First(); + var typeInfo1 = model.GetTypeInfo(memberAccess1); + Assert.Null(typeInfo1.Type); + Assert.Equal("System.Action", typeInfo1.ConvertedType!.ToTestDisplayString()); + Assert.Equal("void C.M()", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M(C c)", "void C.M()"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "C.M").Last(); + var typeInfo2 = model.GetTypeInfo(memberAccess2); + Assert.Null(typeInfo2.Type); + Assert.True(typeInfo2.ConvertedType!.IsErrorType()); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal(["void C.M(C c)", "void C.M()"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } - comp = CreateCompilation(source, parseOptions: useCSharp13 ? TestOptions.Regular13 : TestOptions.RegularPreview); - comp.VerifyDiagnostics( - // (1,21): error CS0123: No overload for 'M' matches delegate 'Action' - // System.Action x = C.M; - Diagnostic(ErrorCode.ERR_MethDelegateMismatch, "M").WithArguments("M", "System.Action").WithLocation(1, 21), - // (4,9): error CS8917: The delegate type could not be inferred. - // var z = C.M; - Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9) - ); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : instead of finding the extension method and reporting an error on its receiver, + // we should instead discard it as a candidate early like we do for instance methods in OverloadResolution.RemoveStaticInstanceMismatches + { + var comp = CreateCompilation(source, parseOptions: useCSharp13 ? TestOptions.Regular13 : TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (1,19): error CS0120: An object reference is required for the non-static field, method, or property 'E.M(C)' + // System.Action x = C.M; + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.M").WithArguments("E.M(C)").WithLocation(1, 19), + // (4,9): error CS8917: The delegate type could not be inferred. + // var z = C.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "C.M").First(); + var typeInfo1 = model.GetTypeInfo(memberAccess1); + Assert.Null(typeInfo1.Type); + Assert.Equal("System.Action", typeInfo1.ConvertedType!.ToTestDisplayString()); + Assert.Equal("void C.M()", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M(C c)", "void C.M()"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "C.M").Last(); + var typeInfo2 = model.GetTypeInfo(memberAccess2); + Assert.Null(typeInfo2.Type); + Assert.True(typeInfo2.ConvertedType!.IsErrorType()); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal(["void C.M(C c)", "void C.M()"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + } - var tree = comp.SyntaxTrees[0]; - var model = comp.GetSemanticModel(tree); - var memberAccess = GetSyntaxes(tree, "C.M").Last(); - var typeInfo = model.GetTypeInfo(memberAccess); - Assert.Null(typeInfo.Type); - Assert.True(typeInfo.ConvertedType!.IsErrorType()); - Assert.Null(model.GetSymbolInfo(memberAccess).Symbol); - AssertEx.Equal(["void C.M(C c)"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/csharplang/issues/7364")] + public void MethodGroup_ScopeByScope_TypeReceiver_2(bool useCSharp13) + { + // Extension methods are ignored on type receiver + var source = """ +System.Action x = C.M; +x(); + +var z = C.M; +z(); + +public class C { } + +public static class E +{ + public static void M(this C c) { } // ignored +} +"""; + { + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular12); + comp.VerifyDiagnostics( + // (1,19): error CS0120: An object reference is required for the non-static field, method, or property 'E.M(C)' + // System.Action x = C.M; + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.M").WithArguments("E.M(C)").WithLocation(1, 19), + // (4,9): error CS8917: The delegate type could not be inferred. + // var z = C.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "C.M").First(); + var typeInfo1 = model.GetTypeInfo(memberAccess1); + Assert.Null(typeInfo1.Type); + Assert.Equal("System.Action", typeInfo1.ConvertedType!.ToTestDisplayString()); + Assert.Equal("void C.M()", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "C.M").Last(); + var typeInfo2 = model.GetTypeInfo(memberAccess2); + Assert.Null(typeInfo2.Type); + Assert.True(typeInfo2.ConvertedType!.IsErrorType()); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal(["void C.M()"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : instead of finding the extension method and reporting an error on its receiver, + // we should instead discard it as a candidate early like we do for instance methods in OverloadResolution.RemoveStaticInstanceMismatches + { + var comp = CreateCompilation(source, parseOptions: useCSharp13 ? TestOptions.RegularNext : TestOptions.RegularPreview); + comp.VerifyDiagnostics( + // (1,19): error CS0120: An object reference is required for the non-static field, method, or property 'E.M(C)' + // System.Action x = C.M; + Diagnostic(ErrorCode.ERR_ObjectRequired, "C.M").WithArguments("E.M(C)").WithLocation(1, 19), + // (4,9): error CS8917: The delegate type could not be inferred. + // var z = C.M; + Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "C.M").WithLocation(4, 9)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var memberAccess1 = GetSyntaxes(tree, "C.M").First(); + var typeInfo1 = model.GetTypeInfo(memberAccess1); + Assert.Null(typeInfo1.Type); + Assert.Equal("System.Action", typeInfo1.ConvertedType!.ToTestDisplayString()); + Assert.Equal("void C.M()", model.GetSymbolInfo(memberAccess1).Symbol.ToTestDisplayString()); + Assert.Equal(["void C.M()"], model.GetMemberGroup(memberAccess1).ToTestDisplayStrings()); + + var memberAccess2 = GetSyntaxes(tree, "C.M").Last(); + var typeInfo2 = model.GetTypeInfo(memberAccess2); + Assert.Null(typeInfo2.Type); + Assert.True(typeInfo2.ConvertedType!.IsErrorType()); + Assert.Null(model.GetSymbolInfo(memberAccess2).Symbol); + Assert.Equal(["void C.M()"], model.GetMemberGroup(memberAccess2).ToTestDisplayStrings()); + } } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/csharplang/issues/7364")] @@ -2916,9 +3018,7 @@ public void M(object o) where T : class { } var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax(tree, "new C().M"); Assert.Equal("void C.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); - - AssertEx.Equal(["void C.M()", "void C.M(System.Object o)"], - model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + AssertEx.Equal(["void C.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69222")] @@ -3042,9 +3142,7 @@ public static void M(this T t, object ignored) where T : struct { } var model = comp.GetSemanticModel(tree); var memberAccess = GetSyntax(tree, "new object().M"); Assert.Equal("void System.Object.M()", model.GetSymbolInfo(memberAccess).Symbol.ToTestDisplayString()); - - AssertEx.Equal(["void System.Object.M()", "void System.Object.M(System.Object ignored)"], - model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); + AssertEx.Equal(["void System.Object.M()"], model.GetMemberGroup(memberAccess).ToTestDisplayStrings()); } [Theory, CombinatorialData] @@ -16121,7 +16219,7 @@ public static void Main() } [Fact] - public void LambdWithDefaultNamedDelegateConversion_LambdaMissingOptional() + public void LambdaWithDefaultNamedDelegateConversion_LambdaMissingOptional() { var source = """ class Program diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs index 2d7ff0cb065f0..d03737a24fc77 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InheritanceBindingTests.cs @@ -7320,9 +7320,9 @@ public static void Main() // (27,27): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Derived1' // public class Derived2 : Outer>>.Inner>>.Interface> Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Derived1").WithLocation(27, 27), - // (37,28): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Derived1' + // (37,28): warning CS0693: Type parameter 'T' has the same name as the type parameter from outer type 'Derived1.Derived2' // public void Method(List> A, List>[] B, List C, Outer>>.Inner>>.Interface, T> D) - Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Derived1").WithLocation(37, 28), + Diagnostic(ErrorCode.WRN_TypeParameterSameAsOuterTypeParameter, "T").WithArguments("T", "Derived1.Derived2").WithLocation(37, 28), // (27,32): error CS0535: 'Derived1.Derived2' does not implement interface member 'Outer>>.Inner>>.Interface>.Method(System.Collections.Generic.List>, System.Collections.Generic.List>[], System.Collections.Generic.List, Outer>>.Inner>>.Interface, K>)' // public class Derived2 : Outer>>.Inner>>.Interface> Diagnostic(ErrorCode.ERR_UnimplementedInterfaceMember, "Outer>>.Inner>>.Interface>").WithArguments("Derived1.Derived2", "Outer>>.Inner>>.Interface>.Method(System.Collections.Generic.List>, System.Collections.Generic.List>[], System.Collections.Generic.List, Outer>>.Inner>>.Interface, K>)").WithLocation(27, 32), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs index 379986f86a708..69f12e7e3d7e2 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/IteratorTests.cs @@ -447,10 +447,7 @@ public void TopLevelYieldReturn() // (1,18): error CS1002: ; expected // yield return int. Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 18), - // (1,18): error CS0117: 'int' does not contain a definition for '' - // yield return int. - Diagnostic(ErrorCode.ERR_NoSuchMember, "").WithArguments("int", "").WithLocation(1, 18), - // (1,1): error CS7020: You cannot use 'yield' in top-level script code + // (1,1): error CS7020: Cannot use 'yield' in top-level script code // yield return int. Diagnostic(ErrorCode.ERR_YieldNotAllowedInScript, "yield").WithLocation(1, 1)); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs index 948e31354dbfb..c1f39092873b6 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs @@ -2430,7 +2430,7 @@ record C(int X) : Base`(X`) members), Remove( "System.Int32 C.X { get; init; }"), - Add( // paremeters + Add( // parameters "System.Int32 X")), Combine(s_pop, s_pop, s_pop), Add( // Members @@ -2514,7 +2514,7 @@ class C(int X) : Base`(X`) members), Remove( "System.Int32 C.X { get; }"), - Add( // paremeters + Add( // parameters "System.Int32 X")), Combine(s_pop, s_pop, s_pop), Add( // Members diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullConditionalAssignmentTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullConditionalAssignmentTests.cs new file mode 100644 index 0000000000000..62c5aa94d9179 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullConditionalAssignmentTests.cs @@ -0,0 +1,2896 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable + +using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class NullConditionalAssignmentTests : SemanticModelTestBase + { + [Fact] + public void LangVersion_01() + { + var source = """ + class C + { + string f; + static void M(C c) + { + c?.f = "a"; + } + } + """; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (3,12): warning CS0414: The field 'C.f' is assigned but its value is never used + // string f; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "f").WithArguments("C.f").WithLocation(3, 12), + // (6,14): error CS8652: The feature 'null conditional assignment' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // c?.f = "a"; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("null conditional assignment").WithLocation(6, 14)); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,12): warning CS0414: The field 'C.f' is assigned but its value is never used + // string f; + Diagnostic(ErrorCode.WRN_UnreferencedFieldAssg, "f").WithArguments("C.f").WithLocation(3, 12)); + } + + [Fact] + public void LangVersion_02() + { + // The only thing we want to diagnose is a member binding expression as the LHS of any assignment. + // Nested assignments within conditional access args, etc. have always been allowed. + var source = """ + class C + { + public string this[string s] { get => s; set { } } + + static void M(C c) + { + string s = "a"; + _ = c?[s = "b"]; + c?[s = "b"] = "c"; // 1 + } + } + """; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular13); + comp.VerifyEmitDiagnostics( + // (7,16): warning CS0219: The variable 's' is assigned but its value is never used + // string s = "a"; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "s").WithArguments("s").WithLocation(7, 16), + // (9,21): error CS8652: The feature 'null conditional assignment' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // c?[s = "b"] = "c"; // 1 + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("null conditional assignment").WithLocation(9, 21)); + + comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,16): warning CS0219: The variable 's' is assigned but its value is never used + // string s = "a"; + Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "s").WithArguments("s").WithLocation(7, 16)); + } + + [Theory] + [InlineData(SyntaxKind.BarEqualsToken)] + [InlineData(SyntaxKind.AmpersandEqualsToken)] + [InlineData(SyntaxKind.CaretEqualsToken)] + [InlineData(SyntaxKind.LessThanLessThanEqualsToken)] + [InlineData(SyntaxKind.GreaterThanGreaterThanEqualsToken)] + [InlineData(SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken)] + [InlineData(SyntaxKind.PlusEqualsToken)] + [InlineData(SyntaxKind.MinusEqualsToken)] + [InlineData(SyntaxKind.AsteriskEqualsToken)] + [InlineData(SyntaxKind.SlashEqualsToken)] + [InlineData(SyntaxKind.PercentEqualsToken)] + [InlineData(SyntaxKind.EqualsToken)] + [InlineData(SyntaxKind.QuestionQuestionEqualsToken)] + public void LangVersion_03(SyntaxKind kind) + { + string op = SyntaxFacts.GetText(kind); + string source = $$""" + class C + { + public object F; + + public static void M(C c) + { + c?.F {{op}} new object(); + } + } + """; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular13); + comp.GetEmitDiagnostics() + .Where(diag => diag.Code == (int)ErrorCode.ERR_FeatureInPreview) + .Verify( + // (7,14): error CS8652: The feature 'null conditional assignment' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // c?.F |= new object(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, op).WithArguments("null conditional assignment").WithLocation(7, 14)); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.GetEmitDiagnostics() + .Where(diag => diag.Code == (int)ErrorCode.ERR_FeatureInPreview) + .Verify(); + } + + [Fact] + public void FieldAccessAssignment_01() + { + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + var c = new C(); + M1(c, 1); + M2(c, 2); + c = null; + M3(c, 3); + M4(c, 4); + } + + static void M1(C c, int i) + { + c?.f = i; + Console.Write(c.f); + } + + static void M2(C c, int i) + { + Console.Write(c?.f = i); + } + + static void M3(C c, int i) + { + c?.f = i; + Console.Write(c?.f is null); + } + + static void M4(C c, int i) + { + Console.Write((c?.f = i) is null); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "12TrueTrue"); + verifier.VerifyIL("C.M1", """ + { + // Code size 22 (0x16) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: stfld "int C.f" + IL_000a: ldarg.0 + IL_000b: ldfld "int C.f" + IL_0010: call "void System.Console.Write(int)" + IL_0015: ret + } + """); + + verifier.VerifyIL("C.M2", """ + { + // Code size 40 (0x28) + .maxstack 3 + .locals init (int? V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000e + IL_0003: ldloca.s V_0 + IL_0005: initobj "int?" + IL_000b: ldloc.0 + IL_000c: br.s IL_001d + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: stfld "int C.f" + IL_0017: ldloc.1 + IL_0018: newobj "int?..ctor(int)" + IL_001d: box "int?" + IL_0022: call "void System.Console.Write(object)" + IL_0027: ret + } + """); + + verifier.VerifyIL("C.M3", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (int? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: stfld "int C.f" + IL_000a: ldarg.0 + IL_000b: brtrue.s IL_0018 + IL_000d: ldloca.s V_1 + IL_000f: initobj "int?" + IL_0015: ldloc.1 + IL_0016: br.s IL_0023 + IL_0018: ldarg.0 + IL_0019: ldfld "int C.f" + IL_001e: newobj "int?..ctor(int)" + IL_0023: stloc.0 + IL_0024: ldloca.s V_0 + IL_0026: call "bool int?.HasValue.get" + IL_002b: ldc.i4.0 + IL_002c: ceq + IL_002e: call "void System.Console.Write(bool)" + IL_0033: ret + } + """); + + verifier.VerifyIL("C.M4", """ + { + // Code size 46 (0x2e) + .maxstack 3 + .locals init (int? V_0, + int? V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000e + IL_0003: ldloca.s V_1 + IL_0005: initobj "int?" + IL_000b: ldloc.1 + IL_000c: br.s IL_001d + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: dup + IL_0011: stloc.2 + IL_0012: stfld "int C.f" + IL_0017: ldloc.2 + IL_0018: newobj "int?..ctor(int)" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: call "bool int?.HasValue.get" + IL_0025: ldc.i4.0 + IL_0026: ceq + IL_0028: call "void System.Console.Write(bool)" + IL_002d: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void FieldAccessAssignment_StructReceiver_01() + { + // NB: assignment of a 'readonly' setter is permitted even when property access receiver is not a variable + // See also https://github.com/dotnet/csharplang/issues/9174 + var source = """ + using System; + + struct S + { + int f; + int P { get; set; } + readonly int RP { get => 0; set { } } + + static void M1(S? s) + { + s?.f = 1; // 1 + s?.P = 2; // 2 + s?.RP = 2; + } + + static void M2(S? s) + { + Console.Write(s?.f = 4); // 3 + Console.Write(s?.P = 5); // 4 + Console.Write(s?.RP = 6); + } + + static void M2(S s) + { + s?.f = 7; // 5 + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (11,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // s?.f = 1; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".f").WithLocation(11, 11), + // (12,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // s?.P = 2; // 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".P").WithLocation(12, 11), + // (18,25): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // Console.Write(s?.f = 4); // 3 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".f").WithLocation(18, 25), + // (19,25): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // Console.Write(s?.P = 5); // 4 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".P").WithLocation(19, 25), + // (25,10): error CS0023: Operator '?' cannot be applied to operand of type 'S' + // s?.f = 7; // 5 + Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "S").WithLocation(25, 10)); + } + + [Fact] + public void FieldAccessAssignment_StructReceiver_02() + { + // NB: assignment of a 'readonly' setter is permitted even when property access receiver is not a variable + // See also https://github.com/dotnet/csharplang/issues/9174 + var source = """ + using System; + + class C + { + public int F; + } + + struct S + { + C c; + + readonly int RP { get => c.F; set => c.F = value; } + + static void Main() + { + M1(new S() { c = new C() }); + M2(new S() { c = new C() }); + M1(null); + M2(null); + } + + static void M1(S? s) + { + s?.RP = 1; + Console.Write(s?.RP ?? 3); + } + + static void M2(S? s) + { + Console.Write((s?.RP = 2) ?? 4); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "1234"); + + verifier.VerifyIL("S.M1", """ + { + // Code size 58 (0x3a) + .maxstack 2 + .locals init (S V_0) + IL_0000: ldarga.s V_0 + IL_0002: call "bool S?.HasValue.get" + IL_0007: brfalse.s IL_0019 + IL_0009: ldarga.s V_0 + IL_000b: call "S S?.GetValueOrDefault()" + IL_0010: stloc.0 + IL_0011: ldloca.s V_0 + IL_0013: ldc.i4.1 + IL_0014: call "readonly void S.RP.set" + IL_0019: ldarga.s V_0 + IL_001b: call "bool S?.HasValue.get" + IL_0020: brtrue.s IL_0025 + IL_0022: ldc.i4.3 + IL_0023: br.s IL_0034 + IL_0025: ldarga.s V_0 + IL_0027: call "S S?.GetValueOrDefault()" + IL_002c: stloc.0 + IL_002d: ldloca.s V_0 + IL_002f: call "readonly int S.RP.get" + IL_0034: call "void System.Console.Write(int)" + IL_0039: ret + } + """); + + verifier.VerifyIL("S.M2", """ + { + // Code size 37 (0x25) + .maxstack 3 + .locals init (int V_0, + S V_1) + IL_0000: ldarga.s V_0 + IL_0002: call "bool S?.HasValue.get" + IL_0007: brtrue.s IL_000c + IL_0009: ldc.i4.4 + IL_000a: br.s IL_001f + IL_000c: ldarga.s V_0 + IL_000e: call "S S?.GetValueOrDefault()" + IL_0013: stloc.1 + IL_0014: ldloca.s V_1 + IL_0016: ldc.i4.2 + IL_0017: dup + IL_0018: stloc.0 + IL_0019: call "readonly void S.RP.set" + IL_001e: ldloc.0 + IL_001f: call "void System.Console.Write(int)" + IL_0024: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void IndexerAssignment_StructReceiver_01() + { + // See also https://github.com/dotnet/csharplang/issues/9174 + var source = """ + using System; + + struct S + { + int f; + public int this[int i] { get => f; set => f = i; } + + static void M(S? s) + { + s?[1] = 2; // 1 + Console.WriteLine(s?[3] = 4); // 2 + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (10,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // s?[1] = 2; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[1]").WithLocation(10, 11), + // (11,29): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // Console.WriteLine(s?[3] = 4); // 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[3]").WithLocation(11, 29)); + } + + [Fact] + public void IndexerAssignment_StructReceiver_02() + { + // See also https://github.com/dotnet/csharplang/issues/9174 + var source = """ + using System; + + class C { public int f; } + struct S + { + public C C = new C(); + public S() { } + public int this[int i] { get => C.f; readonly set => C.f = i; } + + static void Main() + { + M1(new S { C = { f = 1 } }); + M1(null); + M2(new S { C = { f = 2 } }); + M2(null); + } + + static void M1(S? s) + { + s?[3] = 4; + Console.Write(s?[1] ?? 5); + } + + static void M2(S? s) + { + Console.Write((s?[6] = 7) ?? 8); + } + } + """; + CompileAndVerify(source, expectedOutput: "3578"); + } + + [Fact] + public void DeconstructionLeft() + { + var source = """ + using System; + + class C + { + int F; + + static void M1(C c1, C c2) + { + (c1?.F, c2?.F) = (1, 2); // 1 + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,9): warning CS0649: Field 'C.F' is never assigned to, and will always have its default value 0 + // int F; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("C.F", "0").WithLocation(5, 9), + // (9,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (c1?.F, c2?.F) = (1, 2); // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "c1?.F").WithLocation(9, 10), + // (9,17): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (c1?.F, c2?.F) = (1, 2); // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "c2?.F").WithLocation(9, 17)); + } + + [Fact] + public void RefAssignment_01() + { + var source = """ + using System; + + ref struct RS + { + ref int RF; + + static void M1() + { + int i = 0; + var rs = new RS { RF = ref i }; + rs?.RF = ref i; // 1 + + RS? nrs = rs; // 2 + nrs?.RF = ref i; // 3 + nrs?.RF = i; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net90); + comp.VerifyEmitDiagnostics( + // (11,11): error CS0023: Operator '?' cannot be applied to operand of type 'RS' + // rs?.RF = ref i; // 1 + Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "RS").WithLocation(11, 11), + // (13,9): error CS9244: The type 'RS' may not be a ref struct or a type parameter allowing ref structs in order to use it as parameter 'T' in the generic type or method 'Nullable' + // RS? nrs = rs; // 2 + Diagnostic(ErrorCode.ERR_NotRefStructConstraintNotSatisfied, "RS?").WithArguments("System.Nullable", "T", "RS").WithLocation(13, 9), + // (14,13): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // nrs?.RF = ref i; // 3 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".RF").WithLocation(14, 13)); + } + + [Fact] + public void AssignRefReturningMethod_01() + { + var source = """ + using System; + + class C + { + static int F; + ref int M() => ref F; + + static void Main() + { + M1(new C()); + M2(new C()); + M1(null); + M2(null); + } + + static void M1(C c) + { + c?.M() = 1; + Console.Write(c?.M() ?? 3); + } + + static void M2(C c) + { + Console.Write((c?.M() = 2) ?? 4); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "1234"); + + verifier.VerifyIL("C.M1", """ + { + // Code size 30 (0x1e) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000b + IL_0003: ldarg.0 + IL_0004: call "ref int C.M()" + IL_0009: ldc.i4.1 + IL_000a: stind.i4 + IL_000b: ldarg.0 + IL_000c: brtrue.s IL_0011 + IL_000e: ldc.i4.3 + IL_000f: br.s IL_0018 + IL_0011: ldarg.0 + IL_0012: call "ref int C.M()" + IL_0017: ldind.i4 + IL_0018: call "void System.Console.Write(int)" + IL_001d: ret + } + """); + + verifier.VerifyIL("C.M2", """ + { + // Code size 23 (0x17) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldc.i4.4 + IL_0004: br.s IL_0011 + IL_0006: ldarg.0 + IL_0007: call "ref int C.M()" + IL_000c: ldc.i4.2 + IL_000d: dup + IL_000e: stloc.0 + IL_000f: stind.i4 + IL_0010: ldloc.0 + IL_0011: call "void System.Console.Write(int)" + IL_0016: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccessAssignment_01() + { + var source = """ + using System; + + class C + { + int P { get; set; } + + static void Main() + { + var c = new C(); + M1(c, 1); + M2(c, 2); + c = null; + M3(c, 3); + M4(c, 4); + } + + static void M1(C c, int i) + { + c?.P = i; + Console.Write(c.P); + } + + static void M2(C c, int i) + { + Console.Write(c?.P = i); + } + + static void M3(C c, int i) + { + c?.P = i; + Console.Write(c?.P is null); + } + + static void M4(C c, int i) + { + Console.Write((c?.P = i) is null); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "12TrueTrue"); + verifier.VerifyIL("C.M1", """ + { + // Code size 22 (0x16) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: call "void C.P.set" + IL_000a: ldarg.0 + IL_000b: callvirt "int C.P.get" + IL_0010: call "void System.Console.Write(int)" + IL_0015: ret + } + """); + + verifier.VerifyIL("C.M2", """ + { + // Code size 40 (0x28) + .maxstack 3 + .locals init (int? V_0, + int V_1) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000e + IL_0003: ldloca.s V_0 + IL_0005: initobj "int?" + IL_000b: ldloc.0 + IL_000c: br.s IL_001d + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: dup + IL_0011: stloc.1 + IL_0012: call "void C.P.set" + IL_0017: ldloc.1 + IL_0018: newobj "int?..ctor(int)" + IL_001d: box "int?" + IL_0022: call "void System.Console.Write(object)" + IL_0027: ret + } + """); + + verifier.VerifyIL("C.M3", """ + { + // Code size 52 (0x34) + .maxstack 2 + .locals init (int? V_0, + int? V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: call "void C.P.set" + IL_000a: ldarg.0 + IL_000b: brtrue.s IL_0018 + IL_000d: ldloca.s V_1 + IL_000f: initobj "int?" + IL_0015: ldloc.1 + IL_0016: br.s IL_0023 + IL_0018: ldarg.0 + IL_0019: call "int C.P.get" + IL_001e: newobj "int?..ctor(int)" + IL_0023: stloc.0 + IL_0024: ldloca.s V_0 + IL_0026: call "bool int?.HasValue.get" + IL_002b: ldc.i4.0 + IL_002c: ceq + IL_002e: call "void System.Console.Write(bool)" + IL_0033: ret + } + """); + + verifier.VerifyIL("C.M4", """ + { + // Code size 46 (0x2e) + .maxstack 3 + .locals init (int? V_0, + int? V_1, + int V_2) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_000e + IL_0003: ldloca.s V_1 + IL_0005: initobj "int?" + IL_000b: ldloc.1 + IL_000c: br.s IL_001d + IL_000e: ldarg.0 + IL_000f: ldarg.1 + IL_0010: dup + IL_0011: stloc.2 + IL_0012: call "void C.P.set" + IL_0017: ldloc.2 + IL_0018: newobj "int?..ctor(int)" + IL_001d: stloc.0 + IL_001e: ldloca.s V_0 + IL_0020: call "bool int?.HasValue.get" + IL_0025: ldc.i4.0 + IL_0026: ceq + IL_0028: call "void System.Console.Write(bool)" + IL_002d: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccessAssignment_02() + { + // init prop + var source = """ + var c = new C(); + c?.Prop = "a"; // 1 + + class C + { + public string Prop { get; init; } + } + """; + + var comp = CreateCompilation([source, IsExternalInitTypeDefinition]); + comp.VerifyEmitDiagnostics( + // (2,3): error CS8852: Init-only property or indexer 'C.Prop' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor. + // c?.Prop = "a"; // 1 + Diagnostic(ErrorCode.ERR_AssignmentInitOnly, ".Prop").WithArguments("C.Prop").WithLocation(2, 3)); + } + + [Fact] + public void EventAssignment_01() + { + var source = """ + using System; + + class C + { + public event Action E; + + static void Main() + { + M(new C()); + M(null); + } + + static void M(C c) + { + var handlerB = () => Console.Write("b"); + var handlerC = () => Console.Write("c"); + + try + { + c?.E(); + } + catch (NullReferenceException) + { + Console.Write("a"); + } + + ConditionalAddHandler(c, handlerB); + c?.E(); + ConditionalAddHandler(c, handlerC); + c?.E(); + ConditionalRemoveHandler(c, handlerB); + c?.E(); + ConditionalRemoveHandler(c, handlerC); + + try + { + c?.E(); + } + catch (NullReferenceException) + { + Console.Write("d"); + } + } + + static void ConditionalAddHandler(C c, Action a) + { + c?.E += a; + } + + static void ConditionalRemoveHandler(C c, Action a) + { + c?.E -= a; + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "abbccd"); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.ConditionalAddHandler", """ + { + // Code size 11 (0xb) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: call "void C.E.add" + IL_000a: ret + } + """); + + verifier.VerifyIL("C.ConditionalRemoveHandler", """ + { + // Code size 11 (0xb) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: call "void C.E.remove" + IL_000a: ret + } + """); + } + + [Fact] + public void ExpressionTree() + { + var source = """ + using System; + using System.Linq.Expressions; + + Expression> s = c => c?.F = "a"; // 1, 2 + + class C { public string F; } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (4,38): error CS8072: An expression tree lambda may not contain a null propagating operator. + // Expression> s = c => c?.F = "a"; // 1, 2 + Diagnostic(ErrorCode.ERR_NullPropagatingOpInExpressionTree, @"c?.F = ""a""").WithLocation(4, 38), + // (4,40): error CS0832: An expression tree may not contain an assignment operator + // Expression> s = c => c?.F = "a"; // 1, 2 + Diagnostic(ErrorCode.ERR_ExpressionTreeContainsAssignment, @".F = ""a""").WithLocation(4, 40)); + } + + [Fact] + public void Dynamic_01() + { + var source = """ + using System; + + dynamic d = new C(); + d?.F = "a"; + + Console.Write(d.F); + Console.Write(d?.F = "b"); + + d = null; + d?.F = "c"; + Console.Write(d?.F ?? ""); + Console.Write((d?.F = "d") ?? ""); + + class C { public string F = null!; } + """; + var verifier = CompileAndVerify(source, targetFramework: TargetFramework.StandardAndCSharp, expectedOutput: "ab"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void PointerDereference_01() + { + var source = """ + + struct S + { + public int F; + + static unsafe void M(S?* x) + { + *x?.F = 1; + } + } + """; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (4,16): warning CS0649: Field 'S.F' is never assigned to, and will always have its default value 0 + // public int F; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("S.F", "0").WithLocation(4, 16), + // (8,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // *x?.F = 1; + Diagnostic(ErrorCode.ERR_IllegalStatement, "*x?.F = 1").WithLocation(8, 9), + // (8,11): error CS0023: Operator '?' cannot be applied to operand of type 'S?*' + // *x?.F = 1; + Diagnostic(ErrorCode.ERR_BadUnaryOp, "?").WithArguments("?", "S?*").WithLocation(8, 11)); + } + + [Fact] + public void PointerDereference_02() + { + var source = """ + using System; + + struct S + { + public int F; + + static unsafe void M1(S?* x) + { + (*x)?.F = 1; // 1 + } + + static unsafe void M2(S?* x) + { + Console.Write((*x)?.F = 3); // 2 + } + } + """; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (9,14): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (*x)?.F = 1; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".F").WithLocation(9, 14), + // (14,28): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // Console.Write((*x)?.F = 3); // 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".F").WithLocation(14, 28)); + } + + [Fact] + public void PointerDereference_03() + { + var source = """ + using System; + + unsafe struct S + { + public int* F; + + static void M1(S? x) + { + *x?.F = 1; // 1, 2 + } + } + """; + var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); + comp.VerifyEmitDiagnostics( + // (9,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // *x?.F = 1; // 1, 2 + Diagnostic(ErrorCode.ERR_IllegalStatement, "*x?.F = 1").WithLocation(9, 9), + // (9,12): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // *x?.F = 1; // 1, 2 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".F").WithLocation(9, 12)); + } + + [Fact] + public void PointerDereference_04() + { + var source = """ + using System; + + unsafe struct S + { + public static int SF; + public int P { get => SF; readonly set => SF = value; } + + static void M1(S?* x) + { + (*x)?.P = 1; + Console.Write((*x)?.P ?? 2); + } + + static void M2(S?* x) + { + Console.Write(((*x)?.P = 3) ?? 4); + } + + static void Main() + { + S s = default; + S? s1 = s; + S?* s1p = &s1; + M1(s1p); + M2(s1p); + + s1 = null; + M1(s1p); + M2(s1p); + } + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.UnsafeDebugExe, verify: Verification.Skipped, expectedOutput: "1324"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void PointerDereference_05() + { + // Similar to _04 but accessing reference type field 'C' instead of using a readonly setter + var source = """ + using System; + + class C { public int F; } + unsafe struct S + { + public C C; + + #pragma warning disable 8500 // This takes the address of, gets the size of, or declares a pointer to a managed type + static void M1(S?* x) + { + (*x)?.C.F = 1; + Console.Write((*x)?.C.F ?? 2); + } + + static void M2(S?* x) + { + Console.Write(((*x)?.C.F = 3) ?? 4); + } + + static void Main() + { + var s = new S { C = new C() }; + S? s1 = s; + S?* s1p = &s1; + M1(s1p); + M2(s1p); + + s1 = null; + M1(s1p); + M2(s1p); + } + } + """; + var verifier = CompileAndVerify(source, options: TestOptions.UnsafeDebugExe, verify: Verification.Skipped, expectedOutput: "1324"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void CompoundAssignment_01() + { + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + M1(new C()); + M1(null); + M2(new C()); + M2(null); + } + + static void M1(C c) + { + c?.f += 1; + Console.Write(c?.f ?? 2); + } + + static void M2(C c) + { + Console.Write((c?.f += 3) ?? 4); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "1234"); + verifier.VerifyIL("C.M1", """ + { + // Code size 35 (0x23) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0011 + IL_0003: ldarg.0 + IL_0004: dup + IL_0005: ldfld "int C.f" + IL_000a: ldc.i4.1 + IL_000b: add + IL_000c: stfld "int C.f" + IL_0011: ldarg.0 + IL_0012: brtrue.s IL_0017 + IL_0014: ldc.i4.2 + IL_0015: br.s IL_001d + IL_0017: ldarg.0 + IL_0018: ldfld "int C.f" + IL_001d: call "void System.Console.Write(int)" + IL_0022: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldc.i4.4 + IL_0004: br.s IL_0017 + IL_0006: ldarg.0 + IL_0007: dup + IL_0008: ldfld "int C.f" + IL_000d: ldc.i4.3 + IL_000e: add + IL_000f: dup + IL_0010: stloc.0 + IL_0011: stfld "int C.f" + IL_0016: ldloc.0 + IL_0017: call "void System.Console.Write(int)" + IL_001c: ret + } + """); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void CompoundAssignment_02() + { + // Logical operator + // Note that there are no conditional versions of the "conditional logical operators" + // So, the set of behaviors we can observe through exhaustive testing is limited. + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + M1(new C() { f = 1 }); + M1(null); + M2(new C() { f = 2 }); + M2(null); + } + + static void M1(C c) + { + c?.f |= 4; + Console.Write(c?.f ?? 8); + } + + static void M2(C c) + { + Console.Write((c?.f |= 4) ?? 8); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "5868"); + verifier.VerifyIL("C.M1", """ + { + // Code size 35 (0x23) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0011 + IL_0003: ldarg.0 + IL_0004: dup + IL_0005: ldfld "int C.f" + IL_000a: ldc.i4.4 + IL_000b: or + IL_000c: stfld "int C.f" + IL_0011: ldarg.0 + IL_0012: brtrue.s IL_0017 + IL_0014: ldc.i4.8 + IL_0015: br.s IL_001d + IL_0017: ldarg.0 + IL_0018: ldfld "int C.f" + IL_001d: call "void System.Console.Write(int)" + IL_0022: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldc.i4.8 + IL_0004: br.s IL_0017 + IL_0006: ldarg.0 + IL_0007: dup + IL_0008: ldfld "int C.f" + IL_000d: ldc.i4.4 + IL_000e: or + IL_000f: dup + IL_0010: stloc.0 + IL_0011: stfld "int C.f" + IL_0016: ldloc.0 + IL_0017: call "void System.Console.Write(int)" + IL_001c: ret + } + """); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void CompoundAssignment_03() + { + // Shift operator + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + M1(new C() { f = 1 }); + M1(null); + M2(new C() { f = 2 }); + M2(null); + } + + static void M1(C c) + { + c?.f <<= 1; + Console.Write(c?.f ?? 8); + } + + static void M2(C c) + { + Console.Write((c?.f <<= 1) ?? 8); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "2848"); + verifier.VerifyIL("C.M1", """ + { + // Code size 35 (0x23) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0011 + IL_0003: ldarg.0 + IL_0004: dup + IL_0005: ldfld "int C.f" + IL_000a: ldc.i4.1 + IL_000b: shl + IL_000c: stfld "int C.f" + IL_0011: ldarg.0 + IL_0012: brtrue.s IL_0017 + IL_0014: ldc.i4.8 + IL_0015: br.s IL_001d + IL_0017: ldarg.0 + IL_0018: ldfld "int C.f" + IL_001d: call "void System.Console.Write(int)" + IL_0022: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 29 (0x1d) + .maxstack 3 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldc.i4.8 + IL_0004: br.s IL_0017 + IL_0006: ldarg.0 + IL_0007: dup + IL_0008: ldfld "int C.f" + IL_000d: ldc.i4.1 + IL_000e: shl + IL_000f: dup + IL_0010: stloc.0 + IL_0011: stfld "int C.f" + IL_0016: ldloc.0 + IL_0017: call "void System.Console.Write(int)" + IL_001c: ret + } + """); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void FieldAccessAssignment_Nested_01() + { + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + var c = new C(); + int x = 1; + c?.f = x = 2; + Console.Write(c.f); + Console.Write(x); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "22"); + verifier.VerifyIL("C.Main", """ + { + // Code size 39 (0x27) + .maxstack 4 + .locals init (int V_0) //x + IL_0000: newobj "C..ctor()" + IL_0005: ldc.i4.1 + IL_0006: stloc.0 + IL_0007: dup + IL_0008: dup + IL_0009: brtrue.s IL_000e + IL_000b: pop + IL_000c: br.s IL_0016 + IL_000e: ldc.i4.2 + IL_000f: dup + IL_0010: stloc.0 + IL_0011: stfld "int C.f" + IL_0016: ldfld "int C.f" + IL_001b: call "void System.Console.Write(int)" + IL_0020: ldloc.0 + IL_0021: call "void System.Console.Write(int)" + IL_0026: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void FieldAccessAssignment_Nested_02() + { + var source = """ + using System; + + class C + { + int f; + + static void Main() + { + C c = null; + int x = 1; + c?.f = x = 2; + Console.Write(c?.f is null); + Console.Write(x); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "True1"); + verifier.VerifyIL("C.Main", """ + { + // Code size 66 (0x42) + .maxstack 4 + .locals init (int V_0, //x + int? V_1, + int? V_2) + IL_0000: ldnull + IL_0001: ldc.i4.1 + IL_0002: stloc.0 + IL_0003: dup + IL_0004: dup + IL_0005: brtrue.s IL_000a + IL_0007: pop + IL_0008: br.s IL_0012 + IL_000a: ldc.i4.2 + IL_000b: dup + IL_000c: stloc.0 + IL_000d: stfld "int C.f" + IL_0012: dup + IL_0013: brtrue.s IL_0021 + IL_0015: pop + IL_0016: ldloca.s V_2 + IL_0018: initobj "int?" + IL_001e: ldloc.2 + IL_001f: br.s IL_002b + IL_0021: ldfld "int C.f" + IL_0026: newobj "int?..ctor(int)" + IL_002b: stloc.1 + IL_002c: ldloca.s V_1 + IL_002e: call "bool int?.HasValue.get" + IL_0033: ldc.i4.0 + IL_0034: ceq + IL_0036: call "void System.Console.Write(bool)" + IL_003b: ldloc.0 + IL_003c: call "void System.Console.Write(int)" + IL_0041: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void FieldAccessAssignment_Nested_03() + { + var source = """ + using System; + + class C + { + string f; + + static void Main() + { + TestNestedCondAssignment(null, null); + TestNestedCondAssignment(new C(), null); + TestNestedCondAssignment(null, new C()); + TestNestedCondAssignment(new C(), new C()); + } + + static void TestNestedCondAssignment(C c1, C c2) + { + GetReceiver(1, c1)?.f = GetReceiver(2, c2)?.f = GetAssignValue(); + Report(c1, c2); + } + + static C GetReceiver(int id, C c) + { + Console.WriteLine($"GetReceiver {id}: {c?.f ?? ""}"); + return c; + } + + static string GetAssignValue() + { + Console.WriteLine($"GetAssignValue"); + return "a"; + } + + static void Report(C c1, C c2) + { + Console.WriteLine($"Report: c1?.f: {c1?.f ?? ""}; c2?.f: {c2?.f ?? ""}"); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: """ + GetReceiver 1: + Report: c1?.f: ; c2?.f: + GetReceiver 1: + GetReceiver 2: + Report: c1?.f: ; c2?.f: + GetReceiver 1: + Report: c1?.f: ; c2?.f: + GetReceiver 1: + GetReceiver 2: + GetAssignValue + Report: c1?.f: a; c2?.f: a + """); + verifier.VerifyIL("C.TestNestedCondAssignment", """ + { + // Code size 53 (0x35) + .maxstack 4 + .locals init (string V_0) + IL_0000: ldc.i4.1 + IL_0001: ldarg.0 + IL_0002: call "C C.GetReceiver(int, C)" + IL_0007: dup + IL_0008: brtrue.s IL_000d + IL_000a: pop + IL_000b: br.s IL_002d + IL_000d: ldc.i4.2 + IL_000e: ldarg.1 + IL_000f: call "C C.GetReceiver(int, C)" + IL_0014: dup + IL_0015: brtrue.s IL_001b + IL_0017: pop + IL_0018: ldnull + IL_0019: br.s IL_0028 + IL_001b: call "string C.GetAssignValue()" + IL_0020: dup + IL_0021: stloc.0 + IL_0022: stfld "string C.f" + IL_0027: ldloc.0 + IL_0028: stfld "string C.f" + IL_002d: ldarg.0 + IL_002e: ldarg.1 + IL_002f: call "void C.Report(C, C)" + IL_0034: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void FieldAccessAssignment_Nested_04() + { + // similar to _03 except the value of the assignment expression is used. + var source = """ + using System; + + class C + { + string f; + + static void Main() + { + TestNestedCondAssignment(null, null); + TestNestedCondAssignment(new C(), null); + TestNestedCondAssignment(null, new C()); + TestNestedCondAssignment(new C(), new C()); + } + + static void TestNestedCondAssignment(C c1, C c2) + { + Report(GetReceiver(1, c1)?.f = GetReceiver(2, c2)?.f = GetAssignValue(), c1, c2); + } + + static C GetReceiver(int id, C c) + { + Console.WriteLine($"GetReceiver {id}: {c?.f ?? ""}"); + return c; + } + + static string GetAssignValue() + { + Console.WriteLine($"GetAssignValue"); + return "a"; + } + + static void Report(string result, C c1, C c2) + { + Console.WriteLine($"Report: result: {result ?? ""}; c1?.f: {c1?.f ?? ""}; c2?.f: {c2?.f ?? ""}"); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: """ + GetReceiver 1: + Report: result: ; c1?.f: ; c2?.f: + GetReceiver 1: + GetReceiver 2: + Report: result: ; c1?.f: ; c2?.f: + GetReceiver 1: + Report: result: ; c1?.f: ; c2?.f: + GetReceiver 1: + GetReceiver 2: + GetAssignValue + Report: result: a; c1?.f: a; c2?.f: a + """); + verifier.VerifyIL("C.TestNestedCondAssignment", """ + { + // Code size 57 (0x39) + .maxstack 4 + .locals init (string V_0) + IL_0000: ldc.i4.1 + IL_0001: ldarg.0 + IL_0002: call "C C.GetReceiver(int, C)" + IL_0007: dup + IL_0008: brtrue.s IL_000e + IL_000a: pop + IL_000b: ldnull + IL_000c: br.s IL_0031 + IL_000e: ldc.i4.2 + IL_000f: ldarg.1 + IL_0010: call "C C.GetReceiver(int, C)" + IL_0015: dup + IL_0016: brtrue.s IL_001c + IL_0018: pop + IL_0019: ldnull + IL_001a: br.s IL_0029 + IL_001c: call "string C.GetAssignValue()" + IL_0021: dup + IL_0022: stloc.0 + IL_0023: stfld "string C.f" + IL_0028: ldloc.0 + IL_0029: dup + IL_002a: stloc.0 + IL_002b: stfld "string C.f" + IL_0030: ldloc.0 + IL_0031: ldarg.0 + IL_0032: ldarg.1 + IL_0033: call "void C.Report(string, C, C)" + IL_0038: ret + } + """); + + verifier.VerifyDiagnostics(); + } + + [Fact] + public void PropertyAccessAssignment_Nested_01() + { + var source = """ + using System; + + class C(string id) + { + public string Id => id; + + C Prop + { + get + { + Console.WriteLine($"Prop.get {id}"); + return field; + } + set + { + Console.WriteLine($"Prop.set {id}"); + field = value; + } + } + + static void Main() + { + TestNestedCondAccess(null); + TestNestedCondAccess(new C("1")); + TestNestedCondAccess(new C("2") { Prop = new C("3") }); + TestNestedCondAccess(new C("4") { Prop = new C("5") { Prop = new C("6") } }); + } + + static void TestNestedCondAccess(C c) + { + Console.WriteLine($"TestNestedCondAccess {c?.Id ?? ""}"); + c?.Prop?.Prop = GetAssignValue(); + } + + static C GetAssignValue() + { + Console.WriteLine("GetAssignValue"); + return new C("7"); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: """ + TestNestedCondAccess + TestNestedCondAccess 1 + Prop.get 1 + Prop.set 2 + TestNestedCondAccess 2 + Prop.get 2 + GetAssignValue + Prop.set 3 + Prop.set 5 + Prop.set 4 + TestNestedCondAccess 4 + Prop.get 4 + GetAssignValue + Prop.set 5 + """); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.TestNestedCondAccess", """ + { + // Code size 61 (0x3d) + .maxstack 3 + IL_0000: ldstr "TestNestedCondAccess " + IL_0005: ldarg.0 + IL_0006: brtrue.s IL_000b + IL_0008: ldnull + IL_0009: br.s IL_0011 + IL_000b: ldarg.0 + IL_000c: call "string C.Id.get" + IL_0011: dup + IL_0012: brtrue.s IL_001a + IL_0014: pop + IL_0015: ldstr "" + IL_001a: call "string string.Concat(string, string)" + IL_001f: call "void System.Console.WriteLine(string)" + IL_0024: ldarg.0 + IL_0025: brfalse.s IL_003c + IL_0027: ldarg.0 + IL_0028: call "C C.Prop.get" + IL_002d: dup + IL_002e: brtrue.s IL_0032 + IL_0030: pop + IL_0031: ret + IL_0032: call "C C.GetAssignValue()" + IL_0037: call "void C.Prop.set" + IL_003c: ret + } + """); + } + + [Fact] + public void PropertyAccessAssignment_Nested_02() + { + // Similar to _01 except the assignment expression is used. + var source = """ + using System; + + class C + { + public C(string id) => Id = id; + public string Id { get; } + + C Prop + { + get + { + Console.WriteLine($"Prop.get {Id} => {field?.Id ?? ""}"); + return field; + } + set + { + Console.WriteLine($"Prop.set {Id} = {value.Id}"); + field = value; + } + } + + static void Main() + { + TestNestedCondAccess(null); + TestNestedCondAccess(new C("1")); + TestNestedCondAccess(new C("2") { Prop = new C("3") }); + TestNestedCondAccess(new C("4") { Prop = new C("5") { Prop = new C("6") } }); + } + + static void TestNestedCondAccess(C c) + { + Report(c?.Prop?.Prop = GetAssignValue()); + } + + static C GetAssignValue() + { + Console.WriteLine("GetAssignValue"); + return new C("7"); + } + + static void Report(C c) + { + Console.WriteLine($"AssignmentResult {c?.Id ?? ""}"); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: """ + AssignmentResult + Prop.get 1 => + AssignmentResult + Prop.set 2 = 3 + Prop.get 2 => 3 + GetAssignValue + Prop.set 3 = 7 + AssignmentResult 7 + Prop.set 5 = 6 + Prop.set 4 = 5 + Prop.get 4 => 5 + GetAssignValue + Prop.set 5 = 7 + AssignmentResult 7 + """); + + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("C.TestNestedCondAccess", """ + { + // Code size 38 (0x26) + .maxstack 3 + .locals init (C V_0) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldnull + IL_0004: br.s IL_0020 + IL_0006: ldarg.0 + IL_0007: call "C C.Prop.get" + IL_000c: dup + IL_000d: brtrue.s IL_0013 + IL_000f: pop + IL_0010: ldnull + IL_0011: br.s IL_0020 + IL_0013: call "C C.GetAssignValue()" + IL_0018: dup + IL_0019: stloc.0 + IL_001a: call "void C.Prop.set" + IL_001f: ldloc.0 + IL_0020: call "void C.Report(C)" + IL_0025: ret + } + """); + } + + [Fact] + public void TypeParameter_01() + { + var source = """ + class C + { + public T t; + public static void M(C c) + { + c?.t = default; + var x = c?.t = default; // 1 + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,19): error CS8978: 'T' cannot be made nullable. + // var x = c?.t = default; // 1 + Diagnostic(ErrorCode.ERR_CannotBeMadeNullable, ".t = default").WithArguments("T").WithLocation(7, 19)); + } + + [Fact] + public void TypeParameter_02() + { + var source = """ + using System; + + class Program + { + public static void Main() + { + C.M(new C()); + } + } + + class C where T : class + { + public T t; + public static void M(C c) + { + c?.t = null; + var x = c?.t = null; + Console.Write(c.t is null); + Console.Write(x is null); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "TrueTrue"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 80 (0x50) + .maxstack 3 + .locals init (T V_0) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000f + IL_0003: ldarg.0 + IL_0004: ldflda "T C.t" + IL_0009: initobj "T" + IL_000f: ldarg.0 + IL_0010: brtrue.s IL_001d + IL_0012: ldloca.s V_0 + IL_0014: initobj "T" + IL_001a: ldloc.0 + IL_001b: br.s IL_002f + IL_001d: ldarg.0 + IL_001e: ldloca.s V_0 + IL_0020: initobj "T" + IL_0026: ldloc.0 + IL_0027: dup + IL_0028: stloc.0 + IL_0029: stfld "T C.t" + IL_002e: ldloc.0 + IL_002f: ldarg.0 + IL_0030: ldfld "T C.t" + IL_0035: box "T" + IL_003a: ldnull + IL_003b: ceq + IL_003d: call "void System.Console.Write(bool)" + IL_0042: box "T" + IL_0047: ldnull + IL_0048: ceq + IL_004a: call "void System.Console.Write(bool)" + IL_004f: ret + } + """); + } + + [Fact] + public void UseResult_ReferenceType() + { + var source = """ + using System; + + class Program + { + public static void Main() + { + C.M(new C()); + C.M(null); + } + } + + class C + { + public string t; + public static void M(C c) + { + c?.t = "a"; + var x = c?.t = "a"; + Console.Write(c?.t ?? "b"); + Console.Write(x ?? "b"); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "aabb"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void UseResult_ElementAssignment() + { + var source = """ + using System; + + class Program + { + public static void Main() + { + C.M(new C()); + C.M(null); + } + } + + class C + { + private string t; + public string this[string s] { get => t; set => t = value; } + public static void M(C c) + { + c?["a"] = "b"; + var x = c?["a"] = "b"; + Console.Write(c?.t ?? "c"); + Console.Write(x ?? "c"); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "bbcc"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void SideEffects_01() + { + // Arguments to an indexer assignment are conditionally evaluated + var source = """ + using System; + + class C + { + public string this[string s] { get => s; set { Console.Write($"(set {value})"); } } + + public static string GetString() + { + Console.Write("GetString()"); + return "a"; + } + + public static void M(C c) + { + Console.Write((c?[GetString()] = "b") ?? "c"); + } + + public static void Main() + { + M(new C()); + M(null); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "GetString()(set b)bc"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void SideEffects_02() + { + // Arguments to an invocation assignment are conditionally evaluated + var source = """ + using System; + + class C + { + public static string _s; + public ref string M(string s) { Console.Write($"M({s})"); return ref _s; } + + public static string GetString() + { + Console.Write("GetString()"); + return "a"; + } + + public static void M(C c) + { + Console.Write((c?.M(GetString()) = "b") ?? "c"); + } + + public static void Main() + { + M(new C()); + M(null); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "GetString()M(a)bc"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void Await_01() + { + var source = """ + using System.Threading.Tasks; + using System; + + class C + { + public string F; + + static async Task M(Task tc) + { + (await tc)?.F = "a"; + } + + public static async Task Main() + { + var c = new C(); + await M(Task.FromResult(c)); + Console.Write(c.F); + + c = null; + await M(Task.FromResult(c)); + Console.Write(c?.F ?? "b"); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "ab"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void TypeParameter_03() + { + var source = """ + using System; + + class Program + { + public static void Main() + { + C.M(new C(), 1); + } + } + + class C where T : struct + { + public T t; + public static void M(C c, T param) + { + c?.t = param; + var x = c?.t = param; + Console.Write(c?.t); + Console.Write(x); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: "11"); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M", """ + { + // Code size 85 (0x55) + .maxstack 3 + .locals init (T? V_0, + T V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_000a + IL_0003: ldarg.0 + IL_0004: ldarg.1 + IL_0005: stfld "T C.t" + IL_000a: ldarg.0 + IL_000b: brtrue.s IL_0018 + IL_000d: ldloca.s V_0 + IL_000f: initobj "T?" + IL_0015: ldloc.0 + IL_0016: br.s IL_0027 + IL_0018: ldarg.0 + IL_0019: ldarg.1 + IL_001a: dup + IL_001b: stloc.1 + IL_001c: stfld "T C.t" + IL_0021: ldloc.1 + IL_0022: newobj "T?..ctor(T)" + IL_0027: ldarg.0 + IL_0028: brtrue.s IL_0035 + IL_002a: ldloca.s V_0 + IL_002c: initobj "T?" + IL_0032: ldloc.0 + IL_0033: br.s IL_0040 + IL_0035: ldarg.0 + IL_0036: ldfld "T C.t" + IL_003b: newobj "T?..ctor(T)" + IL_0040: box "T?" + IL_0045: call "void System.Console.Write(object)" + IL_004a: box "T?" + IL_004f: call "void System.Console.Write(object)" + IL_0054: ret + } + """); + } + + [Fact] + public void TypeParameter_04() + { + var source = """ + using System; + + C c = null; + F1(c); + + c = new C(); + F1(c); + Console.WriteLine($"Assigned value: {c.P}"); + + S s = default; + s = F1(s); + Console.WriteLine($"Assigned value: {s.P}"); + + partial class Program + { + static T F1(T t) where T : I + { + t?.P = GetValue(1); + return t; + } + + static int GetValue(int i) + { + Console.WriteLine($"GetValue {i}"); + return i; + } + } + + interface I + { + int P { get; set; } + } + + class C : I + { + public int P { get; set; } + } + + struct S : I + { + public int P { get; set; } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: """ + GetValue 1 + Assigned value: 1 + GetValue 1 + Assigned value: 1 + """); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.F1", """ + { + // Code size 56 (0x38) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldarga.s V_0 + IL_0002: ldloca.s V_0 + IL_0004: initobj "T" + IL_000a: ldloc.0 + IL_000b: box "T" + IL_0010: brtrue.s IL_0025 + IL_0012: ldobj "T" + IL_0017: stloc.0 + IL_0018: ldloca.s V_0 + IL_001a: ldloc.0 + IL_001b: box "T" + IL_0020: brtrue.s IL_0025 + IL_0022: pop + IL_0023: br.s IL_0036 + IL_0025: ldc.i4.1 + IL_0026: call "int Program.GetValue(int)" + IL_002b: constrained. "T" + IL_0031: callvirt "void I.P.set" + IL_0036: ldarg.0 + IL_0037: ret + } + """); + } + + [Fact] + public void TypeParameter_05() + { + var source = """ + class C + { + static void F2(T? t) where T : struct, I + { + t?.P = 1; // 1 + } + } + + interface I + { + int P { get; set; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,11): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // t?.P = 1; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, ".P").WithLocation(5, 11)); + } + + [Fact] + public void Parentheses_Assignment_LHS_01() + { + var source = """ + using System; + + class C + { + int F; + static void M(C c) + { + (c?.F) = 1; // 1 + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,9): warning CS0649: Field 'C.F' is never assigned to, and will always have its default value 0 + // int F; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("C.F", "0").WithLocation(5, 9), + // (8,10): error CS0131: The left-hand side of an assignment must be a variable, property or indexer + // (c?.F) = 1; // 1 + Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "c?.F").WithLocation(8, 10)); + } + + [Fact] + public void NullCoalescingAssignment_01() + { + var source = """ + using System; + + class C + { + string F; + static void Main() + { + M1(null); + M2(null); + M1(new C()); + M2(new C()); + M1(new C() { F = "b" }); + M2(new C() { F = "b" }); + } + + static string GetAssignValue() + { + Console.WriteLine("GetAssignValue"); + return "a"; + } + + static void M1(C c) + { + c?.F ??= GetAssignValue(); + Report(c?.F); + } + + static void M2(C c) + { + Report(c?.F ??= GetAssignValue()); + } + + static void Report(string value) + { + Console.WriteLine(value ?? ""); + } + } + """; + + var verifier = CompileAndVerify(source, expectedOutput: """ + + + GetAssignValue + a + GetAssignValue + a + b + b + """); + verifier.VerifyIL("C.M1", """ + { + // Code size 42 (0x2a) + .maxstack 2 + .locals init (C V_0) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0018 + IL_0003: ldarg.0 + IL_0004: stloc.0 + IL_0005: ldloc.0 + IL_0006: ldfld "string C.F" + IL_000b: brtrue.s IL_0018 + IL_000d: ldloc.0 + IL_000e: call "string C.GetAssignValue()" + IL_0013: stfld "string C.F" + IL_0018: ldarg.0 + IL_0019: brtrue.s IL_001e + IL_001b: ldnull + IL_001c: br.s IL_0024 + IL_001e: ldarg.0 + IL_001f: ldfld "string C.F" + IL_0024: call "void C.Report(string)" + IL_0029: ret + } + """); + verifier.VerifyIL("C.M2", """ + { + // Code size 38 (0x26) + .maxstack 3 + .locals init (C V_0, + string V_1) + IL_0000: ldarg.0 + IL_0001: brtrue.s IL_0006 + IL_0003: ldnull + IL_0004: br.s IL_0020 + IL_0006: ldarg.0 + IL_0007: stloc.0 + IL_0008: ldloc.0 + IL_0009: ldfld "string C.F" + IL_000e: dup + IL_000f: brtrue.s IL_0020 + IL_0011: pop + IL_0012: ldloc.0 + IL_0013: call "string C.GetAssignValue()" + IL_0018: dup + IL_0019: stloc.1 + IL_001a: stfld "string C.F" + IL_001f: ldloc.1 + IL_0020: call "void C.Report(string)" + IL_0025: ret + } + """); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void NullCoalescingAssignValue_01() + { + // rhs of assignment is a '??' expr. + var source = """ + class C + { + int F; + + static void M(C c) + { + int i = c?.F = 1 ?? 2; // 1 + int j = (c?.F = 3) ?? 4; // ok + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (7,24): error CS0019: Operator '??' cannot be applied to operands of type 'int' and 'int' + // int i = c?.F = 1 ?? 2; // 1 + Diagnostic(ErrorCode.ERR_BadBinaryOps, "1 ?? 2").WithArguments("??", "int", "int").WithLocation(7, 24)); + } + + [Fact] + public void DefiniteAssignment_01() + { + // nb: there are no interesting cases involving struct fields + // since those will all have non-nullable value type receivers + // Instead we can exercise AnalyzeDataFlow + var source = """ + class C + { + string F; + + static void M(C c) + { + c?.F = "a"; + } + } + """; + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var analysis = model.AnalyzeDataFlow(node); + Assert.Empty(analysis.AlwaysAssigned); + Assert.Equal("C c", analysis.ReadInside.Single().ToTestDisplayString()); + Assert.Empty(analysis.WrittenInside); + + var expr = tree.GetRoot().DescendantNodes().OfType().Single(); + analysis = model.AnalyzeDataFlow(expr); + Assert.Empty(analysis.AlwaysAssigned); + Assert.Empty(analysis.ReadInside); + Assert.Empty(analysis.WrittenInside); + } + + [Fact] + public void DefiniteAssignment_02() + { + // Show similarity with an equivalent case that doesn't use a conditional access + var source = """ + class C + { + string F; + + static void M(C c) + { + if (c != null) + c.F = "a"; + } + } + """; + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + + var node = tree.GetRoot().DescendantNodes().OfType().Single(); + var analysis = model.AnalyzeDataFlow(node); + Assert.Empty(analysis.AlwaysAssigned); + Assert.Equal("C c", analysis.ReadInside.Single().ToTestDisplayString()); + Assert.Empty(analysis.WrittenInside); + + var expr = tree.GetRoot().DescendantNodes().OfType().Single(); + analysis = model.AnalyzeDataFlow(node); + Assert.Empty(analysis.AlwaysAssigned); + Assert.Equal("C c", analysis.ReadInside.Single().ToTestDisplayString()); + Assert.Empty(analysis.WrittenInside); + } + + [Fact] + public void DefiniteAssignment_03() + { + var source = """ + #nullable enable + + class C + { + ref string M1(int p) => throw null!; + static void M2(C? c) + { + int a; + c?.M1(a = 42) = "b"; + a.ToString(); // 1 + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (10,9): error CS0165: Use of unassigned local variable 'a' + // a.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(10, 9)); + } + + [Fact] + public void DefiniteAssignment_04() + { + var source = """ + #nullable enable + using System; + + class C + { + static string? _s; + ref string? M1(int p1, string p2) + { + Console.Write(p1); + Console.Write(p2); + return ref _s; + } + static void M2(C? c) + { + int a; + c?.M1(a = 42, a.ToString()) = "b"; + Console.Write(_s); + } + + static void Main() + { + M2(new C()); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "4242b"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void NullableAnalysis_01() + { + var source = """ + #nullable enable + + class C + { + string? F; + + static void M1(C c) + { + c.F.ToString(); // 1 + c?.F = "a"; + c.F.ToString(); // 2 + } + + static void M2(C c) + { + c?.F = "a"; + c.F.ToString(); // 3, 4 + } + + static void M3(C c) + { + if ((c?.F = "a") != null) + { + c.F.ToString(); + } + c.F.ToString(); // 5, 6 + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c.F").WithLocation(9, 9), + // (11,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(11, 9), + // (17,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 3, 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(17, 9), + // (17,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 3, 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c.F").WithLocation(17, 9), + // (26,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 5, 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c").WithLocation(26, 9), + // (26,9): warning CS8602: Dereference of a possibly null reference. + // c.F.ToString(); // 5, 6 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c.F").WithLocation(26, 9)); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/77741")] + public void NullableAnalysis_02() + { + // Problem: the conditional receiver and its field are getting their own slots. + // But, when the assignment '.F = null' is processed, we do look up the slot for 'c' thru 'NullableWalker._lastConditionalAccessSlot'. + // Thus the state for 'c.F' gets updated to maybe-null, but the state for '.F' remains not-null. + // When we get a slot for RHS of next 'c?.F' expression, we get the slot for '.F', and see the .F as having not-null state. + // Thus, the expected warning is missing. + // We may want to solve this by ensuring we don't create a slot for the placeholder, and that getting a slot for the placeholder always gives the slot for the original receiver instead. + var source = """ + #nullable enable + + class C + { + string? F; + + static void M1() + { + var c = new C { F = "a" }; + c.F.ToString(); + c?.F = null; + c?.F.ToString(); // 1 + } + } + """; + + var comp = CreateCompilation(source); + // Expected warning is missing here. https://github.com/dotnet/roslyn/issues/77741 + comp.VerifyEmitDiagnostics(); + } + + [Fact] + public void NullableAnalysis_03() + { + var source = """ + #nullable enable + using System; + + class C + { + string? F; + + static void M(C? c) + { + c?.F = c.F; + Console.Write(c?.F ?? ""); + } + + static void Main() + { + M(new C() { F = "a" }); + M(null); + } + } + """; + var verifier = CompileAndVerify(source, expectedOutput: "a"); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void NullableAnalysis_04() + { + var source = """ + #nullable enable + + class C + { + public string? F = null; + + static void M(C? c1, C c2) + { + c1?.F! = "a"; // 1 + c2.F! = "a"; // 2 + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (9,12): error CS8598: The suppression operator is not allowed in this context + // c1?.F! = "a"; // 1 + Diagnostic(ErrorCode.ERR_IllegalSuppression, ".F").WithLocation(9, 12), + // (10,9): error CS8598: The suppression operator is not allowed in this context + // c2.F! = "a"; // 2 + Diagnostic(ErrorCode.ERR_IllegalSuppression, "c2.F").WithLocation(10, 9)); + } + + [Fact] + public void IncrementDecrement_01() + { + var source = """ + class C + { + int F; + static void M(C c) + { + c?.F++; + --c?.F; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,9): warning CS0649: Field 'C.F' is never assigned to, and will always have its default value 0 + // int F; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "F").WithArguments("C.F", "0").WithLocation(3, 9), + // (6,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // c?.F++; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "c?.F").WithLocation(6, 9), + // (7,11): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer + // --c?.F; + Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "c?.F").WithLocation(7, 11)); + } + + [Fact] + public void ControlFlowGraph_01() + { + // Verify that conditional accesses and expressions are rewritten in CFG. + var source = """ + class C + { + public string F; + + static void M(C? c, bool b) + { + c?.F = b ? "1" : "2"; + } + } + """; + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var methodDecl = tree.GetRoot().DescendantNodes().OfType().Single(); + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(methodDecl.Body, model); + ControlFlowGraphVerifier.VerifyGraph(comp, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} {R2} + .locals {R1} + { + CaptureIds: [1] [2] + .locals {R2} + { + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'c') + Value: + IParameterReferenceOperation: c (OperationKind.ParameterReference, Type: C) (Syntax: 'c') + Jump if True (Regular) to Block[B7] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'c') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'c') + Leaving: {R2} {R1} + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '.F') + Value: + IFieldReferenceOperation: System.String C.F (OperationKind.FieldReference, Type: System.String) (Syntax: '.F') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'c') + Next (Regular) Block[B3] + Leaving: {R2} + } + Block[B3] - Block + Predecessors: [B2] + Statements (0) + Jump if False (Regular) to Block[B5] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B3] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '"1"') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "1") (Syntax: '"1"') + Next (Regular) Block[B6] + Block[B5] - Block + Predecessors: [B3] + Statements (1) + IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '"2"') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "2") (Syntax: '"2"') + Next (Regular) Block[B6] + Block[B6] - Block + Predecessors: [B4] [B5] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'c?.F = b ? "1" : "2";') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String) (Syntax: '.F = b ? "1" : "2"') + Left: + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.String, IsImplicit) (Syntax: '.F') + Right: + IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.String, IsImplicit) (Syntax: 'b ? "1" : "2"') + Next (Regular) Block[B7] + Leaving: {R1} + } + Block[B7] - Exit + Predecessors: [B1] [B6] + Statements (0) + """, + graph, symbol); + } + + [Fact] + public void ControlFlowGraph_02() + { + // Verify that conditional accesses and expressions are rewritten in CFG. + var source = """ + class C + { + public string F; + + static void M(bool b, C? c1, C? c2) + { + (b ? c1 : c2)?.F = "a"; + } + } + """; + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var methodDecl = tree.GetRoot().DescendantNodes().OfType().Single(); + var (graph, symbol) = ControlFlowGraphVerifier.GetControlFlowGraph(methodDecl.Body, model); + ControlFlowGraphVerifier.VerifyGraph(comp, """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + .locals {R1} + { + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Jump if False (Regular) to Block[B3] + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Next (Regular) Block[B2] + Block[B2] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'c1') + Value: + IParameterReferenceOperation: c1 (OperationKind.ParameterReference, Type: C) (Syntax: 'c1') + Next (Regular) Block[B4] + Block[B3] - Block + Predecessors: [B1] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'c2') + Value: + IParameterReferenceOperation: c2 (OperationKind.ParameterReference, Type: C) (Syntax: 'c2') + Next (Regular) Block[B4] + Block[B4] - Block + Predecessors: [B2] [B3] + Statements (0) + Jump if True (Regular) to Block[B6] + IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'b ? c1 : c2') + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'b ? c1 : c2') + Leaving: {R1} + Next (Regular) Block[B5] + Block[B5] - Block + Predecessors: [B4] + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: '(b ? c1 : c2)?.F = "a";') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.String) (Syntax: '.F = "a"') + Left: + IFieldReferenceOperation: System.String C.F (OperationKind.FieldReference, Type: System.String) (Syntax: '.F') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: C, IsImplicit) (Syntax: 'b ? c1 : c2') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "a") (Syntax: '"a"') + Next (Regular) Block[B6] + Leaving: {R1} + } + Block[B6] - Exit + Predecessors: [B4] [B5] + Statements (0) + """, + graph, symbol); + } + } +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticAnalyzerTests.cs index 93ad025784bbc..a0590ad3d72c5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SemanticAnalyzerTests.cs @@ -450,16 +450,16 @@ static void M() } }"; CreateCompilationWithMscorlib40(source).VerifyDiagnostics( - // (69,28): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (69,28): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // public static P Select(this P p, Func projection) Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(69, 28), - // (84,29): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (84,29): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // public static void PExt(this P p) {} Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(84, 29), - // (80,33): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (80,33): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // public static object Select(this Q q, object projection) Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(80, 33), - // (76,28): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (76,28): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // public static P Select(this P p, Func projection) Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(76, 28), // (95,11): error CS0121: The call is ambiguous between the following methods or properties: 'P.Ambiguous(string, object)' and 'P.Ambiguous(object, string)' diff --git a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs index b0882d7a80536..d588cfd967eca 100644 --- a/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs +++ b/src/Compilers/CSharp/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.cs @@ -549,7 +549,7 @@ class C { } [Fact] public void FindAttributeOnTopLevelClass_WhenSearchingForClassDeclaration_OuterAliasReferencesInnerAlias() { - // note: this is not legal. it's ok if this ever stops working in the futuer. + // note: this is not legal. it's ok if this ever stops working in the future. var source = """ using BAttribute = AAttribute; namespace N diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs index f4f2e6bcdee7b..e23a2130fd8f5 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetSemanticInfoTests.cs @@ -268,21 +268,16 @@ class K "; var semanticInfo = GetSemanticInfoForTest(sourceCode); - 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.Null(semanticInfo.Type); + Assert.Null(semanticInfo.ConvertedType); Assert.Equal(ConversionKind.Identity, semanticInfo.ImplicitConversion.Kind); Assert.Null(semanticInfo.Symbol); - 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(CandidateReason.None, semanticInfo.CandidateReason); + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : see if we can restore a behavior closer to previous (ie. returning the field as candidate symbol) + Assert.Empty(semanticInfo.CandidateSymbols); Assert.Equal(0, semanticInfo.MethodGroup.Length); - Assert.False(semanticInfo.IsCompileTimeConstant); } diff --git a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs index a795619ee010d..596f54386b7a9 100644 --- a/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/DocumentationComments/DocumentationCommentCompilerTests.cs @@ -1972,9 +1972,9 @@ void verify(CSharpTestSource source) Assert.Equal("p2", indexer.Parameters.Single().Name); }); verifier.VerifyDiagnostics( - // (5,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // (5,24): warning CS9256: Partial member declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. // public partial int this[int p1] => 42; - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); var actual = GetDocumentationCommentText(compilation); var expected = """ @@ -2033,9 +2033,9 @@ void verify(CSharpTestSource source) // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p2', but there is no parameter by that name // /** Accepts . */ Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p2").WithArguments("p2", "C.this[int]").WithLocation(4, 42), - // (5,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // (5,24): warning CS9256: Partial member declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. // public partial int this[int p1] => 42; - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(5, 24)); var actual = GetDocumentationCommentText(compilation, // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p2', but there is no parameter by that name @@ -2094,9 +2094,9 @@ void verify(CSharpTestSource source) Assert.Equal("p2", indexer.Parameters.Single().Name); }); verifier.VerifyDiagnostics( - // (4,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // (4,24): warning CS9256: Partial member declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. // public partial int this[int p1] => 42; - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24), // (4,42): warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'p1', but there is no parameter by that name // /** Accepts . */ Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p1").WithArguments("p1", "C.this[int]").WithLocation(4, 42)); @@ -2158,9 +2158,9 @@ void verify(CSharpTestSource source) Assert.Equal("p2", indexer.Parameters.Single().Name); }); verifier.VerifyDiagnostics( - // (4,24): warning CS9256: Partial property declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. + // (4,24): warning CS9256: Partial member declarations 'int C.this[int p2]' and 'int C.this[int p1]' have signature differences. // public partial int this[int p1] => 42; - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p2]", "int C.this[int p1]").WithLocation(4, 24)); var actual = GetDocumentationCommentText(compilation); var expected = """ @@ -2180,6 +2180,1061 @@ void verify(CSharpTestSource source) } } + [Fact] + public void PartialEvent_NoImplementation() + { + var source = """ + public partial class C + { + /** Summary 1 */ + public partial event System.Action E; + } + """; + var comp = CreateCompilation(source, + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics( + // (1,22): warning CS1591: Missing XML comment for publicly visible type or member 'C' + // public partial class C + Diagnostic(ErrorCode.WRN_MissingXMLComment, "C").WithArguments("C").WithLocation(1, 22), + // (4,40): error CS9275: Partial member 'C.E' must have an implementation part. + // public partial event System.Action E; + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "E").WithArguments("C.E").WithLocation(4, 40)); + var e = comp.GlobalNamespace.GetMember("C.E"); + + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + + + + Test + + + + + """, GetDocumentationCommentText(comp, + // (1,22): warning CS1591: Missing XML comment for publicly visible type or member 'C' + // public partial class C + Diagnostic(ErrorCode.WRN_MissingXMLComment, "C").WithArguments("C").WithLocation(1, 22))); + + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + + Summary 1 + + """, DocumentationCommentCompiler.GetDocumentationCommentXml(e, processIncludes: true, cancellationToken: default)); + } + + [Fact] + public void PartialConstructor_NoImplementation() + { + var source = """ + public partial class C + { + /** Summary 1 */ + public partial C(); + } + """; + var comp = CreateCompilation(source, + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics( + // (1,22): warning CS1591: Missing XML comment for publicly visible type or member 'C' + // public partial class C + Diagnostic(ErrorCode.WRN_MissingXMLComment, "C").WithArguments("C").WithLocation(1, 22), + // (4,20): error CS9275: Partial member 'C.C()' must have an implementation part. + // public partial C(); + Diagnostic(ErrorCode.ERR_PartialMemberMissingImplementation, "C").WithArguments("C.C()").WithLocation(4, 20)); + var ctor = comp.GlobalNamespace.GetMember("C..ctor"); + + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + + + + Test + + + + + """, GetDocumentationCommentText(comp, + // (1,22): warning CS1591: Missing XML comment for publicly visible type or member 'C' + // public partial class C + Diagnostic(ErrorCode.WRN_MissingXMLComment, "C").WithArguments("C").WithLocation(1, 22))); + + AssertEx.AssertEqualToleratingWhitespaceDifferences(""" + + Summary 1 + + """, DocumentationCommentCompiler.GetDocumentationCommentXml(ctor, processIncludes: true, cancellationToken: default)); + } + + [Fact] + public void PartialEvent_MultipleFiles() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + /** Summary 1 */ + public partial event System.Action E; + } + """; + var source2 = """ + public partial class C + { + /** Summary 2 */ + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 2 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialEvent_MultipleFiles_ImplementationComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + public partial event System.Action E; + } + """; + var source2 = """ + public partial class C + { + /** Summary 2 */ + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 2 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialEvent_MultipleFiles_DefinitionComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + /** Summary 1 */ + public partial event System.Action E; + } + """; + var source2 = """ + public partial class C + { + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 1 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + /** Summary 1 */ + public partial C(); + } + """; + var source2 = """ + public partial class C + { + /** Summary 2 */ + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 2 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_ImplementationComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + public partial C(); + } + """; + var source2 = """ + public partial class C + { + /** Summary 2 */ + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 2 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_DefinitionComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + /** Summary 1 */ + public partial C(); + } + """; + var source2 = """ + public partial class C + { + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + Summary 1 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialEvent_MultipleFiles_NoComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + public partial event System.Action E; + } + """; + var source2 = """ + public partial class C + { + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + + """; + + var expectedDiagnostics = new[] + { + // (4,40): warning CS1591: Missing XML comment for publicly visible type or member 'C.E' + // public partial event System.Action E; + Diagnostic(ErrorCode.WRN_MissingXMLComment, "E").WithArguments("C.E").WithLocation(4, 40) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_NoComment() + { + var source1 = """ + /** Summary 0 */ + public partial class C + { + public partial C(); + } + """; + var source2 = """ + public partial class C + { + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + Summary 0 + + + + """; + + var expectedDiagnostics = new[] + { + // (4,20): warning CS1591: Missing XML comment for publicly visible type or member 'C.C()' + // public partial C(); + Diagnostic(ErrorCode.WRN_MissingXMLComment, "C").WithArguments("C.C()").WithLocation(4, 20) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + } + + [Fact] + public void PartialEvent_MultipleFiles_Overlap() + { + var source1 = """ + partial class C + { + /** Summary 1 */ + public partial event System.Action E; + } + """; + var source2 = """ + partial class C + { + /** Remarks 2 */ + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Remarks 2 + + + + """; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_Overlap() + { + var source1 = """ + partial class C + { + /// Summary 1 + /// Param 1 + public partial C(int x, int y); + } + """; + var source2 = """ + partial class C + { + /// Remarks 2 + /// Param 2 + public partial C(int x, int y) { } + } + """; + + var expected = """ + + + + Test + + + + Remarks 2 + Param 2 + + + + """; + + var expectedDiagnostics = new[] + { + // (5,26): warning CS1573: Parameter 'x' has no matching param tag in the XML comment for 'C.C(int, int)' (but other parameters do) + // public partial C(int x, int y) { } + Diagnostic(ErrorCode.WRN_MissingParamTag, "x").WithArguments("x", "C.C(int, int)").WithLocation(5, 26), + // (5,33): warning CS1573: Parameter 'y' has no matching param tag in the XML comment for 'C.C(int, int)' (but other parameters do) + // public partial C(int x, int y); + Diagnostic(ErrorCode.WRN_MissingParamTag, "y").WithArguments("y", "C.C(int, int)").WithLocation(5, 33) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedDiagnostics)); + } + + [Fact] + public void PartialEvent_MultipleFiles_InvalidImplComment() + { + var source1 = """ + partial class C + { + /** Summary 1 */ + public partial event System.Action E; + } + """; + var source2 = """ + partial class C + { + /** */ + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + + + """; + + var expectedDiagnostics = new[] + { + // (3,20): warning CS1570: XML comment has badly formed XML -- 'End tag 'a' does not match the start tag 'summary'.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "a").WithArguments("a", "summary").WithLocation(3, 20), + // (3,22): warning CS1570: XML comment has badly formed XML -- 'End tag was not expected at this location.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "<").WithLocation(3, 22) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_InvalidImplComment() + { + var source1 = """ + partial class C + { + /** Summary 1 */ + public partial C(); + } + """; + var source2 = """ + partial class C + { + /** */ + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + + + """; + + var expectedDiagnostics = new[] + { + // (3,20): warning CS1570: XML comment has badly formed XML -- 'End tag 'a' does not match the start tag 'summary'.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "a").WithArguments("a", "summary").WithLocation(3, 20), + // (3,22): warning CS1570: XML comment has badly formed XML -- 'End tag was not expected at this location.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "<").WithLocation(3, 22) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialEvent_MultipleFiles_InvalidDefComment() + { + var source1 = """ + partial class C + { + /** */ + public partial event System.Action E; + } + """; + var source2 = """ + partial class C + { + /** Summary 2 */ + public partial event System.Action E { add { } remove { } } + } + """; + + var expected = """ + + + + Test + + + + Summary 2 + + + + + """; + + var expectedDiagnostics = new[] + { + // (3,20): warning CS1570: XML comment has badly formed XML -- 'End tag 'a' does not match the start tag 'summary'.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "a").WithArguments("a", "summary").WithLocation(3, 20), + // (3,22): warning CS1570: XML comment has badly formed XML -- 'End tag was not expected at this location.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "<").WithLocation(3, 22) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_MultipleFiles_InvalidDefComment() + { + var source1 = """ + partial class C + { + /** */ + public partial C(); + } + """; + var source2 = """ + partial class C + { + /** Summary 2 */ + public partial C() { } + } + """; + + var expected = """ + + + + Test + + + + Summary 2 + + + + + """; + + var expectedDiagnostics = new[] + { + // (3,20): warning CS1570: XML comment has badly formed XML -- 'End tag 'a' does not match the start tag 'summary'.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "a").WithArguments("a", "summary").WithLocation(3, 20), + // (3,22): warning CS1570: XML comment has badly formed XML -- 'End tag was not expected at this location.' + // /** */ + Diagnostic(ErrorCode.WRN_XMLParseError, "<").WithLocation(3, 22) + }; + + var comp = CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments) + .VerifyDiagnostics(expectedDiagnostics); + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + } + + [Fact] + public void PartialConstructor_Paramref_01() + { + var source1 = """ + partial class C + { + /** Accepts . */ + public partial C(int p1) { } + } + """; + var source2 = """ + partial class C + { + public partial C(int p2); + } + """; + + var expected = """ + + + + Test + + + + Accepts . + + + + """; + + var expectedDiagnostics = new[] + { + // (4,20): warning CS9256: Partial member declarations 'C.C(int p2)' and 'C.C(int p1)' have signature differences. + // public partial C(int p1) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int p2)", "C.C(int p1)").WithLocation(4, 20) + }; + + var comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + static void validate(ModuleSymbol module) + { + var ctor = module.GlobalNamespace.GetMember("C..ctor"); + Assert.Equal("p2", ctor.Parameters.Single().Name); + } + } + + [Fact] + public void PartialConstructor_Paramref_02() + { + var source1 = """ + partial class C + { + /** Accepts . */ + public partial C(int p1) { } + } + """; + var source2 = """ + partial class C + { + public partial C(int p2); + } + """; + + var expected = """ + + + + Test + + + + Accepts . + + + + """; + + var expectedXmlDiagnostic = + // (3,42): warning CS1734: XML comment on 'C.C(int)' has a paramref tag for 'p2', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p2").WithArguments("p2", "C.C(int)").WithLocation(3, 42); + + var expectedDiagnostics = new[] + { + expectedXmlDiagnostic, + // (4,20): warning CS9256: Partial member declarations 'C.C(int p2)' and 'C.C(int p1)' have signature differences. + // public partial C(int p1) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int p2)", "C.C(int p1)").WithLocation(4, 20) + }; + + var comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedXmlDiagnostic)); + + comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedXmlDiagnostic)); + + static void validate(ModuleSymbol module) + { + var ctor = module.GlobalNamespace.GetMember("C..ctor"); + Assert.Equal("p2", ctor.Parameters.Single().Name); + } + } + + [Fact] + public void PartialConstructor_Paramref_03() + { + var source1 = """ + partial class C + { + public partial C(int p1) { } + } + """; + var source2 = """ + partial class C + { + /** Accepts . */ + public partial C(int p2); + } + """; + + var expected = """ + + + + Test + + + + Accepts . + + + + """; + + var expectedXmlDiagnostic = + // (3,42): warning CS1734: XML comment on 'C.C(int)' has a paramref tag for 'p1', but there is no parameter by that name + // /** Accepts . */ + Diagnostic(ErrorCode.WRN_UnmatchedParamRefTag, "p1").WithArguments("p1", "C.C(int)").WithLocation(3, 42); + + var expectedDiagnostics = new[] + { + // (3,20): warning CS9256: Partial member declarations 'C.C(int p2)' and 'C.C(int p1)' have signature differences. + // public partial C(int p1) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int p2)", "C.C(int p1)").WithLocation(3, 20), + expectedXmlDiagnostic + }; + + var comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedXmlDiagnostic)); + + comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp, expectedXmlDiagnostic)); + + static void validate(ModuleSymbol module) + { + var ctor = module.GlobalNamespace.GetMember("C..ctor"); + Assert.Equal("p2", ctor.Parameters.Single().Name); + } + } + + [Fact] + public void PartialConstructor_Paramref_04() + { + var source1 = """ + partial class C + { + public partial C(int p1) { } + } + """; + var source2 = """ + partial class C + { + /** Accepts . */ + public partial C(int p2); + } + """; + + var expected = """ + + + + Test + + + + Accepts . + + + + """; + + var expectedDiagnostics = new[] + { + // (3,20): warning CS9256: Partial member declarations 'C.C(int p2)' and 'C.C(int p1)' have signature differences. + // public partial C(int p1) { } + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "C").WithArguments("C.C(int p2)", "C.C(int p1)").WithLocation(3, 20) + }; + + var comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source1, source2], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + comp = (CSharpCompilation)CompileAndVerify(CreateCompilation([source2, source1], + assemblyName: "Test", + parseOptions: TestOptions.RegularPreviewWithDocumentationComments), + sourceSymbolValidator: validate, + symbolValidator: validate) + .VerifyDiagnostics(expectedDiagnostics).Compilation; + AssertEx.AssertEqualToleratingWhitespaceDifferences(expected, GetDocumentationCommentText(comp)); + + static void validate(ModuleSymbol module) + { + var ctor = module.GlobalNamespace.GetMember("C..ctor"); + Assert.Equal("p2", ctor.Parameters.Single().Name); + } + } + #endregion Partial methods #region Crefs diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/CheckedUserDefinedOperatorsTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/CheckedUserDefinedOperatorsTests.cs index 50b3fe4037aac..e9af65d9d9309 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/CheckedUserDefinedOperatorsTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/CheckedUserDefinedOperatorsTests.cs @@ -1994,7 +1994,7 @@ class C [InlineData("-")] [InlineData("*")] [InlineData("/")] - public void BinarOperator_Supported_CRef_TwoParameters_03(string op) + public void BinaryOperator_Supported_CRef_TwoParameters_03(string op) { var source = @" /// diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs index ee425b79b53d3..9d38781e3e73e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ConversionTests.cs @@ -525,7 +525,7 @@ public static implicit operator C(System.Func intDelegate) } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75833")] - public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InAssignemnt() + public void GetSymbolInfo_ImplicitUserDefinedConversionOnMethodGroup_InAssignment() { var src = """ public class C diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs index 28b8ca9a92740..e27380e19f546 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/DefaultInterfaceImplementationTests.cs @@ -44585,30 +44585,36 @@ interface I19 // (62,20): error CS0106: The modifier 'virtual' is not valid for this item // virtual static I13() => throw null; Diagnostic(ErrorCode.ERR_BadMemberFlag, "I13").WithArguments("virtual").WithLocation(62, 20), - // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I14(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5), - // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (66,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I14(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(66, 5), // (70,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) // static partial I15(); Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(70, 12), + // (70,12): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static partial I15(); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(70, 12), // (70,20): error CS0501: 'I15.I15()' must declare a body because it is not marked abstract, extern, or partial // static partial I15(); Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I15").WithArguments("I15.I15()").WithLocation(70, 20), // (70,20): error CS0542: 'I15': member names cannot be the same as their enclosing type // static partial I15(); Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I15").WithArguments("I15").WithLocation(70, 20), - // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I16() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5), - // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (74,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I16() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(74, 5), // (78,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) // static partial I17() => throw null; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(78, 12), + // (78,12): error CS8652: The feature 'partial events and constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static partial I17() => throw null; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "partial").WithArguments("partial events and constructors").WithLocation(78, 12), // (78,20): error CS0542: 'I17': member names cannot be the same as their enclosing type // static partial I17() => throw null; Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I17").WithArguments("I17").WithLocation(78, 20), diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 2efaa17455327..6ea09a1ce0553 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -432,9 +432,9 @@ static void Goo(this string x) { } // (9,18): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group. // bool x = s.Goo is Action; Diagnostic(ErrorCode.ERR_LambdaInIsAs, "s.Goo is Action").WithLocation(9, 18), - // (12,18): error CS0837: The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group. + // (12,20): error CS1061: 'int' does not contain a definition for 'Goo' and no accessible extension method 'Goo' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?) // bool y = i.Goo is Action; - Diagnostic(ErrorCode.ERR_LambdaInIsAs, "i.Goo is Action").WithLocation(12, 18), + Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "Goo").WithArguments("int", "Goo").WithLocation(12, 20), // (9,18): error CS0165: Use of unassigned local variable 's' // bool x = s.Goo is Action; Diagnostic(ErrorCode.ERR_UseDefViolation, "s").WithArguments("s").WithLocation(9, 18), @@ -2204,7 +2204,7 @@ private static void Main(string[] args) { } "; var compilation = CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }); compilation.VerifyDiagnostics( - // (4,29): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (4,29): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(4, 29)); } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs index d3578cb524e59..93d304d2e2f8a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MissingSpecialMember.cs @@ -126,7 +126,7 @@ public static void Extension(this string x) {} comp.MakeMemberMissing(WellKnownMember.System_Diagnostics_DebuggerHiddenAttribute__ctor); comp.VerifyEmitDiagnostics( - // (9,34): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (9,34): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? // public static void Extension(this string x) {} Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(9, 34) ); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs index 854eed416473e..924559fed5860 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockNamedTypeSymbol.cs @@ -201,6 +201,9 @@ public sealed override bool IsRefLikeType } } + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override bool IsReadOnly { get @@ -335,6 +338,7 @@ internal override AttributeUsageInfo GetAttributeUsageInfo() internal override bool IsRecordStruct => false; internal override bool HasPossibleWellKnownCloneMethod() => false; internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol ExtensionParameter => null; internal sealed override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() { diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs index 6673e386e5806..fe5ee45ce77a6 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/PartialPropertiesTests.cs @@ -1552,15 +1552,15 @@ partial class C """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,20): error CS9255: Both partial property declarations must have the same type. + // (6,20): error CS9255: Both partial member declarations must have the same type. // partial string P1 { get => ""; set { } } - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P1").WithLocation(6, 20), - // (9,26): error CS9255: Both partial property declarations must have the same type. + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "P1").WithLocation(6, 20), + // (9,26): error CS9255: Both partial member declarations must have the same type. // partial List P2 { get => []; set { } } - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P2").WithLocation(9, 26), - // (12,33): error CS9255: Both partial property declarations must have the same type. + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "P2").WithLocation(9, 26), + // (12,33): error CS9255: Both partial member declarations must have the same type. // partial IEnumerable P3 { get => []; set { } } - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P3").WithLocation(12, 33)); + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "P3").WithLocation(12, 33)); } [Fact] @@ -1585,18 +1585,18 @@ partial class C """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,20): error CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // (5,20): error CS9256: Partial member declarations 'string? C.P1' and 'string C.P1' have signature differences. // partial string P1 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(5, 20), - // (8,21): error CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(5, 20), + // (8,21): error CS9256: Partial member declarations 'string C.P2' and 'string? C.P2' have signature differences. // partial string? P2 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(8, 21), - // (11,22): error CS9256: Partial property declarations 'string?[] C.P3' and 'string[] C.P3' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(8, 21), + // (11,22): error CS9256: Partial member declarations 'string?[] C.P3' and 'string[] C.P3' have signature differences. // partial string[] P3 { get => []; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P3").WithArguments("string?[] C.P3", "string[] C.P3").WithLocation(11, 22), - // (14,23): error CS9256: Partial property declarations 'string[] C.P4' and 'string?[] C.P4' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P3").WithArguments("string?[] C.P3", "string[] C.P3").WithLocation(11, 22), + // (14,23): error CS9256: Partial member declarations 'string[] C.P4' and 'string?[] C.P4' have signature differences. // partial string?[] P4 { get => []; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P4").WithArguments("string[] C.P4", "string?[] C.P4").WithLocation(14, 23)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P4").WithArguments("string[] C.P4", "string?[] C.P4").WithLocation(14, 23)); } [Fact] @@ -1623,12 +1623,12 @@ partial class C var comp = CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Enable)); comp.VerifyEmitDiagnostics( - // (4,27): warning CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // (4,27): warning CS9256: Partial member declarations 'string? C.P1' and 'string C.P1' have signature differences. // public partial string P1 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), - // (7,28): warning CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), + // (7,28): warning CS9256: Partial member declarations 'string C.P2' and 'string? C.P2' have signature differences. // public partial string? P2 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), // (10,27): warning CS8826: Partial method declarations 'string? C.M1()' and 'string C.M1()' have signature differences. // public partial string M1() => ""; Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("string? C.M1()", "string C.M1()").WithLocation(10, 27), @@ -1638,12 +1638,12 @@ partial class C comp = CreateCompilation(source, options: TestOptions.DebugDll.WithNullableContextOptions(NullableContextOptions.Annotations)); comp.VerifyEmitDiagnostics( - // (4,27): warning CS9256: Partial property declarations 'string? C.P1' and 'string C.P1' have signature differences. + // (4,27): warning CS9256: Partial member declarations 'string? C.P1' and 'string C.P1' have signature differences. // public partial string P1 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), - // (7,28): warning CS9256: Partial property declarations 'string C.P2' and 'string? C.P2' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P1").WithArguments("string? C.P1", "string C.P1").WithLocation(4, 27), + // (7,28): warning CS9256: Partial member declarations 'string C.P2' and 'string? C.P2' have signature differences. // public partial string? P2 { get => ""; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "P2").WithArguments("string C.P2", "string? C.P2").WithLocation(7, 28), // (10,27): warning CS8826: Partial method declarations 'string? C.M1()' and 'string C.M1()' have signature differences. // public partial string M1() => ""; Diagnostic(ErrorCode.WRN_PartialMethodTypeDifference, "M1").WithArguments("string? C.M1()", "string C.M1()").WithLocation(10, 27)); @@ -1734,24 +1734,24 @@ void Usage() var verifier = CompileAndVerify(source, symbolValidator: verify, sourceSymbolValidator: verify); verifier.VerifyDiagnostics( - // (5,28): warning CS9256: Partial property declarations 'string C.this[string? x]' and 'string? C.this[string x]' have signature differences. + // (5,28): warning CS9256: Partial member declarations 'string C.this[string? x]' and 'string? C.this[string x]' have signature differences. // public partial string? this[string x] // 1 - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string C.this[string? x]", "string? C.this[string x]").WithLocation(5, 28), - // (12,27): warning CS9256: Partial property declarations 'string? C.this[string x, bool ignored]' and 'string C.this[string? x, bool ignored]' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("string C.this[string? x]", "string? C.this[string x]").WithLocation(5, 28), + // (12,27): warning CS9256: Partial member declarations 'string? C.this[string x, bool ignored]' and 'string C.this[string? x, bool ignored]' have signature differences. // public partial string this[string? x, bool ignored] // 2 - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string? C.this[string x, bool ignored]", "string C.this[string? x, bool ignored]").WithLocation(12, 27), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("string? C.this[string x, bool ignored]", "string C.this[string? x, bool ignored]").WithLocation(12, 27), // (14,16): warning CS8602: Dereference of a possibly null reference. // get => x.ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(14, 16), // (15,16): warning CS8602: Dereference of a possibly null reference. // set => x.ToString(); // 4 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(15, 16), - // (19,28): warning CS9256: Partial property declarations 'string C.this[bool ignored]' and 'string? C.this[bool ignored]' have signature differences. + // (19,28): warning CS9256: Partial member declarations 'string C.this[bool ignored]' and 'string? C.this[bool ignored]' have signature differences. // public partial string? this[bool ignored] // 5 - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string C.this[bool ignored]", "string? C.this[bool ignored]").WithLocation(19, 28), - // (25,27): warning CS9256: Partial property declarations 'string? C.this[int ignored]' and 'string C.this[int ignored]' have signature differences. + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("string C.this[bool ignored]", "string? C.this[bool ignored]").WithLocation(19, 28), + // (25,27): warning CS9256: Partial member declarations 'string? C.this[int ignored]' and 'string C.this[int ignored]' have signature differences. // public partial string this[int ignored] // 6 - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("string? C.this[int ignored]", "string C.this[int ignored]").WithLocation(25, 27), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("string? C.this[int ignored]", "string C.this[int ignored]").WithLocation(25, 27), // (27,16): warning CS8603: Possible null reference return. // get => null; // 7 Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(27, 16), @@ -1913,9 +1913,9 @@ partial class C var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (6,52): error CS9255: Both partial property declarations must have the same type. + // (6,52): error CS9255: Both partial member declarations must have the same type. // public partial ref readonly (long x, string y) Prop => throw null!; - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "Prop").WithLocation(6, 52), + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "Prop").WithLocation(6, 52), // (6,52): error CS8818: Partial member declarations must have matching ref return values. // public partial ref readonly (long x, string y) Prop => throw null!; Diagnostic(ErrorCode.ERR_PartialMemberRefReturnDifference, "Prop").WithLocation(6, 52)); @@ -2411,9 +2411,9 @@ partial class C var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (15,26): error CS9255: Both partial property declarations must have the same type. + // (15,26): error CS9255: Both partial member declarations must have the same type. // public partial MyInt P3 { get => 3; set { } } - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "P3").WithLocation(15, 26)); + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "P3").WithLocation(15, 26)); } [Fact] @@ -2779,9 +2779,9 @@ partial class C var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,24): warning CS9256: Partial property declarations 'int C.this[string s]' and 'int C.this[string? s]' have signature differences. + // (5,24): warning CS9256: Partial member declarations 'int C.this[string s]' and 'int C.this[string? s]' have signature differences. // public partial int this[string? s] { get => 1; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[string s]", "int C.this[string? s]").WithLocation(5, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[string s]", "int C.this[string? s]").WithLocation(5, 24)); } [Fact] @@ -2798,9 +2798,9 @@ partial class C var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,24): warning CS9256: Partial property declarations 'int C.this[dynamic[] s]' and 'int C.this[object[] s]' have signature differences. + // (5,24): warning CS9256: Partial member declarations 'int C.this[dynamic[] s]' and 'int C.this[object[] s]' have signature differences. // public partial int this[object[] s] { get => 1; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[dynamic[] s]", "int C.this[object[] s]").WithLocation(5, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[dynamic[] s]", "int C.this[object[] s]").WithLocation(5, 24)); } [Fact] @@ -3253,9 +3253,9 @@ partial class C var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (4,29): error CS9255: Both partial property declarations must have the same type. + // (4,29): error CS9255: Both partial member declarations must have the same type. // public partial string[] this[int x] { get => []; set { } } - Diagnostic(ErrorCode.ERR_PartialPropertyTypeDifference, "this").WithLocation(4, 29)); + Diagnostic(ErrorCode.ERR_PartialMemberTypeDifference, "this").WithLocation(4, 29)); } [Fact] @@ -4689,9 +4689,9 @@ static void Main() var verifier = CompileAndVerify(source, expectedOutput: "1", symbolValidator: verify, sourceSymbolValidator: verify); verifier.VerifyDiagnostics( - // (6,24): warning CS9256: Partial property declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. + // (6,24): warning CS9256: Partial member declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. // public partial int this[int p2] { get => p2; set { } } - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(6, 24)); + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(6, 24)); void verify(ModuleSymbol module) { @@ -4761,9 +4761,9 @@ public partial void M( // 9 // (20,18): error CS0103: The name 'p2' does not exist in the current context // [Attr(nameof(p2))] // 4 Diagnostic(ErrorCode.ERR_NameNotInContext, "p2").WithArguments("p2").WithLocation(20, 18), - // (21,24): warning CS9256: Partial property declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. + // (21,24): warning CS9256: Partial member declarations 'int C.this[int p1]' and 'int C.this[int p2]' have signature differences. // public partial int this[int p2] // 5 - Diagnostic(ErrorCode.WRN_PartialPropertySignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(21, 24), + Diagnostic(ErrorCode.WRN_PartialMemberSignatureDifference, "this").WithArguments("int C.this[int p1]", "int C.this[int p2]").WithLocation(21, 24), // (25,29): error CS0103: The name 'p1' does not exist in the current context // [param: Attr(nameof(p1))] // 6 Diagnostic(ErrorCode.ERR_NameNotInContext, "p1").WithArguments("p1").WithLocation(25, 29), @@ -5107,7 +5107,37 @@ partial struct S1 """; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp).VerifyDiagnostics().VerifyTypeIL("S1", +""" +.class private sequential ansi sealed beforefieldinit S1 + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 06 4d 79 4e 61 6d 65 00 00 + ) + .pack 0 + .size 1 + // Methods + .method public hidebysig specialname + instance int32 get_MyName ( + int32 x + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ret + } // end of method S1::get_MyName + // Properties + .property instance int32 MyName( + int32 x + ) + { + .get instance int32 S1::get_MyName(int32) + } +} // end of class S1 +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76842")] @@ -5126,7 +5156,39 @@ partial struct S1 """; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics(); + + // Note, the indexer name in metadata is "Item", expected "MyName" + CompileAndVerify(comp).VerifyDiagnostics().VerifyTypeIL("S1", +""" +.class private sequential ansi sealed beforefieldinit S1 + extends [mscorlib]System.ValueType +{ + .custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( + 01 00 04 49 74 65 6d 00 00 + ) + .pack 0 + .size 1 + // Methods + .method public hidebysig specialname + instance int32 get_Item ( + int32 x + ) cil managed + { + // Method begins at RVA 0x2067 + // Code size 2 (0x2) + .maxstack 8 + IL_0000: ldarg.1 + IL_0001: ret + } // end of method S1::get_Item + // Properties + .property instance int32 Item( + int32 x + ) + { + .get instance int32 S1::get_Item(int32) + } +} // end of class S1 +""".Replace("[mscorlib]", ExecutionConditionUtil.IsMonoOrCoreClr ? "[netstandard]" : "[mscorlib]")); } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs index 996ecaa302bdd..b27624e0c3748 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/RequiredMembersTests.cs @@ -7584,7 +7584,7 @@ public void TupleWithRequiredFields(bool setsRequiredMembers) [Theory] [CombinatorialData] - public void TupleWithRequiredFields_TupleExpressonSyntax(bool setsRequiredMembers) + public void TupleWithRequiredFields_TupleExpressionSyntax(bool setsRequiredMembers) { var comp = CreateCompilation(new[] { """ #pragma warning disable CS0219 // Unused local diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs index 36851ee503cf4..c3966f42229d7 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs @@ -2765,5 +2765,123 @@ .event [netstandard]System.Action E1 } // end of class Test1 ".Replace("[netstandard]", ExecutionConditionUtil.IsDesktop ? "[mscorlib]" : "[netstandard]")); } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/77254")] + [InlineData(LanguageVersion.CSharp1)] + [InlineData(LanguageVersion.CSharp13)] + [InlineData(LanguageVersion.Preview)] + public void Attributes_Locations(LanguageVersion langVersion) + { + var source = """ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class A : Attribute { public A(int i) { } } + + public class C + { + [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action E; + + [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public extern event Action F; + + [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action G + { + [A(21)] [method: A(22)] [param: A(23)] [return: A(24)] [event: A(25)] [field: A(26)] add { } + [A(31)] [method: A(32)] [param: A(33)] [return: A(34)] [event: A(35)] [field: A(36)] remove { } + } + } + """; + CompileAndVerify(source, + parseOptions: TestOptions.Regular.WithLanguageVersion(langVersion), + options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), + symbolValidator: validate, + sourceSymbolValidator: validate, + // PEVerify fails when extern methods lack an implementation + verify: Verification.FailsPEVerify with + { + PEVerifyMessage = """ + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Error: Method marked Abstract, Runtime, InternalCall or Imported must have zero RVA, and vice versa. + Type load failed. + """, + }) + .VerifyDiagnostics( + // (8,28): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, field, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, field, event").WithLocation(8, 28), + // (8,42): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, field, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action E; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "method, field, event").WithLocation(8, 42), + // (8,104): warning CS0067: The event 'C.E' is never used + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action E; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "E").WithArguments("C.E").WithLocation(8, 104), + // (10,28): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public extern event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "method, event").WithLocation(10, 28), + // (10,42): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public extern event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "method, event").WithLocation(10, 42), + // (10,71): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public extern event Action F; + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, event").WithLocation(10, 71), + // (12,13): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action G + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "event").WithLocation(12, 13), + // (12,28): warning CS0657: 'param' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action G + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "param").WithArguments("param", "event").WithLocation(12, 28), + // (12,42): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action G + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "event").WithLocation(12, 42), + // (12,71): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'event'. All attributes in this block will be ignored. + // [A(1)] [method: A(2)] [param: A(3)] [return: A(4)] [event: A(5)] [field: A(6)] public event Action G + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "event").WithLocation(12, 71), + // (14,65): warning CS0657: 'event' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(21)] [method: A(22)] [param: A(23)] [return: A(24)] [event: A(25)] [field: A(26)] add { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "event").WithArguments("event", "method, param, return").WithLocation(14, 65), + // (14,80): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(21)] [method: A(22)] [param: A(23)] [return: A(24)] [event: A(25)] [field: A(26)] add { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, param, return").WithLocation(14, 80), + // (15,65): warning CS0657: 'event' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(31)] [method: A(32)] [param: A(33)] [return: A(34)] [event: A(35)] [field: A(36)] remove { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "event").WithArguments("event", "method, param, return").WithLocation(15, 65), + // (15,80): warning CS0657: 'field' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'method, param, return'. All attributes in this block will be ignored. + // [A(31)] [method: A(32)] [param: A(33)] [return: A(34)] [event: A(35)] [field: A(36)] remove { } + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "field").WithArguments("field", "method, param, return").WithLocation(15, 80)); + + static void validate(ModuleSymbol module) + { + var isSource = module is SourceModuleSymbol; + ReadOnlySpan compiledGeneratedAttr = isSource ? [] : ["System.Runtime.CompilerServices.CompilerGeneratedAttribute"]; + + var e = module.GlobalNamespace.GetMember("C.E"); + AssertEx.Equal(["A(1)", "A(5)"], e.GetAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(2)"], e.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)"], e.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], e.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(2)"], e.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)"], e.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], e.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(6)"], e.AssociatedField.GetAttributes().ToStrings()); + + var f = module.GlobalNamespace.GetMember("C.F"); + AssertEx.Equal(["A(1)", "A(5)"], f.GetAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(2)"], f.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)"], f.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], f.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal([.. compiledGeneratedAttr, "A(2)"], f.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(3)"], f.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal([], f.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + + var g = module.GlobalNamespace.GetMember("C.G"); + AssertEx.Equal(["A(1)", "A(5)"], g.GetAttributes().ToStrings()); + AssertEx.Equal(["A(21)", "A(22)"], g.AddMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(23)"], g.AddMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(24)"], g.AddMethod.GetReturnTypeAttributes().ToStrings()); + AssertEx.Equal(["A(31)", "A(32)"], g.RemoveMethod!.GetAttributes().ToStrings()); + AssertEx.Equal(["A(33)"], g.RemoveMethod.Parameters.Single().GetAttributes().ToStrings()); + AssertEx.Equal(["A(34)"], g.RemoveMethod.GetReturnTypeAttributes().ToStrings()); + } + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 9f2c305a4d345..dbeff6ba1229c 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -819,34 +819,34 @@ partial sealed static I3() {} // (4,19): error CS0106: The modifier 'sealed' is not valid for this item // sealed static I1() {} Diagnostic(ErrorCode.ERR_BadMemberFlag, "I1").WithArguments("sealed").WithLocation(4, 19), - // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I2(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), - // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (9,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I2(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 5), // (9,27): error CS0106: The modifier 'sealed' is not valid for this item // partial sealed static I2(); Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(9, 27), - // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I2() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), - // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (14,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I2() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 5), // (14,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types // partial static I2() {} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(14, 20), - // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I3(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), - // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (19,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static I3(); Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 5), - // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I3() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), - // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // (24,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial sealed static I3() {} Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(24, 5), // (24,27): error CS0106: The modifier 'sealed' is not valid for this item @@ -901,48 +901,30 @@ sealed static partial I3() {} targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (4,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // (4,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // sealed static partial I2(); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(4, 19), - // (4,27): error CS0501: 'I2.I2()' must declare a body because it is not marked abstract, extern, or partial + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(4, 19), + // (4,27): error CS0106: The modifier 'sealed' is not valid for this item // sealed static partial I2(); - Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I2").WithArguments("I2.I2()").WithLocation(4, 27), - // (4,27): error CS0542: 'I2': member names cannot be the same as their enclosing type - // sealed static partial I2(); - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I2").WithArguments("I2").WithLocation(4, 27), - // (9,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) - // static partial I2() {} - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(9, 12), - // (9,20): error CS0542: 'I2': member names cannot be the same as their enclosing type + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I2").WithArguments("sealed").WithLocation(4, 27), + // (9,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial I2() {} - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I2").WithArguments("I2").WithLocation(9, 20), + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(9, 12), // (9,20): error CS0111: Type 'I2' already defines a member called 'I2' with the same parameter types // static partial I2() {} Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I2").WithArguments("I2", "I2").WithLocation(9, 20), - // (9,20): error CS0161: 'I2.I2()': not all code paths return a value - // static partial I2() {} - Diagnostic(ErrorCode.ERR_ReturnExpected, "I2").WithArguments("I2.I2()").WithLocation(9, 20), - // (14,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // (14,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial I3(); - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(14, 12), - // (14,20): error CS0501: 'I3.I3()' must declare a body because it is not marked abstract, extern, or partial - // static partial I3(); - Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "I3").WithArguments("I3.I3()").WithLocation(14, 20), - // (14,20): error CS0542: 'I3': member names cannot be the same as their enclosing type - // static partial I3(); - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I3").WithArguments("I3").WithLocation(14, 20), - // (19,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(14, 12), + // (19,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // sealed static partial I3() {} - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(19, 19), - // (19,27): error CS0542: 'I3': member names cannot be the same as their enclosing type + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(19, 19), + // (19,27): error CS0106: The modifier 'sealed' is not valid for this item // sealed static partial I3() {} - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "I3").WithArguments("I3").WithLocation(19, 27), + Diagnostic(ErrorCode.ERR_BadMemberFlag, "I3").WithArguments("sealed").WithLocation(19, 27), // (19,27): error CS0111: Type 'I3' already defines a member called 'I3' with the same parameter types // sealed static partial I3() {} - Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I3").WithArguments("I3", "I3").WithLocation(19, 27), - // (19,27): error CS0161: 'I3.I3()': not all code paths return a value - // sealed static partial I3() {} - Diagnostic(ErrorCode.ERR_ReturnExpected, "I3").WithArguments("I3.I3()").WithLocation(19, 27) + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "I3").WithArguments("I3", "I3").WithLocation(19, 27) ); } @@ -6476,7 +6458,7 @@ class C6 : C5 else { compilation2.VerifyEmitDiagnostics( - // (43,7): error CS8920: The interface 'I1' cannot be used as type argumen. Member 'I1.M01()' does not have a most specific implementation in the interface. + // (43,7): error CS8920: The interface 'I1' cannot be used as type argument. Static member 'I1.M01()' does not have a most specific implementation in the interface. // class C6 : C5 Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "C6").WithArguments("I1", "I1.M01()").WithLocation(43, 7) ); @@ -6494,7 +6476,7 @@ class C6 : C5 else { compilation2.VerifyEmitDiagnostics( - // (43,7): error CS8920: The interface 'I1' cannot be used as type argumen. Member 'I1.M01()' does not have a most specific implementation in the interface. + // (43,7): error CS8920: The interface 'I1' cannot be used as type argument. Static member 'I1.M01()' does not have a most specific implementation in the interface. // class C6 : C5 Diagnostic(ErrorCode.ERR_GenericConstraintNotSatisfiedInterfaceWithStaticAbstractMembers, "C6").WithArguments("I1", "I1.M01()").WithLocation(43, 7) ); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs index ba8f6bcd8ae5a..35eeaa428bee8 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolErrorTests.cs @@ -14953,9 +14953,9 @@ public static void M4(this object o) { } }"; var compilation = CreateEmptyCompilation(source, new[] { Net40.References.mscorlib }); compilation.VerifyDiagnostics( - // (3,27): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (3,27): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(3, 27), - // (4,27): error CS1110: Cannot define a new extension method because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? + // (4,27): error CS1110: Cannot define a new extension because the compiler required type 'System.Runtime.CompilerServices.ExtensionAttribute' cannot be found. Are you missing a reference to System.Core.dll? Diagnostic(ErrorCode.ERR_ExtensionAttrNotFound, "this").WithArguments("System.Runtime.CompilerServices.ExtensionAttribute").WithLocation(4, 27), // (4,42): error CS1100: Method 'M2' has a parameter modifier 'this' which is not on the first parameter Diagnostic(ErrorCode.ERR_BadThisParam, "this").WithArguments("M2").WithLocation(4, 42), @@ -22136,30 +22136,27 @@ partial public PartialPublicCtor() { } } """ }).VerifyDiagnostics( - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 2.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public PartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - // partial public PartialPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) - // partial PartialCtor() { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 5), - // (3,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) - // public partial PublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 12), - // (3,13): error CS0542: 'PartialCtor': member names cannot be the same as their enclosing type + // 0.cs(3,13): error CS0751: A partial member must be declared within a partial type // partial PartialCtor() { } - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "PartialCtor").WithArguments("PartialCtor").WithLocation(3, 13), - // (3,13): error CS0161: 'PartialCtor.PartialCtor()': not all code paths return a value + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PartialCtor").WithLocation(3, 13), + // 0.cs(3,13): error CS9276: Partial member 'PartialCtor.PartialCtor()' must have a definition part. // partial PartialCtor() { } - Diagnostic(ErrorCode.ERR_ReturnExpected, "PartialCtor").WithArguments("PartialCtor.PartialCtor()").WithLocation(3, 13), - // (3,20): error CS0542: 'PublicPartialCtor': member names cannot be the same as their enclosing type + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PartialCtor").WithArguments("PartialCtor.PartialCtor()").WithLocation(3, 13), + // 2.cs(3,20): error CS0751: A partial member must be declared within a partial type + // partial public PartialPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PartialPublicCtor").WithLocation(3, 20), + // 1.cs(3,20): error CS0751: A partial member must be declared within a partial type // public partial PublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "PublicPartialCtor").WithArguments("PublicPartialCtor").WithLocation(3, 20), - // (3,20): error CS0161: 'PublicPartialCtor.PublicPartialCtor()': not all code paths return a value + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "PublicPartialCtor").WithLocation(3, 20), + // 1.cs(3,20): error CS9276: Partial member 'PublicPartialCtor.PublicPartialCtor()' must have a definition part. // public partial PublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_ReturnExpected, "PublicPartialCtor").WithArguments("PublicPartialCtor.PublicPartialCtor()").WithLocation(3, 20)); + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PublicPartialCtor").WithArguments("PublicPartialCtor.PublicPartialCtor()").WithLocation(3, 20), + // 2.cs(3,20): error CS9276: Partial member 'PartialPublicCtor.PartialPublicCtor()' must have a definition part. + // partial public PartialPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMemberMissingDefinition, "PartialPublicCtor").WithArguments("PartialPublicCtor.PartialPublicCtor()").WithLocation(3, 20)); } [Fact] @@ -22216,75 +22213,63 @@ partial public static PartialPublicStaticCtor() { } } """, }).VerifyDiagnostics( - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static public PartialStaticPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // 6.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial static public PartialStaticPublicCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), + // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static PartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 0.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial static PartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public static PartialPublicStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 7.cs(3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // partial public static PartialPublicStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - // partial static public PartialStaticPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,5): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. - // partial static public PartialStaticPublicCtor() { } - Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 5), - // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // public partial static PublicPartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 3.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // public partial static PublicPartialStaticCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 1.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // static partial StaticPartialCtor() { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), + // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial public StaticPartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // (3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', or a method return type. + // 5.cs(3,12): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static partial public StaticPartialPublicCtor() { } Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 12), - // (3,12): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) - // static partial StaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 12), - // (3,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + // 2.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // public static partial PublicStaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 19), - // (3,19): error CS0246: The type or namespace name 'partial' could not be found (are you missing a using directive or an assembly reference?) + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19), + // 4.cs(3,19): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. // static public partial StaticPublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "partial").WithArguments("partial").WithLocation(3, 19), - // (3,20): error CS0542: 'StaticPartialCtor': member names cannot be the same as their enclosing type - // static partial StaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "StaticPartialCtor").WithArguments("StaticPartialCtor").WithLocation(3, 20), - // (3,20): error CS0161: 'StaticPartialCtor.StaticPartialCtor()': not all code paths return a value - // static partial StaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_ReturnExpected, "StaticPartialCtor").WithArguments("StaticPartialCtor.StaticPartialCtor()").WithLocation(3, 20), - // (3,27): error CS0515: 'PartialPublicStaticCtor.PartialPublicStaticCtor()': access modifiers are not allowed on static constructors - // partial public static PartialPublicStaticCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialPublicStaticCtor").WithArguments("PartialPublicStaticCtor.PartialPublicStaticCtor()").WithLocation(3, 27), - // (3,27): error CS0515: 'PublicPartialStaticCtor.PublicPartialStaticCtor()': access modifiers are not allowed on static constructors + Diagnostic(ErrorCode.ERR_PartialMisplaced, "partial").WithLocation(3, 19), + // 3.cs(3,27): error CS0515: 'PublicPartialStaticCtor.PublicPartialStaticCtor()': access modifiers are not allowed on static constructors // public partial static PublicPartialStaticCtor() { } Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicPartialStaticCtor").WithArguments("PublicPartialStaticCtor.PublicPartialStaticCtor()").WithLocation(3, 27), - // (3,27): error CS0515: 'StaticPartialPublicCtor.StaticPartialPublicCtor()': access modifiers are not allowed on static constructors - // static partial public StaticPartialPublicCtor() { } - Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPartialPublicCtor").WithArguments("StaticPartialPublicCtor.StaticPartialPublicCtor()").WithLocation(3, 27), - // (3,27): error CS0515: 'PartialStaticPublicCtor.PartialStaticPublicCtor()': access modifiers are not allowed on static constructors + // 6.cs(3,27): error CS0515: 'PartialStaticPublicCtor.PartialStaticPublicCtor()': access modifiers are not allowed on static constructors // partial static public PartialStaticPublicCtor() { } Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialStaticPublicCtor").WithArguments("PartialStaticPublicCtor.PartialStaticPublicCtor()").WithLocation(3, 27), - // (3,27): error CS0542: 'StaticPublicPartialCtor': member names cannot be the same as their enclosing type - // static public partial StaticPublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "StaticPublicPartialCtor").WithArguments("StaticPublicPartialCtor").WithLocation(3, 27), - // (3,27): error CS0542: 'PublicStaticPartialCtor': member names cannot be the same as their enclosing type + // 2.cs(3,27): error CS0515: 'PublicStaticPartialCtor.PublicStaticPartialCtor()': access modifiers are not allowed on static constructors // public static partial PublicStaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "PublicStaticPartialCtor").WithArguments("PublicStaticPartialCtor").WithLocation(3, 27), - // (3,27): error CS0161: 'StaticPublicPartialCtor.StaticPublicPartialCtor()': not all code paths return a value + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PublicStaticPartialCtor").WithArguments("PublicStaticPartialCtor.PublicStaticPartialCtor()").WithLocation(3, 27), + // 4.cs(3,27): error CS0515: 'StaticPublicPartialCtor.StaticPublicPartialCtor()': access modifiers are not allowed on static constructors // static public partial StaticPublicPartialCtor() { } - Diagnostic(ErrorCode.ERR_ReturnExpected, "StaticPublicPartialCtor").WithArguments("StaticPublicPartialCtor.StaticPublicPartialCtor()").WithLocation(3, 27), - // (3,27): error CS0161: 'PublicStaticPartialCtor.PublicStaticPartialCtor()': not all code paths return a value - // public static partial PublicStaticPartialCtor() { } - Diagnostic(ErrorCode.ERR_ReturnExpected, "PublicStaticPartialCtor").WithArguments("PublicStaticPartialCtor.PublicStaticPartialCtor()").WithLocation(3, 27)); + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPublicPartialCtor").WithArguments("StaticPublicPartialCtor.StaticPublicPartialCtor()").WithLocation(3, 27), + // 7.cs(3,27): error CS0515: 'PartialPublicStaticCtor.PartialPublicStaticCtor()': access modifiers are not allowed on static constructors + // partial public static PartialPublicStaticCtor() { } + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "PartialPublicStaticCtor").WithArguments("PartialPublicStaticCtor.PartialPublicStaticCtor()").WithLocation(3, 27), + // 5.cs(3,27): error CS0515: 'StaticPartialPublicCtor.StaticPartialPublicCtor()': access modifiers are not allowed on static constructors + // static partial public StaticPartialPublicCtor() { } + Diagnostic(ErrorCode.ERR_StaticConstructorWithAccessModifiers, "StaticPartialPublicCtor").WithArguments("StaticPartialPublicCtor.StaticPartialPublicCtor()").WithLocation(3, 27)); } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/UnsignedRightShiftTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/UnsignedRightShiftTests.cs index e76f6deb1b77f..b961e7c8c0b91 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/UnsignedRightShiftTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/UnsignedRightShiftTests.cs @@ -2325,7 +2325,7 @@ static void Main() } [Fact] - public void UserDefined_CompountAssignment_01() + public void UserDefined_CompoundAssignment_01() { var source1 = @" public class C1 @@ -2545,7 +2545,7 @@ static void Main() } [Fact] - public void UserDefined_Lifted_CompountAssignment_01() + public void UserDefined_Lifted_CompoundAssignment_01() { var source1 = @" public struct C1 @@ -3457,7 +3457,7 @@ class C } [Fact] - public void UserDefined_CompountAssignment_LangVersion_01() + public void UserDefined_CompoundAssignment_LangVersion_01() { var source0 = @" public class C1 @@ -3561,7 +3561,7 @@ class C } [Fact] - public void UserDefined_Lifted_CompountAssignment_LangVersion_01() + public void UserDefined_Lifted_CompoundAssignment_LangVersion_01() { var source0 = @" public struct C1 diff --git a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs index 0f68ba4c2475f..a24253bd6119e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs +++ b/src/Compilers/CSharp/Test/Syntax/Diagnostics/DiagnosticTest.cs @@ -324,7 +324,7 @@ public void WarningLevel_2() case ErrorCode.WRN_Experimental: case ErrorCode.WRN_ExperimentalWithMessage: case ErrorCode.WRN_ConvertingLock: - case ErrorCode.WRN_PartialPropertySignatureDifference: + case ErrorCode.WRN_PartialMemberSignatureDifference: case ErrorCode.WRN_UnscopedRefAttributeOldRules: Assert.Equal(1, ErrorFacts.GetWarningLevel(errorCode)); break; diff --git a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs index 88f2342f45eec..10a3e4a9a239e 100644 --- a/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs +++ b/src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs @@ -520,6 +520,9 @@ private static Syntax.InternalSyntax.DelegateDeclarationSyntax GenerateDelegateD private static Syntax.InternalSyntax.EnumMemberDeclarationSyntax GenerateEnumMemberDeclaration() => InternalSyntaxFactory.EnumMemberDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Identifier("Identifier"), null); + private static Syntax.InternalSyntax.ExtensionDeclarationSyntax GenerateExtensionDeclaration() + => InternalSyntaxFactory.ExtensionDeclaration(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.ExtensionKeyword), null, null, new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), null, new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), null, null); + private static Syntax.InternalSyntax.BaseListSyntax GenerateBaseList() => InternalSyntaxFactory.BaseList(InternalSyntaxFactory.Token(SyntaxKind.ColonToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList()); @@ -602,7 +605,7 @@ private static Syntax.InternalSyntax.BracketedParameterListSyntax GenerateBracke => InternalSyntaxFactory.BracketedParameterList(InternalSyntaxFactory.Token(SyntaxKind.OpenBracketToken), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SeparatedSyntaxList(), InternalSyntaxFactory.Token(SyntaxKind.CloseBracketToken)); private static Syntax.InternalSyntax.ParameterSyntax GenerateParameter() - => InternalSyntaxFactory.Parameter(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), null, InternalSyntaxFactory.Identifier("Identifier"), null); + => InternalSyntaxFactory.Parameter(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), null, null, null); private static Syntax.InternalSyntax.FunctionPointerParameterSyntax GenerateFunctionPointerParameter() => InternalSyntaxFactory.FunctionPointerParameter(new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), new Microsoft.CodeAnalysis.Syntax.InternalSyntax.SyntaxList(), GenerateIdentifierName()); @@ -739,6 +742,9 @@ private static Syntax.InternalSyntax.LoadDirectiveTriviaSyntax GenerateLoadDirec private static Syntax.InternalSyntax.ShebangDirectiveTriviaSyntax GenerateShebangDirectiveTrivia() => InternalSyntaxFactory.ShebangDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.ExclamationToken), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static Syntax.InternalSyntax.IgnoredDirectiveTriviaSyntax GenerateIgnoredDirectiveTrivia() + => InternalSyntaxFactory.IgnoredDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.ColonToken), InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static Syntax.InternalSyntax.NullableDirectiveTriviaSyntax GenerateNullableDirectiveTrivia() => InternalSyntaxFactory.NullableDirectiveTrivia(InternalSyntaxFactory.Token(SyntaxKind.HashToken), InternalSyntaxFactory.Token(SyntaxKind.NullableKeyword), InternalSyntaxFactory.Token(SyntaxKind.EnableKeyword), null, InternalSyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); #endregion Green Generators @@ -2891,6 +2897,25 @@ public void TestEnumMemberDeclarationFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestExtensionDeclarationFactoryAndProperties() + { + var node = GenerateExtensionDeclaration(); + + Assert.Equal(default, node.AttributeLists); + Assert.Equal(default, node.Modifiers); + Assert.Equal(SyntaxKind.ExtensionKeyword, node.Keyword.Kind); + Assert.Null(node.TypeParameterList); + Assert.Null(node.ParameterList); + Assert.Equal(default, node.ConstraintClauses); + Assert.Null(node.OpenBraceToken); + Assert.Equal(default, node.Members); + Assert.Null(node.CloseBraceToken); + Assert.Null(node.SemicolonToken); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestBaseListFactoryAndProperties() { @@ -3268,7 +3293,7 @@ public void TestParameterFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.Null(node.Type); - Assert.Equal(SyntaxKind.IdentifierToken, node.Identifier.Kind); + Assert.Null(node.Identifier); Assert.Null(node.Default); AttachAndCheckDiagnostics(node); @@ -3860,6 +3885,19 @@ public void TestShebangDirectiveTriviaFactoryAndProperties() AttachAndCheckDiagnostics(node); } + [Fact] + public void TestIgnoredDirectiveTriviaFactoryAndProperties() + { + var node = GenerateIgnoredDirectiveTrivia(); + + Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind); + Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind); + Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind); + Assert.Equal(new bool(), node.IsActive); + + AttachAndCheckDiagnostics(node); + } + [Fact] public void TestNullableDirectiveTriviaFactoryAndProperties() { @@ -8297,6 +8335,32 @@ public void TestEnumMemberDeclarationIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExtensionDeclarationTokenDeleteRewriter() + { + var oldNode = GenerateExtensionDeclaration(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExtensionDeclarationIdentityRewriter() + { + var oldNode = GenerateExtensionDeclaration(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestBaseListTokenDeleteRewriter() { @@ -10195,6 +10259,32 @@ public void TestShebangDirectiveTriviaIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestIgnoredDirectiveTriviaTokenDeleteRewriter() + { + var oldNode = GenerateIgnoredDirectiveTrivia(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestIgnoredDirectiveTriviaIdentityRewriter() + { + var oldNode = GenerateIgnoredDirectiveTrivia(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestNullableDirectiveTriviaTokenDeleteRewriter() { @@ -10736,6 +10826,9 @@ private static DelegateDeclarationSyntax GenerateDelegateDeclaration() private static EnumMemberDeclarationSyntax GenerateEnumMemberDeclaration() => SyntaxFactory.EnumMemberDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Identifier("Identifier"), default(EqualsValueClauseSyntax)); + private static ExtensionDeclarationSyntax GenerateExtensionDeclaration() + => SyntaxFactory.ExtensionDeclaration(new SyntaxList(), new SyntaxTokenList(), SyntaxFactory.Token(SyntaxKind.ExtensionKeyword), default(TypeParameterListSyntax), default(ParameterListSyntax), new SyntaxList(), default(SyntaxToken), new SyntaxList(), default(SyntaxToken), default(SyntaxToken)); + private static BaseListSyntax GenerateBaseList() => SyntaxFactory.BaseList(SyntaxFactory.Token(SyntaxKind.ColonToken), new SeparatedSyntaxList()); @@ -10818,7 +10911,7 @@ private static BracketedParameterListSyntax GenerateBracketedParameterList() => SyntaxFactory.BracketedParameterList(SyntaxFactory.Token(SyntaxKind.OpenBracketToken), new SeparatedSyntaxList(), SyntaxFactory.Token(SyntaxKind.CloseBracketToken)); private static ParameterSyntax GenerateParameter() - => SyntaxFactory.Parameter(new SyntaxList(), new SyntaxTokenList(), default(TypeSyntax), SyntaxFactory.Identifier("Identifier"), default(EqualsValueClauseSyntax)); + => SyntaxFactory.Parameter(new SyntaxList(), new SyntaxTokenList(), default(TypeSyntax), default(SyntaxToken), default(EqualsValueClauseSyntax)); private static FunctionPointerParameterSyntax GenerateFunctionPointerParameter() => SyntaxFactory.FunctionPointerParameter(new SyntaxList(), new SyntaxTokenList(), GenerateIdentifierName()); @@ -10955,6 +11048,9 @@ private static LoadDirectiveTriviaSyntax GenerateLoadDirectiveTrivia() private static ShebangDirectiveTriviaSyntax GenerateShebangDirectiveTrivia() => SyntaxFactory.ShebangDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ExclamationToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static IgnoredDirectiveTriviaSyntax GenerateIgnoredDirectiveTrivia() + => SyntaxFactory.IgnoredDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.ColonToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); + private static NullableDirectiveTriviaSyntax GenerateNullableDirectiveTrivia() => SyntaxFactory.NullableDirectiveTrivia(SyntaxFactory.Token(SyntaxKind.HashToken), SyntaxFactory.Token(SyntaxKind.NullableKeyword), SyntaxFactory.Token(SyntaxKind.EnableKeyword), default(SyntaxToken), SyntaxFactory.Token(SyntaxKind.EndOfDirectiveToken), new bool()); #endregion Red Generators @@ -13107,6 +13203,25 @@ public void TestEnumMemberDeclarationFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestExtensionDeclarationFactoryAndProperties() + { + var node = GenerateExtensionDeclaration(); + + Assert.Equal(default, node.AttributeLists); + Assert.Equal(default, node.Modifiers); + Assert.Equal(SyntaxKind.ExtensionKeyword, node.Keyword.Kind()); + Assert.Null(node.TypeParameterList); + Assert.Null(node.ParameterList); + Assert.Equal(default, node.ConstraintClauses); + Assert.Equal(SyntaxKind.None, node.OpenBraceToken.Kind()); + Assert.Equal(default, node.Members); + Assert.Equal(SyntaxKind.None, node.CloseBraceToken.Kind()); + Assert.Equal(SyntaxKind.None, node.SemicolonToken.Kind()); + var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithKeyword(node.Keyword).WithTypeParameterList(node.TypeParameterList).WithParameterList(node.ParameterList).WithConstraintClauses(node.ConstraintClauses).WithOpenBraceToken(node.OpenBraceToken).WithMembers(node.Members).WithCloseBraceToken(node.CloseBraceToken).WithSemicolonToken(node.SemicolonToken); + Assert.Equal(node, newNode); + } + [Fact] public void TestBaseListFactoryAndProperties() { @@ -13484,7 +13599,7 @@ public void TestParameterFactoryAndProperties() Assert.Equal(default, node.AttributeLists); Assert.Equal(default, node.Modifiers); Assert.Null(node.Type); - Assert.Equal(SyntaxKind.IdentifierToken, node.Identifier.Kind()); + Assert.Equal(SyntaxKind.None, node.Identifier.Kind()); Assert.Null(node.Default); var newNode = node.WithAttributeLists(node.AttributeLists).WithModifiers(node.Modifiers).WithType(node.Type).WithIdentifier(node.Identifier).WithDefault(node.Default); Assert.Equal(node, newNode); @@ -14076,6 +14191,19 @@ public void TestShebangDirectiveTriviaFactoryAndProperties() Assert.Equal(node, newNode); } + [Fact] + public void TestIgnoredDirectiveTriviaFactoryAndProperties() + { + var node = GenerateIgnoredDirectiveTrivia(); + + Assert.Equal(SyntaxKind.HashToken, node.HashToken.Kind()); + Assert.Equal(SyntaxKind.ColonToken, node.ColonToken.Kind()); + Assert.Equal(SyntaxKind.EndOfDirectiveToken, node.EndOfDirectiveToken.Kind()); + Assert.Equal(new bool(), node.IsActive); + var newNode = node.WithHashToken(node.HashToken).WithColonToken(node.ColonToken).WithEndOfDirectiveToken(node.EndOfDirectiveToken).WithIsActive(node.IsActive); + Assert.Equal(node, newNode); + } + [Fact] public void TestNullableDirectiveTriviaFactoryAndProperties() { @@ -18513,6 +18641,32 @@ public void TestEnumMemberDeclarationIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestExtensionDeclarationTokenDeleteRewriter() + { + var oldNode = GenerateExtensionDeclaration(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestExtensionDeclarationIdentityRewriter() + { + var oldNode = GenerateExtensionDeclaration(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestBaseListTokenDeleteRewriter() { @@ -20411,6 +20565,32 @@ public void TestShebangDirectiveTriviaIdentityRewriter() Assert.Same(oldNode, newNode); } + [Fact] + public void TestIgnoredDirectiveTriviaTokenDeleteRewriter() + { + var oldNode = GenerateIgnoredDirectiveTrivia(); + var rewriter = new TokenDeleteRewriter(); + var newNode = rewriter.Visit(oldNode); + + if(!oldNode.IsMissing) + { + Assert.NotEqual(oldNode, newNode); + } + + Assert.NotNull(newNode); + Assert.True(newNode.IsMissing, "No tokens => missing"); + } + + [Fact] + public void TestIgnoredDirectiveTriviaIdentityRewriter() + { + var oldNode = GenerateIgnoredDirectiveTrivia(); + var rewriter = new IdentityRewriter(); + var newNode = rewriter.Visit(oldNode); + + Assert.Same(oldNode, newNode); + } + [Fact] public void TestNullableDirectiveTriviaTokenDeleteRewriter() { diff --git a/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs index bdc11fed3b1ff..fbcd7a9a1be36 100644 --- a/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/IncrementalParsing/IncrementalParsingTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Xunit; @@ -844,6 +845,437 @@ public void M2() WalkTreeAndVerify(withCloseBraceDeletedTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot()); } + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromExtensionToClass() + { + var text = @" +class C +{ + extension(object x) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("extension", "class D"); + oldTree.GetDiagnostics().Verify(); + newTree.GetDiagnostics().Verify(); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassKeyword, + SyntaxKind.IdentifierToken); + + UsingTree(newTree); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromExtensionToClass_NoParameterIdentifier() + { + var text = @" +class C +{ + extension(object) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("extension", "class D"); + oldTree.GetDiagnostics().Verify(); + newTree.GetDiagnostics().Verify( + // (4,19): error CS1001: Identifier expected + // class D(object) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 19)); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassKeyword, + SyntaxKind.IdentifierToken, + SyntaxKind.ParameterList, + SyntaxKind.Parameter, + SyntaxKind.IdentifierToken); + + UsingTree(newTree, + // (4,19): error CS1001: Identifier expected + // class D(object) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 19)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromExtensionToClass_WithName() + { + var text = @" +class C +{ + extension E(object x) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("extension", "class"); + oldTree.GetDiagnostics().Verify( + // (4,15): error CS9281: Extension declarations may not have a name. + // extension E(object x) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "E").WithLocation(4, 15)); + newTree.GetDiagnostics().Verify(); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassDeclaration, + SyntaxKind.ClassKeyword, + SyntaxKind.IdentifierToken); + + UsingTree(newTree); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromClassToExtension() + { + var text = @" +class C +{ + class D(object x) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("class D", "extension"); + oldTree.GetDiagnostics().Verify(); + newTree.GetDiagnostics().Verify(); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ExtensionDeclaration, + SyntaxKind.ExtensionKeyword); + + UsingTree(newTree); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromClassToExtension_NoParameterIdentifier() + { + var text = @" +class C +{ + class D(object) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("class D", "extension"); + oldTree.GetDiagnostics().Verify( + // (4,19): error CS1001: Identifier expected + // class D(object) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(4, 19)); + newTree.GetDiagnostics().Verify(); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ExtensionDeclaration, + SyntaxKind.ExtensionKeyword, + SyntaxKind.ParameterList, + SyntaxKind.Parameter); + + UsingTree(newTree); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateFromClassToExtension_WithName() + { + var text = @" +class C +{ + struct D(object x) { } +} +"; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("struct", "extension"); + oldTree.GetDiagnostics().Verify(); + newTree.GetDiagnostics().Verify( + // (4,15): error CS9281: Extension declarations may not have a name. + // extension D(object x) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "D").WithLocation(4, 15)); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ExtensionDeclaration, + SyntaxKind.ExtensionKeyword); + + UsingTree(newTree, + // (4,15): error CS9281: Extension declarations may not have a name. + // extension D(object x) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "D").WithLocation(4, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, CompilerTrait(CompilerFeature.Extensions)] + public void UpdateExtension_ChangeParameterList() + { + var text = """ +class C +{ + extension(object, Type z1) { } +} +"""; + var oldTree = this.Parse(text, LanguageVersionFacts.CSharpNext); + var newTree = oldTree.WithReplaceFirst("z1", "z2"); + oldTree.GetDiagnostics().Verify(); + newTree.GetDiagnostics().Verify(); + + var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree); + TestDiffsInOrder(diffs, + SyntaxKind.CompilationUnit, + SyntaxKind.ClassDeclaration, + SyntaxKind.ExtensionDeclaration, + SyntaxKind.ExtensionKeyword, + SyntaxKind.ParameterList, + SyntaxKind.Parameter, + SyntaxKind.IdentifierToken); + + UsingTree(newTree); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "z2"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + #region "Regression" #if false diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs index 563f7e270746e..b99cf257d1a95 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/AsyncParsingTests.cs @@ -1567,9 +1567,6 @@ class C { async partial event ", - // (4,19): error CS1519: Invalid token 'event' in class, record, struct, or interface member declaration - // async partial event - Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "event").WithArguments("event").WithLocation(4, 19), // (4,24): error CS1031: Type expected // async partial event Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(4, 24), @@ -1589,16 +1586,10 @@ async partial event N(SyntaxKind.ClassKeyword); N(SyntaxKind.IdentifierToken, "C"); N(SyntaxKind.OpenBraceToken); - N(SyntaxKind.IncompleteMember); - { - N(SyntaxKind.AsyncKeyword); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "partial"); - } - } N(SyntaxKind.EventDeclaration); { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.PartialKeyword); N(SyntaxKind.EventKeyword); M(SyntaxKind.IdentifierName); { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs index 57d82e0f09cfd..95acc81fd07c9 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/CSharpParseOptionsTests.cs @@ -63,6 +63,7 @@ public void TestFieldsForEqualsAndGetHashCode() ReflectionAssert.AssertPublicAndInternalFieldsAndProperties( typeof(CSharpParseOptions), "Features", + "FileBasedProgram", "Language", "LanguageVersion", "InterceptorsNamespaces", diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs index 903cb67544db5..fbb979b559f63 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeconstructionTests.cs @@ -3304,15 +3304,15 @@ public void BadTypeForDeconstruct_07() UsingStatement(@"var?.var (x, y) = e;"); N(SyntaxKind.ExpressionStatement); { - N(SyntaxKind.SimpleAssignmentExpression); + N(SyntaxKind.ConditionalAccessExpression); { - N(SyntaxKind.ConditionalAccessExpression); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "var"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "var"); - } - N(SyntaxKind.QuestionToken); N(SyntaxKind.InvocationExpression); { N(SyntaxKind.MemberBindingExpression); @@ -3344,11 +3344,11 @@ public void BadTypeForDeconstruct_07() N(SyntaxKind.CloseParenToken); } } - } - N(SyntaxKind.EqualsToken); - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "e"); + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } } } N(SyntaxKind.SemicolonToken); diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index 268d4e0ce5d5d..f31340b3006b5 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -5965,7 +5965,7 @@ public void RangeExpression_MethodInvocation_TwoOperands() } [Fact] - public void RangeExpression_ConditionalAccessExpression() + public void RangeExpression_ConditionalAccessExpression_01() { UsingExpression("c?..b", // (1,6): error CS1003: Syntax error, ':' expected @@ -5999,6 +5999,50 @@ public void RangeExpression_ConditionalAccessExpression() EOF(); } + [Fact] + public void RangeExpression_ConditionalAccessExpression_02() + { + // It would also be reasonable to parse this as '(c?.b)..(a)', but a Range doesn't accept nullable operands, so the below parse is acceptable. + UsingExpression("c?.b..a", + // (1,6): error CS1001: Identifier expected + // c?.b..a + Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(1, 6)); + + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + } + EOF(); + } + [Fact] public void BaseExpression_01() { diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs new file mode 100644 index 0000000000000..dbd2ceed5f107 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExtensionsParsingTests.cs @@ -0,0 +1,4936 @@ +// 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.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +[CompilerTrait(CompilerFeature.Extensions)] +public class ExtensionsParsingTests : ParsingTests +{ + public ExtensionsParsingTests(ITestOutputHelper output) : base(output) { } + + [Fact] + public void LangVer13() + { + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider giving a LangVer error to trigger UpgradeProject + UsingTree(""" +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 + // extension(object o) where T : struct { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 45)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + } + } + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "o"); + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "where"); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + } + N(SyntaxKind.StructDeclaration); + { + N(SyntaxKind.StructKeyword); + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void LangVer14(bool useCSharp14) + { + UsingTree(""" +class C +{ + extension(object o) where T : struct { } +} +""", + useCSharp14 ? TestOptions.RegularNext : TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MultipleConstraints() + { + UsingTree(""" +class C +{ + extension(object o) where T1 : struct where T2 : class { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T2"); + } + 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.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T1"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T2"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.ClassConstraint); + { + N(SyntaxKind.ClassKeyword); + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void MultipleConstraints_Incomplete() + { + UsingTree(""" +class C +{ + extension(object o) where T1 where T2 : class { } +} +""", + TestOptions.RegularPreview, + // (3,42): error CS1003: Syntax error, ':' expected + // extension(object o) where T1 where T2 : class { } + Diagnostic(ErrorCode.ERR_SyntaxError, "where").WithArguments(":").WithLocation(3, 42), + // (3,42): error CS1031: Type expected + // extension(object o) where T1 where T2 : class { } + Diagnostic(ErrorCode.ERR_TypeExpected, "where").WithLocation(3, 42)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T2"); + } + 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.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T1"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.TypeConstraint); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T2"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.ClassConstraint); + { + N(SyntaxKind.ClassKeyword); + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithName() + { + UsingTree(""" +class C +{ + extension Name(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,15): error CS9500: Extension declarations may not have a name. + // extension Name(Type) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "Name").WithLocation(3, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithName_02() + { + UsingTree(""" +class C +{ + extension Name(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,15): error CS9500: Extension declarations may not have a name. + // extension Name(Type) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "Name").WithLocation(3, 15)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithDefaultParameterValue() + { + var src = """ +static class C +{ + extension(object x = null) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(object x = null) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "object x = null").WithLocation(3, 15)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithDefaultParameterValue_02() + { + var src = """ +static class C +{ + extension(object = null) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,15): error CS9284: The receiver parameter of an extension cannot have a default value + // extension(object = null) { } + Diagnostic(ErrorCode.ERR_ExtensionParameterDisallowsDefaultValue, "object = null").WithLocation(3, 15)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithDefaultParameterValue_03() + { + var src = """ +class C +{ + extension(Type =) { } +} +"""; + UsingTree(src, TestOptions.RegularPreview, + // (3,21): error CS1525: Invalid expression term ')' + // extension(Type =) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(3, 21)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithBaseList() + { + var src = """ +class C +{ + extension Name(Type) : Type2() { } +} +"""; + UsingTree(src, TestOptions.RegularPreview, + // (3,15): error CS9500: Extension declarations may not have a name. + // extension Name(Type) : Type2() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsName, "Name").WithLocation(3, 15), + // (3,26): error CS1514: { expected + // extension Name(Type) : Type2() { } + Diagnostic(ErrorCode.ERR_LbraceExpected, ":").WithLocation(3, 26), + // (3,26): error CS1513: } expected + // extension Name(Type) : Type2() { } + Diagnostic(ErrorCode.ERR_RbraceExpected, ":").WithLocation(3, 26), + // (3,26): error CS1519: Invalid token ':' in a member declaration + // extension Name(Type) : Type2() { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ":").WithArguments(":").WithLocation(3, 26)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "Type2"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void TypeNamedExtension(bool useCSharp14) + { + UsingTree(""" +class extension +{ + extension(Type constructorParameter) { } +} +""", + TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "constructorParameter"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + // Tracked by https://github.com/dotnet/roslyn/issues/76130 : report error for declaring type named "extension" + // Note: break from C# 13 + UsingTree(""" +class extension +{ + extension(Type constructorParameter) { } +} +""", + useCSharp14 ? TestOptions.RegularNext : TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "constructorParameter"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + + UsingTree(""" +class extension +{ + @extension(Type constructorParameter) { } +} +""", + useCSharp14 ? TestOptions.RegularNext : TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "@extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "constructorParameter"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_NoName() + { + UsingTree(""" +class C +{ + extension(Type) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_NoName_02() + { + UsingTree(""" +class C +{ + extension(object) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple() + { + var src = """ +static class C +{ + extension(object x1, string x2) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,26): error CS9285: An extension container can have only one receiver parameter + // extension(object x1, string x2) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "string x2").WithLocation(3, 26)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "x1"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + N(SyntaxKind.IdentifierToken, "x2"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_02() + { + var src = """ +static class C +{ + extension(object, string) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,23): error CS9285: An extension container can have only one receiver parameter + // extension(object, string) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "string").WithLocation(3, 23)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_03() + { + var src = """ +class C +{ + extension(object object) { } +} +"""; + UsingTree(src, TestOptions.RegularPreview, + // (3,22): error CS1003: Syntax error, ',' expected + // extension(object object) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "object").WithArguments(",").WithLocation(3, 22)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_04() + { + var src = """ +class C +{ + extension(Type, object object) { } +} +"""; + + UsingTree(src, TestOptions.RegularPreview, + // (3,28): error CS1003: Syntax error, ',' expected + // extension(Type, object object) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "object").WithArguments(",").WithLocation(3, 28)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_05() + { + var src = """ +class C +{ + extension(Type, object =) { } +} +"""; + UsingTree(src, TestOptions.RegularPreview, + // (3,29): error CS1525: Invalid expression term ')' + // extension(Type, object =) { } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(3, 29)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_06() + { + var src = """ +class C +{ + extension(Type, object { } +} +"""; + UsingTree(src, TestOptions.RegularPreview, + // (3,28): error CS1026: ) expected + // extension(Type, object { } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "{").WithLocation(3, 28)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_Multiple_07() + { + var src = """ +static class C +{ + extension(object, params object[]) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,23): error CS9285: An extension container can have only one receiver parameter + // extension(object, params object[]) { } + Diagnostic(ErrorCode.ERR_ReceiverParameterOnlyOne, "params object[]").WithLocation(3, 23)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ParamsKeyword); + N(SyntaxKind.ArrayType); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.ArrayRankSpecifier); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.OmittedArraySizeExpression); + { + N(SyntaxKind.OmittedArraySizeExpressionToken); + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReceiverParameter_MissingClosingParen() + { + var src = """ +class C +{ + extension(object { } +} +"""; + + UsingTree(src, TestOptions.RegularPreview, + // (3,22): error CS1026: ) expected + // extension(object { } + Diagnostic(ErrorCode.ERR_CloseParenExpected, "{").WithLocation(3, 22)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + M(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void NoClosingBrace() + { + UsingTree(""" +class C +{ + extension(Type) { void M() { } +} +""", + TestOptions.RegularPreview, + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + 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.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void TopLevel() + { + var src = """ +extension(object) { } +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (1,1): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(Type) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(1, 1)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void InNestedType() + { + var src = """ +static class C +{ + class Nested + { + extension(object) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,9): error CS9283: Extensions must be declared in a top-level, non-generic, static class + // extension(object) { } + Diagnostic(ErrorCode.ERR_BadExtensionContainingType, "extension").WithLocation(5, 9)); + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Nested"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void InExtension() + { + var src = """ +static class C +{ + extension(object) + { + extension(string) { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,9): error CS9282: Extension declarations can include only methods or properties + // extension(Type2) { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "extension").WithLocation(5, 9)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.StringKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithAttributes() + { + UsingTree(""" +class C +{ + [MyAttribute] + extension(Type) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyAttribute"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Partial() + { + var src = """ +static class C +{ + partial extension(Type) { } +} +"""; + + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,13): error CS0267: The 'partial' modifier can only appear immediately before 'class', 'record', 'struct', 'interface', 'event', an instance constructor name, or a method or property return type. + // partial extension(Type) { } + Diagnostic(ErrorCode.ERR_PartialMisplaced, "extension").WithLocation(3, 13), + // (3,23): error CS0246: The type or namespace name 'Type' could not be found (are you missing a using directive or an assembly reference?) + // partial extension(Type) { } + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Type").WithArguments("Type").WithLocation(3, 23) + ); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Scoped() + { + UsingTree(""" +class C +{ + scoped extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,26): error CS1001: Identifier expected + // scoped extension(Type) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(3, 26)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "scoped"); + } + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Async() + { + UsingTree(""" +class C +{ + async extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,25): error CS1001: Identifier expected + // async extension(Type) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, ")").WithLocation(3, 25)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "async"); + } + N(SyntaxKind.IdentifierToken, "extension"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Const() + { + UsingTree(""" +class C +{ + const extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,20): error CS1001: Identifier expected + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(3, 20), + // (3,20): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_BadVarDecl, "(Type").WithLocation(3, 20), + // (3,20): error CS1003: Syntax error, '[' expected + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[").WithLocation(3, 20), + // (3,25): error CS1003: Syntax error, ']' expected + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]").WithLocation(3, 25), + // (3,27): error CS1003: Syntax error, ',' expected + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(3, 27), + // (3,29): error CS1002: ; expected + // const extension(Type) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 29), + // (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.FieldDeclaration); + { + N(SyntaxKind.ConstKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.BracketedArgumentList); + { + M(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.CloseBracketToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Fixed() + { + UsingTree(""" +class C +{ + fixed extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,20): error CS1001: Identifier expected + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "(").WithLocation(3, 20), + // (3,20): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration) + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_BadVarDecl, "(Type").WithLocation(3, 20), + // (3,20): error CS1003: Syntax error, '[' expected + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[").WithLocation(3, 20), + // (3,25): error CS1003: Syntax error, ']' expected + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]").WithLocation(3, 25), + // (3,27): error CS1003: Syntax error, ',' expected + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(3, 27), + // (3,29): error CS1002: ; expected + // fixed extension(Type) { } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(3, 29), + // (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.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + N(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + N(SyntaxKind.BracketedArgumentList); + { + M(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.CloseBracketToken); + } + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifiers_Ref() + { + UsingTree(""" +class C +{ + ref extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,18): error CS1519: Invalid token '(' in a member declaration + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "(").WithArguments("(").WithLocation(3, 18), + // (3,23): error CS8124: Tuple must contain at least two elements. + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(3, 23), + // (3,25): error CS1519: Invalid token '{' in a member declaration + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 25), + // (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.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + } + } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData("abstract", SyntaxKind.AbstractKeyword)] + [InlineData("sealed", SyntaxKind.SealedKeyword)] + [InlineData("static", SyntaxKind.StaticKeyword)] + [InlineData("new", SyntaxKind.NewKeyword)] + [InlineData("public", SyntaxKind.PublicKeyword)] + [InlineData("protected", SyntaxKind.ProtectedKeyword)] + [InlineData("private", SyntaxKind.PrivateKeyword)] + [InlineData("readonly", SyntaxKind.ReadOnlyKeyword)] + [InlineData("volatile", SyntaxKind.VolatileKeyword)] + [InlineData("extern", SyntaxKind.ExternKeyword)] + [InlineData("unsafe", SyntaxKind.UnsafeKeyword)] + [InlineData("virtual", SyntaxKind.VirtualKeyword)] + [InlineData("override", SyntaxKind.OverrideKeyword)] + [InlineData("required", SyntaxKind.RequiredKeyword)] + [InlineData("file", SyntaxKind.FileKeyword)] + public void WithModifiers_Misc(string modifier, SyntaxKind expected) + { + var src = $$""" +static class C +{ + {{modifier}} extension(object) { } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (3,14): error CS0106: The modifier 'abstract' is not valid for this item + // abstract extension(object) { } + Diagnostic(ErrorCode.ERR_BadMemberFlag, "extension").WithArguments(modifier)); + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(expected); + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Const() + { + var src = """ +static class C +{ + extension(object) + { + const int i = 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,19): error CS9282: Extension declarations can include only methods or properties + // const int i = 0; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "i").WithLocation(5, 19)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.ConstKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "i"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_FixedField() + { + var src = """ +static class C +{ + extension(object o) + { + fixed int field[10]; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,19): error CS1642: Fixed size buffer fields may only be members of structs + // fixed int field[10]; + Diagnostic(ErrorCode.ERR_FixedNotInStruct, "field").WithLocation(5, 19), + // (5,19): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context + // fixed int field[10]; + Diagnostic(ErrorCode.ERR_UnsafeNeeded, "field[10]").WithLocation(5, 19), + // (5,19): error CS9282: Extension declarations can include only methods or properties + // fixed int field[10]; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 19)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.FieldDeclaration); + { + N(SyntaxKind.FixedKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "field"); + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "10"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_EventField() + { + var src = """ +static class C +{ + extension(object o) + { + event System.EventHandler eventField; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,35): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler eventField; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), + // (5,35): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler eventField; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), + // (5,35): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler eventField; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "eventField").WithLocation(5, 35), + // (5,35): warning CS0067: The event 'C.extension(object).eventField' is never used + // event System.EventHandler eventField; + Diagnostic(ErrorCode.WRN_UnreferencedEvent, "eventField").WithArguments("C.extension(object).eventField").WithLocation(5, 35)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.EventFieldDeclaration); + { + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "System"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "EventHandler"); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "eventField"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Event() + { + var src = """ +static class C +{ + extension(object o) + { + event System.EventHandler Event { add { } remove { } } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,35): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler Event { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Event").WithLocation(5, 35), + // (5,43): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler Event { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "add").WithLocation(5, 43), + // (5,51): error CS9282: Extension declarations can include only methods or properties + // event System.EventHandler Event { add { } remove { } } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "remove").WithLocation(5, 51)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.EventDeclaration); + { + N(SyntaxKind.EventKeyword); + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "System"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "EventHandler"); + } + } + N(SyntaxKind.IdentifierToken, "Event"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AddAccessorDeclaration); + { + N(SyntaxKind.AddKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.RemoveAccessorDeclaration); + { + N(SyntaxKind.RemoveKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_MethodAndProperty() + { + UsingTree(""" +class C +{ + extension(Type) + { + void M() { } + int Property { get; set; } + } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + 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.CloseBraceToken); + } + } + N(SyntaxKind.PropertyDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "Property"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_NestedType() + { + var src = """ +static class C +{ + extension(object) + { + class Nested { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,15): error CS9282: Extension declarations can include only methods or properties + // class Nested { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Nested").WithLocation(5, 15)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "Nested"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Constructor() + { + var src = """ +static class C +{ + extension(object o) + { + Constructor() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,9): error CS1520: Method must have a return type + // Constructor() { } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "Constructor").WithLocation(5, 9), + // (5,9): error CS9282: Extension declarations can include only methods or properties + // Constructor() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Constructor").WithLocation(5, 9)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "Constructor"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_StaticConstructor() + { + var src = """ +static class C +{ + extension(object) + { + static Constructor() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,16): error CS1520: Method must have a return type + // static Constructor() { } + Diagnostic(ErrorCode.ERR_MemberNeedsType, "Constructor").WithLocation(5, 16), + // (5,16): error CS9282: Extension declarations can include only methods or properties + // static Constructor() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Constructor").WithLocation(5, 16)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.IdentifierToken, "Constructor"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Finalizer() + { + var src = """ +static class C +{ + extension(object o) + { + ~Finalizer() { } + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,10): error CS9282: Extension declarations can include only methods or properties + // ~Finalizer() { } + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "Finalizer").WithLocation(5, 10)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.DestructorDeclaration); + { + N(SyntaxKind.TildeToken); + N(SyntaxKind.IdentifierToken, "Finalizer"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Field() + { + var src = """ +static class C +{ + extension(object o) + { + int field; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,13): error CS9282: Extension declarations can include only methods or properties + // int field; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "field").WithLocation(5, 13), + // (5,13): warning CS0169: The field 'C.extension(object).field' is never used + // int field; + Diagnostic(ErrorCode.WRN_UnreferencedField, "field").WithArguments("C.extension(object).field").WithLocation(5, 13)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "field"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Indexer() + { + var src = """ +class C +{ + extension(Type) + { + int this[int i] { get => 0; set { } } + } +} +"""; + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.IndexerDeclaration); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ThisKeyword); + N(SyntaxKind.BracketedParameterList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.IdentifierToken, "i"); + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.GetAccessorDeclaration); + { + N(SyntaxKind.GetKeyword); + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.SetAccessorDeclaration); + { + N(SyntaxKind.SetKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_Operator() + { + var src = """ +static class C +{ + extension(object) + { + public static object operator +(object a, object b) => a; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,39): error CS0563: One of the parameters of a binary operator must be the containing type + // public static object operator +(object a, object b) => a; + Diagnostic(ErrorCode.ERR_BadBinaryOperatorSignature, "+").WithLocation(5, 39), + // (5,39): error CS9282: Extension declarations can include only methods or properties + // public static object operator +(object a, object b) => a; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "+").WithLocation(5, 39)); + + UsingTree(src, TestOptions.RegularPreview); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.OperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PlusToken); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "b"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void Member_ConversionOperator() + { + var src = """ +static class C +{ + extension(object) + { + public static implicit operator int(object t) => 0; + } +} +"""; + var comp = CreateCompilation(src); + comp.VerifyEmitDiagnostics( + // (5,41): error CS0556: User-defined conversion must convert to or from the enclosing type + // public static implicit operator int(object t) => 0; + Diagnostic(ErrorCode.ERR_ConversionNotInvolvingContainedType, "int").WithLocation(5, 41), + // (5,41): error CS9282: Extension declarations can include only methods or properties + // public static implicit operator int(object t) => 0; + Diagnostic(ErrorCode.ERR_ExtensionDisallowsMember, "int").WithLocation(5, 41)); + + UsingTree(src, TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConversionOperatorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.StaticKeyword); + N(SyntaxKind.ImplicitKeyword); + N(SyntaxKind.OperatorKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.ObjectKeyword); + } + N(SyntaxKind.IdentifierToken, "t"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithRef() + { + UsingTree(""" +class C +{ + ref extension(Type) { } +} +""", + TestOptions.RegularPreview, + // (3,18): error CS1519: Invalid token '(' in class, record, struct, or interface member declaration + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "(").WithArguments("(").WithLocation(3, 18), + // (3,23): error CS8124: Tuple must contain at least two elements. + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_TupleTooFewElements, ")").WithLocation(3, 23), + // (3,25): error CS1519: Invalid token '{' in class, record, struct, or interface member declaration + // ref extension(Type) { } + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 25), + // (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.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "extension"); + } + } + } + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.TupleType); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.TupleElement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + M(SyntaxKind.CommaToken); + M(SyntaxKind.TupleElement); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithAttributeOnParameter() + { + UsingTree(""" +class C +{ + extension([MyAttribute] Type) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "MyAttribute"); + } + } + N(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifierOnParameter() + { + UsingTree(""" +class C +{ + extension(ref Type) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifierOnParameter_Scoped() + { + UsingTree(""" +class C +{ + extension(scoped Type x) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithModifierOnParameter_ScopedRef() + { + UsingTree(""" +class C +{ + extension(scoped ref Type x) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.ScopedKeyword); + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory] + [InlineData("partial")] + [InlineData("await")] + [InlineData("on")] + [InlineData("by")] + public void MiscIdentifier(string identifier) + { + UsingTree($$""" +class C +{ + extension(Type {{identifier}}) { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + N(SyntaxKind.IdentifierToken, identifier); + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_SemiColon() + { + UsingTree(""" +class C +{ + extension(Type) { ; + class D { } +} +""", + TestOptions.RegularPreview, + // (3,23): error CS1519: Invalid token ';' in a member declaration + // extension(Type) { ; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 23), + // (5,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(5, 2)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_SemiColon_02() + { + UsingTree(""" +class C +{ + extension(Type) { ; +} +""", + TestOptions.RegularPreview, + // (3,23): error CS1519: Invalid token ';' in a member declaration + // extension(Type) { ; + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(3, 23), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_SemiColon_03() + { + UsingTree(""" +class C +{ + extension' expected + // extension").WithLocation(3, 17), + // (3,17): error CS1003: Syntax error, '(' expected + // extension(Type) where T : struct; + class D { } +} +""", + TestOptions.RegularPreview); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace() + { + UsingTree(""" +class C +{ + extension(Type) { { +} +""", + TestOptions.RegularPreview, + // (3,23): error CS1519: Invalid token '{' in a member declaration + // extension(Type) { { + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "{").WithArguments("{").WithLocation(3, 23), + // (4,2): error CS1513: } expected + // } + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 2)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + N(SyntaxKind.ExtensionKeyword); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.Parameter); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + M(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace_02() + { + UsingTree(""" +class C +{ + extension(Type) where { } +} +""", + TestOptions.RegularPreview, + // (3,30): error CS1001: Identifier expected + // extension(Type) where { } + Diagnostic(ErrorCode.ERR_IdentifierExpected, "{").WithLocation(3, 30), + // (3,30): error CS1003: Syntax error, ':' expected + // extension(Type) where { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(":").WithLocation(3, 30), + // (3,30): error CS1031: Type expected + // extension(Type) where { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 30)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.TypeConstraint); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace_03() + { + UsingTree(""" +class C +{ + extension(Type) where T { } + class D { } +} +""", + TestOptions.RegularPreview, + // (3,32): error CS1003: Syntax error, ':' expected + // extension(Type) where T { } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(":").WithLocation(3, 32), + // (3,32): error CS1031: Type expected + // extension(Type) where T { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 32)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + M(SyntaxKind.ColonToken); + M(SyntaxKind.TypeConstraint); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "D"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace_04() + { + UsingTree(""" +class C +{ + extension(Type) where T : { } +} +""", + TestOptions.RegularPreview, + // (3,34): error CS1031: Type expected + // extension(Type) where T : { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 34)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + M(SyntaxKind.TypeConstraint); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace_05() + { + UsingTree(""" +class C +{ + extension(Type) where T : struct, { } +} +""", + TestOptions.RegularPreview, + // (3,42): error CS1031: Type expected + // extension(Type) where T : struct, { } + Diagnostic(ErrorCode.ERR_TypeExpected, "{").WithLocation(3, 42)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExtensionDeclaration); + { + 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.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Type"); + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.TypeParameterConstraintClause); + { + N(SyntaxKind.WhereKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.ColonToken); + N(SyntaxKind.StructConstraint); + { + N(SyntaxKind.StructKeyword); + } + N(SyntaxKind.CommaToken); + M(SyntaxKind.TypeConstraint); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + } + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void WithTerminator_OpenBrace_06() + { + UsingTree(""" +class C +{ + extension' expected + // extension").WithLocation(3, 17), + // (3,17): error CS1003: Syntax error, '(' expected + // extension x;", expectedErrors: // (1,19): error CS9027: Unexpected keyword 'unchecked' diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/NullConditionalAssignmentParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/NullConditionalAssignmentParsingTests.cs new file mode 100644 index 0000000000000..39fed020c2e63 --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/NullConditionalAssignmentParsingTests.cs @@ -0,0 +1,595 @@ +// 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.Diagnostics; +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; +using Roslyn.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public class NullConditionalAssignmentParsingTests : ParsingTests + { + public static TheoryData AllTestOptions { get; } = new TheoryData([TestOptions.Regular13, TestOptions.RegularPreview]); + + public NullConditionalAssignmentParsingTests(ITestOutputHelper output) : base(output) { } + + protected override SyntaxTree ParseTree(string text, CSharpParseOptions? options) + { + return SyntaxFactory.ParseSyntaxTree(text, options: options); + } + + protected override CSharpSyntaxNode ParseNode(string text, CSharpParseOptions? options) + { + return SyntaxFactory.ParseExpression(text, options: options); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Assignment_LeftMemberAccess(CSharpParseOptions parseOptions) + { + string source = "a?.b = c"; + UsingExpression(source, parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Assignment_LeftMemberAccess_Nested_01(CSharpParseOptions parseOptions) + { + string source = "a?.b = c = d"; + UsingExpression(source, parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Assignment_LeftMemberAccess_Nested_02(CSharpParseOptions parseOptions) + { + string source = "a?.b = c = d = e"; + UsingExpression(source, parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Assignment_LeftMemberAccess_Nested_03(CSharpParseOptions parseOptions) + { + string source = "a?.b = c?[d] = e?.f"; + UsingExpression(source, parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.ElementBindingExpression); + { + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "e"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "f"); + } + } + } + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Increment_LeftMemberAccess(CSharpParseOptions parseOptions) + { + // Increment/decrement of a conditional access is not supported. + string source = "a?.b++"; + UsingExpression(source, parseOptions); + N(SyntaxKind.PostIncrementExpression); + { + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + } + N(SyntaxKind.PlusPlusToken); + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void PreIncrement_ConditionalElementAccess(CSharpParseOptions parseOptions) + { + // Increment/decrement of a conditional access is not supported. + string source = "--a?[b]"; + UsingExpression(source, parseOptions); + N(SyntaxKind.PreDecrementExpression); + { + N(SyntaxKind.MinusMinusToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ElementBindingExpression); + { + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void NullCoalescing_LeftMemberAccess(CSharpParseOptions parseOptions) + { + string source = "a?.b = c ?? d"; + UsingExpression(source, parseOptions); + + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.CoalesceExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionQuestionToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "d"); + } + } + } + } + EOF(); + } + + [Theory] + [InlineData(SyntaxKind.BarEqualsToken)] + [InlineData(SyntaxKind.AmpersandEqualsToken)] + [InlineData(SyntaxKind.CaretEqualsToken)] + [InlineData(SyntaxKind.LessThanLessThanEqualsToken)] + [InlineData(SyntaxKind.GreaterThanGreaterThanEqualsToken)] + [InlineData(SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken)] + [InlineData(SyntaxKind.PlusEqualsToken)] + [InlineData(SyntaxKind.MinusEqualsToken)] + [InlineData(SyntaxKind.AsteriskEqualsToken)] + [InlineData(SyntaxKind.SlashEqualsToken)] + [InlineData(SyntaxKind.PercentEqualsToken)] + [InlineData(SyntaxKind.EqualsToken)] + [InlineData(SyntaxKind.QuestionQuestionEqualsToken)] + public void VariousAssignmentKinds_LeftMemberAccess(SyntaxKind kind) + { + string op = SyntaxFacts.GetText(kind); + string source = $"a?.b {op} c"; + UsingExpression(source, TestOptions.Regular13); + verify(); + + UsingExpression(source, TestOptions.RegularPreview); + verify(); + + void verify() + { + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxFacts.GetAssignmentExpression(kind)); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(kind); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + EOF(); + } + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Parentheses_Assignment_LHS_01(CSharpParseOptions parseOptions) + { + UsingExpression("(c?.F) = 1", parseOptions); + + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.ParenthesizedExpression); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + } + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Invocation_01(CSharpParseOptions parseOptions) + { + UsingExpression("c?.M() = 1", parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "M"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void RefAssignment_01(CSharpParseOptions parseOptions) + { + UsingExpression("c?.F = ref x", parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void RefReturningLambda_01(CSharpParseOptions parseOptions) + { + UsingExpression("c?.F = ref int () => ref x", parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.RefExpression); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + } + } + } + EOF(); + } + + [Theory] + [MemberData(nameof(AllTestOptions))] + public void Suppression_01(CSharpParseOptions parseOptions) + { + UsingExpression("a?.b! = c", parseOptions); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "a"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.SimpleAssignmentExpression); + { + N(SyntaxKind.SuppressNullableWarningExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "b"); + } + } + N(SyntaxKind.ExclamationToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + } + } + EOF(); + } + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs index 930ced6a0a2bc..f69df41310e44 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ParsingTests.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; using Xunit.Sdk; @@ -20,7 +21,8 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests public abstract class ParsingTests : CSharpTestBase { private CSharpSyntaxNode? _node; - private IEnumerator? _treeEnumerator; + private IEnumerator? _treeEnumerator; + private bool _verifyTrivia; private readonly ITestOutputHelper _output; public ParsingTests(ITestOutputHelper output) @@ -192,6 +194,12 @@ protected void UsingNode(CSharpSyntaxNode root) _treeEnumerator = nodes.GetEnumerator(); } + protected void VerifyTrivia(bool enabled = true) + { + VerifyEnumeratorConsumed(); + _verifyTrivia = enabled; + } + /// /// Moves the enumerator and asserts that the current node is of the given kind. /// @@ -204,6 +212,7 @@ protected SyntaxNodeOrToken N(SyntaxKind kind, string? value = null) var current = _treeEnumerator.Current; Assert.Equal(kind, current.Kind()); + Assert.False(current.IsTrivia); Assert.False(current.IsMissing); if (value != null) @@ -211,7 +220,7 @@ protected SyntaxNodeOrToken N(SyntaxKind kind, string? value = null) Assert.Equal(current.ToString(), value); } - return current; + return current.NodeOrToken; } catch when (DumpAndCleanup()) { @@ -229,10 +238,53 @@ protected SyntaxNodeOrToken M(SyntaxKind kind) try { Assert.True(_treeEnumerator!.MoveNext()); - SyntaxNodeOrToken current = _treeEnumerator.Current; + var current = _treeEnumerator.Current; Assert.Equal(kind, current.Kind()); Assert.True(current.IsMissing); - return current; + return current.NodeOrToken; + } + catch when (DumpAndCleanup()) + { + throw; + } + } + + /// + /// Asserts leading trivia. + /// + [DebuggerHidden] + protected SyntaxTrivia L(SyntaxKind kind, string? value = null) + { + return Trivia(kind, value, trailing: false); + } + + /// + /// Asserts trailing trivia. + /// + [DebuggerHidden] + protected SyntaxTrivia T(SyntaxKind kind, string? value = null) + { + return Trivia(kind, value, trailing: true); + } + + [DebuggerHidden] + private SyntaxTrivia Trivia(SyntaxKind kind, string? value, bool trailing) + { + try + { + Assert.True(_treeEnumerator!.MoveNext()); + var current = _treeEnumerator.Current; + + Assert.Equal(kind, current.Kind()); + Assert.True(current.IsTrivia); + Assert.Equal(trailing, current.IsTrailing); + + if (value != null) + { + Assert.Equal(current.ToString().ReplaceLineEndings("\n"), value); + } + + return current.Trivia; } catch when (DumpAndCleanup()) { @@ -254,35 +306,80 @@ protected void EOF() } } - private IEnumerable EnumerateNodes(CSharpSyntaxNode node, bool dump) + private IEnumerable EnumerateNodes(CSharpSyntaxNode node, bool dump) { Print(node, dump); yield return node; - var stack = new Stack(24); - stack.Push(node.ChildNodesAndTokens().GetEnumerator()); + var stack = new Stack<(SyntaxTriviaList.Enumerator, ChildSyntaxList.Enumerator, SyntaxTriviaList.Enumerator)>(24); + stack.Push((default, node.ChildNodesAndTokens().GetEnumerator(), default)); Open(dump); while (stack.Count > 0) { - var en = stack.Pop(); - if (!en.MoveNext()) + var (en1, en2, en3) = stack.Pop(); + + byte en; + SyntaxTrivia currentTrivia = default; + SyntaxNodeOrToken currentChild = default; + + if (en1.MoveNext()) + { + currentTrivia = en1.Current; + en = 1; + } + else if (en2.MoveNext()) + { + currentChild = en2.Current; + en = 2; + } + else if (en3.MoveNext()) + { + currentTrivia = en3.Current; + en = 3; + } + else { // no more down this branch Close(dump); continue; } - var current = en.Current; - stack.Push(en); // put it back on stack (struct enumerator) + stack.Push((en1, en2, en3)); // put it back on stack (struct enumerator) + + if (en != 2) // we are on a trivia + { + Print(currentTrivia, trailing: en == 3, dump); + yield return new SyntaxNodeOrTokenOrTrivia(currentTrivia, trailing: en == 3); - Print(current, dump); - yield return current; + if (currentTrivia.TryGetStructure(out var triviaStructure)) + { + // trivia has a structure, so consider its children + stack.Push((default, triviaStructure.ChildNodesAndTokens().GetEnumerator(), default)); + Open(dump); + } - if (current.IsNode) + continue; + } + + Print(currentChild, dump); + yield return currentChild; + + if (currentChild.AsNode(out var currentChildNode)) { // not token, so consider children - stack.Push(current.ChildNodesAndTokens().GetEnumerator()); + stack.Push((default, currentChildNode.ChildNodesAndTokens().GetEnumerator(), default)); + Open(dump); + continue; + } + + if (_verifyTrivia && currentChild.AsToken(out var currentChildToken) && + (currentChildToken.HasLeadingTrivia || currentChildToken.HasTrailingTrivia)) + { + // token with trivia + stack.Push((currentChildToken.GetLeadingTrivia().GetEnumerator(), + default, + currentChildToken.GetTrailingTrivia().GetEnumerator())); Open(dump); continue; } @@ -295,30 +392,53 @@ private void Print(SyntaxNodeOrToken node, bool dump) { if (dump) { - switch (node.Kind()) + if (!node.IsMissing && ShouldIncludeText(node.Kind())) { - case SyntaxKind.IdentifierToken: - case SyntaxKind.NumericLiteralToken: - case SyntaxKind.StringLiteralToken: - case SyntaxKind.Utf8StringLiteralToken: - case SyntaxKind.SingleLineRawStringLiteralToken: - case SyntaxKind.Utf8SingleLineRawStringLiteralToken: - case SyntaxKind.MultiLineRawStringLiteralToken: - case SyntaxKind.Utf8MultiLineRawStringLiteralToken: - if (node.IsMissing) - { - goto default; - } - var value = node.ToString().Replace("\"", "\\\""); - _output.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), value); - break; - default: - _output.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind()); - break; + var value = node.ToString().Replace("\"", "\\\""); + _output.WriteLine(@"N(SyntaxKind.{0}, ""{1}"");", node.Kind(), value); } + else + { + _output.WriteLine("{0}(SyntaxKind.{1});", node.IsMissing ? "M" : "N", node.Kind()); + } + } + } + + private void Print(SyntaxTrivia trivia, bool trailing, bool dump) + { + if (dump) + { + string? value = ShouldIncludeText(trivia.Kind()) + ? $""" + , "{trivia.ToString().Replace("\"", "\\\"").ReplaceLineEndings("\\n")}" + """ + : null; + _output.WriteLine($""" + {(trailing ? "T" : "L")}(SyntaxKind.{trivia.Kind()}{value}); + """); } } + private static bool ShouldIncludeText(SyntaxKind kind) + { + // This can be changed without failing existing tests, + // it only affects the baseline output printed when a test fails. + return kind + is SyntaxKind.IdentifierToken + or SyntaxKind.NumericLiteralToken + or SyntaxKind.StringLiteralToken + or SyntaxKind.Utf8StringLiteralToken + or SyntaxKind.SingleLineRawStringLiteralToken + or SyntaxKind.Utf8SingleLineRawStringLiteralToken + or SyntaxKind.MultiLineRawStringLiteralToken + or SyntaxKind.Utf8MultiLineRawStringLiteralToken + or SyntaxKind.BadToken + or SyntaxKind.WhitespaceTrivia + or SyntaxKind.EndOfLineTrivia + or SyntaxKind.PreprocessingMessageTrivia + ; + } + private void Open(bool dump) { if (dump) @@ -379,5 +499,45 @@ protected static void ParseIncompleteSyntax(string text) return tokensBuilder.ToImmutableAndFree(); } } + + private readonly struct SyntaxNodeOrTokenOrTrivia + { + public bool IsTrivia { get; } + public bool IsTrailing { get; } + public SyntaxNodeOrToken NodeOrToken { get; } + public SyntaxTrivia Trivia { get; } + + public SyntaxNodeOrTokenOrTrivia(SyntaxNodeOrToken nodeOrToken) + { + NodeOrToken = nodeOrToken; + Trivia = default; + IsTrivia = false; + IsTrailing = false; + } + + public SyntaxNodeOrTokenOrTrivia(SyntaxTrivia trivia, bool trailing) + { + NodeOrToken = default; + Trivia = trivia; + IsTrivia = true; + IsTrailing = trailing; + } + + public bool IsMissing => !IsTrivia && NodeOrToken.IsMissing; + + public SyntaxKind Kind() => IsTrivia ? Trivia.Kind() : NodeOrToken.Kind(); + + public override string ToString() => IsTrivia ? Trivia.ToString() : NodeOrToken.ToString(); + + public static implicit operator SyntaxNodeOrTokenOrTrivia(SyntaxNode node) + { + return new SyntaxNodeOrTokenOrTrivia(node); + } + + public static implicit operator SyntaxNodeOrTokenOrTrivia(SyntaxNodeOrToken nodeOrToken) + { + return new SyntaxNodeOrTokenOrTrivia(nodeOrToken); + } + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PartialEventsAndConstructorsParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PartialEventsAndConstructorsParsingTests.cs new file mode 100644 index 0000000000000..62eb5c4c8a0cd --- /dev/null +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PartialEventsAndConstructorsParsingTests.cs @@ -0,0 +1,1528 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests; + +public sealed class PartialEventsAndConstructorsParsingTests(ITestOutputHelper output) : ParsingTests(output) +{ + private sealed class CSharp14_Preview() + : CombinatorialValuesAttribute(LanguageVersionFacts.CSharpNext, LanguageVersion.Preview); + + private sealed class CSharp13_CSharp14_Preview() + : CombinatorialValuesAttribute(LanguageVersion.CSharp13, LanguageVersionFacts.CSharpNext, LanguageVersion.Preview); + + [Theory, CombinatorialData] + public void Event_Tree([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + partial class C + { + partial event Action E; + partial event Action E { add { } remove { } } + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.EventDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AddAccessorDeclaration); + { + N(SyntaxKind.AddKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.RemoveAccessorDeclaration); + { + N(SyntaxKind.RemoveKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_Multiple([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E, F; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_Initializer([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E = null; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_Multiple_Initializer([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E, F = null; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_Multiple_Initializers([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E = null, F = null; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.EqualsValueClause); + { + N(SyntaxKind.EqualsToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_PartialAfterEvent([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + event partial Action E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,7): error CS1031: Type expected + // event partial Action E; + Diagnostic(ErrorCode.ERR_TypeExpected, "partial").WithLocation(1, 7), + // (1,7): error CS1525: Invalid expression term 'partial' + // event partial Action E; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "partial").WithArguments("partial").WithLocation(1, 7), + // (1,7): error CS1003: Syntax error, ',' expected + // event partial Action E; + Diagnostic(ErrorCode.ERR_SyntaxError, "partial").WithArguments(",").WithLocation(1, 7)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.EventKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_PartialAfterType([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + event Action partial E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,22): error CS1003: Syntax error, ',' expected + // event Action partial E; + Diagnostic(ErrorCode.ERR_SyntaxError, "E").WithArguments(",").WithLocation(1, 22)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_PartialAfterPublic([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + public partial event Action E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_PartialBeforePublic([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial public event Action E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_DoublePartial([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial partial event Action E; + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,9): error CS1525: Invalid expression term 'partial' + // partial partial event Action E; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "partial").WithArguments("partial").WithLocation(1, 9), + // (1,9): error CS1003: Syntax error, ',' expected + // partial partial event Action E; + Diagnostic(ErrorCode.ERR_SyntaxError, "partial").WithArguments(",").WithLocation(1, 9)); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Definition_MissingRest([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,14): error CS1031: Type expected + // partial event + Diagnostic(ErrorCode.ERR_TypeExpected, "").WithLocation(1, 14), + // (1,14): error CS1514: { expected + // partial event + Diagnostic(ErrorCode.ERR_LbraceExpected, "").WithLocation(1, 14), + // (1,14): error CS1513: } expected + // partial event + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(1, 14)); + + N(SyntaxKind.EventDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.IdentifierToken); + M(SyntaxKind.AccessorList); + { + M(SyntaxKind.OpenBraceToken); + M(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Implementation([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E { add { } remove { } } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AddAccessorDeclaration); + { + N(SyntaxKind.AddKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.RemoveAccessorDeclaration); + { + N(SyntaxKind.RemoveKeyword); + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Implementation_Multiple([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E, F { add { } remove { } } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,27): error CS1003: Syntax error, ',' expected + // partial event Action E, F { add { } remove { } } + Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments(",").WithLocation(1, 27), + // (1,49): error CS1002: ; expected + // partial event Action E, F { add { } remove { } } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 49)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + N(SyntaxKind.CommaToken); + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + M(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Implementation_PartialAfterEvent([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + event partial Action E { add { } remove { } } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,7): error CS1031: Type expected + // event partial Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_TypeExpected, "partial").WithLocation(1, 7), + // (1,7): error CS1525: Invalid expression term 'partial' + // event partial Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "partial").WithArguments("partial").WithLocation(1, 7), + // (1,7): error CS1003: Syntax error, ',' expected + // event partial Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_SyntaxError, "partial").WithArguments(",").WithLocation(1, 7), + // (1,46): error CS1002: ; expected + // event partial Action E { add { } remove { } } + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 46)); + + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.EventKeyword); + M(SyntaxKind.VariableDeclaration); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + M(SyntaxKind.VariableDeclarator); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Implementation_SemicolonAccessors([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E { add; remove; } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.EventDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.AddAccessorDeclaration); + { + N(SyntaxKind.AddKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.RemoveAccessorDeclaration); + { + N(SyntaxKind.RemoveKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_Implementation_PartialAccessors([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial event Action E { partial add; partial remove; } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,26): error CS1055: An add or remove accessor expected + // partial event Action E { partial add; partial remove; } + Diagnostic(ErrorCode.ERR_AddOrRemoveExpected, "partial").WithLocation(1, 26), + // (1,39): error CS1055: An add or remove accessor expected + // partial event Action E { partial add; partial remove; } + Diagnostic(ErrorCode.ERR_AddOrRemoveExpected, "partial").WithLocation(1, 39)); + + N(SyntaxKind.EventDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.IdentifierToken, "E"); + N(SyntaxKind.AccessorList); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.UnknownAccessorDeclaration); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.AddAccessorDeclaration); + { + N(SyntaxKind.AddKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.UnknownAccessorDeclaration); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.RemoveAccessorDeclaration); + { + N(SyntaxKind.RemoveKeyword); + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Event_InPlaceOfIdentifier([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + partial class C + { + [Attr( + partial event Action E; + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (3,11): error CS1026: ) expected + // [Attr( + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 11), + // (3,11): error CS1003: Syntax error, ']' expected + // [Attr( + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("]").WithLocation(3, 11)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.EventFieldDeclaration); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + N(SyntaxKind.AttributeArgumentList); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.EventKeyword); + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Action"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "E"); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_Tree([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + partial class C + { + partial C(); + partial C() { } + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_Declaration([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial C() { } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Fact] + public void Constructor_Declaration_CSharp13() + { + UsingDeclaration(""" + partial C() { } + """, + TestOptions.Regular13); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.CloseBraceToken); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_ArrowBody([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial C() => throw null; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.ThrowExpression); + { + N(SyntaxKind.ThrowKeyword); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_NoParens([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial C; + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.FieldDeclaration); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "C"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_NoName([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial (); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.IdentifierToken, "partial"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_PartialAsName([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial partial(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "partial"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_PartialAfterName([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + C partial(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.IdentifierToken, "partial"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_PartialAfterPublic([CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + public partial C(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_PartialBeforePublic([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial public C(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.PublicKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_TypeTwice([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial C C(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "C"); + } + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_PartialEscaped([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + @partial C(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@partial"); + } + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_KeywordName([CSharp13_CSharp14_Preview] LanguageVersion langVersion) + { + UsingDeclaration(""" + partial const(); + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,1): error CS1073: Unexpected token 'const' + // partial const(); + Diagnostic(ErrorCode.ERR_UnexpectedToken, "partial").WithArguments("const").WithLocation(1, 1), + // (1,9): error CS1519: Invalid token 'const' in class, record, struct, or interface member declaration + // partial const(); + Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "const").WithArguments("const").WithLocation(1, 9)); + + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + } + EOF(); + } + + [Theory, CombinatorialData] + public void Constructor_InPlaceOfIdentifier([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + partial class C + { + [Attr( + partial C(); + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (3,11): error CS1026: ) expected + // [Attr( + Diagnostic(ErrorCode.ERR_CloseParenExpected, "").WithLocation(3, 11), + // (3,11): error CS1003: Syntax error, ']' expected + // [Attr( + Diagnostic(ErrorCode.ERR_SyntaxError, "").WithArguments("]").WithLocation(3, 11)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.AttributeList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Attribute); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Attr"); + } + N(SyntaxKind.AttributeArgumentList); + { + N(SyntaxKind.OpenParenToken); + M(SyntaxKind.CloseParenToken); + } + } + M(SyntaxKind.CloseBracketToken); + } + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void ReturningPartialType_LocalFunction_InMethod([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + class C + { + void M() + { + partial F() => null; + } + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (4,6): error CS1513: } expected + // { + Diagnostic(ErrorCode.ERR_RbraceExpected, "").WithLocation(4, 6), + // (7,1): error CS1022: Type or namespace definition, or end-of-file expected + // } + Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(7, 1)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + 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); + M(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReturningPartialType_LocalFunction_InMethod_CSharp13() + { + UsingTree(""" + class C + { + void M() + { + partial F() => null; + } + } + """, + TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + 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.LocalFunctionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void ReturningPartialType_LocalFunction_TopLevel([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + partial F() => null; + """, + TestOptions.Regular.WithLanguageVersion(langVersion), + // (1,9): error CS0116: A namespace cannot directly contain members such as fields, methods or statements + // partial F() => null; + Diagnostic(ErrorCode.ERR_NamespaceUnexpected, "F").WithLocation(1, 9)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.IncompleteMember); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "F"); + } + } + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.ParenthesizedLambdaExpression); + { + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReturningPartialType_LocalFunction_TopLevel_CSharp13() + { + UsingTree(""" + partial F() => null; + """, + TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.IdentifierToken, "F"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Theory, CombinatorialData] + public void ReturningPartialType_Method([CSharp14_Preview] LanguageVersion langVersion) + { + UsingTree(""" + class C + { + partial M() => null; + @partial M() => null; + } + """, + TestOptions.Regular.WithLanguageVersion(langVersion)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ConstructorDeclaration); + { + N(SyntaxKind.PartialKeyword); + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@partial"); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact] + public void ReturningPartialType_Method_CSharp13() + { + UsingTree(""" + class C + { + partial M() => null; + @partial M() => null; + } + """, + TestOptions.Regular13); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "partial"); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "@partial"); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.ArrowExpressionClause); + { + N(SyntaxKind.EqualsGreaterThanToken); + N(SyntaxKind.NullLiteralExpression); + { + N(SyntaxKind.NullKeyword); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } +} diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs index 18a55b0dd7804..1ed38eebcfb49 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ScriptParsingTests.cs @@ -9716,8 +9716,7 @@ public void ShebangInComment() [Fact] public void ShebangNotInScript() { - ParseAndValidate("#!/usr/bin/env csi", TestOptions.Regular, - new ErrorDescription { Code = (int)ErrorCode.ERR_PPDirectiveExpected, Line = 1, Column = 1 }); + ParseAndValidate("#!/usr/bin/env csi", TestOptions.Regular); } private void TestShebang(SyntaxTrivia trivia, string expectedSkippedText) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/SuppressNullableWarningExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/SuppressNullableWarningExpressionParsingTests.cs index 731cda0c1dab7..984c3f62133c6 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/SuppressNullableWarningExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/SuppressNullableWarningExpressionParsingTests.cs @@ -596,15 +596,15 @@ public void ConditionalAccess_07() N(SyntaxKind.ConditionalExpression); { - N(SyntaxKind.ConditionalAccessExpression); + N(SyntaxKind.SuppressNullableWarningExpression); { - N(SyntaxKind.IdentifierName); - { - N(SyntaxKind.IdentifierToken, "x"); - } - N(SyntaxKind.QuestionToken); - N(SyntaxKind.SuppressNullableWarningExpression); + N(SyntaxKind.ConditionalAccessExpression); { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.QuestionToken); N(SyntaxKind.MemberBindingExpression); { N(SyntaxKind.DotToken); @@ -613,8 +613,8 @@ public void ConditionalAccess_07() N(SyntaxKind.IdentifierToken, "y"); } } - N(SyntaxKind.ExclamationToken); } + N(SyntaxKind.ExclamationToken); } N(SyntaxKind.QuestionToken); N(SyntaxKind.LogicalNotExpression); @@ -650,6 +650,46 @@ public void ConditionalAccess_07() EOF(); } + [Fact, WorkItem(47712, "https://github.com/dotnet/roslyn/pull/47712")] + public void ConditionalAccess_09() + { + UsingNode("x?.y?.z!"); + + N(SyntaxKind.SuppressNullableWarningExpression); + { + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "x"); + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.ConditionalAccessExpression); + { + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "y"); + } + } + N(SyntaxKind.QuestionToken); + N(SyntaxKind.MemberBindingExpression); + { + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "z"); + } + } + } + } + N(SyntaxKind.ExclamationToken); + } + EOF(); + } + [Fact, WorkItem(47712, "https://github.com/dotnet/roslyn/pull/47712")] public void ConditionalAccess_ElementAccess() { diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs index 645ee0ad29ad3..913605abf6fbc 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNodeTests.cs @@ -383,10 +383,11 @@ public void TestContainsDirective() testContainsHelper1("#undef x", SyntaxKind.UndefDirectiveTrivia); testContainsHelper1("#warning", SyntaxKind.WarningDirectiveTrivia); - // #! is special and is only recognized at start of a script file and nowhere else. + // #! is special and is only recognized at start of a file and nowhere else. testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Script)); testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit(" #!command", options: TestOptions.Script)); - testContainsHelper2(new[] { SyntaxKind.BadDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular)); + testContainsHelper2(new[] { SyntaxKind.ShebangDirectiveTrivia }, SyntaxFactory.ParseCompilationUnit("#!command", options: TestOptions.Regular)); + testContainsHelper2([SyntaxKind.IgnoredDirectiveTrivia], SyntaxFactory.ParseCompilationUnit("#:x")); return; @@ -470,7 +471,7 @@ static void testContainsHelper2(SyntaxKind[] directiveKinds, CompilationUnitSynt { Assert.True(compilationUnit.ContainsDirectives); foreach (var directiveKind in directiveKinds) - Assert.True(compilationUnit.ContainsDirective(directiveKind)); + Assert.True(compilationUnit.ContainsDirective(directiveKind), directiveKind.ToString()); for (var kind = SyntaxKind.TildeToken; kind < SyntaxKind.XmlElement; kind++) { diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs index bcb044788b178..856a0faa50743 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/SyntaxNormalizerTests.cs @@ -3705,6 +3705,27 @@ void Goo(A x) """); } + [Fact] + public void IgnoredDirectives() + { + TestNormalizeDeclaration(""" + #:a + #: b c + { + #:d + } + #:e + """, """ + #:a + #:b c + { + #:d + } + #:e + + """); + } + [Fact, WorkItem(542887, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542887")] public void TestFormattingForBlockSyntax() { @@ -3790,6 +3811,26 @@ void goo() "}"); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76856")] + public void TestNormalizeDocumentationMultiLineCommentsWithTrailingNewline() + { + TestNormalizeStatement(""" + /** + * + * Escape XML special characters + */ + private String EscapeXML(String str) + { + """, """ + /// + /// + /// Escape XML special characters + ////// + private String EscapeXML(String str) + { + """); + } + [Fact] public void TestNormalizeEOL() { diff --git a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs index ee3925cc2d9ef..a21427abe5c62 100644 --- a/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/AnalyzerAssemblyLoaderTests.cs @@ -23,6 +23,9 @@ using Basic.Reference.Assemblies; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.UnitTests.Diagnostics; +using System.Diagnostics; +using System.ComponentModel; #if NET using Roslyn.Test.Utilities.CoreClr; @@ -69,20 +72,6 @@ public enum AnalyzerTestKind /// /// Similar to Limitation 1 is when the dependency, B.dll, is already present in the Load or LoadFrom context /// then that will be used. The runtime will not attempt to load a better version (an exact match for example). - /// - /// Limitation 3: Shadow copy breaks up directories - /// - /// The shadow copy loader strategy is to put every analyzer dependency into a different shadow directory. That - /// means if A.dll and B.dll are in the same directory for a normal load, they are in different directories - /// during a shadow copy load. - /// - /// This causes significant issues in .NET Framework because we don't have the ability to know where a load - /// is coming from. The AppDomain.AssemblyResolve event just requests "B, Version=1.0.0.0" but gives no context - /// as to where the request is coming from. That means we often end up loading a different copy of B.dll in a - /// shadow load scenario. - /// - /// Long term this is something that needs to be addressed. Tracked by https://github.com/dotnet/roslyn/issues/66532 - /// /// [Collection(AssemblyLoadTestFixtureCollection.Name)] public sealed class AnalyzerAssemblyLoaderTests : TestBase @@ -102,95 +91,106 @@ public AnalyzerAssemblyLoaderTests(ITestOutputHelper testOutputHelper, AssemblyL private void Run( AnalyzerTestKind kind, Action testAction, - IAnalyzerAssemblyResolver[]? externalResolvers = null, - [CallerMemberName] string? memberName = null) => + ImmutableArray pathResolvers = default, + ImmutableArray assemblyResolvers = default) => Run( kind, - static (_, _) => { }, - testAction, - externalResolvers, - memberName); + state: null, + testAction.Method, + pathResolvers.NullToEmpty(), + assemblyResolvers.NullToEmpty()); private void Run( AnalyzerTestKind kind, object state, Action testAction, - IAnalyzerAssemblyResolver[]? externalResolvers = null, - [CallerMemberName] string? memberName = null) => + ImmutableArray pathResolvers = default, + ImmutableArray assemblyResolvers = default) => Run( kind, state, - static (_, _) => { }, testAction.Method, - externalResolvers, - memberName); + pathResolvers.NullToEmpty(), + assemblyResolvers.NullToEmpty()); private void Run( AnalyzerTestKind kind, - Action prepLoadContextAction, - Action testAction, - IAnalyzerAssemblyResolver[]? externalResolvers = null, - [CallerMemberName] string? memberName = null) => - Run( + object? state, + MethodInfo method, + ImmutableArray pathResolvers, + ImmutableArray assemblyResolvers) + { + var util = new InvokeUtil(); + util.Exec( + TestOutputHelper, + pathResolvers, + assemblyResolvers, + TestFixture, kind, - state: null, - prepLoadContextAction, - testAction.Method, - externalResolvers, - memberName); + method.DeclaringType!.FullName!, + method.Name, + state); + } private void Run( - AnalyzerTestKind kind, - object? state, - Action prepLoadContextAction, - MethodInfo method, - IAnalyzerAssemblyResolver[]? externalResolvers, - string? memberName) + AnalyzerAssemblyLoader loader, + Action testAction) { - var alc = new AssemblyLoadContext($"Test {memberName}", isCollectible: true); - try - { - prepLoadContextAction(alc, TestFixture); - var util = new InvokeUtil(); - util.Exec(TestOutputHelper, alc, TestFixture, kind, method.DeclaringType!.FullName!, method.Name, externalResolvers ?? [], state); - } - finally - { - alc.Unload(); - } + var util = new InvokeUtil(); + util.Exec( + TestOutputHelper, + TestFixture, + loader, + testAction.Method.DeclaringType!.FullName!, + testAction.Method.Name, + state: null); } + private void Run( + AnalyzerAssemblyLoader loader, + object state, + Action testAction) + { + var util = new InvokeUtil(); + util.Exec( + TestOutputHelper, + TestFixture, + loader, + testAction.Method.DeclaringType!.FullName!, + testAction.Method.Name, + state: state); + } #else private void Run( AnalyzerTestKind kind, Action testAction, - IAnalyzerAssemblyResolver[]? externalResolvers = null, + ImmutableArray pathResolvers = default, [CallerMemberName] string? memberName = null) => Run( kind, state: null, testAction.Method, - externalResolvers, + pathResolvers.NullToEmpty(), memberName); private void Run( AnalyzerTestKind kind, object state, Action testAction, - IAnalyzerAssemblyResolver[]? externalResolvers = null, + ImmutableArray pathResolvers = default, [CallerMemberName] string? memberName = null) => Run(kind, state, testAction.Method, - externalResolvers, + pathResolvers.NullToEmpty(), memberName); private void Run( AnalyzerTestKind kind, object state, MethodInfo method, - IAnalyzerAssemblyResolver[]? externalResolvers, + ImmutableArray pathResolvers, string? memberName) { AppDomain? appDomain = null; @@ -200,7 +200,7 @@ private void Run( var testOutputHelper = new AppDomainTestOutputHelper(TestOutputHelper); var type = typeof(InvokeUtil); var util = (InvokeUtil)appDomain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName); - util.Exec(testOutputHelper, TestFixture, kind, method.DeclaringType.FullName, method.Name, externalResolvers ?? [], state); + util.Exec(testOutputHelper, TestFixture, kind, method.DeclaringType.FullName, method.Name, pathResolvers.ToArray(), state); } finally { @@ -264,8 +264,8 @@ public void AddDependencyLocationThrowsOnNull(AnalyzerTestKind kind) { Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => { - Assert.Throws("fullPath", () => loader.AddDependencyLocation(null!)); - Assert.Throws("fullPath", () => loader.AddDependencyLocation("a")); + Assert.Throws("originalPath", () => loader.AddDependencyLocation(null!)); + Assert.Throws("originalPath", () => loader.AddDependencyLocation("a")); }); } @@ -327,30 +327,22 @@ public void AssemblyLoading_Multiple(AnalyzerTestKind kind) } /// - /// The loaders should not actually look at the contents of the disk until a - /// call has occurred. This is historical behavior that doesn't have a clear reason for existing. There - /// is strong suspicion it's to delay loading of analyzers until absolutely necessary. As such we're - /// enshrining the behavior here so it is not _accidentally_ changed. + /// The loaders should not _require_ contents to actually be on disk until the + /// call has occurred. If the file + /// contents were required immediately then would throw in its ctor + /// rather than when using the reference. /// [Theory] [CombinatorialData] - public void AssemblyLoading_OverwriteBeforeLoad(AnalyzerTestKind kind) + public void AssemblyLoading_AssemblyLocationInvalid(AnalyzerTestKind kind) { Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); - var delta1Copy = tempDir.CreateDirectory("a").CreateFile("Delta.dll").CopyContentFrom(testFixture.Delta1).Path; - loader.AddDependencyLocation(delta1Copy); - File.Copy(testFixture.Delta2, delta1Copy, overwrite: true); - var assembly = loader.LoadFromPath(delta1Copy); - - var name = AssemblyName.GetAssemblyName(testFixture.Delta2); - Assert.Equal(name.FullName, assembly.GetName().FullName); - - VerifyDependencyAssemblies( - loader, - delta1Copy); + var analyzerPath = Path.Combine(tempDir.CreateDirectory("a").Path, "analyzer.dll"); + loader.AddDependencyLocation(analyzerPath); + Assert.Throws(() => loader.LoadFromPath(analyzerPath)); }); } @@ -362,7 +354,7 @@ public void AssemblyLoading_AssemblyLocationNotAdded(AnalyzerTestKind kind) { loader.AddDependencyLocation(testFixture.Gamma); loader.AddDependencyLocation(testFixture.Delta1); - Assert.Throws(() => loader.LoadFromPath(testFixture.Beta)); + Assert.Throws(() => loader.LoadFromPath(testFixture.Beta)); }); } @@ -370,7 +362,7 @@ public void AssemblyLoading_AssemblyLocationNotAdded(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_DependencyLocationNotAdded(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { StringBuilder sb = new StringBuilder(); @@ -381,7 +373,7 @@ public void AssemblyLoading_DependencyLocationNotAdded(AnalyzerTestKind kind) var b = beta.CreateInstance("Beta.B")!; var writeMethod = b.GetType().GetMethod("Write")!; - if (ExecutionConditionUtil.IsCoreClr || loader is ShadowCopyAnalyzerAssemblyLoader) + if (ExecutionConditionUtil.IsCoreClr || state is AnalyzerTestKind.ShadowLoad) { // We don't pass Alpha's path to AddDependencyLocation here, and therefore expect // calling Beta.B.Write to fail because loader will prevent the load of Alpha @@ -408,16 +400,27 @@ private static void VerifyAssemblies(AnalyzerAssemblyLoader loader, IEnumerable< private static void VerifyAssemblies(AnalyzerAssemblyLoader loader, IEnumerable assemblies, int? expectedCopyCount, params (string simpleName, string version, string path)[] expected) { - Assert.Equal( - expected - .Select(x => (x.simpleName, x.version, getExpectedLoadPath(x.path))) - .OrderBy(static x => x) - .ToArray(), - assemblies.Select(assembly => (assembly.GetName().Name!, assembly.GetName().Version!.ToString(), assembly.Location)) + var expectedVersions = expected + .Select(x => $"{x.simpleName} {x.version}") + .OrderBy(static x => x) + .ToArray(); + var assemblyVersions = assemblies + .Select(assembly => $"{assembly.GetName().Name!} {assembly.GetName().Version}") .OrderBy(static x => x) - .ToArray()); + .ToArray(); + Assert.Equal(expectedVersions, assemblyVersions); + + var expectedPaths = expected + .Select(x => getExpectedLoadPath(x.path)) + .OrderBy(static x => x) + .ToArray(); + var assemblyPaths = assemblies + .Select(x => x.Location) + .OrderBy(static x => x) + .ToArray(); + Assert.Equal(expectedPaths, assemblyPaths); - if (loader is ShadowCopyAnalyzerAssemblyLoader shadowLoader) + if (loader.AnalyzerPathResolvers.OfType().FirstOrDefault() is { } shadowLoader) { Assert.All(assemblies, x => x.Location.StartsWith(shadowLoader.BaseDirectory, StringComparison.Ordinal)); Assert.Equal(expectedCopyCount ?? expected.Length, shadowLoader.CopyCount); @@ -426,7 +429,7 @@ private static void VerifyAssemblies(AnalyzerAssemblyLoader loader, IEnumerable< string getExpectedLoadPath(string path) { #if NET - if (loader is AnalyzerAssemblyLoader { AnalyzerLoadOption: AnalyzerLoadOption.LoadFromStream }) + if (loader.AnalyzerAssemblyResolvers.Any(x => x == AnalyzerAssemblyLoader.StreamAnalyzerAssemblyResolver)) { return ""; } @@ -434,14 +437,14 @@ string getExpectedLoadPath(string path) if (path.EndsWith(".resources.dll", StringComparison.Ordinal)) { - return getRealSatelliteLoadPath(path) ?? ""; + return getRealSatellitePath(path) ?? ""; } - return loader.GetRealAnalyzerLoadPath(path ?? ""); + return loader.GetResolvedAnalyzerPath(path ?? ""); } // When PreparePathToLoad is overridden this returns the most recent // real path for the given analyzer satellite assembly path - string? getRealSatelliteLoadPath(string originalSatelliteFullPath) + string? getRealSatellitePath(string originalSatelliteFullPath) { // This is a satellite assembly, need to find the mapped path of the real assembly, then // adjust that mapped path for the suffix of the satellite assembly @@ -458,7 +461,7 @@ string getExpectedLoadPath(string path) // Real assembly is located in the directory above this one var assemblyPath = Path.Combine(assemblyDir, assemblyFileName); - return loader.GetRealSatelliteLoadPath(assemblyPath, cultureInfo); + return loader.GetResolvedSatellitePath(assemblyPath, cultureInfo); } } @@ -495,6 +498,13 @@ private static void VerifyDependencyAssemblies(AnalyzerAssemblyLoader loader, in .GetAssemblies() .Where(x => isInLoadFromContext(loader, x)); + // When debugging, the debugger will load this DLL and that can throw off the debugging + // session so exclude it here. + if (Debugger.IsAttached) + { + loadedAssemblies = loadedAssemblies.Where(x => x.GetName().Name != "Microsoft.VisualStudio.Debugger.Runtime.Desktop"); + } + static bool isInLoadFromContext(AnalyzerAssemblyLoader loader, Assembly assembly) { var undidHook = false; @@ -594,7 +604,7 @@ public void AssemblyLoading_DependencyInDifferentDirectory(AnalyzerTestKind kind [CombinatorialData] public void AssemblyLoading_RazorCompiler1(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); @@ -609,7 +619,7 @@ public void AssemblyLoading_RazorCompiler1(AnalyzerTestKind kind) // Even though EA.RazorCompiler is loaded from the compiler directory the shadow copy loader // still does a defensive copy. - var copyCount = loader is ShadowCopyAnalyzerAssemblyLoader + var copyCount = state is AnalyzerTestKind.ShadowLoad ? 1 : (int?)null; @@ -628,7 +638,7 @@ public void AssemblyLoading_RazorCompiler1(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_RazorCompiler2(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); @@ -647,7 +657,7 @@ public void AssemblyLoading_RazorCompiler2(AnalyzerTestKind kind) // Even though EA.RazorCompiler is loaded from the compiler directory the shadow copy loader // still does a defensive copy. - var copyCount = loader is ShadowCopyAnalyzerAssemblyLoader + var copyCount = state is AnalyzerTestKind.ShadowLoad ? 2 : (int?)null; VerifyDependencyAssemblies( @@ -667,7 +677,7 @@ public void AssemblyLoading_RazorCompiler2(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_DependencyInDifferentDirectory2(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); @@ -694,22 +704,11 @@ public void AssemblyLoading_DependencyInDifferentDirectory2(AnalyzerTestKind kin Assert.Equal(@"Delta: Gamma: Test G ", actual); - if (ExecutionConditionUtil.IsDesktop && loader is ShadowCopyAnalyzerAssemblyLoader) - { - // See limitation 3 - VerifyDependencyAssemblies( - loader, - deltaFile1, - gammaFile); - } - else - { - VerifyDependencyAssemblies( - loader, - deltaFile2, - gammaFile); - - } + VerifyDependencyAssemblies( + loader, + copyCount: 3, + deltaFile2, + gammaFile); }); } @@ -765,8 +764,8 @@ public void AssemblyLoading_DependencyInDifferentDirectory4(AnalyzerTestKind kin var analyzerDependencyReference = new AnalyzerFileReference(analyzerDependencyFile, loader); analyzerDependencyReference.AnalyzerLoadFailed += (_, e) => AssertEx.Fail(e.Exception!.Message); - Assert.True(loader.IsAnalyzerDependencyPath(analyzerMainFile)); - Assert.True(loader.IsAnalyzerDependencyPath(analyzerDependencyFile)); + Assert.NotNull(loader.GetResolvedAnalyzerPath(analyzerMainFile)); + Assert.NotNull(loader.GetResolvedAnalyzerPath(analyzerDependencyFile)); var analyzers = analyzerMainReference.GetAnalyzersForAllLanguages(); Assert.Equal(1, analyzers.Length); @@ -842,7 +841,7 @@ public void AssemblyLoading_MultipleVersions(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_MultipleVersions_NoExactMatch(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { StringBuilder sb = new StringBuilder(); @@ -855,7 +854,7 @@ public void AssemblyLoading_MultipleVersions_NoExactMatch(AnalyzerTestKind kind) e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" }); var actual = sb.ToString(); - if (ExecutionConditionUtil.IsCoreClr || loader is ShadowCopyAnalyzerAssemblyLoader) + if (ExecutionConditionUtil.IsCoreClr || state is AnalyzerTestKind.ShadowLoad) { // In .NET Core we have _full_ control over assembly loading and can prevent implicit // loads from probing paths. That means we can avoid implicitly loading the Delta v2 @@ -880,7 +879,7 @@ public void AssemblyLoading_MultipleVersions_NoExactMatch(AnalyzerTestKind kind) { // See limitation 1 // The Epsilon.dll has Delta.dll (v2) next to it in the directory. - Assert.Throws(() => loader.GetRealAnalyzerLoadPath(testFixture.Delta2)); + Assert.Throws(() => loader.GetResolvedAnalyzerPath(testFixture.Delta2)); // Fake the dependency so we can verify the rest of the load loader.AddDependencyLocation(testFixture.Delta2); @@ -901,7 +900,7 @@ public void AssemblyLoading_MultipleVersions_NoExactMatch(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_MultipleVersions_MultipleEqualMatches(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { StringBuilder sb = new StringBuilder(); @@ -913,37 +912,19 @@ public void AssemblyLoading_MultipleVersions_MultipleEqualMatches(AnalyzerTestKi var e = epsilon.CreateInstance("Epsilon.E")!; e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" }); - if (ExecutionConditionUtil.IsDesktop && loader is ShadowCopyAnalyzerAssemblyLoader) - { - // Delta2B and Delta2 have the same version, but we prefer Delta2B because it's added first and - // in shadow loader we can't fall back to same directory because the runtime doesn't provide - // context for who requested the load. Just have to go to best version. - VerifyDependencyAssemblies( - loader, - testFixture.Delta2B, - testFixture.Epsilon); - - var actual = sb.ToString(); - Assert.Equal( - @"Delta.2B: Epsilon: Test E -", - actual); - } - else - { - // See limitation 1 - // Delta2B and Delta2 have the same version, but we prefer Delta2 because it's in the same directory as Epsilon. - VerifyDependencyAssemblies( - loader, - testFixture.Delta2, - testFixture.Epsilon); + // See limitation 1 + // Delta2B and Delta2 have the same version, but we prefer Delta2 because it's in the same directory as Epsilon. + VerifyDependencyAssemblies( + loader, + copyCount: 3, + testFixture.Delta2, + testFixture.Epsilon); - var actual = sb.ToString(); - Assert.Equal( - @"Delta.2: Epsilon: Test E + var actual = sb.ToString(); + Assert.Equal( +@"Delta.2: Epsilon: Test E ", - actual); - } + actual); }); } @@ -951,7 +932,7 @@ public void AssemblyLoading_MultipleVersions_MultipleEqualMatches(AnalyzerTestKi [CombinatorialData] public void AssemblyLoading_MultipleVersions_MultipleVersionsOfSameAnalyzerItself(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { StringBuilder sb = new StringBuilder(); @@ -966,11 +947,11 @@ public void AssemblyLoading_MultipleVersions_MultipleVersionsOfSameAnalyzerItsel #if NET // On Core, we're able to load both of these into separate AssemblyLoadContexts. - if (loader.AnalyzerLoadOption == AnalyzerLoadOption.LoadFromDisk) + if (state is AnalyzerTestKind.LoadDirect) { Assert.NotEqual(delta2B.Location, delta2.Location); - Assert.Equal(loader.GetRealAnalyzerLoadPath(testFixture.Delta2), delta2.Location); - Assert.Equal(loader.GetRealAnalyzerLoadPath(testFixture.Delta2B), delta2B.Location); + Assert.Equal(loader.GetResolvedAnalyzerPath(testFixture.Delta2), delta2.Location); + Assert.Equal(loader.GetResolvedAnalyzerPath(testFixture.Delta2B), delta2B.Location); } #else @@ -988,50 +969,58 @@ public void AssemblyLoading_MultipleVersions_MultipleVersionsOfSameAnalyzerItsel [CombinatorialData] public void AssemblyLoading_MultipleVersions_ExactAndGreaterMatch(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { - StringBuilder sb = new StringBuilder(); + using var temp = new TempRoot(); + var tempDir = temp.CreateDirectory(); - loader.AddDependencyLocation(testFixture.Delta2B); - loader.AddDependencyLocation(testFixture.Delta3); - loader.AddDependencyLocation(testFixture.Epsilon); + // This test is about validating how dependencies resolve when there are multiple versions + // on disk with some registered and some not-registered. In this case Epislon has a dependency + // on Delta2. - Assembly epsilon = loader.LoadFromPath(testFixture.Epsilon); + var dir1 = tempDir.CreateDirectory("1"); + var unregisteredDeltaPath = dir1.CopyFile(testFixture.Delta2).Path; + var epsilonPath = dir1.CopyFile(testFixture.Epsilon).Path; + + var dir2 = tempDir.CreateDirectory("2"); + var registeredDeltaPath = dir2.CopyFile(testFixture.Delta2).Path; + + loader.AddDependencyLocation(registeredDeltaPath); + loader.AddDependencyLocation(epsilonPath); + + Assembly epsilon = loader.LoadFromPath(epsilonPath); + StringBuilder sb = new StringBuilder(); var e = epsilon.CreateInstance("Epsilon.E")!; e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" }); + Assert.Equal( + @"Delta.2: Epsilon: Test E +", + sb.ToString()); - var actual = sb.ToString(); - if (ExecutionConditionUtil.IsCoreClr || loader is ShadowCopyAnalyzerAssemblyLoader) + if (ExecutionConditionUtil.IsCoreClr || state is AnalyzerTestKind.ShadowLoad) { - // This works in CoreClr because we have full control over assembly loading. It - // works in shadow copy because all the DLLs are put into different directories - // so everything is a AppDomain.AssemblyResolve event and we get full control there. + // This works in CoreClr because we have full control over assembly loading. + // This works in ShadowLoad because the unregistered dependency is not copied hence can't be + // implicitly loaded VerifyDependencyAssemblies( loader, - testFixture.Delta2B, - testFixture.Epsilon); - - Assert.Equal( - @"Delta.2B: Epsilon: Test E -", - actual); + copyCount: 2, + registeredDeltaPath, + epsilonPath); } else { - // See limitation 2 - Assert.Throws(() => loader.GetRealAnalyzerLoadPath(testFixture.Delta2)); + // On desktop without shadow load then the desktop loader will grab the unregistered + // dependency because it's in the same directory as the main assembly and LoadFrom + // rules will pick it without a chance to intervene - // Fake the dependency so we can verify the rest of the load - loader.AddDependencyLocation(testFixture.Delta2); + // Add the dependency location just so we can run the verify below + loader.AddDependencyLocation(unregisteredDeltaPath); VerifyDependencyAssemblies( loader, - testFixture.Delta2, - testFixture.Epsilon); - - Assert.Equal( - @"Delta.2: Epsilon: Test E -", - actual); + copyCount: 2, + unregisteredDeltaPath, + epsilonPath); } }); } @@ -1040,14 +1029,14 @@ public void AssemblyLoading_MultipleVersions_ExactAndGreaterMatch(AnalyzerTestKi [CombinatorialData] public void AssemblyLoading_MultipleVersions_WorseMatchInSameDirectory(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); StringBuilder sb = new StringBuilder(); var tempDir = temp.CreateDirectory(); - var tempDir1 = tempDir.CreateDirectory("a"); - var tempDir2 = tempDir.CreateDirectory("b"); + var tempDir1 = tempDir.CreateDirectory("1"); + var tempDir2 = tempDir.CreateDirectory("2"); var epsilonFile = tempDir1.CreateFile("Epsilon.dll").CopyContentFrom(testFixture.Epsilon).Path; var delta1File = tempDir1.CreateFile("Delta.dll").CopyContentFrom(testFixture.Delta1).Path; var delta2File = tempDir2.CreateFile("Delta.dll").CopyContentFrom(testFixture.Delta2).Path; @@ -1060,38 +1049,18 @@ public void AssemblyLoading_MultipleVersions_WorseMatchInSameDirectory(AnalyzerT var e = epsilon.CreateInstance("Epsilon.E")!; e.GetType().GetMethod("Write")!.Invoke(e, new object[] { sb, "Test E" }); - if (ExecutionConditionUtil.IsDesktop && loader is ShadowCopyAnalyzerAssemblyLoader) - { - // In desktop + shadow load the dependencies are in different directories with - // no context available when the load for Delta comes in. So we pick the best - // option. - // Epsilon wants Delta2, but since Delta1 is in the same directory, we prefer Delta1 over Delta2. - VerifyDependencyAssemblies( - loader, - copyCount: 3, - delta2File, - epsilonFile); - - var actual = sb.ToString(); - Assert.Equal( - @"Delta.2: Epsilon: Test E -", - actual); - } - else - { - // See limitation 2 - VerifyDependencyAssemblies( - loader, - delta1File, - epsilonFile); + // See limitation 2 + VerifyDependencyAssemblies( + loader, + copyCount: 3, + delta1File, + epsilonFile); - var actual = sb.ToString(); - Assert.Equal( - @"Delta: Epsilon: Test E + var actual = sb.ToString(); + Assert.Equal( + @"Delta: Epsilon: Test E ", - actual); - } + actual); }); } @@ -1106,7 +1075,7 @@ public void AssemblyLoading_MultipleVersions_MultipleLoaders(AnalyzerTestKind ki loader1.AddDependencyLocation(testFixture.Gamma); loader1.AddDependencyLocation(testFixture.Delta1); - var loader2 = new DefaultAnalyzerAssemblyLoader(); + var loader2 = new AnalyzerAssemblyLoader(); loader2.AddDependencyLocation(testFixture.Epsilon); loader2.AddDependencyLocation(testFixture.Delta2); @@ -1484,7 +1453,7 @@ public void AssemblyLoading_NativeDependency(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_DeleteAfterLoad1(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); @@ -1492,7 +1461,7 @@ public void AssemblyLoading_DeleteAfterLoad1(AnalyzerTestKind kind) loader.AddDependencyLocation(deltaCopy); _ = loader.LoadFromPath(deltaCopy); - if (loader is ShadowCopyAnalyzerAssemblyLoader || !ExecutionConditionUtil.IsWindows) + if (state is AnalyzerTestKind.ShadowLoad || !ExecutionConditionUtil.IsWindows) { File.Delete(deltaCopy); } @@ -1507,7 +1476,7 @@ public void AssemblyLoading_DeleteAfterLoad1(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_DeleteAfterLoad2(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); StringBuilder sb = new StringBuilder(); @@ -1517,7 +1486,7 @@ public void AssemblyLoading_DeleteAfterLoad2(AnalyzerTestKind kind) loader.AddDependencyLocation(deltaCopy); Assembly? delta = loader.LoadFromPath(deltaCopy); - if (loader is ShadowCopyAnalyzerAssemblyLoader || !ExecutionConditionUtil.IsWindows) + if (state is AnalyzerTestKind.ShadowLoad || !ExecutionConditionUtil.IsWindows) { File.Delete(deltaCopy); } @@ -1538,7 +1507,7 @@ public void AssemblyLoading_DeleteAfterLoad2(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoading_DeleteAfterLoad3(AnalyzerTestKind kind) { - Run(kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + Run(kind, state: kind, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { using var temp = new TempRoot(); var tempDir = temp.CreateDirectory(); @@ -1561,7 +1530,7 @@ public void AssemblyLoading_DeleteAfterLoad3(AnalyzerTestKind kind) var writeMethod = b.GetType().GetMethod("Write")!; writeMethod.Invoke(b, new object[] { sb, "Test G" }); - if (loader is ShadowCopyAnalyzerAssemblyLoader) + if (state is AnalyzerTestKind.ShadowLoad) { File.Delete(delta1File); File.Delete(delta2File); @@ -1608,9 +1577,10 @@ public void AssemblyLoading_RepeatedLoads2(AnalyzerTestKind kind) loader.AddDependencyLocation(path); var expected = loader.LoadFromPath(path); + var shadowLoader = loader.AnalyzerPathResolvers.OfType().FirstOrDefault(); for (var i = 0; i < 5; i++) { - if (loader is ShadowCopyAnalyzerAssemblyLoader) + if (shadowLoader is not null) { File.WriteAllBytes(path, new byte[] { 42 }); } @@ -1619,7 +1589,7 @@ public void AssemblyLoading_RepeatedLoads2(AnalyzerTestKind kind) Assert.Same(expected, actual); } - if (loader is ShadowCopyAnalyzerAssemblyLoader shadowLoader) + if (shadowLoader is not null) { // Ensure that despite the on disk changes only one shadow copy occurred Assert.Equal(1, shadowLoader.CopyCount); @@ -1649,7 +1619,7 @@ public void AssemblyLoading_Resources(AnalyzerTestKind kind) // The copy count is 1 here as only one real assembly was copied, the resource // dlls don't apply for this count. - VerifyDependencyAssemblies(loader, copyCount: 1, analyzerPath, analyzerResourcesPath); + VerifyDependencyAssemblies(loader, copyCount: 2, analyzerPath, analyzerResourcesPath); }); } @@ -1670,9 +1640,7 @@ public void AssemblyLoading_ResourcesInParent(AnalyzerTestKind kind) .GetMethod("Exec", BindingFlags.Static | BindingFlags.Public)!; methodInfo.Invoke(null, ["es-ES"]); - // The copy count is 1 here as only one real assembly was copied, the resource - // dlls don't apply for this count. - VerifyDependencyAssemblies(loader, copyCount: 1, analyzerPath, analyzerResourcesPath); + VerifyDependencyAssemblies(loader, copyCount: 2, analyzerPath, analyzerResourcesPath); }); } @@ -1682,36 +1650,43 @@ public void AssemblyLoading_ResourcesInParent(AnalyzerTestKind kind) [CombinatorialData] public void AssemblyLoadingInNonDefaultContext_AnalyzerReferencesSystemCollectionsImmutable(AnalyzerTestKind kind) { - Run(kind, - static (AssemblyLoadContext compilerContext, AssemblyLoadTestFixture testFixture) => - { - // Load the compiler assembly and a modified version of S.C.I into the compiler load context. We - // expect the analyzer will use the bogus S.C.I in the compiler context instead of the one - // in the host context. - _ = compilerContext.LoadFromAssemblyPath(testFixture.UserSystemCollectionsImmutable); - _ = compilerContext.LoadFromAssemblyPath(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly.Location); - }, - static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => - { - StringBuilder sb = new StringBuilder(); + // Load the compiler assembly and a modified version of S.C.I into the compiler load context. We + // expect the analyzer will use the bogus S.C.I in the compiler context instead of the one + // in the host context. + var alc = new AssemblyLoadContext(nameof(AssemblyResolver_FirstOneWins), isCollectible: true); + _ = alc.LoadFromAssemblyPath(TestFixture.UserSystemCollectionsImmutable); + _ = alc.LoadFromAssemblyPath(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly.Location); + var loader = kind switch + { + AnalyzerTestKind.LoadStream => new AnalyzerAssemblyLoader([], [AnalyzerAssemblyLoader.StreamAnalyzerAssemblyResolver], alc), + AnalyzerTestKind.LoadDirect => new AnalyzerAssemblyLoader([], [AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver], alc), + AnalyzerTestKind.ShadowLoad => new AnalyzerAssemblyLoader([new ShadowCopyAnalyzerPathResolver(Temp.CreateDirectory().Path)], [AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver], alc), + _ => throw ExceptionUtilities.UnexpectedValue(kind) + }; - loader.AddDependencyLocation(testFixture.UserSystemCollectionsImmutable); - loader.AddDependencyLocation(testFixture.AnalyzerReferencesSystemCollectionsImmutable1); + Run(loader, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + StringBuilder sb = new StringBuilder(); - Assembly analyzerAssembly = loader.LoadFromPath(testFixture.AnalyzerReferencesSystemCollectionsImmutable1); - var analyzer = analyzerAssembly.CreateInstance("Analyzer")!; - analyzer.GetType().GetMethod("Method")!.Invoke(analyzer, new object[] { sb }); + loader.AddDependencyLocation(testFixture.UserSystemCollectionsImmutable); + loader.AddDependencyLocation(testFixture.AnalyzerReferencesSystemCollectionsImmutable1); - Assert.Equal("42", sb.ToString()); - }); + Assembly analyzerAssembly = loader.LoadFromPath(testFixture.AnalyzerReferencesSystemCollectionsImmutable1); + var analyzer = analyzerAssembly.CreateInstance("Analyzer")!; + analyzer.GetType().GetMethod("Method")!.Invoke(analyzer, new object[] { sb }); + + Assert.Equal("42", sb.ToString()); + }); + + alc.Unload(); } #endif [Theory] [CombinatorialData] - public void ExternalResolver_CanIntercept_ReturningNull(AnalyzerTestKind kind) + public void PathResolver_CanIntercept_ReturningNull(AnalyzerTestKind kind) { - var resolver = new TestAnalyzerAssemblyResolver(n => null); + var resolver = new TestAnalyzerPathResolver(n => null); Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => { loader.AddDependencyLocation(testFixture.Delta1); @@ -1719,60 +1694,37 @@ public void ExternalResolver_CanIntercept_ReturningNull(AnalyzerTestKind kind) Assert.NotNull(delta); VerifyDependencyAssemblies(loader, testFixture.Delta1); - }, externalResolvers: [resolver]); - Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Delta", a.Name))); - } - - [Theory] - [CombinatorialData] - public void ExternalResolver_CanIntercept_ReturningAssembly(AnalyzerTestKind kind) - { - var resolver = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); - Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => - { - // net core assembly loader checks that the resolved assembly name is the same as the requested one - // so we use the assembly the tests are contained in as its already be loaded - var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; - loader.AddDependencyLocation(thisAssembly.Location); - Assembly loaded = loader.LoadFromPath(thisAssembly.Location); - Assert.Equal(thisAssembly, loaded); - - }, externalResolvers: [resolver]); - Assert.Collection(resolver.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + }, pathResolvers: [resolver]); + Assert.Equal([TestFixture.Delta1], resolver.CalledFor); } [Theory] [CombinatorialData] - public void ExternalResolver_CanIntercept_ReturningAssembly_Or_Null(AnalyzerTestKind kind) + public void PathResolver_CanIntercept_ReturningAssembly_Or_Null(AnalyzerTestKind kind) { - var thisAssemblyName = GetType().Assembly.GetName(); - var resolver = new TestAnalyzerAssemblyResolver(n => n == thisAssemblyName ? GetType().Assembly : null); + var resolver1 = new TestAnalyzerPathResolver(n => n == TestFixture.Alpha ? n : null); + var resolver2 = new TestAnalyzerPathResolver(n => n); Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => { - var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; - loader.AddDependencyLocation(testFixture.Alpha); Assembly alpha = loader.LoadFromPath(testFixture.Alpha); Assert.NotNull(alpha); - loader.AddDependencyLocation(thisAssembly.Location); - Assembly loaded = loader.LoadFromPath(thisAssembly.Location); - Assert.Equal(thisAssembly, loaded); - loader.AddDependencyLocation(testFixture.Delta1); Assembly delta = loader.LoadFromPath(testFixture.Delta1); Assert.NotNull(delta); - }, externalResolvers: [resolver]); - Assert.Collection(resolver.CalledFor, (a => Assert.Equal("Alpha", a.Name)), a => Assert.Equal(thisAssemblyName.Name, a.Name), a => Assert.Equal("Delta", a.Name)); + }, pathResolvers: [resolver1, resolver2]); + + Assert.Equal([TestFixture.Alpha, TestFixture.Delta1], resolver1.CalledFor); } [Theory] [CombinatorialData] - public void ExternalResolver_MultipleResolvers_CanIntercept_ReturningNull(AnalyzerTestKind kind) + public void PathResolver_MultipleResolvers_CanIntercept_ReturningNull(AnalyzerTestKind kind) { - var resolver1 = new TestAnalyzerAssemblyResolver(n => null); - var resolver2 = new TestAnalyzerAssemblyResolver(n => null); + var resolver1 = new TestAnalyzerPathResolver(n => null); + var resolver2 = new TestAnalyzerPathResolver(n => null); Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => { loader.AddDependencyLocation(testFixture.Delta1); @@ -1780,41 +1732,106 @@ public void ExternalResolver_MultipleResolvers_CanIntercept_ReturningNull(Analyz Assert.NotNull(delta); VerifyDependencyAssemblies(loader, testFixture.Delta1); - }, externalResolvers: [resolver1, resolver2]); - Assert.Collection(resolver1.CalledFor, (a => Assert.Equal("Delta", a.Name))); - Assert.Collection(resolver2.CalledFor, (a => Assert.Equal("Delta", a.Name))); + }, pathResolvers: [resolver1, resolver2]); + Assert.Equal([TestFixture.Delta1], resolver1.CalledFor); + Assert.Equal([TestFixture.Delta1], resolver2.CalledFor); } +#if NET + [Theory] [CombinatorialData] - public void ExternalResolver_MultipleResolvers_ResolutionStops_AfterFirstResolve(AnalyzerTestKind kind) + public void AssemblyResolver_CanIntercept_Identity(AnalyzerTestKind kind) { - var resolver1 = new TestAnalyzerAssemblyResolver(n => GetType().Assembly); - var resolver2 = new TestAnalyzerAssemblyResolver(n => null); - Run(kind, (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + var assembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; + var resolver = new TestAnalyzerAssemblyResolver((_, _, assemblyName, _) => assemblyName.Name == assembly.GetName().Name ? assembly : null); + Run(kind, state: assembly, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => { - var thisAssembly = typeof(AnalyzerAssemblyLoaderTests).Assembly; - loader.AddDependencyLocation(thisAssembly.Location); - Assembly loaded = loader.LoadFromPath(thisAssembly.Location); - Assert.Equal(thisAssembly, loaded); + // net core assembly loader checks that the resolved assembly name is the same as the requested one + // so we use the assembly the tests are contained in as its already be loaded + var assembly = (Assembly)state; + loader.AddDependencyLocation(assembly.Location); + Assembly loaded = loader.LoadFromPath(assembly.Location); + Assert.Same(assembly, loaded); + }, assemblyResolvers: [resolver, AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver]); + } - }, externalResolvers: [resolver1, resolver2]); - Assert.Collection(resolver1.CalledFor, (a => Assert.Equal(GetType().Assembly.GetName().Name, a.Name))); + [Fact] + public void AssemblyResolver_FirstOneWins() + { + var alc = new AssemblyLoadContext(nameof(AssemblyResolver_FirstOneWins), isCollectible: true); + var name = Path.GetFileNameWithoutExtension(TestFixture.Delta1); + var resolver1 = new TestAnalyzerAssemblyResolver((_, assemblyName, current, _) => + assemblyName.Name == name ? current.LoadFromAssemblyPath(TestFixture.Delta1) : null); + var resolver2 = new TestAnalyzerAssemblyResolver((_, _, assemblyName, _) => null); + var loader = new AnalyzerAssemblyLoader([], [resolver1, resolver2], alc); + + Run(loader, state: name, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture, object state) => + { + // net core assembly loader checks that the resolved assembly name is the same as the requested one + // so we use the assembly the tests are contained in as its already be loaded + loader.AddDependencyLocation(testFixture.Delta1); + Assembly loaded = loader.LoadFromPath(testFixture.Delta1); + Assert.Equal((string)state, loaded.GetName().Name); + }); + + Assert.Equal([name], resolver1.CalledFor.Select(x => x.Name)); Assert.Empty(resolver2.CalledFor); + alc.Unload(); + } + + [Fact] + public void AssemblyResolver_ThrowOnNoMatch() + { + var name = Path.GetFileNameWithoutExtension(TestFixture.Delta1); + var alc = new AssemblyLoadContext(nameof(AssemblyResolver_FirstOneWins), isCollectible: true); + var resolver = new TestAnalyzerAssemblyResolver((_, _, assemblyName, _) => null); + var loader = new AnalyzerAssemblyLoader([], [resolver], alc); + + Run(loader, static (AnalyzerAssemblyLoader loader, AssemblyLoadTestFixture testFixture) => + { + // net core assembly loader checks that the resolved assembly name is the same as the requested one + // so we use the assembly the tests are contained in as its already be loaded + loader.AddDependencyLocation(testFixture.Delta1); + Assert.Throws(() => loader.LoadFromPath(testFixture.Delta1)); + }); + + Assert.Equal([Path.GetFileNameWithoutExtension(TestFixture.Delta1)], resolver.CalledFor.Select(x => x.Name)); } +#endif - [Serializable] - private class TestAnalyzerAssemblyResolver(Func func) : MarshalByRefObject, IAnalyzerAssemblyResolver + private class TestAnalyzerPathResolver(Func getRealFilePathFunc) : MarshalByRefObject, IAnalyzerPathResolver { - private readonly Func _func = func; + private readonly Func _getRealFilePathFunc = getRealFilePathFunc; + + public List CalledFor { get; } = []; + + public bool IsAnalyzerPathHandled(string originalPath) + { + CalledFor.Add(originalPath); + return _getRealFilePathFunc(originalPath) is not null; + } + + public string GetResolvedAnalyzerPath(string originalAnalyzerPath) => _getRealFilePathFunc(originalAnalyzerPath)!; + + public string? GetResolvedSatellitePath(string originalAnalyzerPath, CultureInfo cultureInfo) => null; + } + +#if NET + + private class TestAnalyzerAssemblyResolver(Func resolveFunc) : IAnalyzerAssemblyResolver + { + private readonly Func _resolveFunc = resolveFunc; public List CalledFor { get; } = []; - public Assembly? ResolveAssembly(AssemblyName assemblyName, string rootDirectory) + public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) { CalledFor.Add(assemblyName); - return _func(assemblyName); + return _resolveFunc(loader, assemblyName, directoryContext, directory); } } + +#endif } } diff --git a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceAppDomainTests.cs b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceAppDomainTests.cs index f79eaa292b2c4..2c5a72490f256 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceAppDomainTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceAppDomainTests.cs @@ -26,7 +26,7 @@ public override object InitializeLifetimeService() public Exception LoadAnalyzer(string shadowPath, string analyzerPath) { - var loader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(shadowPath); + var loader = AnalyzerAssemblyLoader.CreateNonLockingLoader(shadowPath, []); Exception analyzerLoadException = null; var analyzerRef = new AnalyzerFileReference(analyzerPath, loader); analyzerRef.AnalyzerLoadFailed += (s, e) => analyzerLoadException = e.Exception; diff --git a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs index 700e0a6b64f8d..d03535c875e2e 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs @@ -29,7 +29,7 @@ private AssemblyLoadTestFixtureCollection() { } [Collection(AssemblyLoadTestFixtureCollection.Name)] public class AnalyzerFileReferenceTests : TestBase { - private static readonly AnalyzerAssemblyLoader s_analyzerLoader = new DefaultAnalyzerAssemblyLoader(); + private static readonly AnalyzerAssemblyLoader s_analyzerLoader = new AnalyzerAssemblyLoader(); private readonly AssemblyLoadTestFixture _testFixture; public AnalyzerFileReferenceTests(AssemblyLoadTestFixture testFixture) { @@ -319,13 +319,13 @@ public void AssemblyLoading_ReferencesLaterFakeCompiler_EndToEnd_CSharp() args: new[] { "/nologo", $@"/analyzer:""{_testFixture.AnalyzerWithLaterFakeCompilerDependency}""", "/nostdlib", $@"/r:""{corlib}""", "/out:something.dll", source.Path }, new BuildPaths(clientDir: directory.Path, workingDir: directory.Path, sdkDir: null, tempDir: null), additionalReferenceDirectories: null, - new DefaultAnalyzerAssemblyLoader()); + new AnalyzerAssemblyLoader()); var writer = new StringWriter(); var result = compiler.Run(writer); Assert.Equal(0, result); AssertEx.Equal($""" - warning CS9057: The analyzer assembly '{_testFixture.AnalyzerWithLaterFakeCompilerDependency}' references version '100.0.0.0' of the compiler, which is newer than the currently running version '{typeof(DefaultAnalyzerAssemblyLoader).Assembly.GetName().Version}'. + warning CS9057: Analyzer assembly '{_testFixture.AnalyzerWithLaterFakeCompilerDependency}' cannot be used because it references version '100.0.0.0' of the compiler, which is newer than the currently running version '42.42.42.42'. in.cs(1,5): warning CS0219: The variable 'x' is assigned but its value is never used """, writer.ToString()); @@ -348,7 +348,7 @@ public void DuplicateAnalyzerReference() args: new[] { "/nologo", $@"/analyzer:""{_testFixture.AnalyzerWithFakeCompilerDependency}""", $@"/analyzer:""{_testFixture.AnalyzerWithFakeCompilerDependency}""", "/nostdlib", $@"/r:""{corlib}""", "/out:something.dll", source.Path }, new BuildPaths(clientDir: directory.Path, workingDir: directory.Path, sdkDir: null, tempDir: null), additionalReferenceDirectories: null, - new DefaultAnalyzerAssemblyLoader()); + new AnalyzerAssemblyLoader()); var writer = new StringWriter(); var result = compiler.Run(writer); diff --git a/src/Compilers/Core/CodeAnalysisTest/CompilerAnalyzerAssemblyResolverTests.cs b/src/Compilers/Core/CodeAnalysisTest/CompilerAnalyzerAssemblyResolverTests.cs deleted file mode 100644 index 262e903666513..0000000000000 --- a/src/Compilers/Core/CodeAnalysisTest/CompilerAnalyzerAssemblyResolverTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#if NET -using System.Runtime.Loader; -using System.Reflection; -using Xunit; - -namespace Microsoft.CodeAnalysis.UnitTests; - -public sealed class CompilerAnalyzerAssemblyResolverTests -{ - [Fact] - public void ExceptionReturnsNull() - { - var context = new AssemblyLoadContext(nameof(ExceptionReturnsNull), isCollectible: true); - var resolver = new AnalyzerAssemblyLoader.CompilerAnalyzerAssemblyResolver(context); - var name = new AssemblyName("NotARealAssembly"); - Assert.Null(resolver.ResolveAssembly(name, directoryName: "")); - context.Unload(); - } -} -#endif diff --git a/src/Compilers/Core/CodeAnalysisTest/CompilerResolverTests.cs b/src/Compilers/Core/CodeAnalysisTest/CompilerResolverTests.cs new file mode 100644 index 0000000000000..546546ba3f649 --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/CompilerResolverTests.cs @@ -0,0 +1,59 @@ +// 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. + +#if NET +using System; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public sealed class CompilerResolverTests : IDisposable +{ + public TempRoot TempRoot { get; } + public int DefaultLoadContextCount { get; } + public AssemblyLoadContext CompilerContext { get; } + public AssemblyLoadContext ScratchContext { get; } + public Assembly AssemblyInCompilerContext { get; } + internal AnalyzerAssemblyLoader Loader { get; } + + public CompilerResolverTests() + { + TempRoot = new TempRoot(); + DefaultLoadContextCount = AssemblyLoadContext.Default.Assemblies.Count(); + CompilerContext = new AssemblyLoadContext(nameof(CompilerResolverTests), isCollectible: true); + AssemblyInCompilerContext = CompilerContext.LoadFromAssemblyPath(typeof(AnalyzerAssemblyLoader).Assembly.Location); + ScratchContext = new AssemblyLoadContext("Scratch", isCollectible: true); + Loader = new AnalyzerAssemblyLoader([], [AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver], CompilerContext); + } + + public void Dispose() + { + // This test should not pollute the default load context and hence interfere with other tests. + Assert.Equal(DefaultLoadContextCount, AssemblyLoadContext.Default.Assemblies.Count()); + CompilerContext.Unload(); + ScratchContext.Unload(); + TempRoot.Dispose(); + } + + [Fact] + public void ResolveReturnsNullForNonHostAssembly() + { + var name = new AssemblyName("NotARealAssembly"); + var assembly = Loader.CompilerAnalyzerAssemblyResolver.Resolve(Loader, name, ScratchContext, TempRoot.CreateDirectory().Path); + Assert.Null(assembly); + } + + [Fact] + public void ResolveReturnsForHostAssembly() + { + var assembly = Loader.CompilerAnalyzerAssemblyResolver.Resolve(Loader, AssemblyInCompilerContext.GetName(), ScratchContext, TempRoot.CreateDirectory().Path); + Assert.Same(AssemblyInCompilerContext, assembly); + } +} +#endif diff --git a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs index 265511935dd67..a7879283bce40 100644 --- a/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs +++ b/src/Compilers/Core/CodeAnalysisTest/InvokeUtil.cs @@ -35,7 +35,54 @@ namespace Microsoft.CodeAnalysis.UnitTests public sealed class InvokeUtil { - internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compilerContext, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers, object? state = null) + internal void Exec( + ITestOutputHelper testOutputHelper, + ImmutableArray pathResolvers, + ImmutableArray assemblyResolvers, + AssemblyLoadTestFixture fixture, + AnalyzerTestKind kind, + string typeName, + string methodName, + object? state = null) + { + using var tempRoot = new TempRoot(); + switch (kind) + { + case AnalyzerTestKind.LoadDirect: + assemblyResolvers = [.. assemblyResolvers, AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver]; + break; + case AnalyzerTestKind.LoadStream: + assemblyResolvers = [.. assemblyResolvers, AnalyzerAssemblyLoader.StreamAnalyzerAssemblyResolver]; + break; + case AnalyzerTestKind.ShadowLoad: + pathResolvers = [.. pathResolvers, new ShadowCopyAnalyzerPathResolver(tempRoot.CreateDirectory().Path)]; + assemblyResolvers = [.. assemblyResolvers, AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver]; + break; + default: + throw ExceptionUtilities.Unreachable(); + } + + var loader = new AnalyzerAssemblyLoader(pathResolvers, assemblyResolvers, compilerLoadContext: null); + var compilerContextAssemblyCount = loader.CompilerLoadContext.Assemblies.Count(); + try + { + Exec(testOutputHelper, fixture, loader, typeName, methodName, state); + } + finally + { + // When using the actual compiler load context (the one shared by all of our unit tests) the test + // did not load any additional assemblies that could interfere with later tests. + Assert.Equal(compilerContextAssemblyCount, loader.CompilerLoadContext.Assemblies.Count()); + } + } + + internal void Exec( + ITestOutputHelper testOutputHelper, + AssemblyLoadTestFixture fixture, + AnalyzerAssemblyLoader loader, + string typeName, + string methodName, + object? state = null) { // Ensure that the test did not load any of the test fixture assemblies into // the default load context. That should never happen. Assemblies should either @@ -43,16 +90,7 @@ internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compi // // Not only is this bad behavior it also pollutes future test results. var defaultContextCount = AssemblyLoadContext.Default.Assemblies.Count(); - var compilerContextCount = compilerContext.Assemblies.Count(); - using var tempRoot = new TempRoot(); - using AnalyzerAssemblyLoader loader = kind switch - { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers.ToImmutableArray()), - AnalyzerTestKind.LoadStream => new DefaultAnalyzerAssemblyLoader(compilerContext, AnalyzerLoadOption.LoadFromStream, externalResolvers.ToImmutableArray()), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(compilerContext, tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), - _ => throw ExceptionUtilities.Unreachable() - }; try { @@ -71,19 +109,18 @@ internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compi } } - if (loader is ShadowCopyAnalyzerAssemblyLoader shadowLoader) + if (loader.AnalyzerPathResolvers.OfType().FirstOrDefault() is { } shadowResolver) { - testOutputHelper.WriteLine($"Shadow loader: {shadowLoader.BaseDirectory}"); + testOutputHelper.WriteLine($"{nameof(ShadowCopyAnalyzerPathResolver)}: {shadowResolver.BaseDirectory}"); } testOutputHelper.WriteLine($"Loader path maps"); foreach (var pair in loader.GetPathMapSnapshot()) { - testOutputHelper.WriteLine($"\t{pair.OriginalAssemblyPath} -> {pair.RealAssemblyPath}"); + testOutputHelper.WriteLine($"\t{pair.OriginalAssemblyPath} -> {pair.ResolvedAssemblyPath}"); } Assert.Equal(defaultContextCount, AssemblyLoadContext.Default.Assemblies.Count()); - Assert.Equal(compilerContextCount, compilerContext.Assemblies.Count()); } } } @@ -92,16 +129,25 @@ internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadContext compi public sealed class InvokeUtil : MarshalByRefObject { - internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture fixture, AnalyzerTestKind kind, string typeName, string methodName, IAnalyzerAssemblyResolver[] externalResolvers, object? state) + internal void Exec( + ITestOutputHelper testOutputHelper, + AssemblyLoadTestFixture fixture, + AnalyzerTestKind kind, + string typeName, + string methodName, + IAnalyzerPathResolver[] pathResolvers, + object? state) { using var tempRoot = new TempRoot(); - AnalyzerAssemblyLoader loader = kind switch + pathResolvers = kind switch { - AnalyzerTestKind.LoadDirect => new DefaultAnalyzerAssemblyLoader(externalResolvers.ToImmutableArray()), - AnalyzerTestKind.ShadowLoad => new ShadowCopyAnalyzerAssemblyLoader(tempRoot.CreateDirectory().Path, externalResolvers.ToImmutableArray()), - _ => throw ExceptionUtilities.Unreachable() + AnalyzerTestKind.LoadDirect => pathResolvers, + AnalyzerTestKind.ShadowLoad => [.. pathResolvers, new ShadowCopyAnalyzerPathResolver(tempRoot.CreateDirectory().Path)], + _ => throw ExceptionUtilities.Unreachable(), }; + var loader = new AnalyzerAssemblyLoader(pathResolvers.ToImmutableArray()); + try { AnalyzerAssemblyLoaderTests.InvokeTestCode(loader, fixture, typeName, methodName, state); @@ -121,15 +167,15 @@ internal void Exec(ITestOutputHelper testOutputHelper, AssemblyLoadTestFixture f testOutputHelper.WriteLine($"\t{assembly.FullName} -> {assembly.Location}"); } - if (loader is ShadowCopyAnalyzerAssemblyLoader shadowLoader) + if (loader.AnalyzerPathResolvers.OfType().FirstOrDefault() is { } shadowResolver) { - testOutputHelper.WriteLine($"Shadow loader: {shadowLoader.BaseDirectory}"); + testOutputHelper.WriteLine($"{nameof(ShadowCopyAnalyzerPathResolver)}: {shadowResolver.BaseDirectory}"); } testOutputHelper.WriteLine($"Loader path maps"); foreach (var pair in loader.GetPathMapSnapshot()) { - testOutputHelper.WriteLine($"\t{pair.OriginalAssemblyPath} -> {pair.RealAssemblyPath}"); + testOutputHelper.WriteLine($"\t{pair.OriginalAssemblyPath} -> {pair.ResolvedAssemblyPath}"); } } } diff --git a/src/Compilers/Core/CodeAnalysisTest/ShadowCopyAnalyzerPathResolverTests.cs b/src/Compilers/Core/CodeAnalysisTest/ShadowCopyAnalyzerPathResolverTests.cs new file mode 100644 index 0000000000000..b1df8e2b8680f --- /dev/null +++ b/src/Compilers/Core/CodeAnalysisTest/ShadowCopyAnalyzerPathResolverTests.cs @@ -0,0 +1,108 @@ +// 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.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.UnitTests.Collections; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests; + +public sealed class ShadowCopyAnalyzerPathResolverTests : IDisposable +{ + public TempRoot TempRoot { get; } + internal ShadowCopyAnalyzerPathResolver PathResolver { get; } + + public ShadowCopyAnalyzerPathResolverTests() + { + TempRoot = new TempRoot(); + PathResolver = new ShadowCopyAnalyzerPathResolver(TempRoot.CreateDirectory().Path); + } + + public void Dispose() + { + TempRoot.Dispose(); + } + + [Fact] + public void IsAnalyzerPathHandled() + { + var analyzerPath = TempRoot.CreateDirectory().CreateFile("analyzer.dll").Path; + Assert.True(PathResolver.IsAnalyzerPathHandled(analyzerPath)); + } + + /// + /// Don't create the shadow directory until a copy actually happens + /// + [Fact] + public void ShadowDirectoryIsDelayCreated() + { + Assert.False(Directory.Exists(PathResolver.ShadowDirectory)); + } + + /// + /// A shadow copy of a file that doesn't exist should produce a file that doesn't exist, not throw + /// + [Fact] + public void GetRealPath_FileDoesNotExist() + { + var analyzerPath = Path.Combine(TempRoot.CreateDirectory().Path, "analyzer.dll"); + var shadowPath = PathResolver.GetResolvedAnalyzerPath(analyzerPath); + Assert.False(File.Exists(shadowPath)); + } + + [Fact] + public void GetRealPath_Copies() + { + var analyzerPath = Path.Combine(TempRoot.CreateDirectory().Path, "analyzer.dll"); + File.WriteAllText(analyzerPath, "test"); + var shadowPath = PathResolver.GetResolvedAnalyzerPath(analyzerPath); + Assert.True(File.Exists(shadowPath)); + Assert.Equal("test", File.ReadAllText(shadowPath)); + } + + /// + /// When shadow copying two files in the same directory they should end up in the same shadow + /// directory + /// + [Fact] + public void GetRealPath_FilesInSameDirectory() + { + var dir = TempRoot.CreateDirectory().Path; + var analyzer1Path = Path.Combine(dir, "analyzer1.dll"); + File.WriteAllText(analyzer1Path, "test"); + var analyzer2Path = Path.Combine(dir, "analyzer2.dll"); + File.WriteAllText(analyzer2Path, "test"); + var shadow1Path = PathResolver.GetResolvedAnalyzerPath(analyzer1Path); + var shadow2Path = PathResolver.GetResolvedAnalyzerPath(analyzer2Path); + Assert.Equal(Path.GetDirectoryName(shadow1Path), Path.GetDirectoryName(shadow2Path)); + } + + [Fact] + public void GetRealPath_GroupOnDirectory() + { + var dir = TempRoot.CreateDirectory().Path; + var group1AnalyzerPath = createAnalyzer("group1", "analyzer.dll"); + var group2AnalyzerPath = createAnalyzer("group2", "analyzer.dll"); + var group1ShadowPath = PathResolver.GetResolvedAnalyzerPath(group1AnalyzerPath); + var group2ShadowPath = PathResolver.GetResolvedAnalyzerPath(group2AnalyzerPath); + Assert.NotEqual(group1ShadowPath, group2ShadowPath); + Assert.Equal("group1-analyzer.dll", File.ReadAllText(group1ShadowPath)); + Assert.Equal("group2-analyzer.dll", File.ReadAllText(group2ShadowPath)); + + string createAnalyzer(string groupName, string name) + { + var groupDir = Path.Combine(dir, groupName, "analyzers"); + _ = Directory.CreateDirectory(groupDir); + var filePath = Path.Combine(groupDir, name); + File.WriteAllText(filePath, $"{Path.GetFileName(groupName)}-{name}"); + return filePath; + } + } +} diff --git a/src/Compilers/Core/Portable/CodeGen/SequencePointList.cs b/src/Compilers/Core/Portable/CodeGen/SequencePointList.cs index 7ca22820a4292..fa05e381022cb 100644 --- a/src/Compilers/Core/Portable/CodeGen/SequencePointList.cs +++ b/src/Compilers/Core/Portable/CodeGen/SequencePointList.cs @@ -25,7 +25,7 @@ internal class SequencePointList private SequencePointList _next; // Linked list of all points. // No sequence points. - private static readonly SequencePointList s_empty = new SequencePointList(); + internal static readonly SequencePointList Empty = new SequencePointList(); // Construct a list with no sequence points. private SequencePointList() @@ -48,7 +48,7 @@ public static SequencePointList Create(ArrayBuilder seqPointBu { if (seqPointBuilder.Count == 0) { - return SequencePointList.s_empty; + return SequencePointList.Empty; } SequencePointList first = null, current = null; diff --git a/src/Compilers/Core/Portable/Compilation/DeterministicKeyBuilder.cs b/src/Compilers/Core/Portable/Compilation/DeterministicKeyBuilder.cs index be92b6a27b567..210421fe3375f 100644 --- a/src/Compilers/Core/Portable/Compilation/DeterministicKeyBuilder.cs +++ b/src/Compilers/Core/Portable/Compilation/DeterministicKeyBuilder.cs @@ -186,7 +186,7 @@ void writeGenerators() foreach (var generator in generators) { cancellationToken.ThrowIfCancellationRequested(); - WriteType(writer, generator.GetType()); + WriteType(writer, generator.GetGeneratorType()); } writer.WriteArrayEnd(); } diff --git a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs index 85f861b6a26bc..c5b7638e2fe69 100644 --- a/src/Compilers/Core/Portable/Compilation/SemanticModel.cs +++ b/src/Compilers/Core/Portable/Compilation/SemanticModel.cs @@ -449,7 +449,7 @@ internal abstract SemanticModel ContainingPublicModelOrSelf /// scope around position is used. /// The name of the symbol to find. If null is specified then symbols /// with any names are returned. - /// Consider (reduced) extension methods. + /// Consider extension members. Classic extension methods will be returned in reduced form. /// A list of symbols that were found. If no symbols were found, an empty list is returned. /// /// The "position" is used to determine what variables are visible and accessible. Even if "container" is diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs index b1e5e659f600c..a4b6fb9a55513 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Core.cs @@ -18,43 +18,56 @@ namespace Microsoft.CodeAnalysis { - internal enum AnalyzerLoadOption + internal sealed partial class AnalyzerAssemblyLoader { - /// - /// Once the assembly path is chosen, load it directly from disk at that location - /// - LoadFromDisk, + internal static IAnalyzerAssemblyResolver DiskAnalyzerAssemblyResolver => DiskResolver.Instance; + internal static IAnalyzerAssemblyResolver StreamAnalyzerAssemblyResolver => StreamResolver.Instance; /// - /// Once the assembly path is chosen, read the contents of disk and load from memory + /// Map of resolved directory paths to load contexts that manage their assemblies. /// - /// - /// While Windows supports this option it comes with a significant performance penalty due - /// to anti virus scans. It can have a load time of 300-500ms while loading from disk - /// is generally 1-2ms. Use this with caution on Windows. - /// - LoadFromStream - } + private readonly Dictionary _loadContextByDirectory = new Dictionary(GeneratedPathComparer); - internal partial class AnalyzerAssemblyLoader - { - private readonly AssemblyLoadContext _compilerLoadContext; - private readonly Dictionary _loadContextByDirectory = new Dictionary(StringComparer.Ordinal); - private readonly AnalyzerLoadOption _loadOption; + public IAnalyzerAssemblyResolver CompilerAnalyzerAssemblyResolver { get; } + public AssemblyLoadContext CompilerLoadContext { get; } + public ImmutableArray AnalyzerAssemblyResolvers { get; } - internal AssemblyLoadContext CompilerLoadContext => _compilerLoadContext; - internal AnalyzerLoadOption AnalyzerLoadOption => _loadOption; + internal AnalyzerAssemblyLoader() + : this(pathResolvers: []) + { + } - internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) - : this(null, AnalyzerLoadOption.LoadFromDisk, externalResolvers) + internal AnalyzerAssemblyLoader(ImmutableArray pathResolvers) + : this(pathResolvers, assemblyResolvers: [DiskAnalyzerAssemblyResolver], compilerLoadContext: null) { } - internal AnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, AnalyzerLoadOption loadOption, ImmutableArray externalResolvers) + /// + /// Create a new with the given resolvers. + /// + /// This is the where the compiler resides. This parameter + /// is primarily used for testing purposes but is also useful in hosted scenarios where the compiler may be loaded outside + /// the default context. When null this will be the the compiler currently resides + /// in + /// + internal AnalyzerAssemblyLoader( + ImmutableArray pathResolvers, + ImmutableArray assemblyResolvers, + AssemblyLoadContext? compilerLoadContext) { - _loadOption = loadOption; - _compilerLoadContext = compilerLoadContext ?? AssemblyLoadContext.GetLoadContext(typeof(AnalyzerAssemblyLoader).GetTypeInfo().Assembly)!; - _externalResolvers = [.. externalResolvers, new CompilerAnalyzerAssemblyResolver(_compilerLoadContext)]; + if (assemblyResolvers.Length == 0) + { + throw new ArgumentException("Cannot be empty", nameof(assemblyResolvers)); + } + + CompilerLoadContext = compilerLoadContext ?? AssemblyLoadContext.GetLoadContext(typeof(SyntaxTree).GetTypeInfo().Assembly)!; + CompilerAnalyzerAssemblyResolver = new CompilerResolver(CompilerLoadContext); + AnalyzerPathResolvers = pathResolvers; + + // The CompilerAnalyzerAssemblyResolver must be first here as the host is _always_ given a chance + // to resolve the assembly before any other resolver. This is crucial to allow for items like + // unification of System.Collections.Immutable or other core assemblies for a host. + AnalyzerAssemblyResolvers = [CompilerAnalyzerAssemblyResolver, .. assemblyResolvers]; } public bool IsHostAssembly(Assembly assembly) @@ -62,14 +75,14 @@ public bool IsHostAssembly(Assembly assembly) CheckIfDisposed(); var alc = AssemblyLoadContext.GetLoadContext(assembly); - return alc == _compilerLoadContext || alc == AssemblyLoadContext.Default; + return alc == CompilerLoadContext || alc == AssemblyLoadContext.Default; } - private partial Assembly Load(AssemblyName assemblyName, string assemblyOriginalPath) + private partial Assembly Load(AssemblyName assemblyName, string resolvedPath) { DirectoryLoadContext? loadContext; - var fullDirectoryPath = Path.GetDirectoryName(assemblyOriginalPath) ?? throw new ArgumentException(message: null, paramName: nameof(assemblyOriginalPath)); + var fullDirectoryPath = Path.GetDirectoryName(resolvedPath) ?? throw new ArgumentException(message: null, paramName: nameof(resolvedPath)); lock (_guard) { if (!_loadContextByDirectory.TryGetValue(fullDirectoryPath, out loadContext)) @@ -83,6 +96,60 @@ private partial Assembly Load(AssemblyName assemblyName, string assemblyOriginal return loadContext.LoadFromAssemblyName(assemblyName); } + /// + /// Is this a registered analyzer file path that the loader knows about. + /// + /// Note: this is using resolved paths, not the original file paths + /// + private bool IsRegisteredAnalyzerPath(string resolvedPath) + { + CheckIfDisposed(); + + lock (_guard) + { + return _resolvedToOriginalPathMap.ContainsKey(resolvedPath); + } + } + + private string? GetAssemblyLoadPath(AssemblyName assemblyName, string directory) + { + // Prefer registered dependencies in the same directory first. + var simpleName = assemblyName.Name!; + var assemblyPath = Path.Combine(directory, simpleName + ".dll"); + if (IsRegisteredAnalyzerPath(assemblyPath)) + { + return assemblyPath; + } + + // Next if this is a resource assembly for a known assembly then load it from the + // appropriate sub directory if it exists + // + // Note: when loading from disk the .NET runtime has a fallback step that will handle + // satellite assembly loading if the call to Load(satelliteAssemblyName) fails. This + // loader has a mode where it loads from Stream though and the runtime will not handle + // that automatically. Rather than bifurcate our loading behavior between Disk and + // Stream both modes just handle satellite loading directly + if (assemblyName.CultureInfo is not null && simpleName.EndsWith(".resources", SimpleNameComparer.Comparison)) + { + var analyzerFileName = Path.ChangeExtension(simpleName, ".dll"); + var analyzerFilePath = Path.Combine(directory, analyzerFileName); + return GetSatelliteLoadPath(analyzerFilePath, assemblyName.CultureInfo); + } + + // Next prefer registered dependencies from other directories. Ideally this would not + // be necessary but msbuild target defaults have caused a number of customers to + // fall into this path. See discussion here for where it comes up + // https://github.com/dotnet/roslyn/issues/56442 + var (_, bestResolvedPath) = GetBestResolvedPath(assemblyName); + if (bestResolvedPath is not null) + { + return bestResolvedPath; + } + + // No analyzer registered this dependency. Time to fail + return null; + } + private partial bool IsMatch(AssemblyName requestedName, AssemblyName candidateName) => requestedName.Name == candidateName.Name; @@ -137,75 +204,24 @@ public DirectoryLoadContext(string directory, AnalyzerAssemblyLoader loader) protected override Assembly? Load(AssemblyName assemblyName) { - if (_loader.ResolveAssemblyExternally(assemblyName, Directory) is { } externallyResolvedAssembly) + foreach (var resolver in _loader.AnalyzerAssemblyResolvers) { - return externallyResolvedAssembly; - } - - // Prefer registered dependencies in the same directory first. - var simpleName = assemblyName.Name!; - var assemblyPath = Path.Combine(Directory, simpleName + ".dll"); - if (_loader.IsAnalyzerDependencyPath(assemblyPath)) - { - (_, var loadPath) = _loader.GetAssemblyInfoForPath(assemblyPath); - return loadCore(loadPath); - } - - // Next if this is a resource assembly for a known assembly then load it from the - // appropriate sub directory if it exists - // - // Note: when loading from disk the .NET runtime has a fallback step that will handle - // satellite assembly loading if the call to Load(satelliteAssemblyName) fails. This - // loader has a mode where it loads from Stream though and the runtime will not handle - // that automatically. Rather than bifurcate our loading behavior between Disk and - // Stream both modes just handle satellite loading directly - if (assemblyName.CultureInfo is not null && simpleName.EndsWith(".resources", StringComparison.Ordinal)) - { - var analyzerFileName = Path.ChangeExtension(simpleName, ".dll"); - var analyzerFilePath = Path.Combine(Directory, analyzerFileName); - var satelliteLoadPath = _loader.GetRealSatelliteLoadPath(analyzerFilePath, assemblyName.CultureInfo); - if (satelliteLoadPath is not null) + var assembly = resolver.Resolve(_loader, assemblyName, this, Directory); + if (assembly is not null) { - return loadCore(satelliteLoadPath); + return assembly; } - - return null; - } - - // Next prefer registered dependencies from other directories. Ideally this would not - // be necessary but msbuild target defaults have caused a number of customers to - // fall into this path. See discussion here for where it comes up - // https://github.com/dotnet/roslyn/issues/56442 - var (_, bestRealPath) = _loader.GetBestPath(assemblyName); - if (bestRealPath is not null) - { - return loadCore(bestRealPath); } - // No analyzer registered this dependency. Time to fail return null; - - Assembly loadCore(string assemblyPath) - { - if (_loader.AnalyzerLoadOption == AnalyzerLoadOption.LoadFromDisk) - { - return LoadFromAssemblyPath(assemblyPath); - } - else - { - using var stream = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read); - return LoadFromStream(stream); - } - } } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { var assemblyPath = Path.Combine(Directory, unmanagedDllName + ".dll"); - if (_loader.IsAnalyzerDependencyPath(assemblyPath)) + if (_loader.IsRegisteredAnalyzerPath(assemblyPath)) { - (_, var loadPath) = _loader.GetAssemblyInfoForPath(assemblyPath); - return LoadUnmanagedDllFromPath(loadPath); + return LoadUnmanagedDllFromPath(assemblyPath); } return IntPtr.Zero; @@ -226,11 +242,11 @@ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) /// /// The that the core /// compiler assemblies are already loaded into. - internal sealed class CompilerAnalyzerAssemblyResolver(AssemblyLoadContext compilerContext) : IAnalyzerAssemblyResolver + private sealed class CompilerResolver(AssemblyLoadContext compilerContext) : IAnalyzerAssemblyResolver { private readonly AssemblyLoadContext _compilerAlc = compilerContext; - public Assembly? ResolveAssembly(AssemblyName assemblyName, string directoryName) + public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) { try { @@ -244,6 +260,42 @@ internal sealed class CompilerAnalyzerAssemblyResolver(AssemblyLoadContext compi } } } + + private sealed class DiskResolver : IAnalyzerAssemblyResolver + { + public static readonly IAnalyzerAssemblyResolver Instance = new DiskResolver(); + public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) + { + var assemblyPath = loader.GetAssemblyLoadPath(assemblyName, directory); + return assemblyPath is not null ? directoryContext.LoadFromAssemblyPath(assemblyPath) : null; + } + } + + /// + /// This loads the assemblies from a which is advantageous because it does + /// not lock the underlying assembly on disk. + /// + /// + /// This should be avoided on Windows. Yes locks files on disks but it also + /// amortizes the cost of AV scanning the assemblies. When loading from + /// the AV will scan the assembly every single time. That cost is significant and easily shows up in + /// performance profiles. + /// + private sealed class StreamResolver : IAnalyzerAssemblyResolver + { + public static readonly IAnalyzerAssemblyResolver Instance = new StreamResolver(); + public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) + { + var assemblyPath = loader.GetAssemblyLoadPath(assemblyName, directory); + if (assemblyPath is null) + { + return null; + } + + using var stream = File.Open(assemblyPath, FileMode.Open, FileAccess.Read, FileShare.Read); + return directoryContext.LoadFromStream(stream); + } + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs index 698a16bb28747..edaf59a3f1094 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.Desktop.cs @@ -14,24 +14,18 @@ namespace Microsoft.CodeAnalysis { - /// - /// Loads analyzer assemblies from their original locations in the file system. - /// Assemblies will only be loaded from the locations specified when the loader - /// is instantiated. - /// - /// - /// This type is meant to be used in scenarios where it is OK for the analyzer - /// assemblies to be locked on disk for the lifetime of the host; for example, - /// csc.exe and vbc.exe. In scenarios where support for updating or deleting - /// the analyzer on disk is required a different loader should be used. - /// internal partial class AnalyzerAssemblyLoader { private bool _hookedAssemblyResolve; - internal AnalyzerAssemblyLoader(ImmutableArray externalResolvers) + internal AnalyzerAssemblyLoader() + : this(analyzerPathResolvers: []) { - _externalResolvers = externalResolvers; + } + + internal AnalyzerAssemblyLoader(ImmutableArray analyzerPathResolvers) + { + AnalyzerPathResolvers = analyzerPathResolvers; } private partial void DisposeWorker() @@ -63,13 +57,9 @@ public bool IsHostAssembly(Assembly assembly) return false; } - private partial Assembly? Load(AssemblyName assemblyName, string assemblyOriginalPath) + private partial Assembly? Load(AssemblyName assemblyName, string resolvedPath) { EnsureResolvedHooked(); - if (ResolveAssemblyExternally(assemblyName, Path.GetDirectoryName(assemblyOriginalPath)) is { } externallyResolvedAssembly) - { - return externallyResolvedAssembly; - } return AppDomain.CurrentDomain.Load(assemblyName); } @@ -120,27 +110,25 @@ internal bool EnsureResolvedUnhooked() const string resourcesExtension = ".resources"; var assemblyName = new AssemblyName(args.Name); var simpleName = assemblyName.Name; - var isSatelliteAssembly = - assemblyName.CultureInfo is not null && - simpleName.EndsWith(resourcesExtension, StringComparison.Ordinal); - if (isSatelliteAssembly) + string? loadPath; + if (assemblyName.CultureInfo is not null && simpleName.EndsWith(resourcesExtension, SimpleNameComparer.Comparison)) { // Satellite assemblies should get the best path information using the // non-resource part of the assembly name. Once the path information is obtained - // GetSatelliteInfoForPath will translate to the resource assembly path. + // GetSatelliteLoadPath will translate to the resource assembly path. assemblyName.Name = simpleName[..^resourcesExtension.Length]; + var (_, resolvedPath) = GetBestResolvedPath(assemblyName); + loadPath = resolvedPath is not null ? GetSatelliteLoadPath(resolvedPath, assemblyName.CultureInfo) : null; } - - var (originalPath, realPath) = GetBestPath(assemblyName); - if (isSatelliteAssembly && originalPath is not null) + else { - realPath = GetRealSatelliteLoadPath(originalPath, assemblyName.CultureInfo); + (_, loadPath) = GetBestResolvedPath(assemblyName); } - if (realPath is not null) + if (loadPath is not null) { - return Assembly.LoadFrom(realPath); + return Assembly.LoadFrom(loadPath); } return null; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs index c3f6ffc6c2d31..36e2fad512fc3 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerAssemblyLoader.cs @@ -5,11 +5,12 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; -using Microsoft.CodeAnalysis.ErrorReporting; +using System.Runtime.InteropServices; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -31,66 +32,85 @@ internal interface IAnalyzerAssemblyLoaderInternal : IAnalyzerAssemblyLoader, ID } /// - /// The base implementation for . This type provides caching and tracking of inputs given + /// The implementation for . This type provides caching and tracking of inputs given /// to . /// /// /// This type generally assumes that files on disk aren't changing, since it ensure that two calls to /// will always return the same thing, per that interface's contract. + /// + /// A given analyzer can have two paths that represent it: the original path of the analyzer passed into this type + /// and the path returned after calling . In the + /// places where differentiating between the two is important, the original path will be referred to as the "original" and + /// the latter is referred to as "resolved". /// - internal abstract partial class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoaderInternal + internal sealed partial class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoaderInternal { private readonly object _guard = new(); /// - /// Set of analyzer dependencies original full paths to the data calculated for that path + /// The original paths are compared ordinally as the expectation is the host needs to handle normalization, + /// if necessary. That means if the host passes in a.dll and a.DLL the loader will treat them as different + /// even if the underlying file system is case insensitive. /// - /// - /// Access must be guarded by - /// - private readonly Dictionary _analyzerAssemblyInfoMap = new(); + internal static readonly StringComparer OriginalPathComparer = StringComparer.Ordinal; + + /// + /// These are paths generated by the loader, or one of its plugins. They need no normalization and hence + /// should be compared ordinally. + /// + internal static readonly StringComparer GeneratedPathComparer = StringComparer.Ordinal; + + /// + /// Simple names are not case sensitive + /// + internal static readonly (StringComparer Comparer, StringComparison Comparison) SimpleNameComparer = (StringComparer.OrdinalIgnoreCase, StringComparison.OrdinalIgnoreCase); /// - /// Mapping of analyzer dependency original full path and culture to the real satellite - /// assembly path. If the satellite assembly doesn't exist for the original analyzer and - /// culture, the real path value stored will be null. + /// This is a map between the original full path and how it is represented in this loader. Specifically + /// the key is the original path before it is considered by . /// /// /// Access must be guarded by /// - private readonly Dictionary<(string OriginalAnalyzerPath, CultureInfo CultureInfo), string?> _analyzerSatelliteAssemblyRealPaths = new(); + private readonly Dictionary _originalPathInfoMap = new(OriginalPathComparer); /// - /// Maps analyzer dependency simple names to the set of original full paths it was loaded from. This _only_ - /// tracks the paths provided to the analyzer as it's a place to look for indirect loads. + /// This is a map between assembly simple names and the collection of original paths that map to them. /// /// /// Access must be guarded by + /// + /// Simple names are not case sensitive /// - private readonly Dictionary> _knownAssemblyPathsBySimpleName = new(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary> _assemblySimpleNameToOriginalPathListMap = new(SimpleNameComparer.Comparer); /// - /// A collection of s that can be used to override the assembly resolution process. + /// Map from resolved paths to the original ones /// /// - /// When multiple resolvers are present they are consulted in-order, with the first resolver to return a non-null - /// winning. - private readonly ImmutableArray _externalResolvers; + /// Access must be guarded by + /// + /// The paths are compared ordinally here as these are computed values, not user supplied ones, and the + /// values should refer to the file on disk with no alteration of its path. + /// + private readonly Dictionary _resolvedToOriginalPathMap = new(GeneratedPathComparer); /// - /// Whether or not we're disposed. Once disposed, all functionality on this type should throw. + /// Whether or not we're disposed. Once disposed, all functionality on this type should throw. /// private bool _isDisposed; + public ImmutableArray AnalyzerPathResolvers { get; } + /// - /// The implementation needs to load an with the specified . The - /// parameter is the original path. It may be different than - /// as that is empty on .NET Core. + /// The implementation needs to load an with the specified from + /// the specified path. /// /// /// This method should return an instance or throw. /// - private partial Assembly Load(AssemblyName assemblyName, string assemblyOriginalPath); + private partial Assembly Load(AssemblyName assemblyName, string resolvedPath); /// /// Determines if the satisfies the request for @@ -120,183 +140,194 @@ public void Dispose() private partial void DisposeWorker(); - internal bool IsAnalyzerDependencyPath(string fullPath) + public void AddDependencyLocation(string originalPath) { CheckIfDisposed(); - lock (_guard) + CompilerPathUtilities.RequireAbsolutePath(originalPath, nameof(originalPath)); + var simpleName = PathUtilities.GetFileName(originalPath, includeExtension: false); + string resolvedPath = originalPath; + IAnalyzerPathResolver? resolver = null; + foreach (var current in AnalyzerPathResolvers) { - return _analyzerAssemblyInfoMap.ContainsKey(fullPath); + if (current.IsAnalyzerPathHandled(originalPath)) + { + resolver = current; + resolvedPath = resolver.GetResolvedAnalyzerPath(originalPath); + break; + } } - } - public void AddDependencyLocation(string fullPath) - { - CheckIfDisposed(); - - CompilerPathUtilities.RequireAbsolutePath(fullPath, nameof(fullPath)); - string simpleName = PathUtilities.GetFileName(fullPath, includeExtension: false); + var assemblyName = readAssemblyName(resolvedPath); lock (_guard) { - if (!_knownAssemblyPathsBySimpleName.TryGetValue(simpleName, out var paths)) + if (_originalPathInfoMap.TryAdd(originalPath, (resolver, resolvedPath, assemblyName))) { - paths = ImmutableHashSet.Create(PathUtilities.Comparer, fullPath); - _knownAssemblyPathsBySimpleName.Add(simpleName, paths); + // In the case multiple original paths map to the same resolved path then the first one + // wins. + // + // An example reason to map multiple original paths to the same real path would be to + // unify references. + _ = _resolvedToOriginalPathMap.TryAdd(resolvedPath, originalPath); + + if (!_assemblySimpleNameToOriginalPathListMap.TryGetValue(simpleName, out var set)) + { + set = new(OriginalPathComparer); + _assemblySimpleNameToOriginalPathListMap[simpleName] = set; + } + + _ = set.Add(originalPath); } else { - _knownAssemblyPathsBySimpleName[simpleName] = paths.Add(fullPath); + Debug.Assert(GeneratedPathComparer.Equals(_originalPathInfoMap[originalPath].ResolvedPath, resolvedPath)); + } + } + + static AssemblyName? readAssemblyName(string filePath) + { + AssemblyName? assemblyName; + try + { + assemblyName = AssemblyName.GetAssemblyName(filePath); + } + catch + { + // The above can fail when the assembly doesn't exist because it's corrupted, + // doesn't exist on disk, or is a native DLL. Those failures are handled when + // the actual load is attempted. Just record the failure now. + assemblyName = null; } - // This type assumes the file system is static for the duration of the - // it's instance. Repeated calls to this method, even if the underlying - // file system contents, should reuse the results of the first call. - _ = _analyzerAssemblyInfoMap.TryAdd(fullPath, null); + return assemblyName; } } - public Assembly LoadFromPath(string originalAnalyzerPath) + /// + /// Called from the consumer of to load an analyzer assembly from disk. It + /// should _not_ be called from the implementation. + /// + public Assembly LoadFromPath(string originalPath) { CheckIfDisposed(); - CompilerPathUtilities.RequireAbsolutePath(originalAnalyzerPath, nameof(originalAnalyzerPath)); - - (AssemblyName? assemblyName, _) = GetAssemblyInfoForPath(originalAnalyzerPath); - - // Not a managed assembly, nothing else to do + CompilerPathUtilities.RequireAbsolutePath(originalPath, nameof(originalPath)); + var (resolvedPath, assemblyName) = GetResolvedAnalyzerPathAndName(originalPath); if (assemblyName is null) { - throw new ArgumentException($"Not a valid assembly: {originalAnalyzerPath}"); + // Not a managed assembly, nothing else to do + throw new ArgumentException($"Not a valid assembly: {originalPath}"); } try { - return Load(assemblyName, originalAnalyzerPath); + return Load(assemblyName, resolvedPath); } catch (Exception ex) { - throw new InvalidOperationException($"Unable to load {assemblyName.Name}", ex); + throw new InvalidOperationException($"Unable to load {assemblyName.Name}: {ex.Message}", ex); } } - /// - /// Get the and the path it should be loaded from for the given original - /// analyzer path - /// - /// - /// This is used in the implementation of the loader instead of - /// because we only want information for registered paths. Using unregistered paths inside the - /// implementation should result in errors. - /// - protected (AssemblyName? AssemblyName, string RealAssemblyPath) GetAssemblyInfoForPath(string originalAnalyzerPath) + private (string ResolvedPath, AssemblyName? AssemblyName) GetResolvedAnalyzerPathAndName(string originalPath) { CheckIfDisposed(); - lock (_guard) { - if (!_analyzerAssemblyInfoMap.TryGetValue(originalAnalyzerPath, out var tuple)) + if (!_originalPathInfoMap.TryGetValue(originalPath, out var info)) { - throw new InvalidOperationException(); + throw new ArgumentException("Path not registered: " + originalPath, nameof(originalPath)); } - if (tuple is { } info) - { - return info; - } + return (info.ResolvedPath, info.AssemblyName); } + } - string realPath = PreparePathToLoad(originalAnalyzerPath); - AssemblyName? assemblyName; - try - { - assemblyName = AssemblyName.GetAssemblyName(realPath); - } - catch + public string GetResolvedAnalyzerPath(string originalPath) => + GetResolvedAnalyzerPathAndName(originalPath).ResolvedPath; + + public string? GetResolvedSatellitePath(string originalPath, CultureInfo cultureInfo) + { + CheckIfDisposed(); + + IAnalyzerPathResolver? resolver; + lock (_guard) { - // The above can fail when the assembly doesn't exist because it's corrupted, - // doesn't exist on disk, or is a native DLL. Those failures are handled when - // the actual load is attempted. Just record the failure now. - assemblyName = null; + if (!_originalPathInfoMap.TryGetValue(originalPath, out var info)) + { + throw new ArgumentException("Path not registered: " + originalPath, nameof(originalPath)); + } + + resolver = info.Resolver; } - lock (_guard) + if (resolver is not null) { - _analyzerAssemblyInfoMap[originalAnalyzerPath] = (assemblyName, realPath); + return resolver.GetResolvedSatellitePath(originalPath, cultureInfo); } - return (assemblyName, realPath); + return GetSatelliteAssemblyPath(originalPath, cultureInfo); } /// - /// Get the path a satellite assembly should be loaded from for the given original + /// Get the path a satellite assembly should be loaded from for the given resolved /// analyzer path and culture /// - /// - /// This is used during assembly resolve for satellite assemblies to determine the - /// path from where the satellite assembly should be loaded for the specified culture. - /// This method calls to ensure this path - /// contains the satellite assembly. - /// - internal string? GetRealSatelliteLoadPath(string originalAnalyzerPath, CultureInfo cultureInfo) + private string? GetSatelliteLoadPath(string resolvedPath, CultureInfo cultureInfo) { - CheckIfDisposed(); - - string? realSatelliteAssemblyPath = null; + string? originalPath; lock (_guard) { - if (_analyzerSatelliteAssemblyRealPaths.TryGetValue((originalAnalyzerPath, cultureInfo), out realSatelliteAssemblyPath)) + if (!_resolvedToOriginalPathMap.TryGetValue(resolvedPath, out originalPath)) { - return realSatelliteAssemblyPath; + return null; } } - var actualCultureName = getSatelliteCultureName(originalAnalyzerPath, cultureInfo); - if (actualCultureName != null) - { - realSatelliteAssemblyPath = PrepareSatelliteAssemblyToLoad(originalAnalyzerPath, actualCultureName); - } + return GetResolvedSatellitePath(originalPath, cultureInfo); + } - lock (_guard) + /// + /// This method mimics the .NET lookup rules for satellite assemblies and will return the ideal + /// resource assembly for the given culture. + /// + internal static string? GetSatelliteAssemblyPath(string assemblyFilePath, CultureInfo cultureInfo) + { + var assemblyFileName = Path.GetFileName(assemblyFilePath); + var satelliteAssemblyName = Path.ChangeExtension(assemblyFileName, ".resources.dll"); + var path = Path.GetDirectoryName(assemblyFilePath); + if (path is null) { - _analyzerSatelliteAssemblyRealPaths[(originalAnalyzerPath, cultureInfo)] = realSatelliteAssemblyPath; + return null; } - return realSatelliteAssemblyPath; - - // Discover the most specific culture name to use for the specified analyzer path and culture - static string? getSatelliteCultureName(string originalAnalyzerPath, CultureInfo cultureInfo) + while (cultureInfo != CultureInfo.InvariantCulture) { - var path = Path.GetDirectoryName(originalAnalyzerPath)!; - var resourceFileName = GetSatelliteFileName(Path.GetFileName(originalAnalyzerPath)); - - while (cultureInfo != CultureInfo.InvariantCulture) + var filePath = Path.Combine(path, cultureInfo.Name, satelliteAssemblyName); + if (File.Exists(filePath)) { - var resourceFilePath = Path.Combine(path, cultureInfo.Name, resourceFileName); - - if (File.Exists(resourceFilePath)) - { - return cultureInfo.Name; - } - - cultureInfo = cultureInfo.Parent; + return filePath; } - return null; + cultureInfo = cultureInfo.Parent; } + + return null; } public string? GetOriginalDependencyLocation(AssemblyName assemblyName) { CheckIfDisposed(); - return GetBestPath(assemblyName).BestOriginalPath; + return GetBestResolvedPath(assemblyName).BestOriginalPath; } + /// - /// Return the best (original, real) path information for loading an assembly with the specified . + /// Return the best (original, resolved) path information for loading an assembly with the specified . /// - protected (string? BestOriginalPath, string? BestRealPath) GetBestPath(AssemblyName requestedName) + private (string? BestOriginalPath, string? BestResolvedPath) GetBestResolvedPath(AssemblyName requestedName) { CheckIfDisposed(); @@ -305,23 +336,23 @@ public Assembly LoadFromPath(string originalAnalyzerPath) return (null, null); } - ImmutableHashSet? paths; + List originalPaths; lock (_guard) { - if (!_knownAssemblyPathsBySimpleName.TryGetValue(requestedName.Name, out paths)) + if (!_assemblySimpleNameToOriginalPathListMap.TryGetValue(requestedName.Name, out var set)) { return (null, null); } + + originalPaths = set.OrderBy(x => x).ToList(); } - // Sort the candidate paths by ordinal, to ensure determinism with the same inputs if you were to have - // multiple assemblies providing the same version. - string? bestRealPath = null; + string? bestResolvedPath = null; string? bestOriginalPath = null; AssemblyName? bestName = null; - foreach (var candidateOriginalPath in paths.OrderBy(StringComparer.Ordinal)) + foreach (var candidateOriginalPath in originalPaths) { - (AssemblyName? candidateName, string candidateRealPath) = GetAssemblyInfoForPath(candidateOriginalPath); + var (candidateResolvedPath, candidateName) = GetResolvedAnalyzerPathAndName(candidateOriginalPath); if (candidateName is null) { continue; @@ -331,98 +362,91 @@ public Assembly LoadFromPath(string originalAnalyzerPath) { if (candidateName.Version == requestedName.Version) { - return (candidateOriginalPath, candidateRealPath); + return (candidateOriginalPath, candidateResolvedPath); } if (bestName is null || candidateName.Version > bestName.Version) { bestOriginalPath = candidateOriginalPath; - bestRealPath = candidateRealPath; + bestResolvedPath = candidateResolvedPath; bestName = candidateName; } } } - return (bestOriginalPath, bestRealPath); + return (bestOriginalPath, bestResolvedPath); } - protected static string GetSatelliteFileName(string assemblyFileName) => - Path.ChangeExtension(assemblyFileName, ".resources.dll"); - - /// - /// When overridden in a derived class, allows substituting an assembly path after we've - /// identified the context to load an assembly in, but before the assembly is actually - /// loaded from disk. This is used to substitute out the original path with the shadow-copied version. - /// - protected abstract string PreparePathToLoad(string assemblyFilePath); - - /// - /// When overridden in a derived class, allows substituting a satellite assembly path after we've - /// identified the context to load a satellite assembly in, but before the satellite assembly is actually - /// loaded from disk. This is used to substitute out the original path with the shadow-copied version. - /// - protected abstract string PrepareSatelliteAssemblyToLoad(string assemblyFilePath, string cultureName); - - /// - /// When is overridden this returns the most recent - /// real path calculated for the - /// - internal string GetRealAnalyzerLoadPath(string originalFullPath) + internal ImmutableArray<(string OriginalAssemblyPath, string ResolvedAssemblyPath)> GetPathMapSnapshot() { CheckIfDisposed(); lock (_guard) { - if (!_analyzerAssemblyInfoMap.TryGetValue(originalFullPath, out var tuple)) - { - throw new InvalidOperationException($"Invalid original path: {originalFullPath}"); - } - - return tuple is { } value ? value.RealAssemblyPath : originalFullPath; + return _resolvedToOriginalPathMap.Select(x => (x.Value, x.Key)).ToImmutableArray(); } } - internal (string OriginalAssemblyPath, string RealAssemblyPath)[] GetPathMapSnapshot() +#if NET + /// + /// Return an which does not lock assemblies on disk that is + /// most appropriate for the current platform. + /// + /// A shadow copy path will be created on Windows and this value + /// will be the base directory where shadow copy assemblies are stored. + internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader( + string windowsShadowPath, + ImmutableArray pathResolvers = default, + ImmutableArray assemblyResolvers = default, + System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null) { - CheckIfDisposed(); + pathResolvers = pathResolvers.NullToEmpty(); + assemblyResolvers = assemblyResolvers.NullToEmpty(); - lock (_guard) + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return _analyzerAssemblyInfoMap - .Select(x => (x.Key, x.Value?.RealAssemblyPath ?? "")) - .OrderBy(x => x.Key) - .ToArray(); + // The only reason we don't use LoadFromStream on Windows is because the anti-virus checker will amortize + // the cost of scanning files on disk. When loading from stream there is no such amortization, the + // anti-virus checker will scan the file every time it is loaded and the performance hit is significant. + // + // On non-Windows OS this is generally not a concern and loading from stream is sufficient to avoid + // locking the file. + return new AnalyzerAssemblyLoader( + pathResolvers, + [.. assemblyResolvers, StreamResolver.Instance], + compilerLoadContext); } + + // The goal here is to avoid locking files on disk that are reasonably expected to be changed by + // developers for the lifetime of VBCSCompiler, Visual Studio, VS Code, etc ... Places like + // Program Files are not expected to change and so locking is not a concern. But for everything else + // we want to avoid locking and use shadow copy. + return new AnalyzerAssemblyLoader( + [.. pathResolvers, ProgramFilesAnalyzerPathResolver.Instance, new ShadowCopyAnalyzerPathResolver(windowsShadowPath)], + [.. assemblyResolvers, DiskResolver.Instance], + compilerLoadContext); } +#else + /// - /// Iterates the if any, to see if any of them can resolve - /// the given to an . + /// Return an which does not lock assemblies on disk that is + /// most appropriate for the current platform. /// - /// The name of the assembly to resolve - /// An if one of the resolvers is successful, or - internal Assembly? ResolveAssemblyExternally(AssemblyName assemblyName, string rootDirectory) + /// A shadow copy path will be created on Windows and this value + /// will be the base directory where shadow copy assemblies are stored. + internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader( + string windowsShadowPath, + ImmutableArray pathResolvers = default) { - CheckIfDisposed(); + pathResolvers = pathResolvers.NullToEmpty(); - if (!_externalResolvers.IsDefaultOrEmpty) - { - foreach (var resolver in _externalResolvers) - { - try - { - if (resolver.ResolveAssembly(assemblyName, rootDirectory) is { } resolvedAssembly) - { - return resolvedAssembly; - } - } - catch (Exception ex) when (FatalError.ReportAndCatch(ex, ErrorSeverity.Diagnostic)) - { - // Ignore if the external resolver throws - } - } - } - return null; + // The goal here is to avoid locking files on disk that are reasonably expected to be changed by + // developers for the lifetime of VBCSCompiler, Visual Studio, VS Code, etc ... Places like + // Program Files are not expected to change and so locking is not a concern. But for everything else + // we want to avoid locking and use shadow copy. + return new AnalyzerAssemblyLoader([.. pathResolvers, ProgramFilesAnalyzerPathResolver.Instance, new ShadowCopyAnalyzerPathResolver(windowsShadowPath)]); } +#endif } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs deleted file mode 100644 index 40f7afa19d91f..0000000000000 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DefaultAnalyzerAssemblyLoader.cs +++ /dev/null @@ -1,80 +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.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis -{ - internal sealed class DefaultAnalyzerAssemblyLoader : AnalyzerAssemblyLoader - { - internal DefaultAnalyzerAssemblyLoader() - : base([]) - { - } - - internal DefaultAnalyzerAssemblyLoader(ImmutableArray externalResolvers) - : base(externalResolvers) - { - } - -#if NET - - internal DefaultAnalyzerAssemblyLoader(System.Runtime.Loader.AssemblyLoadContext? compilerLoadContext = null, AnalyzerLoadOption loadOption = AnalyzerLoadOption.LoadFromDisk, ImmutableArray? externalResolvers = null) - : base(compilerLoadContext, loadOption, externalResolvers ?? []) - { - } - -#endif - - /// - /// The default implementation is to simply load in place. - /// - protected override string PreparePathToLoad(string fullPath) => fullPath; - - /// - /// The default implementation is to simply load in place. - /// - protected override string PrepareSatelliteAssemblyToLoad(string assemblyFilePath, string cultureName) - { - var directory = Path.GetDirectoryName(assemblyFilePath)!; - var fileName = GetSatelliteFileName(Path.GetFileName(assemblyFilePath)); - - return Path.Combine(directory, cultureName, fileName); - } - - /// - /// Return an which does not lock assemblies on disk that is - /// most appropriate for the current platform. - /// - /// A shadow copy path will be created on Windows and this value - /// will be the base directory where shadow copy assemblies are stored. - internal static IAnalyzerAssemblyLoaderInternal CreateNonLockingLoader(string windowsShadowPath, ImmutableArray? externalResolvers = null) - { -#if NET - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return new DefaultAnalyzerAssemblyLoader(loadOption: AnalyzerLoadOption.LoadFromStream, externalResolvers: externalResolvers); - } -#endif - - // The shadow copy analyzer should only be created on Windows. To create on Linux we cannot use - // GetTempPath as it's not per-user. Generally there is no need as LoadFromStream achieves the same - // effect - if (!Path.IsPathRooted(windowsShadowPath)) - { - throw new ArgumentException("Must be a full path.", nameof(windowsShadowPath)); - } - - return new ShadowCopyAnalyzerAssemblyLoader(windowsShadowPath, externalResolvers); - } - } -} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs index 3041e458fa2c9..3f3228b8655a4 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerAssemblyResolver.cs @@ -2,21 +2,39 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#if NET + using System.Reflection; +using System.Runtime.Loader; namespace Microsoft.CodeAnalysis { /// - /// Allows a host to override how assembly resolution is performed by the . + /// This interface allows hosts to control exactly how a given is resolved to an + /// instance. This is useful for hosts that need to load assemblies in a custom way like + /// Razor or stream based loading. /// internal interface IAnalyzerAssemblyResolver { /// - /// Attempts to resolve an assembly by name. + /// Resolve an for the given parameters. /// - /// The assembly to resolve. - /// The root directory the calling is configured with. - /// The resolved assembly, or - Assembly? ResolveAssembly(AssemblyName assemblyName, string rootDirectory); + /// + /// The will partition analyzers into the directories they live + /// in and will create a separate for each directory. That instance + /// and the directory name represent and . + /// + /// This is invoked as part of . Exceptions in + /// the implementation of this interface will escape from that method and be registered as the result + /// of load. + /// + /// The instance that is performing the load + /// The name of the assembly to be loaded + /// The for the + /// The resolved directory where the assembly is being loaded from + /// The resolved or null if it's not handled by this instance + Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory); } } + +#endif diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerPathResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerPathResolver.cs new file mode 100644 index 0000000000000..513b7fd7d20eb --- /dev/null +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/IAnalyzerPathResolver.cs @@ -0,0 +1,61 @@ + +// 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.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + /// + /// This interface gives the host the ability to control the actaul path used to load an analyzer into the + /// compiler. + /// + /// Instances of these types are considered in the order they are added to the . + /// The first instance to return true from will be considered to + /// be the owner of that path. From then on only that instance will be called for the other methods on this + /// interface. + /// + /// For example in a typical session: the will return true for + /// analyzer paths under C:\Program Files\dotnet. That means the , + /// which appears last on Windows, will never see these paths and hence won't shadow copy them. + /// + /// + /// Instances of this type will be accessed from multiple threads. All method implementations are expected + /// to be idempotent. + /// + internal interface IAnalyzerPathResolver + { + /// + /// Is this path handled by this instance? + /// + bool IsAnalyzerPathHandled(string analyzerPath); + + /// + /// This method is used to allow compiler hosts to intercept an analyzer path and redirect it to a + /// a different location. + /// + /// + /// This will only be called for paths that return true from . + /// + string GetResolvedAnalyzerPath(string originalAnalyzerPath); + + /// + /// This method is used to allow compiler hosts to intercept an analyzer satellite path and redirect it to a + /// a different location. A null return here means there is no available satellite assembly for that + /// culture. + /// + /// + /// This will only be called for paths that return true from . + /// + string? GetResolvedSatellitePath(string originalAnalyzerPath, CultureInfo cultureInfo); + } +} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ProgramFilesAnalyzerPathResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ProgramFilesAnalyzerPathResolver.cs new file mode 100644 index 0000000000000..fa04907b9fdb7 --- /dev/null +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ProgramFilesAnalyzerPathResolver.cs @@ -0,0 +1,52 @@ +// 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.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis; + +/// +/// This implementation is used to handle analyzers that +/// exist in global install locations on Windows. These locations do not need to be shadow +/// copied because they are read-only and are not expected to be updated. Putting this resolver +/// before shadow copy will let them load in place. +/// +#if NET +[SupportedOSPlatform("windows")] +#endif +internal sealed class ProgramFilesAnalyzerPathResolver : IAnalyzerPathResolver +{ + internal static readonly IAnalyzerPathResolver Instance = new ProgramFilesAnalyzerPathResolver(); + + private string DotNetPath { get; } + + private ProgramFilesAnalyzerPathResolver() + { + var programFilesPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + DotNetPath = Path.Combine(programFilesPath, "dotnet"); + } + + public bool IsAnalyzerPathHandled(string analyzerPath) + => analyzerPath.StartsWith(DotNetPath, StringComparison.OrdinalIgnoreCase); + + public string GetResolvedAnalyzerPath(string originalAnalyzerPath) + { + Debug.Assert(IsAnalyzerPathHandled(originalAnalyzerPath)); + return originalAnalyzerPath; + } + + public string? GetResolvedSatellitePath(string originalAnalyzerPath, CultureInfo cultureInfo) + { + Debug.Assert(IsAnalyzerPathHandled(originalAnalyzerPath)); + return AnalyzerAssemblyLoader.GetSatelliteAssemblyPath(originalAnalyzerPath, cultureInfo); + } +} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs deleted file mode 100644 index 1df2f9c6b5cf7..0000000000000 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerAssemblyLoader.cs +++ /dev/null @@ -1,254 +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.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Roslyn.Utilities; -using System.Collections.Immutable; -using System.Reflection; - -#if NET -using System.Runtime.Loader; -#endif - -namespace Microsoft.CodeAnalysis -{ - internal sealed class ShadowCopyAnalyzerAssemblyLoader : AnalyzerAssemblyLoader - { - /// - /// The base directory for shadow copies. Each instance of - /// gets its own - /// subdirectory under this directory. This is also the starting point - /// for scavenge operations. - /// - private readonly string _baseDirectory; - - internal readonly Task DeleteLeftoverDirectoriesTask; - - /// - /// The directory where this instance of - /// will shadow-copy assemblies, and the mutex created to mark that the owner of it is still active. - /// - private readonly Lazy<(string directory, Mutex)> _shadowCopyDirectoryAndMutex; - - private readonly ConcurrentDictionary> _mvidPathMap = new ConcurrentDictionary>(); - private readonly ConcurrentDictionary<(Guid, string), Task> _mvidSatelliteAssemblyPathMap = new ConcurrentDictionary<(Guid, string), Task>(); - - internal string BaseDirectory => _baseDirectory; - - internal int CopyCount => _mvidPathMap.Count; - -#if NET - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) - : this(null, baseDirectory, externalResolvers) - { - } - - public ShadowCopyAnalyzerAssemblyLoader(AssemblyLoadContext? compilerLoadContext, string baseDirectory, ImmutableArray? externalResolvers = null) - : base(compilerLoadContext, AnalyzerLoadOption.LoadFromDisk, externalResolvers ?? []) -#else - public ShadowCopyAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) - : base(externalResolvers ?? []) -#endif - { - if (baseDirectory is null) - { - throw new ArgumentNullException(nameof(baseDirectory)); - } - - _baseDirectory = baseDirectory; - _shadowCopyDirectoryAndMutex = new Lazy<(string directory, Mutex)>( - () => CreateUniqueDirectoryForProcess(), LazyThreadSafetyMode.ExecutionAndPublication); - - DeleteLeftoverDirectoriesTask = Task.Run(DeleteLeftoverDirectories); - } - - private void DeleteLeftoverDirectories() - { - // Avoid first chance exception - if (!Directory.Exists(_baseDirectory)) - return; - - IEnumerable subDirectories; - try - { - subDirectories = Directory.EnumerateDirectories(_baseDirectory); - } - catch (DirectoryNotFoundException) - { - return; - } - - foreach (var subDirectory in subDirectories) - { - string name = Path.GetFileName(subDirectory).ToLowerInvariant(); - Mutex? mutex = null; - try - { - // We only want to try deleting the directory if no-one else is currently - // using it. That is, if there is no corresponding mutex. - if (!Mutex.TryOpenExisting(name, out mutex)) - { - try - { - // Avoid calling ClearReadOnlyFlagOnFiles before calling Directory.Delete. In general, files - // created by the shadow copy should not be marked read-only (CopyFile also clears the - // read-only flag), and clearing the read-only flag for the entire directory requires - // significant disk access. - // - // If the deletion fails for an IOException, it may have been the result of a file being - // marked read-only. We catch that exception and perform an explicit clear before trying - // again. - Directory.Delete(subDirectory, recursive: true); - } - catch (IOException) - { - // Retry after clearing the read-only flag - ClearReadOnlyFlagOnFiles(subDirectory); - Directory.Delete(subDirectory, recursive: true); - } - } - } - catch - { - // If something goes wrong we will leave it to the next run to clean up. - // Just swallow the exception and move on. - } - finally - { - if (mutex != null) - { - mutex.Dispose(); - } - } - } - } - - protected override string PreparePathToLoad(string originalAnalyzerPath) - { - var mvid = AssemblyUtilities.ReadMvid(originalAnalyzerPath); - - return PrepareLoad(_mvidPathMap, mvid, copyAnalyzerContents); - - string copyAnalyzerContents() - { - var analyzerFileName = Path.GetFileName(originalAnalyzerPath); - var shadowDirectory = Path.Combine(_shadowCopyDirectoryAndMutex.Value.directory, mvid.ToString()); - var shadowAnalyzerPath = Path.Combine(shadowDirectory, analyzerFileName); - CopyFile(originalAnalyzerPath, shadowAnalyzerPath); - - return shadowAnalyzerPath; - } - } - - protected override string PrepareSatelliteAssemblyToLoad(string originalAnalyzerPath, string cultureName) - { - var mvid = AssemblyUtilities.ReadMvid(originalAnalyzerPath); - - return PrepareLoad(_mvidSatelliteAssemblyPathMap, (mvid, cultureName), copyAnalyzerContents); - - string copyAnalyzerContents() - { - var analyzerFileName = Path.GetFileName(originalAnalyzerPath); - var shadowDirectory = Path.Combine(_shadowCopyDirectoryAndMutex.Value.directory, mvid.ToString()); - var shadowAnalyzerPath = Path.Combine(shadowDirectory, analyzerFileName); - - var originalDirectory = Path.GetDirectoryName(originalAnalyzerPath)!; - var satelliteFileName = GetSatelliteFileName(analyzerFileName); - - var originalSatellitePath = Path.Combine(originalDirectory, cultureName, satelliteFileName); - var shadowSatellitePath = Path.Combine(shadowDirectory, cultureName, satelliteFileName); - CopyFile(originalSatellitePath, shadowSatellitePath); - - return shadowSatellitePath; - } - } - - private static string PrepareLoad(ConcurrentDictionary> mvidPathMap, TKey mvidKey, Func copyContents) - where TKey : notnull - { - if (mvidPathMap.TryGetValue(mvidKey, out Task? copyTask)) - { - return copyTask.Result; - } - - var tcs = new TaskCompletionSource(); - var task = mvidPathMap.GetOrAdd(mvidKey, tcs.Task); - if (object.ReferenceEquals(task, tcs.Task)) - { - // This thread won and we need to do the copy. - try - { - var shadowAnalyzerPath = copyContents(); - tcs.SetResult(shadowAnalyzerPath); - return shadowAnalyzerPath; - } - catch (Exception ex) - { - tcs.SetException(ex); - throw; - } - } - else - { - // This thread lost and we need to wait for the winner to finish the copy. - return task.Result; - } - } - - private static void CopyFile(string originalPath, string shadowCopyPath) - { - var directory = Path.GetDirectoryName(shadowCopyPath); - if (directory is null) - { - throw new ArgumentException($"Shadow copy path '{shadowCopyPath}' must not be the root directory"); - } - - _ = Directory.CreateDirectory(directory); - File.Copy(originalPath, shadowCopyPath); - ClearReadOnlyFlagOnFile(new FileInfo(shadowCopyPath)); - } - - private static void ClearReadOnlyFlagOnFiles(string directoryPath) - { - DirectoryInfo directory = new DirectoryInfo(directoryPath); - - foreach (var file in directory.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.AllDirectories)) - { - ClearReadOnlyFlagOnFile(file); - } - } - - private static void ClearReadOnlyFlagOnFile(FileInfo fileInfo) - { - try - { - if (fileInfo.IsReadOnly) - { - fileInfo.IsReadOnly = false; - } - } - catch - { - // There are many reasons this could fail. Ignore it and keep going. - } - } - - private (string directory, Mutex mutex) CreateUniqueDirectoryForProcess() - { - string guid = Guid.NewGuid().ToString("N").ToLowerInvariant(); - string directory = Path.Combine(_baseDirectory, guid); - - var mutex = new Mutex(initiallyOwned: false, name: guid); - - Directory.CreateDirectory(directory); - - return (directory, mutex); - } - } -} diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerPathResolver.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerPathResolver.cs new file mode 100644 index 0000000000000..cbac550b1bf55 --- /dev/null +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/ShadowCopyAnalyzerPathResolver.cs @@ -0,0 +1,271 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Roslyn.Utilities; +using System.Collections.Immutable; +using System.Reflection; +using System.Globalization; +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis +{ + internal sealed class ShadowCopyAnalyzerPathResolver : IAnalyzerPathResolver + { + /// + /// The base directory for shadow copies. Each instance of + /// gets its own + /// subdirectory under this directory. This is also the starting point + /// for scavenge operations. + /// + internal string BaseDirectory { get; } + + internal string ShadowDirectory { get; } + + /// + /// As long as this mutex is alive, other instances of this type will not try to clean + /// up the shadow directory. + /// + private Mutex Mutex { get; } + + internal Task DeleteLeftoverDirectoriesTask { get; } + + /// + /// This is a counter that is incremented each time a new shadow sub directory is created to ensure they + /// have unique names. + /// + private int _directoryCount; + + /// + /// This is a map from the original directory name to the numbered directory name it + /// occupies in the shadow directory. + /// + private ConcurrentDictionary OriginalDirectoryMap { get; } = new(AnalyzerAssemblyLoader.OriginalPathComparer); + + /// + /// This interface can be called from multiple threads for the same original assembly path. This + /// is a map between the original path and the Task that completes when the shadow copy for that + /// original path completes. + /// + private ConcurrentDictionary> CopyMap { get; } = new(AnalyzerAssemblyLoader.OriginalPathComparer); + + /// + /// This is the number of shadow copies that have occurred in this instance. + /// + /// + /// This is used for testing, it should not be used for any other purpose. + /// + internal int CopyCount => CopyMap.Count; + + public ShadowCopyAnalyzerPathResolver(string baseDirectory) + { + if (baseDirectory is null) + { + throw new ArgumentNullException(nameof(baseDirectory)); + } + + // The shadow copy analyzer should only be created on Windows. To create on Linux we cannot use + // GetTempPath as it's not per-user. Generally there is no need as LoadFromStream achieves the same + // effect + if (!Path.IsPathRooted(baseDirectory)) + { + throw new ArgumentException($"Must be a full path: {baseDirectory}", nameof(baseDirectory)); + } + + BaseDirectory = baseDirectory; + var shadowDirectoryName = Guid.NewGuid().ToString("N").ToLowerInvariant(); + + // The directory is deliberately _not_ created at this point. It will only be created when the first + // request comes in. This avoids creating unnecessary directories when no analyzers are loaded + // via the shadow layer. + ShadowDirectory = Path.Combine(BaseDirectory, shadowDirectoryName); + Mutex = new Mutex(initiallyOwned: false, name: shadowDirectoryName); + DeleteLeftoverDirectoriesTask = Task.Run(DeleteLeftoverDirectories); + } + + private void DeleteLeftoverDirectories() + { + // Avoid first chance exception + if (!Directory.Exists(BaseDirectory)) + return; + + IEnumerable subDirectories; + try + { + subDirectories = Directory.EnumerateDirectories(BaseDirectory); + } + catch (DirectoryNotFoundException) + { + return; + } + + foreach (var subDirectory in subDirectories) + { + string name = Path.GetFileName(subDirectory).ToLowerInvariant(); + Mutex? mutex = null; + try + { + // We only want to try deleting the directory if no-one else is currently + // using it. That is, if there is no corresponding mutex. + if (!Mutex.TryOpenExisting(name, out mutex)) + { + try + { + // Avoid calling ClearReadOnlyFlagOnFiles before calling Directory.Delete. In general, files + // created by the shadow copy should not be marked read-only (CopyFile also clears the + // read-only flag), and clearing the read-only flag for the entire directory requires + // significant disk access. + // + // If the deletion fails for an IOException, it may have been the result of a file being + // marked read-only. We catch that exception and perform an explicit clear before trying + // again. + Directory.Delete(subDirectory, recursive: true); + } + catch (IOException) + { + // Retry after clearing the read-only flag + ClearReadOnlyFlagOnFiles(subDirectory); + Directory.Delete(subDirectory, recursive: true); + } + } + } + catch + { + // If something goes wrong we will leave it to the next run to clean up. + // Just swallow the exception and move on. + } + finally + { + if (mutex != null) + { + mutex.Dispose(); + } + } + } + } + + public bool IsAnalyzerPathHandled(string analyzerFilePath) => true; + + public string GetResolvedAnalyzerPath(string originalAnalyzerPath) + { + var analyzerShadowDir = GetAnalyzerShadowDirectory(originalAnalyzerPath); + var analyzerShadowPath = Path.Combine(analyzerShadowDir, Path.GetFileName(originalAnalyzerPath)); + ShadowCopyFile(originalAnalyzerPath, analyzerShadowPath); + return analyzerShadowPath; + } + + public string? GetResolvedSatellitePath(string originalAnalyzerPath, CultureInfo cultureInfo) + { + var satelliteFilePath = AnalyzerAssemblyLoader.GetSatelliteAssemblyPath(originalAnalyzerPath, cultureInfo); + if (satelliteFilePath is null) + { + return null; + } + + var analyzerShadowDir = GetAnalyzerShadowDirectory(originalAnalyzerPath); + var satelliteFileName = Path.GetFileName(satelliteFilePath); + var satelliteDirectoryName = Path.GetFileName(Path.GetDirectoryName(satelliteFilePath)); + var shadowSatellitePath = Path.Combine(analyzerShadowDir, satelliteDirectoryName!, satelliteFileName); + ShadowCopyFile(satelliteFilePath, shadowSatellitePath); + return shadowSatellitePath; + } + + /// + /// Get the shadow directory for the given original analyzer file path. + /// + private string GetAnalyzerShadowDirectory(string analyzerFilePath) + { + var originalDirName = Path.GetDirectoryName(analyzerFilePath)!; + var shadowDirName = OriginalDirectoryMap.GetOrAdd(originalDirName, _ => Interlocked.Increment(ref _directoryCount)).ToString(); + return Path.Combine(ShadowDirectory, shadowDirName); + } + + /// + /// This type has to account for multiple threads calling into the various resolver APIs. To avoid two threads + /// writing at the same time this method is used to ensure only one thread _wins_ and both can wait for + /// that thread to complete the copy. + /// + private void ShadowCopyFile(string originalFilePath, string shadowCopyPath) + { + if (CopyMap.TryGetValue(originalFilePath, out var copyTask)) + { + copyTask.Wait(); + return; + } + + var tcs = new TaskCompletionSource(); + var task = CopyMap.GetOrAdd(originalFilePath, tcs.Task); + if (object.ReferenceEquals(task, tcs.Task)) + { + // This thread won and we need to do the copy. + try + { + copyFile(originalFilePath, shadowCopyPath); + tcs.SetResult(shadowCopyPath); + } + catch (Exception ex) + { + tcs.SetException(ex); + throw; + } + } + else + { + // This thread lost and we need to wait for the winner to finish the copy. + task.Wait(); + Debug.Assert(AnalyzerAssemblyLoader.GeneratedPathComparer.Equals(shadowCopyPath, task.Result)); + } + + static void copyFile(string originalPath, string shadowCopyPath) + { + var directory = Path.GetDirectoryName(shadowCopyPath); + if (directory is null) + { + throw new ArgumentException($"Shadow copy path '{shadowCopyPath}' must not be the root directory"); + } + + _ = Directory.CreateDirectory(directory); + + // The shadow copy should only copy files that exist. For files that don't exist, this best + // emulates not having the shadow copy layer + if (File.Exists(originalPath)) + { + File.Copy(originalPath, shadowCopyPath); + ClearReadOnlyFlagOnFile(new FileInfo(shadowCopyPath)); + } + } + } + + private static void ClearReadOnlyFlagOnFiles(string directoryPath) + { + DirectoryInfo directory = new DirectoryInfo(directoryPath); + + foreach (var file in directory.EnumerateFiles(searchPattern: "*", searchOption: SearchOption.AllDirectories)) + { + ClearReadOnlyFlagOnFile(file); + } + } + + private static void ClearReadOnlyFlagOnFile(FileInfo fileInfo) + { + try + { + if (fileInfo.IsReadOnly) + { + fileInfo.IsReadOnly = false; + } + } + catch + { + // There are many reasons this could fail. Ignore it and keep going. + } + } + + } +} diff --git a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs index 1177f7add1a6d..44b8c70265965 100644 --- a/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs +++ b/src/Compilers/Core/Portable/Emit/CommonPEModuleBuilder.cs @@ -906,7 +906,7 @@ private SynthesizedDefinitions GetOrAddSynthesizedDefinitions(TNamedTypeSymbol c return _synthesizedTypeMembers.GetOrAdd(container, _ => new SynthesizedDefinitions()); } - public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IMethodDefinition method) + public virtual void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IMethodDefinition method) { Debug.Assert(method != null); @@ -919,7 +919,7 @@ public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IMethodDefi defs.Methods.Enqueue(method); } - public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IPropertyDefinition property) + public virtual void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IPropertyDefinition property) { Debug.Assert(property != null); @@ -932,7 +932,7 @@ public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IPropertyDe defs.Properties.Enqueue(property); } - public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IFieldDefinition field) + public virtual void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IFieldDefinition field) { Debug.Assert(field != null); @@ -945,7 +945,7 @@ public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.IFieldDefin defs.Fields.Enqueue(field); } - public void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.INestedTypeDefinition nestedType) + public virtual void AddSynthesizedDefinition(TNamedTypeSymbol container, Cci.INestedTypeDefinition nestedType) { Debug.Assert(nestedType != null); diff --git a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs index ba93aec54dd86..49f1a5854a2e2 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/FatalError.cs @@ -229,7 +229,7 @@ public static bool ReportAndCatchUnlessCanceled(Exception exception, Cancellatio private static readonly object s_reportedMarker = Guid.NewGuid(); // Do not allow this method to be inlined. That way when we have a dump we can see this frame in the stack and - // can examine things like s_reportedExceptionMessage. Without this, it's a lot tricker as FatalError is linked + // can examine things like s_reportedExceptionMessage. Without this, it's a lot trickier as FatalError is linked // into many assemblies and finding the right type can be much harder. [MethodImpl(MethodImplOptions.NoInlining)] private static void Report(Exception exception, ErrorSeverity severity = ErrorSeverity.Uncategorized, bool forceDump = false) diff --git a/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs b/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs index 9394f13b21c30..3919a71673d49 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/RoslynExperiments.cs @@ -17,4 +17,7 @@ internal static class RoslynExperiments internal const string GeneratorHostOutputs = "RSEXPERIMENTAL004"; internal const string GeneratorHostOutputs_Url = "https://github.com/dotnet/roslyn/issues/74753"; + + internal const string IgnoredDirectives = "RSEXPERIMENTAL005"; + internal const string IgnoredDirectives_Url = "https://github.com/dotnet/roslyn/issues/77697"; } diff --git a/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs b/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs index 29c428581833d..74cec5acf2eec 100644 --- a/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs +++ b/src/Compilers/Core/Portable/MetadataReader/MetadataHelpers.cs @@ -2,12 +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. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Reflection.Metadata; @@ -39,24 +38,24 @@ internal static class MetadataHelpers internal readonly struct AssemblyQualifiedTypeName { - internal readonly string TopLevelType; - internal readonly string[] NestedTypes; - internal readonly AssemblyQualifiedTypeName[] TypeArguments; + internal readonly string? TopLevelType; + internal readonly string[]? NestedTypes; + internal readonly AssemblyQualifiedTypeName[]? TypeArguments; internal readonly int PointerCount; /// /// Rank equal 0 is used to denote an SzArray, rank equal 1 denotes multi-dimensional array of rank 1. /// - internal readonly int[] ArrayRanks; - internal readonly string AssemblyName; + internal readonly int[]? ArrayRanks; + internal readonly string? AssemblyName; internal AssemblyQualifiedTypeName( - string topLevelType, - string[] nestedTypes, - AssemblyQualifiedTypeName[] typeArguments, + string? topLevelType, + string[]? nestedTypes, + AssemblyQualifiedTypeName[]? typeArguments, int pointerCount, - int[] arrayRanks, - string assemblyName) + int[]? arrayRanks, + string? assemblyName) { this.TopLevelType = topLevelType; this.NestedTypes = nestedTypes; @@ -132,12 +131,12 @@ internal AssemblyQualifiedTypeName DecodeTypeName(bool isTypeArgument = false, b { Debug.Assert(!isTypeArgumentWithAssemblyName || isTypeArgument); - string topLevelType = null; - ArrayBuilder nestedTypesBuilder = null; - AssemblyQualifiedTypeName[] typeArguments = null; + string? topLevelType = null; + ArrayBuilder? nestedTypesBuilder = null; + AssemblyQualifiedTypeName[]? typeArguments = null; int pointerCount = 0; - ArrayBuilder arrayRanksBuilder = null; - string assemblyName = null; + ArrayBuilder? arrayRanksBuilder = null; + string? assemblyName = null; bool decodingTopLevelType = true; bool isGenericTypeName = false; @@ -276,7 +275,7 @@ internal AssemblyQualifiedTypeName DecodeTypeName(bool isTypeArgument = false, b assemblyName); } - private static void HandleDecodedTypeName(string decodedTypeName, bool decodingTopLevelType, ref string topLevelType, ref ArrayBuilder nestedTypesBuilder) + private static void HandleDecodedTypeName(string decodedTypeName, bool decodingTopLevelType, ref string? topLevelType, ref ArrayBuilder? nestedTypesBuilder) { if (decodedTypeName.Length != 0) { @@ -318,7 +317,7 @@ private string DecodeGenericName(int i) return _input.Substring(start, _offset - start); } - private AssemblyQualifiedTypeName[] DecodeTypeArguments() + private AssemblyQualifiedTypeName[]? DecodeTypeArguments() { if (EndOfInput) { @@ -380,7 +379,7 @@ private AssemblyQualifiedTypeName DecodeTypeArgument() return result; } - private string DecodeAssemblyName(bool isTypeArgumentWithAssemblyName) + private string? DecodeAssemblyName(bool isTypeArgumentWithAssemblyName) { if (EndOfInput) { @@ -409,7 +408,7 @@ private string DecodeAssemblyName(bool isTypeArgumentWithAssemblyName) /// /// Rank equal 0 is used to denote an SzArray, rank equal 1 denotes multi-dimensional array of rank 1. /// - private void DecodeArrayShape(StringBuilder typeNameBuilder, ref ArrayBuilder arrayRanksBuilder) + private void DecodeArrayShape(StringBuilder typeNameBuilder, ref ArrayBuilder? arrayRanksBuilder) { Debug.Assert(Current == '['); @@ -475,12 +474,10 @@ internal static string GetAritySuffix(int arity) return (arity <= 9) ? s_aritySuffixesOneToNine[arity - 1] : string.Concat(GenericTypeNameManglingString, arity.ToString(CultureInfo.InvariantCulture)); } -#nullable enable internal static string ComposeAritySuffixedMetadataName(string name, int arity, string? associatedFileIdentifier) { return associatedFileIdentifier + (arity == 0 ? name : name + GetAritySuffix(arity)); } -#nullable disable internal static int InferTypeArityFromMetadataName(string emittedTypeName) { @@ -714,7 +711,7 @@ internal static ReadOnlyMemory SplitQualifiedName( } internal static string BuildQualifiedName( - string qualifier, + string? qualifier, string name) { Debug.Assert(name != null); @@ -768,8 +765,8 @@ public static void GetInfoForImmediateNamespaceMembers( int namespaceNameLength, IEnumerable> typesByNS, StringComparer nameComparer, - out IEnumerable> types, - out IEnumerable>>> namespaces) + [NotNull] out IEnumerable>? types, + [NotNull] out IEnumerable>>>? namespaces) { Debug.Assert(typesByNS != null); Debug.Assert(namespaceNameLength >= 0); @@ -795,11 +792,11 @@ public static void GetInfoForImmediateNamespaceMembers( var pair = enumerator.Current; // Simple name of the last encountered child namespace. - string lastChildNamespaceName = null; + string? lastChildNamespaceName = null; // A list accumulating information about types within the last encountered child namespace. // The list is similar to the sequence passed to this function. - List> typesInLastChildNamespace = null; + List>? typesInLastChildNamespace = null; // if there are any types in this namespace, // they will be in the first several groups if their key length @@ -831,6 +828,11 @@ public static void GetInfoForImmediateNamespaceMembers( int cmp = nameComparer.Compare(lastChildNamespaceName, childNamespaceName); if (cmp == 0) { + // This cannot be null because the starting state for lastChildNamespaceName is null and + // hence we can't hit this branch in the first iteration. The else branch will always + // allocate this value. + Debug.Assert((object?)typesInLastChildNamespace != null); + // We are still processing the same child namespace typesInLastChildNamespace.Add(pair); } @@ -840,13 +842,14 @@ public static void GetInfoForImmediateNamespaceMembers( if (cmp > 0) { // The sort order is violated for child namespace names. Obfuscation is the likely reason for this. - Debug.Assert((object)lastChildNamespaceName != null); + Debug.Assert(lastChildNamespaceName != null); possiblyHavePairsWithDuplicateKey = true; } // Preserve information about previous child namespace. if (typesInLastChildNamespace != null) { + Debug.Assert(lastChildNamespaceName != null); Debug.Assert(typesInLastChildNamespace.Count != 0); nestedNamespaces.Add( new KeyValuePair>>( @@ -855,7 +858,6 @@ public static void GetInfoForImmediateNamespaceMembers( typesInLastChildNamespace = new List>(); lastChildNamespaceName = childNamespaceName; - Debug.Assert((object)lastChildNamespaceName != null); typesInLastChildNamespace.Add(pair); } @@ -865,6 +867,7 @@ public static void GetInfoForImmediateNamespaceMembers( // Preserve information about last child namespace. if (typesInLastChildNamespace != null) { + Debug.Assert(lastChildNamespaceName != null); Debug.Assert(typesInLastChildNamespace.Count != 0); nestedNamespaces.Add( new KeyValuePair>>( @@ -947,7 +950,7 @@ private static string ExtractSimpleNameOfChildNamespace( /// /// Determines whether given string can be used as a non-empty metadata identifier (a NUL-terminated UTF-8 string). /// - internal static bool IsValidMetadataIdentifier(string str) + internal static bool IsValidMetadataIdentifier(string? str) { return !string.IsNullOrEmpty(str) && str.IsValidUnicodeString() && str.IndexOf('\0') == -1; } @@ -955,19 +958,19 @@ internal static bool IsValidMetadataIdentifier(string str) /// /// True if the string doesn't contain incomplete surrogates. /// - internal static bool IsValidUnicodeString(string str) + internal static bool IsValidUnicodeString(string? str) { return str == null || str.IsValidUnicodeString(); } - internal static bool IsValidAssemblyOrModuleName(string name) + internal static bool IsValidAssemblyOrModuleName(string? name) { return GetAssemblyOrModuleNameErrorArgumentResourceName(name) == null; } internal static void CheckAssemblyOrModuleName(string name, CommonMessageProvider messageProvider, int code, DiagnosticBag diagnostics) { - string errorArgumentResourceId = GetAssemblyOrModuleNameErrorArgumentResourceName(name); + string? errorArgumentResourceId = GetAssemblyOrModuleNameErrorArgumentResourceName(name); if (errorArgumentResourceId != null) { diagnostics.Add( @@ -978,7 +981,7 @@ internal static void CheckAssemblyOrModuleName(string name, CommonMessageProvide internal static void CheckAssemblyOrModuleName(string name, CommonMessageProvider messageProvider, int code, ArrayBuilder builder) { - string errorArgumentResourceId = GetAssemblyOrModuleNameErrorArgumentResourceName(name); + string? errorArgumentResourceId = GetAssemblyOrModuleNameErrorArgumentResourceName(name); if (errorArgumentResourceId != null) { builder.Add( @@ -987,7 +990,7 @@ internal static void CheckAssemblyOrModuleName(string name, CommonMessageProvide } } - private static string GetAssemblyOrModuleNameErrorArgumentResourceName(string name) + private static string? GetAssemblyOrModuleNameErrorArgumentResourceName(string? name) { if (name == null) { diff --git a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs index 7e2c6a5eae7f6..bdb30612ee7df 100644 --- a/src/Compilers/Core/Portable/MetadataReader/PEModule.cs +++ b/src/Compilers/Core/Portable/MetadataReader/PEModule.cs @@ -645,6 +645,8 @@ private IEnumerable GetTypeDefsOrThrow(bool topLevelOnly) } } +#nullable enable + /// /// The function groups types defined in the module by their fully-qualified namespace name. /// The case-sensitivity of the grouping depends upon the provided StringComparer. @@ -670,9 +672,11 @@ internal IEnumerable> GroupTypesByNamesp // merged, even if they are equal according to the provided comparer. This improves the error // experience because types retain their exact namespaces. - Dictionary> namespaces = new Dictionary>(); + Dictionary?> namespaces = new Dictionary?>(); - GetTypeNamespaceNamesOrThrow(namespaces); + // Note: the ! assertion here is for the ArrayBuilder values being non-null in this + // method. The dictionary is empty so this is trivially true. + GetTypeNamespaceNamesOrThrow(namespaces!); GetForwardedTypeNamespaceNamesOrThrow(namespaces); var result = new ArrayBuilder>(namespaces.Count); @@ -686,7 +690,7 @@ internal IEnumerable> GroupTypesByNamesp return result; } - internal class TypesByNamespaceSortComparer : IComparer> + internal sealed class TypesByNamespaceSortComparer : IComparer> { private readonly StringComparer _nameComparer; @@ -695,13 +699,23 @@ public TypesByNamespaceSortComparer(StringComparer nameComparer) _nameComparer = nameComparer; } - public int Compare(IGrouping left, IGrouping right) + public int Compare(IGrouping? left, IGrouping? right) { if (left == right) { return 0; } + if (left is null) + { + return -1; + } + + if (right is null) + { + return 1; + } + int result = _nameComparer.Compare(left.Key, right.Key); if (result == 0) @@ -745,7 +759,7 @@ private void GetTypeNamespaceNamesOrThrow(Dictionary builder; + ArrayBuilder? builder; if (namespaceHandles.TryGetValue(nsHandle, out builder)) { @@ -761,7 +775,7 @@ private void GetTypeNamespaceNamesOrThrow(Dictionary builder; + ArrayBuilder? builder; if (namespaces.TryGetValue(@namespace, out builder)) { @@ -816,7 +830,7 @@ public int GetHashCode(NamespaceDefinitionHandle obj) /// the qualifier). /// /// An exception from metadata reader. - private void GetForwardedTypeNamespaceNamesOrThrow(Dictionary> namespaces) + private void GetForwardedTypeNamespaceNamesOrThrow(Dictionary?> namespaces) { EnsureForwardTypeToAssemblyMap(); @@ -831,6 +845,8 @@ private void GetForwardedTypeNamespaceNamesOrThrow(Dictionary bool +Microsoft.CodeAnalysis.IEventSymbol.PartialDefinitionPart.get -> Microsoft.CodeAnalysis.IEventSymbol? +Microsoft.CodeAnalysis.IEventSymbol.PartialImplementationPart.get -> Microsoft.CodeAnalysis.IEventSymbol? Microsoft.CodeAnalysis.IncrementalGeneratorPostInitializationContext.AddEmbeddedAttributeDefinition() -> void override Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.ToString() -> string! + +Microsoft.CodeAnalysis.ITypeSymbol.IsExtension.get -> bool +Microsoft.CodeAnalysis.TypeKind.Extension = 14 -> Microsoft.CodeAnalysis.TypeKind +Microsoft.CodeAnalysis.ITypeSymbol.ExtensionParameter.get -> Microsoft.CodeAnalysis.IParameterSymbol? diff --git a/src/Compilers/Core/Portable/Symbols/IEventSymbol.cs b/src/Compilers/Core/Portable/Symbols/IEventSymbol.cs index 962e396bec592..7c573dd430c62 100644 --- a/src/Compilers/Core/Portable/Symbols/IEventSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IEventSymbol.cs @@ -65,5 +65,20 @@ public interface IEventSymbol : ISymbol /// Properties imported from metadata can explicitly implement more than one event. /// ImmutableArray ExplicitInterfaceImplementations { get; } + + /// + /// If this is a partial event implementation part, returns the corresponding definition part, otherwise . + /// + IEventSymbol? PartialDefinitionPart { get; } + + /// + /// If this is a partial event definition part, returns the corresponding implementation part, otherwise . + /// + IEventSymbol? PartialImplementationPart { get; } + + /// + /// Returns if this is a partial definition part, otherwise . + /// + bool IsPartialDefinition { get; } } } diff --git a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs index 2a6a9ceb1ff8c..a5a2d685d6da5 100644 --- a/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/INamedTypeSymbol.cs @@ -150,7 +150,7 @@ public interface INamedTypeSymbol : ITypeSymbol ImmutableArray StaticConstructors { get; } /// - /// Get the both instance and static constructors for this type. + /// Get both instance and static constructors for this type. /// ImmutableArray Constructors { get; } diff --git a/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs b/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs index e50c977543812..a27a5547b229d 100644 --- a/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/ITypeSymbol.cs @@ -81,6 +81,16 @@ public interface ITypeSymbol : INamespaceOrTypeSymbol /// bool IsNativeIntegerType { get; } + /// + /// Is this a symbol for an extension declaration. + /// + bool IsExtension { get; } + + /// + /// The extension parameter if this is an extension declaration ( is true). + /// + IParameterSymbol? ExtensionParameter { get; } + /// /// The original definition of this symbol. If this symbol is constructed from another /// symbol by type substitution then gets the original symbol as it was defined in diff --git a/src/Compilers/Core/Portable/Symbols/TypeKind.cs b/src/Compilers/Core/Portable/Symbols/TypeKind.cs index e0e2aec8b5165..d695bc1793681 100644 --- a/src/Compilers/Core/Portable/Symbols/TypeKind.cs +++ b/src/Compilers/Core/Portable/Symbols/TypeKind.cs @@ -86,6 +86,11 @@ public enum TypeKind : byte /// Type is a function pointer. /// FunctionPointer = 13, + + /// + /// Type is an extension container. + /// + Extension = 14, } internal static class TypeKindInternal diff --git a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs index 85feb4e5a2da5..e7fb022842297 100644 --- a/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs +++ b/src/Compilers/Core/Portable/Symbols/WellKnownMemberNames.cs @@ -406,5 +406,10 @@ public static class WellKnownMemberNames internal const string CastUpMethodName = "CastUp"; internal const string MemoryExtensionsTypeFullName = "System.MemoryExtensions"; internal const string AsSpanMethodName = "AsSpan"; + + /// + /// The name of marker method for an extension type. + /// + internal const string ExtensionMarkerMethodName = "$"; } } diff --git a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs index f8f1d60ede2ea..ca95cc1bf5288 100644 --- a/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/CSharpDeterministicKeyBuilderTests.cs @@ -55,7 +55,7 @@ protected override CSharpCompilation CreateCompilation(SyntaxTree[] syntaxTrees, public void VerifyUpToDate() { verifyCount(11); - verifyCount(12); + verifyCount(13); verifyCount(63); verifyCount(9); diff --git a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.Helpers.cs b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.Helpers.cs index 101aa11f3b4a1..3ea136dda94f3 100644 --- a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.Helpers.cs +++ b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.Helpers.cs @@ -54,5 +54,10 @@ private sealed class Generator2 : ISourceGenerator public void Initialize(GeneratorInitializationContext context) => throw new NotImplementedException(); } + + private sealed class Generator3 : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) => throw new NotImplementedException(); + } } } diff --git a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs index 6a5c441267e7c..da69156710812 100644 --- a/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs +++ b/src/Compilers/Core/RebuildTest/DeterministicKeyBuilderTests.cs @@ -803,7 +803,8 @@ public void Generators() var array = GetGeneratorValues( CreateCompilation(Array.Empty()), new Generator(), - new Generator2()); + new Generator2(), + new Generator3().AsSourceGenerator()); var assembly = typeof(Generator).Assembly; var expected = @$" @@ -817,6 +818,11 @@ public void Generators() ""fullName"": ""{typeof(Generator2).FullName}"", ""assemblyName"": ""{assembly.FullName}"", ""mvid"": ""{DeterministicKeyBuilder.GetGuidValue(assembly.ManifestModule.ModuleVersionId)}"" + }}, + {{ + ""fullName"": ""{typeof(Generator3).FullName}"", + ""assemblyName"": ""{assembly.FullName}"", + ""mvid"": ""{DeterministicKeyBuilder.GetGuidValue(assembly.ManifestModule.ModuleVersionId)}"" }} ] "; diff --git a/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj b/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj index 2421908a81b79..d2c5a63800975 100644 --- a/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj +++ b/src/Compilers/Extension/Roslyn.Compilers.Extension.csproj @@ -29,14 +29,14 @@ - - - - - - - - + + + + + + + + diff --git a/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs b/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs index 3b326885d17b9..39342ed3a16ad 100644 --- a/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs +++ b/src/Compilers/Server/VBCSCompiler/CompilerRequestHandler.cs @@ -72,7 +72,7 @@ internal CompilerServerHost(string clientDirectory, string? sdkDirectory, ICompi ClientDirectory = clientDirectory; SdkDirectory = sdkDirectory; Logger = logger; - AnalyzerAssemblyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(Path.Combine(Path.GetTempPath(), "VBCSCompiler", "AnalyzerAssemblyLoader")); + AnalyzerAssemblyLoader = Microsoft.CodeAnalysis.AnalyzerAssemblyLoader.CreateNonLockingLoader(Path.Combine(Path.GetTempPath(), "VBCSCompiler", "AnalyzerAssemblyLoader")); } public bool TryCreateCompiler(in RunRequest request, BuildPaths buildPaths, [NotNullWhen(true)] out CommonCompiler? compiler) diff --git a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs index 4c381c8dc8d4d..bdaf520454864 100644 --- a/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/AnalyzerConsistencyCheckerTests.cs @@ -84,7 +84,7 @@ public void LoadLibraryWithMissingReference() { var directory = Temp.CreateDirectory(); _ = directory.CopyFile(TestFixture.Alpha); - var assemblyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); + var assemblyLoader = AnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); var analyzerReferences = ImmutableArray.Create(new CommandLineAnalyzerReference("Alpha.dll")); var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, assemblyLoader, Logger); @@ -95,7 +95,7 @@ public void LoadLibraryWithMissingReference() public void LoadLibraryAll() { var directory = Temp.CreateDirectory(); - var assemblyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); + var assemblyLoader = AnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); var analyzerReferences = ImmutableArray.Create( new CommandLineAnalyzerReference("Alpha.dll"), new CommandLineAnalyzerReference("Beta.dll"), @@ -110,7 +110,7 @@ public void LoadLibraryAll() public void DifferingMvidsDifferentDirectory() { var directory = Temp.CreateDirectory(); - var assemblyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); + var assemblyLoader = AnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); var key = NetStandard20.References.netstandard.GetAssemblyIdentity().PublicKey; var mvidAlpha1 = CreateNetStandardDll(directory.CreateDirectory("mvid1"), "MvidAlpha", "1.0.0.0", key, "class C { }"); @@ -135,7 +135,7 @@ public void DifferingMvidsDifferentDirectory() public void DifferingMvidsSameDirectory() { var directory = Temp.CreateDirectory(); - var assemblyLoader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); + var assemblyLoader = AnalyzerAssemblyLoader.CreateNonLockingLoader(directory.CreateDirectory("shadow").Path); var key = NetStandard20.References.netstandard.GetAssemblyIdentity().PublicKey; var mvidAlpha1 = CreateNetStandardDll(directory, "MvidAlpha", "1.0.0.0", key, "class C { }"); @@ -180,7 +180,7 @@ public void LoadingLibraryFromCompiler() // This test must use the DefaultAnalyzerAssemblyLoader as we want assembly binding redirects // to take affect here. - var assemblyLoader = new DefaultAnalyzerAssemblyLoader(); + var assemblyLoader = new AnalyzerAssemblyLoader(); var analyzerReferences = ImmutableArray.Create( new CommandLineAnalyzerReference("System.Memory.dll")); @@ -203,7 +203,7 @@ public void LoadingLibraryFromRuntime() // This test must use the DefaultAnalyzerAssemblyLoader as we want assembly binding redirects // to take affect here. - var assemblyLoader = new DefaultAnalyzerAssemblyLoader(); + var assemblyLoader = new AnalyzerAssemblyLoader(); var analyzerReferences = ImmutableArray.Create( new CommandLineAnalyzerReference("System.Core.dll")); @@ -239,7 +239,7 @@ public void LoadingSimpleLibrary() var analyzerReferences = ImmutableArray.Create(new CommandLineAnalyzerReference(compFile.Path)); - var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, new DefaultAnalyzerAssemblyLoader(), Logger); + var result = AnalyzerConsistencyChecker.Check(directory.Path, analyzerReferences, new AnalyzerAssemblyLoader(), Logger); Assert.True(result); } diff --git a/src/Compilers/Server/VBCSCompilerTests/TouchedFileLoggingTests.cs b/src/Compilers/Server/VBCSCompilerTests/TouchedFileLoggingTests.cs index 81e45b441bc92..f6c4d1eada0a2 100644 --- a/src/Compilers/Server/VBCSCompilerTests/TouchedFileLoggingTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/TouchedFileLoggingTests.cs @@ -46,7 +46,7 @@ End Class [ConditionalFact(typeof(DesktopOnly))] public void CSharpTrivialMetadataCaching() { - var loader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(Temp.CreateDirectory().Path); + var loader = AnalyzerAssemblyLoader.CreateNonLockingLoader(Temp.CreateDirectory().Path); var filelist = new List(); // Do the following compilation twice. @@ -97,7 +97,7 @@ public void CSharpTrivialMetadataCaching() [ConditionalFact(typeof(DesktopOnly))] public void VisualBasicTrivialMetadataCaching() { - var loader = DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(Temp.CreateDirectory().Path); + var loader = AnalyzerAssemblyLoader.CreateNonLockingLoader(Temp.CreateDirectory().Path); var filelist = new List(); // Do the following compilation twice. diff --git a/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerServerTests.cs index 9ab19b4b2724a..a71a309f6ea35 100644 --- a/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerServerTests.cs @@ -38,8 +38,8 @@ public class StartupTests : VBCSCompilerServerTests public async Task ShadowCopyAnalyzerAssemblyLoaderMissingDirectory() { var baseDirectory = Path.Combine(Path.GetTempPath(), TestBase.GetUniqueName()); - var loader = new ShadowCopyAnalyzerAssemblyLoader(baseDirectory: baseDirectory); - var task = loader.DeleteLeftoverDirectoriesTask; + var shadowResolver = new ShadowCopyAnalyzerPathResolver(baseDirectory); + var task = shadowResolver.DeleteLeftoverDirectoriesTask; await task; Assert.False(task.IsFaulted); } diff --git a/src/Compilers/Shared/BuildClient.cs b/src/Compilers/Shared/BuildClient.cs index 88fec0d5687c8..b54117b2b71ec 100644 --- a/src/Compilers/Shared/BuildClient.cs +++ b/src/Compilers/Shared/BuildClient.cs @@ -192,7 +192,7 @@ public Task RunCompilationAsync(IEnumerable origin private int RunLocalCompilation(string[] arguments, BuildPaths buildPaths, TextWriter textWriter) { - var loader = new DefaultAnalyzerAssemblyLoader(); + var loader = new AnalyzerAssemblyLoader(); return _compileFunc(arguments, buildPaths, textWriter, loader); } diff --git a/src/Compilers/Test/Core/Assert/ArtifactUploadUtil.cs b/src/Compilers/Test/Core/Assert/ArtifactUploadUtil.cs index cbe4b909edabd..3a1026eb28749 100644 --- a/src/Compilers/Test/Core/Assert/ArtifactUploadUtil.cs +++ b/src/Compilers/Test/Core/Assert/ArtifactUploadUtil.cs @@ -17,7 +17,7 @@ namespace Roslyn.Test.Utilities /// the test execution. This utility is helpful at getting those artifacts attached to AzDO / Helix /// when executed in our CI process. /// - /// The utilility works by collecting a set of file paths to artifacts. If the test succeeds it should + /// The utility works by collecting a set of file paths to artifacts. If the test succeeds it should /// call the method. Otherwise if the test fails an exception is generated, /// the call is skipped and in we prepare the artifacts for upload. /// diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index c709948406117..b8fb0762efd44 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -633,7 +633,8 @@ internal string VisualizeIL(CompilationTestData.MethodData methodData, bool real throw new Exception($"Failed to extract PDB information. PdbToXmlConverter returned:{Environment.NewLine}{actualPdbXml}"); } - var methodDef = (Cci.IMethodDefinition)methodData.Method.GetCciAdapter(); + var method = methodData.Method.PartialDefinitionPart ?? methodData.Method; + var methodDef = (Cci.IMethodDefinition)method.GetCciAdapter(); var methodToken = MetadataTokens.GetToken(_testData.MetadataWriter.GetMethodDefinitionOrReferenceHandle(methodDef)); var xmlDocument = XElement.Parse(actualPdbXml); var xmlMethod = ILValidation.GetMethodElement(xmlDocument, methodToken); diff --git a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs index 0ce0f38fbe4ac..a66fda3648859 100644 --- a/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs +++ b/src/Compilers/Test/Core/Diagnostics/CommonDiagnosticAnalyzers.cs @@ -1556,7 +1556,7 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) var sortedCallbackCodeBlockNames = new SortedSet(); if (_testIsGeneratedCodeInCallbacks) { - // Test all remaining analysis contexts that expose "IsGeneratdCode" flag + // Test all remaining analysis contexts that expose "IsGeneratedCode" flag context.RegisterSyntaxNodeAction(context => sortedCallbackSyntaxNodeNames.Add($"{context.ContainingSymbol.Name}(IsGeneratedCode:{context.IsGeneratedCode})"), ImmutableArray.Create(ClassDeclarationSyntaxKind)); diff --git a/src/Compilers/Test/Core/Metadata/ILBuilderVisualizer.cs b/src/Compilers/Test/Core/Metadata/ILBuilderVisualizer.cs index 845c29432a88f..39c539e512d61 100644 --- a/src/Compilers/Test/Core/Metadata/ILBuilderVisualizer.cs +++ b/src/Compilers/Test/Core/Metadata/ILBuilderVisualizer.cs @@ -74,7 +74,7 @@ public override string VisualizeLocalType(object type) /// private static List GetHandlerSpans(ImmutableArray regions) { - if (regions.Length == 0) + if (regions.IsDefaultOrEmpty) { return null; } diff --git a/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs b/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs index efda098d14017..1b71535ce293f 100644 --- a/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs +++ b/src/Compilers/Test/Core/SourceGeneration/TestGenerators.cs @@ -46,7 +46,7 @@ public void Initialize(GeneratorInitializationContext context) } /// - /// A generator that produces diagnostics against existng source trees, rather than generating new content. + /// A generator that produces diagnostics against existing source trees, rather than generating new content. /// internal class DiagnosticProducingGenerator : ISourceGenerator { @@ -77,7 +77,7 @@ public SingleFileTestGenerator2(string content, string hintName = "generatedFile } } -#pragma warning disable RS0062 // Do not implicitly capture primary constructor paramters +#pragma warning disable RS0062 // Do not implicitly capture primary constructor parameters internal class CallbackGenerator( Action onInit, Action onExecute, @@ -125,7 +125,7 @@ public void Execute(GeneratorExecutionContext context) } } } -#pragma warning restore RS0062 // Do not implicitly capture primary constructor paramters +#pragma warning restore RS0062 // Do not implicitly capture primary constructor parameters internal class CallbackGenerator2 : CallbackGenerator { diff --git a/src/Compilers/Test/Core/TestHelpers.cs b/src/Compilers/Test/Core/TestHelpers.cs index 05f8777281074..204bae9fc1819 100644 --- a/src/Compilers/Test/Core/TestHelpers.cs +++ b/src/Compilers/Test/Core/TestHelpers.cs @@ -23,6 +23,11 @@ namespace Roslyn.Test.Utilities { public static class TestHelpers { + /// + /// A long timeout used to avoid hangs in tests, where a test failure manifests as an operation never occurring. + /// + public static readonly TimeSpan HangMitigatingTimeout = TimeSpan.FromMinutes(4); + public static ImmutableDictionary CreateImmutableDictionary( IEqualityComparer comparer, params (K, V)[] entries) diff --git a/src/Compilers/Test/Core/TestableCompiler.cs b/src/Compilers/Test/Core/TestableCompiler.cs index a8cc3c9873f31..6e647596259fb 100644 --- a/src/Compilers/Test/Core/TestableCompiler.cs +++ b/src/Compilers/Test/Core/TestableCompiler.cs @@ -137,7 +137,7 @@ internal static TestableCompiler CreateCSharpNetCoreApp(params string[] commandL private sealed class CSharpCompilerImpl : CSharpCompiler { internal CSharpCompilerImpl(string[] args, BuildPaths buildPaths, TestableFileSystem? fileSystem) - : base(CSharpCommandLineParser.Default, responseFile: null, args, buildPaths, additionalReferenceDirectories: null, new DefaultAnalyzerAssemblyLoader(), fileSystem: fileSystem) + : base(CSharpCommandLineParser.Default, responseFile: null, args, buildPaths, additionalReferenceDirectories: null, new AnalyzerAssemblyLoader(), fileSystem: fileSystem) { } } @@ -204,7 +204,7 @@ internal static TestableCompiler CreateBasicNetCoreApp(params string[] commandLi private sealed class BasicCompilerImpl : VisualBasicCompiler { internal BasicCompilerImpl(string[] args, BuildPaths buildPaths, TestableFileSystem? fileSystem) - : base(VisualBasicCommandLineParser.Default, responseFile: null, args, buildPaths, additionalReferenceDirectories: null, new DefaultAnalyzerAssemblyLoader(), fileSystem: fileSystem) + : base(VisualBasicCommandLineParser.Default, responseFile: null, args, buildPaths, additionalReferenceDirectories: null, new AnalyzerAssemblyLoader(), fileSystem: fileSystem) { } } diff --git a/src/Compilers/Test/Core/Traits/CompilerFeature.cs b/src/Compilers/Test/Core/Traits/CompilerFeature.cs index 6032c211ef423..d21ca01eded5e 100644 --- a/src/Compilers/Test/Core/Traits/CompilerFeature.cs +++ b/src/Compilers/Test/Core/Traits/CompilerFeature.cs @@ -43,5 +43,6 @@ public enum CompilerFeature RecordStructs, RequiredMembers, RefLifetime, + Extensions, } } diff --git a/src/Compilers/Test/Core/Traits/Traits.cs b/src/Compilers/Test/Core/Traits/Traits.cs index 11d5b56217166..b5003cb91f2ed 100644 --- a/src/Compilers/Test/Core/Traits/Traits.cs +++ b/src/Compilers/Test/Core/Traits/Traits.cs @@ -76,6 +76,7 @@ public static class Features public const string CodeActionsConvertQueryToForEach = "CodeActions.ConvertQueryToForEach"; public const string CodeActionsConvertToRawString = "CodeActions.CodeActionsConvertToRawString"; public const string CodeActionsConvertSwitchStatementToExpression = "CodeActions.ConvertSwitchStatementToExpression"; + public const string CodeActionsConvertToExtension = "CodeActions.ConvertToExtension"; public const string CodeActionsConvertToInterpolatedString = "CodeActions.ConvertToInterpolatedString"; public const string CodeActionsConvertToIterator = "CodeActions.ConvertToIterator"; public const string CodeActionsConvertToRecord = "CodeActions.ConvertToRecord"; @@ -238,6 +239,7 @@ public static class Features public const string ConvertAutoPropertyToFullProperty = nameof(ConvertAutoPropertyToFullProperty); public const string ConvertCast = nameof(ConvertCast); public const string ConvertTypeOfToNameOf = nameof(ConvertTypeOfToNameOf); + public const string CopilotImplementNotImplementedException = nameof(CopilotImplementNotImplementedException); public const string DebuggingBreakpoints = "Debugging.Breakpoints"; public const string DebuggingDataTips = "Debugging.DataTips"; public const string DebuggingEditAndContinue = "Debugging.EditAndContinue"; @@ -289,6 +291,7 @@ public static class Features public const string NetCore = nameof(NetCore); public const string NormalizeModifiersOrOperators = nameof(NormalizeModifiersOrOperators); public const string ObjectBrowser = nameof(ObjectBrowser); + public const string OnTheFlyDocs = nameof(OnTheFlyDocs); public const string Options = nameof(Options); public const string Organizing = nameof(Organizing); public const string Outlining = nameof(Outlining); diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index 32197356378ef..cabe8fa772230 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -213,6 +213,22 @@ public NotNullIfNotNullAttribute(string parameterName) { } } "; + protected static readonly string CallerArgumentExpressionAttributeDefinition = """ + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)] + public sealed class CallerArgumentExpressionAttribute : Attribute + { + public CallerArgumentExpressionAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; } + } + } + """; + protected static readonly string IsExternalInitTypeDefinition = @" namespace System.Runtime.CompilerServices { diff --git a/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs b/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs index 8e73c67d40334..6705574b7c551 100644 --- a/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs +++ b/src/Compilers/Test/Utilities/CSharp/MockCSharpCompiler.cs @@ -30,7 +30,7 @@ public MockCSharpCompiler(string responseFile, string workingDirectory, string[] } public MockCSharpCompiler(string responseFile, BuildPaths buildPaths, string[] args, ImmutableArray analyzers = default, ImmutableArray generators = default, AnalyzerAssemblyLoader loader = null, GeneratorDriverCache driverCache = null, ImmutableArray additionalReferences = default) - : base(CSharpCommandLineParser.Default, responseFile, args, buildPaths, Environment.GetEnvironmentVariable("LIB"), loader ?? new DefaultAnalyzerAssemblyLoader(), driverCache) + : base(CSharpCommandLineParser.Default, responseFile, args, buildPaths, Environment.GetEnvironmentVariable("LIB"), loader ?? new AnalyzerAssemblyLoader(), driverCache) { _analyzers = analyzers.NullToEmpty(); _generators = generators.NullToEmpty(); diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockVbi.vb b/src/Compilers/Test/Utilities/VisualBasic/MockVbi.vb index 41b0c256906a3..64369063be1fe 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/MockVbi.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/MockVbi.vb @@ -12,7 +12,7 @@ Friend Class MockVbi Inherits VisualBasicCompiler Public Sub New(responseFile As String, workingDirectory As String, args As String()) - MyBase.New(VisualBasicCommandLineParser.Script, responseFile, args, CreateBuildPaths(workingDirectory), Nothing, New DefaultAnalyzerAssemblyLoader()) + MyBase.New(VisualBasicCommandLineParser.Script, responseFile, args, CreateBuildPaths(workingDirectory), Nothing, New AnalyzerAssemblyLoader()) End Sub Private Shared Function CreateBuildPaths(workingDirectory As String) As BuildPaths diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb b/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb index 616e61833c417..8a66be7909764 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/MockVisualBasicCompiler.vb @@ -37,7 +37,7 @@ Friend Class MockVisualBasicCompiler End Sub Public Sub New(responseFile As String, buildPaths As BuildPaths, args As String(), Optional analyzers As DiagnosticAnalyzer() = Nothing, Optional generators As ISourceGenerator() = Nothing, Optional additionalReferences As MetadataReference() = Nothing) - MyBase.New(VisualBasicCommandLineParser.Default, responseFile, args, buildPaths, Environment.GetEnvironmentVariable("LIB"), New DefaultAnalyzerAssemblyLoader()) + MyBase.New(VisualBasicCommandLineParser.Default, responseFile, args, buildPaths, Environment.GetEnvironmentVariable("LIB"), New AnalyzerAssemblyLoader()) _analyzers = analyzers.AsImmutableOrEmpty() _generators = generators.AsImmutableOrEmpty() diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index c8a52432fcef3..ea1f4b7b63046 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -715,7 +715,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Private Shared Sub GetDocumentsForMethodsAndNestedTypes(documentList As PooledHashSet(Of Cci.DebugSourceDocument), typesToProcess As ArrayBuilder(Of Cci.ITypeDefinition), context As EmitContext) - ' Temporarily disable assert to unblock getting net8.0 teststing re-nabled on Unix. Will + ' Temporarily disable assert to unblock getting net8.0 testing re-enabled on Unix. Will ' remove this shortly. ' https://github.com/dotnet/roslyn/issues/71571 ' Debug.Assert(Not context.MetadataOnly) diff --git a/src/Compilers/VisualBasic/Portable/Symbols/EventSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/EventSymbol.vb index f0e41198bddf3..b4bf4b88f4d5e 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/EventSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/EventSymbol.vb @@ -323,6 +323,27 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols End Get End Property + Private ReadOnly Property IEventSymbol_PartialDefinitionPart As IEventSymbol Implements IEventSymbol.PartialDefinitionPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + + Private ReadOnly Property IEventSymbol_PartialImplementationPart As IEventSymbol Implements IEventSymbol.PartialImplementationPart + Get + ' Feature not supported in VB + Return Nothing + End Get + End Property + + Private ReadOnly Property IEventSymbol_IsPartialDefinition As Boolean Implements IEventSymbol.IsPartialDefinition + Get + ' Feature not supported in VB + Return False + End Get + End Property + Public Overrides Sub Accept(visitor As SymbolVisitor) visitor.VisitEvent(Me) End Sub diff --git a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb index c6f532857bfda..4398a64648fb6 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/TypeSymbol.vb @@ -783,5 +783,16 @@ Done: Return Me End Function + Public ReadOnly Property IsExtension As Boolean Implements ITypeSymbol.IsExtension + Get + Return False + End Get + End Property + + Public ReadOnly Property ExtensionParameter As IParameterSymbol Implements ITypeSymbol.ExtensionParameter + Get + Return Nothing + End Get + End Property End Class End Namespace diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 81ec12c060ab6..c153096c40ad6 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5627,10 +5627,10 @@ The loaded assembly references .NET Framework, which is not supported. - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. assigning to or passing 'ByRef' properties with init-only setters diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf index b4d56c30756f5..cd99d16dde1e7 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.cs.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. - The analyzer assembly references a newer version of the compiler than the currently running version. - Sestavení analyzátoru odkazuje na novější verzi kompilátoru, než je aktuálně spuštěná verze. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Sestavení analyzátoru odkazuje na novější verzi kompilátoru, než je aktuálně spuštěná verze. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf index a87fc378808ba..f52b27726a1a1 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.de.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. - The analyzer assembly references a newer version of the compiler than the currently running version. - Die Analyzerassembly verweist auf eine neuere Version des Compilers als die derzeit ausgeführte Version. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Die Analyzerassembly verweist auf eine neuere Version des Compilers als die derzeit ausgeführte Version. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf index 381329a49c474..be3274c2999a9 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.es.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. - The analyzer assembly references a newer version of the compiler than the currently running version. - El ensamblado del analizador hace referencia a una versión más reciente del compilador que la versión que se está ejecutando actualmente. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + El ensamblado del analizador hace referencia a una versión más reciente del compilador que la versión que se está ejecutando actualmente. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf index 9c1dd2305cc38..4b05beae4f487 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.fr.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - L'assembly de l'analyseur fait référence à une version plus récente du compilateur que la version en cours d'exécution. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + L'assembly de l'analyseur fait référence à une version plus récente du compilateur que la version en cours d'exécution. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf index 26f06bcf0aa76..d7659c5a7ee3b 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.it.xlf @@ -630,13 +630,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - L'assembly dell'analizzatore fa riferimento alla versione del compilatore, che è più recente della versione attualmente in esecuzione. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + L'assembly dell'analizzatore fa riferimento alla versione del compilatore, che è più recente della versione attualmente in esecuzione. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf index e2af58578c59c..440445efa7967 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ja.xlf @@ -631,13 +631,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 - The analyzer assembly references a newer version of the compiler than the currently running version. - アナライザー アセンブリが参照しるコンパイラのバージョンは、現在実行中のバージョンよりも新しいです。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + アナライザー アセンブリが参照しるコンパイラのバージョンは、現在実行中のバージョンよりも新しいです。 diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf index 976d357dcb374..3ccca121c0470 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ko.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. - The analyzer assembly references a newer version of the compiler than the currently running version. - 분석기 어셈블리는 현재 실행 중인 버전보다 최신 버전의 컴파일러를 참조합니다. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 분석기 어셈블리는 현재 실행 중인 버전보다 최신 버전의 컴파일러를 참조합니다. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf index 070da15bf98c5..ea736d6e939fe 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pl.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. - The analyzer assembly references a newer version of the compiler than the currently running version. - Zestaw analizatora odwołuje się do nowszej wersji kompilatora niż obecnie uruchomiona wersja. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Zestaw analizatora odwołuje się do nowszej wersji kompilatora niż obecnie uruchomiona wersja. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf index 4d8313004b310..dd23654acf80a 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.pt-BR.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. - The analyzer assembly references a newer version of the compiler than the currently running version. - O assembly do analisador faz referência a uma versão mais recente do compilador do que a versão em execução no momento. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + O assembly do analisador faz referência a uma versão mais recente do compilador do que a versão em execução no momento. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf index cb28192722614..075a143a0723a 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.ru.xlf @@ -629,13 +629,13 @@ optionstrict[+|-] Принудительное применени - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". - The analyzer assembly references a newer version of the compiler than the currently running version. - Сборка анализатора ссылается на более новую версию компилятора, чем установленная сейчас. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Сборка анализатора ссылается на более новую версию компилятора, чем установленная сейчас. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf index 797293fb56cbb..6af7446cd9a65 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.tr.xlf @@ -630,13 +630,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. - The analyzer assembly references a newer version of the compiler than the currently running version. - Çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışandan daha yeni bir sürümüne başvurur. + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + Çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışandan daha yeni bir sürümüne başvurur. diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf index 3e7eddf6a3295..7a5a5f73a5c49 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hans.xlf @@ -629,13 +629,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 - The analyzer assembly references a newer version of the compiler than the currently running version. - 分析器程序集引用的编译器版本高于当前正在运行的版本。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 分析器程序集引用的编译器版本高于当前正在运行的版本。 diff --git a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf index 809204ff7d05d..440e9c48e4924 100644 --- a/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf +++ b/src/Compilers/VisualBasic/Portable/xlf/VBResources.zh-Hant.xlf @@ -630,13 +630,13 @@ - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 - The analyzer assembly references a newer version of the compiler than the currently running version. - 分析程式組件參考的編譯器版本比目前執行的版本新。 + Analyzer assembly cannot be used because it references a newer version of the compiler than the currently running version. + 分析程式組件參考的編譯器版本比目前執行的版本新。 diff --git a/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb b/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb index 2f3b931ab752d..2263160ba4391 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Semantics/StaticLocalsSemanticTests.vb @@ -1442,7 +1442,7 @@ End Class Public Sub Semantic_MaximumLength_StaticLocalIdentifier() - 'The Use of Static Locals with an identifier at maxmimum length to ensure functionality + 'The Use of Static Locals with an identifier at maximum length to ensure functionality 'works and generated backing field is correctly supported. Dim compilationDef = CreateCompilationWithMscorlib40AndVBRuntime( diff --git a/src/Compilers/VisualBasic/Test/Semantic/Compilation/GetImportScopesTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Compilation/GetImportScopesTests.vb index 524689f53a18f..3b8c725991d22 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Compilation/GetImportScopesTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Compilation/GetImportScopesTests.vb @@ -338,7 +338,7 @@ end namespace #Region "xml namespace" - Public Sub TestBeforeXmlNamespae() + Public Sub TestBeforeXmlNamespace() Dim Text = "'pos Imports " Dim scopes = GetImportScopes(Text) diff --git a/src/Compilers/VisualBasic/Test/Semantic/Semantics/BinaryOperators.vb b/src/Compilers/VisualBasic/Test/Semantic/Semantics/BinaryOperators.vb index 6950bb2c47e41..a38f792aa5360 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Semantics/BinaryOperators.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Semantics/BinaryOperators.vb @@ -59,7 +59,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics End Sub - + Public Sub Test1_Date() ' test binary operator between Date value and another type data ' call ToString() on it defeat the purpose of these scenarios @@ -381,7 +381,7 @@ False End Sub - + Public Sub Test5_DateConst() ' test binary operator between Date const and another type data ' call ToString() on it defeat the purpose of these scenarios diff --git a/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb index 6bd8efbe6c381..1188390890653 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/SourceGeneration/GeneratorDriverTests_Attributes_SimpleName.vb @@ -468,7 +468,7 @@ end class Dim source = " imports AAttribute : X - + class C end class " diff --git a/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs b/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs index 0d05a0d03f487..414e316e9ef5c 100644 --- a/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs +++ b/src/EditorFeatures/CSharp/ChangeSignature/CSharpChangeSignatureCommandHandler.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.ChangeSignature; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs index 2bc5648d8b235..6f45619a5fc92 100644 --- a/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs +++ b/src/EditorFeatures/CSharp/CompleteStatement/CompleteStatementCommandHandler.cs @@ -172,7 +172,7 @@ private static bool TryGetStartingNode( startingNode = tokenOnLeft.GetRequiredParent(); - // If the caret is before an opening delimiter or after a closing delimeter, + // If the caret is before an opening delimiter or after a closing delimiter, // start analysis with node outside of delimiters. // // Examples, diff --git a/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs b/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs index 809ef38fa78aa..202bca0eb7023 100644 --- a/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs +++ b/src/EditorFeatures/CSharp/EventHookup/EventHookupCommandHandler_TabKeyCommand.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.EventHookup; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs index 538b8250aaaae..fd247d02858f6 100644 --- a/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs +++ b/src/EditorFeatures/CSharp/Formatting/CSharpFormattingInteractionService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs index 877e0a9c72a7c..9b6ee3f333efe 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Commanding; -using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_TypeChar.cs b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_TypeChar.cs index f652c81fd8d2b..39b6b3ee56903 100644 --- a/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_TypeChar.cs +++ b/src/EditorFeatures/CSharp/RawStringLiteral/RawStringLiteralCommandHandler_TypeChar.cs @@ -53,7 +53,7 @@ private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHa var textChangeOpt = TryGenerateInitialEmptyRawString(caret.Value, cancellationToken) ?? TryGrowInitialEmptyRawString(caret.Value, cancellationToken) ?? - TryGrowRawStringDelimeters(caret.Value, cancellationToken); + TryGrowRawStringDelimiters(caret.Value, cancellationToken); if (textChangeOpt is not TextChange textChange) return false; @@ -115,8 +115,13 @@ private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHa if (token.SpanStart != start) return null; - if (token.Kind() is not (SyntaxKind.StringLiteralToken or SyntaxKind.InterpolatedStringStartToken or SyntaxKind.InterpolatedSingleLineRawStringStartToken)) + if (token.Kind() is not (SyntaxKind.StringLiteralToken or + SyntaxKind.InterpolatedStringStartToken or + SyntaxKind.InterpolatedSingleLineRawStringStartToken or + SyntaxKind.InterpolatedMultiLineRawStringStartToken)) + { return null; + } return new TextChange(new TextSpan(position + 1, 0), "\"\"\""); } @@ -124,8 +129,8 @@ private bool ExecuteCommandWorker(TypeCharCommandArgs args, Action nextCommandHa /// /// When typing " given a raw string like """$$""" (or a similar multiline form), then update the /// text to be: """"$$"""". i.e. grow both the start and end delimiters to keep the string properly - /// balanced. This differs from TryGrowRawStringDelimeters in that the language will consider that initial - /// """""" text to be a single delimeter, while we want to treat it as two. + /// balanced. This differs from TryGrowRawStringDelimiters in that the language will consider that initial + /// """""" text to be a single delimiter, while we want to treat it as two. /// private static TextChange? TryGrowInitialEmptyRawString( SnapshotPoint caret, @@ -183,7 +188,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or /// update the text to be: """" goo bar """". i.e. grow both the start and end delimiters to keep the /// string properly balanced. /// - private static TextChange? TryGrowRawStringDelimeters( + private static TextChange? TryGrowRawStringDelimiters( SnapshotPoint caret, CancellationToken cancellationToken) { @@ -191,7 +196,7 @@ SyntaxKind.InterpolatedSingleLineRawStringStartToken or var position = caret.Position; // if we have """$$" then typing `"` here should not grow the start/end quotes. we only want to grow them - // if the user is at the end of the start delimeter. + // if the user is at the end of the start delimiter. if (position < snapshot.Length && snapshot[position] == '"') return null; diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/AbstractPasteProcessor.cs b/src/EditorFeatures/CSharp/StringCopyPaste/AbstractPasteProcessor.cs index 162aada98d09b..bf2d7c8abbb5f 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/AbstractPasteProcessor.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/AbstractPasteProcessor.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.CSharp.StringCopyPaste; diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs index d1f7077462368..a2ea589d312ba 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler.cs @@ -26,7 +26,6 @@ using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.CSharp.StringCopyPaste; diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler_CutCopy.cs b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler_CutCopy.cs index b4b10da7c9c5c..85dfeaabe7766 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler_CutCopy.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteCommandHandler_CutCopy.cs @@ -13,7 +13,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.CSharp.StringCopyPaste; diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteData.cs b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteData.cs index d183eb9652434..0178a3eb22b6c 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteData.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/StringCopyPasteData.cs @@ -11,7 +11,6 @@ using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Editor.CSharp.StringCopyPaste; diff --git a/src/EditorFeatures/CSharp/StringCopyPaste/StringInfo.cs b/src/EditorFeatures/CSharp/StringCopyPaste/StringInfo.cs index fa453c402f813..b8b9e298e8091 100644 --- a/src/EditorFeatures/CSharp/StringCopyPaste/StringInfo.cs +++ b/src/EditorFeatures/CSharp/StringCopyPaste/StringInfo.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.CSharp.StringCopyPaste; diff --git a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs index 26f0775e849f4..d7f65690b4a56 100644 --- a/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs +++ b/src/EditorFeatures/CSharp/TextStructureNavigation/CSharpTextStructureNavigatorProvider.cs @@ -97,7 +97,7 @@ protected override TextExtent GetExtentOfWordFromToken(ITextStructureNavigator n contentStart++; } - var end = token.Span.End; + var end = Math.Max(contentStart, token.Span.End); var contentEnd = end; if (CharAt(contentEnd - 1) == '8') @@ -115,6 +115,8 @@ protected override TextExtent GetExtentOfWordFromToken(ITextStructureNavigator n contentEnd--; } + // Ensure that in error conditions like a naked `"` that we don't end up with invalid bounds. + contentEnd = Math.Max(contentStart, contentEnd); return (TextSpan.FromBounds(start, contentStart), TextSpan.FromBounds(contentStart, contentEnd), TextSpan.FromBounds(contentEnd, end)); } diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs index 21dc30a5381f9..5460ab773426a 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticBraceCompletionTests.cs @@ -4,7 +4,9 @@ using System; using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Formatting; @@ -1852,12 +1854,56 @@ namespace NS1 CheckReturn(session.Session, 0, expectedAfterReturn); } + [WpfFact] + public void ModernExtension() + { + var code = """ + namespace N + { + static class C + { + extension(string s) $$ + } + } + """; + + var expectedBeforeReturn = """ + namespace N + { + static class C + { + extension(string s) { } + } + } + """; + + var expectedAfterReturn = """ + namespace N + { + static class C + { + extension(string s) + { + + } + } + } + """; + using var session = CreateSession(code, parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); + Assert.NotNull(session); + + CheckStart(session.Session); + CheckText(session.Session, expectedBeforeReturn); + CheckReturn(session.Session, 12, expectedAfterReturn); + } + internal static Holder CreateSession( [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, - OptionsCollection? globalOptions = null) + OptionsCollection? globalOptions = null, + ParseOptions? parseOptions = null) { return CreateSession( - EditorTestWorkspace.CreateCSharp(code), + EditorTestWorkspace.CreateCSharp(code, parseOptions), CurlyBrace.OpenCharacter, CurlyBrace.CloseCharacter, globalOptions); } } diff --git a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs index 993f53e89eebe..e8b9f97eb9750 100644 --- a/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs +++ b/src/EditorFeatures/CSharpTest/AutomaticCompletion/AutomaticParenthesisCompletionTests.cs @@ -2,8 +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. -#nullable disable - +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.AutomaticCompletion; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -193,10 +193,26 @@ public static A Create() CheckStart(session.Session, expectValidSession: false); } - internal static Holder CreateSession(string code) + [WpfFact] + public void ExtensionParameterList_OpenParenthesis_Delete() + { + var code = """ + static class C + { + extension$$ + } + """; + + using var session = CreateSession(code, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); + Assert.NotNull(session); + CheckStart(session.Session); + CheckBackspace(session.Session); + } + + internal static Holder CreateSession(string code, ParseOptions? parseOptions = null) { return CreateSession( - EditorTestWorkspace.CreateCSharp(code), + EditorTestWorkspace.CreateCSharp(code, parseOptions), Parenthesis.OpenCharacter, Parenthesis.CloseCharacter); } } diff --git a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs index 28358c6810405..a84ea085613c6 100644 --- a/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs +++ b/src/EditorFeatures/CSharpTest/Classification/SyntacticClassifierTests.cs @@ -1504,8 +1504,7 @@ public async Task ShebangAsFirstCommentInNonScript(TestHost testHost) var expected = new[] { - PPKeyword("#"), - PPText("!/usr/bin/env scriptcs"), + Comment("#!/usr/bin/env scriptcs"), Identifier("System"), Operators.Dot, Identifier("Console"), diff --git a/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests.cs index 553f3dc7c3415..7a55d39720be5 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/AddUsing/AddUsingTests.cs @@ -3,13 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs index cacb71edb2822..df6fe69dee917 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/GenerateType/GenerateTypeTests_Dialog.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.GenerateType; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs index 51e66334cc26d..ba0ebdce47118 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/InitializeParameter/AddParameterCheckTests.cs @@ -30,7 +30,7 @@ public async Task TestEmptyFile() } [Fact] - public async Task TestSimpleReferenceType_AlreadyNullChecked() + public async Task TestSimpleReferenceType_AlreadyNullChecked1() { var testCode = """ using System; @@ -53,6 +53,27 @@ public C([||]string s) }.RunAsync(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61181")] + public async Task TestSimpleReferenceType_AlreadyNullChecked2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public C([||]string s) + { + ArgumentNullException.ThrowIfNull(s); + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + [Fact] public async Task TestSimpleReferenceType() { @@ -83,6 +104,36 @@ public C(string s) """); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61181")] + public async Task TestSimpleReferenceType_ThrowIfNull() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public C([||]string s) + { + } + } + """, + FixedCode = """ + using System; + + class C + { + public C(string s) + { + ArgumentNullException.ThrowIfNull(s); + } + } + """, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + [Fact] public async Task TestSimpleReferenceType_CSharp6() { @@ -521,45 +572,81 @@ public C([||]string a, string b, string c) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string a, string b, string c) - {{ - if (string.IsNullOrEmpty(a)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" + class C + { + public C(string a, string b, string c) + { + if (string.IsNullOrEmpty(a)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" " """, """ \" - """)}"", nameof(a)); - }} + """)}}", nameof(a)); + } - if (string.IsNullOrEmpty(b)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(b)}").Replace(""" + if (string.IsNullOrEmpty(b)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(b)}").Replace(""" " """, """ \" - """)}"", nameof(b)); - }} + """)}}", nameof(b)); + } - if (string.IsNullOrEmpty(c)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" + if (string.IsNullOrEmpty(c)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" " """, """ \" - """)}"", nameof(c)); - }} - }} -}}", + """)}}", nameof(c)); + } + } + } + """, CodeActionIndex = 3, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters) }.RunAsync(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61181")] + public async Task TestMultiNullableParameters_Net7() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public C([||]string a, string b, string c) + { + } + } + """, + FixedCode = $$""" + using System; + + class C + { + public C(string a, string b, string c) + { + ArgumentException.ThrowIfNullOrEmpty(a); + ArgumentException.ThrowIfNullOrEmpty(b); + ArgumentException.ThrowIfNullOrEmpty(c); + } + } + """, + CodeActionIndex = 3, + CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters), + ReferenceAssemblies = ReferenceAssemblies.Net.Net70, + }.RunAsync(); + } + [Fact] public async Task TestMultiNullableParametersSomeNullableReferenceTypes() { @@ -577,33 +664,35 @@ public C([||]string a, string b, string? c) } } """, - FixedCode = @$"#nullable enable + FixedCode = $$""" + #nullable enable -using System; + using System; -class C -{{ - public C(string a, string b, string? c) - {{ - if (string.IsNullOrEmpty(a)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" + class C + { + public C(string a, string b, string? c) + { + if (string.IsNullOrEmpty(a)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" " """, """ \" - """)}"", nameof(a)); - }} + """)}}", nameof(a)); + } - if (string.IsNullOrEmpty(b)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(b)}").Replace(""" + if (string.IsNullOrEmpty(b)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(b)}").Replace(""" " """, """ \" - """)}"", nameof(b)); - }} - }} -}}", + """)}}", nameof(b)); + } + } + } + """, CodeActionIndex = 3, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters) }.RunAsync(); @@ -640,31 +729,33 @@ public C(string a, [||]bool b, string c) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string a, bool b, string c) - {{ - if (string.IsNullOrEmpty(a)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" + class C + { + public C(string a, bool b, string c) + { + if (string.IsNullOrEmpty(a)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" " """, """ \" - """)}"", nameof(a)); - }} + """)}}", nameof(a)); + } - if (string.IsNullOrEmpty(c)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" + if (string.IsNullOrEmpty(c)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" " """, """ \" - """)}"", nameof(c)); - }} - }} -}}", + """)}}", nameof(c)); + } + } + } + """, CodeActionIndex = 0, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters) }.RunAsync(); @@ -685,31 +776,33 @@ public C([||]string a, bool b, string c) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string a, bool b, string c) - {{ - if (string.IsNullOrEmpty(a)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" + class C + { + public C(string a, bool b, string c) + { + if (string.IsNullOrEmpty(a)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" " """, """ \" - """)}"", nameof(a)); - }} + """)}}", nameof(a)); + } - if (string.IsNullOrEmpty(c)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" + if (string.IsNullOrEmpty(c)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" " """, """ \" - """)}"", nameof(c)); - }} - }} -}}", + """)}}", nameof(c)); + } + } + } + """, CodeActionIndex = 3, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters) }.RunAsync(); @@ -730,36 +823,38 @@ public C([||]string a, object b, string c) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string a, object b, string c) - {{ - if (string.IsNullOrEmpty(a)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" + class C + { + public C(string a, object b, string c) + { + if (string.IsNullOrEmpty(a)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(a)}").Replace(""" " """, """ \" - """)}"", nameof(a)); - }} + """)}}", nameof(a)); + } - if (b is null) - {{ - throw new ArgumentNullException(nameof(b)); - }} + if (b is null) + { + throw new ArgumentNullException(nameof(b)); + } - if (string.IsNullOrEmpty(c)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" + if (string.IsNullOrEmpty(c)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(c)}").Replace(""" " """, """ \" - """)}"", nameof(c)); - }} - }} -}}", + """)}}", nameof(c)); + } + } + } + """, CodeActionIndex = 3, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_null_checks_for_all_parameters) }.RunAsync(); @@ -1864,22 +1959,24 @@ public C([||]string s) } } """, - FixedCode = $@"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} - }} -}}", + """)}}", nameof(s)); + } + } + } + """, CodeActionIndex = 1, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_string_IsNullOrEmpty_check) }.RunAsync(); @@ -1900,27 +1997,61 @@ public C([||]string s) } } """, - FixedCode = $@"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrWhiteSpace(s)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_whitespace, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrWhiteSpace(s)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_whitespace, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} - }} -}}", + """)}}", nameof(s)); + } + } + } + """, CodeActionIndex = 2, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_string_IsNullOrWhiteSpace_check) }.RunAsync(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/61181")] + public async Task TestSpecialStringCheck2_Net8() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public C([||]string s) + { + } + } + """, + FixedCode = $$""" + using System; + + class C + { + public C(string s) + { + ArgumentException.ThrowIfNullOrWhiteSpace(s); + } + } + """, + CodeActionIndex = 2, + CodeActionEquivalenceKey = nameof(FeaturesResources.Add_string_IsNullOrWhiteSpace_check), + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + }.RunAsync(); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51338")] [UseCulture("de-DE", "de-DE")] public async Task TestSpecialStringCheck3() @@ -1937,22 +2068,24 @@ public C([||]string s) } } """, - FixedCode = $@"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} - }} -}}", + """)}}", nameof(s)); + } + } + } + """, CodeActionIndex = 1, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_string_IsNullOrEmpty_check) }.RunAsync(); @@ -1991,22 +2124,24 @@ static void Main([||]String bar) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class Program -{{ - static void Main(String bar) - {{ - if (String.IsNullOrEmpty(bar)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(bar)}").Replace(""" + class Program + { + static void Main(String bar) + { + if (String.IsNullOrEmpty(bar)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(bar)}").Replace(""" " """, """ \" - """)}"", nameof(bar)); - }} - }} -}}", + """)}}", nameof(bar)); + } + } + } + """, CodeActionIndex = 1, CodeActionEquivalenceKey = nameof(FeaturesResources.Add_string_IsNullOrEmpty_check), Options = @@ -2583,20 +2718,22 @@ public C($$string s) } } """, - FixedCode = $@"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} -}}", + """)}}", nameof(s)); + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, @@ -2623,20 +2760,22 @@ public C($$string s) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} -}}", + """)}}", nameof(s)); + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, @@ -2663,22 +2802,24 @@ public C($$string s) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} - }} -}}", + """)}}", nameof(s)); + } + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, @@ -2705,19 +2846,21 @@ public C($$string s) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} -}}", + """)}}", nameof(s)); + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, @@ -2744,19 +2887,21 @@ public C($$string s) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} -}}", + """)}}", nameof(s)); + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, @@ -2783,22 +2928,24 @@ public C($$string s) } } """, - FixedCode = @$"using System; + FixedCode = $$""" + using System; -class C -{{ - public C(string s) - {{ - if (string.IsNullOrEmpty(s)) - {{ - throw new ArgumentException($""{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" + class C + { + public C(string s) + { + if (string.IsNullOrEmpty(s)) + { + throw new ArgumentException($"{{string.Format(FeaturesResources._0_cannot_be_null_or_empty, "{nameof(s)}").Replace(""" " """, """ \" - """)}"", nameof(s)); - }} - }} -}}", + """)}}", nameof(s)); + } + } + } + """, Options = { { CSharpCodeStyleOptions.PreferThrowExpression, false }, diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs index fa203ef7f5a51..6f6d30a1ece97 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveScope.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings.MoveType; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs index a698667972f75..1fd02d76caab3 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/MoveType/MoveTypeTests.MoveToNewFile.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs index b6377fe8b2dbd..52708bf17e7d4 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/SyncNamespace/CSharpSyncNamespaceTestsBase.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Test.Utilities; using Roslyn.Utilities; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs index 73ee6614a0c5b..d815e3673d99e 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/CrefCompletionProviderTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; @@ -15,7 +14,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using RoslynTrigger = Microsoft.CodeAnalysis.Completion.CompletionTrigger; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProviderTests.cs index da1717ad8e0d8..f8a06a905dd66 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProviderTests.cs @@ -169,7 +169,7 @@ class Bar : IGoo { void IGoo.Goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -549,7 +549,7 @@ class Bar : IGoo { int IGoo.Generic(K key, V value) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -1214,7 +1214,7 @@ class C : IFoo { static bool IFoo.TryDecode(out DecodeError? decodeError, out string? errorMessage) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs index d04b2b1cb67c4..8f607e10d4a66 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ExtensionMethodImportCompletionProviderTests.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion.Data; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs index 488b059558925..4c5e053f27347 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests.cs @@ -1878,7 +1878,7 @@ public override int goo { get { - return base.goo;$$ + [|return base.goo;|] } set @@ -1921,7 +1921,7 @@ public override int goo { get { - return base.goo;$$ + [|return base.goo;|] } set @@ -1963,7 +1963,7 @@ public override int goo { get { - return base.goo;$$ + [|return base.goo;|] } set @@ -2005,7 +2005,7 @@ public override int goo { set { - base.goo = value;$$ + [|base.goo = value;|] } } } @@ -2041,7 +2041,7 @@ public override int goo { get { - return base.goo;$$ + [|return base.goo;|] } } } @@ -2102,7 +2102,7 @@ public override int this[[MyPublic] int i] { get { - return base[i];$$ + [|return base[i];|] } set @@ -2392,7 +2392,7 @@ public override T this[int i] { get { - return base[i];$$ + [|return base[i];|] } set @@ -2435,7 +2435,7 @@ public override T this[int i] { get { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } set @@ -2624,7 +2624,7 @@ public override int @class { get { - return base.@class;$$ + [|return base.@class;|] } set @@ -2939,7 +2939,7 @@ public override int Goo { get { - return base.Goo;$$ + [|return base.Goo;|] } } } @@ -3594,7 +3594,7 @@ public override required int Prop { get { - return base.Prop;$$ + [|return base.Prop;|] } } } @@ -3635,7 +3635,7 @@ public override required int Prop { get { - return base.Prop;$$ + [|return base.Prop;|] } } } @@ -3676,7 +3676,7 @@ public override required int Prop { get { - return base.Prop;$$ + [|return base.Prop;|] } } } @@ -3743,7 +3743,7 @@ public override int this[int i] { get { - return base[i];$$ + [|return base[i];|] } set @@ -3885,7 +3885,7 @@ public override int goo { get { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } set diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests_ExpressionBody.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests_ExpressionBody.cs index 9be11ad9073a2..44b6bea63e94b 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests_ExpressionBody.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/OverrideCompletionProviderTests_ExpressionBody.cs @@ -51,7 +51,7 @@ class B public virtual int A { get; set; } class C : B { - public override int A { get => base.A$$; set => base.A = value; } + public override int A { get => [|base.A|]; set => base.A = value; } } } """; @@ -79,7 +79,7 @@ class B public virtual int A { get; } class C : B { - public override int A => base.A;$$ + public override int A => [|base.A|]; } } """; @@ -107,7 +107,7 @@ class B public virtual int A() => 2; class C : B { - public override int A() => base.A();$$ + public override int A() => [|base.A()|]; } } """; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs index ad53c33da24ba..6b09c636c744f 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/PartialMethodCompletionProviderTests.cs @@ -417,7 +417,7 @@ partial class c partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -444,7 +444,7 @@ partial class c public partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -471,7 +471,7 @@ partial class c partial void goo(T bar) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -498,7 +498,7 @@ partial class c public partial void goo(T bar) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -525,7 +525,7 @@ partial class c partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -552,7 +552,7 @@ partial class c private partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -585,7 +585,7 @@ partial class c { partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -618,7 +618,7 @@ partial class c { public partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -645,7 +645,7 @@ partial struct c partial void goo() { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } """; @@ -744,7 +744,7 @@ partial class Bar async partial void Goo() { - throw new NotImplementedException();$$ + [|throw new NotImplementedException();|] } } """; @@ -775,7 +775,7 @@ partial class Bar public async partial void Goo() { - throw new NotImplementedException();$$ + [|throw new NotImplementedException();|] } } """; @@ -806,7 +806,7 @@ partial class Bar partial void Goo() { - throw new NotImplementedException();$$ + [|throw new NotImplementedException();|] } } """; @@ -838,7 +838,7 @@ partial class PClass partial void PMethod(int i) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } } @@ -870,7 +870,7 @@ partial class PClass public partial void PMethod(int i) { - throw new System.NotImplementedException();$$ + [|throw new System.NotImplementedException();|] } } } @@ -903,7 +903,7 @@ partial class Bar partial class Bar { partial void Foo(); - partial void Foo() => throw new NotImplementedException();$$ + partial void Foo() => [|throw new NotImplementedException()|]; } """; @@ -936,7 +936,7 @@ partial class Bar partial class Bar { public partial void Foo(); - public partial void Foo() => throw new NotImplementedException();$$ + public partial void Foo() => [|throw new NotImplementedException()|]; } """ ; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs index eea714b6f5cb6..eafd50df635d8 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SuggestionModeCompletionProviderTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index 852c05cdd1aed..e852e3ab07c26 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -15080,6 +15080,22 @@ await VerifyExpectedItemsAsync(markup, [ ]); } + [Theory, CombinatorialData] + public async Task PartialPropertyOrConstructor( + [CombinatorialValues("class", "struct", "record", "record class", "record struct", "interface")] string typeKind, + [CombinatorialValues("", "public", "private", "static", "extern")] string modifiers) + { + var markup = $$""" + partial {{typeKind}} C + { + {{modifiers}} partial $$ + } + """; + await VerifyExpectedItemsAsync(markup, [ + ItemExpectation.Exists("C"), + ]); + } + private static string MakeMarkup([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string source, string languageVersion = "Preview") { return $$""" diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs index 199cf2081e86b..eaa5f45171cc0 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests.Diagnostics; @@ -50,7 +49,9 @@ public async Task DiagnosticAnalyzerDriverAllInOne() SyntaxKind.RecordDeclaration, SyntaxKind.CollectionExpression, SyntaxKind.ExpressionElement, - SyntaxKind.SpreadElement + SyntaxKind.SpreadElement, + // Tracked by https://github.com/dotnet/roslyn/issues/76130 Add to all-in-one + SyntaxKind.ExtensionDeclaration, }; var analyzer = new CSharpTrackingDiagnosticAnalyzer(); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs index fa125e94658f5..3330a4014a865 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTrackingServiceTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,7 +13,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests; diff --git a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs index 2116634f0c926..13eb5390aa1f5 100644 --- a/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ExtractInterface/ExtractInterfaceTests.cs @@ -10,6 +10,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.ExtractInterface; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; @@ -2087,4 +2088,24 @@ public static void M() { } await TestExtractInterfaceCodeActionCSharpAsync(markup, expectedMarkup); } + + [WpfFact] + public async Task ExtractInterface_Invocation_FromExtension() + { + var markup = """ + using System; + + static class C + { + $$extension(string s) + { + public void Goo() { } + } + } + """; + + await TestExtractInterfaceCommandCSharpAsync( + markup, expectedSuccess: false, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); + } } diff --git a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs index 3a71fabe43350..e34b8bd657eba 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/CodeCleanupTests.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeStyle; @@ -22,7 +21,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs index 443014bd748e9..38c007dd89a04 100644 --- a/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs +++ b/src/EditorFeatures/CSharpTest/Formatting/RazorLineFormattingOptionsTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests; diff --git a/src/EditorFeatures/CSharpTest/Intents/IntentTestsBase.cs b/src/EditorFeatures/CSharpTest/Intents/IntentTestsBase.cs index 760b1ca4465a7..e1f1635e7463c 100644 --- a/src/EditorFeatures/CSharpTest/Intents/IntentTestsBase.cs +++ b/src/EditorFeatures/CSharpTest/Intents/IntentTestsBase.cs @@ -115,7 +115,7 @@ internal static async Task> GetIntentsAsync( // Get the text change to pass into the API that rewinds the current document to the prior document. var currentDocument = currentTextBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); var textDiffService = workspace.CurrentSolution.Services.GetRequiredService(); - var changes = await textDiffService.GetTextChangesAsync(currentDocument, priorDocument, CancellationToken.None).ConfigureAwait(false); + var changes = await textDiffService.GetTextChangesAsync(currentDocument!, priorDocument, CancellationToken.None).ConfigureAwait(false); // Get the current snapshot span to pass in. var currentSnapshot = new SnapshotSpan(currentTextBuffer.CurrentSnapshot, new Span(0, currentTextBuffer.CurrentSnapshot.Length)); diff --git a/src/EditorFeatures/CSharpTest/MoveToNamespace/MoveToNamespaceTests.cs b/src/EditorFeatures/CSharpTest/MoveToNamespace/MoveToNamespaceTests.cs index e51030176bce5..9bd49914cde6a 100644 --- a/src/EditorFeatures/CSharpTest/MoveToNamespace/MoveToNamespaceTests.cs +++ b/src/EditorFeatures/CSharpTest/MoveToNamespace/MoveToNamespaceTests.cs @@ -1532,4 +1532,34 @@ struct MyType2 { {"A.MyType", "B.MyType" } }); + + [Theory] + [InlineData("class MyClass[||](int x, int y)")] + [InlineData("class [||]MyClass(int x, int y)")] + [InlineData("class MyC[||]lass(int x, int y)")] + public Task MoveToNamespace_PrimaryConstructor(string decl) + => TestMoveToNamespaceAsync( + $$""" + namespace A; + + {{decl}} + { + public int X => x; + public int Y => y; + } + """, + expectedMarkup: """ + namespace {|Warning:B|}; + + class MyClass(int x, int y) + { + public int X => x; + public int Y => y; + } + """, + targetNamespace: "B", + expectedSymbolChanges: new Dictionary() + { + {"A.MyClass", "B.MyClass" } + }); } diff --git a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs index a38c21e711418..da281b494c550 100644 --- a/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs +++ b/src/EditorFeatures/CSharpTest/NavigateTo/NavigateToTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; @@ -40,45 +39,45 @@ await TestAsync(testHost, composition, "", async w => public async Task FindClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + testHost, composition, """ + class Goo + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindRecord(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -record Goo -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + testHost, composition, """ + record Goo + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindRecordClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -record class Goo -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + testHost, composition, """ + record class Goo + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] @@ -126,324 +125,322 @@ await TestAsync(testHost, composition, content, async w => public async Task FindVerbatimClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class @static -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("static")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "static", "[|static|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + testHost, composition, """ + class @static + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("static")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "static", "[|static|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - // Check searching for @static too - item = (await _aggregator.GetItemsAsync("@static")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "static", "[|static|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + // Check searching for @static too + item = (await _aggregator.GetItemsAsync("@static")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "static", "[|static|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindNestedClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - class Bar - { - internal class DogBed - { - } - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("DogBed")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "DogBed", "[|DogBed|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + testHost, composition, """ + class Goo + { + class Bar + { + internal class DogBed + { + } + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("DogBed")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "DogBed", "[|DogBed|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindMemberInANestedClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - class Bar - { - class DogBed - { - public void Method() + testHost, composition, """ + class Goo { + class Bar + { + class DogBed + { + public void Method() + { + } + } + } } - } - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Method")).Single(); - VerifyNavigateToResultItem(item, "Method", "[|Method|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo.Bar.DogBed", "Test")); - }); + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Method")).Single(); + VerifyNavigateToResultItem(item, "Method", "[|Method|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo.Bar.DogBed", "Test")); + }); } [Theory, CombinatorialData] public async Task FindGenericClassWithConstraints(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -using System.Collections; + testHost, composition, """ + using System.Collections; -class Goo where T : IEnumerable -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + class Goo where T : IEnumerable + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindGenericMethodWithConstraints(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -using System; + testHost, composition, """ + using System; -class Goo -{ - public void Bar(T item) where T : IComparable - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Bar")).Single(); - VerifyNavigateToResultItem(item, "Bar", "[|Bar|](T)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + class Goo + { + public void Bar(T item) where T : IComparable + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Bar")).Single(); + VerifyNavigateToResultItem(item, "Bar", "[|Bar|](T)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindPartialClass(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -public partial class Goo -{ - int a; -} - -partial class Goo -{ - int b; -} -""", async w => - { - var expecteditem1 = new NavigateToItem("Goo", NavigateToItemKind.Class, "csharp", null, null, s_emptyExactPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem1 }; + testHost, composition, """ + public partial class Goo + { + int a; + } - var items = await _aggregator.GetItemsAsync("Goo"); + partial class Goo + { + int b; + } + """, async w => + { + var items = await _aggregator.GetItemsAsync("Goo"); - VerifyNavigateToResultItems(expecteditems, items); - }); + var expecteditem1 = new NavigateToItem("Goo", NavigateToItemKind.Class, "csharp", null, null, s_emptyExactPatternMatch, null); + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); + }); } [Theory, CombinatorialData] public async Task FindTypesInMetadata(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -using System; + testHost, composition, """ + using System; -Class Program { FileStyleUriParser f; } -""", async w => - { - var items = await _aggregator.GetItemsAsync("FileStyleUriParser"); - Assert.Equal(0, items.Count()); - }); + Class Program { FileStyleUriParser f; } + """, async w => + { + var items = await _aggregator.GetItemsAsync("FileStyleUriParser"); + Assert.Equal(0, items.Count()); + }); } [Theory, CombinatorialData] public async Task FindClassInNamespace(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -namespace Bar -{ - class Goo - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); - }); + testHost, composition, """ + namespace Bar + { + class Goo + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Class, Glyph.ClassInternal); + }); } [Theory, CombinatorialData] public async Task FindStruct(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -struct Bar -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("B")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Bar", "[|B|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Structure, Glyph.StructureInternal); - }); + testHost, composition, """ + struct Bar + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("B")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Bar", "[|B|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Structure, Glyph.StructureInternal); + }); } [Theory, CombinatorialData] public async Task FindEnum(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -enum Colors -{ - Red, - Green, - Blue -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Colors")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "Colors", "[|Colors|]", PatternMatchKind.Exact, NavigateToItemKind.Enum, Glyph.EnumInternal); - }); + testHost, composition, """ + enum Colors + { + Red, + Green, + Blue + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Colors")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "Colors", "[|Colors|]", PatternMatchKind.Exact, NavigateToItemKind.Enum, Glyph.EnumInternal); + }); } [Theory, CombinatorialData] public async Task FindEnumMember(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -enum Colors -{ - Red, - Green, - Blue -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("R")).Single(); - VerifyNavigateToResultItem(item, "Red", "[|R|]ed", PatternMatchKind.Prefix, NavigateToItemKind.EnumItem, Glyph.EnumMemberPublic); - }); + testHost, composition, """ + enum Colors + { + Red, + Green, + Blue + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("R")).Single(); + VerifyNavigateToResultItem(item, "Red", "[|R|]ed", PatternMatchKind.Prefix, NavigateToItemKind.EnumItem, Glyph.EnumMemberPublic); + }); } [Theory, CombinatorialData] public async Task FindField1(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int bar; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("b")).Single(); - VerifyNavigateToResultItem(item, "bar", "[|b|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + int bar; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("b")).Single(); + VerifyNavigateToResultItem(item, "bar", "[|b|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindField2(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int bar; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("ba")).Single(); - VerifyNavigateToResultItem(item, "bar", "[|ba|]r", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + int bar; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("ba")).Single(); + VerifyNavigateToResultItem(item, "bar", "[|ba|]r", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindField3(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int bar; -} -""", async w => - { - Assert.Empty(await _aggregator.GetItemsAsync("ar")); - }); + testHost, composition, """ + class Goo + { + int bar; + } + """, async w => + { + Assert.Empty(await _aggregator.GetItemsAsync("ar")); + }); } [Theory, CombinatorialData] public async Task FindVerbatimField(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int @string; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("string")).Single(); - VerifyNavigateToResultItem(item, "string", "[|string|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + testHost, composition, """ + class Goo + { + int @string; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("string")).Single(); + VerifyNavigateToResultItem(item, "string", "[|string|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - // Check searching for@string too - item = (await _aggregator.GetItemsAsync("@string")).Single(); - VerifyNavigateToResultItem(item, "string", "[|string|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + // Check searching for@string too + item = (await _aggregator.GetItemsAsync("@string")).Single(); + VerifyNavigateToResultItem(item, "string", "[|string|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindPtrField1(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int* bar; -} -""", async w => - { - Assert.Empty(await _aggregator.GetItemsAsync("ar")); - }); + testHost, composition, """ + class Goo + { + int* bar; + } + """, async w => + { + Assert.Empty(await _aggregator.GetItemsAsync("ar")); + }); } [Theory, CombinatorialData] public async Task FindPtrField2(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int* bar; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("b")).Single(); - VerifyNavigateToResultItem(item, "bar", "[|b|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate); - }); + testHost, composition, """ + class Goo + { + int* bar; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("b")).Single(); + VerifyNavigateToResultItem(item, "bar", "[|b|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Field, Glyph.FieldPrivate); + }); } [Theory, CombinatorialData] public async Task FindConstField(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - const int bar = 7; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("ba")).Single(); - VerifyNavigateToResultItem(item, "bar", "[|ba|]r", PatternMatchKind.Prefix, NavigateToItemKind.Constant, Glyph.ConstantPrivate); - }); + testHost, composition, """ + class Goo + { + const int bar = 7; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("ba")).Single(); + VerifyNavigateToResultItem(item, "bar", "[|ba|]r", PatternMatchKind.Prefix, NavigateToItemKind.Constant, Glyph.ConstantPrivate); + }); } [Theory, CombinatorialData] @@ -472,124 +469,124 @@ await TestAsync(testHost, composition, program, async w => public async Task FindAutoProperty(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - int Bar { get; set; } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("B")).Single(); - VerifyNavigateToResultItem(item, "Bar", "[|B|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Property, Glyph.PropertyPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + int Bar { get; set; } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("B")).Single(); + VerifyNavigateToResultItem(item, "Bar", "[|B|]ar", PatternMatchKind.Prefix, NavigateToItemKind.Property, Glyph.PropertyPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindMethod(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - void DoSomething(); -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("DS")).Single(); - VerifyNavigateToResultItem(item, "DoSomething", "[|D|]o[|S|]omething()", PatternMatchKind.CamelCaseExact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + void DoSomething(); + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("DS")).Single(); + VerifyNavigateToResultItem(item, "DoSomething", "[|D|]o[|S|]omething()", PatternMatchKind.CamelCaseExact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindVerbatimMethod(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - void @static(); -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("static")).Single(); - VerifyNavigateToResultItem(item, "static", "[|static|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + testHost, composition, """ + class Goo + { + void @static(); + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("static")).Single(); + VerifyNavigateToResultItem(item, "static", "[|static|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - // Verify if we search for @static too - item = (await _aggregator.GetItemsAsync("@static")).Single(); - VerifyNavigateToResultItem(item, "static", "[|static|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + // Verify if we search for @static too + item = (await _aggregator.GetItemsAsync("@static")).Single(); + VerifyNavigateToResultItem(item, "static", "[|static|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindParameterizedMethod(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - void DoSomething(int a, string b) - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("DS")).Single(); - VerifyNavigateToResultItem(item, "DoSomething", "[|D|]o[|S|]omething(int, string)", PatternMatchKind.CamelCaseExact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + void DoSomething(int a, string b) + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("DS")).Single(); + VerifyNavigateToResultItem(item, "DoSomething", "[|D|]o[|S|]omething(int, string)", PatternMatchKind.CamelCaseExact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindConstructor(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - public Goo() - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + public Goo() + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindParameterizedConstructor(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - public Goo(int i) - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|](int)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + public Goo(int i) + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|](int)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindStaticConstructor(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - static Goo() - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method && t.Name != ".ctor"); - VerifyNavigateToResultItem(item, "Goo", "[|Goo|].static Goo()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + static Goo() + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(t => t.Kind == NavigateToItemKind.Method && t.Name != ".ctor"); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|].static Goo()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] @@ -598,11 +595,10 @@ public async Task FindPartialMethods(TestHost testHost, Composition composition) await TestAsync(testHost, composition, "partial class Goo { partial void Bar(); } partial class Goo { partial void Bar() { Console.Write(\"hello\"); } }", async w => { var expecteditem1 = new NavigateToItem("Bar", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem1 }; var items = await _aggregator.GetItemsAsync("Bar"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); }); } @@ -612,46 +608,81 @@ public async Task FindPartialProperties(TestHost testHost, Composition compositi await TestAsync(testHost, composition, "partial class Goo { partial int Prop { get; set; } } partial class Goo { partial int Prop { get => 1; set { } } }", async w => { var expecteditem1 = new NavigateToItem("Prop", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem1 }; var items = await _aggregator.GetItemsAsync("Prop"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); }); } [Theory, CombinatorialData] - public async Task FindPartialMethodDefinitionOnly(TestHost testHost, Composition composition) + public async Task FindPartialEvents(TestHost testHost, Composition composition) { - await TestAsync( -testHost, composition, """ -partial class Goo -{ - partial void Bar(); -} -""", async w => + await TestAsync(testHost, composition, """ + partial class C + { + partial event System.Action E; + partial event System.Action E { add { } remove { } } + } + """, async w => { - var item = (await _aggregator.GetItemsAsync("Bar")).Single(); - VerifyNavigateToResultItem(item, "Bar", "[|Bar|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_1_2, "Goo", "test1.cs", "Test")); + var expecteditem1 = new NavigateToItem("E", NavigateToItemKind.Event, "csharp", null, null, s_emptyExactPatternMatch, null); + var items = await _aggregator.GetItemsAsync("E"); + + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); }); } + [Theory, CombinatorialData] + public async Task FindPartialConstructors(TestHost testHost, Composition composition) + { + await TestAsync(testHost, composition, """ + partial class C + { + public partial C(); + public partial C() { } + } + """, async w => + { + var expecteditem1 = new NavigateToItem("C", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null); + var items = (await _aggregator.GetItemsAsync("C")).Where(t => t.Kind == NavigateToItemKind.Method); + + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); + }); + } + + [Theory, CombinatorialData] + public async Task FindPartialMethodDefinitionOnly(TestHost testHost, Composition composition) + { + await TestAsync( + testHost, composition, """ + partial class Goo + { + partial void Bar(); + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Bar")).Single(); + VerifyNavigateToResultItem(item, "Bar", "[|Bar|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_1_2, "Goo", "test1.cs", "Test")); + }); + } + [Theory, CombinatorialData] public async Task FindPartialMethodImplementationOnly(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -partial class Goo -{ - partial void Bar() - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("Bar")).Single(); - VerifyNavigateToResultItem(item, "Bar", "[|Bar|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_1_2, "Goo", "test1.cs", "Test")); - }); + testHost, composition, """ + partial class Goo + { + partial void Bar() + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("Bar")).Single(); + VerifyNavigateToResultItem(item, "Bar", "[|Bar|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, string.Format(FeaturesResources.in_0_1_2, "Goo", "test1.cs", "Test")); + }); } [Theory, CombinatorialData] @@ -661,11 +692,10 @@ public async Task FindOverriddenMembers(TestHost testHost, Composition compositi await TestAsync(testHost, composition, program, async w => { var expecteditem1 = new NavigateToItem("Name", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem1 }; var items = await _aggregator.GetItemsAsync("Name"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem1], items); var item = items.ElementAt(1); var itemDisplay = item.DisplayFactory.CreateItemDisplay(item); @@ -687,15 +717,15 @@ await TestAsync(testHost, composition, program, async w => public async Task FindInterface(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -public interface IGoo -{ -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("IG")).Single(); - VerifyNavigateToResultItem(item, "IGoo", "[|IG|]oo", PatternMatchKind.Prefix, NavigateToItemKind.Interface, Glyph.InterfacePublic); - }); + testHost, composition, """ + public interface IGoo + { + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("IG")).Single(); + VerifyNavigateToResultItem(item, "IGoo", "[|IG|]oo", PatternMatchKind.Prefix, NavigateToItemKind.Interface, Glyph.InterfacePublic); + }); } [Theory, CombinatorialData] @@ -813,76 +843,73 @@ void Bar() public async Task FindDelegateInNamespace(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -namespace Goo -{ - delegate void DoStuff(); -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("DoStuff")).Single(x => x.Kind != "Method"); - VerifyNavigateToResultItem(item, "DoStuff", "[|DoStuff|]", PatternMatchKind.Exact, NavigateToItemKind.Delegate, Glyph.DelegateInternal); - }); + testHost, composition, """ + namespace Goo + { + delegate void DoStuff(); + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("DoStuff")).Single(x => x.Kind != "Method"); + VerifyNavigateToResultItem(item, "DoStuff", "[|DoStuff|]", PatternMatchKind.Exact, NavigateToItemKind.Delegate, Glyph.DelegateInternal); + }); } [Theory, CombinatorialData] public async Task FindLambdaExpression(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -using System; + testHost, composition, """ + using System; -class Goo -{ - Func sqr = x => x * x; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("sqr")).Single(); - VerifyNavigateToResultItem(item, "sqr", "[|sqr|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + class Goo + { + Func sqr = x => x * x; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("sqr")).Single(); + VerifyNavigateToResultItem(item, "sqr", "[|sqr|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindArray(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ - object[] itemArray; -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("itemArray")).Single(); - VerifyNavigateToResultItem(item, "itemArray", "[|itemArray|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); - }); + testHost, composition, """ + class Goo + { + object[] itemArray; + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("itemArray")).Single(); + VerifyNavigateToResultItem(item, "itemArray", "[|itemArray|]", PatternMatchKind.Exact, NavigateToItemKind.Field, Glyph.FieldPrivate, string.Format(FeaturesResources.in_0_project_1, "Goo", "Test")); + }); } [Theory, CombinatorialData] public async Task FindClassAndMethodWithSameName(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class Goo -{ -} + testHost, composition, """ + class Goo + { + } -class Test -{ - void Goo() - { - } -} -""", async w => - { - var expectedItems = new List + class Test { - new NavigateToItem("Goo", NavigateToItemKind.Class, "csharp", "Goo", null, s_emptyExactPatternMatch, null), - new NavigateToItem("Goo", NavigateToItemKind.Method, "csharp", "Goo", null, s_emptyExactPatternMatch, null), - }; + void Goo() + { + } + } + """, async w => + { var items = await _aggregator.GetItemsAsync("Goo"); - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("Goo", NavigateToItemKind.Class, "csharp", "Goo", null, s_emptyExactPatternMatch, null), + new("Goo", NavigateToItemKind.Method, "csharp", "Goo", null, s_emptyExactPatternMatch, null)], items); }); } @@ -890,80 +917,77 @@ void Goo() public async Task FindMethodNestedInGenericTypes(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class A -{ - class B - { - struct C - { - void M() + testHost, composition, """ + class A { + class B + { + struct C + { + void M() + { + } + } + } } - } - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("M")).Single(); - VerifyNavigateToResultItem(item, "M", "[|M|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "A.B.C", "Test")); - }); + """, async w => + { + var item = (await _aggregator.GetItemsAsync("M")).Single(); + VerifyNavigateToResultItem(item, "M", "[|M|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate, additionalInfo: string.Format(FeaturesResources.in_0_project_1, "A.B.C", "Test")); + }); } [Theory, CombinatorialData] public async Task OrderingOfConstructorsAndTypes(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class C1 -{ - C1(int i) - { - } -} + testHost, composition, """ + class C1 + { + C1(int i) + { + } + } -class C2 -{ - C2(float f) - { - } + class C2 + { + C2(float f) + { + } - static C2() - { - } -} -""", async w => - { - 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), - }; - var items = (await _aggregator.GetItemsAsync("C")).ToList(); - items.Sort(CompareNavigateToItems); - VerifyNavigateToResultItems(expecteditems, items); - }); + static C2() + { + } + } + """, async w => + { + var items = (await _aggregator.GetItemsAsync("C")).ToList(); + items.Sort(CompareNavigateToItems); + VerifyNavigateToResultItems([ + 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)], items); + }); } [Theory, CombinatorialData] public async Task NavigateToMethodWithNullableParameter(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class C -{ - void M(object? o) - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("M")).Single(); - VerifyNavigateToResultItem(item, "M", "[|M|](object?)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate); - }); + testHost, composition, """ + class C + { + void M(object? o) + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("M")).Single(); + VerifyNavigateToResultItem(item, "M", "[|M|](object?)", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPrivate); + }); } [Theory, CombinatorialData] @@ -971,23 +995,23 @@ public async Task StartStopSanity(TestHost testHost, Composition composition) { // Verify that multiple calls to start/stop and dispose don't blow up await TestAsync( -testHost, composition, """ -public class Goo -{ -} -""", async w => - { - // Do one set of queries - Assert.Single((await _aggregator.GetItemsAsync("Goo")), x => x.Kind != "Method"); - _provider.StopSearch(); + testHost, composition, """ + public class Goo + { + } + """, async w => + { + // Do one set of queries + Assert.Single((await _aggregator.GetItemsAsync("Goo")), x => x.Kind != "Method"); + _provider.StopSearch(); - // Do the same query again, make sure nothing was left over - Assert.Single((await _aggregator.GetItemsAsync("Goo")), x => x.Kind != "Method"); - _provider.StopSearch(); + // Do the same query again, make sure nothing was left over + Assert.Single((await _aggregator.GetItemsAsync("Goo")), x => x.Kind != "Method"); + _provider.StopSearch(); - // Dispose the provider - _provider.Dispose(); - }); + // Dispose the provider + _provider.Dispose(); + }); } [Theory, CombinatorialData] @@ -1026,13 +1050,10 @@ await TestAsync(testHost, composition, source, async w => var expecteditem1 = new NavigateToItem("get_keyword", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive, null); var expecteditem2 = new NavigateToItem("get_key_word", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive, null); var expecteditem3 = new NavigateToItem("GetKeyWord", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCasePrefixPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem2, expecteditem3 }; var items = await _aggregator.GetItemsAsync("GK"); - Assert.Equal(expecteditems.Count, items.Count()); - - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem2, expecteditem3], items); }); } @@ -1044,11 +1065,10 @@ await TestAsync(testHost, composition, source, async w => { var expecteditem1 = new NavigateToItem("get_key_word", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCaseNonContiguousPrefixPatternMatch_NotCaseSensitive, null); var expecteditem2 = new NavigateToItem("GetKeyWord", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCaseExactPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem2 }; var items = await _aggregator.GetItemsAsync("GKW"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem2], items); }); } @@ -1060,11 +1080,10 @@ await TestAsync(testHost, composition, source, async w => { var expecteditem1 = new NavigateToItem("get_key_word", NavigateToItemKind.Field, "csharp", null, null, s_emptyCamelCaseSubstringPatternMatch_NotCaseSensitive, null); var expecteditem2 = new NavigateToItem("GetKeyWord", NavigateToItemKind.Field, "csharp", null, null, s_emptySubstringPatternMatch, null); - var expecteditems = new List { expecteditem1, expecteditem2 }; var items = await _aggregator.GetItemsAsync("K W"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([expecteditem1, expecteditem2], items); }); } @@ -1096,17 +1115,13 @@ public async Task TermSplittingTest6(TestHost testHost, Composition composition) var source = "class SyllableBreaking {int GetKeyWord; int get_key_word; string get_keyword; int getkeyword; int wake;}"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("getkeyword", NavigateToItemKind.Field, "csharp", null, null, s_emptyFuzzyPatternMatch, null), - new NavigateToItem("get_keyword", NavigateToItemKind.Field, "csharp", null, null, s_emptyFuzzyPatternMatch, null), - new NavigateToItem("get_key_word", NavigateToItemKind.Field, "csharp", null, null,s_emptySubstringPatternMatch, null), - new NavigateToItem("GetKeyWord", NavigateToItemKind.Field, "csharp", null, null, s_emptySubstringPatternMatch_NotCaseSensitive, null) - }; - var items = await _aggregator.GetItemsAsync("get word"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([ + new("getkeyword", NavigateToItemKind.Field, "csharp", null, null, s_emptyFuzzyPatternMatch, null), + new("get_keyword", NavigateToItemKind.Field, "csharp", null, null, s_emptyFuzzyPatternMatch, null), + new("get_key_word", NavigateToItemKind.Field, "csharp", null, null,s_emptySubstringPatternMatch, null), + new("GetKeyWord", NavigateToItemKind.Field, "csharp", null, null, s_emptySubstringPatternMatch_NotCaseSensitive, null)], items); }); } @@ -1142,14 +1157,9 @@ void Goo() """; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("this", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null), - }; - var items = await _aggregator.GetItemsAsync("this"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("this", NavigateToItemKind.Property, "csharp", null, null, s_emptyExactPatternMatch, null)], items); }); } @@ -1159,14 +1169,9 @@ public async Task DottedPattern1(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("B.Q"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null)], items); }); } @@ -1176,13 +1181,9 @@ public async Task DottedPattern2(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - }; - var items = await _aggregator.GetItemsAsync("C.Q"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([], items); }); } @@ -1192,14 +1193,9 @@ public async Task DottedPattern3(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("B.B.Q"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null)], items); }); } @@ -1209,14 +1205,9 @@ public async Task DottedPattern4(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("Baz.Quux"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); }); } @@ -1226,14 +1217,9 @@ public async Task DottedPattern5(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("G.B.B.Quux"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); }); } @@ -1243,13 +1229,9 @@ public async Task DottedPattern6(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - }; - var items = await _aggregator.GetItemsAsync("F.F.B.B.Quux"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([], items); }); } @@ -1260,14 +1242,9 @@ public async Task DottedPattern7(TestHost testHost, Composition composition) var source = "namespace Goo { namespace Bar { class Baz { void Quux() { } } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("Baz.Q"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([new("Quux", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null)], items); }); } @@ -1278,15 +1255,11 @@ public async Task DottedPatternMatchKind(TestHost testHost, Composition composit var source = "namespace System { class Console { void Write(string s) { } void WriteLine(string s) { } } }"; await TestAsync(testHost, composition, source, async w => { - var expecteditems = new List - { - new NavigateToItem("Write", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("WriteLine", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null) - }; - var items = await _aggregator.GetItemsAsync("Console.Write"); - VerifyNavigateToResultItems(expecteditems, items); + VerifyNavigateToResultItems([ + new("Write", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("WriteLine", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null)], items); }); } @@ -1323,15 +1296,12 @@ public void VisibleMethod_Generated() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod_Generated", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null) - }; // The pattern matcher should match 'VisibleMethod' to both 'VisibleMethod' and 'VisibleMethod_Not', except that // the _Not method is declared in a generated file. - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod_Generated", NavigateToItemKind.Method, "csharp", null, null, s_emptyPrefixPatternMatch, null)], items); } [Theory, WorkItem("https://github.com/dotnet/roslyn/pull/11474")] @@ -1339,18 +1309,18 @@ public void VisibleMethod_Generated() { } public async Task FindFuzzy1(TestHost testHost, Composition composition) { await TestAsync( -testHost, composition, """ -class C -{ - public void ToError() - { - } -} -""", async w => - { - var item = (await _aggregator.GetItemsAsync("ToEror")).Single(); - VerifyNavigateToResultItem(item, "ToError", "ToError()", PatternMatchKind.Fuzzy, NavigateToItemKind.Method, Glyph.MethodPublic); - }); + testHost, composition, """ + class C + { + public void ToError() + { + } + } + """, async w => + { + var item = (await _aggregator.GetItemsAsync("ToEror")).Single(); + VerifyNavigateToResultItem(item, "ToError", "ToError()", PatternMatchKind.Fuzzy, NavigateToItemKind.Method, Glyph.MethodPublic); + }); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/18843")] @@ -1749,13 +1719,10 @@ public void VisibleMethod() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); Assert.Single(items, i => i.SecondarySort.StartsWith("0000") && IsFromFile(i, "File1.cs")); Assert.Single(items, i => i.SecondarySort.StartsWith("0001") && IsFromFile(i, "File2.cs")); @@ -1794,13 +1761,10 @@ public void VisibleMethod() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); Assert.Single(items, i => i.SecondarySort.StartsWith("0000") && IsFromFile(i, "File1.cs")); Assert.Single(items, i => i.SecondarySort.StartsWith("0001") && IsFromFile(i, "File2.cs")); @@ -1839,13 +1803,10 @@ public void VisibleMethod() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); Assert.Single(items, i => i.SecondarySort.StartsWith("0000") && IsFromFile(i, "File1.cs")); Assert.Single(items, i => i.SecondarySort.StartsWith("0002") && IsFromFile(i, "File2.cs")); @@ -1884,13 +1845,10 @@ public void VisibleMethod() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); Assert.Single(items, i => i.SecondarySort.StartsWith("0000") && IsFromFile(i, "File1.cs")); Assert.Single(items, i => i.SecondarySort.StartsWith("0002") && IsFromFile(i, "File2.cs")); @@ -1929,17 +1887,93 @@ public void VisibleMethod() { } _aggregator = new NavigateToTestAggregator(_provider); var items = await _aggregator.GetItemsAsync("VisibleMethod"); - var expectedItems = new List() - { - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), - new NavigateToItem("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null) - }; - VerifyNavigateToResultItems(expectedItems, items); + VerifyNavigateToResultItems([ + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null), + new("VisibleMethod", NavigateToItemKind.Method, "csharp", null, null, s_emptyExactPatternMatch, null)], items); Assert.Single(items, i => i.SecondarySort.StartsWith("0000") && IsFromFile(i, "File1.cs")); Assert.Single(items, i => i.SecondarySort.StartsWith("0003") && IsFromFile(i, "File2.cs")); }); } + + [Theory, CombinatorialData] + public async Task FindModernExtensionMethod1(TestHost testHost, Composition composition) + { + var content = XElement.Parse(""" + + + + static class Class + { + extension(string s) + { + public void Goo() + { + } + } + } + + + + """); + await TestAsync(testHost, composition, content, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic); + }); + } + + [Theory, CombinatorialData] + public async Task FindModernExtensionMethod2(TestHost testHost, Composition composition) + { + var content = XElement.Parse(""" + + + + static class Class + { + extension(string s) + { + public static void Goo() + { + } + } + } + + + + """); + await TestAsync(testHost, composition, content, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]()", PatternMatchKind.Exact, NavigateToItemKind.Method, Glyph.MethodPublic); + }); + } + + [Theory, CombinatorialData] + public async Task FindModernExtensionProperty(TestHost testHost, Composition composition) + { + var content = XElement.Parse(""" + + + + static class Class + { + extension(string s) + { + public int Goo => 0; + } + } + + + + """); + await TestAsync(testHost, composition, content, async w => + { + var item = (await _aggregator.GetItemsAsync("Goo")).Single(); + VerifyNavigateToResultItem(item, "Goo", "[|Goo|]", PatternMatchKind.Exact, NavigateToItemKind.Property, Glyph.PropertyPublic); + }); + } } #pragma warning restore CS0618 // MatchKind is obsolete diff --git a/src/EditorFeatures/CSharpTest/OnTheFlyDocs/OnTheFlyDocsUtilitiesTests.cs b/src/EditorFeatures/CSharpTest/OnTheFlyDocs/OnTheFlyDocsUtilitiesTests.cs new file mode 100644 index 0000000000000..d52c79cb9d1c3 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/OnTheFlyDocs/OnTheFlyDocsUtilitiesTests.cs @@ -0,0 +1,130 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.QuickInfo; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.OnTheFlyDocs; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.OnTheFlyDocs)] +public sealed class OnTheFlyDocsUtilitiesTests +{ + [Fact] + public async Task TestAdditionalContextNoContext() + { + var testCode = """ + class C + { + void AddMethod(int a, int b) + { + return a + b; + } + } + """; + + using var workspace = EditorTestWorkspace.CreateCSharp(testCode); + var solution = workspace.CurrentSolution; + var document = solution.Projects.First().Documents.First(); + + var syntaxTree = await document.GetSyntaxTreeAsync(); + var semanticModel = await document.GetSemanticModelAsync(); + + var methodDeclaration = syntaxTree!.GetRoot() + .DescendantNodes() + .OfType() + .First(); + + var methodSymbol = semanticModel!.GetDeclaredSymbol(methodDeclaration); + + var result = OnTheFlyDocsUtilities.GetAdditionalOnTheFlyDocsContext(solution, methodSymbol!); + Assert.True(result.All(item => item == null)); + } + + [Fact] + public async Task TestAdditionalContextWithTypeParameters() + { + var testCode = """ + class C + { + int AddMethod(A a, int b) + { + return a.x + b; + } + } + + class A + { + public int x; + } + """; + + using var workspace = EditorTestWorkspace.CreateCSharp(testCode); + var solution = workspace.CurrentSolution; + var document = solution.Projects.First().Documents.First(); + + var syntaxTree = await document.GetSyntaxTreeAsync(); + var semanticModel = await document.GetSemanticModelAsync(); + + var methodDeclaration = syntaxTree!.GetRoot() + .DescendantNodes() + .OfType() + .First(); + + var methodSymbol = semanticModel!.GetDeclaredSymbol(methodDeclaration); + + var result = OnTheFlyDocsUtilities.GetAdditionalOnTheFlyDocsContext(solution, methodSymbol!); + Assert.NotNull(result.First()); + Assert.Null(result.Last()); + } + + [Fact] + public async Task TestAdditionalContextWithTypeArguments() + { + var testCode = """ + class C + { + void Method(T t, U u) where T : class where U : struct + { + } + + void CallMethod() + { + Method(new CustomClass(), new CustomStruct()); + } + } + + class CustomClass + { + public string Name { get; set; } + } + + struct CustomStruct + { + public int Value { get; set; } + } + """; + + using var workspace = EditorTestWorkspace.CreateCSharp(testCode); + var solution = workspace.CurrentSolution; + var document = solution.Projects.First().Documents.First(); + + var syntaxTree = await document.GetSyntaxTreeAsync(); + var semanticModel = await document.GetSemanticModelAsync(); + + var methodInvocation = syntaxTree!.GetRoot() + .DescendantNodes() + .OfType() + .First(); + + var methodSymbol = semanticModel!.GetSymbolInfo(methodInvocation).Symbol; + + var result = OnTheFlyDocsUtilities.GetAdditionalOnTheFlyDocsContext(solution, methodSymbol!); + Assert.True(result.All(item => item is not null)); + } +} diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs index 151b4737bdb03..0f20492573709 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/AbstractPdbSourceDocumentTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs index ddc805beef25f..2f6bdcc2eae60 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/NullResultMetadataAsSourceFileProvider.cs @@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.PdbSourceDocument; @@ -26,7 +25,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.PdbSourceDocument; internal class NullResultMetadataAsSourceFileProvider : IMetadataAsSourceFileProvider { // Represents a null result - public static MetadataAsSourceFile NullResult = new("", null, null, null); + public static MetadataAsSourceFile NullResult = new("", null!, null!, null!); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs index d4efd62b990da..7500179017f49 100644 --- a/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/PdbSourceDocument/PdbSourceDocumentLoaderServiceTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.IO; using System.Security.Cryptography; using System.Text; diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 8e6fd350f3e08..35f8214686dfe 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -10701,4 +10701,181 @@ void M(IMyInterface i) """, MainDescription($"({CSharpFeaturesResources.awaitable}) ValueTask IAsyncDisposable.DisposeAsync()")); } + + [Fact] + public async Task NullConditionalAssignment() + { + await VerifyWithNet8Async(""" + class C + { + string s; + + void M(C c) + { + c?.$$s = ""; + } + } + """, + MainDescription($"({FeaturesResources.field}) string C.s")); + } + + [Fact] + public async Task TestModernExtension1() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + extension(string s) + { + public void Goo() { } + } + } + + class C + { + void M(string s) + { + s.$$Goo(); + } + } + """, + MainDescription($"void Extensions.extension(string).Goo()")); + } + + [Fact] + public async Task TestModernExtension2() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + extension(string s) + { + public void Goo() { } + public void Goo(int i) { } + } + } + + class C + { + void M(string s) + { + s.$$Goo(); + } + } + """, + MainDescription($"void Extensions.extension(string).Goo() (+ 1 {FeaturesResources.overload})")); + } + + [Fact] + public async Task TestModernExtension3() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + extension(string s) + { + public void Goo() { } + public void Goo(int i) { } + } + } + + class C + { + void M(string s) + { + s.$$Goo(0); + } + } + """, + MainDescription($"void Extensions.extension(string).Goo(int i) (+ 1 {FeaturesResources.overload})")); + } + + [Fact] + public async Task TestModernExtension4() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + extension(string s) + { + public int Prop => 0; + } + } + + class C + { + void M(string s) + { + var v = s.$$Prop; + } + } + """, + MainDescription($$"""int Extensions.extension(string).Prop { get; }""")); + } + + [Fact] + public async Task TestModernExtension5() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + extension(string s) + { + public void Goo() + { + Console.WriteLine($$s); + } + } + } + """, + MainDescription($"({FeaturesResources.parameter}) string s")); + } + + [Fact] + public async Task TestModernExtension6() + { + await TestWithOptionsAsync( + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), + """ + using System; + using System.Threading.Tasks; + + static class Extensions + { + $$extension(string s) + { + public void Goo() + { + Console.WriteLine(s); + } + } + } + """, + MainDescription($"Extensions.extension(System.String)")); + } } diff --git a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs index 7a65e619c7374..03ddbbb1a3364 100644 --- a/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs +++ b/src/EditorFeatures/CSharpTest/RawStringLiteral/RawStringLiteralCommandHandlerTests.cs @@ -616,6 +616,27 @@ public void TestGenerateWithInterpolatedString_TwoDollarSigns() """", withSpansOnly: true); } + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/77724")] + public void TestGenerateWithInterpolatedString_TwoDollarSigns_InLocalFunction() + { + using var testState = RawStringLiteralTestState.CreateTestState( + """ + void M() + { + var v = $$""[||] + } + """, withSpansOnly: true); + + testState.SendTypeChar('"'); + testState.AssertCodeIs( + """" + void M() + { + var v = $$"""[||]""" + } + """", withSpansOnly: true); + } + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/66538")] public void TestGenerateWithInterpolatedString_TwoDollarSigns_FourthDoubleQuote() { diff --git a/src/EditorFeatures/CSharpTest/Rename/CSharpInlineRenameServiceTests.cs b/src/EditorFeatures/CSharpTest/Rename/CSharpInlineRenameServiceTests.cs index b1d9a20568333..351a3d29e7509 100644 --- a/src/EditorFeatures/CSharpTest/Rename/CSharpInlineRenameServiceTests.cs +++ b/src/EditorFeatures/CSharpTest/Rename/CSharpInlineRenameServiceTests.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.IO.Hashing; using System.Linq; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs index 125b4b6e3baed..dd566e4d83910 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/AbstractCSharpSignatureHelpProviderTests.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.Collections.Generic; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs index bcdde03575248..4fae40dec048c 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/AttributeSignatureHelpProviderTests.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.SignatureHelp; using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/InitializerExpressionSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/InitializerExpressionSignatureHelpProviderTests.cs index 39350549bccfe..b153378e84216 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/InitializerExpressionSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/InitializerExpressionSignatureHelpProviderTests.cs @@ -2,21 +2,17 @@ // 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.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.SignatureHelp; -using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; [Trait(Traits.Feature, Traits.Features.SignatureHelp)] -public class InitializerExpressionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests +public sealed class InitializerExpressionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests { internal override Type GetSignatureHelpProviderType() => typeof(InitializerExpressionSignatureHelpProvider); @@ -36,12 +32,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void List.Add(int item)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("void List.Add(int item)", currentParameterIndex: 0)]); } [Fact] @@ -59,12 +50,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void Dictionary.Add(int key, string value)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("void Dictionary.Add(int key, string value)", currentParameterIndex: 0)]); } [Fact] @@ -82,12 +68,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void Dictionary.Add(int key, string value)", currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("void Dictionary.Add(int key, string value)", currentParameterIndex: 1)]); } [Fact] @@ -110,12 +91,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void Dictionary.Add(int key, string value)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("void Dictionary.Add(int key, string value)", currentParameterIndex: 0)]); } [Fact] @@ -136,12 +112,7 @@ void Goo() new Bar { D = { { $$ """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void Dictionary.Add(int key, string value)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("void Dictionary.Add(int key, string value)", currentParameterIndex: 0)]); } [Fact] @@ -164,14 +135,10 @@ void Goo() new Bar { { $$ """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("void Bar.Add(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("void Bar.Add(int i, string s)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("void Bar.Add(int i, string s, bool b)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("void Bar.Add(int i)", currentParameterIndex: 0), + new("void Bar.Add(int i, string s)", currentParameterIndex: 0, isSelected: true), + new("void Bar.Add(int i, string s, bool b)", currentParameterIndex: 0)]); } [Fact] @@ -194,9 +161,7 @@ void Goo() new Bar { { $$ """; - var expectedOrderedItems = new List(); - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, expectedOrderedItemsOrNull: []); } [Fact] @@ -223,13 +188,9 @@ void Goo() new Bar { { $$ """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void Bar.Add(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void Bar.Add(int i, string s)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem($"({CSharpFeaturesResources.extension}) void Bar.Add(int i, string s, bool b)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, sourceCodeKind: SourceCodeKind.Regular); + await TestAsync(markup, [ + new($"({CSharpFeaturesResources.extension}) void Bar.Add(int i)", currentParameterIndex: 0), + new($"({CSharpFeaturesResources.extension}) void Bar.Add(int i, string s)", currentParameterIndex: 0, isSelected: true), + new($"({CSharpFeaturesResources.extension}) void Bar.Add(int i, string s, bool b)", currentParameterIndex: 0)], sourceCodeKind: SourceCodeKind.Regular); } } diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/ObjectCreationExpressionSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/ObjectCreationExpressionSignatureHelpProviderTests.cs index 6c3e92d69569e..7d61c2498d611 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/ObjectCreationExpressionSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/ObjectCreationExpressionSignatureHelpProviderTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; [Trait(Traits.Feature, Traits.Features.SignatureHelp)] -public class ObjectCreationExpressionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests +public sealed class ObjectCreationExpressionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests { internal override Type GetSignatureHelpProviderType() => typeof(ObjectCreationExpressionSignatureHelpProvider); @@ -36,12 +36,7 @@ void goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C()", string.Empty, null, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] @@ -63,12 +58,7 @@ void M() """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C()", string.Empty, null, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] @@ -89,11 +79,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C()", "Summary for C", null, currentParameterIndex: 0) - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C()", "Summary for C", null, currentParameterIndex: 0)]); } [Fact] @@ -111,12 +97,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0)]); } [Fact] @@ -134,12 +115,7 @@ void M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0)]); } [Fact] @@ -162,12 +138,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", "Summary for C", "Param a", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", "Summary for C", "Param a", currentParameterIndex: 0)]); } [Fact] @@ -184,12 +155,8 @@ void Goo() } } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1)]); } [Fact] @@ -211,12 +178,8 @@ void Goo() } } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", "Summary for C", "Param b", currentParameterIndex: 1) - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", "Summary for C", "Param b", currentParameterIndex: 1)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] @@ -234,13 +197,10 @@ void M() D(int i) => throw null; } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0), - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("D(int i)", currentParameterIndex: 0, isSelected: true), + new("D(string i)", currentParameterIndex: 0),]); } [Fact] @@ -258,13 +218,10 @@ void M() D(int i) => throw null; } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0, isSelected: true), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0), - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("D(int i)", currentParameterIndex: 0, isSelected: true), + new("D(string i)", currentParameterIndex: 0),]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25830")] @@ -282,13 +239,10 @@ void M() D(int i) => throw null; } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0, isSelected: true), - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("D(int i)", currentParameterIndex: 0), + new("D(string i)", currentParameterIndex: 0, isSelected: true),]); } [Fact] @@ -306,13 +260,10 @@ void M() D(int i) => throw null; } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("D(int i)", currentParameterIndex: 0), - new SignatureHelpTestItem("D(string i)", currentParameterIndex: 0, isSelected: true), - }; - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("D(int i)", currentParameterIndex: 0), + new("D(string i)", currentParameterIndex: 0, isSelected: true),]); } [Fact] @@ -328,12 +279,7 @@ void goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C()", string.Empty, null, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C()", string.Empty, null, currentParameterIndex: 0)]); } [Fact] @@ -351,12 +297,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 0)]); } [Fact] @@ -374,12 +315,7 @@ void Goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("C(int a, int b)", string.Empty, string.Empty, currentParameterIndex: 1)]); } [Fact] @@ -397,12 +333,7 @@ void goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Action(void (int, int) target)", string.Empty, string.Empty, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("Action(void (int, int) target)", string.Empty, string.Empty, currentParameterIndex: 0, isSelected: true)]); } #endregion @@ -446,12 +377,7 @@ void goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C()", string.Empty, null, currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C()", string.Empty, null, currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -471,12 +397,7 @@ void goo() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(int a, string b)", string.Empty, string.Empty, currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C(int a, string b)", string.Empty, string.Empty, currentParameterIndex: 1)], usePreviousCharAsTrigger: true); } [Fact] @@ -496,8 +417,7 @@ void goo() } """; - var expectedOrderedItems = new List(); - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, expectedOrderedItemsOrNull: [], usePreviousCharAsTrigger: true); } [Fact] @@ -537,15 +457,16 @@ public Goo(int x) var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0) + new("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0) }; - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: expectedOrderedItems, - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp); + await TestSignatureHelpInEditorBrowsableContextsAsync( + markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: expectedOrderedItems, + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp); } [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] @@ -571,17 +492,13 @@ public Goo(int x) } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: new List(), - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp); + await TestSignatureHelpInEditorBrowsableContextsAsync( + markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: [], + expectedOrderedItemsSameSolution: [new("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0)], + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp); } [Fact, WorkItem(7336, "DevDiv_Projects/Roslyn")] @@ -608,16 +525,17 @@ public Goo() """; var expectedOrderedItems = new List { - new SignatureHelpTestItem("Goo()", string.Empty, null, currentParameterIndex: 0) + new("Goo()", string.Empty, null, currentParameterIndex: 0) }; - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: new List(), - expectedOrderedItemsSameSolution: expectedOrderedItems, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp, - hideAdvancedMembers: true); + await TestSignatureHelpInEditorBrowsableContextsAsync( + markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: new List(), + expectedOrderedItemsSameSolution: expectedOrderedItems, + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp, + hideAdvancedMembers: true); await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, referencedCode: referencedCode, @@ -654,23 +572,16 @@ public Goo(long y) } } """; - var expectedOrderedItemsMetadataReference = new List - { - new SignatureHelpTestItem("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - - var expectedOrderedItemsSameSolution = new List - { - new SignatureHelpTestItem("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), - new SignatureHelpTestItem("Goo(long y)", string.Empty, string.Empty, currentParameterIndex: 0) - }; - await TestSignatureHelpInEditorBrowsableContextsAsync(markup: markup, - referencedCode: referencedCode, - expectedOrderedItemsMetadataReference: expectedOrderedItemsMetadataReference, - expectedOrderedItemsSameSolution: expectedOrderedItemsSameSolution, - sourceLanguage: LanguageNames.CSharp, - referencedLanguage: LanguageNames.CSharp); + await TestSignatureHelpInEditorBrowsableContextsAsync( + markup: markup, + referencedCode: referencedCode, + expectedOrderedItemsMetadataReference: [new("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0)], + expectedOrderedItemsSameSolution: [ + new("Goo(int x)", string.Empty, string.Empty, currentParameterIndex: 0), + new("Goo(long y)", string.Empty, string.Empty, currentParameterIndex: 0)], + sourceLanguage: LanguageNames.CSharp, + referencedLanguage: LanguageNames.CSharp); } #endregion @@ -702,8 +613,9 @@ void goo() """; - var expectedDescription = new SignatureHelpTestItem($"D()\r\n\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", currentParameterIndex: 0); - await VerifyItemWithReferenceWorkerAsync(markup, [expectedDescription], false); + + await VerifyItemWithReferenceWorkerAsync( + markup, [new($"D()\r\n\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj2", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", currentParameterIndex: 0)], false); } [Fact] @@ -740,8 +652,8 @@ void goo() """; - var expectedDescription = new SignatureHelpTestItem($"D()\r\n\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", currentParameterIndex: 0); - await VerifyItemWithReferenceWorkerAsync(markup, [expectedDescription], false); + await VerifyItemWithReferenceWorkerAsync( + markup, [new($"D()\r\n\r\n{string.Format(FeaturesResources._0_1, "Proj1", FeaturesResources.Available)}\r\n{string.Format(FeaturesResources._0_1, "Proj3", FeaturesResources.Not_Available)}\r\n\r\n{FeaturesResources.You_can_use_the_navigation_bar_to_switch_contexts}", currentParameterIndex: 0)], false); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1067933")] @@ -785,12 +697,7 @@ public C M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C(object o)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -808,12 +715,7 @@ public C M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C(object o)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -831,12 +733,7 @@ public C M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C(object o)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -854,12 +751,7 @@ public C M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("C(object o)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("C(object o)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Theory] @@ -892,13 +784,10 @@ static void M() """; 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++), - }; - await TestAsync(markup.Replace("ARGUMENTS", arguments), expectedOrderedItems); + await TestAsync(markup.Replace("ARGUMENTS", arguments), [ + new("Program(int i, string s)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++), + new("Program(string s, string s2)", currentParameterIndex: expectedParameterIndex, isSelected: expecteSelectedIndex == index++),]); } [Theory] diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProviderTests.cs index bf0b98ffba5e3..2a6cebd5a3577 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/PrimaryConstructorBaseTypeSignatureHelpProviderTests.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; [Trait(Traits.Feature, Traits.Features.SignatureHelp)] -public class PrimaryConstructorBaseTypeSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests +public sealed class PrimaryConstructorBaseTypeSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests { internal override Type GetSignatureHelpProviderType() => typeof(PrimaryConstructorBaseTypeSignatureHelpProvider); @@ -32,13 +32,9 @@ private Base(string ignored) : this(1, 2) { } record Derived(int Other) : [|Base($$1|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(Base original)", string.Empty, null, currentParameterIndex: 0), - new SignatureHelpTestItem("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(Base original)", string.Empty, null, currentParameterIndex: 0), + new("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true)]); } [Fact] @@ -52,12 +48,7 @@ private Base(string ignored) : this(1, 2) { } class Derived(int Other) : [|Base($$1|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true)]); } [Fact] @@ -71,14 +62,10 @@ protected Base(string name) : this(1, 2) { } record Derived(int Other) : [|Base(1, $$2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(Base original)", string.Empty, null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(string name)", string.Empty, null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(Base original)", string.Empty, null, currentParameterIndex: 1), + new("Base(string name)", string.Empty, null, currentParameterIndex: 1), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true)]); } [Fact] @@ -92,13 +79,9 @@ protected Base(string name) : this(1, 2) { } class Derived(int Other) : [|Base(1, $$2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(string name)", string.Empty, null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(string name)", string.Empty, null, currentParameterIndex: 1), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true)]); } [Fact] @@ -113,14 +96,10 @@ protected Base(string name) : this(1, 2) { } record Derived(int Other) : [|Base(1, $$2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(Base original)", string.Empty, null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(string name)", "Summary for constructor", null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(Base original)", string.Empty, null, currentParameterIndex: 1), + new("Base(string name)", "Summary for constructor", null, currentParameterIndex: 1), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true)]); } [Fact] @@ -135,13 +114,9 @@ protected Base(string name) : this(1, 2) { } class Derived(int Other) : [|Base(1, $$2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(string name)", "Summary for constructor", null, currentParameterIndex: 1), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(string name)", "Summary for constructor", null, currentParameterIndex: 1), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 1, isSelected: true)]); } [Fact] @@ -157,14 +132,10 @@ protected Base(string name) : this(1, 2) { } record Derived(int Other) : [|Base($$1, 2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(Base original)", string.Empty, null, currentParameterIndex: 0), - new SignatureHelpTestItem("Base(string name)", "Summary for constructor", "Param name", currentParameterIndex: 0), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(Base original)", string.Empty, null, currentParameterIndex: 0), + new("Base(string name)", "Summary for constructor", "Param name", currentParameterIndex: 0), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 0, isSelected: true)]); } [Fact] @@ -180,13 +151,9 @@ protected Base(string name) : this(1, 2) { } class Derived(int Other) : [|Base($$1, 2|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(string name)", "Summary for constructor", "Param name", currentParameterIndex: 0), - new SignatureHelpTestItem("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [ + new("Base(string name)", "Summary for constructor", "Param name", currentParameterIndex: 0), + new("Base(int Identifier1, int Identifier2)", string.Empty, null, currentParameterIndex: 0, isSelected: true)]); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/70106")] @@ -199,11 +166,6 @@ abstract class Base(int Identifier) class Derived(int Other) : [|Base($$1|]); """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true) - }; - - await TestAsync(markup, expectedOrderedItems); + await TestAsync(markup, [new("Base(int Identifier)", string.Empty, null, currentParameterIndex: 0, isSelected: true)]); } } diff --git a/src/EditorFeatures/CSharpTest/SignatureHelp/TupleConstructionSignatureHelpProviderTests.cs b/src/EditorFeatures/CSharpTest/SignatureHelp/TupleConstructionSignatureHelpProviderTests.cs index 7233e31edad96..a17595a6e197b 100644 --- a/src/EditorFeatures/CSharpTest/SignatureHelp/TupleConstructionSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/SignatureHelp/TupleConstructionSignatureHelpProviderTests.cs @@ -2,13 +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. -#nullable disable - using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.SignatureHelp; -using Microsoft.CodeAnalysis.Editor.UnitTests.SignatureHelp; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -16,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.SignatureHelp; [Trait(Traits.Feature, Traits.Features.SignatureHelp)] -public class TupleConstructionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests +public sealed class TupleConstructionSignatureHelpProviderTests : AbstractCSharpSignatureHelpProviderTests { internal override Type GetSignatureHelpProviderType() => typeof(TupleConstructionSignatureHelpProvider); @@ -31,12 +27,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0, parameterDocumentation: "") - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, int)", currentParameterIndex: 0, parameterDocumentation: "")], usePreviousCharAsTrigger: true); } [Fact] @@ -49,12 +40,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(string?, string)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(string?, string)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/655607")] @@ -70,12 +56,7 @@ void M() } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(object a, object)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(object a, object)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -88,12 +69,7 @@ class C } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, int)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -106,12 +82,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, int)", currentParameterIndex: 1, parameterDocumentation: "") - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, int)", currentParameterIndex: 1, parameterDocumentation: "")], usePreviousCharAsTrigger: true); } [Fact] @@ -124,12 +95,7 @@ class C } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, int)", currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, int)", currentParameterIndex: 1)], usePreviousCharAsTrigger: true); } [Fact] @@ -142,15 +108,11 @@ class C |]} """; - var expectedOrderedItems = new List - { + await TestAsync(markup, [ // currentParameterIndex only considers the position in the argument list // and not names, hence passing 0 even though the controller will highlight // "int b" in the actual display - new SignatureHelpTestItem("(int a, int b)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems); + new("(int a, int b)", currentParameterIndex: 0)]); } [Fact(Skip = "https://github.com/dotnet/roslyn/issues/14277")] @@ -163,12 +125,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int b, int c)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int b, int c)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -181,12 +138,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, object)", currentParameterIndex: 1)], usePreviousCharAsTrigger: true); } [Fact] @@ -199,12 +151,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, object)", currentParameterIndex: 1)], usePreviousCharAsTrigger: true); } [Fact] @@ -217,12 +164,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, object)", currentParameterIndex: 1) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(int, object)", currentParameterIndex: 1)], usePreviousCharAsTrigger: true); } [Fact] @@ -235,12 +177,7 @@ class C |]} """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(object, object)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [new("(object, object)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact] @@ -259,13 +196,9 @@ static void Do1((string, string) s) { } } """; - var expectedOrderedItems = new List - { - new SignatureHelpTestItem("(int, int)", currentParameterIndex: 0), - new SignatureHelpTestItem("(string, string)", currentParameterIndex: 0) - }; - - await TestAsync(markup, expectedOrderedItems, usePreviousCharAsTrigger: true); + await TestAsync(markup, [ + new("(int, int)", currentParameterIndex: 0), + new("(string, string)", currentParameterIndex: 0)], usePreviousCharAsTrigger: true); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14793")] @@ -295,7 +228,7 @@ void goo() """; - var expectedDescription = new SignatureHelpTestItem($"(int, string)", currentParameterIndex: 0); - await VerifyItemWithReferenceWorkerAsync(markup, [expectedDescription], false); + await VerifyItemWithReferenceWorkerAsync( + markup, [new($"(int, string)", currentParameterIndex: 0)], hideAdvancedMembers: false); } } diff --git a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs index 3fa9bc368bd14..8607f9f65d2ac 100644 --- a/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs +++ b/src/EditorFeatures/CSharpTest/Structure/CommentStructureTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Structure; using Microsoft.CodeAnalysis.Editor.UnitTests.Structure; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs index a7882ee2881ef..5cb0f22646bef 100644 --- a/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs +++ b/src/EditorFeatures/CSharpTest/SymbolKey/SymbolKeyCompilationsTests.cs @@ -192,6 +192,64 @@ public partial class C1 Assert.Equal(implementation, ResolveSymbol(implementation, comp, SymbolKeyComparison.None)); } + [Fact] + public void ExtendedPartialEventDefinitionAndImplementationResolveCorrectly() + { + var src = """ + using System; + namespace NS + { + public partial class C1 + { + public partial event System.Action Event; + public partial event System.Action Event { add { } remove { } } + } + } + """; + + var comp = (Compilation)CreateCompilation(src, assemblyName: "Test"); + + var ns = comp.SourceModule.GlobalNamespace.GetMembers("NS").Single() as INamespaceSymbol; + var type = ns.GetTypeMembers("C1").FirstOrDefault(); + var definition = type.GetMembers("Event").First() as IEventSymbol; + var implementation = definition.PartialImplementationPart; + Assert.NotNull(implementation); + Assert.NotEqual(implementation, definition); + + // Assert that both the definition and implementation resolve back to themselves + Assert.Equal(definition, ResolveSymbol(definition, comp, SymbolKeyComparison.None)); + Assert.Equal(implementation, ResolveSymbol(implementation, comp, SymbolKeyComparison.None)); + } + + [Fact] + public void ExtendedPartialConstructorDefinitionAndImplementationResolveCorrectly() + { + var src = """ + using System; + namespace NS + { + public partial class C1 + { + public partial C1(); + public partial C1() { } + } + } + """; + + var comp = (Compilation)CreateCompilation(src, assemblyName: "Test"); + + var ns = comp.SourceModule.GlobalNamespace.GetMembers("NS").Single() as INamespaceSymbol; + var type = ns.GetTypeMembers("C1").FirstOrDefault(); + var definition = type.GetMembers(".ctor").First() as IMethodSymbol; + var implementation = definition.PartialImplementationPart; + Assert.NotNull(implementation); + Assert.NotEqual(implementation, definition); + + // Assert that both the definition and implementation resolve back to themselves + Assert.Equal(definition, ResolveSymbol(definition, comp, SymbolKeyComparison.None)); + Assert.Equal(implementation, ResolveSymbol(implementation, comp, SymbolKeyComparison.None)); + } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/916341")] public void ExplicitIndexerImplementationResolvesCorrectly() { diff --git a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs index 89159b427b39f..0d4299d121c1b 100644 --- a/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs +++ b/src/EditorFeatures/CSharpTest/TextStructureNavigation/TextStructureNavigatorTests.cs @@ -561,4 +561,13 @@ public void TestClampStringLiteral(string content) { AssertExtent(content); } + + [WpfTheory, WorkItem("https://github.com/dotnet/roslyn/issues/77401")] + [InlineData(@"{|Significant:""|}$$")] + [InlineData(@"{|Significant:""""""|}$$")] + [InlineData(@"""""""{|Significant:u8|}$$")] + public void TestClampStringLiteral_Invalid(string content) + { + AssertExtent(content); + } } diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs index e4a42458c6bfa..3b32e03b5cdd4 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests_EditorFeatures.cs @@ -13,7 +13,6 @@ using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs index cf2736a2be179..a8ae804f62cfb 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/AbstractKeywordRecommenderTests.cs @@ -484,5 +484,20 @@ class C { override $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs index 3637abd1754a3..c1536f02862e7 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/AsyncKeywordRecommenderTests.cs @@ -401,5 +401,20 @@ public async Task TestLocalFunction7(bool topLevelStatement) await VerifyKeywordAsync(AddInsideMethod( @"static $$", topLevelStatement: topLevelStatement), options: CSharp9ParseOptions); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs index a78023a3bf842..a8bf7bf1b504a 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/BoolKeywordRecommenderTests.cs @@ -329,9 +329,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1106,5 +1106,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs index 76d43d6604003..654eaee844801 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ByteKeywordRecommenderTests.cs @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1106,5 +1106,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs index 828bcdaa9fd06..c88dd40be804f 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/CharKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { - public class CharKeywordRecommenderTests : KeywordRecommenderTests + public sealed class CharKeywordRecommenderTests : KeywordRecommenderTests { [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] public async Task TestAtRoot_Interactive() @@ -349,9 +349,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1171,5 +1171,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs index f58aa3947595c..43ceea5a54489 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ClassKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ClassKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ClassKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -543,5 +543,22 @@ public async Task TestAfterAttributeFileScopedNamespace() await VerifyKeywordAsync( @"namespace NS; [Attr] $$"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ConstKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ConstKeywordRecommenderTests.cs index e596353323c25..af4e9d57f3121 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ConstKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ConstKeywordRecommenderTests.cs @@ -448,5 +448,22 @@ int Goo { $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs index fc4ed190203ec..460788997a7f9 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DecimalKeywordRecommenderTests.cs @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1123,5 +1123,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs index c5dd8fa1b50d0..3664642f6ee3b 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DelegateKeywordRecommenderTests.cs @@ -641,4 +641,19 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs index 2283d0e9fc4ce..0d2b9307d8f92 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DoubleKeywordRecommenderTests.cs @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1109,5 +1109,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/DynamicKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/DynamicKeywordRecommenderTests.cs index 585d0ab2710fc..6398091cc46fc 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/DynamicKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/DynamicKeywordRecommenderTests.cs @@ -347,9 +347,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -771,5 +771,20 @@ class C delegate*$$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/EnumKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/EnumKeywordRecommenderTests.cs index 7b325f7c6d165..9dad53fbcf0d5 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/EnumKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/EnumKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class EnumKeywordRecommenderTests : KeywordRecommenderTests + public sealed class EnumKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -329,5 +329,22 @@ public async Task TestNotAfterAbstractPublic() [Fact] public async Task TestNotAfterEnum() => await VerifyAbsenceAsync(@"enum $$"); + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/EventKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/EventKeywordRecommenderTests.cs index 501f19e0b3f66..9c1cd1d91adb4 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/EventKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/EventKeywordRecommenderTests.cs @@ -318,6 +318,17 @@ record C(int i, int j) { """, absent: false, options: TestOptions.RegularPreview); } + [Theory, CombinatorialData] + public async Task TestPartialMember( + [CombinatorialValues("class", "record", "struct", "interface")] string kind) + { + await VerifyKeywordAsync( + $$""" + {{kind}} C { + partial $$ + """); + } + [Fact] public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); @@ -644,4 +655,21 @@ await VerifyAbsenceAsync( record R([$$] int i) { } """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ExtensionKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ExtensionKeywordRecommenderTests.cs new file mode 100644 index 0000000000000..c8fe8ee566b3e --- /dev/null +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ExtensionKeywordRecommenderTests.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations; + +[Trait(Traits.Feature, Traits.Features.KeywordRecommending)] +public sealed class ExtensionKeywordRecommenderTests : KeywordRecommenderTests +{ + private static readonly CSharpParseOptions s_options = CSharpNextParseOptions; + + [Fact] + public async Task NotInRoot() + { + await VerifyAbsenceAsync(@"$$", s_options); + } + + [Fact] + public async Task NotInNormalClass() + { + await VerifyAbsenceAsync(""" + class C + { + $$ + } + """, s_options); + } + + [Fact] + public async Task InStaticClass() + { + await VerifyKeywordAsync(""" + static class C + { + $$ + } + """, s_options); + } + + [Fact] + public async Task NotAfterAccessibilityInStaticClass() + { + await VerifyAbsenceAsync(""" + static class C + { + public $$ + } + """, s_options); + } + + [Fact] + public async Task NotAfterModifierInStaticClass() + { + await VerifyAbsenceAsync(""" + static class C + { + unsafe $$ + } + """, s_options); + } + + [Fact] + public async Task NotAfterPartialInStaticClass() + { + await VerifyAbsenceAsync(""" + static class C + { + partial $$ + } + """, s_options); + } + + [Fact] + public async Task NotInStaticStructClass() + { + await VerifyAbsenceAsync(""" + static struct C + { + $$ + } + """, s_options); + } + + [Fact] + public async Task AfterMethodInStaticClass() + { + await VerifyKeywordAsync(""" + static class C + { + void M() { } + $$ + } + """, s_options); + } + + [Fact] + public async Task AfterClassInStaticClass() + { + await VerifyKeywordAsync(""" + static class C + { + class M { } + $$ + } + """, s_options); + } + + [Fact] + public async Task AfterExtensionInStaticClass() + { + await VerifyKeywordAsync(""" + static class C + { + extension E() { } + $$ + } + """, s_options); + } + + [Fact] + public async Task NotInClassInStaticClass() + { + await VerifyAbsenceAsync(""" + static class C + { + class C + { + $$ + } + } + """, s_options); + } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } +} diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ExternKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ExternKeywordRecommenderTests.cs index 926fdf0a23e98..ef398572684aa 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ExternKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ExternKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ExternKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ExternKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot() @@ -346,5 +346,20 @@ class C { public $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs index 958a92bfc8de6..14e2f4cbe4ae7 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/FloatKeywordRecommenderTests.cs @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -966,5 +966,20 @@ await VerifyKeywordAsync( ref readonly $$ }}"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs index e7d0eb3bff1e5..d1e09081c850e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/GlobalKeywordRecommenderTests.cs @@ -528,5 +528,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs index b4ae9655b78bc..be447e34064ac 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/IntKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class IntKeywordRecommenderTests : KeywordRecommenderTests + public sealed class IntKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -305,9 +305,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1215,5 +1215,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/InterfaceKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/InterfaceKeywordRecommenderTests.cs index 18dfcc00466d7..84300fcddf54b 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/InterfaceKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/InterfaceKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class InterfaceKeywordRecommenderTests : KeywordRecommenderTests + public sealed class InterfaceKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -333,5 +333,22 @@ public async Task TestNotAfterStatic() [Fact] public async Task TestNotAfterInterface() => await VerifyAbsenceAsync(@"interface $$"); + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/InternalKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/InternalKeywordRecommenderTests.cs index dadf68e402dbc..36dc05656eb51 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/InternalKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/InternalKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class InternalKeywordRecommenderTests : KeywordRecommenderTests + public sealed class InternalKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -476,5 +476,22 @@ class C { int this[int i] { get { } protected $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs index 4aef97388870c..8d7c5195ca445 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/LongKeywordRecommenderTests.cs @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/MethodKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/MethodKeywordRecommenderTests.cs index c8886baabb8fe..2ba731923eab3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/MethodKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/MethodKeywordRecommenderTests.cs @@ -270,4 +270,36 @@ void F() } """); } + + [Fact] + public async Task TestWithinExtension1() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } + + [Fact] + public async Task TestWithinExtension2() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + [$$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs index 0b13d621b62d1..9967f116778f3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NamespaceKeywordRecommenderTests.cs @@ -543,5 +543,20 @@ await VerifyAbsenceAsync(SourceCodeKind.Script, $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs index 322ddd4898186..c13f4bee4cbfd 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NativeIntegerKeywordRecommenderTests.cs @@ -28,6 +28,17 @@ await VerifyKeywordAsync(AddInsideMethod( @"$$")); } + [Fact] + public async Task TestInClass() + { + await VerifyKeywordAsync( + """ + class C + { + $$ + """); + } + [Fact] public async Task TestInParameterList() { @@ -591,4 +602,21 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/NewKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/NewKeywordRecommenderTests.cs index 2c18df41418af..3380cd5062b95 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/NewKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/NewKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class NewKeywordRecommenderTests : KeywordRecommenderTests + public sealed class NewKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot() @@ -1325,5 +1325,22 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs index 0fc8d684ea2e1..d43f3324ccf10 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ObjectKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ObjectKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ObjectKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -329,9 +329,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1130,5 +1130,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/OperatorKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/OperatorKeywordRecommenderTests.cs index 2977a3301a4e1..be78fa202787a 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/OperatorKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/OperatorKeywordRecommenderTests.cs @@ -139,5 +139,37 @@ interface Goo { public static int $$ """); } + + [Fact] + public async Task TestWithinExtension1() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } + + [Fact] + public async Task TestWithinExtension2() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + public static explicit $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/OverrideKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/OverrideKeywordRecommenderTests.cs index b01caac19e286..d5eb94c35542c 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/OverrideKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/OverrideKeywordRecommenderTests.cs @@ -443,5 +443,20 @@ class C { private protected $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/PartialKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/PartialKeywordRecommenderTests.cs index 5acbf825d5178..b424245897e71 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/PartialKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/PartialKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class PartialKeywordRecommenderTests : KeywordRecommenderTests + public sealed class PartialKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -495,5 +495,20 @@ partial class C { virtual $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/PrivateKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/PrivateKeywordRecommenderTests.cs index 9dc016e76e65e..49fdcc03b966e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/PrivateKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/PrivateKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class PrivateKeywordRecommenderTests : KeywordRecommenderTests + public sealed class PrivateKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -518,5 +518,22 @@ class C { int this[int i] { get { } internal $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/PropertyKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/PropertyKeywordRecommenderTests.cs index 2413e43406f28..c62126d4c6541 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/PropertyKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/PropertyKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations; [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] -public class PropertyKeywordRecommenderTests : KeywordRecommenderTests +public sealed class PropertyKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestNotAtRoot_Interactive() @@ -264,4 +264,36 @@ await VerifyKeywordAsync( record R([$$] int i) { } """); } + + [Fact] + public async Task TestWithinExtension1() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } + + [Fact] + public async Task TestWithinExtension2() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + [$$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/PublicKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/PublicKeywordRecommenderTests.cs index 56ffbb2a5df3b..9c563b54df161 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/PublicKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/PublicKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class PublicKeywordRecommenderTests : KeywordRecommenderTests + public sealed class PublicKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -552,5 +552,22 @@ await VerifyKeywordAsync( $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ReadOnlyKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ReadOnlyKeywordRecommenderTests.cs index 801d5f9f721c8..fdc499a06ce03 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ReadOnlyKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ReadOnlyKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ReadOnlyKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ReadOnlyKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot() @@ -716,10 +716,26 @@ class C [InlineData("ref readonly")] public async Task TestNotInFunctionPointerTypeAfterOtherRefModifier(string modifier) { - await VerifyAbsenceAsync($@" -class C -{{ - delegate*<{modifier} $$"); + await VerifyAbsenceAsync($$""" + class C + { + delegate*<{{modifier}} $$ + """); + } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/RecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/RecommenderTests.cs index c27ef93bb9f76..b4ab7bb7e532e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/RecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/RecommenderTests.cs @@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -23,6 +24,8 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations public abstract class RecommenderTests : TestBase { protected static readonly CSharpParseOptions CSharp9ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9); + protected static readonly CSharpParseOptions CSharpNextParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext); + protected static readonly CSharpParseOptions CSharpNextScriptParseOptions = Options.Script.WithLanguageVersion(LanguageVersionExtensions.CSharpNext); protected abstract string KeywordText { get; } internal Func>>? RecommendKeywordsAsync; diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/RecordKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/RecordKeywordRecommenderTests.cs index 02ac3ea83a9dc..bf0ac559f0bba 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/RecordKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/RecordKeywordRecommenderTests.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class RecordKeywordRecommenderTests : KeywordRecommenderTests + public sealed class RecordKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -540,5 +540,22 @@ public async Task TestAfterReadonly() // readonly record struct is allowed. await VerifyKeywordAsync("readonly $$"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/RefKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/RefKeywordRecommenderTests.cs index 360e0bddbd32f..cf2d2f04215cb 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/RefKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/RefKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class RefKeywordRecommenderTests : KeywordRecommenderTests + public sealed class RefKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot() @@ -1384,5 +1384,20 @@ await VerifyAbsenceAsync( class C where T : new(), allows ref $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ReturnKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ReturnKeywordRecommenderTests.cs index e766277fc0abd..ab22e8f34a362 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ReturnKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ReturnKeywordRecommenderTests.cs @@ -455,4 +455,38 @@ await VerifyAbsenceAsync( record R([$$] int i) { } """); } + + [Fact] + public async Task TestWithinExtension1() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } + + [Fact] + public async Task TestWithinExtension2() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + [$$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs index f47b1315ef06b..3d6c7c9f60c2e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SByteKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class SByteKeywordRecommenderTests : KeywordRecommenderTests + public sealed class SByteKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1109,5 +1109,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs index 6011e8077e3ea..738f20128cc21 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/SealedKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class SealedKeywordRecommenderTests : KeywordRecommenderTests + public sealed class SealedKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -471,5 +471,20 @@ class C { int Goo { get; internal $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs index 2e261c1aa6aa9..cb20061a084c5 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ShortKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ShortKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ShortKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -958,10 +958,12 @@ ref readonly $$ public async Task TestAfterRefInClassInterfaceStructRecord(string type) { await VerifyKeywordAsync( -$@"{type} N -{{ - ref $$ -}}"); + $$""" + {{type}} N + { + ref $$ + } + """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/67061")] @@ -972,10 +974,12 @@ await VerifyKeywordAsync( public async Task TestAfterReadonlyInClassInterfaceStructRecord(string type) { await VerifyKeywordAsync( -$@"{type} N -{{ - readonly $$ -}}"); + $$""" + {{type}} N + { + readonly $$ + } + """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/67061")] @@ -986,10 +990,27 @@ await VerifyKeywordAsync( public async Task TestAfterRefReadonlyInClassInterfaceStructRecord(string type) { await VerifyKeywordAsync( -$@"{type} N -{{ - ref readonly $$ -}}"); + $$""" + {{type}} N + { + ref readonly $$ + } + """); + } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs index e46bbf87812e6..719977e6e4ffb 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StaticKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class StaticKeywordRecommenderTests : KeywordRecommenderTests + public sealed class StaticKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -700,9 +700,24 @@ public async Task TestInFor() public async Task TestAfterUsingKeywordBeforeTopLevelStatement() { await VerifyKeywordAsync(""" -using $$ -var i = 1; -"""); + using $$ + var i = 1; + """); + } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs index 1b6fa709c8dd9..1cc970d1a8799 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StringKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class StringKeywordRecommenderTests : KeywordRecommenderTests + public sealed class StringKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -406,9 +406,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1147,5 +1147,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs index 01f496fac662d..36e725a942f62 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/StructKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class StructKeywordRecommenderTests : KeywordRecommenderTests + public sealed class StructKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -708,5 +708,22 @@ await VerifyAbsenceAsync( class C where T : class, allows $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs index 382b988c5dfcc..74de0383fb7b3 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UIntKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class UIntKeywordRecommenderTests : KeywordRecommenderTests + public sealed class UIntKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1109,5 +1109,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs index 78a7092812e0c..ed4ef81119789 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/ULongKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class ULongKeywordRecommenderTests : KeywordRecommenderTests + public sealed class ULongKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1109,5 +1109,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs index c93001d03ff98..c0edee7623d40 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UShortKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class UShortKeywordRecommenderTests : KeywordRecommenderTests + public sealed class UShortKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -350,9 +350,9 @@ public async Task TestNotAfterPartial() => await VerifyAbsenceAsync(@"partial $$"); [Fact] - public async Task TestNotAfterNestedPartial() + public async Task TestAfterNestedPartial() { - await VerifyAbsenceAsync( + await VerifyKeywordAsync( """ class C { partial $$ @@ -1109,5 +1109,20 @@ IEnumerable M() => [string.Empty, .. ($$ } #endregion + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UncheckedKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UncheckedKeywordRecommenderTests.cs index 83b624cda5953..c1475c1aaee98 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UncheckedKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UncheckedKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class UncheckedKeywordRecommenderTests : KeywordRecommenderTests + public sealed class UncheckedKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -115,7 +115,24 @@ void M() public async Task TestAfterRefExpression() { await VerifyKeywordAsync(AddInsideMethod( -@"ref int x = ref $$")); + @"ref int x = ref $$")); + } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UnsafeKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UnsafeKeywordRecommenderTests.cs index a3e485c72a616..92680f0a62255 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UnsafeKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UnsafeKeywordRecommenderTests.cs @@ -497,5 +497,20 @@ public async Task TestAfterStaticKeywordInGlobalUsingDirective() { await VerifyKeywordAsync("global using static $$"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs index c0ef8cd6b2186..976213efd46d7 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/UsingKeywordRecommenderTests.cs @@ -587,5 +587,22 @@ await VerifyKeywordAsync( [assembly: Call()] """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs index 253f5fe3e52ab..f498b06f44377 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/VarKeywordRecommenderTests.cs @@ -483,5 +483,22 @@ public async Task TestAfterScoped() await VerifyKeywordAsync(AddInsideMethod("scoped $$")); await VerifyKeywordAsync("scoped $$"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, + CSharpNextParseOptions, + CSharpNextScriptParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VirtualKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VirtualKeywordRecommenderTests.cs index c7e5cb44a1e2d..774157d5190f7 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/VirtualKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/VirtualKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class VirtualKeywordRecommenderTests : KeywordRecommenderTests + public sealed class VirtualKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestNotAtRoot_Interactive() @@ -407,5 +407,20 @@ class C { private protected $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs index 7f79f1cfd613d..d9e2114ab6da4 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/VoidKeywordRecommenderTests.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class VoidKeywordRecommenderTests : KeywordRecommenderTests + public sealed class VoidKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot() @@ -938,5 +938,20 @@ public async Task TestNotInRegularUsingDirective() { await VerifyAbsenceAsync("using T = $$"); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyKeywordAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/CSharpTest2/Recommendations/VolatileKeywordRecommenderTests.cs b/src/EditorFeatures/CSharpTest2/Recommendations/VolatileKeywordRecommenderTests.cs index 1c5b8dca3195b..692b8e8d1178e 100644 --- a/src/EditorFeatures/CSharpTest2/Recommendations/VolatileKeywordRecommenderTests.cs +++ b/src/EditorFeatures/CSharpTest2/Recommendations/VolatileKeywordRecommenderTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Recommendations { [Trait(Traits.Feature, Traits.Features.KeywordRecommending)] - public class VolatileKeywordRecommenderTests : KeywordRecommenderTests + public sealed class VolatileKeywordRecommenderTests : KeywordRecommenderTests { [Fact] public async Task TestAtRoot_Interactive() @@ -508,5 +508,20 @@ void Goo() { $$ """); } + + [Fact] + public async Task TestWithinExtension() + { + await VerifyAbsenceAsync( + """ + static class C + { + extension(string s) + { + $$ + } + } + """, CSharpNextParseOptions); + } } } diff --git a/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs index b7c9e6277bd73..1949869f19175 100644 --- a/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/Adornments/AbstractAdornmentManager.cs @@ -17,7 +17,6 @@ using Microsoft.VisualStudio.Text.Formatting; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Adornments { diff --git a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs index 8e4e985af0ed3..54e4f7f9b7c32 100644 --- a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs +++ b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorContext.cs @@ -15,7 +15,6 @@ using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator; diff --git a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorScope.cs b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorScope.cs index 21436bb7dfeb1..d65e9fcdd3ae3 100644 --- a/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorScope.cs +++ b/src/EditorFeatures/Core.Wpf/BackgroundWorkIndicator/BackgroundWorkIndicatorScope.cs @@ -5,7 +5,6 @@ using System; using System.Threading; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator; diff --git a/src/EditorFeatures/Core.Wpf/IWpfDifferenceViewerExtensions.cs b/src/EditorFeatures/Core.Wpf/IWpfDifferenceViewerExtensions.cs index d00224da10320..4e702d2f01ab7 100644 --- a/src/EditorFeatures/Core.Wpf/IWpfDifferenceViewerExtensions.cs +++ b/src/EditorFeatures/Core.Wpf/IWpfDifferenceViewerExtensions.cs @@ -13,7 +13,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Projection; using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions { diff --git a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTag.cs b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTag.cs index fc6415f180205..85476119ad962 100644 --- a/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTag.cs +++ b/src/EditorFeatures/Core.Wpf/InlineDiagnostics/InlineDiagnosticsTag.cs @@ -18,7 +18,6 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.InlineDiagnostics { diff --git a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs index c7fad83f466a5..d62014cbd01a0 100644 --- a/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs +++ b/src/EditorFeatures/Core.Wpf/InlineHints/InlineHintsTagger.cs @@ -17,7 +17,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.InlineHints; diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml index be642b6dfac18..36632cbe37f86 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml @@ -12,7 +12,6 @@ xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" - MinWidth="200" KeyDown="Adornment_KeyDown" MouseDown="Adornment_ConsumeMouseEvent" MouseUp="Adornment_ConsumeMouseEvent" @@ -97,13 +96,14 @@ - + @@ -125,6 +125,7 @@ Text="{Binding ElementName=control, Path=SubmitText}" Margin="5, 5, 0, 0" FontStyle="Italic" + TextWrapping="Wrap" /> diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml.cs b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml.cs index 4b35e3e3832f6..ad8b1fbd8e1f3 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/Adornment/RenameFlyout.xaml.cs @@ -23,6 +23,8 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename /// internal partial class RenameFlyout : InlineRenameAdornment { + private const int DefaultMinWidth = 200; + private readonly RenameFlyoutViewModel _viewModel; private readonly IEditorFormatMap _editorFormatMap; private readonly IWpfTextView _textView; @@ -147,8 +149,11 @@ private void PositionAdornment() ? _textView.ViewportRight - width : desiredLeft; - Canvas.SetTop(this, top); - Canvas.SetLeft(this, left); + MaxWidth = _textView.ViewportRight; + MinWidth = Math.Min(DefaultMinWidth, _textView.ViewportWidth); + + Canvas.SetTop(this, Math.Max(0, top)); + Canvas.SetLeft(this, Math.Max(0, left)); } public override void Dispose() diff --git a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml index 5c0518f50525d..69a06a7bcacf0 100644 --- a/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml +++ b/src/EditorFeatures/Core.Wpf/InlineRename/UI/SmartRename/SmartRenameUserInputComboBox.xaml @@ -15,6 +15,7 @@ ItemsSource="{Binding SuggestedNames}" StaysOpenOnEdit="True" IsEditable="True" + IsTextSearchEnabled="False" Style="{StaticResource {x:Static vsfx:VsResourceKeys.ComboBoxStyleKey}}" GotKeyboardFocus="ComboBox_GotKeyboardFocus" VirtualizingStackPanel.IsVirtualizing="True" diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs index bf3b243d0be11..414ee3dd3cef2 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveEvaluator.cs @@ -20,7 +20,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Interactive { diff --git a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveGlobalUndoServiceFactory.cs b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveGlobalUndoServiceFactory.cs index f3cdb820b317c..ed6703990cb54 100644 --- a/src/EditorFeatures/Core.Wpf/Interactive/InteractiveGlobalUndoServiceFactory.cs +++ b/src/EditorFeatures/Core.Wpf/Interactive/InteractiveGlobalUndoServiceFactory.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Text.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Interactive { diff --git a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs index 659e7faf5f05a..42154a5755cee 100644 --- a/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/LineSeparators/LineSeparatorAdornmentManager.cs @@ -9,7 +9,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.LineSeparators { diff --git a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Callback.cs b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Callback.cs index cb42cc3076b4f..cd2996666f6e2 100644 --- a/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Callback.cs +++ b/src/EditorFeatures/Core.Wpf/NavigateTo/NavigateToItemProvider.Callback.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; using Microsoft.VisualStudio.Text.PatternMatching; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo { diff --git a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs index d3b86ff56a40a..f78c5d70cd9ac 100644 --- a/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs +++ b/src/EditorFeatures/Core.Wpf/Peek/PeekableItemFactory.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Linq; using System.Threading; diff --git a/src/EditorFeatures/Core.Wpf/Preview/DifferenceViewerPreview.cs b/src/EditorFeatures/Core.Wpf/Preview/DifferenceViewerPreview.cs index ae8a5ebb885b6..c7c93b2787992 100644 --- a/src/EditorFeatures/Core.Wpf/Preview/DifferenceViewerPreview.cs +++ b/src/EditorFeatures/Core.Wpf/Preview/DifferenceViewerPreview.cs @@ -12,7 +12,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text.Differencing; using Microsoft.VisualStudio.Text.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview { diff --git a/src/EditorFeatures/Core.Wpf/Preview/PreviewFactoryService.cs b/src/EditorFeatures/Core.Wpf/Preview/PreviewFactoryService.cs index de16129008ca8..f34e2dbd6353a 100644 --- a/src/EditorFeatures/Core.Wpf/Preview/PreviewFactoryService.cs +++ b/src/EditorFeatures/Core.Wpf/Preview/PreviewFactoryService.cs @@ -18,7 +18,6 @@ using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Text.Projection; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Preview { diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/Extensions.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/Extensions.cs index eaea8ad593cf6..f3b23e78f1956 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/Extensions.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/Extensions.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.QuickInfo { diff --git a/src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml.cs b/src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml.cs index 424d1c8c775d2..5dcd1d1be8af9 100644 --- a/src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml.cs +++ b/src/EditorFeatures/Core.Wpf/QuickInfo/OnTheFlyDocsView.xaml.cs @@ -14,6 +14,7 @@ using System.Windows.Controls; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Copilot; +using Microsoft.CodeAnalysis.Editor.Copilot; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; @@ -179,7 +180,8 @@ private async Task SetResultTextAsync(ICopilotCodeAnalysisService copilotService try { - var (responseString, isQuotaExceeded) = await copilotService.GetOnTheFlyDocsAsync(_onTheFlyDocsInfo.SymbolSignature, _onTheFlyDocsInfo.DeclarationCode, _onTheFlyDocsInfo.Language, cancellationToken).ConfigureAwait(false); + var prompt = await copilotService.GetOnTheFlyDocsPromptAsync(_onTheFlyDocsInfo, cancellationToken).ConfigureAwait(false); + var (responseString, isQuotaExceeded) = await copilotService.GetOnTheFlyDocsResponseAsync(prompt, cancellationToken).ConfigureAwait(false); var copilotRequestTime = stopwatch.Elapsed; await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -197,7 +199,7 @@ private async Task SetResultTextAsync(ICopilotCodeAnalysisService copilotService // https://dev.azure.com/devdiv/DevDiv/_wiki/wikis/DevDiv.wiki/45121/Free-SKU-Handling-Guidance-and-Recommendations var uiShell = _serviceProvider.GetServiceOnMainThread(); uiShell.PostExecCommand( - new Guid("39B0DEDE-D931-4A92-9AA2-3447BC4998DC"), + CopilotConstants.CopilotQuotaExceededGuid, 0x3901, nCmdexecopt: 0, pvaIn: null); diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session.cs index 2536450973e52..9de3c4ff61bf6 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session.cs @@ -4,9 +4,7 @@ #nullable disable -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp { diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs index 2b913940c08ef..1174c20a726ea 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_ComputeModel.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.VisualStudio.Text; -using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_SetModelSelectedItem.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_SetModelSelectedItem.cs index 026e40169bcb8..9d39545a5aef2 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_SetModelSelectedItem.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Controller.Session_SetModelSelectedItem.cs @@ -6,8 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; -using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp { diff --git a/src/EditorFeatures/Core.Wpf/SignatureHelp/Model.cs b/src/EditorFeatures/Core.Wpf/SignatureHelp/Model.cs index 00d210ad0890c..de9bc206ecfba 100644 --- a/src/EditorFeatures/Core.Wpf/SignatureHelp/Model.cs +++ b/src/EditorFeatures/Core.Wpf/SignatureHelp/Model.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp { diff --git a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.cs b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.cs index 0e0e7d4edb5b3..5b54fe64dff28 100644 --- a/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.cs +++ b/src/EditorFeatures/Core.Wpf/StringIndentation/StringIndentationAdornmentManager.cs @@ -13,7 +13,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.StringIndentation { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs index cb486d50fcc43..e356b48f8dbf4 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionWithNestedFlavors.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs index e227356ca87df..537fea880b42d 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/AbstractFixAllSuggestedAction.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs index 4e52102add66b..e4294a41f0e77 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActions/SuggestedAction.CaretPositionRestorer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs index 549acf728a56a..70ba8fbf31827 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource.cs @@ -9,11 +9,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs index 49b3fe24307b5..1c0176004abff 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSourceProvider.cs @@ -20,7 +20,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions { diff --git a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs index 206c7f08f8f81..4fe80c0b9563a 100644 --- a/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs +++ b/src/EditorFeatures/Core.Wpf/Suggestions/SuggestedActionsSource_Async.cs @@ -12,7 +12,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Shared; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Options; diff --git a/src/EditorFeatures/Core/AddImports/AbstractAddImportsPasteCommandHandler.cs b/src/EditorFeatures/Core/AddImports/AbstractAddImportsPasteCommandHandler.cs index 4f89fe2ee33e7..af45baddacc1a 100644 --- a/src/EditorFeatures/Core/AddImports/AbstractAddImportsPasteCommandHandler.cs +++ b/src/EditorFeatures/Core/AddImports/AbstractAddImportsPasteCommandHandler.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImportOnPaste; using Microsoft.CodeAnalysis.AddMissingImports; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; diff --git a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs index 0a7150c29cc60..76b24069f2701 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/BraceCompletionSessionProvider.BraceCompletionSession.cs @@ -20,7 +20,6 @@ using Microsoft.VisualStudio.Text.BraceCompletion; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AutomaticCompletion; diff --git a/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs b/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs index 26ffb71470ca7..cdda4dd93f8bc 100644 --- a/src/EditorFeatures/Core/AutomaticCompletion/Extensions.cs +++ b/src/EditorFeatures/Core/AutomaticCompletion/Extensions.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 Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.BraceCompletion; diff --git a/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs b/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs index 412030cb17cb7..4c1e0d851ada9 100644 --- a/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/BraceMatching/BraceHighlightingViewTaggerProvider.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.Collections.Immutable; using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; diff --git a/src/EditorFeatures/Core/ChangeSignature/AbstractChangeSignatureCommandHandler.cs b/src/EditorFeatures/Core/ChangeSignature/AbstractChangeSignatureCommandHandler.cs index 8c81a0fe321df..653b84794cce2 100644 --- a/src/EditorFeatures/Core/ChangeSignature/AbstractChangeSignatureCommandHandler.cs +++ b/src/EditorFeatures/Core/ChangeSignature/AbstractChangeSignatureCommandHandler.cs @@ -125,7 +125,7 @@ private static void HandleResult(ChangeSignatureResult result, Solution oldSolut string.Format(EditorFeaturesResources.Preview_Changes_0, EditorFeaturesResources.Change_Signature), "vs.csharp.refactoring.preview", EditorFeaturesResources.Change_Signature_colon, - result.Name, + result.Name!, result.Glyph.GetValueOrDefault(), result.UpdatedSolution, oldSolution); diff --git a/src/EditorFeatures/Core/Classification/ClassificationTags.cs b/src/EditorFeatures/Core/Classification/ClassificationTags.cs index 4fb4aadf7e4ab..95803c17e6442 100644 --- a/src/EditorFeatures/Core/Classification/ClassificationTags.cs +++ b/src/EditorFeatures/Core/Classification/ClassificationTags.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; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs index b26cd16e6f34a..9c5c9091fd842 100644 --- a/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/Classification/Semantic/AbstractSemanticOrEmbeddedClassificationViewTaggerProvider.cs @@ -23,7 +23,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.ClassifiedLineCache.cs b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.ClassifiedLineCache.cs index 7b53e6139c6fc..c1dc9cb55ade9 100644 --- a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.ClassifiedLineCache.cs +++ b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.ClassifiedLineCache.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs index 47b10e864bfcf..64e62295d6774 100644 --- a/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs +++ b/src/EditorFeatures/Core/Classification/Syntactic/SyntacticClassificationTaggerProvider.TagComputer.cs @@ -19,7 +19,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs index 427695b244831..e8b27fb5d1a4d 100644 --- a/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs +++ b/src/EditorFeatures/Core/CodeDefinitionWindow/DefinitionContextTracker.cs @@ -186,7 +186,7 @@ internal async Task> GetContextFrom var declarationFile = await _metadataAsSourceFileService.GetGeneratedFileAsync(workspace, document.Project, symbol, signaturesOnly: false, options: options, cancellationToken: cancellationToken).ConfigureAwait(false); var identifierSpan = declarationFile.IdentifierLocation.GetLineSpan().Span; locations.Add(new CodeDefinitionWindowLocation( - symbol.ToDisplayString(), declarationFile.FilePath, identifierSpan.Start)); + symbol.ToDisplayString(), declarationFile.FilePath!, identifierSpan.Start)); } } } diff --git a/src/EditorFeatures/Core/CodeRefactorings/EditorLayerCodeActionHelpersService.cs b/src/EditorFeatures/Core/CodeRefactorings/EditorLayerCodeActionHelpersService.cs index 8377489ee15b9..18e2e2402b326 100644 --- a/src/EditorFeatures/Core/CodeRefactorings/EditorLayerCodeActionHelpersService.cs +++ b/src/EditorFeatures/Core/CodeRefactorings/EditorLayerCodeActionHelpersService.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.Composition; using Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/Commanding/LegacyCommandHandlerServiceFactory.cs b/src/EditorFeatures/Core/Commanding/LegacyCommandHandlerServiceFactory.cs index c8344eed699a6..d057d4f2bec5e 100644 --- a/src/EditorFeatures/Core/Commanding/LegacyCommandHandlerServiceFactory.cs +++ b/src/EditorFeatures/Core/Commanding/LegacyCommandHandlerServiceFactory.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.ComponentModel.Composition; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/EditorFeatures/Core/CommentSelection/AbstractToggleBlockCommentBase.cs b/src/EditorFeatures/Core/CommentSelection/AbstractToggleBlockCommentBase.cs index 0fcc947eae5b8..9ef36b27f0f74 100644 --- a/src/EditorFeatures/Core/CommentSelection/AbstractToggleBlockCommentBase.cs +++ b/src/EditorFeatures/Core/CommentSelection/AbstractToggleBlockCommentBase.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.Generic; using System.Collections.Immutable; diff --git a/src/EditorFeatures/Core/CommentSelection/CommentSelectionResult.cs b/src/EditorFeatures/Core/CommentSelection/CommentSelectionResult.cs index db211738e78f5..669ab8e37f146 100644 --- a/src/EditorFeatures/Core/CommentSelection/CommentSelectionResult.cs +++ b/src/EditorFeatures/Core/CommentSelection/CommentSelectionResult.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.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Core/CommentSelection/CommentTrackingSpan.cs b/src/EditorFeatures/Core/CommentSelection/CommentTrackingSpan.cs index e97807eab7194..9c0944f0af267 100644 --- a/src/EditorFeatures/Core/CommentSelection/CommentTrackingSpan.cs +++ b/src/EditorFeatures/Core/CommentSelection/CommentTrackingSpan.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 Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CommentSelection; diff --git a/src/EditorFeatures/Core/CommentSelection/ToggleBlockCommentCommandHandler.cs b/src/EditorFeatures/Core/CommentSelection/ToggleBlockCommentCommandHandler.cs index 2d7e9dc241f63..cffb24053e6fd 100644 --- a/src/EditorFeatures/Core/CommentSelection/ToggleBlockCommentCommandHandler.cs +++ b/src/EditorFeatures/Core/CommentSelection/ToggleBlockCommentCommandHandler.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.ComponentModel.Composition; diff --git a/src/EditorFeatures/Core/Copilot/CopilotConstants.cs b/src/EditorFeatures/Core/Copilot/CopilotConstants.cs new file mode 100644 index 0000000000000..e8823211b1f27 --- /dev/null +++ b/src/EditorFeatures/Core/Copilot/CopilotConstants.cs @@ -0,0 +1,25 @@ +// 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 Microsoft.VisualStudio.Language.Suggestions; + +namespace Microsoft.CodeAnalysis.Editor.Copilot; + +internal static class CopilotConstants +{ + public const int CopilotIconLogoId = 1; + public const int CopilotIconSparkleId = 2; + public const int CopilotIconSparkleBlueId = 3; + + /// + /// This flag is used to indicate that a thinking state tip should be shown. + /// This will eventually be defined in Microsoft.VisualStudio.Language.Suggestions.TipStyle once the behavior is finalized. + /// + // Copied from https://devdiv.visualstudio.com/DevDiv/_git/IntelliCode-VS?path=%2Fsrc%2FVSIX%2FIntelliCode.VSIX%2FSuggestionService%2FImplementation%2FSuggestionSession.cs& + public const TipStyle ShowThinkingStateTipStyle = (TipStyle)0x20; + + public static readonly Guid CopilotIconMonikerGuid = new("{4515B9BD-70A1-45FA-9545-D4536417C596}"); + public static readonly Guid CopilotQuotaExceededGuid = new("39B0DEDE-D931-4A92-9AA2-3447BC4998DC"); +} diff --git a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs index 5c3157ba29539..a1105e718c04c 100644 --- a/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs +++ b/src/EditorFeatures/Core/Copilot/CopilotTaggerProvider.cs @@ -17,7 +17,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Copilot; diff --git a/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentManager.cs b/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentManager.cs index 43b47dee86976..8eb987d588840 100644 --- a/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentManager.cs +++ b/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentManager.cs @@ -67,7 +67,7 @@ private async Task GenerateDocumentationCommentProposalsAsync(Document document, var provider = textView.Properties.GetOrCreateSingletonProperty(typeof(CopilotGenerateDocumentationCommentProvider), () => new CopilotGenerateDocumentationCommentProvider(_threadingContext, copilotService)); - await provider!.InitializeAsync(textView, _suggestionServiceBase!, cancellationToken).ConfigureAwait(false); + await provider.InitializeAsync(textView, _suggestionServiceBase!, cancellationToken).ConfigureAwait(false); return provider; } diff --git a/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentProvider.cs b/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentProvider.cs index fe1e84615b246..77c18de2cf426 100644 --- a/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentProvider.cs +++ b/src/EditorFeatures/Core/DocumentationComments/CopilotGenerateDocumentationCommentProvider.cs @@ -3,31 +3,35 @@ // See the LICENSE file in the project root for more information. using System; +using System.CodeDom.Compiler; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Text; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Language.Proposals; using Microsoft.VisualStudio.Language.Suggestions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DocumentationComments { - internal class CopilotGenerateDocumentationCommentProvider : SuggestionProviderBase + internal sealed class CopilotGenerateDocumentationCommentProvider : SuggestionProviderBase { private SuggestionManagerBase? _suggestionManager; private readonly ICopilotCodeAnalysisService _copilotService; public readonly IThreadingContext ThreadingContext; + [MemberNotNullWhen(true, nameof(_suggestionManager))] public bool Enabled => _enabled && (_suggestionManager != null); private bool _enabled = true; @@ -45,14 +49,15 @@ public async Task InitializeAsync(ITextView textView, SuggestionServiceBase sugg public async Task GenerateDocumentationProposalAsync(DocumentationCommentSnippet snippet, ITextSnapshot oldSnapshot, VirtualSnapshotPoint oldCaret, CancellationToken cancellationToken) { - await Task.Yield().ConfigureAwait(false); - + // Checks to see if the feature is enabled and if the suggestionManager is available if (!Enabled) { return; } - // MemberNode is not null at this point, checked when determining if the file is exluded. + await TaskScheduler.Default; + + // MemberNode is not null at this point, checked when determining if the file is excluded. var snippetProposal = GetSnippetProposal(snippet.SnippetText, snippet.MemberNode!, snippet.Position, snippet.CaretOffset); if (snippetProposal is null) @@ -60,22 +65,21 @@ public async Task GenerateDocumentationProposalAsync(DocumentationCommentSnippet return; } - // Do not do IntelliCode line completions if we're about to generate a documentation comment - // so that won't have interfering grey text. - var intellicodeLineCompletionsDisposable = await _suggestionManager!.DisableProviderAsync(SuggestionServiceNames.IntelliCodeLineCompletions, cancellationToken).ConfigureAwait(false); - - var proposalEdits = await GetProposedEditsAsync(snippetProposal, _copilotService, oldSnapshot, snippet.IndentText, cancellationToken).ConfigureAwait(false); + // We need to disable IntelliCode Line Completions when starting a documentation comment session. Our suggestions take precedence to line completions in the documentation comment case. + // It needs to be disposed of in any case we have left the session, either after a user has accepted grey text or if we needed to bail out earlier in the process. + // Disposing of the provider is necessary to reenable the provider. + var intelliCodeLineCompletionsDisposable = await _suggestionManager.DisableProviderAsync(SuggestionServiceNames.IntelliCodeLineCompletions, cancellationToken).ConfigureAwait(false); - var proposal = Proposal.TryCreateProposal(null, proposalEdits, oldCaret, flags: ProposalFlags.SingleTabToAccept); - - if (proposal is null) + var suggestion = new DocumentationCommentSuggestion(this, _suggestionManager, intelliCodeLineCompletionsDisposable); + Func> generateProposal = async (cancellationToken) => { - await intellicodeLineCompletionsDisposable.DisposeAsync().ConfigureAwait(false); - return; - } + var proposalEdits = await GetProposedEditsAsync( + snippetProposal, _copilotService, oldSnapshot, snippet.IndentText, cancellationToken).ConfigureAwait(false); + + return Proposal.TryCreateProposal(description: null, proposalEdits, oldCaret, flags: ProposalFlags.ShowCommitHighlight); + }; - var suggestion = new DocumentationCommentSuggestion(this, proposal, _suggestionManager, intellicodeLineCompletionsDisposable); - await suggestion.TryDisplaySuggestionAsync(cancellationToken).ConfigureAwait(false); + await suggestion.StartSuggestionSessionWithProposalAsync(generateProposal, cancellationToken).ConfigureAwait(false); } /// @@ -96,9 +100,12 @@ public async Task GenerateDocumentationProposalAsync(DocumentationCommentSnippet var summaryEndTag = comments.IndexOf("", index, StringComparison.Ordinal); if (summaryEndTag != -1 && summaryStartTag != -1) { - proposedEdits.Add(new DocumentationCommentProposedEdit(new TextSpan(caret + startIndex, 0), null, DocumentationCommentTagType.Summary)); + proposedEdits.Add(new DocumentationCommentProposedEdit(new TextSpan(caret + startIndex, 0), symbolName: null, DocumentationCommentTagType.Summary)); } + // We may receive remarks from the model. In that case, we want to insert the remark tags and remark directly after the summary. + proposedEdits.Add(new DocumentationCommentProposedEdit(new TextSpan(summaryEndTag + "".Length + startIndex, 0), symbolName: null, DocumentationCommentTagType.Remarks)); + while (true) { var typeParamEndTag = comments.IndexOf("", index, StringComparison.Ordinal); @@ -144,7 +151,7 @@ public async Task GenerateDocumentationProposalAsync(DocumentationCommentSnippet var returnsEndTag = comments.IndexOf("", index, StringComparison.Ordinal); if (returnsEndTag != -1) { - proposedEdits.Add(new DocumentationCommentProposedEdit(new TextSpan(returnsEndTag + startIndex, 0), null, DocumentationCommentTagType.Returns)); + proposedEdits.Add(new DocumentationCommentProposedEdit(new TextSpan(returnsEndTag + startIndex, 0), symbolName: null, DocumentationCommentTagType.Returns)); } while (true) @@ -199,47 +206,67 @@ private static async Task> GetProposedEditsAsync( foreach (var edit in proposal.ProposedEdits) { - string? copilotStatement = null; var textSpan = edit.SpanToReplace; string? symbolKey = null; if (edit.SymbolName is not null) { - symbolKey = edit.TagType.ToString() + "- " + edit.SymbolName; + symbolKey = edit.TagType.ToString() + "-" + edit.SymbolName; + } + + var copilotStatement = GetCopilotStatement(documentationCommentDictionary, edit, symbolKey); + + // Just skip this piece of the documentation comment if, for some reason, it is not found. + if (copilotStatement is null) + { + continue; } + var proposedEdit = new ProposedEdit(new SnapshotSpan(oldSnapshot, textSpan.Start, textSpan.Length), + AddNewLinesToCopilotText(copilotStatement, indentText, edit.TagType, characterLimit: 120)); + list.Add(proposedEdit); + } + + return list; + + static string? GetCopilotStatement(Dictionary documentationCommentDictionary, DocumentationCommentProposedEdit edit, string? symbolKey) + { if (edit.TagType == DocumentationCommentTagType.Summary && documentationCommentDictionary.TryGetValue(DocumentationCommentTagType.Summary.ToString(), out var summary) && !string.IsNullOrEmpty(summary)) { - copilotStatement = summary; + return summary; + } + else if (edit.TagType == DocumentationCommentTagType.Remarks && documentationCommentDictionary.TryGetValue(DocumentationCommentTagType.Remarks.ToString(), out var remarks) && !string.IsNullOrEmpty(remarks)) + { + return remarks; } - if (edit.TagType == DocumentationCommentTagType.TypeParam && documentationCommentDictionary.TryGetValue(symbolKey!, out var typeParam) && !string.IsNullOrEmpty(typeParam)) + else if (edit.TagType == DocumentationCommentTagType.TypeParam && documentationCommentDictionary.TryGetValue(symbolKey!, out var typeParam) && !string.IsNullOrEmpty(typeParam)) { - copilotStatement = typeParam; + return typeParam; } else if (edit.TagType == DocumentationCommentTagType.Param && documentationCommentDictionary.TryGetValue(symbolKey!, out var param) && !string.IsNullOrEmpty(param)) { - copilotStatement = param; + return param; } else if (edit.TagType == DocumentationCommentTagType.Returns && documentationCommentDictionary.TryGetValue(DocumentationCommentTagType.Returns.ToString(), out var returns) && !string.IsNullOrEmpty(returns)) { - copilotStatement = returns; + return returns; } else if (edit.TagType == DocumentationCommentTagType.Exception && documentationCommentDictionary.TryGetValue(symbolKey!, out var exception) && !string.IsNullOrEmpty(exception)) { - copilotStatement = exception; + return exception; } - var proposedEdit = new ProposedEdit(new SnapshotSpan(oldSnapshot, textSpan.Start, textSpan.Length), - AddNewLinesToCopilotText(copilotStatement!, indentText, characterLimit: 120)); - list.Add(proposedEdit); + return null; } - return list; - - static string AddNewLinesToCopilotText(string copilotText, string? indentText, int characterLimit) + static string AddNewLinesToCopilotText(string copilotText, string? indentText, DocumentationCommentTagType tagType, int characterLimit) { + // Double check that the resultant from Copilot does not produce any strings containing new line characters. + copilotText = Regex.Replace(copilotText, @"\r?\n", " "); var builder = new StringBuilder(); + copilotText = BuildCopilotTextForRemarks(copilotText, indentText, tagType, builder); + var words = copilotText.Split(' '); var currentLineLength = 0; characterLimit -= (indentText!.Length + "/// ".Length); @@ -264,6 +291,22 @@ static string AddNewLinesToCopilotText(string copilotText, string? indentText, i } return builder.ToString(); + + static string BuildCopilotTextForRemarks(string copilotText, string? indentText, DocumentationCommentTagType tagType, StringBuilder builder) + { + if (tagType is DocumentationCommentTagType.Remarks) + { + builder.AppendLine(); + builder.Append(indentText); + builder.Append("/// "); + builder.Append(copilotText); + builder.Append(""); + copilotText = builder.ToString(); + builder.Clear(); + } + + return copilotText; + } } } diff --git a/src/EditorFeatures/Core/DocumentationComments/DocumentationCommentSuggestion.cs b/src/EditorFeatures/Core/DocumentationComments/DocumentationCommentSuggestion.cs index 0d9cad652e085..37a9478025758 100644 --- a/src/EditorFeatures/Core/DocumentationComments/DocumentationCommentSuggestion.cs +++ b/src/EditorFeatures/Core/DocumentationComments/DocumentationCommentSuggestion.cs @@ -4,25 +4,27 @@ using System; using System.ComponentModel; +using System.Drawing; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Copilot; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Language.Proposals; using Microsoft.VisualStudio.Language.Suggestions; using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Threading; namespace Microsoft.CodeAnalysis.DocumentationComments { - internal class DocumentationCommentSuggestion(CopilotGenerateDocumentationCommentProvider providerInstance, ProposalBase proposal, - SuggestionManagerBase suggestionManager, VisualStudio.Threading.IAsyncDisposable? intellicodeLineCompletionsDisposable) : SuggestionBase + internal sealed class DocumentationCommentSuggestion(CopilotGenerateDocumentationCommentProvider providerInstance, + SuggestionManagerBase suggestionManager, VisualStudio.Threading.IAsyncDisposable? intelliCodeLineCompletionsDisposable) : SuggestionBase { - public ProposalBase Proposal { get; } = proposal; - public SuggestionManagerBase SuggestionManager { get; } = suggestionManager; - public VisualStudio.Threading.IAsyncDisposable? IntellicodeLineCompletionsDisposable { get; set; } = intellicodeLineCompletionsDisposable; + public VisualStudio.Threading.IAsyncDisposable? IntelliCodeLineCompletionsDisposable { get; set; } = intelliCodeLineCompletionsDisposable; - public override TipStyle TipStyle => TipStyle.AlwaysShowTip; + public override TipStyle TipStyle => TipStyle.AlwaysShowTip | CopilotConstants.ShowThinkingStateTipStyle; public override EditDisplayStyle EditStyle => EditDisplayStyle.GrayText; @@ -37,7 +39,7 @@ public override async Task OnAcceptedAsync(SuggestionSessionBase session, Propos var threadingContext = providerInstance.ThreadingContext; await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancel); - await DisposeAsync().ConfigureAwait(false); + await DisposeIntelliCodeCompletionsDisposableAsync().ConfigureAwait(false); Logger.Log(FunctionId.Copilot_Generate_Documentation_Accepted, logLevel: LogLevel.Information); } @@ -65,34 +67,121 @@ public override Task OnProposalUpdatedAsync(SuggestionSessionBase session, Propo return Task.CompletedTask; } - public async Task TryDisplaySuggestionAsync(CancellationToken cancellationToken) + public async Task StartSuggestionSessionWithProposalAsync( + Func> generateProposal, CancellationToken cancellationToken) { - _suggestionSession = await SuggestionManager.TryDisplaySuggestionAsync(this, cancellationToken).ConfigureAwait(false); + var sessionStarted = await StartSuggestionSessionAsync(cancellationToken).ConfigureAwait(false); + if (!sessionStarted) + { + return; + } - if (_suggestionSession != null) + var proposal = await generateProposal(cancellationToken).ConfigureAwait(false); + if (proposal is null) { - var success = await TryDisplayProposalAsync(_suggestionSession, cancellationToken).ConfigureAwait(false); - if (success) - { - Logger.Log(FunctionId.Copilot_Generate_Documentation_Displayed, logLevel: LogLevel.Information); - } + await DismissSuggestionSessionAsync(cancellationToken).ConfigureAwait(false); + return; } + + await TryDisplayDocumentationSuggestionAsync(proposal, cancellationToken).ConfigureAwait(false); } - private async Task TryDisplayProposalAsync(SuggestionSessionBase session, CancellationToken cancellationToken) + /// + /// Starts the Suggestion Session. The TryDisplaySuggestion call doesn't display any grey text, but starts the session such that we have the + /// exclusive right to display grey text later. + /// + /// If true, user will see the thinking state as long as the Suggestion Session is active and replace with grey text if a call to DisplayProposal succeeds. + /// If unable to retrieve the session, the caller should bail out. + /// + private async Task StartSuggestionSessionAsync(CancellationToken cancellationToken) + { + _suggestionSession = await RunWithEnqueueActionAsync( + "StartWork", + async () => await SuggestionManager.TryDisplaySuggestionAsync(this, cancellationToken).ConfigureAwait(false), + cancellationToken).ConfigureAwait(false); + + if (_suggestionSession is null) + { + await DisposeIntelliCodeCompletionsDisposableAsync().ConfigureAwait(false); + return false; + } + + return true; + } + + /// + /// This is where we actually try to display the grey-text from the proposal + /// we created. + /// + public async Task TryDisplayDocumentationSuggestionAsync(ProposalBase proposal, CancellationToken cancellationToken) { try { - await providerInstance.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - await session.DisplayProposalAsync(Proposal, cancellationToken).ConfigureAwait(false); - return true; + await RunWithEnqueueActionAsync( + "DisplayProposal", + async () => + { + await _suggestionSession!.DisplayProposalAsync(proposal, cancellationToken).ConfigureAwait(false); + return true; + }, + cancellationToken).ConfigureAwait(false); + + Logger.Log(FunctionId.Copilot_Generate_Documentation_Displayed, logLevel: LogLevel.Information); } catch (OperationCanceledException) { Logger.Log(FunctionId.Copilot_Generate_Documentation_Canceled, logLevel: LogLevel.Information); } + } + + /// + /// Dismisses the session if the proposal we generated was invalid. + /// Needs to dispose of the IntelliCodeCompletionsDisposable so we no longer have exclusive right to + /// display any grey text. + /// + private async Task DismissSuggestionSessionAsync(CancellationToken cancellationToken) + { + await RunWithEnqueueActionAsync( + "DismissSuggestionSession", + async () => + { + await ClearSuggestionAsync(ReasonForDismiss.DismissedDueToInvalidProposal, cancellationToken).ConfigureAwait(false); + return true; + }, + cancellationToken).ConfigureAwait(false); + } + + /// + /// In general, calls to a SuggestionManager or SuggestionSession need to be wrapped in an EnqueueAction. + /// This is the pattern recommended by VS Platform to avoid races. + /// Pattern from platform shown here: + /// https://devdiv.visualstudio.com/DevDiv/_git/IntelliCode-VS?path=/src/VSIX/IntelliCode.VSIX/SuggestionService/AmbientAI/SuggestionProviderForAmbientAI.cs + /// + private async Task RunWithEnqueueActionAsync(string description, Func> action, CancellationToken cancellationToken) + { + Assumes.NotNull(SuggestionManager); + + var taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + await providerInstance.ThreadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + SuggestionManager.EnqueueAction(description, async () => + { + try + { + var result = await action().ConfigureAwaitRunInline(); + taskCompletionSource.TrySetResult(result); + } + catch (OperationCanceledException operationCanceledException) + { + taskCompletionSource.TrySetCanceled(operationCanceledException.CancellationToken); + } + catch (Exception exception) + { + taskCompletionSource.TrySetException(exception); + } + }); - return false; + return await taskCompletionSource.Task.WithCancellation(cancellationToken).ConfigureAwait(false); } private async Task ClearSuggestionAsync(ReasonForDismiss reason, CancellationToken cancellationToken) @@ -103,15 +192,19 @@ private async Task ClearSuggestionAsync(ReasonForDismiss reason, CancellationTok } _suggestionSession = null; - await DisposeAsync().ConfigureAwait(false); + await DisposeIntelliCodeCompletionsDisposableAsync().ConfigureAwait(false); } - private async Task DisposeAsync() + /// + /// The IntelliCodeLineCompletionDisposable needs to be disposed any time we exit the SuggestionSession so that + /// line completions can be shown again. + /// + private async Task DisposeIntelliCodeCompletionsDisposableAsync() { - if (IntellicodeLineCompletionsDisposable != null) + if (IntelliCodeLineCompletionsDisposable != null) { - await IntellicodeLineCompletionsDisposable.DisposeAsync().ConfigureAwait(false); - IntellicodeLineCompletionsDisposable = null; + await IntelliCodeLineCompletionsDisposable.DisposeAsync().ConfigureAwait(false); + IntelliCodeLineCompletionsDisposable = null; } } } diff --git a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs index 533dabd5e54e8..dfab5f77e1cc3 100644 --- a/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs +++ b/src/EditorFeatures/Core/EditAndContinue/ActiveStatementTaggerProvider.cs @@ -4,8 +4,6 @@ using System; using System.ComponentModel.Composition; -using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; @@ -18,7 +16,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs index 74948a69c5ad0..34add1bee8489 100644 --- a/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs +++ b/src/EditorFeatures/Core/EditAndContinue/EditAndContinueLanguageService.cs @@ -378,10 +378,19 @@ public async ValueTask GetUpdatesAsync(ImmutableArray p.FilePath != null && runningProjectPaths.Contains(p.FilePath)).Select(static p => p.Id).ToImmutableHashSet(); var result = await GetDebuggingSession().EmitSolutionUpdateAsync(solution, runningProjectIds, activeStatementSpanProvider, cancellationToken).ConfigureAwait(false); - // Only store the solution if we have any changes to apply, otherwise CommitUpdatesAsync/DiscardUpdatesAsync won't be called. - if (result.ModuleUpdates.Status == ModuleUpdateStatus.Ready) + switch (result.ModuleUpdates.Status) { - _pendingUpdatedDesignTimeSolution = designTimeSolution; + case ModuleUpdateStatus.Ready: + // We have updates to be applied. The debugger will call Commit/Discard on the solution + // based on whether the updates will be applied successfully or not. + _pendingUpdatedDesignTimeSolution = designTimeSolution; + break; + + case ModuleUpdateStatus.None: + // No significant changes have been made. + // Commit the solution to apply any changes in comments that do not generate updates. + _committedDesignTimeSolution = designTimeSolution; + break; } UpdateApplyChangesDiagnostics(result.Diagnostics); diff --git a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs index c53f4d5102135..a4409bace7975 100644 --- a/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs +++ b/src/EditorFeatures/Core/Editor/GoToAdjacentMemberCommandHandler.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; diff --git a/src/EditorFeatures/Core/Editor/ITextBufferAssociatedViewService.cs b/src/EditorFeatures/Core/Editor/ITextBufferAssociatedViewService.cs index c9c32606f1e80..bdc96aa5d35c3 100644 --- a/src/EditorFeatures/Core/Editor/ITextBufferAssociatedViewService.cs +++ b/src/EditorFeatures/Core/Editor/ITextBufferAssociatedViewService.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.Generic; using System.Collections.ObjectModel; diff --git a/src/EditorFeatures/Core/Editor/ITextUndoHistoryWorkspaceService.cs b/src/EditorFeatures/Core/Editor/ITextUndoHistoryWorkspaceService.cs index 6dbfc7d6bb813..360d2f5c1cbb3 100644 --- a/src/EditorFeatures/Core/Editor/ITextUndoHistoryWorkspaceService.cs +++ b/src/EditorFeatures/Core/Editor/ITextUndoHistoryWorkspaceService.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 Microsoft.CodeAnalysis.Host; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Operations; diff --git a/src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingStyles/SourceTextExtensions.cs b/src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingStyles/SourceTextExtensions.cs index bcfccb834d896..9bb6b98bfb8dc 100644 --- a/src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingStyles/SourceTextExtensions.cs +++ b/src/EditorFeatures/Core/EditorConfigSettings/Updater/NamingStyles/SourceTextExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/EditorFeatures/Core/EncapsulateField/AbstractEncapsulateFieldCommandHandler.cs b/src/EditorFeatures/Core/EncapsulateField/AbstractEncapsulateFieldCommandHandler.cs index 71916add99162..63fac5bc1056b 100644 --- a/src/EditorFeatures/Core/EncapsulateField/AbstractEncapsulateFieldCommandHandler.cs +++ b/src/EditorFeatures/Core/EncapsulateField/AbstractEncapsulateFieldCommandHandler.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; @@ -18,7 +17,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Text.Operations; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EncapsulateField; diff --git a/src/EditorFeatures/Core/Extensibility/Commands/ExportInteractiveCommandAttribute.cs b/src/EditorFeatures/Core/Extensibility/Commands/ExportInteractiveCommandAttribute.cs index 1d49a1309b221..0cc7cbe0e53e5 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/ExportInteractiveCommandAttribute.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/ExportInteractiveCommandAttribute.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.Generic; using System.ComponentModel.Composition; diff --git a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs index 43a3d4f969a00..ae0e0f428e240 100644 --- a/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Commands/PredefinedCommandHandlerNames.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. -#nullable disable - -using Microsoft.CodeAnalysis.Host; using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion; namespace Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/Extensibility/Completion/ExportCompletionProviderAttribute.cs b/src/EditorFeatures/Core/Extensibility/Completion/ExportCompletionProviderAttribute.cs index 48be90a3f2da5..22dfaf100cbf3 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/ExportCompletionProviderAttribute.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/ExportCompletionProviderAttribute.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.ComponentModel.Composition; using Microsoft.CodeAnalysis.Completion; diff --git a/src/EditorFeatures/Core/Extensibility/Completion/PredefinedCompletionProviderNames.cs b/src/EditorFeatures/Core/Extensibility/Completion/PredefinedCompletionProviderNames.cs index cdd08a294d4f4..8b5c73cc8c480 100644 --- a/src/EditorFeatures/Core/Extensibility/Completion/PredefinedCompletionProviderNames.cs +++ b/src/EditorFeatures/Core/Extensibility/Completion/PredefinedCompletionProviderNames.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 - namespace Microsoft.CodeAnalysis.Editor; internal static class PredefinedCompletionProviderNames diff --git a/src/EditorFeatures/Core/Extensibility/Composition/IContentTypeMetadata.cs b/src/EditorFeatures/Core/Extensibility/Composition/IContentTypeMetadata.cs index fa25e7c01c22d..f1830023b5795 100644 --- a/src/EditorFeatures/Core/Extensibility/Composition/IContentTypeMetadata.cs +++ b/src/EditorFeatures/Core/Extensibility/Composition/IContentTypeMetadata.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.Collections.Generic; namespace Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs b/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs index 67a2922a8d923..cb4b407e8de55 100644 --- a/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs +++ b/src/EditorFeatures/Core/Extensibility/NavigationBar/AbstractEditorNavigationBarItemService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.NavigationBar.RoslynNavigationBarItem; namespace Microsoft.CodeAnalysis.Editor.Extensibility.NavigationBar; diff --git a/src/EditorFeatures/Core/Extensibility/NavigationBar/INavigationBarControllerFactoryService.cs b/src/EditorFeatures/Core/Extensibility/NavigationBar/INavigationBarControllerFactoryService.cs index f88d4818bf1ac..3067a0557e4cd 100644 --- a/src/EditorFeatures/Core/Extensibility/NavigationBar/INavigationBarControllerFactoryService.cs +++ b/src/EditorFeatures/Core/Extensibility/NavigationBar/INavigationBarControllerFactoryService.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 Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarAutomationStrings.cs b/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarAutomationStrings.cs index 7aa89660ee9a0..6ec4f248283ff 100644 --- a/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarAutomationStrings.cs +++ b/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarAutomationStrings.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 - namespace Microsoft.CodeAnalysis.Editor; internal static class NavigationBarAutomationStrings diff --git a/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarProjectItem.cs b/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarProjectItem.cs index 83659cf4529d9..7f61e907bc911 100644 --- a/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarProjectItem.cs +++ b/src/EditorFeatures/Core/Extensibility/NavigationBar/NavigationBarProjectItem.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/Extensibility/SignatureHelp/ISignatureHelpPresenterSession.cs b/src/EditorFeatures/Core/Extensibility/SignatureHelp/ISignatureHelpPresenterSession.cs index b5fcb1a79acd2..a3f1c82d76382 100644 --- a/src/EditorFeatures/Core/Extensibility/SignatureHelp/ISignatureHelpPresenterSession.cs +++ b/src/EditorFeatures/Core/Extensibility/SignatureHelp/ISignatureHelpPresenterSession.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.Generic; using Microsoft.CodeAnalysis.SignatureHelp; diff --git a/src/EditorFeatures/Core/Extensibility/SignatureHelp/PredefinedSignatureHelpPresenterNames.cs b/src/EditorFeatures/Core/Extensibility/SignatureHelp/PredefinedSignatureHelpPresenterNames.cs index 169faddbb2498..2f2c7d3456c63 100644 --- a/src/EditorFeatures/Core/Extensibility/SignatureHelp/PredefinedSignatureHelpPresenterNames.cs +++ b/src/EditorFeatures/Core/Extensibility/SignatureHelp/PredefinedSignatureHelpPresenterNames.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 - namespace Microsoft.CodeAnalysis.Editor; internal static class PredefinedSignatureHelpPresenterNames diff --git a/src/EditorFeatures/Core/Extensibility/SignatureHelp/SignatureHelpItemEventArgs.cs b/src/EditorFeatures/Core/Extensibility/SignatureHelp/SignatureHelpItemEventArgs.cs index a57372b3b5a38..757482db4c928 100644 --- a/src/EditorFeatures/Core/Extensibility/SignatureHelp/SignatureHelpItemEventArgs.cs +++ b/src/EditorFeatures/Core/Extensibility/SignatureHelp/SignatureHelpItemEventArgs.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 Microsoft.CodeAnalysis.SignatureHelp; diff --git a/src/EditorFeatures/Core/Extensions/GlyphExtensions.cs b/src/EditorFeatures/Core/Extensions/GlyphExtensions.cs index 372ebdbecb8ab..313fd19a8b05e 100644 --- a/src/EditorFeatures/Core/Extensions/GlyphExtensions.cs +++ b/src/EditorFeatures/Core/Extensions/GlyphExtensions.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 Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.VisualStudio.Imaging.Interop; diff --git a/src/EditorFeatures/Core/Extensions/LSPExtensions.cs b/src/EditorFeatures/Core/Extensions/LSPExtensions.cs index 8a8ee7573819f..f202dfeb6a51d 100644 --- a/src/EditorFeatures/Core/Extensions/LSPExtensions.cs +++ b/src/EditorFeatures/Core/Extensions/LSPExtensions.cs @@ -23,7 +23,7 @@ public static Roslyn.Text.Adornments.ClassifiedTextElement ToLSPElement(this Vis public static Roslyn.Text.Adornments.ContainerElement ToLSPElement(this VisualStudio.Text.Adornments.ContainerElement element) => new((Roslyn.Text.Adornments.ContainerElementStyle)element.Style, element.Elements.Select(ToLSPElement)); - private static object? ToLSPElement(object? value) + private static object ToLSPElement(object value) => value switch { VisualStudio.Core.Imaging.ImageId imageId => imageId.ToLSPImageId(), @@ -31,7 +31,6 @@ public static Roslyn.Text.Adornments.ContainerElement ToLSPElement(this VisualSt VisualStudio.Text.Adornments.ContainerElement element => element.ToLSPElement(), VisualStudio.Text.Adornments.ClassifiedTextElement element => element.ToLSPElement(), VisualStudio.Text.Adornments.ClassifiedTextRun run => run.ToLSPRun(), - _ => value, }; } diff --git a/src/EditorFeatures/Core/ExternalAccess/IntelliCode/IntentProcessor.cs b/src/EditorFeatures/Core/ExternalAccess/IntelliCode/IntentProcessor.cs index 13af115a66219..61960e93289b1 100644 --- a/src/EditorFeatures/Core/ExternalAccess/IntelliCode/IntentProcessor.cs +++ b/src/EditorFeatures/Core/ExternalAccess/IntelliCode/IntentProcessor.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Features.Intents; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/UnitTestGeneratorOrganizeImportsAccessor.cs b/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/UnitTestGeneratorOrganizeImportsAccessor.cs index 0dc02e545b49c..05e7e81ca84c3 100644 --- a/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/UnitTestGeneratorOrganizeImportsAccessor.cs +++ b/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/UnitTestGeneratorOrganizeImportsAccessor.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/WrappedAddImportFixData.cs b/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/WrappedAddImportFixData.cs index 327fc9d38048a..209c4bc86d2f2 100644 --- a/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/WrappedAddImportFixData.cs +++ b/src/EditorFeatures/Core/ExternalAccess/UnitTestGenerator/Api/WrappedAddImportFixData.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTestGenerator.Api; diff --git a/src/EditorFeatures/Core/ExternalAccess/UnitTesting/UnitTestingReferencesService.cs b/src/EditorFeatures/Core/ExternalAccess/UnitTesting/UnitTestingReferencesService.cs index 48a5af7b6ef01..90cf99a24d3fd 100644 --- a/src/EditorFeatures/Core/ExternalAccess/UnitTesting/UnitTestingReferencesService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/UnitTesting/UnitTestingReferencesService.cs @@ -2,11 +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. -#nullable disable - using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeLens; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptEditorInlineRenameService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptEditorInlineRenameService.cs index 4d5c099549a6f..38bdbecd6ffce 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptEditorInlineRenameService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptEditorInlineRenameService.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; using System.Threading.Tasks; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptInlineRenameReplacementInfo.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptInlineRenameReplacementInfo.cs index 8bae295f55bed..f3aecfe5ec102 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptInlineRenameReplacementInfo.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/IVSTypeScriptInlineRenameReplacementInfo.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.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousTaggerProvider.cs index 103a05ea0bc92..c1ae35460bc68 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousTaggerProvider.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Workspaces; using Microsoft.VisualStudio.Text.Tagging; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; @@ -14,31 +12,14 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; internal abstract class VSTypeScriptAsynchronousTaggerProvider : AsynchronousViewTaggerProvider where TTag : ITag { - [Obsolete("Use constructor that takes ITextBufferVisibilityTracker. Use `[Import(AllowDefault = true)] ITextBufferVisibilityTracker? visibilityTracker`")] - protected VSTypeScriptAsynchronousTaggerProvider( - IThreadingContext threadingContext, - IAsynchronousOperationListenerProvider asyncListenerProvider, - VSTypeScriptGlobalOptions globalOptions) - : this( - threadingContext, - globalOptions, - visibilityTracker: null, - asyncListenerProvider) - { - } - - [Obsolete("Use constructor that takes a single TaggerHost")] - protected VSTypeScriptAsynchronousTaggerProvider( - IThreadingContext threadingContext, - VSTypeScriptGlobalOptions globalOptions, - ITextBufferVisibilityTracker? visibilityTracker, - IAsynchronousOperationListenerProvider asyncListenerProvider) - : base(new TaggerHost(threadingContext, globalOptions.Service, visibilityTracker, asyncListenerProvider), FeatureAttribute.Classification) + [Obsolete("Use constructor that takes VSTypeScriptTaggerHost")] + protected VSTypeScriptAsynchronousTaggerProvider(TaggerHost taggerHost) + : base(taggerHost, FeatureAttribute.Classification) { } - protected VSTypeScriptAsynchronousTaggerProvider(TaggerHost taggerHost) - : base(taggerHost, FeatureAttribute.Classification) + protected VSTypeScriptAsynchronousTaggerProvider(VSTypeScriptTaggerHost taggerHost) + : base(taggerHost.UnderlyingObject, FeatureAttribute.Classification) { } } diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentSpan.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentSpan.cs index bb66de14574dc..bce95d6da5fca 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentSpan.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptDocumentSpan.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 Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameInfo.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameInfo.cs index 5e859b093ee6f..ea263c1cd0251 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameInfo.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameInfo.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.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameLocationWrapper.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameLocationWrapper.cs index 73be95096df28..88855108f7938 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameLocationWrapper.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameLocationWrapper.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 Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameReplacementWrapper.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameReplacementWrapper.cs index 345fb9de25f20..2d2f88c4fd3ce 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameReplacementWrapper.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptInlineRenameReplacementWrapper.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 Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptTaggerHost.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptTaggerHost.cs new file mode 100644 index 0000000000000..a5d1e79337dbd --- /dev/null +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/Api/VSTypeScriptTaggerHost.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Editor.Tagging; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +[Export(typeof(VSTypeScriptTaggerHost)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class VSTypeScriptTaggerHost(TaggerHost underlyingObject) +{ + internal TaggerHost UnderlyingObject { get; } = underlyingObject; +} diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptEditorInlineRenameService.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptEditorInlineRenameService.cs index aa32797addd49..06748d0f67d8d 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptEditorInlineRenameService.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptEditorInlineRenameService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs index 59cf758bf0fc7..db9e62e01055b 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInProcLanguageClient.cs @@ -18,7 +18,6 @@ using Microsoft.VisualStudio.Utilities; using Newtonsoft.Json; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInlineRenameReplacementKindHelpers.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInlineRenameReplacementKindHelpers.cs index 41931120f1da6..93e18faa3c03e 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInlineRenameReplacementKindHelpers.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptInlineRenameReplacementKindHelpers.cs @@ -2,11 +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. -#nullable disable - using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; diff --git a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs index d4c90787529e1..498df4c794f6a 100644 --- a/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs +++ b/src/EditorFeatures/Core/ExternalAccess/VSTypeScript/VSTypeScriptRequestExecutionQueueProvider.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CommonLanguageServerProtocol.Framework; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; diff --git a/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs index 439de5926d51c..7b81483e9d54a 100644 --- a/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs +++ b/src/EditorFeatures/Core/FindReferences/FindReferencesCommandHandler.cs @@ -23,7 +23,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindReferences; diff --git a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs index 77b4e7623910c..89658c823fcaa 100644 --- a/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs +++ b/src/EditorFeatures/Core/Formatting/FormatCommandHandler.cs @@ -4,14 +4,12 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; @@ -26,7 +24,6 @@ using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs index 9635cf19695ee..427531e14a9f6 100644 --- a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs +++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionCommandHandler.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; @@ -31,15 +30,11 @@ namespace Microsoft.CodeAnalysis.GoToDefinition; [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal class GoToDefinitionCommandHandler( - IGlobalOptionService globalOptionService, IThreadingContext threadingContext, - IUIThreadOperationExecutor executor, IAsynchronousOperationListenerProvider listenerProvider) : ICommandHandler { - private readonly IGlobalOptionService _globalOptionService = globalOptionService; private readonly IThreadingContext _threadingContext = threadingContext; - private readonly IUIThreadOperationExecutor _executor = executor; private readonly IAsynchronousOperationListener _listener = listenerProvider.GetListener(FeatureAttribute.GoToDefinition); public string DisplayName => EditorFeaturesResources.Go_to_Definition; diff --git a/src/EditorFeatures/Core/Host/IPreviewDialogService.cs b/src/EditorFeatures/Core/Host/IPreviewDialogService.cs index 66e05ac12eae9..d8c999ecbacf1 100644 --- a/src/EditorFeatures/Core/Host/IPreviewDialogService.cs +++ b/src/EditorFeatures/Core/Host/IPreviewDialogService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Editor.Host; diff --git a/src/EditorFeatures/Core/Host/IPreviewPaneService.cs b/src/EditorFeatures/Core/Host/IPreviewPaneService.cs index 33ec7c74d8bb1..817006e99a8c8 100644 --- a/src/EditorFeatures/Core/Host/IPreviewPaneService.cs +++ b/src/EditorFeatures/Core/Host/IPreviewPaneService.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.Collections.Generic; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; diff --git a/src/EditorFeatures/Core/ICommandHandlerServiceFactory.cs b/src/EditorFeatures/Core/ICommandHandlerServiceFactory.cs index 6913532ac170f..c58fcb773b46b 100644 --- a/src/EditorFeatures/Core/ICommandHandlerServiceFactory.cs +++ b/src/EditorFeatures/Core/ICommandHandlerServiceFactory.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; namespace Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/IContentTypeLanguageService.cs b/src/EditorFeatures/Core/IContentTypeLanguageService.cs index c2b38ba6c8574..6b386953e392e 100644 --- a/src/EditorFeatures/Core/IContentTypeLanguageService.cs +++ b/src/EditorFeatures/Core/IContentTypeLanguageService.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 Microsoft.CodeAnalysis.Host; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/Core/IInlineRenameSession.cs b/src/EditorFeatures/Core/IInlineRenameSession.cs index f758c652b4f75..a7e52195f24dd 100644 --- a/src/EditorFeatures/Core/IInlineRenameSession.cs +++ b/src/EditorFeatures/Core/IInlineRenameSession.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Threading; using System.Threading.Tasks; using Microsoft.VisualStudio.Utilities; diff --git a/src/EditorFeatures/Core/IIntellisensePresenterSession.cs b/src/EditorFeatures/Core/IIntellisensePresenterSession.cs index 23470bb1f5756..37e532cb63c51 100644 --- a/src/EditorFeatures/Core/IIntellisensePresenterSession.cs +++ b/src/EditorFeatures/Core/IIntellisensePresenterSession.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 Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/IRefactorNotifyService.cs b/src/EditorFeatures/Core/IRefactorNotifyService.cs index 4c420434982ad..d14381d06bf20 100644 --- a/src/EditorFeatures/Core/IRefactorNotifyService.cs +++ b/src/EditorFeatures/Core/IRefactorNotifyService.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.Collections.Generic; namespace Microsoft.CodeAnalysis.Editor; diff --git a/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.InlineRenameLocationSet.cs b/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.InlineRenameLocationSet.cs index edf97e98f5c60..25078c2b74754 100644 --- a/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.InlineRenameLocationSet.cs +++ b/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.InlineRenameLocationSet.cs @@ -7,8 +7,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Rename; +using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; @@ -35,7 +35,7 @@ public InlineRenameLocationSet( private InlineRenameLocation ConvertLocation(RenameLocation location) { return new InlineRenameLocation( - _renameLocationSet.Solution.GetDocument(location.DocumentId), location.Location.SourceSpan); + _renameLocationSet.Solution.GetRequiredDocument(location.DocumentId), location.Location.SourceSpan); } public async Task GetReplacementsAsync( diff --git a/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.SymbolRenameInfo.cs b/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.SymbolRenameInfo.cs index 84ce9c84ad181..8808c07537a33 100644 --- a/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.SymbolRenameInfo.cs +++ b/src/EditorFeatures/Core/InlineRename/AbstractEditorInlineRenameService.SymbolRenameInfo.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs index 7420b423c5e84..c578b382308c2 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RefactoringWithCommandHandler.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.InlineRename; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.Text.Editor.Commanding; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; diff --git a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs index 0a8cd1fe5cb12..fbadb6588e48f 100644 --- a/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs +++ b/src/EditorFeatures/Core/InlineRename/CommandHandlers/AbstractRenameCommandHandler_RenameHandler.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.BackgroundWorkIndicator; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.InlineRename; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/EditorFeatures/Core/InlineRename/IEditorInlineRenameService.cs b/src/EditorFeatures/Core/InlineRename/IEditorInlineRenameService.cs index be4c7530fcfda..377b5f34722a4 100644 --- a/src/EditorFeatures/Core/InlineRename/IEditorInlineRenameService.cs +++ b/src/EditorFeatures/Core/InlineRename/IEditorInlineRenameService.cs @@ -2,19 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Rename.ConflictEngine; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor; @@ -164,7 +160,7 @@ internal interface IInlineRenameInfo /// Provides the reason that can be displayed to the user if the entity at the selected /// location cannot be renamed. /// - string LocalizedErrorMessage { get; } + string? LocalizedErrorMessage { get; } /// /// The span of the entity that is being renamed. diff --git a/src/EditorFeatures/Core/InlineRename/InlineRenameService.cs b/src/EditorFeatures/Core/InlineRename/InlineRenameService.cs index 7697a502b5e02..c34351edb50be 100644 --- a/src/EditorFeatures/Core/InlineRename/InlineRenameService.cs +++ b/src/EditorFeatures/Core/InlineRename/InlineRenameService.cs @@ -20,7 +20,6 @@ using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; diff --git a/src/EditorFeatures/Core/InlineRename/Taggers/RenameTagger.cs b/src/EditorFeatures/Core/InlineRename/Taggers/RenameTagger.cs index e7c4f32d22cde..c03e5b3c5c080 100644 --- a/src/EditorFeatures/Core/InlineRename/Taggers/RenameTagger.cs +++ b/src/EditorFeatures/Core/InlineRename/Taggers/RenameTagger.cs @@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis.Editor.Implementation.InlineRename.HighlightTags; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.InlineRename; diff --git a/src/EditorFeatures/Core/IntelliSense/AbstractController.cs b/src/EditorFeatures/Core/IntelliSense/AbstractController.cs index b4265f3b01c70..5d67f48531cde 100644 --- a/src/EditorFeatures/Core/IntelliSense/AbstractController.cs +++ b/src/EditorFeatures/Core/IntelliSense/AbstractController.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using System.Threading.Tasks; diff --git a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs index 7d4b4c67a47bc..cd0abaf590d4b 100644 --- a/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs +++ b/src/EditorFeatures/Core/IntelliSense/AsyncCompletion/CommitManager.cs @@ -278,11 +278,11 @@ private AsyncCompletionData.CommitResult Commit( updatedCurrentSnapshot = edit.Apply(); } - if (change.NewPosition.HasValue) + if (change.NewSelection.HasValue) { - // Roslyn knows how to position the caret in the snapshot we just created. - // If there were more edits made by extensions, TryMoveCaretToAndEnsureVisible maps the snapshot point to the most recent one. - view.TryMoveCaretToAndEnsureVisible(new SnapshotPoint(updatedCurrentSnapshot, change.NewPosition.Value)); + // Roslyn knows how to set the selection in the snapshot we just created. + // If there were more edits made by extensions, TrySetSelectionAndEnsureVisible maps the snapshot point to the most recent one. + view.TrySetSelectionAndEnsureVisible(new SnapshotSpan(updatedCurrentSnapshot, change.NewSelection.Value.ToSpan())); } else { diff --git a/src/EditorFeatures/Core/IntelliSense/ISession.cs b/src/EditorFeatures/Core/IntelliSense/ISession.cs index e1b777b77937c..fb224283e8f36 100644 --- a/src/EditorFeatures/Core/IntelliSense/ISession.cs +++ b/src/EditorFeatures/Core/IntelliSense/ISession.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; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense; diff --git a/src/EditorFeatures/Core/IntelliSense/QuickInfo/Model.cs b/src/EditorFeatures/Core/IntelliSense/QuickInfo/Model.cs index bfc1378a802b6..8829f6c01f392 100644 --- a/src/EditorFeatures/Core/IntelliSense/QuickInfo/Model.cs +++ b/src/EditorFeatures/Core/IntelliSense/QuickInfo/Model.cs @@ -2,13 +2,10 @@ // 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.QuickInfo; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.QuickInfo; diff --git a/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs b/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs index f7e8b4c1764ee..57019ea4d2bac 100644 --- a/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs +++ b/src/EditorFeatures/Core/IntelliSense/QuickInfo/QuickInfoSourceProvider.QuickInfoSource.cs @@ -22,7 +22,6 @@ using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; using IntellisenseQuickInfoItem = Microsoft.VisualStudio.Language.Intellisense.QuickInfoItem; using Microsoft.CodeAnalysis.Editor.InlineRename; diff --git a/src/EditorFeatures/Core/IntelliSense/Session.cs b/src/EditorFeatures/Core/IntelliSense/Session.cs index e3c7a1bdb52df..17a8bf6f8f876 100644 --- a/src/EditorFeatures/Core/IntelliSense/Session.cs +++ b/src/EditorFeatures/Core/IntelliSense/Session.cs @@ -7,7 +7,6 @@ using System; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense; diff --git a/src/EditorFeatures/Core/Intents/DeleteParameterIntentProvider.cs b/src/EditorFeatures/Core/Intents/DeleteParameterIntentProvider.cs index 403c3981ef0d8..1b685db55cba0 100644 --- a/src/EditorFeatures/Core/Intents/DeleteParameterIntentProvider.cs +++ b/src/EditorFeatures/Core/Intents/DeleteParameterIntentProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.ChangeSignature; using Microsoft.CodeAnalysis.Features.Intents; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/EditorFeatures/Core/Intents/RenameIntentProvider.cs b/src/EditorFeatures/Core/Intents/RenameIntentProvider.cs index 04c4895e01e1e..3e4a1972ad4d5 100644 --- a/src/EditorFeatures/Core/Intents/RenameIntentProvider.cs +++ b/src/EditorFeatures/Core/Intents/RenameIntentProvider.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditorFeatures.Intents; diff --git a/src/EditorFeatures/Core/Interactive/ISendToInteractiveSubmissionProvider.cs b/src/EditorFeatures/Core/Interactive/ISendToInteractiveSubmissionProvider.cs index 8a9dfa1cd9d42..f583c4ab6ef08 100644 --- a/src/EditorFeatures/Core/Interactive/ISendToInteractiveSubmissionProvider.cs +++ b/src/EditorFeatures/Core/Interactive/ISendToInteractiveSubmissionProvider.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 Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding; using System.Threading; diff --git a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs index 4a00dd65a66f7..0814b5074e3f7 100644 --- a/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs +++ b/src/EditorFeatures/Core/Interactive/InteractiveWorkspace.cs @@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Interactive; diff --git a/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs b/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs index 96c2edeeaaa96..ce6ad983f656f 100644 --- a/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.cs +++ b/src/EditorFeatures/Core/KeywordHighlighting/HighlighterViewTaggerProvider.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.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.Composition; @@ -16,6 +14,7 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.KeywordHighlighting; using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; @@ -23,7 +22,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.Highlighting; @@ -101,7 +99,7 @@ protected override async Task ProduceTagsAsync( using (Logger.LogBlock(FunctionId.Tagger_Highlighter_TagProducer_ProduceTags, cancellationToken)) using (s_listPool.GetPooledObject(out var highlights)) { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); _highlightingService.AddHighlights(root, position, highlights, cancellationToken); diff --git a/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs b/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs index fa1900e27a55d..e4f3ec32d1a19 100644 --- a/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.cs +++ b/src/EditorFeatures/Core/KeywordHighlighting/KeywordHighlightTag.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 Microsoft.CodeAnalysis.Editor.Shared.Tagging; namespace Microsoft.CodeAnalysis.Editor.Implementation.Highlighting; diff --git a/src/EditorFeatures/Core/ModernCommands/GoToImplementationCommandArgs.cs b/src/EditorFeatures/Core/ModernCommands/GoToImplementationCommandArgs.cs index cccb9c6789a30..29e9a99f6e308 100644 --- a/src/EditorFeatures/Core/ModernCommands/GoToImplementationCommandArgs.cs +++ b/src/EditorFeatures/Core/ModernCommands/GoToImplementationCommandArgs.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.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/ModernCommands/OrganizeDocumentCommandArgs.cs b/src/EditorFeatures/Core/ModernCommands/OrganizeDocumentCommandArgs.cs index 0e2ccf238ef3f..18f7783fb6694 100644 --- a/src/EditorFeatures/Core/ModernCommands/OrganizeDocumentCommandArgs.cs +++ b/src/EditorFeatures/Core/ModernCommands/OrganizeDocumentCommandArgs.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.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/ModernCommands/SortAndRemoveUnnecessaryImportsCommandArgs.cs b/src/EditorFeatures/Core/ModernCommands/SortAndRemoveUnnecessaryImportsCommandArgs.cs index f3faa1694223f..a989abc144edd 100644 --- a/src/EditorFeatures/Core/ModernCommands/SortAndRemoveUnnecessaryImportsCommandArgs.cs +++ b/src/EditorFeatures/Core/ModernCommands/SortAndRemoveUnnecessaryImportsCommandArgs.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.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/ModernCommands/SortImportsCommandArgs.cs b/src/EditorFeatures/Core/ModernCommands/SortImportsCommandArgs.cs index c7ea673010532..52b05787993b6 100644 --- a/src/EditorFeatures/Core/ModernCommands/SortImportsCommandArgs.cs +++ b/src/EditorFeatures/Core/ModernCommands/SortImportsCommandArgs.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.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; diff --git a/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToLinkService.cs b/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToLinkService.cs index 266e45bfdafa3..477ecd75ad962 100644 --- a/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToLinkService.cs +++ b/src/EditorFeatures/Core/NavigateTo/DefaultNavigateToLinkService.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.Composition; using System.Threading; diff --git a/src/EditorFeatures/Core/NavigateTo/INavigateToLinkService.cs b/src/EditorFeatures/Core/NavigateTo/INavigateToLinkService.cs index 1b6d388cdbbf3..51c0b6546ff1c 100644 --- a/src/EditorFeatures/Core/NavigateTo/INavigateToLinkService.cs +++ b/src/EditorFeatures/Core/NavigateTo/INavigateToLinkService.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.Threading; using System.Threading.Tasks; diff --git a/src/EditorFeatures/Core/Navigation/IDocumentNavigationServiceExtensions.cs b/src/EditorFeatures/Core/Navigation/IDocumentNavigationServiceExtensions.cs index fbaa8a20e8f98..d2bc7ec0c15da 100644 --- a/src/EditorFeatures/Core/Navigation/IDocumentNavigationServiceExtensions.cs +++ b/src/EditorFeatures/Core/Navigation/IDocumentNavigationServiceExtensions.cs @@ -75,7 +75,7 @@ public static async Task TryNavigateToLineAndOffsetAsync( // Navigation should not change the context of linked files and Shared Projects. documentId = workspace.GetDocumentIdInCurrentContext(documentId); - var document = workspace.CurrentSolution.GetDocument(documentId); + var document = workspace.CurrentSolution.GetTextDocument(documentId); if (document is null) return false; diff --git a/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs b/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs index dfb89e2b7740c..4a8cf20cedada 100644 --- a/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs +++ b/src/EditorFeatures/Core/NavigationBar/NavigationBarController.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Data.Common; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs index c7e05e1cbb2a6..cb23f2040e390 100644 --- a/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs +++ b/src/EditorFeatures/Core/Organizing/OrganizeDocumentCommandHandler.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Organizing; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; diff --git a/src/EditorFeatures/Core/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs b/src/EditorFeatures/Core/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs index 74ff0b2bebccb..af09bce87c616 100644 --- a/src/EditorFeatures/Core/RenameTracking/IRenameTrackingLanguageHeuristicsService.cs +++ b/src/EditorFeatures/Core/RenameTracking/IRenameTrackingLanguageHeuristicsService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingCancellationCommandHandler.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingCancellationCommandHandler.cs index 50d4ea654dfc0..992b44cec2288 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingCancellationCommandHandler.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingCancellationCommandHandler.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.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTag.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTag.cs index c2e3f57b04d60..0da73767d419e 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTag.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTag.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 Microsoft.VisualStudio.Text.Tagging; namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingSolutionSet.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingSolutionSet.cs index 02d248980c121..50423344157aa 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingSolutionSet.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.RenameTrackingSolutionSet.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 - namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking; internal sealed partial class RenameTrackingTaggerProvider diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.Tagger.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.Tagger.cs index deaef04b1024e..d1206c6c1bf71 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.Tagger.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.Tagger.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.Generic; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs index de3b3ce56c86e..8e4deef024bea 100644 --- a/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs +++ b/src/EditorFeatures/Core/RenameTracking/RenameTrackingTaggerProvider.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; @@ -16,14 +15,12 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SQLite.Interop; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.Text.Tagging; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking; diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextSelectionExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextSelectionExtensions.cs index 9e73dc6aaea40..09425c84ddfc6 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextSelectionExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextSelectionExtensions.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.AutoClosingViewProperty.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.AutoClosingViewProperty.cs index 282aa1add2ab0..daa93c2bdd88f 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.AutoClosingViewProperty.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.AutoClosingViewProperty.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; diff --git a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs index 8c5027115121d..b328be35f519c 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/ITextViewExtensions.cs @@ -108,6 +108,15 @@ public static void SetMultiSelection(this ITextView textView, IEnumerable textView.TryMoveCaretToAndEnsureVisible(new VirtualSnapshotPoint(point), outliningManagerService, ensureSpanVisibleOptions); diff --git a/src/EditorFeatures/Core/Shared/Extensions/IThreadingContextExtensions.cs b/src/EditorFeatures/Core/Shared/Extensions/IThreadingContextExtensions.cs index ece4af98e0f52..8db832bba0c30 100644 --- a/src/EditorFeatures/Core/Shared/Extensions/IThreadingContextExtensions.cs +++ b/src/EditorFeatures/Core/Shared/Extensions/IThreadingContextExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Extensions; diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.CaretPositionChangedEventSource.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.CaretPositionChangedEventSource.cs index 52e1fb9ccf7ef..443d2b34f9c6c 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.CaretPositionChangedEventSource.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.CaretPositionChangedEventSource.cs @@ -4,7 +4,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging; diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.ReadOnlyRegionsChangedEventSource.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.ReadOnlyRegionsChangedEventSource.cs index a88b8892fb254..960cec990820d 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.ReadOnlyRegionsChangedEventSource.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.ReadOnlyRegionsChangedEventSource.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging; diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.TextChangedEventSource.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.TextChangedEventSource.cs index 71feb49801ae2..8395901632e36 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.TextChangedEventSource.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.TextChangedEventSource.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Tagging; diff --git a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.cs b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.cs index 5b70580fabf45..6dc2d5eaa66c0 100644 --- a/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.cs +++ b/src/EditorFeatures/Core/Shared/Tagging/EventSources/TaggerEventSources.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Options; diff --git a/src/EditorFeatures/Core/Shared/Utilities/ClassificationTypeMap.cs b/src/EditorFeatures/Core/Shared/Utilities/ClassificationTypeMap.cs index 62c3eea7c82f0..fdc2524f47df4 100644 --- a/src/EditorFeatures/Core/Shared/Utilities/ClassificationTypeMap.cs +++ b/src/EditorFeatures/Core/Shared/Utilities/ClassificationTypeMap.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Text.Classification; -using Roslyn.Utilities; using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer; namespace Microsoft.CodeAnalysis.Editor.Shared.Utilities; diff --git a/src/EditorFeatures/Core/Shared/Utilities/ResettableDelay.cs b/src/EditorFeatures/Core/Shared/Utilities/ResettableDelay.cs index b7cf6c32c4d03..1d0574a9436b8 100644 --- a/src/EditorFeatures/Core/Shared/Utilities/ResettableDelay.cs +++ b/src/EditorFeatures/Core/Shared/Utilities/ResettableDelay.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Shared.Utilities; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs index 4e4abff3bcf06..e102d431b7ea3 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_IEqualityComparer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Text.Tagging; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ReferenceCounting.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ReferenceCounting.cs index 9a3503d556999..9c2095dd746ec 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ReferenceCounting.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ReferenceCounting.cs @@ -5,7 +5,6 @@ using System; using System.Diagnostics; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs index d0a37a1634e12..6a09fbf6164d9 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.cs @@ -24,7 +24,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs index 4b2b118f431de..8d26290225dda 100644 --- a/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.cs +++ b/src/EditorFeatures/Core/Tagging/AsynchronousViewportTaggerProvider.SingleViewportTaggerProvider.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 System.Linq; using System.Threading; diff --git a/src/EditorFeatures/Core/Tagging/ITaggerEventSource.cs b/src/EditorFeatures/Core/Tagging/ITaggerEventSource.cs index 43cc5947723c3..1a8e1f613febd 100644 --- a/src/EditorFeatures/Core/Tagging/ITaggerEventSource.cs +++ b/src/EditorFeatures/Core/Tagging/ITaggerEventSource.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; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tagging/TaggerCaretChangeBehavior.cs b/src/EditorFeatures/Core/Tagging/TaggerCaretChangeBehavior.cs index b419e476adae7..dea71e642e720 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerCaretChangeBehavior.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerCaretChangeBehavior.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; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tagging/TaggerDelay.cs b/src/EditorFeatures/Core/Tagging/TaggerDelay.cs index d6a1e2757f658..fd44366bfdc31 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerDelay.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerDelay.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 - namespace Microsoft.CodeAnalysis.Editor.Tagging; /// diff --git a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs index c7606e884ed3c..b850082da78a1 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerMainThreadManager.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; using TaggerUIData = (bool isVisible, Microsoft.VisualStudio.Text.SnapshotPoint? caretPosition, Roslyn.Utilities.OneOrMany spansToTag); diff --git a/src/EditorFeatures/Core/Tagging/TaggerTextChangeBehavior.cs b/src/EditorFeatures/Core/Tagging/TaggerTextChangeBehavior.cs index 008425992693f..95871b9271c50 100644 --- a/src/EditorFeatures/Core/Tagging/TaggerTextChangeBehavior.cs +++ b/src/EditorFeatures/Core/Tagging/TaggerTextChangeBehavior.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; namespace Microsoft.CodeAnalysis.Editor.Tagging; diff --git a/src/EditorFeatures/Core/Tags/IImageIdService.cs b/src/EditorFeatures/Core/Tags/IImageIdService.cs index a584807d5a355..260e384116869 100644 --- a/src/EditorFeatures/Core/Tags/IImageIdService.cs +++ b/src/EditorFeatures/Core/Tags/IImageIdService.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.Collections.Immutable; using Microsoft.VisualStudio.Core.Imaging; diff --git a/src/EditorFeatures/Core/TextViewRoles.cs b/src/EditorFeatures/Core/TextViewRoles.cs index 8838f360c21ae..5b06a6d5e33a9 100644 --- a/src/EditorFeatures/Core/TextViewRoles.cs +++ b/src/EditorFeatures/Core/TextViewRoles.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 - namespace Microsoft.CodeAnalysis.Editor; internal static class TextViewRoles diff --git a/src/EditorFeatures/Core/TypeForwarders.cs b/src/EditorFeatures/Core/TypeForwarders.cs index fbfdc58a439fd..c80f7e43f06e1 100644 --- a/src/EditorFeatures/Core/TypeForwarders.cs +++ b/src/EditorFeatures/Core/TypeForwarders.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.Runtime.CompilerServices; // Microsoft.CodeAnalysis.Editor.ContentTypeNames has been moved to Microsoft.CodeAnalysis.Editor.Text.dll diff --git a/src/EditorFeatures/Core/Undo/IGlobalUndoService.cs b/src/EditorFeatures/Core/Undo/IGlobalUndoService.cs index d9ac0af07f873..d618221c10a79 100644 --- a/src/EditorFeatures/Core/Undo/IGlobalUndoService.cs +++ b/src/EditorFeatures/Core/Undo/IGlobalUndoService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Editor.Undo; diff --git a/src/EditorFeatures/Core/Undo/ISourceTextUndoService.cs b/src/EditorFeatures/Core/Undo/ISourceTextUndoService.cs index 9a399e8fdfb6d..bcddc17c81aa9 100644 --- a/src/EditorFeatures/Core/Undo/ISourceTextUndoService.cs +++ b/src/EditorFeatures/Core/Undo/ISourceTextUndoService.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 Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Core/Undo/ISourceTextUndoTransaction.cs b/src/EditorFeatures/Core/Undo/ISourceTextUndoTransaction.cs index 2a853e5b4d3c5..4a716ff219ca1 100644 --- a/src/EditorFeatures/Core/Undo/ISourceTextUndoTransaction.cs +++ b/src/EditorFeatures/Core/Undo/ISourceTextUndoTransaction.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 Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Core/Undo/IWorkspaceGlobalUndoTransaction.cs b/src/EditorFeatures/Core/Undo/IWorkspaceGlobalUndoTransaction.cs index 930ebc1283781..e501ca2d50e92 100644 --- a/src/EditorFeatures/Core/Undo/IWorkspaceGlobalUndoTransaction.cs +++ b/src/EditorFeatures/Core/Undo/IWorkspaceGlobalUndoTransaction.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; namespace Microsoft.CodeAnalysis.Editor.Undo; diff --git a/src/EditorFeatures/Core/Undo/NoOpGlobalUndoServiceFactory.cs b/src/EditorFeatures/Core/Undo/NoOpGlobalUndoServiceFactory.cs index 42007f1d6886c..a28a06c79f32d 100644 --- a/src/EditorFeatures/Core/Undo/NoOpGlobalUndoServiceFactory.cs +++ b/src/EditorFeatures/Core/Undo/NoOpGlobalUndoServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/EditorFeatures/Core/Workspaces/TextUndoHistoryWorkspaceServiceFactoryService.cs b/src/EditorFeatures/Core/Workspaces/TextUndoHistoryWorkspaceServiceFactoryService.cs index 9a2487541b1d1..9fa6242f5f521 100644 --- a/src/EditorFeatures/Core/Workspaces/TextUndoHistoryWorkspaceServiceFactoryService.cs +++ b/src/EditorFeatures/Core/Workspaces/TextUndoHistoryWorkspaceServiceFactoryService.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs index d80d8fdd84195..c9890767674d3 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/AbstractChangeSignatureTests.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities.ChangeSignature; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Xunit; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs index 536fad3c1140f..622c97d33cea8 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/ChangeSignature/ChangeSignatureTestState.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.ChangeSignature; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.VisualBasic; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs index cd5193deb76f5..f8d2996d8dcb6 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/Diagnostics/AbstractUserDiagnosticTest_GenerateTypeDialog.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.GenerateType; using Microsoft.CodeAnalysis.GenerateType; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Utilities; using Xunit; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/MoveToNamespace/AbstractMoveToNamespaceTests.cs b/src/EditorFeatures/DiagnosticsTestUtilities/MoveToNamespace/AbstractMoveToNamespaceTests.cs index e221ea06844ec..d835b86f22b55 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/MoveToNamespace/AbstractMoveToNamespaceTests.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/MoveToNamespace/AbstractMoveToNamespaceTests.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; diff --git a/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs b/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs index 7d8739acaece3..4ef6536f45d98 100644 --- a/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs +++ b/src/EditorFeatures/DiagnosticsTestUtilities/MoveType/AbstractMoveTypeTest.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs index b788abaaf22a3..247b8c5923e3b 100644 --- a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs +++ b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.CSharp.cs @@ -1334,6 +1334,50 @@ await Assert.ThrowsAsync(async () => await TestAddAttributeAsync(input, expected, typeof(SerializableAttribute), RefKeyword)); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] + public async Task AddAttributeWithArrayParams() + { + var input = """ + using System; + class ExampleAttribute : Attribute + { + public ExampleAttribute(int[] items) { } + } + class ExampleType + { + [Example(new[] { 1, 2, 3 })] + public void {|method:M|}() { } + } + class C + { + public void [|M|]2() { } + } + """; + var expected = """ + using System; + class ExampleAttribute : Attribute + { + public ExampleAttribute(int[] items) { } + } + class ExampleType + { + [Example(new[] { 1, 2, 3 })] + public void M() { } + } + class C + { + [Example(new[] { 1, 2, 3 })] + public void M2() { } + } + """; + await TestAddAttributeAsync(input, expected, (context) => + { + var method = context.GetAnnotatedDeclaredSymbols("method", context.SemanticModel).Single(); + var attribute = method.GetAttributes().Single(); + return attribute; + }); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeGeneration)] public async Task RemoveAttributeWithTrivia() { diff --git a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.cs b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.cs index 9ef1f3e876c23..0fbbfb405b851 100644 --- a/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.cs +++ b/src/EditorFeatures/Test/CodeGeneration/CodeGenerationTests.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; @@ -526,19 +525,34 @@ internal static async Task TestAddNamedTypeAsync( CancellationToken.None); } - internal static async Task TestAddAttributeAsync( + internal static Task TestAddAttributeAsync( string initial, string expected, Type attributeClass, SyntaxToken? target = null) + { + return TestAddAttributeAsync(initial, expected, (testContext) => + { + var attr = CodeGenerationSymbolFactory.CreateAttributeData(GetTypeSymbol(attributeClass)(testContext.SemanticModel)); + return attr; + }, target); + } + + internal static async Task TestAddAttributeAsync( + string initial, + string expected, + Func attributeToGenerate, + SyntaxToken? target = null) { using var testContext = await TestContext.CreateAsync(initial, expected); - var attr = CodeGenerationSymbolFactory.CreateAttributeData(GetTypeSymbol(attributeClass)(testContext.SemanticModel)); + + var attributeData = attributeToGenerate(testContext); + var oldNode = testContext.GetDestinationNode(); var codeGenerator = testContext.Document.GetRequiredLanguageService(); var options = await testContext.Document.GetCodeGenerationOptionsAsync(CancellationToken.None); var info = codeGenerator.GetInfo(CodeGenerationContext.Default, options, oldNode.SyntaxTree.Options); - var newNode = codeGenerator.AddAttributes(oldNode, [attr], target, info, CancellationToken.None) + var newNode = codeGenerator.AddAttributes(oldNode, [attributeData], target, info, CancellationToken.None) .WithAdditionalAnnotations(Formatter.Annotation); testContext.Result = testContext.Document.WithSyntaxRoot(testContext.SemanticModel.SyntaxTree.GetRoot().ReplaceNode(oldNode, newNode)); } diff --git a/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs index d753286bb6498..54ebad543cd5d 100644 --- a/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs +++ b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs index db5836abe05f2..1cdbde9790790 100644 --- a/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/DiagnosticAnalyzerServiceTests.cs @@ -12,14 +12,12 @@ using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.CSharp; -using Microsoft.CodeAnalysis.Editor.Test; 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; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs index 36980fea3c6c2..b30dc02875c8f 100644 --- a/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/IDEDiagnosticIDConfigurationTests.cs @@ -38,7 +38,8 @@ public class IDEDiagnosticIDConfigurationTests ValidateHelpLinkForDiagnostic(diagnosticId, descriptor.HelpLinkUri); if (diagnosticId.StartsWith("ENC") || - !char.IsDigit(diagnosticId[^1])) + !char.IsDigit(diagnosticId[^1]) || + diagnosticId == IDEDiagnosticIds.CopilotImplementNotImplementedExceptionDiagnosticId) { // Ignore non-IDE diagnostic IDs (such as ENCxxxx diagnostics) and // diagnostic IDs for suggestions, fading, etc. (such as IDExxxxWithSuggestion) diff --git a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs index 55e0553125946..cc4f1a61e97ed 100644 --- a/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs +++ b/src/EditorFeatures/Test/Diagnostics/SuppressMessageAttributeWorkspaceTests.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Linq; using System.Runtime.ExceptionServices; using System.Threading; diff --git a/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs index ddc84b2b89489..4dc75d28322d7 100644 --- a/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs +++ b/src/EditorFeatures/Test/LanguageServer/VSTypeScriptHandlerTests.cs @@ -7,7 +7,6 @@ using System.Composition; using System.IO; using System.Linq; -using System.ServiceModel.Syndication; using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; @@ -18,7 +17,6 @@ using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CommonLanguageServerProtocol.Framework; -using Nerdbank.Streams; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using StreamJsonRpc; diff --git a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs index 8e00e418bd095..f256667745074 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/AbstractMetadataAsSourceTests.TestContext.cs @@ -4,14 +4,12 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Security; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.DecompiledSource; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs index 4cba03fd1ce5d..398e549ea00c1 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.CSharp.cs @@ -2,9 +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.Threading.Tasks; -using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; diff --git a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs index e0c0bd654e729..eb25773dd2beb 100644 --- a/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs +++ b/src/EditorFeatures/Test/MetadataAsSource/MetadataAsSourceTests.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; diff --git a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs index 7f088bd377916..19b887caa37c0 100644 --- a/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs +++ b/src/EditorFeatures/Test/RenameTracking/RenameTrackingTaggerProviderTests.cs @@ -6,7 +6,6 @@ using System; using System.Linq; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/Test/StackTraceExplorer/StackTraceExplorerTests.cs b/src/EditorFeatures/Test/StackTraceExplorer/StackTraceExplorerTests.cs index cea9e8f436176..f498aabe36b78 100644 --- a/src/EditorFeatures/Test/StackTraceExplorer/StackTraceExplorerTests.cs +++ b/src/EditorFeatures/Test/StackTraceExplorer/StackTraceExplorerTests.cs @@ -32,8 +32,7 @@ private static async Task TestSymbolFoundAsync(string inputLine, string code) var reparsedResult = await StackTraceAnalyzer.AnalyzeAsync(stackFrame.ToString(), CancellationToken.None); Assert.Single(reparsedResult.ParsedFrames); - var reparsedFrame = reparsedResult.ParsedFrames[0] as ParsedStackFrame; - AssertEx.NotNull(reparsedFrame); + var reparsedFrame = Assert.IsType(reparsedResult.ParsedFrames[0]); StackFrameUtils.AssertEqual(stackFrame.Root, reparsedFrame.Root); // Get the definition for the parsed frame @@ -820,9 +819,9 @@ class C var result = await StackTraceAnalyzer.AnalyzeAsync(line, CancellationToken.None); Assert.Equal(1, result.ParsedFrames.Length); - var parsedFame = result.ParsedFrames.OfType().Single(); + var parsedFrame = Assert.IsType(result.ParsedFrames[0]); var service = workspace.Services.GetRequiredService(); - var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, parsedFame, StackFrameSymbolPart.Method, CancellationToken.None); + var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, parsedFrame, StackFrameSymbolPart.Method, CancellationToken.None); Assert.Null(definition); } @@ -850,13 +849,89 @@ public async Task TestMetadataSymbol() var result = await StackTraceAnalyzer.AnalyzeAsync("at System.String.ToLower()", CancellationToken.None); Assert.Single(result.ParsedFrames); - var frame = result.ParsedFrames[0] as ParsedStackFrame; - AssertEx.NotNull(frame); - + var frame = Assert.IsType(result.ParsedFrames[0]); var service = workspace.Services.GetRequiredService(); var definition = await service.TryFindDefinitionAsync(workspace.CurrentSolution, frame, StackFrameSymbolPart.Method, CancellationToken.None); AssertEx.NotNull(definition); Assert.Equal("String.ToLower", definition.NameDisplayParts.ToVisibleDisplayString(includeLeftToRightMarker: false)); } + + [Fact] + public async Task TestAdditionalFileExactMatchAsync() + { + using var workspace = TestWorkspace.Create( + """ + + + + class C + { + void M() {} + } + + + @page "/" + + @code + { + void M() + { + } + } + + + + """); + + var result = await StackTraceAnalyzer.AnalyzeAsync("at Path.To.Component.M() in C:/path/to/Component.razor:line 5", CancellationToken.None); + Assert.Single(result.ParsedFrames); + + var frame = Assert.IsType(result.ParsedFrames[0]); + var service = workspace.Services.GetRequiredService(); + var (document, line) = service.GetDocumentAndLine(workspace.CurrentSolution, frame); + Assert.Equal(5, line); + + AssertEx.NotNull(document); + Assert.Equal(@"C:/path/to/Component.razor", document.FilePath); + } + + [Fact] + public async Task TestAdditionalFileNameMatchAsync() + { + using var workspace = TestWorkspace.Create( + """ + + + + class C + { + void M() {} + } + + + @page "/" + + @code + { + void M() + { + } + } + + + + """); + + var result = await StackTraceAnalyzer.AnalyzeAsync("at Path.To.Component.M() in Component.razor:line 5", CancellationToken.None); + Assert.Single(result.ParsedFrames); + + var frame = Assert.IsType(result.ParsedFrames[0]); + var service = workspace.Services.GetRequiredService(); + var (document, line) = service.GetDocumentAndLine(workspace.CurrentSolution, frame); + Assert.Equal(5, line); + + AssertEx.NotNull(document); + Assert.Equal(@"C:/path/to/Component.razor", document.FilePath); + } } diff --git a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs index 3d5e668a3686d..b5c377ac93e40 100644 --- a/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs +++ b/src/EditorFeatures/Test/Structure/StructureTaggerTests.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Implementation.Structure; -using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Structure; diff --git a/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs b/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs index 2494beeb52c7b..346b781d717a5 100644 --- a/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs +++ b/src/EditorFeatures/Test/Utilities/SymbolEquivalenceComparerTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb index cc2638bc70786..5176fb2119f8f 100644 --- a/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb +++ b/src/EditorFeatures/Test2/CodeFixes/CodeFixServiceTests.vb @@ -16,14 +16,15 @@ Imports Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.ErrorLogger +Imports Microsoft.CodeAnalysis.FindSymbols Imports Microsoft.CodeAnalysis.Host Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.QuickInfo Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.UnitTests Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests - <[UseExportProvider]> Public Class CodeFixServiceTests @@ -321,6 +322,10 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Public Function IsGenerateDocumentationCommentOptionEnabledAsync() As Task(Of Boolean) Implements ICopilotOptionsService.IsGenerateDocumentationCommentOptionEnabledAsync Return Task.FromResult(True) End Function + + Public Function IsImplementNotImplementedExceptionEnabledAsync() As Task(Of Boolean) Implements ICopilotOptionsService.IsImplementNotImplementedExceptionEnabledAsync + Return Task.FromResult(True) + End Function End Class @@ -354,10 +359,6 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Return Task.CompletedTask End Function - Public Function GetOnTheFlyDocsAsync(symbolSignature As String, declarationCode As ImmutableArray(Of String), language As String, cancellationToken As CancellationToken) As Task(Of (responseString As String, isQuotaExceeded As Boolean)) Implements ICopilotCodeAnalysisService.GetOnTheFlyDocsAsync - Return Task.FromResult(("", False)) - End Function - Public Function IsFileExcludedAsync(filePath As String, cancellationToken As CancellationToken) As Task(Of Boolean) Implements ICopilotCodeAnalysisService.IsFileExcludedAsync Return Task.FromResult(False) End Function @@ -365,6 +366,22 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeFixes.UnitTests Public Function GetDocumentationCommentAsync(proposal As DocumentationCommentProposal, cancellationToken As CancellationToken) As Task(Of (responseDictionary As Dictionary(Of String, String), isQuotaExceeded As Boolean)) Implements ICopilotCodeAnalysisService.GetDocumentationCommentAsync Return Task.FromResult((New Dictionary(Of String, String), False)) End Function + + Public Function GetOnTheFlyDocsPromptAsync(onTheFlyDocsInfo As OnTheFlyDocsInfo, cancellationToken As CancellationToken) As Task(Of String) Implements ICopilotCodeAnalysisService.GetOnTheFlyDocsPromptAsync + Return Task.FromResult(String.Empty) + End Function + + Public Function GetOnTheFlyDocsResponseAsync(prompt As String, cancellationToken As CancellationToken) As Task(Of (responseString As String, isQuotaExceeded As Boolean)) Implements ICopilotCodeAnalysisService.GetOnTheFlyDocsResponseAsync + Return Task.FromResult((String.Empty, False)) + End Function + + Public Function IsImplementNotImplementedExceptionsAvailableAsync(cancellationToken As CancellationToken) As Task(Of Boolean) Implements ICopilotCodeAnalysisService.IsImplementNotImplementedExceptionsAvailableAsync + Return Task.FromResult(False) + End Function + + Public Function ImplementNotImplementedExceptionsAsync(document As Document, methodOrProperties As ImmutableDictionary(Of SyntaxNode, ImmutableArray(Of ReferencedSymbol)), cancellationToken As CancellationToken) As Task(Of ImmutableDictionary(Of SyntaxNode, ImplementationDetails)) Implements ICopilotCodeAnalysisService.ImplementNotImplementedExceptionsAsync + Return Task.FromResult(ImmutableDictionary(Of SyntaxNode, ImplementationDetails).Empty) + End Function End Class End Class End Namespace diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb index 21ea35471f180..c805f54928310 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ConstructorSymbols.vb @@ -1379,6 +1379,29 @@ class C } + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function PartialConstructor(kind As TestKind, host As TestHost) As Task + Dim input = + + + +using System; +partial class Program +{ + public partial {|Definition:Program|}(); + public partial {|Definition:P$$rogram|}() { } + + static void Main(string[] args) + { + var p = new [|Program|](); + } +} + + Await TestAPIAndFeature(input, kind, host) End Function diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb index a102c5854d806..ded1fcc943905 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.EventSymbols.vb @@ -462,5 +462,29 @@ class C3_2 : I3 Await TestAPI(input, host) End Function + + + Public Async Function PartialEvent(kind As TestKind, host As TestHost) As Task + Dim input = + + + +using System; +partial class Program +{ + public static partial event Action {|Definition:Event|}; + public static partial event Action {|Definition:E$$vent|} { add { } remove { } } + + static void Main(string[] args) + { + Program.[|Event|] += null; + Program.[|Event|] -= null; + } +} + + + + Await TestAPIAndFeature(input, kind, host) + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionMethodSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionMethodSymbols.vb index f6c4339682901..cdcd735dc53d8 100644 --- a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionMethodSymbols.vb +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionMethodSymbols.vb @@ -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. -Imports System.Threading.Tasks Imports Microsoft.CodeAnalysis.Remote.Testing Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences diff --git a/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb new file mode 100644 index 0000000000000..195ad461e2fec --- /dev/null +++ b/src/EditorFeatures/Test2/FindReferences/FindReferencesTests.ExtensionSymbols.vb @@ -0,0 +1,159 @@ +' 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. + +Imports Microsoft.CodeAnalysis.Remote.Testing + +Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences + + Partial Public Class FindReferencesTests + + Public Async Function TestModernExtensionMethod1(kind As TestKind, host As TestHost) As Task + Dim input = + + + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestModernExtensionMethod2(kind As TestKind, host As TestHost) As Task + Dim input = + + + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestModernExtensionProperty1(kind As TestKind, host As TestHost) As Task + Dim input = + + + 0; + } +} +]]> + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestModernExtensionMethodParameter1(kind As TestKind, host As TestHost) As Task + Dim input = + + + + + + + Await TestAPIAndFeature(input, kind, host) + End Function + + + Public Async Function TestModernExtensionMethodTypeParameter1(kind As TestKind, host As TestHost) As Task + Dim input = + + + (string s) + { + public int ExtensionMethod([|T|] t) + { + return s.Length; + } + } +} +]]> + + + + Await TestAPIAndFeature(input, kind, host) + End Function + End Class +End Namespace diff --git a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb index 68843dbf48673..3b3ae7b85a943 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/CSharpGoToDefinitionTests.vb @@ -314,6 +314,69 @@ class Program Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGotoDefinitionPartialEvent() As Task + Dim workspace = + + + + partial class Test + { + public partial event System.Action E; + } + + + partial class Test + { + void Goo() + { + var t = new Test(); + int i = t.E$$; + } + + public partial event System.Action [|E|] + { + add { } + remove { } + } + } + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGotoDefinitionPartialConstructor() As Task + Dim workspace = + + + + partial class Test + { + public partial Test(); + } + + + partial class Test + { + void Goo() + { + var t = new Te$$st(); + } + + public partial [|Test|]() + { + } + } + + + + + Await TestAsync(workspace) + End Function + Public Async Function TestCSharpGotoDefinitionOnMethodCall1() As Task Dim workspace = @@ -4072,8 +4135,6 @@ partial partial class Program Await TestAsync(workspace) End Function -#Disable Warning RSEXPERIMENTAL002 ' Type is for evaluation purposes only and is subject to change or removal in future updates. - Private Const s_interceptsLocationCode = " namespace System.Runtime.CompilerServices { @@ -4363,6 +4424,111 @@ public partial class Program Await TestAsync(workspace) End Function + + Public Async Function TestCSharpGotoDefinitionNullConditionalAwait() As Task + Dim workspace = + + + + partial class Test + { + string [|s|]; + + public void M(Test t) + { + t?.$$s = ""; + } + } + + + + + Await TestAsync(workspace) + End Function + #Enable Warning RSEXPERIMENTAL002 ' Type is for evaluation purposes only and is subject to change or removal in future updates. + + Public Async Function TestCSharpGoToExtensionMethod1() As Task + Dim workspace = + + + + static class Extensions + { + extension(string s) + { + public void [|Goo|]() { } + } + } + + class X + { + void M(string s) + { + s.$$Goo(); + } + } + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToExtensionMethod2() As Task + Dim workspace = + + + + static class Extensions + { + extension(string s) + { + public static void [|Goo|]() { } + } + } + + class X + { + void M(string s) + { + string.$$Goo(); + } + } + + + + + Await TestAsync(workspace) + End Function + + + Public Async Function TestCSharpGoToExtensionProperty() As Task + Dim workspace = + + + + static class Extensions + { + extension(string s) + { + public int [|Goo|] => 0; + } + } + + class X + { + void M(string s) + { + var v = s.$$Goo; + } + } + + + + + Await TestAsync(workspace) + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb index bdd248e842de0..cdeca6bf967c3 100644 --- a/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/GoToDefinition/GoToDefinitionCommandHandlerTests.vb @@ -56,9 +56,7 @@ class C Dim provider = workspace.GetService(Of IAsynchronousOperationListenerProvider)() Dim waiter = provider.GetWaiter(FeatureAttribute.GoToDefinition) Dim handler = New GoToDefinitionCommandHandler( - workspace.GetService(Of IGlobalOptionService), workspace.GetService(Of IThreadingContext), - workspace.GetService(Of IUIThreadOperationExecutor), provider) handler.ExecuteCommand(New GoToDefinitionCommandArgs(view, baseDocument.GetTextBuffer()), TestCommandExecutionContext.Create()) @@ -98,9 +96,7 @@ int y = x$$ Dim provider = workspace.GetService(Of IAsynchronousOperationListenerProvider)() Dim waiter = provider.GetWaiter(FeatureAttribute.GoToDefinition) Dim handler = New GoToDefinitionCommandHandler( - workspace.GetService(Of IGlobalOptionService), workspace.GetService(Of IThreadingContext), - workspace.GetService(Of IUIThreadOperationExecutor), provider) handler.ExecuteCommand(New GoToDefinitionCommandArgs(view, document.GetTextBuffer()), TestCommandExecutionContext.Create()) @@ -143,9 +139,7 @@ class C Dim provider = workspace.GetService(Of IAsynchronousOperationListenerProvider)() Dim waiter = provider.GetWaiter(FeatureAttribute.GoToDefinition) Dim handler = New GoToDefinitionCommandHandler( - workspace.GetService(Of IGlobalOptionService), workspace.GetService(Of IThreadingContext), - workspace.GetService(Of IUIThreadOperationExecutor), provider) Dim snapshot = document.GetTextBuffer().CurrentSnapshot diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 3c9256fba03e2..8b30178d6dccd 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -12,6 +12,7 @@ Imports Microsoft.CodeAnalysis.Completion.Providers Imports Microsoft.CodeAnalysis.CSharp Imports Microsoft.CodeAnalysis.CSharp.ExternalAccess.Pythia.Api Imports Microsoft.CodeAnalysis.CSharp.Formatting +Imports Microsoft.CodeAnalysis.CSharp.Shared.Extensions Imports Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion Imports Microsoft.CodeAnalysis.Editor.Shared.Options Imports Microsoft.CodeAnalysis.Editor.[Shared].Utilities @@ -5847,7 +5848,7 @@ class C Dim textBuffer = testDocument.GetTextBuffer() Dim snapshotBeforeCommit = textBuffer.CurrentSnapshot - provider.SetInfo(snapshotBeforeCommit.GetText(), testDocument.CursorPosition.Value) + provider.SetInfo(testDocument.CursorPosition.Value) ' First send a space to trigger out special completionImplementation provider. state.SendInvokeCompletionList() @@ -5915,7 +5916,7 @@ class C Dim textBuffer = testDocument.GetTextBuffer() Dim snapshotBeforeCommit = textBuffer.CurrentSnapshot - provider.SetInfo(snapshotBeforeCommit.GetText(), testDocument.CursorPosition.Value) + provider.SetInfo(testDocument.CursorPosition.Value) ' First send a space to trigger out special completionImplementation provider. state.SendInvokeCompletionList() @@ -8202,7 +8203,6 @@ namespace NS Private Class MultipleChangeCompletionProvider Inherits CompletionProvider - Private _text As String Private _caretPosition As Integer @@ -8210,8 +8210,7 @@ namespace NS Public Sub New() End Sub - Public Sub SetInfo(text As String, caretPosition As Integer) - _text = text + Public Sub SetInfo(caretPosition As Integer) _caretPosition = caretPosition End Sub @@ -12820,5 +12819,182 @@ internal class Program Await state.AssertCompletionItemsContain("C", displayTextSuffix:="") End Using End Function + + + Public Async Function NullConditionalAssignment1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + +public class Class1 +{ + private string s; + + public void M(Class1 c) + { + c?.s = new$$ + } +} + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendTypeChars(" "c) + Await state.AssertCompletionSession() + + Await state.AssertSelectedCompletionItem("string", isHardSelected:=True) + End Using + End Function + + + Public Async Function NullConditionalAssignment2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + +enum E +{ + Red, + Green, +} + +public class Class1 +{ + private E e; + + public void M(Class1 c) + { + c?.e =$$ + } +} + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendTypeChars(" "c) + Await state.AssertCompletionSession() + + Await state.AssertSelectedCompletionItem("E", isHardSelected:=True) + End Using + End Function + + + + Public Async Function TestExtensionParameter1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + static class C + { + extension($$) + { + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("string", displayTextSuffix:="") + End Using + End Function + + + + Public Async Function TestExtensionParameter2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + static class C + { + extension(Customer $$) + { + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("customer", displayTextSuffix:="") + End Using + End Function + + + + Public Async Function TestSpeculativeTInExtension(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + static class C + { + extension(Customer customer) + { + $$ + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("T", displayTextSuffix:="") + End Using + End Function + + + + Public Async Function TestReturnSymbolInExtension1(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + using System; + + static class C + { + extension(Customer customer) + { + $$ + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("String", displayTextSuffix:="") + End Using + End Function + + + + Public Async Function TestReturnSymbolInExtension2(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + using System; + + static class C + { + extension(Customer customer) + { + public $$ + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("String", displayTextSuffix:="") + End Using + End Function + + + Public Async Function TestStaticExtensionMethod(showCompletionInArgumentLists As Boolean) As Task + Using state = TestStateFactory.CreateCSharpTestState( + + using System; + + object.$$ + + static class C + { + extension(object o) + { + public static void EM() { } + } + } + , + showCompletionInArgumentLists:=showCompletionInArgumentLists, languageVersion:=LanguageVersionExtensions.CSharpNext) + + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContain("EM", displayTextSuffix:="") + End Using + End Function End Class End Namespace diff --git a/src/EditorFeatures/Test2/NavigationBar/CSharpNavigationBarTests.vb b/src/EditorFeatures/Test2/NavigationBar/CSharpNavigationBarTests.vb index effd06d0c34e7..5bac824ee61aa 100644 --- a/src/EditorFeatures/Test2/NavigationBar/CSharpNavigationBarTests.vb +++ b/src/EditorFeatures/Test2/NavigationBar/CSharpNavigationBarTests.vb @@ -367,5 +367,28 @@ class C Item("C", Glyph.ClassInternal), False, Item("explicit operator checked string(C x)", Glyph.Operator), False) End Function + + + Public Async Function TestModernExtensionMethod1(host As TestHost) As Task + Await AssertSelectedItemsAreAsync( + + + +static class C +{ + extension(string s) + { + public void Goo()$$ + { + } + } +} + + + , + host, + Item("C.extension(string)", Glyph.ClassPublic), False, + Item("Goo()", Glyph.MethodPublic), False) + End Function End Class End Namespace diff --git a/src/EditorFeatures/TestUtilities/ChangeSignature/TestChangeSignatureOptionsService.cs b/src/EditorFeatures/TestUtilities/ChangeSignature/TestChangeSignatureOptionsService.cs index 18d1b47e9c2d4..a90a4db89a0a5 100644 --- a/src/EditorFeatures/TestUtilities/ChangeSignature/TestChangeSignatureOptionsService.cs +++ b/src/EditorFeatures/TestUtilities/ChangeSignature/TestChangeSignatureOptionsService.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.ChangeSignature; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Test.Utilities.ChangeSignature; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.ChangeSignature; diff --git a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs index e5fe84c3f9b88..d114b959a19b9 100644 --- a/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs +++ b/src/EditorFeatures/TestUtilities/Classification/FormattedClassification.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Immutable; using System.Linq; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification { diff --git a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs index 55e0869431cca..b4808de89e235 100644 --- a/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Completion/AbstractCompletionProviderTests.cs @@ -15,6 +15,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.AsyncCompletion; +using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -636,7 +637,7 @@ private async Task VerifyCustomCommitWorkerAsync( { using var workspaceFixture = GetOrCreateWorkspaceFixture(); - MarkupTestFile.GetPosition(expectedCodeAfterCommit, out var actualExpectedCode, out int expectedCaretPosition); + MarkupTestFile.GetPositionAndSpan(expectedCodeAfterCommit, out var actualExpectedCode, out var expectedCaretPosition, out TextSpan? expectedSelectionSpan); var options = GetCompletionOptions(); @@ -661,10 +662,15 @@ private async Task VerifyCustomCommitWorkerAsync( var textBuffer = workspaceFixture.Target.CurrentDocument.GetTextBuffer(); var actualCodeAfterCommit = textBuffer.CurrentSnapshot.AsText().ToString(); + var selectionSpan = commit.NewSelection ?? textView.Selection.StreamSelectionSpan.SnapshotSpan.Span.ToTextSpan(); var caretPosition = commit.NewPosition ?? textView.Caret.Position.BufferPosition.Position; AssertEx.EqualOrDiff(actualExpectedCode, actualCodeAfterCommit); - Assert.Equal(expectedCaretPosition, caretPosition); + + if (expectedSelectionSpan != null) + Assert.Equal(expectedSelectionSpan, selectionSpan); + else if (expectedCaretPosition != null) + Assert.Equal(expectedCaretPosition, caretPosition); } private void VerifyCustomCommitWorker( diff --git a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs index f78dc19aca163..e8537f2d2b2a6 100644 --- a/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs +++ b/src/EditorFeatures/TestUtilities/Diagnostics/DiagnosticTaggerWrapper.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.InlineDiagnostics; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Editor.Tagging; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/AbstractExtractInterfaceTests.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/AbstractExtractInterfaceTests.cs index 6e8b6d2f410c5..c911544f93e60 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/AbstractExtractInterfaceTests.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/AbstractExtractInterfaceTests.cs @@ -26,7 +26,8 @@ public static async Task TestExtractInterfaceCommandCSharpAsync( string expectedNamespaceName = null, string expectedTypeParameterSuffix = null, string expectedUpdatedOriginalDocumentCode = null, - string expectedInterfaceCode = null) + string expectedInterfaceCode = null, + ParseOptions parseOptions = null) { await TestExtractInterfaceCommandAsync( markup, @@ -37,7 +38,8 @@ await TestExtractInterfaceCommandAsync( expectedNamespaceName, expectedTypeParameterSuffix, expectedUpdatedOriginalDocumentCode, - expectedInterfaceCode); + expectedInterfaceCode, + parseOptions: parseOptions); } public static async Task TestExtractInterfaceCodeActionCSharpAsync( @@ -94,9 +96,11 @@ private static async Task TestExtractInterfaceCommandAsync( string expectedTypeParameterSuffix = null, string expectedUpdatedOriginalDocumentCode = null, string expectedInterfaceCode = null, - CompilationOptions compilationOptions = null) + CompilationOptions compilationOptions = null, + ParseOptions parseOptions = null) { - using var testState = ExtractInterfaceTestState.Create(markup, languageName, compilationOptions, + using var testState = ExtractInterfaceTestState.Create( + markup, languageName, compilationOptions, parseOptions, options: new OptionsCollection(languageName) { { CodeStyleOptions2.AccessibilityModifiersRequired, AccessibilityModifiersRequired.Never, NotificationOption2.Silent } @@ -112,7 +116,7 @@ private static async Task TestExtractInterfaceCommandAsync( if (expectedMemberName != null) { - Assert.Equal(1, testState.TestExtractInterfaceOptionsService.AllExtractableMembers.Count()); + Assert.Equal(1, testState.TestExtractInterfaceOptionsService.AllExtractableMembers.Length); Assert.Equal(expectedMemberName, testState.TestExtractInterfaceOptionsService.AllExtractableMembers.Single().Name); } diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs index 3c3d768448bb6..b60c8e0dae112 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/ExtractInterfaceTestState.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; diff --git a/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs b/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs index 846c12ca6ba8b..d3b047d196645 100644 --- a/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs +++ b/src/EditorFeatures/TestUtilities/ExtractInterface/TestExtractInterfaceOptions.cs @@ -6,8 +6,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; -using System.Threading; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.Host.Mef; @@ -18,9 +18,9 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.ExtractInterface; [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] internal sealed class TestExtractInterfaceOptionsService() : IExtractInterfaceOptionsService { - public IEnumerable AllExtractableMembers { get; private set; } + public ImmutableArray AllExtractableMembers { get; private set; } public string DefaultInterfaceName { get; private set; } - public List ConflictingTypeNames { get; private set; } + public ImmutableArray ConflictingTypeNames { get; private set; } public string DefaultNamespace { get; private set; } public string GeneratedNameTypeParameterSuffix { get; set; } @@ -32,12 +32,11 @@ internal sealed class TestExtractInterfaceOptionsService() : IExtractInterfaceOp public ExtractInterfaceOptionsResult GetExtractInterfaceOptions( Document document, - List extractableMembers, + ImmutableArray extractableMembers, string defaultInterfaceName, - List conflictingTypeNames, + ImmutableArray conflictingTypeNames, string defaultNamespace, - string generatedNameTypeParameterSuffix, - CancellationToken cancellationToken) + string generatedNameTypeParameterSuffix) { this.AllExtractableMembers = extractableMembers; this.DefaultInterfaceName = defaultInterfaceName; diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs b/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs index ce6b83a2906dc..26171ca394dbe 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/AbstractNavigateToTests.cs @@ -27,7 +27,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; -using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.PatternMatching; using Microsoft.VisualStudio.Utilities; using Roslyn.Test.EditorUtilities.NavigateTo; diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs index 33a09ed7c6423..54e667b22a550 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.Callback.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; -using Roslyn.Utilities; namespace Roslyn.Test.EditorUtilities.NavigateTo { diff --git a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs index b7be4d6a4249c..9829cbe60bc3a 100644 --- a/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs +++ b/src/EditorFeatures/TestUtilities/NavigateTo/NavigateToTestAggregator.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.NavigateTo.Interfaces; using Moq; -using Roslyn.Utilities; namespace Roslyn.Test.EditorUtilities.NavigateTo { diff --git a/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs b/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs index 30083d8e8c448..760c486e4c9c0 100644 --- a/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/SignatureHelp/AbstractSignatureHelpProviderTests.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.SignatureHelp.Presentation; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs index b76d73bede467..1757d7874b973 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/SquiggleUtilities.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs index 67f6f4c7346da..557baa3b52d67 100644 --- a/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs +++ b/src/EditorFeatures/TestUtilities/Squiggles/TestDiagnosticTagProducer.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Squiggles; diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs index 239e5692b2e79..473d944da5ffb 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxNodeStructureProviderTests.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Structure; using Xunit; diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs index 53d6264cb552a..d553cb6d826c8 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxStructureProviderTests.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Structure; diff --git a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs index c678acd1b4253..b730b6036133c 100644 --- a/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs +++ b/src/EditorFeatures/TestUtilities/Structure/AbstractSyntaxTriviaStructureProviderTests.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Structure; namespace Microsoft.CodeAnalysis.Editor.UnitTests.Structure diff --git a/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs b/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs index bb234f12cb3f0..a425a709ea646 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/EditorTestWorkspace.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp.DecompiledSource; using Microsoft.CodeAnalysis.Diagnostics; @@ -17,7 +15,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; using Microsoft.VisualStudio.Composition; diff --git a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs index 06c9a5c27f9e0..4dfb2f8e10b36 100644 --- a/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs +++ b/src/EditorFeatures/Text/Extensions.SnapshotSourceText.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.CompilerServices; diff --git a/src/EditorFeatures/Text/ITextImageHelpers.cs b/src/EditorFeatures/Text/ITextImageHelpers.cs index 5f3f60e2a3216..7770777f92a9d 100644 --- a/src/EditorFeatures/Text/ITextImageHelpers.cs +++ b/src/EditorFeatures/Text/ITextImageHelpers.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.VisualStudio.Text; diff --git a/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs b/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs index 27f3a280aa6cd..e1ab19415e9d6 100644 --- a/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs +++ b/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotExtensions.cs @@ -5,7 +5,6 @@ using System; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Tagging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Text.Shared.Extensions { diff --git a/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotLineExtensions.cs b/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotLineExtensions.cs index 71421eb46de34..93dc129501c48 100644 --- a/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotLineExtensions.cs +++ b/src/EditorFeatures/Text/Shared/Extensions/ITextSnapshotLineExtensions.cs @@ -7,7 +7,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Text.Shared.Extensions { diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs index 11229c7f76d1f..04af7e975cd94 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/CompilationContext.cs @@ -837,7 +837,7 @@ private static Binder CreateBinderChain( { // We're re-getting the namespace, rather than using the one containing // the current frame method, because we want the merged namespace. - @namespace = @namespace.GetNestedNamespace(namespaceName); + @namespace = @namespace.GetNestedNamespace(namespaceName)!; RoslynDebug.AssertNotNull(@namespace); } else diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs index c0ae503882254..23643883a6bba 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/EENamedTypeSymbol.cs @@ -323,6 +323,9 @@ public sealed override bool IsRefLikeType get { return false; } } + internal override string ExtensionName + => throw ExceptionUtilities.Unreachable(); + public sealed override bool IsReadOnly { get { return false; } @@ -356,6 +359,8 @@ internal override bool IsInterface internal override bool HasPossibleWellKnownCloneMethod() => false; internal override bool IsInterpolatedStringHandlerType => false; + internal sealed override ParameterSymbol ExtensionParameter => null; + [Conditional("DEBUG")] internal static void VerifyTypeParameters(Symbol container, ImmutableArray typeParameters) { diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs index 14cfe904b623a..47f0ed0f0b488 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/SynthesizedContextMethodSymbol.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator /// expressions outside of a method - specifically, binding /// DebuggerDisplayAttribute expressions. /// - internal sealed class SynthesizedContextMethodSymbol : SynthesizedInstanceMethodSymbol + internal sealed class SynthesizedContextMethodSymbol : SynthesizedMethodSymbol { private readonly NamedTypeSymbol _container; diff --git a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs index e779f7003170d..7a7db60392937 100644 --- a/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs +++ b/src/Features/CSharp/Portable/AddImport/CSharpAddImportFeatureService.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 8171ec91bb9d0..5c501128a87f7 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -581,4 +581,10 @@ required property + + Convert '{0}' extension methods to extension + + + Convert all extension methods in '{0}' to extension + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs index 2e61efc068f70..b17b102ed3365 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs @@ -9,11 +9,9 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; @@ -188,30 +186,53 @@ public static SyntaxNode GetTargetCaretPositionForMethod(BaseMethodDeclarationSy } } - public static SyntaxNode GetTargetCaretNodeForInsertedMember(SyntaxNode caretTarget) + public static TextSpan GetTargetSelectionSpanForMethod(BaseMethodDeclarationSyntax methodDeclaration) + { + if (methodDeclaration.ExpressionBody is not null) + { + // select the expression span + return methodDeclaration.ExpressionBody.Expression.Span; + } + else if (methodDeclaration.Body is not null) + { + // select the last statement in the method + return methodDeclaration.Body.Statements.Last().Span; + } + else + { + return methodDeclaration.Span; + } + } + + public static TextSpan GetTargetSelectionSpanForInsertedMember(SyntaxNode caretTarget) { switch (caretTarget) { case EventFieldDeclarationSyntax: - // Inserted Event declarations are a single line, so move to the end of the line. - return caretTarget; + // Inserted Event declarations are a single line, so move caret to the end of the line. + return new TextSpan(caretTarget.Span.End, 0); case BaseMethodDeclarationSyntax methodDeclaration: - return GetTargetCaretPositionForMethod(methodDeclaration); + return GetTargetSelectionSpanForMethod(methodDeclaration); case BasePropertyDeclarationSyntax propertyDeclaration: { - // property: no accessors; move to the end of the declaration if (propertyDeclaration.AccessorList is { Accessors: [var firstAccessor, ..] }) { - // move to the end of the last statement of the first accessor + // select the last statement of the first accessor var firstAccessorStatement = (SyntaxNode)firstAccessor.Body?.Statements.LastOrDefault() ?? firstAccessor.ExpressionBody!.Expression; - return firstAccessorStatement; + return firstAccessorStatement.Span; + } + else if (propertyDeclaration is PropertyDeclarationSyntax propertyDeclarationSyntax && propertyDeclarationSyntax.ExpressionBody.Expression is ExpressionSyntax expression) + { + // expression-bodied property: select the expression + return expression.Span; } else { - return propertyDeclaration; + // property: no accessors; move caret to the end of the declaration + return new TextSpan(propertyDeclaration.Span.End, 0); } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.ItemGetter.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.ItemGetter.cs index 019a0e89063e7..f1d474d0dbae5 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.ItemGetter.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.ItemGetter.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.ComponentModel; using System.Linq; using System.Text; using System.Threading; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs index 9312dc1603a1a..36ebd6c640332 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; @@ -97,9 +96,9 @@ protected override SyntaxNode GetSyntax(SyntaxToken token) throw ExceptionUtilities.UnexpectedValue(token); } - protected override int GetTargetCaretPosition(SyntaxNode caretTarget) + protected override TextSpan GetTargetSelectionSpan(SyntaxNode caretTarget) { - return CompletionUtilities.GetTargetCaretNodeForInsertedMember(caretTarget).GetLocation().SourceSpan.End; + return CompletionUtilities.GetTargetSelectionSpanForInsertedMember(caretTarget); } public override async Task ProvideCompletionsAsync(CompletionContext context) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs index 7958a6948366e..500cbacf8d656 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs @@ -15,166 +15,160 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; -[ExportCompletionProvider(nameof(KeywordCompletionProvider), LanguageNames.CSharp)] +[ExportCompletionProvider(nameof(KeywordCompletionProvider), LanguageNames.CSharp), Shared] [ExtensionOrder(After = nameof(NamedParameterCompletionProvider))] -[Shared] -internal class KeywordCompletionProvider : AbstractKeywordCompletionProvider +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class KeywordCompletionProvider() : AbstractKeywordCompletionProvider([ + new AbstractKeywordRecommender(), + new AddKeywordRecommender(), + new AliasKeywordRecommender(), + new AllowsKeywordRecommender(), + new AndKeywordRecommender(), + new AnnotationsKeywordRecommender(), + new AscendingKeywordRecommender(), + new AsKeywordRecommender(), + new AssemblyKeywordRecommender(), + new AsyncKeywordRecommender(), + new BaseKeywordRecommender(), + new BoolKeywordRecommender(), + new BreakKeywordRecommender(), + new ByKeywordRecommender(), + new ByteKeywordRecommender(), + new CaseKeywordRecommender(), + new CatchKeywordRecommender(), + new CharKeywordRecommender(), + new CheckedKeywordRecommender(), + new ChecksumKeywordRecommender(), + new ClassKeywordRecommender(), + new ConstKeywordRecommender(), + new ContinueKeywordRecommender(), + new DecimalKeywordRecommender(), + new DefaultKeywordRecommender(), + new DefineKeywordRecommender(), + new DelegateKeywordRecommender(), + new DescendingKeywordRecommender(), + new DisableKeywordRecommender(), + new DoKeywordRecommender(), + new DoubleKeywordRecommender(), + new DynamicKeywordRecommender(), + new ElifKeywordRecommender(), + new ElseKeywordRecommender(), + new EnableKeywordRecommender(), + new EndIfKeywordRecommender(), + new EndRegionKeywordRecommender(), + new EnumKeywordRecommender(), + new EqualsKeywordRecommender(), + new ErrorKeywordRecommender(), + new EventKeywordRecommender(), + new ExplicitKeywordRecommender(), + new ExtensionKeywordRecommender(), + new ExternKeywordRecommender(), + new FalseKeywordRecommender(), + new FieldKeywordRecommender(), + new FileKeywordRecommender(), + new FinallyKeywordRecommender(), + new FixedKeywordRecommender(), + new FloatKeywordRecommender(), + new ForEachKeywordRecommender(), + new ForKeywordRecommender(), + new FromKeywordRecommender(), + new GetKeywordRecommender(), + new GlobalKeywordRecommender(), + new GotoKeywordRecommender(), + new GroupKeywordRecommender(), + new HiddenKeywordRecommender(), + new IfKeywordRecommender(), + new ImplicitKeywordRecommender(), + new InitKeywordRecommender(), + new InKeywordRecommender(), + new InterfaceKeywordRecommender(), + new InternalKeywordRecommender(), + new IntKeywordRecommender(), + new IntoKeywordRecommender(), + new IsKeywordRecommender(), + new JoinKeywordRecommender(), + new LetKeywordRecommender(), + new LineKeywordRecommender(), + new LoadKeywordRecommender(), + new LockKeywordRecommender(), + new LongKeywordRecommender(), + new ManagedKeywordRecommender(), + new MethodKeywordRecommender(), + new ModuleKeywordRecommender(), + new NameOfKeywordRecommender(), + new NamespaceKeywordRecommender(), + new NewKeywordRecommender(), + new NintKeywordRecommender(), + new NotKeywordRecommender(), + new NotNullKeywordRecommender(), + new NuintKeywordRecommender(), + new NullableKeywordRecommender(), + new NullKeywordRecommender(), + new ObjectKeywordRecommender(), + new OnKeywordRecommender(), + new OperatorKeywordRecommender(), + new OrderByKeywordRecommender(), + new OrKeywordRecommender(), + new OutKeywordRecommender(), + new OverrideKeywordRecommender(), + new ParamKeywordRecommender(), + new ParamsKeywordRecommender(), + new PartialKeywordRecommender(), + new PragmaKeywordRecommender(), + new PrivateKeywordRecommender(), + new PropertyKeywordRecommender(), + new ProtectedKeywordRecommender(), + new PublicKeywordRecommender(), + new ReadOnlyKeywordRecommender(), + new RecordKeywordRecommender(), + new ReferenceKeywordRecommender(), + new RefKeywordRecommender(), + new RegionKeywordRecommender(), + new RemoveKeywordRecommender(), + new RequiredKeywordRecommender(), + new RestoreKeywordRecommender(), + new ReturnKeywordRecommender(), + new SByteKeywordRecommender(), + new ScopedKeywordRecommender(), + new SealedKeywordRecommender(), + new SelectKeywordRecommender(), + new SetKeywordRecommender(), + new ShortKeywordRecommender(), + new SizeOfKeywordRecommender(), + new StackAllocKeywordRecommender(), + new StaticKeywordRecommender(), + new StringKeywordRecommender(), + new StructKeywordRecommender(), + new SwitchKeywordRecommender(), + new ThisKeywordRecommender(), + new ThrowKeywordRecommender(), + new TrueKeywordRecommender(), + new TryKeywordRecommender(), + new TypeKeywordRecommender(), + new TypeOfKeywordRecommender(), + new TypeVarKeywordRecommender(), + new UIntKeywordRecommender(), + new ULongKeywordRecommender(), + new UncheckedKeywordRecommender(), + new UndefKeywordRecommender(), + new UnmanagedKeywordRecommender(), + new UnsafeKeywordRecommender(), + new UShortKeywordRecommender(), + new UsingKeywordRecommender(), + new VarKeywordRecommender(), + new VirtualKeywordRecommender(), + new VoidKeywordRecommender(), + new VolatileKeywordRecommender(), + new WarningKeywordRecommender(), + new WarningsKeywordRecommender(), + new WhenKeywordRecommender(), + new WhereKeywordRecommender(), + new WhileKeywordRecommender(), + new WithKeywordRecommender(), + new YieldKeywordRecommender(), + ]) { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public KeywordCompletionProvider() - : base( - [ - new AbstractKeywordRecommender(), - new AddKeywordRecommender(), - new AliasKeywordRecommender(), - new AllowsKeywordRecommender(), - new AndKeywordRecommender(), - new AnnotationsKeywordRecommender(), - new AscendingKeywordRecommender(), - new AsKeywordRecommender(), - new AssemblyKeywordRecommender(), - new AsyncKeywordRecommender(), - new BaseKeywordRecommender(), - new BoolKeywordRecommender(), - new BreakKeywordRecommender(), - new ByKeywordRecommender(), - new ByteKeywordRecommender(), - new CaseKeywordRecommender(), - new CatchKeywordRecommender(), - new CharKeywordRecommender(), - new CheckedKeywordRecommender(), - new ChecksumKeywordRecommender(), - new ClassKeywordRecommender(), - new ConstKeywordRecommender(), - new ContinueKeywordRecommender(), - new DecimalKeywordRecommender(), - new DefaultKeywordRecommender(), - new DefineKeywordRecommender(), - new DelegateKeywordRecommender(), - new DescendingKeywordRecommender(), - new DisableKeywordRecommender(), - new DoKeywordRecommender(), - new DoubleKeywordRecommender(), - new DynamicKeywordRecommender(), - new ElifKeywordRecommender(), - new ElseKeywordRecommender(), - new EnableKeywordRecommender(), - new EndIfKeywordRecommender(), - new EndRegionKeywordRecommender(), - new EnumKeywordRecommender(), - new EqualsKeywordRecommender(), - new ErrorKeywordRecommender(), - new EventKeywordRecommender(), - new ExplicitKeywordRecommender(), - new ExternKeywordRecommender(), - new FalseKeywordRecommender(), - new FieldKeywordRecommender(), - new FileKeywordRecommender(), - new FinallyKeywordRecommender(), - new FixedKeywordRecommender(), - new FloatKeywordRecommender(), - new ForEachKeywordRecommender(), - new ForKeywordRecommender(), - new FromKeywordRecommender(), - new GetKeywordRecommender(), - new GlobalKeywordRecommender(), - new GotoKeywordRecommender(), - new GroupKeywordRecommender(), - new HiddenKeywordRecommender(), - new IfKeywordRecommender(), - new ImplicitKeywordRecommender(), - new InitKeywordRecommender(), - new InKeywordRecommender(), - new InterfaceKeywordRecommender(), - new InternalKeywordRecommender(), - new IntKeywordRecommender(), - new IntoKeywordRecommender(), - new IsKeywordRecommender(), - new JoinKeywordRecommender(), - new LetKeywordRecommender(), - new LineKeywordRecommender(), - new LoadKeywordRecommender(), - new LockKeywordRecommender(), - new LongKeywordRecommender(), - new ManagedKeywordRecommender(), - new MethodKeywordRecommender(), - new ModuleKeywordRecommender(), - new NameOfKeywordRecommender(), - new NamespaceKeywordRecommender(), - new NewKeywordRecommender(), - new NintKeywordRecommender(), - new NotKeywordRecommender(), - new NotNullKeywordRecommender(), - new NuintKeywordRecommender(), - new NullableKeywordRecommender(), - new NullKeywordRecommender(), - new ObjectKeywordRecommender(), - new OnKeywordRecommender(), - new OperatorKeywordRecommender(), - new OrderByKeywordRecommender(), - new OrKeywordRecommender(), - new OutKeywordRecommender(), - new OverrideKeywordRecommender(), - new ParamKeywordRecommender(), - new ParamsKeywordRecommender(), - new PartialKeywordRecommender(), - new PragmaKeywordRecommender(), - new PrivateKeywordRecommender(), - new PropertyKeywordRecommender(), - new ProtectedKeywordRecommender(), - new PublicKeywordRecommender(), - new ReadOnlyKeywordRecommender(), - new RecordKeywordRecommender(), - new ReferenceKeywordRecommender(), - new RefKeywordRecommender(), - new RegionKeywordRecommender(), - new RemoveKeywordRecommender(), - new RequiredKeywordRecommender(), - new RestoreKeywordRecommender(), - new ReturnKeywordRecommender(), - new SByteKeywordRecommender(), - new ScopedKeywordRecommender(), - new SealedKeywordRecommender(), - new SelectKeywordRecommender(), - new SetKeywordRecommender(), - new ShortKeywordRecommender(), - new SizeOfKeywordRecommender(), - new StackAllocKeywordRecommender(), - new StaticKeywordRecommender(), - new StringKeywordRecommender(), - new StructKeywordRecommender(), - new SwitchKeywordRecommender(), - new ThisKeywordRecommender(), - new ThrowKeywordRecommender(), - new TrueKeywordRecommender(), - new TryKeywordRecommender(), - new TypeKeywordRecommender(), - new TypeOfKeywordRecommender(), - new TypeVarKeywordRecommender(), - new UIntKeywordRecommender(), - new ULongKeywordRecommender(), - new UncheckedKeywordRecommender(), - new UndefKeywordRecommender(), - new UnmanagedKeywordRecommender(), - new UnsafeKeywordRecommender(), - new UShortKeywordRecommender(), - new UsingKeywordRecommender(), - new VarKeywordRecommender(), - new VirtualKeywordRecommender(), - new VoidKeywordRecommender(), - new VolatileKeywordRecommender(), - new WarningKeywordRecommender(), - new WarningsKeywordRecommender(), - new WhenKeywordRecommender(), - new WhereKeywordRecommender(), - new WhileKeywordRecommender(), - new WithKeywordRecommender(), - new YieldKeywordRecommender(), - ]) - { - } - internal override string Language => LanguageNames.CSharp; public override bool IsInsertionTrigger(SourceText text, int characterPosition, CompletionOptions options) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/OperatorsAndIndexer/UnnamedSymbolCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/OperatorsAndIndexer/UnnamedSymbolCompletionProvider.cs index 8e5999af497b1..00570566f14be 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/OperatorsAndIndexer/UnnamedSymbolCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/OperatorsAndIndexer/UnnamedSymbolCompletionProvider.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs index 0dfefd0d30513..a126df2d1f5c6 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/OverrideCompletionProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; @@ -204,8 +203,8 @@ public override ImmutableArray FilterOverrides(ImmutableArray return filteredMembers.Length > 0 ? filteredMembers : members; } - protected override int GetTargetCaretPosition(SyntaxNode caretTarget) + protected override TextSpan GetTargetSelectionSpan(SyntaxNode caretTarget) { - return CompletionUtilities.GetTargetCaretNodeForInsertedMember(caretTarget).GetLocation().SourceSpan.End; + return CompletionUtilities.GetTargetSelectionSpanForInsertedMember(caretTarget); } } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/PartialMethodCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/PartialMethodCompletionProvider.cs index 65f39e620f798..f1c6f787a9098 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/PartialMethodCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/PartialMethodCompletionProvider.cs @@ -61,10 +61,10 @@ protected override SyntaxNode GetSyntax(SyntaxToken token) ?? throw ExceptionUtilities.UnexpectedValue(token); } - protected override int GetTargetCaretPosition(SyntaxNode caretTarget) + protected override TextSpan GetTargetSelectionSpan(SyntaxNode caretTarget) { var methodDeclaration = (MethodDeclarationSyntax)caretTarget; - return CompletionUtilities.GetTargetCaretPositionForMethod(methodDeclaration).GetLocation().SourceSpan.End; + return CompletionUtilities.GetTargetSelectionSpanForInsertedMember(methodDeclaration); } protected override SyntaxToken GetToken(CompletionItem completionItem, SyntaxTree tree, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs index 76a30bd231ad3..1c3bfbed51d03 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs index e63e511349261..7b47119643a4c 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs @@ -101,7 +101,7 @@ private static bool IsStartOfSpeculativeTContext(SyntaxTree syntaxTree, int posi { var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); - return syntaxTree.IsMemberDeclarationContext(position, context: null, SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken) || + return syntaxTree.IsMemberDeclarationContext(position, context: null, SyntaxKindSet.AllMemberModifiers, SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken) || syntaxTree.IsStatementContext(position, token, cancellationToken) || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || syntaxTree.IsGlobalStatementContext(position, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs index b5fa226cdc805..618804e218ce7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AbstractKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AbstractKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AbstractKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AbstractKeyword) { private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -45,11 +45,6 @@ internal class AbstractKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.FileKeyword, }; - public AbstractKeywordRecommender() - : base(SyntaxKind.AbstractKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AddKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AddKeywordRecommender.cs index 1435aa60d1a65..6e8ab182339bd 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AddKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AddKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AddKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AddKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AddKeyword) { - public AddKeywordRecommender() - : base(SyntaxKind.AddKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.TargetToken.IsAccessorDeclarationContext(position, SyntaxKind.AddKeyword); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AliasKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AliasKeywordRecommender.cs index f3f6d2c5f56ea..7d6320f01b172 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AliasKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AliasKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AliasKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AliasKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AliasKeyword) { - public AliasKeywordRecommender() - : base(SyntaxKind.AliasKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AllowsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AllowsKeywordRecommender.cs index 4a805d2a16ba7..c047705a1c956 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AllowsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AllowsKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AllowsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AllowsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AllowsKeyword) { - public AllowsKeywordRecommender() - : base(SyntaxKind.AllowsKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return IsAllowsRefStructConstraintContext(context); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AndKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AndKeywordRecommender.cs index b38e4af55dd06..efe7e52e95c98 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AndKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AndKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AndKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AndKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AndKeyword) { - public AndKeywordRecommender() - : base(SyntaxKind.AndKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsAtEndOfPattern; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AnnotationsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AnnotationsKeywordRecommender.cs index 3137f750a709c..371f3fb4f6020 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AnnotationsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AnnotationsKeywordRecommender.cs @@ -7,13 +7,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AnnotationsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AnnotationsKeywordRecommender() + : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AnnotationsKeyword, isValidInPreprocessorContext: true) { - public AnnotationsKeywordRecommender() - : base(SyntaxKind.AnnotationsKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var previousToken1 = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsKeywordRecommender.cs index 4bd98f826ca8d..948e99553d869 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AsKeyword) { - public AsKeywordRecommender() - : base(SyntaxKind.AsKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => !context.IsInNonUserCode && context.IsIsOrAsOrSwitchOrWithExpressionContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AscendingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AscendingKeywordRecommender.cs index 7676ae1f91bad..5671c1ed87b4f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AscendingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AscendingKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AscendingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AscendingKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AscendingKeyword) { - public AscendingKeywordRecommender() - : base(SyntaxKind.AscendingKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.TargetToken.IsOrderByDirectionContext(); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AssemblyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AssemblyKeywordRecommender.cs index 511ecb03a91c7..a7ea2679d1324 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AssemblyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AssemblyKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AssemblyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AssemblyKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AssemblyKeyword) { - public AssemblyKeywordRecommender() - : base(SyntaxKind.AssemblyKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs index 938e52df3c476..bbb1fc81cdaab 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/AsyncKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class AsyncKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class AsyncKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.AsyncKeyword, isValidInPreprocessorContext: false) { - public AsyncKeywordRecommender() - : base(SyntaxKind.AsyncKeyword, isValidInPreprocessorContext: false) - { - } - private static readonly ISet s_validLocalFunctionModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.StaticKeyword, @@ -42,7 +37,7 @@ private static bool InMemberDeclarationContext(int position, CSharpSyntaxContext || context.SyntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BaseKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BaseKeywordRecommender.cs index 1395a08503574..b78b3b3745e7b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BaseKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BaseKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class BaseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class BaseKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.BaseKeyword) { - public BaseKeywordRecommender() - : base(SyntaxKind.BaseKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // We need to at least be in a type declaration context. This prevents us from showing diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs index d8a0e47124241..bfaae8ae25f9d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BoolKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class BoolKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class BoolKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.BoolKeyword) { - public BoolKeywordRecommender() - : base(SyntaxKind.BoolKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -46,8 +41,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BreakKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BreakKeywordRecommender.cs index 0b2408309c513..12279599e2196 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BreakKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/BreakKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class BreakKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class BreakKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.BreakKeyword) { - public BreakKeywordRecommender() - : base(SyntaxKind.BreakKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByKeywordRecommender.cs index e0a41695d0bb2..7b77ea5eec5e8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ByKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ByKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ByKeyword) { - public ByKeywordRecommender() - : base(SyntaxKind.ByKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs index c9b5ebfd4d1aa..a7b5e06e48371 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ByteKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ByteKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class ByteKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.ByteKeyword) { - public ByteKeywordRecommender() - : base(SyntaxKind.ByteKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CaseKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CaseKeywordRecommender.cs index d45fa895ef8ac..2a5e494498cc5 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CaseKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CaseKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class CaseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class CaseKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.CaseKeyword) { - public CaseKeywordRecommender() - : base(SyntaxKind.CaseKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CatchKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CatchKeywordRecommender.cs index 3662ee41ebed9..9d34aaa5c5f09 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CatchKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CatchKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class CatchKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class CatchKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.CatchKeyword) { - public CatchKeywordRecommender() - : base(SyntaxKind.CatchKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsCatchOrFinallyContext(position, context.LeftToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs index 7f1da322b9aa7..95aca34d8a3b0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CharKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class CharKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class CharKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.CharKeyword) { - public CharKeywordRecommender() - : base(SyntaxKind.CharKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -46,8 +41,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CheckedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CheckedKeywordRecommender.cs index 606e0dfa120ee..a2cc6065078d8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CheckedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/CheckedKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class CheckedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class CheckedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.CheckedKeyword) { - public CheckedKeywordRecommender() - : base(SyntaxKind.CheckedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsStatementContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ChecksumKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ChecksumKeywordRecommender.cs index 24c0523e6d6ca..d35436aa98fd8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ChecksumKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ChecksumKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ChecksumKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ChecksumKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ChecksumKeyword, isValidInPreprocessorContext: true) { - public ChecksumKeywordRecommender() - : base(SyntaxKind.ChecksumKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // # pragma | diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs index 9102e13a1777b..4937e10c9e00b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ClassKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ClassKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ClassKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ClassKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -25,11 +25,6 @@ internal class ClassKeywordRecommender : AbstractSyntacticSingleKeywordRecommend SyntaxKind.FileKeyword, }; - public ClassKeywordRecommender() - : base(SyntaxKind.ClassKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -37,7 +32,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context context.IsGlobalStatementContext || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || context.IsRecordDeclarationContext(s_validModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ConstKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ConstKeywordRecommender.cs index 7c0fe78ee4912..af781fdb2f3d4 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ConstKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ConstKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ConstKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ConstKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ConstKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -28,11 +28,6 @@ internal class ConstKeywordRecommender : AbstractSyntacticSingleKeywordRecommend SyntaxKind.PrivateKeyword, }; - public ConstKeywordRecommender() - : base(SyntaxKind.ConstKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ContinueKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ContinueKeywordRecommender.cs index 6ec1cc64b0e1b..48cd8fc9e7079 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ContinueKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ContinueKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ContinueKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ContinueKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ContinueKeyword) { - public ContinueKeywordRecommender() - : base(SyntaxKind.ContinueKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (!context.IsStatementContext) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs index 89c52e2e156fa..0ddc2cebf60e0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DecimalKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DecimalKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class DecimalKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.DecimalKeyword) { - public DecimalKeywordRecommender() - : base(SyntaxKind.DecimalKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -46,8 +41,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefaultKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefaultKeywordRecommender.cs index 4a44532fc58bb..188576daaea57 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefaultKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefaultKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DefaultKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class DefaultKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.DefaultKeyword, isValidInPreprocessorContext: true) { - public DefaultKeywordRecommender() - : base(SyntaxKind.DefaultKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefineKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefineKeywordRecommender.cs index 695838bb564b1..c4e9b9b882d96 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefineKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DefineKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DefineKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class DefineKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.DefineKeyword, isValidInPreprocessorContext: true) { - public DefineKeywordRecommender() - : base(SyntaxKind.DefineKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext && context.SyntaxTree.IsBeforeFirstToken(position, cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs index 6b00e220af89c..8a89291292c7c 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DelegateKeywordRecommender.cs @@ -28,7 +28,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context IsAfterAsyncKeywordInExpressionContext(context, cancellationToken) || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DescendingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DescendingKeywordRecommender.cs index 2b3a0c1c0f2ae..75ceeb107e0b9 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DescendingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DescendingKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DescendingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class DescendingKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.DescendingKeyword) { - public DescendingKeywordRecommender() - : base(SyntaxKind.DescendingKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.TargetToken.IsOrderByDirectionContext(); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DisableKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DisableKeywordRecommender.cs index ad3b3dd970523..d673115143674 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DisableKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DisableKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DisableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class DisableKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.DisableKeyword, isValidInPreprocessorContext: true) { - public DisableKeywordRecommender() - : base(SyntaxKind.DisableKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var previousToken1 = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoKeywordRecommender.cs index c08d2634c328e..ce5fd34ce6740 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class DoKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.DoKeyword) { - public DoKeywordRecommender() - : base(SyntaxKind.DoKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext || context.IsGlobalStatementContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs index d3186c219d6c3..1eea7f3af0b26 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DoubleKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DoubleKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class DoubleKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.DoubleKeyword) { - public DoubleKeywordRecommender() - : base(SyntaxKind.DoubleKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -46,8 +41,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs index 293458e9899f1..2bf8fb80d5d37 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/DynamicKeywordRecommender.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class DynamicKeywordRecommender : IKeywordRecommender +internal sealed class DynamicKeywordRecommender : IKeywordRecommender { private static bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { @@ -31,7 +31,7 @@ public ImmutableArray RecommendKeywords(int position, CSharp : []; } - protected static bool IsDynamicTypeContext( + private static bool IsDynamicTypeContext( int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -63,8 +63,8 @@ protected static bool IsDynamicTypeContext( syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElifKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElifKeywordRecommender.cs index 5ff98840b21f1..072f5cf512582 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElifKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElifKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ElifKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ElifKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ElifKeyword, isValidInPreprocessorContext: true) { - public ElifKeywordRecommender() - : base(SyntaxKind.ElifKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElseKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElseKeywordRecommender.cs index 2fe2b078a1522..6390290ae90bb 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElseKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ElseKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ElseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ElseKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ElseKeyword, isValidInPreprocessorContext: true) { - public ElseKeywordRecommender() - : base(SyntaxKind.ElseKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsPreProcessorKeywordContext) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnableKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnableKeywordRecommender.cs index fc345c8f8eec4..b8c56a7511556 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnableKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnableKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class EnableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class EnableKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.EnableKeyword, isValidInPreprocessorContext: true) { - public EnableKeywordRecommender() - : base(SyntaxKind.EnableKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var previousToken1 = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndIfKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndIfKeywordRecommender.cs index 3f88e5985b0eb..4c2f537f8ea66 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndIfKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndIfKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class EndIfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class EndIfKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.EndIfKeyword, isValidInPreprocessorContext: true) { - public EndIfKeywordRecommender() - : base(SyntaxKind.EndIfKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndRegionKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndRegionKeywordRecommender.cs index 2ce9c088fca51..e97b33bef0bb3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndRegionKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EndRegionKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class EndRegionKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class EndRegionKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.EndRegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true) { - public EndRegionKeywordRecommender() - : base(SyntaxKind.EndRegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnumKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnumKeywordRecommender.cs index d2c0ff0abe005..e96a0ecb1ee14 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnumKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EnumKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class EnumKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class EnumKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.EnumKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -19,18 +19,13 @@ internal class EnumKeywordRecommender : AbstractSyntacticSingleKeywordRecommende SyntaxKind.ProtectedKeyword, }; - public EnumKeywordRecommender() - : base(SyntaxKind.EnumKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsGlobalStatementContext || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EqualsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EqualsKeywordRecommender.cs index 6273639451bc5..94152eed802bd 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EqualsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EqualsKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class EqualsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class EqualsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.EqualsKeyword) { - public EqualsKeywordRecommender() - : base(SyntaxKind.EqualsKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ErrorKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ErrorKeywordRecommender.cs index c8aac6fad50b5..707f192319b97 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ErrorKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ErrorKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ErrorKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ErrorKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ErrorKeyword, isValidInPreprocessorContext: true) { - public ErrorKeywordRecommender() - : base(SyntaxKind.ErrorKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EventKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EventKeywordRecommender.cs index 8fcdd6a51aac0..42cee0617a245 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EventKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/EventKeywordRecommender.cs @@ -39,8 +39,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context return (context.IsGlobalStatementContext && syntaxTree.IsScript()) || syntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || - context.IsMemberDeclarationContext(validModifiers: s_validClassModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || - context.IsMemberDeclarationContext(validModifiers: s_validStructModifiers, validTypeDeclarations: SyntaxKindSet.StructOnlyTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || - context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, includingRecordParameters: false, cancellationToken); + context.IsMemberDeclarationContext(validModifiers: s_validClassModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || + context.IsMemberDeclarationContext(validModifiers: s_validStructModifiers, validTypeDeclarations: SyntaxKindSet.StructOnlyTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || + context.IsMemberAttributeContext(SyntaxKindSet.NonEnumTypeDeclarations, includingRecordParameters: false, cancellationToken); } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs index b0dbf2efebe31..b5613bc31c191 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExplicitKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ExplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ExplicitKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ExplicitKeyword) { private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -27,11 +27,6 @@ internal class ExplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.UnsafeKeyword, }; - public ExplicitKeywordRecommender() - : base(SyntaxKind.ExplicitKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsMemberDeclarationContext(validModifiers: s_validNonInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExtensionKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExtensionKeywordRecommender.cs new file mode 100644 index 0000000000000..ce7bc623fce5e --- /dev/null +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExtensionKeywordRecommender.cs @@ -0,0 +1,29 @@ +// 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 Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; + +namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; + +internal sealed class ExtensionKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ExtensionKeyword) +{ + private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer); + private static readonly ISet s_validTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.ClassDeclaration + }; + + protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) + { + return + context.ContainingTypeDeclaration?.Modifiers.Any(SyntaxKind.StaticKeyword) is true && + context.IsTypeDeclarationContext( + validModifiers: s_validModifiers, + validTypeDeclarations: s_validTypeDeclarations, + canBePartial: false, + cancellationToken); + } +} diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExternKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExternKeywordRecommender.cs index 4826f6fe419f2..843a3dad47ace 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExternKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ExternKeywordRecommender.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ExternKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ExternKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ExternKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -42,11 +42,6 @@ internal class ExternKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.UnsafeKeyword }; - public ExternKeywordRecommender() - : base(SyntaxKind.ExternKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -56,7 +51,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context syntaxTree.IsGlobalMemberDeclarationContext(position, s_validGlobalModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || context.SyntaxTree.IsLocalFunctionDeclarationContext(position, s_validLocalFunctionModifiers, cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FalseKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FalseKeywordRecommender.cs index d51e8b52d8879..6f0c1104bbefd 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FalseKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FalseKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FalseKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class FalseKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.FalseKeyword, isValidInPreprocessorContext: true) { - public FalseKeywordRecommender() - : base(SyntaxKind.FalseKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs index 73510f4b46dd1..826699f2aa505 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FileKeywordRecommender.cs @@ -11,17 +11,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class FileKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.FileKeyword) { private static readonly ISet s_validModifiers = SyntaxKindSet.AllMemberModifiers .Where(s => s != SyntaxKind.FileKeyword && !SyntaxFacts.IsAccessibilityModifier(s)) .ToSet(); - public FileKeywordRecommender() - : base(SyntaxKind.FileKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.ContainingTypeDeclaration == null diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FinallyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FinallyKeywordRecommender.cs index ab711d32b68ec..080bd52098674 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FinallyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FinallyKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FinallyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class FinallyKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.FinallyKeyword) { - public FinallyKeywordRecommender() - : base(SyntaxKind.FinallyKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsCatchOrFinallyContext(position, context.LeftToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FixedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FixedKeywordRecommender.cs index a7577b1acabfe..f360eab7ab89b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FixedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FixedKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FixedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class FixedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.FixedKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -21,11 +21,6 @@ internal class FixedKeywordRecommender : AbstractSyntacticSingleKeywordRecommend SyntaxKind.UnsafeKeyword, }; - public FixedKeywordRecommender() - : base(SyntaxKind.FixedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => IsUnsafeStatementContext(context) || IsMemberDeclarationContext(context, cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs index 5fd2119e67386..8a96db64e4bfa 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FloatKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FloatKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class FloatKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.FloatKeyword) { - public FloatKeywordRecommender() - : base(SyntaxKind.FloatKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -46,8 +41,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForEachKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForEachKeywordRecommender.cs index a6205dc9fc236..6f79827f24500 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForEachKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForEachKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ForEachKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ForEachKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ForEachKeyword) { - public ForEachKeywordRecommender() - : base(SyntaxKind.ForEachKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForKeywordRecommender.cs index f9650905da950..4444c1465c0b0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ForKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ForKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ForKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ForKeyword) { - public ForKeywordRecommender() - : base(SyntaxKind.ForKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext || context.IsGlobalStatementContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FromKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FromKeywordRecommender.cs index 92e2383fa433a..c1f92982e8030 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FromKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/FromKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class FromKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class FromKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.FromKeyword) { - public FromKeywordRecommender() - : base(SyntaxKind.FromKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GetKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GetKeywordRecommender.cs index 0167db80144ac..aa2cf1e444176 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GetKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GetKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class GetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class GetKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.GetKeyword) { - public GetKeywordRecommender() - : base(SyntaxKind.GetKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs index f2d7e65824754..6a29d5ba5d3a7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GlobalKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class GlobalKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class GlobalKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.GlobalKeyword) { - public GlobalKeywordRecommender() - : base(SyntaxKind.GlobalKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GotoKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GotoKeywordRecommender.cs index 9e606dbe09025..8c2792cccae3e 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GotoKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GotoKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class GotoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class GotoKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.GotoKeyword) { - public GotoKeywordRecommender() - : base(SyntaxKind.GotoKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext || context.IsGlobalStatementContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GroupKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GroupKeywordRecommender.cs index 4980092241f3c..e8e6425185461 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GroupKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/GroupKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class GroupKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class GroupKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.GroupKeyword) { - public GroupKeywordRecommender() - : base(SyntaxKind.GroupKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/HiddenKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/HiddenKeywordRecommender.cs index 022299a64f1f4..ea1ec031491f1 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/HiddenKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/HiddenKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class HiddenKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class HiddenKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.HiddenKeyword, isValidInPreprocessorContext: true) { - public HiddenKeywordRecommender() - : base(SyntaxKind.HiddenKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IfKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IfKeywordRecommender.cs index 1499e9b475ada..21e71459b9371 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IfKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IfKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class IfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class IfKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.IfKeyword, isValidInPreprocessorContext: true) { - public IfKeywordRecommender() - : base(SyntaxKind.IfKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs index 91665dc9014c9..d55f8a27a49fb 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ImplicitKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ImplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ImplicitKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ImplicitKeyword) { private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -27,11 +27,6 @@ internal class ImplicitKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.UnsafeKeyword, }; - public ImplicitKeywordRecommender() - : base(SyntaxKind.ImplicitKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsMemberDeclarationContext(validModifiers: s_validNonInterfaceMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs index 25ebc0dc3647e..fdfed14df36b1 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class InKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class InKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.InKeyword) { - public InKeywordRecommender() - : base(SyntaxKind.InKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InitKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InitKeywordRecommender.cs index 3121422afe03f..afa0035f02b9f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InitKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InitKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class InitKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class InitKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.InitKeyword) { - public InitKeywordRecommender() - : base(SyntaxKind.InitKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs index 37f5011ee1f69..3ccaa9304d466 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntKeywordRecommender.cs @@ -42,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InterfaceKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InterfaceKeywordRecommender.cs index ccd6bab4d1484..b5c548d93f714 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InterfaceKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InterfaceKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class InterfaceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class InterfaceKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.InterfaceKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -20,18 +20,13 @@ internal class InterfaceKeywordRecommender : AbstractSyntacticSingleKeywordRecom SyntaxKind.UnsafeKeyword }; - public InterfaceKeywordRecommender() - : base(SyntaxKind.InterfaceKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsGlobalStatementContext || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InternalKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InternalKeywordRecommender.cs index 6836a4e78275d..06a8b50c05b76 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InternalKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/InternalKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class InternalKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class InternalKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.InternalKeyword) { - public InternalKeywordRecommender() - : base(SyntaxKind.InternalKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return @@ -39,7 +34,7 @@ private static bool IsValidContextForAccessor(CSharpSyntaxContext context) private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || - context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { return CheckPreviousAccessibilityModifiers(context); } @@ -49,7 +44,7 @@ private static bool IsValidContextForMember(CSharpSyntaxContext context, Cancell private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { return CheckPreviousAccessibilityModifiers(context); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntoKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntoKeywordRecommender.cs index 4f12793e4f4e6..86e5a72f815a4 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntoKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IntoKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class IntoKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class IntoKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.IntoKeyword) { - public IntoKeywordRecommender() - : base(SyntaxKind.IntoKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IsKeywordRecommender.cs index 4f7bcfc2072c5..b1602eda69473 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/IsKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class IsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class IsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.IsKeyword) { - public IsKeywordRecommender() - : base(SyntaxKind.IsKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/JoinKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/JoinKeywordRecommender.cs index 5b292040505d4..fac60b6838946 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/JoinKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/JoinKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class JoinKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class JoinKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.JoinKeyword) { - public JoinKeywordRecommender() - : base(SyntaxKind.JoinKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsValidContextForJoinClause(position, context.LeftToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LetKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LetKeywordRecommender.cs index 9880d572bc173..799bb5981d203 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LetKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LetKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class LetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class LetKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.LetKeyword) { - public LetKeywordRecommender() - : base(SyntaxKind.LetKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LineKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LineKeywordRecommender.cs index 645eca23f69ce..6bbe5a76dcc6a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LineKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LineKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class LineKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class LineKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.LineKeyword, isValidInPreprocessorContext: true) { - public LineKeywordRecommender() - : base(SyntaxKind.LineKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LoadKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LoadKeywordRecommender.cs index 41f1a6bbcbb78..789d3316b4968 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LoadKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LoadKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class LoadKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class LoadKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.LoadKeyword, isValidInPreprocessorContext: true) { - public LoadKeywordRecommender() - : base(SyntaxKind.LoadKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LockKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LockKeywordRecommender.cs index 687866b7874af..a0ffa8605ebd2 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LockKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LockKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class LockKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class LockKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.LockKeyword) { - public LockKeywordRecommender() - : base(SyntaxKind.LockKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext || context.IsGlobalStatementContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs index 54cc4818a037d..c40b72585e082 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/LongKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class LongKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class LongKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.LongKeyword) { - public LongKeywordRecommender() - : base(SyntaxKind.LongKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ManagedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ManagedKeywordRecommender.cs index 843244716dd46..7b8323f12f409 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ManagedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ManagedKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ManagedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ManagedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ManagedKeyword) { - public ManagedKeywordRecommender() - : base(SyntaxKind.ManagedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsFunctionPointerCallingConventionContext(context.TargetToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/MethodKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/MethodKeywordRecommender.cs index 87ff3b4947bae..9ff6e77a30883 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/MethodKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/MethodKeywordRecommender.cs @@ -14,7 +14,7 @@ internal sealed class MethodKeywordRecommender() : AbstractSyntacticSingleKeywor { protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, includingRecordParameters: false, cancellationToken)) + if (context.IsMemberAttributeContext(SyntaxKindSet.NonEnumTypeDeclarations, includingRecordParameters: false, cancellationToken)) return true; var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ModuleKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ModuleKeywordRecommender.cs index a1d40d6d268a4..8bab53fe1f5ca 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ModuleKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ModuleKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ModuleKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ModuleKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ModuleKeyword) { - public ModuleKeywordRecommender() - : base(SyntaxKind.ModuleKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsTypeAttributeContext(cancellationToken)) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NameOfKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NameOfKeywordRecommender.cs index 3046b484e68cc..8e52b33a7467b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NameOfKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NameOfKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NameOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NameOfKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NameOfKeyword) { - public NameOfKeywordRecommender() - : base(SyntaxKind.NameOfKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs index 4708ade62a49d..7a5b8c98e9d70 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NamespaceKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NamespaceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NamespaceKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NamespaceKeyword) { - public NamespaceKeywordRecommender() - : base(SyntaxKind.NamespaceKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NewKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NewKeywordRecommender.cs index a8ba3376974ca..a9fe82cee4220 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NewKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NewKeywordRecommender.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NewKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NewKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NewKeyword) { private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -30,7 +30,7 @@ internal class NewKeywordRecommender : AbstractSyntacticSingleKeywordRecommender SyntaxKind.VolatileKeyword, }; - protected static readonly ISet ValidTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) + private static readonly ISet ValidTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.AbstractKeyword, SyntaxKind.InternalKeyword, @@ -42,11 +42,6 @@ internal class NewKeywordRecommender : AbstractSyntacticSingleKeywordRecommender SyntaxKind.UnsafeKeyword }; - public NewKeywordRecommender() - : base(SyntaxKind.NewKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotKeywordRecommender.cs index 580ac26655f37..047de30f2a829 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NotKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NotKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NotKeyword) { - public NotKeywordRecommender() - : base(SyntaxKind.NotKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsAtStartOfPattern; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotnullKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotnullKeywordRecommender.cs index 16d25b78936d1..996ee34cf0871 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotnullKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NotnullKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NotNullKeywordRecommender : IKeywordRecommender +internal sealed class NotNullKeywordRecommender : IKeywordRecommender { public ImmutableArray RecommendKeywords(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullKeywordRecommender.cs index bc4d1b532abfb..201df1ecf8f59 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NullKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NullKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NullKeyword) { - public NullKeywordRecommender() - : base(SyntaxKind.NullKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsAnyExpressionContext || context.IsStatementContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullableKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullableKeywordRecommender.cs index de2074a42b9d3..ee8b1f254697a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullableKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/NullableKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class NullableKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class NullableKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.NullableKeyword, isValidInPreprocessorContext: true) { - public NullableKeywordRecommender() - : base(SyntaxKind.NullableKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs index 88e691ef9650b..51eb6ea8b527d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ObjectKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ObjectKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class ObjectKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.ObjectKeyword) { - public ObjectKeywordRecommender() - : base(SyntaxKind.ObjectKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -45,8 +40,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OnKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OnKeywordRecommender.cs index 3a837c275a228..df24c6cc1b69f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OnKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OnKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OnKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OnKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OnKeyword) { - public OnKeywordRecommender() - : base(SyntaxKind.OnKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OperatorKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OperatorKeywordRecommender.cs index d84e2761e63d2..72dcfa0391a60 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OperatorKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OperatorKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OperatorKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OperatorKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OperatorKeyword) { - public OperatorKeywordRecommender() - : base(SyntaxKind.OperatorKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: @@ -22,7 +17,6 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context var token = context.TargetToken; return - token.Kind() is SyntaxKind.ImplicitKeyword or - SyntaxKind.ExplicitKeyword; + token.Kind() is SyntaxKind.ImplicitKeyword or SyntaxKind.ExplicitKeyword; } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrKeywordRecommender.cs index 325167c1cb05b..026f7552bd6a0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OrKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OrKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OrKeyword) { - public OrKeywordRecommender() - : base(SyntaxKind.OrKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsAtEndOfPattern; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrderByKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrderByKeywordRecommender.cs index dbd6cd10a614e..b56b9616675b4 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrderByKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OrderByKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OrderByKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OrderByKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OrderByKeyword) { - public OrderByKeywordRecommender() - : base(SyntaxKind.OrderByKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs index 1197922281e17..3185ca210629a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OutKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OutKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OutKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OutKeyword) { - public OutKeywordRecommender() - : base(SyntaxKind.OutKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OverrideKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OverrideKeywordRecommender.cs index 754299cd041cb..50c90bd7ec639 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OverrideKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/OverrideKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class OverrideKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class OverrideKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.OverrideKeyword) { private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -23,11 +23,6 @@ internal class OverrideKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.AbstractKeyword, }; - public OverrideKeywordRecommender() - : base(SyntaxKind.OverrideKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (!context.IsMemberDeclarationContext( diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ParamsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ParamsKeywordRecommender.cs index 51bca79b76354..309050b9264b9 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ParamsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ParamsKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ParamsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ParamsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ParamsKeyword) { - public ParamsKeywordRecommender() - : base(SyntaxKind.ParamsKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsParamsModifierContext(position, context.LeftToken, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PartialKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PartialKeywordRecommender.cs index 6a708de5cc59c..cf5fdf9fdd792 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PartialKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PartialKeywordRecommender.cs @@ -12,13 +12,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class PartialKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class PartialKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.PartialKeyword) { - public PartialKeywordRecommender() - : base(SyntaxKind.PartialKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PragmaKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PragmaKeywordRecommender.cs index 343e7a168d418..14753537a9009 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PragmaKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PragmaKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class PragmaKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class PragmaKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.PragmaKeyword, isValidInPreprocessorContext: true) { - public PragmaKeywordRecommender() - : base(SyntaxKind.PragmaKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PrivateKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PrivateKeywordRecommender.cs index dd696e8abad99..b7bd818b93cbf 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PrivateKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PrivateKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class PrivateKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class PrivateKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.PrivateKeyword) { - public PrivateKeywordRecommender() - : base(SyntaxKind.PrivateKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return @@ -40,7 +35,7 @@ private static bool IsValidContextForAccessor(CSharpSyntaxContext context) private static bool IsValidContextForMember(CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || - context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + context.IsMemberDeclarationContext(validModifiers: SyntaxKindSet.AllMemberModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { var modifiers = context.PrecedingModifiers; @@ -61,7 +56,7 @@ private static bool IsValidContextForMember(CSharpSyntaxContext context, Cancell private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { // private things can't be in namespaces. var typeDecl = context.ContainingTypeDeclaration; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PropertyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PropertyKeywordRecommender.cs index c7cb0e76054d4..494c9200c2e60 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PropertyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PropertyKeywordRecommender.cs @@ -11,5 +11,5 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; internal sealed class PropertyKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.PropertyKeyword) { protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) - => context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, includingRecordParameters: true, cancellationToken); + => context.IsMemberAttributeContext(SyntaxKindSet.NonEnumTypeDeclarations, includingRecordParameters: true, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ProtectedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ProtectedKeywordRecommender.cs index 5241f912c44a8..8bbf2040af5d3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ProtectedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ProtectedKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ProtectedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ProtectedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ProtectedKeyword) { - public ProtectedKeywordRecommender() - : base(SyntaxKind.ProtectedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PublicKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PublicKeywordRecommender.cs index 095a3289e7738..f8fa603c7c6a3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PublicKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/PublicKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class PublicKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class PublicKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.PublicKeyword) { - public PublicKeywordRecommender() - : base(SyntaxKind.PublicKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return @@ -28,7 +23,7 @@ private static bool IsValidContextForMember(CSharpSyntaxContext context, Cancell if (context.SyntaxTree.IsGlobalMemberDeclarationContext(context.Position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { @@ -40,7 +35,7 @@ private static bool IsValidContextForMember(CSharpSyntaxContext context, Cancell private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken) { - if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) + if (context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken)) { return CheckPreviousAccessibilityModifiers(context); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs index 074bcc4111aac..b6790f0fb13c3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReadOnlyKeywordRecommender.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ReadOnlyKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ReadOnlyKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ReadOnlyKeyword) { private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -23,11 +23,6 @@ internal class ReadOnlyKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.StaticKeyword, }; - public ReadOnlyKeywordRecommender() - : base(SyntaxKind.ReadOnlyKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RecordKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RecordKeywordRecommender.cs index e714ba81a1001..f3b6cb1d37916 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RecordKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RecordKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RecordKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RecordKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RecordKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -26,18 +26,13 @@ internal class RecordKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.FileKeyword, }; - public RecordKeywordRecommender() - : base(SyntaxKind.RecordKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsGlobalStatementContext || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs index 73c48681ebb1d..4c938e9c00599 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RefKeywordRecommender.cs @@ -12,13 +12,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RefKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RefKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RefKeyword) { - public RefKeywordRecommender() - : base(SyntaxKind.RefKeyword) - { - } - /// /// Same as with ref specific exclusions /// @@ -147,7 +142,7 @@ private static bool IsValidNewByRefContext(SyntaxTree syntaxTree, int position, syntaxTree.IsGlobalMemberDeclarationContext(position, syntaxTree.IsScript() ? RefGlobalMemberScriptModifiers : RefGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: RefMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken); } @@ -222,6 +217,6 @@ private static bool IsValidRefExpressionContext(CSharpSyntaxContext context) private static bool IsValidContextForType(CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsTypeDeclarationContext(validModifiers: SyntaxKindSet.AllTypeModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: true, cancellationToken); + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken); } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReferenceKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReferenceKeywordRecommender.cs index 08ee7bb6211c7..7fef0850b6520 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReferenceKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReferenceKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ReferenceKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ReferenceKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ReferenceKeyword, isValidInPreprocessorContext: true) { - public ReferenceKeywordRecommender() - : base(SyntaxKind.ReferenceKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RegionKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RegionKeywordRecommender.cs index 82456ae718e0a..2c95fa7443db3 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RegionKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RegionKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RegionKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RegionKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true) { - public RegionKeywordRecommender() - : base(SyntaxKind.RegionKeyword, isValidInPreprocessorContext: true, shouldFormatOnCommit: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsPreProcessorKeywordContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RemoveKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RemoveKeywordRecommender.cs index 05fad0d3880bd..769711c3b6c83 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RemoveKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RemoveKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RemoveKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RemoveKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RemoveKeyword) { - public RemoveKeywordRecommender() - : base(SyntaxKind.RemoveKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.TargetToken.IsAccessorDeclarationContext(position, SyntaxKind.RemoveKeyword); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RequiredKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RequiredKeywordRecommender.cs index e6e4ac276dc54..69302638b1dea 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RequiredKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RequiredKeywordRecommender.cs @@ -11,17 +11,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RequiredKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RequiredKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RequiredKeyword) { private static readonly ISet s_validModifiers = SyntaxKindSet.AllMemberModifiers.Where(s => s is not (SyntaxKind.RequiredKeyword or SyntaxKind.StaticKeyword or SyntaxKind.ReadOnlyKeyword or SyntaxKind.ConstKeyword)).ToSet(); private static readonly ISet s_validTypeDeclarations = SyntaxKindSet.ClassStructRecordTypeDeclarations; - public RequiredKeywordRecommender() - : base(SyntaxKind.RequiredKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsMemberDeclarationContext(s_validModifiers, s_validTypeDeclarations, canBePartial: true, cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RestoreKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RestoreKeywordRecommender.cs index d061038fa0b36..c34a11a74d73d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RestoreKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/RestoreKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class RestoreKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class RestoreKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.RestoreKeyword, isValidInPreprocessorContext: true) { - public RestoreKeywordRecommender() - : base(SyntaxKind.RestoreKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var previousToken1 = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReturnKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReturnKeywordRecommender.cs index 73026436d41e1..ede56a855f395 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReturnKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ReturnKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ReturnKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ReturnKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ReturnKeyword) { - public ReturnKeywordRecommender() - : base(SyntaxKind.ReturnKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return @@ -29,7 +24,7 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context private static bool IsAttributeContext(CSharpSyntaxContext context, CancellationToken cancellationToken) { return - context.IsMemberAttributeContext(SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, includingRecordParameters: false, cancellationToken) || + context.IsMemberAttributeContext(SyntaxKindSet.NonEnumTypeDeclarations, includingRecordParameters: false, cancellationToken) || (context.SyntaxTree.IsScript() && context.IsTypeAttributeContext(cancellationToken)) || context.IsStatementAttributeContext() || IsAccessorAttributeContext(); diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs index 8ed9464600648..d72299b6e81c8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SByteKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SByteKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class SByteKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.SByteKeyword) { - public SByteKeywordRecommender() - : base(SyntaxKind.SByteKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs index 47ea6d582d493..06f7427bd5e6a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SealedKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SealedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class SealedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.SealedKeyword) { private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -45,11 +45,6 @@ internal class SealedKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.FileKeyword, }; - public SealedKeywordRecommender() - : base(SyntaxKind.SealedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SelectKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SelectKeywordRecommender.cs index 628dc12cd2a45..2cc550777ef3f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SelectKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SelectKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SelectKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class SelectKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.SelectKeyword) { - public SelectKeywordRecommender() - : base(SyntaxKind.SelectKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SetKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SetKeywordRecommender.cs index b814c8efe45b6..aafb4473eaaf0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SetKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SetKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SetKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class SetKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.SetKeyword) { - public SetKeywordRecommender() - : base(SyntaxKind.SetKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs index ec64422318929..5b292a35e1635 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ShortKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ShortKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class ShortKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.ShortKeyword) { - public ShortKeywordRecommender() - : base(SyntaxKind.ShortKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SizeOfKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SizeOfKeywordRecommender.cs index 8118f88558edf..ab07ec1d945e0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SizeOfKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SizeOfKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SizeOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class SizeOfKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.SizeOfKeyword) { - public SizeOfKeywordRecommender() - : base(SyntaxKind.SizeOfKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StackAllocKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StackAllocKeywordRecommender.cs index 79e12b7bc40c7..32969cbf57c5b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StackAllocKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StackAllocKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class StackAllocKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class StackAllocKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.StackAllocKeyword) { - public StackAllocKeywordRecommender() - : base(SyntaxKind.StackAllocKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // Beginning with C# 8.0, stackalloc expression can be used inside other expressions diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs index 6ecd476970359..62694d71ce8c2 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StaticKeywordRecommender.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class StaticKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class StaticKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.StaticKeyword) { private static readonly ISet s_validTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -73,11 +73,6 @@ internal class StaticKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.UnsafeKeyword }; - public StaticKeywordRecommender() - : base(SyntaxKind.StaticKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return @@ -110,7 +105,7 @@ private static bool IsValidContextForType(CSharpSyntaxContext context, Cancellat { return context.IsTypeDeclarationContext( validModifiers: s_validTypeModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs index c249f8a4e6774..551d43ddc299a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StringKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal sealed class StringKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class StringKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.StringKeyword) { - public StringKeywordRecommender() - : base(SyntaxKind.StringKeyword) - { - } - protected override SpecialType SpecialType => SpecialType.System_String; protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken); } } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs index 65e0c9813d0eb..20e7d957579a7 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/StructKeywordRecommender.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class StructKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class StructKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.StructKeyword) { private static readonly ISet s_validModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -24,18 +24,13 @@ internal class StructKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.FileKeyword, }; - public StructKeywordRecommender() - : base(SyntaxKind.StructKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsGlobalStatementContext || context.IsTypeDeclarationContext( validModifiers: s_validModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken: cancellationToken) || context.IsRecordDeclarationContext(s_validModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SwitchKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SwitchKeywordRecommender.cs index 37dd1c2392afd..5ef72fc3605fe 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SwitchKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/SwitchKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class SwitchKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class SwitchKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.SwitchKeyword) { - public SwitchKeywordRecommender() - : base(SyntaxKind.SwitchKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThisKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThisKeywordRecommender.cs index a6265c5f1f67e..771d543c67d29 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThisKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThisKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ThisKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ThisKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ThisKeyword) { - public ThisKeywordRecommender() - : base(SyntaxKind.ThisKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThrowKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThrowKeywordRecommender.cs index 9b5c3719e4a22..7731e70dbaf12 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThrowKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ThrowKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ThrowKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class ThrowKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.ThrowKeyword) { - public ThrowKeywordRecommender() - : base(SyntaxKind.ThrowKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsStatementContext || context.IsGlobalStatementContext) diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TrueKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TrueKeywordRecommender.cs index e54d946462601..fe2f15f668fcc 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TrueKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TrueKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class TrueKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class TrueKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.TrueKeyword, isValidInPreprocessorContext: true) { - public TrueKeywordRecommender() - : base(SyntaxKind.TrueKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TryKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TryKeywordRecommender.cs index 61d43d87fb380..fd68d1c27327a 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TryKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TryKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class TryKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class TryKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.TryKeyword) { - public TryKeywordRecommender() - : base(SyntaxKind.TryKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext || context.IsGlobalStatementContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeKeywordRecommender.cs index cbd7efb89671e..20568c3c43e18 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class TypeKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class TypeKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.TypeKeyword) { - public TypeKeywordRecommender() - : base(SyntaxKind.TypeKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsTypeAttributeContext(cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeOfKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeOfKeywordRecommender.cs index 84418bc497561..201a37d676ac0 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeOfKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeOfKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class TypeOfKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class TypeOfKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.TypeOfKeyword) { - public TypeOfKeywordRecommender() - : base(SyntaxKind.TypeOfKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeVarKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeVarKeywordRecommender.cs index 50bbe50118d7c..573bbf764bb00 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeVarKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/TypeVarKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class TypeVarKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class TypeVarKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.TypeVarKeyword) { - public TypeVarKeywordRecommender() - : base(SyntaxKind.TypeVarKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var token = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs index 4a65540388bfe..cbf50b8d07b38 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UIntKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UIntKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class UIntKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.UIntKeyword) { - public UIntKeywordRecommender() - : base(SyntaxKind.UIntKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs index 11b33880b5da6..9b49d37b359a5 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/ULongKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class ULongKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class ULongKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.ULongKeyword) { - public ULongKeywordRecommender() - : base(SyntaxKind.ULongKeyword) - { - } - protected override bool IsValidContextWorker(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; @@ -47,8 +42,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs index d97ca289b7d34..0a03737b12dbe 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UShortKeywordRecommender.cs @@ -12,13 +12,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UShortKeywordRecommender : AbstractSpecialTypePreselectingKeywordRecommender +internal sealed class UShortKeywordRecommender() : AbstractSpecialTypePreselectingKeywordRecommender(SyntaxKind.UShortKeyword) { - public UShortKeywordRecommender() - : base(SyntaxKind.UShortKeyword) - { - } - /// /// We set the of this item less than the default value so that /// completion selects the keyword over it as the user starts typing. @@ -54,8 +49,8 @@ protected override bool IsValidContextWorker(int position, CSharpSyntaxContext c context.IsPossibleTupleContext || context.IsMemberDeclarationContext( validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UncheckedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UncheckedKeywordRecommender.cs index 9edcd2becf758..e191af2005769 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UncheckedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UncheckedKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UncheckedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class UncheckedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.UncheckedKeyword) { - public UncheckedKeywordRecommender() - : base(SyntaxKind.UncheckedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UndefKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UndefKeywordRecommender.cs index 18aacb19c9812..821960d7e7328 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UndefKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UndefKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UndefKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class UndefKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.UndefKeyword, isValidInPreprocessorContext: true) { - public UndefKeywordRecommender() - : base(SyntaxKind.UndefKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs index a1745d7c8f2e8..58a6e822f7732 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnmanagedKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UnmanagedKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class UnmanagedKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.UnmanagedKeyword) { - public UnmanagedKeywordRecommender() - : base(SyntaxKind.UnmanagedKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.SyntaxTree.IsTypeParameterConstraintContext(position, context.LeftToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnsafeKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnsafeKeywordRecommender.cs index df0bb5739fa09..9dea889f75b93 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnsafeKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UnsafeKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UnsafeKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class UnsafeKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.UnsafeKeyword) { private static readonly ISet s_validTypeModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -60,22 +60,17 @@ internal class UnsafeKeywordRecommender : AbstractSyntacticSingleKeywordRecommen SyntaxKind.AsyncKeyword }; - public UnsafeKeywordRecommender() - : base(SyntaxKind.UnsafeKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var syntaxTree = context.SyntaxTree; return context.IsStatementContext || context.IsGlobalStatementContext || - context.IsTypeDeclarationContext(validModifiers: s_validTypeModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || + context.IsTypeDeclarationContext(validModifiers: s_validTypeModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || syntaxTree.IsGlobalMemberDeclarationContext(position, s_validGlobalMemberModifiers, cancellationToken) || context.IsMemberDeclarationContext( validModifiers: s_validMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: false, cancellationToken: cancellationToken) || syntaxTree.IsLocalFunctionDeclarationContext(position, s_validLocalFunctionModifiers, cancellationToken) || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs index 4cdafe0a3aa7b..e3918c2f73bb5 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/UsingKeywordRecommender.cs @@ -9,13 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class UsingKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class UsingKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.UsingKeyword) { - public UsingKeywordRecommender() - : base(SyntaxKind.UsingKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // cases: diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VarKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VarKeywordRecommender.cs index 1fb8f1fc15076..23e82d602dddd 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VarKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VarKeywordRecommender.cs @@ -9,12 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class VarKeywordRecommender : IKeywordRecommender +internal sealed class VarKeywordRecommender : IKeywordRecommender { - public VarKeywordRecommender() - { - } - private static bool IsValidContext(CSharpSyntaxContext context) { if (context.IsStatementContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VirtualKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VirtualKeywordRecommender.cs index 10ffd1b011c64..60877128448d8 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VirtualKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VirtualKeywordRecommender.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class VirtualKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class VirtualKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.VirtualKeyword) { private static readonly ISet s_validNonInterfaceMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -34,11 +34,6 @@ internal class VirtualKeywordRecommender : AbstractSyntacticSingleKeywordRecomme SyntaxKind.UnsafeKeyword, }; - public VirtualKeywordRecommender() - : base(SyntaxKind.VirtualKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (!context.IsMemberDeclarationContext( diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs index 199bf26b52b66..836b2a2a33a13 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VoidKeywordRecommender.cs @@ -110,6 +110,6 @@ private static bool IsUnsafeUsingDirectiveContext(CSharpSyntaxContext context) private static bool IsMemberReturnTypeContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.SyntaxTree.IsGlobalMemberDeclarationContext(position, SyntaxKindSet.AllGlobalMemberModifiers, cancellationToken) || - context.IsMemberDeclarationContext(validModifiers: s_validClassInterfaceRecordModifiers, validTypeDeclarations: SyntaxKindSet.ClassInterfaceRecordTypeDeclarations, canBePartial: true, cancellationToken) || + context.IsMemberDeclarationContext(validModifiers: s_validClassInterfaceRecordModifiers, validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, canBePartial: true, cancellationToken) || context.IsMemberDeclarationContext(validModifiers: s_validStructModifiers, validTypeDeclarations: SyntaxKindSet.StructOnlyTypeDeclarations, canBePartial: false, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VolatileKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VolatileKeywordRecommender.cs index 8267b48c2b984..14db9f64c8841 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VolatileKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/VolatileKeywordRecommender.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class VolatileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class VolatileKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.VolatileKeyword) { private static readonly ISet s_validMemberModifiers = new HashSet(SyntaxFacts.EqualityComparer) { @@ -22,11 +22,6 @@ internal class VolatileKeywordRecommender : AbstractSyntacticSingleKeywordRecomm SyntaxKind.StaticKeyword, }; - public VolatileKeywordRecommender() - : base(SyntaxKind.VolatileKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningKeywordRecommender.cs index 3e762bf3e53f8..11c2b8d1ea9eb 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WarningKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WarningKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WarningKeyword, isValidInPreprocessorContext: true) { - public WarningKeywordRecommender() - : base(SyntaxKind.WarningKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { // # warning diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningsKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningsKeywordRecommender.cs index 7a142ef1080d8..a4dbf2fc7163b 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningsKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WarningsKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WarningsKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WarningsKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WarningsKeyword, isValidInPreprocessorContext: true) { - public WarningsKeywordRecommender() - : base(SyntaxKind.WarningsKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { var previousToken1 = context.TargetToken; diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhenKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhenKeywordRecommender.cs index eaeebe9bed1c6..4b896f470d049 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhenKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhenKeywordRecommender.cs @@ -10,13 +10,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WhenKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WhenKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WhenKeyword, isValidInPreprocessorContext: true) { - public WhenKeywordRecommender() - : base(SyntaxKind.WhenKeyword, isValidInPreprocessorContext: true) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return context.IsCatchFilterContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs index 54e09fcf7e941..30164f90c309f 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhereKeywordRecommender.cs @@ -11,13 +11,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WhereKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WhereKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WhereKeyword) { - public WhereKeywordRecommender() - : base(SyntaxKind.WhereKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { return diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhileKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhileKeywordRecommender.cs index b1fc429f226ba..e36db8cfae7c1 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhileKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WhileKeywordRecommender.cs @@ -8,13 +8,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WhileKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WhileKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WhileKeyword) { - public WhileKeywordRecommender() - : base(SyntaxKind.WhileKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) { if (context.IsStatementContext || diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WithKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WithKeywordRecommender.cs index de122a95edc0c..4289f46d4156d 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WithKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/WithKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class WithKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class WithKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.WithKeyword) { - public WithKeywordRecommender() - : base(SyntaxKind.WithKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => !context.IsInNonUserCode && context.IsIsOrAsOrSwitchOrWithExpressionContext; } diff --git a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/YieldKeywordRecommender.cs b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/YieldKeywordRecommender.cs index 8792b57a6e950..e3cd6e56af5e5 100644 --- a/src/Features/CSharp/Portable/Completion/KeywordRecommenders/YieldKeywordRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/KeywordRecommenders/YieldKeywordRecommender.cs @@ -7,13 +7,8 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.KeywordRecommenders; -internal class YieldKeywordRecommender : AbstractSyntacticSingleKeywordRecommender +internal sealed class YieldKeywordRecommender() : AbstractSyntacticSingleKeywordRecommender(SyntaxKind.YieldKeyword) { - public YieldKeywordRecommender() - : base(SyntaxKind.YieldKeyword) - { - } - protected override bool IsValidContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken) => context.IsStatementContext; } diff --git a/src/Features/CSharp/Portable/ConvertIfToSwitch/CSharpConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs b/src/Features/CSharp/Portable/ConvertIfToSwitch/CSharpConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs index cd1028b1bfe08..fbf6694b316ba 100644 --- a/src/Features/CSharp/Portable/ConvertIfToSwitch/CSharpConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs +++ b/src/Features/CSharp/Portable/ConvertIfToSwitch/CSharpConvertIfToSwitchCodeRefactoringProvider.Rewriting.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertIfToSwitch; diff --git a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs index 99e9b4143629d..1f71395c3a949 100644 --- a/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs +++ b/src/Features/CSharp/Portable/ConvertLinq/ConvertForEachToLinqQuery/AbstractToMethodConverter.cs @@ -5,7 +5,6 @@ #nullable disable using System.Collections.Generic; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.ConvertLinq.ConvertForEachToLinqQuery; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Features/CSharp/Portable/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorCodeRefactoringProvider.cs index 072adadd49073..afbe366f62f9e 100644 --- a/src/Features/CSharp/Portable/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorCodeRefactoringProvider.cs @@ -51,6 +51,11 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte if (typeDeclaration is RecordDeclarationSyntax) return; + // Extensions use the .ParameterList to represent the parameters of the extension method. But this is not a + // constructor, and we cannot offer to convert it to have a regular constructor. + if (typeDeclaration is ExtensionDeclarationSyntax) + return; + var triggerSpan = TextSpan.FromBounds(typeDeclaration.SpanStart, typeDeclaration.ParameterList.FullSpan.End); if (!triggerSpan.Contains(span)) return; diff --git a/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..45dd5e3112ac5 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionCodeRefactoringProvider.cs @@ -0,0 +1,353 @@ +// 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.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertToExtension; + +using static CSharpSyntaxTokens; +using static SyntaxFactory; + +/// +/// Refactoring to convert from classic extension methods to modern C# 14 extension types. Practically all classic +/// extension methods are supported except for those where the 'this' parameter references method type +/// parameters that are not the starting type parameters of the extension method. Those extension methods do not +/// have a 'modern' form as modern extensions have no way of lowering to that classic ABI shape. +/// +[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.ConvertToExtension), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class ConvertToExtensionCodeRefactoringProvider() : CodeRefactoringProvider +{ + /// + /// Information about a class extension method we can convert to a modern extension. Extension methods with + /// 'identical' receiver parameters will compare/hash as equal. That way we can easily find and group all the + /// methods we want to move into a single extension together. + /// + private readonly record struct ExtensionMethodInfo( + ClassDeclarationSyntax ClassDeclaration, + MethodDeclarationSyntax ExtensionMethod, + IParameterSymbol FirstParameter, + ImmutableArray MethodTypeParameters) + { + public bool Equals(ExtensionMethodInfo info) + => ExtensionMethodEqualityComparer.Instance.Equals(this, info); + + public override int GetHashCode() + => ExtensionMethodEqualityComparer.Instance.GetHashCode(this); + } + + internal override FixAllProvider? GetFixAllProvider() + => new ConvertToExtensionFixAllProvider(); + + private static ExtensionMethodInfo? TryGetExtensionMethodInfo( + SemanticModel semanticModel, + MethodDeclarationSyntax methodDeclaration, + CancellationToken cancellationToken) + { + // For ease of processing, only operate on legal extension methods in a legal static class. e.g. + // + // static class S { static R M(this T t, ...) { } } + + if (methodDeclaration.ParameterList.Parameters is not [var firstParameter, ..]) + return null; + + if (!firstParameter.Modifiers.Any(SyntaxKind.ThisKeyword)) + return null; + + if (methodDeclaration.Parent is not ClassDeclarationSyntax classDeclaration) + return null; + + if (!methodDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword)) + return null; + + if (!classDeclaration.Modifiers.Any(SyntaxKind.StaticKeyword)) + return null; + + // Has to be in a top level class. This also makes the fix-all provider easier to implement as we can just look + // for top level static classes to examine for extension methods. + if (classDeclaration.Parent is not BaseNamespaceDeclarationSyntax and not CompilationUnitSyntax) + return null; + + var firstParameterSymbol = semanticModel.GetRequiredDeclaredSymbol(firstParameter, cancellationToken); + + // Gather the referenced method type parameters (in their original method order) in the extension method 'this' + // parameter. If method type parameters are used in that parameter, they must be the a prefix of the type + // parameters of the method. + using var _ = ArrayBuilder.GetInstance(out var methodTypeParameters); + firstParameterSymbol.Type.AddReferencedMethodTypeParameters(methodTypeParameters); + + methodTypeParameters.Sort(static (t1, t2) => t1.Ordinal - t2.Ordinal); + for (var i = 0; i < methodTypeParameters.Count; i++) + { + var typeParameter = methodTypeParameters[i]; + if (typeParameter.Ordinal != i) + return null; + } + + return new(classDeclaration, methodDeclaration, firstParameterSymbol, methodTypeParameters.ToImmutableAndClear()); + } + + /// + /// Returns all the legal extension methods in grouped by their receiver + /// parameter. The groupings are only for receiver parameters that are considered identical, and thus could + /// be the extension parameter P in a new extension(P) declaration. This means they must have the same type, + /// name, ref-ness, constraints, attributes, etc. + /// + /// + /// Because the methods are processed in order within the , the arrays of grouped + /// extension methods in the dictionary will also be similarly ordered. + /// + private static ImmutableDictionary> GetAllExtensionMethods( + SemanticModel semanticModel, ClassDeclarationSyntax classDeclaration, CancellationToken cancellationToken) + { + var map = PooledDictionary>.GetInstance(); + + foreach (var member in classDeclaration.Members) + { + if (member is not MethodDeclarationSyntax methodDeclaration) + continue; + + var extensionInfo = TryGetExtensionMethodInfo(semanticModel, methodDeclaration, cancellationToken); + if (extensionInfo == null) + continue; + + map.MultiAdd(extensionInfo.Value, extensionInfo.Value); + } + + return map.ToImmutableMultiDictionaryAndFree(); + } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var cancellationToken = context.CancellationToken; + + var document = context.Document; + + // Only offer if the user us on C# 14 or later where extension types are supported. + if (!document.Project.ParseOptions!.LanguageVersion().SupportsExtensions()) + return; + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var methodDeclaration = await context.TryGetRelevantNodeAsync().ConfigureAwait(false); + + // If the user is on an extension method itself, offer to convert all the extension methods in the containing + // class with the same receiver parameter to an extension. + if (methodDeclaration != null) + { + var specificExtension = TryGetExtensionMethodInfo(semanticModel, methodDeclaration, cancellationToken); + if (specificExtension == null) + return; + + var allExtensionMethods = GetAllExtensionMethods( + semanticModel, specificExtension.Value.ClassDeclaration, cancellationToken); + + // Offer to change all the extension methods that match this particular parameter + context.RegisterRefactoring(CodeAction.Create( + string.Format(CSharpFeaturesResources.Convert_0_extension_methods_to_extension, specificExtension.Value.FirstParameter.Type.ToDisplayString()), + cancellationToken => ConvertToExtensionAsync( + document, specificExtension.Value.ClassDeclaration, allExtensionMethods, specificExtension, cancellationToken), + CSharpFeaturesResources.Convert_0_extension_methods_to_extension)); + } + else + { + // Otherwise, if they're on a static class, which contains extension methods, offer to convert all of them. + var classDeclaration = await context.TryGetRelevantNodeAsync().ConfigureAwait(false); + if (classDeclaration != null) + { + var allExtensionMethods = GetAllExtensionMethods( + semanticModel, classDeclaration, cancellationToken); + if (allExtensionMethods.IsEmpty) + return; + + context.RegisterRefactoring(CodeAction.Create( + string.Format(CSharpFeaturesResources.Convert_all_extension_methods_in_0_to_extension, classDeclaration.Identifier.ValueText), + cancellationToken => ConvertToExtensionAsync( + document, classDeclaration, allExtensionMethods, specificExtension: null, cancellationToken), + CSharpFeaturesResources.Convert_all_extension_methods_in_0_to_extension)); + } + } + } + + private static async Task ConvertToExtensionAsync( + Document document, + ClassDeclarationSyntax classDeclaration, + ImmutableDictionary> allExtensionMethods, + ExtensionMethodInfo? specificExtension, + CancellationToken cancellationToken) + { + Contract.ThrowIfTrue(allExtensionMethods.IsEmpty); + + var codeGenerationService = document.GetRequiredLanguageService(); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var newDeclaration = ConvertToExtension( + codeGenerationService, classDeclaration, allExtensionMethods, specificExtension); + + var newRoot = root.ReplaceNode(classDeclaration, newDeclaration); + return document.WithSyntaxRoot(newRoot); + } + + /// + /// Core function that the normal fix and the fix-all-provider call into to fixup one class declaration and the set + /// of desired extension methods within that class declaration. When called on an extension method itself, this + /// will just be one extension method. When called on a class declaration, this will be all the extension methods + /// in that class. + /// + private static ClassDeclarationSyntax ConvertToExtension( + ICodeGenerationService codeGenerationService, + ClassDeclarationSyntax classDeclaration, + ImmutableDictionary> allExtensionMethods, + ExtensionMethodInfo? specificExtension) + { + Contract.ThrowIfTrue(allExtensionMethods.IsEmpty); + + var classDeclarationEditor = new SyntaxEditor(classDeclaration, CSharpSyntaxGenerator.Instance); + + if (specificExtension != null) + { + // If we were invoked on a specific extension, then only convert the extensions in this class with the same + // receiver parameter. + ConvertAndReplaceExtensions(allExtensionMethods[specificExtension.Value]); + } + else + { + // Otherwise, convert all the extension methods in this class. + foreach (var (_, matchingExtensions) in allExtensionMethods) + ConvertAndReplaceExtensions(matchingExtensions); + } + + return (ClassDeclarationSyntax)classDeclarationEditor.GetChangedRoot(); + + void ConvertAndReplaceExtensions(ImmutableArray extensionMethods) + { + // Replace the first extension method in the group (which will always be earliest in the class decl) with + // the new extension declaration itself. + classDeclarationEditor.ReplaceNode( + extensionMethods.First().ExtensionMethod, + CreateExtension(extensionMethods)); + + // Then remove the rest of the extensions in the group. + foreach (var siblingExtension in extensionMethods.Skip(1)) + classDeclarationEditor.RemoveNode(siblingExtension.ExtensionMethod); + } + + ExtensionDeclarationSyntax CreateExtension(ImmutableArray group) + { + Contract.ThrowIfTrue(group.IsEmpty); + + var codeGenerationInfo = new CSharpCodeGenerationContextInfo( + CodeGenerationContext.Default, + CSharpCodeGenerationOptions.Default, + (CSharpCodeGenerationService)codeGenerationService, + LanguageVersionExtensions.CSharpNext); + + var firstExtensionInfo = group[0]; + var typeParameters = firstExtensionInfo.MethodTypeParameters.CastArray(); + + // Create a disconnected parameter. This way when we look at it, we won't think of it as an extension method + // parameter any more. This will prevent us from undesirable things (like placing 'this' on it when adding to + // the extension declaration). + var firstParameter = CodeGenerationSymbolFactory.CreateParameterSymbol(firstExtensionInfo.FirstParameter); + + var extensionDeclaration = ExtensionDeclaration( + attributeLists: default, + modifiers: default, + ExtensionKeyword, + TypeParameterGenerator.GenerateTypeParameterList(typeParameters, codeGenerationInfo), + ParameterGenerator.GenerateParameterList([firstParameter], isExplicit: false, codeGenerationInfo), + typeParameters.GenerateConstraintClauses(), + OpenBraceToken, + [.. group.Select(ConvertExtensionMethod)], + CloseBraceToken, + semicolonToken: default); + + // Move the blank lines above the first extension method inside the extension to the extension itself. + firstExtensionInfo.ExtensionMethod.GetNodeWithoutLeadingBlankLines(out var leadingBlankLines); + return extensionDeclaration.WithLeadingTrivia(leadingBlankLines); + } + + MethodDeclarationSyntax ConvertExtensionMethod( + ExtensionMethodInfo extensionMethodInfo, int index) + { + var extensionMethod = extensionMethodInfo.ExtensionMethod; + var parameterList = extensionMethod.ParameterList; + + var converted = extensionMethodInfo.ExtensionMethod + // skip the first parameter, which is the 'this' parameter, and the comma that follows it. + .WithParameterList(parameterList.WithParameters(SeparatedList( + parameterList.Parameters.GetWithSeparators().Skip(2)))) + .WithTypeParameterList(ConvertTypeParameters(extensionMethodInfo)) + .WithConstraintClauses(ConvertConstraintClauses(extensionMethodInfo)); + + // remove 'static' from the classic extension method, now that it is in the extension declaration. it + // represents an 'instance' method in the new form. + converted = CSharpSyntaxGenerator.Instance.WithModifiers(converted, + CSharpSyntaxGenerator.Instance.GetModifiers(converted).WithIsStatic(false)); + + // If we're on the first extension method in the group, then remove its leading blank lines. Those will be + // moved to the extension declaration itself. + if (index == 0) + converted = converted.GetNodeWithoutLeadingBlankLines(); + + // Note: Formatting in this fashion is not desirable. Ideally we would use + // https://github.com/dotnet/roslyn/issues/59228 to just attach an indentation annotation to the extension + // method to indent it instead. + return converted.WithAdditionalAnnotations(Formatter.Annotation); + } + + static TypeParameterListSyntax? ConvertTypeParameters( + ExtensionMethodInfo extensionMethodInfo) + { + var extensionMethod = extensionMethodInfo.ExtensionMethod; + var movedTypeParameterCount = extensionMethodInfo.MethodTypeParameters.Length; + + // If the extension method wasn't generic, or we're not removing any type parameters, there's nothing to do. + if (extensionMethod.TypeParameterList is null || movedTypeParameterCount == 0) + return extensionMethod.TypeParameterList; + + // If we're removing all the type parameters, remove the type parameter list entirely. + if (extensionMethod.TypeParameterList.Parameters.Count == movedTypeParameterCount) + return null; + + // We want to remove the type parameter and the comma that follows it. So we multiple the count of type + // parameters we're removing by two to grab both. + return extensionMethod.TypeParameterList.WithParameters(SeparatedList( + extensionMethod.TypeParameterList.Parameters.GetWithSeparators().Skip(movedTypeParameterCount * 2))); + } + + static SyntaxList ConvertConstraintClauses( + ExtensionMethodInfo extensionMethodInfo) + { + var extensionMethod = extensionMethodInfo.ExtensionMethod; + var movedTypeParameterCount = extensionMethodInfo.MethodTypeParameters.Length; + + // If the extension method had no constraints, or we're not removing any type parameters, there's nothing to do. + if (extensionMethod.ConstraintClauses.Count == 0 || movedTypeParameterCount == 0) + return extensionMethod.ConstraintClauses; + + // Remove clauses referring to type parameters that are being moved to the extension method. + return [.. extensionMethod.ConstraintClauses.Where( + c => !extensionMethodInfo.MethodTypeParameters.Any(t => t.Name == c.Name.Identifier.ValueText))]; + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs b/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs new file mode 100644 index 0000000000000..bf092c673fac6 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertToExtension/ConvertToExtensionFixAllProvider.cs @@ -0,0 +1,80 @@ +// 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 System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeGeneration; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeGeneration; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertToExtension; + +using FixAllScope = CodeAnalysis.CodeFixes.FixAllScope; + +internal sealed partial class ConvertToExtensionCodeRefactoringProvider +{ + private sealed class ConvertToExtensionFixAllProvider() + : DocumentBasedFixAllProvider( + [FixAllScope.Document, FixAllScope.Project, FixAllScope.Solution, FixAllScope.ContainingType]) + { + protected override async Task FixAllAsync( + FixAllContext fixAllContext, + Document document, + Optional> fixAllSpans) + { + var cancellationToken = fixAllContext.CancellationToken; + + var codeGenerationService = (CSharpCodeGenerationService)document.GetRequiredLanguageService(); + + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var editor = new SyntaxEditor(root, document.Project.Solution.Services); + foreach (var declaration in GetTopLevelClassDeclarations(root, fixAllSpans)) + { + // We might hit partial parts that have no extension methods in them. Just skip those. + var extensionMethods = GetAllExtensionMethods(semanticModel, declaration, cancellationToken); + if (extensionMethods.IsEmpty) + continue; + + // For each class declaration we hit that has extension methods in it, convert all the extension methods + // to extensions and replace the old declaration with the new one. + var newDeclaration = ConvertToExtension( + codeGenerationService, declaration, extensionMethods, specificExtension: null); + editor.ReplaceNode(declaration, newDeclaration); + } + + var newRoot = editor.GetChangedRoot(); + return document.WithSyntaxRoot(newRoot); + } + + private static IEnumerable GetTopLevelClassDeclarations( + SyntaxNode root, Optional> fixAllSpans) + { + if (fixAllSpans.HasValue) + { + // User selected 'fix all in containing type'. Core code refactoring engine will return the spans of + // the containing class. Process each of those individually, converting all the extension methods in + // each partial part to extension declarations. + return fixAllSpans.Value + .Select(span => root.FindNode(span) as ClassDeclarationSyntax) + .WhereNotNull(); + } + else + { + // Processing the whole file. Return all top level classes in the file. + return root + .DescendantNodes(descendIntoChildren: n => n is CompilationUnitSyntax or BaseNamespaceDeclarationSyntax) + .OfType(); + } + } + } +} diff --git a/src/Features/CSharp/Portable/ConvertToExtension/ExtensionMethodEqualityComparer.cs b/src/Features/CSharp/Portable/ConvertToExtension/ExtensionMethodEqualityComparer.cs new file mode 100644 index 0000000000000..f7a5fbc065bf1 --- /dev/null +++ b/src/Features/CSharp/Portable/ConvertToExtension/ExtensionMethodEqualityComparer.cs @@ -0,0 +1,146 @@ +// 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.CodeAnalysis; +using System.Linq; +using Roslyn.Utilities; +using Microsoft.CodeAnalysis.Shared.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.ConvertToExtension; + +internal sealed partial class ConvertToExtensionCodeRefactoringProvider +{ + private sealed class ExtensionMethodEqualityComparer : + IEqualityComparer, + IEqualityComparer, + IEqualityComparer + { + public static readonly ExtensionMethodEqualityComparer Instance = new(); + + private static readonly SymbolEquivalenceComparer s_equivalenceComparer = new( + assemblyComparer: null, + distinguishRefFromOut: true, + // `void Goo(this (int x, int y) tuple)` doesn't match `void Goo(this (int a, int b) tuple) + tupleNamesMustMatch: true, + // `void Goo(this string? x)` doesn't matches `void Goo(this string x)` + ignoreNullableAnnotations: false, + // `void Goo(this object x)` doesn't matches `void Goo(this dynamic x)` + objectAndDynamicCompareEqually: false, + // `void Goo(this string[] x)` doesn't matches `void Goo(this Span x)` + arrayAndReadOnlySpanCompareEqually: false); + + #region IEqualityComparer + + private bool AttributesMatch(ImmutableArray attributes1, ImmutableArray attributes2) + => attributes1.SequenceEqual(attributes2, this); + + public bool Equals(AttributeData? x, AttributeData? y) + { + if (x == y) + return true; + + if (x is null || y is null) + return false; + + // Ensure the attributes reference the same attribute class, and have the same constructor/named-parameter + // values in the same order. + if (!Equals(x.AttributeClass, y.AttributeClass)) + return false; + + return x.ConstructorArguments.SequenceEqual(y.ConstructorArguments) && + x.NamedArguments.SequenceEqual(y.NamedArguments); + } + + // Not needed as we never group by attributes. We only SequenceEqual compare them. + public int GetHashCode([DisallowNull] AttributeData obj) + => throw ExceptionUtilities.Unreachable(); + + #endregion + + #region IEqualityComparer + + public bool Equals(ITypeParameterSymbol? x, ITypeParameterSymbol? y) + { + if (x == y) + return true; + + if (x is null || y is null) + return false; + + // Names must match as the code in the extension methods may reference the type parameters by name and has + // to continue working. + if (x.Name != y.Name) + return false; + + // Attributes have to match as we're moving these type parameters up to the extension itself. + if (!AttributesMatch(x.GetAttributes(), y.GetAttributes())) + return false; + + // Constraints have to match as we're moving these type parameters up to the extension itself. + if (x.HasConstructorConstraint != y.HasConstructorConstraint) + return false; + + if (x.HasNotNullConstraint != y.HasNotNullConstraint) + return false; + + if (x.HasReferenceTypeConstraint != y.HasReferenceTypeConstraint) + return false; + + if (x.HasUnmanagedTypeConstraint != y.HasUnmanagedTypeConstraint) + return false; + + if (x.HasValueTypeConstraint != y.HasValueTypeConstraint) + return false; + + // Constraints have to match as we're moving these type parameters up to the extension itself. We again use + // s_equivalenceComparer.SignatureTypeEquivalenceComparer here as we want method type parameters compared by + // ordinal so that if we constraints that reference the method type parameters, that we can tell they're + // equivalent across disparate methods. + if (!x.ConstraintTypes.SequenceEqual(y.ConstraintTypes, s_equivalenceComparer.SignatureTypeEquivalenceComparer)) + return false; + + return true; + } + + // Not needed as we never group by type parameters. We only SequenceEqual compare them. + public int GetHashCode([DisallowNull] ITypeParameterSymbol obj) + => throw ExceptionUtilities.Unreachable(); + + #endregion + + #region IEqualityComparer + + public bool Equals(ExtensionMethodInfo x, ExtensionMethodInfo y) + { + if (x.ExtensionMethod == y.ExtensionMethod) + return true; + + // For us to consider two extension methods to be equivalent, they must have a first parameter that we + // consider equal, any method type parameters they use must have the same constraints, and they must have + // the same attributes on them. + // + // Notes: s_equivalenceComparer.ParameterEquivalenceComparer will check the parameter name, type, ref kinds, + // custom modifiers. All things we want to match to merge extension methods into the same method. + // + // Note: The initial check will ensure that the same method-type-parameters are used in both methods *when + // compared by type parameter ordinal*. The MethodTypeParameterMatch will then check that the type + // parameters that we would lift to the extension method would be considered the same as well. + + return + s_equivalenceComparer.ParameterEquivalenceComparer.Equals(x.FirstParameter, y.FirstParameter, compareParameterName: true, isCaseSensitive: true) && + AttributesMatch(x.FirstParameter.GetAttributes(), y.FirstParameter.GetAttributes()) && + x.MethodTypeParameters.SequenceEqual(y.MethodTypeParameters, this); + } + + public int GetHashCode(ExtensionMethodInfo obj) + // Loosely match any extension methods if they have the same first parameter type (treating method type + // parameters by ordinal) and same name. We'll do a more full match in .Equals above. + => s_equivalenceComparer.ParameterEquivalenceComparer.GetHashCode(obj.FirstParameter) ^ obj.FirstParameter.Name.GetHashCode(); + + #endregion + } +} diff --git a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs index a92e0e476b5ea..211d6d3c05114 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/ConvertRegularStringToRawStringCodeRefactoringProvider.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertToRawString; diff --git a/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs index 567aaf3eec6cc..f5ed7832fd664 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/ConvertStringToRawStringCodeRefactoringProvider.cs @@ -18,7 +18,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertToRawString; diff --git a/src/Features/CSharp/Portable/ConvertToRawString/IConvertStringProvider.cs b/src/Features/CSharp/Portable/ConvertToRawString/IConvertStringProvider.cs index 356e923e0c94f..e49147063f929 100644 --- a/src/Features/CSharp/Portable/ConvertToRawString/IConvertStringProvider.cs +++ b/src/Features/CSharp/Portable/ConvertToRawString/IConvertStringProvider.cs @@ -5,7 +5,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ConvertToRawString; diff --git a/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..101645b2a7535 --- /dev/null +++ b/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzer.cs @@ -0,0 +1,119 @@ +// 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 Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +internal sealed class CSharpImplementNotImplementedExceptionDiagnosticAnalyzer() + : AbstractBuiltInCodeStyleDiagnosticAnalyzer( + IDEDiagnosticIds.CopilotImplementNotImplementedExceptionDiagnosticId, + EnforceOnBuildValues.CopilotImplementNotImplementedException, + option: null, + new LocalizableResourceString( + nameof(CSharpAnalyzersResources.Implement_with_Copilot), CSharpAnalyzersResources.ResourceManager, typeof(CSharpAnalyzersResources)), + configurable: false) +{ + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() + => DiagnosticAnalyzerCategory.SemanticSpanAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + { + context.RegisterCompilationStartAction(context => + { + var notImplementedExceptionType = context.Compilation.GetTypeByMetadataName(typeof(NotImplementedException).FullName!); + if (notImplementedExceptionType is null) + return; + + context.RegisterOperationBlockAction(context => AnalyzeOperationBlock(context, notImplementedExceptionType)); + }); + } + + private void AnalyzeOperationBlock( + OperationBlockAnalysisContext context, + INamedTypeSymbol notImplementedExceptionType) + { + foreach (var block in context.OperationBlocks) + AnalyzeBlock(block); + + void AnalyzeBlock(IOperation block) + { + foreach (var operation in block.DescendantsAndSelf()) + { + if (operation is IThrowOperation + { + Exception: IConversionOperation + { + Operand: IObjectCreationOperation + { + Constructor.ContainingType: INamedTypeSymbol constructedType, + }, + }, + Syntax: var throwSyntax + } throwOperation && + notImplementedExceptionType.Equals(constructedType)) + { + // Report diagnostic for each throw operation + context.ReportDiagnostic(Diagnostic.Create( + Descriptor, + throwOperation.Syntax.GetLocation())); + + // If the throw is the top-level operation in the containing symbol, report a diagnostic on the + // symbol as well. Note: consider reporting on the entire symbol, instead of just the name. And in + // this case, do not report directly on the throw as well. + if (ShouldReportAsTopLevel(block, operation)) + { + foreach (var location in context.OwningSymbol.Locations) + { + if (location.SourceTree == context.FilterTree) + { + context.ReportDiagnostic(Diagnostic.Create( + Descriptor, + location)); + } + } + } + } + } + } + } + + private static bool ShouldReportAsTopLevel(IOperation block, IOperation operation) + { + if (block is IBlockOperation { Operations: [var child] }) + { + // Handle: { throw new NotImplementedException(); } + if (child == operation) + return true; + + // Handle: => throw new NotImplementedException(); + if (child is IReturnOperation + { + ReturnedValue: IConversionOperation + { + Operand: var operand + } + } && operand == operation && + // Excluding property declarations with expression bodies + // Because their location is an exact match with the throw operation + // and we don't want to report the same location twice + operation.Syntax.Parent is not ArrowExpressionClauseSyntax { Parent: PropertyDeclarationSyntax }) + { + // Include expression-bodied methods and get accessors + return true; + } + + // Handle expression-bodied set accessors + if (child is IExpressionStatementOperation { Operation: var throwOperation } && throwOperation == operation) + return true; + } + + return false; + } +} diff --git a/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionFixProvider.cs b/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionFixProvider.cs new file mode 100644 index 0000000000000..3e6c9caeed567 --- /dev/null +++ b/src/Features/CSharp/Portable/Copilot/CSharpImplementNotImplementedExceptionFixProvider.cs @@ -0,0 +1,154 @@ +// 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.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Copilot; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Utilities; +using static Microsoft.CodeAnalysis.CodeActions.CodeAction; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.CopilotImplementNotImplementedException), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpImplementNotImplementedExceptionFixProvider() : SyntaxEditorBasedCodeFixProvider +{ + private static SyntaxAnnotation WarningAnnotation { get; } + = CodeActions.WarningAnnotation.Create( + CSharpFeaturesResources.Warning_colon_AI_suggestions_might_be_inaccurate); + + public override ImmutableArray FixableDiagnosticIds { get; } + = [IDEDiagnosticIds.CopilotImplementNotImplementedExceptionDiagnosticId]; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var document = context.Document; + var cancellationToken = context.CancellationToken; + + // Checks for feature flag + if (document.GetLanguageService() is not { } optionsService || + await optionsService.IsImplementNotImplementedExceptionEnabledAsync().ConfigureAwait(false) is false) + { + return; + } + + // Checks for service availability + if (document.GetLanguageService() is not { } copilotService || + await copilotService.IsImplementNotImplementedExceptionsAvailableAsync(cancellationToken).ConfigureAwait(false) is false) + { + return; + } + + var diagnosticNode = context.Diagnostics[0].Location.FindNode(getInnermostNodeForTie: true, cancellationToken); + + // Preliminary analysis before registering fix + var methodOrProperty = diagnosticNode.FirstAncestorOrSelf(); + + if (methodOrProperty is BasePropertyDeclarationSyntax or BaseMethodDeclarationSyntax) + { + // Pull out the computation into a lazy computation here. That way if we compute (and thus cache) the + // result for the preview window, we'll produce the same value when the fix is actually applied. + var lazy = AsyncLazy.Create(GetDocumentUpdater(context)); + + context.RegisterCodeFix(Create( + title: CSharpAnalyzersResources.Implement_with_Copilot, + lazy.GetValueAsync, + equivalenceKey: nameof(CSharpAnalyzersResources.Implement_with_Copilot)), + context.Diagnostics); + + Logger.Log(FunctionId.Copilot_Implement_NotImplementedException_Fix_Registered, logLevel: LogLevel.Information); + } + } + + protected override async Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + var memberReferencesBuilder = ImmutableDictionary.CreateBuilder>(); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (var diagnostic in diagnostics) + { + var diagnosticNode = diagnostic.Location.FindNode(getInnermostNodeForTie: true, cancellationToken); + var methodOrProperty = diagnosticNode.FirstAncestorOrSelf(); + + Contract.ThrowIfFalse(methodOrProperty is BasePropertyDeclarationSyntax or BaseMethodDeclarationSyntax); + + if (!memberReferencesBuilder.ContainsKey(methodOrProperty)) + { + var memberSymbol = semanticModel.GetRequiredDeclaredSymbol(methodOrProperty, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + var searchOptions = FindReferencesSearchOptions.GetFeatureOptionsForStartingSymbol(memberSymbol); + var references = await SymbolFinder.FindReferencesAsync(memberSymbol, document.Project.Solution, searchOptions, cancellationToken).ConfigureAwait(false); + memberReferencesBuilder.Add(methodOrProperty, references); + } + } + + var copilotService = document.GetRequiredLanguageService(); + var memberImplementationDetails = await copilotService.ImplementNotImplementedExceptionsAsync(document, memberReferencesBuilder.ToImmutable(), cancellationToken).ConfigureAwait(false); + + foreach (var node in memberReferencesBuilder.Keys) + { + var methodOrProperty = (MemberDeclarationSyntax)node; + + Contract.ThrowIfFalse(memberImplementationDetails.TryGetValue(methodOrProperty, out var implementationDetails)); + + var replacement = implementationDetails.ReplacementNode; + if (replacement is BasePropertyDeclarationSyntax or BaseMethodDeclarationSyntax) + { + replacement = replacement + .WithTriviaFrom(methodOrProperty) + .WithAdditionalAnnotations(Formatter.Annotation, WarningAnnotation, Simplifier.Annotation); + } + else + { + Contract.ThrowIfTrue(string.IsNullOrWhiteSpace(implementationDetails.Message)); + replacement = AddErrorComment(methodOrProperty, implementationDetails.Message); + } + + editor.ReplaceNode(methodOrProperty, replacement); + } + + editor.ReplaceNode(editor.OriginalRoot, editor.GetChangedRoot()); + Logger.Log(FunctionId.Copilot_Implement_NotImplementedException_Completed, logLevel: LogLevel.Information); + } + + private static MemberDeclarationSyntax AddErrorComment(MemberDeclarationSyntax member, string errorMessage) + { + Logger.Log(FunctionId.Copilot_Implement_NotImplementedException_Failed, errorMessage, logLevel: LogLevel.Error); + + var comment = SyntaxFactory.TriviaList( + SyntaxFactory.Comment($"/* {errorMessage} */"), + SyntaxFactory.CarriageReturnLineFeed); + var leadingTrivia = member.GetLeadingTrivia(); + + // Find the last EndOfLineTrivia + var syntaxTrivia = leadingTrivia.LastOrDefault(static trivia => trivia.IsKind(SyntaxKind.EndOfLineTrivia)); + var lastEndOfLineIndex = leadingTrivia.IndexOf(syntaxTrivia); + + // Insert the comment after the last EndOfLineTrivia or at the start if none found + var insertIndex = lastEndOfLineIndex >= 0 ? lastEndOfLineIndex + 1 : 0; + var newLeadingTrivia = leadingTrivia.InsertRange(insertIndex, comment); + + return member + .WithLeadingTrivia(newLeadingTrivia) + .WithAdditionalAnnotations(Formatter.Annotation, WarningAnnotation, Simplifier.Annotation); + } +} diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs index b2e054977d814..6278cd82325a4 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ExtractMethod; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs index f325648475da2..990bc6b6c9c6b 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.CallSiteContainerRewriter.cs @@ -8,10 +8,8 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs index acdeb4533ce4d..842a16f5c4305 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ExtractMethod; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs index aac178a6d1082..befaafb31ffc3 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ExtractMethod; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs index 0cbdbe776212e..082e857e2a425 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.TriviaResult.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.TriviaResult.cs index 4bd7d9d93f26a..10b0fc7d214d2 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.TriviaResult.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.TriviaResult.cs @@ -6,8 +6,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.ExtractMethod; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs index 5b494017fc1a3..c6d4375439b5f 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs index f7f13fd408ec5..bf67d178dc59f 100644 --- a/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs +++ b/src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.ExpressionResult.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ExtractMethod; diff --git a/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs b/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs index 756efd419e9b1..7a3aaeccf1c89 100644 --- a/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs +++ b/src/Features/CSharp/Portable/GenerateMember/GenerateVariable/CSharpGenerateVariableService.cs @@ -93,21 +93,29 @@ protected override bool TryInitializeIdentifierNameState( if (identifierToken.ValueText != string.Empty && !IsProbablyGeneric(identifierName, cancellationToken)) { - var memberAccess = identifierName.Parent as MemberAccessExpressionSyntax; - var conditionalMemberAccess = identifierName.Parent.Parent as ConditionalAccessExpressionSyntax; - if (memberAccess?.Name == identifierName) + if (identifierName.Parent is MemberAccessExpressionSyntax memberAccessExpression && + memberAccessExpression.Name == identifierName) { - simpleNameOrMemberAccessExpression = memberAccess; + simpleNameOrMemberAccessExpression = memberAccessExpression; } - else if ((conditionalMemberAccess?.WhenNotNull as MemberBindingExpressionSyntax)?.Name == identifierName) + else if (identifierName.Parent.Parent is ConditionalAccessExpressionSyntax conditionalAccessExpression && + conditionalAccessExpression.WhenNotNull == identifierName.Parent) { - simpleNameOrMemberAccessExpression = conditionalMemberAccess; + simpleNameOrMemberAccessExpression = conditionalAccessExpression; + } + else if (identifierName.Parent is MemberBindingExpressionSyntax memberBindingExpression && + identifierName.Parent.Parent is AssignmentExpressionSyntax assignmentExpression && + assignmentExpression.Left == memberBindingExpression) + { + simpleNameOrMemberAccessExpression = memberBindingExpression; } else { simpleNameOrMemberAccessExpression = identifierName; } + isConditionalAccessExpression = identifierName.Parent.Parent is ConditionalAccessExpressionSyntax; + // If we're being invoked, then don't offer this, offer generate method instead. // Note: we could offer to generate a field with a delegate type. However, that's // very esoteric and probably not what most users want. @@ -120,7 +128,6 @@ protected override bool TryInitializeIdentifierNameState( var block = identifierName.GetAncestor(); isInExecutableBlock = block != null && !block.OverlapsHiddenPosition(cancellationToken); - isConditionalAccessExpression = conditionalMemberAccess != null; return true; } diff --git a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs index 6ec0d0a19f672..902a2fdbaeaab 100644 --- a/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs +++ b/src/Features/CSharp/Portable/GenerateType/CSharpGenerateTypeService.cs @@ -24,7 +24,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.GenerateType; diff --git a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs index 2927c9b9a107c..b31cd2819eb08 100644 --- a/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ImplementInterface/CSharpImplementImplicitlyCodeRefactoringProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ImplementInterface; diff --git a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromParameterCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromParameterCodeRefactoringProvider.cs index 74d1afc45fc16..cb4955e25a9fd 100644 --- a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromParameterCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromParameterCodeRefactoringProvider.cs @@ -4,7 +4,6 @@ using System.Composition; using System.Diagnostics.CodeAnalysis; -using System.Threading; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.InitializeParameter; diff --git a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider_Update.cs b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider_Update.cs index d894e142d450c..1ed30878af5b1 100644 --- a/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider_Update.cs +++ b/src/Features/CSharp/Portable/InitializeParameter/CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider_Update.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.InitializeParameter; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.InitializeParameter; diff --git a/src/Features/CSharp/Portable/InlineHints/CSharpInlineTypeHintsService.cs b/src/Features/CSharp/Portable/InlineHints/CSharpInlineTypeHintsService.cs index bf41eeb2ad2d8..637a5ecff3a2b 100644 --- a/src/Features/CSharp/Portable/InlineHints/CSharpInlineTypeHintsService.cs +++ b/src/Features/CSharp/Portable/InlineHints/CSharpInlineTypeHintsService.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.InlineHints; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.InlineHints; diff --git a/src/Features/CSharp/Portable/IntroduceUsingStatement/CSharpIntroduceUsingStatementCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/IntroduceUsingStatement/CSharpIntroduceUsingStatementCodeRefactoringProvider.cs index 9786dffe0f8e2..36c6cecd19d91 100644 --- a/src/Features/CSharp/Portable/IntroduceUsingStatement/CSharpIntroduceUsingStatementCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/IntroduceUsingStatement/CSharpIntroduceUsingStatementCodeRefactoringProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.IntroduceUsingStatement; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.IntroduceUsingStatement; @@ -40,7 +39,7 @@ protected override (SyntaxList tryStatements, SyntaxList (tryStatement.Block.Statements, tryStatement.Finally?.Block.Statements ?? default); protected override bool CanRefactorToContainBlockStatements(SyntaxNode parent) - => parent is BlockSyntax || parent is SwitchSectionSyntax || parent.IsEmbeddedStatementOwner(); + => parent is BlockSyntax or SwitchSectionSyntax || parent.IsEmbeddedStatementOwner(); protected override SyntaxList GetSurroundingStatements(StatementSyntax statement) => statement.GetRequiredParent() switch diff --git a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceLocalForExpressionCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceLocalForExpressionCodeRefactoringProvider.cs index 7d96adc036d07..065d9126858a2 100644 --- a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceLocalForExpressionCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceLocalForExpressionCodeRefactoringProvider.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs index 3340b0799a889..5ee7dda238bbb 100644 --- a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs +++ b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceLocal.cs @@ -51,11 +51,12 @@ protected override Document IntroduceLocal( var updatedExpression = expression.WithoutTrivia(); var simplifierOptions = (CSharpSimplifierOptions)options.SimplifierOptions; - // If the target-type new syntax is preferred and "var" is not preferred under any circumstance, then we use the - // target-type new syntax. The approach is not exhaustive. We aim to support codebases that rely strictly on the - // target-type new syntax (i.e., no "var"). - if (simplifierOptions.ImplicitObjectCreationWhenTypeIsApparent.Value && simplifierOptions.GetUseVarPreference() == UseVarPreference.None - && updatedExpression is ObjectCreationExpressionSyntax objectCreationExpression) + // If the implicit-object-creation is preferred and "var" is not preferred under any circumstance, then we use + // the implicit creation form when it it available. + if (simplifierOptions.ImplicitObjectCreationWhenTypeIsApparent.Value && + simplifierOptions.GetUseVarPreference() == UseVarPreference.None && + updatedExpression is ObjectCreationExpressionSyntax objectCreationExpression && + document.Root.SyntaxTree.Options.LanguageVersion() >= LanguageVersion.CSharp9) { var (newKeyword, argumentList) = objectCreationExpression.ArgumentList is null ? (objectCreationExpression.NewKeyword.WithoutTrailingTrivia(), ArgumentList().WithoutLeadingTrivia().WithTrailingTrivia(objectCreationExpression.NewKeyword.TrailingTrivia)) diff --git a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs index c67f104c08359..1e2a3fa381f95 100644 --- a/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs +++ b/src/Features/CSharp/Portable/IntroduceVariable/CSharpIntroduceVariableService_IntroduceQueryLocal.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Features/CSharp/Portable/LineSeparators/CSharpLineSeparatorService.cs b/src/Features/CSharp/Portable/LineSeparators/CSharpLineSeparatorService.cs index 3c50392862f8f..d90427127b991 100644 --- a/src/Features/CSharp/Portable/LineSeparators/CSharpLineSeparatorService.cs +++ b/src/Features/CSharp/Portable/LineSeparators/CSharpLineSeparatorService.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.LineSeparators; diff --git a/src/Features/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Features.csproj b/src/Features/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Features.csproj index 700525f239a5b..29bad84922417 100644 --- a/src/Features/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Features.csproj +++ b/src/Features/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Features.csproj @@ -36,6 +36,7 @@ + diff --git a/src/Features/CSharp/Portable/MoveToNamespace/CSharpMoveToNamespaceService.cs b/src/Features/CSharp/Portable/MoveToNamespace/CSharpMoveToNamespaceService.cs index 80b69757880ca..d1bbe22782f97 100644 --- a/src/Features/CSharp/Portable/MoveToNamespace/CSharpMoveToNamespaceService.cs +++ b/src/Features/CSharp/Portable/MoveToNamespace/CSharpMoveToNamespaceService.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MoveToNamespace; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.MoveToNamespace; @@ -18,6 +17,14 @@ internal class CSharpMoveToNamespaceService( [Import(AllowDefault = true)] IMoveToNamespaceOptionsService optionsService) : AbstractMoveToNamespaceService(optionsService) { + protected override BaseTypeDeclarationSyntax? GetNamedTypeDeclarationSyntax(SyntaxNode node) + => node switch + { + BaseTypeDeclarationSyntax namedTypeDeclaration => namedTypeDeclaration, + ParameterListSyntax parameterList => parameterList.Parent as BaseTypeDeclarationSyntax, + _ => null + }; + protected override string GetNamespaceName(SyntaxNode container) => container switch { diff --git a/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs b/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs index bd8d73f27781c..2a05c1b82d185 100644 --- a/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs +++ b/src/Features/CSharp/Portable/NavigationBar/CSharpNavigationBarItemService.cs @@ -21,7 +21,9 @@ namespace Microsoft.CodeAnalysis.CSharp.NavigationBar; [ExportLanguageService(typeof(INavigationBarItemService), LanguageNames.CSharp), Shared] -internal class CSharpNavigationBarItemService : AbstractNavigationBarItemService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpNavigationBarItemService() : AbstractNavigationBarItemService { private static readonly SymbolDisplayFormat s_typeFormat = SymbolDisplayFormat.CSharpErrorMessageFormat.AddGenericsOptions(SymbolDisplayGenericsOptions.IncludeVariance); @@ -39,22 +41,21 @@ internal class CSharpNavigationBarItemService : AbstractNavigationBarItemService SymbolDisplayMiscellaneousOptions.AllowDefaultLiteral | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier); - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpNavigationBarItemService() - { - } - protected override async Task> GetItemsInCurrentProcessAsync( Document document, bool supportsCodeGeneration, CancellationToken cancellationToken) { - var typesInFile = await GetTypesInFileAsync(document, cancellationToken).ConfigureAwait(false); - var tree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - return GetMembersInTypes(document.Project.Solution, tree, typesInFile, cancellationToken); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + using var _ = PooledHashSet.GetInstance(out var typesInFile); + + AddTypesInFile(semanticModel, typesInFile, cancellationToken); + if (cancellationToken.IsCancellationRequested) + return []; + + return GetMembersInTypes(document.Project.Solution, semanticModel.SyntaxTree, typesInFile, cancellationToken); } private static ImmutableArray GetMembersInTypes( - Solution solution, SyntaxTree tree, IEnumerable types, CancellationToken cancellationToken) + Solution solution, SyntaxTree tree, HashSet types, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.NavigationBar_ItemService_GetMembersInTypes_CSharp, cancellationToken)) { @@ -83,10 +84,15 @@ private static ImmutableArray GetMembersInTypes( memberItems.AddIfNotNull(CreateItemForMember(solution, propertySymbol, tree, cancellationToken)); memberItems.AddIfNotNull(CreateItemForMember(solution, propertySymbol.PartialImplementationPart, tree, cancellationToken)); } - else if (member is IMethodSymbol or IPropertySymbol) + else if (member is IEventSymbol { PartialImplementationPart: { } } eventSymbol) + { + memberItems.AddIfNotNull(CreateItemForMember(solution, eventSymbol, tree, cancellationToken)); + memberItems.AddIfNotNull(CreateItemForMember(solution, eventSymbol.PartialImplementationPart, tree, cancellationToken)); + } + else if (member is IMethodSymbol or IPropertySymbol or IEventSymbol) { - Debug.Assert(member is IMethodSymbol { PartialDefinitionPart: null } or IPropertySymbol { PartialDefinitionPart: null }, - $"NavBar expected GetMembers to return partial method/property definition parts but the implementation part was returned."); + Debug.Assert(member is IMethodSymbol { PartialDefinitionPart: null } or IPropertySymbol { PartialDefinitionPart: null } or IEventSymbol { PartialDefinitionPart: null }, + $"NavBar expected GetMembers to return partial method/property/event definition parts but the implementation part was returned."); memberItems.AddIfNotNull(CreateItemForMember(solution, member, tree, cancellationToken)); } @@ -120,18 +126,11 @@ private static ImmutableArray GetMembersInTypes( } } - private static async Task> GetTypesInFileAsync(Document document, CancellationToken cancellationToken) - { - var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - - return GetTypesInFile(semanticModel, cancellationToken); - } - - private static IEnumerable GetTypesInFile(SemanticModel semanticModel, CancellationToken cancellationToken) + private static void AddTypesInFile( + SemanticModel semanticModel, HashSet types, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.NavigationBar_ItemService_GetTypesInFile_CSharp, cancellationToken)) { - var types = new HashSet(); using var _ = ArrayBuilder.GetInstance(out var nodesToVisit); nodesToVisit.Push(semanticModel.SyntaxTree.GetRoot(cancellationToken)); @@ -139,36 +138,27 @@ private static IEnumerable GetTypesInFile(SemanticModel semant while (nodesToVisit.TryPop(out var node)) { if (cancellationToken.IsCancellationRequested) - return []; + return; - var type = GetType(semanticModel, node, cancellationToken); - - if (type != null) - { - types.Add((INamedTypeSymbol)type); - } + types.AddIfNotNull(GetType(semanticModel, node, cancellationToken)); if (node is BaseMethodDeclarationSyntax or - BasePropertyDeclarationSyntax or - BaseFieldDeclarationSyntax or - StatementSyntax or - ExpressionSyntax) + BasePropertyDeclarationSyntax or + BaseFieldDeclarationSyntax or + StatementSyntax or + ExpressionSyntax) { // quick bail out to prevent us from creating every nodes exist in current file continue; } foreach (var child in node.ChildNodes()) - { nodesToVisit.Push(child); - } } - - return types; } } - private static ISymbol? GetType(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + private static INamedTypeSymbol? GetType(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) => node switch { BaseTypeDeclarationSyntax t => semanticModel.GetDeclaredSymbol(t, cancellationToken), @@ -188,7 +178,7 @@ private static bool IsAccessor(ISymbol member) return false; } - private static RoslynNavigationBarItem? CreateItemForMember( + private static SymbolItem? CreateItemForMember( Solution solution, ISymbol member, SyntaxTree tree, CancellationToken cancellationToken) { var location = GetSymbolLocation(solution, member, tree, cancellationToken); diff --git a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs index 81fd490ab6f3e..96b38b78d4825 100644 --- a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs @@ -193,14 +193,21 @@ protected override NullableFlowState GetNullabilityAnalysis(SemanticModel semant } } - var maxLength = 1000; - var symbolStrings = symbol.DeclaringSyntaxReferences.Select(reference => + var solution = document.Project.Solution; + var declarationCode = symbol.DeclaringSyntaxReferences.Select(reference => { var span = reference.Span; - var sourceText = reference.SyntaxTree.GetText(cancellationToken); - return sourceText.GetSubText(new Text.TextSpan(span.Start, Math.Min(maxLength, span.Length))).ToString(); + var syntaxReferenceDocument = solution.GetDocument(reference.SyntaxTree); + if (syntaxReferenceDocument is not null) + { + return new OnTheFlyDocsRelevantFileInfo(syntaxReferenceDocument, span); + } + + return null; }).ToImmutableArray(); - return new OnTheFlyDocsInfo(symbol.ToDisplayString(), symbolStrings, symbol.Language, hasContentExcluded); + var additionalContext = OnTheFlyDocsUtilities.GetAdditionalOnTheFlyDocsContext(solution, symbol); + + return new OnTheFlyDocsInfo(symbol.ToDisplayString(), declarationCode, symbol.Language, hasContentExcluded, additionalContext); } } diff --git a/src/Features/CSharp/Portable/QuickInfo/OnTheFlyDocsUtilities.cs b/src/Features/CSharp/Portable/QuickInfo/OnTheFlyDocsUtilities.cs new file mode 100644 index 0000000000000..74779aebf1546 --- /dev/null +++ b/src/Features/CSharp/Portable/QuickInfo/OnTheFlyDocsUtilities.cs @@ -0,0 +1,50 @@ +// 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.Linq; +using Microsoft.CodeAnalysis.QuickInfo; +using Microsoft.CodeAnalysis.Shared.Extensions; + +namespace Microsoft.CodeAnalysis.CSharp.QuickInfo; + +internal static class OnTheFlyDocsUtilities +{ + public static ImmutableArray GetAdditionalOnTheFlyDocsContext(Solution solution, ISymbol symbol) + { + var parameters = symbol.GetParameters(); + var typeArguments = symbol.GetTypeArguments(); + + var parameterStrings = parameters.Select(parameter => + { + var typeSymbol = parameter.Type; + return GetOnTheFlyDocsRelevantFileInfo(typeSymbol); + + }).ToImmutableArray(); + + var typeArgumentStrings = typeArguments.Select(typeArgument => + { + return GetOnTheFlyDocsRelevantFileInfo(typeArgument); + + }).ToImmutableArray(); + + return parameterStrings.AddRange(typeArgumentStrings); + + OnTheFlyDocsRelevantFileInfo? GetOnTheFlyDocsRelevantFileInfo(ITypeSymbol typeSymbol) + { + var typeSyntaxReference = typeSymbol.DeclaringSyntaxReferences.FirstOrDefault(); + if (typeSyntaxReference is not null) + { + var typeSpan = typeSyntaxReference.Span; + var syntaxReferenceDocument = solution.GetDocument(typeSyntaxReference.SyntaxTree); + if (syntaxReferenceDocument is not null) + { + return new OnTheFlyDocsRelevantFileInfo(syntaxReferenceDocument, typeSpan); + } + } + + return null; + } + } +} diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs index 34132c1690b07..d88cd6af1630b 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.ReplacePropertyWithMethods; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods; @@ -36,7 +35,7 @@ public override async Task> GetReplacementMembersAsyn Document document, IPropertySymbol property, SyntaxNode propertyDeclarationNode, - IFieldSymbol propertyBackingField, + IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken) diff --git a/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs index 72870319d2bf2..058bfde4a0fe5 100644 --- a/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/ReverseForStatement/CSharpReverseForStatementCodeRefactoringProvider.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ReverseForStatement; diff --git a/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs index edce4c3f60b11..e0b7d7707f28b 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs index 3012605327bd2..326004f333195 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/ElementAccessExpressionSignatureHelpProvider.cs @@ -295,7 +295,7 @@ internal static bool IsArgumentListToken(ElementAccessExpressionSyntax expressio internal static TextSpan GetTextSpan(SyntaxToken openBracket) { Contract.ThrowIfFalse(openBracket.Parent is BracketedArgumentListSyntax && - (openBracket.Parent.Parent is ElementAccessExpressionSyntax || openBracket.Parent.Parent is ElementBindingExpressionSyntax)); + openBracket.Parent.Parent is ElementAccessExpressionSyntax or ElementBindingExpressionSyntax); return SignatureHelpUtilities.GetSignatureHelpSpan((BracketedArgumentListSyntax)openBracket.Parent); } diff --git a/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs index c9dea3cc7a383..3fb298ef18f9c 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/GenericNamePartiallyWrittenSignatureHelpProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.SignatureHelp; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp; diff --git a/src/Features/CSharp/Portable/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs b/src/Features/CSharp/Portable/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs index b209718dc4d27..85709fb5dcd1d 100644 --- a/src/Features/CSharp/Portable/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/SimplifyTypeNames/SimplifyTypeNamesCodeFixProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.SimplifyTypeNames; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SimplifyTypeNames; diff --git a/src/Features/CSharp/Portable/SolutionCrawler/CSharpDocumentDifferenceService.cs b/src/Features/CSharp/Portable/SolutionCrawler/CSharpDocumentDifferenceService.cs index 27d621f018b42..f1192c2ae8574 100644 --- a/src/Features/CSharp/Portable/SolutionCrawler/CSharpDocumentDifferenceService.cs +++ b/src/Features/CSharp/Portable/SolutionCrawler/CSharpDocumentDifferenceService.cs @@ -2,21 +2,40 @@ // 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.Composition; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.SolutionCrawler; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.SolutionCrawler; [ExportLanguageService(typeof(IDocumentDifferenceService), LanguageNames.CSharp), Shared] -internal class CSharpDocumentDifferenceService : AbstractDocumentDifferenceService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpDocumentDifferenceService() : AbstractDocumentDifferenceService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpDocumentDifferenceService() + protected override bool IsContainedInMemberBody(SyntaxNode node, TextSpan span) { + switch (node) + { + case ConstructorDeclarationSyntax constructor: + return (constructor.Body != null && GetBlockBodySpan(constructor.Body).Contains(span)) || + (constructor.Initializer != null && constructor.Initializer.Span.Contains(span)); + case BaseMethodDeclarationSyntax method: + return method.Body != null && GetBlockBodySpan(method.Body).Contains(span); + case BasePropertyDeclarationSyntax property: + return property.AccessorList != null && property.AccessorList.Span.Contains(span); + case EnumMemberDeclarationSyntax @enum: + return @enum.EqualsValue != null && @enum.EqualsValue.Span.Contains(span); + case BaseFieldDeclarationSyntax field: + return field.Declaration != null && field.Declaration.Span.Contains(span); + } + + return false; } + + private static TextSpan GetBlockBodySpan(BlockSyntax body) + => TextSpan.FromBounds(body.OpenBraceToken.Span.End, body.CloseBraceToken.SpanStart); } diff --git a/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs b/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs index f32d1420cf2c7..c1d92b080d7de 100644 --- a/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs +++ b/src/Features/CSharp/Portable/Structure/CSharpStructureHelpers.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/CSharp/Portable/Structure/Providers/DisabledTextTriviaStructureProvider.cs b/src/Features/CSharp/Portable/Structure/Providers/DisabledTextTriviaStructureProvider.cs index eaba300c303c9..ddcfbf14429c5 100644 --- a/src/Features/CSharp/Portable/Structure/Providers/DisabledTextTriviaStructureProvider.cs +++ b/src/Features/CSharp/Portable/Structure/Providers/DisabledTextTriviaStructureProvider.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Structure; diff --git a/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs b/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs index a2489176cb857..72bd4966bbb92 100644 --- a/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs +++ b/src/Features/CSharp/Portable/TaskList/CSharpTaskListService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.TaskList; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.TaskList; diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs index 5edbf8a0f3448..c34dbb872552c 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs @@ -58,12 +58,10 @@ internal sealed class UseExpressionBodyCodeRefactoringProvider() : SyntaxEditorB public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var (document, textSpan, cancellationToken) = context; - if (textSpan.Length > 0) - return; var position = textSpan.Start; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var node = root.FindToken(position).Parent!; + var node = root.FindNode(textSpan); var containingLambda = node.FirstAncestorOrSelf(); if (containingLambda != null && diff --git a/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeRefactoringProvider.cs index 6732298aab63c..fdca9437eb188 100644 --- a/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBodyForLambda/UseExpressionBodyForLambdaCodeRefactoringProvider.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBodyForLambda; diff --git a/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs index ad87123375a11..a5d72f246bdba 100644 --- a/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs +++ b/src/Features/CSharp/Portable/Wrapping/CSharpSyntaxWrappingOptions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.Options; diff --git a/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs index feaabfa4ff44d..0ea27a49fb7f7 100644 --- a/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/Wrapping/CSharpWrappingCodeRefactoringProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Wrapping.BinaryExpression; using Microsoft.CodeAnalysis.CSharp.Wrapping.ChainedExpression; diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpCollectionExpressionWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpCollectionExpressionWrapper.cs index 8af4080e9b32a..e6bdc4a290f4f 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpCollectionExpressionWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpCollectionExpressionWrapper.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Wrapping; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList; diff --git a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs index 1ca87e5cf30e0..b0da25e9dafe1 100644 --- a/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs +++ b/src/Features/CSharp/Portable/Wrapping/SeparatedSyntaxList/CSharpInitializerExpressionWrapper.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Wrapping; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Wrapping.SeparatedSyntaxList; diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 4778cfdc61d32..9afef3157d0e5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -142,11 +142,21 @@ Změnit na přetypování + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Převést {0} na záznam(record) {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Převést na metodu diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index a43075c282e64..b896d14b72616 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -142,11 +142,21 @@ In "cast" ändern + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record „{0}“ in record konvertieren {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method In Methode konvertieren diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index ee05b21aac3c6..417202d3a6169 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -142,11 +142,21 @@ Cambiar a cast + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Convertir "{0}" a record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Convertir al método diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index bac86248f38a6..6700d2de14180 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -142,11 +142,21 @@ Changer en cast + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Convertir '{0}' en record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Convertir en méthode diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index a007bde291ebf..6ceb62dd4c22e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -142,11 +142,21 @@ Cambia in cast + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Convertire '{0}' in record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Converti in metodo diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 416158a7fd634..dab287445e7e0 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -142,11 +142,21 @@ キャストに変更 + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record '{0}' を record に変換 {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method メソッドに変換 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 67785e6aaa311..5bf806f5ab1a5 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -142,11 +142,21 @@ 캐스트로 변경 + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record '{0}'을(를) record로 변환 {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method 메서드로 변환 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index 6b83850e4c74e..b1f1772f41097 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -142,11 +142,21 @@ Zmień na rzutowanie + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Konwertuj „{0}” do record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Konwertuj na metodę diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 3a553c0900553..ee22f0b01c7b9 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -142,11 +142,21 @@ Alterar para conversão + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Converter '{0}' em record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Converter em método diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 4c5eaad7c4c86..3cb08f55240d6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -142,11 +142,21 @@ Изменить на приведение + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record Преобразовать "{0}" в record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Преобразовать в метод diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index ce1534937ea0c..05ba26db3652a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -142,11 +142,21 @@ Tür dönüştürme olarak değiştir + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record '{0}'i record’a dönüştür {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method Yönteme dönüştür diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index f52df918fb35a..a41c02a6819be 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -142,11 +142,21 @@ 更改为强制转换 + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record 将“{0}”转换为 record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method 转换为方法 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index e1c899cc99fb6..cec850afe8c1c 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -142,11 +142,21 @@ 變更為 'cast' + + Convert '{0}' extension methods to extension + Convert '{0}' extension methods to extension + + Convert '{0}' to record 將 '{0}' 轉換為 record {Locked="record"} "record" is a C# keyword and should not be localized. + + Convert all extension methods in '{0}' to extension + Convert all extension methods in '{0}' to extension + + Convert to method 轉換為方法 diff --git a/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs index 02bef89e5260f..9a986b7cd4c31 100644 --- a/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs +++ b/src/Features/CSharpTest/ConvertAutoPropertyToFullProperty/ConvertAutoPropertyToFullPropertyTests_OptionSets.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index 38deae0651b3c..b6740d8190d2b 100644 --- a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeActions.ConvertIfToSwitch; diff --git a/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs b/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs index 05bd85be44986..ef3e74f1460b7 100644 --- a/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs +++ b/src/Features/CSharpTest/ConvertPrimaryToRegularConstructor/ConvertPrimaryToRegularConstructorTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.ConvertPrimaryToRegularConstructor; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; @@ -2738,4 +2739,22 @@ public Class1() LanguageVersion = LanguageVersion.CSharp12, }.RunAsync(); } + + [Fact] + public async Task TestNotOnExtension() + { + await new VerifyCS.Test + { + TestCode = """ + static class Class1 + { + [|extension(string s)|] + { + public void Goo() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } } diff --git a/src/Features/CSharpTest/ConvertToExtension/ConvertToExtensionTests.cs b/src/Features/CSharpTest/ConvertToExtension/ConvertToExtensionTests.cs new file mode 100644 index 0000000000000..694afec21e6ab --- /dev/null +++ b/src/Features/CSharpTest/ConvertToExtension/ConvertToExtensionTests.cs @@ -0,0 +1,1984 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.ConvertToExtension; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.ConvertToExtension; + +using VerifyCS = CSharpCodeRefactoringVerifier; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.CodeActionsConvertToExtension)] +public sealed class ConvertToExtensionTests +{ + [Fact] + public async Task TestBaseCase() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestRefReceiver() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this ref int i) { } + } + """, + FixedCode = """ + static class C + { + extension(ref int i) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInReceiver() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this in int i) { } + } + """, + FixedCode = """ + static class C + { + extension(in int i) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestRefReadonlyReceiver() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this ref readonly int i) { } + } + """, + FixedCode = """ + static class C + { + extension(ref readonly int i) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNotOnCSharp13() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i) { } + } + """, + LanguageVersion = LanguageVersion.CSharp13, + }.RunAsync(); + } + + [Fact] + public async Task TestWithMultipleParameters() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i, string j) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M(string j) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInsideNamespace1() + { + await new VerifyCS.Test + { + TestCode = """ + namespace N + { + static class C + { + [||]public static void M(this int i, string j) { } + } + } + """, + FixedCode = """ + namespace N + { + static class C + { + extension(int i) + { + public void M(string j) { } + } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestInsideNamespace2() + { + await new VerifyCS.Test + { + TestCode = """ + namespace N; + + static class C + { + [||]public static void M(this int i, string j) { } + } + """, + FixedCode = """ + namespace N; + + static class C + { + extension(int i) + { + public void M(string j) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNotWithNoParameters() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M() { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestNotInsideStruct() + { + await new VerifyCS.Test + { + TestCode = """ + struct C + { + [||]public static void M(this int i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + TestState = + { + ExpectedDiagnostics = + { + // /0/Test0.cs(1,8): error CS1106: Extension method must be defined in a non-generic static class + DiagnosticResult.CompilerError("CS1106").WithSpan(1, 8, 1, 9), + } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotInsideInstanceClass() + { + await new VerifyCS.Test + { + TestCode = """ + class C + { + [||]public static void M(this int i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + TestState = + { + ExpectedDiagnostics = + { + // /0/Test0.cs(1,7): error CS1106: Extension method must be defined in a non-generic static class + DiagnosticResult.CompilerError("CS1106").WithSpan(1, 7, 1, 8), + } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotForInstanceMethod() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public void M(this int i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + TestState = + { + ExpectedDiagnostics = + { + // /0/Test0.cs(3,17): error CS0708: 'M': cannot declare instance members in a static class + DiagnosticResult.CompilerError("CS0708").WithSpan(3, 17, 3, 18).WithArguments("M"), + // /0/Test0.cs(3,17): error CS1105: Extension method must be static + DiagnosticResult.CompilerError("CS1105").WithSpan(3, 17, 3, 18), + } + } + }.RunAsync(); + } + + [Fact] + public async Task TestNotForNestedClass() + { + await new VerifyCS.Test + { + TestCode = """ + static class Outer + { + static class C + { + [||]public void M(this int i) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + TestState = + { + ExpectedDiagnostics = + { + // /0/Test0.cs(5,21): error CS0708: 'M': cannot declare instance members in a static class + DiagnosticResult.CompilerError("CS0708").WithSpan(5, 21, 5, 22).WithArguments("M"), + // /0/Test0.cs(5,21): error CS1109: Extension methods must be defined in a top level static class; C is a nested class + DiagnosticResult.CompilerError("CS1109").WithSpan(5, 21, 5, 22).WithArguments("C"), + } + } + }.RunAsync(); + } + + [Fact] + public async Task TestWithReferencedTypeParameterInOrder1() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IList list) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestWithReferencedTypeParameterInOrder2() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IDictionary map) { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IDictionary map) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestWithReferencedTypeParameterInOrder3() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IDictionary map) { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IDictionary map) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestWithReferencedTypeParameterInOrder4() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IList list) + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestWithReferencedTypeParameterNotInOrder1() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping1() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i) { } + public static void N(this int i) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping1_A() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + public static void M(this int i) { } + [||]public static void N(this int i) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping2() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i) { } + public static void N(this int i, string s) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() { } + public void N(string s) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping2_A() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + public static void M(this int i) { } + [||]public static void N(this int i, string s) { } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() { } + public void N(string s) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_MatchingAttributes1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + [||]public static void N([X] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + extension([X] int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_MatchingAttributes2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + [||]public static void N([XAttribute] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + extension([X] int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_MatchingAttributes3() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute + { + public XAttribute(int i) { } + } + + static class C + { + public static void M([X(0)] this int i) { } + [||]public static void N([X(0)] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute + { + public XAttribute(int i) { } + } + + static class C + { + extension([X(0)] int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_MatchingAttributes4() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute + { + public int I; + } + + static class C + { + public static void M([X(I=1)] this int i) { } + [||]public static void N([X(I=1)] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute + { + public int I; + } + + static class C + { + extension([X(I = 1)] int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_NonMatchingAttributes1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + [||]public static void N(this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + extension(int i) + { + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_NonMatchingAttributes2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + [||]public static void N([Y] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + public static void M([X] this int i) { } + extension([Y] int i) + { + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_NonMatchingAttributes3() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute + { + public XAttribute(int i) { } + } + + static class C + { + public static void M([X(0)] this int i) { } + [||]public static void N([X(1)] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute + { + public XAttribute(int i) { } + } + + static class C + { + public static void M([X(0)] this int i) { } + extension([X(1)] int i) + { + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_NonMatchingAttributes4() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute + { + public int I; + } + + static class C + { + public static void M([X(I=1)] this int i) { } + [||]public static void N([X(I=2)] this int i) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute + { + public int I; + } + + static class C + { + public static void M([X(I=1)] this int i) { } + extension([X(I = 2)] int i) + { + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_UnrelatedAttributes() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + public static void M(this int i, [X] string s) { } + [||]public static void N(this int i, [Y] string s) { } + } + """, + FixedCode = """ + using System; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + extension(int i) + { + public void M([X] string s) { } + public void N([Y] string s) { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + [||]public static void M(this IList list) { } + public static void N(this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + extension(IList list) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_DifferentName() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + [||]public static void M(this IList list) { } + public static void N(this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + extension(IList list) + { + public void M() { } + } + + public static void N(this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_SameAttributes() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + [||]public static void M([X] this IList list) { } + public static void N([X] this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + extension([X] IList list) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_DifferentAttributes() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + [||]public static void M([X] this IList list) { } + public static void N([Y] this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + class XAttribute : Attribute { } + class YAttribute : Attribute { } + + static class C + { + extension([X] IList list) + { + public void M() { } + } + + public static void N([Y] this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_SameConstructorConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : new() { } + public static void N(this IList list) where T : new() { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : new() + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_DifferentConstructorConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : new() { } + public static void N(this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : new() + { + public void M() { } + } + + public static void N(this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_SameClassConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : class { } + public static void N(this IList list) where T : class { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : class + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_DifferentClassConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : class { } + public static void N(this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : class + { + public void M() { } + } + + public static void N(this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_SameTypeConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : IList { } + public static void N(this IList list) where T : IList { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : IList + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_TypeParameters_DifferentTypeConstraint() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList list) where T : IList { } + public static void N(this IList list) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(IList list) where T : IList + { + public void M() { } + } + + public static void N(this IList list) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_SameParameterRef() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this ref int i) { } + public static void N(this ref int i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(ref int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_DifferentParameterRef() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this ref int i) { } + public static void N(this int i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(ref int i) + { + public void M() { } + } + + public static void N(this int i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_SameParameterName() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this int i) { } + public static void N(this int i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_DifferentParameterName() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this int i) { } + public static void N(this int j) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + } + + public static void N(this int j) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_SameParameterTupleNames() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this (int i, int j) i) { } + public static void N(this (int i, int j) i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension((int i, int j) i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_DifferentParameterTupleNames() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this (int i, int j) i) { } + public static void N(this (int k, int l) i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension((int i, int j) i) + { + public void M() { } + } + + public static void N(this (int k, int l) i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_SameParameterNullability() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this string? i) { } + public static void N(this string? i) { } + } + """, + FixedCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + extension(string? i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_DifferentParameterNullability() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this string? i) { } + public static void N(this string i) { } + } + """, + FixedCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + extension(string? i) + { + public void M() { } + } + + public static void N(this string i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_SameParameterDynamic() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this {|CS1103:dynamic|} i) { } + public static void N(this {|CS1103:dynamic|} i) { } + } + """, + FixedCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + extension(dynamic i) + { + public void M() { } + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestSimpleGrouping_Parameters_DifferentDynamic() + { + await new VerifyCS.Test + { + TestCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + [||]public static void M(this {|CS1103:dynamic|} i) { } + public static void N(this object i) { } + } + """, + FixedCode = """ + #nullable enable + + using System; + using System.Collections.Generic; + + static class C + { + extension(dynamic i) + { + public void M() { } + } + + public static void N(this object i) { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestTriviaMove1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + [||]public static void N(this int j) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + extension(int j) + { + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestTriviaMove2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + /// + [||]public static void N(this int j) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + extension(int j) + { + /// + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestGroupingWithNonExtension1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void O() { } + + public static void M(this int i) { } + + [||]public static void N(this int i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void O() { } + + extension(int i) + { + public void M() { } + + public void N() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestGroupingWithNonExtension2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + public static void O() { } + + [||]public static void N(this int i) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + + public void N() { } + } + + public static void O() { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestGroupingWithNonExtension3() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + static class C + { + public static void M(this int i) { } + + [||]public static void N(this int i) { } + + public static void O() { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + + public void N() { } + } + + public static void O() { } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestFixClass1() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + [||]static class C + { + public static void M(this int i) { } + + public static void N(this int i) { } + + public static void O(this int j) { } + + public static void P(this int j) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + + public void N() { } + } + + extension(int j) + { + public void O() { } + + public void P() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestFixClass2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + + [||]static class C + { + public static void M(this int i) { } + + public static void O(this int j) { } + + public static void N(this int i) { } + + public static void P(this int j) { } + } + """, + FixedCode = """ + using System; + using System.Collections.Generic; + + static class C + { + extension(int i) + { + public void M() { } + + public void N() { } + } + + extension(int j) + { + public void O() { } + + public void P() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestCodeBody1() + { + await new VerifyCS.Test + { + TestCode = """ + static class C + { + [||]public static void M(this int i) + { + return; + } + } + """, + FixedCode = """ + static class C + { + extension(int i) + { + public void M() + { + return; + } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestCodeBody2() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + static class C + { + [||]public static DateTime M(this int i) + { + return new() + { + }; + } + } + """, + FixedCode = """ + using System; + + static class C + { + extension(int i) + { + public DateTime M() + { + return new() + { + }; + } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestMultipleConstraints1() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IDictionary map) where K : struct where V : class { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IDictionary map) + where K : struct + where V : class + { + public void M() { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } + + [Fact] + public async Task TestMultipleConstraints2() + { + await new VerifyCS.Test + { + TestCode = """ + using System.Collections.Generic; + + static class C + { + [||]public static void M(this IList map) where K : struct where V : class { } + } + """, + FixedCode = """ + using System.Collections.Generic; + + static class C + { + extension(IList map) where K : struct + { + public void M() where V : class { } + } + } + """, + LanguageVersion = LanguageVersionExtensions.CSharpNext, + }.RunAsync(); + } +} diff --git a/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs b/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs index 1740fbf5968e0..b86e7d85ac73d 100644 --- a/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs +++ b/src/Features/CSharpTest/ConvertTupleToStruct/ConvertTupleToStructTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.ConvertTupleToStruct; diff --git a/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzerTests.cs b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzerTests.cs new file mode 100644 index 0000000000000..6874fc59461ca --- /dev/null +++ b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionDiagnosticAnalyzerTests.cs @@ -0,0 +1,455 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot.UnitTests; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpImplementNotImplementedExceptionDiagnosticAnalyzer, + EmptyCodeFixProvider>; + +public sealed class CSharpImplementNotImplementedExceptionDiagnosticAnalyzerTests +{ + [Fact] + public async Task TestThrowNotImplementedExceptionInStatement() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + void {|IDE3000:M|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInExpression() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + int P => {|IDE3000:throw new NotImplementedException()|}; + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInConstructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public {|IDE3000:C|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInDestructor() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + ~{|IDE3000:C|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInIndexer() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public int this[int index] + { + {|IDE3000:get|} { {|IDE3000:throw new NotImplementedException();|} } + {|IDE3000:set|} { {|IDE3000:throw new NotImplementedException();|} } + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInEvent() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public event EventHandler MyEvent + { + {|IDE3000:add|} { {|IDE3000:throw new NotImplementedException();|} } + {|IDE3000:remove|} { {|IDE3000:throw new NotImplementedException();|} } + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestThrowNotImplementedExceptionInOperator() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + + class C + { + public static C operator {|IDE3000:+|}(C a, C b) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task TestDifferentFlavorsOfThrowNotImplementedException() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + + class C + { + void {|IDE3000:M1|}() + { + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + void {|IDE3000:M1WithComment|}() + { + // Some comment + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + void {|IDE3000:M2|}() + { + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + int P1 + { + {|IDE3000:get|} { {|IDE3000:throw new NotImplementedException();|} } + } + + int P2 + { + {|IDE3000:get|} { {|IDE3000:throw new NotImplementedException();|} } + {|IDE3000:set|} { {|IDE3000:throw new NotImplementedException();|} } + } + + int this[int index] + { + {|IDE3000:get|} { {|IDE3000:throw new NotImplementedException();|} } + {|IDE3000:set|} { {|IDE3000:throw new NotImplementedException();|} } + } + + int P11 + { + {|IDE3000:get|} { {|IDE3000:throw new NotImplementedException();|} /*I am a comment*/ } + } + + void {|IDE3000:M6|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + + void {|IDE3000:M7|}() + { + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + public double {|IDE3000:CalculateSquareRoot|}(double number) => {|IDE3000:throw new NotImplementedException("CalculateSquareRoot method not implemented")|}; + + internal void ThrowOnAllStatements(bool condition) + { + {|IDE3000:throw new NotImplementedException("Not implemented");|} + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } + + [Fact] + public async Task WhenShouldNotReportOnMember() + { + await new VerifyCS.Test + { + TestCode = """ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Threading.Tasks; + class C + { + private string _name; + public string Name + { + get => _name; + set => _name = value ?? {|IDE3000:throw new NotImplementedException()|}; + } + + void LambdaThrowWithFunc() + { + Func func = () => {|IDE3000:throw new NotImplementedException()|}; + func(); + } + + void LambdaThrow() + { + Action action = () => {|IDE3000:throw new NotImplementedException()|}; + action(); + } + + void AnonymousMethodThrow() + { + Action action = delegate + { + {|IDE3000:throw new NotImplementedException();|} + }; + action(); + } + + void LocalFunctionThrow() + { + void Local() + { + {|IDE3000:throw new NotImplementedException();|} + } + + Local(); + } + + void NestedBlockThrow() + { + if (true) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + + void LoopThrow() + { + for (int i = 0; i < 10; i++) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + + public int GetValue(string type) => + type switch + { + "A" => 1, + "B" => 2, + _ => {|IDE3000:throw new NotImplementedException($"Type '{type}' not implemented")|} + }; + + void UsingThrow() + { + using (var resource = new System.IO.MemoryStream()) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + + void TryCatchThrow() + { + try + { + // Some code + } + catch (Exception) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + + void LockThrow() + { + lock (new object()) + { + {|IDE3000:throw new NotImplementedException();|} + } + } + + void TernaryThrow(bool condition) + { + var result = condition ? 1 : {|IDE3000:throw new NotImplementedException()|}; + } + + void AnonymousTypeWithLambdaThrow() + { + var result = new { Value = (Func)(() => {|IDE3000:throw new NotImplementedException()|}) }; + } + + void LinqThrow() + { + var result = new[] { 1, 2, 3 }.Select(x => x > 0 ? x : {|IDE3000:throw new NotImplementedException()|}); + } + + public int[] ComplexQuery() + { + return new[] { 1, 2, 3 } + .Where(x => x > 0) + .Select(x => x * 2) + .Where(x => x > 0 ? true : {|IDE3000:throw new NotImplementedException()|}) + .ToArray(); + } + + public void ProcessData(List data) + { + var result = data.Select(item => item switch + { + string s => s.ToUpper(), + int i => i.ToString(), + DateTime d => d.ToShortDateString(), + _ => {|IDE3000:throw new NotImplementedException("Unsupported data type")|} + }); + } + + internal Person CreatePerson(string name, int age) + { + return new Person + { + Name = name ?? {|IDE3000:throw new NotImplementedException("Name cannot be null")|}, + Age = age < 0 ? {|IDE3000:throw new NotImplementedException("Age must be positive")|} : age, + Skills = new() { "C#", "F#" } + }; + } + + public void ProcessWithLocalMethod(string input) + { + string ParseInput(string text) + { + return text?.Length > 5 ? text : {|IDE3000:throw new NotImplementedException("Input too short")|}; + } + } + + public Func GetCalculator(string operation) + { + return operation switch + { + "square" => x => x * x, + "double" => x => x * 2, + _ => {|IDE3000:throw new NotImplementedException($"Operation {operation} not implemented")|} + }; + } + + public void ProcessWithNestedDelegates() + { + Func> createOperation = x => + y => x > 0 ? x + y : {|IDE3000:throw new NotImplementedException("Negative values not implemented")|}; + } + + public async Task GetPersonAsync(int id) + { + var supervisor = id > 100 + ? new Person { Name = "Manager" } + : {|IDE3000:throw new NotImplementedException("Non-manager employees not implemented")|}; + + return supervisor; + } + + void SwitchThrow(int value) + { + switch (value) + { + case 1: + {|IDE3000:throw new NotImplementedException();|} + } + } + + internal void WontReportOnMemberWhenThrowIsNotDirect(bool condition) + { + var result = condition ? 1 : {|IDE3000:throw new NotImplementedException()|}; + + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + internal void WontReportOnMemberWhenNonThrowStatementsExist(bool condition) + { + Console.WriteLine(condition ? 1 : 0); + + {|IDE3000:throw new NotImplementedException("Not implemented");|} + } + + internal class Person + { + public string Name { get; set; } + public int Age { get; set; } + public List Skills { get; set; } + public Person Supervisor { get; set; } + } + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + }.RunAsync(); + } +} diff --git a/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.CodeBlockSuggestions.cs b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.CodeBlockSuggestions.cs new file mode 100644 index 0000000000000..b88a61aba1639 --- /dev/null +++ b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.CodeBlockSuggestions.cs @@ -0,0 +1,189 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot.UnitTests; + +[Trait(Traits.Feature, Traits.Features.CopilotImplementNotImplementedException)] +public sealed partial class CSharpImplementNotImplementedExceptionFixProviderTests +{ + public static IEnumerable TestMethodCodeBlockSuggestions() + { + foreach (var kvp in s_codeBlockSuggestions) + { + var notImplementedMember = kvp.Key; + var codeBlocks = kvp.Value; + foreach (var codeBlock in codeBlocks) + { + yield return new object[] { notImplementedMember, codeBlock }; + } + } + } + + [Theory] + [MemberData(nameof(TestMethodCodeBlockSuggestions))] + public async Task FixerResponse_ReplacesCodeBlockCorrectly(string notImplementedCodeBlock, string replacementCodeBlock) + { + await new CustomCompositionCSharpTest + { + CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipLocalDiagnosticCheck, + TestCode = $$""" +using System; +using System.Threading.Tasks; + +public class TestService +{ + {{notImplementedCodeBlock}} +} +""", + FixedCode = $$""" +using System; +using System.Threading.Tasks; + +public class TestService +{ + {{replacementCodeBlock}} +} +""", + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + } + .WithMockCopilotService(copilotService => + { + copilotService.PrepareUsingSingleFakeResult = new() + { + ReplacementNode = SyntaxFactory.ParseMemberDeclaration(replacementCodeBlock), + }; + }) + .RunAsync(); + } + + private static readonly Dictionary s_codeBlockSuggestions = new() + { + // Single statement with NotImplementedException + [ + @"public void {|IDE3000:TestMethod|}() + { + {|IDE3000:throw new NotImplementedException();|} + }" + ] = + [ + @"public void TestMethod() => Console.WriteLine(""Hello, World!"");", + @"public void TestMethod() + { + Console.WriteLine(""This is a single statement""); + }", + @"public void TestMethod() + { + int x = 10; + int y = 20; + Console.WriteLine(x + y); + }", + @"public void TestMethod() + { + /* Comment before */ + Console.WriteLine(""First line""); + /* Comment after */ + Console.WriteLine(""Second line""); + }", + @"public void TestMethod() + { + // Initialize variables + int a = 5; + int b = 10; + // Perform calculation + int result = a + b; + Console.WriteLine(result); + }", + @"public void TestMethod() + { + var list = new int[] { 1, 2, 3, 4, 5 }; + foreach (var item in list) + { + Console.WriteLine(item); + } + }", + @"public void TestMethod() + { + try + { + // Try block + Console.WriteLine(""This is a test method.""); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + }", + @"public void TestMethod() + { + if (DateTime.Now.DayOfWeek == DayOfWeek.Friday) + { + Console.WriteLine(""It's Friday!""); + } + else + { + Console.WriteLine(""It's not Friday.""); + } + }", + @"public void TestMethod() + { + Console.WriteLine(""Start""); // Comment at the end + }", + @"public void TestMethod() + { + /* Multi-line comment at the beginning */ + Console.WriteLine(""Middle""); + }", + @"public void TestMethod() + { + Console.WriteLine(""End""); /* Multi-line comment at the end */ + }", + @"public void TestMethod() + { + // Single-line comment at the beginning + Console.WriteLine(""Middle""); + }", + @"public void TestMethod() + { + Console.WriteLine(""End""); // Single-line comment at the end + }", + @"public void TestMethod() + { + Console.WriteLine(""Hi""); + throw new InvalidOperationException(); + }" + ], + // Async method with NotImplementedException + [ + @"public async Task {|IDE3000:TestMethodAsync|}() + { + {|IDE3000:throw new NotImplementedException();|} + }" + ] = + [ + @"public async Task TestMethodAsync() + { + await Task.Delay(1000); + Console.WriteLine(""Async operation completed""); + }", + @"public async Task TestMethodAsync() + => await Task.Run(() => Console.WriteLine(""Running async task""));" + ], + // Property with NotImplementedException in expression-bodied member + [ + @"public int TestProperty => {|IDE3000:throw new NotImplementedException()|};" + ] = + [ + @"public int TestProperty => 42;", + @"public int TestProperty + => DateTime.Now.Year;" + ] + }; +} diff --git a/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.cs b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.cs new file mode 100644 index 0000000000000..5dbab97724c98 --- /dev/null +++ b/src/Features/CSharpTest/Copilot/CSharpImplementNotImplementedExceptionFixProviderTests.cs @@ -0,0 +1,718 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Copilot; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.DocumentationComments; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.QuickInfo; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.Copilot.UnitTests; + +using VerifyCS = CSharpCodeFixVerifier< + CSharpImplementNotImplementedExceptionDiagnosticAnalyzer, + CSharpImplementNotImplementedExceptionFixProvider>; + +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.CopilotImplementNotImplementedException)] +public sealed partial class CSharpImplementNotImplementedExceptionFixProviderTests +{ + [Fact] + public async Task FixAll_ParseSuccessfully() + { + await new CustomCompositionCSharpTest + { + CodeFixTestBehaviors = CodeFixTestBehaviors.SkipLocalDiagnosticCheck, + TestCode = """ + using System; + using System.Threading.Tasks; + using System.Linq; + + public class MathService : IMathService + { + public int {|IDE3000:Add|}(int a, int b) + { + {|IDE3000:throw new NotImplementedException("Add method not implemented");|} + } + + public int {|IDE3000:Subtract|}(int a, int b) => {|IDE3000:throw new NotImplementedException("Subtract method not implemented")|}; + + public int {|IDE3000:Multiply|}(int a, int b) { + {|IDE3000:throw new NotImplementedException("Multiply method not implemented");|} + } + + public double {|IDE3000:Divide|}(int a, int b) + { + {|IDE3000:throw new NotImplementedException("Divide method not implemented");|} + } + + public double {|IDE3000:CalculateSquareRoot|}(double number) => {|IDE3000:throw new NotImplementedException("CalculateSquareRoot method not implemented")|}; + + public int {|IDE3000:Factorial|}(int number) + { + {|IDE3000:throw new NotImplementedException("Factorial method not implemented");|} + } + + public int ConstantValue => {|IDE3000:throw new NotImplementedException("Property not implemented")|}; + + public {|IDE3000:MathService|}() + { + {|IDE3000:throw new NotImplementedException("Constructor not implemented");|} + } + + ~{|IDE3000:MathService|}() + { + {|IDE3000:throw new NotImplementedException("Destructor not implemented");|} + } + + public event EventHandler MyEvent + { + {|IDE3000:add|} { {|IDE3000:throw new NotImplementedException("Event add not implemented");|} } + {|IDE3000:remove|} { {|IDE3000:throw new NotImplementedException("Event remove not implemented");|} } + } + + public static MathService operator {|IDE3000:+|}(MathService a, MathService b) + { + {|IDE3000:throw new NotImplementedException("Operator not implemented");|} + } + } + + public interface IMathService + { + int Add(int a, int b); + int Subtract(int a, int b); + int Multiply(int a, int b); + double Divide(int a, int b); + double CalculateSquareRoot(double number); + int Factorial(int number); + int ConstantValue { get; } + event EventHandler MyEvent; + } + """, + FixedCode = """ + using System; + using System.Threading.Tasks; + using System.Linq; + + public class MathService : IMathService + { + public int Add(int a, int b) + { + return a + b; + } + + public int Subtract(int a, int b) => a - b; + + public int Multiply(int a, int b) + { + return a * b; + } + + public double Divide(int a, int b) + { + if (b == 0) throw new DivideByZeroException("Division by zero is not allowed"); + return (double)a / b; + } + + public double CalculateSquareRoot(double number) => Math.Sqrt(number); + + public int Factorial(int number) + { + if (number < 0) throw new ArgumentException("Number must be non-negative", nameof(number)); + return number == 0 ? 1 : number * Factorial(number - 1); + } + + public int ConstantValue => 42; + + public MathService() + { + // Constructor implementation + } + + ~MathService() + { + // Destructor implementation + } + + public event EventHandler MyEvent + { + add { /* Event add implementation */ } + remove { /* Event remove implementation */ } + } + + public static MathService operator +(MathService a, MathService b) + { + return new MathService(); // Operator implementation + } + } + + public interface IMathService + { + int Add(int a, int b); + int Subtract(int a, int b); + int Multiply(int a, int b); + double Divide(int a, int b); + double CalculateSquareRoot(double number); + int Factorial(int number); + int ConstantValue { get; } + event EventHandler MyEvent; + } + """, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + } + .WithMockCopilotService(copilotService => + { + copilotService.SetupFixAll = (Document document, ImmutableDictionary> memberReferences, CancellationToken cancellationToken) => + { + // Create a map of method/property implementations + var implementationMap = new Dictionary + { + ["Add"] = "public int Add(int a, int b)\n{\n return a + b;\n}\n", + ["Subtract"] = "public int Subtract(int a, int b) => a - b;\n", + ["Multiply"] = "public int Multiply(int a, int b)\n{\n return a * b;\n}\n", + ["Divide"] = "public double Divide(int a, int b)\n{\n if (b == 0) throw new DivideByZeroException(\"Division by zero is not allowed\");\n return (double)a / b;\n}\n", + ["CalculateSquareRoot"] = "public double CalculateSquareRoot(double number) => Math.Sqrt(number);\n", + ["Factorial"] = "public int Factorial(int number)\n{\n if (number < 0) throw new ArgumentException(\"Number must be non-negative\", nameof(number));\n return number == 0 ? 1 : number * Factorial(number - 1);\n}\n", + ["ConstantValue"] = "public int ConstantValue => 42;\n", + ["MathService"] = "public MathService()\n{\n // Constructor implementation\n}\n", + ["~MathService"] = "~MathService()\n{\n // Destructor implementation\n}\n", + ["MyEvent"] = "public event EventHandler MyEvent\n{\n add { /* Event add implementation */ }\n remove { /* Event remove implementation */ }\n}\n", + ["operator +"] = "public static MathService operator +(MathService a, MathService b)\n{\n return new MathService(); // Operator implementation\n}\n", + }; + return BuildResult(memberReferences, implementationMap); + }; + }) + .RunAsync(); + } + + [Theory] + [InlineData("Failed to receive implementation from Copilot service")] + [InlineData("Generated implementation doesn't match the original method signature.")] + [InlineData("The generated implementation isn't a valid method or property.")] + public async Task NullReplacementNode_MethodHasCommentsInVariousForms_PrintsMessageAsComment(string copilotErrorMessage) + { + await new CustomCompositionCSharpTest + { + CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipLocalDiagnosticCheck, + TestCode = """ + using System; + using System.Threading.Tasks; + + public class DataService : IDataService + { + public void {|IDE3000:AddData|}(string data) + { + {|IDE3000:throw new NotImplementedException("AddData method not implemented");|} + } + + public string {|IDE3000:GetData|}(int id) => {|IDE3000:throw new NotImplementedException()|}; + + /* Updates the data for a given ID */ + public void UpdateData(int id, string data) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException("UpdateData method not implemented");|} + } + + // Deletes data by ID + public void DeleteData(int id) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException();|} + } + + /// + /// Saves changes asynchronously + /// + /// A task representing the save operation + public Task {|IDE3000:SaveChangesAsync|}() + { + {|IDE3000:throw new NotImplementedException("SaveChangesAsync method not implemented");|} + } + + public int DataCount => {|IDE3000:throw new NotImplementedException("Property not implemented")|}; + } + + public interface IDataService + { + void AddData(string data); + string GetData(int id); + void UpdateData(int id, string data); + void DeleteData(int id); + Task SaveChangesAsync(); + int DataCount { get; } + } + """, + FixedCode = $$""" + using System; + using System.Threading.Tasks; + + public class DataService : IDataService + { + /* {{copilotErrorMessage}} */ + public void {|IDE3000:AddData|}(string data) + { + {|IDE3000:throw new NotImplementedException("AddData method not implemented");|} + } + + public string {|IDE3000:GetData|}(int id) => {|IDE3000:throw new NotImplementedException()|}; + + /* Updates the data for a given ID */ + public void UpdateData(int id, string data) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException("UpdateData method not implemented");|} + } + + // Deletes data by ID + public void DeleteData(int id) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException();|} + } + + /// + /// Saves changes asynchronously + /// + /// A task representing the save operation + public Task {|IDE3000:SaveChangesAsync|}() + { + {|IDE3000:throw new NotImplementedException("SaveChangesAsync method not implemented");|} + } + + public int DataCount => {|IDE3000:throw new NotImplementedException("Property not implemented")|}; + } + + public interface IDataService + { + void AddData(string data); + string GetData(int id); + void UpdateData(int id, string data); + void DeleteData(int id); + Task SaveChangesAsync(); + int DataCount { get; } + } + """, + BatchFixedCode = $$""" + using System; + using System.Threading.Tasks; + + public class DataService : IDataService + { + /* {{copilotErrorMessage}} */ + public void {|IDE3000:AddData|}(string data) + { + {|IDE3000:throw new NotImplementedException("AddData method not implemented");|} + } + + /* {{copilotErrorMessage}} */ + public string {|IDE3000:GetData|}(int id) => {|IDE3000:throw new NotImplementedException()|}; + + /* Updates the data for a given ID */ + /* {{copilotErrorMessage}} */ + public void UpdateData(int id, string data) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException("UpdateData method not implemented");|} + } + + // Deletes data by ID + /* {{copilotErrorMessage}} */ + public void DeleteData(int id) + { + if (id <= 0) throw new ArgumentException("ID must be greater than zero", nameof(id)); + {|IDE3000:throw new NotImplementedException();|} + } + + /* {{copilotErrorMessage}} */ + /// + /// Saves changes asynchronously + /// + /// A task representing the save operation + public Task {|IDE3000:SaveChangesAsync|}() + { + {|IDE3000:throw new NotImplementedException("SaveChangesAsync method not implemented");|} + } + + /* {{copilotErrorMessage}} */ + public int DataCount => {|IDE3000:throw new NotImplementedException("Property not implemented")|}; + } + + public interface IDataService + { + void AddData(string data); + string GetData(int id); + void UpdateData(int id, string data); + void DeleteData(int id); + Task SaveChangesAsync(); + int DataCount { get; } + } + """, + FixedState = + { + MarkupHandling = MarkupMode.Allow, + }, + BatchFixedState = + { + MarkupHandling = MarkupMode.Allow, + }, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + } + .WithMockCopilotService(copilotService => + { + copilotService.PrepareUsingSingleFakeResult = new() + { + ReplacementNode = null, + Message = copilotErrorMessage, + }; + }) + .RunAsync(); + } + + [Fact] + public async Task HandleInvalidCode_SuggestsAsComment() + { + await new CustomCompositionCSharpTest + { + CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipLocalDiagnosticCheck, + TestCode = """ + using System; + + class C + { + void {|IDE3000:M|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + FixedCode = """ + using System; + + class C + { + /* The generated implementation isn't a valid method or property: + using System; + class C + { + void M() + { + throw new NotImplementedException(); + } + } */ + void {|IDE3000:M|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + FixedState = + { + MarkupHandling = MarkupMode.Allow, + }, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + } + .WithMockCopilotService(copilotService => + { + var replacement = """ + using System; + class C + { + void M() + { + throw new NotImplementedException(); + } + } + """; + copilotService.PrepareUsingSingleFakeResult = new() + { + ReplacementNode = SyntaxFactory.ParseCompilationUnit(replacement), + Message = $"The generated implementation isn't a valid method or property:{Environment.NewLine}{replacement}", + }; + }) + .RunAsync(); + } + + [Fact] + public async Task ReplacementNode_Null_NotifiesWithComment() + { + await TestHandlesInvalidReplacementNode( + new() + { + ReplacementNode = null, + Message = "Custom Error Message", + }); + } + + [Theory] + [InlineData("class MyClass { }", typeof(ClassDeclarationSyntax))] + [InlineData("struct MyStruct { }", typeof(StructDeclarationSyntax))] + [InlineData("interface IMyInterface { }", typeof(InterfaceDeclarationSyntax))] + [InlineData("enum MyEnum { Value1, Value2 }", typeof(EnumDeclarationSyntax))] + [InlineData("delegate void MyDelegate();", typeof(DelegateDeclarationSyntax))] + [InlineData("int myField;", typeof(FieldDeclarationSyntax))] + [InlineData("event EventHandler MyEvent;", typeof(EventFieldDeclarationSyntax))] + [InlineData("record MyRecord { }", typeof(RecordDeclarationSyntax))] + public async Task TestInvalidNodeReplacement(string syntax, Type type) + { + await TestHandlesInvalidReplacementNode( + new() + { + ReplacementNode = SyntaxFactory.ParseMemberDeclaration(syntax), + Message = $"Copilot response is of type {type}, but expected method or property", + }) + .ConfigureAwait(false); + } + + [Theory] + [InlineData(" ")] + [InlineData("")] + public async Task TestHandlesEmptyReplacementNode(string emptyReplacement) + { + await TestHandlesInvalidReplacementNode( + new() + { + ReplacementNode = SyntaxFactory.ParseMemberDeclaration(emptyReplacement), + Message = "Custom Error Message", + }) + .ConfigureAwait(false); + } + + private static async Task TestHandlesInvalidReplacementNode(ImplementationDetails implementationDetails) + { + Assumes.False(string.IsNullOrWhiteSpace(implementationDetails.Message)); + await new CustomCompositionCSharpTest + { + CodeFixTestBehaviors = CodeFixTestBehaviors.FixOne | CodeFixTestBehaviors.SkipLocalDiagnosticCheck, + TestCode = """ + using System; + + class C + { + void {|IDE3000:M|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + FixedCode = $$""" + using System; + + class C + { + /* {{implementationDetails.Message}} */ + void {|IDE3000:M|}() + { + {|IDE3000:throw new NotImplementedException();|} + } + } + """, + FixedState = + { + MarkupHandling = MarkupMode.Allow, + }, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.Net.Net60, + } + .WithMockCopilotService(copilotService => + { + copilotService.PrepareUsingSingleFakeResult = implementationDetails; + }) + .RunAsync(); + } + + private static ImmutableDictionary BuildResult(ImmutableDictionary> memberReferences, Dictionary implementationMap) + { + // Process each member reference and create implementation details + var resultsBuilder = ImmutableDictionary.CreateBuilder(); + foreach (var memberReference in memberReferences) + { + var memberNode = memberReference.Key; + + // Get the identifier based on node type + var identifier = memberNode switch + { + MethodDeclarationSyntax method => method.Identifier.Text, + PropertyDeclarationSyntax property => property.Identifier.Text, + ConstructorDeclarationSyntax constructor => constructor.Identifier.Text, + DestructorDeclarationSyntax destructor => destructor.TildeToken.Text + destructor.Identifier.Text, + EventDeclarationSyntax @event => @event.Identifier.Text, + OperatorDeclarationSyntax @operator => "operator " + @operator.OperatorToken.Text, + _ => string.Empty + }; + + // Look up implementation in our map + Assumes.True(implementationMap.TryGetValue(identifier, out var implementation)); + resultsBuilder.Add( + memberNode, + new ImplementationDetails + { + ReplacementNode = SyntaxFactory.ParseMemberDeclaration(implementation), + }); + } + + return resultsBuilder.ToImmutable(); + } + + private class CustomCompositionCSharpTest : VerifyCS.Test + { + private TestComposition? _testComposition; + private TestWorkspace? _testWorkspace; + private Action? _copilotServiceSetupAction; + + protected override Task CreateWorkspaceImplAsync() + { + _testComposition = FeaturesTestCompositions.Features + .AddParts([typeof(TestCopilotOptionsService), typeof(TestCopilotCodeAnalysisService)]); + _testWorkspace = new TestWorkspace(_testComposition); + + // Trigger the action if it's set + _copilotServiceSetupAction?.Invoke(GetCopilotService(_testWorkspace)); + return Task.FromResult(_testWorkspace); + } + + public CustomCompositionCSharpTest WithMockCopilotService(Action setup) + { + _copilotServiceSetupAction = setup; + + // If _testWorkspace is already set, trigger the action immediately + if (_testWorkspace != null) + { + setup(GetCopilotService(_testWorkspace)); + } + + return this; + } + + private static TestCopilotCodeAnalysisService GetCopilotService(TestWorkspace testWorkspace) + { + var copilotService = testWorkspace.Services.GetLanguageServices(LanguageNames.CSharp) + .GetRequiredService() as TestCopilotCodeAnalysisService; + Assert.NotNull(copilotService); + return copilotService; + } + } + + [ExportLanguageService(typeof(ICopilotOptionsService), LanguageNames.CSharp), Shared, PartNotDiscoverable] + private sealed class TestCopilotOptionsService : ICopilotOptionsService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public TestCopilotOptionsService() { } + + public Task IsRefineOptionEnabledAsync() + => Task.FromResult(true); + + public Task IsCodeAnalysisOptionEnabledAsync() + => Task.FromResult(true); + + public Task IsOnTheFlyDocsOptionEnabledAsync() + => Task.FromResult(true); + + public Task IsGenerateDocumentationCommentOptionEnabledAsync() + => Task.FromResult(true); + + public Task IsImplementNotImplementedExceptionEnabledAsync() + => Task.FromResult(true); + } + + [ExportLanguageService(typeof(ICopilotCodeAnalysisService), LanguageNames.CSharp), Shared, PartNotDiscoverable] + private sealed class TestCopilotCodeAnalysisService : ICopilotCodeAnalysisService + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public TestCopilotCodeAnalysisService() + { + } + + public Func>, CancellationToken, ImmutableDictionary>? SetupFixAll { get; internal set; } + + public ImplementationDetails? PrepareUsingSingleFakeResult { get; internal set; } + + public Task AnalyzeDocumentAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task> GetAvailablePromptTitlesAsync(Document document, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task> GetCachedDocumentDiagnosticsAsync(Document document, TextSpan? span, ImmutableArray promptTitles, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task IsAvailableAsync(CancellationToken cancellationToken) + => Task.FromResult(true); + + public Task IsFileExcludedAsync(string filePath, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + Task<(Dictionary? responseDictionary, bool isQuotaExceeded)> ICopilotCodeAnalysisService.GetDocumentationCommentAsync(DocumentationCommentProposal proposal, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + public Task> ImplementNotImplementedExceptionsAsync( + Document document, + ImmutableDictionary> methodOrProperties, + CancellationToken cancellationToken) + { + if (SetupFixAll != null) + { + return Task.FromResult(SetupFixAll.Invoke(document, methodOrProperties, cancellationToken)); + } + + if (PrepareUsingSingleFakeResult != null) + { + return Task.FromResult(CreateSingleNodeResult(methodOrProperties, PrepareUsingSingleFakeResult)); + } + + return Task.FromResult(ImmutableDictionary.Empty); + } + + private static ImmutableDictionary CreateSingleNodeResult( + ImmutableDictionary> methodOrProperties, + ImplementationDetails implementationDetails) + { + var resultsBuilder = ImmutableDictionary.CreateBuilder(); + foreach (var methodOrProperty in methodOrProperties) + { + resultsBuilder.Add(methodOrProperty.Key, implementationDetails); + } + + return resultsBuilder.ToImmutable(); + } + + public Task IsImplementNotImplementedExceptionsAvailableAsync(CancellationToken cancellationToken) + { + return Task.FromResult(true); + } + + public Task GetOnTheFlyDocsPromptAsync(OnTheFlyDocsInfo onTheFlyDocsInfo, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + public Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseAsync(string prompt, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs b/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs index 778bff54e0d09..8bacb6566843e 100644 --- a/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs +++ b/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -14,10 +13,8 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Emit; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests; diff --git a/src/Features/CSharpTest/EditAndContinue/LineEditTests.cs b/src/Features/CSharpTest/EditAndContinue/LineEditTests.cs index df93a862ec0f0..30fed39210f06 100644 --- a/src/Features/CSharpTest/EditAndContinue/LineEditTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/LineEditTests.cs @@ -6,7 +6,6 @@ #pragma warning disable IDE0055 // Collection expression formatting using System; -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/Features/CSharpTest/EditAndContinue/SyntaxComparerTests.cs b/src/Features/CSharpTest/EditAndContinue/SyntaxComparerTests.cs index 2be0c587ea5d7..7be8051114b13 100644 --- a/src/Features/CSharpTest/EditAndContinue/SyntaxComparerTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/SyntaxComparerTests.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index c71f29a688aa4..ee295a7dc131d 100644 --- a/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -10257,6 +10257,17 @@ public void Method_Partial_DeleteImplementation() ]); } + [Fact] + public void Method_Partial_DeleteDefinition() + { + var src1 = "partial class C { partial void F(); }"; + var src2 = "partial class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics(); + } + [Fact] public void Method_Partial_DeleteBoth() { @@ -10269,8 +10280,7 @@ public void Method_Partial_DeleteBoth() EditAndContinueValidation.VerifySemantics( [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)], [ - DocumentResults( - semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")]), + DocumentResults(), DocumentResults( semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")]), ]); @@ -11830,11 +11840,7 @@ public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_ SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")), SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")), ]), - DocumentResults( - semanticEdits: - [ - SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"), - ]), + DocumentResults(), ], capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType); } @@ -19301,12 +19307,7 @@ public void Property_Partial_DeleteBoth() EditAndContinueValidation.VerifySemantics( [GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)], [ - DocumentResults( - semanticEdits: - [ - SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"), - SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C") - ]), + DocumentResults(), DocumentResults( semanticEdits: [ diff --git a/src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs b/src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs index 0f78f87fde8ce..8439430ace568 100644 --- a/src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs +++ b/src/Features/CSharpTest/FullyQualify/FullyQualifyTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.CodeFixes.FullyQualify; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; diff --git a/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs b/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs index c958065347fcf..ae11956e5cffa 100644 --- a/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs +++ b/src/Features/CSharpTest/GenerateVariable/GenerateVariableTests.cs @@ -11150,4 +11150,122 @@ class Test } """); } + + [Fact] + public async Task TestNullConditionalAssignment1() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + void Method(Class c) + { + c?.[|goo|] = 1; + } + } + """, + """ + class Class + { + private int goo; + + void Method(Class c) + { + c?.goo = 1; + } + } + """); + } + + [Fact] + public async Task TestNullConditionalAssignment2() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + void Method(Class c) + { + c?.[|Goo|] = 1; + } + } + """, + """ + class Class + { + public int Goo { get; private set; } + + void Method(Class c) + { + c?.Goo = 1; + } + } + """); + } + + [Fact] + public async Task TestNullConditionalAssignment3() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + void Method(D c) + { + c?.[|Goo|] = 1; + } + } + + class D + { + } + """, + """ + class Class + { + void Method(D c) + { + c?.Goo = 1; + } + } + + class D + { + public int Goo { get; internal set; } + } + """); + } + + [Fact] + public async Task TestNullConditionalAssignment4() + { + await TestInRegularAndScriptAsync( + """ + class Class + { + void Method(D? c) + { + c?.[|Goo|] = 1; + } + } + + struct D + { + } + """, + """ + class Class + { + void Method(D? c) + { + c?.Goo = 1; + } + } + + struct D + { + public int Goo { get; internal set; } + } + """); + } } diff --git a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs index 9053cd70d1bbb..da2c5e4fc8822 100644 --- a/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs +++ b/src/Features/CSharpTest/IntroduceVariable/IntroduceVariableTests.cs @@ -4375,12 +4375,10 @@ private class Numbers {} } """; - OptionsCollection optionsCollection = new(GetLanguage()) + await TestInRegularAndScriptAsync(code, expected, options: new(GetLanguage()) { { CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, new CodeStyleOption2(true, NotificationOption2.Warning) }, - }; - - await TestInRegularAndScriptAsync(code, expected, options: optionsCollection); + }); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77276")] @@ -4425,6 +4423,57 @@ public sealed class Class1 }); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77428")] + public async Task TestIntroduceLocalWithTargetTypedNew1_CSharp8() + { + var code = + """ + using System; + class SampleType + { + public SampleType() + { + int sum = Sum([|new Numbers()|]); + } + + private int Sum(Numbers numbers) + { + return 42; + } + + private class Numbers {} + } + """; + + var expected = + """ + using System; + class SampleType + { + public SampleType() + { + Numbers {|Rename:numbers|} = new Numbers(); + int sum = Sum(numbers); + } + + private int Sum(Numbers numbers) + { + return 42; + } + + private class Numbers {} + } + """; + + await TestInRegularAndScriptAsync( + code, expected, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8), + options: new(GetLanguage()) + { + { CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, new CodeStyleOption2(true, NotificationOption2.Warning) }, + }); + } + [Fact] public async Task TestIntroduceFieldInExpressionBodiedPropertyGetter() { diff --git a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs index 0c01d91ff3d31..3765e7675d292 100644 --- a/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs +++ b/src/Features/CSharpTest/SemanticSearch/CSharpSemanticSearchServiceTests.cs @@ -3,12 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.CodeDom; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -417,4 +415,51 @@ static ISymbol F(ISymbol s) $" at ")); } + + /// + /// Checks that flow pass handles semantic query code end-to-end + /// (specifically, module cancellation and stack overflow instrumentation). + /// + [ConditionalFact(typeof(CoreClrOnly))] + public async Task FlowPass() + { + using var workspace = TestWorkspace.Create(DefaultWorkspaceXml, composition: FeaturesTestCompositions.Features); + + var solution = workspace.CurrentSolution; + + var service = solution.Services.GetRequiredLanguageService(LanguageNames.CSharp); + + var query = """ + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + static IEnumerable Find(IMethodSymbol method) + { + var syntaxReference = method.DeclaringSyntaxReferences.FirstOrDefault(); + if (syntaxReference != null) + { + while (true) + { + var syntaxNode = syntaxReference.GetSyntax() as MethodDeclarationSyntax; + if (syntaxNode != null) + { + yield return method; + } + + break; + } + } + } + """; + + var results = new List(); + var observer = new MockSemanticSearchResultsObserver() { OnDefinitionFoundImpl = results.Add }; + var traceSource = new TraceSource("test"); + + var options = workspace.GlobalOptions.GetClassificationOptionsProvider(); + var result = await service.ExecuteQueryAsync(solution, query, s_referenceAssembliesDir, observer, options, traceSource, CancellationToken.None); + + Assert.Null(result.ErrorMessage); + AssertEx.Equal(["void C.VisibleMethod(int)"], results.Select(Inspect)); + } } diff --git a/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs b/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs index acb29d12abff4..3cf32e0fb904d 100644 --- a/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs +++ b/src/Features/CSharpTest/SemanticSearch/Mocks/MockSemanticSearchResultsObserver.cs @@ -43,11 +43,5 @@ public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, Cance OnUserCodeExceptionImpl?.Invoke(exception); return ValueTaskFactory.CompletedTask; } - - public ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) - { - OnCompilationFailureImpl?.Invoke(errors); - return ValueTaskFactory.CompletedTask; - } } diff --git a/src/Features/CSharpTest/Snippets/CSharpProprSnippetProviderTests.cs b/src/Features/CSharpTest/Snippets/CSharpProprSnippetProviderTests.cs index 5f75aef2b5a01..e905241afd8f4 100644 --- a/src/Features/CSharpTest/Snippets/CSharpProprSnippetProviderTests.cs +++ b/src/Features/CSharpTest/Snippets/CSharpProprSnippetProviderTests.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Snippets; diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs index 5011b0a17dae3..df68a60fc7121 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -83,207 +83,310 @@ private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_Disabl public async Task TestUpdatePropertyIfPropertyWantsBlockAndAccessorWantsExpression() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody2() { await TestMissingAsync( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - return [||]Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo + { + get + { + return [||]Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody2() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - return [||]Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo + { + get + { + return [||]Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - return [||]Bar(); - } - } -}", -@"class C -{ - int Goo - { - get => Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo + { + get + { + return [||]Bar(); + } + } + } + """, + """ + class C + { + int Goo + { + get => Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferExpressionBodyForPropertyIfPropertyAndAccessorBothPreferExpressions() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - return [||]Bar(); - } - } -}", -@"class C -{ - int Goo => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo + { + get + { + return [||]Bar(); + } + } + } + """, + """ + class C + { + int Goo => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - int Goo { get => [||]Bar(); } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo { get => [||]Bar(); } -}", + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody2() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo { get => [||]Bar(); } -}", + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestOfferedForPropertyIfUserPrefersBlockPropertiesAndHasBlockProperty() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo { get => [||]Bar(); } -}", + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestOfferForPropertyIfPropertyPrefersBlockButCouldBecomeExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo { get => [||]Bar(); } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20350")] public async Task TestAccessorListFormatting() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo { get => [||]Bar(); } -}", -@"class C -{ - int Goo + """ + class C + { + int Goo { get => [||]Bar(); } + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideExpressionBody() { - get - { - return Bar(); - } + await TestInRegularAndScript1Async( + """ + class C + { + int Goo + { + get + { + return [|Bar()|]; + } + } + } + """, + """ + class C + { + int Goo + { + get => Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideExpressionBody() + { + await TestMissingAsync( + """ + class C + { + int Goo + { + get + { + return [|Bar(); + } + }|] + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs index b6d1855b4d4e7..8fa5b0c2b58a0 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs @@ -38,103 +38,170 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - public C() - { - [||]Bar(); - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + public C() + { + [||]Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public C() - { - [||]Bar(); - } -}", -@"class C -{ - public C() => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + public C() + { + [||]Bar(); + } + } + """, + """ + class C + { + public C() => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public C() - { - [||]Bar(); - } -}", -@"class C -{ - public C() => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public C() + { + [||]Bar(); + } + } + """, + """ + class C + { + public C() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - public C() - { - return () => { [||] }; - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public C() + { + return () => { [||] }; + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - public C() => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public C() => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public C() => [||]Bar(); -}", -@"class C -{ - public C() - { - Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + public C() => [||]Bar(); + } + """, + """ + class C + { + public C() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public C() => [||]Bar(); -}", -@"class C -{ - public C() + """ + class C + { + public C() => [||]Bar(); + } + """, + """ + class C + { + public C() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideExpressionBody() { - Bar(); + await TestInRegularAndScript1Async( + """ + class C + { + public C() + { + [|Bar()|]; + } + } + """, + """ + class C + { + public C() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideExpressionBody() + { + await TestMissingAsync( + """ + class C + { + public C() + { + [|Bar(); + } + }|] + """, + parameters: new TestParameters(options: UseBlockBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs index 133ea945e407a..5b5c446a2e846 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs @@ -38,103 +38,170 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - public static implicit operator bool(C c1) - { - [||]Bar(); - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + public static implicit operator bool(C c1) + { + [||]Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static implicit operator bool(C c1) - { - [||]Bar(); - } -}", -@"class C -{ - public static implicit operator bool(C c1) => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + public static implicit operator bool(C c1) + { + [||]Bar(); + } + } + """, + """ + class C + { + public static implicit operator bool(C c1) => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static implicit operator bool(C c1) - { - [||]Bar(); - } -}", -@"class C -{ - public static implicit operator bool(C c1) => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static implicit operator bool(C c1) + { + [||]Bar(); + } + } + """, + """ + class C + { + public static implicit operator bool(C c1) => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - public static implicit operator bool(C c1) - { - return () => { [||] }; - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static implicit operator bool(C c1) + { + return () => { [||] }; + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - public static implicit operator bool(C c1) => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static implicit operator bool(C c1) => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static implicit operator bool(C c1) => [||]Bar(); -}", -@"class C -{ - public static implicit operator bool(C c1) - { - return Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + public static implicit operator bool(C c1) => [||]Bar(); + } + """, + """ + class C + { + public static implicit operator bool(C c1) + { + return Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static implicit operator bool(C c1) => [||]Bar(); -}", -@"class C -{ - public static implicit operator bool(C c1) + """ + class C + { + public static implicit operator bool(C c1) => [||]Bar(); + } + """, + """ + class C + { + public static implicit operator bool(C c1) + { + return Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - return Bar(); + await TestInRegularAndScript1Async( + """ + class C + { + public static implicit operator bool(C c1) + { + [|Bar()|]; + } + } + """, + """ + class C + { + public static implicit operator bool(C c1) => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingAsync( + """ + class C + { + public static implicit operator bool(C c1) + { + [|Bar(); + } + }|] + """, + parameters: new TestParameters(options: UseBlockBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs index 4e49850fffdf7..70d744f4865db 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs @@ -39,121 +39,194 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - int this[int i] - { - get - { - [||]return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + int this[int i] + { + get + { + [||]return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int this[int i] - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int this[int i] => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + int this[int i] + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int this[int i] => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int this[int i] - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int this[int i] => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + int this[int i] + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int this[int i] => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - Action Goo[int i] - { - get - { - return () => { [||] }; - } - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + Action Goo[int i] + { + get + { + return () => { [||] }; + } + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - int this[int i] => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + int this[int i] => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int this[int i] => [||]Bar(); -}", -@"class C -{ - int this[int i] - { - get - { - return Bar(); - } - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + int this[int i] => [||]Bar(); + } + """, + """ + class C + { + int this[int i] + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20363")] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int this[int i] => [||]Bar(); -}", -@"class C -{ - int this[int i] + """ + class C + { + int this[int i] => [||]Bar(); + } + """, + """ + class C + { + int this[int i] + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - get - { - return Bar(); - } + await TestInRegularAndScript1Async( + """ + class C + { + int this[int i] + { + get + { + [|return Bar()|]; + } + } + } + """, + """ + class C + { + int this[int i] => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingAsync( + """ + class C + { + int this[int i] + { + get + { + [|return Bar(); + } + } + }|] + """, + parameters: new TestParameters(options: UseBlockBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs index eba9f49b0322c..1be89b9af754b 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForLocalFunctionsRefactoringTests.cs @@ -38,120 +38,193 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - void Goo() - { - void Bar() - { - [||]Test(); - } - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + void Goo() + { + void Bar() + { + [||]Test(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - void Bar() - { - [||]Test(); - } - } -}", -@"class C -{ - void Goo() - { - void Bar() => Test(); - } -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + void Goo() + { + void Bar() + { + [||]Test(); + } + } + } + """, + """ + class C + { + void Goo() + { + void Bar() => Test(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - void Bar() - { - [||]Test(); - } - } -}", -@"class C -{ - void Goo() - { - void Bar() => Test(); - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + void Goo() + { + void Bar() + { + [||]Test(); + } + } + } + """, + """ + class C + { + void Goo() + { + void Bar() => Test(); + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - void Goo() - { - void Bar() => [||]Test(); - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + void Goo() + { + void Bar() => [||]Test(); + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - void Bar() => [||]Test(); - } -}", -@"class C -{ - void Goo() - { - void Bar() - { - Test(); - } - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + void Goo() + { + void Bar() => [||]Test(); + } + } + """, + """ + class C + { + void Goo() + { + void Bar() + { + Test(); + } + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - void Bar() => [||]Test(); + """ + class C + { + void Goo() + { + void Bar() => [||]Test(); + } + } + """, + """ + class C + { + void Goo() + { + void Bar() + { + Test(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } -}", -@"class C -{ - void Goo() + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - void Bar() - { - Test(); - } + await TestInRegularAndScript1Async( + """ + class C + { + void Goo() + { + void Bar() + { + [|Test()|]; + } + } + } + """, + """ + class C + { + void Goo() + { + void Bar() => Test(); + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingAsync( + """ + class C + { + void Goo() + { + void Bar() + { + [|Test(); + } + }|] + } + """, + parameters: new TestParameters(options: UseBlockBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs index b1a4b95308780..a967266cb9f8d 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs @@ -37,204 +37,332 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - void Goo() - { - [||]Bar(); - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + void Goo() + { + [||]Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - [||]Bar(); - } -}", -@"class C -{ - void Goo() => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + void Goo() + { + [||]Bar(); + } + } + """, + """ + class C + { + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() - { - [||]Bar(); - } -}", -@"class C -{ - void Goo() => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + void Goo() + { + [||]Bar(); + } + } + """, + """ + class C + { + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - Action Goo() - { - return () => { [||] }; - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + Action Goo() + { + return () => { [||] }; + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - void Goo() => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + void Goo() => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() => [||]Bar(); -}", -@"class C -{ - void Goo() - { - Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + void Goo() => [||]Bar(); + } + """, + """ + class C + { + void Goo() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - void Goo() => [||]Bar(); -}", -@"class C -{ - void Goo() - { - Bar(); - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + void Goo() => [||]Bar(); + } + """, + """ + class C + { + void Goo() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25501")] public async Task TestOfferedAtStartOfMethod() { await TestInRegularAndScript1Async( -@"class C -{ - [||]void Goo() - { - Bar(); - } -}", -@"class C -{ - void Goo() => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + [||]void Goo() + { + Bar(); + } + } + """, + """ + class C + { + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25501")] public async Task TestOfferedBeforeMethodOnSameLine() { await TestInRegularAndScript1Async( -@"class C -{ -[||] void Goo() - { - Bar(); - } -}", -@"class C -{ - void Goo() => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + [||] void Goo() + { + Bar(); + } + } + """, + """ + class C + { + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25501")] public async Task TestOfferedBeforeAttributes() { await TestInRegularAndScript1Async( -@"class C -{ - [||][A] - void Goo() - { - Bar(); - } -}", -@"class C -{ - [A] - void Goo() => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + [||][A] + void Goo() + { + Bar(); + } + } + """, + """ + class C + { + [A] + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25501")] public async Task TestNotOfferedBeforeComments() { await TestMissingInRegularAndScriptAsync( -@"class C -{ - [||]/// - void Goo() - { - Bar(); - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + [||]/// + void Goo() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25501")] public async Task TestNotOfferedInComments() { await TestMissingInRegularAndScriptAsync( -@"class C -{ - /// [||] - void Goo() - { - Bar(); - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + /// [||] + void Goo() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/53532")] public async Task TestTriviaOnArrow1() { await TestInRegularAndScript1Async( -@"class C -{ - void M() - // Test - [||]=> Console.WriteLine(); -}", -@"class C -{ - void M() + """ + class C + { + void M() + // Test + [||]=> Console.WriteLine(); + } + """, + """ + class C + { + void M() + { + // Test + Console.WriteLine(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - // Test - Console.WriteLine(); + await TestInRegularAndScript1Async( + """ + class C + { + void Goo() + { + [|Bar()|]; + } + } + """, + """ + class C + { + void Goo() => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingInRegularAndScriptAsync( + """ + class C + { + void Goo() + { + [|Bar(); + } + }|] + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestOfferedWithSelectionInsideExpressionBody() + { + await TestInRegularAndScript1Async( + """ + class C + { + void Goo() => [|Bar()|]; + } + """, + """ + class C + { + void Goo() + { + Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideExpressionBody() + { + await TestMissingInRegularAndScriptAsync( + """ + class C + { + void Goo() => [|Bar(); + }|] + """, + parameters: new TestParameters(options: UseExpressionBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs index efd9fa80f8460..9ecd34ddde838 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs @@ -38,103 +38,170 @@ private OptionsCollection UseBlockBodyDisabledDiagnostic public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - public static bool operator +(C c1, C c2) - { - [||]Bar(); - } -}", parameters: new TestParameters(options: UseExpressionBody)); + """ + class C + { + public static bool operator +(C c1, C c2) + { + [||]Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static bool operator +(C c1, C c2) - { - [||]Bar(); - } -}", -@"class C -{ - public static bool operator +(C c1, C c2) => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); + """ + class C + { + public static bool operator +(C c1, C c2) + { + [||]Bar(); + } + } + """, + """ + class C + { + public static bool operator +(C c1, C c2) => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static bool operator +(C c1, C c2) - { - [||]Bar(); - } -}", -@"class C -{ - public static bool operator +(C c1, C c2) => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static bool operator +(C c1, C c2) + { + [||]Bar(); + } + } + """, + """ + class C + { + public static bool operator +(C c1, C c2) => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - public static bool operator +(C c1, C c2) - { - return () => { [||] }; - } -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static bool operator +(C c1, C c2) + { + return () => { [||] }; + } + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - public static bool operator +(C c1, C c2) => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); + """ + class C + { + public static bool operator +(C c1, C c2) => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static bool operator +(C c1, C c2) => [||]Bar(); -}", -@"class C -{ - public static bool operator +(C c1, C c2) - { - return Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); + """ + class C + { + public static bool operator +(C c1, C c2) => [||]Bar(); + } + """, + """ + class C + { + public static bool operator +(C c1, C c2) + { + return Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyDisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - public static bool operator +(C c1, C c2) => [||]Bar(); -}", -@"class C -{ - public static bool operator +(C c1, C c2) + """ + class C + { + public static bool operator +(C c1, C c2) => [||]Bar(); + } + """, + """ + class C + { + public static bool operator +(C c1, C c2) + { + return Bar(); + } + } + """, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - return Bar(); + await TestInRegularAndScript1Async( + """ + class C + { + public static bool operator +(C c1, C c2) + { + [|Bar()|]; + } + } + """, + """ + class C + { + public static bool operator +(C c1, C c2) => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBody)); } -}", parameters: new TestParameters(options: UseExpressionBody)); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingAsync( + """ + class C + { + public static bool operator +(C c1, C c2) + { + [|Bar(); + } + }|] + """, + parameters: new TestParameters(options: UseBlockBody)); } } diff --git a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs index cbd6e5394f8f4..4db58e84f1612 100644 --- a/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs +++ b/src/Features/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs @@ -77,235 +77,336 @@ private OptionsCollection UseBlockBodyForAccessors_BlockBodyForProperties_Disabl public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() { await TestMissingAsync( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesWithoutDiagnosticAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestUpdateAccessorIfAccessWantsBlockAndPropertyWantsExpression() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Goo - { - get => Bar(); - } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int Goo + { + get => Bar(); + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody2() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Goo => Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo + { + get + { + [||]return Bar(); + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestNotOfferedInLambda() { await TestMissingAsync( -@"class C -{ - Action Goo - { - get - { - return () => { [||] }; - } - } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + Action Goo + { + get + { + return () => { [||] }; + } + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() { await TestMissingAsync( -@"class C -{ - int Goo => [||]Bar(); -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody2() { await TestMissingAsync( -@"class C -{ - int Goo => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo => [||]Bar(); -}", -@"class C -{ - int Goo - { - get - { - return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); } [Fact] public async Task TestOfferedIfUserPrefersBlockBodiesWithoutDiagnosticAndInExpressionBody2() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo => [||]Bar(); -}", -@"class C -{ - int Goo - { - get - { - return Bar(); - } - } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties_DisabledDiagnostic)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20363")] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo => [||]Bar(); -}", -@"class C -{ - int Goo - { - get - { - return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); } [Fact] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody2() { await TestInRegularAndScript1Async( -@"class C -{ - int Goo => [||]Bar(); -}", -@"class C -{ - int Goo - { - get - { - return Bar(); - } - } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + """ + class C + { + int Goo => [||]Bar(); + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20360")] public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody_CSharp6() { await TestAsync( -@"class C -{ - int Goo => [||]Bar(); -}", -@"class C -{ - int Goo + """ + class C + { + int Goo => [||]Bar(); + } + """, + """ + class C + { + int Goo + { + get + { + return Bar(); + } + } + } + """, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6), + options: UseExpressionBodyForAccessors_ExpressionBodyForProperties); + } + + [Fact] + public async Task TestOfferedWithSelectionInsideBlockBody() { - get - { - return Bar(); - } + await TestInRegularAndScript1Async( + """ + class C + { + int Goo + { + get + { + [|return Bar()|]; + } + } + } + """, + """ + class C + { + int Goo => Bar(); + } + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } -}", parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp6), -options: UseExpressionBodyForAccessors_ExpressionBodyForProperties); + + [Fact] + public async Task TestNotOfferedWithSelectionOutsideBlockBody() + { + await TestMissingAsync( + """ + class C + { + int Goo + { + get + { + [|return Bar(); + } + } + }|] + """, + parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } } diff --git a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs index a4e6a7b315b60..98611699ffc8d 100644 --- a/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddImport/AbstractAddImportCodeFixProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Packaging; diff --git a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs index 95e2a8cc1e314..3646b545bce14 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/AddImportCodeAction.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs index af6a66bb020ed..8360fc1f3120b 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/InstallPackageAndAddImportCodeAction.cs @@ -6,14 +6,12 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddPackage; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport; diff --git a/src/Features/Core/Portable/AddImport/CodeActions/InstallWithPackageManagerCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/InstallWithPackageManagerCodeAction.cs index 25984050cfc0b..9d1f04f95a08d 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/InstallWithPackageManagerCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/InstallWithPackageManagerCodeAction.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs index 6acecd82cae76..fde00770f53cf 100644 --- a/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs +++ b/src/Features/Core/Portable/AddImport/IAddImportFeatureService.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.SymbolSearch; diff --git a/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs b/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs index c1f74045e0f9c..c6b2d81f0bd51 100644 --- a/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.cs +++ b/src/Features/Core/Portable/AddImport/Remote/IRemoteMissingImportDiscoveryService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs index 11d34bf3904c2..f643933f13776 100644 --- a/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.cs +++ b/src/Features/Core/Portable/AddImport/SearchScopes/ProjectSearchScope.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.AddImport; internal abstract partial class AbstractAddImportFeatureService diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs index 2509bd09c77ab..7732b06817714 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder.cs @@ -336,10 +336,12 @@ private async Task> GetReferencesForMatchingFiel private bool HasAccessibleStaticFieldOrProperty(INamedTypeSymbol namedType, string fieldOrPropertyName) { - return namedType.GetMembers(fieldOrPropertyName) - .Any(static (m, self) => (m is IFieldSymbol || m is IPropertySymbol) && - m.IsStatic && - m.IsAccessibleWithin(self._semanticModel.Compilation.Assembly), this); + return namedType + .GetMembers(fieldOrPropertyName) + .Any(static (m, self) => + m is IFieldSymbol or IPropertySymbol && + m.IsStatic && + m.IsAccessibleWithin(self._semanticModel.Compilation.Assembly), this); } /// diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index 5c64e0eb6f7cb..6693163cf0e2e 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -2,9 +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.Concurrent; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Features/Core/Portable/AddMissingReference/AbstractAddMissingReferenceCodeFixProvider.cs b/src/Features/Core/Portable/AddMissingReference/AbstractAddMissingReferenceCodeFixProvider.cs index f94b609e766f3..1893de04d1020 100644 --- a/src/Features/Core/Portable/AddMissingReference/AbstractAddMissingReferenceCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddMissingReference/AbstractAddMissingReferenceCodeFixProvider.cs @@ -10,10 +10,8 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SymbolSearch; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddMissingReference; diff --git a/src/Features/Core/Portable/AddPackage/AbstractAddSpecificPackageCodeFixProvider.cs b/src/Features/Core/Portable/AddPackage/AbstractAddSpecificPackageCodeFixProvider.cs index ddebcbe127d78..afad453be99db 100644 --- a/src/Features/Core/Portable/AddPackage/AbstractAddSpecificPackageCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddPackage/AbstractAddSpecificPackageCodeFixProvider.cs @@ -7,8 +7,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Packaging; -using Microsoft.CodeAnalysis.SymbolSearch; namespace Microsoft.CodeAnalysis.AddPackage; diff --git a/src/Features/Core/Portable/AddPackage/InstallPackageDirectlyCodeAction.cs b/src/Features/Core/Portable/AddPackage/InstallPackageDirectlyCodeAction.cs index 3d1994705418b..3489f4c596dc1 100644 --- a/src/Features/Core/Portable/AddPackage/InstallPackageDirectlyCodeAction.cs +++ b/src/Features/Core/Portable/AddPackage/InstallPackageDirectlyCodeAction.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/AddPackage/InstallWithPackageManagerCodeAction.cs b/src/Features/Core/Portable/AddPackage/InstallWithPackageManagerCodeAction.cs index 3e2e26bc7c043..9bdda9c841eea 100644 --- a/src/Features/Core/Portable/AddPackage/InstallWithPackageManagerCodeAction.cs +++ b/src/Features/Core/Portable/AddPackage/InstallWithPackageManagerCodeAction.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/BraceMatching/BraceMatchingService.cs b/src/Features/Core/Portable/BraceMatching/BraceMatchingService.cs index d01e32dd5e861..f37bf8cd8b698 100644 --- a/src/Features/Core/Portable/BraceMatching/BraceMatchingService.cs +++ b/src/Features/Core/Portable/BraceMatching/BraceMatchingService.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.Generic; using System.Collections.Immutable; diff --git a/src/Features/Core/Portable/BraceMatching/ExportBraceMatcherAttribute.cs b/src/Features/Core/Portable/BraceMatching/ExportBraceMatcherAttribute.cs index a69f2265ff65c..7b791ce5656e9 100644 --- a/src/Features/Core/Portable/BraceMatching/ExportBraceMatcherAttribute.cs +++ b/src/Features/Core/Portable/BraceMatching/ExportBraceMatcherAttribute.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.Composition; diff --git a/src/Features/Core/Portable/ChangeSignature/ChangeSignatureTelemetryLogger.cs b/src/Features/Core/Portable/ChangeSignature/ChangeSignatureTelemetryLogger.cs index 6232b66e14c85..c904ab622e1e8 100644 --- a/src/Features/Core/Portable/ChangeSignature/ChangeSignatureTelemetryLogger.cs +++ b/src/Features/Core/Portable/ChangeSignature/ChangeSignatureTelemetryLogger.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 Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/Features/Core/Portable/ChangeSignature/IUnifiedArgumentSyntax.cs b/src/Features/Core/Portable/ChangeSignature/IUnifiedArgumentSyntax.cs index 98c9cde47b328..6eb1bbb1f9df2 100644 --- a/src/Features/Core/Portable/ChangeSignature/IUnifiedArgumentSyntax.cs +++ b/src/Features/Core/Portable/ChangeSignature/IUnifiedArgumentSyntax.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 - namespace Microsoft.CodeAnalysis.ChangeSignature; internal interface IUnifiedArgumentSyntax diff --git a/src/Features/Core/Portable/ChangeSignature/Parameter.cs b/src/Features/Core/Portable/ChangeSignature/Parameter.cs index 05356fa3d7917..e8ee522823ee4 100644 --- a/src/Features/Core/Portable/ChangeSignature/Parameter.cs +++ b/src/Features/Core/Portable/ChangeSignature/Parameter.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.ChangeSignature; /// diff --git a/src/Features/Core/Portable/ClassifiedSpansAndHighlightSpan.cs b/src/Features/Core/Portable/ClassifiedSpansAndHighlightSpan.cs index ee5f8cfffa159..b69e1dfc667d9 100644 --- a/src/Features/Core/Portable/ClassifiedSpansAndHighlightSpan.cs +++ b/src/Features/Core/Portable/ClassifiedSpansAndHighlightSpan.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.Collections.Immutable; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs b/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs index 606674ac7c07e..34834da5aa148 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixCollection.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixCollection.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.Collections.Immutable; using Microsoft.CodeAnalysis.Text; @@ -17,7 +15,7 @@ internal sealed class CodeFixCollection( object provider, TextSpan span, ImmutableArray fixes, - FixAllState fixAllState, + FixAllState? fixAllState, ImmutableArray supportedScopes, Diagnostic firstDiagnostic) { @@ -28,7 +26,7 @@ internal sealed class CodeFixCollection( /// /// Optional fix all context, which is non-null if the given supports fix all occurrences code fix. /// - public FixAllState FixAllState { get; } = fixAllState; + public FixAllState? FixAllState { get; } = fixAllState; public ImmutableArray SupportedScopes { get; } = supportedScopes.NullToEmpty(); public Diagnostic FirstDiagnostic { get; } = firstDiagnostic; } diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs index 2ce7630a16636..10c4eb96236b4 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureCodeStyle/ConfigureCodeStyleOptionCodeFixProvider.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.CodeActions.CodeAction; namespace Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureCodeStyle; diff --git a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.TopLevelConfigureSeverityCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.TopLevelConfigureSeverityCodeAction.cs index 41ce22e1b6c6a..f54119e14fcd3 100644 --- a/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.TopLevelConfigureSeverityCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Configuration/ConfigureSeverity/ConfigureSeverityLevelCodeFixProvider.TopLevelConfigureSeverityCodeAction.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.Collections.Immutable; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs index c618db13e848f..8e9a1aa4b3cea 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/FixMultipleCodeAction.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 Microsoft.CodeAnalysis.CodeFixesAndRefactorings; namespace Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs index 6f1691cc9108a..5c66d2e38aed6 100644 --- a/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs +++ b/src/Features/Core/Portable/CodeFixes/FixAllOccurrences/IFixMultipleOccurrencesService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/CodeFixes/ICodeFixProviderFactory.cs b/src/Features/Core/Portable/CodeFixes/ICodeFixProviderFactory.cs index d4f2b739a7742..fabccd188050e 100644 --- a/src/Features/Core/Portable/CodeFixes/ICodeFixProviderFactory.cs +++ b/src/Features/Core/Portable/CodeFixes/ICodeFixProviderFactory.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.AbstractSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.AbstractSuppressionCodeAction.cs index e100dde757739..eff28d66e0b6c 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.AbstractSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.AbstractSuppressionCodeAction.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 - namespace Microsoft.CodeAnalysis.CodeFixes.Suppression; internal abstract partial class AbstractSuppressionCodeFixProvider diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs index fef93e3f5c4ac..dbb9c1594392d 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.FixAllProvider.cs @@ -5,10 +5,8 @@ #nullable disable using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs index 74ad683824a18..be345648227a7 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.GlobalSuppressMessageCodeAction.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. -#nullable disable - using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.IPragmaBasedCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.IPragmaBasedCodeAction.cs index ef72378420df2..4519081ad7253 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.IPragmaBasedCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.IPragmaBasedCodeAction.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; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs index 204d8296e4658..a6ed417c47449 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.PragmaWarningCodeAction.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs index b90e142d69b3b..9c2c87d041fbb 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.RemoveSuppressionCodeAction.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs index 5929cb0098c9c..9149a0150074f 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/AbstractSuppressionCodeFixProvider.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/ExportConfigurationFixProviderAttribute.cs b/src/Features/Core/Portable/CodeFixes/Suppression/ExportConfigurationFixProviderAttribute.cs index ea31ad2864db4..bf047cac1d951 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/ExportConfigurationFixProviderAttribute.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/ExportConfigurationFixProviderAttribute.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.Composition; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs index c980a965211d0..0a5dce0c02437 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/NestedSuppressionCodeAction.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 Microsoft.CodeAnalysis.CodeActions; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs index 2b1cf52b0e5cf..5840abac92c73 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/SuppressionHelpers.cs @@ -2,13 +2,10 @@ // 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.Immutable; using System.Globalization; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.Suppression; diff --git a/src/Features/Core/Portable/CodeFixes/Suppression/TopLevelSuppressionCodeAction.cs b/src/Features/Core/Portable/CodeFixes/Suppression/TopLevelSuppressionCodeAction.cs index 248788d106b3c..a2394ff67c03d 100644 --- a/src/Features/Core/Portable/CodeFixes/Suppression/TopLevelSuppressionCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixes/Suppression/TopLevelSuppressionCodeAction.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.Collections.Immutable; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs b/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs index 16c7df7bd1487..cb9287f68011d 100644 --- a/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs +++ b/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllCodeAction.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Roslyn.Utilities; using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; diff --git a/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs b/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs index d47f72d6a39d8..d8e4e1e2111de 100644 --- a/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs +++ b/src/Features/Core/Portable/CodeFixesAndRefactorings/AbstractFixAllGetFixesService.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; diff --git a/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs b/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs index 5a0a535ac915c..57d694691610d 100644 --- a/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.cs +++ b/src/Features/Core/Portable/CodeLens/CodeLensFindReferenceProgress.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.Generic; using System.Collections.Immutable; diff --git a/src/Features/Core/Portable/CodeLens/CodeLensReferencesServiceFactory.cs b/src/Features/Core/Portable/CodeLens/CodeLensReferencesServiceFactory.cs index ec245f4668dbc..a5f63eac45e4c 100644 --- a/src/Features/Core/Portable/CodeLens/CodeLensReferencesServiceFactory.cs +++ b/src/Features/Core/Portable/CodeLens/CodeLensReferencesServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/CodeLens/ICodeLensDisplayInfoService.cs b/src/Features/Core/Portable/CodeLens/ICodeLensDisplayInfoService.cs index a269a5ae88e4c..bc9d2f463fb35 100644 --- a/src/Features/Core/Portable/CodeLens/ICodeLensDisplayInfoService.cs +++ b/src/Features/Core/Portable/CodeLens/ICodeLensDisplayInfoService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.CodeLens; diff --git a/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs b/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs index fbdc2ffc16686..54d75ca881458 100644 --- a/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.cs +++ b/src/Features/Core/Portable/CodeLens/IRemoteCodeLensReferencesService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -16,5 +14,5 @@ internal interface IRemoteCodeLensReferencesService ValueTask GetReferenceCountAsync(Checksum solutionChecksum, DocumentId documentId, TextSpan textSpan, int maxResultCount, CancellationToken cancellationToken); ValueTask?> FindReferenceLocationsAsync(Checksum solutionChecksum, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); ValueTask?> FindReferenceMethodsAsync(Checksum solutionChecksum, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); - ValueTask GetFullyQualifiedNameAsync(Checksum solutionChecksum, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); + ValueTask GetFullyQualifiedNameAsync(Checksum solutionChecksum, DocumentId documentId, TextSpan textSpan, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs b/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs index bb08943825cf2..e0074dbdb38ad 100644 --- a/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.cs +++ b/src/Features/Core/Portable/CodeLens/ReferenceLocationDescriptor.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.Runtime.Serialization; diff --git a/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs b/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs index e666941894cf1..12955d2634456 100644 --- a/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.cs +++ b/src/Features/Core/Portable/CodeLens/ReferenceMethodDescriptor.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.Runtime.Serialization; namespace Microsoft.CodeAnalysis.CodeLens; diff --git a/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs b/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs index 24ef70bf21c2a..bba947dab6d41 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeRefactorings.ExtractMethod; diff --git a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringProviderFactory.cs b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringProviderFactory.cs index 05c5af6265fe3..f9a4837bf228a 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringProviderFactory.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ICodeRefactoringProviderFactory.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.CodeRefactorings; diff --git a/src/Features/Core/Portable/CodeRefactorings/MoveType/IMoveTypeService.cs b/src/Features/Core/Portable/CodeRefactorings/MoveType/IMoveTypeService.cs index 23c02bfa1ca52..51674c63f44a6 100644 --- a/src/Features/Core/Portable/CodeRefactorings/MoveType/IMoveTypeService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/MoveType/IMoveTypeService.cs @@ -2,13 +2,10 @@ // 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.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs index 019ad957c381e..559eb5f952faa 100644 --- a/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs +++ b/src/Features/Core/Portable/CodeRefactorings/PredefinedCodeRefactoringProviderNames.cs @@ -30,6 +30,7 @@ internal static class PredefinedCodeRefactoringProviderNames public const string ConvertNumericLiteral = nameof(ConvertNumericLiteral); public const string ConvertPlaceholderToInterpolatedString = nameof(ConvertPlaceholderToInterpolatedString); public const string ConvertPrimaryToRegularConstructor = nameof(ConvertPrimaryToRegularConstructor); + public const string ConvertToExtension = nameof(ConvertToExtension); public const string ConvertToInterpolatedString = "Convert To Interpolated String Code Action Provider"; public const string ConvertToProgramMain = "Convert To Program.Main"; public const string ConvertToRawString = nameof(ConvertToRawString); diff --git a/src/Features/Core/Portable/CodeRefactorings/ServicesLayerCodeActionHelpersService.cs b/src/Features/Core/Portable/CodeRefactorings/ServicesLayerCodeActionHelpersService.cs index d881ce4234945..67c4c3e5664bc 100644 --- a/src/Features/Core/Portable/CodeRefactorings/ServicesLayerCodeActionHelpersService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/ServicesLayerCodeActionHelpersService.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/IAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/IAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs index 059a65cac955c..414ff17bdc7cd 100644 --- a/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/IAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/IAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.CodeActions.WorkspaceServices; diff --git a/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs index 48de894b7db0f..8a6f7e5670348 100644 --- a/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs +++ b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.CodeActions.WorkspaceServices; diff --git a/src/Features/Core/Portable/Common/GlyphExtensions.cs b/src/Features/Core/Portable/Common/GlyphExtensions.cs index 6f5fde9a39849..66f610befa68b 100644 --- a/src/Features/Core/Portable/Common/GlyphExtensions.cs +++ b/src/Features/Core/Portable/Common/GlyphExtensions.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.Collections.Immutable; using Microsoft.CodeAnalysis.Tags; diff --git a/src/Features/Core/Portable/Common/GlyphTags.cs b/src/Features/Core/Portable/Common/GlyphTags.cs index d84cc4d3b6aaf..d7737b9b4ac31 100644 --- a/src/Features/Core/Portable/Common/GlyphTags.cs +++ b/src/Features/Core/Portable/Common/GlyphTags.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.Collections.Immutable; using Microsoft.CodeAnalysis.Tags; diff --git a/src/Features/Core/Portable/Common/TextTags.cs b/src/Features/Core/Portable/Common/TextTags.cs index 105ee48bcf446..d280725037810 100644 --- a/src/Features/Core/Portable/Common/TextTags.cs +++ b/src/Features/Core/Portable/Common/TextTags.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 - namespace Microsoft.CodeAnalysis; /// diff --git a/src/Features/Core/Portable/Completion/CompletionChange.cs b/src/Features/Core/Portable/Completion/CompletionChange.cs index 34c86ae91789c..489acd9982f58 100644 --- a/src/Features/Core/Portable/Completion/CompletionChange.cs +++ b/src/Features/Core/Portable/Completion/CompletionChange.cs @@ -31,7 +31,13 @@ public sealed class CompletionChange /// The new caret position after the change has been applied. /// If null then the new caret position will be determined by the completion host. /// - public int? NewPosition { get; } + public int? NewPosition { get => NewSelection?.End; } + + /// + /// The new selection after the change has been applied. + /// If null then the new caret position will be determined by the completion host. + /// + internal TextSpan? NewSelection { get; } /// /// True if the changes include the typed character that caused the @@ -50,9 +56,15 @@ private CompletionChange( private CompletionChange( TextChange textChange, ImmutableArray textChanges, int? newPosition, bool includesCommitCharacter, ImmutableDictionary properties) + : this(textChange, textChanges, newPosition != null ? new TextSpan(newPosition.Value, 0) : null, includesCommitCharacter, properties) + { + } + + private CompletionChange( + TextChange textChange, ImmutableArray textChanges, TextSpan? newSelection, bool includesCommitCharacter, ImmutableDictionary properties) { TextChange = textChange; - NewPosition = newPosition; + NewSelection = newSelection; IncludesCommitCharacter = includesCommitCharacter; TextChanges = textChanges.NullToEmpty(); if (TextChanges.IsEmpty) @@ -106,6 +118,16 @@ public static CompletionChange Create( return new CompletionChange(textChange, textChanges, newPosition, includesCommitCharacter); } + internal static CompletionChange Create( + TextChange textChange, + ImmutableArray textChanges, + ImmutableDictionary properties, + TextSpan? newSpan = null, + bool includesCommitCharacter = false) + { + return new CompletionChange(textChange, textChanges, newSpan, includesCommitCharacter, properties); + } + internal static CompletionChange Create( TextChange textChange, ImmutableArray textChanges, diff --git a/src/Features/Core/Portable/Completion/CompletionOptions.cs b/src/Features/Core/Portable/Completion/CompletionOptions.cs index e625ab9819d59..bc674c7ff9778 100644 --- a/src/Features/Core/Portable/Completion/CompletionOptions.cs +++ b/src/Features/Core/Portable/Completion/CompletionOptions.cs @@ -2,9 +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 Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Recommendations; using Microsoft.CodeAnalysis.Shared; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs index ed1a97629f109..c920fce114139 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractAggregateEmbeddedLanguageCompletionProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs index 55db699a45d8a..e32dfa6ef5b15 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractCrefCompletionProvider.cs @@ -6,8 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs index b3864d7dcf94e..84fe32c5f955d 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractKeywordCompletionProvider.cs @@ -54,7 +54,7 @@ private async Task> RecommendKeywordsAsync( TContext context, CancellationToken cancellationToken) { - var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken)) return []; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs index 85ecf6aa8d684..7760cc453e3b2 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractMemberInsertingCompletionProvider.cs @@ -38,7 +38,8 @@ internal abstract partial class AbstractMemberInsertingCompletionProvider : LSPC protected abstract Task GenerateMemberAsync( Document document, CompletionItem item, Compilation compilation, ISymbol member, INamedTypeSymbol containingType, CancellationToken cancellationToken); - protected abstract int GetTargetCaretPosition(SyntaxNode caretTarget); + protected abstract TextSpan GetTargetSelectionSpan(SyntaxNode caretTarget); + protected abstract SyntaxNode GetSyntax(SyntaxToken commonSyntaxToken); protected static CompletionItemRules GetRules() @@ -46,17 +47,17 @@ protected static CompletionItemRules GetRules() public override async Task GetChangeAsync(Document document, CompletionItem item, char? commitKey = null, CancellationToken cancellationToken = default) { - var (newDocument, newPosition) = await DetermineNewDocumentAsync(document, item, cancellationToken).ConfigureAwait(false); + var (newDocument, newSpan) = await DetermineNewDocumentAsync(document, item, cancellationToken).ConfigureAwait(false); var newText = await newDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); var changes = await newDocument.GetTextChangesAsync(document, cancellationToken).ConfigureAwait(false); var changesArray = changes.ToImmutableArray(); var change = Utilities.Collapse(newText, changesArray); - return CompletionChange.Create(change, changesArray, newPosition, includesCommitCharacter: true); + return CompletionChange.Create(change, changesArray, properties: ImmutableDictionary.Empty, newSpan, includesCommitCharacter: true); } - private async Task<(Document, int? caretPosition)> DetermineNewDocumentAsync( + private async Task<(Document, TextSpan? caretPosition)> DetermineNewDocumentAsync( Document document, CompletionItem completionItem, CancellationToken cancellationToken) @@ -168,7 +169,7 @@ private TextSpan ComputeDestinationSpan(SyntaxNode insertionRoot) return TextSpan.FromBounds(startToken.Value.SpanStart, line.EndIncludingLineBreak); } - private async Task<(Document Document, int? CaretPosition)> RemoveDestinationNodeAsync( + private async Task<(Document Document, TextSpan? Selection)> RemoveDestinationNodeAsync( Document memberContainingDocument, CodeCleanupOptions cleanupOptions, CancellationToken cancellationToken) { // We now have a replacement node inserted into the document, but we still have the source code that triggered completion (with associated trivia). @@ -228,18 +229,18 @@ private TextSpan ComputeDestinationSpan(SyntaxNode insertionRoot) var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); // We have basically the final tree. Calculate the new caret position while we still have the annotations. - int? caretPosition = null; + TextSpan? newSpan = null; var caretTarget = root.GetAnnotatedNodes(_annotation).FirstOrDefault(); if (caretTarget != null) { - var targetPosition = GetTargetCaretPosition(caretTarget); + var targetSelectionSpan = GetTargetSelectionSpan(caretTarget); - if (targetPosition > 0 && targetPosition <= text.Length) + if (targetSelectionSpan.Start > 0 && targetSelectionSpan.End <= text.Length) { // The new replacement method should always be inserted before the destination span we're removing. - // This means the caret position in the inserted method should be safe to return as-is. - Debug.Assert(targetPosition < destinationSpan.Start); - caretPosition = targetPosition; + // This means the end selection position in the inserted method should be safe to return as-is. + Debug.Assert(targetSelectionSpan.End < destinationSpan.Start); + newSpan = targetSelectionSpan; } } @@ -249,7 +250,7 @@ private TextSpan ComputeDestinationSpan(SyntaxNode insertionRoot) var textChange = new TextChange(text.Lines[lineNumber].SpanIncludingLineBreak, string.Empty); text = text.WithChanges(textChange); - return (document.WithText(text), caretPosition); + return (document.WithText(text), newSpan); } internal override Task GetDescriptionWorkerAsync(Document document, CompletionItem item, CompletionOptions options, SymbolDescriptionOptions displayOptions, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.cs index c5d077a6e7208..063f7796c3a79 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Completion.Providers; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs index 584deebb8a09d..98384794866e5 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractRecommendationServiceBasedCompletionProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Features/Core/Portable/Completion/Providers/Scripting/AbstractLoadDirectiveCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/Scripting/AbstractLoadDirectiveCompletionProvider.cs index f94d6a005db03..72bc45690570d 100644 --- a/src/Features/Core/Portable/Completion/Providers/Scripting/AbstractLoadDirectiveCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/Scripting/AbstractLoadDirectiveCompletionProvider.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.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Collections; diff --git a/src/Features/Core/Portable/Completion/Providers/Scripting/GlobalAssemblyCacheCompletionHelper.cs b/src/Features/Core/Portable/Completion/Providers/Scripting/GlobalAssemblyCacheCompletionHelper.cs index dcc1e89aa24b9..a0aa0b1fd4684 100644 --- a/src/Features/Core/Portable/Completion/Providers/Scripting/GlobalAssemblyCacheCompletionHelper.cs +++ b/src/Features/Core/Portable/Completion/Providers/Scripting/GlobalAssemblyCacheCompletionHelper.cs @@ -2,13 +2,10 @@ // 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.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/ForEachInfo.cs b/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/ForEachInfo.cs index 492de0fb3fcf0..147b0f645195e 100644 --- a/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/ForEachInfo.cs +++ b/src/Features/Core/Portable/ConvertLinq/ConvertForEachToLinqQuery/ForEachInfo.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.ConvertLinq.ConvertForEachToLinqQuery; diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs index 865ec14ec093d..2547e03fcc758 100644 --- a/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertTupleToStruct/AbstractConvertTupleToStructCodeRefactoringProvider.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; diff --git a/src/Features/Core/Portable/ConvertTupleToStruct/IConvertTupleToStructCodeRefactoringProvider.cs b/src/Features/Core/Portable/ConvertTupleToStruct/IConvertTupleToStructCodeRefactoringProvider.cs index 1220eaed05d3d..2d8e2660d3c50 100644 --- a/src/Features/Core/Portable/ConvertTupleToStruct/IConvertTupleToStructCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ConvertTupleToStruct/IConvertTupleToStructCodeRefactoringProvider.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/Copilot/CopilotConstants.cs b/src/Features/Core/Portable/Copilot/CopilotConstants.cs deleted file mode 100644 index 7b6f866d9f06a..0000000000000 --- a/src/Features/Core/Portable/Copilot/CopilotConstants.cs +++ /dev/null @@ -1,15 +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; - -namespace Microsoft.CodeAnalysis.Copilot; - -internal static class CopilotConstants -{ - public const int CopilotIconLogoId = 1; - public const int CopilotIconSparkleId = 2; - public const int CopilotIconSparkleBlueId = 3; - public static readonly Guid CopilotIconMonikerGuid = new("{4515B9BD-70A1-45FA-9545-D4536417C596}"); -} diff --git a/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs b/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs index 3b7326f2b8ab2..c8e4a55485f86 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotCodeAnalysisService.cs @@ -2,13 +2,14 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DocumentationComments; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Copilot; @@ -64,15 +65,17 @@ internal interface ICopilotCodeAnalysisService : ILanguageService Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken); /// - /// Method to fetch the on-the-fly documentation based on a a symbols - /// and the code for the symbols in . - /// - /// is a formatted string representation of an .
- /// is a list of a code definitions from an . - /// is the language of the originating . - ///
+ /// Retrieves the prompt ///
- Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken); + /// Type containing code and other context about the symbol being examined. + /// + Task GetOnTheFlyDocsPromptAsync(OnTheFlyDocsInfo onTheFlyDocsInfo, CancellationToken cancellationToken); + + /// + /// Retrieves the response from Copilot summarizing what a symbol is being used for and whether or not the quota has exceeded. + /// + /// The input text used to generate the response. + Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseAsync(string prompt, CancellationToken cancellationToken); /// /// Determines if the given is excluded in the workspace. @@ -84,4 +87,18 @@ internal interface ICopilotCodeAnalysisService : ILanguageService /// /// The documentation comment that has been broken down into its individual pieces. Task<(Dictionary? responseDictionary, bool isQuotaExceeded)> GetDocumentationCommentAsync(DocumentationCommentProposal proposal, CancellationToken cancellationToken); + + /// + /// Checks if the feature for implementing is available. + /// + Task IsImplementNotImplementedExceptionsAvailableAsync(CancellationToken cancellationToken); + + /// + /// Implements methods or properties containing throws in the given . + /// + /// A dictionary mapping the original syntax nodes to their implementation details. + Task> ImplementNotImplementedExceptionsAsync( + Document document, + ImmutableDictionary> methodOrProperties, + CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/Copilot/ICopilotOptionsService.cs b/src/Features/Core/Portable/Copilot/ICopilotOptionsService.cs index 26ec2078681d8..fce617f51ef5b 100644 --- a/src/Features/Core/Portable/Copilot/ICopilotOptionsService.cs +++ b/src/Features/Core/Portable/Copilot/ICopilotOptionsService.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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; @@ -33,4 +34,9 @@ internal interface ICopilotOptionsService : ILanguageService /// Returns true if Copilot generate documentation comment feature is enabled. ///
Task IsGenerateDocumentationCommentOptionEnabledAsync(); + + /// + /// Returns true if Copilot generate method implementation feature is enabled. + /// + Task IsImplementNotImplementedExceptionEnabledAsync(); } diff --git a/src/Features/Core/Portable/Copilot/ImplementationDetails.cs b/src/Features/Core/Portable/Copilot/ImplementationDetails.cs new file mode 100644 index 0000000000000..3d001e1e8cc71 --- /dev/null +++ b/src/Features/Core/Portable/Copilot/ImplementationDetails.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.Copilot; + +/// +/// Holds details about a replacement node, providing either a message explaining the absence of a replacement or the +/// replacement syntax node itself. One of or must always be set. +/// +internal sealed class ImplementationDetails +{ + /// + /// Gets the message explaining why a replacement node is not provided. Either this property or must be set. + /// + public string? Message { get; init; } + + /// + /// Gets the replacement syntax node. Either this property or must be set. + /// + public SyntaxNode? ReplacementNode { get; init; } +} diff --git a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.NameAndArity.cs b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.NameAndArity.cs index c893ef0494379..42647c95058c8 100644 --- a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.NameAndArity.cs +++ b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.NameAndArity.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 - namespace Microsoft.CodeAnalysis.Debugging; internal abstract partial class AbstractBreakpointResolver diff --git a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs index 75229c8f43e08..e6d9ecaa380d9 100644 --- a/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs +++ b/src/Features/Core/Portable/Debugging/AbstractBreakpointResolver.cs @@ -231,6 +231,7 @@ private async Task> GetAllTypesAsync(CancellationT { SymbolKind.Method => ((IMethodSymbol)symbol).PartialImplementationPart, SymbolKind.Property => ((IPropertySymbol)symbol).PartialImplementationPart, + SymbolKind.Event => ((IEventSymbol)symbol).PartialImplementationPart, _ => null }; diff --git a/src/Features/Core/Portable/Debugging/IProximityExpressionsService.cs b/src/Features/Core/Portable/Debugging/IProximityExpressionsService.cs index a578e78e14098..b34a832e695bd 100644 --- a/src/Features/Core/Portable/Debugging/IProximityExpressionsService.cs +++ b/src/Features/Core/Portable/Debugging/IProximityExpressionsService.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.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/DocumentDiagnosticAnalyzer.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/DocumentDiagnosticAnalyzer.cs index 5c1544cdb470b..abcc5ba0a84dc 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/DocumentDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/DocumentDiagnosticAnalyzer.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.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs index de3204768c301..7cae891a5897b 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Linq; -using Microsoft.CodeAnalysis.Simplification; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.AddImport; diff --git a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs index e1d85cc8844a9..6855cedf92a45 100644 --- a/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs +++ b/src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs @@ -8,8 +8,6 @@ using System.Linq; using System.Reflection; using Microsoft.CodeAnalysis.Diagnostics.Telemetry; -using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Features/Core/Portable/Diagnostics/IAnalyzerDriverService.cs b/src/Features/Core/Portable/Diagnostics/IAnalyzerDriverService.cs index c8b3bb1119f07..77526616e7c6e 100644 --- a/src/Features/Core/Portable/Diagnostics/IAnalyzerDriverService.cs +++ b/src/Features/Core/Portable/Diagnostics/IAnalyzerDriverService.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; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Features/Core/Portable/Diagnostics/IDiagnosticsRefresher.cs b/src/Features/Core/Portable/Diagnostics/IDiagnosticsRefresher.cs index bd1b413174aa7..e185068a39c34 100644 --- a/src/Features/Core/Portable/Diagnostics/IDiagnosticsRefresher.cs +++ b/src/Features/Core/Portable/Diagnostics/IDiagnosticsRefresher.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; + namespace Microsoft.CodeAnalysis.Diagnostics; /// @@ -9,6 +11,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics; /// internal interface IDiagnosticsRefresher { + event Action? WorkspaceRefreshRequested; + /// /// Requests workspace diagnostics refresh. /// Any component that maintains state whose change may affect reported diagnostics should call whenever that state changes. diff --git a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs index 3d1f95c1a8c1e..d212ebc32a8a1 100644 --- a/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs +++ b/src/Features/Core/Portable/Diagnostics/IRemoteDiagnosticAnalyzerService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Runtime.Serialization; diff --git a/src/Features/Core/Portable/Diagnostics/Log/DiagnosticLogger.cs b/src/Features/Core/Portable/Diagnostics/Log/DiagnosticLogger.cs index 31e0854b54f5e..fc9e912614162 100644 --- a/src/Features/Core/Portable/Diagnostics/Log/DiagnosticLogger.cs +++ b/src/Features/Core/Portable/Diagnostics/Log/DiagnosticLogger.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 Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.Diagnostics.Log; diff --git a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs index 75c1d327e24c4..923f4cf08b690 100644 --- a/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs +++ b/src/Features/Core/Portable/DocumentationComments/AbstractDocumentationCommentSnippetService.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposal.cs b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposal.cs index 88b6f89184e71..f4392262d16d8 100644 --- a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposal.cs +++ b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposal.cs @@ -2,9 +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.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposedEdit.cs b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposedEdit.cs index 446a2cd989d1e..8ec2489563732 100644 --- a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposedEdit.cs +++ b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentProposedEdit.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentSnippet.cs b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentSnippet.cs index e8618b78a4b5b..6daa1f9e2069e 100644 --- a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentSnippet.cs +++ b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentSnippet.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. -using System.Collections.Generic; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentTagType.cs b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentTagType.cs index 6c81814e8c979..69a4f3d651a36 100644 --- a/src/Features/Core/Portable/DocumentationComments/DocumentationCommentTagType.cs +++ b/src/Features/Core/Portable/DocumentationComments/DocumentationCommentTagType.cs @@ -2,20 +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.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Microsoft.CodeAnalysis.DocumentationComments { internal enum DocumentationCommentTagType { Summary, + Remarks, TypeParam, Param, Returns, + Value, Exception, } } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 0eb3caab26dfd..f4871492616c1 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2721,8 +2721,8 @@ newSymbol is IPropertySymbol newProperty && } // If a partial method/property/indexer definition is deleted (and not moved to another partial type declaration, which is handled above) - // so must be the implementation. An edit will be issued for the implementation change. - if (newSymbol?.IsPartialDefinition() == true) + // so must be the implementation (if it exists). An edit will be issued for the implementation change. + if (oldSymbol.IsPartialDefinition()) { continue; } diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementExceptionRegions.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementExceptionRegions.cs index 1e81372175f8b..50ed55948041b 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementExceptionRegions.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementExceptionRegions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs index b14c697b558bc..d03722a3b56b6 100644 --- a/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/DebuggingSession.cs @@ -511,13 +511,27 @@ public async ValueTask EmitSolutionUpdateAsync( solutionUpdate.Log(SessionLog, updateId); _lastModuleUpdatesLog = solutionUpdate.ModuleUpdates.Updates; - if (solutionUpdate.ModuleUpdates.Status == ModuleUpdateStatus.Ready) + switch (solutionUpdate.ModuleUpdates.Status) { - StorePendingUpdate(new PendingSolutionUpdate( - solution, - solutionUpdate.ProjectBaselines, - solutionUpdate.ModuleUpdates.Updates, - solutionUpdate.NonRemappableRegions)); + case ModuleUpdateStatus.Ready: + // We have updates to be applied. The debugger will call Commit/Discard on the solution + // based on whether the updates will be applied successfully or not. + StorePendingUpdate(new PendingSolutionUpdate( + solution, + solutionUpdate.ProjectBaselines, + solutionUpdate.ModuleUpdates.Updates, + solutionUpdate.NonRemappableRegions)); + + break; + + case ModuleUpdateStatus.None: + Contract.ThrowIfFalse(solutionUpdate.ModuleUpdates.Updates.IsEmpty); + Contract.ThrowIfFalse(solutionUpdate.NonRemappableRegions.IsEmpty); + + // No significant changes have been made. + // Commit the solution to apply any changes in comments that do not generate updates. + LastCommittedSolution.CommitSolution(solution); + break; } using var _ = ArrayBuilder.GetInstance(out var rudeEditDiagnostics); diff --git a/src/Features/Core/Portable/EditAndContinue/DocumentActiveStatementChanges.cs b/src/Features/Core/Portable/EditAndContinue/DocumentActiveStatementChanges.cs index 3d77a22e13c1d..cb0b443d7d41b 100644 --- a/src/Features/Core/Portable/EditAndContinue/DocumentActiveStatementChanges.cs +++ b/src/Features/Core/Portable/EditAndContinue/DocumentActiveStatementChanges.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 0ce5dcb32db29..d4b48c9582807 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -166,8 +166,8 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddGeneralDiagnostic(EditAndContinueErrorCode.ErrorReadingFile, nameof(FeaturesResources.ErrorReadingFile)); AddGeneralDiagnostic(EditAndContinueErrorCode.CannotApplyChangesUnexpectedError, nameof(FeaturesResources.CannotApplyChangesUnexpectedError)); AddGeneralDiagnostic(EditAndContinueErrorCode.ChangesDisallowedWhileStoppedAtException, nameof(FeaturesResources.ChangesDisallowedWhileStoppedAtException)); - AddGeneralDiagnostic(EditAndContinueErrorCode.DocumentIsOutOfSyncWithDebuggee, nameof(FeaturesResources.DocumentIsOutOfSyncWithDebuggee), DiagnosticSeverity.Warning); - AddGeneralDiagnostic(EditAndContinueErrorCode.UnableToReadSourceFileOrPdb, nameof(FeaturesResources.UnableToReadSourceFileOrPdb), DiagnosticSeverity.Warning); + AddGeneralDiagnostic(EditAndContinueErrorCode.DocumentIsOutOfSyncWithDebuggee, nameof(FeaturesResources.DocumentIsOutOfSyncWithDebuggee)); + AddGeneralDiagnostic(EditAndContinueErrorCode.UnableToReadSourceFileOrPdb, nameof(FeaturesResources.UnableToReadSourceFileOrPdb)); AddGeneralDiagnostic(EditAndContinueErrorCode.AddingTypeRuntimeCapabilityRequired, nameof(FeaturesResources.ChangesRequiredSynthesizedType)); s_descriptors = builder.ToImmutable(); diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 4e9ea34a5b0a8..83e5711a2c544 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -821,8 +821,9 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution var oldSolution = DebuggingSession.LastCommittedSolution; - var isBlocked = false; + var blockUpdates = false; var hasEmitErrors = false; + var hadDocumentReadError = false; foreach (var newProject in solution.Projects) { if (!newProject.SupportsEditAndContinue(Log)) @@ -866,7 +867,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution diagnostics.Add(new(newProject.Id, [mvidReadError])); Telemetry.LogProjectAnalysisSummary(ProjectAnalysisSummary.ValidChanges, newProject.State.ProjectInfo.Attributes.TelemetryId, ImmutableArray.Create(mvidReadError.Descriptor.Id)); - isBlocked = true; + blockUpdates = true; continue; } @@ -899,6 +900,11 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution // If in future the file is updated so that its content matches the PDB checksum, the document transitions to a matching state, // and we consider any further changes to it for application. diagnostics.Add(new(newProject.Id, documentDiagnostics)); + + if (documentDiagnostics.Any(d => d.Severity == DiagnosticSeverity.Error)) + { + blockUpdates = hadDocumentReadError = true; + } } foreach (var changedDocumentAnalysis in changedDocumentAnalyses) @@ -935,12 +941,12 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution if (isModuleEncBlocked) { diagnostics.Add(new(newProject.Id, moduleDiagnostics)); - isBlocked = true; + blockUpdates = true; } if (projectSummary is ProjectAnalysisSummary.SyntaxErrors or ProjectAnalysisSummary.RudeEdits) { - isBlocked = true; + blockUpdates = true; } // Report rude edit diagnostics - these can be blocking (errors) or non-blocking (warnings): @@ -972,7 +978,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution diagnostics.Add(new(newProject.Id, createBaselineErrors)); Telemetry.LogProjectAnalysisSummary(projectSummary, newProject.State.ProjectInfo.Attributes.TelemetryId, createBaselineErrors); - isBlocked = true; + blockUpdates = true; await LogDocumentChangesAsync(generation: null, cancellationToken).ConfigureAwait(false); continue; } @@ -1050,7 +1056,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution if (!emitResult.Success) { // error - isBlocked = hasEmitErrors = true; + blockUpdates = hasEmitErrors = true; emitDiagnostics = emitResult.Diagnostics; break; } @@ -1062,7 +1068,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution { emitDiagnostics = [unsupportedChangesDiagnostic]; diagnostics.Add(new(newProject.Id, emitDiagnostics)); - isBlocked = true; + blockUpdates = true; break; } @@ -1127,13 +1133,17 @@ async ValueTask LogDocumentChangesAsync(int? generation, CancellationToken cance } // log capabilities for edit sessions with changes or reported errors: - if (isBlocked || deltas.Count > 0) + if (blockUpdates || deltas.Count > 0) { Telemetry.LogRuntimeCapabilities(await Capabilities.GetValueAsync(cancellationToken).ConfigureAwait(false)); } - var update = isBlocked - ? SolutionUpdate.Blocked(diagnostics.ToImmutable(), documentsWithRudeEdits.ToImmutable(), syntaxError, hasEmitErrors) + var update = blockUpdates + ? SolutionUpdate.Empty( + diagnostics.ToImmutable(), + documentsWithRudeEdits.ToImmutable(), + syntaxError, + syntaxError != null || hasEmitErrors || hadDocumentReadError ? ModuleUpdateStatus.Blocked : ModuleUpdateStatus.RestartRequired) : new SolutionUpdate( new ModuleUpdates( (deltas.Count > 0) ? ModuleUpdateStatus.Ready : ModuleUpdateStatus.None, diff --git a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs index cc39e13802a7f..abbbf80f06865 100644 --- a/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs +++ b/src/Features/Core/Portable/EditAndContinue/IEditAndContinueService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs index 376c21819f30c..21d56b9a3274b 100644 --- a/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.cs +++ b/src/Features/Core/Portable/EditAndContinue/Remote/RemoteDebuggingSessionProxy.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 System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/EditAndContinue/SolutionSnapshotRegistry.cs b/src/Features/Core/Portable/EditAndContinue/SolutionSnapshotRegistry.cs index f070c83001665..c5b914ff79999 100644 --- a/src/Features/Core/Portable/EditAndContinue/SolutionSnapshotRegistry.cs +++ b/src/Features/Core/Portable/EditAndContinue/SolutionSnapshotRegistry.cs @@ -8,7 +8,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Contracts.Client; using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/Features/Core/Portable/EditAndContinue/SolutionUpdate.cs b/src/Features/Core/Portable/EditAndContinue/SolutionUpdate.cs index 3b6caa83edbb3..54411d4be74a6 100644 --- a/src/Features/Core/Portable/EditAndContinue/SolutionUpdate.cs +++ b/src/Features/Core/Portable/EditAndContinue/SolutionUpdate.cs @@ -24,13 +24,13 @@ internal readonly struct SolutionUpdate( public readonly ImmutableArray<(DocumentId DocumentId, ImmutableArray Diagnostics)> DocumentsWithRudeEdits = documentsWithRudeEdits; public readonly Diagnostic? SyntaxError = syntaxError; - public static SolutionUpdate Blocked( + public static SolutionUpdate Empty( ImmutableArray diagnostics, ImmutableArray<(DocumentId, ImmutableArray)> documentsWithRudeEdits, Diagnostic? syntaxError, - bool hasEmitErrors) + ModuleUpdateStatus status) => new( - new(syntaxError != null || hasEmitErrors ? ModuleUpdateStatus.Blocked : ModuleUpdateStatus.RestartRequired, []), + new(status, Updates: []), nonRemappableRegions: [], projectBaselines: [], diagnostics, diff --git a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs index ec8859bb31ff3..37359943592a9 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/EmbeddedLanguageDetector.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/ExportEmbeddedLanguageFeatureServiceAttribute.cs b/src/Features/Core/Portable/EmbeddedLanguages/ExportEmbeddedLanguageFeatureServiceAttribute.cs index 1b6b7ef232887..256072c40ec1b 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/ExportEmbeddedLanguageFeatureServiceAttribute.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/ExportEmbeddedLanguageFeatureServiceAttribute.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EmbeddedLanguages; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonLexer.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonLexer.cs index bc07d43a097fd..a99d7f22d249d 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonLexer.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/JsonLexer.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.Json; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/AbstractJsonDiagnosticAnalyzer.cs b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/AbstractJsonDiagnosticAnalyzer.cs index 1fa13d07cf929..ff7204ab6e17c 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/AbstractJsonDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/Json/LanguageServices/AbstractJsonDiagnosticAnalyzer.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.EmbeddedLanguages; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.Features.EmbeddedLanguages.Json.LanguageServices; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/IRegexNodeVisitor.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/IRegexNodeVisitor.cs index 8770faabbacd5..388df5e1572c8 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/IRegexNodeVisitor.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/IRegexNodeVisitor.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 - namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions; internal interface IRegexNodeVisitor diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexCharClass.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexCharClass.cs index 8e1974186964a..1bccdf1542ee4 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexCharClass.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexCharClass.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 - // LICENSING NOTE: The license for this file is from the originating // source and not the general https://github.com/dotnet/roslyn license. // See https://github.com/dotnet/runtime/blob/5b5bd46c03c86f8545f2c4c8628ac25d875210fe/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexLexer.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexLexer.cs index 056f1278ffb3b..d931882a81304 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexLexer.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexLexer.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.Collections.Immutable; using System.Diagnostics; using System.Text.RegularExpressions; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNode.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNode.cs index ad7e40a179e7f..d83c30efce233 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNode.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNode.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 Microsoft.CodeAnalysis.EmbeddedLanguages.Common; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNodes.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNodes.cs index d5debdeeacfda..33bd1687e4eaf 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNodes.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexNodes.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexParser.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexParser.cs index 1e5bb6f880021..03b98b5d837e8 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexParser.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexParser.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.RegularExpressions; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexTree.cs b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexTree.cs index 2c47898336cc8..dbe5582ef778f 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexTree.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/RegularExpressions/RegexTree.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.Collections.Immutable; using Microsoft.CodeAnalysis.EmbeddedLanguages.Common; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; diff --git a/src/Features/Core/Portable/EmbeddedLanguages/StackFrame/StackFrameTree.cs b/src/Features/Core/Portable/EmbeddedLanguages/StackFrame/StackFrameTree.cs index c457311e87240..e69ff2bd30731 100644 --- a/src/Features/Core/Portable/EmbeddedLanguages/StackFrame/StackFrameTree.cs +++ b/src/Features/Core/Portable/EmbeddedLanguages/StackFrame/StackFrameTree.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using Microsoft.CodeAnalysis.EmbeddedLanguages.Common; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; diff --git a/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs b/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs index 0318404ba0175..f176f8d3ae829 100644 --- a/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.cs +++ b/src/Features/Core/Portable/EncapsulateField/EncapsulateFieldResult.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.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs b/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs index 74bec39df7c2f..87afcffb404be 100644 --- a/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.cs +++ b/src/Features/Core/Portable/EncapsulateField/IRemoteEncapsulateFieldService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs b/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs index 4983379f1e651..8a725049a7dcf 100644 --- a/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs +++ b/src/Features/Core/Portable/ExternalAccess/Pythia/Api/PythiaCompletionProviderBase.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/IUnitTestingStackTraceServiceAccessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/IUnitTestingStackTraceServiceAccessor.cs index 3fe7fe959d471..b043ef3e96381 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/IUnitTestingStackTraceServiceAccessor.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/IUnitTestingStackTraceServiceAccessor.cs @@ -14,5 +14,6 @@ internal interface IUnitTestingStackTraceServiceAccessor : IWorkspaceService Task> TryParseAsync(string input, Workspace workspace, CancellationToken cancellationToken); Task TryFindMethodDefinitionAsync(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame, CancellationToken cancellationToken); (Document? document, int lineNumber) GetDocumentAndLine(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame); + (TextDocument? textDocument, int lineNumber) GetTextDocumentAndLine(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame); Task TryNavigateToAsync(Workspace workspace, UnitTestingDefinitionItemWrapper definitionItem, bool showInPreviewTab, bool activateTab, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs index f1cf82d3fe09c..947a2a790761d 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/NewUnitTestingIncrementalAnalyzerProvider.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; using Microsoft.CodeAnalysis.Host; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingInvocationReasons_Constants.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingInvocationReasons_Constants.cs index fe52a29fb9bca..17020653efd37 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingInvocationReasons_Constants.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/API/UnitTestingInvocationReasons_Constants.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System.Collections.Immutable; - namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; internal readonly partial struct UnitTestingInvocationReasons diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs index cc6921ce48d12..39d8e7780b19c 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/AbstractUnitTestingDocumentDifferenceService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs index 91b2972431612..751cb8ad47b9f 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/ExportUnitTestingIncrementalAnalyzerProviderAttribute.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.Composition; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs index f8e0c8bef76b5..d8b705fd40085 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/IUnitTestingIncrementalAnalyzer.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs index ba19a19a75adc..0746d6bc4e899 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingAsyncProjectWorkItemQueue.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs index a9bf69e2f06c9..4d63273c453cd 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/SolutionCrawler/UnitTestingWorkCoordinator.UnitTestingLowPriorityProcessor.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.UnitTesting.SolutionCrawler; diff --git a/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs b/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs index aeb2a32036ddb..394da78f82438 100644 --- a/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs +++ b/src/Features/Core/Portable/ExternalAccess/UnitTesting/UnitTestingStackTraceServiceAccessor.cs @@ -19,9 +19,20 @@ internal sealed class UnitTestingStackTraceServiceAccessor( { private readonly IStackTraceExplorerService _stackTraceExplorerService = stackTraceExplorerService; - public (Document? document, int lineNumber) GetDocumentAndLine(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame) + public (TextDocument? textDocument, int lineNumber) GetTextDocumentAndLine(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame) => _stackTraceExplorerService.GetDocumentAndLine(workspace.CurrentSolution, parsedFrame.UnderlyingObject); + public (Document? document, int lineNumber) GetDocumentAndLine(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame) + { + var (textDocument, lineNumber) = GetTextDocumentAndLine(workspace, parsedFrame); + if (textDocument is Document document) + { + return (document, lineNumber); + } + + return (null, default); + } + public async Task TryFindMethodDefinitionAsync(Workspace workspace, UnitTestingParsedFrameWrapper parsedFrame, CancellationToken cancellationToken) { var definition = await _stackTraceExplorerService.TryFindDefinitionAsync(workspace.CurrentSolution, parsedFrame.UnderlyingObject, StackFrameSymbolPart.Method, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticData.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticData.cs index 24ab7f2ba2dc0..37113a83ad6c9 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticData.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptDiagnosticData.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptSignatureHelpProviderBase.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptSignatureHelpProviderBase.cs index a1920c9d3aa03..d0466a9cbb719 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptSignatureHelpProviderBase.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptSignatureHelpProviderBase.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SignatureHelp; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; diff --git a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptGlyphHelpers.cs b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptGlyphHelpers.cs index d0764122e64e8..1fb17bd0fa40f 100644 --- a/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptGlyphHelpers.cs +++ b/src/Features/Core/Portable/ExternalAccess/VSTypeScript/VSTypeScriptGlyphHelpers.cs @@ -2,10 +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. -#nullable disable - using Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript; diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index c6de8f95f817a..2985b5b576ddc 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -190,7 +190,9 @@ public async Task GetUpdatesAsync(Solution solution, IImmutableSet instance._sessionId; + + public IEditAndContinueService EncService + => instance._encService; } } #endif diff --git a/src/Features/Core/Portable/ExtractClass/ExtractClassOptions.cs b/src/Features/Core/Portable/ExtractClass/ExtractClassOptions.cs index 7af4fb35e2e08..91fea2f287893 100644 --- a/src/Features/Core/Portable/ExtractClass/ExtractClassOptions.cs +++ b/src/Features/Core/Portable/ExtractClass/ExtractClassOptions.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.ExtractClass; diff --git a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs index 335f892d48495..30069eb82a519 100644 --- a/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/ExtractClass/ExtractClassWithDialogCodeAction.cs @@ -85,7 +85,8 @@ protected override async Task> ComputeOperation _selectedType.IsRecord, TypeKind.Class, extractClassOptions.TypeName, - typeParameters: ExtractTypeHelpers.GetRequiredTypeParametersForMembers(_selectedType, extractClassOptions.MemberAnalysisResults.Select(m => m.Member))); + typeParameters: ExtractTypeHelpers.GetRequiredTypeParametersForMembers( + _selectedType, extractClassOptions.MemberAnalysisResults.SelectAsArray(m => m.Member))); var containingNamespaceDisplay = namespaceService.GetContainingNamespaceDisplay( _selectedType, diff --git a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs index 14a5aa1e13e79..a764882369862 100644 --- a/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs +++ b/src/Features/Core/Portable/ExtractInterface/AbstractExtractInterfaceService.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup; @@ -15,7 +14,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -91,13 +89,13 @@ public async Task AnalyzeTypeAtPositionAsync } var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); - if (semanticModel.GetDeclaredSymbol(typeNode, cancellationToken) is not INamedTypeSymbol typeToExtractFrom) + if (semanticModel.GetDeclaredSymbol(typeNode, cancellationToken) is not INamedTypeSymbol { TypeKind: not TypeKind.Extension } typeToExtractFrom) { var errorMessage = FeaturesResources.Could_not_extract_interface_colon_The_selection_is_not_inside_a_class_interface_struct; return new ExtractInterfaceTypeAnalysisResult(errorMessage); } - var extractableMembers = typeToExtractFrom.GetMembers().Where(IsExtractableMember); + var extractableMembers = typeToExtractFrom.GetMembers().WhereAsArray(IsExtractableMember); if (!extractableMembers.Any()) { var errorMessage = FeaturesResources.Could_not_extract_interface_colon_The_type_does_not_contain_any_member_that_can_be_extracted_to_an_interface; @@ -256,12 +254,12 @@ private async Task ExtractInterfaceToSameFileAsync( internal static ExtractInterfaceOptionsResult GetExtractInterfaceOptions( Document document, INamedTypeSymbol type, - IEnumerable extractableMembers, + ImmutableArray extractableMembers, string containingNamespace, SyntaxFormattingOptions formattingOptions, CancellationToken cancellationToken) { - var conflictingTypeNames = type.ContainingNamespace.GetAllTypes(cancellationToken).Select(t => t.Name); + var conflictingTypeNames = type.ContainingNamespace.GetAllTypes(cancellationToken).SelectAsArray(t => t.Name); var candidateInterfaceName = type.TypeKind == TypeKind.Interface ? type.Name : "I" + type.Name; var defaultInterfaceName = NameGenerator.GenerateUniqueName(candidateInterfaceName, name => !conflictingTypeNames.Contains(name)); var generatedNameTypeParameterSuffix = ExtractTypeHelpers.GetTypeParameterSuffix(document, formattingOptions, type, extractableMembers, cancellationToken); @@ -269,12 +267,11 @@ internal static ExtractInterfaceOptionsResult GetExtractInterfaceOptions( var service = document.Project.Solution.Services.GetRequiredService(); return service.GetExtractInterfaceOptions( document, - [.. extractableMembers], + extractableMembers, defaultInterfaceName, - [.. conflictingTypeNames], + conflictingTypeNames, containingNamespace, - generatedNameTypeParameterSuffix, - cancellationToken); + generatedNameTypeParameterSuffix); } private static async Task GetFormattedSolutionAsync(Solution unformattedSolution, IEnumerable documentIds, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs b/src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs index c043189d7b8ba..9b2631a36edff 100644 --- a/src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs +++ b/src/Features/Core/Portable/ExtractInterface/ExtractInterfaceTypeAnalysisResult.cs @@ -5,6 +5,7 @@ #nullable disable using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.ExtractInterface; @@ -15,7 +16,7 @@ internal sealed class ExtractInterfaceTypeAnalysisResult public readonly Document DocumentToExtractFrom; public readonly SyntaxNode TypeNode; public readonly INamedTypeSymbol TypeToExtractFrom; - public readonly IEnumerable ExtractableMembers; + public readonly ImmutableArray ExtractableMembers; public readonly SyntaxFormattingOptions FormattingOptions; public readonly string ErrorMessage; @@ -23,7 +24,7 @@ public ExtractInterfaceTypeAnalysisResult( Document documentToExtractFrom, SyntaxNode typeNode, INamedTypeSymbol typeToExtractFrom, - IEnumerable extractableMembers, + ImmutableArray extractableMembers, SyntaxFormattingOptions formattingOptions) { CanExtractInterface = true; diff --git a/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs b/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs index 6281c68a90e76..e420c284e41d5 100644 --- a/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs +++ b/src/Features/Core/Portable/ExtractInterface/IExtractInterfaceOptionsService.cs @@ -2,8 +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.Generic; -using System.Threading; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.ExtractInterface; @@ -12,10 +11,9 @@ internal interface IExtractInterfaceOptionsService : IWorkspaceService { ExtractInterfaceOptionsResult GetExtractInterfaceOptions( Document document, - List extractableMembers, + ImmutableArray extractableMembers, string defaultInterfaceName, - List conflictingTypeNames, + ImmutableArray conflictingTypeNames, string defaultNamespace, - string generatedNameTypeParameterSuffix, - CancellationToken cancellationToken); + string generatedNameTypeParameterSuffix); } diff --git a/src/Features/Core/Portable/ExtractMethod/AbstractSyntaxTriviaService.cs b/src/Features/Core/Portable/ExtractMethod/AbstractSyntaxTriviaService.cs index 0137fc6fd450b..b12ececb621d5 100644 --- a/src/Features/Core/Portable/ExtractMethod/AbstractSyntaxTriviaService.cs +++ b/src/Features/Core/Portable/ExtractMethod/AbstractSyntaxTriviaService.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.Generic; using System.Diagnostics; diff --git a/src/Features/Core/Portable/ExtractMethod/Extensions.cs b/src/Features/Core/Portable/ExtractMethod/Extensions.cs index fbe19e31d10d3..ed7117c77b246 100644 --- a/src/Features/Core/Portable/ExtractMethod/Extensions.cs +++ b/src/Features/Core/Portable/ExtractMethod/Extensions.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExtractMethod; diff --git a/src/Features/Core/Portable/ExtractMethod/ExtractMethodFlowControlInformation.cs b/src/Features/Core/Portable/ExtractMethod/ExtractMethodFlowControlInformation.cs index 96320aa1f77d2..7ef992a4f3587 100644 --- a/src/Features/Core/Portable/ExtractMethod/ExtractMethodFlowControlInformation.cs +++ b/src/Features/Core/Portable/ExtractMethod/ExtractMethodFlowControlInformation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExtractMethod; diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs index f0bcd89c04a44..63bef091ccac2 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.AnalyzerResult.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.AnalyzerResult.cs index 0e6d7f3bd3e48..c9f6670cbe216 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.AnalyzerResult.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.AnalyzerResult.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs index fddabcd7a5f73..4fea5a3cca284 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs @@ -4,11 +4,9 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs index 5302d93111925..edb4bfe144ced 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.TypeParameterCollector.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.Generic; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -21,10 +19,10 @@ protected class TypeParameterCollector : SymbolVisitor { private readonly List _typeParameters = []; - public static IEnumerable Collect(ITypeSymbol typeSymbol) + public static IEnumerable Collect(ITypeSymbol? typeSymbol) { var collector = new TypeParameterCollector(); - typeSymbol.Accept(collector); + typeSymbol?.Accept(collector); return collector._typeParameters; } diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableInfo.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableInfo.cs index a217f5dd0629b..537e58af2e7c9 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableInfo.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.VariableInfo.cs @@ -7,7 +7,6 @@ using System; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.cs b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.cs index a65a48fe3c1b2..de90315159099 100644 --- a/src/Features/Core/Portable/ExtractMethod/MethodExtractor.cs +++ b/src/Features/Core/Portable/ExtractMethod/MethodExtractor.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/Features/Core/Portable/ExtractMethod/OperationStatus.cs b/src/Features/Core/Portable/ExtractMethod/OperationStatus.cs index 028fdf2055d99..b727c06ebaaec 100644 --- a/src/Features/Core/Portable/ExtractMethod/OperationStatus.cs +++ b/src/Features/Core/Portable/ExtractMethod/OperationStatus.cs @@ -2,16 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExtractMethod; internal sealed partial class OperationStatus { - public OperationStatus(bool succeeded, string reason) + public OperationStatus(bool succeeded, string? reason) { Succeeded = succeeded; Reasons = reason == null ? [] : [reason]; diff --git a/src/Features/Core/Portable/ExtractMethod/OperationStatus_Statics.cs b/src/Features/Core/Portable/ExtractMethod/OperationStatus_Statics.cs index 1938f6d573211..9a0bed51fdbf3 100644 --- a/src/Features/Core/Portable/ExtractMethod/OperationStatus_Statics.cs +++ b/src/Features/Core/Portable/ExtractMethod/OperationStatus_Statics.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 - namespace Microsoft.CodeAnalysis.ExtractMethod; internal sealed partial class OperationStatus diff --git a/src/Features/Core/Portable/ExtractMethod/OperationStatus`1.cs b/src/Features/Core/Portable/ExtractMethod/OperationStatus`1.cs index f1a621a29d163..e3586a17967f0 100644 --- a/src/Features/Core/Portable/ExtractMethod/OperationStatus`1.cs +++ b/src/Features/Core/Portable/ExtractMethod/OperationStatus`1.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 - namespace Microsoft.CodeAnalysis.ExtractMethod; /// diff --git a/src/Features/Core/Portable/ExtractMethod/UniqueNameGenerator.cs b/src/Features/Core/Portable/ExtractMethod/UniqueNameGenerator.cs index b958ef1103e14..fc790e776cf12 100644 --- a/src/Features/Core/Portable/ExtractMethod/UniqueNameGenerator.cs +++ b/src/Features/Core/Portable/ExtractMethod/UniqueNameGenerator.cs @@ -2,10 +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. -#nullable disable - using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExtractMethod; diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index b8a92a308470b..079506c51424e 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2553,7 +2553,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of The assembly '{0}' containing type '{1}' references .NET Framework, which is not supported. - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. Apply file header preferences diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs index 421cf9b778d42..362e59c3fb62f 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService.ProgressAdapter.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.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs index c3350662e2f92..287b621957335 100644 --- a/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs +++ b/src/Features/Core/Portable/FindUsages/AbstractFindUsagesService_FindImplementations.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs index 90c740c26d18e..b1345c49b226d 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.DefaultDefinitionItem.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindUsages; diff --git a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs index 2371806d424cd..3589eaa3c5a78 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionItem.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionItem.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.Tags; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindUsages; diff --git a/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs b/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs index 6ccf57b185286..21a2763990a1e 100644 --- a/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs +++ b/src/Features/Core/Portable/FindUsages/DefinitionsAndReferences.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using Roslyn.Utilities; diff --git a/src/Features/Core/Portable/FindUsages/FindUsagesHelpers.cs b/src/Features/Core/Portable/FindUsages/FindUsagesHelpers.cs index 1a2d3910fcd87..207c7ba1e36db 100644 --- a/src/Features/Core/Portable/FindUsages/FindUsagesHelpers.cs +++ b/src/Features/Core/Portable/FindUsages/FindUsagesHelpers.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SymbolMapping; @@ -18,25 +19,40 @@ internal static class FindUsagesHelpers public static string GetDisplayName(ISymbol symbol) => symbol.IsConstructor() ? symbol.ContainingType.Name : symbol.Name; + public static Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync( + Document document, int position, CancellationToken cancellationToken) + => GetRelevantSymbolAndProjectAtPositionAsync(document, position, preferPrimaryConstructor: false, cancellationToken); + /// - /// Common helper for both the synchronous and streaming versions of FAR. - /// It returns the symbol we want to search for and the solution we should - /// be searching. - /// - /// Note that the returned may absolutely *not* be - /// the same as document.Project.Solution. This is because - /// there may be symbol mapping involved (for example in Metadata-As-Source - /// scenarios). + /// Common helper for both the synchronous and streaming versions of FAR. It returns the symbol we want to search + /// for and the solution we should be searching. + /// Note that the returned may absolutely *not* be the same as + /// document.Project.Solution. This is because there may be symbol mapping involved (for example in + /// Metadata-As-Source scenarios). /// + /// Whether the named type or primary constructor should be preferred if the + /// position is on a type-header fof a type declaration that has primary constructor parameters. public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync( - Document document, int position, CancellationToken cancellationToken) + Document document, int position, bool preferPrimaryConstructor, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); - var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false); + var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false); if (symbol == null) return null; + if (preferPrimaryConstructor && symbol is INamedTypeSymbol namedType) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var headerFacts = document.GetRequiredLanguageService(); + var semanticFacts = document.GetRequiredLanguageService(); + if (headerFacts.IsOnTypeHeader(root, position, out _) && + semanticFacts.TryGetPrimaryConstructor(namedType, out var primaryConstructor)) + { + symbol = primaryConstructor; + } + } + // If this document is not in the primary workspace, we may want to search for results // in a solution different from the one we started in. Use the starting workspace's // ISymbolMappingService to get a context for searching in the proper solution. diff --git a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs index 9e9b542e57027..65610abfb1b33 100644 --- a/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs +++ b/src/Features/Core/Portable/FindUsages/IRemoteFindUsagesService.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindUsages; diff --git a/src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs b/src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs index 56344da89e667..e84a08fa19d73 100644 --- a/src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs +++ b/src/Features/Core/Portable/FindUsages/SourceReferenceItem.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.Collections.Immutable; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyService.cs b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyService.cs index b23221d2d3f9e..e743bde9072fe 100644 --- a/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyService.cs +++ b/src/Features/Core/Portable/FullyQualify/AbstractFullyQualifyService.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs b/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs index ac299b49b2e62..036250273f867 100644 --- a/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/GenerateComparisonOperators/GenerateComparisonOperatorsCodeRefactoringProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateComparisonOperators; diff --git a/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.ConstructorDelegatingCodeAction.cs b/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.ConstructorDelegatingCodeAction.cs index a6eeb8b1b2547..07117719bc0ec 100644 --- a/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.ConstructorDelegatingCodeAction.cs +++ b/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.ConstructorDelegatingCodeAction.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -12,7 +11,6 @@ using Microsoft.CodeAnalysis.GenerateFromMembers; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateConstructors; diff --git a/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.GenerateConstructorWithDialogCodeAction.cs b/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.GenerateConstructorWithDialogCodeAction.cs index 49de2af49508f..8c1468578f1f6 100644 --- a/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.GenerateConstructorWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/GenerateConstructors/AbstractGenerateConstructorsCodeRefactoringProvider.GenerateConstructorWithDialogCodeAction.cs @@ -9,8 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.GenerateFromMembers; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PickMembers; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndHashWithDialogCodeAction.cs b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndHashWithDialogCodeAction.cs index 9a1146eadc7d7..968d6cd8f2fb5 100644 --- a/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndHashWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/GenerateEqualsAndGetHashCodeFromMembers/GenerateEqualsAndHashWithDialogCodeAction.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PickMembers; using Roslyn.Utilities; diff --git a/src/Features/Core/Portable/GenerateFromMembers/SelectedMemberInfo.cs b/src/Features/Core/Portable/GenerateFromMembers/SelectedMemberInfo.cs index 260a7e17e3dfd..87b370f02facf 100644 --- a/src/Features/Core/Portable/GenerateFromMembers/SelectedMemberInfo.cs +++ b/src/Features/Core/Portable/GenerateFromMembers/SelectedMemberInfo.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Microsoft.CodeAnalysis.CodeRefactorings; namespace Microsoft.CodeAnalysis.GenerateFromMembers; diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs index e7872f4dae9d6..a12d05bd7e66a 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.CodeAction.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.ProjectManagement; @@ -102,7 +101,7 @@ public override object GetOptions(CancellationToken cancellationToken) var generateTypeOptionsService = _document.Project.Solution.Services.GetRequiredService(); var notificationService = _document.Project.Solution.Services.GetService(); var projectManagementService = _document.Project.Solution.Services.GetService(); - var syntaxFactsService = _document.GetLanguageService(); + var syntaxFactsService = _document.GetRequiredLanguageService(); var typeKindValue = GetTypeKindOption(_state); var isPublicOnlyAccessibility = IsPublicOnlyAccessibility(_state, _document.Project); return generateTypeOptionsService.GetGenerateTypeOptions( diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs index 2712595590208..604a4f3d190f7 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.Editor.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateType; diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs index 7c9bf2684747e..03a183873e775 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.State.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.FindSymbols; diff --git a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs index 2fb0774c29d1f..13ecc170cc013 100644 --- a/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs +++ b/src/Features/Core/Portable/GenerateType/AbstractGenerateTypeService.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Internal.Log; @@ -19,7 +18,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GenerateType; diff --git a/src/Features/Core/Portable/GenerateType/GenerateTypeDialogOptions.cs b/src/Features/Core/Portable/GenerateType/GenerateTypeDialogOptions.cs index 30de33e1b39b4..4ee19f68f6817 100644 --- a/src/Features/Core/Portable/GenerateType/GenerateTypeDialogOptions.cs +++ b/src/Features/Core/Portable/GenerateType/GenerateTypeDialogOptions.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 - namespace Microsoft.CodeAnalysis.GenerateType; internal sealed class GenerateTypeDialogOptions( diff --git a/src/Features/Core/Portable/GenerateType/IGenerateTypeOptionService.cs b/src/Features/Core/Portable/GenerateType/IGenerateTypeOptionService.cs index 868e6c9c125c4..4c9325d563ecc 100644 --- a/src/Features/Core/Portable/GenerateType/IGenerateTypeOptionService.cs +++ b/src/Features/Core/Portable/GenerateType/IGenerateTypeOptionService.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 Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Notification; @@ -17,7 +15,7 @@ GenerateTypeOptionsResult GetGenerateTypeOptions( string className, GenerateTypeDialogOptions generateTypeDialogOptions, Document document, - INotificationService notificationService, - IProjectManagementService projectManagementService, + INotificationService? notificationService, + IProjectManagementService? projectManagementService, ISyntaxFactsService syntaxFactsService); } diff --git a/src/Features/Core/Portable/GenerateType/IGenerateTypeService.cs b/src/Features/Core/Portable/GenerateType/IGenerateTypeService.cs index 5ee998341958c..a9720dee79cbe 100644 --- a/src/Features/Core/Portable/GenerateType/IGenerateTypeService.cs +++ b/src/Features/Core/Portable/GenerateType/IGenerateTypeService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.GenerateType; diff --git a/src/Features/Core/Portable/GenerateType/TypeKindOptions.cs b/src/Features/Core/Portable/GenerateType/TypeKindOptions.cs index 68a6c52a4688a..283744f62ace9 100644 --- a/src/Features/Core/Portable/GenerateType/TypeKindOptions.cs +++ b/src/Features/Core/Portable/GenerateType/TypeKindOptions.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; namespace Microsoft.CodeAnalysis.GenerateType; diff --git a/src/Features/Core/Portable/GoToBase/FindBaseHelpers.cs b/src/Features/Core/Portable/GoToBase/FindBaseHelpers.cs index 7b8b75e598fa3..9b3b673588057 100644 --- a/src/Features/Core/Portable/GoToBase/FindBaseHelpers.cs +++ b/src/Features/Core/Portable/GoToBase/FindBaseHelpers.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.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.FindSymbols.FindReferences; diff --git a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs index fc6f6674cc9ab..cd1f8c77540e6 100644 --- a/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs +++ b/src/Features/Core/Portable/GoToDefinition/GoToDefinitionFeatureHelpers.cs @@ -18,6 +18,9 @@ internal static class GoToDefinitionFeatureHelpers public static ISymbol? TryGetPreferredSymbol( Solution solution, ISymbol? symbol, CancellationToken cancellationToken) { + if (symbol is null) + return null; + // VB global import aliases have a synthesized SyntaxTree. // We can't go to the definition of the alias, so use the target type. @@ -46,8 +49,13 @@ internal static class GoToDefinitionFeatureHelpers symbol = definition ?? symbol; // If symbol has a partial implementation part, prefer to go to it, since that is where the body is. - symbol = (symbol as IMethodSymbol)?.PartialImplementationPart ?? symbol; - symbol = (symbol as IPropertySymbol)?.PartialImplementationPart ?? symbol; + symbol = symbol switch + { + IMethodSymbol method => method.PartialImplementationPart, + IPropertySymbol property => property.PartialImplementationPart, + IEventSymbol ev => ev.PartialImplementationPart, + _ => symbol, + } ?? symbol; return symbol; } diff --git a/src/Features/Core/Portable/Highlighting/ExportHighlighterAttribute.cs b/src/Features/Core/Portable/Highlighting/ExportHighlighterAttribute.cs index 783b812d13c90..b86a3654a325a 100644 --- a/src/Features/Core/Portable/Highlighting/ExportHighlighterAttribute.cs +++ b/src/Features/Core/Portable/Highlighting/ExportHighlighterAttribute.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.Composition; using System.Diagnostics.CodeAnalysis; diff --git a/src/Features/Core/Portable/Highlighting/HighlightingService.cs b/src/Features/Core/Portable/Highlighting/HighlightingService.cs index 2f35c389ba6a5..c453490a5ed05 100644 --- a/src/Features/Core/Portable/Highlighting/HighlightingService.cs +++ b/src/Features/Core/Portable/Highlighting/HighlightingService.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.Generic; using System.Composition; diff --git a/src/Features/Core/Portable/Highlighting/IHighlighter.cs b/src/Features/Core/Portable/Highlighting/IHighlighter.cs index d71022d82d6fc..d74cb3cacd135 100644 --- a/src/Features/Core/Portable/Highlighting/IHighlighter.cs +++ b/src/Features/Core/Portable/Highlighting/IHighlighter.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.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/Highlighting/IHighlightingService.cs b/src/Features/Core/Portable/Highlighting/IHighlightingService.cs index 334cbe3935bba..454c0918e5137 100644 --- a/src/Features/Core/Portable/Highlighting/IHighlightingService.cs +++ b/src/Features/Core/Portable/Highlighting/IHighlightingService.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.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 627d67cd72741..d0fa8ba6bfb07 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; @@ -19,7 +18,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InitializeParameter; @@ -43,6 +41,17 @@ internal abstract class AbstractAddParameterCheckCodeRefactoringProvider< where TBinaryExpressionSyntax : TExpressionSyntax where TSimplifierOptions : SimplifierOptions { + private const string s_isPrefix = "Is"; + private const string s_throwIfPrefix = "ThrowIf"; + + private const string s_nullSuffix = "Null"; + private const string s_nullOrEmptySuffix = "NullOrEmpty"; + private const string s_nullOrWhiteSpaceSuffix = "NullOrWhiteSpace"; + + private const string s_throwIfNullName = s_throwIfPrefix + s_nullSuffix; + private const string s_throwIfNullOrEmptyName = s_throwIfPrefix + s_nullOrEmptySuffix; + private const string s_throwIfNullOrWhiteSpaceName = s_throwIfPrefix + s_nullOrWhiteSpaceSuffix; + protected abstract bool CanOffer(SyntaxNode body); protected abstract bool PrefersThrowExpression(TSimplifierOptions options); protected abstract string EscapeResourceString(string input); @@ -109,12 +118,12 @@ protected override async Task> GetRefactoringsForSing { result.Add(CodeAction.Create( FeaturesResources.Add_string_IsNullOrEmpty_check, - cancellationToken => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), simplifierOptions, cancellationToken), + cancellationToken => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, s_nullOrEmptySuffix, simplifierOptions, cancellationToken), nameof(FeaturesResources.Add_string_IsNullOrEmpty_check))); result.Add(CodeAction.Create( FeaturesResources.Add_string_IsNullOrWhiteSpace_check, - cancellationToken => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, nameof(string.IsNullOrWhiteSpace), simplifierOptions, cancellationToken), + cancellationToken => AddStringCheckAsync(document, parameter, functionDeclaration, methodSymbol, blockStatementOpt, s_nullOrWhiteSpaceSuffix, simplifierOptions, cancellationToken), nameof(FeaturesResources.Add_string_IsNullOrWhiteSpace_check))); } @@ -159,7 +168,7 @@ private async Task UpdateDocumentForRefactoringAsync( // commonly used in this regard according to telemetry and UX testing. if (parameter.Type.SpecialType == SpecialType.System_String) { - document = await AddStringCheckAsync(document, parameter, functionDeclaration, (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, nameof(string.IsNullOrEmpty), lazySimplifierOptions, cancellationToken).ConfigureAwait(false); + document = await AddStringCheckAsync(document, parameter, functionDeclaration, (IMethodSymbol)parameter.ContainingSymbol, blockStatementOpt, s_nullOrEmptySuffix, lazySimplifierOptions, cancellationToken).ConfigureAwait(false); continue; } @@ -279,6 +288,9 @@ protected bool ParameterValidForNullCheck(Document document, IParameterSymbol pa if (IsIfNullCheck(statement, parameter)) return false; + if (IsAnyThrowIfNullInvocation(statement, parameter)) + return false; + if (ContainsNullCoalesceCheck( syntaxFacts, semanticModel, statement, parameter, cancellationToken)) @@ -291,11 +303,32 @@ protected bool ParameterValidForNullCheck(Document document, IParameterSymbol pa return true; } + private static bool IsAnyThrowIfNullInvocation(IOperation statement, IParameterSymbol? parameter) + { + if (statement is IExpressionStatementOperation + { + Operation: IInvocationOperation + { + TargetMethod: + { + ContainingType.Name: nameof(ArgumentNullException) or nameof(ArgumentException), + Name: s_throwIfNullName or s_throwIfNullOrEmptyName or s_throwIfNullOrWhiteSpaceName, + }, + Arguments: [{ Value: var argumentValue }, ..] + } + }) + { + if (argumentValue.UnwrapImplicitConversion() is IParameterReferenceOperation parameterReference) + return parameter is null || parameter.Equals(parameterReference.Parameter); + } + + return false; + } + private static bool IsStringCheck(IOperation condition, IParameterSymbol parameter) { - if (condition is IInvocationOperation invocation && - invocation.Arguments.Length == 1 && - IsParameterReference(invocation.Arguments[0].Value, parameter)) + if (condition is IInvocationOperation { Arguments: [{ Value: var argumentValue }] } invocation && + IsParameterReference(argumentValue, parameter)) { var targetMethod = invocation.TargetMethod; if (targetMethod?.Name is nameof(string.IsNullOrEmpty) or nameof(string.IsNullOrWhiteSpace)) @@ -337,13 +370,13 @@ private async Task AddStringCheckAsync( SyntaxNode functionDeclaration, IMethodSymbol method, IBlockOperation? blockStatementOpt, - string methodName, + string methodNameSuffix, TSimplifierOptions options, CancellationToken cancellationToken) { return await AddNullCheckStatementAsync( document, parameter, functionDeclaration, method, blockStatementOpt, - (s, g) => CreateStringCheckStatement(s.Compilation, g, parameter, methodName, options), + (s, g) => CreateStringCheckStatement(s.Compilation, g, parameter, methodNameSuffix, options), cancellationToken).ConfigureAwait(false); } @@ -383,23 +416,62 @@ private static async Task AddNullCheckStatementAsync( } private TStatementSyntax CreateNullCheckStatement(SemanticModel semanticModel, SyntaxGenerator generator, IParameterSymbol parameter, TSimplifierOptions options) - => CreateParameterCheckIfStatement( + { + var argumentNullExceptionType = semanticModel.Compilation.ArgumentNullExceptionType(); + if (parameter.Type.IsReferenceType && argumentNullExceptionType != null) + { + var throwIfNullMethod = argumentNullExceptionType + .GetMembers(s_throwIfNullName) + .OfType() + .FirstOrDefault(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Object }, ..]); + if (throwIfNullMethod != null) + { + return (TStatementSyntax)generator.ExpressionStatement(generator.InvocationExpression( + generator.MemberAccessExpression( + generator.TypeExpression(argumentNullExceptionType), + s_throwIfNullName), + generator.IdentifierName(parameter.Name))); + } + } + + return CreateParameterCheckIfStatement( (TExpressionSyntax)generator.CreateNullCheckExpression(generator.SyntaxGeneratorInternal, semanticModel, parameter.Name), (TStatementSyntax)generator.CreateThrowArgumentNullExceptionStatement(semanticModel.Compilation, parameter), options); + } private TStatementSyntax CreateStringCheckStatement( - Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter, string methodName, TSimplifierOptions options) + Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter, string methodNameSuffix, TSimplifierOptions options) { + var argumentExceptionType = compilation.ArgumentExceptionType(); + if (argumentExceptionType != null) + { + var throwMethodName = "ThrowIf" + methodNameSuffix; + var throwIfNullMethod = argumentExceptionType + .GetMembers(throwMethodName) + .OfType() + .FirstOrDefault(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, ..]); + if (throwIfNullMethod != null) + { + return (TStatementSyntax)generator.ExpressionStatement(generator.InvocationExpression( + generator.MemberAccessExpression( + generator.TypeExpression(argumentExceptionType), + throwMethodName), + generator.IdentifierName(parameter.Name))); + } + } + var stringType = compilation.GetSpecialType(SpecialType.System_String); // generates: if (string.IsXXX(s)) throw new ArgumentException("message", nameof(s)) + var isMethodName = s_isPrefix + methodNameSuffix; var condition = (TExpressionSyntax)generator.InvocationExpression( - generator.MemberAccessExpression( - generator.TypeExpression(stringType), - generator.IdentifierName(methodName)), - generator.Argument(generator.IdentifierName(parameter.Name))); - var throwStatement = (TStatementSyntax)generator.ThrowStatement(CreateArgumentException(compilation, generator, parameter, methodName)); + generator.MemberAccessExpression( + generator.TypeExpression(stringType), + generator.IdentifierName(isMethodName)), + generator.Argument(generator.IdentifierName(parameter.Name))); + var throwStatement = (TStatementSyntax)generator.ThrowStatement( + CreateArgumentException(compilation, generator, parameter, isMethodName)); return CreateParameterCheckIfStatement(condition, throwStatement, options); } @@ -465,6 +537,14 @@ private TStatementSyntax CreateStringCheckStatement( if (statement.IsImplicit) continue; + if (IsAnyThrowIfNullInvocation(statement, parameter: null)) + { + if (IsAnyThrowIfNullInvocation(statement, parameterSymbol)) + return statement; + + continue; + } + if (statement is IConditionalOperation ifStatement) { if (ContainsParameterReference(semanticModel, ifStatement.Condition, parameterSymbol, cancellationToken)) @@ -473,7 +553,7 @@ private TStatementSyntax CreateStringCheckStatement( continue; } - // Stop hunting after we hit something that isn't an if-statement + // Stop hunting after we hit something that isn't an if-statement or a ThrowIfNull invocation. break; } } diff --git a/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs b/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs index 77c6e2b33b298..a8624dcfdc22b 100644 --- a/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.cs +++ b/src/Features/Core/Portable/InlineHints/AbstractInlineParameterNameHintsService.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 System.Threading; using System.Threading.Tasks; @@ -11,7 +10,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InlineHints; diff --git a/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs b/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs index b077a2f293c12..e7f24a25ae4a4 100644 --- a/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs +++ b/src/Features/Core/Portable/InlineHints/AbstractInlineTypeHintsService.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs index d2c0584ca6e04..4a44639ecacef 100644 --- a/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceParameter/AbstractIntroduceParameterCodeRefactoringProvider.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.FindSymbols; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceLocalForExpressionCodeRefactoringProvider.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceLocalForExpressionCodeRefactoringProvider.cs index 7805add83558e..eb2d21aa6251d 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceLocalForExpressionCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceLocalForExpressionCodeRefactoringProvider.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.IntroduceVariable; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs index 98a8f9c2efd9f..f43833a972a0c 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Attribute.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 - namespace Microsoft.CodeAnalysis.IntroduceVariable; internal abstract partial class AbstractIntroduceVariableService diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs index 1ce33eceba825..878de27cc3640 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Block.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.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs index d42bc25f47d16..bb9d8cc65d4e3 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_ConstructorInitializer.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.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs index 602cadea361dc..b9dfe663da057 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Field.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.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs index 281e88446b152..ef98fa21910bf 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Parameter.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.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs index da5378e493828..4a276a44a3df2 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.State_Query.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.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs index 90a727ecad74c..e109d42cf8e86 100644 --- a/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs +++ b/src/Features/Core/Portable/IntroduceVariable/AbstractIntroduceVariableService.cs @@ -13,8 +13,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; diff --git a/src/Features/Core/Portable/IntroduceVariable/IIntroduceVariableService.cs b/src/Features/Core/Portable/IntroduceVariable/IIntroduceVariableService.cs index 9a6554d449a40..36e653bccb636 100644 --- a/src/Features/Core/Portable/IntroduceVariable/IIntroduceVariableService.cs +++ b/src/Features/Core/Portable/IntroduceVariable/IIntroduceVariableService.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/ISymbolDisplayService.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/ISymbolDisplayService.cs index 44eea00d4f175..977b8a461507f 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/ISymbolDisplayService.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/ISymbolDisplayService.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.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionGroups.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionGroups.cs index ef15c0ef3fb44..c00e3548a2b4b 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionGroups.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/SymbolDescriptionGroups.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; namespace Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.AbstractWrappedNamespaceOrTypeSymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.AbstractWrappedNamespaceOrTypeSymbol.cs index e45cdc2916e9a..b245b5fc50205 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.AbstractWrappedNamespaceOrTypeSymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.AbstractWrappedNamespaceOrTypeSymbol.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.Collections.Immutable; using Microsoft.CodeAnalysis.DocumentationComments; diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedEventSymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedEventSymbol.cs index ddb01cd12a000..28ea01f224172 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedEventSymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedEventSymbol.cs @@ -32,5 +32,8 @@ public ImmutableArray ExplicitInterfaceImplementations public IMethodSymbol? RemoveMethod => _symbol.RemoveMethod; public ITypeSymbol Type => _symbol.Type; public NullableAnnotation NullableAnnotation => _symbol.NullableAnnotation; + public IEventSymbol? PartialDefinitionPart => _symbol.PartialDefinitionPart; + public IEventSymbol? PartialImplementationPart => _symbol.PartialImplementationPart; + public bool IsPartialDefinition => _symbol.IsPartialDefinition; } } diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs index 336e01d4dfa18..716d3f4136238 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.WrappedNamedTypeSymbol.cs @@ -9,7 +9,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.DocumentationComments; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MetadataAsSource; @@ -144,6 +143,10 @@ public ImmutableArray ToMinimalDisplayParts(SemanticModel sem public bool IsNativeIntegerType => _symbol.IsNativeIntegerType; + public bool IsExtension => _symbol.IsExtension; + + public IParameterSymbol ExtensionParameter => _symbol.ExtensionParameter; + public bool IsFileLocal => _symbol.IsFileLocal; public INamedTypeSymbol NativeIntegerUnderlyingType => _symbol.NativeIntegerUnderlyingType; diff --git a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs index 3a03e5aebe762..e62bd83955cff 100644 --- a/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/AbstractMetadataAsSourceService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; @@ -14,7 +13,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MetadataAsSource; diff --git a/src/Features/Core/Portable/MetadataAsSource/DocumentationCommentUtilities.cs b/src/Features/Core/Portable/MetadataAsSource/DocumentationCommentUtilities.cs index 8615e35053ce9..66201cd2313ec 100644 --- a/src/Features/Core/Portable/MetadataAsSource/DocumentationCommentUtilities.cs +++ b/src/Features/Core/Portable/MetadataAsSource/DocumentationCommentUtilities.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.Generic; using System.IO; diff --git a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs index c06d750ebcc7b..693b09389f449 100644 --- a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileProvider.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.SymbolMapping; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileService.cs b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileService.cs index bf42a4c73fc68..db51360a23f40 100644 --- a/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileService.cs +++ b/src/Features/Core/Portable/MetadataAsSource/IMetadataAsSourceFileService.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.MetadataAsSource; diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFile.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFile.cs index f8f9b6a167482..05c86a4005c33 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFile.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceFile.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 - namespace Microsoft.CodeAnalysis.MetadataAsSource; internal sealed class MetadataAsSourceFile diff --git a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceHelpers.cs b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceHelpers.cs index 3baf7b5df636e..f591c01b808d8 100644 --- a/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceHelpers.cs +++ b/src/Features/Core/Portable/MetadataAsSource/MetadataAsSourceHelpers.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MetadataAsSource; diff --git a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj index 1424adb97aa32..237de95690587 100644 --- a/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj +++ b/src/Features/Core/Portable/Microsoft.CodeAnalysis.Features.csproj @@ -48,6 +48,7 @@ + diff --git a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs index 78e6882f40b32..852a0581718f6 100644 --- a/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceCodeRefactoringProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Composition; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs b/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs index d5bd227589e2a..941fe57932d2d 100644 --- a/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/MoveStaticMembers/MoveStaticMembersWithDialogCodeAction.cs @@ -127,7 +127,7 @@ protected override async Task> ComputeOperation var members = memberNodes .Select(node => root.GetCurrentNode(node)) .WhereNotNull() - .SelectAsArray(node => (semanticModel.GetDeclaredSymbol(node, cancellationToken), false)); + .SelectAsArray(node => (semanticModel.GetRequiredDeclaredSymbol(node, cancellationToken), false)); var pullMembersUpOptions = PullMembersUpOptionsBuilder.BuildPullMembersUpOptions(newType, members); var movedSolution = await MembersPuller.PullMembersUpAsync(sourceDoc, pullMembersUpOptions, cancellationToken).ConfigureAwait(false); @@ -202,7 +202,7 @@ private static async Task RefactorAndMoveAsync( var members = oldMemberNodes .Select(node => root.GetCurrentNode(node)) .WhereNotNull() - .SelectAsArray(node => (semanticModel.GetDeclaredSymbol(node, cancellationToken), false)); + .SelectAsArray(node => (semanticModel.GetRequiredDeclaredSymbol(node, cancellationToken), false)); newTypeDoc = solutionWithFixedReferences.GetRequiredDocument(newTypeDoc.Id); newTypeRoot = await newTypeDoc.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs index a9dd06c4ec693..fdffff0191d32 100644 --- a/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/AbstractMoveToNamespaceService.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.Generic; using System.Collections.Immutable; @@ -40,6 +38,7 @@ internal abstract class AbstractMoveToNamespaceService AnalyzeTypeAtPositionAsync( int position, CancellationToken cancellationToken) { - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); var node = token.Parent; + if (node is null) + { + return MoveToNamespaceAnalysisResult.Invalid; + } var moveToNamespaceAnalysisResult = await TryAnalyzeNamespaceAsync(document, node, position, cancellationToken).ConfigureAwait(false); @@ -81,8 +84,7 @@ public async Task AnalyzeTypeAtPositionAsync( moveToNamespaceAnalysisResult = await TryAnalyzeNamedTypeAsync(document, node, cancellationToken).ConfigureAwait(false); return moveToNamespaceAnalysisResult ?? MoveToNamespaceAnalysisResult.Invalid; } - - private async Task TryAnalyzeNamespaceAsync( + private async Task TryAnalyzeNamespaceAsync( Document document, SyntaxNode node, int position, CancellationToken cancellationToken) { var declarationSyntax = node.FirstAncestorOrSelf(); @@ -94,7 +96,7 @@ private async Task TryAnalyzeNamespaceAsync( // The underlying ChangeNamespace service doesn't support nested namespace declaration. if (GetNamespaceInSpineCount(declarationSyntax) == 1) { - var changeNamespaceService = document.GetLanguageService(); + var changeNamespaceService = document.GetRequiredLanguageService(); if (await changeNamespaceService.CanChangeNamespaceAsync(document, declarationSyntax, cancellationToken).ConfigureAwait(false)) { var namespaceName = GetNamespaceName(declarationSyntax); @@ -118,14 +120,14 @@ private async Task TryAnalyzeNamedTypeAsync( return MoveToNamespaceAnalysisResult.Invalid; } - SyntaxNode container = null; + SyntaxNode? container = null; // Moving one of the many members declared in global namespace is not currently supported, // but if it's the only member declared, then that's fine. if (namespaceInSpineCount == 0) { - container = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - var syntaxFacts = document.GetLanguageService(); + container = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); if (syntaxFacts.GetMembersOfCompilationUnit(container).Count > 1) { @@ -133,23 +135,30 @@ private async Task TryAnalyzeNamedTypeAsync( } } - if (node is TNamedTypeDeclarationSyntax namedTypeDeclarationSyntax) + var namedTypeDeclarationSyntax = GetNamedTypeDeclarationSyntax(node); + if (namedTypeDeclarationSyntax is null) { - // If we are inside a namespace declaration, then find it as the container. - container ??= GetContainingNamespace(namedTypeDeclarationSyntax); - var changeNamespaceService = document.GetLanguageService(); + return MoveToNamespaceAnalysisResult.Invalid; + } - if (await changeNamespaceService.CanChangeNamespaceAsync(document, container, cancellationToken).ConfigureAwait(false)) - { - var namespaces = await GetNamespacesAsync(document, cancellationToken).ConfigureAwait(false); - return new MoveToNamespaceAnalysisResult(document, namedTypeDeclarationSyntax, GetNamespaceName(container), [.. namespaces], MoveToNamespaceAnalysisResult.ContainerType.NamedType); - } + // If we are inside a namespace declaration, then find it as the container. + container ??= GetContainingNamespace(namedTypeDeclarationSyntax); + if (container is null) + { + return MoveToNamespaceAnalysisResult.Invalid; + } + + var changeNamespaceService = document.GetRequiredLanguageService(); + if (await changeNamespaceService.CanChangeNamespaceAsync(document, container, cancellationToken).ConfigureAwait(false)) + { + var namespaces = await GetNamespacesAsync(document, cancellationToken).ConfigureAwait(false); + return new MoveToNamespaceAnalysisResult(document, namedTypeDeclarationSyntax, GetNamespaceName(container), [.. namespaces], MoveToNamespaceAnalysisResult.ContainerType.NamedType); } return MoveToNamespaceAnalysisResult.Invalid; } - private static TNamespaceDeclarationSyntax GetContainingNamespace(TNamedTypeDeclarationSyntax namedTypeSyntax) + private static TNamespaceDeclarationSyntax? GetContainingNamespace(TNamedTypeDeclarationSyntax namedTypeSyntax) => namedTypeSyntax.FirstAncestorOrSelf(); private static int GetNamespaceInSpineCount(SyntaxNode node) @@ -176,20 +185,26 @@ public Task MoveToNamespaceAsync( private static async Task> GetMemberSymbolsAsync(Document document, SyntaxNode container, CancellationToken cancellationToken) { - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var syntaxFacts = document.GetLanguageService(); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var syntaxFacts = document.GetRequiredLanguageService(); switch (container) { case TNamespaceDeclarationSyntax namespaceNode: var namespaceMembers = syntaxFacts.GetMembersOfBaseNamespaceDeclaration(namespaceNode); - return namespaceMembers.SelectAsArray(member => semanticModel.GetDeclaredSymbol(member, cancellationToken)); + return namespaceMembers + .Select(member => semanticModel.GetDeclaredSymbol(member, cancellationToken)) + .WhereNotNull() + .ToImmutableArray(); case TCompilationUnitSyntax compilationUnit: var compilationUnitMembers = syntaxFacts.GetMembersOfCompilationUnit(compilationUnit); // We are trying to move a selected type from global namespace to the target namespace. // This is supported if the selected type is the only member declared in the global namespace in this document. // (See `TryAnalyzeNamedTypeAsync`) Debug.Assert(compilationUnitMembers.Count == 1); - return compilationUnitMembers.SelectAsArray(member => semanticModel.GetDeclaredSymbol(member, cancellationToken)); + return compilationUnitMembers + .Select(member => semanticModel.GetDeclaredSymbol(member, cancellationToken)) + .WhereNotNull() + .ToImmutableArray(); default: throw ExceptionUtilities.UnexpectedValue(container); @@ -236,15 +251,15 @@ private static async Task MoveTypeToNamespaceAsync( moveSpan, MoveTypeOperationKind.MoveTypeNamespaceScope, cancellationToken).ConfigureAwait(false); - var modifiedDocument = modifiedSolution.GetDocument(document.Id); + var modifiedDocument = modifiedSolution.GetRequiredDocument(document.Id); // Since MoveTypeService doesn't handle linked files, we need to merge the diff ourselves, // otherwise, we will end up with multiple linked documents with different content. var formattingOptions = await document.GetSyntaxFormattingOptionsAsync(cancellationToken).ConfigureAwait(false); var mergedSolution = await PropagateChangeToLinkedDocumentsAsync(modifiedDocument, formattingOptions, cancellationToken).ConfigureAwait(false); - var mergedDocument = mergedSolution.GetDocument(document.Id); + var mergedDocument = mergedSolution.GetRequiredDocument(document.Id); - var syntaxRoot = await mergedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntaxRoot = await mergedDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var syntaxNode = syntaxRoot.GetAnnotatedNodes(AbstractMoveTypeService.NamespaceScopeMovedAnnotation).SingleOrDefault(); // The type might be declared in global namespace @@ -289,7 +304,7 @@ protected static string GetQualifiedName(INamespaceSymbol namespaceSymbol) private static async Task> GetNamespacesAsync(Document document, CancellationToken cancellationToken) { - var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); return compilation.GlobalNamespace.GetAllNamespaces(cancellationToken) .Where(n => n.NamespaceKind == NamespaceKind.Module && n.ContainingAssembly == compilation.Assembly) @@ -301,7 +316,7 @@ public MoveToNamespaceOptionsResult GetChangeNamespaceOptions( string defaultNamespace, ImmutableArray namespaces) { - var syntaxFactsService = document.GetLanguageService(); + var syntaxFactsService = document.GetRequiredLanguageService(); return OptionsService.GetChangeNamespaceOptions( defaultNamespace, diff --git a/src/Features/Core/Portable/MoveToNamespace/IMoveToNamespaceOptionsService.cs b/src/Features/Core/Portable/MoveToNamespace/IMoveToNamespaceOptionsService.cs index cbe2b012e8c8b..3b8ea32faf48e 100644 --- a/src/Features/Core/Portable/MoveToNamespace/IMoveToNamespaceOptionsService.cs +++ b/src/Features/Core/Portable/MoveToNamespace/IMoveToNamespaceOptionsService.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.Collections.Immutable; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Features/Core/Portable/NavigateTo/INavigateToSearchResult.cs b/src/Features/Core/Portable/NavigateTo/INavigateToSearchResult.cs index ded2ae3bca7f4..543e7509c2f74 100644 --- a/src/Features/Core/Portable/NavigateTo/INavigateToSearchResult.cs +++ b/src/Features/Core/Portable/NavigateTo/INavigateToSearchResult.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.Navigation; using Microsoft.CodeAnalysis.PatternMatching; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs index abd3cf893d7be..2b1d14b7578a3 100644 --- a/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs +++ b/src/Features/Core/Portable/NavigateTo/IRemoteNavigateToSearchService.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/Features/Core/Portable/NavigateTo/NavigateToItemKind.cs b/src/Features/Core/Portable/NavigateTo/NavigateToItemKind.cs index e48ea91550f3d..03e5b0805f5ca 100644 --- a/src/Features/Core/Portable/NavigateTo/NavigateToItemKind.cs +++ b/src/Features/Core/Portable/NavigateTo/NavigateToItemKind.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 - namespace Microsoft.CodeAnalysis.NavigateTo; internal static class NavigateToItemKind diff --git a/src/Features/Core/Portable/Navigation/NavigableItemFactory.cs b/src/Features/Core/Portable/Navigation/NavigableItemFactory.cs index 7853327f30496..b4f4f5024132a 100644 --- a/src/Features/Core/Portable/Navigation/NavigableItemFactory.cs +++ b/src/Features/Core/Portable/Navigation/NavigableItemFactory.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Features/Core/Portable/NavigationBar/IRemoteNavigationBarItemService.cs b/src/Features/Core/Portable/NavigationBar/IRemoteNavigationBarItemService.cs index 65e8247a559a1..341d4358e20cc 100644 --- a/src/Features/Core/Portable/NavigationBar/IRemoteNavigationBarItemService.cs +++ b/src/Features/Core/Portable/NavigationBar/IRemoteNavigationBarItemService.cs @@ -6,7 +6,6 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; -using Roslyn.Utilities; using static Microsoft.CodeAnalysis.NavigationBar.RoslynNavigationBarItem; namespace Microsoft.CodeAnalysis.NavigationBar; diff --git a/src/Features/Core/Portable/Notification/INotificationServiceCallback.cs b/src/Features/Core/Portable/Notification/INotificationServiceCallback.cs index 02b40654504cf..cdfa6cdf8372d 100644 --- a/src/Features/Core/Portable/Notification/INotificationServiceCallback.cs +++ b/src/Features/Core/Portable/Notification/INotificationServiceCallback.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; namespace Microsoft.CodeAnalysis.Notification; diff --git a/src/Features/Core/Portable/Organizing/AbstractOrganizingService.cs b/src/Features/Core/Portable/Organizing/AbstractOrganizingService.cs index 0bba5b78437f3..043536a0ead73 100644 --- a/src/Features/Core/Portable/Organizing/AbstractOrganizingService.cs +++ b/src/Features/Core/Portable/Organizing/AbstractOrganizingService.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.Concurrent; using System.Collections.Generic; diff --git a/src/Features/Core/Portable/Organizing/IOrganizingService.cs b/src/Features/Core/Portable/Organizing/IOrganizingService.cs index 46730b8ec98af..f1b34c5f2f9ff 100644 --- a/src/Features/Core/Portable/Organizing/IOrganizingService.cs +++ b/src/Features/Core/Portable/Organizing/IOrganizingService.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.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/Organizing/Organizers/AbstractSyntaxNodeOrganizer.cs b/src/Features/Core/Portable/Organizing/Organizers/AbstractSyntaxNodeOrganizer.cs index 0e8055e5f809a..8062a09d6c8af 100644 --- a/src/Features/Core/Portable/Organizing/Organizers/AbstractSyntaxNodeOrganizer.cs +++ b/src/Features/Core/Portable/Organizing/Organizers/AbstractSyntaxNodeOrganizer.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.Generic; using System.Threading; diff --git a/src/Features/Core/Portable/Organizing/Organizers/ExportSyntaxNodeOrganizerAttribute.cs b/src/Features/Core/Portable/Organizing/Organizers/ExportSyntaxNodeOrganizerAttribute.cs index 36bdec9c11e7a..66368dad3845c 100644 --- a/src/Features/Core/Portable/Organizing/Organizers/ExportSyntaxNodeOrganizerAttribute.cs +++ b/src/Features/Core/Portable/Organizing/Organizers/ExportSyntaxNodeOrganizerAttribute.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.Composition; diff --git a/src/Features/Core/Portable/Organizing/Organizers/ISyntaxOrganizer.cs b/src/Features/Core/Portable/Organizing/Organizers/ISyntaxOrganizer.cs index 006030c16864e..9f41c91f8c7b3 100644 --- a/src/Features/Core/Portable/Organizing/Organizers/ISyntaxOrganizer.cs +++ b/src/Features/Core/Portable/Organizing/Organizers/ISyntaxOrganizer.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.Generic; using System.Threading; diff --git a/src/Features/Core/Portable/PasteTracking/IPasteTrackingService.cs b/src/Features/Core/Portable/PasteTracking/IPasteTrackingService.cs index feb10fc54315c..198a20131f3a1 100644 --- a/src/Features/Core/Portable/PasteTracking/IPasteTrackingService.cs +++ b/src/Features/Core/Portable/PasteTracking/IPasteTrackingService.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 Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.PasteTracking; diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbFileLocatorService.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbFileLocatorService.cs index 69bbcdedc15c1..7362e8f6984fb 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbFileLocatorService.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbFileLocatorService.cs @@ -12,7 +12,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.PdbSourceDocument; diff --git a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs index 5c47785d67d55..cfab78d978320 100644 --- a/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs +++ b/src/Features/Core/Portable/PdbSourceDocument/PdbSourceDocumentMetadataAsSourceFileProvider.cs @@ -15,7 +15,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MetadataAsSource; @@ -301,7 +300,7 @@ internal sealed class PdbSourceDocumentMetadataAsSourceFileProvider( sourceDescription); var documentTooltip = navigateDocument.FilePath + Environment.NewLine + dllPath; - return new MetadataAsSourceFile(navigateDocument.FilePath, navigateLocation, documentName, documentTooltip); + return new MetadataAsSourceFile(navigateDocument.FilePath!, navigateLocation, documentName, documentTooltip); } private ProjectInfo? CreateProjectInfo(Workspace workspace, Project project, ImmutableDictionary pdbCompilationOptions, string assemblyName, string assemblyVersion, SourceHashAlgorithm checksumAlgorithm) diff --git a/src/Features/Core/Portable/PickMembers/IPickMembersService.cs b/src/Features/Core/Portable/PickMembers/IPickMembersService.cs index 94c2d1120ddab..e7d5953121838 100644 --- a/src/Features/Core/Portable/PickMembers/IPickMembersService.cs +++ b/src/Features/Core/Portable/PickMembers/IPickMembersService.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.Collections.Immutable; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeConstants.cs b/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeConstants.cs index a14686162bfc3..70f2775787906 100644 --- a/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeConstants.cs +++ b/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeConstants.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.PreferFrameworkType; @@ -11,6 +9,6 @@ namespace Microsoft.CodeAnalysis.PreferFrameworkType; internal static class PreferFrameworkTypeConstants { public const string PreferFrameworkType = nameof(PreferFrameworkType); - public static readonly ImmutableDictionary Properties = - ImmutableDictionary.Empty.Add(PreferFrameworkType, ""); + public static readonly ImmutableDictionary Properties = + ImmutableDictionary.Empty.Add(PreferFrameworkType, ""); } diff --git a/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeDiagnosticAnalyzerBase.cs index 86c6904057290..4742177544d51 100644 --- a/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/PreferFrameworkType/PreferFrameworkTypeDiagnosticAnalyzerBase.cs @@ -5,8 +5,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.PreferFrameworkType; diff --git a/src/Features/Core/Portable/ProjectManagement/IProjectManagementService.cs b/src/Features/Core/Portable/ProjectManagement/IProjectManagementService.cs index f66e84d3aa0ef..02a405113328e 100644 --- a/src/Features/Core/Portable/ProjectManagement/IProjectManagementService.cs +++ b/src/Features/Core/Portable/ProjectManagement/IProjectManagementService.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.Collections.Generic; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/PullMemberUp/Dialog/IPullMemberUpOptionsService.cs b/src/Features/Core/Portable/PullMemberUp/Dialog/IPullMemberUpOptionsService.cs index 65589de33b85e..d4c8674478659 100644 --- a/src/Features/Core/Portable/PullMemberUp/Dialog/IPullMemberUpOptionsService.cs +++ b/src/Features/Core/Portable/PullMemberUp/Dialog/IPullMemberUpOptionsService.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.Collections.Immutable; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PullMemberUp; diff --git a/src/Features/Core/Portable/PullMemberUp/Dialog/PullMemberUpWithDialogCodeAction.cs b/src/Features/Core/Portable/PullMemberUp/Dialog/PullMemberUpWithDialogCodeAction.cs index 74fb1bbecc84b..b0fd3c54d02b3 100644 --- a/src/Features/Core/Portable/PullMemberUp/Dialog/PullMemberUpWithDialogCodeAction.cs +++ b/src/Features/Core/Portable/PullMemberUp/Dialog/PullMemberUpWithDialogCodeAction.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeRefactorings.PullMemberUp.Dialog; using Microsoft.CodeAnalysis.PullMemberUp; diff --git a/src/Features/Core/Portable/PullMemberUp/MemberAnalysisResult.cs b/src/Features/Core/Portable/PullMemberUp/MemberAnalysisResult.cs index 0a34dbd66a608..e55398c3f241d 100644 --- a/src/Features/Core/Portable/PullMemberUp/MemberAnalysisResult.cs +++ b/src/Features/Core/Portable/PullMemberUp/MemberAnalysisResult.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 - namespace Microsoft.CodeAnalysis.PullMemberUp; internal readonly struct MemberAnalysisResult( diff --git a/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptions.cs b/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptions.cs index 78b372851381e..06921835c682f 100644 --- a/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptions.cs +++ b/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptions.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.Collections.Immutable; using System.Linq; diff --git a/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptionsBuilder.cs b/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptionsBuilder.cs index d2f9b65dfc93b..da24f0ffbfbbf 100644 --- a/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptionsBuilder.cs +++ b/src/Features/Core/Portable/PullMemberUp/PullMembersUpOptionsBuilder.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.Collections.Immutable; using Microsoft.CodeAnalysis.PullMemberUp; diff --git a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs index 90c1d19f2da1c..d6cee9eddc130 100644 --- a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs @@ -10,7 +10,7 @@ internal abstract partial class CommonSemanticQuickInfoProvider { public readonly struct TokenInformation(ImmutableArray symbols, bool showAwaitReturn = false, NullableFlowState nullableFlowState = NullableFlowState.None) { - public readonly ImmutableArray Symbols = symbols; + public ImmutableArray Symbols => symbols.NullToEmpty(); /// /// True if this quick info came from hovering over an 'await' keyword, which we show the return diff --git a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs index 2b3d5c9125e5c..7b981048d818c 100644 --- a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs @@ -195,21 +195,16 @@ private TokenInformation BindToken( var syntaxFacts = languageServices.GetRequiredService(); var enclosingType = semanticModel.GetEnclosingNamedType(token.SpanStart, cancellationToken); - var symbols = GetSymbolsFromToken(token, services, semanticModel, cancellationToken); - var bindableParent = syntaxFacts.TryGetBindableParent(token); - var overloads = bindableParent != null - ? semanticModel.GetMemberGroup(bindableParent, cancellationToken) - : []; - symbols = [.. symbols.Where(IsOk) - .Where(s => IsAccessible(s, enclosingType)) - .Concat(overloads) - .Distinct(SymbolEquivalenceComparer.Instance)]; + var symbolSet = new HashSet(SymbolEquivalenceComparer.Instance); + using var _ = ArrayBuilder.GetInstance(out var filteredSymbols); + + AddSymbols(GetSymbolsFromToken(token, services, semanticModel, cancellationToken), checkAccessibility: true); + AddSymbols(bindableParent != null ? semanticModel.GetMemberGroup(bindableParent, cancellationToken) : [], checkAccessibility: false); - if (symbols.Any()) + if (filteredSymbols is [var firstSymbol, ..]) { - var firstSymbol = symbols.First(); var isAwait = syntaxFacts.IsAwaitKeyword(token); var nullableFlowState = NullableFlowState.None; if (bindableParent != null) @@ -217,7 +212,7 @@ private TokenInformation BindToken( nullableFlowState = GetNullabilityAnalysis(semanticModel, firstSymbol, bindableParent, cancellationToken); } - return new TokenInformation(symbols, isAwait, nullableFlowState); + return new TokenInformation(filteredSymbols.ToImmutableAndClear(), isAwait, nullableFlowState); } // Couldn't bind the token to specific symbols. If it's an operator, see if we can at @@ -226,12 +221,25 @@ private TokenInformation BindToken( { var typeInfo = semanticModel.GetTypeInfo(token.Parent!, cancellationToken); if (IsOk(typeInfo.Type)) - { return new TokenInformation([typeInfo.Type]); - } } - return new TokenInformation([]); + return default; + + void AddSymbols(ImmutableArray symbols, bool checkAccessibility) + { + foreach (var symbol in symbols) + { + if (!IsOk(symbol)) + continue; + + if (checkAccessibility && !IsAccessible(symbol, enclosingType)) + continue; + + if (symbolSet.Add(symbol)) + filteredSymbols.Add(symbol); + } + } } private ImmutableArray GetSymbolsFromToken(SyntaxToken token, SolutionServices services, SemanticModel semanticModel, CancellationToken cancellationToken) diff --git a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsInfo.cs b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsInfo.cs index c1a69a7d374ff..2e46a28525140 100644 --- a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsInfo.cs +++ b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsInfo.cs @@ -13,12 +13,13 @@ namespace Microsoft.CodeAnalysis.QuickInfo; /// the symbol's declaration code /// the language of the symbol /// whether the symbol has comments -internal sealed class OnTheFlyDocsInfo(string symbolSignature, ImmutableArray declarationCode, string language, bool isContentExcluded, bool hasComments = false) +internal sealed class OnTheFlyDocsInfo(string symbolSignature, ImmutableArray declarationCode, string language, bool isContentExcluded, ImmutableArray additionalContext, bool hasComments = false) { public string SymbolSignature { get; } = symbolSignature; - public ImmutableArray DeclarationCode { get; } = declarationCode; + public ImmutableArray DeclarationCode { get; } = declarationCode; public string Language { get; } = language; public bool IsContentExcluded { get; set; } = isContentExcluded; + public ImmutableArray AdditionalContext { get; } = additionalContext; // Added for telemetry collection purposes. public bool HasComments { get; set; } = hasComments; diff --git a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs index 36edcf66005c0..89afe7ae5dd56 100644 --- a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs +++ b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsLogger.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.QuickInfo; diff --git a/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsRelevantFileInfo.cs b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsRelevantFileInfo.cs new file mode 100644 index 0000000000000..aa5390dac3e35 --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/OnTheFlyDocsRelevantFileInfo.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Microsoft.CodeAnalysis.QuickInfo; + +internal sealed record OnTheFlyDocsRelevantFileInfo +{ + public Document Document { get; } + public TextSpan TextSpan { get; } + + public OnTheFlyDocsRelevantFileInfo(Document document, TextSpan textSpan) + { + Document = document; + TextSpan = textSpan; + } +} + diff --git a/src/Features/Core/Portable/QuickInfo/Presentation/TaggedTextExtensions.cs b/src/Features/Core/Portable/QuickInfo/Presentation/TaggedTextExtensions.cs index d235efc36a702..1bea570041ff1 100644 --- a/src/Features/Core/Portable/QuickInfo/Presentation/TaggedTextExtensions.cs +++ b/src/Features/Core/Portable/QuickInfo/Presentation/TaggedTextExtensions.cs @@ -7,7 +7,6 @@ using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Shared.Collections; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.QuickInfo.Presentation; diff --git a/src/Features/Core/Portable/ReplaceConditionalWithStatements/AbstractReplaceConditionalWithStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/ReplaceConditionalWithStatements/AbstractReplaceConditionalWithStatementsCodeRefactoringProvider.cs index 4b9ef9e06ac79..70aec07bdb656 100644 --- a/src/Features/Core/Portable/ReplaceConditionalWithStatements/AbstractReplaceConditionalWithStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ReplaceConditionalWithStatements/AbstractReplaceConditionalWithStatementsCodeRefactoringProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ReplaceConditionalWithStatements; diff --git a/src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs b/src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs index a7adaee6ab60c..ee3b5332292fc 100644 --- a/src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs +++ b/src/Features/Core/Portable/ReplaceMethodWithProperty/AbstractReplaceMethodWithPropertyService.cs @@ -28,7 +28,7 @@ internal abstract class AbstractReplaceMethodWithPropertyService ReplaceGetMethodsAndRemoveSetMethodsAsync( var setMethodDeclaration = await GetMethodDeclarationAsync(setMethod, cancellationToken).ConfigureAwait(false); var setMethodDocument = updatedSolution.GetDocument(setMethodDeclaration?.SyntaxTree); - if (setMethodDocument?.Id == documentId) + if (setMethodDocument?.Id == documentId && setMethodDeclaration != null) { service.RemoveSetMethod(editor, setMethodDeclaration); } diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs index 5a18b1e68b7a1..b79956802af4a 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs @@ -4,10 +4,10 @@ using System; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; @@ -27,7 +27,7 @@ internal abstract class AbstractReplacePropertyWithMethodsService> GetReplacementMembersAsync( - Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, IFieldSymbol propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken); + Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken); protected abstract TCrefSyntax? TryGetCrefSyntax(TIdentifierNameSyntax identifierName); protected abstract TCrefSyntax CreateCrefSyntax(TCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode? parameterType); @@ -54,7 +54,7 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS public async Task ReplaceReferenceAsync( Document document, SyntaxEditor editor, SyntaxNode identifierName, - IPropertySymbol property, IFieldSymbol propertyBackingField, + IPropertySymbol property, IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken) { @@ -79,7 +79,7 @@ private readonly struct ReferenceReplacer private readonly ISemanticFactsService _semanticFacts; private readonly SyntaxEditor _editor; private readonly IPropertySymbol _property; - private readonly IFieldSymbol _propertyBackingField; + private readonly IFieldSymbol? _propertyBackingField; private readonly string _desiredGetMethodName; private readonly string _desiredSetMethodName; @@ -96,7 +96,7 @@ public ReferenceReplacer( SyntaxEditor editor, TIdentifierNameSyntax identifierName, IPropertySymbol property, - IFieldSymbol propertyBackingField, + IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken) @@ -316,12 +316,12 @@ private TCrefSyntax GetCrefReference(TCrefSyntax originalCref) return _service.CreateCrefSyntax(originalCref, newIdentifierToken, parameterType); } - private SyntaxNode QualifyIfAppropriate(SyntaxNode newIdentifierName) + private SyntaxNode QualifyIfAppropriate(IFieldSymbol propertyBackingField, SyntaxNode newIdentifierName) { // See if already qualified appropriate. if (_expression is TIdentifierNameSyntax) { - var container = _propertyBackingField.IsStatic + var container = propertyBackingField.IsStatic ? Generator.TypeExpression(_property.ContainingType) : Generator.ThisExpression(); @@ -338,7 +338,7 @@ private TExpressionSyntax GetReadExpression( if (ShouldReadFromBackingField()) { var newIdentifierToken = AddConflictAnnotation(Generator.Identifier(_propertyBackingField.Name), conflictMessage); - var newIdentifierName = QualifyIfAppropriate(Generator.IdentifierName(newIdentifierToken)); + var newIdentifierName = QualifyIfAppropriate(_propertyBackingField, Generator.IdentifierName(newIdentifierToken)); if (keepTrivia) { @@ -361,7 +361,7 @@ private SyntaxNode GetWriteExpression( if (ShouldWriteToBackingField()) { var newIdentifierToken = AddConflictAnnotation(Generator.Identifier(_propertyBackingField.Name), conflictMessage); - var newIdentifierName = QualifyIfAppropriate(Generator.IdentifierName(newIdentifierToken)); + var newIdentifierName = QualifyIfAppropriate(_propertyBackingField, Generator.IdentifierName(newIdentifierToken)); if (keepTrivia) { @@ -411,6 +411,7 @@ private TExpressionSyntax GetInvocationExpression( return (TExpressionSyntax)invocation; } + [MemberNotNullWhen(true, nameof(_propertyBackingField))] private bool ShouldReadFromBackingField() => _propertyBackingField != null && _property.GetMethod == null; @@ -423,6 +424,7 @@ private SyntaxNode GetSetInvocationExpression( conflictMessage: conflictMessage); } + [MemberNotNullWhen(true, nameof(_propertyBackingField))] private bool ShouldWriteToBackingField() => _propertyBackingField != null && _property.SetMethod == null; diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/IReplacePropertyWithMethodsService.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/IReplacePropertyWithMethodsService.cs index 2c5511e7d5289..9e28e79a9d7c2 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/IReplacePropertyWithMethodsService.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/IReplacePropertyWithMethodsService.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. -#nullable disable - using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host; @@ -16,19 +13,19 @@ namespace Microsoft.CodeAnalysis.ReplacePropertyWithMethods; internal interface IReplacePropertyWithMethodsService : ILanguageService { - Task GetPropertyDeclarationAsync(CodeRefactoringContext context); + Task GetPropertyDeclarationAsync(CodeRefactoringContext context); Task ReplaceReferenceAsync( Document document, SyntaxEditor editor, SyntaxNode identifierName, - IPropertySymbol property, IFieldSymbol propertyBackingField, + IPropertySymbol property, IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken); Task> GetReplacementMembersAsync( Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, - IFieldSymbol propertyBackingField, + IFieldSymbol? propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs b/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs index f462ab458ac68..b6d9a7cb3bedd 100644 --- a/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs +++ b/src/Features/Core/Portable/SemanticSearch/AbstractSemanticSearchService.cs @@ -120,13 +120,11 @@ public async Task ExecuteQueryAsync( } } - await observer.OnCompilationFailureAsync( - emitResult.Diagnostics.SelectAsArray( + var errors = emitResult.Diagnostics.SelectAsArray( d => d.Severity == DiagnosticSeverity.Error, - d => new QueryCompilationError(d.Id, d.GetMessage(), (d.Location.SourceTree == queryTree) ? d.Location.SourceSpan : default)), - cancellationToken).ConfigureAwait(false); + d => new QueryCompilationError(d.Id, d.GetMessage(), (d.Location.SourceTree == queryTree) ? d.Location.SourceSpan : default)); - return CreateResult(FeaturesResources.Semantic_search_query_failed_to_compile); + return CreateResult(errors, FeaturesResources.Semantic_search_query_failed_to_compile); } peStream.Position = 0; @@ -145,7 +143,7 @@ await observer.OnCompilationFailureAsync( if (!TryGetFindMethod(queryAssembly, out var findMethod, out var queryKind, out var errorMessage, out var errorMessageArgs)) { traceSource.TraceInformation($"Semantic search failed: {errorMessage}"); - return CreateResult(errorMessage, errorMessageArgs); + return CreateResult(compilationErrors: [], errorMessage, errorMessageArgs); } var invocationContext = new QueryExecutionContext(queryText, findMethod, observer, classificationOptions, traceSource); @@ -155,7 +153,7 @@ await observer.OnCompilationFailureAsync( if (invocationContext.TerminatedWithException) { - return CreateResult(FeaturesResources.Semantic_search_query_terminated_with_exception); + return CreateResult(compilationErrors: [], FeaturesResources.Semantic_search_query_terminated_with_exception); } } finally @@ -175,10 +173,10 @@ await observer.OnCompilationFailureAsync( } } - return CreateResult(errorMessage: null); + return CreateResult(compilationErrors: [], errorMessage: null); - ExecuteQueryResult CreateResult(string? errorMessage, params string[]? args) - => new(errorMessage, args, emitTime, executionTime); + ExecuteQueryResult CreateResult(ImmutableArray compilationErrors, string? errorMessage, params string[]? args) + => new(compilationErrors, errorMessage, args, emitTime, executionTime); } catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) { diff --git a/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs b/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs index eff18ccc9528c..7c8a819e2268c 100644 --- a/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs +++ b/src/Features/Core/Portable/SemanticSearch/ExecuteQueryResult.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Immutable; using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.SemanticSearch; @@ -10,6 +11,7 @@ namespace Microsoft.CodeAnalysis.SemanticSearch; /// /// The result of Semantic Search query execution. /// +/// Compilation errors. /// An error message if the execution failed. /// /// Arguments to be substituted to . @@ -20,7 +22,8 @@ namespace Microsoft.CodeAnalysis.SemanticSearch; /// Time it took to execute the query. [DataContract] internal readonly record struct ExecuteQueryResult( - [property: DataMember(Order = 0)] string? ErrorMessage, - [property: DataMember(Order = 1)] string[]? ErrorMessageArgs = null, - [property: DataMember(Order = 2)] TimeSpan EmitTime = default, - [property: DataMember(Order = 3)] TimeSpan ExecutionTime = default); + [property: DataMember(Order = 0)] ImmutableArray compilationErrors, + [property: DataMember(Order = 1)] string? ErrorMessage, + [property: DataMember(Order = 2)] string[]? ErrorMessageArgs = null, + [property: DataMember(Order = 3)] TimeSpan EmitTime = default, + [property: DataMember(Order = 4)] TimeSpan ExecutionTime = default); diff --git a/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs b/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs index e59b3ae81a36d..513f95af37dd1 100644 --- a/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs +++ b/src/Features/Core/Portable/SemanticSearch/IRemoteSemanticSearchService.cs @@ -21,7 +21,6 @@ internal interface ICallback { ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, SerializableDefinitionItem definition, CancellationToken cancellationToken); ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken); - ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray errors, CancellationToken cancellationToken); ValueTask GetClassificationOptionsAsync(RemoteServiceCallbackId callbackId, string language, CancellationToken cancellationToken); ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken); ValueTask ItemsCompletedAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken); @@ -43,9 +42,6 @@ public ValueTask OnDefinitionFoundAsync(RemoteServiceCallbackId callbackId, Seri public ValueTask OnUserCodeExceptionAsync(RemoteServiceCallbackId callbackId, UserCodeExceptionInfo exception, CancellationToken cancellationToken) => ((ServerCallback)GetCallback(callbackId)).OnUserCodeExceptionAsync(exception, cancellationToken); - public ValueTask OnCompilationFailureAsync(RemoteServiceCallbackId callbackId, ImmutableArray errors, CancellationToken cancellationToken) - => ((ServerCallback)GetCallback(callbackId)).OnCompilationFailureAsync(errors, cancellationToken); - public ValueTask AddItemsAsync(RemoteServiceCallbackId callbackId, int itemCount, CancellationToken cancellationToken) => ((ServerCallback)GetCallback(callbackId)).AddItemsAsync(itemCount, cancellationToken); @@ -81,17 +77,6 @@ public async ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, } } - public async ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) - { - try - { - await observer.OnCompilationFailureAsync(errors, cancellationToken).ConfigureAwait(false); - } - catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) - { - } - } - public async ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) { try @@ -132,7 +117,7 @@ public static async ValueTask ExecuteQueryAsync(Solution sol var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client == null) { - return new ExecuteQueryResult(FeaturesResources.Semantic_search_only_supported_on_net_core); + return new ExecuteQueryResult(compilationErrors: [], FeaturesResources.Semantic_search_only_supported_on_net_core); } var serverCallback = new ServerCallback(solution, results, classificationOptions); diff --git a/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs index 078e157b8c391..86e46236874c5 100644 --- a/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs +++ b/src/Features/Core/Portable/SemanticSearch/ISemanticSearchResultsObserver.cs @@ -14,7 +14,6 @@ namespace Microsoft.CodeAnalysis.SemanticSearch; internal interface ISemanticSearchResultsObserver { ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken); - ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken); ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken); ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken); ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken); diff --git a/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs b/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs index fb767cfe2bf1f..cc0ab05fae1a8 100644 --- a/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs +++ b/src/Features/Core/Portable/SemanticSearch/SearchCompilationFailureDefinitionItem.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.SemanticSearch; -internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationError error, Document queryDocument) +internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationError error, Document? queryDocument) : DefinitionItem( tags: [ @@ -24,10 +24,7 @@ internal sealed class SearchCompilationFailureDefinitionItem(QueryCompilationErr new TaggedText(TextTags.Text, error.Message) ], nameDisplayParts: [], - sourceSpans: - [ - new DocumentSpan(queryDocument, error.Span) - ], + sourceSpans: queryDocument != null ? [new DocumentSpan(queryDocument, error.Span)] : [], classifiedSpans: [], metadataLocations: [], properties: null, diff --git a/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs b/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs index bb14c8bafb093..927ec6da8ae43 100644 --- a/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs +++ b/src/Features/Core/Portable/SemanticSearch/SearchExceptionDefinitionItem.cs @@ -28,7 +28,7 @@ internal sealed class SearchExceptionDefinitionItem(string message, ImmutableArr .. stackTrace ], nameDisplayParts: exceptionTypeName, - sourceSpans: [documentSpan], + sourceSpans: documentSpan.SourceSpan.IsEmpty ? [] : [documentSpan], classifiedSpans: [], metadataLocations: [], properties: null, diff --git a/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs index e1dbba9c7589e..236e180050fdd 100644 --- a/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/DocumentExtensions.cs @@ -8,10 +8,8 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Shared.Naming; using Roslyn.Utilities; using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.SymbolSpecification; diff --git a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs index 0b78a77b904cb..71b8b98c413aa 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ISymbolExtensions_2.cs @@ -57,6 +57,7 @@ public static Glyph GetGlyph(this ISymbol symbol) { switch (((INamedTypeSymbol)symbol).TypeKind) { + case TypeKind.Extension: case TypeKind.Class: publicIcon = Glyph.ClassPublic; break; diff --git a/src/Features/Core/Portable/Shared/Extensions/ProjectExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/ProjectExtensions.cs index 1721fbd6e0f25..f83adff01ea0d 100644 --- a/src/Features/Core/Portable/Shared/Extensions/ProjectExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/ProjectExtensions.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 - namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static class ProjectExtensions diff --git a/src/Features/Core/Portable/Shared/Extensions/SyntaxTokenListExtensions.cs b/src/Features/Core/Portable/Shared/Extensions/SyntaxTokenListExtensions.cs index 58a720314eb7c..0d2d7404539ff 100644 --- a/src/Features/Core/Portable/Shared/Extensions/SyntaxTokenListExtensions.cs +++ b/src/Features/Core/Portable/Shared/Extensions/SyntaxTokenListExtensions.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 Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Features/Core/Portable/Shared/IDocumentSupportsFeatureService.cs b/src/Features/Core/Portable/Shared/IDocumentSupportsFeatureService.cs index 6e4e21b57f012..a34876f4af3a5 100644 --- a/src/Features/Core/Portable/Shared/IDocumentSupportsFeatureService.cs +++ b/src/Features/Core/Portable/Shared/IDocumentSupportsFeatureService.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/Shared/TestHooks/Legacy/ListenerForwarders.cs b/src/Features/Core/Portable/Shared/TestHooks/Legacy/ListenerForwarders.cs index 64f1070acbd09..60847b4fb7530 100644 --- a/src/Features/Core/Portable/Shared/TestHooks/Legacy/ListenerForwarders.cs +++ b/src/Features/Core/Portable/Shared/TestHooks/Legacy/ListenerForwarders.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.Runtime.CompilerServices; [assembly: TypeForwardedTo(typeof(Microsoft.CodeAnalysis.Shared.TestHooks.AsynchronousOperationListener))] diff --git a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs index 56278394a7f2b..4b472a4b56f8f 100644 --- a/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs +++ b/src/Features/Core/Portable/Shared/Utilities/ExtractTypeHelpers.cs @@ -112,7 +112,8 @@ internal static class ExtractTypeHelpers return (formattedDocument, typeAnnotation); } - public static string GetTypeParameterSuffix(Document document, SyntaxFormattingOptions formattingOptions, INamedTypeSymbol type, IEnumerable extractableMembers, CancellationToken cancellationToken) + public static string GetTypeParameterSuffix( + Document document, SyntaxFormattingOptions formattingOptions, INamedTypeSymbol type, ImmutableArray extractableMembers, CancellationToken cancellationToken) { var typeParameters = GetRequiredTypeParametersForMembers(type, extractableMembers); @@ -127,7 +128,8 @@ public static string GetTypeParameterSuffix(Document document, SyntaxFormattingO return Formatter.Format(syntaxGenerator.SyntaxGeneratorInternal.TypeParameterList(typeParameterNames), document.Project.Solution.Services, formattingOptions, cancellationToken).ToString(); } - public static ImmutableArray GetRequiredTypeParametersForMembers(INamedTypeSymbol type, IEnumerable includedMembers) + public static ImmutableArray GetRequiredTypeParametersForMembers( + INamedTypeSymbol type, ImmutableArray includedMembers) { var potentialTypeParameters = GetPotentialTypeParameters(type); @@ -181,7 +183,8 @@ private static ImmutableArray GetPotentialTypeParameters(I return typeParameters.ToImmutableAndClear(); } - private static ImmutableArray GetDirectlyReferencedTypeParameters(IEnumerable potentialTypeParameters, IEnumerable includedMembers) + private static ImmutableArray GetDirectlyReferencedTypeParameters( + ImmutableArray potentialTypeParameters, ImmutableArray includedMembers) { using var _ = ArrayBuilder.GetInstance(out var directlyReferencedTypeParameters); foreach (var typeParameter in potentialTypeParameters) diff --git a/src/Features/Core/Portable/SignatureHelp/SignatureHelpTriggerInfo.cs b/src/Features/Core/Portable/SignatureHelp/SignatureHelpTriggerInfo.cs index dacb61904988c..c1c203859338e 100644 --- a/src/Features/Core/Portable/SignatureHelp/SignatureHelpTriggerInfo.cs +++ b/src/Features/Core/Portable/SignatureHelp/SignatureHelpTriggerInfo.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.SignatureHelp; internal readonly struct SignatureHelpTriggerInfo diff --git a/src/Features/Core/Portable/Snippets/AbstractSnippetService.cs b/src/Features/Core/Portable/Snippets/AbstractSnippetService.cs index 985c9e525d990..ae14a9d926fa8 100644 --- a/src/Features/Core/Portable/Snippets/AbstractSnippetService.cs +++ b/src/Features/Core/Portable/Snippets/AbstractSnippetService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Snippets.SnippetProviders; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Snippets; diff --git a/src/Features/Core/Portable/Snippets/ISnippetInfoService.cs b/src/Features/Core/Portable/Snippets/ISnippetInfoService.cs index 61cc2c77f5dfe..8f557d7612c01 100644 --- a/src/Features/Core/Portable/Snippets/ISnippetInfoService.cs +++ b/src/Features/Core/Portable/Snippets/ISnippetInfoService.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.Collections.Generic; using Microsoft.CodeAnalysis.Host; @@ -12,6 +10,6 @@ namespace Microsoft.CodeAnalysis.Snippets; internal interface ISnippetInfoService : ILanguageService { IEnumerable GetSnippetsIfAvailable(); - bool SnippetShortcutExists_NonBlocking(string shortcut); + bool SnippetShortcutExists_NonBlocking(string? shortcut); bool ShouldFormatSnippet(SnippetInfo snippetInfo); } diff --git a/src/Features/Core/Portable/Snippets/RoslynLSPSnippetConverter.cs b/src/Features/Core/Portable/Snippets/RoslynLSPSnippetConverter.cs index 7052c0a1bb86b..98d034daaaa42 100644 --- a/src/Features/Core/Portable/Snippets/RoslynLSPSnippetConverter.cs +++ b/src/Features/Core/Portable/Snippets/RoslynLSPSnippetConverter.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Snippets; diff --git a/src/Features/Core/Portable/Snippets/SnippetInfo.cs b/src/Features/Core/Portable/Snippets/SnippetInfo.cs index 3f84621858ba2..a2a84efb42060 100644 --- a/src/Features/Core/Portable/Snippets/SnippetInfo.cs +++ b/src/Features/Core/Portable/Snippets/SnippetInfo.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 - namespace Microsoft.CodeAnalysis.Snippets; internal sealed class SnippetInfo(string shortcut, string title, string description, string path) diff --git a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractConsoleSnippetProvider.cs b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractConsoleSnippetProvider.cs index 574930fcbf3a3..cff1abe841c67 100644 --- a/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractConsoleSnippetProvider.cs +++ b/src/Features/Core/Portable/Snippets/SnippetProviders/AbstractConsoleSnippetProvider.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Snippets.SnippetProviders; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Snippets; diff --git a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs index fc434d7a511e2..bb386db8118ec 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AbstractDocumentDifferenceService.cs @@ -9,12 +9,13 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SolutionCrawler; internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceService { + protected abstract bool IsContainedInMemberBody(SyntaxNode oldMember, TextSpan span); + public async Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { try @@ -100,7 +101,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS } } - private static SyntaxNode? GetChangedMember( + private SyntaxNode? GetChangedMember( ISyntaxFactsService syntaxFactsService, SyntaxNode oldRoot, SyntaxNode newRoot, TextChangeRange range) { // if either old or new tree contains skipped text, re-analyze whole document @@ -119,7 +120,7 @@ internal abstract class AbstractDocumentDifferenceService : IDocumentDifferenceS } // member doesn't contain the change - if (!syntaxFactsService.ContainsInMemberBody(oldMember, range.Span)) + if (!IsContainedInMemberBody(oldMember, range.Span)) { return null; } diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs index b4516fb56f5ca..8cf3421b36ab4 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/AbstractMergeIfStatementsCodeRefactoringProvider.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs index 923d54b3e98d0..49e37866eaad9 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/Consecutive/AbstractSplitIntoConsecutiveIfStatementsCodeRefactoringProvider.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/SplitOrMergeIfStatements/IIfLikeStatementGenerator.cs b/src/Features/Core/Portable/SplitOrMergeIfStatements/IIfLikeStatementGenerator.cs index fe1d6c962f31c..6f6657c7d98b0 100644 --- a/src/Features/Core/Portable/SplitOrMergeIfStatements/IIfLikeStatementGenerator.cs +++ b/src/Features/Core/Portable/SplitOrMergeIfStatements/IIfLikeStatementGenerator.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.Collections.Immutable; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host; diff --git a/src/Features/Core/Portable/StackTraceExplorer/IStackTraceExplorerService.cs b/src/Features/Core/Portable/StackTraceExplorer/IStackTraceExplorerService.cs index 68f42960dfc3b..d8aabcce75751 100644 --- a/src/Features/Core/Portable/StackTraceExplorer/IStackTraceExplorerService.cs +++ b/src/Features/Core/Portable/StackTraceExplorer/IStackTraceExplorerService.cs @@ -16,7 +16,7 @@ internal interface IStackTraceExplorerService : IWorkspaceService /// in a solution. Looks for an exact filepath match first, then defaults to /// a best guess. /// - (Document? document, int line) GetDocumentAndLine(Solution solution, ParsedFrame frame); + (TextDocument? document, int line) GetDocumentAndLine(Solution solution, ParsedFrame frame); Task TryFindDefinitionAsync(Solution solution, ParsedFrame frame, StackFrameSymbolPart symbolPart, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs index e809b29c3f232..22c06ad2d006f 100644 --- a/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs +++ b/src/Features/Core/Portable/StackTraceExplorer/StackTraceExplorerService.cs @@ -2,30 +2,29 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.EmbeddedLanguages.StackFrame; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.StackTraceExplorer; [ExportWorkspaceService(typeof(IStackTraceExplorerService)), Shared] -internal sealed class StackTraceExplorerService : IStackTraceExplorerService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class StackTraceExplorerService() : IStackTraceExplorerService { - [ImportingConstructor] - [System.Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public StackTraceExplorerService() - { - } - - public (Document? document, int line) GetDocumentAndLine(Solution solution, ParsedFrame frame) + public (TextDocument? document, int line) GetDocumentAndLine(Solution solution, ParsedFrame frame) { if (frame is ParsedStackFrame parsedFrame) { @@ -73,7 +72,7 @@ public StackTraceExplorerService() return await StackTraceExplorerUtilities.GetDefinitionAsync(solution, parsedFrame.Root, symbolPart, cancellationToken).ConfigureAwait(false); } - private static ImmutableArray GetFileMatches(Solution solution, StackFrameCompilationUnit root, out int lineNumber) + private static ImmutableArray GetFileMatches(Solution solution, StackFrameCompilationUnit root, out int lineNumber) { lineNumber = 0; if (root.FileInformationExpression is null) @@ -86,19 +85,28 @@ private static ImmutableArray GetFileMatches(Solution solution, StackF RoslynDebug.AssertNotNull(lineString); lineNumber = int.Parse(lineString); + var documentId = solution.GetDocumentIdsWithFilePath(fileName).FirstOrDefault(); + + if (documentId is not null) + { + var document = solution.GetRequiredTextDocument(documentId); + return [document]; + } + var documentName = Path.GetFileName(fileName); - var potentialMatches = new HashSet(); + var potentialMatches = new HashSet(); foreach (var project in solution.Projects) { - foreach (var document in project.Documents) - { - if (document.FilePath == fileName) - { - return [document]; - } + // As of writing there is no way to get all the documents for a specific project + // so we need to check both the main and additional documents. If more document types + // get added this likely will need to be updated. + var allDocuments = project.Documents.Concat(project.AdditionalDocuments); - else if (document.Name == documentName) + foreach (var document in allDocuments) + { + var name = Path.GetFileName(document.Name); + if (name.Equals(documentName, StringComparison.OrdinalIgnoreCase)) { potentialMatches.Add(document); } diff --git a/src/Features/Core/Portable/Structure/BlockStructure.cs b/src/Features/Core/Portable/Structure/BlockStructure.cs index a7353954b5d01..9f232eca1bb21 100644 --- a/src/Features/Core/Portable/Structure/BlockStructure.cs +++ b/src/Features/Core/Portable/Structure/BlockStructure.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.Structure; diff --git a/src/Features/Core/Portable/Structure/BlockStructureContext.cs b/src/Features/Core/Portable/Structure/BlockStructureContext.cs index da3d108b1d6c7..9f7184e063b8a 100644 --- a/src/Features/Core/Portable/Structure/BlockStructureContext.cs +++ b/src/Features/Core/Portable/Structure/BlockStructureContext.cs @@ -5,7 +5,6 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Structure; diff --git a/src/Features/Core/Portable/Structure/BlockTypes.cs b/src/Features/Core/Portable/Structure/BlockTypes.cs index e715be39d990a..4d118d0eda4b1 100644 --- a/src/Features/Core/Portable/Structure/BlockTypes.cs +++ b/src/Features/Core/Portable/Structure/BlockTypes.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 - namespace Microsoft.CodeAnalysis.Structure; internal static class BlockTypes diff --git a/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs b/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs index 30b0271f8ecb1..a81c2a84ff107 100644 --- a/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs +++ b/src/Features/Core/Portable/Structure/Syntax/AbstractBlockStructureProvider.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Structure; diff --git a/src/Features/Core/Portable/Structure/Syntax/BlockStructureExtensions.cs b/src/Features/Core/Portable/Structure/Syntax/BlockStructureExtensions.cs index 52811854f5556..808928eb22b47 100644 --- a/src/Features/Core/Portable/Structure/Syntax/BlockStructureExtensions.cs +++ b/src/Features/Core/Portable/Structure/Syntax/BlockStructureExtensions.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; diff --git a/src/Features/Core/Portable/SymbolMapping/SymbolMappingResult.cs b/src/Features/Core/Portable/SymbolMapping/SymbolMappingResult.cs index 2bd03eb6725b3..ee967b837ed40 100644 --- a/src/Features/Core/Portable/SymbolMapping/SymbolMappingResult.cs +++ b/src/Features/Core/Portable/SymbolMapping/SymbolMappingResult.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.SymbolMapping; internal sealed class SymbolMappingResult diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/IAddReferenceDatabaseWrapper.cs b/src/Features/Core/Portable/SymbolSearch/Windows/IAddReferenceDatabaseWrapper.cs index 85cc85b9ace5d..13908953fa677 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/IAddReferenceDatabaseWrapper.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/IAddReferenceDatabaseWrapper.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 Microsoft.CodeAnalysis.Elfie.Model; namespace Microsoft.CodeAnalysis.SymbolSearch; diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/IPatchService.cs b/src/Features/Core/Portable/SymbolSearch/Windows/IPatchService.cs index afb5da781ae0d..9183ba25a27eb 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/IPatchService.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/IPatchService.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 - namespace Microsoft.CodeAnalysis.SymbolSearch; /// diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/NativePatching.cs b/src/Features/Core/Portable/SymbolSearch/Windows/NativePatching.cs index 9682c6af9ae09..20f280c58e42b 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/NativePatching.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/NativePatching.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.ComponentModel; using System.Runtime.InteropServices; diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.DelayService.cs b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.DelayService.cs index f7f27b62241df..ea917220569fd 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.DelayService.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.DelayService.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; namespace Microsoft.CodeAnalysis.SymbolSearch; diff --git a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.PatchService.cs b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.PatchService.cs index 1390a65cf5497..dae5b6b2b2029 100644 --- a/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.PatchService.cs +++ b/src/Features/Core/Portable/SymbolSearch/Windows/SymbolSearchUpdateEngine.PatchService.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 - namespace Microsoft.CodeAnalysis.SymbolSearch; internal sealed partial class SymbolSearchUpdateEngine diff --git a/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs b/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs index a64225f234aab..677432813b071 100644 --- a/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs +++ b/src/Features/Core/Portable/SyncNamespaces/ISyncNamespacesService.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.SyncNamespaces; diff --git a/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs b/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs index 4fffe4664dde4..707a9827ee98a 100644 --- a/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs +++ b/src/Features/Core/Portable/TaskList/AbstractTaskListService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; diff --git a/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs index 9bf3bec419197..8d88841b022ef 100644 --- a/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs +++ b/src/Features/Core/Portable/ValueTracking/SerializableValueTrackedItem.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking; diff --git a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs index 50eaca6dbf26a..ae379c7485cfc 100644 --- a/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs +++ b/src/Features/Core/Portable/ValueTracking/ValueTrackedItem.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ValueTracking; diff --git a/src/Features/Core/Portable/Workspace/CompileTimeSolutionProvider.cs b/src/Features/Core/Portable/Workspace/CompileTimeSolutionProvider.cs index b7f4fc33ddcc3..ba732e0261e17 100644 --- a/src/Features/Core/Portable/Workspace/CompileTimeSolutionProvider.cs +++ b/src/Features/Core/Portable/Workspace/CompileTimeSolutionProvider.cs @@ -35,7 +35,7 @@ public Factory() } [Obsolete(MefConstruction.FactoryMethodMessage, error: true)] - public IWorkspaceService? CreateService(HostWorkspaceServices workspaceServices) + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) => new CompileTimeSolutionProvider(workspaceServices.Workspace); } diff --git a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs index d57f2479f6d4c..9612c1b5b3da1 100644 --- a/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/Wrapping/AbstractWrappingCodeRefactoringProvider.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs index 445c4d721a952..2c5e666bd5634 100644 --- a/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.cs +++ b/src/Features/Core/Portable/Wrapping/ISyntaxWrapper.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; using System.Threading.Tasks; @@ -25,6 +23,6 @@ internal interface ISyntaxWrapper /// Returns the that produces wrapping code actions for the /// node passed in. Returns if this Wrapper cannot wrap this node. /// - Task TryCreateComputerAsync( + Task TryCreateComputerAsync( Document document, int position, SyntaxNode node, SyntaxWrappingOptions options, bool containsSyntaxError, CancellationToken cancellationToken); } diff --git a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs index 2c6276e32b4e4..4c74a01041c65 100644 --- a/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs +++ b/src/Features/Core/Portable/Wrapping/SeparatedSyntaxList/SeparatedSyntaxListCodeActionComputer.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs b/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs index 5fa29164d5cca..959500575b3d6 100644 --- a/src/Features/Core/Portable/Wrapping/WrapItemsAction.cs +++ b/src/Features/Core/Portable/Wrapping/WrapItemsAction.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.Generic; using System.Collections.Immutable; diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index e1748c27a41ec..08bc6cbc15f53 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2696,8 +2696,8 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Sestavení analyzátoru {0} odkazuje na verzi {1} kompilátoru, která je novější než aktuálně spuštěná verze {2}. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 43b36cb22bd06..04652524b6549 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2696,8 +2696,8 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Die Analyzerassembly „{0}“ verweist auf Version „{1}“ des Compilers, die neuer ist als die aktuell ausgeführte Version „{2}“. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 334a14efbc8d3..08230a9f12fd1 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2696,8 +2696,8 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + El ensamblado del analizador ”{0}” hace referencia a la versión ”{1}” del compilador, que es más reciente que la versión "{2}" que se está ejecutando actualmente. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 8fbd01951b844..86f2a0c048051 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2696,8 +2696,8 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly d'analyseur '{0}' fait référence à la version '{1}' du compilateur, qui est plus récente que la version en cours d'exécution '{2}'. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 8e77413c13e92..d0b40fab387c4 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2696,8 +2696,8 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + L'assembly dell'analizzatore '{0}' fa riferimento alla versione '{1}' del compilatore, che è più recente della versione attualmente in esecuzione '{2}'. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 14cf770dcad68..c5110c4b6d30c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2696,8 +2696,8 @@ Zero-width positive lookbehind assertions are typically used at the beginning of - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + アナライザー アセンブリ '{0}' は、コンパイラのバージョン '{1}' を参照しています。これは、現在実行中のバージョン '{2}' よりも新しいバージョンです。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index f956d6ed66dda..80bf56020bc1e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2696,8 +2696,8 @@ Zero-width positive lookbehind assertions are typically used at the beginning of - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 분석기 어셈블리 '{0}'은(는) 컴파일러의 '{1}' 버전을 참조하며, 이 버전은 현재 실행 중인 버전 '{2}'보다 최신 버전입니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 1864fe88f3e60..4086c13937cb8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2696,8 +2696,8 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Zestaw analizatora „{0}” odwołuje się do wersji „{1}” kompilatora, która jest nowsza niż obecnie uruchomiona wersja „{2}”. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index cde1e451d21ec..d3487bb3dd06f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2696,8 +2696,8 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + O assembly do analisador '{0}' referencia a versão '{1}' do compilador, que é mais recente que a versão em execução no momento '{2}'. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 27cd16b178d43..f83713c17fb83 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2696,8 +2696,8 @@ Zero-width positive lookbehind assertions are typically used at the beginning of - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + Сборка анализатора "{0}" ссылается на версию "{1}" компилятора, являющуюся более новой, чем версия "{2}". diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index ecc2368b68dc1..76137f8e96d3a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2696,8 +2696,8 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + '{0}' çözümleyici bütünleştirilmiş kodu, derleyicinin şu anda çalışan '{2}' sürümünden daha yeni olan '{1}' sürümüne başvurur. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index bfe2ea6dbb3f3..dc56299ba8c2f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2696,8 +2696,8 @@ Zero-width positive lookbehind assertions are typically used at the beginning of - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析器程序集“{0}”引用了编译器的版本“{1}”,该版本高于当前正在运行的版本“{2}”。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 878da57389cfc..6267639a72c23 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2696,8 +2696,8 @@ Zero-width positive lookbehind assertions are typically used at the beginning of - The analyzer assembly '{0}' references version '{1}' of the compiler, which is newer than the currently running version '{2}'. - 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 + Analyzer assembly '{0}' cannot be used because it references version '{1}' of the compiler, which is newer than the currently running version '{2}'. + 分析程式組件 '{0}' 參考編譯器的版本 '{1}' ,比目前執行的版本 '{2}' 還要新。 diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs index 37288f531803f..bedf0d8515caa 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeFixVerifier`2+Test.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 System.Diagnostics.CodeAnalysis; using System.Net; @@ -15,7 +14,6 @@ using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Text; using Xunit; #if !CODE_STYLE @@ -70,6 +68,9 @@ public Test() [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] public new string FixedCode { set => base.FixedCode = value; } + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] + public new string BatchFixedCode { set => base.BatchFixedCode = value; } + /// public string? EditorConfig { diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs index 2585055daa8dc..81f4d7e50dfe9 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/CSharpCodeRefactoringVerifier`1+Test.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; using System.Diagnostics.CodeAnalysis; #if !CODE_STYLE diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs index 7a8ddc3c69d2e..e340cfeee417a 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/SharedVerifierState.cs @@ -4,8 +4,6 @@ using System; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; -using Microsoft.CodeAnalysis.Diagnostics; #if !CODE_STYLE using Microsoft.CodeAnalysis.CodeActions; diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs index 4a89ea39fc172..5a0f22e64aaa3 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeFixVerifier`2+Test.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Testing; using Xunit; diff --git a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs index 7b253715f4971..419ef7295d036 100644 --- a/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs +++ b/src/Features/DiagnosticsTestUtilities/CodeActions/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Testing.Verifiers; using Microsoft.CodeAnalysis.VisualBasic; using Microsoft.CodeAnalysis.VisualBasic.Testing; diff --git a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs index 9fb723a0ac1c4..38fa73de279e3 100644 --- a/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs +++ b/src/Features/DiagnosticsTestUtilities/Diagnostics/AbstractSuppressionAllCodeTests.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.Suppression; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests.Diagnostics; using Roslyn.Utilities; diff --git a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreCSharpVirtualCharService.cs b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreCSharpVirtualCharService.cs index 94c9102be13e7..a173839f7ea4c 100644 --- a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreCSharpVirtualCharService.cs +++ b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreCSharpVirtualCharService.cs @@ -2,10 +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.Text; using Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages { diff --git a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs index d526b8c731c76..dc9b381ca14b6 100644 --- a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs +++ b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualChar.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Text; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs index 39ed6a0ae36d4..b45321a2fa95e 100644 --- a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs +++ b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/AspNetCoreVirtualCharSequence.cs @@ -2,11 +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; using System.Collections.Generic; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages diff --git a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/AspNetCoreBraceMatchingResult.cs b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/AspNetCoreBraceMatchingResult.cs index eb9e9bf4cc3ae..98afd2569aa63 100644 --- a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/AspNetCoreBraceMatchingResult.cs +++ b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/AspNetCoreBraceMatchingResult.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.BraceMatching; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages diff --git a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/IAspNetCoreEmbeddedLanguageBraceMatcher.cs b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/IAspNetCoreEmbeddedLanguageBraceMatcher.cs index e5a1b76c93a19..67ee321b0e3f3 100644 --- a/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/IAspNetCoreEmbeddedLanguageBraceMatcher.cs +++ b/src/Features/ExternalAccess/AspNetCore/EmbeddedLanguages/BraceMatching/IAspNetCoreEmbeddedLanguageBraceMatcher.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.BraceMatching; namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages diff --git a/src/Features/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageBraceMatcher.cs b/src/Features/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageBraceMatcher.cs index e441ba5fc5e45..0810c14bf6534 100644 --- a/src/Features/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageBraceMatcher.cs +++ b/src/Features/ExternalAccess/AspNetCore/Internal/EmbeddedLanguages/AspNetCoreEmbeddedLanguageBraceMatcher.cs @@ -6,9 +6,6 @@ using System.Composition; using System.Threading; using Microsoft.CodeAnalysis.BraceMatching; -using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.EmbeddedLanguages; using Microsoft.CodeAnalysis.Host.Mef; namespace Microsoft.CodeAnalysis.ExternalAccess.AspNetCore.Internal.EmbeddedLanguages diff --git a/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs index 8bd3c115522e8..397b31031be87 100644 --- a/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Analyzer/IExternalCSharpCopilotCodeAnalysisService.cs @@ -17,6 +17,5 @@ internal interface IExternalCSharpCopilotCodeAnalysisService Task> AnalyzeDocumentAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken); Task> GetCachedDiagnosticsAsync(Document document, string promptTitle, CancellationToken cancellationToken); Task StartRefinementSessionAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken); - Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken); Task IsFileExcludedAsync(string filePath, CancellationToken cancellationToken); } diff --git a/src/Features/ExternalAccess/Copilot/GenerateDocumentation/CopilotDocumentationCommentTagType.cs b/src/Features/ExternalAccess/Copilot/GenerateDocumentation/CopilotDocumentationCommentTagType.cs index 42a8df6f6ee7a..2a560477f21e6 100644 --- a/src/Features/ExternalAccess/Copilot/GenerateDocumentation/CopilotDocumentationCommentTagType.cs +++ b/src/Features/ExternalAccess/Copilot/GenerateDocumentation/CopilotDocumentationCommentTagType.cs @@ -9,9 +9,11 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot internal enum CopilotDocumentationCommentTagType { Summary = DocumentationCommentTagType.Summary, + Remarks = DocumentationCommentTagType.Remarks, TypeParam = DocumentationCommentTagType.TypeParam, Param = DocumentationCommentTagType.Param, Returns = DocumentationCommentTagType.Returns, + Value = DocumentationCommentTagType.Value, Exception = DocumentationCommentTagType.Exception, } } diff --git a/src/Features/ExternalAccess/Copilot/GenerateDocumentation/IExternalCSharpCopilotGenerateDocumentationService.cs b/src/Features/ExternalAccess/Copilot/GenerateDocumentation/IExternalCSharpCopilotGenerateDocumentationService.cs index 5c825ea54cc61..4b6bce93306a8 100644 --- a/src/Features/ExternalAccess/Copilot/GenerateDocumentation/IExternalCSharpCopilotGenerateDocumentationService.cs +++ b/src/Features/ExternalAccess/Copilot/GenerateDocumentation/IExternalCSharpCopilotGenerateDocumentationService.cs @@ -2,10 +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.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/src/Features/ExternalAccess/Copilot/GenerateImplementation/IExternalCSharpCopilotGenerateImplementationService.cs b/src/Features/ExternalAccess/Copilot/GenerateImplementation/IExternalCSharpCopilotGenerateImplementationService.cs new file mode 100644 index 0000000000000..906712857e536 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/GenerateImplementation/IExternalCSharpCopilotGenerateImplementationService.cs @@ -0,0 +1,19 @@ +// 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.ExternalAccess.Copilot.GenerateImplementation; +using Microsoft.CodeAnalysis.FindSymbols; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot; + +internal interface IExternalCSharpCopilotGenerateImplementationService +{ + Task> ImplementNotImplementedExceptionsAsync( + Document document, + ImmutableDictionary> methodOrProperties, + CancellationToken cancellationToken); +} diff --git a/src/Features/ExternalAccess/Copilot/GenerateImplementation/ImplementationDetailsWrapper.cs b/src/Features/ExternalAccess/Copilot/GenerateImplementation/ImplementationDetailsWrapper.cs new file mode 100644 index 0000000000000..9cfcb8bdf5c4b --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/GenerateImplementation/ImplementationDetailsWrapper.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation; + +/// +/// Holds details about a replacement node, providing either a message explaining the absence of a replacement or the +/// replacement syntax node itself. One of or must always be set. +/// +internal sealed class ImplementationDetailsWrapper +{ + /// + /// Gets the message explaining why a replacement node is not provided. Either this property or must be set. + /// + public string? Message { get; init; } + + /// + /// Gets the replacement syntax node. Either this property or must be set. + /// + public SyntaxNode? ReplacementNode { get; init; } +} diff --git a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs index 74d95ddf37c15..48af70a1e8696 100644 --- a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/AbstractCopilotCodeAnalysisService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -12,7 +11,9 @@ using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DocumentationComments; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; @@ -40,9 +41,12 @@ internal abstract class AbstractCopilotCodeAnalysisService(IDiagnosticsRefresher protected abstract Task> AnalyzeDocumentCoreAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken); protected abstract Task> GetCachedDiagnosticsCoreAsync(Document document, string promptTitle, CancellationToken cancellationToken); protected abstract Task StartRefinementSessionCoreAsync(Document oldDocument, Document newDocument, Diagnostic? primaryDiagnostic, CancellationToken cancellationToken); - protected abstract Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsCoreAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken); + protected abstract Task GetOnTheFlyDocsPromptCoreAsync(OnTheFlyDocsInfo onTheFlyDocsInfo, CancellationToken cancellationToken); + protected abstract Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseCoreAsync(string prompt, CancellationToken cancellationToken); protected abstract Task IsFileExcludedCoreAsync(string filePath, CancellationToken cancellationToken); protected abstract Task<(Dictionary? responseDictionary, bool isQuotaExceeded)> GetDocumentationCommentCoreAsync(DocumentationCommentProposal proposal, CancellationToken cancellationToken); + protected abstract Task> ImplementNotImplementedExceptionsCoreAsync(Document document, ImmutableDictionary> methodOrProperties, CancellationToken cancellationToken); + protected abstract bool IsImplementNotImplementedExceptionsAvailableCore(); public Task IsAvailableAsync(CancellationToken cancellationToken) => IsAvailableCoreAsync(cancellationToken); @@ -175,12 +179,16 @@ public async Task StartRefinementSessionAsync(Document oldDocument, Document new await StartRefinementSessionCoreAsync(oldDocument, newDocument, primaryDiagnostic, cancellationToken).ConfigureAwait(false); } - public async Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken) + public async Task GetOnTheFlyDocsPromptAsync(OnTheFlyDocsInfo onTheFlyDocsInfo, CancellationToken cancellationToken) + { + return await GetOnTheFlyDocsPromptCoreAsync(onTheFlyDocsInfo, cancellationToken).ConfigureAwait(false); + } + public async Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseAsync(string prompt, CancellationToken cancellationToken) { if (!await IsAvailableAsync(cancellationToken).ConfigureAwait(false)) return (string.Empty, false); - return await GetOnTheFlyDocsCoreAsync(symbolSignature, declarationCode, language, cancellationToken).ConfigureAwait(false); + return await GetOnTheFlyDocsResponseCoreAsync(prompt, cancellationToken).ConfigureAwait(false); } public async Task IsFileExcludedAsync(string filePath, CancellationToken cancellationToken) @@ -193,9 +201,23 @@ public async Task IsFileExcludedAsync(string filePath, CancellationToken c public async Task<(Dictionary? responseDictionary, bool isQuotaExceeded)> GetDocumentationCommentAsync(DocumentationCommentProposal proposal, CancellationToken cancellationToken) { - if (!IsAvailableAsync(cancellationToken).ConfigureAwait(false).GetAwaiter().GetResult()) + if (!await IsAvailableAsync(cancellationToken).ConfigureAwait(false)) return (null, false); return await GetDocumentationCommentCoreAsync(proposal, cancellationToken).ConfigureAwait(false); } + + public async Task IsImplementNotImplementedExceptionsAvailableAsync(CancellationToken cancellationToken) + { + return await IsAvailableAsync(cancellationToken).ConfigureAwait(false) + && IsImplementNotImplementedExceptionsAvailableCore(); + } + + public async Task> ImplementNotImplementedExceptionsAsync( + Document document, + ImmutableDictionary> methodOrProperties, + CancellationToken cancellationToken) + { + return await ImplementNotImplementedExceptionsCoreAsync(document, methodOrProperties, cancellationToken).ConfigureAwait(false); + } } diff --git a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs index f776e58c8b6f6..ac005eb0be0e0 100644 --- a/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs +++ b/src/Features/ExternalAccess/Copilot/Internal/Analyzer/CSharp/CSharpCopilotCodeAnalysisService.cs @@ -11,16 +11,15 @@ using Microsoft.CodeAnalysis.Copilot; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.DocumentationComments; -using Microsoft.CodeAnalysis.Elfie.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Remote; +using Microsoft.CodeAnalysis.QuickInfo; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.Analyzer.CSharp; @@ -29,23 +28,35 @@ internal sealed class CSharpCopilotCodeAnalysisService : AbstractCopilotCodeAnal { private IExternalCSharpCopilotCodeAnalysisService? AnalysisService { get; } private IExternalCSharpCopilotGenerateDocumentationService? GenerateDocumentationService { get; } + private IExternalCSharpOnTheFlyDocsService? OnTheFlyDocsService { get; } + private IExternalCSharpCopilotGenerateImplementationService? GenerateImplementationService { get; } [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpCopilotCodeAnalysisService( [Import(AllowDefault = true)] IExternalCSharpCopilotCodeAnalysisService? externalCopilotService, [Import(AllowDefault = true)] IExternalCSharpCopilotGenerateDocumentationService? externalCSharpCopilotGenerateDocumentationService, + [Import(AllowDefault = true)] IExternalCSharpOnTheFlyDocsService? externalCSharpOnTheFlyDocsService, + [Import(AllowDefault = true)] IExternalCSharpCopilotGenerateImplementationService? externalCSharpCopilotGenerateImplementationService, IDiagnosticsRefresher diagnosticsRefresher ) : base(diagnosticsRefresher) { if (externalCopilotService is null) - FatalError.ReportAndCatch(new NullReferenceException("ExternalCSharpCopilotCodeAnalysisService is unavailable."), ErrorSeverity.Diagnostic); + FatalError.ReportAndCatch(new ArgumentNullException(nameof(externalCopilotService)), ErrorSeverity.Diagnostic); if (externalCSharpCopilotGenerateDocumentationService is null) - FatalError.ReportAndCatch(new NullReferenceException("ExternalCSharpCopilotGenerateDocumentationService is unavailable."), ErrorSeverity.Diagnostic); + FatalError.ReportAndCatch(new ArgumentNullException(nameof(externalCSharpCopilotGenerateDocumentationService)), ErrorSeverity.Diagnostic); + + if (externalCSharpOnTheFlyDocsService is null) + FatalError.ReportAndCatch(new ArgumentNullException(nameof(externalCSharpOnTheFlyDocsService)), ErrorSeverity.Diagnostic); + + if (externalCSharpCopilotGenerateImplementationService is null) + FatalError.ReportAndCatch(new ArgumentNullException(nameof(externalCSharpCopilotGenerateImplementationService)), ErrorSeverity.Diagnostic); AnalysisService = externalCopilotService; GenerateDocumentationService = externalCSharpCopilotGenerateDocumentationService; + OnTheFlyDocsService = externalCSharpOnTheFlyDocsService; + GenerateImplementationService = externalCSharpCopilotGenerateImplementationService; } protected override Task> AnalyzeDocumentCoreAsync(Document document, TextSpan? span, string promptTitle, CancellationToken cancellationToken) @@ -88,10 +99,18 @@ protected override Task StartRefinementSessionCoreAsync(Document oldDocument, Do return Task.CompletedTask; } - protected override Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsCoreAsync(string symbolSignature, ImmutableArray declarationCode, string language, CancellationToken cancellationToken) + protected override Task GetOnTheFlyDocsPromptCoreAsync(OnTheFlyDocsInfo onTheFlyDocsInfo, CancellationToken cancellationToken) { - if (AnalysisService is not null) - return AnalysisService.GetOnTheFlyDocsAsync(symbolSignature, declarationCode, language, cancellationToken); + if (OnTheFlyDocsService is not null) + return OnTheFlyDocsService.GetOnTheFlyDocsPromptAsync(new CopilotOnTheFlyDocsInfoWrapper(onTheFlyDocsInfo), cancellationToken); + + return Task.FromResult(string.Empty); + } + + protected override Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseCoreAsync(string prompt, CancellationToken cancellationToken) + { + if (OnTheFlyDocsService is not null) + return OnTheFlyDocsService.GetOnTheFlyDocsResponseAsync(prompt, cancellationToken); return Task.FromResult((string.Empty, false)); } @@ -131,4 +150,32 @@ protected override Task IsFileExcludedCoreAsync(string filePath, Cancellat return Task.FromResult<(Dictionary?, bool)>((null, false)); } + + protected override bool IsImplementNotImplementedExceptionsAvailableCore() + { + return GenerateImplementationService is not null; + } + + protected override async Task> ImplementNotImplementedExceptionsCoreAsync( + Document document, + ImmutableDictionary> methodOrProperties, + CancellationToken cancellationToken) + { + Contract.ThrowIfNull(GenerateImplementationService); + var nodeToWrappers = await GenerateImplementationService.ImplementNotImplementedExceptionsAsync(document, methodOrProperties, cancellationToken).ConfigureAwait(false); + + var resultBuilder = ImmutableDictionary.CreateBuilder(); + foreach (var nodeToWrapper in nodeToWrappers) + { + resultBuilder.Add( + nodeToWrapper.Key, + new ImplementationDetails + { + ReplacementNode = nodeToWrapper.Value.ReplacementNode, + Message = nodeToWrapper.Value.Message + }); + } + + return resultBuilder.ToImmutable(); + } } diff --git a/src/Features/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchQueryExecutor.cs b/src/Features/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchQueryExecutor.cs new file mode 100644 index 0000000000000..e4ce02fedc3e9 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchQueryExecutor.cs @@ -0,0 +1,109 @@ +// 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.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.SemanticSearch; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.SemanticSearch; + +[Export(typeof(ICopilotSemanticSearchQueryExecutor)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CopilotSemanticSearchQueryExecutor(IHostWorkspaceProvider workspaceProvider) : ICopilotSemanticSearchQueryExecutor +{ + private sealed class ResultsObserver(CancellationTokenSource cancellationSource, int resultCountLimit) : ISemanticSearchResultsObserver + { + private ImmutableList _results = []; + public string? RuntimeException { get; private set; } + public bool LimitReached { get; private set; } + + public ImmutableList Results => _results; + + public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + => ValueTaskFactory.CompletedTask; + + public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + => ValueTaskFactory.CompletedTask; + + public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + { + RuntimeException ??= $"{exception.TypeName.ToVisibleDisplayString(includeLeftToRightMarker: false)}: {exception.Message}{Environment.NewLine}{exception.StackTrace.ToVisibleDisplayString(includeLeftToRightMarker: false)}"; + cancellationSource.Cancel(); + return ValueTaskFactory.CompletedTask; + } + + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) + { + if (!ImmutableInterlocked.Update(ref _results, + list => list.Count == resultCountLimit ? list : list.Add(definition.NameDisplayParts.ToVisibleDisplayString(includeLeftToRightMarker: false)))) + { + LimitReached = true; + cancellationSource.Cancel(); + } + + return ValueTaskFactory.CompletedTask; + } + } + + /// + /// We only use symbol display names, classification is not relevant. + /// + private sealed class DefaultClassificationOptionsProvider : OptionsProvider + { + public static readonly DefaultClassificationOptionsProvider Instance = new(); + + public ValueTask GetOptionsAsync(LanguageServices languageServices, CancellationToken cancellationToken) + => new(ClassificationOptions.Default); + } + + private readonly Workspace _workspace = workspaceProvider.Workspace; + + public async Task ExecuteAsync(string query, int resultCountLimit, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(resultCountLimit > 0); + + using var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var observer = new ResultsObserver(cancellationSource, resultCountLimit); + + try + { + var result = await RemoteSemanticSearchServiceProxy.ExecuteQueryAsync( + _workspace.CurrentSolution, + LanguageNames.CSharp, + query, + SemanticSearchUtilities.ReferenceAssembliesDirectory, + observer, + DefaultClassificationOptionsProvider.Instance, + cancellationSource.Token).ConfigureAwait(false); + + return new CopilotSemanticSearchQueryResults() + { + Symbols = observer.Results, + CompilationErrors = result.compilationErrors.SelectAsArray(e => (e.Id, e.Message)), + Error = (result.ErrorMessage != null) ? string.Format(result.ErrorMessage, result.ErrorMessageArgs ?? []) : null, + LimitReached = false, + }; + } + catch (OperationCanceledException) when (cancellationSource.IsCancellationRequested) + { + return new CopilotSemanticSearchQueryResults() + { + Symbols = observer.Results, + CompilationErrors = [], + Error = observer.RuntimeException != null ? $"The query failed with an exception: {observer.RuntimeException}" : null, + LimitReached = observer.LimitReached, + }; + } + } +} diff --git a/src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index 22b9cd3805bd8..e489cba08dda5 100644 --- a/src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/Features/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -13,12 +13,32 @@ Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentPropose Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentProposedEditWrapper.SymbolName.get -> string? Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentProposedEditWrapper.TagType.get -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType -Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Exception = 4 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType -Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Param = 2 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType -Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Returns = 3 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Exception = 6 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Param = 3 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Remarks = 1 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Returns = 4 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Summary = 0 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType -Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.TypeParam = 1 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.TypeParam = 2 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType.Value = 5 -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentTagType +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.AdditionalContext.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.CopilotOnTheFlyDocsInfoWrapper(Microsoft.CodeAnalysis.QuickInfo.OnTheFlyDocsInfo! onTheFlyDocsInfo) -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.DeclarationCode.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.HasComments.get -> bool +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.IsContentExcluded.get -> bool +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.Language.get -> string! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper.SymbolSignature.get -> string! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsRelevantFileInfoWrapper +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsRelevantFileInfoWrapper.CopilotOnTheFlyDocsRelevantFileInfoWrapper(Microsoft.CodeAnalysis.QuickInfo.OnTheFlyDocsRelevantFileInfo! onTheFlyDocsRelevantFileInfo) -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsRelevantFileInfoWrapper.Document.get -> Microsoft.CodeAnalysis.Document! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsRelevantFileInfoWrapper.TextSpan.get -> Microsoft.CodeAnalysis.Text.TextSpan Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotUtilities +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper.ImplementationDetailsWrapper() -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper.Message.get -> string? +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper.Message.init -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper.ReplacementNode.get -> Microsoft.CodeAnalysis.SyntaxNode? +Microsoft.CodeAnalysis.ExternalAccess.Copilot.GenerateImplementation.ImplementationDetailsWrapper.ReplacementNode.init -> void Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.AnalyzeDocumentAsync(Microsoft.CodeAnalysis.Document! document, Microsoft.CodeAnalysis.Text.TextSpan? span, string! promptTitle, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.GetAvailablePromptTitlesAsync(Microsoft.CodeAnalysis.Document! document, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task>! @@ -29,8 +49,25 @@ Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysis Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotCodeAnalysisService.StartRefinementSessionAsync(Microsoft.CodeAnalysis.Document! oldDocument, Microsoft.CodeAnalysis.Document! newDocument, Microsoft.CodeAnalysis.Diagnostic? primaryDiagnostic, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotGenerateDocumentationService Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotGenerateDocumentationService.GetDocumentationCommentAsync(Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotDocumentationCommentProposalWrapper! proposal, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(System.Collections.Generic.Dictionary? responseDictionary, bool isQuotaExceeded)>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotGenerateImplementationService.ImplementNotImplementedExceptionsAsync(Microsoft.CodeAnalysis.Document! document, System.Collections.Immutable.ImmutableDictionary>! methodOrProperties, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpOnTheFlyDocsService +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpOnTheFlyDocsService.GetOnTheFlyDocsPromptAsync(Microsoft.CodeAnalysis.ExternalAccess.Copilot.CopilotOnTheFlyDocsInfoWrapper! onTheFlyDocsInfo, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpOnTheFlyDocsService.GetOnTheFlyDocsResponseAsync(string! prompt, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<(string! responseString, bool isQuotaExceeded)>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.IExternalCSharpCopilotGenerateImplementationService Microsoft.CodeAnalysis.ExternalAccess.Copilot.RelatedDocuments.ICopilotRelatedDocumentsService Microsoft.CodeAnalysis.ExternalAccess.Copilot.RelatedDocuments.ICopilotRelatedDocumentsService.GetRelatedDocumentIdsAsync(Microsoft.CodeAnalysis.Document! document, int position, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask>! callbackAsync, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CompilationErrors.get -> System.Collections.Generic.IReadOnlyList<(string! id, string! message)>! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CompilationErrors.init -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.CopilotSemanticSearchQueryResults() -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Error.get -> string? +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Error.init -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.LimitReached.get -> bool +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.LimitReached.init -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Symbols.get -> System.Collections.Generic.IReadOnlyList! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.CopilotSemanticSearchQueryResults.Symbols.init -> void +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchQueryExecutor +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchQueryExecutor.ExecuteAsync(string! query, int resultCountLimit, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotServiceImpl Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotServiceImpl.TryGetQueryAsync(string! text, Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.SemanticSearchCopilotContextImpl! context, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.SemanticSearchCopilotContextImpl diff --git a/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsInfoWrapper.cs b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsInfoWrapper.cs new file mode 100644 index 0000000000000..c7e1cb312b447 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsInfoWrapper.cs @@ -0,0 +1,31 @@ +// 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.QuickInfo; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot; + +internal sealed class CopilotOnTheFlyDocsInfoWrapper +{ + private readonly OnTheFlyDocsInfo _onTheFlyDocsInfo; + private readonly ImmutableArray _wrappedDeclarationCode; + private readonly ImmutableArray _wrappedAdditionalContext; + + public CopilotOnTheFlyDocsInfoWrapper(OnTheFlyDocsInfo onTheFlyDocsInfo) + { + _onTheFlyDocsInfo = onTheFlyDocsInfo; + _wrappedDeclarationCode = _onTheFlyDocsInfo.DeclarationCode.SelectAsArray(c => c is not null ? new CopilotOnTheFlyDocsRelevantFileInfoWrapper(c) : null); + _wrappedAdditionalContext = _onTheFlyDocsInfo.AdditionalContext.SelectAsArray(c => c is not null ? new CopilotOnTheFlyDocsRelevantFileInfoWrapper(c) : null); + + } + + public string SymbolSignature => _onTheFlyDocsInfo.SymbolSignature; + public ImmutableArray DeclarationCode => _wrappedDeclarationCode; + public string Language => _onTheFlyDocsInfo.Language; + public bool IsContentExcluded => _onTheFlyDocsInfo.IsContentExcluded; + public ImmutableArray AdditionalContext => _wrappedAdditionalContext; + public bool HasComments => _onTheFlyDocsInfo.HasComments; +} + diff --git a/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsRelevantFileInfoWrapper.cs b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsRelevantFileInfoWrapper.cs new file mode 100644 index 0000000000000..c56903c86cd82 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/CopilotOnTheFlyDocsRelevantFileInfoWrapper.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.QuickInfo; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot; + +internal sealed class CopilotOnTheFlyDocsRelevantFileInfoWrapper(OnTheFlyDocsRelevantFileInfo onTheFlyDocsRelevantFileInfo) +{ + private readonly OnTheFlyDocsRelevantFileInfo _onTheFlyDocsRelevantFileInfo = onTheFlyDocsRelevantFileInfo; + + public Document Document => _onTheFlyDocsRelevantFileInfo.Document; + public TextSpan TextSpan => _onTheFlyDocsRelevantFileInfo.TextSpan; +} diff --git a/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/IExternalCSharpOnTheFlyDocsService.cs b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/IExternalCSharpOnTheFlyDocsService.cs new file mode 100644 index 0000000000000..ba83f33f96137 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/OnTheFlyDocs/IExternalCSharpOnTheFlyDocsService.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot +{ + internal interface IExternalCSharpOnTheFlyDocsService + { + Task GetOnTheFlyDocsPromptAsync(CopilotOnTheFlyDocsInfoWrapper onTheFlyDocsInfo, CancellationToken cancellationToken); + Task<(string responseString, bool isQuotaExceeded)> GetOnTheFlyDocsResponseAsync(string prompt, CancellationToken cancellationToken); + } +} diff --git a/src/Features/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchQueryExecutor.cs b/src/Features/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchQueryExecutor.cs new file mode 100644 index 0000000000000..c06ebd99ae159 --- /dev/null +++ b/src/Features/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchQueryExecutor.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; + +internal interface ICopilotSemanticSearchQueryExecutor +{ + Task ExecuteAsync(string query, int resultCountLimit, CancellationToken cancellationToken); +} + +internal readonly struct CopilotSemanticSearchQueryResults +{ + public required IReadOnlyList Symbols { get; init; } + public required IReadOnlyList<(string id, string message)> CompilationErrors { get; init; } + public string? Error { get; init; } + public required bool LimitReached { get; init; } +} diff --git a/src/Features/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs b/src/Features/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs index 844abf30f4831..9b68b7451a033 100644 --- a/src/Features/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs +++ b/src/Features/ExternalAccess/OmniSharp.CSharp/Formatting/OmniSharpSyntaxFormattingOptionsFactory.cs @@ -2,11 +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 Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CSharp.Formatting; -using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting; -using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.CSharp.Formatting { diff --git a/src/Features/ExternalAccess/OmniSharp/Analyzers/OmnisharpAnalyzerLoaderFactory.cs b/src/Features/ExternalAccess/OmniSharp/Analyzers/OmnisharpAnalyzerLoaderFactory.cs index e876349f28962..c10f63386bfe6 100644 --- a/src/Features/ExternalAccess/OmniSharp/Analyzers/OmnisharpAnalyzerLoaderFactory.cs +++ b/src/Features/ExternalAccess/OmniSharp/Analyzers/OmnisharpAnalyzerLoaderFactory.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Diagnostics; using System.IO; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Analyzers @@ -12,7 +11,7 @@ internal static class OmnisharpAnalyzerAssemblyLoaderFactory public static IAnalyzerAssemblyLoader CreateShadowCopyAnalyzerAssemblyLoader(string? baseDirectory = null) { baseDirectory ??= Path.Combine(Path.GetTempPath(), "CodeAnalysis", "OmnisharpAnalyzerShadowCopies"); - return DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader(baseDirectory); + return AnalyzerAssemblyLoader.CreateNonLockingLoader(baseDirectory); } } } diff --git a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs index e97f58dfcd72a..2b598f0757b29 100644 --- a/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs +++ b/src/Features/ExternalAccess/OmniSharp/DocumentationComments/OmniSharpDocumentationCommentOptionsWrapper.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.DocumentationComments; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Options; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/Features/ExternalAccess/OmniSharp/ExtractClass/IOmniSharpExtractClassOptionsService.cs b/src/Features/ExternalAccess/OmniSharp/ExtractClass/IOmniSharpExtractClassOptionsService.cs index 6e50eefd8ee60..3d48e03532c97 100644 --- a/src/Features/ExternalAccess/OmniSharp/ExtractClass/IOmniSharpExtractClassOptionsService.cs +++ b/src/Features/ExternalAccess/OmniSharp/ExtractClass/IOmniSharpExtractClassOptionsService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ExtractClass { diff --git a/src/Features/ExternalAccess/OmniSharp/ExtractInterface/IOmniSharpExtractInterfaceOptionsService.cs b/src/Features/ExternalAccess/OmniSharp/ExtractInterface/IOmniSharpExtractInterfaceOptionsService.cs index 270e11fdfebeb..f9a7d14bc4137 100644 --- a/src/Features/ExternalAccess/OmniSharp/ExtractInterface/IOmniSharpExtractInterfaceOptionsService.cs +++ b/src/Features/ExternalAccess/OmniSharp/ExtractInterface/IOmniSharpExtractInterfaceOptionsService.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Threading.Tasks; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ExtractInterface { diff --git a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs index 5e690bb39bd71..7d099289b289b 100644 --- a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs +++ b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpOrganizeImportsOptionsWrapper.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.OrganizeImports; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting { diff --git a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs index 334ec58a57273..4c1e3d42db5d8 100644 --- a/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs +++ b/src/Features/ExternalAccess/OmniSharp/Formatting/OmniSharpSyntaxFormattingOptionsWrapper.cs @@ -5,8 +5,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Options; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/Features/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsOptions.cs b/src/Features/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsOptions.cs index 9f7defb342070..792f8545d4a61 100644 --- a/src/Features/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsOptions.cs +++ b/src/Features/ExternalAccess/OmniSharp/InlineHints/OmniSharpInlineHintsOptions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.InlineHints; -using Microsoft.CodeAnalysis.LanguageService; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.InlineHints; diff --git a/src/Features/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs b/src/Features/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs index 0569f5894203e..1b756d5272c24 100644 --- a/src/Features/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs +++ b/src/Features/ExternalAccess/OmniSharp/Internal/ExtractClass/OmniSharpExtractClassOptionsService.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.ExtractClass; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Internal.ExtractClass; diff --git a/src/Features/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs b/src/Features/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs index 4b7bdbb8eb102..5194d0788f843 100644 --- a/src/Features/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs +++ b/src/Features/ExternalAccess/OmniSharp/Internal/ExtractInterface/OmniSharpExtractInterfaceOptionsService.cs @@ -3,9 +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.Threading; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ExtractInterface; using Microsoft.CodeAnalysis.ExtractInterface; using Microsoft.CodeAnalysis.Host.Mef; @@ -22,14 +21,14 @@ internal sealed class OmniSharpExtractInterfaceOptionsService( public ExtractInterfaceOptionsResult GetExtractInterfaceOptions( Document document, - List extractableMembers, + ImmutableArray extractableMembers, string defaultInterfaceName, - List conflictingTypeNames, + ImmutableArray conflictingTypeNames, string defaultNamespace, - string generatedNameTypeParameterSuffix, - CancellationToken cancellationToken) + string generatedNameTypeParameterSuffix) { - var result = _omniSharpExtractInterfaceOptionsService.GetExtractInterfaceOptions(extractableMembers, defaultInterfaceName); + var result = _omniSharpExtractInterfaceOptionsService.GetExtractInterfaceOptions( + [.. extractableMembers], defaultInterfaceName); return new( result.IsCancelled, result.IncludedMembers, diff --git a/src/Features/ExternalAccess/OmniSharp/MetadataAsSource/OmniSharpMetadataAsSourceService.cs b/src/Features/ExternalAccess/OmniSharp/MetadataAsSource/OmniSharpMetadataAsSourceService.cs index 56ec424d51d3d..abc4cde6e6b23 100644 --- a/src/Features/ExternalAccess/OmniSharp/MetadataAsSource/OmniSharpMetadataAsSourceService.cs +++ b/src/Features/ExternalAccess/OmniSharp/MetadataAsSource/OmniSharpMetadataAsSourceService.cs @@ -2,18 +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.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Formatting; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.MetadataAsSource; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.MetadataAsSource { diff --git a/src/Features/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchResult.cs b/src/Features/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchResult.cs index 6f62810cec32c..71ebe9d98ea88 100644 --- a/src/Features/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchResult.cs +++ b/src/Features/ExternalAccess/OmniSharp/NavigateTo/OmniSharpNavigateToSearchResult.cs @@ -2,10 +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.Text; using Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Navigation; using Microsoft.CodeAnalysis.Text; diff --git a/src/Features/ExternalAccess/OmniSharp/Options/IOmniSharpLineFormattingOptionsProvider.cs b/src/Features/ExternalAccess/OmniSharp/Options/IOmniSharpLineFormattingOptionsProvider.cs index 8b92e27116e95..32515de5c6442 100644 --- a/src/Features/ExternalAccess/OmniSharp/Options/IOmniSharpLineFormattingOptionsProvider.cs +++ b/src/Features/ExternalAccess/OmniSharp/Options/IOmniSharpLineFormattingOptionsProvider.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Text; - namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Options { internal interface IOmniSharpLineFormattingOptionsProvider diff --git a/src/Features/ExternalAccess/OmniSharp/Options/OmniSharpSolutionAnalyzerConfigOptionsUpdater.cs b/src/Features/ExternalAccess/OmniSharp/Options/OmniSharpSolutionAnalyzerConfigOptionsUpdater.cs index dd1209b916f32..f040bebf31562 100644 --- a/src/Features/ExternalAccess/OmniSharp/Options/OmniSharpSolutionAnalyzerConfigOptionsUpdater.cs +++ b/src/Features/ExternalAccess/OmniSharp/Options/OmniSharpSolutionAnalyzerConfigOptionsUpdater.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.ImplementType; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Options; diff --git a/src/Features/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs b/src/Features/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs index badcd77216474..cce78bd6662b6 100644 --- a/src/Features/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs +++ b/src/Features/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Rename; using Roslyn.Utilities; diff --git a/src/Features/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs b/src/Features/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs index 52b0b27612bd5..bfd7009ed432c 100644 --- a/src/Features/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs +++ b/src/Features/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.Structure; namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Structure diff --git a/src/Features/Lsif/Generator/CompilerInvocation.cs b/src/Features/Lsif/Generator/CompilerInvocation.cs index faaa64cb2e950..90b116f73d2e6 100644 --- a/src/Features/Lsif/Generator/CompilerInvocation.cs +++ b/src/Features/Lsif/Generator/CompilerInvocation.cs @@ -68,7 +68,7 @@ public static async Task CreateFromInvocationInfoAsync(CompilerInvocati var commandLineParserService = languageServices.GetRequiredService(); var parsedCommandLine = commandLineParserService.Parse(splitCommandLine, Path.GetDirectoryName(invocationInfo.ProjectFilePath), isInteractive: false, sdkDirectory: null); - var analyzerLoader = new DefaultAnalyzerAssemblyLoader(); + var analyzerLoader = new AnalyzerAssemblyLoader(); var projectId = ProjectId.CreateNewId(invocationInfo.ProjectFilePath); diff --git a/src/Features/Lsif/Generator/Generator.cs b/src/Features/Lsif/Generator/Generator.cs index 354e412cf2d71..f885d8521c6d4 100644 --- a/src/Features/Lsif/Generator/Generator.cs +++ b/src/Features/Lsif/Generator/Generator.cs @@ -466,19 +466,22 @@ private static async Task GenerateSemanticTokensAsync( IdFactory idFactory, LsifDocument documentVertex) { + var cancellationToken = CancellationToken.None; + // Compute colorization data. // // Unlike the mainline LSP scenario, where we control both the syntactic colorizer (in-proc syntax tagger) // and the semantic colorizer (LSP semantic tokens) LSIF is more likely to be consumed by clients which may // have different syntactic classification behavior than us, resulting in missing colors. To avoid this, we // include syntax tokens in the generated data. + var text = await document.GetTextAsync(cancellationToken); var data = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( // Just get the pure-lsp semantic tokens here. document, - spans: [], + spans: [text.Lines.GetLinePositionSpan(new TextSpan(0, text.Length))], supportsVisualStudioExtensions: true, options: Classification.ClassificationOptions.Default, - cancellationToken: CancellationToken.None); + cancellationToken); var semanticTokensResult = new SemanticTokensResult(new SemanticTokens { Data = data }, idFactory); var semanticTokensEdge = Edge.Create(Methods.TextDocumentSemanticTokensFullName, documentVertex.GetId(), semanticTokensResult.GetId(), idFactory); diff --git a/src/Features/Lsif/Generator/Graph/Edge.cs b/src/Features/Lsif/Generator/Graph/Edge.cs index 1a10b36ecf2dd..5752a03863dd8 100644 --- a/src/Features/Lsif/Generator/Graph/Edge.cs +++ b/src/Features/Lsif/Generator/Graph/Edge.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Newtonsoft.Json; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Graph { diff --git a/src/Features/Lsif/Generator/Graph/RangeBasedDocumentSymbol.cs b/src/Features/Lsif/Generator/Graph/RangeBasedDocumentSymbol.cs index 70db03d5bbc5f..2e55b2cd33773 100644 --- a/src/Features/Lsif/Generator/Graph/RangeBasedDocumentSymbol.cs +++ b/src/Features/Lsif/Generator/Graph/RangeBasedDocumentSymbol.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; using Newtonsoft.Json; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Graph; diff --git a/src/Features/Lsif/Generator/Logging/LsifFormatLogger.cs b/src/Features/Lsif/Generator/Logging/LsifFormatLogger.cs index 190dc336a9015..7e2a1871b9e45 100644 --- a/src/Features/Lsif/Generator/Logging/LsifFormatLogger.cs +++ b/src/Features/Lsif/Generator/Logging/LsifFormatLogger.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Writing; using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator.Logging { diff --git a/src/Features/RulesMissingDocumentation.md b/src/Features/RulesMissingDocumentation.md index 07536d5ee0624..16fad36e94713 100644 --- a/src/Features/RulesMissingDocumentation.md +++ b/src/Features/RulesMissingDocumentation.md @@ -29,3 +29,4 @@ JSON001 | | Invalid JSON pattern | JSON002 | | Probable JSON string detected | RE0001 | | Invalid regex pattern | RemoveUnnecessaryImportsFixable | | | +IDE3000 | | Implement with Copilot | \ No newline at end of file diff --git a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 22e23e97cd922..2fadd765778cc 100644 --- a/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/Features/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -153,9 +153,9 @@ public async Task StartDebuggingSession_CapturingDocuments(bool captureAllDocume EnterBreakState(debuggingSession); var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); + Assert.Equal(ModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); - AssertEx.Equal([$"P.csproj: (0,0)-(0,0): Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}"], InspectDiagnostics(emitDiagnostics)); + AssertEx.Equal([$"P.csproj: (0,0)-(0,0): Error ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFileB.Path)}"], InspectDiagnostics(emitDiagnostics)); EndDebuggingSession(debuggingSession); } @@ -595,9 +595,9 @@ public async Task ErrorReadingPdbFile() // an error occurred so we need to call update to determine whether we have changes to apply or not: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); + Assert.Equal(ModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); - AssertEx.Equal([$"proj.csproj: (0,0)-(0,0): Warning ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); + AssertEx.Equal([$"proj.csproj: (0,0)-(0,0): Error ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); EndDebuggingSession(debuggingSession); @@ -642,9 +642,9 @@ public async Task ErrorReadingSourceFile() // an error occurred so we need to call update to determine whether we have changes to apply or not: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); + Assert.Equal(ModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); - AssertEx.Equal([$"test.csproj: (0,0)-(0,0): Warning ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); + AssertEx.Equal([$"test.csproj: (0,0)-(0,0): Error ENC1006: {string.Format(FeaturesResources.UnableToReadSourceFileOrPdb, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); fileLock.Dispose(); @@ -1168,9 +1168,9 @@ public async Task RudeEdits_DocumentOutOfSync(bool breakMode) // since the document is out-of-sync we need to call update to determine whether we have changes to apply or not: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); + Assert.Equal(ModuleUpdateStatus.Blocked, updates.Status); Assert.Empty(updates.Updates); - AssertEx.Equal([$"proj.csproj: (0,0)-(0,0): Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); + AssertEx.Equal([$"proj.csproj: (0,0)-(0,0): Error ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); // update the file to match the build: sourceFile.WriteAllText(source0, Encoding.UTF8); @@ -2312,8 +2312,8 @@ public async Task ValidSignificantChange_FileUpdateNotObservedBeforeDebuggingSes // since the document is out-of-sync we need to call update to determine whether we have changes to apply or not: var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); - Assert.Equal(ModuleUpdateStatus.None, updates.Status); - AssertEx.Equal([$"test.csproj: (0,0)-(0,0): Warning ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); + Assert.Equal(ModuleUpdateStatus.Blocked, updates.Status); + AssertEx.Equal([$"test.csproj: (0,0)-(0,0): Error ENC1005: {string.Format(FeaturesResources.DocumentIsOutOfSyncWithDebuggee, sourceFile.Path)}"], InspectDiagnostics(emitDiagnostics)); // undo: solution = solution.WithDocumentText(documentId, CreateText(source1)); @@ -3111,6 +3111,44 @@ public async Task ValidSignificantChange_SourceGenerators_DocumentRemove() EndDebuggingSession(debuggingSession); } + [Fact] + public async Task ValidInsignificantChange() + { + var sourceV1 = "class C1 { void M() { /* System.Console.WriteLine(1); */ } }"; + var sourceV2 = "class C1 { void M() { /* System.Console.WriteLine(2); */ } }"; + + using var _ = CreateWorkspace(out var solution, out var service); + (solution, var document1) = AddDefaultTestProject(solution, sourceV1); + + var moduleId = EmitAndLoadLibraryToDebuggee(sourceV1); + + var debuggingSession = await StartDebuggingSessionAsync(service, solution); + + // change the source (valid edit): + solution = solution.WithDocumentText(document1.Id, CreateText(sourceV2)); + var document2 = solution.GetDocument(document1.Id); + + var diagnostics1 = await service.GetDocumentDiagnosticsAsync(document2, s_noActiveSpans, CancellationToken.None); + AssertEx.Empty(diagnostics1); + + // validate solution update status and emit: + var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); + Assert.Empty(emitDiagnostics); + Assert.Equal(ModuleUpdateStatus.None, updates.Status); + + // solution has been updated: + var text = await debuggingSession.LastCommittedSolution.GetRequiredProject(document1.Project.Id).GetDocument(document1.Id).GetTextAsync(); + Assert.Equal(sourceV2, text.ToString()); + + EndDebuggingSession(debuggingSession); + + AssertEx.SequenceEqual( + [ + "Debugging_EncSession: SolutionSessionId={00000000-AAAA-AAAA-AAAA-000000000000}|SessionId=1|SessionCount=0|EmptySessionCount=0|HotReloadSessionCount=1|EmptyHotReloadSessionCount=0", + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=False|HadValidInsignificantChanges=True|RudeEditsCount=0|EmitDeltaErrorIdCount=0|InBreakState=False|Capabilities=0|ProjectIdsWithAppliedChanges=|ProjectIdsWithUpdatedBaselines=" + ], _telemetryLog); + } + [Fact] public async Task RudeEdit() { diff --git a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs index 1b4aec81d413d..0d9924e15d765 100644 --- a/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs +++ b/src/Features/Test/EditAndContinue/EmitSolutionUpdateResultsTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -16,7 +15,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests diff --git a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs index 9cd39cf62b4e9..cb7b9c834e12e 100644 --- a/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs +++ b/src/Features/Test/EditAndContinue/RemoteEditAndContinueServiceTests.cs @@ -21,7 +21,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Roslyn.VisualStudio.Next.UnitTests.EditAndContinue; diff --git a/src/Features/Test/EditAndContinue/UnitTestingHotReloadServiceTests.cs b/src/Features/Test/EditAndContinue/UnitTestingHotReloadServiceTests.cs index fe44ddf327e40..efbcd0fd292b5 100644 --- a/src/Features/Test/EditAndContinue/UnitTestingHotReloadServiceTests.cs +++ b/src/Features/Test/EditAndContinue/UnitTestingHotReloadServiceTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading; diff --git a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs index 60062cf1b21f2..a222cda9bbcff 100644 --- a/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs +++ b/src/Features/Test/EditAndContinue/WatchHotReloadServiceTests.cs @@ -34,10 +34,11 @@ public async Task Test() // See https://github.com/dotnet/sdk/blob/main/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs#L125 var source1 = "class C { void M() { System.Console.WriteLine(1); } }"; - var source2 = "class C { void M() { System.Console.WriteLine(2); } }"; - var source3 = "class C { void M() { System.Console.WriteLine(2); } }"; - var source4 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }"; - var source5 = "class C { void M() { Unknown(); } }"; + var source2 = "class C { void M() { System.Console.WriteLine(2); /*2*/} }"; + var source3 = "class C { void M() { System.Console.WriteLine(2); /*3*/} }"; + var source4 = "class C { void M() { System.Console.WriteLine(2); } }"; + var source5 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }"; + var source6 = "class C { void M() { Unknown(); } }"; var dir = Temp.CreateDirectory(); var sourceFileA = dir.CreateFile("A.cs").WriteAllText(source1, Encoding.UTF8); @@ -79,9 +80,29 @@ public async Task Test() Assert.Equal(1, result.ProjectUpdates.Length); AssertEx.Equal([0x02000002], result.ProjectUpdates[0].UpdatedTypes); - // Rude edit: + // Insignificant change: solution = solution.WithDocumentText(documentIdA, CreateText(source3)); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: [], CancellationToken.None); + Assert.Empty(result.Diagnostics); + Assert.Empty(result.Diagnostics); + Assert.Empty(result.ProjectUpdates); + Assert.Equal(ModuleUpdateStatus.None, result.Status); + + var updatedText = await ((EditAndContinueService)hotReload.GetTestAccessor().EncService) + .GetTestAccessor() + .GetActiveDebuggingSessions() + .Single() + .LastCommittedSolution + .GetRequiredProject(documentIdA.ProjectId) + .GetRequiredDocument(documentIdA) + .GetTextAsync(); + + Assert.Equal(source3, updatedText.ToString()); + + // Rude edit: + solution = solution.WithDocumentText(documentIdA, CreateText(source4)); + result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( ["ENC0110: " + string.Format(FeaturesResources.Changing_the_signature_of_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime, FeaturesResources.method)], @@ -91,7 +112,7 @@ public async Task Test() AssertEx.SetEqual(["P"], result.ProjectIdsToRebuild.Select(p => solution.GetRequiredProject(p).Name)); // Syntax error: - solution = solution.WithDocumentText(documentIdA, CreateText(source4)); + solution = solution.WithDocumentText(documentIdA, CreateText(source5)); result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( @@ -102,7 +123,7 @@ public async Task Test() Assert.Empty(result.ProjectIdsToRebuild); // Semantic error: - solution = solution.WithDocumentText(documentIdA, CreateText(source5)); + solution = solution.WithDocumentText(documentIdA, CreateText(source6)); result = await hotReload.GetUpdatesAsync(solution, runningProjects: solution.ProjectIds.ToImmutableHashSet(), CancellationToken.None); AssertEx.Equal( diff --git a/src/Features/TestUtilities/EditAndContinue/MatchingPair.cs b/src/Features/TestUtilities/EditAndContinue/MatchingPair.cs index 6b266d515fe13..3705f66c904cc 100644 --- a/src/Features/TestUtilities/EditAndContinue/MatchingPair.cs +++ b/src/Features/TestUtilities/EditAndContinue/MatchingPair.cs @@ -6,7 +6,6 @@ using System.Collections; using System.Collections.Generic; -using System.Linq; using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests diff --git a/src/Features/TestUtilities/EditAndContinue/SyntaxMapDescription.cs b/src/Features/TestUtilities/EditAndContinue/SyntaxMapDescription.cs index dad5ba2fe15ee..282c408ab49f1 100644 --- a/src/Features/TestUtilities/EditAndContinue/SyntaxMapDescription.cs +++ b/src/Features/TestUtilities/EditAndContinue/SyntaxMapDescription.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.Differencing; diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/OverrideCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/OverrideCompletionProvider.vb index d57901b07dd5a..0214f41b3aa66 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/OverrideCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/OverrideCompletionProvider.vb @@ -197,20 +197,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return False End Function - Protected Overrides Function GetTargetCaretPosition(caretTarget As SyntaxNode) As Integer + Protected Overrides Function GetTargetSelectionSpan(caretTarget As SyntaxNode) As TextSpan Dim node = DirectCast(caretTarget, SyntaxNode) ' MustOverride Sub | MustOverride Function: move to end of line Dim methodStatement = TryCast(node, MethodStatementSyntax) If methodStatement IsNot Nothing Then - Return methodStatement.GetLocation().SourceSpan.End + Return New TextSpan(methodStatement.GetLocation().SourceSpan.End, 0) End If Dim methodBlock = TryCast(node, MethodBlockBaseSyntax) If methodBlock IsNot Nothing Then Dim lastStatement = methodBlock.Statements.LastOrDefault() If lastStatement IsNot Nothing Then - Return lastStatement.GetLocation().SourceSpan.End + Return New TextSpan(lastStatement.GetLocation().SourceSpan.End, 0) End If End If @@ -220,12 +220,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers If firstAccessor IsNot Nothing Then Dim lastAccessorStatement = firstAccessor.Statements.LastOrDefault() If lastAccessorStatement IsNot Nothing Then - Return lastAccessorStatement.GetLocation().SourceSpan.End + Return New TextSpan(lastAccessorStatement.GetLocation().SourceSpan.End, 0) End If End If End If - Return -1 + Return New TextSpan(0, 0) End Function End Class End Namespace diff --git a/src/Features/VisualBasic/Portable/NavigationBar/VisualBasicNavigationBarItemService.vb b/src/Features/VisualBasic/Portable/NavigationBar/VisualBasicNavigationBarItemService.vb index aa8ef5c25ad8f..1302e602d6ec6 100644 --- a/src/Features/VisualBasic/Portable/NavigationBar/VisualBasicNavigationBarItemService.vb +++ b/src/Features/VisualBasic/Portable/NavigationBar/VisualBasicNavigationBarItemService.vb @@ -19,7 +19,7 @@ Imports Roslyn.Utilities Namespace Microsoft.CodeAnalysis.NavigationBar - Partial Friend Class VisualBasicNavigationBarItemService + Partial Friend NotInheritable Class VisualBasicNavigationBarItemService Inherits AbstractNavigationBarItemService Private ReadOnly _typeFormat As SymbolDisplayFormat = New SymbolDisplayFormat( diff --git a/src/Features/VisualBasic/Portable/SolutionCrawler/VisualBasicDocumentDifferenceService.vb b/src/Features/VisualBasic/Portable/SolutionCrawler/VisualBasicDocumentDifferenceService.vb index decb93449bacf..c41980b0cb5ea 100644 --- a/src/Features/VisualBasic/Portable/SolutionCrawler/VisualBasicDocumentDifferenceService.vb +++ b/src/Features/VisualBasic/Portable/SolutionCrawler/VisualBasicDocumentDifferenceService.vb @@ -5,15 +5,69 @@ Imports System.Composition Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.SolutionCrawler +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.SolutionCrawler - Friend Class VisualBasicDocumentDifferenceService + Friend NotInheritable Class VisualBasicDocumentDifferenceService Inherits AbstractDocumentDifferenceService Public Sub New() End Sub + + Protected Overrides Function IsContainedInMemberBody(node As SyntaxNode, span As Text.TextSpan) As Boolean + Dim method = TryCast(node, MethodBlockBaseSyntax) + If method IsNot Nothing Then + Return method.Statements.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan(method.Statements), span) + End If + + Dim [event] = TryCast(node, EventBlockSyntax) + If [event] IsNot Nothing Then + Return [event].Accessors.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan([event].Accessors), span) + End If + + Dim [property] = TryCast(node, PropertyBlockSyntax) + If [property] IsNot Nothing Then + Return [property].Accessors.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan([property].Accessors), span) + End If + + Dim field = TryCast(node, FieldDeclarationSyntax) + If field IsNot Nothing Then + Return field.Declarators.Count > 0 AndAlso ContainsExclusively(GetSeparatedSyntaxListSpan(field.Declarators), span) + End If + + Dim [enum] = TryCast(node, EnumMemberDeclarationSyntax) + If [enum] IsNot Nothing Then + Return [enum].Initializer IsNot Nothing AndAlso ContainsExclusively([enum].Initializer.Span, span) + End If + + Dim propStatement = TryCast(node, PropertyStatementSyntax) + If propStatement IsNot Nothing Then + Return propStatement.Initializer IsNot Nothing AndAlso ContainsExclusively(propStatement.Initializer.Span, span) + End If + + Return False + End Function + + Private Shared Function ContainsExclusively(outerSpan As TextSpan, innerSpan As TextSpan) As Boolean + If innerSpan.IsEmpty Then + Return outerSpan.Contains(innerSpan.Start) + End If + + Return outerSpan.Contains(innerSpan) + End Function + + Private Shared Function GetSyntaxListSpan(Of T As SyntaxNode)(list As SyntaxList(Of T)) As TextSpan + Debug.Assert(list.Count > 0) + Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) + End Function + + Private Shared Function GetSeparatedSyntaxListSpan(Of T As SyntaxNode)(list As SeparatedSyntaxList(Of T)) As TextSpan + Debug.Assert(list.Count > 0) + Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) + End Function End Class End Namespace diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 7bbfd6ed38090..ddabdd4e4c3e3 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb @@ -60,7 +60,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty Dim generator = SyntaxGenerator.GetGenerator(propertyDocument.Project) Dim canBeReadOnly = Not isWrittenToOutsideOfConstructor AndAlso Not propertyDeclaration.Accessors.Any(SyntaxKind.SetAccessorBlock) - statement = DirectCast(generator.WithModifiers(statement, generator.GetModifiers(propertyDeclaration).WithIsReadOnly(canBeReadOnly)), PropertyStatementSyntax) + statement = generator.WithModifiers(statement, generator.GetModifiers(propertyDeclaration).WithIsReadOnly(canBeReadOnly)) Dim initializer = Await GetFieldInitializerAsync(fieldSymbol, cancellationToken).ConfigureAwait(False) If initializer.equalsValue IsNot Nothing Then diff --git a/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index e329b02dea940..c315a384a20df 100644 --- a/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/Features/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -6029,11 +6029,7 @@ End Interface EditAndContinueValidation.VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, { - DocumentResults( - semanticEdits:= - { - SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMember(Of MethodSymbol)("C.F").PartialImplementationPart, deletedSymbolContainerProvider:=Function(c) c.GetMember("C"), partialType:="C") - }), + DocumentResults(), DocumentResults( semanticEdits:= { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/MefServiceBrokerOfExportedServices.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/MefServiceBrokerOfExportedServices.cs index 2e6436f80af37..c50758a7db4c3 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/MefServiceBrokerOfExportedServices.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/MefServiceBrokerOfExportedServices.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.Utilities.ServiceBroker; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs index 4f8294626029f..4e6b65efb69a7 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/ServiceBrokerFactory.cs @@ -9,7 +9,6 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Shell.ServiceBroker; -using Roslyn.Utilities; using ExportProvider = Microsoft.VisualStudio.Composition.ExportProvider; namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs index b77955279d70b..a789a3d276ea0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/Services/BrokeredServiceBridgeManifest/BrokeredServiceBridgeManifestService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.Extensions.Logging; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs index 5acad362275c8..ae04a1b06efe7 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/BrokeredServices/WrappedServiceBroker.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -8,7 +8,6 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Shell.ServiceBroker; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.BrokeredServices; #pragma warning restore RS0030 // Do not used banned APIs diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs index f62d05679f3a6..2461b9b2c136c 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LanguageServerWorkspaceFactory.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; using Microsoft.CodeAnalysis.ProjectSystem; -using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs index 72a9d11a5612e..c2cae1e5418ee 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/LoadedProject.cs @@ -133,6 +133,7 @@ public void Dispose() _projectSystemProject.OutputRefFilePath = newProjectInfo.OutputRefFilePath; _projectSystemProject.GeneratedFilesOutputDirectory = newProjectInfo.GeneratedFilesOutputDirectory; _projectSystemProject.CompilationOutputAssemblyFilePath = newProjectInfo.IntermediateOutputFilePath; + _projectSystemProject.DefaultNamespace = newProjectInfo.DefaultNamespace; if (newProjectInfo.TargetFrameworkIdentifier != null) { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs index e52224ce1e0aa..f94f1f82d111c 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/ProjectDependencyHelper.cs @@ -11,7 +11,6 @@ using NuGet.ProjectModel; using NuGet.Versioning; using Roslyn.Utilities; -using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; internal static class ProjectDependencyHelper diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorClientLanguageServerManagerFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorClientLanguageServerManagerFactory.cs new file mode 100644 index 0000000000000..dcdb0854b7425 --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorClientLanguageServerManagerFactory.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Composition; +using Microsoft.CodeAnalysis.ExternalAccess.Razor; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; + +[Shared] +[ExportLspServiceFactory(typeof(IRazorClientLanguageServerManager), ProtocolConstants.RoslynLspLanguagesContract)] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class RazorClientLanguageServerManagerFactory() : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + var notificationManager = lspServices.GetRequiredService(); + + return new RazorClientLanguageServerManager(notificationManager); + } +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs deleted file mode 100644 index 48c1c3fa8595f..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedHandler.cs +++ /dev/null @@ -1,34 +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.Composition; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -[Shared] -[ExportCSharpVisualBasicStatelessLspService(typeof(RazorDynamicFileChangedHandler))] -[Method("razor/dynamicFileInfoChanged")] -internal class RazorDynamicFileChangedHandler : ILspServiceNotificationHandler -{ - private readonly RazorDynamicFileInfoProvider _razorDynamicFileInfoProvider; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorDynamicFileChangedHandler(RazorDynamicFileInfoProvider razorDynamicFileInfoProvider) - { - _razorDynamicFileInfoProvider = razorDynamicFileInfoProvider; - } - - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => false; - - public Task HandleNotificationAsync(RazorDynamicFileChangedParams request, RequestContext requestContext, CancellationToken cancellationToken) - { - var filePath = ProtocolConversions.GetDocumentFilePathFromUri(request.RazorDocument.Uri); - _razorDynamicFileInfoProvider.Update(filePath); - return Task.CompletedTask; - } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs deleted file mode 100644 index 73a2cf4dd186c..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileChangedParams.cs +++ /dev/null @@ -1,14 +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.Text.Json.Serialization; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class RazorDynamicFileChangedParams -{ - [JsonPropertyName("razorDocument")] - public required TextDocumentIdentifier RazorDocument { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs deleted file mode 100644 index 0f62d58b1d221..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.TextChangesTextLoader.cs +++ /dev/null @@ -1,113 +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.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal partial class RazorDynamicFileInfoProvider -{ - private sealed class TextChangesTextLoader( - TextDocument? document, - IEnumerable updates, - byte[] checksum, - SourceHashAlgorithm checksumAlgorithm, - int? codePage, - Uri razorUri) : TextLoader - { - private readonly TextDocument? _document = document; - private readonly IEnumerable _updates = updates; - private readonly byte[] _checksum = checksum; - private readonly SourceHashAlgorithm _checksumAlgorithm = checksumAlgorithm; - private readonly int? _codePage = codePage; - private readonly Uri _razorUri = razorUri; - - private readonly Lazy _emptySourceText = new Lazy(() => - { - var encoding = codePage is null ? null : Encoding.GetEncoding(codePage.Value); - return SourceText.From("", checksumAlgorithm: checksumAlgorithm, encoding: encoding); - }); - - public override async Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) - { - if (_document is null) - { - var text = UpdateSourceTextWithEdits(_emptySourceText.Value, _updates); - return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); - } - - var sourceText = await _document.GetTextAsync(cancellationToken).ConfigureAwait(false); - - // Validate the checksum information so the edits are known to be correct - if (IsSourceTextMatching(sourceText)) - { - var version = await _document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - var newText = UpdateSourceTextWithEdits(sourceText, _updates); - return TextAndVersion.Create(newText, version.GetNewerVersion()); - } - - return await GetFullDocumentFromServerAsync(cancellationToken).ConfigureAwait(false); - } - - private bool IsSourceTextMatching(SourceText sourceText) - { - if (sourceText.ChecksumAlgorithm != _checksumAlgorithm) - { - return false; - } - - if (sourceText.Encoding?.CodePage != _codePage) - { - return false; - } - - if (!sourceText.GetChecksum().SequenceEqual(_checksum)) - { - return false; - } - - return true; - } - - private async Task GetFullDocumentFromServerAsync(CancellationToken cancellationToken) - { - Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); - var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - - var response = await clientLanguageServerManager.SendRequestAsync( - ProvideRazorDynamicFileInfoMethodName, - new RazorProvideDynamicFileParams - { - RazorDocument = new() - { - Uri = _razorUri, - }, - FullText = true - }, - cancellationToken); - - RoslynDebug.AssertNotNull(response.Updates); - RoslynDebug.Assert(response.Updates.IsSingle()); - - var text = UpdateSourceTextWithEdits(_emptySourceText.Value, response.Updates); - return TextAndVersion.Create(text, VersionStamp.Default.GetNewerVersion()); - } - - private static SourceText UpdateSourceTextWithEdits(SourceText sourceText, IEnumerable updates) - { - foreach (var update in updates) - { - var changes = update.Edits.Select(e => new TextChange(e.Span.ToTextSpan(), e.NewText)); - sourceText = sourceText.WithChanges(changes); - } - - return sourceText; - } - } - -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs index 214a518c3ec98..07d0ecc9a73f6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileInfoProvider.cs @@ -3,146 +3,80 @@ // See the LICENSE file in the project root for more information. using System.Composition; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.Extensions.Logging; +using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; [Shared] [Export(typeof(IDynamicFileInfoProvider))] -[Export(typeof(RazorDynamicFileInfoProvider))] +[ExportRazorStatelessLspService(typeof(RazorDynamicFileInfoProvider))] [ExportMetadata("Extensions", new string[] { "cshtml", "razor", })] -internal partial class RazorDynamicFileInfoProvider : IDynamicFileInfoProvider +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class RazorDynamicFileInfoProvider(Lazy workspaceFactory, ILoggerFactory loggerFactory) : IDynamicFileInfoProvider, ILspService, IOnInitialized { - private const string ProvideRazorDynamicFileInfoMethodName = "razor/provideDynamicFileInfo"; - private const string RemoveRazorDynamicFileInfoMethodName = "razor/removeDynamicFileInfo"; - - private readonly Lazy _razorWorkspaceListenerInitializer; - private readonly LanguageServerWorkspaceFactory _workspaceFactory; - private readonly AsyncBatchingWorkQueue _updateWorkQueue; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorDynamicFileInfoProvider( - Lazy razorWorkspaceListenerInitializer, - LanguageServerWorkspaceFactory workspaceFactory, - IAsynchronousOperationListenerProvider listenerProvider) - { - _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; - _updateWorkQueue = new AsyncBatchingWorkQueue( - TimeSpan.FromMilliseconds(200), - UpdateAsync, - listenerProvider.GetListener(nameof(RazorDynamicFileInfoProvider)), - CancellationToken.None); - _workspaceFactory = workspaceFactory; - } + private RazorWorkspaceService? _razorWorkspaceService; + private RazorLspDynamicFileInfoProvider? _dynamicFileInfoProvider; public event EventHandler? Updated; - public void Update(string filePath) - { - _updateWorkQueue.AddWork(filePath); - } + private readonly ILogger _logger = loggerFactory.CreateLogger(); public async Task GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) { - _razorWorkspaceListenerInitializer.Value.NotifyDynamicFile(projectId); - - var razorUri = ProtocolConversions.CreateAbsoluteUri(filePath); - var requestParams = new RazorProvideDynamicFileParams - { - RazorDocument = new() - { - Uri = razorUri - } - }; - - Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); - var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - - var response = await clientLanguageServerManager.SendRequestAsync( - ProvideRazorDynamicFileInfoMethodName, requestParams, cancellationToken); - - if (response.CSharpDocument is null) + if (_dynamicFileInfoProvider is null || _razorWorkspaceService is null) { return null; } - // Since we only sent one file over, we should get either zero or one URI back - var responseUri = response.CSharpDocument.Uri; - var dynamicFileInfoFilePath = ProtocolConversions.GetDocumentFilePathFromUri(responseUri); + _razorWorkspaceService.NotifyDynamicFile(projectId); - if (response.Updates is not null) + var dynamicInfo = await _dynamicFileInfoProvider.GetDynamicFileInfoAsync(workspaceFactory.Value.Workspace, projectId, projectFilePath, filePath, cancellationToken).ConfigureAwait(false); + if (dynamicInfo is null) { - var textDocument = await _workspaceFactory.Workspace.CurrentSolution.GetTextDocumentAsync(response.CSharpDocument, cancellationToken).ConfigureAwait(false); - var checksum = Convert.FromBase64String(response.Checksum); - var textLoader = new TextChangesTextLoader( - textDocument, - response.Updates, - checksum, - response.ChecksumAlgorithm, - response.SourceEncodingCodePage, - razorUri); - - return new DynamicFileInfo( - dynamicFileInfoFilePath, - SourceCodeKind.Regular, - textLoader, - designTimeOnly: true, - documentServiceProvider: null); + return null; } return new DynamicFileInfo( - dynamicFileInfoFilePath, - SourceCodeKind.Regular, - EmptyStringTextLoader.Instance, + dynamicInfo.FilePath, + dynamicInfo.SourceCodeKind, + dynamicInfo.TextLoader, designTimeOnly: true, documentServiceProvider: null); } - public Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) + public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) { - var notificationParams = new RazorRemoveDynamicFileParams - { - CSharpDocument = new() - { - Uri = ProtocolConversions.CreateAbsoluteUri(filePath) - } - }; - - Contract.ThrowIfNull(LanguageServerHost.Instance, "We don't have an LSP channel yet to send this request through."); - var clientLanguageServerManager = LanguageServerHost.Instance.GetRequiredLspService(); - - return clientLanguageServerManager.SendNotificationAsync( - RemoveRazorDynamicFileInfoMethodName, notificationParams, cancellationToken).AsTask(); - } + _razorWorkspaceService = context.GetService(); + _dynamicFileInfoProvider = context.GetService(); - private ValueTask UpdateAsync(ImmutableSegmentedList paths, CancellationToken token) - { - foreach (var path in paths) + if (_razorWorkspaceService is null || _dynamicFileInfoProvider is null) { - token.ThrowIfCancellationRequested(); - Updated?.Invoke(this, path); + _logger.LogError("RazorDynamicFileInfoProvider not initialized. RazorWorkspaceService or RazorLspDynamicFileInfoProvider is null."); + return Task.CompletedTask; } - return ValueTask.CompletedTask; + _dynamicFileInfoProvider.Updated += (s, uri) => + { + var filePath = ProtocolConversions.GetDocumentFilePathFromUri(uri); + Updated?.Invoke(this, filePath); + }; + + return Task.CompletedTask; } - private sealed class EmptyStringTextLoader() : TextLoader + public async Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken) { - public static readonly TextLoader Instance = new EmptyStringTextLoader(); - - public override Task LoadTextAndVersionAsync(LoadTextOptions options, CancellationToken cancellationToken) + if (_dynamicFileInfoProvider is null) { - return Task.FromResult(TextAndVersion.Create(SourceText.From(""), VersionStamp.Default)); + return; } + + await _dynamicFileInfoProvider.RemoveDynamicFileInfoAsync(workspaceFactory.Value.Workspace, projectId, projectFilePath, filePath, cancellationToken).ConfigureAwait(false); } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileUpdate.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileUpdate.cs deleted file mode 100644 index 4853793e33e84..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorDynamicFileUpdate.cs +++ /dev/null @@ -1,15 +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.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Text; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -class RazorDynamicFileUpdate -{ - [JsonPropertyName("edits")] - public required ServerTextChange[] Edits { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs deleted file mode 100644 index 546b0a3f6edd7..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorInitializeHandler.cs +++ /dev/null @@ -1,41 +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.Composition; -using System.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CommonLanguageServerProtocol.Framework; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -[ExportCSharpVisualBasicStatelessLspService(typeof(RazorInitializeHandler)), Shared] -[Method("razor/initialize")] -internal class RazorInitializeHandler : ILspServiceNotificationHandler -{ - private readonly Lazy _razorWorkspaceListenerInitializer; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorInitializeHandler(Lazy razorWorkspaceListenerInitializer) - { - _razorWorkspaceListenerInitializer = razorWorkspaceListenerInitializer; - } - - public bool MutatesSolutionState => false; - public bool RequiresLSPSolution => false; - - Task INotificationHandler.HandleNotificationAsync(RazorInitializeParams request, RequestContext requestContext, CancellationToken cancellationToken) - { - _razorWorkspaceListenerInitializer.Value.Initialize(request.PipeName); - - return Task.CompletedTask; - } -} - -internal class RazorInitializeParams -{ - [JsonPropertyName("pipeName")] - public required string PipeName { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.cs deleted file mode 100644 index 1d6f4a24dbd22..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileParams.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.Text.Json.Serialization; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class RazorProvideDynamicFileParams -{ - [JsonPropertyName("razorDocument")] - public required TextDocumentIdentifier RazorDocument { get; set; } - - /// - /// When true, the full text of the document will be sent over as a single - /// edit instead of diff edits - /// - [JsonPropertyName("fullText")] - public bool FullText { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs deleted file mode 100644 index 6d51c27199d86..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorProvideDynamicFileResponse.cs +++ /dev/null @@ -1,27 +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.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Text; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class RazorProvideDynamicFileResponse -{ - [JsonPropertyName("csharpDocument")] - public required TextDocumentIdentifier CSharpDocument { get; set; } - - [JsonPropertyName("updates")] - public RazorDynamicFileUpdate[]? Updates { get; set; } - - [JsonPropertyName("checksum")] - public required string Checksum { get; set; } - - [JsonPropertyName("checksumAlgorithm")] - public SourceHashAlgorithm ChecksumAlgorithm { get; set; } - - [JsonPropertyName("encodingCodePage")] - public int? SourceEncodingCodePage { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs deleted file mode 100644 index 2388208108769..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorRemoveDynamicFileParams.cs +++ /dev/null @@ -1,14 +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.Text.Json.Serialization; -using Roslyn.LanguageServer.Protocol; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class RazorRemoveDynamicFileParams -{ - [JsonPropertyName("csharpDocument")] - public required TextDocumentIdentifier CSharpDocument { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs deleted file mode 100644 index 80d366ca58c13..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/RazorWorkspaceListenerInitializer.cs +++ /dev/null @@ -1,80 +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.Composition; -using Microsoft.AspNetCore.Razor.ExternalAccess.RoslynWorkspace; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.Extensions.Logging; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -[Export(typeof(RazorWorkspaceListenerInitializer)), Shared] -internal sealed class RazorWorkspaceListenerInitializer -{ - private readonly ILogger _logger; - private readonly Workspace _workspace; - private readonly ILoggerFactory _loggerFactory; - - // Locks all access to _razorWorkspaceListener and _projectIdWithDynamicFiles - private readonly object _initializeGate = new(); - private HashSet _projectIdWithDynamicFiles = []; - - private RazorWorkspaceListener? _razorWorkspaceListener; - - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public RazorWorkspaceListenerInitializer(LanguageServerWorkspaceFactory workspaceFactory, ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(nameof(RazorWorkspaceListenerInitializer)); - - _workspace = workspaceFactory.Workspace; - _loggerFactory = loggerFactory; - } - - internal void Initialize(string pipeName) - { - HashSet projectsToInitialize; - lock (_initializeGate) - { - // Only initialize once - if (_razorWorkspaceListener is not null) - { - return; - } - - _logger.LogTrace("Initializing the Razor workspace listener with pipe name {0}", pipeName); - _razorWorkspaceListener = new RazorWorkspaceListener(_loggerFactory); - _razorWorkspaceListener.EnsureInitialized(_workspace, pipeName); - - projectsToInitialize = _projectIdWithDynamicFiles; - // May as well clear out the collection, it will never get used again anyway. - _projectIdWithDynamicFiles = []; - } - - foreach (var projectId in projectsToInitialize) - { - _logger.LogTrace("{projectId} notifying a dynamic file for the first time", projectId); - _razorWorkspaceListener.NotifyDynamicFile(projectId); - } - } - - internal void NotifyDynamicFile(ProjectId projectId) - { - lock (_initializeGate) - { - if (_razorWorkspaceListener is null) - { - // We haven't been initialized by the extension yet, so just store the project id, to tell Razor later - _logger.LogTrace("{projectId} queuing up a dynamic file notify for later", projectId); - _projectIdWithDynamicFiles.Add(projectId); - - return; - } - } - - // We've been initialized, so just pass the information along - _logger.LogTrace("{projectId} forwarding on a dynamic file notification because we're initialized", projectId); - _razorWorkspaceListener.NotifyDynamicFile(projectId); - } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.cs deleted file mode 100644 index 272a69316d4b0..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextChange.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 System.Text.Json.Serialization; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class ServerTextChange -{ - [JsonPropertyName("span")] - public required ServerTextSpan Span { get; set; } - - [JsonPropertyName("newText")] - public required string NewText { get; set; } -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.cs deleted file mode 100644 index 80d3d035aeb3e..0000000000000 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/Razor/ServerTextSpan.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.Text.Json.Serialization; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace.Razor; - -internal class ServerTextSpan -{ - [JsonPropertyName("start")] - public int Start { get; set; } - - [JsonPropertyName("length")] - public int Length { get; set; } - - public TextSpan ToTextSpan() - => new(Start, Length); -} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProject.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProject.cs index daa449f14596d..954d292b2c95b 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProject.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProject.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Remote.ProjectSystem; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.Extensions.Logging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProjectFactoryService.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProjectFactoryService.cs index 255be35a19bd2..eba2a8218aff9 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProjectFactoryService.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/HostWorkspace/WorkspaceProjectFactoryService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.ComponentModel.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Telemetry; @@ -10,7 +9,6 @@ using Microsoft.Extensions.Logging; using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Shell.ServiceBroker; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.HostWorkspace; #pragma warning disable RS0030 // This is intentionally using System.ComponentModel.Composition for compatibility with MEF service broker. diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Logging/UpdateLogLevelHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Logging/UpdateLogLevelHandler.cs index 0fb33697f2b09..2ff0032ea4c69 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Logging/UpdateLogLevelHandler.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Logging/UpdateLogLevelHandler.cs @@ -6,9 +6,7 @@ using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; -using Microsoft.CodeAnalysis.LanguageServer.Logging; using Microsoft.Extensions.Logging; -using Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer.Handler.Logging; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorableProjectsHandler.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorableProjectsHandler.cs index a943bb91eeacf..dff2f53aa8ce6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorableProjectsHandler.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/Handler/Restore/RestorableProjectsHandler.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.DebugConfiguration; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs index 4432adc2f49f4..a874f6049f8c6 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/LanguageServer/LanguageServerHost.cs @@ -7,7 +7,6 @@ using Microsoft.CommonLanguageServerProtocol.Framework; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Composition; -using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer.LanguageServer; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLogger.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLogger.cs index 9e0c92b8975a6..182a6d8b9c382 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLogger.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLogger.cs @@ -6,7 +6,6 @@ using Microsoft.Extensions.Logging; using Roslyn.LanguageServer.Protocol; using StreamJsonRpc; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Logging; @@ -14,23 +13,17 @@ namespace Microsoft.CodeAnalysis.LanguageServer.Logging; /// Implements an ILogger that seamlessly switches from a fallback logger /// to LSP log messages as soon as the server initializes. /// -internal sealed class LspLogMessageLogger(string categoryName, ILoggerFactory fallbackLoggerFactory, ServerConfiguration serverConfiguration) : ILogger +internal sealed class LspLogMessageLogger(string categoryName, ILoggerFactory fallbackLoggerFactory, ServerConfiguration serverConfiguration, IExternalScopeProvider? externalScopeProvider) : ILogger { private readonly Lazy _fallbackLogger = new(() => fallbackLoggerFactory.CreateLogger(categoryName)); + private readonly IExternalScopeProvider? _externalScopeProvider = externalScopeProvider; - public IDisposable BeginScope(TState state) where TState : notnull - { - throw new NotImplementedException(); - } - - public bool IsEnabled(LogLevel logLevel) - { - return serverConfiguration.LogConfiguration.GetLogLevel() <= logLevel; - } + public IDisposable? BeginScope(TState state) where TState : notnull => _externalScopeProvider?.Push(state); + public bool IsEnabled(LogLevel logLevel) => serverConfiguration.LogConfiguration.GetLogLevel() <= logLevel; public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) { - if (!IsEnabled(logLevel)) + if (!IsEnabled(logLevel) || logLevel == LogLevel.None) { return; } @@ -56,22 +49,30 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except message += " " + exceptionString; } - if (message != null && logLevel != LogLevel.None) + string messagePrefix = ""; + + _externalScopeProvider?.ForEachScope((scope, _) => { - message = $"[{categoryName}] {message}"; - try + if (scope is LspLoggingScope lspLoggingScope) { - var _ = server.GetRequiredLspService().SendNotificationAsync(Methods.WindowLogMessageName, new LogMessageParams() - { - Message = message, - MessageType = LogLevelToMessageType(logLevel), - }, CancellationToken.None); + messagePrefix += $"[{lspLoggingScope.Context}] "; } - catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) + }, state); + + messagePrefix += $"[{categoryName}]"; + + try + { + var _ = server.GetRequiredLspService().SendNotificationAsync(Methods.WindowLogMessageName, new LogMessageParams() { - // It is entirely possible that we're shutting down and the connection is lost while we're trying to send a log notification - // as this runs outside of the guaranteed ordering in the queue. We can safely ignore this exception. - } + Message = $"{messagePrefix} {message}", + MessageType = LogLevelToMessageType(logLevel), + }, CancellationToken.None); + } + catch (Exception ex) when (ex is ObjectDisposedException or ConnectionLostException) + { + // It is entirely possible that we're shutting down and the connection is lost while we're trying to send a log notification + // as this runs outside of the guaranteed ordering in the queue. We can safely ignore this exception. } } @@ -79,7 +80,8 @@ private static MessageType LogLevelToMessageType(LogLevel logLevel) { return logLevel switch { - LogLevel.Trace => MessageType.Log, + // Count "Trace" as "Debug", as right now the VS Code LSP client doesn't have a concept of "trace", and using a generic "Log" puts no severity at all which is even more confusing. + LogLevel.Trace => MessageType.Debug, LogLevel.Debug => MessageType.Debug, LogLevel.Information => MessageType.Info, LogLevel.Warning => MessageType.Warning, diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLoggerProvider.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLoggerProvider.cs index a257ad35e4b96..064416aa48f4d 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLoggerProvider.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLogMessageLoggerProvider.cs @@ -6,13 +6,14 @@ using Microsoft.Extensions.Logging; namespace Microsoft.CodeAnalysis.LanguageServer.Logging; -internal class LspLogMessageLoggerProvider(ILoggerFactory fallbackLoggerFactory, ServerConfiguration serverConfiguration) : ILoggerProvider +internal class LspLogMessageLoggerProvider(ILoggerFactory fallbackLoggerFactory, ServerConfiguration serverConfiguration) : ILoggerProvider, ISupportExternalScope { private readonly ConcurrentDictionary _loggers = new(StringComparer.OrdinalIgnoreCase); + private IExternalScopeProvider? _externalScopeProvider; public ILogger CreateLogger(string categoryName) { - return _loggers.GetOrAdd(categoryName, new LspLogMessageLogger(categoryName, fallbackLoggerFactory, serverConfiguration)); + return _loggers.GetOrAdd(categoryName, new LspLogMessageLogger(categoryName, fallbackLoggerFactory, serverConfiguration, _externalScopeProvider)); } public void Dispose() @@ -20,4 +21,9 @@ public void Dispose() _loggers.Clear(); fallbackLoggerFactory.Dispose(); } + + public void SetScopeProvider(IExternalScopeProvider scopeProvider) + { + _externalScopeProvider = scopeProvider; + } } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLoggingScope.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLoggingScope.cs new file mode 100644 index 0000000000000..c5df92175269e --- /dev/null +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspLoggingScope.cs @@ -0,0 +1,8 @@ +// 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.LanguageServer.Logging +{ + internal sealed record LspLoggingScope(string Context); +} diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspServiceLogger.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspServiceLogger.cs index 861d4956c3d1b..37243b44426a3 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspServiceLogger.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/LspServiceLogger.cs @@ -19,9 +19,9 @@ public LspServiceLogger(ILogger hostLogger) _hostLogger = hostLogger; } - public override void LogDebug(string message, params object[] @params) => _hostLogger.LogDebug(message, @params); + public override IDisposable? CreateContext(string context) => _hostLogger.BeginScope(new LspLoggingScope(context)); - public override void LogEndContext(string message, params object[] @params) => _hostLogger.LogDebug($"[{DateTime.UtcNow:hh:mm:ss.fff}][End]{message}", @params); + public override void LogDebug(string message, params object[] @params) => _hostLogger.LogDebug(message, @params); public override void LogError(string message, params object[] @params) => _hostLogger.LogError(message, @params); @@ -32,7 +32,5 @@ public LspServiceLogger(ILogger hostLogger) /// public override void LogInformation(string message, params object[] @params) => _hostLogger.LogDebug(message, @params); - public override void LogStartContext(string message, params object[] @params) => _hostLogger.LogDebug($"[{DateTime.UtcNow:hh:mm:ss.fff}][Start]{message}", @params); - public override void LogWarning(string message, params object[] @params) => _hostLogger.LogWarning(message, @params); } diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/RoslynLogger.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/RoslynLogger.cs index 8cb9f6d920bbb..8eaf22ddc745f 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/RoslynLogger.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/RoslynLogger.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Logging { diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ServerLoggerFactory.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ServerLoggerFactory.cs index 84ea12bffbd89..c284b33a547f5 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ServerLoggerFactory.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ServerLoggerFactory.cs @@ -5,7 +5,6 @@ using System.Composition; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.Extensions.Logging; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.Logging; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs index f223114d93364..0e62c7912c8e0 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Logging/ShowToastNotification.cs @@ -4,7 +4,6 @@ using System.Text.Json.Serialization; using Microsoft.CodeAnalysis.LanguageServer.LanguageServer; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer; diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj index b2238ec387021..ae21a40d1d2da 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/Microsoft.CodeAnalysis.LanguageServer.csproj @@ -52,6 +52,11 @@ $(TargetRid) $(BaseOS) win-x64;win-arm64;linux-x64;linux-arm64;linux-musl-x64;linux-musl-arm64;osx-x64;osx-arm64 + + + false + false + true @@ -73,7 +78,27 @@ - $(PublishReadyToRunCrossgen2ExtraArgs);--opt-cross-module:*;--non-local-generics-module:"$(TargetName)" + $(PublishReadyToRunCrossgen2ExtraArgs);--non-local-generics-module:"$(TargetName)" + + + + + + + <_OptCrossModuleSwitch + Condition="'%(_ResolvedProjectReferencePaths.ReferenceOutputAssembly)'=='true'" + Include="@(_ResolvedProjectReferencePaths->'--opt-cross-module:%(FileName)')" /> + + + $(PublishReadyToRunCrossgen2ExtraArgs);@(_OptCrossModuleSwitch) @@ -94,6 +119,7 @@ + diff --git a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/PackAllRids.targets b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/PackAllRids.targets index 53ced13fbc184..e4937d844a1ec 100644 --- a/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/PackAllRids.targets +++ b/src/LanguageServer/Microsoft.CodeAnalysis.LanguageServer/PackAllRids.targets @@ -7,15 +7,6 @@ - - - @@ -27,7 +18,19 @@ - + + + + + @@ -44,6 +45,7 @@ + diff --git a/src/LanguageServer/Protocol/NoOpLspLogger.cs b/src/LanguageServer/Protocol/NoOpLspLogger.cs index 5a74a68253a8c..cffbe1ba10786 100644 --- a/src/LanguageServer/Protocol/NoOpLspLogger.cs +++ b/src/LanguageServer/Protocol/NoOpLspLogger.cs @@ -13,6 +13,8 @@ internal sealed class NoOpLspLogger : AbstractLspLogger, ILspService private NoOpLspLogger() { } + public override IDisposable? CreateContext(string context) => null; + public override void LogDebug(string message, params object[] @params) { } @@ -32,13 +34,5 @@ public override void LogWarning(string message, params object[] @params) public override void LogError(string message, params object[] @params) { } - - public override void LogStartContext(string message, params object[] @params) - { - } - - public override void LogEndContext(string message, params object[] @params) - { - } } } diff --git a/src/LanguageServer/Protocol/Protocol/FileOperations/FileSystemWatcher.cs b/src/LanguageServer/Protocol/Protocol/FileOperations/FileSystemWatcher.cs index 0fba2560eadfe..ca199f394b4c5 100644 --- a/src/LanguageServer/Protocol/Protocol/FileOperations/FileSystemWatcher.cs +++ b/src/LanguageServer/Protocol/Protocol/FileOperations/FileSystemWatcher.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.ComponentModel; using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs b/src/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs index 0d237c7b55ce7..c3dbf34793b83 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/Text/ContainerElement.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.Generic; using System.Collections.Immutable; diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalDataTips.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalDataTips.cs index cddf349772e96..7ed3d3421619c 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalDataTips.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalDataTips.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Runtime.Serialization; using System.Text.Json.Serialization; namespace Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs index 915dc06853f81..540cc629c01d7 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalLocation.cs @@ -30,7 +30,7 @@ public object? Text set { - if (value is ImageElement || value is ContainerElement || value is ClassifiedTextElement || value is string) + if (value is ImageElement or ContainerElement or ClassifiedTextElement or string) { this.textValue = value; } @@ -41,4 +41,4 @@ public object? Text } } } -} \ No newline at end of file +} diff --git a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs index fefbd26e43797..ad08e41e7b7d0 100644 --- a/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs +++ b/src/LanguageServer/Protocol/Protocol/Internal/VSInternalReferenceItem.cs @@ -68,8 +68,8 @@ public object? DefinitionText set { - if (value == null || // Null is accepted since for non-definition references - (value is ImageElement || value is ContainerElement || value is ClassifiedTextElement || value is string)) + // Null is accepted since for non-definition references + if (value is null or ImageElement or ContainerElement or ClassifiedTextElement or string) { this.definitionTextValue = value; } @@ -157,7 +157,7 @@ public object? Text set { - if (value is ImageElement || value is ContainerElement || value is ClassifiedTextElement || value is string) + if (value is ImageElement or ContainerElement or ClassifiedTextElement or string) { this.textValue = value; } diff --git a/src/LanguageServer/Protocol/Protocol/Methods.DocumentSynchronization.cs b/src/LanguageServer/Protocol/Protocol/Methods.DocumentSynchronization.cs index cf3b68c31e35a..812ffeb39016d 100644 --- a/src/LanguageServer/Protocol/Protocol/Methods.DocumentSynchronization.cs +++ b/src/LanguageServer/Protocol/Protocol/Methods.DocumentSynchronization.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. -using Roslyn.LanguageServer.Protocol; - namespace Roslyn.LanguageServer.Protocol; // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_synchronization diff --git a/src/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullParams.cs b/src/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullParams.cs new file mode 100644 index 0000000000000..ab4a5cb8300d7 --- /dev/null +++ b/src/LanguageServer/Protocol/Protocol/SemanticTokens/SemanticTokensFullParams.cs @@ -0,0 +1,34 @@ +// 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.Text.Json.Serialization; + +namespace Roslyn.LanguageServer.Protocol; + +/// +/// Parameters for 'textDocument/semanticTokens/full' request. +/// +/// See the Language Server Protocol specification for additional information. +/// +/// +/// Since LSP 3.16 +internal sealed class SemanticTokensFullParams : ITextDocumentParams, IWorkDoneProgressParams, IPartialResultParams +{ + /// + /// Gets or sets an identifier for the document to fetch semantic tokens from. + /// + [JsonPropertyName("textDocument")] + [JsonRequired] + public TextDocumentIdentifier TextDocument { get; set; } + + /// + [JsonPropertyName(Methods.WorkDoneTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IProgress? WorkDoneToken { get; set; } + + /// + [JsonPropertyName(Methods.PartialResultTokenName)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IProgress? PartialResultToken { get; set; } +} diff --git a/src/LanguageServer/Protocol/RoslynLanguageServer.cs b/src/LanguageServer/Protocol/RoslynLanguageServer.cs index a68788ad03c87..fda759ead5c8c 100644 --- a/src/LanguageServer/Protocol/RoslynLanguageServer.cs +++ b/src/LanguageServer/Protocol/RoslynLanguageServer.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.ServerLifetime; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.LanguageServer diff --git a/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs b/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs index a99f84b743393..678734b21f1d7 100644 --- a/src/LanguageServer/Protocol/WellKnownLspServerKinds.cs +++ b/src/LanguageServer/Protocol/WellKnownLspServerKinds.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.LanguageServer; internal enum WellKnownLspServerKinds diff --git a/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs b/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs index 741476e85769d..1b679d550649e 100644 --- a/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/CodeActions/RunCodeActionsTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Linq; using System.Text.Json; using System.Threading; @@ -11,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs index 7eb8c621e703c..5970d60fddfe6 100644 --- a/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Definitions/GoToDefinitionTests.cs @@ -4,10 +4,10 @@ #nullable disable -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; using Xunit; @@ -205,6 +205,53 @@ public partial class C AssertLocationsEqual(testLspServer.GetLocations("definition"), results); } + [Theory, CombinatorialData] + public async Task TestGotoDefinitionPartialEvents(bool mutatingLspWorkspace) + { + var markup = """ + using System; + + public partial class C + { + partial event Action {|caret:|}E; + } + + public partial class C + { + partial event Action {|definition:E|} { add { } remove { } } + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace); + + var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + + [Theory, CombinatorialData] + public async Task TestGotoDefinitionPartialConstructors(bool mutatingLspWorkspace) + { + var markup = """ + using System; + + public partial class C + { + partial {|caret:|}C(); + } + + public partial class C + { + partial {|definition:C|}() { } + } + """; + await using var testLspServer = await CreateTestLspServerAsync(markup, mutatingLspWorkspace, new InitializationOptions + { + ParseOptions = TestOptions.RegularPreview, + }); + + var results = await RunGotoDefinitionAsync(testLspServer, testLspServer.GetLocations("caret").Single()); + AssertLocationsEqual(testLspServer.GetLocations("definition"), results); + } + [Theory] [InlineData("ValueTuple valueTuple1;")] [InlineData("ValueTuple valueTuple2;")] diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs index 24cf9bca13ee0..e1ac7c2162ff0 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/PullDiagnosticTests.cs @@ -18,8 +18,8 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Threading; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; @@ -2040,7 +2040,7 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspTextChanges(bool useVSDiagn await testLspServer.OpenDocumentAsync(uri); // Assert the task completes after a change occurs - var results = await resultTask; + var results = await resultTask.WithTimeout(TestHelpers.HangMitigatingTimeout); Assert.NotEmpty(results); } @@ -2068,7 +2068,36 @@ public async Task TestWorkspaceDiagnosticsWaitsForLspSolutionChanges(bool useVSD testLspServer.TestWorkspace.OnProjectReloaded(projectInfo); // Assert the task completes after a change occurs - var results = await resultTask; + var results = await resultTask.WithTimeout(TestHelpers.HangMitigatingTimeout); + Assert.NotEmpty(results); + } + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/77495")] + public async Task TestWorkspaceDiagnosticsWaitsForRefreshRequestedEvent(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var markup1 = @"class A {"; + var markup2 = ""; + await using var testLspServer = await CreateTestWorkspaceWithDiagnosticsAsync( + [markup1, markup2], mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + + // The very first request should return immediately (as we're have no prior state to tell if the sln changed). + var resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); + await resultTask; + + // The second request should wait for a solution change before returning. + resultTask = RunGetWorkspacePullDiagnosticsAsync(testLspServer, useVSDiagnostics, useProgress: true, triggerConnectionClose: false); + + // Assert that the connection isn't closed and task doesn't complete even after some delay. + await Task.Delay(TimeSpan.FromSeconds(5)); + Assert.False(resultTask.IsCompleted); + + // Make workspace change that will trigger connection close. + var refreshService = testLspServer.TestWorkspace.ExportProvider.GetExportedValue(); + refreshService.RequestWorkspaceRefresh(); + + // Assert the task completes after a change occurs + var results = await resultTask.WithTimeout(TestHelpers.HangMitigatingTimeout); Assert.NotEmpty(results); } diff --git a/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs b/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs index ec2739b133c05..fcf5913cb5151 100644 --- a/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.cs +++ b/src/LanguageServer/ProtocolUnitTests/DocumentChanges/DocumentChangesTests.WithFindAllReferences.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.Diagnostics; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.UnitTests.References; -using Roslyn.LanguageServer.Protocol; using Xunit; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.DocumentChanges diff --git a/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs b/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs index f18a10d01e602..835d06b907f08 100644 --- a/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/FoldingRanges/FoldingRangesTests.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Structure; using Roslyn.Test.Utilities; using Xunit; using Xunit.Abstractions; diff --git a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs index 5963b91d09b83..a5231be58caf8 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlayHint/CSharpInlayHintTests.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.InlayHint; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; -using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs b/src/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs index b709ade88f520..92e71ee794a7a 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlineCompletions/InlineCompletionsTests.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler.InlineCompletions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/InlineCompletions/TestSnippetInfoService.cs b/src/LanguageServer/ProtocolUnitTests/InlineCompletions/TestSnippetInfoService.cs index ae884917e1988..f1185e57cd466 100644 --- a/src/LanguageServer/ProtocolUnitTests/InlineCompletions/TestSnippetInfoService.cs +++ b/src/LanguageServer/ProtocolUnitTests/InlineCompletions/TestSnippetInfoService.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.Handler.InlineCompletions; using Microsoft.CodeAnalysis.Snippets; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; @@ -45,7 +44,7 @@ public bool ShouldFormatSnippet(SnippetInfo snippetInfo) throw new NotImplementedException(); } - public bool SnippetShortcutExists_NonBlocking(string shortcut) + public bool SnippetShortcutExists_NonBlocking(string? shortcut) { throw new NotImplementedException(); } diff --git a/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs b/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs index 733d6c9306bd8..4092dcdee3c8c 100644 --- a/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Metadata/LspMetadataAsSourceWorkspaceTests.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.MetadataAsSource; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs index 5299ad3bec399..98a0fc076aa74 100644 --- a/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Miscellaneous/LspMiscellaneousFilesWorkspaceTests.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs b/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs index c0d60cc1c5588..5b96b3485b4e8 100644 --- a/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/OnAutoInsert/OnAutoInsertTests.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.LanguageServer.Protocol; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; diff --git a/src/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs b/src/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs index a4ae9d8449b3f..dfa0d5378f2e5 100644 --- a/src/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/References/FindImplementationsTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs index 007d6d4fbcfac..ef45a583c019b 100644 --- a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/AbstractSemanticTokensTests.cs @@ -27,6 +27,14 @@ protected AbstractSemanticTokensTests(ITestOutputHelper testOutputHelper) : base private protected static IReadOnlyDictionary GetTokenTypeToIndex(TestLspServer server) => SemanticTokensSchema.GetSchema(server.ClientCapabilities.HasVisualStudioLspCapability()).TokenTypeToIndex; + private protected static async Task RunGetSemanticTokensFullAsync(TestLspServer testLspServer, LSP.Location caret) + { + var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensFullName, + CreateSemanticTokensFullParams(caret), CancellationToken.None); + Contract.ThrowIfNull(result); + return result; + } + private protected static async Task RunGetSemanticTokensRangeAsync(TestLspServer testLspServer, LSP.Location caret, LSP.Range range) { var result = await testLspServer.ExecuteRequestAsync(LSP.Methods.TextDocumentSemanticTokensRangeName, @@ -43,6 +51,12 @@ private protected static IReadOnlyDictionary GetTokenTypeToIndex(Te return result; } + private static LSP.SemanticTokensFullParams CreateSemanticTokensFullParams(LSP.Location caret) + => new LSP.SemanticTokensFullParams + { + TextDocument = new LSP.TextDocumentIdentifier { Uri = caret.Uri } + }; + private static LSP.SemanticTokensRangeParams CreateSemanticTokensRangeParams(LSP.Location caret, LSP.Range range) => new LSP.SemanticTokensRangeParams { diff --git a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensFullTests.cs b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensFullTests.cs new file mode 100644 index 0000000000000..8d9496ebf7700 --- /dev/null +++ b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensFullTests.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.LanguageServer.Handler.SemanticTokens; +using Roslyn.LanguageServer.Protocol; +using Roslyn.Test.Utilities; +using Xunit; +using Xunit.Abstractions; +using LSP = Roslyn.LanguageServer.Protocol; + +#pragma warning disable format // We want to force explicit column spacing within the collection literals in this file, so we disable formatting. + +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens; + +public sealed class SemanticTokensFullTests(ITestOutputHelper testOutputHelper) : AbstractSemanticTokensTests(testOutputHelper) +{ + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensFull_FullDocAsync(bool mutatingLspWorkspace, bool isVS) + { + var markup = + """ + {|caret:|}// Comment + static class C { } + + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var results = await RunGetSemanticTokensFullAsync(testLspServer, testLspServer.GetLocations("caret").First()); + + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); + } +} diff --git a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs index 34ff01d65aa4e..029e55d893df5 100644 --- a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangeTests.cs @@ -17,581 +17,592 @@ #pragma warning disable format // We want to force explicit column spacing within the collection literals in this file, so we disable formatting. -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens; + +public sealed class SemanticTokensRangeTests(ITestOutputHelper testOutputHelper) + : AbstractSemanticTokensTests(testOutputHelper) { - public class SemanticTokensRangeTests : AbstractSemanticTokensTests + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRange_FullDocAsync(bool mutatingLspWorkspace, bool isVS) { - public SemanticTokensRangeTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + var markup = + """ + {|caret:|}// Comment + static class C { } + + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; + var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); + + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; } - - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_FullDocAsync(bool mutatingLspWorkspace, bool isVS) + else { - var markup = -@"{|caret:|}// Comment -static class C { } -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); - - var range = new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) }; - var results = await RunGetSemanticTokensRangeAsync(testLspServer, testLspServer.GetLocations("caret").First(), range); - - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - else + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); + } + + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRanges_ComputesTokensWithMultipleRanges(bool mutatingLspWorkspace, bool isVS) + { + // Razor docs should be returning semantic + syntactic results. + var markup = + """ + {|caret:|}// + #pragma warning disable 1591 + namespace Razor { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; + #line hidden + public class Template + { + #pragma warning disable 219 + private void __RazorDirectiveTokenHelpers__() { + ((global::System.Action)(() => { + #nullable restore + #line 1 "test.cshtml" + var z = 1; + + #line default + #line hidden + #nullable disable + } + ))(); + } + #pragma warning restore 219 + #pragma warning disable 0414 + private static object __o = null; + #pragma warning restore 0414 + #pragma warning disable 1998 + public async override global::System.Threading.Tasks.Task ExecuteAsync() + { + #nullable restore + #line 2 "test.cshtml" + var x = + + #line default + #line hidden + #nullable disable + } + #pragma warning restore 1998 + } } + #pragma warning restore 1591 - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); - } + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + ImmutableArray spans = [ + new LinePositionSpan(new LinePosition(12, 0), new LinePosition(13, 0)), + new LinePositionSpan(new LinePosition(29, 0), new LinePosition(30, 0)), + ]; + + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans, isVS, options, CancellationToken.None); - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRanges_ComputesTokensWithMultipleRanges(bool mutatingLspWorkspace, bool isVS) + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { - // Razor docs should be returning semantic + syntactic results. - var markup = -@"{|caret:|}// -#pragma warning disable 1591 -namespace Razor -{ - #line hidden - public class Template - { - #pragma warning disable 219 - private void __RazorDirectiveTokenHelpers__() { - ((global::System.Action)(() => { -#nullable restore -#line 1 ""test.cshtml"" -var z = 1; - -#line default -#line hidden -#nullable disable - } - ))(); + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 12, 0, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'z' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 17, 3, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + ]; } - #pragma warning restore 219 - #pragma warning disable 0414 - private static object __o = null; - #pragma warning restore 0414 - #pragma warning disable 1998 - public async override global::System.Threading.Tasks.Task ExecuteAsync() + else { -#nullable restore -#line 2 ""test.cshtml"" - var x = + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 12, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'z' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 17, 3, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + ]; + } -#line default -#line hidden -#nullable disable + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); + } + + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRange_PartialDocAsync(bool mutatingLspWorkspace, bool isVS) + { + // Razor docs should be returning semantic + syntactic results. + var markup = + """ + {|caret:|}// Comment + static class C { } + + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + ImmutableArray spans = [new LinePositionSpan(new LinePosition(1, 0), new LinePosition(2, 0))]; + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans, isVS, options, CancellationToken.None); + + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; } - #pragma warning restore 1998 + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } -} -#pragma warning restore 1591 -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); - - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - ImmutableArray spans = [ - new LinePositionSpan(new LinePosition(12, 0), new LinePosition(13, 0)), - new LinePositionSpan(new LinePosition(29, 0), new LinePosition(30, 0)), + + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRange_MultiLineComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + { + // Testing as a Razor doc so we get both syntactic + semantic results; otherwise the results would be empty. + var markup = + """ + {|caret:|}class C { /* one + + two + three */ } + + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(4, 0))]; + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans, isVS, options, CancellationToken.None); + + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' + 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' + 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' + 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' + 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' + 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' + 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' ]; + } - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans, isVS, options, CancellationToken.None); + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); + } - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 12, 0, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'z' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 17, 3, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - ]; - } - else + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRange_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + { + var markup = + """ + {|caret:|}class C { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 12, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'z' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Number], 0, // '1' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 17, 3, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - ]; + void M() + { + var x = @"one + two "" + three"; + } } - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); - } + """; - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_PartialDocAsync(bool mutatingLspWorkspace, bool isVS) - { - // Razor docs should be returning semantic + syntactic results. - var markup = -@"{|caret:|}// Comment -static class C { } -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); - - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - ImmutableArray spans = [new LinePositionSpan(new LinePosition(1, 0), new LinePosition(2, 0))]; - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans, isVS, options, CancellationToken.None); - - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - else - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); - } + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(9, 0))]; + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans, isVS, options, CancellationToken.None); - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_MultiLineComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { - // Testing as a Razor doc so we get both syntactic + semantic results; otherwise the results would be empty. - var markup = -@"{|caret:|}class C { /* one - -two -three */ } -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); - - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(4, 0))]; - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans, isVS, options, CancellationToken.None); - - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' - 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' - 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' - 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - else - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 6, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '/* one' - 2, 0, 3, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'two' - 1, 0, 8, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // 'three */' - 0, 9, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 8, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 5, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"one' + 1, 0, 4, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'two ' + 0, 4, 2, tokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' + 1, 0, 6, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'three"' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; } - - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_StringLiteral_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + else { - var markup = -@"{|caret:|}class C -{ - void M() - { - var x = @""one -two """" -three""; - } -} -"; + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' + 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 8, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 5, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '@"one' + 1, 0, 4, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // 'two ' + 0, 4, 2, tokenTypeToIndex[CustomLspSemanticTokenNames.StringEscapeCharacter], 0, // '""' + 1, 0, 6, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // 'three"' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); + } - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(9, 0))]; - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans, isVS, options, CancellationToken.None); + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRange_Regex_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + { + var markup = + """ + {|caret:|}using System.Text.RegularExpressions; - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 8, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 5, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"one' - 1, 0, 4, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'two ' - 0, 4, 2, tokenTypeToIndex[ClassificationTypeNames.StringEscapeCharacter], 0, // '""' - 1, 0, 6, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // 'three"' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - else + class C { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 4, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' - 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 8, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 5, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '@"one' - 1, 0, 4, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // 'two ' - 0, 4, 2, tokenTypeToIndex[CustomLspSemanticTokenNames.StringEscapeCharacter], 0, // '""' - 1, 0, 6, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // 'three"' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; + void M() + { + var x = new Regex("(abc)*"); + } } - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); - } + """; - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRange_Regex_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); + + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(9, 0))]; + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans, isVS, options, CancellationToken.None); + + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) { - var markup = -@"{|caret:|}using System.Text.RegularExpressions; + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + ]; + } + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } + ]; + } -class C -{ - void M() - { - var x = new Regex(""(abc)*""); + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); } -} -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); - - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - ImmutableArray spans = [new LinePositionSpan(new LinePosition(0, 0), new LinePosition(9, 0))]; - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans, isVS, options, CancellationToken.None); + [Theory, CombinatorialData] + [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1710519")] + public async Task TestGetSemanticTokensRange_RegexWithComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) + { + var markup = + """ + {|caret:|}using System.Text.RegularExpressions; - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' - 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' - 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' - 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' - 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' - 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - ]; - } - else + class C { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' - 0, 18, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' - 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' - 1, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' - 0, 5, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // '(' - 0, 1, 3, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexText], 0, // 'abc' - 0, 3, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // ')' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexQuantifier], 0, // '*' - 0, 1, 1, tokenTypeToIndex[SemanticTokenTypes.String], 0, // '"' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } - 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } - ]; + void M() + { + var x = new Regex(@"(abc)* #comment + ", RegexOptions.IgnorePatternWhitespace); + } } - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); - } + """; - [Theory, CombinatorialData] - [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1710519")] - public async Task TestGetSemanticTokensRange_RegexWithComment_IncludeSyntacticClassificationsAsync(bool mutatingLspWorkspace, bool isVS) - { - var markup = -@"{|caret:|}using System.Text.RegularExpressions; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); -class C -{ - void M() - { - var x = new Regex(@""(abc)* #comment - "", RegexOptions.IgnorePatternWhitespace); - } -} -"; - - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); + var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); + var text = await document.GetTextAsync(); + var options = ClassificationOptions.Default; + var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( + document, spans: [text.Lines.GetLinePositionSpan(new(0, text.Length))], isVS, options: options, cancellationToken: CancellationToken.None); - var document = testLspServer.GetCurrentSolution().Projects.First().Documents.First(); - var options = ClassificationOptions.Default; - var results = await SemanticTokensHelpers.ComputeSemanticTokensDataAsync( - document, spans: [], isVS, options: options, cancellationToken: CancellationToken.None); + var expectedResults = new LSP.SemanticTokens(); - var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' + 0, 1, 2, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // ' ' + 0, 1, 9, tokenTypeToIndex[ClassificationTypeNames.RegexComment], 0, // '#comment' + 1, 0, 27, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '"' + 0, 27, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ',' + 0, 2, 12, tokenTypeToIndex[ClassificationTypeNames.EnumName], 0, // 'RegexOptions' + 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 23, tokenTypeToIndex[ClassificationTypeNames.EnumMemberName], 0, // 'IgnorePatternWhitespace' + 0, 23, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } + ]; + } + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' + 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' + 0, 18, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' + 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' + 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' + 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' + 1, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' + 1, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' + 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' + 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' + 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' + 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' + 0, 5, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' + 0, 1, 2, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '@"' + 0, 2, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // '(' + 0, 1, 3, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexText], 0, // 'abc' + 0, 3, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // ')' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexQuantifier], 0, // '*' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // ' ' + 0, 1, 9, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexComment], 0, // '#comment' + 1, 0, 27, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '"' + 0, 27, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ',' + 0, 2, 12, tokenTypeToIndex[SemanticTokenTypes.Enum], 0, // 'RegexOptions' + 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' + 0, 1, 23, tokenTypeToIndex[SemanticTokenTypes.EnumMember], 0, // 'IgnorePatternWhitespace' + 0, 23, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' + 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' + 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } + 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } + ]; + } - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'System' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'Text' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, tokenTypeToIndex[ClassificationTypeNames.NamespaceName], 0, // 'RegularExpressions' - 0, 18, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.MethodName], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 1, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 1, 2, 3, tokenTypeToIndex[ClassificationTypeNames.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[ClassificationTypeNames.LocalName], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, tokenTypeToIndex[ClassificationTypeNames.ClassName], 0, // 'Regex' - 0, 5, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '(' - 0, 1, 2, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '@"' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // '(' - 0, 1, 3, tokenTypeToIndex[ClassificationTypeNames.RegexText], 0, // 'abc' - 0, 3, 1, tokenTypeToIndex[ClassificationTypeNames.RegexGrouping], 0, // ')' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.RegexQuantifier], 0, // '*' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // ' ' - 0, 1, 9, tokenTypeToIndex[ClassificationTypeNames.RegexComment], 0, // '#comment' - 1, 0, 27, tokenTypeToIndex[ClassificationTypeNames.VerbatimStringLiteral], 0, // '"' - 0, 27, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ',' - 0, 2, 12, tokenTypeToIndex[ClassificationTypeNames.EnumName], 0, // 'RegexOptions' - 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 23, tokenTypeToIndex[ClassificationTypeNames.EnumMemberName], 0, // 'IgnorePatternWhitespace' - 0, 23, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ')' - 0, 1, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - 1, 0, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // } - ]; - } - else - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'using' - 0, 6, 6, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'System' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'Text' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 18, tokenTypeToIndex[SemanticTokenTypes.Namespace], 0, // 'RegularExpressions' - 0, 18, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' - 2, 0, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'C' - 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 1, 4, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'void' - 0, 5, 1, tokenTypeToIndex[SemanticTokenTypes.Method], 0, // 'M' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' - 1, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '{' - 1, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'var' - 0, 4, 1, tokenTypeToIndex[SemanticTokenTypes.Variable], 0, // 'x' - 0, 2, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '=' - 0, 2, 3, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'new' - 0, 4, 5, tokenTypeToIndex[SemanticTokenTypes.Class], 0, // 'Regex' - 0, 5, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // '(' - 0, 1, 2, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '@"' - 0, 2, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // '(' - 0, 1, 3, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexText], 0, // 'abc' - 0, 3, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexGrouping], 0, // ')' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexQuantifier], 0, // '*' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // ' ' - 0, 1, 9, tokenTypeToIndex[CustomLspSemanticTokenNames.RegexComment], 0, // '#comment' - 1, 0, 27, tokenTypeToIndex[CustomLspSemanticTokenNames.StringVerbatim], 0, // '"' - 0, 27, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ',' - 0, 2, 12, tokenTypeToIndex[SemanticTokenTypes.Enum], 0, // 'RegexOptions' - 0, 12, 1, tokenTypeToIndex[SemanticTokenTypes.Operator], 0, // '.' - 0, 1, 23, tokenTypeToIndex[SemanticTokenTypes.EnumMember], 0, // 'IgnorePatternWhitespace' - 0, 23, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ')' - 0, 1, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // ';' - 1, 4, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } - 1, 0, 1, tokenTypeToIndex[CustomLspSemanticTokenNames.Punctuation], 0, // } - ]; - } + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); + } - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results)); - } + [Theory, CombinatorialData] + public void TestGetSemanticTokensRange_AssertCustomTokenTypes(bool isVS) + { + var capabilities = GetCapabilities(isVS); + var schema = SemanticTokensSchema.GetSchema(capabilities.HasVisualStudioLspCapability()); - [Theory, CombinatorialData] - public void TestGetSemanticTokensRange_AssertCustomTokenTypes(bool isVS) + var expectedNames = ClassificationTypeNames.AllTypeNames.Where(s => !ClassificationTypeNames.AdditiveTypeNames.Contains(s)); + foreach (var expectedClassificationName in expectedNames) { - var capabilities = GetCapabilities(isVS); - var schema = SemanticTokensSchema.GetSchema(capabilities.HasVisualStudioLspCapability()); + // Assert that the classification type name exists and is mapped to a semantic token name. + Assert.True(schema.TokenTypeMap.ContainsKey(expectedClassificationName), $"Missing token type for {expectedClassificationName}."); - var expectedNames = ClassificationTypeNames.AllTypeNames.Where(s => !ClassificationTypeNames.AdditiveTypeNames.Contains(s)); - foreach (var expectedClassificationName in expectedNames) - { - // Assert that the classification type name exists and is mapped to a semantic token name. - Assert.True(schema.TokenTypeMap.ContainsKey(expectedClassificationName), $"Missing token type for {expectedClassificationName}."); - - var tokenName = schema.TokenTypeMap[expectedClassificationName]; - Assert.True(schema.AllTokenTypes.Contains(tokenName)); - } + var tokenName = schema.TokenTypeMap[expectedClassificationName]; + Assert.True(schema.AllTokenTypes.Contains(tokenName)); } } } diff --git a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangesTests.cs b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangesTests.cs index 3b7b832625688..a2cea4ca9be85 100644 --- a/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangesTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/SemanticTokens/SemanticTokensRangesTests.cs @@ -12,60 +12,57 @@ using Xunit.Abstractions; using LSP = Roslyn.LanguageServer.Protocol; -namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens +namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests.SemanticTokens; + +public sealed class SemanticTokensRangesTests(ITestOutputHelper testOutputHelper) : AbstractSemanticTokensTests(testOutputHelper) { - public class SemanticTokensRangesTests : AbstractSemanticTokensTests + [Theory, CombinatorialData] + public async Task TestGetSemanticTokensRanges_FullDocAsync(bool mutatingLspWorkspace, bool isVS) { - public SemanticTokensRangesTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - } + var markup = + """ + {|caret:|}// Comment + static class C { } - [Theory, CombinatorialData] - public async Task TestGetSemanticTokensRanges_FullDocAsync(bool mutatingLspWorkspace, bool isVS) - { - var markup = -@"{|caret:|}// Comment -static class C { } -"; - await using var testLspServer = await CreateTestLspServerAsync( - markup, mutatingLspWorkspace, GetCapabilities(isVS)); + """; + await using var testLspServer = await CreateTestLspServerAsync( + markup, mutatingLspWorkspace, GetCapabilities(isVS)); - var ranges = new[] { new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) } }; - var results = await RunGetSemanticTokensRangesAsync(testLspServer, testLspServer.GetLocations("caret").First(), ranges); + var ranges = new[] { new LSP.Range { Start = new Position(0, 0), End = new Position(2, 0) } }; + var results = await RunGetSemanticTokensRangesAsync(testLspServer, testLspServer.GetLocations("caret").First(), ranges); - var expectedResults = new LSP.SemanticTokens(); - var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); - if (isVS) - { - expectedResults.Data = + var expectedResults = new LSP.SemanticTokens(); + var tokenTypeToIndex = GetTokenTypeToIndex(testLspServer); + if (isVS) + { + expectedResults.Data = #pragma warning disable format // Force explicit column spacing. - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } - else - { - expectedResults.Data = - [ - // Line | Char | Len | Token type | Modifier - 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' - 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' - 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' - 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' - 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' - ]; - } + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[ClassificationTypeNames.ClassName], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } + else + { + expectedResults.Data = + [ + // Line | Char | Len | Token type | Modifier + 0, 0, 10, tokenTypeToIndex[SemanticTokenTypes.Comment], 0, // '// Comment' + 1, 0, 6, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'static' + 0, 7, 5, tokenTypeToIndex[SemanticTokenTypes.Keyword], 0, // 'class' + 0, 6, 1, tokenTypeToIndex[SemanticTokenTypes.Class], (int)TokenModifiers.Static, // 'C' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '{' + 0, 2, 1, tokenTypeToIndex[ClassificationTypeNames.Punctuation], 0, // '}' + ]; + } #pragma warning restore format - await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); - AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); - } + await VerifyBasicInvariantsAndNoMultiLineTokens(testLspServer, results.Data).ConfigureAwait(false); + AssertEx.Equal(ConvertToReadableFormat(testLspServer.ClientCapabilities, expectedResults.Data), ConvertToReadableFormat(testLspServer.ClientCapabilities, results.Data)); } } diff --git a/src/LanguageServer/ProtocolUnitTests/TestConfigurableDocumentHandler.cs b/src/LanguageServer/ProtocolUnitTests/TestConfigurableDocumentHandler.cs index 30c853f44cc77..71e235bb5b8e3 100644 --- a/src/LanguageServer/ProtocolUnitTests/TestConfigurableDocumentHandler.cs +++ b/src/LanguageServer/ProtocolUnitTests/TestConfigurableDocumentHandler.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; using static Roslyn.Test.Utilities.AbstractLanguageServerProtocolTests; namespace Microsoft.CodeAnalysis.LanguageServer.UnitTests; diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs index 8226be4f8c8b4..3c3c0c0ef7c6f 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/LspWorkspaceManagerTests.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.CodeAnalysis.Testing; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; diff --git a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs index eced98f15883e..e62dca52af502 100644 --- a/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Workspaces/SourceGeneratedDocumentTests.cs @@ -8,8 +8,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.SourceGenerators; -using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Test.Utilities.TestGenerators; diff --git a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj index 33d9538cb027a..ad6db8f1feb82 100644 --- a/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj +++ b/src/NuGet/VS.ExternalAPIs.Roslyn.Package/VS.ExternalAPIs.Roslyn.Package.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/RoslynAnalyzers/Assets/EULA.txt b/src/RoslynAnalyzers/Assets/EULA.txt new file mode 100644 index 0000000000000..5381538068d65 --- /dev/null +++ b/src/RoslynAnalyzers/Assets/EULA.txt @@ -0,0 +1,44 @@ +MICROSOFT PRE-RELEASE SOFTWARE LICENSE TERMS +MICROSOFT CODE ANALYSIS 2017 NONE +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. They apply to the software named above. The terms also apply to any Microsoft services or updates for the software, except to the extent those have different terms. +IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. +1. INSTALLATION AND USE RIGHTS. +You may install and use any number of copies of the software to develop and test your applications. +2. PRE-RELEASE SOFTWARE. This software is a pre-release version. It may not work the way a final version of the software will. We may change it for the final, commercial version. We also may not release a commercial version. +3. FEEDBACK. If you give feedback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because we include your feedback in them. These rights survive this agreement. +4. DISTRIBUTABLE CODE. The software contains code that you are permitted to distribute in applications you develop if you comply with the terms below. (For this Section the term “distribution” also means deployment of your applications for third parties to access over the Internet.) +i. Distribution Rights. The code and text files listed below are “Distributable Code.” +• REDIST.TXT Files. You may copy and distribute the object code form of code listed in REDIST.TXT files, and/or any files listed on the REDIST list located following this Microsoft Software License Terms. +• Sample Code and Template. You may copy, modify and distribute the source and object code form of code marked as “sample” and “template”. +• Third Party Distribution. You may permit distributors of your applications to copy and distribute the Distributable Code as part of those applications. +ii. Distribution Requirements. For any Distributable Code you distribute, you must +• add significant primary functionality to it in your applications; +• distribute Distributable Code included in a setup program only as part of that setup program without modification; +• require distributors and external end users to agree to terms that protect the Distributable Code at least as much as this agreement; and, +• indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related to the distribution or use of your applications, except to the extent that any claim is based solely on the Distributable Code. +iii. Distribution Restrictions. You may not use Microsoft’s trademarks in your applications’ names or branding in a way that suggests your applications come from or are endorsed by Microsoft; or modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that (i) the code be disclosed or distributed in source code form; or (ii) others have the right to modify it. +5. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not +• work around any technical limitations in the software; +• reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation; +• remove, minimize, block or modify any notices of Microsoft or its suppliers in the software; +• use the software in any way that is against the law; or +• share, publish or lend the software, or provide the software as a stand-alone hosted as solution for others to use, or transfer the software or this agreement to any third party. +6. EXPORT RESTRICTIONS. Microsoft software, online services, professional services and related technology are subject to U.S. export jurisdiction. You must comply with all applicable international and national laws, including the U.S. Export Administration Regulations, the International Traffic in Arms Regulations, Office of Foreign Assets Control sanctions programs, and end-user, end use and destination restrictions by the U.S. and other governments related to Microsoft products, services and technologies. For additional information, see www.microsoft.com/exporting. +7. SUPPORT SERVICES. Because this software is “as is,” we may not provide support services for it. +8. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. +9. APPLICABLE LAW. If you acquired the software in the United States, Washington law applies to interpretation of and claims for breach of this agreement, and the laws of the state where you live apply to all other claims. If you acquired the software in any other country, its laws apply. +10. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the laws of your state or country. This agreement does not change your rights under the laws of your state or country if the laws of your state or country do not permit it to do so. Without limitation of the foregoing, for Australia, YOU HAVE STATUTORY GUARANTEES UNDER THE AUSTRALIAN CONSUMER LAW AND NOTHING IN THESE TERMS IS INTENDED TO AFFECT THOSE RIGHTS. +11. DISCLAIMER OF WARRANTY. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. +12. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. LIMITATION ON AND EXCLUSION OF DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. +This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages. +Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. +Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en français. +EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. +Cette limitation concerne: +• tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et +• les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. +Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne s’appliquera pas à votre égard. +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de votre pays si celles-ci ne le permettent pas. + diff --git a/src/RoslynAnalyzers/Assets/ThirdPartyNotices.txt b/src/RoslynAnalyzers/Assets/ThirdPartyNotices.txt new file mode 100644 index 0000000000000..1ea2df7a9583b --- /dev/null +++ b/src/RoslynAnalyzers/Assets/ThirdPartyNotices.txt @@ -0,0 +1,260 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +The Roslyn Analyzers software incorporates material from third parties. Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +%% .NET Compiler Platform NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) .NET Foundation. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +========================================= +END OF .NET Compiler Platform NOTICES AND INFORMATION + +%% Humanizer NOTICES AND INFORMATION BEGIN HERE +========================================= +The MIT License (MIT) + +Copyright (c) 2012-2014 Mehdi Khalili + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +============================================================================== + +Inflector (https://github.com/srkirkland/Inflector) +The MIT License (MIT) +Copyright (c) 2013 Scott Kirkland + +============================================================================== + +ByteSize (https://github.com/omar/ByteSize) +The MIT License (MIT) +Copyright (c) 2013-2014 Omar Khudeira (http://omar.io) + +============================================================================== +========================================= +END OF Humanizer NOTICES AND INFORMATION diff --git a/src/RoslynAnalyzers/Assets/install.ps1 b/src/RoslynAnalyzers/Assets/install.ps1 new file mode 100644 index 0000000000000..e3629746d6de5 --- /dev/null +++ b/src/RoslynAnalyzers/Assets/install.ps1 @@ -0,0 +1,55 @@ +param($installPath, $toolsPath, $package, $project) + +$analyzersDir = Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" +if (-Not (Test-Path $analyzersDir)) +{ + return +} + +$analyzersPaths = Join-Path ( $analyzersDir ) * -Resolve + +foreach($analyzersPath in $analyzersPaths) +{ + # Install the language agnostic analyzers. + if (Test-Path $analyzersPath) + { + foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) + { + if($project.Object.AnalyzerReferences) + { + $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) + } + } + } +} + +# $project.Type gives the language name like (C# or VB.NET) +$languageFolder = "" +if($project.Type -eq "C#") +{ + $languageFolder = "cs" +} +if($project.Type -eq "VB.NET") +{ + $languageFolder = "vb" +} +if($languageFolder -eq "") +{ + return +} + +foreach($analyzersPath in $analyzersPaths) +{ + # Install language specific analyzers. + $languageAnalyzersPath = join-path $analyzersPath $languageFolder + if (Test-Path $languageAnalyzersPath) + { + foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) + { + if($project.Object.AnalyzerReferences) + { + $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) + } + } + } +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Assets/uninstall.ps1 b/src/RoslynAnalyzers/Assets/uninstall.ps1 new file mode 100644 index 0000000000000..009291bb6aec1 --- /dev/null +++ b/src/RoslynAnalyzers/Assets/uninstall.ps1 @@ -0,0 +1,62 @@ +param($installPath, $toolsPath, $package, $project) + +$analyzersDir = Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" +if (-Not (Test-Path $analyzersDir)) +{ + return +} + +$analyzersPaths = Join-Path ( $analyzersDir ) * -Resolve + +foreach($analyzersPath in $analyzersPaths) +{ + # Uninstall the language agnostic analyzers. + if (Test-Path $analyzersPath) + { + foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll) + { + if($project.Object.AnalyzerReferences) + { + $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) + } + } + } +} + +# $project.Type gives the language name like (C# or VB.NET) +$languageFolder = "" +if($project.Type -eq "C#") +{ + $languageFolder = "cs" +} +if($project.Type -eq "VB.NET") +{ + $languageFolder = "vb" +} +if($languageFolder -eq "") +{ + return +} + +foreach($analyzersPath in $analyzersPaths) +{ + # Uninstall language specific analyzers. + $languageAnalyzersPath = join-path $analyzersPath $languageFolder + if (Test-Path $languageAnalyzersPath) + { + foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll) + { + if($project.Object.AnalyzerReferences) + { + try + { + $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) + } + catch + { + + } + } + } + } +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Common/Test/App.config b/src/RoslynAnalyzers/Common/Test/App.config new file mode 100644 index 0000000000000..6b281a3a5318d --- /dev/null +++ b/src/RoslynAnalyzers/Common/Test/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Directory.Build.props b/src/RoslynAnalyzers/Directory.Build.props new file mode 100644 index 0000000000000..35c7766c5e3b3 --- /dev/null +++ b/src/RoslynAnalyzers/Directory.Build.props @@ -0,0 +1,49 @@ + + + + + + true + $(MSBuildThisFileDirectory)Assets\ + + + true + + + + + 3 + 12 + 0 + $(MajorVersion).$(MinorVersion).$(PatchVersion) + beta1 + + $(MajorVersion).$(MinorVersion).0.0 + + + + + false + + + + + $(Features);flow-analysis + + $(DefineConstants),LEGACY_CODE_METRICS_MODE + + + + + + $(NoWarn);CS1574;CS8602 + + $(NoWarn);CS8603 + + diff --git a/src/RoslynAnalyzers/Directory.Build.targets b/src/RoslynAnalyzers/Directory.Build.targets new file mode 100644 index 0000000000000..0e012f14dddd5 --- /dev/null +++ b/src/RoslynAnalyzers/Directory.Build.targets @@ -0,0 +1,62 @@ + + + + + + true + + + + + $(NoWarn);RS2008 + + + + + $(NoWarn);RS2007 + + + + + + EULA.txt + true + + + ThirdPartyNotices.txt + true + + + + + + + + + + + PreserveNewest + AnalyzerReleases\$(AssemblyName)\AnalyzerReleases.Unshipped.md + + + + + + + + + PreserveNewest + AnalyzerReleases\$(AssemblyName)\AnalyzerReleases.Shipped.md + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.csproj new file mode 100644 index 0000000000000..30693b49566f9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + true + true + true + + + $(NoWarn);RS0026 + $(MicrosoftCodeAnalysisVersionForCodeAnalysisAnalyzers) + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Shipped.txt b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Shipped.txt new file mode 100644 index 0000000000000..5f282702bb03e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Shipped.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000000..56c26da3562a9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.AnalyzerUtilities/PublicAPI.Unshipped.txt @@ -0,0 +1,879 @@ +#nullable enable +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ComputeEqualsByHashCodeParts(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext! obj) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ComputeHashCodePartsSpecific(ref Analyzer.Utilities.RoslynHashCode hashCode) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.ValueDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.ComputeEqualsByHashCodeParts(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable! obj) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.ComputeHashCodeParts(ref Analyzer.Utilities.RoslynHashCode hashCode) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetAbstractDefaultValue(Microsoft.CodeAnalysis.ITypeSymbol? type) -> TAbstractAnalysisValue +Analyzer.Utilities.AnalyzerOptionsExtensions +Analyzer.Utilities.DisposeAnalysisKind +Analyzer.Utilities.DisposeAnalysisKind.AllPaths = 0 -> Analyzer.Utilities.DisposeAnalysisKind +Analyzer.Utilities.DisposeAnalysisKind.AllPathsOnlyNotDisposed = 1 -> Analyzer.Utilities.DisposeAnalysisKind +Analyzer.Utilities.DisposeAnalysisKind.NonExceptionPaths = 2 -> Analyzer.Utilities.DisposeAnalysisKind +Analyzer.Utilities.DisposeAnalysisKind.NonExceptionPathsOnlyNotDisposed = 3 -> Analyzer.Utilities.DisposeAnalysisKind +Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult +Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult.Flagged = 2 -> Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult +Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult.MaybeFlagged = 1 -> Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult +Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult.Unflagged = 0 -> Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis.HazardousUsageEvaluationResult +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Dll = 1 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.FilePathInjection = 4 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.HardcodedCertificate = 14 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.HardcodedEncryptionKey = 13 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.InformationDisclosure = 2 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Ldap = 7 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.ProcessCommand = 5 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Redirect = 8 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Regex = 6 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Sql = 0 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Xaml = 11 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Xml = 10 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.XPath = 9 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.Xss = 3 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind.ZipSlip = 12 -> Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis.SinkKind +Analyzer.Utilities.Options.EnumValuesPrefixTrigger +Analyzer.Utilities.Options.EnumValuesPrefixTrigger.AllEnumValues = 1 -> Analyzer.Utilities.Options.EnumValuesPrefixTrigger +Analyzer.Utilities.Options.EnumValuesPrefixTrigger.AnyEnumValue = 0 -> Analyzer.Utilities.Options.EnumValuesPrefixTrigger +Analyzer.Utilities.Options.EnumValuesPrefixTrigger.Heuristic = 2 -> Analyzer.Utilities.Options.EnumValuesPrefixTrigger +Analyzer.Utilities.RoslynHashCode +Analyzer.Utilities.RoslynHashCode.Add(T value) -> void +Analyzer.Utilities.RoslynHashCode.Add(T value, System.Collections.Generic.IEqualityComparer? comparer) -> void +Analyzer.Utilities.RoslynHashCode.RoslynHashCode() -> void +Analyzer.Utilities.RoslynHashCode.ToHashCode() -> int +Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Abstract = 8 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Async = 256 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Const = 2 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Extern = 128 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.None = 0 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Override = 32 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.ReadOnly = 4 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Sealed = 64 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Shared = 1 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Static = 1 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolModifiers.Virtual = 16 -> Analyzer.Utilities.SymbolModifiers +Analyzer.Utilities.SymbolNamesWithValueOption +Analyzer.Utilities.SymbolNamesWithValueOption.Contains(Microsoft.CodeAnalysis.ISymbol! symbol) -> bool +Analyzer.Utilities.SymbolNamesWithValueOption.Equals(Analyzer.Utilities.SymbolNamesWithValueOption? other) -> bool +Analyzer.Utilities.SymbolNamesWithValueOption.IsEmpty.get -> bool +Analyzer.Utilities.SymbolNamesWithValueOption.NameParts +Analyzer.Utilities.SymbolNamesWithValueOption.NameParts.AssociatedValue.get -> TValue +Analyzer.Utilities.SymbolNamesWithValueOption.NameParts.NameParts(string! symbolName, TValue associatedValue) -> void +Analyzer.Utilities.SymbolNamesWithValueOption.NameParts.SymbolName.get -> string! +Analyzer.Utilities.SymbolNamesWithValueOption.TryGetValue(Microsoft.CodeAnalysis.ISymbol! symbol, out TValue value) -> bool +Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.All = Analyzer.Utilities.SymbolVisibilityGroup.Public | Analyzer.Utilities.SymbolVisibilityGroup.Friend | Analyzer.Utilities.SymbolVisibilityGroup.Private -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.Friend = 2 -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.Internal = 2 -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.None = 0 -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.Private = 4 -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.SymbolVisibilityGroup.Public = 1 -> Analyzer.Utilities.SymbolVisibilityGroup +Analyzer.Utilities.Unit +Analyzer.Utilities.Unit.Equals(Analyzer.Utilities.Unit other) -> bool +Analyzer.Utilities.Unit.Unit() -> void +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.Children.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.CoupledNamedTypes.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.CyclomaticComplexity.get -> int +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.DepthOfInheritance.get -> int? +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ExecutableLines.get -> long +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.MaintainabilityIndex.get -> int +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.SourceLines.get -> long +Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.Symbol.get -> Microsoft.CodeAnalysis.ISymbol! +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.WellKnownTypeProvider.get -> Analyzer.Utilities.WellKnownTypeProvider! +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.BranchValue.get -> Microsoft.CodeAnalysis.IOperation? +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.ControlFlowConditionKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowConditionKind +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.Destination.get -> Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.EnteringRegions.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.FinallyRegions.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.Kind.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowBranchSemantics +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.LeavingRegionFlowCaptures.get -> System.Collections.Generic.IEnumerable! +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.LeavingRegionLocals.get -> System.Collections.Generic.IEnumerable! +Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo.LeavingRegions.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisData.AbstractAnalysisData() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisData.Dispose() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisData.IsDisposed.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain.AbstractAnalysisDomain() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult.AbstractBlockAnalysisResult(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! basicBlock) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractBlockAnalysisResult.BasicBlock.get -> Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.EntityForInstanceLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryGetCopyValueForFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.CaptureId captureId, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! copyValue) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.LambdaAndLocalFunctionAnalysisInfo.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StandaloneLocalFunctionAnalysisResultsMap.get -> System.Collections.Immutable.ImmutableDictionary!>! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StreamingContextNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetPointsToAbstractValueAtEntryBlockEnd(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue? pointsToAbstractValue) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult.LambdaAndLocalFunctionAnalysisInfo.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.InterproceduralAnalysisConfiguration() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.InterproceduralAnalysisData(TAnalysisData? initialAnalysisData, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity?, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue!)? invocationInstance, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity!, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue!)? thisOrMeInstanceForCaller, System.Collections.Immutable.ImmutableDictionary!>! argumentValuesMap, System.Collections.Immutable.ImmutableDictionary! capturedVariablesMap, System.Collections.Immutable.ImmutableDictionary! addressSharedEntities, System.Collections.Immutable.ImmutableStack! callStack, System.Collections.Immutable.ImmutableHashSet! methodsBeingAnalyzed, System.Func! getCachedAbstractValueFromCaller, System.Func! getInterproceduralControlFlowGraph, System.Func! getAnalysisEntityForFlowCapture, System.Func?>! getInterproceduralCallStackForOwningSymbol) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.InterproceduralCaptureId() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo.AnalyzedLambdas.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo.AnalyzedLocalFunctions.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo.EscapedLambdas.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.LambdaAndLocalFunctionAnalysisInfo.EscapedLocalFunctions.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisContext.AdditionalSupportedValueTypes.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisContext.GetValueForAdditionalSupportedValueTypeOperation.get -> System.Func? +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ForkForInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! invokedCfg, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? interproceduralAnalysisData) -> TAnalysisContext! +override Analyzer.Utilities.RoslynHashCode.Equals(object? obj) -> bool +override Analyzer.Utilities.RoslynHashCode.GetHashCode() -> int +override Analyzer.Utilities.SymbolNamesWithValueOption.Equals(object? obj) -> bool +override Analyzer.Utilities.SymbolNamesWithValueOption.GetHashCode() -> int +override Analyzer.Utilities.Unit.Equals(object? obj) -> bool +override Analyzer.Utilities.Unit.GetHashCode() -> int +override Analyzer.Utilities.Unit.ToString() -> string! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisContext.ForkForInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! invokedCfg, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? interproceduralAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisContext! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.Visit(Microsoft.CodeAnalysis.IOperation? operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitDelegateCreation(Microsoft.CodeAnalysis.Operations.IDelegateCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisContext.ForkForInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! invokedCfg, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData!, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisContext!, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue!>? interproceduralAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisContext! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisContext.ForkForInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! invokedCfg, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? interproceduralAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisContext! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisContext.ForkForInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! invokedCfg, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? interproceduralAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisContext! +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ComputeEqualsByHashCodeParts(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable! obj) -> bool +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ComputeHashCodeParts(ref Analyzer.Utilities.RoslynHashCode hashCode) -> void +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetAdditionalRequiredGenericInterfaces(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetAdditionalRequiredSuffixesOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetAdditionalStringFormattingMethodsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetAdditionalUseResultsMethodsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetAnalyzedSymbolKindsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, System.Collections.Immutable.ImmutableHashSet! defaultSymbolKinds) -> System.Collections.Immutable.ImmutableHashSet! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetBoolOptionValue(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! optionName, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetBoolOptionValue(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! optionName, Microsoft.CodeAnalysis.DiagnosticDescriptor? rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetCopyAnalysisOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetCopyAnalysisOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisallowedSymbolNamesWithValueOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.DisposeAnalysisKind defaultValue) -> Analyzer.Utilities.DisposeAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.DisposeAnalysisKind defaultValue) -> Analyzer.Utilities.DisposeAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeOwnershipTransferAtConstructorOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeOwnershipTransferAtConstructorOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeOwnershipTransferAtMethodCall(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetDisposeOwnershipTransferAtMethodCall(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, bool defaultValue) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetEnumerationMethodsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetEnumValuesPrefixTriggerOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.Options.EnumValuesPrefixTrigger defaultValue) -> Analyzer.Utilities.Options.EnumValuesPrefixTrigger +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetInheritanceExcludedSymbolNamesOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, string! defaultForcedValue) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetInterproceduralAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultValue) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetInterproceduralAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultValue) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetLinqChainMethodsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetMSBuildItemMetadataValues(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! itemOptionName, Microsoft.CodeAnalysis.Compilation! compilation) -> System.Collections.Immutable.ImmutableArray +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetMSBuildPropertyValue(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! optionName, Microsoft.CodeAnalysis.Compilation! compilation) -> string? +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetNullCheckValidationMethodsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetOutputKindsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> System.Collections.Immutable.ImmutableHashSet! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetOutputKindsOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, System.Collections.Immutable.ImmutableHashSet! defaultValue) -> System.Collections.Immutable.ImmutableHashSet! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetPointsToAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultValue) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetPointsToAnalysisKindOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultValue) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetStringOptionValue(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! optionName, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation) -> string! +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetSymbolVisibilityGroupOption(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.SymbolVisibilityGroup defaultValue) -> Analyzer.Utilities.SymbolVisibilityGroup +static Analyzer.Utilities.AnalyzerOptionsExtensions.GetUnsignedIntegralOptionValue(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, string! optionName, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.SyntaxTree! tree, Microsoft.CodeAnalysis.Compilation! compilation, uint defaultValue) -> uint +static Analyzer.Utilities.AnalyzerOptionsExtensions.IsConfiguredToSkipAnalysis(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.IsConfiguredToSkipAnalysis(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.ISymbol! containingContextSymbol, Microsoft.CodeAnalysis.Compilation! compilation) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.MatchesConfiguredModifiers(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.SymbolModifiers defaultRequiredModifiers = Analyzer.Utilities.SymbolModifiers.None) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.MatchesConfiguredVisibility(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.SymbolVisibilityGroup defaultRequiredVisibility = Analyzer.Utilities.SymbolVisibilityGroup.Public) -> bool +static Analyzer.Utilities.AnalyzerOptionsExtensions.MatchesConfiguredVisibility(this Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! options, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.ISymbol! containingContextSymbol, Microsoft.CodeAnalysis.Compilation! compilation, Analyzer.Utilities.SymbolVisibilityGroup defaultRequiredVisibility = Analyzer.Utilities.SymbolVisibilityGroup.Public) -> bool +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3, T4 value4) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2, T3 value3) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1, T2 value2) -> int +static Analyzer.Utilities.RoslynHashCode.Combine(T1 value1) -> int +static Analyzer.Utilities.SymbolNamesWithValueOption.Create(System.Collections.Immutable.ImmutableArray symbolNames, Microsoft.CodeAnalysis.Compilation! compilation, string? optionalPrefix, System.Func.NameParts!>! getSymbolNamePartsFunc) -> Analyzer.Utilities.SymbolNamesWithValueOption! +static Analyzer.Utilities.Unit.Default.get -> Analyzer.Utilities.Unit +static Analyzer.Utilities.Unit.operator !=(Analyzer.Utilities.Unit first, Analyzer.Utilities.Unit second) -> bool +static Analyzer.Utilities.Unit.operator ==(Analyzer.Utilities.Unit first, Analyzer.Utilities.Unit second) -> bool +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeSynchronously(Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData! +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeSynchronously(Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.ISymbol? symbol, System.Collections.Immutable.ImmutableArray indices, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? parent, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? entityForInstanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, System.Collections.Immutable.ImmutableHashSet! disposeOwnershipTransferLikelyTypes, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultPointsToAnalysisKind, bool trackInstanceFields, bool exceptionPathsAnalysis, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind interproceduralAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.ContextSensitive, bool performCopyAnalysisIfNotUserConfigured = false, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, bool defaultDisposeOwnershipTransferAtConstructor = false, bool defaultDisposeOwnershipTransferAtMethodCall = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisResult? +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Create(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, uint defaultMaxInterproceduralMethodCallChain = 3, uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Create(Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, System.Collections.Immutable.ImmutableArray rules, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.Compilation! compilation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, uint defaultMaxInterproceduralMethodCallChain = 3, uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultPointsToAnalysisKind, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind interproceduralAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.None, bool pessimisticAnalysis = true) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.DiagnosticDescriptor! rule, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind defaultPointsToAnalysisKind, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind interproceduralAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.None, bool pessimisticAnalysis = true, bool performCopyAnalysisIfNotUserConfigured = false, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, System.Collections.Immutable.ImmutableArray additionalSupportedValueTypes = default(System.Collections.Immutable.ImmutableArray), System.Func? getValueContentValueForAdditionalSupportedValueTypeOperation = null) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.AbstractDataFlowAnalysisContext(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! valueDomain, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! controlFlowGraph, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, bool pessimisticAnalysis, bool predicateAnalysis, bool exceptionPathsAnalysis, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? valueContentAnalysisResult, System.Func! tryGetOrComputeAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? parentControlFlowGraph, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? interproceduralAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.AnalyzerOptions.get -> Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.CopyAnalysisResult.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ExceptionPathsAnalysis.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.GetAnonymousFunctionControlFlowGraph(Microsoft.CodeAnalysis.FlowAnalysis.IFlowAnonymousFunctionOperation! lambda) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.GetLocalFunctionControlFlowGraph(Microsoft.CodeAnalysis.IMethodSymbol! localFunction) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.TryGetOrComputeAnalysisResult.get -> System.Func! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.InterproceduralAnalysisConfiguration.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.InterproceduralAnalysisData.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.InterproceduralAnalysisPredicate.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.OwningSymbol.get -> Microsoft.CodeAnalysis.ISymbol! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ParentControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.PessimisticAnalysis.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.PointsToAnalysisResult.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ValueContentAnalysisResult.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.PredicateAnalysis.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.ValueDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDataFlowAnalysisContext.WellKnownTypeProvider.get -> Analyzer.Utilities.WellKnownTypeProvider! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.AbstractDomain() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.Compare(T oldValue, T newValue) -> int +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.Equals(T value1, T value2) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex.AbstractIndex() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.AnalysisEntity.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CaptureId.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreationCallStack.get -> System.Collections.Immutable.ImmutableStack! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.Creation.get -> Microsoft.CodeAnalysis.IOperation? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.GetTopOfCreationCallStackOrCreation() -> Microsoft.CodeAnalysis.IOperation? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.IsAnalysisEntityDefaultLocation.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.IsNoLocation.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.IsNull.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.LocationType.get -> Microsoft.CodeAnalysis.ITypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.Symbol.get -> Microsoft.CodeAnalysis.ISymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.TryGetNodeToReportDiagnostic(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult) -> Microsoft.CodeAnalysis.SyntaxNode? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.AbstractLocationDataFlowOperationVisitor(TAnalysisContext! analysisContext) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreDataAtException, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreCurrentAnalysisData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.ResetAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! currentAnalysisData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, TAbstractAnalysisValue value) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.SetAbstractValue(System.Collections.Generic.IEnumerable! locations, TAbstractAnalysisValue value) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain.AbstractValueDomain() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.CaptureId.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.EqualsIgnoringInstanceLocation(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.EqualsIgnoringInstanceLocationId.get -> int +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.HasAncestor(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! ancestor) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.HasConstantValue.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.HasUnknownInstanceLocation.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Indices.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.InstanceLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.InstanceReferenceOperationSyntax.get -> Microsoft.CodeAnalysis.SyntaxNode? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.IsChildOrInstanceMember.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.IsLValueFlowCaptureEntity.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.IsThisOrMeInstance.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Parent.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Symbol.get -> Microsoft.CodeAnalysis.ISymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Type.get -> Microsoft.CodeAnalysis.ITypeSymbol! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.WithMergedInstanceLocation(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntityToMerge) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AddTrackedEntities(System.Collections.Generic.HashSet! builder) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AnalysisEntityBasedPredicateAnalysisData() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AnalysisEntityBasedPredicateAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data2, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AnalysisEntityBasedPredicateAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! fromData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AnalysisEntityBasedPredicateAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! mergedCoreAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! predicatedData1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! predicatedData2, bool isReachableData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.AnalysisEntityBasedPredicateAnalysisData(System.Collections.Generic.IDictionary! fromData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.ApplyPredicatedDataForEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, bool trueData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.BaseCompareHelper(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! newData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> int +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.CoreAnalysisData.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.HasAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.RemoveEntries(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.StartTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData? truePredicateData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData? falsePredicateData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.TryGetValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key, out TValue value) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.this[Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key].get -> TValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.AddTrackedEntities(System.Collections.Generic.HashSet! builder, bool forInterproceduralAnalysis = false) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.AnalysisEntityDataFlowOperationVisitor(TAnalysisContext! analysisContext) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ApplyInterproceduralAnalysisResultHelper(System.Collections.Generic.IDictionary! resultToApply) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreDataAtException, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreCurrentAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo! throwBranchWithExceptionType) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetChildAnalysisEntities(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue? instanceLocation) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetClonedAnalysisDataHelper(System.Collections.Generic.IDictionary! analysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetTrimmedCurrentAnalysisDataHelper(System.Collections.Generic.IEnumerable! withEntities, System.Collections.Generic.IDictionary! existingValues, System.Action! setAbstractValue) -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ResetAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! currentAnalysisData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.CreateWithNewInstanceRoot(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! newRootInstance) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.ThisOrMeInstance.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryCreate(Microsoft.CodeAnalysis.IOperation! operation, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryCreateForArrayElementInitializer(Microsoft.CodeAnalysis.Operations.IArrayCreationOperation! arrayCreation, System.Collections.Immutable.ImmutableArray indices, Microsoft.CodeAnalysis.ITypeSymbol! elementType, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryCreateForSymbolDeclaration(Microsoft.CodeAnalysis.ISymbol! symbol, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryCreateForTupleElements(Microsoft.CodeAnalysis.Operations.ITupleOperation! tupleOperation, out System.Collections.Immutable.ImmutableArray elementEntities) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryGetForFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.CaptureId captureId, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory.TryGetForInterproceduralAnalysis(Microsoft.CodeAnalysis.IOperation! operation, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.AnalysisEntityMapAbstractDomain(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! valueDomain, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? pointsToAnalysisResult) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo.AnalysisEntity.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo.ArgumentInfo(Microsoft.CodeAnalysis.IOperation! operation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? analysisEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, TAbstractAnalysisValue value) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo.InstanceLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo.Operation.get -> Microsoft.CodeAnalysis.IOperation! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo.Value.get -> TAbstractAnalysisValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.CacheBasedEquatable() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.Equals(T? other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue.AnalysisEntities.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue.Kind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind.Invalid = 4 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind.KnownReferenceCopy = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind.KnownValueCopy = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind.NotApplicable = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind.Unknown = 3 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyBlockAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyBlockAnalysisResult.Data.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyBlockAnalysisResult.IsReachable.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.AnalysisDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.DataFlowAnalysis(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain! analysisDomain, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor! operationVisitor) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.TryGetOrComputeResultCore(TAnalysisContext! analysisContext, bool cacheResult) -> TAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.OperationVisitor.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.ControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.DataFlowAnalysisResult(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult! other) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.EntryBlockOutput.get -> TBlockAnalysisResult! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.ExceptionPathsExitBlockOutput.get -> TBlockAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.ExitBlockOutput.get -> TBlockAnalysisResult! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.GetPredicateKind(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.MergedStateForUnhandledThrowOperations.get -> TBlockAnalysisResult? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.ReturnValueAndPredicateKind.get -> (TAbstractAnalysisValue Value, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind PredicateValueKind)? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.this[Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! block].get -> TBlockAnalysisResult! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.this[Microsoft.CodeAnalysis.IOperation! operation].get -> TAbstractAnalysisValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult.this[Microsoft.CodeAnalysis.OperationKind operationKind, Microsoft.CodeAnalysis.SyntaxNode! syntax].get -> TAbstractAnalysisValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.AnalysisEntityFactory.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityFactory! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreDataAtException, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreCurrentAnalysisData, System.Func? predicate) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.CacheAbstractValue(Microsoft.CodeAnalysis.IOperation! operation, TAbstractAnalysisValue value) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.CurrentAnalysisData.get -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.CurrentBasicBlock.get -> Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.DataFlowAnalysisContext.get -> TAnalysisContext! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.DataFlowOperationVisitor(TAnalysisContext! analysisContext) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.FlowBranchConditionKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowConditionKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetCachedAbstractValue(Microsoft.CodeAnalysis.IOperation! operation) -> TAbstractAnalysisValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetClonedCurrentAnalysisData() -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetDefaultCopyValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetEscapedLocations(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! parameterEntity) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetEscapedLocations(Microsoft.CodeAnalysis.IOperation! operation) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetNullAbstractValue(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HasCompletePointsToAnalysisResult.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsAnyAssertMethod(Microsoft.CodeAnalysis.IMethodSymbol! method) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsLValueFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureOperation! flowCapture) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsLValueFlowCaptureReference(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureReferenceOperation! flowCaptureReference) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MergeAnalysisData(TAnalysisData! value1, TAnalysisData! value2, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! forBlock, bool forBackEdge) -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.OnStartBlockAnalysis(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! block, TAnalysisData! input) -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetOrComputeAnalysisResult.get -> System.Func! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HasPointsToAnalysisResult.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.InterproceduralResultsMap.get -> System.Collections.Immutable.ImmutableDictionary!>! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsCurrentBlockReachable() -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsInsideAnonymousObjectInitializer.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsParameterEntityForCurrentMethod(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.OnEndBlockAnalysis(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! block, TAnalysisData! analysisData) -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.OnLeavingRegions(System.Collections.Generic.IEnumerable! leavingRegionLocals, System.Collections.Generic.IEnumerable! leavingRegionFlowCaptures, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! currentBasicBlock, TAnalysisData! input) -> TAnalysisData! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.OwningSymbol.get -> Microsoft.CodeAnalysis.ISymbol! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.PessimisticAnalysis.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.PredicateAnalysis.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ThisOrMePointsToAbstractValue.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetAddressSharedCopyValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetInterproceduralAnalysisResult(Microsoft.CodeAnalysis.IOperation! operation, out TAnalysisResult? analysisResult) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetMergedNullAbstractValueAtUnhandledThrowOperationsInGraph(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue nullAbstractValue) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TryGetNullAbstractValueAtCurrentBlockEntry(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue nullAbstractValue) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.UpdateValuesForAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! targetAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! newAnalysisData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ValueDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitArray(System.Collections.Generic.IEnumerable! operations, object? argument) -> TAbstractAnalysisValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.WellKnownTypeProvider.get -> Analyzer.Utilities.WellKnownTypeProvider! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ExceptionNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TaskNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MemoryStreamNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.InterlockedNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ContractNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IDisposableNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GenericIEquatableNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SerializationInfoNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MonitorNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GenericTaskNamedType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StringReaderType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.CollectionNamedTypes.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Add(System.Collections.Generic.KeyValuePair item) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Add(TKey key, TValue value) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Clear() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Contains(System.Collections.Generic.KeyValuePair item) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.ContainsKey(TKey key) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.CopyTo(System.Collections.Generic.KeyValuePair[]! array, int arrayIndex) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Count.get -> int +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.DictionaryAnalysisData() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.DictionaryAnalysisData(System.Collections.Generic.IDictionary! initializer) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.GetEnumerator() -> System.Collections.Generic.IEnumerator>! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.IsReadOnly.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Keys.get -> System.Collections.Generic.ICollection! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Remove(System.Collections.Generic.KeyValuePair item) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Remove(TKey key) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.ToImmutableDictionary() -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.TryGetValue(TKey key, out TValue value) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.Values.get -> System.Collections.Generic.ICollection! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.this[TKey key].get -> TValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData.this[TKey key].set -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.DisposingOrEscapingOperations.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.Kind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.Disposed = 5 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.Escaped = 3 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.Invalid = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.MaybeDisposed = 6 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.NotDisposable = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.NotDisposed = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.NotDisposedOrEscaped = 4 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind.Unknown = 7 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAnalysisResult.TrackedInstanceFieldPointsToMap.get -> System.Collections.Immutable.ImmutableDictionary? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeBlockAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeBlockAnalysisResult.Data.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ForwardDataFlowAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ForwardDataFlowAnalysis.ForwardDataFlowAnalysis(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain! analysisDomain, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor! operationVisitor) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext.ControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext.GetAnonymousFunctionControlFlowGraph(Microsoft.CodeAnalysis.FlowAnalysis.IFlowAnonymousFunctionOperation! lambda) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext.GetLocalFunctionControlFlowGraph(Microsoft.CodeAnalysis.IMethodSymbol! localFunction) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext.OwningSymbol.get -> Microsoft.CodeAnalysis.ISymbol! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult.AnalysisDataForUnhandledThrowOperations.get -> object? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult.ControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult.ReturnValueAndPredicateKind.get -> (TAbstractAnalysisValue Value, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind PredicateValueKind)? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisResult.TaskWrappedValuesMap.get -> object? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.InterproceduralAnalysisKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.MaxInterproceduralLambdaOrLocalFunctionCallChain.get -> uint +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.MaxInterproceduralMethodCallChain.get -> uint +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.AddressSharedEntities.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.ArgumentValuesMap.get -> System.Collections.Immutable.ImmutableDictionary!>! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.CallStack.get -> System.Collections.Immutable.ImmutableStack! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.CapturedVariablesMap.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.GetAnalysisEntityForFlowCapture.get -> System.Func! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.GetCachedAbstractValueFromCaller.get -> System.Func! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.GetInterproceduralCallStackForOwningSymbol.get -> System.Func?>! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.GetInterproceduralControlFlowGraph.get -> System.Func! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.InitialAnalysisData.get -> TAnalysisData? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.InvocationInstance.get -> (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.MethodsBeingAnalyzed.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisData.ThisOrMeInstanceForCaller.get -> (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.ContextSensitive = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind.None = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate.InterproceduralAnalysisPredicate(System.Func? skipAnalysisForInvokedMethodPredicate, System.Func? skipAnalysisForInvokedLambdaOrLocalFunctionPredicate, System.Func? skipAnalysisForInvokedContextPredicate) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate.SkipInterproceduralAnalysis(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.IDataFlowAnalysisContext! interproceduralAnalysisContext) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate.SkipInterproceduralAnalysis(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, bool isLambdaOrLocalFunction) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.ControlFlowGraph.get -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.Id.get -> Microsoft.CodeAnalysis.FlowAnalysis.CaptureId +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.IsLValueFlowCapture.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.MapAbstractDomain(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! valueDomain) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.ValueDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue.Invalid = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue.MaybeNull = 4 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue.NotNull = 3 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue.Null = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue.Undefined = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.Kind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.LValueCapturedOperations.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.Locations.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.NullState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.NullAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.Invalid = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.KnownLValueCaptures = 3 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.KnownLocations = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.Undefined = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.Unknown = 6 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.UnknownNotNull = 5 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind.UnknownNull = 4 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisContext.PointsToAnalysisKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind.Complete = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind.None = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult.GetEscapedAbstractLocations(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult.GetEscapedAbstractLocations(Microsoft.CodeAnalysis.IOperation! operation) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult.PointsToAnalysisKind.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToBlockAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToBlockAnalysisResult.Data.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToBlockAnalysisResult.IsReachable.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.PredicateAnalysisEntityDataFlowOperationVisitor(TAnalysisContext! analysisContext) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind.AlwaysFalse = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind.AlwaysTrue = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind.Unknown = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.ApplyPredicatedDataForEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, bool trueData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.AssertValidPredicatedAnalysisData(System.Action!>! assertValidAnalysisData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.BaseCompareHelper(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! newData) -> int +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.HasPredicatedData.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.HasPredicatedDataForEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.IsReachableBlockData.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.IsReachableBlockData.set -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData.Dispose() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData.FalsePredicatedData.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData.PerEntityPredicatedAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? truePredicatedData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? falsePredicatedData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData.PerEntityPredicatedAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData! fromData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PerEntityPredicatedAnalysisData.TruePredicatedData.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PredicatedAnalysisData() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PredicatedAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! fromData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.PredicatedAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! predicatedData1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData! predicatedData2, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreAnalysisData1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreAnalysisData2, bool isReachableData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.RemoveEntriesInPredicatedData(TKey key) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.ResetPredicatedData() -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.StartTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? truePredicatedData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? falsePredicatedData) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.StopTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.TransferPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! fromEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! toEntity) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.CoreDataAnalysisDomain.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.PredicatedAnalysisDataDomain(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> void +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain.Intersect(System.Collections.Immutable.ImmutableHashSet! value1, System.Collections.Immutable.ImmutableHashSet! value2) -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo? other) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState.Invalid = 0 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState.Maybe = 3 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState.No = 2 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState.Undefined = 1 -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.IsLiteralState.get -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.TryGetSingleNonNullLiteral(out T literalValue) -> bool +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.LiteralValues.get -> System.Collections.Immutable.ImmutableHashSet! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.NonLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContainsNonLiteralState +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysis +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisContext +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisData +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentBlockAnalysisResult +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentBlockAnalysisResult.Data.get -> System.Collections.Immutable.ImmutableDictionary! +Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentBlockAnalysisResult.IsReachable.get -> bool +Microsoft.CodeAnalysis.RulesetToEditorconfig.Converter +Analyzer.Utilities.WellKnownTypeProvider +Analyzer.Utilities.WellKnownTypeProvider.Compilation.get -> Microsoft.CodeAnalysis.Compilation! +Analyzer.Utilities.WellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(string! fullTypeName, out Microsoft.CodeAnalysis.INamedTypeSymbol? namedTypeSymbol) -> bool +Analyzer.Utilities.WellKnownTypeProvider.GetOrCreateTypeByMetadataName(string! fullTypeName) -> Microsoft.CodeAnalysis.INamedTypeSymbol? +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain.Clone(TAnalysisData! value) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain.Compare(TAnalysisData! oldValue, TAnalysisData! newValue) -> int +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain.Equals(TAnalysisData! value1, TAnalysisData! value2) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisDomain.Merge(TAnalysisData! value1, TAnalysisData! value2) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.Bottom.get -> T +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.Compare(T oldValue, T newValue, bool assertMonotonicity) -> int +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.Merge(T value1, T value2) -> T +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.EscapeValueForParameterPointsToLocationOnExit(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, System.Collections.Immutable.ImmutableHashSet! escapedLocations) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.GetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! location) -> TAbstractAnalysisValue +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! location, TAbstractAnalysisValue value) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.SetValueForParameterPointsToLocationOnEntry(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! pointsToAbstractValue) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.StopTrackingAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! location) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractValueDomain.UnknownOrMayBeValue.get -> T +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.Clone() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.Compare(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! other, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> int +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.WithMergedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.AddTrackedEntities(TAnalysisData! analysisData, System.Collections.Generic.HashSet! builder, bool forInterproceduralAnalysis = false) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ApplyInterproceduralAnalysisResultCore(TAnalysisData! resultData) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> TAbstractAnalysisValue +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetTrimmedCurrentAnalysisData(System.Collections.Generic.IEnumerable! withEntities) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.HasAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ResetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, TAbstractAnalysisValue value) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.StopTrackingEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, TAnalysisData! analysisData) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.AssertValidEntryForMergedMap(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, TValue value) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.CanSkipNewEntry(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, TValue value) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.GetDefaultValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> TValue +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.ToBlockResult(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! basicBlock, TAnalysisData! blockAnalysisData) -> TBlockAnalysisResult! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.ToResult(TAnalysisContext! analysisContext, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult! dataFlowAnalysisResult) -> TAnalysisResult! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(TAnalysisData! dataAtException, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo! throwBranchWithExceptionType) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.Equals(TAnalysisData! value1, TAnalysisData! value2) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.EscapeValueForParameterOnExit(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetClonedAnalysisData(TAnalysisData! analysisData) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetEmptyAnalysisData() -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetExitBlockOutputData(TAnalysisResult! analysisResult) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HasAnyAbstractValue(TAnalysisData! data) -> bool +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MergeAnalysisData(TAnalysisData! value1, TAnalysisData! value2) -> TAnalysisData! +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ResetCurrentAnalysisData() -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ResetReferenceTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! pointsToAbstractValue) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ResetValueTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetAbstractValueForArrayElementInitializer(Microsoft.CodeAnalysis.Operations.IArrayCreationOperation! arrayCreation, System.Collections.Immutable.ImmutableArray indices, Microsoft.CodeAnalysis.ITypeSymbol! elementType, Microsoft.CodeAnalysis.IOperation! initializer, TAbstractAnalysisValue value) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetAbstractValueForAssignment(Microsoft.CodeAnalysis.IOperation! target, Microsoft.CodeAnalysis.IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue, bool mayBeAssignment = false) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetAbstractValueForTupleElementAssignment(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! tupleElementEntity, Microsoft.CodeAnalysis.IOperation! assignedValueOperation, TAbstractAnalysisValue assignedValue) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetValueForParameterOnEntry(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo? assignedValue) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StopTrackingDataForParameter(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +abstract Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.UpdateValuesForAnalysisData(TAnalysisData! targetAnalysisData) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.ComputeAnalysisValueForEscapedRefOrOutArgument(Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.EscapeValueForParameterOnExit(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.ResetReferenceTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! pointsToAbstractValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.ResetValueTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.SetValueForParameterOnEntry(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo? assignedValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.StopTrackingDataForParameter(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitAnonymousObjectCreation(Microsoft.CodeAnalysis.Operations.IAnonymousObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitArrayCreation(Microsoft.CodeAnalysis.Operations.IArrayCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitDelegateCreation(Microsoft.CodeAnalysis.Operations.IDelegateCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitDynamicObjectCreation(Microsoft.CodeAnalysis.Operations.IDynamicObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitObjectCreation(Microsoft.CodeAnalysis.Operations.IObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitReDimClause(Microsoft.CodeAnalysis.Operations.IReDimClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.VisitTypeParameterObjectCreation(Microsoft.CodeAnalysis.Operations.ITypeParameterObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.Dispose(bool disposing) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ComputeAnalysisValueForReferenceOperation(Microsoft.CodeAnalysis.IOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.EscapeValueForParameterOnExit(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetInitialInterproceduralAnalysisData(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? invocationInstance, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? thisOrMeInstanceForCaller, System.Collections.Immutable.ImmutableDictionary!>! argumentValuesMap, System.Collections.Generic.IDictionary? pointsToValues, System.Collections.Generic.IDictionary? copyValues, System.Collections.Generic.IDictionary? valueContentValues, bool isLambdaOrLocalFunction, bool hasParameterWithDelegateType) -> TAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetMergedAnalysisDataForPossibleThrowingOperation(TAnalysisData? existingData, Microsoft.CodeAnalysis.IOperation! operation) -> TAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetMergedDataForUnhandledThrowOperations() -> TAnalysisData? +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ProcessOutOfScopeLocalsAndFlowCaptures(System.Collections.Generic.IEnumerable! locals, System.Collections.Generic.IEnumerable! flowCaptures) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ResetReferenceTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! pointsToAbstractValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ResetValueTypeInstanceAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetAbstractValueForArrayElementInitializer(Microsoft.CodeAnalysis.Operations.IArrayCreationOperation! arrayCreation, System.Collections.Immutable.ImmutableArray indices, Microsoft.CodeAnalysis.ITypeSymbol! elementType, Microsoft.CodeAnalysis.IOperation! initializer, TAbstractAnalysisValue value) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetAbstractValueForAssignment(Microsoft.CodeAnalysis.IOperation! target, Microsoft.CodeAnalysis.IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue, bool mayBeAssignment = false) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetAbstractValueForTupleElementAssignment(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! tupleElementEntity, Microsoft.CodeAnalysis.IOperation! assignedValueOperation, TAbstractAnalysisValue assignedValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetValueForParameterOnEntry(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ArgumentInfo? assignedValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.VisitDeconstructionAssignment(Microsoft.CodeAnalysis.Operations.IDeconstructionAssignmentOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.Merge(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! map1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! map2) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData.Clone() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData.Compare(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! other, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData.Reset(System.Func! getResetValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! value) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysisData.WithMergedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.DefaultVisit(Microsoft.CodeAnalysis.IOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitAnonymousObjectCreation(Microsoft.CodeAnalysis.Operations.IAnonymousObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitArrayElementReference(Microsoft.CodeAnalysis.Operations.IArrayElementReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitArrayInitializer(Microsoft.CodeAnalysis.Operations.IArrayInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitAwait(Microsoft.CodeAnalysis.Operations.IAwaitOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitCaughtException(Microsoft.CodeAnalysis.FlowAnalysis.ICaughtExceptionOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitCompoundAssignment(Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitConstantPattern(Microsoft.CodeAnalysis.Operations.IConstantPatternOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitConversion(Microsoft.CodeAnalysis.Operations.IConversionOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitDeconstructionAssignment(Microsoft.CodeAnalysis.Operations.IDeconstructionAssignmentOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitDefaultValue(Microsoft.CodeAnalysis.Operations.IDefaultValueOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitDynamicMemberReference(Microsoft.CodeAnalysis.Operations.IDynamicMemberReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitEventReference(Microsoft.CodeAnalysis.Operations.IEventReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitFieldReference(Microsoft.CodeAnalysis.Operations.IFieldReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitFlowAnonymousFunction(Microsoft.CodeAnalysis.FlowAnalysis.IFlowAnonymousFunctionOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitFlowCapture(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitFlowCaptureReference(Microsoft.CodeAnalysis.FlowAnalysis.IFlowCaptureReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitIncrementOrDecrement(Microsoft.CodeAnalysis.Operations.IIncrementOrDecrementOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInterpolatedStringText(Microsoft.CodeAnalysis.Operations.IInterpolatedStringTextOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInterpolation(Microsoft.CodeAnalysis.Operations.IInterpolationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitIsNull(Microsoft.CodeAnalysis.FlowAnalysis.IIsNullOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitIsPattern(Microsoft.CodeAnalysis.Operations.IIsPatternOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitLocalReference(Microsoft.CodeAnalysis.Operations.ILocalReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitLock(Microsoft.CodeAnalysis.Operations.ILockOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitMethodReference(Microsoft.CodeAnalysis.Operations.IMethodReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitObjectCreation(Microsoft.CodeAnalysis.Operations.IObjectCreationOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitParameterReference(Microsoft.CodeAnalysis.Operations.IParameterReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitParenthesized(Microsoft.CodeAnalysis.Operations.IParenthesizedOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitPropertyReference(Microsoft.CodeAnalysis.Operations.IPropertyReferenceOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitSimpleAssignment(Microsoft.CodeAnalysis.Operations.ISimpleAssignmentOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitStaticLocalInitializationSemaphore(Microsoft.CodeAnalysis.FlowAnalysis.IStaticLocalInitializationSemaphoreOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitTranslatedQuery(Microsoft.CodeAnalysis.Operations.ITranslatedQueryOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitTuple(Microsoft.CodeAnalysis.Operations.ITupleOperation! operation, object? argument) -> TAbstractAnalysisValue +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.Equals(object! obj) -> bool +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.GetHashCode() -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.Equals(object! obj) -> bool +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.GetHashCode() -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.Clone(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! value) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.Merge(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! value1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! value2) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData.Clone() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData.Compare(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! other, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData.Reset(System.Func! getResetValue) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! value) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisData.WithMergedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.IsReachableBlockData(TAnalysisData! analysisData) -> bool +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.SetPredicateValueKind(Microsoft.CodeAnalysis.IOperation! operation, TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind predicateValueKind) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.UpdateReachability(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! basicBlock, TAnalysisData! analysisData, bool isReachable) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.Dispose(bool disposing) -> void +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.Clone(TAnalysisData! value) -> TAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.Compare(TAnalysisData! oldValue, TAnalysisData! newValue) -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.Equals(TAnalysisData! value1, TAnalysisData! value2) -> bool +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisDataDomain.Merge(TAnalysisData! value1, TAnalysisData! value2) -> TAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain.Bottom.get -> System.Collections.Immutable.ImmutableHashSet! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain.Compare(System.Collections.Immutable.ImmutableHashSet! oldValue, System.Collections.Immutable.ImmutableHashSet! newValue, bool assertMonotonicity) -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain.Merge(System.Collections.Immutable.ImmutableHashSet! value1, System.Collections.Immutable.ImmutableHashSet! value2) -> System.Collections.Immutable.ImmutableHashSet! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo.Equals(object! obj) -> bool +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ThrownExceptionInfo.GetHashCode() -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.ToString() -> string! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisData.Clone() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisData.Compare(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! other, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> int +override Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAnalysisData.WithMergedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! data, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain! coreDataAnalysisDomain) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData! +override sealed Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ToString() -> string! +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ApplyInterproceduralAnalysisResult(TAnalysisData! resultData, bool isLambdaOrLocalFunction, bool hasDelegateTypeArgument, TAnalysisResult! analysisResult) -> void +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ComputeAnalysisValueForEscapedRefOrOutArgument(Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.StopTrackingDataForParameter(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> void +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.StopTrackingDataForParameters(System.Collections.Immutable.ImmutableDictionary! parameterEntities) -> void +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.Equals(object? obj) -> bool +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.GetHashCode() -> int +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitAnonymousFunction(Microsoft.CodeAnalysis.Operations.IAnonymousFunctionOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitArgument(Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitBinaryOperator(Microsoft.CodeAnalysis.Operations.IBinaryOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitBlock(Microsoft.CodeAnalysis.Operations.IBlockOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitBranch(Microsoft.CodeAnalysis.Operations.IBranchOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitCatchClause(Microsoft.CodeAnalysis.Operations.ICatchClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitCoalesce(Microsoft.CodeAnalysis.Operations.ICoalesceOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitConditional(Microsoft.CodeAnalysis.Operations.IConditionalOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitConditionalAccess(Microsoft.CodeAnalysis.Operations.IConditionalAccessOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitConditionalAccessInstance(Microsoft.CodeAnalysis.Operations.IConditionalAccessInstanceOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitDefaultCaseClause(Microsoft.CodeAnalysis.Operations.IDefaultCaseClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitEmpty(Microsoft.CodeAnalysis.Operations.IEmptyOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitEnd(Microsoft.CodeAnalysis.Operations.IEndOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitFieldInitializer(Microsoft.CodeAnalysis.Operations.IFieldInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitForEachLoop(Microsoft.CodeAnalysis.Operations.IForEachLoopOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitForLoop(Microsoft.CodeAnalysis.Operations.IForLoopOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitForToLoop(Microsoft.CodeAnalysis.Operations.IForToLoopOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInvocation(Microsoft.CodeAnalysis.Operations.IInvocationOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitLabeled(Microsoft.CodeAnalysis.Operations.ILabeledOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitLocalFunction(Microsoft.CodeAnalysis.Operations.ILocalFunctionOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitMemberInitializer(Microsoft.CodeAnalysis.Operations.IMemberInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitNameOf(Microsoft.CodeAnalysis.Operations.INameOfOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitObjectOrCollectionInitializer(Microsoft.CodeAnalysis.Operations.IObjectOrCollectionInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitParameterInitializer(Microsoft.CodeAnalysis.Operations.IParameterInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitPatternCaseClause(Microsoft.CodeAnalysis.Operations.IPatternCaseClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitPropertyInitializer(Microsoft.CodeAnalysis.Operations.IPropertyInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitRangeCaseClause(Microsoft.CodeAnalysis.Operations.IRangeCaseClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitRelationalCaseClause(Microsoft.CodeAnalysis.Operations.IRelationalCaseClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitReturn(Microsoft.CodeAnalysis.Operations.IReturnOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitSingleValueCaseClause(Microsoft.CodeAnalysis.Operations.ISingleValueCaseClauseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitSwitch(Microsoft.CodeAnalysis.Operations.ISwitchOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitSwitchCase(Microsoft.CodeAnalysis.Operations.ISwitchCaseOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitThrow(Microsoft.CodeAnalysis.Operations.IThrowOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitTry(Microsoft.CodeAnalysis.Operations.ITryOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitUnaryOperator(Microsoft.CodeAnalysis.Operations.IUnaryOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitUsing(Microsoft.CodeAnalysis.Operations.IUsingOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitVariableDeclaration(Microsoft.CodeAnalysis.Operations.IVariableDeclarationOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitVariableDeclarationGroup(Microsoft.CodeAnalysis.Operations.IVariableDeclarationGroupOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitVariableDeclarator(Microsoft.CodeAnalysis.Operations.IVariableDeclaratorOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitVariableInitializer(Microsoft.CodeAnalysis.Operations.IVariableInitializerOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitWhileLoop(Microsoft.CodeAnalysis.Operations.IWhileLoopOperation! operation, object? argument) -> TAbstractAnalysisValue +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.Compare(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! oldValue, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! newValue) -> int +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.MapAbstractDomain.Equals(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! value1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! value2) -> bool +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.ApplyPredicatedDataForEntity(TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, bool trueData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.HasPredicatedDataForEntity(TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> bool +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.StartTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, TAnalysisData? truePredicateData, TAnalysisData? falsePredicateData) -> void +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.StopTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> void +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.SupportsPredicateAnalysis.get -> bool +override sealed Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateAnalysisEntityDataFlowOperationVisitor.TransferPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! fromEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! toEntity) -> void +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeAsync(Microsoft.CodeAnalysis.Compilation! compilation, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeAsync(Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.Compilation! compilation, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractDomain.FireNonMonotonicAssertIfNeeded(bool assertMonotonicity) -> void +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex.Create(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex.Create(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex.Create(int index) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractIndex! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateAllocationLocation(Microsoft.CodeAnalysis.IOperation! creation, Microsoft.CodeAnalysis.ITypeSymbol! locationType, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisContext! analysisContext) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateAnalysisEntityDefaultLocation(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateFlowCaptureLocation(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId captureId, Microsoft.CodeAnalysis.ITypeSymbol! locationType, System.Collections.Immutable.ImmutableStack? creationCallStack) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateSymbolLocation(Microsoft.CodeAnalysis.ISymbol! symbol, System.Collections.Immutable.ImmutableStack? creationCallStack) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.CreateThisOrMeLocation(Microsoft.CodeAnalysis.INamedTypeSymbol! namedTypeSymbol, System.Collections.Immutable.ImmutableStack? creationCallStack) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.GetClonedAnalysisDataHelper(System.Collections.Generic.IDictionary! analysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.GetEmptyAnalysisDataHelper() -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId interproceduralCaptureId, Microsoft.CodeAnalysis.ITypeSymbol! type, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.Create(Microsoft.CodeAnalysis.Operations.IInstanceReferenceOperation! instanceReferenceOperation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity.CreateThisOrMeInstance(Microsoft.CodeAnalysis.INamedTypeSymbol! typeSymbol, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.IsChildAnalysisEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! entity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! ancestorEntity) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.IsChildAnalysisEntity(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! entity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.operator !=(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable? value1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable? value2) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable.operator ==(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable? value1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CacheBasedEquatable? value2) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue.Invalid.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue.NotApplicable.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue.Unknown.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, bool pessimisticAnalysis = true, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind pointsToAnalysisKind = Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties, bool exceptionPathsAnalysis = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.Flow(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor! operationVisitor, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! block, TAnalysisData! data) -> TAnalysisData! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysis.FlowBranch(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor! operationVisitor, Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowBranch! branch, TAnalysisData! data) -> TAnalysisData! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.EqualsHelper(System.Collections.Generic.IDictionary! dict1, System.Collections.Generic.IDictionary! dict2) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.operator !=(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration left, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration right) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration.operator ==(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration left, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration right) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.operator !=(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId left, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId right) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId.operator ==(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId left, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralCaptureId right) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.Invalid.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.NoLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.NullLocation.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.Undefined.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.Unknown.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.UnknownNotNull.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue.UnknownNull.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind pointsToAnalysisKind, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, bool pessimisticAnalysis = true, bool performCopyAnalysis = false, bool exceptionPathsAnalysis = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! cfg, Microsoft.CodeAnalysis.ISymbol! owningSymbol, Microsoft.CodeAnalysis.Diagnostics.AnalyzerOptions! analyzerOptions, Analyzer.Utilities.WellKnownTypeProvider! wellKnownTypeProvider, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisKind pointsToAnalysisKind, out Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowAnalysisResult? copyAnalysisResult, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, bool pessimisticAnalysis = true, bool performCopyAnalysis = false, bool exceptionPathsAnalysis = false) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAnalysisResult? +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.EqualsHelper(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? dict1, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData? dict2) -> bool +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain.Default.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.SetAbstractDomain! +static Analyzer.Utilities.WellKnownTypeProvider.GetOrCreate(Microsoft.CodeAnalysis.Compilation! compilation) -> Analyzer.Utilities.WellKnownTypeProvider! +static readonly Analyzer.Utilities.SymbolNamesWithValueOption.Empty -> Analyzer.Utilities.SymbolNamesWithValueOption! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.NoLocation -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation.Null -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocation! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.Invalid -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.NotDisposable -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.NotDisposed -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue! +static readonly Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue.Unknown -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis.DisposeAbstractValue! +static Microsoft.CodeAnalysis.RulesetToEditorconfig.Converter.GenerateEditorconfig(string! rulesetFilePath, string! editorconfigFilePath) -> void +static Microsoft.CodeAnalysis.RulesetToEditorconfig.Converter.GetEditorconfig(string! rulesetFilePath) -> string! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.ContainsEmptyStringLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.ContainsOneIntergralLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.ContainsNullLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.ContainsZeroIntergralLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.DoesNotContainLiteralOrNonLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.InvalidState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.MayBeContainsNonLiteralState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue.UndefinedState.get -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractAnalysisData.Dispose(bool disposing) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AbstractLocationDataFlowOperationVisitor.HandleInstanceCreation(Microsoft.CodeAnalysis.IOperation! creation, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! instanceLocation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.HasAnyAbstractValue.get -> bool +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.Reset(System.Func! getResetValue) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityBasedPredicateAnalysisData.SetAbstractValue(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! key, TValue value) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.ComputeAnalysisValueForEscapedRefOrOutArgument(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity, Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetDefaultValueForParameterOnEntry(Microsoft.CodeAnalysis.IParameterSymbol! parameter, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! analysisEntity) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.GetDefaultValueForParameterOnExit(Microsoft.CodeAnalysis.ITypeSymbol! parameterType) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SetAbstractValueForAssignment(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! targetAnalysisEntity, Microsoft.CodeAnalysis.IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityDataFlowOperationVisitor.SupportsPredicateAnalysis.get -> bool +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.AssertValidAnalysisData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! map) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntityMapAbstractDomain.OnNewMergedValue(TValue value) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ApplyInterproceduralAnalysisResult(TAnalysisData! resultData, bool isLambdaOrLocalFunction, bool hasDelegateTypeArgument, TAnalysisResult! analysisResult) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ApplyPredicatedDataForEntity(TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, bool trueData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.AssertValidAnalysisData(TAnalysisData! analysisData) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ComputeAnalysisValueForEscapedRefOrOutArgument(Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ComputeAnalysisValueForReferenceOperation(Microsoft.CodeAnalysis.IOperation! operation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ComputeValueForCompoundAssignment(Microsoft.CodeAnalysis.Operations.ICompoundAssignmentOperation! operation, TAbstractAnalysisValue targetValue, TAbstractAnalysisValue assignedValue, Microsoft.CodeAnalysis.ITypeSymbol? targetType, Microsoft.CodeAnalysis.ITypeSymbol? assignedValueType) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ComputeValueForIncrementOrDecrementOperation(Microsoft.CodeAnalysis.Operations.IIncrementOrDecrementOperation! operation, TAbstractAnalysisValue targetValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.Flow(Microsoft.CodeAnalysis.IOperation! statement, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! block, TAnalysisData! input) -> TAnalysisData! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.FlowBranch(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! fromBlock, Microsoft.CodeAnalysis.FlowAnalysis.BranchWithInfo! branch, TAnalysisData! input) -> (TAnalysisData! output, bool isFeasibleBranch) +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetAbstractDefaultValueForCatchVariable(Microsoft.CodeAnalysis.Operations.ICatchClauseOperation! catchClause) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetAssignedValueForPattern(Microsoft.CodeAnalysis.Operations.IIsPatternOperation! operation, TAbstractAnalysisValue operandValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetCopyAbstractValue(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAbstractValue! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetInitialInterproceduralAnalysisData(Microsoft.CodeAnalysis.IMethodSymbol! invokedMethod, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity? Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? invocationInstance, (Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! Instance, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! PointsToValue)? thisOrMeInstanceForCaller, System.Collections.Immutable.ImmutableDictionary!>! argumentValuesMap, System.Collections.Generic.IDictionary? pointsToValues, System.Collections.Generic.IDictionary? copyValues, System.Collections.Generic.IDictionary? valueContentValues, bool isLambdaOrLocalFunction, bool hasParameterWithDelegateType) -> TAnalysisData! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetMergedAnalysisDataForPossibleThrowingOperation(TAnalysisData? existingData, Microsoft.CodeAnalysis.IOperation! operation) -> TAnalysisData! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetMergedDataForUnhandledThrowOperations() -> TAnalysisData? +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetPointsToAbstractValue(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis.PointsToAbstractValue! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetValueContentAbstractValue(Microsoft.CodeAnalysis.IOperation! operation) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis.ValueContentAbstractValue! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.GetReturnValueAndPredicateKind() -> (TAbstractAnalysisValue Value, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind PredicateValueKind)? +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HandleEnterLockOperation(Microsoft.CodeAnalysis.IOperation! lockedObject) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HandlePossibleThrowingOperation(Microsoft.CodeAnalysis.IOperation! operation) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.HasPredicatedDataForEntity(TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> bool +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.IsReachableBlockData(TAnalysisData! analysisData) -> bool +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MergeAnalysisData(TAnalysisData! value1, TAnalysisData! value2, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! forBlock) -> TAnalysisData! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.MergeAnalysisDataForBackEdge(TAnalysisData! value1, TAnalysisData! value2, Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! forBlock) -> TAnalysisData! +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.PostProcessArgument(Microsoft.CodeAnalysis.Operations.IArgumentOperation! operation, bool isEscaped) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ProcessOutOfScopeLocalsAndFlowCaptures(System.Collections.Generic.IEnumerable! locals, System.Collections.Generic.IEnumerable! flowCaptures) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ProcessReturnValue(Microsoft.CodeAnalysis.IOperation? returnValue) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.ProcessThrowValue(Microsoft.CodeAnalysis.IOperation? thrownValue) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetPredicateValueKind(Microsoft.CodeAnalysis.IOperation! operation, TAnalysisData! analysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind predicateValueKind) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetValueForComparisonOperator(Microsoft.CodeAnalysis.Operations.IBinaryOperation! operation, TAnalysisData! targetAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetValueForEqualsOrNotEqualsComparisonOperator(Microsoft.CodeAnalysis.IOperation! leftOperand, Microsoft.CodeAnalysis.IOperation! rightOperand, bool equals, bool isReferenceEquality, TAnalysisData! targetAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.SetValueForIsNullComparisonOperator(Microsoft.CodeAnalysis.IOperation! leftOperand, bool equals, TAnalysisData! targetAnalysisData) -> Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicateValueKind +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StartTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity, TAnalysisData? truePredicateData, TAnalysisData? falsePredicateData) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StopTrackingDataForParameters(System.Collections.Immutable.ImmutableDictionary! parameterEntities) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.StopTrackingPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! predicatedEntity) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.TransferPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! fromEntity, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.AnalysisEntity! toEntity) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.UpdateReachability(Microsoft.CodeAnalysis.FlowAnalysis.BasicBlock! basicBlock, TAnalysisData! analysisData, bool isReachable) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitAssignmentOperation(Microsoft.CodeAnalysis.Operations.IAssignmentOperation! operation, object? argument) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitBinaryOperatorCore(Microsoft.CodeAnalysis.Operations.IBinaryOperation! operation, object? argument) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInvocation_Lambda(Microsoft.CodeAnalysis.FlowAnalysis.IFlowAnonymousFunctionOperation! lambda, System.Collections.Immutable.ImmutableArray visitedArguments, Microsoft.CodeAnalysis.IOperation! originalOperation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInvocation_LocalFunction(Microsoft.CodeAnalysis.IMethodSymbol! localFunction, System.Collections.Immutable.ImmutableArray visitedArguments, Microsoft.CodeAnalysis.IOperation! originalOperation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(Microsoft.CodeAnalysis.IMethodSymbol! method, Microsoft.CodeAnalysis.IOperation? visitedInstance, System.Collections.Immutable.ImmutableArray visitedArguments, bool invokedAsDelegate, Microsoft.CodeAnalysis.IOperation! originalOperation, TAbstractAnalysisValue defaultValue) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DataFlowOperationVisitor.VisitUnaryOperatorCore(Microsoft.CodeAnalysis.Operations.IUnaryOperation! operation, object? argument) -> TAbstractAnalysisValue +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.ApplyPredicatedData(Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! coreAnalysisData, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! predicatedData) -> void +virtual Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PredicatedAnalysisData.RemoveEntryInPredicatedData(TKey key, Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DictionaryAnalysisData! predicatedData) -> void +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.CodeMetricsAnalysisContext(Microsoft.CodeAnalysis.Compilation! compilation, System.Threading.CancellationToken cancellationToken, System.Func? isExcludedFromInheritanceCountFunc = null) -> void +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.Compilation.get -> Microsoft.CodeAnalysis.Compilation! +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken +Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext.IsExcludedFromInheritanceCountFunc.get -> System.Func! +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeAsync(Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> System.Threading.Tasks.Task! +static Microsoft.CodeAnalysis.CodeMetrics.CodeAnalysisMetricData.ComputeAsync(Microsoft.CodeAnalysis.ISymbol! symbol, Microsoft.CodeAnalysis.CodeMetrics.CodeMetricsAnalysisContext! context) -> System.Threading.Tasks.Task! diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..7e724be2cf302 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Shipped.md @@ -0,0 +1,15 @@ +## Release 2.9.8 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpImmutableObjectMethodAnalyzer + +## Release 3.3.4 + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpImmutableObjectMethodAnalyzer diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..adf1e316cc2c7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS1039 | MicrosoftCodeAnalysisCorrectness | Warning | SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer +RS1040 | MicrosoftCodeAnalysisCorrectness | Warning | CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpUpgradeMSBuildWorkspaceAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpUpgradeMSBuildWorkspaceAnalyzer.cs new file mode 100644 index 0000000000000..bed14a9ceef31 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/CSharpUpgradeMSBuildWorkspaceAnalyzer.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpUpgradeMSBuildWorkspaceAnalyzer : UpgradeMSBuildWorkspaceAnalyzer + { + protected override void RegisterIdentifierAnalysis(CompilationStartAnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeIdentifier, SyntaxKind.IdentifierName); + } + + private void AnalyzeIdentifier(SyntaxNodeAnalysisContext context) + { + if (context.Node is IdentifierNameSyntax identifierName && + identifierName.Identifier.ToString() == MSBuildWorkspace) + { + var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierName, context.CancellationToken); + if (symbolInfo.Symbol == null) + { + context.ReportDiagnostic(identifierName.CreateDiagnostic(UpgradeMSBuildWorkspaceDiagnosticRule)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerAPIUsageAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerAPIUsageAnalyzer.cs new file mode 100644 index 0000000000000..2b200c63daf1e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerAPIUsageAnalyzer.cs @@ -0,0 +1,29 @@ +// 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.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpDiagnosticAnalyzerApiUsageAnalyzer : DiagnosticAnalyzerApiUsageAnalyzer + { + protected override bool IsNamedTypeDeclarationBlock(SyntaxNode syntax) + { + return syntax.Kind() switch + { + SyntaxKind.ClassDeclaration + or SyntaxKind.StructDeclaration + or SyntaxKind.EnumDeclaration +#if CODEANALYSIS_V3_OR_BETTER + or SyntaxKind.RecordDeclaration: +#endif + or SyntaxKind.InterfaceDeclaration => true, + _ => false, + }; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerFieldsAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerFieldsAnalyzer.cs new file mode 100644 index 0000000000000..0ff7975c34028 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpDiagnosticAnalyzerFieldsAnalyzer.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpDiagnosticAnalyzerFieldsAnalyzer : DiagnosticAnalyzerFieldsAnalyzer + { + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpRegisterActionAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpRegisterActionAnalyzer.cs new file mode 100644 index 0000000000000..74154d00d0353 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpRegisterActionAnalyzer.cs @@ -0,0 +1,86 @@ +// 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.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpRegisterActionAnalyzer : RegisterActionAnalyzer + { + internal const string CSharpSyntaxKindName = @"Microsoft.CodeAnalysis.CSharp.SyntaxKind"; + internal const string BasicSyntaxKindName = @"Microsoft.CodeAnalysis.VisualBasic.SyntaxKind"; + + protected override RegisterActionCodeBlockAnalyzer GetCodeBlockAnalyzer( + Compilation compilation, + INamedTypeSymbol analysisContext, + INamedTypeSymbol compilationStartAnalysisContext, + INamedTypeSymbol codeBlockStartAnalysisContext, + INamedTypeSymbol operationBlockStartAnalysisContext, + INamedTypeSymbol symbolKind) + { + INamedTypeSymbol? csharpSyntaxKind = compilation.GetOrCreateTypeByMetadataName(CSharpSyntaxKindName); + INamedTypeSymbol? basicSyntaxKind = compilation.GetOrCreateTypeByMetadataName(BasicSyntaxKindName); + return new CSharpRegisterActionCodeBlockAnalyzer(csharpSyntaxKind, basicSyntaxKind, analysisContext, compilationStartAnalysisContext, + codeBlockStartAnalysisContext, operationBlockStartAnalysisContext, symbolKind); + } + + private sealed class CSharpRegisterActionCodeBlockAnalyzer : RegisterActionCodeBlockAnalyzer + { + private readonly ITypeSymbol? _csharpSyntaxKind, _basicSyntaxKind; + + public CSharpRegisterActionCodeBlockAnalyzer( + INamedTypeSymbol? csharpSyntaxKind, + INamedTypeSymbol? basicSyntaxKind, + INamedTypeSymbol analysisContext, + INamedTypeSymbol compilationStartAnalysisContext, + INamedTypeSymbol codeBlockStartAnalysisContext, + INamedTypeSymbol operationBlockStartAnalysisContext, + INamedTypeSymbol symbolKind) + : base(analysisContext, compilationStartAnalysisContext, codeBlockStartAnalysisContext, operationBlockStartAnalysisContext, symbolKind) + { + _csharpSyntaxKind = csharpSyntaxKind; + _basicSyntaxKind = basicSyntaxKind; + } + + protected override SyntaxKind InvocationExpressionKind => SyntaxKind.InvocationExpression; + protected override SyntaxKind ArgumentSyntaxKind => SyntaxKind.Argument; + protected override SyntaxKind ParameterSyntaxKind => SyntaxKind.Parameter; + + protected override IEnumerable? GetArgumentExpressions(InvocationExpressionSyntax invocation) + { + if (invocation.ArgumentList != null) + { + return invocation.ArgumentList.Arguments.Select(a => a.Expression); + } + + return null; + } + + protected override SyntaxNode GetArgumentExpression(ArgumentSyntax argument) + { + return argument.Expression; + } + + protected override SyntaxNode GetInvocationExpression(InvocationExpressionSyntax invocation) + { + return invocation.Expression; + } + + protected override SyntaxNode? GetInvocationReceiver(InvocationExpressionSyntax invocation) + { + return (invocation.Expression as MemberAccessExpressionSyntax)?.Expression; + } + + protected override bool IsSyntaxKind(ITypeSymbol type) + => SymbolEqualityComparer.Default.Equals(type, _csharpSyntaxKind) + || SymbolEqualityComparer.Default.Equals(type, _basicSyntaxKind); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpReportDiagnosticAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpReportDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..a41f21cddbe00 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpReportDiagnosticAnalyzer.cs @@ -0,0 +1,57 @@ +// 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 System.Linq; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpReportDiagnosticAnalyzer : ReportDiagnosticAnalyzer + { + protected override ReportDiagnosticCompilationAnalyzer GetAnalyzer(ImmutableHashSet contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + return new CSharpReportDiagnosticCompilationAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute); + } + + private sealed class CSharpReportDiagnosticCompilationAnalyzer : ReportDiagnosticCompilationAnalyzer + { + public CSharpReportDiagnosticCompilationAnalyzer(ImmutableHashSet contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + : base(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute) + { + } + + protected override IEnumerable? GetArgumentExpressions(InvocationExpressionSyntax invocation) + { + if (invocation.ArgumentList != null) + { + return invocation.ArgumentList.Arguments.Select(a => a.Expression); + } + + return null; + } + + protected override SyntaxNode GetPropertyGetterBlockSyntax(SyntaxNode declaringSyntaxRefNode) + { + if (declaringSyntaxRefNode is AccessorDeclarationSyntax accessor && + accessor.Body == null && + accessor.ExpressionBody == null) + { + // Walk up to the property initializer. + var propertyDecl = accessor.FirstAncestorOrSelf(); + if (propertyDecl?.Initializer != null) + { + return propertyDecl.Initializer; + } + } + + return base.GetPropertyGetterBlockSyntax(declaringSyntaxRefNode); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer.cs new file mode 100644 index 0000000000000..8a5d23a6b25e2 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer.cs @@ -0,0 +1,101 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor DiagnosticDescriptor = new( + DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNull, + CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)), + CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor FieldDiagnosticDescriptor = new( + DiagnosticIds.SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField, + CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullTitle)), + CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(SemanticModelGetDeclaredSymbolAlwaysReturnsNullForFieldDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DiagnosticDescriptor, FieldDiagnosticDescriptor); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(static context => + { + var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); + IMethodSymbol? getDeclaredSymbolMethod; + if (!typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpExtensions, out var csharpExtensions) + || !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisModelExtensions, out var modelExtensions) + || !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxBaseFieldDeclarationSyntax, out var baseFieldDeclaration) + || !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpSyntaxLocalFunctionStatementSyntax, out var localFunctionStatement) + || !typeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisSyntaxNode, out var syntaxNode) + || (getDeclaredSymbolMethod = (IMethodSymbol?)modelExtensions.GetMembers(nameof(ModelExtensions.GetDeclaredSymbol)).FirstOrDefault(m => m is IMethodSymbol { Parameters.Length: >= 2 })) is null) + { + return; + } + + var allowedTypes = csharpExtensions.GetMembers(nameof(CSharpExtensions.GetDeclaredSymbol)) + .OfType() + .Where(m => m.Parameters.Length >= 2) + .Select(m => m.Parameters[1].Type); + + context.RegisterOperationAction(ctx => AnalyzeInvocation(ctx, getDeclaredSymbolMethod, allowedTypes, baseFieldDeclaration, localFunctionStatement, syntaxNode), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocation( + OperationAnalysisContext context, + IMethodSymbol getDeclaredSymbolMethod, + IEnumerable allowedTypes, + INamedTypeSymbol baseFieldDeclarationType, + INamedTypeSymbol localFunctionStatementType, + INamedTypeSymbol syntaxNodeType) + { + var invocation = (IInvocationOperation)context.Operation; + if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, getDeclaredSymbolMethod)) + { + var syntaxNodeDerivingType = invocation.Arguments.GetArgumentForParameterAtIndex(1).Value.WalkDownConversion().Type; + if (syntaxNodeDerivingType is null || syntaxNodeDerivingType.Equals(syntaxNodeType)) + { + return; + } + + if (syntaxNodeDerivingType.DerivesFrom(baseFieldDeclarationType)) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(FieldDiagnosticDescriptor, syntaxNodeDerivingType.Name)); + } + else if (allowedTypes.All(type => !syntaxNodeDerivingType.DerivesFrom(type, baseTypesOnly: true, checkTypeParameterConstraints: false) + && !syntaxNodeDerivingType.Equals(localFunctionStatementType, SymbolEqualityComparer.Default))) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(DiagnosticDescriptor, syntaxNodeDerivingType.Name)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.cs new file mode 100644 index 0000000000000..e49f2edefac9a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/CSharpSymbolIsBannedInAnalyzersAnalyzer.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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Analyzers; +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpSymbolIsBannedInAnalyzersAnalyzer : SymbolIsBannedInAnalyzersAnalyzer + { + protected override SyntaxKind XmlCrefSyntaxKind => SyntaxKind.XmlCrefAttribute; + + protected override ImmutableArray BaseTypeSyntaxKinds => ImmutableArray.Create(SyntaxKind.BaseList); + + protected override SymbolDisplayFormat SymbolDisplayFormat => SymbolDisplayFormat.CSharpShortErrorMessageFormat; + + protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref; + + protected override IEnumerable GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpApplyDiagnosticAnalyzerAttributeFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpApplyDiagnosticAnalyzerAttributeFix.cs new file mode 100644 index 0000000000000..ec8c1b791ad58 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpApplyDiagnosticAnalyzerAttributeFix.cs @@ -0,0 +1,23 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Simplification; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpApplyDiagnosticAnalyzerAttributeFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpApplyDiagnosticAnalyzerAttributeFix() : ApplyDiagnosticAnalyzerAttributeFix + { + protected override SyntaxNode ParseExpression(string expression) + { + return SyntaxFactory.ParseExpression(expression).WithAdditionalAnnotations(Simplifier.Annotation); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpCompareSymbolsCorrectlyFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpCompareSymbolsCorrectlyFix.cs new file mode 100644 index 0000000000000..f35a57fffd5a7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpCompareSymbolsCorrectlyFix.cs @@ -0,0 +1,25 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CompareSymbolsCorrectlyFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpCompareSymbolsCorrectlyFix() : CompareSymbolsCorrectlyFix + { + protected override SyntaxNode CreateConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull) + => SyntaxFactory.ConditionalAccessExpression((ExpressionSyntax)expression, (ExpressionSyntax)whenNotNull); + + protected override SyntaxNode GetExpression(IInvocationOperation invocationOperation) + => ((InvocationExpressionSyntax)invocationOperation.Syntax).Expression; + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpConfigureGeneratedCodeAnalysisFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpConfigureGeneratedCodeAnalysisFix.cs new file mode 100644 index 0000000000000..55a2db0252683 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpConfigureGeneratedCodeAnalysisFix.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpConfigureGeneratedCodeAnalysisFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpConfigureGeneratedCodeAnalysisFix() : ConfigureGeneratedCodeAnalysisFix + { + protected override IEnumerable GetStatements(SyntaxNode methodDeclaration) + { + if (methodDeclaration is MethodDeclarationSyntax method) + { + if (method.ExpressionBody != null) + { + return new[] { method.ExpressionBody.Expression }; + } + else if (method.Body != null) + { + return method.Body.Statements; + } + } + + return Enumerable.Empty(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpEnableConcurrentExecutionFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpEnableConcurrentExecutionFix.cs new file mode 100644 index 0000000000000..0cf0c5a535566 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpEnableConcurrentExecutionFix.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Composition; +using System.Linq; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpEnableConcurrentExecutionFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpEnableConcurrentExecutionFix() : EnableConcurrentExecutionFix + { + protected override IEnumerable GetStatements(SyntaxNode methodDeclaration) + { + if (methodDeclaration is MethodDeclarationSyntax method) + { + if (method.ExpressionBody != null) + { + return new[] { method.ExpressionBody.Expression }; + } + else if (method.Body != null) + { + return method.Body.Statements; + } + } + + return Enumerable.Empty(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpPreferIsKindFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpPreferIsKindFix.cs new file mode 100644 index 0000000000000..491dca283cee1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/MetaAnalyzers/Fixers/CSharpPreferIsKindFix.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; +using System.Composition; +using System.Diagnostics; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpPreferIsKindFix))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpPreferIsKindFix() : PreferIsKindFix + { + protected override SyntaxNode? TryGetNodeToFix(SyntaxNode root, TextSpan span) + { + var binaryExpression = root.FindNode(span, getInnermostNodeForTie: true).FirstAncestorOrSelf(); + if (binaryExpression is null) + return null; + + if (binaryExpression.Left.IsKind(SyntaxKind.InvocationExpression) || + binaryExpression.Left.IsKind(SyntaxKind.ConditionalAccessExpression)) + { + return binaryExpression; + } + + return null; + } + + protected override void FixDiagnostic(DocumentEditor editor, SyntaxNode nodeToFix) + { + editor.ReplaceNode( + nodeToFix, + (nodeToFix, generator) => + { + var binaryExpression = (BinaryExpressionSyntax)nodeToFix; + InvocationExpressionSyntax? newInvocation = null; + if (binaryExpression.Left.IsKind(SyntaxKind.ConditionalAccessExpression)) + { + var conditionalAccess = (ConditionalAccessExpressionSyntax)binaryExpression.Left; + newInvocation = SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + conditionalAccess.Expression, + SyntaxFactory.IdentifierName(SyntaxFactory.Identifier("IsKind")))); + } + else if (binaryExpression.Left.IsKind(SyntaxKind.InvocationExpression)) + { + var invocation = (InvocationExpressionSyntax)binaryExpression.Left; + newInvocation = invocation.WithExpression(ConvertKindNameToIsKind(invocation.Expression)); + } + else + { + Debug.Fail("Unreachable."); + return nodeToFix; + } + + newInvocation = newInvocation + .AddArgumentListArguments(SyntaxFactory.Argument(binaryExpression.Right.WithoutTrailingTrivia())) + .WithTrailingTrivia(binaryExpression.Right.GetTrailingTrivia()); + var negate = binaryExpression.OperatorToken.IsKind(SyntaxKind.ExclamationEqualsToken); + if (negate) + { + return SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, newInvocation.WithoutLeadingTrivia()).WithLeadingTrivia(newInvocation.GetLeadingTrivia()); + } + else + { + return newInvocation; + } + }); + } + + private static ExpressionSyntax ConvertKindNameToIsKind(ExpressionSyntax expression) + { + if (expression is MemberAccessExpressionSyntax memberAccessExpression) + { + return memberAccessExpression.WithName(SyntaxFactory.IdentifierName(SyntaxFactory.Identifier("IsKind"))); + } + else + { + return expression; + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/Microsoft.CodeAnalysis.CSharp.Analyzers.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/Microsoft.CodeAnalysis.CSharp.Analyzers.csproj new file mode 100644 index 0000000000000..272274f053142 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/CSharp/Microsoft.CodeAnalysis.CSharp.Analyzers.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForCodeAnalysisAnalyzers) + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerBannedSymbols.txt b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerBannedSymbols.txt new file mode 100644 index 0000000000000..8d19dbe1691df --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerBannedSymbols.txt @@ -0,0 +1,16 @@ +T:System.Console; Analyzers should not be reading / writing to the console +T:System.Diagnostics.Process; Analyzers should not inspect or create processes +T:System.Diagnostics.ProcessStartInfo; Analyzers should not inspect or create processes +T:System.Environment; Analyzers should not read their settings directly from environment variables +P:System.Globalization.CultureInfo.CurrentCulture; Analyzers should use LocalizableResourceString for culture-dependent messages +P:System.Globalization.CultureInfo.CurrentUICulture; Analyzers should use LocalizableResourceString for culture-dependent messages +T:System.IO.File; Do not do file IO in analyzers +T:System.IO.Directory; Do not do file IO in analyzers +M:System.IO.Path.GetTempPath; Do not do file IO in analyzers +T:System.Random; Analyzers should be deterministic +M:System.Reflection.Assembly.Load(System.Byte[]); Analyzers should only load their dependencies via standard runtime mechanisms +M:System.Reflection.Assembly.Load(System.String); Analyzers should only load their dependencies via standard runtime mechanisms +M:System.Reflection.Assembly.Load(System.Reflection.AssemblyName); Analyzers should only load their dependencies via standard runtime mechanisms +M:System.Reflection.Assembly.Load(System.Byte[],System.Byte[]); Analyzers should only load their dependencies via standard runtime mechanisms +T:Microsoft.CodeAnalysis.GeneratorInitializationContext; Non-incremental source generators should not be used, implement IIncrementalGenerator instead +T:Microsoft.CodeAnalysis.GeneratorExecutionContext; Non-incremental source generators should not be used, implement IIncrementalGenerator instead diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..a67d646a6e128 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Shipped.md @@ -0,0 +1,80 @@ +## Release 2.9.8 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | ---------------------------------- | -------- | ------------------------------------ | +| RS1001 | MicrosoftCodeAnalysisCorrectness | Warning | DiagnosticAnalyzerAttributeAnalyzer | +| RS1002 | MicrosoftCodeAnalysisCorrectness | Warning | RegisterActionAnalyzer | +| RS1003 | MicrosoftCodeAnalysisCorrectness | Warning | RegisterActionAnalyzer | +| RS1004 | MicrosoftCodeAnalysisCorrectness | Warning | DiagnosticAnalyzerAttributeAnalyzer | +| RS1005 | MicrosoftCodeAnalysisCorrectness | Warning | ReportDiagnosticAnalyzer | +| RS1006 | MicrosoftCodeAnalysisCorrectness | Warning | RegisterActionAnalyzer | +| RS1007 | MicrosoftCodeAnalysisLocalization | Disabled | DiagnosticDescriptorCreationAnalyzer | +| RS1008 | MicrosoftCodeAnalysisPerformance | Warning | DiagnosticAnalyzerFieldsAnalyzer | +| RS1009 | MicrosoftCodeAnalysisCompatibility | Error | InternalImplementationOnlyAnalyzer | +| RS1010 | Correctness | Warning | FixerWithFixAllAnalyzer | +| RS1011 | Correctness | Warning | FixerWithFixAllAnalyzer | +| RS1012 | MicrosoftCodeAnalysisPerformance | Warning | RegisterActionAnalyzer | +| RS1013 | MicrosoftCodeAnalysisPerformance | Warning | RegisterActionAnalyzer | +| RS1015 | MicrosoftCodeAnalysisDocumentation | Disabled | DiagnosticDescriptorCreationAnalyzer | +| RS1016 | Correctness | Warning | FixerWithFixAllAnalyzer | +| RS1017 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1018 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1019 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1020 | MicrosoftCodeAnalysisDesign | Disabled | DiagnosticDescriptorCreationAnalyzer | +| RS1021 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1022 | MicrosoftCodeAnalysisCorrectness | Warning | DiagnosticAnalyzerApiUsageAnalyzer | +| RS1023 | Library | Warning | UpgradeMSBuildWorkspaceAnalyzer | + +## Release 3.0.0 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | ------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS1024 | MicrosoftCodeAnalysisCorrectness | Warning | CompareSymbolsCorrectlyAnalyzer | +| RS1025 | MicrosoftCodeAnalysisCorrectness | Warning | ConfigureGeneratedCodeAnalysisAnalyzer | +| RS1026 | MicrosoftCodeAnalysisCorrectness | Warning | EnableConcurrentExecutionAnalyzer | +| RS1027 | MicrosoftCodeAnalysisCorrectness | Warning | ClassIsNotDiagnosticAnalyzer | +| RS1028 | MicrosoftCodeAnalysisDocumentation | Disabled | DiagnosticDescriptorCreationAnalyzer | +| RS1029 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1030 | MicrosoftCodeAnalysisCorrectness | Warning | DoNotUseCompilationGetSemanticModelAnalyzer | +| RS2000 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2001 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2002 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2003 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2004 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2005 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2006 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2007 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | +| RS2008 | MicrosoftCodeAnalysisReleaseTracking | Warning | DiagnosticDescriptorCreationAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md) | + +## Release 3.3.0 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------------------------- | -------- | ------------------------------------ | +| RS1031 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1032 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | +| RS1033 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | + +## Release 3.3.3 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | -------------------------------- | -------- | -------------------- | +| RS1034 | MicrosoftCodeAnalysisPerformance | Warning | PreferIsKindAnalyzer | + +## Release 3.3.4 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | -------------------------------- | -------- | ------------------------------------ | +| RS1014 | MicrosoftCodeAnalysisCorrectness | Warning | ImmutableObjectMethodAnalyzer | +| RS1035 | MicrosoftCodeAnalysisCorrectness | Error | SymbolIsBannedInAnalyzersAnalyzer | +| RS1036 | MicrosoftCodeAnalysisCorrectness | Warning | SymbolIsBannedInAnalyzersAnalyzer | +| RS1037 | MicrosoftCodeAnalysisDesign | Warning | DiagnosticDescriptorCreationAnalyzer | diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..59f24de3c2b14 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -0,0 +1,10 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| RS1038 | MicrosoftCodeAnalysisCorrectness | Warning | CompilerExtensionStrictApiAnalyzer | +| RS1041 | MicrosoftCodeAnalysisCorrectness | Warning | CompilerExtensionTargetFrameworkAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1041.md) | +| RS1042 | MicrosoftCodeAnalysisCompatibility | Error | ImplementationIsObsoleteAnalyzer | +| RS1043 | MicrosoftCodeAnalysisCorrectness | Error | DoNotUseFileTypesForAnalyzersOrGenerators | diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.cs new file mode 100644 index 0000000000000..de19b7ca8b41c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + internal static partial class CodeAnalysisDiagnosticsResources + { + private static readonly Type s_resourcesType = typeof(CodeAnalysisDiagnosticsResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx new file mode 100644 index 0000000000000..0f06e938eddf5 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/CodeAnalysisDiagnosticsResources.resx @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Missing '{0}' attribute + + + Missing diagnostic analyzer attribute + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + + + Recommend adding language support to diagnostic analyzer + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + + + Apply DiagnosticAnalyzer attribute for '{0}'. + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + + + Specify at least one OperationKind of interest when registering an operation analyzer action + + + Missing kind argument when registering an analyzer action + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + + + SymbolKind '{0}' is not supported for symbol analyzer actions + + + Unsupported SymbolKind argument when registering a symbol analyzer action + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + + + Invalid type argument for DiagnosticAnalyzer's Register method + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + + + Start action has no registered non-end actions + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + + + Start action has no registered actions + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + + + Provide localizable arguments to diagnostic descriptor constructor + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + + + Only internal implementations of this interface are allowed + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + + + Code fix providers should provide FixAll support + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + + + Override GetFixAllProvider. + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + + + Do not ignore values returned by methods on immutable objects + + + DiagnosticId for analyzers must be a non-null constant + + + Diagnostic Id for rule '{0}' must be a non-null constant + + + DiagnosticId for analyzers must be a non-null constant. + + + DiagnosticId for analyzers must be in specified format + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + + + DiagnosticId for analyzers must be in specified format. + + + DiagnosticId must be unique across analyzers + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + + + DiagnosticId must be unique across analyzers. + + + Category for analyzers must be from the specified values + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + + + Category for analyzers must be from the specified values. + + + Invalid entry in analyzer category and diagnostic ID range specification file + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + + + Invalid entry in analyzer category and diagnostic ID range specification file. + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + + + Do not use types from Workspaces assembly in an analyzer + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + + + Upgrade MSBuildWorkspace + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + + + Use 'SymbolEqualityComparer' when comparing symbols + + + Symbols should be compared for equality + + + Use a 'SymbolEqualityComparer' for symbol comparison + + + Configure generated code analysis + + + Configure generated code analysis + + + Enable concurrent execution + + + Enable concurrent execution + + + Configure generated code analysis + + + Enable concurrent execution + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + + + DiagnosticId for analyzers should not use reserved IDs. + + + '{0}' is a reserved diagnostic ID + + + Do not use reserved diagnostic IDs + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + + + Add analyzer diagnostic IDs to analyzer release + + + Rule '{0}' is not part of any analyzer release + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + + + Add rule entry to unshipped release file + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + + + Update rule entry in unshipped release file + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + + + Remove duplicate entries for diagnostic ID in the same analyzer release + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + + + Remove duplicate entries for diagnostic ID between analyzer releases + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + + + Remove duplicate entries for diagnostic ID between analyzer releases. + + + Invalid entry in analyzer release file + + + Analyzer release file '{0}' has an invalid entry '{1}' + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + + + Invalid entry in analyzer release file. + + + Enable analyzer release tracking + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + + + Define diagnostic title correctly + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + + + Define diagnostic message correctly + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + + + Define diagnostic description correctly + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + + + Prefer 'IsKind' for checking syntax kinds + + + Prefer 'IsKind' for checking syntax kinds + + + Use 'IsKind' instead of 'Kind' + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + + + The symbol '{0}' is banned for use by analyzers{1} + + + Do not use APIs banned for analyzers + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + Specify analyzer banned API enforcement setting + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + + + Compiler extensions should be implemented in assemblies with compiler-provided references + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + + + The author of this interface has deprecated implementing this interface. + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + {2} is a URL + + + Implementations of this interface are not allowed + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + Type '{0}' should not be marked with 'file' + + + Do not use file types for implementing analyzers, generators, and code fixers + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs new file mode 100644 index 0000000000000..307615ca1351b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/DiagnosticIds.cs @@ -0,0 +1,64 @@ +// 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.Analyzers +{ + internal static class DiagnosticIds + { + public const string MissingDiagnosticAnalyzerAttributeRuleId = "RS1001"; + public const string MissingKindArgumentToRegisterActionRuleId = "RS1002"; + public const string UnsupportedSymbolKindArgumentRuleId = "RS1003"; + public const string AddLanguageSupportToAnalyzerRuleId = "RS1004"; + public const string InvalidReportDiagnosticRuleId = "RS1005"; + public const string InvalidSyntaxKindTypeArgumentRuleId = "RS1006"; + public const string UseLocalizableStringsInDescriptorRuleId = "RS1007"; + public const string DoNotStorePerCompilationDataOntoFieldsRuleId = "RS1008"; + public const string InternalImplementationOnlyRuleId = "RS1009"; + public const string CreateCodeActionWithEquivalenceKeyRuleId = "RS1010"; + public const string OverrideCodeActionEquivalenceKeyRuleId = "RS1011"; + public const string StartActionWithNoRegisteredActionsRuleId = "RS1012"; + public const string StartActionWithOnlyEndActionRuleId = "RS1013"; + public const string DoNotIgnoreReturnValueOnImmutableObjectMethodInvocation = "RS1014"; + public const string ProvideHelpUriInDescriptorRuleId = "RS1015"; + public const string OverrideGetFixAllProviderRuleId = "RS1016"; + public const string DiagnosticIdMustBeAConstantRuleId = "RS1017"; + public const string DiagnosticIdMustBeInSpecifiedFormatRuleId = "RS1018"; + public const string UseUniqueDiagnosticIdRuleId = "RS1019"; + public const string UseCategoriesFromSpecifiedRangeRuleId = "RS1020"; + public const string AnalyzerCategoryAndIdRangeFileInvalidRuleId = "RS1021"; + public const string DoNotUseTypesFromAssemblyRuleId = "RS1022"; + public const string UpgradeMSBuildWorkspaceRuleId = "RS1023"; + public const string CompareSymbolsCorrectlyRuleId = "RS1024"; + public const string ConfigureGeneratedCodeAnalysisRuleId = "RS1025"; + public const string EnableConcurrentExecutionRuleId = "RS1026"; + public const string TypeIsNotDiagnosticAnalyzerRuleId = "RS1027"; + public const string ProvideCustomTagsInDescriptorRuleId = "RS1028"; + public const string DoNotUseReservedDiagnosticIdRuleId = "RS1029"; + public const string DoNotUseCompilationGetSemanticModelRuleId = "RS1030"; + public const string DefineDiagnosticTitleCorrectlyRuleId = "RS1031"; + public const string DefineDiagnosticMessageCorrectlyRuleId = "RS1032"; + public const string DefineDiagnosticDescriptionCorrectlyRuleId = "RS1033"; + public const string PreferIsKindRuleId = "RS1034"; + public const string SymbolIsBannedInAnalyzersRuleId = "RS1035"; + public const string NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId = "RS1036"; + public const string AddCompilationEndCustomTagRuleId = "RS1037"; + public const string DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId = "RS1038"; + public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNull = "RS1039"; + public const string SemanticModelGetDeclaredSymbolAlwaysReturnsNullForField = "RS1040"; + public const string DoNotRegisterCompilerTypesWithBadTargetFrameworkRuleId = "RS1041"; + public const string ImplementationIsObsoleteRuleId = "RS1042"; + public const string DoNotUseFileTypesForAnalyzersOrGenerators = "RS1043"; + + // Release tracking analyzer IDs + public const string DeclareDiagnosticIdInAnalyzerReleaseRuleId = "RS2000"; + public const string UpdateDiagnosticIdInAnalyzerReleaseRuleId = "RS2001"; + public const string RemoveUnshippedDeletedDiagnosticIdRuleId = "RS2002"; + public const string RemoveShippedDeletedDiagnosticIdRuleId = "RS2003"; + public const string UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRuleId = "RS2004"; + public const string RemoveDuplicateEntriesForAnalyzerReleaseRuleId = "RS2005"; + public const string RemoveDuplicateEntriesBetweenAnalyzerReleasesRuleId = "RS2006"; + public const string InvalidEntryInAnalyzerReleasesFileRuleId = "RS2007"; + public const string EnableAnalyzerReleaseTrackingRuleId = "RS2008"; + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.Fixer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.Fixer.cs new file mode 100644 index 0000000000000..0cd3fcd00830b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.Fixer.cs @@ -0,0 +1,84 @@ +// 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.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers.FixAnalyzers; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + /// + /// RS1016: Code fix providers should provide FixAll support. + /// + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(FixerWithFixAllFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class FixerWithFixAllFix() : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIds.OverrideGetFixAllProviderRuleId); + + public sealed override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxToken token = root.FindToken(context.Span.Start); + if (!token.Span.IntersectsWith(context.Span)) + { + return; + } + + SyntaxGenerator generator = SyntaxGenerator.GetGenerator(context.Document); + SyntaxNode classDecl = generator.GetDeclaration(token.Parent); + if (classDecl == null) + { + return; + } + + // Register code fix. + var title = CodeAnalysisDiagnosticsResources.OverrideGetFixAllProviderCodeFixTitle; + context.RegisterCodeFix(CodeAction.Create(title, c => AddMethodAsync(context.Document, classDecl, c), equivalenceKey: title), context.Diagnostics); + } + + private static async Task AddMethodAsync(Document document, SyntaxNode classDecl, CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + var model = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var typeIsSealed = ((INamedTypeSymbol)model.GetDeclaredSymbol(classDecl, cancellationToken)!).IsSealed; + + INamedTypeSymbol? codeFixProviderSymbol = model.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCodeFixesCodeFixProvider); + IMethodSymbol? getFixAllProviderMethod = codeFixProviderSymbol?.GetMembers(FixerWithFixAllAnalyzer.GetFixAllProviderMethodName).OfType().FirstOrDefault(); + if (getFixAllProviderMethod == null) + { + return document; + } + + var returnStatement = generator.ReturnStatement(generator.MemberAccessExpression( + generator.IdentifierName("WellKnownFixAllProviders"), "BatchFixer")); + var statements = new SyntaxNode[] { returnStatement }; + + var methodDeclaration = generator.MethodDeclaration(getFixAllProviderMethod, statements); + var methodModifiers = typeIsSealed ? DeclarationModifiers.Override : DeclarationModifiers.Sealed + DeclarationModifiers.Override; + methodDeclaration = generator.WithModifiers(methodDeclaration, methodModifiers); + + editor.AddMember(classDecl, methodDeclaration); + + return editor.GetChangedDocument(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs new file mode 100644 index 0000000000000..68b9213acf94f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/FixAnalyzers/FixerWithFixAllAnalyzer.cs @@ -0,0 +1,334 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.FixAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1010: + /// RS1011: + /// RS1016: + /// A that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique across all registered code actions by this fixer. + /// This enables the to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + /// This analyzer catches violations of this requirement in the code actions registered by a that supports . + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class FixerWithFixAllAnalyzer : DiagnosticAnalyzer + { + private const string CreateMethodName = "Create"; + private const string EquivalenceKeyPropertyName = "EquivalenceKey"; + private const string EquivalenceKeyParameterName = "equivalenceKey"; + internal const string GetFixAllProviderMethodName = "GetFixAllProvider"; + + private static readonly LocalizableString s_localizableCodeActionNeedsEquivalenceKeyDescription = CreateLocalizableResourceString(nameof(CodeActionNeedsEquivalenceKeyDescription)); + + internal static readonly DiagnosticDescriptor CreateCodeActionEquivalenceKeyRule = new( + DiagnosticIds.CreateCodeActionWithEquivalenceKeyRuleId, + CreateLocalizableResourceString(nameof(CreateCodeActionWithEquivalenceKeyTitle)), + CreateLocalizableResourceString(nameof(CreateCodeActionWithEquivalenceKeyMessage)), + "Correctness", + DiagnosticSeverity.Warning, + description: s_localizableCodeActionNeedsEquivalenceKeyDescription, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor OverrideCodeActionEquivalenceKeyRule = new( + DiagnosticIds.OverrideCodeActionEquivalenceKeyRuleId, + CreateLocalizableResourceString(nameof(OverrideCodeActionEquivalenceKeyTitle)), + CreateLocalizableResourceString(nameof(OverrideCodeActionEquivalenceKeyMessage)), + "Correctness", + DiagnosticSeverity.Warning, + description: s_localizableCodeActionNeedsEquivalenceKeyDescription, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor OverrideGetFixAllProviderRule = new( + DiagnosticIds.OverrideGetFixAllProviderRuleId, + CreateLocalizableResourceString(nameof(OverrideGetFixAllProviderTitle)), + CreateLocalizableResourceString(nameof(OverrideGetFixAllProviderMessage)), + "Correctness", + DiagnosticSeverity.Warning, + description: CreateLocalizableResourceString(nameof(OverrideGetFixAllProviderDescription)), + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(CreateCodeActionEquivalenceKeyRule, OverrideCodeActionEquivalenceKeyRule, OverrideGetFixAllProviderRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // We need to analyze generated code, but don't intend to report diagnostics on generated code. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterCompilationStartAction(AnalyzeCompilation); + } + + private static void AnalyzeCompilation(CompilationStartAnalysisContext context) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + INamedTypeSymbol? codeFixProviderSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCodeFixesCodeFixProvider); + if (codeFixProviderSymbol == null) + { + return; + } + + IMethodSymbol? getFixAllProviderMethod = codeFixProviderSymbol.GetMembers(GetFixAllProviderMethodName).OfType().FirstOrDefault(); + if (getFixAllProviderMethod == null) + { + return; + } + + INamedTypeSymbol? codeActionSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCodeActionsCodeAction); + if (codeActionSymbol == null) + { + return; + } + + IPropertySymbol? equivalenceKeyProperty = codeActionSymbol.GetMembers(EquivalenceKeyPropertyName).OfType().FirstOrDefault(); + if (equivalenceKeyProperty == null) + { + return; + } + + INamedTypeSymbol? exportCodeFixProviderAttributeSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCodeFixesExportCodeFixProviderAttribute); + if (exportCodeFixProviderAttributeSymbol == null) + { + return; + } + + var createMethods = codeActionSymbol.GetMembers(CreateMethodName).OfType().ToImmutableHashSet(); + + var analysisTypes = new AnalysisTypes( + CodeFixProviderType: codeFixProviderSymbol, + CodeActionType: codeActionSymbol, + ExportCodeFixProviderAttributeType: exportCodeFixProviderAttributeSymbol, + GetFixAllProviderMethod: getFixAllProviderMethod, + EquivalenceKeyProperty: equivalenceKeyProperty, + CreateMethods: createMethods); + + context.RegisterSymbolStartAction(context => AnalyzeNamedType(context, analysisTypes), SymbolKind.NamedType); + } + + private static void AnalyzeNamedType(SymbolStartAnalysisContext context, AnalysisTypes analysisTypes) + { + var symbol = (INamedTypeSymbol)context.Symbol; + if (!symbol.DerivesFrom(analysisTypes.CodeFixProviderType)) + return; + + var namedTypeAnalyzer = new NamedTypeAnalyzer(analysisTypes); + + context.RegisterOperationBlockStartAction(namedTypeAnalyzer.OperationBlockStart); + context.RegisterSymbolEndAction(namedTypeAnalyzer.SymbolEnd); + } + + private record AnalysisTypes( + INamedTypeSymbol CodeFixProviderType, + INamedTypeSymbol CodeActionType, + INamedTypeSymbol ExportCodeFixProviderAttributeType, + IMethodSymbol GetFixAllProviderMethod, + IPropertySymbol EquivalenceKeyProperty, + ImmutableHashSet CreateMethods); + + private sealed class NamedTypeAnalyzer + { + private readonly AnalysisTypes _analysisTypes; + + /// + /// Map of invocations from code fix providers to invocations that create a code action using the static "Create" methods on . + /// + private readonly Dictionary> _codeActionCreateInvocations = new(); + + /// + /// Map of invocations from code fix providers to object creations that create a code action using sub-types of . + /// + private readonly Dictionary> _codeActionObjectCreations = new(); + + public NamedTypeAnalyzer(AnalysisTypes analysisTypes) + { + _analysisTypes = analysisTypes; + } + + internal void OperationBlockStart(OperationBlockStartAnalysisContext context) + { + if (context.OwningSymbol is not IMethodSymbol method) + { + return; + } + + INamedTypeSymbol namedType = method.ContainingType; + if (!namedType.DerivesFrom(_analysisTypes.CodeFixProviderType)) + { + return; + } + + context.RegisterOperationAction(context => + { + var invocation = (IInvocationOperation)context.Operation; + if (invocation.TargetMethod is IMethodSymbol invocationSym && _analysisTypes.CreateMethods.Contains(invocationSym)) + { + AddOperation(namedType, invocation, _codeActionCreateInvocations); + } + }, + OperationKind.Invocation); + + context.RegisterOperationAction(context => + { + var objectCreation = (IObjectCreationOperation)context.Operation; + IMethodSymbol? constructor = objectCreation.Constructor; + if (constructor != null && constructor.ContainingType.DerivesFrom(_analysisTypes.CodeActionType)) + { + AddOperation(namedType, objectCreation, _codeActionObjectCreations); + } + }, + OperationKind.ObjectCreation); + } + + private static void AddOperation(INamedTypeSymbol namedType, T operation, Dictionary> map) + where T : IOperation + { + lock (map) + { + if (!map.TryGetValue(namedType, out HashSet value)) + { + value = new HashSet(); + map[namedType] = value; + } + + value.Add(operation); + } + } + + internal void SymbolEnd(SymbolAnalysisContext context) + { + // Analyze the fixer if it has FixAll support. + // Otherwise, report RS1016 (OverrideGetFixAllProviderRule) to recommend adding FixAll support. + var fixer = (INamedTypeSymbol)context.Symbol; + if (OverridesGetFixAllProvider(fixer)) + { + if (_codeActionCreateInvocations.Count > 0 || _codeActionObjectCreations.Count > 0) + { + AnalyzeFixerWithFixAll(fixer, context); + } + } + else if (fixer.HasAnyAttribute(_analysisTypes.ExportCodeFixProviderAttributeType)) + { + Diagnostic diagnostic = fixer.CreateDiagnostic(OverrideGetFixAllProviderRule, fixer.Name); + context.ReportDiagnostic(diagnostic); + } + + return; + + // Local functions + bool OverridesGetFixAllProvider(INamedTypeSymbol fixer) + { + foreach (INamedTypeSymbol type in fixer.GetBaseTypesAndThis()) + { + if (!SymbolEqualityComparer.Default.Equals(type, _analysisTypes.CodeFixProviderType)) + { + IMethodSymbol getFixAllProviderMethod = type.GetMembers(GetFixAllProviderMethodName).OfType().FirstOrDefault(); + if (getFixAllProviderMethod != null && getFixAllProviderMethod.IsOverride) + { + return true; + } + } + } + + return false; + } + + static bool IsViolatingCodeActionCreateInvocation(IInvocationOperation invocation) + { + IParameterSymbol? param = invocation.TargetMethod.Parameters.FirstOrDefault(p => p.Name == EquivalenceKeyParameterName); + if (param == null) + { + // User is calling an overload without the equivalenceKey parameter + return false; + } + + foreach (var argument in invocation.Arguments) + { + if (SymbolEqualityComparer.Default.Equals(argument.Parameter, param)) + { + return argument.Value.ConstantValue.HasValue && argument.Value.ConstantValue.Value == null; + } + } + + return true; + } + + void AnalyzeFixerWithFixAll(INamedTypeSymbol fixer, SymbolAnalysisContext context) + { + if (_codeActionCreateInvocations != null + && _codeActionCreateInvocations.TryGetValue(fixer, out HashSet invocations)) + { + foreach (IInvocationOperation invocation in invocations) + { + if (IsViolatingCodeActionCreateInvocation(invocation)) + { + Diagnostic diagnostic = invocation.CreateDiagnostic(CreateCodeActionEquivalenceKeyRule, EquivalenceKeyParameterName); + context.ReportDiagnostic(diagnostic); + } + } + } + + if (_codeActionObjectCreations != null + && _codeActionObjectCreations.TryGetValue(fixer, out HashSet objectCreations)) + { + foreach (IObjectCreationOperation objectCreation in objectCreations) + { + if (IsViolatingCodeActionObjectCreation(objectCreation)) + { + Diagnostic diagnostic = objectCreation.CreateDiagnostic(OverrideCodeActionEquivalenceKeyRule, objectCreation.Constructor!.ContainingType, EquivalenceKeyPropertyName); + context.ReportDiagnostic(diagnostic); + } + } + } + } + + bool IsViolatingCodeActionObjectCreation(IObjectCreationOperation objectCreation) + { + return objectCreation.Constructor != null && objectCreation.Constructor.ContainingType.GetBaseTypesAndThis().All(namedType => !IsCodeActionWithOverriddenEquivalenceKey(namedType)); + + // Local functions + bool IsCodeActionWithOverriddenEquivalenceKey(INamedTypeSymbol namedType) + { + if (SymbolEqualityComparer.Default.Equals(namedType, _analysisTypes.CodeActionType)) + { + return false; + } + + // For types in different compilation, perform the check. + return IsCodeActionWithOverriddenEquivlanceKeyCore(namedType); + } + } + } + + private bool IsCodeActionWithOverriddenEquivlanceKeyCore(INamedTypeSymbol namedType) + { + if (!namedType.DerivesFrom(_analysisTypes.CodeActionType)) + { + // Not a CodeAction. + return false; + } + + IPropertySymbol equivalenceKeyProperty = namedType.GetMembers(EquivalenceKeyPropertyName).OfType().FirstOrDefault(); + return equivalenceKeyProperty != null && equivalenceKeyProperty.IsOverride; + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.cs new file mode 100644 index 0000000000000..8119c169b0f7b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImmutableObjectMethodAnalyzer.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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1014: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class ImmutableObjectMethodAnalyzer : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor DoNotIgnoreReturnValueDiagnosticRule = new( + DiagnosticIds.DoNotIgnoreReturnValueOnImmutableObjectMethodInvocation, + CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationTitle)), + CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotIgnoreReturnValueOnImmutableObjectMethodInvocationDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotIgnoreReturnValueDiagnosticRule); + + private const string SolutionFullName = @"Microsoft.CodeAnalysis.Solution"; + private const string ProjectFullName = @"Microsoft.CodeAnalysis.Project"; + private const string DocumentFullName = @"Microsoft.CodeAnalysis.Document"; + private const string SyntaxNodeFullName = @"Microsoft.CodeAnalysis.SyntaxNode"; + private const string CompilationFullName = @"Microsoft.CodeAnalysis.Compilation"; + + private static readonly ImmutableArray s_immutableMethodNames = ImmutableArray.Create( + "Add", + "Remove", + "Replace", + "With"); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var compilation = context.Compilation; + var builder = ImmutableArray.CreateBuilder(); + var provider = WellKnownTypeProvider.GetOrCreate(compilation); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(SolutionFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(ProjectFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(DocumentFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(SyntaxNodeFullName)); + AddIfNotNull(builder, provider.GetOrCreateTypeByMetadataName(CompilationFullName)); + var immutableTypeSymbols = builder.ToImmutable(); + if (immutableTypeSymbols.Length > 0) + { + context.RegisterOperationAction(context => AnalyzeInvocationForIgnoredReturnValue(context, immutableTypeSymbols), OperationKind.Invocation); + } + }); + + static void AddIfNotNull(ImmutableArray.Builder builder, INamedTypeSymbol? symbol) + { + if (symbol is not null) + { + builder.Add(symbol); + } + } + } + + public static void AnalyzeInvocationForIgnoredReturnValue(OperationAnalysisContext context, ImmutableArray immutableTypeSymbols) + { + var invocation = (IInvocationOperation)context.Operation; + // Returns void happens for the internal AddDebugSourceDocumentsForChecksumDirectives in the compiler itself. + if (invocation.Parent is not IExpressionStatementOperation || invocation.TargetMethod.ReturnsVoid) + { + return; + } + + // If the method doesn't start with something like "With" or "Replace", quit + string methodName = invocation.TargetMethod.Name; + if (!s_immutableMethodNames.Any(n => methodName.StartsWith(n, StringComparison.Ordinal))) + { + return; + } + + // If we're not in one of the known immutable types, quit + if (invocation.GetReceiverType(context.Compilation, beforeConversion: false, context.CancellationToken) is INamedTypeSymbol type + && type.GetBaseTypesAndThis().Any(immutableTypeSymbols.Contains)) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(DoNotIgnoreReturnValueDiagnosticRule, type.Name, methodName)); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImplementationIsObsoleteAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImplementationIsObsoleteAnalyzer.cs new file mode 100644 index 0000000000000..1e702d54b634a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/ImplementationIsObsoleteAnalyzer.cs @@ -0,0 +1,64 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class ImplementationIsObsoleteAnalyzer : DiagnosticAnalyzer + { + private const string ImplementationIsObsoleteAttributeName = "ImplementationIsObsoleteAttribute"; + private const string ImplementationIsObsoleteAttributeFullName = "System.Runtime.CompilerServices.ImplementationIsObsoleteAttribute"; + + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.ImplementationIsObsoleteRuleId, + CreateLocalizableResourceString(nameof(ImplementationIsObsoleteTitle)), + CreateLocalizableResourceString(nameof(ImplementationIsObsoleteMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCompatibility, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(ImplementationIsObsoleteDescription))); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; + + // If any interface implemented by this type has the attribute and if the interface and this type are not + // in "internals visible" context, then issue an error. + foreach (INamedTypeSymbol iface in namedTypeSymbol.AllInterfaces) + { + System.Collections.Generic.IEnumerable attributes = iface.GetAttributes(); + + // We are doing a string comparison of the name here because we don't care where the attribute comes from. + // CodeAnalysis.dll itself has this attribute and if the user assembly also had it, symbol equality will fail + // but we should still issue the error. + if (attributes.FirstOrDefault(a => a is { AttributeClass.Name: ImplementationIsObsoleteAttributeName, ConstructorArguments: [{ Value: string }] } + && a.AttributeClass.ToDisplayString().Equals(ImplementationIsObsoleteAttributeFullName, StringComparison.Ordinal)) is { } attr && + !iface.ContainingAssembly.GivesAccessTo(namedTypeSymbol.ContainingAssembly)) + { + context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(Rule, namedTypeSymbol.Name, iface.Name, (string)attr.ConstructorArguments[0].Value!)); + break; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/InternalImplementationOnlyAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/InternalImplementationOnlyAnalyzer.cs new file mode 100644 index 0000000000000..17f406a12724e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/InternalImplementationOnlyAnalyzer.cs @@ -0,0 +1,67 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1009: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class InternalImplementationOnlyAnalyzer : DiagnosticAnalyzer + { + private const string InternalImplementationOnlyAttributeName = "InternalImplementationOnlyAttribute"; + private const string InternalImplementationOnlyAttributeFullName = "System.Runtime.CompilerServices.InternalImplementationOnlyAttribute"; + + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.InternalImplementationOnlyRuleId, + CreateLocalizableResourceString(nameof(InternalImplementationOnlyTitle)), + CreateLocalizableResourceString(nameof(InternalImplementationOnlyMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCompatibility, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InternalImplementationOnlyDescription))); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; + + // If any interface implemented by this type has the attribute and if the interface and this type are not + // in "internals visible" context, then issue an error. + foreach (INamedTypeSymbol iface in namedTypeSymbol.AllInterfaces) + { + System.Collections.Generic.IEnumerable attributes = iface.GetAttributes(); + + // We are doing a string comparison of the name here because we don't care where the attribute comes from. + // CodeAnalysis.dll itself has this attribute and if the user assembly also had it, symbol equality will fail + // but we should still issue the error. + if (attributes.Any(a => a.AttributeClass is { Name: InternalImplementationOnlyAttributeName } + && a.AttributeClass.ToDisplayString().Equals(InternalImplementationOnlyAttributeFullName, StringComparison.Ordinal)) && + !iface.ContainingAssembly.GivesAccessTo(namedTypeSymbol.ContainingAssembly)) + { + context.ReportDiagnostic(namedTypeSymbol.CreateDiagnostic(Rule, namedTypeSymbol.Name, iface.Name)); + break; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ClassIsNotDiagnosticAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ClassIsNotDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..146692891d6ef --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ClassIsNotDiagnosticAnalyzer.cs @@ -0,0 +1,61 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1027: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class ClassIsNotDiagnosticAnalyzer : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.TypeIsNotDiagnosticAnalyzerRuleId, + CreateLocalizableResourceString(nameof(ClassIsNotDiagnosticAnalyzerTitle)), + CreateLocalizableResourceString(nameof(ClassIsNotDiagnosticAnalyzerMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(csac => + { + var diagnosticAnalyzer = csac.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + var diagnosticAnalyzerAttribute = csac.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); + + if (diagnosticAnalyzer == null || diagnosticAnalyzerAttribute == null) + { + // We don't need to check assemblies unless they're referencing Microsoft.CodeAnalysis which defines DiagnosticAnalyzer. + return; + } + + csac.RegisterSymbolAction(sac => + { + var namedType = (INamedTypeSymbol)sac.Symbol; + + if (namedType.TypeKind == TypeKind.Class && + namedType.HasAnyAttribute(diagnosticAnalyzerAttribute) && + !namedType.DerivesFrom(diagnosticAnalyzer, baseTypesOnly: true)) + { + sac.ReportDiagnostic(namedType.Locations[0].CreateDiagnostic(Rule, namedType.Name)); + } + }, SymbolKind.NamedType); + }); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs new file mode 100644 index 0000000000000..8dda3ff6afaa0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompareSymbolsCorrectlyAnalyzer.cs @@ -0,0 +1,378 @@ +// 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 System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1024: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class CompareSymbolsCorrectlyAnalyzer : DiagnosticAnalyzer + { + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(CompareSymbolsCorrectlyTitle)); + private static readonly LocalizableString s_localizableMessage = CreateLocalizableResourceString(nameof(CompareSymbolsCorrectlyMessage)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(CompareSymbolsCorrectlyDescription)); + + private static readonly string s_symbolTypeFullName = typeof(ISymbol).FullName; + private const string s_symbolEqualsName = nameof(ISymbol.Equals); + private const string s_HashCodeCombineName = "Combine"; + + public const string SymbolEqualityComparerName = "Microsoft.CodeAnalysis.SymbolEqualityComparer"; + public const string RulePropertyName = "Rule"; + + public const string EqualityRuleName = "EqualityRule"; + public const string GetHashCodeRuleName = "GetHashCodeRule"; + public const string CollectionRuleName = "CollectionRule"; + + private static readonly DiagnosticDescriptor s_equalityRule = new( + DiagnosticIds.CompareSymbolsCorrectlyRuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static readonly DiagnosticDescriptor s_getHashCodeRule = new( + DiagnosticIds.CompareSymbolsCorrectlyRuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(CompareSymbolsCorrectlyDescriptionGetHashCode)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static readonly DiagnosticDescriptor s_collectionRule = new( + DiagnosticIds.CompareSymbolsCorrectlyRuleId, + s_localizableTitle, + s_localizableMessage, + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static readonly ImmutableDictionary s_EqualityRuleProperties = + ImmutableDictionary.CreateRange([new KeyValuePair(RulePropertyName, EqualityRuleName)]); + + private static readonly ImmutableDictionary s_GetHashCodeRuleProperties = + ImmutableDictionary.CreateRange([new KeyValuePair(RulePropertyName, GetHashCodeRuleName)]); + + private static readonly ImmutableDictionary s_CollectionRuleProperties = + ImmutableDictionary.CreateRange([new KeyValuePair(RulePropertyName, CollectionRuleName)]); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(s_equalityRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + var compilation = context.Compilation; + var symbolType = compilation.GetOrCreateTypeByMetadataName(s_symbolTypeFullName); + if (symbolType is null) + { + return; + } + + // Check that the EqualityComparer exists and can be used, otherwise the Roslyn version + // being used it too low to need the change for method references + var hasSymbolEqualityComparer = UseSymbolEqualityComparer(compilation); + + context.RegisterOperationAction( + context => HandleBinaryOperator(in context, symbolType), + OperationKind.BinaryOperator); + + var equalityComparerMethods = GetEqualityComparerMethodsToCheck(compilation); + var systemHashCode = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemHashCode); + var iEqualityComparer = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIEqualityComparer1); + + context.RegisterOperationAction( + context => HandleInvocationOperation(in context, symbolType, hasSymbolEqualityComparer, equalityComparerMethods, systemHashCode, iEqualityComparer), + OperationKind.Invocation); + + if (hasSymbolEqualityComparer && iEqualityComparer is not null) + { + var collectionTypesBuilder = ImmutableHashSet.CreateBuilder(SymbolEqualityComparer.Default); + collectionTypesBuilder.AddIfNotNull(compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericDictionary2)); + collectionTypesBuilder.AddIfNotNull(compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericHashSet1)); + collectionTypesBuilder.AddIfNotNull(compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsConcurrentConcurrentDictionary2)); + + context.RegisterOperationAction( + context => HandleObjectCreation(in context, symbolType, iEqualityComparer, collectionTypesBuilder.ToImmutable()), + OperationKind.ObjectCreation); + } + }); + } + + private static void HandleBinaryOperator(in OperationAnalysisContext context, INamedTypeSymbol symbolType) + { + var binary = (IBinaryOperation)context.Operation; + if (binary.OperatorKind is not BinaryOperatorKind.Equals and not BinaryOperatorKind.NotEquals) + { + return; + } + + // Allow user-defined operators + if (binary.OperatorMethod?.ContainingSymbol is INamedTypeSymbol containingType + && containingType.SpecialType != SpecialType.System_Object) + { + return; + } + + // If either operand is 'null' or 'default', do not analyze + if (binary.LeftOperand.HasNullConstantValue() || binary.RightOperand.HasNullConstantValue()) + { + return; + } + + if (!IsSymbolType(binary.LeftOperand, symbolType) + && !IsSymbolType(binary.RightOperand, symbolType)) + { + return; + } + + if (binary.Language == LanguageNames.VisualBasic + && (IsSymbolClassType(binary.LeftOperand) || IsSymbolClassType(binary.RightOperand))) + { + return; + } + + if (IsExplicitCastToObject(binary.LeftOperand) || IsExplicitCastToObject(binary.RightOperand)) + { + return; + } + + context.ReportDiagnostic(binary.Syntax.GetLocation().CreateDiagnostic(s_equalityRule, s_EqualityRuleProperties)); + } + + private static void HandleInvocationOperation( + in OperationAnalysisContext context, + INamedTypeSymbol symbolType, + bool hasSymbolEqualityComparer, + ImmutableDictionary> equalityComparerMethods, + INamedTypeSymbol? systemHashCodeType, + INamedTypeSymbol? iEqualityComparer) + { + var invocationOperation = (IInvocationOperation)context.Operation; + var method = invocationOperation.TargetMethod; + + switch (method.Name) + { + case WellKnownMemberNames.ObjectGetHashCode: + // This is a call for an instance of ISymbol.GetHashCode() + // without the correct arguments + if (IsSymbolType(invocationOperation.Instance, symbolType)) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(s_getHashCodeRule, s_GetHashCodeRuleProperties)); + } + + break; + + case s_symbolEqualsName: + if (hasSymbolEqualityComparer && IsNotInstanceInvocationOrNotOnSymbol(invocationOperation, symbolType)) + { + var parameters = invocationOperation.Arguments; + if (parameters.All(p => IsSymbolType(p.Value, symbolType))) + { + context.ReportDiagnostic(invocationOperation.Syntax.GetLocation().CreateDiagnostic(s_equalityRule, s_EqualityRuleProperties)); + } + } + + break; + + case s_HashCodeCombineName: + // A call System.HashCode.Combine(ISymbol) will do the wrong thing and should be avoided + if (systemHashCodeType is not null && + invocationOperation.Instance is null && + systemHashCodeType.Equals(method.ContainingType, SymbolEqualityComparer.Default) && + invocationOperation.Arguments.Any(arg => IsSymbolType(arg.Value, symbolType))) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(s_getHashCodeRule, s_GetHashCodeRuleProperties)); + } + + break; + + default: + if (equalityComparerMethods.TryGetValue(method.Name, out var possibleMethodTypes) && + hasSymbolEqualityComparer && + possibleMethodTypes.Contains(method.ContainingType.OriginalDefinition) && + IsBehavingOnSymbolType(method, symbolType) && + !invocationOperation.Arguments.Any(arg => IsSymbolType(arg.Value, iEqualityComparer))) + { + context.ReportDiagnostic(invocationOperation.CreateDiagnostic(s_collectionRule, s_CollectionRuleProperties)); + } + + break; + } + + static bool IsNotInstanceInvocationOrNotOnSymbol(IInvocationOperation invocationOperation, INamedTypeSymbol symbolType) + => invocationOperation.Instance is null || IsSymbolType(invocationOperation.Instance, symbolType); + + static bool IsBehavingOnSymbolType(IMethodSymbol? method, INamedTypeSymbol symbolType) + { + if (method is null) + { + return false; + } + else if (!method.TypeArguments.IsEmpty) + { + var destinationTypeIndex = method.TypeParameters + .Select((type, index) => type.Name == "TKey" ? index : -1) + .FirstOrDefault(x => x >= 0); + + Debug.Assert(destinationTypeIndex < method.TypeArguments.Length); + + return IsSymbolType(method.TypeArguments[destinationTypeIndex], symbolType); + } + else if (method.ReducedFrom != null && !method.ReducedFrom.TypeArguments.IsEmpty) + { + // We are in the case where the ReducedFrom has TypeArguments but the original method doesn't. + // This seems to happen only for VB.NET and the only workaround is to force the construction + // of the ReducedFrom. + return IsBehavingOnSymbolType(method.GetConstructedReducedFrom(), symbolType); + } + else + { + return false; + } + } + } + + private static void HandleObjectCreation(in OperationAnalysisContext context, INamedTypeSymbol symbolType, + INamedTypeSymbol iEqualityComparerType, ImmutableHashSet collectionTypes) + { + var objectCreation = (IObjectCreationOperation)context.Operation; + + if (objectCreation.Type is INamedTypeSymbol createdType && + collectionTypes.Contains(createdType.OriginalDefinition) && + !createdType.TypeArguments.IsEmpty && + IsSymbolType(createdType.TypeArguments[0], symbolType) && + !objectCreation.Arguments.Any(arg => IsSymbolType(arg.Value, iEqualityComparerType))) + { + context.ReportDiagnostic(objectCreation.CreateDiagnostic(s_collectionRule, s_CollectionRuleProperties)); + } + } + + private static bool IsSymbolType(IOperation? operation, INamedTypeSymbol? symbolType) + { + if (operation?.Type is object && IsSymbolType(operation.Type.OriginalDefinition, symbolType)) + { + return true; + } + + if (operation is IConversionOperation conversion) + { + return IsSymbolType(conversion.Operand, symbolType); + } + + return false; + } + + private static bool IsSymbolType(ITypeSymbol typeSymbol, INamedTypeSymbol? symbolType) + => typeSymbol != null + && (SymbolEqualityComparer.Default.Equals(typeSymbol, symbolType) + || typeSymbol.AllInterfaces.Any(SymbolEqualityComparer.Default.Equals, symbolType)); + + private static bool IsSymbolClassType(IOperation operation) + { + if (operation.Type is object && + operation.Type.TypeKind == TypeKind.Class && + operation.Type.SpecialType != SpecialType.System_Object) + { + return true; + } + + if (operation is IConversionOperation conversion) + { + return IsSymbolClassType(conversion.Operand); + } + + return false; + } + + private static bool IsExplicitCastToObject(IOperation operation) + { + if (operation is not IConversionOperation conversion) + { + return false; + } + + if (conversion.IsImplicit) + { + return false; + } + + return conversion.Type?.SpecialType == SpecialType.System_Object; + } + + private static ImmutableDictionary> GetEqualityComparerMethodsToCheck(Compilation compilation) + { + var builder = ImmutableDictionary.CreateBuilder.Builder>(); + + if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsImmutableImmutableHashSet, out var immutableHashSetType)) + { + AddOrUpdate(nameof(ImmutableHashSet.CreateBuilder), immutableHashSetType); + AddOrUpdate(nameof(ImmutableHashSet.Create), immutableHashSetType); + AddOrUpdate(nameof(ImmutableHashSet.CreateRange), immutableHashSetType); + AddOrUpdate(nameof(ImmutableHashSet.ToImmutableHashSet), immutableHashSetType); + } + + if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsImmutableImmutableDictionary, out var immutableDictionaryType)) + { + AddOrUpdate(nameof(ImmutableDictionary.CreateBuilder), immutableDictionaryType); + AddOrUpdate(nameof(ImmutableDictionary.Create), immutableDictionaryType); + AddOrUpdate(nameof(ImmutableDictionary.CreateRange), immutableDictionaryType); + AddOrUpdate(nameof(ImmutableDictionary.ToImmutableDictionary), immutableDictionaryType); + } + + if (compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemLinqEnumerable, out var enumerableType)) + { + AddOrUpdate(nameof(Enumerable.Contains), enumerableType); + AddOrUpdate(nameof(Enumerable.Distinct), enumerableType); + AddOrUpdate(nameof(Enumerable.GroupBy), enumerableType); + AddOrUpdate(nameof(Enumerable.GroupJoin), enumerableType); + AddOrUpdate(nameof(Enumerable.Intersect), enumerableType); + AddOrUpdate(nameof(Enumerable.Join), enumerableType); + AddOrUpdate(nameof(Enumerable.SequenceEqual), enumerableType); + AddOrUpdate(nameof(Enumerable.ToDictionary), enumerableType); + AddOrUpdate("ToHashSet", enumerableType); + AddOrUpdate(nameof(Enumerable.ToLookup), enumerableType); + AddOrUpdate(nameof(Enumerable.Union), enumerableType); + } + + return builder.ToImmutableDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutable()); + + void AddOrUpdate(string methodName, INamedTypeSymbol typeSymbol) + { + if (!builder.TryGetValue(methodName, out var methodTypeSymbols)) + { + methodTypeSymbols = ImmutableHashSet.CreateBuilder(SymbolEqualityComparer.Default); + builder.Add(methodName, methodTypeSymbols); + } + + methodTypeSymbols.Add(typeSymbol); + } + } + + public static bool UseSymbolEqualityComparer(Compilation compilation) + => compilation.GetOrCreateTypeByMetadataName(SymbolEqualityComparerName) is object; + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionStrictApiAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionStrictApiAnalyzer.cs new file mode 100644 index 0000000000000..d06c873d2bc10 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionStrictApiAnalyzer.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + internal sealed class CompilerExtensionStrictApiAnalyzer : DiagnosticAnalyzer + { + private const string AssemblyReferenceValidationConfigurationKey = "roslyn_correctness.assembly_reference_validation"; + private const string AssemblyReferenceValidationConfigurationRelaxedValue = "relaxed"; + + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleTitle)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleDescription)); + private const string HelpLinkUri = "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md"; + + public static readonly DiagnosticDescriptor DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceStrictRule = new( + DiagnosticIds.DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule = new( + DiagnosticIds.DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule = new( + DiagnosticIds.DoNotRegisterCompilerTypesWithBadAssemblyReferenceRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceStrictRule, + DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule, + DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule); + + internal static bool IsStrictAnalysisEnabled(AnalyzerOptions options) + { + return !options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue(AssemblyReferenceValidationConfigurationKey, out var value) + || !value.Trim().Equals(AssemblyReferenceValidationConfigurationRelaxedValue, StringComparison.Ordinal); + } + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + // This analyzer is enabled by default via a configuration option that also applies to RS1022. It needs + // to proceed unless .globalconfig contains the following line to enable it: + // + // roslyn_correctness.assembly_reference_validation = relaxed + if (!IsStrictAnalysisEnabled(context.Options)) + { + // RS1022 is being applied instead of RS1038 + return; + } + + var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); + var diagnosticAnalyzer = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + if (diagnosticAnalyzer is null) + return; + + var referencesWorkspaces = false; + var referencesCSharp = false; + var referencesVisualBasic = false; + foreach (var assemblyName in context.Compilation.ReferencedAssemblyNames) + { + if (assemblyName.Name == "Microsoft.CodeAnalysis.Workspaces") + { + referencesWorkspaces = true; + } + else if (assemblyName.Name == "Microsoft.CodeAnalysis.CSharp") + { + referencesCSharp = true; + } + else if (assemblyName.Name == "Microsoft.CodeAnalysis.VisualBasic") + { + referencesVisualBasic = true; + } + } + + if (!referencesWorkspaces && !referencesCSharp && !referencesVisualBasic) + { + // This compilation doesn't reference any assemblies that would produce warnings by this analyzer + return; + } + + var diagnosticAnalyzerAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); + var sourceGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisISourceGenerator); + var incrementalGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisIIncrementalGenerator); + var generatorAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisGeneratorAttribute); + + context.RegisterSymbolAction( + context => + { + var namedType = (INamedTypeSymbol)context.Symbol; + if (!IsRegisteredExtension(namedType, diagnosticAnalyzer, diagnosticAnalyzerAttribute, out var applicationSyntaxReference, out var supportsCSharp, out var supportsVisualBasic) + && !IsRegisteredExtension(namedType, sourceGeneratorInterface, generatorAttribute, out applicationSyntaxReference, out supportsCSharp, out supportsVisualBasic) + && !IsRegisteredExtension(namedType, incrementalGeneratorInterface, generatorAttribute, out applicationSyntaxReference, out supportsCSharp, out supportsVisualBasic)) + { + // This is not a compiler extension + return; + } + + DiagnosticDescriptor descriptor; + if (referencesWorkspaces) + { + descriptor = DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceStrictRule; + } + else if (supportsCSharp && referencesVisualBasic) + { + descriptor = DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule; + } + else if (supportsVisualBasic && referencesCSharp) + { + descriptor = DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule; + } + else + { + return; + } + + context.ReportDiagnostic(Diagnostic.Create(descriptor, Location.Create(applicationSyntaxReference.SyntaxTree, applicationSyntaxReference.Span))); + }, + SymbolKind.NamedType); + }); + } + + private static bool IsRegisteredExtension(INamedTypeSymbol extension, [NotNullWhen(true)] INamedTypeSymbol? extensionClassOrInterface, [NotNullWhen(true)] INamedTypeSymbol? registrationAttributeType, [NotNullWhen(true)] out SyntaxReference? node, out bool supportsCSharp, out bool supportsVisualBasic) + { + supportsCSharp = false; + supportsVisualBasic = false; + + if (!extension.Inherits(extensionClassOrInterface)) + { + node = null; + return false; + } + + foreach (var attribute in extension.GetAttributes()) + { + // Only examine extension registrations in source + Debug.Assert(attribute.ApplicationSyntaxReference is not null, + $"Expected attributes returned by {nameof(ISymbol.GetAttributes)} (as opposed to {nameof(ITypeSymbolExtensions.GetApplicableAttributes)}) to have a non-null application."); + if (attribute.ApplicationSyntaxReference is null) + continue; + + if (!attribute.AttributeClass.Inherits(registrationAttributeType)) + continue; + + foreach (var arg in attribute.ConstructorArguments) + { + CheckLanguage(arg, ref supportsCSharp, ref supportsVisualBasic); + if (arg.Kind == TypedConstantKind.Array) + { + foreach (var element in arg.Values) + { + CheckLanguage(element, ref supportsCSharp, ref supportsVisualBasic); + } + } + } + + node = attribute.ApplicationSyntaxReference; + return true; + } + + node = null; + return false; + } + + private static void CheckLanguage(TypedConstant argument, ref bool supportsCSharp, ref bool supportsVisualBasic) + { + if (argument is { Kind: TypedConstantKind.Primitive, Type.SpecialType: SpecialType.System_String }) + { + var supportedLanguage = (string?)argument.Value; + if (supportedLanguage == LanguageNames.CSharp) + { + supportsCSharp = true; + } + else if (supportedLanguage == LanguageNames.VisualBasic) + { + supportsVisualBasic = true; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzer.cs new file mode 100644 index 0000000000000..30779d53159cd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzer.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.Versioning; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + internal sealed class CompilerExtensionTargetFrameworkAnalyzer : DiagnosticAnalyzer + { + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkRuleTitle)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkRuleDescription)); + private const string HelpLinkUri = "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1041.md"; + + public static readonly DiagnosticDescriptor DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule = new( + DiagnosticIds.DoNotRegisterCompilerTypesWithBadTargetFrameworkRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); + var diagnosticAnalyzer = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + if (diagnosticAnalyzer is null) + return; + + var targetFrameworkAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeVersioningTargetFrameworkAttribute); + if (targetFrameworkAttribute is null) + return; + + AttributeData? appliedTargetFrameworkAttribute = context.Compilation.Assembly.GetAttribute(targetFrameworkAttribute); + if (appliedTargetFrameworkAttribute is null) + return; + + if (appliedTargetFrameworkAttribute.ConstructorArguments.IsEmpty) + { + return; + } + + string displayName; + switch (appliedTargetFrameworkAttribute.ConstructorArguments[0].Value as string) + { + case ".NETStandard,Version=v1.0": + case ".NETStandard,Version=v1.1": + case ".NETStandard,Version=v1.2": + case ".NETStandard,Version=v1.3": + case ".NETStandard,Version=v1.4": + case ".NETStandard,Version=v1.5": + case ".NETStandard,Version=v1.6": + case ".NETStandard,Version=v2.0": + // The compiler supports this target framework + return; + + default: + displayName = appliedTargetFrameworkAttribute.NamedArguments.FirstOrDefault(arg => arg.Key == nameof(TargetFrameworkAttribute.FrameworkDisplayName)).Value.Value as string + ?? appliedTargetFrameworkAttribute.ConstructorArguments[0].Value as string + ?? ""; + break; + } + + var diagnosticAnalyzerAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); + var sourceGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisISourceGenerator); + var incrementalGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisIIncrementalGenerator); + var generatorAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisGeneratorAttribute); + + context.RegisterSymbolAction( + context => + { + var namedType = (INamedTypeSymbol)context.Symbol; + if (!IsRegisteredExtension(namedType, diagnosticAnalyzer, diagnosticAnalyzerAttribute, out var applicationSyntaxReference) + && !IsRegisteredExtension(namedType, sourceGeneratorInterface, generatorAttribute, out applicationSyntaxReference) + && !IsRegisteredExtension(namedType, incrementalGeneratorInterface, generatorAttribute, out applicationSyntaxReference)) + { + // This is not a compiler extension + return; + } + + context.ReportDiagnostic(Diagnostic.Create( + DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule, + Location.Create(applicationSyntaxReference.SyntaxTree, applicationSyntaxReference.Span), + displayName)); + }, + SymbolKind.NamedType); + }); + } + + private static bool IsRegisteredExtension(INamedTypeSymbol extension, [NotNullWhen(true)] INamedTypeSymbol? extensionClassOrInterface, [NotNullWhen(true)] INamedTypeSymbol? registrationAttributeType, [NotNullWhen(true)] out SyntaxReference? node) + { + if (!extension.Inherits(extensionClassOrInterface)) + { + node = null; + return false; + } + + foreach (var attribute in extension.GetAttributes()) + { + // Only examine extension registrations in source + Debug.Assert(attribute.ApplicationSyntaxReference is not null, + $"Expected attributes returned by {nameof(ISymbol.GetAttributes)} (as opposed to {nameof(ITypeSymbolExtensions.GetApplicableAttributes)}) to have a non-null application."); + if (attribute.ApplicationSyntaxReference is null) + continue; + + if (!attribute.AttributeClass.Inherits(registrationAttributeType)) + continue; + + node = attribute.ApplicationSyntaxReference; + return true; + } + + node = null; + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzer.cs new file mode 100644 index 0000000000000..31e623bc15f84 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzer.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1025: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class ConfigureGeneratedCodeAnalysisAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer + { + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.ConfigureGeneratedCodeAnalysisRuleId, + CreateLocalizableResourceString(nameof(ConfigureGeneratedCodeAnalysisTitle)), + CreateLocalizableResourceString(nameof(ConfigureGeneratedCodeAnalysisMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + +#pragma warning disable RS1025 // Configure generated code analysis + public override void Initialize(AnalysisContext context) +#pragma warning restore RS1025 // Configure generated code analysis + { + context.EnableConcurrentExecution(); + + base.Initialize(context); + } + + [SuppressMessage("AnalyzerPerformance", "RS1012:Start action has no registered actions.", Justification = "Method returns an analyzer that is registered by the caller.")] + protected override DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + var compilation = compilationContext.Compilation; + + var analysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsAnalysisContext); + if (analysisContext is null) + { + return null; + } + + compilationContext.RegisterOperationBlockStartAction(context => + { + if (context.OwningSymbol?.Kind != SymbolKind.Method) + { + return; + } + + var method = (IMethodSymbol)context.OwningSymbol; + if (method.Name != nameof(DiagnosticAnalyzer.Initialize)) + { + return; + } + + IParameterSymbol? analysisContextParameter = null; + foreach (var parameter in method.Parameters) + { + if (!SymbolEqualityComparer.Default.Equals(parameter.Type, analysisContext)) + { + continue; + } + + analysisContextParameter = parameter; + break; + } + + if (analysisContextParameter is null) + { + return; + } + + var analyzer = new ConfigureGeneratedCodeAnalyzer(analysisContextParameter); + context.RegisterOperationAction(analyzer.HandleInvocationOperation, OperationKind.Invocation); + context.RegisterOperationBlockEndAction(analyzer.HandleOperationBlockEnd); + }); + + // This analyzer only performs operation block analysis + return null; + } + + private sealed class ConfigureGeneratedCodeAnalyzer + { + private readonly IParameterSymbol _analysisContextParameter; + + public ConfigureGeneratedCodeAnalyzer(IParameterSymbol analysisContextParameter) + { + _analysisContextParameter = analysisContextParameter; + } + + public bool ConfiguredGeneratedCodeAnalysis { get; private set; } + + internal void HandleInvocationOperation(OperationAnalysisContext context) + { + if (ConfiguredGeneratedCodeAnalysis) + { + return; + } + + var invocation = (IInvocationOperation)context.Operation; + if (invocation.TargetMethod?.Name != nameof(AnalysisContext.ConfigureGeneratedCodeAnalysis)) + { + return; + } + + if (invocation.Instance?.Kind != OperationKind.ParameterReference) + { + return; + } + + var parameterReference = (IParameterReferenceOperation)invocation.Instance; + if (!SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, _analysisContextParameter)) + { + return; + } + + ConfiguredGeneratedCodeAnalysis = true; + } + + internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context) + { + if (!ConfiguredGeneratedCodeAnalysis) + { + context.ReportDiagnostic(_analysisContextParameter.CreateDiagnostic(Rule)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAPIUsageAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAPIUsageAnalyzer.cs new file mode 100644 index 0000000000000..7d16388a11d73 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAPIUsageAnalyzer.cs @@ -0,0 +1,283 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1022: + /// + public abstract class DiagnosticAnalyzerApiUsageAnalyzer : DiagnosticAnalyzer + where TTypeSyntax : SyntaxNode + { + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(DoNotUseTypesFromAssemblyRuleTitle)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(DoNotUseTypesFromAssemblyRuleDescription), nameof(AnalysisContext), DiagnosticWellKnownNames.RegisterCompilationStartActionName); + private const string CodeActionMetadataName = "Microsoft.CodeAnalysis.CodeActions.CodeAction"; + private const string HelpLinkUri = "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1022.md"; + private static readonly ImmutableArray s_WorkspaceAssemblyNames = ImmutableArray.Create( + "Microsoft.CodeAnalysis.Workspaces", + "Microsoft.CodeAnalysis.CSharp.Workspaces", + "Microsoft.CodeAnalysis.VisualBasic.Workspaces"); + + public static readonly DiagnosticDescriptor DoNotUseTypesFromAssemblyDirectRule = new( + DiagnosticIds.DoNotUseTypesFromAssemblyRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotUseTypesFromAssemblyRuleDirectMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + public static readonly DiagnosticDescriptor DoNotUseTypesFromAssemblyIndirectRule = new( + DiagnosticIds.DoNotUseTypesFromAssemblyRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotUseTypesFromAssemblyRuleIndirectMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescription, + helpLinkUri: HelpLinkUri, + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + protected abstract bool IsNamedTypeDeclarationBlock(SyntaxNode syntax); + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotUseTypesFromAssemblyDirectRule, DoNotUseTypesFromAssemblyIndirectRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterCompilationStartAction(compilationStartContext => + { + // This analyzer is disabled by default via a configuration option that also applies to RS1038. It only + // needs to proceed if .globalconfig contains the following line to enable it: + // + // roslyn_correctness.assembly_reference_validation = relaxed + if (CompilerExtensionStrictApiAnalyzer.IsStrictAnalysisEnabled(compilationStartContext.Options)) + { + // RS1038 is being applied instead of RS1022 + return; + } + + if (compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(CodeActionMetadataName) == null) + { + // No reference to core Workspaces assembly. + return; + } + + INamedTypeSymbol? diagnosticAnalyzer = compilationStartContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + if (diagnosticAnalyzer == null) + { + // Does not contain any diagnostic analyzers. + return; + } + + var hasAccessToTypeFromWorkspaceAssemblies = false; + var namedTypesToAccessedTypesMap = new ConcurrentDictionary>(); + var diagnosticAnalyzerTypes = new ConcurrentBag(); + compilationStartContext.RegisterSymbolAction(symbolContext => + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + if (namedType.DerivesFrom(diagnosticAnalyzer, baseTypesOnly: true)) + { + diagnosticAnalyzerTypes.Add(namedType); + } + + var usedTypes = GetUsedNamedTypes(namedType, symbolContext.Compilation, symbolContext.CancellationToken, ref hasAccessToTypeFromWorkspaceAssemblies); + var added = namedTypesToAccessedTypesMap.TryAdd(namedType, usedTypes); + Debug.Assert(added); + }, SymbolKind.NamedType); + + compilationStartContext.RegisterCompilationEndAction(compilationEndContext => + { + if (diagnosticAnalyzerTypes.IsEmpty || !hasAccessToTypeFromWorkspaceAssemblies) + { + return; + } + + var typesToProcess = new Queue(); + var processedTypes = new HashSet(); + var violatingTypeNamesBuilder = new SortedSet(); + var violatingUsedTypeNamesBuilder = new SortedSet(); + foreach (INamedTypeSymbol declaredType in namedTypesToAccessedTypesMap.Keys) + { + if (!diagnosticAnalyzerTypes.Contains(declaredType)) + { + continue; + } + + typesToProcess.Clear(); + processedTypes.Clear(); + violatingTypeNamesBuilder.Clear(); + violatingUsedTypeNamesBuilder.Clear(); + typesToProcess.Enqueue(declaredType); + do + { + var typeToProcess = typesToProcess.Dequeue(); + Debug.Assert(SymbolEqualityComparer.Default.Equals(typeToProcess.ContainingAssembly, declaredType.ContainingAssembly)); + Debug.Assert(namedTypesToAccessedTypesMap.ContainsKey(typeToProcess)); + + foreach (INamedTypeSymbol usedType in namedTypesToAccessedTypesMap[typeToProcess]) + { + if (usedType.ContainingAssembly != null && + s_WorkspaceAssemblyNames.Contains(usedType.ContainingAssembly.Name)) + { + violatingTypeNamesBuilder.Add(usedType.ToDisplayString()); + violatingUsedTypeNamesBuilder.Add(typeToProcess.ToDisplayString()); + } + + if (!processedTypes.Contains(usedType) && namedTypesToAccessedTypesMap.ContainsKey(usedType)) + { + typesToProcess.Enqueue(usedType); + } + } + + processedTypes.Add(typeToProcess); + } while (typesToProcess.Count != 0); + + if (violatingTypeNamesBuilder.Count > 0) + { + string[] args; + DiagnosticDescriptor rule; + if (violatingUsedTypeNamesBuilder.Count == 1 && violatingUsedTypeNamesBuilder.Single() == declaredType.ToDisplayString()) + { + // Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + rule = DoNotUseTypesFromAssemblyDirectRule; + args = new[] + { + declaredType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), + string.Join(", ", violatingTypeNamesBuilder) + }; + } + else + { + // Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + rule = DoNotUseTypesFromAssemblyIndirectRule; + args = new[] + { + declaredType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), + string.Join(", ", violatingUsedTypeNamesBuilder), + string.Join(", ", violatingTypeNamesBuilder) + }; + } + + Diagnostic diagnostic = declaredType.CreateDiagnostic(rule, args); + compilationEndContext.ReportDiagnostic(diagnostic); + } + } + }); + }); + } + + private ImmutableHashSet GetUsedNamedTypes(INamedTypeSymbol namedType, Compilation compilation, CancellationToken cancellationToken, ref bool hasAccessToTypeFromWorkspaceAssemblies) + { + var builder = PooledHashSet.GetInstance(); + foreach (var decl in namedType.DeclaringSyntaxReferences) + { + var syntax = decl.GetSyntax(cancellationToken); + + // GetSyntax for VB returns the StatementSyntax instead of BlockSyntax node. + syntax = syntax.FirstAncestorOrSelf(IsNamedTypeDeclarationBlock, ascendOutOfTrivia: false) ?? syntax; + +#pragma warning disable RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree); +#pragma warning restore RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + var nodesToProcess = new Queue<(SyntaxNode node, bool inExecutableCode)>(); + nodesToProcess.Enqueue((node: syntax, inExecutableCode: false)); + + do + { + (SyntaxNode node, bool inExecutableCode) nodeToProcess = nodesToProcess.Dequeue(); + var node = nodeToProcess.node; + var inExecutableCode = nodeToProcess.inExecutableCode; + if (!inExecutableCode && !ReferenceEquals(node, syntax) && IsNamedTypeDeclarationBlock(node)) + { + // Skip type member declarations. + continue; + } + + if (node is TTypeSyntax typeSyntax) + { + var typeInfo = semanticModel.GetTypeInfo(typeSyntax, cancellationToken); + AddUsedNamedTypeCore(typeInfo.Type, builder, ref hasAccessToTypeFromWorkspaceAssemblies); + } + + if (!inExecutableCode) + { + var operationBlock = semanticModel.GetOperation(node, cancellationToken); + if (operationBlock != null) + { + // Add used types within executable code in the operation tree. + inExecutableCode = true; + foreach (var operation in operationBlock.DescendantsAndSelf()) + { + AddUsedNamedTypeCore(operation.Type, builder, ref hasAccessToTypeFromWorkspaceAssemblies); + + // Handle static member accesses specially as there is no operation for static type off which the member is accessed. + if (operation is IMemberReferenceOperation memberReference && + memberReference.Member.IsStatic) + { + AddUsedNamedTypeCore(memberReference.Member.ContainingType, builder, ref hasAccessToTypeFromWorkspaceAssemblies); + } + else if (operation is IInvocationOperation invocation && + (invocation.TargetMethod.IsStatic || invocation.TargetMethod.IsExtensionMethod)) + { + AddUsedNamedTypeCore(invocation.TargetMethod.ContainingType, builder, ref hasAccessToTypeFromWorkspaceAssemblies); + } + } + } + } + + foreach (var child in node.ChildNodes()) + { + nodesToProcess.Enqueue((child, inExecutableCode)); + } + } while (nodesToProcess.Count != 0); + } + + return builder.ToImmutableAndFree(); + } + + private static void AddUsedNamedTypeCore(ITypeSymbol? type, PooledHashSet builder, ref bool hasAccessToTypeFromWorkspaceAssemblies) + { + if (type is INamedTypeSymbol usedType && + usedType.TypeKind != TypeKind.Error) + { + builder.Add(usedType); + + if (!hasAccessToTypeFromWorkspaceAssemblies && + usedType.ContainingAssembly != null && + s_WorkspaceAssemblyNames.Contains(usedType.ContainingAssembly.Name)) + { + hasAccessToTypeFromWorkspaceAssemblies = true; + } + + if (usedType.IsGenericType) + { + foreach (var typeArgument in usedType.TypeArguments) + { + AddUsedNamedTypeCore(typeArgument, builder, ref hasAccessToTypeFromWorkspaceAssemblies); + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAttributeAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAttributeAnalyzer.cs new file mode 100644 index 0000000000000..f865e9b9b83b0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerAttributeAnalyzer.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1001: + /// RS1004: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DiagnosticAnalyzerAttributeAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer + { + public static readonly DiagnosticDescriptor MissingDiagnosticAnalyzerAttributeRule = new( + DiagnosticIds.MissingDiagnosticAnalyzerAttributeRuleId, + CreateLocalizableResourceString(nameof(MissingDiagnosticAnalyzerAttributeTitle)), + CreateLocalizableResourceString(nameof(MissingAttributeMessage), WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(MissingDiagnosticAnalyzerAttributeDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor AddLanguageSupportToAnalyzerRule = new( + DiagnosticIds.AddLanguageSupportToAnalyzerRuleId, + CreateLocalizableResourceString(nameof(AddLanguageSupportToAnalyzerTitle)), + CreateLocalizableResourceString(nameof(AddLanguageSupportToAnalyzerMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AddLanguageSupportToAnalyzerDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(MissingDiagnosticAnalyzerAttributeRule, AddLanguageSupportToAnalyzerRule); + +#pragma warning disable RS1025 // Configure generated code analysis + public override void Initialize(AnalysisContext context) +#pragma warning restore RS1025 // Configure generated code analysis + { + context.EnableConcurrentExecution(); + + base.Initialize(context); + } + + [SuppressMessage("AnalyzerPerformance", "RS1012:Start action has no registered actions.", Justification = "Method returns an analyzer that is registered by the caller.")] + protected override DiagnosticAnalyzerSymbolAnalyzer GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + var attributeUsageAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemAttributeUsageAttribute); + return new AttributeAnalyzer(diagnosticAnalyzer, diagnosticAnalyzerAttribute, attributeUsageAttribute); + } + + private sealed class AttributeAnalyzer : DiagnosticAnalyzerSymbolAnalyzer + { + private const string CSharpCompilationFullName = @"Microsoft.CodeAnalysis.CSharp.CSharpCompilation"; + private const string BasicCompilationFullName = @"Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation"; + + private readonly INamedTypeSymbol? _attributeUsageAttribute; + + public AttributeAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute, INamedTypeSymbol? attributeUsageAttribute) + : base(diagnosticAnalyzer, diagnosticAnalyzerAttribute) + { + _attributeUsageAttribute = attributeUsageAttribute; + } + + protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext) + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + if (namedType.IsAbstract) + { + return; + } + + // 1) MissingDiagnosticAnalyzerAttributeRule: DiagnosticAnalyzer has no DiagnosticAnalyzerAttribute. + // 2) AddLanguageSupportToAnalyzerRule: For analyzer supporting only one of C# or VB languages, detect if it can support the other language. + + var hasAttribute = false; + SyntaxNode? attributeSyntax = null; + bool supportsCSharp = false; + bool supportsVB = false; + + var namedTypeAttributes = namedType.GetApplicableAttributes(_attributeUsageAttribute); + + foreach (AttributeData attribute in namedTypeAttributes) + { + if (attribute.AttributeClass.DerivesFrom(DiagnosticAnalyzerAttribute)) + { + // Bail out for the case where analyzer type derives from a sub-type in different assembly, and the sub-type has the diagnostic analyzer attribute. + if (attribute.ApplicationSyntaxReference == null) + { + return; + } + + hasAttribute = true; + + // The attribute constructor's signature is "(string, params string[])", + // so process both string arguments and string[] arguments. + foreach (TypedConstant arg in attribute.ConstructorArguments) + { + CheckLanguage(arg, ref supportsCSharp, ref supportsVB); + + if (arg.Kind == TypedConstantKind.Array) + { + foreach (TypedConstant element in arg.Values) + { + CheckLanguage(element, ref supportsCSharp, ref supportsVB); + } + } + } + + attributeSyntax = attribute.ApplicationSyntaxReference.GetSyntax(symbolContext.CancellationToken); + } + } + + if (!hasAttribute) + { + Diagnostic diagnostic = namedType.CreateDiagnostic(MissingDiagnosticAnalyzerAttributeRule); + symbolContext.ReportDiagnostic(diagnostic); + } + else if (supportsCSharp ^ supportsVB) + { + RoslynDebug.Assert(attributeSyntax != null); + + // If the analyzer assembly doesn't reference either C# or VB CodeAnalysis assemblies, + // then the analyzer is pretty likely a language-agnostic analyzer. + Compilation compilation = symbolContext.Compilation; + string compilationTypeNameToCheck = supportsCSharp ? CSharpCompilationFullName : BasicCompilationFullName; + INamedTypeSymbol? compilationType = compilation.GetOrCreateTypeByMetadataName(compilationTypeNameToCheck); + if (compilationType == null) + { + string missingLanguage = supportsCSharp ? LanguageNames.VisualBasic : LanguageNames.CSharp; + Diagnostic diagnostic = attributeSyntax.CreateDiagnostic(AddLanguageSupportToAnalyzerRule, namedType.Name, missingLanguage); + symbolContext.ReportDiagnostic(diagnostic); + } + } + } + } + + private static void CheckLanguage(TypedConstant argument, ref bool supportsCSharp, ref bool supportsVB) + { + if (argument.Kind == TypedConstantKind.Primitive && + argument.Type != null && + argument.Type.SpecialType == SpecialType.System_String) + { + var supportedLanguage = (string?)argument.Value; + if (supportedLanguage == LanguageNames.CSharp) + { + supportsCSharp = true; + } + else if (supportedLanguage == LanguageNames.VisualBasic) + { + supportsVB = true; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.DiagnosticAnalyzerSymbolAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.DiagnosticAnalyzerSymbolAnalyzer.cs new file mode 100644 index 0000000000000..4c2adf1539b6b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.DiagnosticAnalyzerSymbolAnalyzer.cs @@ -0,0 +1,54 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer + { + protected abstract class DiagnosticAnalyzerSymbolAnalyzer + { + protected DiagnosticAnalyzerSymbolAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + DiagnosticAnalyzer = diagnosticAnalyzer; + DiagnosticAnalyzerAttribute = diagnosticAnalyzerAttribute; + } + + protected INamedTypeSymbol DiagnosticAnalyzer { get; } + protected INamedTypeSymbol DiagnosticAnalyzerAttribute { get; } + + protected bool IsDiagnosticAnalyzer(INamedTypeSymbol type) + { + return SymbolEqualityComparer.Default.Equals(type, DiagnosticAnalyzer); + } + + internal void AnalyzeSymbol(SymbolAnalysisContext symbolContext) + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + if (namedType.GetBaseTypes().Any(IsDiagnosticAnalyzer)) + { + AnalyzeDiagnosticAnalyzer(symbolContext); + } + } + + protected abstract void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext); + + protected bool HasDiagnosticAnalyzerAttribute(INamedTypeSymbol namedType, INamedTypeSymbol? attributeUsageAttribute) + { + foreach (AttributeData attribute in namedType.GetApplicableAttributes(attributeUsageAttribute)) + { + if (attribute.AttributeClass.DerivesFrom(DiagnosticAnalyzerAttribute)) + { + return true; + } + } + + return false; + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer.cs new file mode 100644 index 0000000000000..decd628a3b075 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer.cs @@ -0,0 +1,63 @@ +// 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.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer + { + protected abstract class SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer : DiagnosticAnalyzerSymbolAnalyzer + where TClassDeclarationSyntax : SyntaxNode + where TStructDeclarationSyntax : SyntaxNode + where TSyntaxNodeOfInterest : SyntaxNode + { + protected SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer(INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + : base(diagnosticAnalyzer, diagnosticAnalyzerAttribute) + { + } + + internal static IEnumerable GetClassDeclarationNodes(INamedTypeSymbol namedType, CancellationToken cancellationToken) + { + foreach (SyntaxNode syntax in namedType.DeclaringSyntaxReferences.Select(s => s.GetSyntax(cancellationToken))) + { + if (syntax != null) + { + TClassDeclarationSyntax? classDecl = syntax.FirstAncestorOrSelf(ascendOutOfTrivia: false); + if (classDecl != null) + { + yield return classDecl; + } + } + } + } + + protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext) + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + IEnumerable classDecls = GetClassDeclarationNodes(namedType, symbolContext.CancellationToken); + foreach (TClassDeclarationSyntax classDecl in classDecls) + { + IEnumerable syntaxNodes = + classDecl.DescendantNodes(n => n is not (TClassDeclarationSyntax or TStructDeclarationSyntax) || + ReferenceEquals(n, classDecl)) + .OfType(); + if (syntaxNodes.Any()) + { + SemanticModel semanticModel = symbolContext.Compilation.GetSemanticModel(classDecl.SyntaxTree); + foreach (TSyntaxNodeOfInterest syntaxNode in syntaxNodes) + { + AnalyzeNode(symbolContext, syntaxNode, semanticModel); + } + } + } + } + + protected abstract void AnalyzeNode(SymbolAnalysisContext symbolContext, TSyntaxNodeOfInterest syntaxNode, SemanticModel semanticModel); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.cs new file mode 100644 index 0000000000000..ba9e38f4c37a6 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerCorrectnessAnalyzer.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + public abstract partial class DiagnosticAnalyzerCorrectnessAnalyzer : DiagnosticAnalyzer + { +#pragma warning disable RS1026 // Enable concurrent execution + public override void Initialize(AnalysisContext context) +#pragma warning restore RS1026 // Enable concurrent execution + { + // CONSIDER: Make all the subtypes thread safe to enable concurrent analyzer callbacks. + //context.EnableConcurrentExecution(); + + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterCompilationStartAction(compilationContext => + { + INamedTypeSymbol? diagnosticAnalyzer = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + INamedTypeSymbol? diagnosticAnalyzerAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); + + if (diagnosticAnalyzer == null || diagnosticAnalyzerAttribute == null) + { + // We don't need to check assemblies unless they're referencing Microsoft.CodeAnalysis which defines DiagnosticAnalyzer. + return; + } + + DiagnosticAnalyzerSymbolAnalyzer? analyzer = GetDiagnosticAnalyzerSymbolAnalyzer(compilationContext, diagnosticAnalyzer, diagnosticAnalyzerAttribute); + if (analyzer != null) + { + compilationContext.RegisterSymbolAction(c => analyzer.AnalyzeSymbol(c), SymbolKind.NamedType); + } + }); + } + + protected abstract DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerFieldsAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerFieldsAnalyzer.cs new file mode 100644 index 0000000000000..8275a93af46e3 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticAnalyzerFieldsAnalyzer.cs @@ -0,0 +1,187 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + public abstract class DiagnosticAnalyzerFieldsAnalyzer< + TClassDeclarationSyntax, + TStructDeclarationSyntax, + TFieldDeclarationSyntax, + TTypeSyntax, + TVariableTypeDeclarationSyntax, + TTypeArgumentListSyntax, + TGenericNameSyntax + > : DiagnosticAnalyzerCorrectnessAnalyzer + where TClassDeclarationSyntax : SyntaxNode + where TStructDeclarationSyntax : SyntaxNode + where TFieldDeclarationSyntax : SyntaxNode + where TTypeSyntax : SyntaxNode + where TVariableTypeDeclarationSyntax : SyntaxNode + where TTypeArgumentListSyntax : SyntaxNode + where TGenericNameSyntax : SyntaxNode + { + private static readonly string s_compilationTypeFullName = typeof(Compilation).FullName; + private static readonly string s_symbolTypeFullName = typeof(ISymbol).FullName; + private static readonly string s_operationTypeFullName = typeof(IOperation).FullName; + + public static readonly DiagnosticDescriptor DoNotStorePerCompilationDataOntoFieldsRule = new( + DiagnosticIds.DoNotStorePerCompilationDataOntoFieldsRuleId, + CreateLocalizableResourceString(nameof(DoNotStorePerCompilationDataOntoFieldsTitle)), + CreateLocalizableResourceString(nameof(DoNotStorePerCompilationDataOntoFieldsMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotStorePerCompilationDataOntoFieldsDescription), nameof(AnalysisContext), DiagnosticWellKnownNames.RegisterCompilationStartActionName), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotStorePerCompilationDataOntoFieldsRule); + +#pragma warning disable RS1025 // Configure generated code analysis + public override void Initialize(AnalysisContext context) +#pragma warning restore RS1025 // Configure generated code analysis + { + context.EnableConcurrentExecution(); + + base.Initialize(context); + } + + [SuppressMessage("AnalyzerPerformance", "RS1012:Start action has no registered actions.", Justification = "Method returns an analyzer that is registered by the caller.")] + protected override DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + WellKnownTypeProvider typeProvider = WellKnownTypeProvider.GetOrCreate(compilationContext.Compilation); + + INamedTypeSymbol? compilationType = typeProvider.GetOrCreateTypeByMetadataName(s_compilationTypeFullName); + if (compilationType == null) + { + return null; + } + + INamedTypeSymbol? symbolType = typeProvider.GetOrCreateTypeByMetadataName(s_symbolTypeFullName); + if (symbolType == null) + { + return null; + } + + INamedTypeSymbol? operationType = typeProvider.GetOrCreateTypeByMetadataName(s_operationTypeFullName); + if (operationType == null) + { + return null; + } + + var attributeUsageAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemAttributeUsageAttribute); + + return new FieldsAnalyzer(compilationType, symbolType, operationType, attributeUsageAttribute, diagnosticAnalyzer, diagnosticAnalyzerAttribute); + } + + private sealed class FieldsAnalyzer : SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer + { + private readonly INamedTypeSymbol _compilationType; + private readonly INamedTypeSymbol _symbolType; + private readonly INamedTypeSymbol _operationType; + private readonly INamedTypeSymbol? _attributeUsageAttribute; + + public FieldsAnalyzer(INamedTypeSymbol compilationType, + INamedTypeSymbol symbolType, + INamedTypeSymbol operationType, + INamedTypeSymbol? attributeUsageAttribute, + INamedTypeSymbol diagnosticAnalyzer, + INamedTypeSymbol diagnosticAnalyzerAttribute) + : base(diagnosticAnalyzer, diagnosticAnalyzerAttribute) + { + _compilationType = compilationType; + _symbolType = symbolType; + _operationType = operationType; + _attributeUsageAttribute = attributeUsageAttribute; + } + + protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext) + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + if (!HasDiagnosticAnalyzerAttribute(namedType, _attributeUsageAttribute)) + { + // We are interested only in DiagnosticAnalyzer types with DiagnosticAnalyzerAttribute. + return; + } + + base.AnalyzeDiagnosticAnalyzer(symbolContext); + } + + protected override void AnalyzeNode(SymbolAnalysisContext symbolContext, TFieldDeclarationSyntax syntaxNode, SemanticModel semanticModel) + { + // Get all the type syntax nodes within the topmost type declaration nodes for field declarations. + System.Collections.Generic.IEnumerable variableTypeDeclarations = syntaxNode.DescendantNodesAndSelf().OfType(); + System.Collections.Generic.IEnumerable topMostTypeNodes = variableTypeDeclarations.SelectMany(typeDecl => typeDecl.ChildNodes().OfType()); + System.Collections.Generic.IEnumerable typeNodes = topMostTypeNodes.SelectMany(t => t.DescendantNodesAndSelf().OfType()); + + foreach (TTypeSyntax typeNode in typeNodes) + { + if (IsContainedInFuncOrAction(typeNode, semanticModel)) + { + continue; + } + + ITypeSymbol? type = semanticModel.GetTypeInfo(typeNode, symbolContext.CancellationToken).Type; + if (type != null) + { + foreach (ITypeSymbol innerType in type.GetBaseTypesAndThis()) + { + if (SymbolEqualityComparer.Default.Equals(innerType, _compilationType)) + { + ReportDiagnostic(type, typeNode, symbolContext); + return; + } + } + + if (SymbolEqualityComparer.Default.Equals(type, _symbolType) || SymbolEqualityComparer.Default.Equals(type, _operationType)) + { + ReportDiagnostic(type, typeNode, symbolContext); + return; + } + + foreach (INamedTypeSymbol iface in type.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(iface, _symbolType) || SymbolEqualityComparer.Default.Equals(iface, _operationType)) + { + ReportDiagnostic(type, typeNode, symbolContext); + return; + } + } + } + } + } + + private static void ReportDiagnostic(ITypeSymbol type, TTypeSyntax typeSyntax, SymbolAnalysisContext context) + { + Diagnostic diagnostic = typeSyntax.CreateDiagnostic(DoNotStorePerCompilationDataOntoFieldsRule, type.ToDisplayString()); + context.ReportDiagnostic(diagnostic); + } + } + + private static bool IsContainedInFuncOrAction(TTypeSyntax typeSyntax, SemanticModel model) + { + var current = typeSyntax.Parent; + while (current is TTypeArgumentListSyntax or TGenericNameSyntax) + { + if (current is TGenericNameSyntax && model.GetSymbolInfo(current).Symbol is INamedTypeSymbol { DelegateInvokeMethod: not null }) + { + return true; + } + + current = current.Parent; + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs new file mode 100644 index 0000000000000..a32c206bda7d1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer.cs @@ -0,0 +1,1323 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + using PooledLocalizabeStringsConcurrentDictionary = PooledConcurrentDictionary>; + using PooledResourcesDataValueConcurrentDictionary = PooledConcurrentDictionary>; + using PooledFieldToResourceNameAndFileNameConcurrentDictionary = PooledConcurrentDictionary; + using PooledFieldToCustomTagsConcurrentDictionary = PooledConcurrentDictionary>; + + /// + /// RS1007 + /// RS1015 + /// RS1017 + /// RS1019 + /// RS1028 + /// RS1029 + /// RS1031 + /// RS1032 + /// RS1033 + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed partial class DiagnosticDescriptorCreationAnalyzer : DiagnosticAnalyzer + { + private const string HelpLinkUriParameterName = "helpLinkUri"; + private const string CategoryParameterName = "category"; + private const string DiagnosticIdParameterName = "id"; + private const string CustomTagsParameterName = "customTags"; + private const string IsEnabledByDefaultParameterName = "isEnabledByDefault"; + private const string DefaultSeverityParameterName = "defaultSeverity"; + private const string RuleLevelParameterName = "ruleLevel"; + private const string CompilationEndWellKnownDiagnosticTag = "CompilationEnd" /*WellKnownDiagnosticTags.CompilationEnd*/; + + internal const string DefineDescriptorArgumentCorrectlyFixValue = nameof(DefineDescriptorArgumentCorrectlyFixValue); + private const string DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo = nameof(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo); + private const string AdditionalDocumentLocationInfoSeparator = ";;"; + + private static readonly ImmutableHashSet CADiagnosticIdAllowedAssemblies = ImmutableHashSet.Create( + StringComparer.Ordinal, + "Microsoft.CodeAnalysis.VersionCheckAnalyzer", + "Microsoft.CodeAnalysis.NetAnalyzers", + "Microsoft.CodeAnalysis.CSharp.NetAnalyzers", + "Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers", + "Microsoft.CodeQuality.Analyzers", + "Microsoft.CodeQuality.CSharp.Analyzers", + "Microsoft.CodeQuality.VisualBasic.Analyzers", + "Microsoft.NetCore.Analyzers", + "Microsoft.NetCore.CSharp.Analyzers", + "Microsoft.NetCore.VisualBasic.Analyzers", + "Microsoft.NetFramework.Analyzers", + "Microsoft.NetFramework.CSharp.Analyzers", + "Microsoft.NetFramework.VisualBasic.Analyzers", + "Text.Analyzers", + "Text.CSharp.Analyzers", + "Text.VisualBasic.Analyzers"); + + public static readonly DiagnosticDescriptor UseLocalizableStringsInDescriptorRule = new( + DiagnosticIds.UseLocalizableStringsInDescriptorRuleId, + CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorTitle)), + CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisLocalization, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor ProvideHelpUriInDescriptorRule = new( + DiagnosticIds.ProvideHelpUriInDescriptorRuleId, + CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorTitle)), + CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDocumentation, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DiagnosticIdMustBeAConstantRule = new( + DiagnosticIds.DiagnosticIdMustBeAConstantRuleId, + CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantTitle)), + CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor UseUniqueDiagnosticIdRule = new( + DiagnosticIds.UseUniqueDiagnosticIdRuleId, + CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdTitle)), + CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdDescription)), + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + public static readonly DiagnosticDescriptor ProvideCustomTagsInDescriptorRule = new( + DiagnosticIds.ProvideCustomTagsInDescriptorRuleId, + CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorTitle)), + CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDocumentation, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DoNotUseReservedDiagnosticIdRule = new( + DiagnosticIds.DoNotUseReservedDiagnosticIdRuleId, + CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdTitle)), + CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DefineDiagnosticTitleCorrectlyRule = new( + DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId, + CreateLocalizableResourceString(nameof(DefineDiagnosticTitleCorrectlyTitle)), + CreateLocalizableResourceString(nameof(DefineDiagnosticTitleCorrectlyMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DefineDiagnosticMessageCorrectlyRule = new( + DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId, + CreateLocalizableResourceString(nameof(DefineDiagnosticMessageCorrectlyTitle)), + CreateLocalizableResourceString(nameof(DefineDiagnosticMessageCorrectlyMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DefineDiagnosticDescriptionCorrectlyRule = new( + DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId, + CreateLocalizableResourceString(nameof(DefineDiagnosticDescriptionCorrectlyTitle)), + CreateLocalizableResourceString(nameof(DefineDiagnosticDescriptionCorrectlyMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor AddCompilationEndCustomTagRule = new( + DiagnosticIds.AddCompilationEndCustomTagRuleId, + CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagTitle)), + CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + UseLocalizableStringsInDescriptorRule, + ProvideHelpUriInDescriptorRule, + DiagnosticIdMustBeAConstantRule, + DiagnosticIdMustBeInSpecifiedFormatRule, + UseUniqueDiagnosticIdRule, + UseCategoriesFromSpecifiedRangeRule, + AnalyzerCategoryAndIdRangeFileInvalidRule, + ProvideCustomTagsInDescriptorRule, + DoNotUseReservedDiagnosticIdRule, + DeclareDiagnosticIdInAnalyzerReleaseRule, + UpdateDiagnosticIdInAnalyzerReleaseRule, + RemoveUnshippedDeletedDiagnosticIdRule, + RemoveShippedDeletedDiagnosticIdRule, + UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRule, + RemoveDuplicateEntriesForAnalyzerReleaseRule, + RemoveDuplicateEntriesBetweenAnalyzerReleasesRule, + InvalidEntryInAnalyzerReleasesFileRule, + InvalidHeaderInAnalyzerReleasesFileRule, + InvalidUndetectedEntryInAnalyzerReleasesFileRule, + InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule, + EnableAnalyzerReleaseTrackingRule, + DefineDiagnosticTitleCorrectlyRule, + DefineDiagnosticMessageCorrectlyRule, + DefineDiagnosticDescriptionCorrectlyRule, + AddCompilationEndCustomTagRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + if (!compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticDescriptor, out var diagnosticDescriptorType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString, out var localizableResourceType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableResourceString, out var localizableResourceStringType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCompilationEndAnalysisContext, out var compilationEndContextType) || + !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnostic, out var diagnosticType)) + { + return; + } + + // Try read the additional file containing the allowed categories, and corresponding ID ranges. + var checkCategoryAndAllowedIds = TryGetCategoryAndAllowedIdsMap( + compilationContext.Options.AdditionalFiles, + compilationContext.CancellationToken, + out AdditionalText? diagnosticCategoryAndIdRangeText, + out ImmutableDictionary>? categoryAndAllowedIdsMap, + out List? invalidFileDiagnostics); + + // Try read the additional files containing the shipped and unshipped analyzer releases. + var isAnalyzerReleaseTracking = TryGetReleaseTrackingData( + compilationContext.Options.AdditionalFiles, + compilationContext.CancellationToken, + out var shippedData, + out var unshippedData, + out List? invalidReleaseFileEntryDiagnostics); + + PooledLocalizabeStringsConcurrentDictionary? localizableTitles = null; + PooledLocalizabeStringsConcurrentDictionary? localizableMessages = null; + PooledLocalizabeStringsConcurrentDictionary? localizableDescriptions = null; + PooledResourcesDataValueConcurrentDictionary? resourcesDataValueMap = null; + + var analyzeResourceStrings = HasResxAdditionalFiles(compilationContext.Options); + if (analyzeResourceStrings) + { + localizableTitles = PooledLocalizabeStringsConcurrentDictionary.GetInstance(); + localizableMessages = PooledLocalizabeStringsConcurrentDictionary.GetInstance(); + localizableDescriptions = PooledLocalizabeStringsConcurrentDictionary.GetInstance(); + resourcesDataValueMap = PooledResourcesDataValueConcurrentDictionary.GetInstance(); + } + + var idToAnalyzerMap = new ConcurrentDictionary>>(); + var seenRuleIds = PooledConcurrentSet.GetInstance(); + var customTagsMap = PooledFieldToCustomTagsConcurrentDictionary.GetInstance(SymbolEqualityComparer.Default); + compilationContext.RegisterOperationAction(operationAnalysisContext => + { + var fieldInitializer = (IFieldInitializerOperation)operationAnalysisContext.Operation; + if (!TryGetDescriptorCreateMethodAndArguments(fieldInitializer, diagnosticDescriptorType, out var creationMethod, out var creationArguments)) + { + return; + } + + var containingType = operationAnalysisContext.ContainingSymbol.ContainingType; + AnalyzeTitle(operationAnalysisContext, creationArguments, fieldInitializer, containingType, + localizableTitles, resourcesDataValueMap, localizableResourceType, localizableResourceStringType); + AnalyzeMessage(operationAnalysisContext, creationArguments, containingType, + localizableMessages, resourcesDataValueMap, localizableResourceType, localizableResourceStringType); + AnalyzeDescription(operationAnalysisContext, creationArguments, containingType, + localizableDescriptions, resourcesDataValueMap, localizableResourceType, localizableResourceStringType); + AnalyzeHelpLinkUri(operationAnalysisContext, creationArguments, out var helpLink); + AnalyzeCustomTags(operationAnalysisContext, creationArguments, fieldInitializer, customTagsMap); + var (isEnabledByDefault, defaultSeverity) = GetDefaultSeverityAndEnabledByDefault(operationAnalysisContext.Compilation, creationArguments); + + if (!TryAnalyzeCategory(operationAnalysisContext, creationArguments, checkCategoryAndAllowedIds, + diagnosticCategoryAndIdRangeText, categoryAndAllowedIdsMap, out var category, out var allowedIdsInfoList)) + { + allowedIdsInfoList = default; + } + + var analyzerName = fieldInitializer.InitializedFields.First().ContainingType.Name; + AnalyzeRuleId(operationAnalysisContext, creationArguments, + isAnalyzerReleaseTracking, shippedData, unshippedData, seenRuleIds, diagnosticCategoryAndIdRangeText, + category, analyzerName, helpLink, isEnabledByDefault, defaultSeverity, allowedIdsInfoList, idToAnalyzerMap); + + }, OperationKind.FieldInitializer); + + if (analyzeResourceStrings) + { + compilationContext.RegisterSymbolStartAction(context => + { + var symbolToResourceMap = PooledFieldToResourceNameAndFileNameConcurrentDictionary.GetInstance(SymbolEqualityComparer.Default); + context.RegisterOperationAction(context => + { + var fieldInitializer = (IFieldInitializerOperation)context.Operation; + if (TryGetLocalizableResourceStringCreation(fieldInitializer.Value, localizableResourceStringType, + out var nameOfLocalizableResource, out var resourceFileName)) + { + foreach (var field in fieldInitializer.InitializedFields) + { + symbolToResourceMap.TryAdd(field, (nameOfLocalizableResource, resourceFileName)); + } + } + }, OperationKind.FieldInitializer); + + context.RegisterSymbolEndAction(context => + { + RoslynDebug.Assert(localizableTitles != null); + RoslynDebug.Assert(localizableMessages != null); + RoslynDebug.Assert(localizableDescriptions != null); + RoslynDebug.Assert(resourcesDataValueMap != null); + + var namedType = (INamedTypeSymbol)context.Symbol; + + AnalyzeLocalizableStrings(localizableTitles, AnalyzeTitleCore, symbolToResourceMap, namedType, + resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken); + AnalyzeLocalizableStrings(localizableMessages, AnalyzeMessageCore, symbolToResourceMap, namedType, + resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken); + AnalyzeLocalizableStrings(localizableDescriptions, AnalyzeDescriptionCore, symbolToResourceMap, namedType, + resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken); + + symbolToResourceMap.Free(context.CancellationToken); + }); + }, SymbolKind.NamedType); + } + + // Flag descriptor fields that are used to report compilation end diagnostics, + // but do not have the required 'WellKnownDiagnosticTags.CompilationEnd' custom tag. + // See https://github.com/dotnet/roslyn-analyzers/issues/6282 for details. + if (compilationEndContextType.GetMembers(DiagnosticWellKnownNames.ReportDiagnosticName).FirstOrDefault() is IMethodSymbol compilationEndReportDiagnosticMethod) + { + var diagnosticCreateMethods = diagnosticType.GetMembers("Create").OfType() + .Where(m => m.IsPublic() && m.Parameters.Length > 0 && SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, diagnosticDescriptorType)) + .ToImmutableHashSet(SymbolEqualityComparer.Default); + compilationContext.RegisterSymbolStartAction(context => + { + var localsToDescriptorsMap = PooledConcurrentDictionary>.GetInstance(SymbolEqualityComparer.Default); + var localsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + var fieldsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + + context.RegisterOperationAction(context => + { + var invocation = (IInvocationOperation)context.Operation; + if (invocation.Arguments.IsEmpty) + return; + + if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, compilationEndReportDiagnosticMethod) && + invocation.Arguments[0].Value.WalkDownConversion() is ILocalReferenceOperation localReference) + { + // Code pattern such as: + // var diagnostic = Diagnostic.Create(field, ...); + // context.ReportDiagnostic(diagnostic); + localsUsedForCompilationEndReportDiagnostic.Add(localReference.Local); + } + else if (diagnosticCreateMethods.Contains(invocation.TargetMethod)) + { + if (invocation.Arguments[0].Value.WalkDownConversion() is IFieldReferenceOperation fieldReference) + { + // Code pattern such as: + // 'Diagnostic.Create(field, ...)' + if (invocation.GetAncestor(OperationKind.Invocation, + inv => SymbolEqualityComparer.Default.Equals(inv.TargetMethod, compilationEndReportDiagnosticMethod)) is not null) + { + // Code pattern such as: + // 'context.ReportDiagnostic(Diagnostic.Create(field, ...));' + fieldsUsedForCompilationEndReportDiagnostic.Add(fieldReference.Field); + } + else + { + switch (invocation.Parent) + { + case IVariableInitializerOperation variableInitializer: + // Code pattern such as: + // 'var diagnostic = Diagnostic.Create(field, ...);' + if (variableInitializer.GetAncestor(OperationKind.VariableDeclaration) is { } variableDeclaration) + { + foreach (var local in variableDeclaration.GetDeclaredVariables()) + { + AddToLocalsToDescriptorsMap(local, fieldReference.Field, localsToDescriptorsMap); + } + } + + break; + + case ISimpleAssignmentOperation simpleAssignment: + // Code pattern such as: + // 'diagnostic = Diagnostic.Create(field, ...);' + if (simpleAssignment.Target is ILocalReferenceOperation localReferenceTarget) + { + AddToLocalsToDescriptorsMap(localReferenceTarget.Local, fieldReference.Field, localsToDescriptorsMap); + } + + break; + } + } + } + + static void AddToLocalsToDescriptorsMap(ILocalSymbol local, IFieldSymbol field, PooledConcurrentDictionary> localsToDescriptorsMap) + { + localsToDescriptorsMap.AddOrUpdate(local, + addValueFactory: _ => + { + var set = PooledConcurrentSet.GetInstance(SymbolEqualityComparer.Default); + set.Add(field); + return set; + }, + updateValueFactory: (_, fields) => + { + fields.Add(field); + return fields; + }); + } + } + }, OperationKind.Invocation); + + context.RegisterSymbolEndAction(context => + { + foreach (var local in localsUsedForCompilationEndReportDiagnostic) + { + if (localsToDescriptorsMap.TryGetValue(local, out var fields)) + { + foreach (var field in fields) + AnalyzeField(field); + } + } + + foreach (var field in fieldsUsedForCompilationEndReportDiagnostic) + { + AnalyzeField(field); + } + + foreach (var value in localsToDescriptorsMap.Values) + value.Free(context.CancellationToken); + localsToDescriptorsMap.Free(context.CancellationToken); + localsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken); + fieldsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken); + + void AnalyzeField(IFieldSymbol field) + { + if (customTagsMap.TryGetValue(field, out var customTags) && + !customTags.IsDefault && + !customTags.Contains(CompilationEndWellKnownDiagnosticTag) && + !field.Locations.IsEmpty && + field.Locations[0].IsInSource) + { + context.ReportDiagnostic(Diagnostic.Create(AddCompilationEndCustomTagRule, field.Locations[0], field.Name)); + } + } + }); + }, SymbolKind.NamedType); + } + + compilationContext.RegisterCompilationEndAction(compilationEndContext => + { + // Report any invalid additional file diagnostics. + if (invalidFileDiagnostics != null) + { + foreach (var diagnostic in invalidFileDiagnostics) + { + compilationEndContext.ReportDiagnostic(diagnostic); + } + } + + // Report diagnostics for duplicate diagnostic ID used across analyzers. + foreach (var kvp in idToAnalyzerMap) + { + var ruleId = kvp.Key; + var analyzerToDescriptorLocationsMap = kvp.Value; + if (analyzerToDescriptorLocationsMap.Count <= 1) + { + // ID used by a single analyzer. + continue; + } + + ImmutableSortedSet sortedAnalyzerNames = analyzerToDescriptorLocationsMap.Keys.ToImmutableSortedSet(); + var skippedAnalyzerName = sortedAnalyzerNames[0]; + foreach (var analyzerName in sortedAnalyzerNames.Skip(1)) + { + var locations = analyzerToDescriptorLocationsMap[analyzerName]; + foreach (var location in locations) + { + // Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + var diagnostic = Diagnostic.Create(UseUniqueDiagnosticIdRule, location, ruleId, skippedAnalyzerName); + compilationEndContext.ReportDiagnostic(diagnostic); + } + } + } + + // Report analyzer release tracking invalid entry and compilation end diagnostics. + if (isAnalyzerReleaseTracking || invalidReleaseFileEntryDiagnostics != null) + { + RoslynDebug.Assert(shippedData != null); + RoslynDebug.Assert(unshippedData != null); + + ReportAnalyzerReleaseTrackingDiagnostics(invalidReleaseFileEntryDiagnostics, shippedData, unshippedData, seenRuleIds, compilationEndContext); + } + + seenRuleIds.Free(compilationEndContext.CancellationToken); + if (analyzeResourceStrings) + { + RoslynDebug.Assert(localizableTitles != null); + RoslynDebug.Assert(localizableMessages != null); + RoslynDebug.Assert(localizableDescriptions != null); + RoslynDebug.Assert(resourcesDataValueMap != null); + + FreeLocalizableStringsMap(localizableTitles, compilationEndContext.CancellationToken); + FreeLocalizableStringsMap(localizableMessages, compilationEndContext.CancellationToken); + FreeLocalizableStringsMap(localizableDescriptions, compilationEndContext.CancellationToken); + resourcesDataValueMap.Free(compilationEndContext.CancellationToken); + } + + customTagsMap.Free(compilationEndContext.CancellationToken); + }); + }); + + static void FreeLocalizableStringsMap(PooledLocalizabeStringsConcurrentDictionary localizableStrings, CancellationToken cancellationToken) + { + foreach (var builder in localizableStrings.Values) + { + builder.Free(cancellationToken); + } + + localizableStrings.Free(cancellationToken); + } + } + + private static bool TryGetDescriptorCreateMethodAndArguments( + IFieldInitializerOperation fieldInitializer, + INamedTypeSymbol diagnosticDescriptorType, + [NotNullWhen(returnValue: true)] out IMethodSymbol? creationMethod, + [NotNullWhen(returnValue: true)] out ImmutableArray creationArguments) + { + (creationMethod, creationArguments) = fieldInitializer.Value.WalkDownConversion() switch + { + IObjectCreationOperation objectCreation when IsDescriptorConstructor(objectCreation.Constructor) + => (objectCreation.Constructor, objectCreation.Arguments), + IInvocationOperation invocation when IsCreateHelper(invocation.TargetMethod) + => (invocation.TargetMethod, invocation.Arguments), + _ => default + }; + + return creationMethod != null; + + bool IsDescriptorConstructor(IMethodSymbol? method) + => SymbolEqualityComparer.Default.Equals(method?.ContainingType, diagnosticDescriptorType); + + // Heuristic to identify helper methods to create DiagnosticDescriptor: + // "A method invocation that returns 'DiagnosticDescriptor' and has a first string parameter named 'id'" + bool IsCreateHelper(IMethodSymbol method) + => SymbolEqualityComparer.Default.Equals(method.ReturnType, diagnosticDescriptorType) && + !method.Parameters.IsEmpty && + method.Parameters[0].Name == DiagnosticIdParameterName && + method.Parameters[0].Type.SpecialType == SpecialType.System_String; + } + + private static bool TryGetLocalizableResourceStringCreation( + IOperation operation, + INamedTypeSymbol localizableResourceStringType, + [NotNullWhen(returnValue: true)] out string? nameOfLocalizableResource, + [NotNullWhen(returnValue: true)] out string? resourceFileName) + { + return TryGetConstructorCreation(out nameOfLocalizableResource, out resourceFileName) || + TryGetHelperMethodCreation(out nameOfLocalizableResource, out resourceFileName); + + // Local functions + + // Attempts to get the resource and file name for the creation of a localizable resource string using the + // constructor on LocalizableResourceString + bool TryGetConstructorCreation([NotNullWhen(true)] out string? nameOfLocalizableResource, [NotNullWhen(true)] out string? resourceFileName) + { + if (operation.WalkDownConversion() is IObjectCreationOperation objectCreation && + SymbolEqualityComparer.Default.Equals(objectCreation.Constructor?.ContainingType, localizableResourceStringType) && + objectCreation.Arguments.Length >= 3 && + objectCreation.Arguments.GetArgumentForParameterAtIndex(0) is { } firstParamArgument && + firstParamArgument.Parameter?.Type.SpecialType == SpecialType.System_String && + firstParamArgument.Value.ConstantValue.HasValue && + firstParamArgument.Value.ConstantValue.Value is string nameOfResource && + objectCreation.Arguments.GetArgumentForParameterAtIndex(2) is { } thirdParamArgument && + thirdParamArgument.Value is ITypeOfOperation typeOfOperation && + typeOfOperation.TypeOperand is { } typeOfType) + { + nameOfLocalizableResource = nameOfResource; + resourceFileName = typeOfType.Name; + return true; + } + + nameOfLocalizableResource = null; + resourceFileName = null; + return false; + } + + // Attempts to get the resource and file name for the creation of a localizable resource string using a + // helper method on the resource class. For an operation to be considered a helper method invocation, it must + // - Be an invocation of a static method + // - Method must have return type 'LocalizableResourceString' + // - Method must have single 'string' parameter + // - Argument must be a compile-time constant (typically a nameof operation on one of the resource class's properties). + bool TryGetHelperMethodCreation([NotNullWhen(true)] out string? nameOfLocalizableResource, [NotNullWhen(true)] out string? resourceFileName) + { + if (operation.WalkDownConversion() is IInvocationOperation invocation && + invocation.TargetMethod.ReturnType.Equals(localizableResourceStringType) && + invocation.Arguments.Length == 1 && + invocation.Arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String && + invocation.Arguments[0].Value.ConstantValue.HasValue && + invocation.Arguments[0].Value.ConstantValue.Value is string nameOfResource) + { + nameOfLocalizableResource = nameOfResource; + resourceFileName = invocation.TargetMethod.ContainingType.Name; + return true; + } + + nameOfLocalizableResource = null; + resourceFileName = null; + return false; + } + } + + private static void AnalyzeTitle( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + IFieldInitializerOperation creation, + INamedTypeSymbol containingType, + PooledLocalizabeStringsConcurrentDictionary? localizableTitles, + PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap, + INamedTypeSymbol localizableStringType, + INamedTypeSymbol localizableResourceStringType) + { + IArgumentOperation? titleArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("title", StringComparison.OrdinalIgnoreCase) == true); + if (titleArgument != null) + { + if (titleArgument.Parameter?.Type.SpecialType == SpecialType.System_String) + { + operationAnalysisContext.ReportDiagnostic(creation.Value.CreateDiagnostic(UseLocalizableStringsInDescriptorRule, WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString)); + } + + AnalyzeDescriptorArgument(operationAnalysisContext, titleArgument, + AnalyzeTitleCore, containingType, localizableTitles, resourceDataValueMap, + localizableStringType, localizableResourceStringType); + } + } + + private static void AnalyzeTitleCore(string title, IArgumentOperation argumentOperation, Location fixLocation, Action reportDiagnostic) + { + var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(title); + if (hasLeadingOrTrailingWhitespaces) + { + title = RemoveLeadingAndTrailingWhitespaces(title); + } + + var isMultiSentences = IsMultiSentences(title); + var endsWithPeriod = EndsWithPeriod(title); + var containsLineReturn = ContainsLineReturn(title); + + if (isMultiSentences || endsWithPeriod || containsLineReturn || hasLeadingOrTrailingWhitespaces) + { + // Leading and trailing spaces were already fixed + var fixedTitle = endsWithPeriod ? RemoveTrailingPeriod(title) : title; + fixedTitle = isMultiSentences ? FixMultiSentences(fixedTitle) : fixedTitle; + fixedTitle = containsLineReturn ? FixLineReturns(fixedTitle, allowMultisentences: false) : fixedTitle; + + ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticTitleCorrectlyRule, + argumentOperation, fixedTitle, fixLocation, reportDiagnostic); + } + } + + private static void ReportDefineDiagnosticArgumentCorrectlyDiagnostic( + DiagnosticDescriptor descriptor, + IArgumentOperation argumentOperation, + string fixValue, + Location fixLocation, + Action reportDiagnostic) + { + // Additional location in an additional document does not seem to be preserved + // from analyzer to code fix due to a Roslyn bug: https://github.com/dotnet/roslyn/issues/46377 + // We workaround this bug by passing additional document file path and location span as strings. + + var additionalLocations = ImmutableArray.Empty; + var properties = ImmutableDictionary.Empty.Add(DefineDescriptorArgumentCorrectlyFixValue, fixValue); + if (fixLocation.IsInSource) + { + additionalLocations = additionalLocations.Add(fixLocation); + } + else + { + var span = fixLocation.SourceSpan; + var locationInfo = $"{span.Start}{AdditionalDocumentLocationInfoSeparator}{span.Length}{AdditionalDocumentLocationInfoSeparator}{fixLocation.GetLineSpan().Path}"; + properties = properties.Add(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo, locationInfo); + } + + reportDiagnostic(argumentOperation.CreateDiagnostic(descriptor, additionalLocations, properties)); + } + + internal static bool TryGetAdditionalDocumentLocationInfo(Diagnostic diagnostic, + [NotNullWhen(returnValue: true)] out string? filePath, + [NotNullWhen(returnValue: true)] out TextSpan? fileSpan) + { + Debug.Assert(diagnostic.Id is DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId or + DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId or + DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId); + + filePath = null; + fileSpan = null; + if (!diagnostic.Properties.TryGetValue(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo, out var locationInfo) + || locationInfo is null) + { + return false; + } + + var parts = locationInfo.Split(new[] { AdditionalDocumentLocationInfoSeparator }, StringSplitOptions.None); + if (parts.Length != 3 || + !int.TryParse(parts[0], out var spanSpart) || + !int.TryParse(parts[1], out var spanLength)) + { + return false; + } + + fileSpan = new TextSpan(spanSpart, spanLength); + filePath = parts[2]; + return !string.IsNullOrEmpty(filePath); + } + + private static void AnalyzeMessage( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + INamedTypeSymbol containingType, + PooledLocalizabeStringsConcurrentDictionary? localizableMessages, + PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap, + INamedTypeSymbol localizableStringType, + INamedTypeSymbol localizableResourceStringType) + { + var messageArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("messageFormat", StringComparison.OrdinalIgnoreCase) == true); + if (messageArgument != null) + { + AnalyzeDescriptorArgument(operationAnalysisContext, messageArgument, + AnalyzeMessageCore, containingType, localizableMessages, resourceDataValueMap, + localizableStringType, localizableResourceStringType); + } + } + + private static void AnalyzeMessageCore(string message, IArgumentOperation argumentOperation, Location fixLocation, Action reportDiagnostic) + { + var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(message); + if (hasLeadingOrTrailingWhitespaces) + { + message = RemoveLeadingAndTrailingWhitespaces(message); + } + + var isMultiSentences = IsMultiSentences(message); + var endsWithPeriod = EndsWithPeriod(message); + var containsLineReturn = ContainsLineReturn(message); + + if (isMultiSentences ^ endsWithPeriod || containsLineReturn || hasLeadingOrTrailingWhitespaces) + { + // Leading and trailing spaces were already fixed + var fixedMessage = containsLineReturn ? FixLineReturns(message, allowMultisentences: true) : message; + isMultiSentences = IsMultiSentences(fixedMessage); + endsWithPeriod = EndsWithPeriod(fixedMessage); + + if (isMultiSentences ^ endsWithPeriod) + { + fixedMessage = endsWithPeriod ? RemoveTrailingPeriod(fixedMessage) : fixedMessage + "."; + } + + ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticMessageCorrectlyRule, + argumentOperation, fixedMessage, fixLocation, reportDiagnostic); + } + } + + private static void AnalyzeDescription( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + INamedTypeSymbol containingType, + PooledLocalizabeStringsConcurrentDictionary? localizableDescriptions, + PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap, + INamedTypeSymbol localizableStringType, + INamedTypeSymbol localizableResourceStringType) + { + IArgumentOperation? descriptionArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("description", StringComparison.OrdinalIgnoreCase) == true); + if (descriptionArgument != null) + { + AnalyzeDescriptorArgument(operationAnalysisContext, descriptionArgument, + AnalyzeDescriptionCore, containingType, localizableDescriptions, resourceDataValueMap, + localizableStringType, localizableResourceStringType); + } + } + + private static void AnalyzeDescriptionCore(string description, IArgumentOperation argumentOperation, Location fixLocation, Action reportDiagnostic) + { + var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(description); + if (hasLeadingOrTrailingWhitespaces) + { + description = RemoveLeadingAndTrailingWhitespaces(description); + } + + var endsWithPunctuation = EndsWithPunctuation(description); + + if (!endsWithPunctuation || hasLeadingOrTrailingWhitespaces) + { + var fixedDescription = !endsWithPunctuation ? description + "." : description; + + ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticDescriptionCorrectlyRule, + argumentOperation, fixedDescription, fixLocation, reportDiagnostic); + } + } + + private static void AnalyzeDescriptorArgument( + OperationAnalysisContext operationAnalysisContext, + IArgumentOperation argument, + Action> analyzeStringValueCore, + INamedTypeSymbol containingType, + PooledLocalizabeStringsConcurrentDictionary? localizableStringsMap, + PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap, + INamedTypeSymbol localizableStringType, + INamedTypeSymbol localizableResourceStringType) + { + if (TryGetNonEmptyConstantStringValue(argument, out var argumentValue, out var argumentValueLocation)) + { + analyzeStringValueCore(argumentValue, argument, argumentValueLocation, operationAnalysisContext.ReportDiagnostic); + } + else if (localizableStringsMap != null && + SymbolEqualityComparer.Default.Equals(argument.Parameter?.Type, localizableStringType)) + { + RoslynDebug.Assert(resourceDataValueMap != null); + + if (TryGetLocalizableResourceStringCreation(argument.Value, localizableResourceStringType, + out var nameOfLocalizableResource, out var resourceFileName)) + { + AnalyzeLocalizableDescriptorArgument(analyzeStringValueCore, nameOfLocalizableResource, resourceFileName, + argument, resourceDataValueMap, operationAnalysisContext.Options, + operationAnalysisContext.ReportDiagnostic, operationAnalysisContext.CancellationToken); + } + else + { + var value = argument.Value.WalkDownConversion(); + if (value is IFieldReferenceOperation fieldReference && + fieldReference.Field.Type.DerivesFrom(localizableStringType, baseTypesOnly: true)) + { + var builder = localizableStringsMap.GetOrAdd(containingType, _ => PooledConcurrentSet<(IFieldSymbol, IArgumentOperation)>.GetInstance()); + builder.Add((fieldReference.Field, argument)); + } + } + } + } + + private static void AnalyzeLocalizableDescriptorArgument( + Action> analyzeStringValueCore, + string nameOfLocalizableResource, + string resourceFileName, + IArgumentOperation argument, + PooledResourcesDataValueConcurrentDictionary resourceDataValueMap, + AnalyzerOptions options, + Action reportDiagnostic, + CancellationToken cancellationToken) + { + var map = GetOrCreateResourceMap(options, resourceFileName, resourceDataValueMap, cancellationToken); + if (map.TryGetValue(nameOfLocalizableResource, out var resourceStringTuple)) + { + analyzeStringValueCore(resourceStringTuple.value, argument, resourceStringTuple.location, reportDiagnostic); + } + } + + private static void AnalyzeLocalizableStrings( + PooledLocalizabeStringsConcurrentDictionary localizableStringsMap, + Action> analyzeLocalizableStringValueCore, + PooledFieldToResourceNameAndFileNameConcurrentDictionary symbolToResourceMap, + INamedTypeSymbol namedType, + PooledResourcesDataValueConcurrentDictionary resourceDataValueMap, + AnalyzerOptions options, + Action reportDiagnostic, + CancellationToken cancellationToken) + { + if (localizableStringsMap.TryRemove(namedType, out var localizableFieldsWithOriginalArguments)) + { + foreach (var (field, argument) in localizableFieldsWithOriginalArguments) + { + if (symbolToResourceMap.TryGetValue(field, out var resourceTuple)) + { + AnalyzeLocalizableDescriptorArgument(analyzeLocalizableStringValueCore, resourceTuple.nameOfResource, resourceTuple.resourceFileName, + argument, resourceDataValueMap, options, reportDiagnostic, cancellationToken); + } + } + + localizableFieldsWithOriginalArguments.Dispose(); + } + } + + private static bool TryGetNonEmptyConstantStringValue( + IArgumentOperation argumentOperation, + [NotNullWhen(true)] out string? value, + [NotNullWhen(true)] out Location? valueLocation) + { + value = null; + valueLocation = null; + + IOperation valueOperation; + var argumentValueOperation = argumentOperation.Value.WalkDownConversion(); + if (argumentValueOperation is ILiteralOperation literalOperation) + { + valueOperation = literalOperation; + } + else if (argumentValueOperation is IFieldReferenceOperation fieldReferenceOperation && + fieldReferenceOperation.Syntax.SyntaxTree == argumentValueOperation.Syntax.SyntaxTree && + fieldReferenceOperation.Field.DeclaringSyntaxReferences.Length == 1 && + fieldReferenceOperation.Field.DeclaringSyntaxReferences[0].GetSyntax() is { } fieldDeclaration && + fieldDeclaration.SyntaxTree == argumentValueOperation.Syntax.SyntaxTree && + GetFieldInitializer(fieldDeclaration, argumentValueOperation.SemanticModel!) is { } fieldInitializer && + fieldInitializer.Value.WalkDownConversion() is ILiteralOperation fieldInitializerLiteral) + { + valueOperation = fieldInitializerLiteral; + } + else + { + valueOperation = argumentValueOperation; + } + + if (!TryGetNonEmptyConstantStringValueCore(valueOperation, out var literalValue)) + { + return false; + } + + value = literalValue; + valueLocation = valueOperation.Syntax.GetLocation(); + return true; + + static IFieldInitializerOperation? GetFieldInitializer(SyntaxNode fieldDeclaration, SemanticModel model) + { + if (fieldDeclaration.Language == LanguageNames.VisualBasic) + { + // For VB, the field initializer is on the parent node. + fieldDeclaration = fieldDeclaration.Parent!; + } + + foreach (var node in fieldDeclaration.DescendantNodes()) + { + if (model.GetOperation(node) is IFieldInitializerOperation initializer) + { + return initializer; + } + } + + return null; + } + } + + private static bool TryGetNonEmptyConstantStringValueCore(IOperation operation, [NotNullWhen(returnValue: true)] out string? literalValue) + { + if (operation.ConstantValue.HasValue && + operation.ConstantValue.Value is string value && + !string.IsNullOrEmpty(value)) + { + literalValue = value; + return true; + } + + literalValue = null; + return false; + } + + // Assumes that a string is a multi-sentences if it contains a period followed by a whitespace ('. '). + private const string MultiSentenceSeparator = ". "; + + private static bool IsMultiSentences(string s) + => s.Contains(MultiSentenceSeparator); + + private static string FixMultiSentences(string s) + { + Debug.Assert(IsMultiSentences(s)); + var index = s.IndexOf(MultiSentenceSeparator, StringComparison.OrdinalIgnoreCase); + return s[..index]; + } + + private static bool EndsWithPeriod(string s) + => s[^1] == '.'; + + private static string RemoveTrailingPeriod(string s) + { + Debug.Assert(EndsWithPeriod(s)); + return s[0..^1]; + } + + private static bool ContainsLineReturn(string s) + => s.Contains("\r") || s.Contains("\n"); + + private static string FixLineReturns(string s, bool allowMultisentences) + { + Debug.Assert(ContainsLineReturn(s)); + + var parts = s.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); + if (!allowMultisentences) + { + return parts[0]; + } + + var builder = new StringBuilder(); + for (var i = 0; i < parts.Length; i++) + { + var part = parts[i]; + if (!EndsWithPeriod(part)) + { + part += "."; + } + + if (part.TrimEnd().Length == part.Length && + i < parts.Length - 1) + { + part += " "; + } + + builder.Append(part); + } + + return builder.ToString(); + } + + private static bool EndsWithPunctuation(string s) + { + var lastChar = s[^1]; + + return lastChar.Equals('.') || lastChar.Equals('!') || lastChar.Equals('?'); + } + + private static bool HasLeadingOrTrailingWhitespaces(string s) + => s.Trim().Length != s.Length; + + private static string RemoveLeadingAndTrailingWhitespaces(string s) + { + Debug.Assert(HasLeadingOrTrailingWhitespaces(s)); + return s.Trim(); + } + + private static void AnalyzeHelpLinkUri( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + out string? helpLink) + { + helpLink = null; + + // Find the matching argument for helpLinkUri + foreach (var argument in creationArguments) + { + if (argument.Parameter?.Name.Equals(HelpLinkUriParameterName, StringComparison.OrdinalIgnoreCase) == true) + { + if (argument.Value.ConstantValue.HasValue) + { + helpLink = argument.Value.ConstantValue.Value as string; + if (helpLink == null) + { + Diagnostic diagnostic = argument.CreateDiagnostic(ProvideHelpUriInDescriptorRule); + operationAnalysisContext.ReportDiagnostic(diagnostic); + } + } + + return; + } + } + } + + private static void AnalyzeCustomTags( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + IFieldInitializerOperation fieldInitializerOperation, + PooledFieldToCustomTagsConcurrentDictionary customTagsMap) + { + // Default to indicate unknown set of custom tags. + ImmutableArray customTags = default; + + try + { + // Find the matching argument for customTags + var argument = creationArguments.FirstOrDefault( + a => a.Parameter?.Name.Equals(CustomTagsParameterName, StringComparison.OrdinalIgnoreCase) == true); + if (argument is null || + argument.Value is not IArrayCreationOperation arrayCreation || + arrayCreation.DimensionSizes.Length != 1) + { + return; + } + + if (arrayCreation.DimensionSizes[0].ConstantValue.HasValue && + arrayCreation.DimensionSizes[0].ConstantValue.Value is int size && + size == 0) + { + Diagnostic diagnostic = argument.CreateDiagnostic(ProvideCustomTagsInDescriptorRule); + operationAnalysisContext.ReportDiagnostic(diagnostic); + + customTags = ImmutableArray.Empty; + } + else if (arrayCreation.Initializer is IArrayInitializerOperation arrayInitializer && + arrayInitializer.ElementValues.All(element => element.ConstantValue.HasValue && element.ConstantValue.Value is string)) + { + customTags = arrayInitializer.ElementValues.Select(element => (string)element.ConstantValue.Value!).ToImmutableArray(); + } + } + finally + { + AddCustomTags(customTags, fieldInitializerOperation, customTagsMap); + } + + static void AddCustomTags( + ImmutableArray customTags, + IFieldInitializerOperation fieldInitializerOperation, + PooledFieldToCustomTagsConcurrentDictionary customTagsMap) + { + foreach (var field in fieldInitializerOperation.InitializedFields) + { + customTagsMap[field] = customTags; + } + } + } + + private static (bool? isEnabledByDefault, DiagnosticSeverity? defaultSeverity) GetDefaultSeverityAndEnabledByDefault(Compilation compilation, ImmutableArray creationArguments) + { + var diagnosticSeverityType = compilation.GetOrCreateTypeByMetadataName(typeof(DiagnosticSeverity).FullName); + var ruleLevelType = compilation.GetOrCreateTypeByMetadataName(typeof(RuleLevel).FullName); + + bool? isEnabledByDefault = null; + DiagnosticSeverity? defaultSeverity = null; + + foreach (var argument in creationArguments) + { + switch (argument.Parameter?.Name) + { + case IsEnabledByDefaultParameterName: + if (argument.Value.ConstantValue.HasValue && + argument.Value.ConstantValue.Value is bool value) + { + isEnabledByDefault = value; + } + + break; + + case DefaultSeverityParameterName: + if (argument.Value is IFieldReferenceOperation fieldReference && + SymbolEqualityComparer.Default.Equals(fieldReference.Field.ContainingType, diagnosticSeverityType) && + Enum.TryParse(fieldReference.Field.Name, out DiagnosticSeverity parsedSeverity)) + { + defaultSeverity = parsedSeverity; + } + + break; + + case RuleLevelParameterName: + if (ruleLevelType != null && + argument.Value is IFieldReferenceOperation fieldReference2 && + SymbolEqualityComparer.Default.Equals(fieldReference2.Field.ContainingType, ruleLevelType) && + Enum.TryParse(fieldReference2.Field.Name, out RuleLevel parsedRuleLevel)) + { + switch (parsedRuleLevel) + { + case RuleLevel.BuildWarning: + defaultSeverity = DiagnosticSeverity.Warning; + isEnabledByDefault = true; + break; + + case RuleLevel.IdeSuggestion: + defaultSeverity = DiagnosticSeverity.Info; + isEnabledByDefault = true; + break; + + case RuleLevel.IdeHidden_BulkConfigurable: + defaultSeverity = DiagnosticSeverity.Hidden; + isEnabledByDefault = true; + break; + + case RuleLevel.Disabled: + case RuleLevel.CandidateForRemoval: + isEnabledByDefault = false; + break; + } + + return (isEnabledByDefault, defaultSeverity); + } + + break; + } + } + + if (isEnabledByDefault == false) + { + defaultSeverity = null; + } + + return (isEnabledByDefault, defaultSeverity); + } + + private static void AnalyzeRuleId( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + bool isAnalyzerReleaseTracking, + ReleaseTrackingData? shippedData, + ReleaseTrackingData? unshippedData, + PooledConcurrentSet seenRuleIds, + AdditionalText? diagnosticCategoryAndIdRangeText, + string? category, + string analyzerName, + string? helpLink, + bool? isEnabledByDefault, + DiagnosticSeverity? defaultSeverity, + ImmutableArray<(string? prefix, int start, int end)> allowedIdsInfoListOpt, + ConcurrentDictionary>> idToAnalyzerMap) + { + var analyzer = ((IFieldSymbol)operationAnalysisContext.ContainingSymbol).ContainingType.OriginalDefinition; + string? ruleId = null; + foreach (var argument in creationArguments) + { + if (argument.Parameter?.Name.Equals(DiagnosticIdParameterName, StringComparison.OrdinalIgnoreCase) == true) + { + // Check if diagnostic ID is a constant string. + if (argument.Value.ConstantValue.HasValue && + argument.Value.Type != null && + argument.Value.Type.SpecialType == SpecialType.System_String && + argument.Value.ConstantValue.Value is string value) + { + ruleId = value; + seenRuleIds.Add(ruleId); + + var location = argument.Value.Syntax.GetLocation(); + static string GetAnalyzerName(INamedTypeSymbol a) => a.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + + // Factory methods to track declaration locations for every analyzer rule ID. + ConcurrentBag AddLocationFactory(string analyzerName) + => new() { location }; + + ConcurrentBag UpdateLocationsFactory(string analyzerName, ConcurrentBag bag) + { + bag.Add(location); + return bag; + } + + ConcurrentDictionary> AddNamedTypeFactory(string r) + { + var dict = new ConcurrentDictionary>(); + dict.AddOrUpdate( + key: GetAnalyzerName(analyzer), + addValueFactory: AddLocationFactory, + updateValueFactory: UpdateLocationsFactory); + return dict; + } + + ConcurrentDictionary> UpdateNamedTypeFactory(string r, ConcurrentDictionary> existingValue) + { + existingValue.AddOrUpdate( + key: GetAnalyzerName(analyzer), + addValueFactory: AddLocationFactory, + updateValueFactory: UpdateLocationsFactory); + return existingValue; + } + + idToAnalyzerMap.AddOrUpdate( + key: ruleId, + addValueFactory: AddNamedTypeFactory, + updateValueFactory: UpdateNamedTypeFactory); + + if (IsReservedDiagnosticId(ruleId, operationAnalysisContext.Compilation.AssemblyName)) + { + operationAnalysisContext.ReportDiagnostic(argument.Value.Syntax.CreateDiagnostic(DoNotUseReservedDiagnosticIdRule, ruleId)); + } + + // If we have an additional file specifying required range and/or format for the ID, validate the ID. + if (!allowedIdsInfoListOpt.IsDefault) + { + AnalyzeAllowedIdsInfoList(ruleId, argument, diagnosticCategoryAndIdRangeText, category, allowedIdsInfoListOpt, operationAnalysisContext.ReportDiagnostic); + } + + // If we have an additional file specifying required range and/or format for the ID, validate the ID. + if (isAnalyzerReleaseTracking) + { + RoslynDebug.Assert(shippedData != null); + RoslynDebug.Assert(unshippedData != null); + + AnalyzeAnalyzerReleases(ruleId, argument, category, analyzerName, helpLink, isEnabledByDefault, + defaultSeverity, shippedData, unshippedData, operationAnalysisContext.ReportDiagnostic); + } + else if (shippedData == null && unshippedData == null) + { + var diagnostic = argument.CreateDiagnostic(EnableAnalyzerReleaseTrackingRule, ruleId); + operationAnalysisContext.ReportDiagnostic(diagnostic); + } + } + else + { + // Diagnostic Id for rule '{0}' must be a non-null constant. + string arg1 = ((IFieldInitializerOperation)operationAnalysisContext.Operation).InitializedFields.Single().Name; + var diagnostic = argument.Value.CreateDiagnostic(DiagnosticIdMustBeAConstantRule, arg1); + operationAnalysisContext.ReportDiagnostic(diagnostic); + } + + return; + } + } + } + + private static bool IsReservedDiagnosticId(string ruleId, string? assemblyName) + { + if (ruleId.Length < 3) + { + return false; + } + + var isCARule = ruleId.StartsWith("CA", StringComparison.Ordinal); + + if (!isCARule && + !ruleId.StartsWith("CS", StringComparison.Ordinal) && + !ruleId.StartsWith("BC", StringComparison.Ordinal)) + { + return false; + } + + if (!ruleId[2..].All(char.IsDigit)) + { + return false; + } + + if (!isCARule) + { + // This is a reserved compiler diagnostic ID (CS or BC prefix) + return true; + } + + if (assemblyName is null) + { + // This is a reserved code analysis ID (CA prefix) being reported from an unspecified assembly + return true; + } + + return !CADiagnosticIdAllowedAssemblies.Contains(assemblyName); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_IdRangeAndCategoryValidation.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_IdRangeAndCategoryValidation.cs new file mode 100644 index 0000000000000..7a0c75574858c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_IdRangeAndCategoryValidation.cs @@ -0,0 +1,385 @@ +// 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.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1018 + /// RS1020 + /// RS1021 + /// + public sealed partial class DiagnosticDescriptorCreationAnalyzer + { + private const string DiagnosticCategoryAndIdRangeFile = "DiagnosticCategoryAndIdRanges.txt"; + private static readonly (string? prefix, int start, int end) s_defaultAllowedIdsInfo = (null, -1, -1); + + public static readonly DiagnosticDescriptor DiagnosticIdMustBeInSpecifiedFormatRule = new( + DiagnosticIds.DiagnosticIdMustBeInSpecifiedFormatRuleId, + CreateLocalizableResourceString(nameof(DiagnosticIdMustBeInSpecifiedFormatTitle)), + CreateLocalizableResourceString(nameof(DiagnosticIdMustBeInSpecifiedFormatMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DiagnosticIdMustBeInSpecifiedFormatDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor UseCategoriesFromSpecifiedRangeRule = new( + DiagnosticIds.UseCategoriesFromSpecifiedRangeRuleId, + CreateLocalizableResourceString(nameof(UseCategoriesFromSpecifiedRangeTitle)), + CreateLocalizableResourceString(nameof(UseCategoriesFromSpecifiedRangeMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(UseCategoriesFromSpecifiedRangeDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor AnalyzerCategoryAndIdRangeFileInvalidRule = new( + DiagnosticIds.AnalyzerCategoryAndIdRangeFileInvalidRuleId, + CreateLocalizableResourceString(nameof(AnalyzerCategoryAndIdRangeFileInvalidTitle)), + CreateLocalizableResourceString(nameof(AnalyzerCategoryAndIdRangeFileInvalidMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AnalyzerCategoryAndIdRangeFileInvalidDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static void AnalyzeAllowedIdsInfoList( + string ruleId, + IArgumentOperation argument, + AdditionalText? additionalText, + string? category, + ImmutableArray<(string? prefix, int start, int end)> allowedIdsInfoList, + Action addDiagnostic) + { + RoslynDebug.Assert(!allowedIdsInfoList.IsDefaultOrEmpty); + RoslynDebug.Assert(category != null); + RoslynDebug.Assert(additionalText != null); + + var foundMatch = false; + static bool ShouldValidateRange((string? prefix, int start, int end) range) + => range.start >= 0 && range.end >= 0; + + // Check if ID matches any one of the required ranges. + foreach (var allowedIds in allowedIdsInfoList) + { + RoslynDebug.Assert(allowedIds.prefix != null); + + if (ruleId.StartsWith(allowedIds.prefix, StringComparison.Ordinal)) + { + if (ShouldValidateRange(allowedIds)) + { + var suffix = ruleId[allowedIds.prefix.Length..]; + if (int.TryParse(suffix, out int ruleIdInt) && + ruleIdInt >= allowedIds.start && + ruleIdInt <= allowedIds.end) + { + foundMatch = true; + break; + } + } + else + { + foundMatch = true; + break; + } + } + } + + if (!foundMatch) + { + // Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}'. + string arg1 = ruleId; + string arg2 = category; + var arg3 = new StringBuilder(); + foreach (var range in allowedIdsInfoList) + { + if (arg3.Length != 0) + { + arg3.Append(", "); + } + + if (ShouldValidateRange(range)) + { + arg3.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}-{0}{2}", range.prefix, range.start, range.end); + } + else + { + arg3.AppendFormat(CultureInfo.InvariantCulture, "{0}XXXX", range.prefix); + } + } + + string arg4 = Path.GetFileName(additionalText.Path); + var diagnostic = argument.Value.CreateDiagnostic(DiagnosticIdMustBeInSpecifiedFormatRule, arg1, arg2, arg3.ToString(), arg4); + addDiagnostic(diagnostic); + } + } + + private static bool TryAnalyzeCategory( + OperationAnalysisContext operationAnalysisContext, + ImmutableArray creationArguments, + bool checkCategoryAndAllowedIds, + AdditionalText? additionalText, + ImmutableDictionary>? categoryAndAllowedIdsInfoMap, + [NotNullWhen(returnValue: true)] out string? category, + out ImmutableArray<(string? prefix, int start, int end)> allowedIdsInfoList) + { + category = null; + allowedIdsInfoList = default; + foreach (var argument in creationArguments) + { + if (argument.Parameter?.Name.Equals(CategoryParameterName, StringComparison.Ordinal) == true) + { + // Check if the category argument is a constant or refers to a string field. + if (argument.Value.ConstantValue.HasValue) + { + if (argument.Value.Type != null && + argument.Value.Type.SpecialType == SpecialType.System_String && + argument.Value.ConstantValue.Value is string value) + { + category = value; + } + } + else if (argument.Value is IFieldReferenceOperation fieldReference && + fieldReference.Field.Type.SpecialType == SpecialType.System_String) + { + category = fieldReference.ConstantValue.HasValue && fieldReference.ConstantValue.Value is string value ? value : fieldReference.Field.Name; + } + + if (!checkCategoryAndAllowedIds) + { + return category != null; + } + + // Check if the category is one of the allowed values. + RoslynDebug.Assert(categoryAndAllowedIdsInfoMap != null); + RoslynDebug.Assert(additionalText != null); + + if (category != null && + categoryAndAllowedIdsInfoMap.TryGetValue(category, out allowedIdsInfoList)) + { + return true; + } + + // Category '{0}' is not from the allowed categories specified in the file '{1}'. + string arg1 = category ?? ""; + string arg2 = Path.GetFileName(additionalText.Path); + var diagnostic = argument.Value.CreateDiagnostic(UseCategoriesFromSpecifiedRangeRule, arg1, arg2); + operationAnalysisContext.ReportDiagnostic(diagnostic); + return false; + } + } + + return false; + } + + private static bool TryGetCategoryAndAllowedIdsMap( + ImmutableArray additionalFiles, + CancellationToken cancellationToken, + [NotNullWhen(returnValue: true)] out AdditionalText? additionalText, + [NotNullWhen(returnValue: true)] out ImmutableDictionary>? categoryAndAllowedIdsMap, + out List? invalidFileDiagnostics) + { + invalidFileDiagnostics = null; + categoryAndAllowedIdsMap = null; + + // Parse the additional file with allowed diagnostic categories and corresponding ID range. + // Bail out if there is no such additional file or it contains at least one invalid entry. + additionalText = TryGetCategoryAndAllowedIdsInfoFile(additionalFiles, cancellationToken); + return additionalText != null && + TryParseCategoryAndAllowedIdsInfoFile(additionalText, cancellationToken, out categoryAndAllowedIdsMap, out invalidFileDiagnostics); + } + + private static AdditionalText? TryGetCategoryAndAllowedIdsInfoFile(ImmutableArray additionalFiles, CancellationToken cancellationToken) + { + StringComparer comparer = StringComparer.Ordinal; + foreach (AdditionalText textFile in additionalFiles) + { + cancellationToken.ThrowIfCancellationRequested(); + + string fileName = Path.GetFileName(textFile.Path); + if (comparer.Equals(fileName, DiagnosticCategoryAndIdRangeFile)) + { + return textFile; + } + } + + return null; + } + + private static bool TryParseCategoryAndAllowedIdsInfoFile( + AdditionalText additionalText, + CancellationToken cancellationToken, + [NotNullWhen(returnValue: true)] out ImmutableDictionary>? categoryAndAllowedIdsInfoMap, + out List? invalidFileDiagnostics) + { + // Parse the additional file with allowed diagnostic categories and corresponding ID range. + // FORMAT: + // 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + + categoryAndAllowedIdsInfoMap = null; + invalidFileDiagnostics = null; + + var builder = ImmutableDictionary.CreateBuilder>(); + var lines = additionalText.GetTextOrEmpty(cancellationToken).Lines; + foreach (var line in lines) + { + var contents = line.ToString(); + if (contents.Length == 0 || contents.StartsWith("#", StringComparison.Ordinal)) + { + // Ignore empty lines and comments. + continue; + } + + var parts = contents.Split(':'); + for (int i = 0; i < parts.Length; i++) + { + parts[i] = parts[i].Trim(); + } + + var isInvalidLine = false; + string category = parts[0]; + if (parts.Length > 2 || // We allow only 0 or 1 ':' separator in the line. + category.Any(char.IsWhiteSpace) || // We do not allow white spaces in category name. + builder.ContainsKey(category)) // We do not allow multiple lines with same category. + { + isInvalidLine = true; + } + else + { + if (parts.Length == 1) + { + // No ':' symbol, so the entry just specifies the category. + builder.Add(category, default); + continue; + } + + // Entry with the following possible formats: + // 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + var ranges = parts[1].Split(','); + + var infoList = ImmutableArray.CreateBuilder<(string? prefix, int start, int end)>(ranges.Length); + for (int i = 0; i < ranges.Length; i++) + { + (string? prefix, int start, int end) allowedIdsInfo = s_defaultAllowedIdsInfo; + string range = ranges[i].Trim(); + if (!range.Contains('-')) + { + if (TryParseIdRangeEntry(range, out string prefix, out int start)) + { + // Specific Id validation. + allowedIdsInfo.prefix = prefix; + allowedIdsInfo.start = start; + allowedIdsInfo.end = start; + } + else if (range.All(char.IsLetter)) + { + // Only prefix validation. + allowedIdsInfo.prefix = range; + } + else + { + isInvalidLine = true; + break; + } + } + else + { + // Prefix and start-end range validation. + var rangeParts = range.Split('-'); + if (TryParseIdRangeEntry(rangeParts[0], out string prefix1, out int start) && + TryParseIdRangeEntry(rangeParts[1], out string prefix2, out int end) && + prefix1.Equals(prefix2, StringComparison.Ordinal)) + { + allowedIdsInfo.prefix = prefix1; + allowedIdsInfo.start = start; + allowedIdsInfo.end = end; + } + else + { + isInvalidLine = true; + break; + } + } + + infoList.Add(allowedIdsInfo); + } + + if (!isInvalidLine) + { + builder.Add(category, infoList.ToImmutable()); + } + } + + if (isInvalidLine) + { + // Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}'. + string arg1 = contents; + string arg2 = Path.GetFileName(additionalText.Path); + LinePositionSpan linePositionSpan = lines.GetLinePositionSpan(line.Span); + Location location = Location.Create(additionalText.Path, line.Span, linePositionSpan); + invalidFileDiagnostics ??= new List(); + var diagnostic = Diagnostic.Create(AnalyzerCategoryAndIdRangeFileInvalidRule, location, arg1, arg2); + invalidFileDiagnostics.Add(diagnostic); + } + } + + categoryAndAllowedIdsInfoMap = builder.ToImmutable(); + return invalidFileDiagnostics == null; + } + + private static bool TryParseIdRangeEntry(string entry, out string prefix, out int suffix) + { + // Parse an entry for diagnostic ID. + // We require diagnostic ID to have an alphabetical prefix followed by a numerical suffix. + var prefixBuilder = new StringBuilder(); + suffix = -1; + var suffixStr = new StringBuilder(); + bool seenDigit = false; + foreach (char ch in entry) + { + bool isDigit = char.IsDigit(ch); + if (seenDigit && !isDigit) + { + prefix = prefixBuilder.ToString(); + return false; + } + + if (isDigit) + { + suffixStr.Append(ch); + seenDigit = true; + } + else if (!char.IsLetter(ch)) + { + prefix = prefixBuilder.ToString(); + return false; + } + else + { + prefixBuilder.Append(ch); + } + } + + prefix = prefixBuilder.ToString(); + return prefix.Length > 0 && int.TryParse(suffixStr.ToString(), out suffix); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ReleaseTracking.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ReleaseTracking.cs new file mode 100644 index 0000000000000..d2dccdaf75f58 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ReleaseTracking.cs @@ -0,0 +1,475 @@ +// 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.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Text; +using static Microsoft.CodeAnalysis.ReleaseTracking.ReleaseTrackingHelper; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + public sealed partial class DiagnosticDescriptorCreationAnalyzer + { + // Property names which are keys for diagnostic property bag passed to the code fixer. + internal const string EntryToAddPropertyName = nameof(EntryToAddPropertyName); + internal const string EntryToUpdatePropertyName = nameof(EntryToUpdatePropertyName); + + internal static readonly DiagnosticDescriptor DeclareDiagnosticIdInAnalyzerReleaseRule = new( + id: DiagnosticIds.DeclareDiagnosticIdInAnalyzerReleaseRuleId, + title: CreateLocalizableResourceString(nameof(DeclareDiagnosticIdInAnalyzerReleaseTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DeclareDiagnosticIdInAnalyzerReleaseMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DeclareDiagnosticIdInAnalyzerReleaseDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor UpdateDiagnosticIdInAnalyzerReleaseRule = new( + id: DiagnosticIds.UpdateDiagnosticIdInAnalyzerReleaseRuleId, + title: CreateLocalizableResourceString(nameof(UpdateDiagnosticIdInAnalyzerReleaseTitle)), + messageFormat: CreateLocalizableResourceString(nameof(UpdateDiagnosticIdInAnalyzerReleaseMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(UpdateDiagnosticIdInAnalyzerReleaseDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor RemoveUnshippedDeletedDiagnosticIdRule = new( + id: DiagnosticIds.RemoveUnshippedDeletedDiagnosticIdRuleId, + title: CreateLocalizableResourceString(nameof(RemoveUnshippedDeletedDiagnosticIdTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveUnshippedDeletedDiagnosticIdMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RemoveUnshippedDeletedDiagnosticIdDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor RemoveShippedDeletedDiagnosticIdRule = new( + id: DiagnosticIds.RemoveShippedDeletedDiagnosticIdRuleId, + title: CreateLocalizableResourceString(nameof(RemoveShippedDeletedDiagnosticIdTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveShippedDeletedDiagnosticIdMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RemoveShippedDeletedDiagnosticIdDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRule = new( + id: DiagnosticIds.UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRuleId, + title: CreateLocalizableResourceString(nameof(UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdTitle)), + messageFormat: CreateLocalizableResourceString(nameof(UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor RemoveDuplicateEntriesForAnalyzerReleaseRule = new( + id: DiagnosticIds.RemoveDuplicateEntriesForAnalyzerReleaseRuleId, + title: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesForAnalyzerReleaseRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesForAnalyzerReleaseRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesForAnalyzerReleaseRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor RemoveDuplicateEntriesBetweenAnalyzerReleasesRule = new( + id: DiagnosticIds.RemoveDuplicateEntriesBetweenAnalyzerReleasesRuleId, + title: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesBetweenAnalyzerReleasesRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesBetweenAnalyzerReleasesRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RemoveDuplicateEntriesBetweenAnalyzerReleasesRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InvalidEntryInAnalyzerReleasesFileRule = new( + id: DiagnosticIds.InvalidEntryInAnalyzerReleasesFileRuleId, + title: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InvalidHeaderInAnalyzerReleasesFileRule = new( + id: DiagnosticIds.InvalidEntryInAnalyzerReleasesFileRuleId, + title: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InvalidHeaderInAnalyzerReleasesFileRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InvalidUndetectedEntryInAnalyzerReleasesFileRule = new( + id: DiagnosticIds.InvalidEntryInAnalyzerReleasesFileRuleId, + title: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InvalidUndetectedEntryInAnalyzerReleasesFileRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule = new( + id: DiagnosticIds.InvalidEntryInAnalyzerReleasesFileRuleId, + title: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRuleMessageMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidEntryInAnalyzerReleasesFileRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor EnableAnalyzerReleaseTrackingRule = new( + id: DiagnosticIds.EnableAnalyzerReleaseTrackingRuleId, + title: CreateLocalizableResourceString(nameof(EnableAnalyzerReleaseTrackingRuleTitle)), + messageFormat: CreateLocalizableResourceString(nameof(EnableAnalyzerReleaseTrackingRuleMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisReleaseTracking, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(EnableAnalyzerReleaseTrackingRuleDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static bool TryGetReleaseTrackingData( + ImmutableArray additionalTexts, + CancellationToken cancellationToken, + [NotNullWhen(returnValue: true)] out ReleaseTrackingData? shippedData, + [NotNullWhen(returnValue: true)] out ReleaseTrackingData? unshippedData, + out List? invalidFileDiagnostics) + { + if (!TryGetReleaseTrackingFiles(additionalTexts, cancellationToken, out var shippedText, out var unshippedText)) + { + // TODO: Report a diagnostic that both must be specified if either shippedText or unshippedText is non-null. + shippedData = shippedText != null ? ReleaseTrackingData.Default : null; + unshippedData = unshippedText != null ? ReleaseTrackingData.Default : null; + invalidFileDiagnostics = null; + return false; + } + + var diagnostics = new List(); + using var reportedInvalidLines = PooledHashSet.GetInstance(); + shippedData = ReadReleaseTrackingData(shippedText.Path, shippedText.GetTextOrEmpty(cancellationToken), OnDuplicateEntryInRelease, OnInvalidEntry, isShippedFile: true); + unshippedData = ReadReleaseTrackingData(unshippedText.Path, unshippedText.GetTextOrEmpty(cancellationToken), OnDuplicateEntryInRelease, OnInvalidEntry, isShippedFile: false); + + invalidFileDiagnostics = diagnostics; + return invalidFileDiagnostics.Count == 0; + + // Local functions. + void OnDuplicateEntryInRelease(string ruleId, Version currentVersion, string path, SourceText sourceText, TextLine line) + { + if (!reportedInvalidLines.Add(line)) + { + // Already reported. + return; + } + + RoslynDebug.Assert(diagnostics != null); + + // Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}'. + string arg1 = ruleId; + string arg2 = currentVersion == UnshippedVersion ? "unshipped" : currentVersion.ToString(); + string arg3 = Path.GetFileName(path); + LinePositionSpan linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span); + Location location = Location.Create(path, line.Span, linePositionSpan); + var diagnostic = Diagnostic.Create(RemoveDuplicateEntriesForAnalyzerReleaseRule, location, arg1, arg2, arg3); + diagnostics.Add(diagnostic); + } + + void OnInvalidEntry(TextLine line, InvalidEntryKind invalidEntryKind, string path, SourceText sourceText) + { + RoslynDebug.Assert(diagnostics != null); + RoslynDebug.Assert(reportedInvalidLines != null); + + if (!reportedInvalidLines.Add(line)) + { + // Already reported. + return; + } + + var rule = invalidEntryKind switch + { + // Analyzer release file '{0}' has a missing or invalid release header '{1}'. + InvalidEntryKind.Header => InvalidHeaderInAnalyzerReleasesFileRule, + + // Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}'. + InvalidEntryKind.UndetectedField => InvalidUndetectedEntryInAnalyzerReleasesFileRule, + + // Analyzer release file '{0}' has an invalid entry '{1}'. + InvalidEntryKind.Other => InvalidEntryInAnalyzerReleasesFileRule, + _ => throw new NotImplementedException(), + }; + + string arg1 = Path.GetFileName(path); + string arg2 = line.ToString(); + LinePositionSpan linePositionSpan = sourceText.Lines.GetLinePositionSpan(line.Span); + Location location = Location.Create(path, line.Span, linePositionSpan); + var diagnostic = Diagnostic.Create(rule, location, arg1, arg2); + diagnostics.Add(diagnostic); + } + } + + private static bool TryGetReleaseTrackingFiles( + ImmutableArray additionalTexts, + CancellationToken cancellationToken, + [NotNullWhen(returnValue: true)] out AdditionalText? shippedText, + [NotNullWhen(returnValue: true)] out AdditionalText? unshippedText) + { + shippedText = null; + unshippedText = null; + + StringComparer comparer = StringComparer.OrdinalIgnoreCase; + foreach (AdditionalText text in additionalTexts) + { + cancellationToken.ThrowIfCancellationRequested(); + + string fileName = Path.GetFileName(text.Path); + if (comparer.Equals(fileName, ShippedFileName)) + { + shippedText = text; + continue; + } + + if (comparer.Equals(fileName, UnshippedFileName)) + { + unshippedText = text; + continue; + } + } + + return shippedText != null && unshippedText != null; + } + + private static void AnalyzeAnalyzerReleases( + string ruleId, + IArgumentOperation ruleIdArgument, + string? category, + string analyzerName, + string? helpLink, + bool? isEnabledByDefault, + DiagnosticSeverity? defaultSeverity, + ReleaseTrackingData shippedData, + ReleaseTrackingData unshippedData, + Action addDiagnostic) + { + if (!TryGetLatestReleaseTrackingLine(ruleId, shippedData, unshippedData, out _, out var releaseTrackingLine) || + releaseTrackingLine.IsShipped && releaseTrackingLine.IsRemovedRule) + { + var properties = ImmutableDictionary.Empty.Add( + EntryToAddPropertyName, GetEntry(ruleId, category, analyzerName, helpLink, isEnabledByDefault, defaultSeverity)); + var diagnostic = ruleIdArgument.CreateDiagnostic(DeclareDiagnosticIdInAnalyzerReleaseRule, properties, ruleId); + addDiagnostic(diagnostic); + return; + } + + if (releaseTrackingLine.IsRemovedRule) + { + var diagnostic = ruleIdArgument.CreateDiagnostic(UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRule, ruleId); + addDiagnostic(diagnostic); + return; + } + + if (category != null && !string.Equals(category, releaseTrackingLine.Category, StringComparison.OrdinalIgnoreCase) || + isEnabledByDefault != null && isEnabledByDefault != releaseTrackingLine.EnabledByDefault || + defaultSeverity != null && defaultSeverity != releaseTrackingLine.DefaultSeverity) + { + string propertyName; + (string category, bool? isEnabledByDefault, DiagnosticSeverity? defaultSeverity)? oldRule = null; + if (!releaseTrackingLine.IsShipped) + { + // For existing entry in unshipped file, we can just update. + propertyName = EntryToUpdatePropertyName; + if (releaseTrackingLine is ChangedRuleReleaseTrackingLine changedLine) + { + oldRule = (changedLine.OldCategory, changedLine.OldEnabledByDefault, changedLine.OldDefaultSeverity); + } + } + else + { + // Need to add a new changed rule entry in unshipped file. + propertyName = EntryToAddPropertyName; + oldRule = (releaseTrackingLine.Category, releaseTrackingLine.EnabledByDefault, releaseTrackingLine.DefaultSeverity); + } + + var newEntry = GetEntry(ruleId, category, analyzerName, helpLink, isEnabledByDefault, defaultSeverity, oldRule); + var properties = ImmutableDictionary.Empty.Add(propertyName, newEntry); + var diagnostic = ruleIdArgument.CreateDiagnostic(UpdateDiagnosticIdInAnalyzerReleaseRule, properties, ruleId); + addDiagnostic(diagnostic); + return; + } + } + + private static string GetEntry( + string ruleId, + string? category, + string analyzerName, + string? helpLink, + bool? isEnabledByDefault, + DiagnosticSeverity? defaultSeverity, + (string category, bool? isEnabledByDefault, DiagnosticSeverity? defaultSeverity)? oldRule = null) + { + // Rule ID | Category | Severity | Notes + // OR + // Rule ID | New Category | New Severity | Old Category | Old Severity | Notes + var entry = $"{ruleId} | {GetCategoryText(category)} | {GetSeverityText(isEnabledByDefault, defaultSeverity)} |"; + + if (oldRule.HasValue) + { + entry += $" {GetCategoryText(oldRule.Value.category)} | {GetSeverityText(oldRule.Value.isEnabledByDefault, oldRule.Value.defaultSeverity)} |"; + } + + entry += $" {analyzerName}"; + + helpLink ??= TryGetHelpLinkForCARule(ruleId); + if (!string.IsNullOrEmpty(helpLink)) + { + entry += $", [Documentation]({helpLink})"; + } + + return entry; + + static string GetCategoryText(string? category) + => category ?? UndetectedText; + + static string GetSeverityText(bool? isEnabledByDefault, DiagnosticSeverity? defaultSeverity) + => isEnabledByDefault == false ? DisabledText : (defaultSeverity?.ToString() ?? UndetectedText); + + static string? TryGetHelpLinkForCARule(string ruleId) + { + if (ruleId.StartsWith("CA", StringComparison.OrdinalIgnoreCase) && + ruleId.Length > 2 && + long.TryParse(ruleId[2..], out _)) + { +#pragma warning disable CA1308 // Normalize strings to uppercase - use lower case ID in help link + return $"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/{ruleId.ToLowerInvariant()}"; +#pragma warning restore CA1308 // Normalize strings to uppercase + } + + return null; + } + } + + private static bool TryGetLatestReleaseTrackingLine( + string ruleId, + ReleaseTrackingData shippedData, + ReleaseTrackingData unshippedData, + [NotNullWhen(returnValue: true)] out Version? version, + [NotNullWhen(returnValue: true)] out ReleaseTrackingLine? releaseTrackingLine) + { + // Unshipped data is considered to always have a higher version then shipped data. + return unshippedData.TryGetLatestReleaseTrackingLine(ruleId, out version, out releaseTrackingLine) || + shippedData.TryGetLatestReleaseTrackingLine(ruleId, out version, out releaseTrackingLine); + } + + private static void ReportAnalyzerReleaseTrackingDiagnostics( + List? invalidReleaseTrackingDiagnostics, + ReleaseTrackingData shippedData, + ReleaseTrackingData unshippedData, + PooledConcurrentSet seenRuleIds, + CompilationAnalysisContext compilationEndContext) + { + // Report any invalid release tracking file diagnostics. + if (invalidReleaseTrackingDiagnostics?.Count > 0) + { + foreach (var diagnostic in invalidReleaseTrackingDiagnostics) + { + compilationEndContext.ReportDiagnostic(diagnostic); + } + + // Do not report additional cascaded diagnostics. + return; + } + + // Map to track and report duplicate entries for same rule ID. + using var lastEntriesByRuleMap = PooledDictionary.GetInstance(); + + // Process each entry in unshipped file to flag rules which are not seen. + foreach (var (ruleId, releaseTrackingDataForRule) in unshippedData.ReleaseTrackingDataByRuleIdMap) + { + var (unshippedVersion, releaseTrackingLine) = releaseTrackingDataForRule.ReleasesByVersionMap.First(); + lastEntriesByRuleMap[ruleId] = (unshippedVersion, releaseTrackingLine); + if (seenRuleIds.Add(ruleId) && !releaseTrackingLine.IsRemovedRule) + { + compilationEndContext.ReportNoLocationDiagnostic(RemoveUnshippedDeletedDiagnosticIdRule, ruleId); + } + } + + // Process each entry in shipped file to flag rules which are not seen. + foreach (var (ruleId, releaseTrackingDataForRule) in shippedData.ReleaseTrackingDataByRuleIdMap) + { + foreach (var (version, releaseTrackingLine) in releaseTrackingDataForRule.ReleasesByVersionMap) + { + if (seenRuleIds.Add(ruleId) && !releaseTrackingLine.IsRemovedRule) + { + compilationEndContext.ReportNoLocationDiagnostic(RemoveShippedDeletedDiagnosticIdRule, ruleId, version); + } + + if (lastEntriesByRuleMap.TryGetValue(ruleId, out var lastEntry) && + lastEntry.version != version && + lastEntry.releaseTrackingLine.Category.Equals(releaseTrackingLine.Category, StringComparison.OrdinalIgnoreCase) && + lastEntry.releaseTrackingLine.EnabledByDefault == releaseTrackingLine.EnabledByDefault && + lastEntry.releaseTrackingLine.DefaultSeverity == releaseTrackingLine.DefaultSeverity && + lastEntry.releaseTrackingLine.Kind == releaseTrackingLine.Kind) + { + // Rule '{0}' has duplicate entry between release '{1}' and release '{2}'. + string arg1 = ruleId; + string arg2 = lastEntry.version == UnshippedVersion ? "unshipped" : lastEntry.version.ToString(); + string arg3 = version.ToString(); + LinePositionSpan linePositionSpan = lastEntry.releaseTrackingLine.SourceText.Lines.GetLinePositionSpan(lastEntry.releaseTrackingLine.Span); + Location location = Location.Create(lastEntry.releaseTrackingLine.Path, lastEntry.releaseTrackingLine.Span, linePositionSpan); + var diagnostic = Diagnostic.Create(RemoveDuplicateEntriesBetweenAnalyzerReleasesRule, location, arg1, arg2, arg3); + compilationEndContext.ReportDiagnostic(diagnostic); + } + + lastEntriesByRuleMap[ruleId] = (version, releaseTrackingLine); + } + } + + // 'lastEntriesByRuleMap' should now have the first entry for each rule. + // Flag each such first entry that is not marked as new rule - a removed/changed rule entry without a prior new entry is invalid. + foreach (var (_, releaseTrackingLine) in lastEntriesByRuleMap.Values) + { + if (releaseTrackingLine.Kind != ReleaseTrackingRuleEntryKind.New) + { + // Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + string arg1 = Path.GetFileName(releaseTrackingLine.Path); + string arg2 = releaseTrackingLine.Kind.ToString(); + string arg3 = releaseTrackingLine.RuleId; + LinePositionSpan linePositionSpan = releaseTrackingLine.SourceText.Lines.GetLinePositionSpan(releaseTrackingLine.Span); + Location location = Location.Create(releaseTrackingLine.Path, releaseTrackingLine.Span, linePositionSpan); + var diagnostic = Diagnostic.Create(InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule, location, arg1, arg2, arg3); + compilationEndContext.ReportDiagnostic(diagnostic); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ResourceStringsFormat.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ResourceStringsFormat.cs new file mode 100644 index 0000000000000..d862c9bc7d20f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzer_ResourceStringsFormat.cs @@ -0,0 +1,111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Xml.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using PooledResourcesDataValueConcurrentDictionary = PooledConcurrentDictionary>; + + public sealed partial class DiagnosticDescriptorCreationAnalyzer + { + private static bool HasResxAdditionalFiles(AnalyzerOptions options) + { + foreach (var file in options.AdditionalFiles) + { + if (string.Equals(".resx", Path.GetExtension(file.Path), StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + private static ImmutableDictionary GetOrCreateResourceMap( + AnalyzerOptions options, + string resourceFileName, + PooledResourcesDataValueConcurrentDictionary resourceMap, + CancellationToken cancellationToken) + { + Debug.Assert(HasResxAdditionalFiles(options)); + Debug.Assert(!resourceFileName.EndsWith(".resx", StringComparison.OrdinalIgnoreCase)); + + resourceFileName += ".resx"; + if (resourceMap.TryGetValue(resourceFileName, out var map)) + { + return map; + } + + map = CreateResourceMap(options, resourceFileName, cancellationToken); + return resourceMap.GetOrAdd(resourceFileName, map); + } + + private static ImmutableDictionary CreateResourceMap(AnalyzerOptions options, string resourceFileName, CancellationToken cancellationToken) + { + foreach (var file in options.AdditionalFiles) + { + var fileName = Path.GetFileName(file.Path); + if (string.Equals(resourceFileName, fileName, StringComparison.OrdinalIgnoreCase)) + { + return CreateResourceMapForFile(file, cancellationToken); + } + } + + return ImmutableDictionary.Empty; + + static ImmutableDictionary CreateResourceMapForFile(AdditionalText file, CancellationToken cancellationToken) + { + const string valueTagPrefix = @""; + const string valueTagSuffix = @""; + var builder = ImmutableDictionary.CreateBuilder(); + var sourceText = file.GetTextOrEmpty(cancellationToken); + var sourceTextStr = sourceText.ToString(); + var parsedDocument = XDocument.Parse(sourceTextStr, LoadOptions.PreserveWhitespace); + foreach (var dataElement in parsedDocument.Descendants("data")) + { + if (!(dataElement.Attribute("name")?.Value is { } name) || + !(dataElement.Elements("value").FirstOrDefault() is { } valueElement)) + { + continue; + } + + var dataElementStr = dataElement.ToString(); + var valueElementStr = valueElement.ToString(); + + var indexOfDataElement = sourceTextStr.IndexOf(dataElementStr, StringComparison.Ordinal); + if (indexOfDataElement < 0 || + !valueElementStr.StartsWith(valueTagPrefix, StringComparison.OrdinalIgnoreCase) || + !valueElementStr.EndsWith(valueTagSuffix, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + var indexOfValue = indexOfDataElement + + dataElementStr.IndexOf(valueElementStr, StringComparison.Ordinal) + + valueTagPrefix.Length; + var valueLength = valueElementStr.Length - (valueTagPrefix.Length + valueTagSuffix.Length); + var span = new TextSpan(indexOfValue, valueLength); + var linePositionSpan = sourceText.Lines.GetLinePositionSpan(span); + var location = Location.Create(file.Path, span, linePositionSpan); + + var value = valueElementStr.Substring(valueTagPrefix.Length, valueLength); + builder[name] = (value, location); + } + + return builder.ToImmutable(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseCompilationGetSemanticModelAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseCompilationGetSemanticModelAnalyzer.cs new file mode 100644 index 0000000000000..3bdab9d945acd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseCompilationGetSemanticModelAnalyzer.cs @@ -0,0 +1,76 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1030: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DoNotUseCompilationGetSemanticModelAnalyzer : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.DoNotUseCompilationGetSemanticModelRuleId, + CreateLocalizableResourceString(nameof(DoNotUseCompilationGetSemanticModelTitle)), + CreateLocalizableResourceString(nameof(DoNotUseCompilationGetSemanticModelMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotUseCompilationGetSemanticModelDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilationContext.Compilation); + + if (!wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer, out var diagnosticAnalyzerType) || + !wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCompilation, out var compilationType)) + { + return; + } + + var csharpCompilation = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpCompilation); + var visualBasicCompilation = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisVisualBasicVisualBasicCompilation); + + compilationContext.RegisterOperationBlockStartAction(operationBlockContext => + { + if (operationBlockContext.OwningSymbol is IMethodSymbol methodSymbol && + methodSymbol.ContainingType.Inherits(diagnosticAnalyzerType)) + { + operationBlockContext.RegisterOperationAction(operationContext => + { + var invocation = (IInvocationOperation)operationContext.Operation; + + if (invocation.TargetMethod.Name.Equals("GetSemanticModel", StringComparison.Ordinal) && + ( + SymbolEqualityComparer.Default.Equals(invocation.TargetMethod.ContainingType, compilationType) || + SymbolEqualityComparer.Default.Equals(invocation.TargetMethod.ContainingType, csharpCompilation) || + SymbolEqualityComparer.Default.Equals(invocation.TargetMethod.ContainingType, visualBasicCompilation) + )) + { + operationContext.ReportDiagnostic(invocation.Syntax.CreateDiagnostic(Rule)); + } + }, OperationKind.Invocation); + } + }); + }); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseFileTypesForAnalyzersOrGenerators.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseFileTypesForAnalyzersOrGenerators.cs new file mode 100644 index 0000000000000..611996e678752 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/DoNotUseFileTypesForAnalyzersOrGenerators.cs @@ -0,0 +1,69 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DoNotUseFileTypesForAnalyzersOrGenerators : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.DoNotUseFileTypesForAnalyzersOrGenerators, + CreateLocalizableResourceString(nameof(DoNotUseFileTypesForAnalyzersOrGeneratorsTitle)), + CreateLocalizableResourceString(nameof(DoNotUseFileTypesForAnalyzersOrGeneratorsMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotUseFileTypesForAnalyzersOrGeneratorsDescription))); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(context => + { + INamedTypeSymbol? diagnosticAnalyzer = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); + if (diagnosticAnalyzer == null) + { + // Does not contain any diagnostic analyzers. + return; + } + + // There may or may not be a code fix provider type and generator types, depending on what this assembly depends on. + INamedTypeSymbol? codeFixProvider = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCodeFixesCodeFixProvider); + INamedTypeSymbol? isourceGenerator = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisISourceGenerator); + INamedTypeSymbol? iincrementalGenerator = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisIIncrementalGenerator); + + context.RegisterSymbolAction(symbolContext => AnalyzeSymbol(symbolContext, diagnosticAnalyzer, codeFixProvider, isourceGenerator, iincrementalGenerator), SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol? codeFixProvider, INamedTypeSymbol? isourceGenerator, INamedTypeSymbol? iincrementalGenerator) + { + var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; + + if (!namedTypeSymbol.IsFileLocal()) + { + return; + } + + if (namedTypeSymbol.Inherits(diagnosticAnalyzer) || + namedTypeSymbol.Inherits(codeFixProvider) || + namedTypeSymbol.Inherits(isourceGenerator) || + namedTypeSymbol.Inherits(iincrementalGenerator)) + { + var diagnostic = Diagnostic.Create(Rule, namedTypeSymbol.Locations[0], namedTypeSymbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/EnableConcurrentExecutionAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/EnableConcurrentExecutionAnalyzer.cs new file mode 100644 index 0000000000000..70e0518fe2f39 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/EnableConcurrentExecutionAnalyzer.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1026: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class EnableConcurrentExecutionAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer + { + public static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.EnableConcurrentExecutionRuleId, + CreateLocalizableResourceString(nameof(EnableConcurrentExecutionTitle)), + CreateLocalizableResourceString(nameof(EnableConcurrentExecutionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + +#pragma warning disable RS1025 // Configure generated code analysis + public override void Initialize(AnalysisContext context) +#pragma warning restore RS1025 // Configure generated code analysis + { + context.EnableConcurrentExecution(); + + base.Initialize(context); + } + + [SuppressMessage("AnalyzerPerformance", "RS1012:Start action has no registered actions.", Justification = "Method returns an analyzer that is registered by the caller.")] + protected override DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + var compilation = compilationContext.Compilation; + + var analysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsAnalysisContext); + if (analysisContext is null) + { + return null; + } + + compilationContext.RegisterOperationBlockStartAction(context => + { + if (context.OwningSymbol?.Kind != SymbolKind.Method) + { + return; + } + + var method = (IMethodSymbol)context.OwningSymbol; + if (method.Name != nameof(DiagnosticAnalyzer.Initialize)) + { + return; + } + + IParameterSymbol? analysisContextParameter = null; + foreach (var parameter in method.Parameters) + { + if (!SymbolEqualityComparer.Default.Equals(parameter.Type, analysisContext)) + { + continue; + } + + analysisContextParameter = parameter; + break; + } + + if (analysisContextParameter is null) + { + return; + } + + var analyzer = new EnableConcurrentExecutionOperationAnalyzer(analysisContextParameter); + context.RegisterOperationAction(analyzer.HandleInvocationOperation, OperationKind.Invocation); + context.RegisterOperationBlockEndAction(analyzer.HandleOperationBlockEnd); + }); + + // This analyzer only performs operation block analysis + return null; + } + + private sealed class EnableConcurrentExecutionOperationAnalyzer + { + private readonly IParameterSymbol _analysisContextParameter; + + public EnableConcurrentExecutionOperationAnalyzer(IParameterSymbol analysisContextParameter) + { + _analysisContextParameter = analysisContextParameter; + } + + public bool EnabledConcurrentExecution { get; private set; } + + internal void HandleInvocationOperation(OperationAnalysisContext context) + { + if (EnabledConcurrentExecution) + { + return; + } + + var invocation = (IInvocationOperation)context.Operation; + if (invocation.TargetMethod?.Name != nameof(AnalysisContext.EnableConcurrentExecution)) + { + return; + } + + if (invocation.Instance?.Kind != OperationKind.ParameterReference) + { + return; + } + + var parameterReference = (IParameterReferenceOperation)invocation.Instance; + if (!SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, _analysisContextParameter)) + { + return; + } + + EnabledConcurrentExecution = true; + } + + internal void HandleOperationBlockEnd(OperationBlockAnalysisContext context) + { + if (!EnabledConcurrentExecution) + { + context.ReportDiagnostic(_analysisContextParameter.CreateDiagnostic(Rule)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.FixAllProvider.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.FixAllProvider.cs new file mode 100644 index 0000000000000..835d2ca9cbd08 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.FixAllProvider.cs @@ -0,0 +1,186 @@ +// 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 System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public sealed partial class AnalyzerReleaseTrackingFix + { + private sealed class ReleaseTrackingFixAllProvider : FixAllProvider + { + public static readonly FixAllProvider Instance = new ReleaseTrackingFixAllProvider(); + public ReleaseTrackingFixAllProvider() { } + public override async Task GetFixAsync(FixAllContext fixAllContext) + { + var diagnosticsToFix = new List>>(); + switch (fixAllContext.Scope) + { + case FixAllScope.Document: + { + ImmutableArray diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document!).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + break; + } + + case FixAllScope.Project: + { + Project project = fixAllContext.Project; + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + break; + } + + case FixAllScope.Solution: + { + foreach (Project project in fixAllContext.Solution.Projects) + { + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(project, diagnostics)); + } + + break; + } + + case FixAllScope.Custom: + return null; + + default: + Debug.Fail($"Unknown FixAllScope '{fixAllContext.Scope}'"); + return null; + } + + if (fixAllContext.CodeActionEquivalenceKey == CodeAnalysisDiagnosticsResources.EnableAnalyzerReleaseTrackingRuleTitle) + { + var projectIds = diagnosticsToFix.Select(d => d.Key.Id).ToImmutableArray(); + return new FixAllAddAdditionalDocumentsAction(projectIds, fixAllContext.Solution); + } + + return new FixAllAdditionalDocumentChangeAction(fixAllContext.Scope, fixAllContext.Solution, diagnosticsToFix, fixAllContext.CodeActionEquivalenceKey); + } + + private sealed class FixAllAdditionalDocumentChangeAction : CodeAction + { + private readonly List>> _diagnosticsToFix; + private readonly Solution _solution; + + public FixAllAdditionalDocumentChangeAction(FixAllScope fixAllScope, Solution solution, List>> diagnosticsToFix, string? equivalenceKey) + { + this.Title = fixAllScope.ToString(); + _solution = solution; + _diagnosticsToFix = diagnosticsToFix; + this.EquivalenceKey = equivalenceKey; + } + + public override string Title { get; } + public override string? EquivalenceKey { get; } + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + var updatedUnshippedText = new List<(DocumentId, SourceText)>(); + + foreach (KeyValuePair> pair in _diagnosticsToFix) + { + Project project = pair.Key; + ImmutableArray diagnostics = pair.Value; + + TextDocument? unshippedDocument = project.AdditionalDocuments.FirstOrDefault(a => a.Name == ReleaseTrackingHelper.UnshippedFileName); + if (unshippedDocument == null || diagnostics.IsEmpty) + { + continue; + } + + if (EquivalenceKey == CodeAnalysisDiagnosticsResources.AddEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle) + { + var newText = await AddEntriesToUnshippedFileForDiagnosticsAsync(unshippedDocument, diagnostics, cancellationToken).ConfigureAwait(false); + updatedUnshippedText.Add((unshippedDocument.Id, newText)); + } + else if (EquivalenceKey == CodeAnalysisDiagnosticsResources.UpdateEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle) + { + var newText = await UpdateEntriesInUnshippedFileForDiagnosticsAsync(unshippedDocument, diagnostics, cancellationToken).ConfigureAwait(false); + updatedUnshippedText.Add((unshippedDocument.Id, newText)); + } + else + { + throw new NotImplementedException(); + } + } + + Solution newSolution = _solution; + foreach (var (unshippedDocumentId, newText) in updatedUnshippedText) + { + newSolution = newSolution.WithAdditionalDocumentText(unshippedDocumentId, newText); + } + + return newSolution; + } + + private static async Task AddEntriesToUnshippedFileForDiagnosticsAsync(TextDocument unshippedDataDocument, ImmutableArray diagnostics, CancellationToken cancellationToken) + { + var entriesToAdd = new SortedSet(); + foreach (var diagnostic in diagnostics) + { + if (IsAddEntryToUnshippedFileDiagnostic(diagnostic, out var entryToAdd)) + { + entriesToAdd.Add(entryToAdd); + } + } + + return await AddEntriesToUnshippedFileAsync(unshippedDataDocument, entriesToAdd, cancellationToken).ConfigureAwait(false); + } + + private static async Task UpdateEntriesInUnshippedFileForDiagnosticsAsync(TextDocument unshippedDataDocument, ImmutableArray diagnostics, CancellationToken cancellationToken) + { + var entriesToUpdate = new Dictionary(); + foreach (var diagnostic in diagnostics) + { + if (IsUpdateEntryToUnshippedFileDiagnostic(diagnostic, out var ruleId, out var entryToUpdate) && + !entriesToUpdate.ContainsKey(ruleId)) + { + entriesToUpdate.Add(ruleId, entryToUpdate); + } + } + + return await UpdateEntriesInUnshippedFileAsync(unshippedDataDocument, entriesToUpdate, cancellationToken).ConfigureAwait(false); + } + } + + private sealed class FixAllAddAdditionalDocumentsAction : CodeAction + { + private readonly ImmutableArray _projectIds; + private readonly Solution _solution; + + public FixAllAddAdditionalDocumentsAction(ImmutableArray projectIds, Solution solution) + { + _projectIds = projectIds; + _solution = solution; + } + + public override string Title => CodeAnalysisDiagnosticsResources.EnableAnalyzerReleaseTrackingRuleTitle; + public override string EquivalenceKey => CodeAnalysisDiagnosticsResources.EnableAnalyzerReleaseTrackingRuleTitle; + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + var newSolution = _solution; + foreach (var projectId in _projectIds) + { + newSolution = await AddAnalyzerReleaseTrackingFilesAsync(newSolution.GetProject(projectId)!).ConfigureAwait(false); + } + + return newSolution; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.cs new file mode 100644 index 0000000000000..5226488bfadd5 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/AnalyzerReleaseTrackingFix.cs @@ -0,0 +1,494 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(AnalyzerReleaseTrackingFix))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed partial class AnalyzerReleaseTrackingFix() : CodeFixProvider + { + private const string EntryFieldSeparator = "|"; + private static readonly string[] s_entryFieldSeparators = new[] { EntryFieldSeparator }; + + internal const string ShippedAnalyzerReleaseTrackingFileDefaultContent = @"; Shipped analyzer releases +" + CommonAnalyzerReleaseTrackingContent; + internal const string UnshippedAnalyzerReleaseTrackingFileDefaultContent = @"; Unshipped analyzer release +" + CommonAnalyzerReleaseTrackingContent; + private const string CommonAnalyzerReleaseTrackingContent = @"; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md + +"; + + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(DiagnosticIds.DeclareDiagnosticIdInAnalyzerReleaseRuleId, DiagnosticIds.UpdateDiagnosticIdInAnalyzerReleaseRuleId, DiagnosticIds.EnableAnalyzerReleaseTrackingRuleId); + + public override FixAllProvider GetFixAllProvider() + => new ReleaseTrackingFixAllProvider(); + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + CodeAction? codeAction = null; + switch (diagnostic.Id) + { + case DiagnosticIds.DeclareDiagnosticIdInAnalyzerReleaseRuleId: + if (IsAddEntryToUnshippedFileDiagnostic(diagnostic, out var entryToAdd)) + { + codeAction = CodeAction.Create( + CodeAnalysisDiagnosticsResources.AddEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle, + cancellationToken => AddEntryToUnshippedFileAsync(context.Document.Project, entryToAdd, cancellationToken), + equivalenceKey: CodeAnalysisDiagnosticsResources.AddEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle); + } + + break; + + case DiagnosticIds.UpdateDiagnosticIdInAnalyzerReleaseRuleId: + if (IsAddEntryToUnshippedFileDiagnostic(diagnostic, out entryToAdd)) + { + codeAction = CodeAction.Create( + CodeAnalysisDiagnosticsResources.AddEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle, + cancellationToken => AddEntryToUnshippedFileAsync(context.Document.Project, entryToAdd, cancellationToken), + equivalenceKey: CodeAnalysisDiagnosticsResources.AddEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle); + } + else if (IsUpdateEntryToUnshippedFileDiagnostic(diagnostic, out var ruleId, out var entryToUpdate)) + { + codeAction = CodeAction.Create( + CodeAnalysisDiagnosticsResources.UpdateEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle, + cancellationToken => UpdateEntryInUnshippedFileAsync(context.Document.Project, ruleId, entryToUpdate, cancellationToken), + equivalenceKey: CodeAnalysisDiagnosticsResources.UpdateEntryForDiagnosticIdInAnalyzerReleaseCodeFixTitle); + } + + break; + + case DiagnosticIds.EnableAnalyzerReleaseTrackingRuleId: + codeAction = CodeAction.Create( + CodeAnalysisDiagnosticsResources.EnableAnalyzerReleaseTrackingRuleTitle, + cancellationToken => AddAnalyzerReleaseTrackingFilesAsync(context.Document.Project), + equivalenceKey: CodeAnalysisDiagnosticsResources.EnableAnalyzerReleaseTrackingRuleTitle); + break; + + default: + Debug.Fail($"Unsupported diagnostic ID {diagnostic.Id}"); + continue; + } + + if (codeAction != null) + { + context.RegisterCodeFix(codeAction, diagnostic); + } + } + + return Task.CompletedTask; + } + + private static Task AddAnalyzerReleaseTrackingFilesAsync(Project project) + { + project = AddAdditionalDocument(project, ReleaseTrackingHelper.ShippedFileName, ShippedAnalyzerReleaseTrackingFileDefaultContent); + project = AddAdditionalDocument(project, ReleaseTrackingHelper.UnshippedFileName, UnshippedAnalyzerReleaseTrackingFileDefaultContent); + return Task.FromResult(project.Solution); + + // Local functions. + static Project AddAdditionalDocument(Project project, string name, string fileContent) + { + TextDocument? additionalDocument = project.AdditionalDocuments.FirstOrDefault(doc => string.Equals(doc.Name, name, StringComparison.OrdinalIgnoreCase)); + if (additionalDocument == null) + { + project = project.AddAdditionalDocument(name, fileContent).Project; + } + + return project; + } + } + + private static bool IsAddEntryToUnshippedFileDiagnostic(Diagnostic diagnostic, [NotNullWhen(returnValue: true)] out string? entryToAdd) + { + if (diagnostic.Properties != null && + diagnostic.Properties.TryGetValue(DiagnosticDescriptorCreationAnalyzer.EntryToAddPropertyName, out entryToAdd) && + !RoslynString.IsNullOrEmpty(entryToAdd)) + { + RoslynDebug.Assert(entryToAdd != null); + return true; + } + + entryToAdd = null; + return false; + } + + private static bool IsUpdateEntryToUnshippedFileDiagnostic( + Diagnostic diagnostic, + [NotNullWhen(returnValue: true)] out string? ruleId, + [NotNullWhen(returnValue: true)] out string? entryToUpdate) + { + if (diagnostic.Properties != null && + diagnostic.Properties.TryGetValue(DiagnosticDescriptorCreationAnalyzer.EntryToUpdatePropertyName, out entryToUpdate) && + !RoslynString.IsNullOrEmpty(entryToUpdate) && + TryGetRuleIdForEntry(entryToUpdate, out ruleId)) + { + return true; + } + + ruleId = null; + entryToUpdate = null; + return false; + } + + private static bool TryGetRuleIdForEntry(string entry, [NotNullWhen(returnValue: true)] out string? ruleId) + { + var index = entry.IndexOf(EntryFieldSeparator, StringComparison.Ordinal); + if (index > 0) + { + ruleId = entry[..index].Trim(); + return true; + } + + ruleId = null; + return false; + } + + private static async Task AddEntryToUnshippedFileAsync(Project project, string entryToAdd, CancellationToken cancellationToken) + { + var unshippedDataDocument = project.AdditionalDocuments.FirstOrDefault(d => d.Name == ReleaseTrackingHelper.UnshippedFileName); + if (unshippedDataDocument == null) + { + return project.Solution; + } + + var newText = await AddEntriesToUnshippedFileAsync(unshippedDataDocument, new SortedSet() { entryToAdd }, cancellationToken).ConfigureAwait(false); + return project.Solution.WithAdditionalDocumentText(unshippedDataDocument.Id, newText); + } + + private static Task AddEntriesToUnshippedFileAsync( + TextDocument unshippedDataDocument, + SortedSet entriesToAdd, + CancellationToken cancellationToken) + => AddOrUpdateEntriesToUnshippedFileAsync(unshippedDataDocument, entriesToAdd, entriesToUpdate: null, cancellationToken); + + private static async Task UpdateEntryInUnshippedFileAsync(Project project, string ruleId, string entryToUpdate, CancellationToken cancellationToken) + { + var unshippedDataDocument = project.AdditionalDocuments.FirstOrDefault(d => d.Name == ReleaseTrackingHelper.UnshippedFileName); + if (unshippedDataDocument == null) + { + return project.Solution; + } + + var newText = await UpdateEntriesInUnshippedFileAsync(unshippedDataDocument, new Dictionary() { { ruleId, entryToUpdate } }, cancellationToken).ConfigureAwait(false); + return project.Solution.WithAdditionalDocumentText(unshippedDataDocument.Id, newText); + } + + private static Task UpdateEntriesInUnshippedFileAsync( + TextDocument unshippedDataDocument, + Dictionary entriesToUpdate, + CancellationToken cancellationToken) + => AddOrUpdateEntriesToUnshippedFileAsync(unshippedDataDocument, entriesToAdd: null, entriesToUpdate, cancellationToken); + + private static Task AddOrUpdateEntriesToUnshippedFileAsync( + TextDocument unshippedDataDocument, + SortedSet? entriesToAdd, + Dictionary? entriesToUpdate, + CancellationToken cancellationToken) + { + // Split entries to add into New rule entries and Changed rule entries as they should be added to separate tables. + // New rule entry: + // "Rule ID | Category | Severity | HelpLink (optional)" + // " 0 | 1 | 2 | 3 " + // + // Changed rule entry: + // "Rule ID | New Category | New Severity | Old Category | Old Severity | HelpLink (optional)" + // " 0 | 1 | 2 | 3 | 4 | 5 " + + SortedSet? newRuleEntriesToAdd = null; + SortedSet? changedRuleEntriesToAdd = null; + if (entriesToAdd != null) + { + foreach (var entry in entriesToAdd) + { + switch (entry.Split(s_entryFieldSeparators, StringSplitOptions.None).Length) + { + case 3: + case 4: + newRuleEntriesToAdd ??= new SortedSet(); + newRuleEntriesToAdd.Add(entry); + break; + + case 5: + case 6: + changedRuleEntriesToAdd ??= new SortedSet(); + changedRuleEntriesToAdd.Add(entry); + break; + + default: + throw new NotImplementedException(); + } + } + } + + return AddOrUpdateEntriesToUnshippedFileAsync(unshippedDataDocument, newRuleEntriesToAdd, changedRuleEntriesToAdd, entriesToUpdate, cancellationToken); + } + + private static async Task AddOrUpdateEntriesToUnshippedFileAsync( + TextDocument unshippedDataDocument, + SortedSet? newRuleEntriesToAdd, + SortedSet? changedRuleEntriesToAdd, + Dictionary? entriesToUpdate, + CancellationToken cancellationToken) + { + var unshippedText = await unshippedDataDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var builder = new StringBuilder(); + RuleEntryTableKind? currentTableKind = null; + var parsingHeaderLines = false; + var first = true; + bool sawNewLine = false; + + foreach (TextLine line in unshippedText.Lines) + { + sawNewLine = false; + if (!first) + { + builder.AppendLine(); + } + else + { + first = false; + } + + string originalLineText = line.ToString(); + var lineText = originalLineText.Trim(); + if (string.IsNullOrWhiteSpace(lineText)) + { + // Check if we were parsing New or Changed rules entries. + // If so, append all the new/changed rule entries to appropriate table. + if (!parsingHeaderLines) + { + if (currentTableKind == RuleEntryTableKind.New) + { + if (AddAllEntries(newRuleEntriesToAdd, builder, prependNewLine: false)) + { + builder.AppendLine(); + } + } + else if (currentTableKind == RuleEntryTableKind.Changed + && AddAllEntries(changedRuleEntriesToAdd, builder, prependNewLine: false)) + { + builder.AppendLine(); + } + } + + // Append the blank line. + builder.Append(originalLineText); + sawNewLine = true; + continue; + } + + if (lineText.StartsWith(";", StringComparison.Ordinal)) + { + builder.Append(originalLineText); + continue; + } + + if (lineText.StartsWith("###", StringComparison.Ordinal)) + { + if (lineText.StartsWith(ReleaseTrackingHelper.TableTitleNewRules, StringComparison.OrdinalIgnoreCase)) + { + currentTableKind = RuleEntryTableKind.New; + } + else + { + // Ensure that new rules table is always above the removed and changed rules. + if (newRuleEntriesToAdd?.Count > 0) + { + AddNewRulesTableHeader(builder, prependNewLine: false); + AddAllEntries(newRuleEntriesToAdd, builder, prependNewLine: true); + builder.AppendLine(); + builder.AppendLine(); + } + + if (lineText.StartsWith(ReleaseTrackingHelper.TableTitleRemovedRules, StringComparison.OrdinalIgnoreCase)) + { + currentTableKind = RuleEntryTableKind.Removed; + } + else if (lineText.StartsWith(ReleaseTrackingHelper.TableTitleChangedRules, StringComparison.OrdinalIgnoreCase)) + { + currentTableKind = RuleEntryTableKind.Changed; + } + else + { + Debug.Fail($"Unexpected line {lineText}"); + currentTableKind = null; + } + } + + builder.Append(originalLineText); + parsingHeaderLines = true; + continue; + } + + if (parsingHeaderLines) + { + builder.Append(originalLineText); + + if (Regex.IsMatch(lineText, ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1RegexPattern, RegexOptions.IgnoreCase) || + Regex.IsMatch(lineText, ReleaseTrackingHelper.TableHeaderChangedRulesLine1RegexPattern, RegexOptions.IgnoreCase)) + { + continue; + } + else if (Regex.IsMatch(lineText, ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2RegexPattern, RegexOptions.IgnoreCase) || + Regex.IsMatch(lineText, ReleaseTrackingHelper.TableHeaderChangedRulesLine2RegexPattern, RegexOptions.IgnoreCase)) + { + parsingHeaderLines = false; + } + else + { + Debug.Fail($"Unexpected line {lineText}"); + } + + continue; + } + + RoslynDebug.Assert(currentTableKind.HasValue); + if (currentTableKind.Value == RuleEntryTableKind.Removed) + { + // Retain the entries in Removed rules table without changes. + builder.Append(lineText); + continue; + } + + var entriesToAdd = currentTableKind.Value == RuleEntryTableKind.New ? newRuleEntriesToAdd : changedRuleEntriesToAdd; + while (entriesToAdd?.Count > 0 && + string.Compare(entriesToAdd.First(), lineText, StringComparison.OrdinalIgnoreCase) <= 0) + { + builder.AppendLine(entriesToAdd.First()); + entriesToAdd.Remove(entriesToAdd.First()); + } + + if (entriesToUpdate?.Count > 0 && + TryGetRuleIdForEntry(lineText, out var ruleIdForLine) && + entriesToUpdate.TryGetValue(ruleIdForLine, out var entryToUpdate)) + { + builder.Append(entryToUpdate); + entriesToUpdate.Remove(ruleIdForLine); + continue; + } + + builder.Append(originalLineText); + } + + if (newRuleEntriesToAdd?.Count > 0 || changedRuleEntriesToAdd?.Count > 0) + { + switch (currentTableKind) + { + case RuleEntryTableKind.New: + AddAllEntries(newRuleEntriesToAdd, builder, prependNewLine: !sawNewLine); + if (changedRuleEntriesToAdd?.Count > 0) + { + AddChangedRulesTableHeader(builder, prependNewLine: true); + AddAllEntries(changedRuleEntriesToAdd, builder, prependNewLine: true); + } + + break; + + case RuleEntryTableKind.Changed: + AddAllEntries(changedRuleEntriesToAdd, builder, prependNewLine: !sawNewLine); + Debug.Assert(newRuleEntriesToAdd == null || newRuleEntriesToAdd.Count == 0); + break; + + default: + var hasNewRuleEntries = newRuleEntriesToAdd?.Count > 0; + if (hasNewRuleEntries) + { + AddNewRulesTableHeader(builder, prependNewLine: false); + AddAllEntries(newRuleEntriesToAdd, builder, prependNewLine: true); + } + + if (changedRuleEntriesToAdd?.Count > 0) + { + AddChangedRulesTableHeader(builder, prependNewLine: hasNewRuleEntries); + AddAllEntries(changedRuleEntriesToAdd, builder, prependNewLine: true); + } + + break; + } + } + + return unshippedText.Replace(new TextSpan(0, unshippedText.Length), builder.ToString()); + + static void AddNewRulesTableHeader(StringBuilder builder, bool prependNewLine) + { + if (prependNewLine) + { + builder.AppendLine(); + builder.AppendLine(); + } + + builder.AppendLine(ReleaseTrackingHelper.TableTitleNewRules); + builder.AppendLine(); + builder.AppendLine(ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1); + builder.Append(ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2); + } + + static void AddChangedRulesTableHeader(StringBuilder builder, bool prependNewLine) + { + if (prependNewLine) + { + builder.AppendLine(); + builder.AppendLine(); + } + + builder.AppendLine(ReleaseTrackingHelper.TableTitleChangedRules); + builder.AppendLine(); + builder.AppendLine(ReleaseTrackingHelper.TableHeaderChangedRulesLine1); + builder.Append(ReleaseTrackingHelper.TableHeaderChangedRulesLine2); + } + + static bool AddAllEntries(SortedSet? entriesToAdd, StringBuilder builder, bool prependNewLine) + { + if (entriesToAdd?.Count > 0) + { + var first = true; + foreach (var entryToAdd in entriesToAdd) + { + if (!first || prependNewLine) + { + builder.AppendLine(); + } + + builder.Append(entryToAdd); + first = false; + } + + entriesToAdd.Clear(); + return true; + } + + return false; + } + } + + private enum RuleEntryTableKind + { + New, + Removed, + Changed, + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ApplyDiagnosticAnalyzerAttributeFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ApplyDiagnosticAnalyzerAttributeFix.cs new file mode 100644 index 0000000000000..d30b46da16277 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ApplyDiagnosticAnalyzerAttributeFix.cs @@ -0,0 +1,85 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public abstract class ApplyDiagnosticAnalyzerAttributeFix : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIds.MissingDiagnosticAnalyzerAttributeRuleId); + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + SyntaxNode root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + SyntaxToken token = root.FindToken(context.Span.Start); + if (!token.Span.IntersectsWith(context.Span)) + { + return; + } + + SyntaxGenerator generator = SyntaxGenerator.GetGenerator(context.Document); + SyntaxNode classDecl = generator.GetDeclaration(token.Parent); + if (classDecl == null) + { + return; + } + + // Register fixes. + + // 1) Apply C# DiagnosticAnalyzerAttribute. + string title = string.Format(CultureInfo.CurrentCulture, CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_1, LanguageNames.CSharp); + AddFix(title, context, root, classDecl, generator, LanguageNames.CSharp); + + // 2) Apply VB DiagnosticAnalyzerAttribute. + title = string.Format(CultureInfo.CurrentCulture, CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_1, LanguageNames.VisualBasic); + AddFix(title, context, root, classDecl, generator, LanguageNames.VisualBasic); + + // 3) Apply both C# and VB DiagnosticAnalyzerAttributes. + title = string.Format(CultureInfo.CurrentCulture, CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_2, LanguageNames.CSharp, LanguageNames.VisualBasic); + AddFix(title, context, root, classDecl, generator, LanguageNames.CSharp, LanguageNames.VisualBasic); + } + + protected abstract SyntaxNode ParseExpression(string expression); + + private void AddFix(string codeFixTitle, CodeFixContext context, SyntaxNode root, SyntaxNode classDecl, SyntaxGenerator generator, params string[] languages) + { + var fix = CodeAction.Create( + codeFixTitle, + c => GetFixAsync(context.Document, root, classDecl, generator, languages), + equivalenceKey: codeFixTitle); + context.RegisterCodeFix(fix, context.Diagnostics); + } + + private Task GetFixAsync(Document document, SyntaxNode root, SyntaxNode classDecl, SyntaxGenerator generator, params string[] languages) + { + string languageNamesFullName = typeof(LanguageNames).FullName; + var arguments = new SyntaxNode[languages.Length]; + + for (int i = 0; i < languages.Length; i++) + { + string language = languages[i] == LanguageNames.CSharp ? nameof(LanguageNames.CSharp) : nameof(LanguageNames.VisualBasic); + string expressionToParse = languageNamesFullName + "." + language; + SyntaxNode parsedExpression = ParseExpression(expressionToParse); + arguments[i] = generator.AttributeArgument(parsedExpression); + } + + SyntaxNode attribute = generator.Attribute(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute, arguments); + SyntaxNode newClassDecl = generator.AddAttributes(classDecl, attribute); + SyntaxNode newRoot = root.ReplaceNode(classDecl, newClassDecl); + return Task.FromResult(document.WithSyntaxRoot(newRoot)); + } + + public sealed override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/CompareSymbolsCorrectlyFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/CompareSymbolsCorrectlyFix.cs new file mode 100644 index 0000000000000..f1f80bf8c27ea --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/CompareSymbolsCorrectlyFix.cs @@ -0,0 +1,296 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public abstract class CompareSymbolsCorrectlyFix : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(DiagnosticIds.CompareSymbolsCorrectlyRuleId); + + protected abstract SyntaxNode CreateConditionalAccessExpression(SyntaxNode expression, SyntaxNode whenNotNull); + + protected abstract SyntaxNode GetExpression(IInvocationOperation invocationOperation); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (diagnostic.Properties.TryGetValue(CompareSymbolsCorrectlyAnalyzer.RulePropertyName, out var rule)) + { + switch (rule) + { + case CompareSymbolsCorrectlyAnalyzer.EqualityRuleName: + context.RegisterCodeFix( + CodeAction.Create( + CodeAnalysisDiagnosticsResources.CompareSymbolsCorrectlyCodeFix, + cancellationToken => ConvertToEqualsAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(CompareSymbolsCorrectlyFix)), + diagnostic); + break; + case CompareSymbolsCorrectlyAnalyzer.CollectionRuleName: + context.RegisterCodeFix( + CodeAction.Create( + CodeAnalysisDiagnosticsResources.CompareSymbolsCorrectlyCodeFix, + cancellationToken => CallOverloadWithEqualityComparerAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(CompareSymbolsCorrectlyFix)), + diagnostic); + break; + } + } + } + + return Task.CompletedTask; + } + + private async Task ConvertToEqualsAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var expression = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + var rawOperation = semanticModel.GetOperation(expression, cancellationToken); + + return rawOperation switch + { + IBinaryOperation binaryOperation => await ConvertToEqualsAsync(document, semanticModel, binaryOperation, cancellationToken).ConfigureAwait(false), + IInvocationOperation invocationOperation => await EnsureEqualsCorrectAsync(document, semanticModel, invocationOperation, cancellationToken).ConfigureAwait(false), + _ => document + }; + } + + private async Task CallOverloadWithEqualityComparerAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var expression = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + var rawOperation = semanticModel.GetOperation(expression, cancellationToken); + + if (!CompareSymbolsCorrectlyAnalyzer.UseSymbolEqualityComparer(semanticModel.Compilation) || + !semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIEqualityComparer1, out var iEqualityComparer)) + { + return document; + } + + return rawOperation switch + { + IObjectCreationOperation objectCreation => + await CallOverloadWithEqualityComparerAsync( + document, objectCreation.Syntax, objectCreation.Constructor, objectCreation.Arguments, isUsedAsExtensionMethod: false, + (generator, args) => generator.ObjectCreationExpression(objectCreation.Type, args), iEqualityComparer, cancellationToken) + .ConfigureAwait(false), + + IInvocationOperation invocation => + await CallOverloadWithEqualityComparerAsync( + document, invocation.Syntax, invocation.TargetMethod, invocation.Arguments, IsExtensionMethodUsedAsSuch(invocation), + (generator, args) => generator.InvocationExpression(GetExpression(invocation), args), iEqualityComparer, cancellationToken) + .ConfigureAwait(false), + + _ => document, + }; + + static bool IsExtensionMethodUsedAsSuch(IInvocationOperation invocation) + => invocation.IsExtensionMethodAndHasNoInstance() + && invocation.Arguments.Length > 0 + && invocation.Arguments[0].IsImplicit; + } + + private static async Task CallOverloadWithEqualityComparerAsync(Document document, SyntaxNode nodeToReplace, IMethodSymbol? methodSymbol, + ImmutableArray arguments, bool isUsedAsExtensionMethod, Func, SyntaxNode> getReplacementNode, + INamedTypeSymbol iEqualityComparer, CancellationToken cancellationToken) + { + if (!TryFindSymbolEqualityComparerOverload(methodSymbol, iEqualityComparer, out var symbolEqualityParameterPosition)) + { + return document; + } + + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + var extensionMethodShift = isUsedAsExtensionMethod ? 1 : 0; + var syntaxArguments = arguments.Skip(extensionMethodShift).Select(x => x.Syntax).ToList(); + if (symbolEqualityParameterPosition == 0) + { + syntaxArguments.Insert(0, GetEqualityComparerDefault(generator)); + } + else + { + syntaxArguments.Add(GetEqualityComparerDefault(generator)); + } + + var replacement = getReplacementNode(generator, syntaxArguments); + editor.ReplaceNode(nodeToReplace, replacement.WithTriviaFrom(nodeToReplace)); + + return editor.GetChangedDocument(); + } + + private static bool TryFindSymbolEqualityComparerOverload(IMethodSymbol? methodSymbol, INamedTypeSymbol iEqualityComparer, out int symbolEqualityParameterPosition) + { + symbolEqualityParameterPosition = -1; + if (methodSymbol == null) + return false; + + var overloads = methodSymbol.GetOverloads(); + methodSymbol = (methodSymbol.ReducedFrom ?? methodSymbol).ConstructedFrom; + var methodArgsCount = methodSymbol.Parameters.Length; + + foreach (var overload in overloads) + { + if (overload.Parameters.Length != methodArgsCount + 1) + { + continue; + } + + // We currently only support adding the equality comparer at the beginning or at the end of the arguments list. + if (SymbolEqualityComparer.Default.Equals(overload.Parameters[0].Type.OriginalDefinition, iEqualityComparer)) + { + if (AreCollectionsEqual(overload.Parameters.Skip(1), methodSymbol.Parameters)) + { + symbolEqualityParameterPosition = 0; + return true; + } + } + else if (SymbolEqualityComparer.Default.Equals(overload.Parameters[^1].Type.OriginalDefinition, iEqualityComparer)) + { + if (AreCollectionsEqual(overload.Parameters.Take(methodArgsCount), methodSymbol.Parameters)) + { + symbolEqualityParameterPosition = methodArgsCount; + return true; + } + } + } + + return false; + + static bool AreCollectionsEqual(IEnumerable c1, IEnumerable c2) + => c1.Zip(c2, (x, y) => x.ParameterTypesAreSame(y)).All(x => x); + } + + private async Task EnsureEqualsCorrectAsync(Document document, SemanticModel semanticModel, IInvocationOperation invocationOperation, CancellationToken cancellationToken) + { + if (!CompareSymbolsCorrectlyAnalyzer.UseSymbolEqualityComparer(semanticModel.Compilation)) + { + return document; + } + + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + // With "a?.b?.c?.Equals(b)", the invocation operation syntax is only ".Equals(b)". + // Walk-up the tree to find all parts of the conditional access and also the root + // so that we can replace the whole expression. + var conditionalAccessMembers = new List(); + IOperation currentOperation = invocationOperation; + while (currentOperation.Parent is IConditionalAccessOperation conditionalAccess) + { + currentOperation = conditionalAccess; + conditionalAccessMembers.Add(conditionalAccess.Operation.Syntax); + } + + var arguments = GetNewInvocationArguments(invocationOperation, conditionalAccessMembers); + var replacement = generator.InvocationExpression(GetEqualityComparerDefaultEquals(generator), arguments); + + var nodeToReplace = currentOperation.Syntax; + editor.ReplaceNode(nodeToReplace, replacement.WithTriviaFrom(nodeToReplace)); + + return editor.GetChangedDocument(); + } + + private IEnumerable GetNewInvocationArguments(IInvocationOperation invocationOperation, + List conditionalAccessMembers) + { + var arguments = invocationOperation.Arguments.Select(argument => argument.Syntax); + + if (invocationOperation.GetInstance() is not IOperation instance) + { + return arguments; + } + + if (instance.Kind != OperationKind.ConditionalAccessInstance) + { + return new[] { instance.Syntax }.Concat(arguments); + } + + // We need to rebuild a new conditional access chain skipping the invocation + if (conditionalAccessMembers.Count == 0) + { + throw new InvalidOperationException("Invocation contains conditional expression but we could not detect the parts."); + } + else if (conditionalAccessMembers.Count == 1) + { + return new[] { conditionalAccessMembers[0] }.Concat(arguments); + } + else + { + var currentExpression = conditionalAccessMembers.Count > 2 + ? CreateConditionalAccessExpression(conditionalAccessMembers[1], conditionalAccessMembers[0]) + : conditionalAccessMembers[0]; + + for (int i = 2; i < conditionalAccessMembers.Count - 1; i++) + { + currentExpression = CreateConditionalAccessExpression(conditionalAccessMembers[i], currentExpression); + } + + currentExpression = CreateConditionalAccessExpression(conditionalAccessMembers[^1], currentExpression); + return new[] { currentExpression }.Concat(arguments); + } + } + + private static async Task ConvertToEqualsAsync(Document document, SemanticModel semanticModel, IBinaryOperation binaryOperation, CancellationToken cancellationToken) + { + var expression = binaryOperation.Syntax; + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + var generator = editor.Generator; + + var replacement = CompareSymbolsCorrectlyAnalyzer.UseSymbolEqualityComparer(semanticModel.Compilation) switch + { + true => + generator.InvocationExpression( + GetEqualityComparerDefaultEquals(generator), + binaryOperation.LeftOperand.Syntax.WithoutLeadingTrivia(), + binaryOperation.RightOperand.Syntax.WithoutTrailingTrivia()), + + false => + generator.InvocationExpression( + generator.MemberAccessExpression( + generator.TypeExpression(semanticModel.Compilation.GetSpecialType(SpecialType.System_Object)), + nameof(object.Equals)), + binaryOperation.LeftOperand.Syntax.WithoutLeadingTrivia(), + binaryOperation.RightOperand.Syntax.WithoutTrailingTrivia()) + }; + + if (binaryOperation.OperatorKind == BinaryOperatorKind.NotEquals) + { + replacement = generator.LogicalNotExpression(replacement); + } + + editor.ReplaceNode(expression, replacement.WithTriviaFrom(expression)); + return editor.GetChangedDocument(); + } + + private static SyntaxNode GetEqualityComparerDefaultEquals(SyntaxGenerator generator) + => generator.MemberAccessExpression( + GetEqualityComparerDefault(generator), + nameof(object.Equals)); + + private static SyntaxNode GetEqualityComparerDefault(SyntaxGenerator generator) + => generator.MemberAccessExpression(generator.DottedName(CompareSymbolsCorrectlyAnalyzer.SymbolEqualityComparerName), "Default"); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ConfigureGeneratedCodeAnalysisFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ConfigureGeneratedCodeAnalysisFix.cs new file mode 100644 index 0000000000000..0dc933b4d7378 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/ConfigureGeneratedCodeAnalysisFix.cs @@ -0,0 +1,88 @@ +// 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 System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public abstract class ConfigureGeneratedCodeAnalysisFix : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(ConfigureGeneratedCodeAnalysisAnalyzer.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeAnalysisDiagnosticsResources.ConfigureGeneratedCodeAnalysisFix, + cancellationToken => ConfigureGeneratedCodeAnalysisAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(ConfigureGeneratedCodeAnalysisFix)), + diagnostic); + } + + return Task.CompletedTask; + } + + private async Task ConfigureGeneratedCodeAnalysisAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var generatedCodeAnalysisFlags = semanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsGeneratedCodeAnalysisFlags); + if (generatedCodeAnalysisFlags is null) + { + return document; + } + + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var analysisContextParameter = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var parameterDeclaration = generator.TryGetContainingDeclaration(analysisContextParameter, DeclarationKind.Parameter); + if (parameterDeclaration is null) + { + return document; + } + + var methodDeclaration = generator.TryGetContainingDeclaration(parameterDeclaration.Parent, DeclarationKind.Method); + if (methodDeclaration is null) + { + return document; + } + + var statements = GetStatements(methodDeclaration); + var newInvocation = generator.InvocationExpression( + generator.MemberAccessExpression( + generator.IdentifierName(generator.GetName(parameterDeclaration)), + nameof(AnalysisContext.ConfigureGeneratedCodeAnalysis)), + generator.BitwiseOrExpression( + generator.MemberAccessExpression( + generator.TypeExpressionForStaticMemberAccess(generatedCodeAnalysisFlags), + nameof(GeneratedCodeAnalysisFlags.Analyze)), + generator.MemberAccessExpression( + generator.TypeExpressionForStaticMemberAccess(generatedCodeAnalysisFlags), + nameof(GeneratedCodeAnalysisFlags.ReportDiagnostics)))); + + var newStatements = new SyntaxNode[] { newInvocation }.Concat(statements); + var newMethodDeclaration = generator.WithStatements(methodDeclaration, newStatements); + return document.WithSyntaxRoot(root.ReplaceNode(methodDeclaration, newMethodDeclaration)); + } + + protected abstract IEnumerable GetStatements(SyntaxNode methodDeclaration); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.CustomFixAllProvider.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.CustomFixAllProvider.cs new file mode 100644 index 0000000000000..86cdb92e1184a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.CustomFixAllProvider.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public sealed partial class DefineDiagnosticDescriptorArgumentsCorrectlyFix : CodeFixProvider + { + private sealed class CustomFixAllProvider : FixAllProvider + { + public static CustomFixAllProvider Instance = new(); + + private CustomFixAllProvider() + { + } + + public override async Task GetFixAsync(FixAllContext fixAllContext) + { + // FixAll for source document fixes are handled fine by the batch fixer. + if (fixAllContext.CodeActionEquivalenceKey!.EndsWith(SourceDocumentEquivalenceKeySuffix, StringComparison.Ordinal)) + { + return await WellKnownFixAllProviders.BatchFixer.GetFixAsync(fixAllContext).ConfigureAwait(false); + } + + // We need custom FixAll handling for additional document fixes. + Debug.Assert(fixAllContext.CodeActionEquivalenceKey.EndsWith(AdditionalDocumentEquivalenceKeySuffix, StringComparison.Ordinal)); + + var diagnosticsToFix = new List>>(); + switch (fixAllContext.Scope) + { + case FixAllScope.Document: + { + ImmutableArray diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document!).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + break; + } + + case FixAllScope.Project: + { + Project project = fixAllContext.Project; + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + break; + } + + case FixAllScope.Solution: + { + foreach (Project project in fixAllContext.Solution.Projects) + { + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(project, diagnostics)); + } + + break; + } + + case FixAllScope.Custom: + return null; + + default: + Debug.Fail($"Unknown FixAllScope '{fixAllContext.Scope}'"); + return null; + } + + return new FixAllAdditionalDocumentChangeAction(fixAllContext.Scope, fixAllContext.Solution, diagnosticsToFix, fixAllContext.CodeActionEquivalenceKey); + } + + private sealed class FixAllAdditionalDocumentChangeAction : CodeAction + { + private readonly List>> _diagnosticsToFix; + private readonly Solution _solution; + + public FixAllAdditionalDocumentChangeAction(FixAllScope fixAllScope, Solution solution, List>> diagnosticsToFix, string equivalenceKey) + { + this.Title = fixAllScope.ToString(); + _solution = solution; + _diagnosticsToFix = diagnosticsToFix; + this.EquivalenceKey = equivalenceKey; + } + + public override string Title { get; } + public override string EquivalenceKey { get; } + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + // Group fixes by additional documents. + var fixInfoMap = new Dictionary>(); + foreach (var kvp in _diagnosticsToFix) + { + var project = kvp.Key; + var diagnostics = kvp.Value; + + var additionalDocuments = project.AdditionalDocuments.ToImmutableArray(); + foreach (var diagnostic in diagnostics) + { + if (TryGetFixValue(diagnostic, out var fixValue) && + TryGetAdditionalDocumentFixInfo(diagnostic, fixValue, additionalDocuments, out var fixInfo)) + { + var additionalDocument = fixInfo.Value.AdditionalDocumentToFix; + RoslynDebug.Assert(additionalDocument != null); + if (!fixInfoMap.TryGetValue(additionalDocument, out var fixInfos)) + { + fixInfos = new List(); + fixInfoMap.Add(additionalDocument, fixInfos); + } + + fixInfos.Add(fixInfo.Value); + } + } + } + + var newSolution = _solution; + foreach (var kvp in fixInfoMap) + { + var additionalDocument = kvp.Key; + var fixInfos = kvp.Value; + + var text = await additionalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + using var textChanges = ArrayBuilder.GetInstance(fixInfos.Count); + using var seenInputSpansToFix = PooledHashSet.GetInstance(); + foreach (var fixInfo in fixInfos) + { + var inputSpanToFix = fixInfo.AdditionalDocumentSpanToFix!.Value; + if (seenInputSpansToFix.Add(inputSpanToFix)) + { + textChanges.Add(new TextChange(inputSpanToFix, fixInfo.FixValue)); + } + } + + var newText = text.WithChanges(textChanges); + newSolution = newSolution.WithAdditionalDocumentText(additionalDocument.Id, newText); + } + + return newSolution; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.cs new file mode 100644 index 0000000000000..8d476ef883ab0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/DefineDiagnosticDescriptorArgumentsCorrectlyFix.cs @@ -0,0 +1,179 @@ +// 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.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed partial class DefineDiagnosticDescriptorArgumentsCorrectlyFix() : CodeFixProvider + { + private const string SourceDocumentEquivalenceKeySuffix = nameof(SourceDocumentEquivalenceKeySuffix); + private const string AdditionalDocumentEquivalenceKeySuffix = nameof(AdditionalDocumentEquivalenceKeySuffix); + + public override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId, + DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId, + DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId); + + public override FixAllProvider GetFixAllProvider() => CustomFixAllProvider.Instance; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span, getInnermostNodeForTie: true); + if (node is null) + { + return; + } + + var additionalDocuments = context.Document.Project.AdditionalDocuments.ToImmutableArray(); + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + foreach (var diagnostic in context.Diagnostics) + { + if (!TryGetFixInfo(diagnostic, root, semanticModel, additionalDocuments, + context.CancellationToken, out var fixInfo)) + { + continue; + } + + var codeFixTitle = diagnostic.Id switch + { + DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId => CodeAnalysisDiagnosticsResources.DefineDiagnosticTitleCorrectlyTitle, + DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId => CodeAnalysisDiagnosticsResources.DefineDiagnosticMessageCorrectlyTitle, + DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId => CodeAnalysisDiagnosticsResources.DefineDiagnosticDescriptionCorrectlyTitle, + _ => throw new InvalidOperationException() + }; + + var equivalenceKeySuffix = fixInfo.Value.AdditionalDocumentToFix != null ? AdditionalDocumentEquivalenceKeySuffix : SourceDocumentEquivalenceKeySuffix; + var equivalenceKey = codeFixTitle + equivalenceKeySuffix; + + var codeAction = CodeAction.Create( + codeFixTitle, + ct => ApplyFixAsync(context.Document, root, fixInfo.Value, ct), + equivalenceKey); + context.RegisterCodeFix(codeAction, diagnostic); + } + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types + private readonly struct FixInfo +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public string FixValue { get; } + + public FixInfo(string fixValue, ILiteralOperation sourceLiteralAtLocationToFix) + { + FixValue = fixValue; + SourceLiteralAtLocationToFix = sourceLiteralAtLocationToFix; + AdditionalDocumentToFix = null; + AdditionalDocumentSpanToFix = null; + } + + public FixInfo(string fixValue, TextDocument additionalDocumentToFix, TextSpan additionalDocumentSpanToFix) + { + FixValue = fixValue; + AdditionalDocumentToFix = additionalDocumentToFix; + AdditionalDocumentSpanToFix = additionalDocumentSpanToFix; + SourceLiteralAtLocationToFix = null; + } + + public ILiteralOperation? SourceLiteralAtLocationToFix { get; } + public TextDocument? AdditionalDocumentToFix { get; } + public TextSpan? AdditionalDocumentSpanToFix { get; } + } + + private static bool TryGetFixInfo( + Diagnostic diagnostic, + SyntaxNode root, + SemanticModel model, + ImmutableArray additionalDocuments, + CancellationToken cancellationToken, + [NotNullWhen(returnValue: true)] out FixInfo? fixInfo) + { + fixInfo = null; + + if (!TryGetFixValue(diagnostic, out var fixValue)) + { + return false; + } + + if (diagnostic.AdditionalLocations.Count == 1) + { + var locationToFix = diagnostic.AdditionalLocations[0]; + if (locationToFix.IsInSource && + root.FindNode(locationToFix.SourceSpan, getInnermostNodeForTie: true) is { } fixNode && + model.GetOperation(fixNode, cancellationToken) is ILiteralOperation literal && + literal.ConstantValue.HasValue && + literal.ConstantValue.Value is string) + { + fixInfo = new FixInfo(fixValue, literal); + return true; + } + + return false; + } + + return TryGetAdditionalDocumentFixInfo(diagnostic, fixValue, additionalDocuments, out fixInfo); + } + + private static bool TryGetFixValue(Diagnostic diagnostic, [NotNullWhen(returnValue: true)] out string? fixValue) + => diagnostic.Properties.TryGetValue(DiagnosticDescriptorCreationAnalyzer.DefineDescriptorArgumentCorrectlyFixValue, out fixValue) && + !string.IsNullOrEmpty(fixValue); + + private static bool TryGetAdditionalDocumentFixInfo( + Diagnostic diagnostic, + string fixValue, + ImmutableArray additionalDocuments, + [NotNullWhen(returnValue: true)] out FixInfo? fixInfo) + { + if (DiagnosticDescriptorCreationAnalyzer.TryGetAdditionalDocumentLocationInfo(diagnostic, out var path, out var fixSpan) && + additionalDocuments.FirstOrDefault(a => string.Equals(a.FilePath, path, StringComparison.Ordinal)) is { } additionalDocument) + { + fixInfo = new FixInfo(fixValue, additionalDocument, fixSpan.Value); + return true; + } + + fixInfo = null; + return false; + } + + private static async Task ApplyFixAsync(Document document, SyntaxNode root, FixInfo fixInfo, CancellationToken cancellationToken) + { + if (fixInfo.SourceLiteralAtLocationToFix is { } literal) + { + RoslynDebug.Assert(literal.ConstantValue.HasValue && literal.ConstantValue.Value is string); + + var generator = SyntaxGenerator.GetGenerator(document); + var newLiteral = generator.LiteralExpression(fixInfo.FixValue).WithTriviaFrom(literal.Syntax); + var newRoot = root.ReplaceNode(literal.Syntax, newLiteral); + return document.WithSyntaxRoot(newRoot).Project.Solution; + } + else + { + RoslynDebug.Assert(fixInfo.AdditionalDocumentToFix != null); + RoslynDebug.Assert(fixInfo.AdditionalDocumentSpanToFix != null); + + var text = await fixInfo.AdditionalDocumentToFix.GetTextAsync(cancellationToken).ConfigureAwait(false); + var textChange = new TextChange(fixInfo.AdditionalDocumentSpanToFix.Value, fixInfo.FixValue); + var newText = text.WithChanges(textChange); + return document.Project.Solution.WithAdditionalDocumentText(fixInfo.AdditionalDocumentToFix.Id, newText); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/EnableConcurrentExecutionFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/EnableConcurrentExecutionFix.cs new file mode 100644 index 0000000000000..bf82669cf1def --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/EnableConcurrentExecutionFix.cs @@ -0,0 +1,73 @@ +// 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 System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public abstract class EnableConcurrentExecutionFix : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(EnableConcurrentExecutionAnalyzer.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeAnalysisDiagnosticsResources.EnableConcurrentExecutionFix, + cancellationToken => EnableConcurrentExecutionAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(EnableConcurrentExecutionFix)), + diagnostic); + } + + return Task.CompletedTask; + } + + private async Task EnableConcurrentExecutionAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var analysisContextParameter = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var parameterDeclaration = generator.TryGetContainingDeclaration(analysisContextParameter, DeclarationKind.Parameter); + if (parameterDeclaration is null) + { + return document; + } + + var methodDeclaration = generator.TryGetContainingDeclaration(parameterDeclaration.Parent, DeclarationKind.Method); + if (methodDeclaration is null) + { + return document; + } + + var statements = GetStatements(methodDeclaration); + var newInvocation = generator.InvocationExpression( + generator.MemberAccessExpression( + generator.IdentifierName(generator.GetName(parameterDeclaration)), + nameof(AnalysisContext.EnableConcurrentExecution))); + + var newStatements = new SyntaxNode[] { newInvocation }.Concat(statements).ToArray(); + var newMethodDeclaration = generator.WithStatements(methodDeclaration, newStatements); + return document.WithSyntaxRoot(root.ReplaceNode(methodDeclaration, newMethodDeclaration)); + } + + protected abstract IEnumerable GetStatements(SyntaxNode methodDeclaration); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/PreferIsKindFix.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/PreferIsKindFix.cs new file mode 100644 index 0000000000000..f9c870e37760c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Fixers/PreferIsKindFix.cs @@ -0,0 +1,79 @@ +// 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.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +{ + public abstract class PreferIsKindFix : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(PreferIsKindAnalyzer.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + CodeAnalysisDiagnosticsResources.PreferIsKindFix, + cancellationToken => ConvertKindToIsKindAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(PreferIsKindFix)), + diagnostic); + } + + return Task.CompletedTask; + } + + private async Task ConvertKindToIsKindAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + if (TryGetNodeToFix(editor.OriginalRoot, sourceSpan) is { } nodeToFix) + { + FixDiagnostic(editor, nodeToFix); + } + + return editor.GetChangedDocument(); + } + + protected abstract SyntaxNode? TryGetNodeToFix(SyntaxNode root, TextSpan span); + + protected abstract void FixDiagnostic(DocumentEditor editor, SyntaxNode nodeToFix); + + private sealed class CustomFixAllProvider : DocumentBasedFixAllProvider + { + private readonly PreferIsKindFix _fixer; + + public CustomFixAllProvider(PreferIsKindFix fixer) + { + _fixer = fixer; + } + + protected override string GetFixAllTitle(FixAllContext fixAllContext) => CodeAnalysisDiagnosticsResources.PreferIsKindFix; + + protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics) + { + var editor = await DocumentEditor.CreateAsync(document, fixAllContext.CancellationToken).ConfigureAwait(false); + foreach (var diagnostic in diagnostics) + { + var nodeToFix = _fixer.TryGetNodeToFix(editor.OriginalRoot, diagnostic.Location.SourceSpan); + if (nodeToFix is null) + continue; + + _fixer.FixDiagnostic(editor, nodeToFix); + } + + return editor.GetChangedDocument(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Helpers/DiagnosticWellKnownNames.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Helpers/DiagnosticWellKnownNames.cs new file mode 100644 index 0000000000000..1cf75aeaeaf68 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/Helpers/DiagnosticWellKnownNames.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 Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers +{ + internal static class DiagnosticWellKnownNames + { + internal const string RegisterSyntaxNodeActionName = nameof(AnalysisContext.RegisterSyntaxNodeAction); + internal const string RegisterSymbolActionName = nameof(AnalysisContext.RegisterSymbolAction); + internal const string RegisterCodeBlockStartActionName = nameof(AnalysisContext.RegisterCodeBlockStartAction); + internal const string RegisterCodeBlockEndActionName = nameof(CodeBlockStartAnalysisContext.RegisterCodeBlockEndAction); + internal const string RegisterCodeBlockActionName = nameof(AnalysisContext.RegisterCodeBlockAction); + internal const string RegisterOperationBlockStartActionName = nameof(AnalysisContext.RegisterOperationBlockStartAction); + internal const string RegisterOperationBlockEndActionName = nameof(OperationBlockStartAnalysisContext.RegisterOperationBlockEndAction); + internal const string RegisterOperationBlockActionName = nameof(AnalysisContext.RegisterOperationBlockAction); + internal const string RegisterOperationActionName = nameof(AnalysisContext.RegisterOperationAction); + internal const string RegisterCompilationStartActionName = nameof(AnalysisContext.RegisterCompilationStartAction); + internal const string RegisterCompilationEndActionName = nameof(CompilationStartAnalysisContext.RegisterCompilationEndAction); + internal const string RegisterCompilationActionName = nameof(AnalysisContext.RegisterCompilationAction); + internal const string ReportDiagnosticName = nameof(CompilationAnalysisContext.ReportDiagnostic); + internal const string SupportedDiagnosticsName = nameof(DiagnosticAnalyzer.SupportedDiagnostics); + internal const string TLanguageKindEnumName = @"TLanguageKindEnum"; + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/PreferIsKindAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/PreferIsKindAnalyzer.cs new file mode 100644 index 0000000000000..07231be671cb3 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/PreferIsKindAnalyzer.cs @@ -0,0 +1,97 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1034: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class PreferIsKindAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + DiagnosticIds.PreferIsKindRuleId, + CreateLocalizableResourceString(nameof(PreferIsKindTitle)), + CreateLocalizableResourceString(nameof(PreferIsKindMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(PreferIsKindDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + // Kind() methods + // Microsoft.CodeAnalysis.CSharp.CSharpExtensions.Kind + // Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions.Kind + // + // IsKind() methods + // Microsoft.CodeAnalysis.CSharpExtensions.IsKind + // Microsoft.CodeAnalysis.VisualBasicExtensions.IsKind + + Dictionary containingTypeMap = new Dictionary(); + if (context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpExtensions) is { } csharpKindExtensions + && context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpExtensions) is { } csharpIsKindExtensions) + { + containingTypeMap[csharpKindExtensions] = csharpIsKindExtensions; + } + + if (context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisVisualBasicVisualBasicExtensions) is { } vbKindExtensions + && context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisVisualBasicExtensions) is { } vbIsKindExtensions) + { + containingTypeMap[vbKindExtensions] = vbIsKindExtensions; + } + + if (containingTypeMap.Count > 0) + { + context.RegisterOperationAction(context => HandleBinaryOperation(context, containingTypeMap), OperationKind.Binary); + } + }); + } + + private static void HandleBinaryOperation(OperationAnalysisContext context, Dictionary containingTypeMap) + { + var operation = (IBinaryOperation)context.Operation; + if (operation.OperatorKind is not (BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals)) + { + return; + } + + var possibleInvocation = operation.LeftOperand.WalkDownConversion(); + if (possibleInvocation is IConditionalAccessOperation conditionalAccess) + { + // We don't currently report on Nullable. If we'll report that in the future, the codefix must behave correctly. + if (conditionalAccess.Operation.Type!.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) + { + return; + } + + possibleInvocation = conditionalAccess.WhenNotNull; + } + + if (possibleInvocation is IInvocationOperation { TargetMethod: { Name: "Kind", ContainingType: var containingType } } + && containingTypeMap.TryGetValue(containingType, out _)) + { + context.ReportDiagnostic(operation.LeftOperand.CreateDiagnostic(Rule)); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/RegisterActionAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/RegisterActionAnalyzer.cs new file mode 100644 index 0000000000000..ccca995f069c3 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/RegisterActionAnalyzer.cs @@ -0,0 +1,584 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1002: + /// RS1003: + /// RS1006: + /// RS1012: + /// RS1013: + /// + public abstract class RegisterActionAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer + where TInvocationExpressionSyntax : SyntaxNode + where TArgumentSyntax : SyntaxNode + where TLanguageKindEnum : struct + { + private static readonly LocalizableString s_localizableTitleMissingKindArgument = CreateLocalizableResourceString(nameof(MissingKindArgumentToRegisterActionTitle)); + private static readonly LocalizableString s_localizableDescriptionMissingKindArgument = CreateLocalizableResourceString(nameof(MissingKindArgumentToRegisterActionDescription)); + + public static readonly DiagnosticDescriptor MissingSymbolKindArgumentRule = new( + DiagnosticIds.MissingKindArgumentToRegisterActionRuleId, + s_localizableTitleMissingKindArgument, + CreateLocalizableResourceString(nameof(MissingSymbolKindArgumentToRegisterActionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescriptionMissingKindArgument, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor MissingSyntaxKindArgumentRule = new( + DiagnosticIds.MissingKindArgumentToRegisterActionRuleId, + s_localizableTitleMissingKindArgument, + CreateLocalizableResourceString(nameof(MissingSyntaxKindArgumentToRegisterActionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescriptionMissingKindArgument, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor MissingOperationKindArgumentRule = new( + DiagnosticIds.MissingKindArgumentToRegisterActionRuleId, + s_localizableTitleMissingKindArgument, + CreateLocalizableResourceString(nameof(MissingOperationKindArgumentToRegisterActionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescriptionMissingKindArgument, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor UnsupportedSymbolKindArgumentRule = new( + DiagnosticIds.UnsupportedSymbolKindArgumentRuleId, + CreateLocalizableResourceString(nameof(UnsupportedSymbolKindArgumentToRegisterActionTitle)), + CreateLocalizableResourceString(nameof(UnsupportedSymbolKindArgumentToRegisterActionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor InvalidSyntaxKindTypeArgumentRule = new( + DiagnosticIds.InvalidSyntaxKindTypeArgumentRuleId, + CreateLocalizableResourceString(nameof(InvalidSyntaxKindTypeArgumentTitle)), + CreateLocalizableResourceString(nameof(InvalidSyntaxKindTypeArgumentMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidSyntaxKindTypeArgumentDescription), nameof(DiagnosticWellKnownNames.TLanguageKindEnumName)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + private static readonly LocalizableString s_localizableDescriptionStatefulAnalyzerRegisterActionsDescription = CreateLocalizableResourceString(nameof(StatefulAnalyzerRegisterActionsDescription), nameof(DiagnosticWellKnownNames.TLanguageKindEnumName)); + + public static readonly DiagnosticDescriptor StartActionWithNoRegisteredActionsRule = new( + DiagnosticIds.StartActionWithNoRegisteredActionsRuleId, + CreateLocalizableResourceString(nameof(StartActionWithNoRegisteredActionsTitle)), + CreateLocalizableResourceString(nameof(StartActionWithNoRegisteredActionsMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescriptionStatefulAnalyzerRegisterActionsDescription, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor StartActionWithOnlyEndActionRule = new( + DiagnosticIds.StartActionWithOnlyEndActionRuleId, + CreateLocalizableResourceString(nameof(StartActionWithOnlyEndActionTitle)), + CreateLocalizableResourceString(nameof(StartActionWithOnlyEndActionMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: s_localizableDescriptionStatefulAnalyzerRegisterActionsDescription, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + MissingSymbolKindArgumentRule, + MissingSyntaxKindArgumentRule, + MissingOperationKindArgumentRule, + UnsupportedSymbolKindArgumentRule, + InvalidSyntaxKindTypeArgumentRule, + StartActionWithNoRegisteredActionsRule, + StartActionWithOnlyEndActionRule); + + protected override DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + Compilation compilation = compilationContext.Compilation; + + INamedTypeSymbol? analysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsAnalysisContext); + if (analysisContext == null) + { + return null; + } + + INamedTypeSymbol? compilationStartAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCompilationStartAnalysisContext); + if (compilationStartAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? codeBlockStartAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCodeBlockStartAnalysisContext1); + if (codeBlockStartAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? operationBlockStartAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsOperationBlockStartAnalysisContext); + if (operationBlockStartAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? symbolKind = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisSymbolKind); + if (symbolKind == null) + { + return null; + } + + compilationContext.RegisterCodeBlockStartAction(codeBlockContext => + { + RegisterActionCodeBlockAnalyzer analyzer = GetCodeBlockAnalyzer(compilation, analysisContext, compilationStartAnalysisContext, + codeBlockStartAnalysisContext, operationBlockStartAnalysisContext, symbolKind); + + analyzer.CodeBlockStartAction(codeBlockContext); + }); + + // We don't want to analyze DiagnosticAnalyzer type symbols, just the code blocks. + return null; + } + + protected abstract RegisterActionCodeBlockAnalyzer GetCodeBlockAnalyzer( + Compilation compilation, + INamedTypeSymbol analysisContext, + INamedTypeSymbol compilationStartAnalysisContext, + INamedTypeSymbol codeBlockStartAnalysisContext, + INamedTypeSymbol operationBlockStartAnalysisContext, + INamedTypeSymbol symbolKind); + + protected abstract class RegisterActionCodeBlockAnalyzer + { + private readonly INamedTypeSymbol _analysisContext; + private readonly INamedTypeSymbol _compilationStartAnalysisContext; + private readonly INamedTypeSymbol _codeBlockStartAnalysisContext; + private readonly INamedTypeSymbol _operationBlockStartAnalysisContext; + private readonly INamedTypeSymbol _symbolKind; + + private static readonly ImmutableHashSet s_supportedSymbolKinds = + ImmutableHashSet.Create( + nameof(SymbolKind.Event), + nameof(SymbolKind.Field), + nameof(SymbolKind.Method), + nameof(SymbolKind.NamedType), + nameof(SymbolKind.Namespace), + nameof(SymbolKind.Parameter), + nameof(SymbolKind.Property)); + +#pragma warning disable CA1815 // Override equals and operator equals on value types + private struct NodeAndSymbol +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public TInvocationExpressionSyntax Invocation { get; set; } + public IMethodSymbol Method { get; set; } + } + + /// + /// Map from declared analysis context type parameters to invocations of Register methods on them. + /// + private Dictionary>? _nestedActionsMap; + + /// + /// Set of declared start analysis context parameters that need to be analyzed for and . + /// + private HashSet? _declaredStartAnalysisContextParams; + + /// + /// Set of declared start analysis context parameters that need to be skipped for and . + /// This is to avoid false positives where context types are passed as arguments to a different invocation, and hence the registration responsibility is not on the current method. + /// + private HashSet? _startAnalysisContextParamsToSkip; + + protected RegisterActionCodeBlockAnalyzer( + INamedTypeSymbol analysisContext, + INamedTypeSymbol compilationStartAnalysisContext, + INamedTypeSymbol codeBlockStartAnalysisContext, + INamedTypeSymbol operationBlockStartAnalysisContext, + INamedTypeSymbol symbolKind) + { + _analysisContext = analysisContext; + _compilationStartAnalysisContext = compilationStartAnalysisContext; + _codeBlockStartAnalysisContext = codeBlockStartAnalysisContext; + _operationBlockStartAnalysisContext = operationBlockStartAnalysisContext; + _symbolKind = symbolKind; + + _nestedActionsMap = null; + _declaredStartAnalysisContextParams = null; + _startAnalysisContextParamsToSkip = null; + } + + protected abstract IEnumerable? GetArgumentExpressions(TInvocationExpressionSyntax invocation); + protected abstract SyntaxNode GetArgumentExpression(TArgumentSyntax argument); + protected abstract SyntaxNode GetInvocationExpression(TInvocationExpressionSyntax invocation); + protected abstract SyntaxNode? GetInvocationReceiver(TInvocationExpressionSyntax invocation); + protected abstract bool IsSyntaxKind(ITypeSymbol type); + protected abstract TLanguageKindEnum InvocationExpressionKind { get; } + protected abstract TLanguageKindEnum ArgumentSyntaxKind { get; } + protected abstract TLanguageKindEnum ParameterSyntaxKind { get; } + + internal void CodeBlockStartAction(CodeBlockStartAnalysisContext codeBlockContext) + { + var method = codeBlockContext.OwningSymbol as IMethodSymbol; + if (!ShouldAnalyze(method)) + { + return; + } + + foreach (IParameterSymbol param in method.Parameters) + { + AnalyzeParameterDeclaration(param); + } + + // Analyze all the Register action invocation expressions. + codeBlockContext.RegisterSyntaxNodeAction(AnalyzeInvocation, InvocationExpressionKind); + + // Analyze all the arguments to invocations. + codeBlockContext.RegisterSyntaxNodeAction(AnalyzeArgumentSyntax, ArgumentSyntaxKind); + + // Analyze all the lambda parameters in the method body, if any. + codeBlockContext.RegisterSyntaxNodeAction(AnalyzerParameterSyntax, ParameterSyntaxKind); + + // Report diagnostics based on the final state. + codeBlockContext.RegisterCodeBlockEndAction(CodeBlockEndAction); + } + + private bool ShouldAnalyze([NotNullWhen(returnValue: true)] IMethodSymbol? method) + { + if (method == null) + { + return false; + } + + // Only analyze this method if declares a parameter with one of the allowed analysis context types. + foreach (IParameterSymbol parameter in method.Parameters) + { + if (parameter.Type is INamedTypeSymbol namedType && + IsContextType(namedType, _analysisContext, _codeBlockStartAnalysisContext, _operationBlockStartAnalysisContext, _compilationStartAnalysisContext)) + { + return true; + } + } + + return false; + } + + private static bool IsContextType(ITypeSymbol type, params INamedTypeSymbol[] allowedContextTypes) + { + INamedTypeSymbol? namedType = (type as INamedTypeSymbol)?.OriginalDefinition; + if (namedType != null) + { + foreach (INamedTypeSymbol contextType in allowedContextTypes) + { + if (SymbolEqualityComparer.Default.Equals(namedType, contextType)) + { + return true; + } + } + } + + return false; + } + + private static bool IsRegisterAction(string expectedName, IMethodSymbol method, params INamedTypeSymbol[] allowedContainingTypes) + { + return method.Name.Equals(expectedName, StringComparison.Ordinal) && + IsContextType(method.ContainingType, allowedContainingTypes); + } + + private void AnalyzeInvocation(SyntaxNodeAnalysisContext context) + { + var invocation = (TInvocationExpressionSyntax)context.Node; + SemanticModel semanticModel = context.SemanticModel; + + ISymbol? symbol = semanticModel.GetSymbolInfo(invocation, context.CancellationToken).Symbol; + if (symbol == null || symbol.Kind != SymbolKind.Method || !symbol.Name.StartsWith("Register", StringComparison.Ordinal)) + { + return; + } + + var method = (IMethodSymbol)symbol; + NoteRegisterActionInvocation(method, invocation, semanticModel, context.CancellationToken); + + bool isRegisterSymbolAction = IsRegisterAction(DiagnosticWellKnownNames.RegisterSymbolActionName, method, _analysisContext, _compilationStartAnalysisContext); + bool isRegisterSyntaxNodeAction = IsRegisterAction(DiagnosticWellKnownNames.RegisterSyntaxNodeActionName, method, _analysisContext, _compilationStartAnalysisContext, _codeBlockStartAnalysisContext); + bool isRegisterCodeBlockStartAction = IsRegisterAction(DiagnosticWellKnownNames.RegisterCodeBlockStartActionName, method, _analysisContext, _compilationStartAnalysisContext); + bool isRegisterOperationAction = IsRegisterAction(DiagnosticWellKnownNames.RegisterOperationActionName, method, _analysisContext, _compilationStartAnalysisContext, _operationBlockStartAnalysisContext); + + if ((isRegisterSymbolAction || isRegisterSyntaxNodeAction || isRegisterOperationAction) && + method.Parameters.Length == 2 && method.Parameters[1].IsParams) + { + IEnumerable? arguments = GetArgumentExpressions(invocation); + if (arguments != null) + { + int argumentCount = arguments.Count(); + if (argumentCount >= 1) + { + ITypeSymbol? type = semanticModel.GetTypeInfo(arguments.First(), context.CancellationToken).ConvertedType; + if (type == null || type.Name.Equals(nameof(Action), StringComparison.Ordinal)) + { + if (argumentCount == 1) + { + DiagnosticDescriptor rule; + if (isRegisterSymbolAction) + { + rule = MissingSymbolKindArgumentRule; + } + else if (isRegisterOperationAction) + { + rule = MissingOperationKindArgumentRule; + } + else + { + rule = MissingSyntaxKindArgumentRule; + } + + SyntaxNode invocationExpression = GetInvocationExpression(invocation); + Diagnostic diagnostic = invocationExpression.CreateDiagnostic(rule); + context.ReportDiagnostic(diagnostic); + } + else if (isRegisterSymbolAction) + { + foreach (SyntaxNode argument in arguments.Skip(1)) + { + symbol = semanticModel.GetSymbolInfo(argument, context.CancellationToken).Symbol; + if (symbol != null && + symbol.Kind == SymbolKind.Field && + SymbolEqualityComparer.Default.Equals(_symbolKind, symbol.ContainingType) && + !s_supportedSymbolKinds.Contains(symbol.Name)) + { + Diagnostic diagnostic = argument.CreateDiagnostic(UnsupportedSymbolKindArgumentRule, symbol.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } + } + } + + if (!method.TypeParameters.IsEmpty && + (isRegisterSyntaxNodeAction || isRegisterCodeBlockStartAction)) + { + ITypeSymbol? typeArgument = null; + if (method.TypeParameters.Length == 1) + { + if (method.TypeParameters[0].Name == DiagnosticWellKnownNames.TLanguageKindEnumName) + { + typeArgument = method.TypeArguments[0]; + } + } + else + { + ITypeParameterSymbol? typeParam = method.TypeParameters.FirstOrDefault(t => t.Name == DiagnosticWellKnownNames.TLanguageKindEnumName); + if (typeParam != null) + { + int index = method.TypeParameters.IndexOf(typeParam); + typeArgument = method.TypeArguments[index]; + } + } + + if (typeArgument != null && + typeArgument.TypeKind != TypeKind.TypeParameter && + typeArgument.TypeKind != TypeKind.Error && + !IsSyntaxKind(typeArgument)) + { + Location location = typeArgument.Locations[0]; + if (!location.IsInSource) + { + SyntaxNode invocationExpression = GetInvocationExpression(invocation); + location = invocationExpression.GetLocation(); + } + + Diagnostic diagnostic = Diagnostic.Create(InvalidSyntaxKindTypeArgumentRule, location, typeArgument.Name, DiagnosticWellKnownNames.TLanguageKindEnumName, method.Name); + context.ReportDiagnostic(diagnostic); + } + } + } + + private void AnalyzeArgumentSyntax(SyntaxNodeAnalysisContext context) + { + SyntaxNode argumentExpression = GetArgumentExpression((TArgumentSyntax)context.Node); + if (argumentExpression != null && + context.SemanticModel.GetSymbolInfo(argumentExpression, context.CancellationToken).Symbol is IParameterSymbol parameter) + { + AnalyzeParameterReference(parameter); + } + } + + private void AnalyzerParameterSyntax(SyntaxNodeAnalysisContext context) + { + if (context.SemanticModel.GetDeclaredSymbol(context.Node, context.CancellationToken) is IParameterSymbol parameter) + { + AnalyzeParameterDeclaration(parameter); + } + } + + private void AnalyzeParameterDeclaration(IParameterSymbol parameter) + { + if (IsContextType(parameter.Type, _compilationStartAnalysisContext, _codeBlockStartAnalysisContext, _operationBlockStartAnalysisContext)) + { + _declaredStartAnalysisContextParams ??= new HashSet(); + _declaredStartAnalysisContextParams.Add(parameter); + } + } + + private void AnalyzeParameterReference(IParameterSymbol parameter) + { + // We skip analysis for context parameters that are passed as arguments to any invocation. + // This is to avoid false positives, as the registration responsibility is not on the current method. + if (IsContextType(parameter.Type, _compilationStartAnalysisContext, _codeBlockStartAnalysisContext, _operationBlockStartAnalysisContext)) + { + _startAnalysisContextParamsToSkip ??= new HashSet(); + _startAnalysisContextParamsToSkip.Add(parameter); + } + } + + private void NoteRegisterActionInvocation(IMethodSymbol method, TInvocationExpressionSyntax invocation, SemanticModel model, CancellationToken cancellationToken) + { + if (SymbolEqualityComparer.Default.Equals(method.ContainingType, _analysisContext)) + { + // Not a nested action. + return; + } + + SyntaxNode? receiver = GetInvocationReceiver(invocation); + if (receiver == null) + { + return; + } + + // Get the context parameter on which we are registering an action. + if (model.GetSymbolInfo(receiver, cancellationToken).Symbol is not IParameterSymbol contextParameter) + { + return; + } + + // Check if we are bailing out on this context parameter. + if (_startAnalysisContextParamsToSkip != null && _startAnalysisContextParamsToSkip.Contains(contextParameter)) + { + return; + } + + _nestedActionsMap ??= new Dictionary>(); + if (!_nestedActionsMap.TryGetValue(contextParameter, out List registerInvocations)) + { + registerInvocations = new List(); + } + + registerInvocations.Add(new NodeAndSymbol { Invocation = invocation, Method = method }); + _nestedActionsMap[contextParameter] = registerInvocations; + } + + private void CodeBlockEndAction(CodeBlockAnalysisContext codeBlockContext) + { + if (_declaredStartAnalysisContextParams == null) + { + // No declared context parameters to analyze. + return; + } + + foreach (IParameterSymbol contextParameter in _declaredStartAnalysisContextParams) + { + // Check if we must bail out on this context parameter. + if (_startAnalysisContextParamsToSkip != null && _startAnalysisContextParamsToSkip.Contains(contextParameter)) + { + continue; + } + + var hasEndAction = false; + var hasNonEndAction = false; + + if (_nestedActionsMap != null && _nestedActionsMap.TryGetValue(contextParameter, out List registeredActions)) + { + foreach (NodeAndSymbol invocationInfo in registeredActions) + { + switch (invocationInfo.Method.Name) + { + case DiagnosticWellKnownNames.RegisterCompilationEndActionName: + case DiagnosticWellKnownNames.RegisterCodeBlockEndActionName: + case DiagnosticWellKnownNames.RegisterOperationBlockEndActionName: + hasEndAction = true; + break; + + default: + hasNonEndAction = true; + break; + } + } + } + + // Report a diagnostic if no non-end actions were registered on start analysis context parameter. + if (!hasNonEndAction) + { + ReportDiagnostic(codeBlockContext, contextParameter, hasEndAction); + } + } + } + + private void ReportDiagnostic(CodeBlockAnalysisContext codeBlockContext, IParameterSymbol contextParameter, bool hasEndAction) + { + Debug.Assert(IsContextType(contextParameter.Type, _codeBlockStartAnalysisContext, _compilationStartAnalysisContext, _operationBlockStartAnalysisContext)); + bool isCompilationStartAction = SymbolEqualityComparer.Default.Equals(contextParameter.Type.OriginalDefinition, _compilationStartAnalysisContext.OriginalDefinition); + bool isOperationBlockStartAction = !isCompilationStartAction && SymbolEqualityComparer.Default.Equals(contextParameter.Type.OriginalDefinition, _operationBlockStartAnalysisContext.OriginalDefinition); + + Location location = contextParameter.DeclaringSyntaxReferences.First() + .GetSyntax(codeBlockContext.CancellationToken).GetLocation(); + + string parameterName = contextParameter.Name; + string endActionName; + string statelessActionName; + string parentRegisterMethodName; + if (isCompilationStartAction) + { + endActionName = "CompilationEndAction"; + statelessActionName = DiagnosticWellKnownNames.RegisterCompilationActionName; + parentRegisterMethodName = "Initialize"; + } + else if (isOperationBlockStartAction) + { + endActionName = "OperationBlockEndAction"; + statelessActionName = DiagnosticWellKnownNames.RegisterOperationBlockActionName; + parentRegisterMethodName = "Initialize, CompilationStartAction"; + } + else + { + endActionName = "CodeBlockEndAction"; + statelessActionName = DiagnosticWellKnownNames.RegisterCodeBlockActionName; + parentRegisterMethodName = "Initialize, CompilationStartAction"; + } + + Diagnostic diagnostic; + if (!hasEndAction) + { + diagnostic = Diagnostic.Create(StartActionWithNoRegisteredActionsRule, location, parameterName, parentRegisterMethodName); + } + else + { + diagnostic = Diagnostic.Create(StartActionWithOnlyEndActionRule, location, parameterName, endActionName, statelessActionName, parentRegisterMethodName); + } + + codeBlockContext.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReleaseTrackingHelper.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReleaseTrackingHelper.cs new file mode 100644 index 0000000000000..4c8607999f0f5 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReleaseTrackingHelper.cs @@ -0,0 +1,522 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text.RegularExpressions; +using Analyzer.Utilities; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Text; + +#if MICROSOFT_CODEANALYSIS_ANALYZERS +using Analyzer.Utilities.Extensions; +#endif + +namespace Microsoft.CodeAnalysis.ReleaseTracking +{ + internal static class ReleaseTrackingHelper + { + internal const string ShippedFileName = "AnalyzerReleases.Shipped.md"; + internal const string UnshippedFileName = "AnalyzerReleases.Unshipped.md"; + internal const string DisabledText = "Disabled"; + internal const string UndetectedText = @"``"; + internal const string ReleasePrefix = "## Release"; + internal const string TableTitleNewRules = "### New Rules"; + internal const string TableTitleRemovedRules = "### Removed Rules"; + internal const string TableTitleChangedRules = "### Changed Rules"; + internal const string TableHeaderNewOrRemovedRulesLine1 = @"Rule ID | Category | Severity | Notes"; + internal const string TableHeaderNewOrRemovedRulesLine2 = @"--------|----------|----------|-------"; + internal const string TableHeaderChangedRulesLine1 = @"Rule ID | New Category | New Severity | Old Category | Old Severity | Notes"; + internal const string TableHeaderChangedRulesLine2 = @"--------|--------------|--------------|--------------|--------------|-------"; + internal const string TableHeaderNewOrRemovedRulesLine1RegexPattern = @"^\s*Rule ID\s*\|\s*Category\s*\|\s*\Severity\s*\|\s*Notes"; + internal const string TableHeaderChangedRulesLine1RegexPattern = @"^\s*Rule ID\s*\|\s*New Category\s*\|\s*New Severity\s*\|\s*Old Category\s*\|\s*Old Severity\s*\|\s*Notes"; + internal const string TableHeaderNewOrRemovedRulesLine2RegexPattern = @"^-{3,}\|-{3,}\|-{3,}\|-{3,}"; + internal const string TableHeaderChangedRulesLine2RegexPattern = @"^-{3,}\|-{3,}\|-{3,}\|-{3,}\|-{3,}\|-{3,}"; + + internal static Version UnshippedVersion { get; } = new Version(int.MaxValue, int.MaxValue); + + internal static ReleaseTrackingData ReadReleaseTrackingData( + string path, + SourceText sourceText, + Action onDuplicateEntryInRelease, + Action onInvalidEntry, + bool isShippedFile) + { + var releaseTrackingDataByRulesBuilder = new Dictionary(); + var currentVersion = UnshippedVersion; + ReleaseTrackingHeaderKind? expectedHeaderKind = isShippedFile ? ReleaseTrackingHeaderKind.ReleaseHeader : ReleaseTrackingHeaderKind.TableHeaderTitle; + ReleaseTrackingRuleEntryKind? currentRuleEntryKind = null; + using var versionsBuilder = PooledHashSet.GetInstance(); + + foreach (TextLine line in sourceText.Lines) + { + string lineText = line.ToString().Trim(); + if (string.IsNullOrWhiteSpace(lineText) || lineText.StartsWith(";", StringComparison.Ordinal)) + { + // Skip blank and comment lines. + continue; + } + + // Parse release header if applicable. + switch (expectedHeaderKind) + { + case ReleaseTrackingHeaderKind.ReleaseHeader: + // Parse new release, if any. + if (lineText.StartsWith(ReleasePrefix, StringComparison.OrdinalIgnoreCase)) + { + // Expect new table after this line. + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderTitle; + + // Parse the release version. + string versionString = lineText[ReleasePrefix.Length..].Trim(); + if (!Version.TryParse(versionString, out var version)) + { + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + } + else + { + currentVersion = version; + versionsBuilder.Add(version); + } + + continue; + } + + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + + case ReleaseTrackingHeaderKind.TableHeaderTitle: + if (lineText.StartsWith(TableTitleNewRules, StringComparison.OrdinalIgnoreCase)) + { + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderNewOrRemovedRulesLine1; + currentRuleEntryKind = ReleaseTrackingRuleEntryKind.New; + } + else if (lineText.StartsWith(TableTitleRemovedRules, StringComparison.OrdinalIgnoreCase)) + { + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderNewOrRemovedRulesLine1; + currentRuleEntryKind = ReleaseTrackingRuleEntryKind.Removed; + } + else if (lineText.StartsWith(TableTitleChangedRules, StringComparison.OrdinalIgnoreCase)) + { + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderChangedRulesLine1; + currentRuleEntryKind = ReleaseTrackingRuleEntryKind.Changed; + } + else + { + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + } + + continue; + + case ReleaseTrackingHeaderKind.TableHeaderNewOrRemovedRulesLine1: + if (Regex.IsMatch(lineText, TableHeaderNewOrRemovedRulesLine1RegexPattern, RegexOptions.IgnoreCase)) + { + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderNewOrRemovedRulesLine2; + continue; + } + + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + + case ReleaseTrackingHeaderKind.TableHeaderNewOrRemovedRulesLine2: + expectedHeaderKind = null; + if (Regex.IsMatch(lineText, TableHeaderNewOrRemovedRulesLine2RegexPattern, RegexOptions.IgnoreCase)) + { + continue; + } + + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + + case ReleaseTrackingHeaderKind.TableHeaderChangedRulesLine1: + if (Regex.IsMatch(lineText, TableHeaderChangedRulesLine1RegexPattern, RegexOptions.IgnoreCase)) + { + expectedHeaderKind = ReleaseTrackingHeaderKind.TableHeaderChangedRulesLine2; + continue; + } + + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + + case ReleaseTrackingHeaderKind.TableHeaderChangedRulesLine2: + expectedHeaderKind = null; + if (Regex.IsMatch(lineText, TableHeaderChangedRulesLine2RegexPattern, RegexOptions.IgnoreCase)) + { + continue; + } + + OnInvalidEntry(line, InvalidEntryKind.Header); + return ReleaseTrackingData.Default; + + default: + // We might be starting a new release or table. + if (lineText.StartsWith("## ", StringComparison.OrdinalIgnoreCase)) + { + goto case ReleaseTrackingHeaderKind.ReleaseHeader; + } + else if (lineText.StartsWith("### ", StringComparison.OrdinalIgnoreCase)) + { + goto case ReleaseTrackingHeaderKind.TableHeaderTitle; + } + + break; + } + + RoslynDebug.Assert(currentRuleEntryKind != null); + + var parts = lineText.Split('|').Select(s => s.Trim()).ToArray(); + if (IsInvalidEntry(parts, currentRuleEntryKind.Value)) + { + // Report invalid entry, but continue parsing remaining entries. + OnInvalidEntry(line, InvalidEntryKind.Other); + continue; + } + + // New or Removed rule entry: + // "Rule ID | Category | Severity | Notes" + // " 0 | 1 | 2 | 3 " + // + // Changed rule entry: + // "Rule ID | New Category | New Severity | Old Category | Old Severity | Notes" + // " 0 | 1 | 2 | 3 | 4 | 5 " + + string ruleId = parts[0]; + + InvalidEntryKind? invalidEntryKind = TryParseFields(parts, categoryIndex: 1, severityIndex: 2, + out var category, out var defaultSeverity, out var enabledByDefault); + if (invalidEntryKind.HasValue) + { + OnInvalidEntry(line, invalidEntryKind.Value); + } + + ReleaseTrackingLine releaseTrackingLine; + if (currentRuleEntryKind.Value == ReleaseTrackingRuleEntryKind.Changed) + { + invalidEntryKind = TryParseFields(parts, categoryIndex: 3, severityIndex: 4, + out var oldCategory, out var oldDefaultSeverity, out var oldEnabledByDefault); + if (invalidEntryKind.HasValue) + { + OnInvalidEntry(line, invalidEntryKind.Value); + } + + // Verify at least one field is changed for the entry: + if (string.Equals(category, oldCategory, StringComparison.OrdinalIgnoreCase) && + defaultSeverity == oldDefaultSeverity && + enabledByDefault == oldEnabledByDefault) + { + OnInvalidEntry(line, InvalidEntryKind.Other); + return ReleaseTrackingData.Default; + } + + releaseTrackingLine = new ChangedRuleReleaseTrackingLine(ruleId, + category, enabledByDefault, defaultSeverity, + oldCategory, oldEnabledByDefault, oldDefaultSeverity, + line.Span, sourceText, path, isShippedFile); + } + else + { + releaseTrackingLine = new NewOrRemovedRuleReleaseTrackingLine(ruleId, + category, enabledByDefault, defaultSeverity, line.Span, sourceText, + path, isShippedFile, currentRuleEntryKind.Value); + } + + if (!releaseTrackingDataByRulesBuilder.TryGetValue(ruleId, out var releaseTrackingDataForRuleBuilder)) + { + releaseTrackingDataForRuleBuilder = new ReleaseTrackingDataForRuleBuilder(); + releaseTrackingDataByRulesBuilder.Add(ruleId, releaseTrackingDataForRuleBuilder); + } + + releaseTrackingDataForRuleBuilder.AddEntry(currentVersion, releaseTrackingLine, out var hasExistingEntry); + if (hasExistingEntry) + { + onDuplicateEntryInRelease(ruleId, currentVersion, path, sourceText, line); + } + } + + var builder = ImmutableSortedDictionary.CreateBuilder(); + foreach (var (ruleId, value) in releaseTrackingDataByRulesBuilder) + { + var releaseTrackingDataForRule = new ReleaseTrackingDataForRule(ruleId, value); + builder.Add(ruleId, releaseTrackingDataForRule); + } + + return new ReleaseTrackingData(builder.ToImmutable(), versionsBuilder.ToImmutable()); + + // Local functions + void OnInvalidEntry(TextLine line, InvalidEntryKind invalidEntryKind) + => onInvalidEntry(line, invalidEntryKind, path, sourceText); + + static bool IsInvalidEntry(string[] parts, ReleaseTrackingRuleEntryKind currentRuleEntryKind) + { + // Expected entry for New or Removed rules has 3 or 4 parts: + // "Rule ID | Category | Severity | Notes" + // + // Expected entry for Changed rules has 5 or 6 parts: + // "Rule ID | New Category | New Severity | Old Category | Old Severity | Notes" + // + // NOTE: Last field 'Helplink' is optional for both cases. + + if (parts.Length is < 3 or > 6) + { + return true; + } + + return currentRuleEntryKind switch + { + ReleaseTrackingRuleEntryKind.New => parts.Length > 4, + ReleaseTrackingRuleEntryKind.Removed => parts.Length > 4, + ReleaseTrackingRuleEntryKind.Changed => parts.Length <= 4, + _ => throw new NotImplementedException() + }; + } + + static InvalidEntryKind? TryParseFields( + string[] parts, int categoryIndex, int severityIndex, + out string category, + out DiagnosticSeverity? defaultSeverity, + out bool? enabledByDefault) + { + InvalidEntryKind? invalidEntryKind = null; + + category = parts[categoryIndex]; + if (category.Equals(UndetectedText, StringComparison.OrdinalIgnoreCase)) + { + invalidEntryKind = InvalidEntryKind.UndetectedField; + } + + var severityPart = parts[severityIndex]; + if (Enum.TryParse(severityPart, ignoreCase: true, out DiagnosticSeverity parsedSeverity)) + { + defaultSeverity = parsedSeverity; + enabledByDefault = true; + } + else + { + defaultSeverity = null; + if (string.Equals(severityPart, DisabledText, StringComparison.OrdinalIgnoreCase)) + { + enabledByDefault = false; + } + else if (severityPart.Equals(UndetectedText, StringComparison.OrdinalIgnoreCase)) + { + enabledByDefault = null; + invalidEntryKind = InvalidEntryKind.UndetectedField; + } + else + { + enabledByDefault = null; + invalidEntryKind = InvalidEntryKind.Other; + } + } + + return invalidEntryKind; + } + } + } + + internal enum InvalidEntryKind + { + Header, + UndetectedField, + Other + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal sealed class ReleaseTrackingData +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public static readonly ReleaseTrackingData Default = new(); + public ImmutableSortedDictionary ReleaseTrackingDataByRuleIdMap { get; } + public ImmutableHashSet Versions { get; } + + private ReleaseTrackingData() + : this(ImmutableSortedDictionary.Empty, ImmutableHashSet.Empty) + { + } + + internal ReleaseTrackingData(ImmutableSortedDictionary releaseTrackingDataByRuleIdMap, ImmutableHashSet versions) + { + ReleaseTrackingDataByRuleIdMap = releaseTrackingDataByRuleIdMap; + Versions = versions; + } + + public bool TryGetLatestReleaseTrackingLine( + string ruleId, + [NotNullWhen(returnValue: true)] out Version? version, + [NotNullWhen(returnValue: true)] out ReleaseTrackingLine? releaseTrackingLine) + => TryGetLatestReleaseTrackingLineCore(ruleId, maxVersion: null, out version, out releaseTrackingLine); + + public bool TryGetLatestReleaseTrackingLine( + string ruleId, + Version maxVersion, + [NotNullWhen(returnValue: true)] out Version? version, + [NotNullWhen(returnValue: true)] out ReleaseTrackingLine? releaseTrackingLine) + => TryGetLatestReleaseTrackingLineCore(ruleId, maxVersion, out version, out releaseTrackingLine); + + private bool TryGetLatestReleaseTrackingLineCore( + string ruleId, + Version? maxVersion, + [NotNullWhen(returnValue: true)] out Version? version, + [NotNullWhen(returnValue: true)] out ReleaseTrackingLine? releaseTrackingLine) + { + version = null; + releaseTrackingLine = null; + if (!ReleaseTrackingDataByRuleIdMap.TryGetValue(ruleId, out var releaseTrackingDataForRule) || + releaseTrackingDataForRule.ReleasesByVersionMap.IsEmpty) + { + return false; + } + + foreach (var (releaseVersion, releaseLine) in releaseTrackingDataForRule.ReleasesByVersionMap) + { + if (maxVersion == null || releaseVersion <= maxVersion) + { + version = releaseVersion; + releaseTrackingLine = releaseLine; + return true; + } + } + + return false; + } + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal readonly struct ReleaseTrackingDataForRule +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public string RuleId { get; } + public ImmutableSortedDictionary ReleasesByVersionMap { get; } + + internal ReleaseTrackingDataForRule(string ruleId, ReleaseTrackingDataForRuleBuilder builder) + { + RuleId = ruleId; + ReleasesByVersionMap = builder.ToImmutable(); + } + } + + internal sealed class ReleaseTrackingDataForRuleBuilder + { + private readonly ImmutableSortedDictionary.Builder _builder + = ImmutableSortedDictionary.CreateBuilder(ReverseComparer.Instance); + + public void AddEntry(Version version, ReleaseTrackingLine releaseTrackingLine, out bool hasExistingEntry) + { + hasExistingEntry = _builder.ContainsKey(version); + _builder[version] = releaseTrackingLine; + } + + public ImmutableSortedDictionary ToImmutable() + => _builder.ToImmutable(); + + private sealed class ReverseComparer : IComparer + { + public static readonly IComparer Instance = new ReverseComparer(); + private ReverseComparer() { } + + public int Compare([AllowNull] Version x, [AllowNull] Version y) + { + return (x?.CompareTo(y)).GetValueOrDefault() * -1; + } + } + } + + internal abstract class ReleaseTrackingLine + { + public string RuleId { get; } + public string Category { get; } + public bool? EnabledByDefault { get; } + public DiagnosticSeverity? DefaultSeverity { get; } + + public TextSpan Span { get; } + public SourceText SourceText { get; } + public string Path { get; } + public bool IsShipped { get; } + public bool IsRemovedRule => Kind == ReleaseTrackingRuleEntryKind.Removed; + public ReleaseTrackingRuleEntryKind Kind { get; } + + internal static ReleaseTrackingLine Create( + string ruleId, string category, bool? enabledByDefault, + DiagnosticSeverity? defaultSeverity, + TextSpan span, SourceText sourceText, + string path, bool isShipped, ReleaseTrackingRuleEntryKind kind) + { + return new NewOrRemovedRuleReleaseTrackingLine(ruleId, category, + enabledByDefault, defaultSeverity, span, sourceText, path, isShipped, kind); + } + + protected ReleaseTrackingLine(string ruleId, string category, bool? enabledByDefault, + DiagnosticSeverity? defaultSeverity, + TextSpan span, SourceText sourceText, + string path, bool isShipped, ReleaseTrackingRuleEntryKind kind) + { + RuleId = ruleId; + Category = category; + EnabledByDefault = enabledByDefault; + DefaultSeverity = defaultSeverity; + Span = span; + SourceText = sourceText; + Path = path; + IsShipped = isShipped; + Kind = kind; + } + } + + internal sealed class NewOrRemovedRuleReleaseTrackingLine : ReleaseTrackingLine + { + internal NewOrRemovedRuleReleaseTrackingLine( + string ruleId, string category, bool? enabledByDefault, + DiagnosticSeverity? defaultSeverity, + TextSpan span, SourceText sourceText, + string path, bool isShipped, ReleaseTrackingRuleEntryKind kind) + : base(ruleId, category, enabledByDefault, defaultSeverity, span, sourceText, path, isShipped, kind) + { + Debug.Assert(kind is ReleaseTrackingRuleEntryKind.New or ReleaseTrackingRuleEntryKind.Removed); + } + } + + internal sealed class ChangedRuleReleaseTrackingLine : ReleaseTrackingLine + { + public string OldCategory { get; } + public bool? OldEnabledByDefault { get; } + public DiagnosticSeverity? OldDefaultSeverity { get; } + + internal ChangedRuleReleaseTrackingLine( + string ruleId, string category, bool? enabledByDefault, + DiagnosticSeverity? defaultSeverity, + string oldCategory, bool? oldEnabledByDefault, + DiagnosticSeverity? oldDefaultSeverity, + TextSpan span, SourceText sourceText, + string path, bool isShipped) + : base(ruleId, category, enabledByDefault, defaultSeverity, span, sourceText, path, isShipped, ReleaseTrackingRuleEntryKind.Changed) + { + OldCategory = oldCategory; + OldEnabledByDefault = oldEnabledByDefault; + OldDefaultSeverity = oldDefaultSeverity; + } + } + + internal enum ReleaseTrackingHeaderKind + { + ReleaseHeader, + TableHeaderTitle, + TableHeaderNewOrRemovedRulesLine1, + TableHeaderNewOrRemovedRulesLine2, + TableHeaderChangedRulesLine1, + TableHeaderChangedRulesLine2, + } + + internal enum ReleaseTrackingRuleEntryKind + { + New, + Removed, + Changed, + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReportDiagnosticAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReportDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..33010ebba2241 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/ReportDiagnosticAnalyzer.cs @@ -0,0 +1,254 @@ +// 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.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +{ + using static CodeAnalysisDiagnosticsResources; + + public abstract class ReportDiagnosticAnalyzer : DiagnosticAnalyzerCorrectnessAnalyzer + where TClassDeclarationSyntax : SyntaxNode + where TStructDeclarationSyntax : SyntaxNode + where TInvocationExpressionSyntax : SyntaxNode + where TIdentifierNameSyntax : SyntaxNode + where TVariableDeclaratorSyntax : SyntaxNode + { + public static readonly DiagnosticDescriptor InvalidReportDiagnosticRule = new( + DiagnosticIds.InvalidReportDiagnosticRuleId, + CreateLocalizableResourceString(nameof(InvalidReportDiagnosticTitle)), + CreateLocalizableResourceString(nameof(InvalidReportDiagnosticMessage)), + DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(InvalidReportDiagnosticDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(InvalidReportDiagnosticRule); + + [SuppressMessage("AnalyzerPerformance", "RS1012:Start action has no registered actions.", Justification = "Method returns an analyzer that is registered by the caller.")] + protected override DiagnosticAnalyzerSymbolAnalyzer? GetDiagnosticAnalyzerSymbolAnalyzer(CompilationStartAnalysisContext compilationContext, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + { + Compilation compilation = compilationContext.Compilation; + + INamedTypeSymbol? compilationEndAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCompilationEndAnalysisContext); + if (compilationEndAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? codeBlockAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCodeBlockAnalysisContext); + if (codeBlockAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? operationBlockAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsOperationBlockAnalysisContext); + if (operationBlockAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? operationAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsOperationAnalysisContext); + if (operationAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? semanticModelAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsSemanticModelAnalysisContext); + if (semanticModelAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? symbolAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsSymbolAnalysisContext); + if (symbolAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? syntaxNodeAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsSyntaxNodeAnalysisContext); + if (syntaxNodeAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? syntaxTreeAnalysisContext = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsSyntaxTreeAnalysisContext); + if (syntaxTreeAnalysisContext == null) + { + return null; + } + + INamedTypeSymbol? diagnosticType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnostic); + if (diagnosticType == null) + { + return null; + } + + INamedTypeSymbol? diagnosticDescriptorType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticDescriptor); + if (diagnosticDescriptorType == null) + { + return null; + } + + ImmutableHashSet contextTypes = ImmutableHashSet.Create(compilationEndAnalysisContext, codeBlockAnalysisContext, + operationBlockAnalysisContext, operationAnalysisContext, semanticModelAnalysisContext, symbolAnalysisContext, syntaxNodeAnalysisContext, syntaxTreeAnalysisContext); + + return GetAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute); + } + + protected abstract ReportDiagnosticCompilationAnalyzer GetAnalyzer(ImmutableHashSet contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute); + + protected abstract class ReportDiagnosticCompilationAnalyzer : SyntaxNodeWithinAnalyzerTypeCompilationAnalyzer + { + private readonly ImmutableHashSet _contextTypes; + private readonly INamedTypeSymbol _diagnosticType; + private readonly INamedTypeSymbol _diagnosticDescriptorType; + + private ImmutableDictionary> _supportedDescriptorFieldsMap; + + protected ReportDiagnosticCompilationAnalyzer(ImmutableHashSet contextTypes, INamedTypeSymbol diagnosticType, INamedTypeSymbol diagnosticDescriptorType, INamedTypeSymbol diagnosticAnalyzer, INamedTypeSymbol diagnosticAnalyzerAttribute) + : base(diagnosticAnalyzer, diagnosticAnalyzerAttribute) + { + _contextTypes = contextTypes; + _diagnosticType = diagnosticType; + _diagnosticDescriptorType = diagnosticDescriptorType; + _supportedDescriptorFieldsMap = ImmutableDictionary>.Empty; + } + + protected abstract IEnumerable? GetArgumentExpressions(TInvocationExpressionSyntax invocation); + protected virtual SyntaxNode GetPropertyGetterBlockSyntax(SyntaxNode declaringSyntaxRefNode) + { + return declaringSyntaxRefNode; + } + + protected override void AnalyzeDiagnosticAnalyzer(SymbolAnalysisContext symbolContext) + { + ImmutableArray descriptorFields = GetSupportedDescriptors(symbolContext.Compilation, (INamedTypeSymbol)symbolContext.Symbol, symbolContext.CancellationToken); + if (!descriptorFields.IsDefaultOrEmpty) + { + base.AnalyzeDiagnosticAnalyzer(symbolContext); + } + } + + private ImmutableArray GetSupportedDescriptors(Compilation compilation, INamedTypeSymbol analyzer, CancellationToken cancellationToken) + { + if (_supportedDescriptorFieldsMap.TryGetValue(analyzer, out ImmutableArray descriptorFields)) + { + return descriptorFields; + } + + descriptorFields = default; + + if (this.DiagnosticAnalyzer.GetMembers(DiagnosticWellKnownNames.SupportedDiagnosticsName).FirstOrDefault() is IPropertySymbol supportedDiagnosticBaseProperty) + { + IPropertySymbol supportedDiagnosticsProperty = analyzer.GetMembers() + .OfType() + .FirstOrDefault(p => p.OverriddenProperty != null && + p.OverriddenProperty.Equals(supportedDiagnosticBaseProperty)); + if (supportedDiagnosticsProperty?.GetMethod != null) + { + SyntaxReference? syntaxRef = supportedDiagnosticsProperty.GetMethod.DeclaringSyntaxReferences.FirstOrDefault(); + if (syntaxRef != null) + { + SyntaxNode syntax = syntaxRef.GetSyntax(cancellationToken); + syntax = GetPropertyGetterBlockSyntax(syntax); + if (syntax != null) + { + SemanticModel semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree); + descriptorFields = GetReferencedDescriptorFields(syntax, semanticModel, cancellationToken); + } + } + } + } + + return ImmutableInterlocked.GetOrAdd(ref _supportedDescriptorFieldsMap, analyzer, descriptorFields); + } + + private ImmutableArray GetReferencedDescriptorFields(SyntaxNode syntax, SemanticModel semanticModel, CancellationToken cancellationToken) + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + foreach (TIdentifierNameSyntax identifier in syntax.DescendantNodes().OfType()) + { + ISymbol? symbol = semanticModel.GetSymbolInfo(identifier, cancellationToken).Symbol; + if (symbol != null && symbol.Kind == SymbolKind.Field) + { + var field = (IFieldSymbol)symbol; + if (field.Type is INamedTypeSymbol fieldType && fieldType.GetBaseTypesAndThis().Contains(_diagnosticDescriptorType)) + { + builder.Add((IFieldSymbol)symbol); + } + } + } + + return builder.ToImmutable(); + } + + protected override void AnalyzeNode(SymbolAnalysisContext symbolContext, TInvocationExpressionSyntax syntaxNode, SemanticModel semanticModel) + { + ISymbol? symbol = semanticModel.GetSymbolInfo(syntaxNode, symbolContext.CancellationToken).Symbol; + if (symbol == null || + symbol.Kind != SymbolKind.Method || + !symbol.Name.Equals(DiagnosticWellKnownNames.ReportDiagnosticName, StringComparison.OrdinalIgnoreCase) || + !_contextTypes.Contains(symbol.ContainingType)) + { + return; + } + + IEnumerable? arguments = GetArgumentExpressions(syntaxNode); + if (arguments?.HasExactly(1) == true) + { + SyntaxNode argument = arguments.First(); + ITypeSymbol? type = semanticModel.GetTypeInfo(argument, symbolContext.CancellationToken).ConvertedType; + if (type != null && type.Equals(_diagnosticType)) + { + ISymbol? argSymbol = semanticModel.GetSymbolInfo(argument, symbolContext.CancellationToken).Symbol; + if (argSymbol != null) + { + SyntaxNode? diagnosticInitializer = null; + + if (argSymbol is ILocalSymbol local) + { + SyntaxReference? syntaxRef = local.DeclaringSyntaxReferences.FirstOrDefault(); + if (syntaxRef != null) + { + diagnosticInitializer = syntaxRef.GetSyntax(symbolContext.CancellationToken).FirstAncestorOrSelf(); + } + } + else + { + if (argSymbol is IMethodSymbol method && + method.ContainingType.Equals(_diagnosticType) && + method.Name.Equals(nameof(Diagnostic.Create), StringComparison.OrdinalIgnoreCase)) + { + diagnosticInitializer = argument; + } + } + + if (diagnosticInitializer != null) + { + ImmutableArray descriptorFields = GetReferencedDescriptorFields(diagnosticInitializer, semanticModel, symbolContext.CancellationToken); + if (descriptorFields.Length == 1 && + !_supportedDescriptorFieldsMap[(INamedTypeSymbol)symbolContext.Symbol].Contains(descriptorFields[0])) + { + Diagnostic diagnostic = syntaxNode.CreateDiagnostic(InvalidReportDiagnosticRule, descriptorFields[0].Name); + symbolContext.ReportDiagnostic(diagnostic); + } + } + } + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/SymbolIsBannedInAnalyzersAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/SymbolIsBannedInAnalyzersAnalyzer.cs new file mode 100644 index 0000000000000..94598e4efe37c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/SymbolIsBannedInAnalyzersAnalyzer.cs @@ -0,0 +1,119 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.BannedApiAnalyzers; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + internal static class SymbolIsBannedInAnalyzersAnalyzer + { + public static readonly DiagnosticDescriptor SymbolIsBannedRule = new( + id: DiagnosticIds.SymbolIsBannedInAnalyzersRuleId, + title: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(SymbolIsBannedInAnalyzersDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor NoSettingSpecifiedSymbolIsBannedRule = new( + id: DiagnosticIds.NoSettingSpecifiedSymbolIsBannedInAnalyzersRuleId, + title: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersMessage)), + category: DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(NoSettingSpecifiedSymbolIsBannedInAnalyzersDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + } + + public abstract class SymbolIsBannedInAnalyzersAnalyzer : SymbolIsBannedAnalyzerBase + where TSyntaxKind : struct + { + public sealed override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(SymbolIsBannedInAnalyzersAnalyzer.SymbolIsBannedRule, SymbolIsBannedInAnalyzersAnalyzer.NoSettingSpecifiedSymbolIsBannedRule); + + protected sealed override DiagnosticDescriptor SymbolIsBannedRule => SymbolIsBannedInAnalyzersAnalyzer.SymbolIsBannedRule; + +#pragma warning disable RS1025, RS1026 // Configure generated code analysis, Enable concurrent execution. Base Initialize handles these. + public sealed override void Initialize(AnalysisContext context) + { + base.Initialize(context); + + context.RegisterCompilationStartAction(analyzeAnalyzersAndGeneratorsIfPropertyNotSpecified); + void analyzeAnalyzersAndGeneratorsIfPropertyNotSpecified(CompilationStartAnalysisContext context) + { + var propertyValue = context.Options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.EnforceExtendedAnalyzerRules, context.Compilation); + // Note: in absence of any value for this property in the project, the generated editorconfig will have an entry like: + // `build_property.EnforceExtendedAnalyzerRules = ` + if (!string.IsNullOrEmpty(propertyValue)) + { + return; + } + + var provider = WellKnownTypeProvider.GetOrCreate(context.Compilation); + var diagnosticAnalyzerAttributeType = provider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); + var generatorAttributeType = provider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisGeneratorAttribute); + context.RegisterSymbolAction(analyzePossibleAnalyzerOrGenerator, SymbolKind.NamedType); + + void analyzePossibleAnalyzerOrGenerator(SymbolAnalysisContext symbolAnalysisContext) + { + var symbol = symbolAnalysisContext.Symbol; + + if (symbol.HasAnyAttribute(diagnosticAnalyzerAttributeType, generatorAttributeType)) + { + symbolAnalysisContext.ReportDiagnostic(symbol.Locations.CreateDiagnostic(SymbolIsBannedInAnalyzersAnalyzer.NoSettingSpecifiedSymbolIsBannedRule, symbol)); + } + } + } + } + +#pragma warning disable RS1012 // 'compilationContext' does not register any analyzer actions. Consider moving actions registered in 'Initialize' that depend on this start action to 'compilationContext'. + protected sealed override Dictionary<(string ContainerName, string SymbolName), ImmutableArray>? ReadBannedApis(CompilationStartAnalysisContext compilationContext) + { + var compilation = compilationContext.Compilation; + var propertyValue = compilationContext.Options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.EnforceExtendedAnalyzerRules, compilation); + if (propertyValue != "true") + return null; + + const string fileName = "Microsoft.CodeAnalysis.Analyzers.AnalyzerBannedSymbols.txt"; + using var stream = typeof(SymbolIsBannedInAnalyzersAnalyzer<>).Assembly.GetManifestResourceStream(fileName); + var source = SourceText.From(stream); + + var result = new Dictionary<(string ContainerName, string SymbolName), List>(); + foreach (var line in source.Lines) + { + var text = line.ToString(); + if (string.IsNullOrWhiteSpace(text)) + continue; + + var entry = new BanFileEntry(compilation, text, line.Span, source, fileName); + var parsed = DocumentationCommentIdParser.ParseDeclaredSymbolId(entry.DeclarationId); + if (parsed != null) + { + if (!result.TryGetValue(parsed.Value, out var existing)) + { + existing = new(); + result.Add(parsed.Value, existing); + } + + existing.Add(entry); + } + } + + return result.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableArray()); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/Microsoft.CodeAnalysis.Analyzers.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/Microsoft.CodeAnalysis.Analyzers.csproj new file mode 100644 index 0000000000000..92f912a980a3a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/Microsoft.CodeAnalysis.Analyzers.csproj @@ -0,0 +1,35 @@ + + + + netstandard2.0 + + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForCodeAnalysisAnalyzers) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/UpgradeMSBuildWorkspaceAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/UpgradeMSBuildWorkspaceAnalyzer.cs new file mode 100644 index 0000000000000..8fd52640db656 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/UpgradeMSBuildWorkspaceAnalyzer.cs @@ -0,0 +1,94 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.Analyzers +{ + using static CodeAnalysisDiagnosticsResources; + + /// + /// RS1023: + /// + public abstract class UpgradeMSBuildWorkspaceAnalyzer : DiagnosticAnalyzer + { + private const string WorkspacesDesktop = "Microsoft.CodeAnalysis.Workspaces.Desktop"; + private const string WorkspacesMSBuild = "Microsoft.CodeAnalysis.Workspaces.MSBuild"; + private const string MSBuildWorkspaceFullName = "Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace"; + protected const string MSBuildWorkspace = "MSBuildWorkspace"; + + public static readonly DiagnosticDescriptor UpgradeMSBuildWorkspaceDiagnosticRule = new( + DiagnosticIds.UpgradeMSBuildWorkspaceRuleId, + CreateLocalizableResourceString(nameof(UpgradeMSBuildWorkspaceTitle)), + CreateLocalizableResourceString(nameof(UpgradeMSBuildWorkspaceMessage)), + DiagnosticCategory.Library, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(UpgradeMSBuildWorkspaceDescription)), + helpLinkUri: "https://go.microsoft.com/fwlink/?linkid=874285", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(UpgradeMSBuildWorkspaceDiagnosticRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(AnalyzeAssemblyReferences); + } + + protected abstract void RegisterIdentifierAnalysis(CompilationStartAnalysisContext context); + + private void AnalyzeAssemblyReferences(CompilationStartAnalysisContext context) + { + // We have to be careful not to report the "upgrade MSBuildWorkspace" diagnostic in such + // a way that it won't conflict with IDE code fixes, such as "Add Using". + // To do that, we only report the diagnostic if the compilation meets the following conditions: + // + // 1. Has a reference Microsoft.CodeAnalysis.Workspaces.Desktop. + // 2. Does not have a reference Microsoft.CodeAnalysis.Workspaces.MSBuild. + // 3. Does not include the type Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace. + // + // It's possible that this diagnostic might be reported when the "Add NuGet package" code fix + // is offered for "Microsoft.CodeAnalysis.Workspaces.MSBuild", but that's OK. When the user + // applies that code fix, this diagnostic should go away. + + var foundWorkspacesDesktop = false; + + foreach (var assemblyIdentity in context.Compilation.ReferencedAssemblyNames) + { + if (assemblyIdentity.Name == WorkspacesMSBuild) + { + // If a reference to Workspaces.MSBuild exists, we're done. + return; + } + + if (!foundWorkspacesDesktop && assemblyIdentity.Name == WorkspacesDesktop) + { + foundWorkspacesDesktop = true; + } + } + + // If there isn't a reference to Workspaces.Desktop, we're done. + if (!foundWorkspacesDesktop) + { + return; + } + + // If this compilation contains the type, Microsoft.CodeAnalysis.MSBuild.MSBuildWorkspace, we're done. + var msbuildWorkspace = context.Compilation.GetOrCreateTypeByMetadataName(MSBuildWorkspaceFullName); + if (msbuildWorkspace != null) + { + return; + } + + // OK, add a syntax node action to look for unbound MSBuildWorkspace symbols. + RegisterIdentifierAnalysis(context); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf new file mode 100644 index 0000000000000..fbd75ce503a0d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.cs.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + Popisovač DiagnosticDescriptor přiřazený k poli se používá k hlášení diagnostiky ukončení kompilace, ale konstruktor DiagnosticDescriptor použitý k jeho inicializaci nepředává požadovanou vlastní značku CompilationEnd. Podrobnosti najdete v dokumentaci k WellKnownDiagnosticTags.CompilationEnd. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Přidat vlastní značku CompilationEnd do popisovače diagnostiky, který se používá k inicializaci pole {0}, protože slouží k nahlášení diagnostiky ukončení kompilace + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Přidat vlastní značku CompilationEnd do popisovače diagnostiky ukončení kompilace + + + + Add rule entry to unshipped release file + Přidat položku pravidla k nevydanému souboru verze + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Zděďte typ {0} z DiagnosticAnalyzer, nebo odeberte atributy DiagnosticAnalyzerAttribute. + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Typy označené jako DiagnosticAnalyzerAttribute by měly dědit z DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + K porovnání symbolů použít SymbolEqualityComparer + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Symboly by se měly porovnávat podle rovnosti, nikoli identity. Použijte přetížení, které přijímá IEqualityComparer, a předejte SymbolEqualityComparer. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Symboly by se měly porovnávat podle rovnosti, nikoli identity. Explicitní volání GetHashCode pravděpodobně způsobí nesprávné chování. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Při porovnávání symbolů použít SymbolEqualityComparer + + + + Symbols should be compared for equality + Symboly by se měly porovnat podle rovnosti. + + + + Configure generated code analysis + Nakonfigurovat analýzu vygenerovaného kódu + + + + Configure generated code analysis + Nakonfigurovat analýzu vygenerovaného kódu + + + + Configure generated code analysis + Nakonfigurovat analýzu vygenerovaného kódu + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Všechna podporovaná ID diagnostiky analyzátoru by měla být součástí verze analyzátoru. + + + + Rule '{0}' is not part of any analyzer release + Pravidlo {0} není součástí žádné verze analyzátoru. + + + + Add analyzer diagnostic IDs to analyzer release + Přidat ID diagnostiky analyzátoru do verze analyzátoru + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + Popis diagnostiky by měl být jedna nebo několik vět, které končí znakem interpunkce a které na začátku ani na konci nemají žádné prázdné znaky. + + + + Define diagnostic description correctly + Definujte správně diagnostický popis + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Diagnostická zpráva by neměla obsahovat žádný znak konce řádku ani žádné prázdné znaky na začátku a na konci. Zároveň by to měla být buď jedna věta bez tečky na konci, nebo více vět s tečkou na konci. + + + + Define diagnostic message correctly + Definujte správně diagnostickou zprávu + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Název diagnostiky by neměl obsahovat tečku, znak konce řádku ani prázdné znaky na začátku a na konci. + + + + Define diagnostic title correctly + Definujte správně nadpis diagnostiky + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Toto rozšíření kompilátoru jazyka C# by nemělo být implementováno v sestavení obsahujícím odkaz na Microsoft.CodeAnalysis.VisualBasic. Sestavení Microsoft.CodeAnalysis.VisualBasic není vždy poskytováno během kompilačních scénářů jazyka C#, takže odkazy na něj by mohly způsobit, že se rozšíření kompilátoru bude chovat nepředvídatelně. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Toto rozšíření kompilátoru by nemělo být implementováno v sestavení s cílovou architekturou {0}. Odkazy na jiné cílové architektury způsobí, že se kompilátor bude chovat nepředvídatelně. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Typy, které implementují body rozšíření kompilátoru, by měly být deklarovány pouze v sestaveních cílených na netstandard2.0. Konkrétnější cílové architektury jsou k dispozici pouze v podmnožině podporovaných scénářů kompilace, takže cílení na ně může způsobit, že se funkce bude chovat nepředvídatelně. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Rozšíření kompilátoru by měla být implementována v sestavách cílících na netstandard2.0. + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Toto rozšíření kompilátoru by nemělo být implementováno v sestavení obsahujícím odkaz na Microsoft.CodeAnalysis.Workspaces. Sestavení Microsoft.CodeAnalysis.Workspaces se během scénářů kompilace příkazového řádku neposkytuje, takže odkazy na něj by mohly způsobit, že se rozšíření kompilátoru bude chovat nepředvídatelně. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Toto rozšíření kompilátoru Visual Basic by nemělo být implementováno v sestavení obsahujícím odkaz na Microsoft.CodeAnalysis.CSharp. Sestavení Microsoft.CodeAnalysis.CSharp není vždy poskytováno během Visual Basic scénářů kompilace, takže odkazy na něj by mohly způsobit, že se rozšíření kompilátoru bude chovat nepředvídatelně. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Typy, které implementují rozšiřující body kompilátoru, by neměly být deklarovány v sestaveních, která obsahují odkazy na sestavení, která nejsou poskytována všemi scénáři kompilace. To může způsobit, že se funkce bude chovat nepředvídatelně. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Rozšíření kompilátoru by se měla implementovat v sestaveních s odkazy poskytnutými kompilátorem. + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + GetSemanticModel je náročná metoda na to, aby se volala v diagnostickém analyzátoru, protože vytváří zcela nový sémantický model, který nesdílí data kompilace s kompilátorem nebo jinými analyzátory. To dále snižuje výkon během analýzy sémantiky. Zvažte místo toho možnost zaregistrovat jinou akci analyzátoru, která umožňuje používat sdílený SemanticModel, třeba RegisterOperationAction, RegisterSyntaxNodeAction nebo RegisterSemanticModelAction. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Nevolejte metodu Compilation.GetSemanticModel() v diagnostickém analyzátoru + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Nevolejte metodu Compilation.GetSemanticModel() v diagnostickém analyzátoru + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + Hodnoty DiagnosticId pro analyzátory by neměly používat rezervovaná ID. + + + + '{0}' is a reserved diagnostic ID + {0} je rezervované ID diagnostiky. + + + + Do not use reserved diagnostic IDs + Nepoužívejte rezervovaná ID diagnostiky + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Když se pro balíčky analyzátoru povolí sledování vydaných verzí, pomůže to sledovat a dokumentovat diagnostiky analyzátoru, které se dodávají a mění s každou jeho vydanou verzí. Podrobnosti najdete na adrese https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Povolit sledování vydaných verzí analyzátoru pro projekt analyzátoru, který obsahuje pravidlo {0} + + + + Enable analyzer release tracking + Povolit sledování vydaných verzí analyzátoru + + + + Enable concurrent execution + Povolit souběžné provádění + + + + Enable concurrent execution + Povolit souběžné provádění + + + + Enable concurrent execution + Povolit souběžné provádění + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Neplatná položka v souboru vydané verze analyzátoru + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Soubor vydané verze analyzátoru {0} má neplatnou položku {1}. + + + + Invalid entry in analyzer release file + Neplatná položka v souboru vydané verze analyzátoru + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + V souboru vydané verze analyzátoru {0} chybí nebo není platná hlavička vydané verze {1}. + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + Soubor vydané verze analyzátoru {0} má neplatnou položku {1} bez dříve vydané verze pro pravidlo {2}. Místo ní přidejte samostatnou položku {1} pro pravidlo v nevydaném souboru verze. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + Soubor vydané verze analyzátoru {0} má položku s nejméně jedním polem Undetected, které se v {1} musí vyplnit ručně. + + + + Missing '{0}' attribute + Chybí atribut {0}. + + + + Missing diagnostic analyzer attribute + Chybí atribut diagnostického analyzátoru + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Neabstraktní podtypy diagnostického analyzátoru musí být označeny atributy DiagnosticAnalyzerAttribute. Argument těchto atributů, pokud existuje, určuje podporované jazyky analyzátoru. Modul analýzy bude typy analyzátoru s tímto atributem ignorovat. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Je možné, že diagnostický analyzátor {0} bude podporovat jak C#, tak i Visual Basic. Zvažte přidání argumentu do atributu DiagnosticAnalyzerAttribute, aby bylo možné podporovat jazyk {1}. + + + + Recommend adding language support to diagnostic analyzer + Do diagnostického analyzátoru doporučujeme přidat podporu jazyků + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Diagnostický analyzátor má podporovat pouze jeden jazyk, ale sestavení analyzátoru podle všeho neodkazuje na žádná sestavení CodeAnalysis pro konkrétní jazyk, a tak pravděpodobně bude fungovat pro více jazyků. Zvažte přidání argumentu pro další jazyk do atributu DiagnosticAnalyzerAttribute. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Použijte atribut DiagnosticAnalyzer pro {0}. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Použijte atribut DiagnosticAnalyzer jak pro {0}, tak i pro {1}. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Při registraci akce analyzátoru symbolů zadejte minimálně jeden požadovaný SymbolKind. + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Při registraci akce analyzátoru uzlů syntaxe zadejte minimálně jeden požadovaný SyntaxKind. + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Při registraci akce analyzátoru operací zadejte minimálně jeden požadovaný OperationKind. + + + + Missing kind argument when registering an analyzer action + Při registraci akce analyzátoru chybí argument druhu + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Při registraci analyzátoru syntaxe, symbolů nebo operací musíte zadat minimálně jeden druh syntaxe, symbolu nebo operace. V opačném případě se registrovaná akce nikdy při analýze nevyvolá. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Projekt obsahující analyzátory nebo zdrojové generátory by měl určovat vlastnost <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + {0}: Projekt obsahující analyzátory nebo zdrojové generátory by měl určovat vlastnost '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + Specify analyzer banned API enforcement setting + Zadat nastavení vynucení rozhraní API se zakázanými analyzátory + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Při kontrole typů syntaxe upřednostňovat syntax.IsKind(kind) před syntax.Kind() == kind. Kód, který používá IsKind, je za běhu o něco efektivnější, proto konzistentní používání tohoto tvaru tam, kde je to možné, pomáhá zlepšit výkon ve scénářích komplexní analýzy. + + + + Use 'IsKind' instead of 'Kind' + Použijte IsKind místo Kind. + + + + Prefer 'IsKind' for checking syntax kinds + Při kontrole typů syntaxí upřednostňujte IsKind. + + + + Prefer 'IsKind' for checking syntax kinds + Při kontrole typů syntaxí upřednostňovat IsKind + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + Hodnota customTags se používá jako způsob, jak povolit konkrétní akce a filtry v diagnostických popisovačích na základě konkrétních hodnot značek. Každý analyzátor Roslyn by měl mít alespoň jednu značku z třídy WellKnownDiagnosticTags. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Pokud chcete povolit filtrování metadat diagnostických popisovačů, zvažte poskytnutí nenulové hodnoty customTags pro konstruktor diagnostických popisovačů. + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Poskytněte konstruktoru diagnostických popisovačů nenulovou hodnotu customTags + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Odebrat duplicitní položky pro ID diagnostiky mezi vydanými verzemi analyzátoru + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + Pravidlo {0} má duplicitní položku mezi vydanými verzemi {1} a {2}. + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Odebrat duplicitní položky pro ID diagnostiky mezi vydanými verzemi analyzátoru + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Odebrat duplicitní položky pro ID diagnostiky ve stejné vydané verzi analyzátoru + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + Pravidlo {0} má více než jednu položku pro vydanou verzi {1} v souboru vydané verze analyzátoru {2}. + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Odebrat duplicitní položky pro ID diagnostiky ve stejné vydané verzi analyzátoru + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Vydaná ID diagnostiky, která se už neohlašují, by měla mít položku v tabulce Removed Rules v nevydaném souboru + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + Pravidlo {0} se dodávalo ve vydané verzi analyzátoru {1}, ale už není podporovanou diagnostikou v žádném analyzátoru. Přidejte položku pro toto pravidlo do tabulky Removed Rules nevydaného souboru. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Vydaná ID diagnostiky, která se už neohlašují, by měla mít položku v tabulce Removed Rules v nevydaném souboru + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Položky pro ID diagnostiky analyzátoru, které se už neohlašují a nedodávají se, se dají odebrat z nevydané verze analyzátoru. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + Pravidlo {0} je součástí další nevydané verze analyzátoru, ale není podporovanou diagnostikou žádného analyzátoru. + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Nepřidávat odebraná ID diagnostiky analyzátoru do nevydané verze analyzátoru + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Volání SemanticModel.GetDeclaredSymbol s určitými typy děděnými z SyntaxNode, například GlobalStatementSyntax a IncompleteMemberSyntax, vždy vrátí null. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Volání SemanticModel.GetDeclaredSymbol s argumentem typu FieldDeclarationSyntax nebo EventFieldDeclarationSyntax vždy vrátí null. Místo toho volejte GetDeclaredSymbol s deklarátory proměnných z pole. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Volání SemanticModel.GetDeclaredSymbol ({0}) vždy vrátí null. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Volání SemanticModel.GetDeclaredSymbol ({0}) vždy vrátí null. + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Toto volání SemanticModel.GetDeclaredSymbol vždy vrátí null. + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Symbol byl označen jako zakázaný pro použití v analyzátorech a místo toho by se měla použít náhrada. + + + + The symbol '{0}' is banned for use by analyzers{1} + Symbol {0} je zakázaný pro použití analyzátory {1} + + + + Do not use APIs banned for analyzers + Nepoužívat rozhraní API zakázaná pro analyzátory + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + ID diagnostiky označená jako odebraná v souboru vydané verze analyzátoru by analyzátory neměly ohlašovat + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + Pravidlo {0} se v nejnovější vydané verzi analyzátoru označilo jako odebrané, ale stále se ohlašuje. + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + ID diagnostiky označená jako odebraná v souboru vydané verze analyzátoru by analyzátory neměly ohlašovat + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + U akcí analyzátoru symbolů se SymbolKind {0} nepodporuje. + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Při registraci akce analyzátoru symbolů byl zadán nepodporovaný argument SymbolKind + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + Funkce ReportDiagnostic vyvolala nepodporovaný DiagnosticDescriptor {0}. + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + Funkce ReportDiagnostic vyvolala nepodporovaný DiagnosticDescriptor + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + Funkce ReportDiagnostic by se měla volat jenom s podporovanými popisovači diagnostiky, které se vrací z vlastnosti DiagnosticAnalyzer.SupportedDiagnostics. V opačném případě modul analýzy reportovanou diagnostiku vyfiltruje. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + Argument typu {0} pro parametr typu {1} metody{2} není výčtem SyntaxKind. + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Neplatný argument typu pro metodu registrace u diagnostického analyzátoru + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Metody registrace u diagnostického analyzátoru pro konkrétní jazyky, například RegisterSyntaxNodeAction, RegisterCodeBlockStartAction a RegisterCodeBlockEndAction, očekávají pro svůj parametr typu {0} argument typu SyntaxKind pro konkrétní jazyk. V opačném případě se registrovaná akce analyzátoru nikdy při analýze nevyvolá. + + + + Start action has no registered non-end actions + Spouštěcí akce nemá žádné registrované neukončovací akce + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + {0} neregistruje žádné akce analyzátoru. Zvažte přesunutí akcí registrovaných v {1}, které závisí na této spouštěcí akci, do {0}. + + + + Start action has no registered actions + Spouštěcí akce nemá žádné registrované akce + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + {0} neregistruje žádné akce analyzátoru s výjimkou {1}. Zvažte nahrazení této dvojice spouštěcí a ukončovací akce akcí {2} nebo přesunutí akcí zaregistrovaných v {3}, které závisí na této spouštěcí akci, do {0}. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Spouštěcí akce analyzátoru umožňuje provádět stavové analýzy u dané jednotky kódu, například u bloku kódu, kompilace atd. Uvážlivý návrh je nezbytný, aby bylo možné zajistit efektivní funkci analyzátoru a nedocházelo k nevracení paměti. Při psaní takových analyzátorů se řiďte následujícími pokyny: +1. Pro registrovanou spouštěcí akci definujte nový obor, nejlépe s privátním vnořeným typem pro analýzu jednotlivých jednotek kódu. +2. Pokud je to potřeba, definujte a inicializujte stav ve spouštěcí akci. +3. Zaregistrujte alespoň jednu neukončovací akci, která odkazuje na tento stav ve spouštěcí akci. Pokud žádnou takovou akci nepotřebujete, zvažte nahrazení spouštěcí akce neukončovací akcí. Například akci CodeBlockStartAction bez registrovaných akcí nebo s pouze zaregistrovanou akcí CodeBlockEndAction byste měli nahradit akcí CodeBlockAction. +4. Pokud je to potřeba, zaregistrujte ukončovací akci, která bude reportovat diagnostiku na základě koncového stavu. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Ujistit se, že se do vydané verze analyzátoru přidávají aktuální položky pro ID diagnostiky analyzátoru + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + Pravidlo {0} má od poslední vydané verze změněné hodnoty Category nebo Severity. Buď vraťte aktualizace ve zdroji, nebo přidejte novou aktuální položku do nevydaného souboru verze. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Ujistit se, že se do vydané verze analyzátoru přidávají aktuální položky pro ID diagnostiky analyzátoru + + + + Update rule entry in unshipped release file + Aktualizovat položku pravidla v nevydaném souboru verze + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Zvažte, že byste konstruktoru diagnostického popisovače poskytovali lokalizovatelné argumenty typu {0}, abyste zajistili možnost lokalizace popisovače. + + + + Provide localizable arguments to diagnostic descriptor constructor + Poskytněte konstruktoru diagnostického popisovače lokalizovatelné argumenty + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Pokud je třeba diagnostický analyzátor a jeho reportovanou diagnostiku lokalizovat, musí být lokalizovatelné také podporované popisovače diagnostiky, které byly použity pro vytvoření diagnostiky. V takovém případě je třeba konstruktoru popisovače diagnostiky poskytnout lokalizovatelné argumenty pro parametr title (a případně také description), aby se zajistila možnost lokalizace popisovače. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Neukládejte data typu {0} pro jednotlivé kompilace do polí diagnostického analyzátoru. + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Neukládejte data pro jednotlivé kompilace do polí diagnostického analyzátoru + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Instance diagnostického analyzátoru by mohla překročit dobu života kompilace. Z tohoto důvodu může ukládání dat jednotlivých kompilací (třeba symbolů) do polí diagnostického analyzátoru způsobit, že zastaralé kompilace zůstanou aktivní a způsobí nevrácení paměti. Tato data byste raději měli ukládat do samostatného typu, jehož instance se vytvoří ve spouštěcí akci kompilace zaregistrované pomocí rozhraní API {0}.{1}. Instance tohoto typu se vytvoří pro jednotlivé kompilace a nepřekročí dobu života kompilace, čímž se předejde nevracení paměti. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + Autor tohoto rozhraní neměl v úmyslu využívat implementace od třetích stran a vyhrazuje si právo to změnit. Implementace tohoto rozhraní by tak mohla v budoucích verzích mít za následek problémy s kompatibilitou zdroje nebo binárního souboru. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Typ {0} nemůže implementovat rozhraní {1}, protože {1} není k dispozici pro veřejnou implementaci. + + + + Only internal implementations of this interface are allowed + Povoleny jsou pouze interní implementace tohoto rozhraní + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + CodeFixProvider, jehož cílem je podpora oprav všech výskytů, musí klasifikovat zaregistrované akce kódu v odpovídajících třídách tak, že jim přiřadí explicitní nenulový ekvivalentní klíč, který je jedinečný pro každý druh akce kódu vytvořené tímto nástrojem pro opravy. To nástroji FixAllProvider umožní opravit veškerou diagnostiku v požadovaném rozsahu tak, že použije akce kódu z tohoto fixeru, které se nachází v ekvivalentní třídě akce aktivace kódu. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Zadejte explicitní argument pro volitelný parametr {0}, který není null a je jedinečný pro každý druh akce kódu vytvořené tímto fixerem. + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Akce kódu pro vytvoření by měly mít jedinečný EquivalenceKey pro podporu oprav všech výskytů + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + {0} má u vlastnosti {1} výchozí hodnotu null. Buď přepište tuto vlastnost na {0}, aby se vracela nenulová a jedinečná hodnota napříč všemi akcemi kódu pro jednotlivé fixery, nebo použijte existující akci kódu. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Použijte akce kódu, které mají jedinečný EquivalenceKey pro podporu oprav všech výskytů + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Mnoho objektů vystavených Roslynem je neměnných. Návratovou hodnotu z vyvolání metody u těchto objektů byste neměli ignorovat. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + Objekt {0} je neměnný a {1} na něj nebude mít žádný vliv. Zvažte použití návratové hodnoty z {1}. + + + + Do not ignore values returned by methods on immutable objects + Neignorovat hodnoty vrácené metodami u neměnných objektů + + + + Code fix providers should provide FixAll support + Zprostředkovatelé oprav kódu by měli poskytovat podporu pro FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + {0} registruje jednu nebo více oprav kódu, ale nepřepisuje metodu CodeFixProvider.GetFixAllProvider. Přepište tuto metodu a poskytněte nenulového zprostředkovatele FixAllProvider, který bude podporovat FixAll (například WellKnownFixAllProviders.BatchFixer), nebo zadejte hodnotu null, která podporu pro FixAll explicitně zakáže. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Zprostředkovatel CodeFixProvider by měl poskytovat podporu pro FixAll, aby uživatelé mohli opravit více instancí příslušné diagnostiky pomocí jedné opravy kódu. Další podrobnosti najdete v dokumentaci na adrese https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. + + + + Override GetFixAllProvider. + Přepište zprostředkovatele GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Zvažte možnost poskytnout konstruktoru popisovače diagnostiky nenulový parametr helpLinkUri, který umožní zobrazit další informace, když se tato diagnostika objeví v seznamu chyb. + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Poskytněte konstruktoru popisovače diagnostiky nenulový parametr helpLinkUri + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + Hodnota helpLinkUri se použije k zobrazení dalších informací, když se tato diagnostika objeví v seznamu chyb. Každý analyzátor by měl mít zadaný parametr helpLinkUri směřující na stránku nápovědy, která se v průběhu času nemění. + + + + DiagnosticId for analyzers must be in specified format + DiagnosticId pro analyzátory musí být v zadaném formátu + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + ID diagnostiky {0}, které patří do kategorie {1}, není v požadovaném rozmezí a/nebo formátu {2} zadaném v souboru {3}. + + + + DiagnosticId for analyzers must be in specified format. + DiagnosticId pro analyzátory musí být v zadaném formátu. + + + + DiagnosticId must be unique across analyzers + DiagnosticId musí být u každého analyzátoru jedinečné + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + ID diagnostiky {0} už používá analyzátor {1}. Použijte prosím jiné ID diagnostiky. + + + + DiagnosticId must be unique across analyzers. + DiagnosticId musí být u každého analyzátoru jedinečné. + + + + Category for analyzers must be from the specified values + Kategorie pro analyzátory musí být jedna ze zadaných hodnot + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + Kategorie {0} nepatří mezi povolené kategorie zadané v souboru {1}. + + + + Category for analyzers must be from the specified values. + Kategorie pro analyzátory musí být jedna ze zadaných hodnot. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + V souboru specifikací kategorií analyzátorů a rozmezí ID diagnostiky je neplatná položka + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + V souboru specifikací kategorií analyzátorů a rozmezí ID diagnostiky {1} je neplatná položka {0}. + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + V souboru specifikací kategorií analyzátorů a rozmezí ID diagnostiky je neplatná položka. + + + + DiagnosticId for analyzers must be a non-null constant + DiagnosticId pro analyzátory musí být nenulová konstanta + + + + Diagnostic Id for rule '{0}' must be a non-null constant + ID diagnostiky pro pravidlo {0} musí být nenulová konstanta. + + + + DiagnosticId for analyzers must be a non-null constant. + DiagnosticId pro analyzátory musí být nenulová konstanta. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Typy diagnostických analyzátorů by neměly používat typy ze sestavení pracovních prostorů. Sestavení pracovních prostorů jsou dostupná jen při spuštění analyzátoru v živé analýze integrovaného vývojového prostředí Visual Studio, ale nejsou dostupná při sestavování z příkazového řádku. Odkazování na typy ze sestavení pracovních prostorů způsobí výjimku modulu runtime během spuštění analyzátoru při sestavení z příkazového řádku. + + + + Do not use types from Workspaces assembly in an analyzer + Nepoužívejte v analyzátoru typy ze sestavení pracovních prostorů. + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Změnou typu diagnostického analyzátoru {0} odeberte všechny přímé a/nebo nepřímé přístupy k typům {1}, které mají přístup k typům {2}. + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Změnou typu diagnostického analyzátoru {0} odeberte všechny přímé přístupy k typům {1}. + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace se přesunul do balíčku NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild a v rozhraní API došlo ke změnám, které mohou způsobit nefunkčnost. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Upgradujte prosím MSBuildWorkspace přidáním odkazu na balíček do balíčku NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild. Podrobnosti o úspěšném použití MSBuildWorkspace najdete zde: https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + Upgradovat MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf new file mode 100644 index 0000000000000..d5619f91ee8d7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.de.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + "DiagnosticDescriptor", der dem Feld zugewiesen ist, wird verwendet, um eine Kompilierungsendediagnose zu melden, aber der zum Initialisieren verwendete DiagnosticDescriptor-Konstruktor übergibt nicht das erforderliche benutzerdefinierte Tag "CompilationEnd". Weitere Informationen finden Sie in der Dokumentation zu "WellKnownDiagnosticTags.CompilationEnd". + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Dem Diagnosedeskriptor das benutzerdefinierte Tag "CompilationEnd" hinzufügen, das zum Initialisieren des Felds "{0}" verwendet wird, da es zum Melden einer Kompilierungsenddiagnose verwendet wird + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Benutzerdefiniertes Tag "CompilationEnd" zum Diagnosedeskriptor für Kompilierungsende hinzufügen + + + + Add rule entry to unshipped release file + Regeleintrag der nicht veröffentlichten Releasedatei hinzufügen + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Übernehmen Sie den Typ "{0}" von DiagnosticAnalyzer, oder entfernen Sie DiagnosticAnalyzerAttribute(s). + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Mit DiagnosticAnalyzerAttribute(s) gekennzeichnete Typen müssen von DiagnosticAnalyzer erben + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Verwenden von "SymbolEqualityComparer" für den Symbolvergleich + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Symbole sollten im Hinblick auf Gleichheit und nicht im Hinblick auf die Identität verglichen werden. Verwenden Sie eine Überladung, die "IEqualityComparer" akzeptiert, und übergeben Sie "SymbolEqualityComparer". + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Symbole sollten im Hinblick auf Gleichheit und nicht im Hinblick auf die Identität verglichen werden. Ein expliziter Aufruf von "GetHashCode" führt wahrscheinlich zu einem falschen Verhalten. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Verwenden von "SymbolEqualityComparer" beim Vergleichen von Symbolen + + + + Symbols should be compared for equality + Symbole sollten auf Gleichheit verglichen werden + + + + Configure generated code analysis + Konfigurieren der Analyse von generiertem Code + + + + Configure generated code analysis + Konfigurieren der Analyse von generiertem Code + + + + Configure generated code analysis + Konfigurieren der Analyse von generiertem Code + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Alle unterstützten Analysetooldiagnose-IDs müssen Teil eines Analysetoolrelease sein. + + + + Rule '{0}' is not part of any analyzer release + Die Regel "{0}" gehört keinem Analysetoolrelease an. + + + + Add analyzer diagnostic IDs to analyzer release + Analysetooldiagnose-IDs zu Analysetoolrelease hinzufügen + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + Die Diagnosebeschreibung muss aus einem oder mehreren Sätzen bestehen, die mit einem Satzzeichen enden und keine führenden oder nachfolgenden Leerzeichen enthalten dürfen. + + + + Define diagnostic description correctly + Diagnosebeschreibung ordnungsgemäß definieren + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Die Diagnosemeldung darf keine Zeilenvorschubzeichen und keine führenden oder nachfolgenden Leerzeichen enthalten und muss entweder einen einzelnen Satz ohne Satzendepunkt oder mehrere Sätze mit Satzendepunkt umfassen. + + + + Define diagnostic message correctly + Diagnosemeldung ordnungsgemäß definieren + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Der Diagnosetitel darf weder einen Punkt, noch ein Zeilenumbruchzeichen, noch führende und nachfolgende Leerzeichen enthalten. + + + + Define diagnostic title correctly + Titel der Diagnosemeldung ordnungsgemäß definieren + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Diese C#-Compilererweiterung sollte nicht in einer Assembly implementiert werden, die einen Verweis auf "Microsoft.CodeAnalysis.VisualBasic" enthält. Die Assembly "Microsoft.CodeAnalysis.VisualBasic" wird nicht immer in C#-Kompilierungsszenarien bereitgestellt. Verweise darauf können daher ein unvorhersehbares Verhalten der Compilererweiterung verursachen. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Diese Compilererweiterung sollte nicht in einer Assembly mit dem Zielframework "{0}" implementiert werden. Verweise auf andere Zielframeworks führen dazu, dass sich der Compiler unvorhersehbar verhält. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Typen, die Compilererweiterungspunkte implementieren, dürfen nur in Assemblys für netstandard2.0 deklariert werden. Spezifischere Zielframeworks sind nur in einer Teilmenge der unterstützten Kompilierungsszenarien verfügbar, weshalb das Festlegen der Zielframeworks dazu führen kann, dass sich das Feature unvorhersehbar verhält. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Compilererweiterungen sollten in Assemblys implementiert werden, die netstandard2.0 als Ziel verwenden + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Diese Compilererweiterung sollte nicht in einer Assembly implementiert werden, die einen Verweis auf "Microsoft.CodeAnalysis.Workspaces" enthält. Die Assembly "Microsoft.CodeAnalysis.Workspaces" wird in Befehlszeilen-Kompilierungsszenarien nicht bereitgestellt. Verweise darauf können daher ein unvorhersehbares Verhalten der Compilererweiterung verursachen. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Diese Visual Basic-Compilererweiterung sollte nicht in einer Assembly implementiert werden, die einen Verweis auf "Microsoft.CodeAnalysis.CSharp" enthält. Die Assembly "Microsoft.CodeAnalysis.CSharp" wird nicht immer in Visual Basic-Kompilierungsszenarien bereitgestellt. Verweise darauf können daher ein unvorhersehbares Verhalten der Compilererweiterung verursachen. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Typen, die Compilererweiterungspunkte implementieren, sollten nicht in Assemblys deklariert werden, die Verweise auf Assemblys enthalten, die nicht von allen Kompilierungsszenarien bereitgestellt werden. Andernfalls kann sich das Feature unvorhersehbar verhalten. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Compilererweiterungen sollten in Assemblys mit vom Compiler bereitgestellten Verweisen implementiert werden + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + "GetSemanticModel" ist eine kostspielige Methode zum Aufruf in einem Diagnoseanalysetool, weil damit ein vollständig neues Semantikmodell erstellt wird, das keine Kompilierungsdaten mit dem Compiler oder anderen Analysetools teilt. Dies führt zu zusätzlichen Leistungseinbußen bei der Semantikanalyse. Erwägen Sie stattdessen die Registrierung einer anderen Analysetoolaktion, die die Verwendung eines freigegebenen "SemanticModel" wie z. B. "RegisterOperationAction", "RegisterSyntaxNodeAction" oder "RegisterSemanticModelAction" ermöglicht. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Compilation.GetSemanticModel()-Methode nicht innerhalb eines Diagnoseanalysetools aufrufen + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Compilation.GetSemanticModel()-Methode nicht innerhalb eines Diagnoseanalysetools aufrufen + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + Die DiagnosticId für Analysetools darf keine reservierten IDs verwenden. + + + + '{0}' is a reserved diagnostic ID + "{0}" ist eine reservierte Diagnose-ID. + + + + Do not use reserved diagnostic IDs + Keine reservierten Diagnose-IDs verwenden + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Das Aktivieren der Versionsnachverfolgung für Analysepakete unterstützt Sie beim Nachverfolgen und Dokumentieren der Analysediagnose, die mit den einzelnen Analysereleases ausgeliefert und/oder geändert wird. Details finden Sie unter https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Analyseversionsnachverfolgung für Analyseprojekt mit Regel "{0}" aktivieren + + + + Enable analyzer release tracking + Analyseversionsnachverfolgung aktivieren + + + + Enable concurrent execution + Gleichzeitige Ausführung aktivieren + + + + Enable concurrent execution + Gleichzeitige Ausführung aktivieren + + + + Enable concurrent execution + Gleichzeitige Ausführung aktivieren + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Ungültiger Eintrag in Analysetool-Releasedatei. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Die Analysetool-Releasedatei "{0}" weist einen ungültigen Eintrag "{1}" auf. + + + + Invalid entry in analyzer release file + Ungültiger Eintrag in Analysetool-Releasedatei + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + In der Analysetool-Releasedatei "{0}" fehlt der Releaseheader "{1}" oder ist ungültig. + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + Die Analysetool-Releasedatei "{0}" weist einen ungültigen Eintrag "{1}" ohne zuvor veröffentlichtes Release für die Regel "{2}" auf. Fügen Sie stattdessen einen separaten Eintrag "{1}" für die Regel in der nicht veröffentlichten Releasedatei hinzu. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + Die Analysetool-Releasedatei "{0}" weist einen Eintrag mit mindestens einem Feld "Nicht erkannt" auf, das in "{1}" manuell gefüllt werden muss. + + + + Missing '{0}' attribute + Fehlendes {0}-Attribut. + + + + Missing diagnostic analyzer attribute + Fehlendes Attribut für Diagnoseanalysetool + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Nicht abstrakte Untertypen von DiagnosticAnalyzer müssen mit "DiagnosticAnalyzerAttribute(s)" markiert werden. Das Argument für diese Attribute, falls vorhanden, legt die unterstützten Sprachen für die Analyse fest. Analysetypen ohne dieses Attribut werden von der Analyse-Engine ignoriert. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Das Diagnoseanalysetool "{0}" kann möglicherweise sowohl C# als auch Visual Basic unterstützen. Ziehen Sie in Betracht, DiagnosticAnalyzerAttribute ein Argument für die Sprachunterstützung "{1}" hinzuzufügen. + + + + Recommend adding language support to diagnostic analyzer + Empfehlung zum Hinzufügen von Sprachunterstützung zum Diagnoseanalysetool + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Das Diagnoseanalysetool ist für die Unterstützung von nur einer Sprache markiert, die Analyseassembly verweist jedoch offenbar auf keine sprachspezifischen CodeAnalysis-Assemblys und funktioniert daher wahrscheinlich für mehrere Sprachen. Ziehen Sie in Betracht, DiagnosticAnalyzerAttribute ein weiteres Sprachargument hinzuzufügen. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Wenden Sie das DiagnosticAnalyzer-Attribut für "{0}" an. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Wenden Sie das DiagnosticAnalyzer-Attribut sowohl für "{0}" als auch für "{1}" an. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Geben Sie bei der Registrierung einer Symbolanalyseaktion mindestens einen relevanten SymbolKind-Wert an. + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Geben Sie bei der Registrierung einer Syntaxknotenanalyse-Aktion mindestens einen relevanten SymbolKind-Wert an. + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Geben Sie bei der Registrierung einer Vorgangsanalyseaktion mindestens einen relevanten OperationKind-Wert an. + + + + Missing kind argument when registering an analyzer action + Fehlendes kind-Argument beim Registrieren einer Analyseaktion + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Sie müssen mindestens eine Variante für Syntax, Symbol oder Vorgang angeben, wenn Sie eine Syntax-, Symbol- oder Vorgangsanalyseaktion registrieren. Andernfalls wird die registrierte Aktion während der Analyse nicht aufgerufen. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Ein Projekt, das Analysetools oder Quellgeneratoren enthält, muss die Eigenschaft "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>" angeben. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + "{0}": Ein Projekt, das Analysetools oder Quellgeneratoren enthält, muss die Eigenschaft "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>" angeben + + + + Specify analyzer banned API enforcement setting + Geben Sie die Erzwingungseinstellung für gesperrte API für das Analysetool an + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Bei der Überprüfung von Syntaxarten ist "syntax.IsKind(kind)" der Syntax "syntax.Kind() == kind" vorzuziehen. Der Code mit "IsKind" ist zur Laufzeit etwas effizienter, daher trägt die konsistente Verwendung dieser Form in komplexen Analyseszenarios zur Verbesserung der Leistung bei. + + + + Use 'IsKind' instead of 'Kind' + "IsKind" anstelle von "Kind" verwenden + + + + Prefer 'IsKind' for checking syntax kinds + "IsKind" für die Überprüfung von Syntaxarten bevorzugen + + + + Prefer 'IsKind' for checking syntax kinds + "IsKind" für die Überprüfung von Syntaxarten bevorzugen + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + Der Wert "customTags" wird verwendet, um bestimmte Aktionen und Filter für Diagnosedeskriptoren basierend auf den spezifischen Werten der Tags zu aktivieren. Jedes Roslyn-Analysetool muss mindestens ein Tag aus der Klasse "WellKnownDiagnosticTags" aufweisen. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Erwägen Sie die Bereitstellung eines customTags-Werts ungleich NULL für den Konstruktor des Diagnosedeskriptors, um die Metadatenfilterung von Diagnosedeskriptoren zu aktivieren. + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + customTags-Wert ungleich NULL für den Konstruktor des Diagnosedeskriptors angeben + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Entfernen Sie doppelte Einträge für die Diagnose-ID zwischen Analysetoolreleases. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + Die Regel "{0}" weist zwischen Release "{1}" und Release "{2}" einen doppelten Eintrag auf. + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Entfernen Sie doppelte Einträge für die Diagnose-ID zwischen Analysetoolreleases. + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Entfernen Sie doppelte Einträge für die Diagnose-ID im selben Analysetoolrelease. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + Die Regel "{0}" enthält mehrere Einträge für das Release "{1}" in der Analysetool-Releasedatei "{2}". + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Entfernen Sie doppelte Einträge für die Diagnose-ID im selben Analysetoolrelease. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Veröffentlichte Diagnose-IDs, die nicht mehr gemeldet werden, müssen in der Tabelle "Entfernte Regeln" in der nicht veröffentlichten Datei einen Eintrag enthalten. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + Die Regel "{0}" wurde im Analysetoolrelease "{1}" ausgeliefert, wird aber als Diagnose für das Analysetool nicht mehr unterstützt. Fügen Sie der nicht veröffentlichten Datei einen Eintrag für diese Regel in einer Tabelle "Entfernte Regeln" hinzu. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Veröffentlichte Diagnose-IDs, die nicht mehr gemeldet werden, müssen in der Tabelle "Entfernte Regeln" in der nicht veröffentlichten Datei einen Eintrag enthalten. + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Einträge für Analysetooldiagnose-IDs, die nicht mehr gemeldet und niemals veröffentlicht werden, können aus dem nicht veröffentlichten Analysetoolrelease entfernt werden. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + Die Regel "{0}" ist Teil des nächsten nicht veröffentlichten Analysetoolrelease, wird aber für kein Analysetool als Diagnose unterstützt. + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Fügen Sie einem nicht veröffentlichten Analysetoolrelease keine entfernten Analysetooldiagnose-IDs hinzu. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Beim Aufruf von "SemanticModel.GetDeclaredSymbol" für bestimmte Typen, die von "SyntaxNode" erben, geben beispielsweise "GlobalStatementSyntax" und "IncompleteMemberSyntax" immer "null" zurück. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Beim Aufrufen von "SemanticModel.GetDeclaredSymbol" mit einem Argument vom Typ "FieldDeclarationSyntax" oder "EventFieldDeclarationSyntax" wird immer "null" zurückgegeben. Rufen Sie stattdessen "GetDeclaredSymbol" mit den Variablendeklaratoren aus dem Feld auf. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Ein Aufruf von "SemanticModel.GetDeclaredSymbol({0})" gibt immer "null" zurück. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Ein Aufruf von "SemanticModel.GetDeclaredSymbol({0})" gibt immer "null" zurück. + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Dieser Aufruf von "SemanticModel.GetDeclaredSymbol()" gibt immer "null" zurück. + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Das Symbol wurde für die Verwendung in dem Analysetool als gesperrt gekennzeichnet, und es muss stattdessen eine Alternative verwendet werden. + + + + The symbol '{0}' is banned for use by analyzers{1} + Das Symbol "{0}" ist für die Verwendung durch Analysetools gesperrt{1} + + + + Do not use APIs banned for analyzers + Verwenden Sie keine für Analysetools gesperrte APIs + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Diagnose-IDs, die in der Analysetool-Releasedatei als entfernt markiert sind, dürfen von Analysetools nicht gemeldet werden. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + Die Regel "{0}" ist im aktuellen Analysetoolrelease als entfernt markiert, wird aber noch gemeldet. + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Diagnose-IDs, die in der Analysetool-Releasedatei als entfernt markiert sind, dürfen von Analysetools nicht gemeldet werden. + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + Der SymbolKind-Wert "{0}" wird für Symbolanalyseaktionen nicht unterstützt. + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Nicht unterstütztes SymbolKind-Argument beim Registrieren einer Symbolanalyseaktion + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic wurde mit einem nicht unterstützten DiagnosticDescriptor "{0}" aufgerufen. + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic wurde mit einem nicht unterstützten DiagnosticDescriptor aufgerufen + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic darf nur mit unterstützten DiagnosticDescriptors aufgerufen werden, die von der Eigenschaft "DiagnosticAnalyzer.SupportedDiagnostics" zurückgegeben werden. Andernfalls wird die gemeldete Diagnose durch die Analyse-Engine herausgefiltert. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + Das Typargument "{0}" für den Typparameter "{1}" der Methode "{2}" ist keine SyntaxKind-Enumeration. + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Ungültiges Typargument für die Register-Methode von DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Die sprachspezifischen Register-Methoden von DiagnosticAnalyzer, beispielsweise "RegisterSyntaxNodeAction", "RegisterCodeBlockStartAction" und "RegisterCodeBlockEndAction", erwarten ein sprachspezifisches SyntaxKind-Typargument für den zugehörigen Typparameter "{0}". Andernfalls kann die registrierte Analyseaktion während der Analyse nicht aufgerufen werden. + + + + Start action has no registered non-end actions + Für die Startaktion wurden keine Nicht-Endaktionen registriert + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + "{0}" registriert keine Analyseaktionen. Ziehen Sie in Betracht, in "{1}" registrierte Aktionen, die von dieser Startaktion abhängen, nach "{0}" zu verschieben. + + + + Start action has no registered actions + Für die Startaktion wurden keine Aktionen registriert + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + "{0}" registriert keine Analyseaktionen mit Ausnahme von "{1}". Ziehen Sie in Betracht, dieses Paar aus Start-/Endaktion durch "{2}" zu ersetzen oder die in "{3}" registrierten Aktionen, die von dieser Startaktion abhängen, nach "{0}" zu verschieben. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Eine Startaktion für das Analysetool ermöglicht die Durchführung einer zustandsbehafteten Analyse über eine bestimmte Codeeinheit wie einen Codeblock, eine Kompilierung etc. Ein sorgfältiges Design ist erforderlich, um eine effiziente Ausführung des Analysetools ohne Arbeitsspeicherverluste zu erzielen. Beachten Sie beim Schreiben solcher Analysen die folgenden Richtlinien: +1. Definieren Sie einen neuen Geltungsbereich für die registrierte Startaktion, möglicherweise mit einem privaten geschachtelten Typ für die Analyse der einzelnen Codeeinheiten. +2. Definieren und initialisieren Sie bei Bedarf den Zustand in der Startaktion. +3. Registrieren Sie mindestens eine Nicht-Endaktion, die auf diesen Zustand in der Startaktion verweist. Falls keine solche Aktion erforderlich ist, ziehen Sie in Betracht, die Startaktion durch eine Nicht-Startaktion zu ersetzen. Beispielsweise sollte eine CodeBlockStartAction ohne registrierte Aktionen oder mit nur einer registrierten CodeBlockEndAction durch eine CodeBlockAction ersetzt werden. +4. Registrieren Sie bei Bedarf eine Endaktion, um die Diagnose basierend auf dem Endzustand zu melden. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Stellen Sie sicher, dass dem Analysetoolrelease ein aktueller Eintrag für Analysetooldiagnose-IDs hinzugefügt wird. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + Die Regel "{0}" weist gegenüber dem letzten Release eine Änderung an "Kategorie" oder "Schweregrad" auf. Setzen Sie entweder die Updates in der Quelle zurück, oder fügen Sie der nicht veröffentlichten Releasedatei einen neuen aktuellen Eintrag hinzu. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Stellen Sie sicher, dass dem Analysetoolrelease ein aktueller Eintrag für Analysetooldiagnose-IDs hinzugefügt wird. + + + + Update rule entry in unshipped release file + Regeleintrag in nicht veröffentlichter Releasedatei aktualisieren + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Ziehen Sie die Bereitstellung lokalisierbarer Argumente vom Typ "{0}" für den Konstruktor des Diagnosedeskriptors in Betracht, um sicherzustellen, dass der Deskriptor lokalisierbar ist. + + + + Provide localizable arguments to diagnostic descriptor constructor + Lokalisierbare Argumente für Konstruktor des Diagnosedeskriptors angeben + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Wenn Ihr Diagnoseanalysetool und die zugehörigen gemeldeten Diagnosen lokalisierbar sein sollen, müssen auch die unterstützten DiagnosticDescriptors für die Erstellung der Diagnose lokalisierbar sein. In diesem Fall müssen dem Konstruktor des Diagnosedeskriptors die lokalisierbaren Argumente für den Parameter "title" (und optional "description") angegeben werden, um sicherzustellen, dass der Deskriptor lokalisierbar ist. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Vermeiden Sie die Speicherung kompilierungsbezogener Daten des Typs "{0}" in den Feldern eines Diagnoseanalysetools. + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Speicherung kompilierungsbezogener Daten in den Feldern eines Diagnoseanalysetools vermeiden + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Die Instanz des Diagnoseanalysetools kann die Lebensdauer der Kompilierung überdauern. Daher kann die Speicherung kompilierungsbezogener Daten (z. B. Symbole) in den Feldern eines Diagnoseanalysetools zur Beibehaltung veralteter Kompilierungen und zu Arbeitsspeicherverlusten führen. Speichern Sie diese Daten stattdessen in einem separaten Typ, der in einer Kompilierungsstartaktion instanziiert wird, die über die {0}.{1}-API registriert ist. Eine Instanz dieses Typs wird pro Kompilierung erstellt und überdauert nicht die Lebensdauer der Kompilierung, sodass Arbeitsspeicherverluste vermieden werden. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + Der Autor dieser Schnittstelle hat keine Drittanbieterimplementierungen dieser Schnittstelle beabsichtigt und behält sich das Recht auf Änderungen vor. Die Implementierung dieser Schnittstelle kann daher zu einem Kompatibilitätsproblem der Quelle oder Binärdatei mit einer zukünftigen Version dieser Schnittstelle führen. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Der Typ "{0}" kann die Schnittstelle "{1}" nicht implementieren, weil "{1}" für die öffentliche Implementierung nicht zur Verfügung steht. + + + + Only internal implementations of this interface are allowed + Es sind nur interne Implementierungen dieser Schnittstelle zulässig + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Ein CodeFixProvider, der die Korrektur aller Vorkommen unterstützen soll, muss die registrierten Codeaktionen in Äquivalenzklassen einteilen, indem ein expliziter Äquivalenzschlüssel ungleich NULL zugewiesen wird, der für jede Art von durch diese Korrekturregel erstellten Codeaktionen eindeutig sein muss. Dadurch kann der FixAllProvider alle Diagnosen im erforderlichen Geltungsbereich korrigieren, indem er Codeaktionen dieser Korrekturregel anwendet, die sich in der Äquivalenzklasse der Triggercodeaktion befinden. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Geben Sie ein explizites Argument für den optionalen Parameter "{0}" an, das nicht NULL und für jede Art von durch diese Korrekturregel erstellten Aktionen eindeutig ist. + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Aktionen zum Erstellen von Code müssen zur Unterstützung von FixAll-Vorkommen einen eindeutigen EquivalenceKey aufweisen + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + "{0}" besitzt den Standardwert "null" für die Eigenschaft "{1}". Setzen Sie diese Eigenschaft für "{0}" außer Kraft, um zu einem für alle Codeaktionen pro Korrekturregel eindeutigen Wert ungleich NULL zurückzukehren, oder verwenden Sie eine solche vorhandene Codeaktion. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Zur Unterstützung von FixAll-Vorkommen Codeaktionen mit eindeutigem EquivalenceKey verwenden + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Viele von Roslyn verfügbar gemachte Objekte sind unveränderlich. Der Rückgabewert aus einem Methodenaufruf für diese Objekte darf nicht ignoriert werden. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + "{0}" ist unveränderlich, "{1}" besitzt darauf keine Auswirkungen. Ziehen Sie in Betracht, den Rückgabewert von "{1}" zu verwenden. + + + + Do not ignore values returned by methods on immutable objects + Keine von Methoden zu unveränderlichen Objekten zurückgegebenen Werte ignorieren + + + + Code fix providers should provide FixAll support + Anbieter von Codekorrekturen müssen FixAll-Unterstützung bereitstellen + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + "{0}" registriert mindestens eine Codefehlerbehebung, aber überschreibt nicht die Methode "CodeFixProvider.GetFixAllProvider". Überschreiben Sie diese Methode, und stellen Sie einen FixAllProvider ungleich NULL für FixAll-Unterstützung bereit, etwa "WellKnownFixAllProviders.BatchFixer" oder "null", um die FixAll-Unterstützung explizit zu deaktivieren. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Ein CodeFixProvider muss FixAll-Unterstützung bereitstellen, damit mehrere Instanzen der zugrunde liegenden Diagnose mit einer einzigen Codefehlerbehebung korrigiert werden können. Weitere Informationen finden Sie in der Dokumentation unter https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. + + + + Override GetFixAllProvider. + Überschreiben Sie GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Erwägen Sie die Bereitstellung eines "helpLinkUri" ungleich NULL zum Konstruktor des Diagnosedeskriptors, um Informationen anzuzeigen, wenn diese Diagnose in der Fehlerliste erscheint. + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + helpLinkUri-Wert ungleich NULL für Konstruktor des Diagnosedeskriptors angeben + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + Der helpLinkUri-Wert wird verwendet, um Informationen anzuzeigen, wenn diese Diagnose in der Fehlerliste erscheint. Jedes Analysetool sollte mithilfe eines helpLinkUri-Werts auf eine Hilfeseite zeigen, die sich im Laufe der Zeit nicht ändert. + + + + DiagnosticId for analyzers must be in specified format + DiagnosticId für das Analysetool muss im angegebenen Format vorliegen + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + Die Diagnose-ID "{0}" der Kategorie "{1}" liegt nicht im erforderlichen Bereich und/oder weist nicht das in der Datei "{3}" angegebene Format "{2}" auf. + + + + DiagnosticId for analyzers must be in specified format. + Die DiagnosticId für das Analysetool muss im angegebenen Format vorliegen. + + + + DiagnosticId must be unique across analyzers + DiagnosticId muss für jedes Analysetool eindeutig sein + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + Die Diagnose-ID "{0}" wird bereits vom Analysetool "{1}" verwendet. Verwenden Sie eine andere Diagnose-ID + + + + DiagnosticId must be unique across analyzers. + Die DiagnosticId muss für jedes Analysetool eindeutig sein. + + + + Category for analyzers must be from the specified values + Kategorie für Analysetools muss einem der angegebenen Werte entsprechen + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + Die Kategorie "{0}" ist nicht in den zulässigen Kategorien enthalten, die in der Datei "{1}" angegeben sind. + + + + Category for analyzers must be from the specified values. + Die Kategorie für Analysetools muss einem der angegebenen Werte entsprechen. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Ungültiger Eintrag in der Spezifikationsdatei für Kategorien und Diagnose-ID-Bereich der Analysetools + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Ungültiger Eintrag "{0}" in der Spezifikationsdatei "{1}" für Kategorien und Diagnose-ID-Bereich der Analysetools. + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Ungültiger Eintrag in der Spezifikationsdatei für Kategorien und Diagnose-ID-Bereich der Analysetools. + + + + DiagnosticId for analyzers must be a non-null constant + Die DiagnosticId für Analysetools muss eine Konstante ungleich NULL sein + + + + Diagnostic Id for rule '{0}' must be a non-null constant + Die Diagnose-ID für die Regel "{0}" muss eine Konstante ungleich NULL sein. + + + + DiagnosticId for analyzers must be a non-null constant. + Die DiagnosticId für Analysetools muss eine Konstante ungleich NULL sein. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Diagnoseanalysetypen dürfen keine Typen aus Arbeitsbereichassemblys verwenden. Arbeitsbereichassemblys sind nur verfügbar, wenn das Analysetool in der Visual Studio IDE-Liveanalyse ausgeführt wird, nicht jedoch während einer Builderstellung über die Befehlszeile. Das Referenzieren von Typen aus Arbeitsbereichassemblys führt zu Laufzeitausnahmen bei der Analyseausführung während der Builderstellung über die Befehlszeile. + + + + Do not use types from Workspaces assembly in an analyzer + Verwenden Sie im Analysetool keine Typen aus Arbeitsbereichassemblys. + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Ändern Sie den Diagnoseanalysetyp "{0}", um alle direkten und/oder indirekten Zugriffe auf die Typen "{1}" zu entfernen, die auf die Typen "{2}" zugreifen. + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Ändern Sie den Diagnoseanalysetyp "{0}", um alle direkten Zugriffe auf die Typen "{1}" zu entfernen. + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace wurde in das NuGet-Paket "Microsoft.CodeAnalysis.Workspaces.MSBuild" verschoben, in dem Breaking Changes an APIs vorgenommen wurden. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Führen Sie ein Upgrade für MSBuildWorkspace durch, indem Sie einen Paketverweis auf das NuGet-Paket "Microsoft.CodeAnalysis.Workspaces.MSBuild" hinzufügen. Einzelheiten zur richtigen Verwendung von MSBuildWorkspace finden Sie unter https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + Upgrade von MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf new file mode 100644 index 0000000000000..061d6f47f4f2b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.es.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + El "DiagnosticDescriptor" asignado al campo se utiliza para informar de un diagnóstico de fin de compilación, pero el constructor "DiagnosticDescriptor" utilizado para inicializarlo no pasa la etiqueta personalizada requerida "CompilationEnd". Consulte la documentación de "WellKnownDiagnosticTags.CompilationEnd" para obtener más información. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Agregar la etiqueta personalizada "CompilationEnd" al descriptor de diagnóstico usado para inicializar el campo "{0}" como se usa para notificar un diagnóstico final de compilación + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Agregar la etiqueta personalizada "CompilationEnd" al descriptor de diagnóstico de fin de compilación + + + + Add rule entry to unshipped release file + Agregar una entrada de regla a un archivo de versión no incluido + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Herede el tipo "{0}" de DiagnosticAnalyzer o quite los elementos DiagnosticAnalyzerAttribute. + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Los tipos marcados con DiagnosticAnalyzerAttribute deben heredar de DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Usar un 'SymbolEqualityComparer' para la comparación de símbolos + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Debe compararse la igualdad de los símbolos, no la identidad. Use una sobrecarga que acepte un elemento "IEqualityComparer" y pase "SymbolEqualityComparer". + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Debe compararse la igualdad de los símbolos, no la identidad. Una llamada explícita a "GetHashCode" probablemente producirá un comportamiento incorrecto. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Usar 'SymbolEqualityComparer' al comparar símbolos + + + + Symbols should be compared for equality + Se debe comparar la igualdad de los símbolos + + + + Configure generated code analysis + Configurar análisis de código generado + + + + Configure generated code analysis + Configurar análisis de código generado + + + + Configure generated code analysis + Configurar análisis de código generado + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Todos los identificadores de diagnóstico del analizador admitidos deben formar parte de una versión del analizador. + + + + Rule '{0}' is not part of any analyzer release + La regla "{0}" no forma parte de ninguna versión del analizador. + + + + Add analyzer diagnostic IDs to analyzer release + Agregar identificadores de diagnóstico del analizador a la versión del analizador + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + La descripción del diagnóstico debe tener una o varias oraciones que terminen con un signo de puntuación y no debe tener espacios en blanco al principio ni al final. + + + + Define diagnostic description correctly + Definir correctamente la descripción del diagnóstico + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + El mensaje de diagnóstico no debe contener ningún carácter de retorno de línea ni espacios en blanco al principio o al final; además, debe ser una sola oración sin punto final o varias oraciones con punto final. + + + + Define diagnostic message correctly + Definir correctamente el mensaje de diagnóstico + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + El título del diagnóstico no debe tener punto final ni ningún carácter de retorno de línea, así como tampoco espacios en blanco al principio ni al final. + + + + Define diagnostic title correctly + Definir el título del diagnóstico correctamente + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensión del compilador de C# no debe implementarse en un ensamblado que contenga una referencia a Microsoft.CodeAnalysis.VisualBasic. El ensamblado Microsoft.CodeAnalysis.VisualBasic no siempre se proporciona durante los escenarios de compilación de C#, por lo que las referencias a él podrían hacer que la extensión del compilador se comporte de forma impredecible. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Esta extensión del compilador no debe implementarse en un ensamblado con la plataforma de destino '{0}'. Las referencias a otras plataformas de destino harán que el compilador se comporte de forma imprevisible. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Los tipos que implementan puntos de extensión del compilador solo deben declararse en ensamblados que tengan como destino netstandard2.0. Las plataformas de destino más específicas solo están disponibles en un subconjunto de escenarios de compilación admitidos, por lo que establecerlas como destino puede provocar que la característica se comporte de forma impredecible. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Las extensiones del compilador deben implementarse en ensamblados que tengan como destino netstandard2.0 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensión del compilador no debe implementarse en un ensamblado que contenga una referencia a Microsoft.CodeAnalysis.Workspaces. El ensamblado Microsoft.CodeAnalysis.Workspaces no se proporciona durante los escenarios de compilación de la línea de comandos, por lo que las referencias a él podrían hacer que la extensión del compilador se comporte de forma impredecible. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensión del compilador de Visual Basic no debe implementarse en un ensamblado que contenga una referencia a Microsoft.CodeAnalysis.CSharp. El ensamblado Microsoft.CodeAnalysis.CSharp no siempre se proporciona durante los escenarios de compilación de Visual Basic, por lo que las referencias a él podrían hacer que la extensión del compilador se comportara de forma impredecible. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Los tipos que implementan puntos de extensión del compilador no deben declararse en ensamblados que contengan referencias a ensamblados que no se proporcionan en todos los escenarios de compilación. Si lo hace, la característica puede comportarse de forma impredecible. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Las extensiones del compilador deben implementarse en ensamblados con referencias proporcionadas por el compilador + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + "GetSemanticModel" es un método caro para invocar dentro de un analizador de diagnóstico porque crea un modelo semántico completamente nuevo que no comparte datos de compilación con el compilador u otros analizadores. Como consecuencia, se generan costos de rendimiento adicionales durante el análisis semántico. En su lugar, podría registrar una acción del analizador diferente que permita usar un elemento "SemanticModel" compartido, como "RegisterOperationAction", "RegisterSyntaxNodeAction" o "RegisterSemanticModelAction". + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + No invoque el método Compilation.GetSemanticModel() en un analizador de diagnóstico + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + No invoque el método Compilation.GetSemanticModel() en un analizador de diagnóstico + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + El valor DiagnosticId para los analizadores no debe usar identificadores reservados. + + + + '{0}' is a reserved diagnostic ID + "{0}" es un identificador de diagnóstico reservado. + + + + Do not use reserved diagnostic IDs + No use identificadores de diagnóstico reservados + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Habilitar el seguimiento de versiones para los paquetes de analizadores ayuda al seguimiento y la documentación de los diagnósticos de analizador que se incluyen o se modifican con cada versión de este. Consulte los detalles en https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Habilitar el seguimiento de versiones del analizador para el proyecto de analizador que contiene la regla "{0}" + + + + Enable analyzer release tracking + Habilitar el seguimiento de versiones del analizador + + + + Enable concurrent execution + Habilitar la ejecución simultánea + + + + Enable concurrent execution + Habilitar la ejecución simultánea + + + + Enable concurrent execution + Habilitar la ejecución simultánea + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Entrada no válida en el archivo de versión del analizador. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + El archivo de versión del analizador "{0}" tiene una entrada "{1}" no válida. + + + + Invalid entry in analyzer release file + Entrada no válida en el archivo de versión del analizador + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + El encabezado de versión "{1}" del archivo de versión del analizador "{0}" falta o no es válido. + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + El archivo de versión del analizador "{0}" tiene una entrada "{1}" no válida sin una versión incluida previamente para la regla "{2}". En su lugar, agregue una entrada "{1}" independiente para la regla en el archivo de versión no incluido. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + El archivo de versión del analizador "{0}" tiene una entrada con uno o más campos "No detectado" que deben rellenarse manualmente en "{1}". + + + + Missing '{0}' attribute + Falta el atributo "{0}". + + + + Missing diagnostic analyzer attribute + Falta el atributo del analizador de diagnóstico + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Los subtipos no abstractos de DiagnosticAnalyzer deben marcarse con el atributo DiagnosticAnalyzerAttribute. El argumento de estos atributos, si lo hay, determina los lenguajes admitidos para el analizador. El motor de análisis omite los tipos de analizador sin este atributo. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + El analizador de diagnóstico “{0}” admite tanto C# como Visual Basic. Considere agregar un argumento a DiagnosticAnalyzerAttribute para la compatibilidad con el lenguaje de “{1}”. + + + + Recommend adding language support to diagnostic analyzer + Se recomienda agregar compatibilidad con lenguajes al analizador de diagnóstico + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + El analizador de diagnóstico está marcado como compatible con un solo lenguaje, pero el ensamblado del analizador no parece hacer referencia a ningún ensamblado CodeAnalysis específico de un lenguaje, por lo que es posible que funcione para más de un lenguaje. Considere agregar un argumento de lenguaje adicional a DiagnosticAnalyzerAttribute. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Aplique el atributo DiagnosticAnalyzer para “{0}”. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Aplique el atributo DiagnosticAnalyzer para “{0}” y “{1}”. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Especifique al menos un elemento SymbolKind de interés al registrar una acción del analizador de símbolos. + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Especifique al menos un elemento SyntaxKind de interés al registrar una acción del analizador de nodos de sintaxis. + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Especifique al menos un elemento OperationKind de interés al registrar una acción del analizador de operaciones. + + + + Missing kind argument when registering an analyzer action + Falta un argumento de tipo al registrar una acción del analizador + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Debe especificar al menos un tipo de sintaxis, símbolo u operación al registrar una acción del analizador de sintaxis, símbolos u operaciones respectivamente. De lo contrario, la acción registrada no se invocará nunca durante el análisis. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Un proyecto que contenga analizadores o generadores de origen debe especificar la propiedad "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>". + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + "{0}": un proyecto que contenga analizadores o generadores de origen debe especificar la propiedad "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>" + + + + Specify analyzer banned API enforcement setting + Especificar la configuración de la aplicación de la API vetada por el analizador + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Use preferentemente "syntax.IsKind(kind)" antes que "syntax.Kind() == kind" al comprobar las variantes de sintaxis. El código que usa "IsKind" es algo más eficaz en tiempo de ejecución, por lo que el uso coherente de esta forma, cuando proceda, ayuda a mejorar el rendimiento en los escenarios de análisis complejos. + + + + Use 'IsKind' instead of 'Kind' + Usar "IsKind" en lugar de "Kind" + + + + Prefer 'IsKind' for checking syntax kinds + Preferir "IsKind" para comprobar las variantes de sintaxis + + + + Prefer 'IsKind' for checking syntax kinds + Preferir "IsKind" para comprobar las variantes de sintaxis + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + El valor "customTags" se usa como una forma de habilitar acciones y filtros específicos en los descriptores de diagnóstico según valores específicos de las etiquetas. Cada analizador de Roslyn debe tener al menos una etiqueta de la clase "WellKnownDiagnosticTags". + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Considere la posibilidad de proporcionar un objeto "customTags" no NULL para el constructor del descriptor de diagnóstico a fin de permitir el filtrado de metadatos de descriptores de diagnóstico. + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Proporcione un valor "customTags" que no sea NULL para el constructor del descriptor de diagnóstico + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Quite las entradas duplicadas del identificador de diagnóstico entre las versiones del analizador. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + La regla "{0}" tiene una entrada duplicada entre la versión "{1}" y la versión "{2}". + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Quitar las entradas duplicadas del identificador de diagnóstico entre versiones del analizador + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Quite las entradas duplicadas del identificador de diagnóstico en la misma versión del analizador. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + La regla "{0}" tiene más de una entrada para la versión "{1}" en el archivo de versión del analizador "{2}". + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Quitar las entradas duplicadas del identificador de diagnóstico en la misma versión del analizador + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Debe haber una entrada en la tabla "Removed Rules" en el archivo no incluido para los identificadores de diagnóstico incluidos que ya no se notifican. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + La regla "{0}" se incluyó en la versión del analizador "{1}", pero ya no es un diagnóstico admitido para ningún analizador. Agregue una entrada para esta regla en una tabla "Removed Rules" a un archivo no incluido. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Debe haber una entrada de los identificadores de diagnóstico incluidos que ya no se notifican en la tabla "Removed Rules" del archivo no incluido + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Las entradas de los identificadores de diagnóstico del analizador que ya no se notifican y que no se incluyen nunca pueden quitarse de la versión del analizador no incluida. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + La regla "{0}" forma parte de la versión del analizador siguiente sin incluir, pero no es un diagnóstico admitido para ningún analizador. + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + No agregar identificadores de diagnóstico del analizador eliminados a una versión del analizador no incluida + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Llamar a "SemanticModel.GetDeclaredSymbol" con determinados tipos que heredan de "SyntaxNode"; por ejemplo, "GlobalStatementSyntax" y "IncompleteMemberSyntax" siempre devolverán "null". + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Llamar a "SemanticModel.GetDeclaredSymbol" con un argumento de tipo "FieldDeclarationSyntax" o "EventFieldDeclarationSyntax" siempre devolverá "null". Llame a "GetDeclaredSymbol" con los declaradores de variable del campo en su lugar. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Una llamada a "SemanticModel.GetDeclaredSymbol({0})" siempre devolverá "null" + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Una llamada a "SemanticModel.GetDeclaredSymbol({0})" siempre devolverá "null" + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Esta llamada a "SemanticModel.GetDeclaredSymbol()" siempre devolverá "null". + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + El símbolo ha sido marcado como de uso prohibido en los analizadores, y en su lugar debe usarse uno alternativo. + + + + The symbol '{0}' is banned for use by analyzers{1} + El símbolo "{0}" está prohibido para su uso por los analizadores{1} + + + + Do not use APIs banned for analyzers + No usar API prohibidas para analizadores + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Los analizadores no deben informar sobre los identificadores de diagnóstico marcados como quitados en el archivo de versión del analizador. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + La regla "{0}" se ha marcado como quitada en la última versión del analizador, pero aún se notifica. + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Los analizadores no deben informar sobre los identificadores de diagnóstico marcados como quitados en el archivo de versión del analizador + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + El argumento SymbolKind "{0}" no se admite para las acciones del analizador de símbolos. + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Argumento SymbolKind no admitido al registrar una acción del analizador de símbolos + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + Se invocó a ReportDiagnostic con un elemento DiagnosticDescriptor "{0}" no admitido. + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + Se invocó a ReportDiagnostic con un elemento DiagnosticDescriptor no admitido + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic solo debe invocarse con elementos DiagnosticDescriptor admitidos que devuelve la propiedad DiagnosticAnalyzer.SupportedDiagnostics. De lo contrario, el motor de análisis descartará el diagnóstico notificado. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + El argumento de tipo "{0}" para el parámetro de tipo "{1}" del método "{2}" no es una enumeración SyntaxKind. + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Argumento de tipo no válido para el método Register de DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Los métodos Register específicos del lenguaje de DiagnosticAnalyzer, como RegisterSyntaxNodeAction, RegisterCodeBlockStartAction y RegisterCodeBlockEndAction, esperan un argumento de tipo “SyntaxKind” específico del lenguaje para el parámetro de tipo “{0}”. De lo contrario, la acción del analizador registrada no se puede invocar nunca durante el análisis. + + + + Start action has no registered non-end actions + La acción de inicio no tiene registrada ninguna acción no final + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + “{0}” no registra ninguna acción del analizador. Considere mover las acciones registradas en “{1}” dependientes de esta acción de inicio a “{0}”. + + + + Start action has no registered actions + La acción de inicio no tiene ninguna acción registrada + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + “{0}” no registra ninguna acción del analizador, excepto “{1}”. Considere reemplazar este par de acciones de inicio/final por un objeto “{2}” o mover las acciones registradas en “{3}” que dependen de esta acción de inicio a “{0}”. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Una acción de inicio del analizador permite realizar un análisis con estado de una unidad de código dada, como un bloque de código, una compilación, etc. Realice un diseño minucioso para lograr una ejecución eficiente del analizador sin fugas de memoria. Siga las instrucciones a continuación para escribir este tipo de analizadores: +1. Defina un nuevo ámbito para la acción de inicio registrada, posiblemente con un tipo anidado privado para analizar cada unidad de código. +2. Si se requiere, defina e inicialice el estado en la acción de inicio. +3. Registre al menos una acción no final que haga referencia a este estado en la acción de inicio. Si no es necesaria una acción de este tipo, considere reemplazar la acción de inicio por otra que no lo sea. Por ejemplo, una acción CodeBlockStartAction sin acciones registradas o solo una acción CodeBlockEndAction registrada deberían reemplazarse por una acción CodeBlockAction. +4. Si se requiere, registre una acción de final para notificar los diagnósticos en función del estado final. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Asegúrese de que se agregue una entrada actualizada de los identificadores de diagnóstico del analizador a la versión del analizador. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + La regla "{0}" tiene el valor "Categoría" o "Gravedad" cambiado respecto a la última versión. Revierta las actualizaciones en el origen o agregue una entrada nueva actualizada al archivo de versión no incluido. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Asegúrese de que se agregue una entrada actualizada de los identificadores de diagnóstico del analizador a la versión del analizador + + + + Update rule entry in unshipped release file + Actualizar la entrada de la regla en un archivo de versión no incluido + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Considere proporcionar argumentos localizables de tipo "{0}" al constructor descriptor de diagnósticos para asegurarse de que el descriptor es localizable. + + + + Provide localizable arguments to diagnostic descriptor constructor + Proporcione argumentos localizables al constructor descriptor de diagnósticos + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Si el analizador de diagnóstico y los diagnósticos notificados deben ser localizables, los descriptores DiagnosticDescriptor usados para crear los diagnósticos también deben ser localizables. En ese caso, deben proporcionarse argumentos localizables para el parámetro “title” (y, opcionalmente, para “description”) al constructor descriptor de diagnósticos a fin de asegurarse de que el descriptor es localizable. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Evite almacenar datos por compilación de tipo "{0}" en los campos de un analizador de diagnóstico. + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Evite almacenar datos por compilación en los campos de un analizador de diagnóstico + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Una instancia de un analizador de diagnóstico puede superar la duración de la compilación. Así, almacenar datos por compilación (por ejemplo, símbolos) en los campos de un analizador de diagnóstico puede hacer que compilaciones obsoletas se mantengan activas y causar fugas de memoria. En su lugar, debe almacenar estos datos en un tipo independiente del que se haya creado una instancia en una acción de inicio de compilación, registrado con la API “{0}.{1}”. Por cada compilación se crea una instancia de este tipo que no sobrepasa la duración de la compilación, lo que evita fugas de memoria. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + El autor de esta interfaz no tenía intención de contar con implementaciones de terceros de la misma y se reserva el derecho de cambiarla. Así, la implementación de la interfaz puede dar lugar a problemas de compatibilidad binaria o de código fuente con una versión futura de esta. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + El tipo {0} no puede implementar la interfaz {1} porque {1} no está disponible para la implementación pública. + + + + Only internal implementations of this interface are allowed + Solo se permiten implementaciones internas de esta interfaz + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Un objeto CodeFixProvider destinado a corregir todas las repeticiones debe clasificar las acciones de código registradas en clases de equivalencia. Para ello, debe asignarles una clave de equivalencia explícita no nula que sea única para cada tipo de acción de código creada por este reparador. Esto permite a FixAllProvider corregir todos los diagnósticos del ámbito requerido aplicando acciones de código de este reparador que están en la clase de equivalencia de la acción de código desencadenador. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Proporcione un argumento explícito para el parámetro "{0}" opcional, que no sea nulo y que sea único para cada tipo de acción de código creada por este reparador. + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Las acciones de creación de código deben tener un elemento EquivalenceKey único para admitir las repeticiones de FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + “{0}” tiene el valor predeterminado “null” para la propiedad “{1}”. Reemplace esta propiedad en “{0}” para devolver un valor único no NULL en todas las acciones de código por reparador o use una acción de código existente de este tipo. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Use acciones de código con un elemento EquivalenceKey único para admitir las repeticiones de FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Muchos de los objetos que Roslyn expone son inmutables. El valor devuelto de una invocación de método en estos objetos no debe omitirse. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + “{0}” es inmutable y “{1}” no tienen ningún efecto en dicho elemento. Considere usar el valor devuelto de “{1}”. + + + + Do not ignore values returned by methods on immutable objects + No omitir los valores devueltos por métodos en objetos inmutables + + + + Code fix providers should provide FixAll support + Los proveedores de corrección de código deberían proporcionar soporte FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + "{0}" registra una o varias correcciones de código, pero no reemplaza el método "CodeFixProvider.GetFixAllProvider". Reemplace este método y proporcione un valor FixAllProvider que no sea nulo para el soporte FixAll; potencialmente "WellKnownFixAllProviders.BatchFixer" o "null" para deshabilitar explícitamente el soporte FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Un valor CodeFixProvider debe proporcionar soporte FixAll para permitir a los usuarios corregir varias instancias del diagnóstico subyacente con una única corrección de código. Consulte la documentación en https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md para obtener más detalles. + + + + Override GetFixAllProvider. + Reemplace GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Considere la posibilidad de proporcionar un valor "helpLinkUri" no NULL para diagnosticar el constructor del descriptor a fin de mostrar información cuando este diagnóstico aparece en la lista de errores. + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Proporcione un valor "helpLinkUri" que no sea nulo para diagnosticar el constructor del descriptor + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + El valor "helpLinkUri" se utiliza para mostrar información cuando este diagnóstico figura en la lista de errores. Todos los analizadores deben tener un helpLinkUri especificado que apunte a una página de ayuda que no cambie con el tiempo. + + + + DiagnosticId for analyzers must be in specified format + El valor DiagnosticId para los analizadores debe estar en el formato especificado + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + El identificador de diagnóstico "{0}" que pertenece a la categoría "{1}" no se encuentra en el rango o el formato requerido "{2}" que se especifica en el archivo "{3}". + + + + DiagnosticId for analyzers must be in specified format. + El valor DiagnosticId para los analizadores debe estar en el formato especificado. + + + + DiagnosticId must be unique across analyzers + El valor de DiagnosticId debe ser único en los analizadores + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + El identificador de diagnóstico "{0}" ya se ha utilizado por el analizador "{1}". Utilice un identificador de diagnóstico diferente. + + + + DiagnosticId must be unique across analyzers. + El valor de DiagnosticId debe ser único en los analizadores. + + + + Category for analyzers must be from the specified values + La categoría de los analizadores debe proceder de los valores especificados + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + La categoría "{0}" no se corresponde con las categorías permitidas que se especifican en el archivo "{1}". + + + + Category for analyzers must be from the specified values. + La categoría de los analizadores debe proceder de los valores especificados. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Entrada no válida en la categoría del analizador y el archivo de especificación del rango de identificadores de diagnóstico + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Entrada "{0}" no válida en la categoría del analizador y el archivo de especificación del rango de identificadores de diagnóstico "{1}". + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Entrada no válida en la categoría del analizador y el archivo de especificación del rango de identificadores de diagnóstico. + + + + DiagnosticId for analyzers must be a non-null constant + El valor DiagnosticId de los analizadores debe ser una constante no nula + + + + Diagnostic Id for rule '{0}' must be a non-null constant + El identificador de diagnóstico de la regla "{0}" debe ser una constante no nula. + + + + DiagnosticId for analyzers must be a non-null constant. + El valor DiagnosticId de los analizadores debe ser una constante no nula. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Los tipos de analizador de diagnóstico no deben usar tipos de ensamblados de Áreas de trabajo. Los ensamblados de Áreas de trabajo solo están disponibles cuando el analizador se ejecuta en el análisis activo del IDE de Visual Studio, pero no están disponibles durante la compilación de la línea de comando. Hacer referencia a tipos de ensamblados de Áreas de trabajo dará lugar a la excepción de tiempo de ejecución durante la ejecución del analizador en la compilación de línea de comando. + + + + Do not use types from Workspaces assembly in an analyzer + No use tipos del ensamblado de Áreas de trabajo en un analizador + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Cambie el tipo de analizador de diagnóstico "{0}" para quitar todo acceso directo o indirecto a los tipos "{1}", que accede a los tipos "{2}" + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Cambie el tipo de analizador de diagnóstico "{0}" para quitar todo acceso directo a los tipos "{1}" + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspacese ha movido al paquete Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet y hay cambios de API novedosos. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Actualice MSBuildWorkspace agregando una referencia de paquete al paquete de NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild. Consulte https://go.microsoft.com/fwlink/?linkid=874285 para obtener información sobre cómo utilizar correctamente MSBuildWorkspace. + + + + Upgrade MSBuildWorkspace + Actualización de MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf new file mode 100644 index 0000000000000..8d56815d50e32 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.fr.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' affecté au champ est utilisé pour signaler un diagnostic de fin de compilation, mais le constructeur 'DiagnosticDescriptor' utilisé pour l’initialiser ne passe pas la balise personnalisée requise « CompilationEnd ». Pour plus d’informations, consultez la documentation de « WellKnownDiagnosticTags.CompilationEnd ». + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Ajouter la balise personnalisée « CompilationEnd » au descripteur de diagnostic utilisé pour initialiser le champ «{0}», car il est utilisé pour signaler un diagnostic de fin de compilation + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Ajouter la balise personnalisée « CompilationEnd » au descripteur de diagnostic de fin de compilation + + + + Add rule entry to unshipped release file + Ajouter une entrée de règle au fichier de version non fourni + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Effectuez un héritage de type '{0}' à partir de DiagnosticAnalyzer, ou supprimez les instances de DiagnosticAnalyzerAttribute + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Les types marqués avec des instances de DiagnosticAnalyzerAttribute doivent hériter de DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Utiliser un ’SymbolEqualityComparer’ pour la comparaison de symboles + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + La comparaison des symboles doit porter sur l'égalité, pas sur l'identité. Utilisez une surcharge acceptant 'IEqualityComparer' et passez 'SymbolEqualityComparer'. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + La comparaison des symboles doit porter sur l'égalité, pas sur l'identité. Un appel explicite à 'GetHashCode' risque d'entraîner un comportement incorrect. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Utiliser ’SymbolEqualityComparer’ lors de la comparaison de symboles + + + + Symbols should be compared for equality + La comparaison des symboles doit porter sur l'égalité. + + + + Configure generated code analysis + Configurer l'analyse du code générée + + + + Configure generated code analysis + Configurer l'analyse du code générée + + + + Configure generated code analysis + Configurer l'analyse du code générée + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Tous les ID de diagnostic d'analyseur pris en charge doivent faire partie d'une version d'analyseur. + + + + Rule '{0}' is not part of any analyzer release + La règle '{0}' ne fait partie d'aucune version d'analyseur + + + + Add analyzer diagnostic IDs to analyzer release + Ajouter des ID de diagnostic d'analyseur à la version d'analyseur + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + La description du diagnostic doit se composer d'une ou de plusieurs phrases se terminant par un signe de ponctuation, sans espaces blancs de début ou de fin + + + + Define diagnostic description correctly + Définir correctement la description de diagnostic + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Le message du diagnostic ne doit comporter aucun caractère de retour de ligne et aucun espace blanc de début ou de fin, et doit tenir en une seule phrase sans point final ou en plusieurs phrases avec un point final + + + + Define diagnostic message correctly + Définir correctement le message de diagnostic + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Le titre du diagnostic ne doit contenir aucun point, aucun caractère de retour de ligne et aucun espace blanc de début ou de fin + + + + Define diagnostic title correctly + Définir correctement le titre de diagnostic + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Cette extension de compilateur ne doit pas être implémentée dans un assembly contenant une référence à Microsoft.CodeAnalysis.Workspaces. L’assembly Microsoft.CodeAnalysis.Workspaces n’est pas fourni pendant les scénarios de compilation de ligne de commande. Par conséquent, les références à celui-ci peuvent entraîner un comportement imprévisible de l’extension du compilateur. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Cette extension de compilateur ne doit pas être implémentée dans un assembly avec la version cible de .Net Framework « {0} ». Les références à d’autres versions cible de .Net Framework entraînent un comportement imprévisible du compilateur. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Les types qui implémentent des points d’extension du compilateur doivent uniquement être déclarés dans les assemblys ciblant netstandard2.0. Les versions cible de .Net Framework plus spécifiques sont disponibles uniquement dans un sous-ensemble de scénarios de compilation pris en charge. Par conséquent, leur ciblage peut entraîner un comportement imprévisible de la fonctionnalité. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Les extensions du compilateur doivent être implémentées dans les assemblys ciblant netstandard2.0 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Cette extension de compilateur ne doit pas être implémentée dans un assembly contenant une référence à Microsoft.CodeAnalysis.Workspaces. L’assembly Microsoft.CodeAnalysis.Workspaces n’est pas fourni pendant les scénarios de compilation de ligne de commande. Par conséquent, les références à celui-ci peuvent entraîner un comportement imprévisible de l’extension du compilateur. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Cette extension de compilateur Visual Basic ne doit pas être implémentée dans un assembly contenant une référence à Microsoft.CodeAnalysis.CSharp. L’assembly Microsoft.CodeAnalysis.CSharp n’étant pas toujours fourni pendant Visual Basic scénarios de compilation, les références à celui-ci peuvent entraîner le comportement imprévisible de l’extension du compilateur. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Les types qui implémentent des points d’extension du compilateur ne doivent pas être déclarés dans les assemblys qui contiennent des références à des assemblys qui ne sont pas fournis par tous les scénarios de compilation. Cela peut entraîner un comportement imprévisible de la fonctionnalité. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Les extensions du compilateur doivent être implémentées dans les assemblys avec des références fournies par le compilateur + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel' est une méthode coûteuse à appeler dans un analyseur de diagnostic, car elle crée un modèle sémantique complètement nouveau, qui ne partage pas les données de compilation avec le compilateur ou d'autres analyseurs. Cela entraîne un coût supplémentaire au niveau des performances au moment de l'analyse sémantique. À la place, inscrivez une autre action d'analyseur qui permet d'utiliser un 'SemanticModel' partagé, par exemple 'RegisterOperationAction', 'RegisterSyntaxNodeAction' ou 'RegisterSemanticModelAction'. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + N'appelez pas la méthode Compilation.GetSemanticModel() dans un analyseur de diagnostic + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + N'appelez pas la méthode Compilation.GetSemanticModel() dans un analyseur de diagnostic + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + DiagnosticId pour les analyseurs ne doit pas utiliser d'ID réservés. + + + + '{0}' is a reserved diagnostic ID + '{0}' est un ID de diagnostic réservé + + + + Do not use reserved diagnostic IDs + N'utilisez pas d'ID de diagnostic réservés + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + L'activation du suivi de version pour les packages d'analyseurs permet de suivre et de documenter les diagnostics d'analyseurs qui sont fournis et/ou changés pour chaque version d'analyseur. Consultez les détails sur https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Activer le suivi de version d'analyseur pour le projet d'analyseur contenant la règle '{0}' + + + + Enable analyzer release tracking + Activer le suivi de version d'analyseur + + + + Enable concurrent execution + Activer l'exécution simultanée + + + + Enable concurrent execution + Activer l'exécution simultanée + + + + Enable concurrent execution + Activer l'exécution simultanée + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Entrée non valide dans le fichier de version d'analyseur. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Le fichier de version d'analyseur '{0}' contient une entrée non valide '{1}' + + + + Invalid entry in analyzer release file + Entrée non valide dans le fichier de version d'analyseur + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + Le fichier de version d'analyseur '{0}' a un en-tête de version '{1}' non valide, ou celui-ci est manquant + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + Le fichier de version d'analyseur '{0}' a une entrée '{1}' non valide sans version antérieure fournie pour la règle '{2}'. À la place, ajoutez une entrée '{1}' distincte pour la règle dans le fichier de version non fourni. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + Le fichier de version d'analyseur '{0}' a une entrée qui contient un ou plusieurs champs 'non détectés' qui doivent être renseignés manuellement dans '{1}' + + + + Missing '{0}' attribute + Attribut '{0}' manquant + + + + Missing diagnostic analyzer attribute + Attribut d'analyseur de diagnostic manquant + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Les sous-types non abstraits de DiagnosticAnalyzer doivent être marqués avec DiagnosticAnalyzerAttribute(s). L'argument de cet attribut (ou de ces attributs), le cas échéant, détermine les langages pris en charge pour l'analyseur. Les types de l'analyseur qui n'ont pas cet attribut sont ignorés par le moteur d'analyse. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + L'analyseur de diagnostic '{0}' peut prendre en charge C# et Visual Basic. Ajoutez un argument à DiagnosticAnalyzerAttribute pour la prise en charge du langage '{1}'. + + + + Recommend adding language support to diagnostic analyzer + Recommandez l'ajout à l'analyseur de diagnostic d'une prise en charge des langages + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Il est indiqué que l'analyseur de diagnostic prend en charge un seul langage. Toutefois, l'assembly de l'analyseur ne semble pas faire référence à des assemblys CodeAnalysis spécifiques à un langage. Il est donc susceptible de fonctionner pour plusieurs langages. Ajoutez un argument de langage supplémentaire à DiagnosticAnalyzerAttribute. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Appliquez l'attribut DiagnosticAnalyzer pour '{0}'. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Appliquez l'attribut DiagnosticAnalyzer pour '{0}' et '{1}'. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Spécifiez au moins un SymbolKind d'intérêt au moment d'inscrire une action d'analyseur de symbole + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Spécifiez au moins un SyntaxKind d'intérêt au moment d'inscrire une action d'analyseur de nœud de syntaxe + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Spécifiez au moins un OperationKind d'intérêt au moment d'inscrire une action d'analyseur d'opération + + + + Missing kind argument when registering an analyzer action + Argument kind manquant au moment d'inscrire une action d'analyseur + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Vous devez spécifier au moins un genre de syntaxe, de symbole ou d'opération au moment d'inscrire une action d'analyseur de syntaxe, de symbole ou d'opération. Sinon, l'action inscrite n'est jamais appelée durant l'analyse. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Un projet contenant des analyseurs ou des générateurs sources doit spécifier la propriété ' <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}' : un projet contenant des analyseurs ou des générateurs sources doit spécifier la propriété ' <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + Specify analyzer banned API enforcement setting + Spécifier le paramètre d’application des API interdites par l’analyseur + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Utilisez 'syntax.IsKind(kind)' au lieu de 'syntax.Kind() == kind' pour la vérification des genres de syntaxe. Dans la mesure où le code utilisant 'IsKind' est légèrement plus efficace au moment de l'exécution, l'utilisation cohérente de ce formulaire permet d'améliorer les performances dans les scénarios d'analyse complexes. + + + + Use 'IsKind' instead of 'Kind' + Utilisez 'IsKind' au lieu de 'Kind' + + + + Prefer 'IsKind' for checking syntax kinds + Préférez 'IsKind' pour la vérification des genres de syntaxe + + + + Prefer 'IsKind' for checking syntax kinds + Préférez 'IsKind' pour la vérification des genres de syntaxe + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + La valeur ’customTags’ est utilisée comme moyen d’activer des actions et des filtres spécifiques sur les descripteurs de diagnostic en fonction des valeurs spécifiques des balises. Tous les analyseurs Roslyn doivent avoir au moins une balise de la classe ’WellKnownDiagnosticTags'. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Si possible, spécifiez une valeur 'customTags' non null pour le constructeur de descripteur de diagnostic afin d'activer le filtrage des métadonnées des descripteurs de diagnostic + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Fournissez une valeur 'customTags' non-null au constructeur de descripteur de diagnostic + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Supprimez les entrées dupliquées de l'ID de diagnostic entre les versions d'analyseur. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + La règle '{0}' contient une entrée dupliquée entre la version '{1}' et la version '{2}' + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Supprimer les entrées dupliquées de l'ID de diagnostic entre les versions d'analyseur + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Supprimez les entrées dupliquées de l'ID de diagnostic dans la même version d'analyseur. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + La règle '{0}' a plusieurs entrées pour la version '{1}' dans le fichier de version d'analyseur '{2}' + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Supprimer les entrées dupliquées de l'ID de diagnostic dans la même version d'analyseur + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Les ID de diagnostic fournis qui ne sont plus signalés doivent avoir une entrée dans la table 'Règles supprimées' du fichier non fourni. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + La règle '{0}' a été fournie dans la version d'analyseur '{1}', mais elle n'est plus un diagnostic pris en charge par un analyseur. Ajoutez une entrée pour cette règle dans une table 'Règles supprimées' au sein du fichier non fourni. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Les ID de diagnostic fournis qui ne sont plus signalés doivent avoir une entrée dans la table 'Règles supprimées' du fichier non fourni + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Les entrées des ID de diagnostic d'analyseur qui ne sont plus signalées et qui ne sont jamais fournies peuvent être supprimées de la version d'analyseur non fournie. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + La règle '{0}' fait partie de la prochaine version d'analyseur non fournie, mais elle n'est pas un diagnostic pris en charge par un analyseur + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Ne pas ajouter d'ID de diagnostic d'analyseur supprimé à une version d'analyseur non fournie + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + L’appel de « SemanticModel.GetDeclaredSymbol » sur certains types héritant de « SyntaxNode », par exemple « GlobalStatementSyntax » et « IncompleteMemberSyntax » retourne toujours « null ». + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + L’appel de « SemanticModel.GetDeclaredSymbol » avec un argument de type « FieldDeclarationSyntax » ou « EventFieldDeclarationSyntax » retourne toujours « null ». Appelez « GetDeclaredSymbol » avec les déclarateur de variable du champ à la place. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Un appel à « SemanticModel.GetDeclaredSymbol({0}) » retourne toujours « null » + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Un appel à « SemanticModel.GetDeclaredSymbol({0}) » retourne toujours « null » + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Cet appel à « SemanticModel.GetDeclaredSymbol() » retourne toujours « null » + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Le symbole a été marqué comme étant interdit d’utilisation dans les analyseurs, et un autre symbole doit être utilisé à la place. + + + + The symbol '{0}' is banned for use by analyzers{1} + Le symbole '{0}' est interdit pour l’utilisation par les analyseurs{1}. + + + + Do not use APIs banned for analyzers + Ne pas utiliser d’API interdites pour les analyseurs + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Les ID de diagnostic marqués comme étant supprimés dans le fichier de version d'analyseur ne doivent pas être signalés par les analyseurs. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + La règle '{0}' est marquée comme étant supprimée dans la dernière version de l'analyseur, mais elle est toujours signalée + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Les ID de diagnostic marqués comme étant supprimés dans le fichier de version d'analyseur ne doivent pas être signalés par les analyseurs + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + Le SymbolKind '{0}' n'est pas pris en charge pour les actions d'analyseur de symbole + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Argument SymbolKind non pris en charge au moment d'inscrire une action d'analyseur de symbole + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic appelé avec un DiagnosticDescriptor '{0}' non pris en charge + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic appelé avec un DiagnosticDescriptor non pris en charge + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic doit être appelé uniquement avec les DiagnosticDescriptors retournés à partir de la propriété DiagnosticAnalyzer.SupportedDiagnostics. Sinon, le diagnostic rapporté est filtré par le moteur d'analyse. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + L'argument de type '{0}' pour le paramètre de type '{1}' de la méthode '{2}' n'est pas un enum SyntaxKind + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Argument de type non valide pour la méthode Register de DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Les méthodes Register spécifiques au langage de DiagnosticAnalyzer, par exemple RegisterSyntaxNodeAction, RegisterCodeBlockStartAction et RegisterCodeBlockEndAction, attendent un argument de type 'SyntaxKind' spécifique au langage pour son paramètre de type '{0}'. Sinon, l'action d'analyseur inscrite ne peut jamais être appelée durant l'analyse. + + + + Start action has no registered non-end actions + L'action de démarrage n'a aucune action inscrite qui n'est pas une action de fin + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' n'inscrit aucune action d'analyseur. Déplacez les actions inscrites dans '{1}' et qui dépendent de cette action de démarrage vers '{0}'. + + + + Start action has no registered actions + L'action de démarrage n'a aucune action inscrite + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}' n'inscrit aucune action d'analyseur, à l'exception de '{1}'. Remplacez cette paire d'actions de début/fin par '{2}', ou déplacez les actions inscrites dans '{3}' et qui dépendent de cette action de démarrage vers '{0}'. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Une action de démarrage de l'analyseur permet d'effectuer une analyse avec état sur une unité de code donnée, par exemple un bloc de code, une compilation, etc. Vous devez effectuer un design soigné pour obtenir une exécution efficace de l'analyseur, sans fuites de mémoire. Utilisez les recommandations suivantes pour écrire des analyseurs de ce type : +1. Définissez une nouvelle portée pour l'action de démarrage inscrite, éventuellement avec un type imbriqué privé pour analyser chaque unité de code. +2. Si nécessaire, définissez et initialisez l'état dans l'action de démarrage. +3. Inscrivez au moins une action qui n'est pas une action de fin et qui fait référence à cet état dans l'action de démarrage. Si aucune action de ce type n'est nécessaire, remplacez l'action de démarrage par une action qui n'est pas une action de fin. Par exemple, un CodeBlockStartAction sans action inscrite ou uniquement un CodeBlockEndAction inscrit doit être remplacé par CodeBlockAction. +4. Si nécessaire, inscrivez une action de fin de pour créer un rapport de diagnostics en fonction de l'état final. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Vérifiez que les entrées actualisées des ID de diagnostic d'analyseur sont bien ajoutées à la version d'analyseur. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + La règle '{0}' a changé de 'Catégorie' ou de 'Gravité' depuis la dernière version. Restaurez la ou les mises à jour appropriées dans la source, ou ajoutez une nouvelle entrée à jour au fichier de version non fourni. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Vérifier que les entrées actualisées des ID de diagnostic d'analyseur sont bien ajoutées à la version d'analyseur + + + + Update rule entry in unshipped release file + Mettre à jour une entrée de règle dans le fichier de version non fourni + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Fournissez des arguments localisables de type '{0}' au constructeur de descripteur de diagnostic pour vérifier que le descripteur est localisable + + + + Provide localizable arguments to diagnostic descriptor constructor + Fournissez des arguments localisables au constructeur de descripteur de diagnostic + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Si votre analyseur de diagnostic et ses rapports de diagnostics doivent être localisables, les DiagnosticDescriptors pris en charge et utilisés pour la construction des diagnostics doivent également être localisables. Dans ce cas, vous devez fournir des arguments localisables pour le paramètre 'title' (et éventuellement 'description') au constructeur de descripteur de diagnostic pour vérifier que le descripteur est localisable. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Évitez de stocker des données par compilation de type '{0}' dans les champs d'un analyseur de diagnostic + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Évitez de stocker des données par compilation dans les champs d'un analyseur de diagnostic + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + L'instance d'un analyseur de diagnostic peut vivre au-delà de la durée de vie de la compilation. Ainsi, le stockage des données par compilation, telles que les symboles, dans les champs d'un analyseur de diagnostic peut entraîner la persistance de compilations obsolètes et provoquer des fuites de mémoire. À la place, vous devez stocker ces données dans un type distinct instancié dans une action de démarrage de compilation, inscrite à l'aide de l'API '{0}.{1}'. Une instance de ce type est créée par compilation et ne survit pas à la durée de vie de la compilation, ce qui permet ainsi d'éviter les fuites de mémoire. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + L'auteur de l'interface n'a pas l'intention d'accepter les implémentations tierces de celle-ci, et se réserve le droit de la changer. L'implémentation de cette interface peut donc entraîner un problème de compatibilité au niveau source ou binaire avec une version future de l'interface. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Le type {0} ne peut pas implémenter l'interface {1}, car {1} n'est pas disponible pour une implémentation publique + + + + Only internal implementations of this interface are allowed + Seules les implémentations internes de cette interface sont autorisées + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Un CodeFixProvider qui a l'intention de prendre en charge la correction de toutes les occurrences doit classer les actions de code inscrites en classes d'équivalence en leur affectant une clé d'équivalence explicite, non-Null et unique pour chaque genre d'action de code créée par ce correcteur. Cela permet à FixAllProvider de corriger tous les diagnostics dans la portée nécessaire en appliquant les actions de code du correcteur situées dans la classe d'équivalence de l'action de code du déclencheur. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Fournissez un argument explicite pour le paramètre optionnel '{0}', qui soit non-null et unique pour chaque genre d'action de code créée par ce correcteur + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Les actions de création de code doivent avoir un EquivalenceKey unique pour la prise en charge des occurrences de FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}' a la valeur par défaut 'null' pour la propriété '{1}'. Remplacez cette propriété sur '{0}' pour retourner une valeur non null et unique dans l'ensemble des actions de code par correcteur, ou utilisez une action de code existante de ce type. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Utilisez les actions de code ayant un EquivalenceKey unique pour la prise en charge des occurrences de FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + De nombreux objets exposés par Roslyn sont non modifiables. La valeur de retour d'un appel de méthode sur ces objets ne doit pas être ignorée. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' est non modifiable et '{1}' n'a aucun effet sur celui-ci. Utilisez la valeur de retour de '{1}'. + + + + Do not ignore values returned by methods on immutable objects + Ne pas ignorer les valeurs retournées par les méthodes sur les objets immuables + + + + Code fix providers should provide FixAll support + Les fournisseurs de correctifs de code doivent prendre en charge FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' inscrit un ou plusieurs correctifs de code, mais ne substitue pas la méthode 'CodeFixProvider.GetFixAllProvider'. Substituez cette méthode et fournissez un FixAllProvider non-null pour la prise en charge de FixAll, éventuellement 'WellKnownFixAllProviders.BatchFixer', ou 'null' pour désactiver explicitement la prise en charge de FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider doit prendre en charge FixAll pour permettre aux utilisateurs de corriger plusieurs instances du diagnostic sous-jacent avec un seul correctif de code. Pour plus d'informations, consultez la documentation sur https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. + + + + Override GetFixAllProvider. + Substituez GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Envisagez de fournir un 'helpLinkUri' non-null au constructeur du descripteur de diagnostic pour afficher des informations quand ce diagnostic apparaît dans la liste des erreurs + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Fournissez une valeur "helpLinkUri" non-null au constructeur du descripteur de diagnostic + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + La valeur 'helpLinkUri' est utilisée pour afficher des informations quand ce diagnostic figure dans la liste des erreurs. Un helpLinkUri qui pointe vers une page d'aide qui ne change pas au fil du temps doit être spécifié pour chaque analyseur. + + + + DiagnosticId for analyzers must be in specified format + Le DiagnosticId pour les analyseurs doit être au format spécifié + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + L'ID de diagnostic '{0}' appartenant à la catégorie '{1}' n'est pas dans la plage et/ou au format '{2}' exigé spécifié dans le fichier '{3}' + + + + DiagnosticId for analyzers must be in specified format. + Le DiagnosticId pour les analyseurs doit être au format spécifié. + + + + DiagnosticId must be unique across analyzers + Le DiagnosticId doit être unique sur l'ensemble des analyseurs + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + L'ID de diagnostic '{0}' est déjà utilisé par l'analyseur '{1}'. Utilisez un ID de diagnostic différent. + + + + DiagnosticId must be unique across analyzers. + Le DiagnosticId doit être unique sur l'ensemble des analyseurs. + + + + Category for analyzers must be from the specified values + La catégorie pour les analyseurs doit provenir des valeurs spécifiées + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + La catégorie '{0}' ne provient pas des catégories autorisées spécifiées dans le fichier '{1}' + + + + Category for analyzers must be from the specified values. + La catégorie pour les analyseurs doit provenir des valeurs spécifiées. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Entrée non valide dans le fichier de spécification de la catégorie de l'analyseur et de la plage d'ID de diagnostic + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Entrée '{0}' non valide dans le fichier de spécification de la catégorie de l'analyseur et de la plage d'ID de diagnostic '{1}' + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Entrée non valide dans le fichier de spécification de la catégorie de l'analyseur et de la plage d'ID de diagnostic. + + + + DiagnosticId for analyzers must be a non-null constant + Le DiagnosticId pour les analyseurs doit être une constante non-null + + + + Diagnostic Id for rule '{0}' must be a non-null constant + L'ID de diagnostic pour la règle '{0}' doit être une constante non-null + + + + DiagnosticId for analyzers must be a non-null constant. + Le DiagnosticId pour les analyseurs doit être une constante non-null. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Les types d'analyseur de diagnostic ne doivent pas utiliser les types d'assemblys Espaces de travail. Les assemblys Espaces de travail sont disponibles uniquement quand l'analyseur s'exécute dans l'analyse en direct de l'IDE Visual Studio, mais ne sont pas disponibles pendant l'exécution de la ligne de commande build. Le référencement de types d'assemblys Espaces de travail lève une exception de runtime pendant l'exécution de l'analyseur dans la ligne de commande build. + + + + Do not use types from Workspaces assembly in an analyzer + Ne pas utiliser les types d'assembly Espaces de travail dans un analyseur + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Changer le type d'analyseur de diagnostic '{0}' pour supprimer tous les accès directs et/ou indirects aux types '{1}', qui accèdent aux types '{2}' + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Changer le type d'analyseur de diagnostic '{0}' pour supprimer tous les accès directs aux types '{1}' + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace a été déplacé vers le package Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet. Il existe des changements d'API importants. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Mettez à niveau MSBuildWorkspace en ajoutant une référence de package au package NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild. Pour plus d'informations sur l'utilisation correcte de MSBuildWorkspace, consultez https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + Mettre à niveau MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf new file mode 100644 index 0000000000000..0a67220c77559 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.it.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 'DiagnosticDescriptor' assegnato al campo viene usato per segnalare una diagnostica di fine compilazione, ma il costruttore 'DiagnosticDescriptor' utilizzato per inizializzarlo non passa il tag personalizzato richiesto "CompilationEnd". Per informazioni dettagliate, vedere la documentazione di 'WellKnownDiagnosticTags.CompilationEnd'. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Aggiungere il tag personalizzato "CompilationEnd" al descrittore di diagnostica usato per inizializzare il campo '{0}' in quanto viene usato per segnalare una diagnostica di fine compilazione + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Aggiungere il tag personalizzato "CompilationEnd" al descrittore di diagnostica di fine della compilazione + + + + Add rule entry to unshipped release file + Aggiungere la voce della regola per il file di versione non distribuito + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Ereditare il tipo '{0}' da DiagnosticAnalyzer o rimuovere gli attributi DiagnosticAnalyzerAttribute + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + I tipi contrassegnati con attributi DiagnosticAnalyzerAttribute devono ereditare da DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Usa un 'SymbolEqualityComparer' per il confronto dei simboli + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + I simboli devono essere confrontati per verificarne l'uguaglianza, non l'identità. Usare un overload che accetta un elemento 'IEqualityComparer' e passare 'SymbolEqualityComparer'. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + I simboli devono essere confrontati per verificarne l'uguaglianza, non l'identità. Una chiamata esplicita a 'GetHashCode' probabilmente causerà un comportamento errato. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Usa 'SymbolEqualityComparer' quando si confrontano i simboli + + + + Symbols should be compared for equality + È necessario confrontare i simboli per verificarne l'uguaglianza. + + + + Configure generated code analysis + Configura Code Analysis per file generati + + + + Configure generated code analysis + Configura Code Analysis per file generati + + + + Configure generated code analysis + Configura Code Analysis per file generati + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Tutti gli ID diagnostica dell'analizzatore supportati devono far parte di una versione dell'analizzatore. + + + + Rule '{0}' is not part of any analyzer release + La regola '{0}' non fa parte di alcuna versione dell'analizzatore + + + + Add analyzer diagnostic IDs to analyzer release + Aggiungere gli ID diagnostica dell'analizzatore alla versione dell'analizzatore + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + La descrizione della diagnostica deve essere costituita da una o più frasi che terminano con un segno di punteggiatura e non deve includere spazi vuoti iniziali o finali + + + + Define diagnostic description correctly + Definisci correttamente la descrizione della diagnostica + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Il messaggio della diagnostica non deve contenere caratteri di ritorno a capo oppure spazi vuoti iniziali o finali e deve essere costituito da una singola frase senza punto finale oppure da più frasi con un punto finale + + + + Define diagnostic message correctly + Definisci correttamente il messaggio della diagnostica + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Il titolo della diagnostica non deve contenere un punto, né un carattere di ritorno a capo oppure spazi vuoti iniziali o finali + + + + Define diagnostic title correctly + Definisci correttamente il titolo della diagnostica + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Questa estensione del compilatore C# non deve essere implementata in un assembly contenente un riferimento a Microsoft.CodeAnalysis.VisualBasic. L'assembly Microsoft.CodeAnalysis.VisualBasic non viene sempre fornito durante gli scenari di compilazione di C#, pertanto i riferimenti ad esso potrebbero causare un comportamento imprevisto dell'estensione del compilatore. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Questa estensione del compilatore non deve essere implementata in un assembly con framework di destinazione '{0}'. I riferimenti ad altri framework di destinazione determineranno un comportamento imprevisto del compilatore. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + I tipi che implementano punti di estensione del compilatore devono essere dichiarati solo in assembly destinati a netstandard2.0. I framework di destinazione più specifici sono disponibili solo in un subset di scenari di compilazione supportati, di conseguenza la destinazione potrebbe causare un comportamento imprevisto della funzionalità. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Le estensioni del compilatore devono essere implementate negli assembly destinati a netstandard2.0 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Questa estensione del compilatore non deve essere implementata in un assembly contenente un riferimento a Microsoft.CodeAnalysis.Workspaces. L'assembly Microsoft.CodeAnalysis.Workspaces non viene fornito durante gli scenari di compilazione da riga di comando, pertanto i riferimenti ad esso potrebbero causare un comportamento imprevisto dell'estensione del compilatore. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Questa estensione del compilatore di Visual Basic non deve essere implementata in un assembly contenente un riferimento a Microsoft.CodeAnalysis.CSharp. L'assembly Microsoft.CodeAnalysis.CSharp non viene sempre fornito durante gli scenari di compilazione di Visual Basic, pertanto i riferimenti ad esso potrebbero causare un comportamento imprevisto dell'estensione del compilatore. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + I tipi che implementano i punti di estensione del compilatore non devono essere dichiarati in assembly che contengono riferimenti ad assembly non forniti da tutti gli scenari di compilazione. Ciò potrebbe causare un comportamento imprevisto della funzionalità. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Le estensioni del compilatore devono essere implementate negli assembly con riferimenti forniti dal compilatore. + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel' è un metodo dispendioso da richiamare in un analizzatore diagnostico perché crea un modello semantico completamente nuovo, che non condivide i dati di compilazione con il compilatore o con altri analizzatori, comportando un ulteriore costo in termini di prestazioni durante l'analisi semantica. Provare invece a registrare un'azione diversa dell'analizzatore che consenta l'uso di un elemento 'SemanticModel' condiviso, come 'RegisterOperationAction', 'RegisterSyntaxNodeAction' o 'RegisterSemanticModelAction'. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Non richiamare il metodo Compilation.GetSemanticModel() in un analizzatore diagnostico + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Non richiamare il metodo Compilation.GetSemanticModel() in un analizzatore diagnostico + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + L'ID diagnostica per gli analizzatori non deve usare ID riservati. + + + + '{0}' is a reserved diagnostic ID + '{0}' è un ID diagnostica riservato + + + + Do not use reserved diagnostic IDs + Non usare ID diagnostica riservati + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + L'abilitazione del rilevamento delle versioni per i pacchetti per i pacchetti dell'analizzatore consente di tenere traccia e documentare la diagnostica dell'analizzatore che viene inclusa e/o modificata con ogni versione dell'analizzatore. Per dettagli, vedere https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Abilita il rilevamento delle versioni dell'analizzatore per il progetto dell'analizzatore che contiene la regola '{0}' + + + + Enable analyzer release tracking + Abilita il rilevamento delle versioni dell'analizzatore + + + + Enable concurrent execution + Abilita l'esecuzione simultanea + + + + Enable concurrent execution + Abilitare l'esecuzione simultanea + + + + Enable concurrent execution + Abilita l'esecuzione simultanea + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Voce non valida nel file di versione dell'analizzatore. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Il file di versione dell'analizzatore '{0}' contiene la voce non valida '{1}' + + + + Invalid entry in analyzer release file + Voce non valida nel file di versione dell'analizzatore + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + Il file di versione dell'analizzatore '{0}' contiene un'intestazione di versione '{1}' mancante o non valida + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + Il file di versione dell'analizzatore '{0}' contiene una voce '{1}' non valida senza una versione distribuita in precedenza per la regola '{2}'. Aggiungere invece una voce '{1}' distinta per la regola nel file di versione non distribuito. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + Il file di versione dell'analizzatore '{0}' contiene una voce con uno o più campi 'Non rilevato' che devono essere compilati manualmente in '{1}' + + + + Missing '{0}' attribute + Manca l'attributo '{0}' + + + + Missing diagnostic analyzer attribute + Manca l'attributo dell'analizzatore diagnostico + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + I sottotipi non astratti di dell'analizzatore DiagnosticAnalyzer devono essere contrassegnati con uno o più elementi DiagnosticAnalyzerAttribute. L'eventuale argomento di questi attributi determina le lingue supportate per l'analizzatore. I tipi di analizzatore senza questo attributo verranno ignorati dal motore di analisi. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + L'analizzatore diagnostico '{0}' potrebbe riuscire a supportare sia C# che Visual Basic. Provare ad aggiungere a DiagnosticAnalyzerAttribute un argomento per includere il supporto per la lingua '{1}'. + + + + Recommend adding language support to diagnostic analyzer + Consigliata l'aggiunta del supporto per la lingua all'analizzatore diagnostico + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + L'analizzatore diagnostico è contrassegnato in modo da supportare una sola lingua, ma l'assembly dell'analizzatore non sembra fare riferimento ad alcun assembly CodeAnalysis specifico della lingua, di conseguenza è probabile che funziono per più lingue. Provare ad aggiungere a DiagnosticAnalyzerAttribute un argomento per una lingua aggiuntiva. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Applicare l'attributo DiagnosticAnalyzer per '{0}'. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Applicare l'attributo DiagnosticAnalyzer sia per '{0}' che per '{1}'. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Specificare almeno un elemento SymbolKind di interesse durante la registrazione di un'azione dell'analizzatore di simboli + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Specificare almeno un elemento SyntaxKind di interesse durante la registrazione di un'azione dell'analizzatore di nodi della sintassi + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Specificare almeno un elemento OperationKind di interesse durante la registrazione di un'azione dell'analizzatore di operazioni + + + + Missing kind argument when registering an analyzer action + Durante la registrazione di un'azione dell'analizzatore manca un argomento di tipo + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Quando si registra un'azione dell'analizzatore di sintassi, operazioni o simboli, è necessario specificare rispettivamente almeno un tipo di sintassi, operazione o simbolo. In caso contrario, l'azione registrata non verrà mai richiamata durante l'analisi. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Un progetto contenente analizzatori o generatori di origine deve specificare la proprietà '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': un progetto contenente analizzatori o generatori di origine deve specificare la proprietà '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + Specify analyzer banned API enforcement setting + Specificare l'impostazione di imposizione dell'API esclusa dell'analizzatore + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Preferire 'syntax.IsKind(kind)' a 'syntax.Kind() == kind' durante la verifica dei tipi di sintassi. Il codice che usa 'IsKind' è leggermente più efficiente in fase di runtime, di conseguenza l'uso coerente di questo formato dove possibile contribuisce a migliorare le prestazioni in scenari di analisi complessi. + + + + Use 'IsKind' instead of 'Kind' + Usare 'IsKind' invece di 'Kind' + + + + Prefer 'IsKind' for checking syntax kinds + Preferire 'IsKind' per la verifica dei tipi di sintassi + + + + Prefer 'IsKind' for checking syntax kinds + Preferire 'IsKind' per la verifica dei tipi di sintassi + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + Il valore 'customTags' viene usato per abilitare azioni e filtri specifici per i descrittori di diagnostica in base a valori specifici dei tag. Ogni analizzatore Roslyn deve includere almeno un tag della classe 'WellKnownDiagnosticTags'. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Provare a fornire un valore 'customTags' non Null al costruttore del descrittore di diagnostica per abilitare il filtro dei metadati dei descrittori di diagnostica + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Fornire un valore 'customTags' non Null al costruttore del descrittore di diagnostica + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Rimuovere le voci duplicate per l'ID diagnostica tra le versioni dell'analizzatore. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + La regola '{0}' contiene una voce duplicata tra la versione '{1}' e la versione '{2}' + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Rimuovere le voci duplicate per l'ID diagnostica tra le versioni dell'analizzatore + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Rimuovere le voci duplicate per gli ID diagnostica nella stessa versione dell'analizzatore. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + La regola '{0}' contiene più di una voce per la versione '{1}' nel file di versione dell'analizzatore '{2}' + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Rimuovere le voci duplicate per gli ID diagnostica nella stessa versione dell'analizzatore + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Per gli ID diagnostica distribuiti che non sono più segnalati deve essere inclusa una voce nella tabella 'Regole rimosse' nel file non distribuito. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + La regola '{0}' è stata distribuita nella versione dell'analizzatore '{1}', ma non è più una diagnostica supportata per qualsiasi analizzatore. Aggiungere una voce per questa regola in una tabella 'Regole rimosse' in un file non distribuito. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Per gli ID diagnostica distribuiti che non sono più segnalati deve essere inclusa una voce nella tabella 'Regole rimosse' nel file non distribuito + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Le voci per gli ID diagnostica dell'analizzatore che non sono più segnalate e non vengono più distribuite possono essere rimosse dalla versione dell'analizzatore non distribuita. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + La regola '{0}' fa parte della successiva versione dell'analizzatore non distribuita, ma non è una diagnostica supportata per qualsiasi analizzatore + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Non aggiungere gli ID diagnostica dell'analizzatore rimossi alla versione dell'analizzatore non distribuita + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + La chiamata a 'SemanticModel.GetDeclaredSymbol' con determinati tipi che ereditano da 'SyntaxNode', ad esempio 'GlobalStatementSyntax' e 'IncompleteMemberSyntax', restituirà sempre 'null'. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + La chiamata a 'SemanticModel.GetDeclaredSymbol' con un argomento di tipo 'FieldDeclarationSyntax' o 'EventFieldDeclarationSyntax' restituirà sempre 'null'. Chiamare 'GetDeclaredSymbol' con i dichiaratori di variabili dal campo. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Una chiamata a 'SemanticModel.GetDeclaredSymbol({0})' restituirà sempre 'null' + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Una chiamata a 'SemanticModel.GetDeclaredSymbol({0})' restituirà sempre 'null' + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Questa chiamata a 'SemanticModel.GetDeclaredSymbol()' restituirà sempre 'null' + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Il simbolo è stato contrassegnato come escluso dall'uso negli analizzatori e al suo posto deve esserne usato uno alternativo. + + + + The symbol '{0}' is banned for use by analyzers{1} + Il simbolo '{0}' è stato escluso per l'uso da parte degli analizzatori{1} + + + + Do not use APIs banned for analyzers + Non usare API escluse per gli analizzatori + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Gli ID diagnostica contrassegnati come rimossi nel file di versione dell'analizzatore non devono essere segnalati dagli analizzatori. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + La regola '{0}' è contrassegnata come rimossa nell'ultima versione dell'analizzatore, ma viene ancora segnalata + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Gli ID diagnostica contrassegnati come rimossi nel file di versione dell'analizzatore non devono essere segnalati dagli analizzatori + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + L'elemento SymbolKind '{0}' non è supportato per le azioni dell'analizzatore di simboli + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + L'argomento SymbolKind non è supportato durante la registrazione di un'azione dell'analizzatore di simboli + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic è stato richiamato con un elemento DiagnosticDescriptor '{0}' non supportato + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic è stato richiamato con un elemento DiagnosticDescriptor non supportato + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic deve essere richiamato solo con elementi DiagnosticDescriptor supportati restituiti dalla proprietà DiagnosticAnalyzer.SupportedDiagnostics. In caso contrario, la diagnostica restituita verrà filtrata dal motore di analisi. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + L'argomento tipo '{0}' per il parametro di tipo '{1}' del metodo '{2}' non è un'enumerazione SyntaxKind + + + + Invalid type argument for DiagnosticAnalyzer's Register method + L'argomento tipo per il metodo Register di DiagnosticAnalyzer non è valido + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Con i metodi Register specifici della lingua di DiagnosticAnalyzer, come RegisterSyntaxNodeAction, RegisterCodeBlockStartAction e RegisterCodeBlockEndAction, è previsto un argomento tipo 'SyntaxKind' specifico della lingua per il parametro di tipo '{0}'. In caso contrario, l'azione registrata dell'analizzatore non può essere mai richiamata durante l'analisi. + + + + Start action has no registered non-end actions + Per l'azione di avvio non esistono azioni non di fine registrate + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' non registra alcuna azione dell'analizzatore. Provare a spostare in '{0}' le azioni registrate in '{1}' che dipendono da questa azione di avvio. + + + + Start action has no registered actions + Per l'azione di avvio non esistono azioni registrate + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}' non registra alcuna azione dell'analizzatore, ad eccezione di '{1}'. Provare a sostituire questa coppia di azioni di avvio/fine con un elemento '{2}' o a spostare in '{0}' le azioni registrate in '{3}' che dipendono da questa azione di avvio. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Un'azione di avvio dell'analizzatore consente di eseguire un'analisi con stato su una data unità di codice, ad esempio un blocco di codice, una compilazione e così via. Per rendere più efficiente l'esecuzione degli analizzatori ed evitare perdite di memoria, è necessaria un'attenta progettazione. Per scrivere tali analizzatori, attenersi alle linea guida seguenti: +1. Definire un nuovo ambito per l'azione di avvio registrata, possibilmente con un tipo nidificato privato per l'analisi delle singole unità di codice. +2. Se necessario, definire e inizializzare lo stato nell'azione di avvio. +3. Registrare almeno un'azione non di fine che fa riferimento a questo stato nell'azione di avvio. Se una tale azione non è necessaria, provare a sostituire l'azione di avvio con un'azione non di avvio. Ad esempio un elemento CodeBlockStartAction senza azioni registrate o un solo elemento CodeBlockEndAction registrato deve essere sostituito da un elemento CodeBlockAction. +4. Se necessario, registrare un'azione di fine per restituire i dati di diagnostica in base allo stato finale. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Assicurarsi che la voce aggiornata per gli ID diagnostica dell'analizzatore vengano aggiunti alla versione dell'analizzatore. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + L'attributo 'Categoria' o 'Gravità' della regola '{0}' è stato modificato rispetto all'ultima versione. Ripristinare gli aggiornamenti nell'origine oppure aggiungere una nuova voce aggiornata al file di versione non distribuito. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Assicurarsi che la voce aggiornata per gli ID diagnostica dell'analizzatore vengano aggiunti alla versione dell'analizzatore + + + + Update rule entry in unshipped release file + Aggiornare la voce della regola nel file di versione non distribuito + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Provare a fornire argomenti localizzabili di tipo '{0}' al costruttore del descrittore di diagnostica per garantire che sia localizzabile + + + + Provide localizable arguments to diagnostic descriptor constructor + Fornire argomenti localizzabili al costruttore del descrittore di diagnostica + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Se l'analizzatore diagnostico e i relativi dati di diagnostica restituiti devono essere localizzabili, devono essere localizzabili anche gli elementi DiagnosticDescriptor supportati usati per costruire i dati di diagnostica. In tal caso, è necessario fornire argomenti localizzabili per il parametro 'title' (e facoltativamente per 'description') al costruttore del descrittore di diagnostica per garantire che il descrittore sia localizzabile. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Evitare di archiviare dati per compilazione di tipo '{0}' nei campi di un analizzatore diagnostico + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Evitare di archiviare dati per compilazione nei campi di un analizzatore diagnostico + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + L'istanza di un analizzatore diagnostico potrebbe sopravvivere alla durata della compilazione. Di conseguenza, se si archiviano i dati per compilazione, ad esempio i simboli, nei campi di un analizzatore diagnostico, compilazioni obsolete potrebbero continuare a esistere e causare perdite di memoria. È invece consigliabile archiviare questi dati in un tipo separato di cui viene creata un'istanza in un'azione di avvio della compilazione, registrata con l'API '{0}.{1}'. Un'istanza di questo tipo verrà creata per compilazione e non sopravviverà alla durata della compilazione, evitando in tal modo perdite di memoria. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + L'autore di questa interfaccia non intendeva includere implementazioni dell'interfaccia di terze parti e si riserva il diritto di modificarla. L'implementazione di questa interfaccia potrebbe quindi generare un problema di compatibilità del codice sorgente o dei binari con una versione futura di questa interfaccia. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Il tipo {0} non può implementare l'interfaccia {1} perché {1} non è disponibile per l'implementazione pubblica + + + + Only internal implementations of this interface are allowed + Sono consentite solo implementazioni interne di questa interfaccia + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Un elemento CodeFixProvider che intende supportare la correzione di tutte le occorrenze deve classificare le azioni codice registrate nelle classi di equivalenza assegnando una chiave di equivalenza non Null che sia univoca per ogni tipo di azione codice creata da questa utilità di correzione. In questo modo FixAllProvider potrà correggere tutte le diagnostiche nell'ambito richiesto applicando codici azioni di questa utilità di correzione che sono inclusi nella classe di equivalenza dell'azione codice di attivazione. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Fornire un argomento esplicito per il parametro facoltativo '{0}', che sia non Null e univoco per ogni tipo di azione codice creata da questa correzione + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Le azioni di creazione codice devono includere un elemento EquivalenceKey univoco per il supporto delle occorrenze di FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + Il valore predefinito di '{0}' è 'null' per la proprietà '{1}'. Eseguire l'override di questa proprietà su '{0}' in modo da restituire un valore non Null e univoco in tutte le azioni codice per utilità di correzione oppure usare tale azione codice esistente. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Usare azioni codice con un elemento EquivalenceKey univoco per il supporto delle occorrenze di FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Molti oggetti esposti da Roslyn non sono modificabili. Il valore restituito da una chiamata di metodo su questi oggetti non deve essere ignorato. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' non è modificabile e '{1}' non avrà alcun effetto su di esso. Provare a usare il valore restituito da '{1}'. + + + + Do not ignore values returned by methods on immutable objects + Non ignorare i valori restituiti dai metodi su oggetti non modificabili + + + + Code fix providers should provide FixAll support + I provider di correzione del codice devono fornire il supporto per FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' registra una o più correzioni del codice, ma non esegue l'override del metodo 'CodeFixProvider.GetFixAllProvider'. Eseguire l'override di questo metodo e fornire un elemento FixAllProvider non Null per il supporto per FixAll, possibilmente 'WellKnownFixAllProviders.BatchFixer', oppure 'null' per disabilitare in modo esplicito il supporto per FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Un elemento CodeFixProvider deve fornire il supporto per FixAll per consentire agli utenti di correggere più istanze della diagnostica sottostante con un'unica correzione del codice. Per maggiori dettagli, vedere la documentazione all'indirizzo https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md. + + + + Override GetFixAllProvider. + Eseguire l'override di GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Provare a fornire un valore 'helpLinkUri' non Null al costruttore del descrittore di diagnostica in modo da visualizzare le informazioni quando questa diagnostica è presente nell'elenco errori + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Fornire un valore 'helpLinkUri' non Null al costruttore del descrittore di diagnostica + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + Il valore 'helpLinkUri' viene usato per visualizzare le informazioni quando questa diagnostica è presente nell'elenco errori. È necessario specificare per ogni analizzatore un elemento helpLinkUri che punta a una pagina che non cambia nel tempo. + + + + DiagnosticId for analyzers must be in specified format + Gli ID diagnostica per gli analizzatori devono essere nel formato specificato + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + L'ID diagnostica '{0}' appartenente alla categoria '{1}' non è incluso nell'intervallo e/o nel formato '{2}' richiesto specificato nel file '{3}' + + + + DiagnosticId for analyzers must be in specified format. + Gli ID diagnostica per gli analizzatori devono essere nel formato specificato. + + + + DiagnosticId must be unique across analyzers + L'ID diagnostica deve essere univoco tra gli analizzatori + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + L'ID diagnostica '{0}' è già usato dall'analizzatore '{1}'. Usarne uno diverso. + + + + DiagnosticId must be unique across analyzers. + L'ID diagnostica deve essere univoco tra gli analizzatori. + + + + Category for analyzers must be from the specified values + La categoria per gli analizzatori deve essere inclusa nei valori specificati + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + La categoria '{0}' non è inclusa nelle categorie consentite specificate nel file '{1}' + + + + Category for analyzers must be from the specified values. + La categoria per gli analizzatori deve essere inclusa nei valori specificati. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + La voce nella categoria dell'analizzatore e nel file di specifica dell'intervallo degli ID diagnostica non è valida + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + La voce '{0}' nella categoria dell'analizzatore e nel file '{1}' di specifica dell'intervallo degli ID diagnostica non è valida + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + La voce nella categoria dell'analizzatore e nel file di specifica dell'intervallo degli ID diagnostica non è valida. + + + + DiagnosticId for analyzers must be a non-null constant + Gli ID diagnostica per gli analizzatori devono essere una costante non Null + + + + Diagnostic Id for rule '{0}' must be a non-null constant + L'ID diagnostica per la regola '{0}' deve essere una costante non Null + + + + DiagnosticId for analyzers must be a non-null constant. + Gli ID diagnostica per gli analizzatori devono essere una costante non Null. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + I tipi di analizzatore diagnostico non devono usare tipi degli assembly di aree di lavoro. Gli assembly di aree di lavoro sono disponibili solo quando l'analizzatore viene eseguito nell'analisi in tempo reale dell'IDE di Visual Studio, ma non sono disponibili durante la compilazione dalla riga di comando. Se si fa riferimento a tipi di assembly di aree di lavoro, si verificherà un'eccezione di runtime durante l'esecuzione dell'analizzatore nella compilazione dalla riga di comando. + + + + Do not use types from Workspaces assembly in an analyzer + Non usare tipi di assembly di aree di lavoro in un analizzatore + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Modificare il tipo di analizzatore diagnostico '{0}' in modo da rimuovere tutti gli accessi diretti e/o indiretti al tipo o ai tipi '{1}', che accedono al tipo o ai tipi '{2}' + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Modificare il tipo di analizzatore diagnostico '{0}' in modo da rimuovere tutti gli accessi diretti al tipo o ai tipi '{1}' + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace è stato spostato nel pacchetto NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild e sono presenti importanti novità relative alle API. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Aggiornare MSBuildWorkspace aggiungendo un riferimento al pacchetto NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild. Per dettagli sull'uso corretto di MSBuildWorkspace, vedere https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + Aggiorna MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf new file mode 100644 index 0000000000000..01e083eded5aa --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ja.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + フィールドに割り当てられた 'DiagnosticDescriptor' は、コンパイル終了の診断を報告するために使用されますが、それを初期化するために使用される 'DiagnosticDescriptor' コンストラクターは、必要なカスタム タグ「CompilationEnd」を渡しません。詳細については、「WellKnownDiagnosticTags.CompilationEnd」のドキュメントを参照してください。 + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + フィールド '{0}' の初期化に使用される診断記述子に「CompilationEnd」カスタム タグを追加します。これは、コンパイル終了の診断を報告するために使用されるためです + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + コンパイル終了診断記述子に「CompilationEnd」カスタム タグを追加する + + + + Add rule entry to unshipped release file + 未出荷のリリース ファイルへのルール エントリの追加 + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + DiagnosticAnalyzer から型 '{0}' を継承するか、DiagnosticAnalyzerAttribute を削除してください + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + DiagnosticAnalyzerAttribute でマークされている型は DiagnosticAnalyzer から継承することが必要 + + + + Use a 'SymbolEqualityComparer' for symbol comparison + シンボル比較に 'SymbolEqualityComparer' を使用する + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + シンボルについては、ID ではなく等値かどうかを比較する必要があります。'IEqualityComparer' を受け取るオーバーロードを使用し、'SymbolEqualityComparer' を渡します。 + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + シンボルについては、ID ではなく等値かどうかを比較する必要があります。'GetHashCode' を明示的に呼び出すと、正しくない動作が発生する可能性があります。 + + + + Use 'SymbolEqualityComparer' when comparing symbols + シンボルを比較するときに 'SymbolEqualityComparer' を使用する + + + + Symbols should be compared for equality + シンボルは等しいかどうかを比較する必要があります + + + + Configure generated code analysis + 生成されたコード分析を構成します + + + + Configure generated code analysis + 生成されたコード分析を構成します + + + + Configure generated code analysis + 生成されたコード分析を構成します + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + サポートされているすべてのアナライザー診断 ID は、アナライザー リリースに含まれている必要があります。 + + + + Rule '{0}' is not part of any analyzer release + ルール '{0}' は、どのアナライザー リリースにも含まれていません + + + + Add analyzer diagnostic IDs to analyzer release + アナライザー診断 ID のアナライザー リリースへの追加 + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + 診断の説明は、句読点で終わる 1 つまたは複数の文にする必要があり、先頭または末尾に空白文字を含めることはできません。 + + + + Define diagnostic description correctly + 診断の説明を正しく定義する + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + 診断メッセージには、改行文字を使用することも、先頭または末尾に空白文字を含めることもできません。末尾にピリオドが付いていない 1 つの文または末尾にピリオドが付いた複数の文にする必要があります。 + + + + Define diagnostic message correctly + 診断メッセージを正しく定義する + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + 診断タイトルには、ピリオドや改行記号を使用することも、先頭または末尾に空白文字を含めることもできません + + + + Define diagnostic title correctly + 診断タイトルを正しく定義する + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + この C# コンパイラ拡張機能は、Microsoft.CodeAnalysis.VisualBasic への参照を含むアセンブリに実装しないでください。Microsoft.CodeAnalysis.VisualBasic アセンブリは、C# のコンパイル シナリオでは常に提供されるわけではないため、このアセンブリを参照すると、コンパイラ拡張機能が予測しない動作をする可能性があります。 + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + このコンパイラ拡張機能は、ターゲット フレームワーク '{0}' のアセンブリに実装しないでください。他のターゲット フレームワークを参照すると、コンパイラが予期しない動作をします。 + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + コンパイラ拡張ポイントを実装する型は、netstandard2.0 を対象とするアセンブリでのみ宣言する必要があります。より具体的なターゲット フレームワークは、サポートされているコンパイル シナリオのサブセットでのみ使用できるため、対象を設定すると、機能が予期しない動作をする可能性があります。 + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + コンパイラ拡張機能は、netstandard2.0 を対象とするアセンブリに実装する必要があります + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + このコンパイラ拡張機能は、Microsoft.CodeAnalysis.Workspaces への参照を含むアセンブリに実装しないでください。Microsoft.CodeAnalysis.Workspaces アセンブリは、コマンド ライン コンパイル シナリオでは提供されないため、このアセンブリを参照すると、コンパイラ拡張機能が予期しない動作をする可能性があります。 + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + この Visual Basicコンパイラ拡張機能は、Microsoft.CodeAnalysis.CSharp への参照を含むアセンブリに実装しないでください。Microsoft.CodeAnalysis.CSharp アセンブリは、Visual Basic のコンパイル シナリオ中に必ずしも提供されるわけではないため、このアセンブリを参照すると、コンパイラ拡張機能が予期しない動作をする可能性があります。 + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + コンパイラ拡張ポイントを実装する型は、すべてのコンパイル シナリオで提供されないアセンブリへの参照を含むアセンブリでは宣言できません。この操作を行うと、機能が予期しない動作をする可能性があります。 + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + コンパイラ拡張機能は、コンパイラ提供の参照を含むアセンブリに実装する必要があります + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel' は、コンパイラやその他のアナライザーとコンパイル データを共有しないまったく新しいセマンティック モデルを作成するため、診断アナライザー内で呼び出すのにコストの高い方法です。セマンティック分析中に追加のパフォーマンス コストが発生します。代わりに、'RegisterOperationAction'、'RegisterSyntaxNodeAction'、'RegisterSemanticModelAction' など、共有されている 'SemanticModel' を使用できる別のアナライザー操作を登録することをご検討ください。 + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 診断アナライザー内で Compilation.GetSemanticModel() メソッドを呼び出さないでください + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 診断アナライザー内での Compilation.GetSemanticModel() メソッドの呼び出し禁止 + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + アナライザーの診断 ID には、予約済み ID を使用しないでください。 + + + + '{0}' is a reserved diagnostic ID + '{0}' は予約済みの診断 ID です + + + + Do not use reserved diagnostic IDs + 予約済みの診断 ID は使用不可 + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + アナライザー パッケージのリリース追跡を有効にすると、各アナライザー リリースで配布または変更されるアナライザー診断の追跡とドキュメント化に役立ちます。詳細については、https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md をご覧ください。 + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + 規則 '{0}' を含むアナライザー プロジェクトのアナライザー リリース追跡を有効にしてください + + + + Enable analyzer release tracking + アナライザー リリース追跡を有効にする + + + + Enable concurrent execution + 同時実行を有効にします + + + + Enable concurrent execution + 同時実行を有効にします + + + + Enable concurrent execution + 同時実行を有効にします + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + アナライザー リリース ファイルに無効なエントリがあります。 + + + + Analyzer release file '{0}' has an invalid entry '{1}' + アナライザー リリース ファイル '{0}' に無効なエントリ '{1}' があります + + + + Invalid entry in analyzer release file + アナライザー リリース ファイルに無効なエントリがある + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + アナライザー リリース ファイル '{0}' のリリース ヘッダー '{1}' が存在しないか、無効です + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + アナライザー リリース ファイル '{0}' に、前の出荷済みリリースが存在しない、ルール '{2}' の無効な '{1}' エントリがあります。代わりに、未出荷のリリース ファイルにそのルールの別個の '{1}' エントリを追加してください。 + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + アナライザー リリース ファイル '{0}' には、'{1}' に手動で入力する必要がある 1 つ以上の '未検出' フィールドを持つエントリがあります + + + + Missing '{0}' attribute + '{0}' 属性がありません + + + + Missing diagnostic analyzer attribute + 診断アナライザー属性が不足 + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + DiagnosticAnalyzer の非抽象サブ型は、DiagnosticAnalyzerAttribute でマークする必要があります。この属性の引数が存在する場合は、アナライザーでサポートされる言語が決まります。この属性を持たないアナライザーの種類は、分析エンジンに無視されます。 + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + 診断アナライザー '{0}' は、C# と Visual Basic の両方をサポートできる場合があります。'{1}' 言語をサポートするために、DiagnosticAnalyzerAttribute に引数を追加することを検討してください。 + + + + Recommend adding language support to diagnostic analyzer + 診断アナライザーへの言語サポートの追加の推奨 + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + 診断アナライザーは、1 つの言語のみをサポートするように設定されていますが、アナライザー アセンブリは言語固有の CodeAnalysis アセンブリを参照しないようです。そのため、複数の言語で動作する可能性があります。DiagnosticAnalyzerAttribute に言語引数をさらに追加することを検討してください。 + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + '{0}' に DiagnosticAnalyzer 属性を適用します。 + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + '{0}' と '{1}' の両方に DiagnosticAnalyzer 属性を適用します。 + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + シンボル アナライザー アクションを登録する際に、関心のある SymbolKind を 1 つ以上指定してください + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + 構文ノード アナライザー アクションを登録する際に、関心のある SyntaxKind を 1 つ以上指定してください + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + 操作アナライザー アクションを登録する際に、関心のある OperationKind を 1 つ以上指定してください + + + + Missing kind argument when registering an analyzer action + アナライザー アクションを登録する際の kind 引数が不足 + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + 構文、シンボル、または操作のアナライザー アクションをそれぞれ登録するときに、少なくとも 1 つの構文、シンボル、または操作の種類を指定する必要があります。そうしないと、登録済みのアクションは分析中に呼び出されません。 + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + アナライザーまたはソース ジェネレーターを含むプロジェクトでは、プロパティ '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' を指定する必要があります。 + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': アナライザーまたはソース ジェネレーターを含むプロジェクトでは、プロパティ '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' を指定する必要があります + + + + Specify analyzer banned API enforcement setting + アナライザーの禁止された API 強制設定の指定 + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + 構文の種類をチェックするときに 'syntax.Kind() == kind' よりも 'syntax.IsKind(kind)' を優先します。'IsKind' を使用するコードは実行時に多少効率がよくなるため、この形式 (存在する場合) を常に使用すると、複雑な分析シナリオのパフォーマンスが向上します。 + + + + Use 'IsKind' instead of 'Kind' + 'Kind' ではなく 'IsKind' を使用する + + + + Prefer 'IsKind' for checking syntax kinds + 構文の種類のチェックで 'IsKind' を優先します + + + + Prefer 'IsKind' for checking syntax kinds + 構文の種類のチェックで 'IsKind' を優先する + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + 'customTags' 値は、タグの特定の値に基づいて診断記述子に対して特定のアクションとフィルターを有効にする方法として使用されます。すべての Roslyn アナライザーには、'WellKnownDiagnosticTags' クラスのタグが少なくとも 1 つ必要です。 + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + 診断記述子のメタデータ フィルターを有効にするため、null 以外の 'customTags' を診断記述子コンストラクターに提供することを検討してください + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + null 以外の 'customTags' 値を診断記述子コンストラクターに提供する + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + アナライザー リリース間で診断 ID の重複しているエントリを削除してください。 + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + ルール '{0}' に、リリース '{1}' とリリース '{2}' の間で重複しているエントリがあります + + + + Remove duplicate entries for diagnostic ID between analyzer releases + アナライザー リリース間で診断 ID の重複しているエントリを削除する + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + 同じアナライザー リリースで診断 ID の重複しているエントリを削除します。 + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + アナライザー リリース ファイル '{2}' で、ルール '{0}' にリリース '{1}' のエントリが複数あります + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + 同じアナライザー リリースで診断 ID の重複しているエントリを削除する + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + 報告されなくなった出荷済みの診断 ID には、未出荷ファイルの '削除されたルール' テーブルにエントリが必要です。 + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + ルール '{0}' はアナライザー リリース '{1}' で出荷されましたが、アナライザーに対してサポートされる診断ではなくなりました。未出荷のファイルに対して、このルールのエントリを '削除されたルール' テーブルに追加してください。 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + 報告されなくなった出荷済みの診断 ID には、未出荷ファイルの '削除されたルール' テーブルにエントリが必要 + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + 報告されなくなり、出荷されたことのないアナライザー診断 ID のエントリは、未出荷のアナライザー リリースから削除できます。 + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + ルール '{0}' は、次回の未出荷のアナライザー リリースに含まれていますが、いずれかのアナライザーでサポートされている診断ではありません + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + 削除したアナライザー診断 ID を未出荷のアナライザー リリースに追加しない + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + 'SyntaxNode' を継承する特定の型を使用して 'SemanticModel.GetDeclaredSymbol' を呼び出しています。たとえば、'GlobalStatementSyntax' や 'IncompleteMemberSyntax' は常に 'null' を返します。 + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + 型 'FieldDeclarationSyntax' または 'EventFieldDeclarationSyntax' の引数を使用して 'SemanticModel.GetDeclaredSymbol' を呼び出すと、常に 'null' を返します。代わりに、フィールドから変数宣言を指定して 'GetDeclaredSymbol' を呼び出します。 + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})' への呼び出しでは、常に 'null' を返します + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})' への呼び出しでは、常に 'null' を返します + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + 'SemanticModel.GetDeclaredSymbol()' に対するこの呼び出しでは、常に 'null' を返します + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + シンボルはこのアナライザーで禁止とマークされているため、代替を使用する必要があります。 + + + + The symbol '{0}' is banned for use by analyzers{1} + シンボル '{0}' はアナライザーによって使用禁止です{1} + + + + Do not use APIs banned for analyzers + アナライザーに対して禁止された API を使用しない + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + アナライザー リリース ファイルで削除済みとマークされている診断 ID は、アナライザーによって報告されないはずです。 + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + ルール '{0}' は、最新のアナライザー リリースで削除済みとしてマークされていますが、まだ報告されています + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + アナライザー リリース ファイルで削除済みとマークされている診断 ID は、アナライザーによって報告されないはずである + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + SymbolKind '{0}' は、シンボル アナライザー アクションではサポートされていません + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + シンボル アナライザー アクションの登録時にサポートされていない SymbolKind 引数 + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic が、サポートされていない DiagnosticDescriptor '{0}' で呼び出されました + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + サポートされていない DiagnosticDescriptor で呼び出された ReportDiagnostic + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic の呼び出しには、DiagnosticAnalyzer.SupportedDiagnostics プロパティから返される、サポートされている DiagnosticDescriptor のみを使用する必要があります。そうしないと、報告された診断は分析エンジンによってフィルターで除外されます。 + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + メソッド '{2}' の型パラメーター '{1}' の型引数 '{0}' が、SyntaxKind 列挙型ではありません + + + + Invalid type argument for DiagnosticAnalyzer's Register method + DiagnosticAnalyzer の Register メソッドの型引数が無効 + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + DiagnosticAnalyzer の言語固有の Register メソッド (RegisterSyntaxNodeAction、RegisterCodeBlockStartAction、RegisterCodeBlockEndAction など) には、'{0}' 型パラメーターとして言語固有の 'SyntaxKind' 型引数が必要です。そうでない場合、登録されたアナライザー アクションを分析中に呼び出すことはできません。 + + + + Start action has no registered non-end actions + 開始アクションには、終了以外の登録済みアクションがない + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' はアナライザー アクションを登録していません。この開始アクションに依存する、'{1}' に登録されたアクションを '{0}' に移動することを検討してください。 + + + + Start action has no registered actions + 開始アクションに登録済みアクションがない + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}' は、'{1}' 以外のアナライザー アクションを登録していません。この開始/終了アクションのペアを '{2}' に置き換えるか、この開始アクションに依存する、'{3}' に登録されたアクションを '{0}' に移動することを検討してください。 + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + アナライザーの開始アクションにより、特定のコード単位 (コード ブロック、コンパイルなど) に対するステートフル分析の実行が可能になります。メモリ リークを発生させずに効率的にアナライザーを実行できるようにするには、慎重に設計する必要があります。そのようなアナライザーを記述する場合は、以下のガイドラインに従ってください。 +1. 登録した開始アクション用に新しいスコープを定義します。できれば、各コード単位を分析するために、プライベートな入れ子にされた型を使用します。 +2. 必要に応じて、開始アクションで状態を定義し、初期化します。 +3. 開始アクションのこの状態を参照する終了以外のアクションを 1 つ以上登録します。このようなアクションが不要な場合は、開始アクションを開始以外のアクションに置き換えることを検討します。たとえば、アクションが登録されていない CodeBlockStartAction や登録された単独の CodeBlockEndAction は、CodeBlockAction に置き換える必要があります。 +4. 必要に応じて、最終状態に基づく診断を報告するために、終了アクションを登録します。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + アナライザー診断 ID の最新のエントリがアナライザー リリースに追加されていることを確認してください。 + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + ルール '{0}' の 'カテゴリ' または '重要度' が前回のリリースから変更されています。ソースの更新を元に戻すか、未出荷のリリース ファイルに新しい最新のエントリを追加してください。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + アナライザー診断 ID の最新のエントリがアナライザー リリースに追加されていることを確認する + + + + Update rule entry in unshipped release file + 未出荷リリース ファイルのルール エントリの更新 + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + 診断記述子コンストラクターに型 '{0}' のローカライズ可能な引数を指定して記述子をローカライズ可能にすることを検討してください + + + + Provide localizable arguments to diagnostic descriptor constructor + 診断記述子コンストラクターにローカライズ可能な引数を指定する + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + 診断アナライザーとその報告される診断をローカライズ可能にする必要がある場合は、診断の構築に使用される、サポートされている DiagnosticDescriptors もローカライズ可能にする必要があります。その場合は、記述子をローカライズ可能にするために、診断記述子コンストラクターのパラメーター 'title' (およびオプションで 'description') にローカライズ可能な引数を指定する必要があります。 + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + コンパイルごとの型 '{0}' のデータを診断アナライザーのフィールドに格納しないでください + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + コンパイルごとのデータは診断アナライザーのフィールドに格納不可 + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + 診断アナライザーのインスタンスは、コンパイルの有効期間を超えて残る場合があります。そのため、コンパイルごとのデータ (シンボルなど) を診断アナライザーのフィールドに格納すると、古いコンパイルが残り、メモリ リークが生じることがあります。代わりに、'{0}.{1}' API を使用して登録されたコンパイル開始アクションでインスタンス化された個別の型に、このデータを格納する必要があります。この型のインスタンスはコンパイルごとに作成され、コンパイルの有効期間を超えて残ることがないため、メモリ リークを避けることができます。 + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + このインターフェイスの作成者は、このインターフェイスがサード パーティによって実装されることを意図しておらず、インターフェイスを変更する権利を留保しています。そのため、このインターフェイスを実装すると、このインターフェイスの将来のバージョンでソースまたはバイナリの互換性の問題が発生する可能性があります。 + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + {1} は公開実装に使用できないため、型 {0} はインターフェイス {1} を実装できません + + + + Only internal implementations of this interface are allowed + このインターフェイスで許可されているのは内部実装のみ + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + すべての出現箇所の修正をサポートすることを目的とした CodeFixProvider は、登録されたコード アクションを同等クラスに分類する必要があります。そのために、この修正ツールによって作成された各種類のコード アクション内で一意となる null 以外の明示的な同等キーを割り当てます。これにより、FixAllProvider は、トリガー コード アクションの同等クラス内にある、この修正ツールのコード アクションを適用して、必要なスコープ内のすべての診断を修正することができます。 + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + 省略可能なパラメーター '{0}' に明示的な引数を指定してください。この引数は、null ではなく、この修正ツールによって作成された各種類のコード アクション内で一意です + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + コードの作成アクションには、FixAll の出現箇所をサポートするために、一意の EquivalenceKey が必要 + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}' のプロパティ '{1}' の既定値は 'null' です。'{0}' でこのプロパティをオーバーライドして、修正ツールごとのすべてのコード アクションで null 以外の一意の値を返すか、そのような既存のコード アクションを使用します。 + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + FixAll の出現箇所をサポートするために、一意の EquivalenceKey を持つコード アクションを使用する + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Roslyn によって公開される多くのオブジェクトは、変更できません。これらのオブジェクトに対するメソッド呼び出しからの戻り値を無視しないでください。 + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' は変更不可能なため、'{1}' による影響はありません。'{1}' からの戻り値を使用することを検討してください。 + + + + Do not ignore values returned by methods on immutable objects + 不変オブジェクトのメソッドによって返される値を無視しないでください + + + + Code fix providers should provide FixAll support + コード修正プログラム プロバイダーは、FixAll サポートを提供することが必要 + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' は 1 つ以上のコード修正プログラムを登録しますが、メソッド 'CodeFixProvider.GetFixAllProvider' をオーバーライドしません。このメソッドをオーバーライドして、NULL 以外の FixAllProvider ('WellKnownFixAllProviders.BatchFixer' など) を FixAll サポートのために提供するか、'null' を指定して FixAll サポートを明示的に無効にしてください。 + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider はユーザーが基本となる診断の複数のインスタンスを 1 つのコード修正プログラムで修正できるようにするために、FixAll サポートを提供する必要があります。詳細については、https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md をご覧ください。 + + + + Override GetFixAllProvider. + GetFixAllProvider をオーバーライドします。 + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + この診断がエラー一覧に表示されたときに情報を表示するために、null 以外の 'helpLinkUri' を診断記述子コンストラクターに提供することを検討してください + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + null 以外の 'helpLinkUri' 値を診断記述子コンストラクターに提供する + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + 'helpLinkUri' 値は、この診断がエラー一覧に表示されたときに情報を表示するために使用されます。すべてのアナライザーには、経時的に変化しないヘルプ ページを指す helpLinkUri が指定されている必要があります。 + + + + DiagnosticId for analyzers must be in specified format + アナライザーの診断 ID は、指定された形式であることが必要 + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + カテゴリ '{1}' に属している診断 ID '{0}' は必要な範囲にないか、ファイル '{3}' で指定されている形式 '{2}' ではないか、またはその両方です + + + + DiagnosticId for analyzers must be in specified format. + アナライザーの診断 ID は、指定された形式に準拠している必要があります。 + + + + DiagnosticId must be unique across analyzers + 診断 ID は、アナライザーにわたって一意であることが必要 + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + 診断 ID '{0}' はアナライザー '{1}' によって既に使用されています。別の診断 ID をご使用ください。 + + + + DiagnosticId must be unique across analyzers. + 診断 ID は、アナライザーをまたいで一意である必要があります。 + + + + Category for analyzers must be from the specified values + アナライザーのカテゴリは、指定された値からのものであることが必要 + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + カテゴリ '{0}' は、ファイル '{1}' で指定された、許可されたカテゴリからのものではありません + + + + Category for analyzers must be from the specified values. + アナライザーのカテゴリは、指定された値からのものである必要があります。 + + + + Invalid entry in analyzer category and diagnostic ID range specification file + アナライザーのカテゴリと診断 ID 範囲の指定ファイルのエントリが無効 + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + アナライザーのカテゴリと診断 ID 範囲の指定ファイル '{1}' のエントリ '{0}' が無効です + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + アナライザーのカテゴリと診断 ID 範囲の指定ファイルのエントリが無効です。 + + + + DiagnosticId for analyzers must be a non-null constant + アナライザーの診断 ID は、null 以外の定数であることが必要 + + + + Diagnostic Id for rule '{0}' must be a non-null constant + ルール '{0}' の診断 ID は、null 以外の定数である必要があります + + + + DiagnosticId for analyzers must be a non-null constant. + アナライザーの診断 ID は、NULL 以外の定数である必要があります。 + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + 診断アナライザーの型は、ワークスペースのアセンブリの型を使用すべきではありません。ワークスペース アセンブリは、Visual Studio IDE ライブ分析でアナライザーが実行される場合にのみ使用できますが、コマンド ライン ビルド中は使用できません。ワークスペース アセンブリの型を参照すると、コマンド ライン ビルドでのアナライザーの実行中にランタイム例外が発生します。 + + + + Do not use types from Workspaces assembly in an analyzer + アナライザーでワークスペース アセンブリからの型を使用しないでください + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + 診断アナライザーの型 '{0}' を変更して、型 '{1}' へのすべての直接アクセスおよび/または間接アクセス (型 '{2}' にアクセスする) を削除します + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + 診断アナライザーの型 '{0}' を変更して、型 '{1}' へのすべての直接アクセスを削除します + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace が Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet パッケージに移動したため、ビルド ブレークを引き起こす API の変更が生じています。 + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + MSBuildWorkspace をアップグレードして、Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet パッケージに対するパッケージ参照を追加してください。MSBuildWorkspace を正しく使用するための詳細については、https://go.microsoft.com/fwlink/?linkid=874285 をご覧ください。 + + + + Upgrade MSBuildWorkspace + MSBuildWorkspace のアップグレード + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf new file mode 100644 index 0000000000000..f1e914fa6a83d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ko.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 필드에 할당된 'DiagnosticDescriptor'는 컴파일 종료 진단을 보고하는 데 사용되지만 이를 초기화하는 데 사용되는 'DiagnosticDescriptor' 생성자는 필요한 사용자 지정 태그 "CompilationEnd"를 전달하지 않습니다. 자세한 내용은 'WellKnownDiagnosticTags.CompilationEnd'에 대한 설명서를 참조하세요. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + 컴파일 종료 진단을 보고하는 데 사용되므로 필드 '{0}'을(를) 초기화하는 데 사용되는 진단 설명자에 "CompilationEnd" 사용자 지정 태그를 추가합니다. + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + 컴파일 종료 진단 설명자에 "CompilationEnd" 사용자 지정 태그를 추가합니다. + + + + Add rule entry to unshipped release file + 제공되지 않은 릴리스 파일에 규칙 항목 추가 + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + DiagnosticAnalyzer에서 '{0}' 형식을 상속하거나 DiagnosticAnalyzerAttribute를 제거하세요. + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + DiagnosticAnalyzerAttribute로 표시된 형식은 DiagnosticAnalyzer에서 상속되어야 함 + + + + Use a 'SymbolEqualityComparer' for symbol comparison + 기호 비교에 'SymbolEqualityComparer' 사용 + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + 기호는 ID가 아니라 같은지 비교해야 합니다. 'IEqualityComparer'를 허용하는 오버로드를 사용하고 'SymbolEqualityComparer'를 전달하세요. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + 기호는 ID가 아니라 같은지 비교해야 합니다. 'GetHashCode'를 명시적으로 호출하면 잘못된 동작이 수행될 수 있습니다. + + + + Use 'SymbolEqualityComparer' when comparing symbols + 기호를 비교할 때 'SymbolEqualityComparer' 사용 + + + + Symbols should be compared for equality + 기호는 동등성을 위해 비교되어야 합니다. + + + + Configure generated code analysis + 생성된 코드 분석 구성 + + + + Configure generated code analysis + 생성된 코드 분석 구성 + + + + Configure generated code analysis + 생성된 코드 분석 구성 + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + 지원되는 모든 분석기 진단 ID는 분석기 릴리스에 포함되어야 합니다. + + + + Rule '{0}' is not part of any analyzer release + '{0}' 규칙은 분석기 릴리스에 포함되지 않습니다. + + + + Add analyzer diagnostic IDs to analyzer release + 분석기 릴리스에 분석기 진단 ID 추가 + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + 진단 설명은 문장 부호로 끝나는 하나 이상의 문장이어야 하며 선행 또는 후행 공백이 없어야 합니다. + + + + Define diagnostic description correctly + 진단 설명 올바르게 정의 + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + 진단 메시지에 줄 바꿈 문자 또는 선행이나 후행 공백을 포함할 수 없으며, 마침표가 없는 단일 문장이거나 마침표가 있는 여러 문장이어야 합니다. + + + + Define diagnostic message correctly + 진단 메시지 올바르게 정의 + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + 진단 제목에는 마침표, 줄 바꿈 문자 또는 선행이나 후행 공백을 포함할 수 없습니다. + + + + Define diagnostic title correctly + 진단 제목을 올바르게 정의 + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 이 C# 컴파일러 확장은 Microsoft.CodeAnalysis.VisualBasic에 대한 참조를 포함하는 어셈블리에서 구현되면 안 됩니다. Microsoft.CodeAnalysis.VisualBasic 어셈블리는 C# 컴파일 시나리오 중에 항상 제공되는 것은 아니기 때문에 이를 참조하면 컴파일러 확장이 예기치 않게 동작할 수 있습니다. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + 이 컴파일러 확장은 대상 프레임워크 '{0}'가 있는 어셈블리에서 구현되어서는 안 됩니다. 다른 대상 프레임워크를 참조하면 컴파일러가 예기치 않게 동작합니다. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + 컴파일러 확장 지점을 구현하는 형식은 netstandard2.0을 대상으로 하는 어셈블리에서만 선언되어야 합니다. 보다 구체적인 대상 프레임워크는 지원되는 컴파일 시나리오의 하위 집합에서만 사용할 수 있으므로 해당 프레임워크를 대상으로 지정하면 기능이 예기치 않게 동작할 수 있습니다. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + 컴파일러 확장은 netstandard2.0을 대상으로 하는 어셈블리에서 구현되어야 합니다. + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 이 컴파일러 확장은 Microsoft.CodeAnalysis.Workspaces에 대한 참조를 포함하는 어셈블리에서 구현되면 안 됩니다. Microsoft.CodeAnalysis.Workspaces 어셈블리는 명령줄 컴파일 시나리오 중에 제공되지 않기 때문에 이를 참조하면 컴파일러 확장이 예기치 않게 동작할 수 있습니다. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 이 Visual Basic 컴파일러 확장은 Microsoft.CodeAnalysis.CSharp에 대한 참조를 포함하는 어셈블리에서 구현되면 안 됩니다. Microsoft.CodeAnalysis.CSharp 어셈블리는 Visual Basic 컴파일 시나리오 중에 항상 제공되는 것은 아니기 때문에 이를 참조하면 컴파일러 확장이 예기치 않게 동작할 수 있습니다. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + 컴파일러 확장 지점을 구현하는 형식은 모든 컴파일 시나리오에서 제공되는 어셈블리에 대한 참조를 포함하는 어셈블리에서 선언해야 합니다. 그렇지 않으면 기능이 예기치 않게 동작할 수 있습니다. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + 컴파일러 확장은 컴파일러 제공 참조가 있는 어셈블리에서 구현해야 합니다. + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel'은 컴파일러나 다른 분석기와 컴파일 데이터를 공유하지 않는 완전히 새로운 의미 체계 모델을 만들므로 진단 분석기 내에서 호출하는 데 부담이 큰 메서드입니다. 이 메서드를 사용하면 의미 체계 분석 중 추가 성능 비용이 발생합니다. 대신 공유 'SemanticModel'을 사용할 수 있는 'RegisterOperationAction', 'RegisterSyntaxNodeAction', 'RegisterSemanticModelAction' 등과 같은 다른 분석기 작업을 등록하세요. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 진단 분석기 내에서 Compilation.GetSemanticModel() 메서드를 호출하지 마세요. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 진단 분석기 내에서 Compilation.GetSemanticModel() 메서드를 호출하지 마세요. + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + 분석기의 DiagnosticId에는 예약된 ID를 사용해서는 안 됩니다. + + + + '{0}' is a reserved diagnostic ID + '{0}'은(는) 예약된 진단 ID입니다. + + + + Do not use reserved diagnostic IDs + 예약된 진단 ID를 사용하면 안 됨 + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + 분석기 패키지에 대해 릴리스 추적을 사용하도록 설정하면 각 분석기 릴리스와 함께 제공 및/또는 변경되는 분석기 진단을 추적하고 문서화하는 데 도움이 됩니다. 자세한 내용은 https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md를 참조하세요. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + '{0}' 규칙을 포함하는 분석기 프로젝트에 대해 분석기 릴리스 추적 사용 + + + + Enable analyzer release tracking + 분석기 릴리스 추적 사용 + + + + Enable concurrent execution + 동시 실행 사용 + + + + Enable concurrent execution + 동시 실행 사용 + + + + Enable concurrent execution + 동시 실행 사용 + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + 분석기 릴리스 파일에 잘못된 항목이 있습니다. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + 분석기 릴리스 파일 '{0}'에 잘못된 항목 '{1}'이(가) 있습니다. + + + + Invalid entry in analyzer release file + 분석기 릴리스 파일에 잘못된 항목이 있습니다. + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + 분석기 릴리스 파일 '{0}'에 누락되거나 잘못된 릴리스 헤더 '{1}'이(가) 있습니다. + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + 분석기 릴리스 파일 '{0}'에 '{2}' 규칙에 대해 제공된 이전 릴리스가 없는 잘못된 '{1}' 항목이 있습니다. 제공되지 않은 릴리스 파일에서 해당 규칙에 대한 별도의 '{1}' 항목을 대신 추가합니다. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + 분석기 릴리스 파일 '{0}'에 '{1}'에 수동으로 채워야 하는 하나 이상의 '검색되지 않은' 필드가 있는 항목이 있습니다. + + + + Missing '{0}' attribute + '{0}' 특성이 없습니다. + + + + Missing diagnostic analyzer attribute + 진단 분석기 특성이 없음 + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + DiagnosticAnalyzer의 비추상 하위 형식은 DiagnosticAnalyzerAttribute로 표시되어야 합니다. 이 특성에 대한 인수가 있는 경우 해당 인수가 분석기에 지원되는 언어를 결정합니다. 이 특성이 없는 분석기 형식은 분석 엔진에 의해 무시됩니다. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + '{0}' 진단 분석기는 C# 및 Visual Basic을 모두 지원할 수 있습니다. '{1}' 언어 지원을 위해 DiagnosticAnalyzerAttribute에 대한 인수를 추가하세요. + + + + Recommend adding language support to diagnostic analyzer + 진단 분석기에 언어 지원을 추가하는 것이 좋음 + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + 진단 분석기는 1개의 언어만 지원하는 것으로 표시되지만, 분석기 어셈블리가 언어 특정 CodeAnalysis 어셈블리를 참조하지 않는 것으로 보이므로 2개 이상의 언어로 작업할 가능성이 있습니다. DiagnosticAnalyzerAttribute에 언어 인수를 추가하세요. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + '{0}'에 대한 DiagnosticAnalyzer 특성을 적용하세요. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + '{0}' 및 '{1}'에 대한 DiagnosticAnalyzer 특성을 적용하세요. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + 기호 분석기 작업을 등록할 때 관심 있는 SymbolKind를 하나 이상 지정하세요. + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + 구문 노드 분석기 작업을 등록할 때 관심 있는 SyntaxKind를 하나 이상 지정하세요. + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + 작업 분석기 작업을 등록할 때 관심 있는 OperationKind를 하나 이상 지정하세요. + + + + Missing kind argument when registering an analyzer action + 분석기 작업을 등록할 때 종류 인수가 없음 + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + 구문, 기호 또는 작업 분석기 작업을 각각 등록할 때 하나 이상의 구문, 기호 또는 작업 종류를 지정해야 합니다. 지정하지 않으면 등록된 작업이 분석하는 동안 호출되지 않습니다. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + 분석기 또는 원본 생성기를 포함하는 프로젝트는 속성 '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'을(를) 지정해야 합니다. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': 분석기 또는 원본 생성기를 포함하는 프로젝트는 속성 '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'을(를) 지정해야 합니다. + + + + Specify analyzer banned API enforcement setting + 분석기 금지 API 적용 설정 지정 + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + 구문 종류를 확인할 때 'syntax.IsKind(kind)' ~ 'syntax.Kind() == kind'를 선호합니다. 'IsKind'를 사용하는 코드는 런타임 시 약간 더 효율적이므로 해당하는 경우 이 형식을 일관되게 사용하면 복잡한 분석 시나리오에서 성능을 향상하는 데 도움이 됩니다. + + + + Use 'IsKind' instead of 'Kind' + 'Kind' 대신 'IsKind' 사용 + + + + Prefer 'IsKind' for checking syntax kinds + 구문 종류 확인에 'IsKind' 선호 + + + + Prefer 'IsKind' for checking syntax kinds + 구문 종류 확인에 'IsKind' 선호 + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + 'customTags' 값은 특정 태그 값을 기준으로 진단 설명자에서 특정 작업 및 필터를 사용하도록 설정하는 방법으로 사용됩니다. 모든 Roslyn 분석기에는 'WellKnownDiagnosticTags' 클래스의 태그가 하나 이상 있어야 합니다. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + 진단 설명자 생성자에 null이 아닌 'customTags'를 제공하여 진단 설명자의 메타데이터 필터링을 사용하도록 설정하는 것이 좋습니다. + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + null이 아닌 'customTags' 값을 진단 설명자 생성자에 제공 + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + 분석기 릴리스 간에 진단 ID의 중복 항목을 제거합니다. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + '{0}' 규칙에서 '{1}' 릴리스 및 '{2}' 릴리스 간에 중복 항목이 있습니다. + + + + Remove duplicate entries for diagnostic ID between analyzer releases + 분석기 릴리스 간에 진단 ID의 중복 항목 제거 + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + 동일한 분석기 릴리스에서 진단 ID의 중복 항목을 제거합니다. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + '{0}' 규칙에 분석기 릴리스 파일 '{2}'의 '{1}' 릴리스에 대한 항목이 둘 이상 있습니다. + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + 동일한 분석기 릴리스에서 진단 ID의 중복 항목 제거 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + 더 이상 보고되지 않는 제공된 진단 ID는 제공되지 않은 파일의 '제거된 규칙' 테이블에 항목이 있어야 합니다. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + '{0}' 규칙은 분석기 릴리스 '{1}'에서 제공되었지만 더 이상 모든 분석기를 지원하는 진단이 아닙니다. '제거된 규칙' 테이블에서 이 규칙에 대한 항목을 제공되지 않은 파일에 추가합니다. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + 더 이상 보고되지 않는 제공된 진단 ID는 제공되지 않은 파일의 '제거된 규칙' 테이블에 항목이 있어야 합니다. + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + 더 이상 보고되지 않고 제공되지 않는 분석기 진단 ID의 항목은 제공되지 않은 분석기 릴리스에서 제거될 수 있습니다. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + '{0}' 규칙은 제공되지 않은 다음 분석기 릴리스에 포함되지만 모든 분석기를 지원하는 진단이 아닙니다. + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + 제거된 분석기 진단 ID를 제공되지 않은 분석기 릴리스에 추가하면 안 됩니다. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + 'SyntaxNode'에서 상속되는 특정 형식을 사용하여 'SemanticModel.GetDeclaredSymbol'을 호출합니다(예: 'GlobalStatementSyntax' 및 'IncompleteMemberSyntax'는 항상 'null'을 반환합니다.) + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + 'FieldDeclarationSyntax' 또는 'EventFieldDeclarationSyntax' 형식의 인수를 사용하여 'SemanticModel.GetDeclaredSymbol'을 호출하면 항상 'null'이 반환됩니다. 대신 필드의 변수 선언자를 사용하여 'GetDeclaredSymbol'을 호출합니다. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})'에 대한 호출은 항상 'null'을 반환합니다. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})'에 대한 호출은 항상 'null'을 반환합니다. + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + 'SemanticModel.GetDeclaredSymbol()'에 대한 이 호출은 항상 'null'을 반환합니다. + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + 기호가 분석기에서 사용 금지된 것으로 표시되었으며 대신 대체 기호를 사용해야 합니다. + + + + The symbol '{0}' is banned for use by analyzers{1} + 기호 '{0}'은(는) 분석기에서 사용할 수 없습니다{1} + + + + Do not use APIs banned for analyzers + 분석기에서 금지된 API 사용 안 함 + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + 분석기 릴리스 파일에서 제거된 것으로 표시된 진단 ID는 분석기에서 보고되면 안 됩니다. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + '{0}' 규칙이 최신 분석기 릴리스에서 제거된 것으로 표시되었지만 계속 보고됩니다. + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + 분석기 릴리스 파일에서 제거된 것으로 표시된 진단 ID는 분석기에서 보고되면 안 됩니다. + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + SymbolKind '{0}'이(가) 기호 분석기 작업에서 지원되지 않습니다. + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + 기호 분석기 작업을 등록할 때 지원되지 않는 SymbolKind 인수 + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + 지원되지 않는 DiagnosticDescriptor '{0}'을(를) 사용하여 호출된 ReportDiagnostic입니다. + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + 지원되지 않는 DiagnosticDescriptor를 사용하여 호출된 ReportDiagnostic + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic은 DiagnosticAnalyzer.SupportedDiagnostics 속성에서 반환된 지원되는 DiagnosticDescriptors를 통해서만 호출되어야 합니다. 그러지 않으면 보고된 진단이 분석 엔진에 의해 필터링됩니다. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + '{2}' 메서드의 '{1}' 형식 매개 변수에 대한 형식 인수 '{0}'은(는) SyntaxKind 열거형이 아닙니다. + + + + Invalid type argument for DiagnosticAnalyzer's Register method + DiagnosticAnalyzer의 Register 메서드에 대한 형식 인수가 잘못됨 + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + RegisterSyntaxNodeAction, RegisterCodeBlockStartAction 및 RegisterCodeBlockEndAction 등의 DiagnosticAnalyzer의 언어 특정 Register 메서드에는 '{0}' 형식 매개 변수에 대한 언어 특정 'SyntaxKind' 형식 인수를 사용해야 합니다. 그러지 않으면 등록된 분석기 작업이 분석하는 동안 호출되지 않습니다. + + + + Start action has no registered non-end actions + 시작 작업에 등록된 비종료 작업이 없음 + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}'은(는) 분석기 작업을 등록하지 않습니다. 이 시작 작업에 종속된 '{1}'에 등록된 작업을 '{0}'(으)로 이동하세요. + + + + Start action has no registered actions + 시작 작업에 등록된 작업이 없음 + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}'은(는) '{1}'을(를) 제외하고 분석기 작업을 등록하지 않았습니다. 이 시작/종료 작업 쌍을 '{2}'(으)로 바꾸거나 이 시작 작업에 종속된 '{3}'에 등록된 작업을 '{0}'(으)로 이동하세요. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + 분석기 시작 작업으로 코드 블록, 컴파일 등의 특정 코드 단위에서 상태 저장 분석을 수행할 수 있습니다. 메모리 누수 없이 분석기를 효율적으로 실행하려면 신중한 설계가 필요합니다. 이러한 분석기를 쓰려면 다음 지침을 따르세요. +1. 등록된 시작 작업에 대해 각 코드 단위를 분석하기 위한 프라이빗 중첩 형식이 있는 새 범위를 정의합니다. +2. 필요한 경우 시작 작업에서 상태를 정의하고 초기화합니다. +3. 시작 작업에서 이 상태를 참조하는 비종료 작업을 하나 이상 등록합니다. 이러한 작업이 필요하지 않은 경우 시작 작업을 비종료 작업으로 바꾸는 것이 좋습니다. 예를 들어 등록된 작업이 없는 CodeBlockStartAction 또는 등록된 CodeBlockEndAction만 CodeBlockAction으로 바꿀 수 있습니다. +4. 필요한 경우 종료 작업을 등록하여 최종 상태를 기준으로 진단을 보고합니다. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + 분석기 진단 ID의 최신 항목이 분석기 릴리스에 추가되었는지 확인합니다. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + '{0}' 규칙에는 마지막 릴리스에서 변경된 '범주' 또는 '심각도'가 있습니다. 소스에서 업데이트를 되돌리거나 제공되지 않은 릴리스 파일에 새 최신 항목을 추가합니다. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + 분석기 진단 ID의 최신 항목이 분석기 릴리스에 추가되었는지 확인 + + + + Update rule entry in unshipped release file + 제공되지 않은 릴리스 파일의 규칙 항목 업데이트 + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + '{0}' 형식의 지역화 가능 인수를 진단 설명자 생성자에 제공하여 설명자가 지역화 가능하도록 하는 것이 좋습니다. + + + + Provide localizable arguments to diagnostic descriptor constructor + 지역화 가능 인수를 진단 설명자 생성자에 제공 + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + 진단 분석기 및 보고된 진단을 현지화해야 하는 경우 진단을 생성하는 데 사용된 지원되는 DiagnosticDescriptors도 현지화할 수 있어야 합니다. 이 경우 매개 변수 '제목'(선택적으로 '설명')에 대해 현지화할 수 있는 인수를 진단 설명자 생성자에 제공하여 설명자를 현지화할 수 있는지 확인해야 합니다. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + '{0}' 형식의 컴파일당 데이터를 진단 분석기의 필드에 저장하지 마세요. + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + 컴파일당 데이터를 진단 분석기의 필드에 저장하면 안 됨 + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + 진단 분석기의 인스턴스는 컴파일 수명보다 더 오래 활성화될 수 있습니다. 그러므로 기호와 같은 컴파일당 데이터를 진단 분석기 필드에 저장하면 활성화된 컴파일의 상태가 부실하여 메모리 손실 문제를 일으킬 수 있습니다. 대신 인스턴스화된 별도 형식의 데이터를 '{0}.{1}' API를 사용하여 등록된 컴파일 시작 작업에 저장해야 합니다. 이 형식의 컴파일당 인스턴스가 만들어지며 컴파일의 수명보다 오래 활성화되지 않아 메모리 손실을 방지할 수 있습니다. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + 이 인터페이스 작성자는 이 인터페이스를 타사에서 구현하도록 의도하지 않았으며 변경할 권리를 갖습니다. 그러므로 이 인터페이스를 구현하면 향후 버전에서 소스 또는 이진 호환성 문제가 발생할 수 있습니다. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + {1}을(를) 공개 구현할 수 없으므로 {0} 형식은 {1} 인터페이스를 구현할 수 없습니다. + + + + Only internal implementations of this interface are allowed + 이 인터페이스의 내부 구현만 허용됨 + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + 모든 발생 수정을 지원하는 CodeFixProvider에 이 수정 도구에서 만들어진 각 종류의 코드 작업에 대해 고유한 명시적이고 null이 아닌 동등 키를 할당하여 등록된 코드 작업을 동등 클래스로 분류해야 합니다. 이렇게 하면 FixAllProvider가 트리거 코드 작업의 동등 클래스에 있는 수정 도구의 코드 작업을 적용하여 필수 범위에서 모든 진단을 수정할 수 있습니다. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + 이 수정 도구에서 생성된 각 종류의 코드 작업에 대해 고유하고 null이 아닌 선택적 매개 변수 '{0}'의 명시적 인수를 제공하세요. + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + 코드 생성 작업에는 FixAll 발생 지원을 위한 고유 EquivalenceKey가 있어야 함 + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}'에는 '{1}' 속성에 대한 'null'의 기본값이 있습니다. '{0}'의 속성을 재정의하여 수정 도구당 모든 코드 작업에서 null이 아닌 고유 값을 반환하거나 기존 코드 작업을 사용하세요. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + FixAll 발생 지원을 위한 고유 EquivalenceKey가 있는 코드 작업 사용 + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Roslyn에서 노출한 대부분의 개체는 변경할 수 없습니다. 이 개체에 대한 메서드 호출의 반환 값은 무시할 수 없습니다. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}'을(를) 변경할 수 없으며 '{1}'은(는) 아무런 영향을 주지 않습니다. '{1}'의 반환 값을 사용하세요. + + + + Do not ignore values returned by methods on immutable objects + 변경할 수 없는 개체의 메서드에서 반환된 값을 무시하지 마세요. + + + + Code fix providers should provide FixAll support + 코드 수정 공급자가 FixAll 지원을 제공해야 함 + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}'은(는) 하나 이상의 코드 수정 사항을 등록하지만 'CodeFixProvider.GetFixAllProvider' 메서드를 재정의하지 않습니다. 이 메서드를 재정의하고 FixAll 지원에 대해 null이 아닌 FixAllProvider(잠재적으로 'WellKnownFixAllProviders.BatchFixer')를 제공하세요. 'null'은 FixAll 지원을 명시적으로 사용하지 않습니다. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + 사용자가 단일 코드 수정 사항을 사용하여 기본 진단의 여러 인스턴스를 수정할 수 있도록 하려면 CodeFixProvider가 FixAll 지원을 제공해야 합니다. 자세한 내용은 https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md에 있는 문서를 참조하세요. + + + + Override GetFixAllProvider. + GetFixAllProvider를 재정의하세요. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + null이 아닌 'helpLinkUri'를 진단 설명자 생성자에 제공하여 이 진단이 오류 목록에 나타날 때 정보를 표시하는 것이 좋습니다. + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + null이 아닌 'helpLinkUri' 값을 진단 설명자 생성자에 제공 + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + 'helpLinkUri' 값은 이 진단이 오류 목록에 표시될 때 정보를 표시하는 데 사용됩니다. 모든 분석기에 시간이 지나도 변경되지 않는 도움말 페이지를 가리키는 지정된 helpLinkUri가 있어야 합니다. + + + + DiagnosticId for analyzers must be in specified format + 분석기의 진단 ID는 지정된 형식이어야 함 + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + '{1}' 범주에 속하는 진단 ID '{0}'이(가) 필요한 범위 내에 있지 않고/않거나 '{3}' 파일에 지정된 '{2}' 형식이 아닙니다. + + + + DiagnosticId for analyzers must be in specified format. + 분석기에 대한 진단 ID는 지정된 형식이어야 합니다. + + + + DiagnosticId must be unique across analyzers + 진단 ID는 분석기 간에 고유해야 함 + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + 진단 ID '{0}'이(가) 분석기 '{1}'에서 이미 사용되고 있습니다. 다른 진단 ID를 사용하세요. + + + + DiagnosticId must be unique across analyzers. + 진단 ID는 분석기 간에 고유해야 합니다. + + + + Category for analyzers must be from the specified values + 분석기 범주는 지정된 값이어야 함 + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + '{0}' 범주가 '{1}' 파일에 지정된 허용 범주에 속해 있지 않습니다. + + + + Category for analyzers must be from the specified values. + 분석기에 대한 범주는 지정된 값이어야 합니다. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + 분석기 범주 및 진단 ID 범위 사양 파일의 항목이 잘못됨 + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + 분석기 범주 및 진단 ID 범위 사양 파일 '{1}'의 '{0}' 항목이 잘못되었습니다. + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + 분석기 범주 및 진단 ID 범위 지정 파일의 항목이 잘못되었습니다. + + + + DiagnosticId for analyzers must be a non-null constant + 분석기의 진단 ID는 null이 아닌 상수여야 함 + + + + Diagnostic Id for rule '{0}' must be a non-null constant + '{0}' 규칙의 진단 ID는 null이 아닌 상수여야 합니다. + + + + DiagnosticId for analyzers must be a non-null constant. + 분석기에 대한 진단 ID는 null이 아닌 상수여야 합니다. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + 진단 분석기 형식은 작업 영역 어셈블리의 형식을 사용해서는 안 됩니다. 작업 영역 어셈블리는 분석기가 Visual Studio IDE 라이브 분석에서 실행될 때에만 사용 가능하지만, 명령줄 빌드 중에는 사용할 수 없습니다. 작업 영역 어셈블리의 형식을 참조하면 명령줄 빌드에서 분석기 실행 중 런타임 예외가 발생합니다. + + + + Do not use types from Workspaces assembly in an analyzer + 분석기에서 작업 영역 어셈블리의 형식을 사용하지 마세요. + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + 진단 분석기 형식 '{0}'을(를) 변경하여 '{1}' 형식에 대해 액세스 형식이 '{2}'인 모든 직접 및/또는 간접 액세스를 제거합니다. + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + 진단 분석기 형식 '{0}'을(를) 변경하여 '{1}' 형식에 대해 모든 직접 액세스를 제거합니다. + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace가 Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 패키지로 이동되었으며 새로운 API 변경 내용이 있습니다. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 패키지에 패키지 참조를 추가하여 MSBuildWorkspace를 업그레이드하세요. 성공적인 MSBuildWorkspace 사용에 대한 자세한 내용은 https://go.microsoft.com/fwlink/?linkid=874285를 참조하세요. + + + + Upgrade MSBuildWorkspace + MSBuildWorkspace 업그레이드 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf new file mode 100644 index 0000000000000..3678ada616261 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pl.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + Element „DiagnosticDescriptor” przypisany do pola jest używany do raportowania diagnostyki zakończenia kompilacji, ale konstruktor „DiagnosticDescriptor” użyty do zainicjowania go nie przekazuje wymaganego niestandardowego tagu „CompilationEnd”. Zobacz dokumentację elementu „WellKnownDiagnosticTags.CompilationEnd”, aby uzyskać szczegółowe informacje. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Dodaj niestandardowy tag „CompilationEnd” do deskryptora diagnostycznego używanego do inicjowania pola „{0}”, ponieważ jest on używany do raportowania diagnostyki zakończenia kompilacji + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Dodaj niestandardowy tag „CompilationEnd” do deskryptora diagnostycznego zakończenia kompilacji + + + + Add rule entry to unshipped release file + Dodaj wpis reguły do niedostarczonego pliku wydania + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Dziedzicz typ „{0}” z elementu DiagnosticAnalyzer lub usuń elementy DiagnosticAnalyzerAttribute + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Typy oznaczone za pomocą elementów DiagnosticAnalyzerAttribute powinny dziedziczyć po elemencie DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Użyj elementu „SymbolEqualityComparer” do porównania symboli + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Symbole powinny być porównywane pod kątem równości, a nie tożsamości. Użyj przeciążenia przyjmującego element „IEqualityComparer” i przekazującego element „SymbolEqualityComparer”. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Symbole powinny być porównywane pod kątem równości, a nie tożsamości. Jawne wywołanie metody „GetHashCode” prawdopodobnie spowoduje nieprawidłowe zachowanie. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Użyj elementu „SymbolEqualityComparer” podczas porównywania symboli + + + + Symbols should be compared for equality + Symbole powinny być porównywane pod kątem równości + + + + Configure generated code analysis + Konfigurowanie analizy wygenerowanego kodu + + + + Configure generated code analysis + Konfigurowanie analizy wygenerowanego kodu + + + + Configure generated code analysis + Konfigurowanie analizy wygenerowanego kodu + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Wszystkie obsługiwane identyfikatory diagnostyczne analizatora powinny być częścią wydania analizatora. + + + + Rule '{0}' is not part of any analyzer release + Reguła „{0}” nie jest częścią żadnego wydania analizatora + + + + Add analyzer diagnostic IDs to analyzer release + Dodaj identyfikatory diagnostyki analizatora do wydania analizatora + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + Opis diagnostyczny powinien być jednym lub wieloma zdaniami kończącymi się znakiem interpunkcyjnym i nie powinien mieć żadnych spacji na początku ani na końcu + + + + Define diagnostic description correctly + Poprawnie zdefiniuj opis diagnostyki + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Komunikat dotyczący diagnostyki nie powinien zawierać znaku nowego wiersza ani odstępów na początku i końcu oraz powinien być pojedynczym zdaniem bez kropki na końcu lub wieloma zdaniami z kropkami na końcu + + + + Define diagnostic message correctly + Poprawnie zdefiniuj komunikat dotyczący diagnostyki + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Tytuł diagnostyki nie powinien zawierać kropki ani żadnego znaku nowego wiersza, a także żadnych spacji na początku i na końcu + + + + Define diagnostic title correctly + Poprawnie zdefiniuj tytuł diagnostyki + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + To rozszerzenie kompilatora języka C# nie powinno być implementowane w zestawie zawierającym odwołanie do zestawu Microsoft.CodeAnalysis.VisualBasic. Zestaw Microsoft.CodeAnalysis.VisualBasic nie jest dostarczany podczas scenariuszy kompilacji w języku C#, więc odwołania do niego mogą spowodować nieprzewidywalne działanie rozszerzenia kompilatora. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + To rozszerzenie kompilatora nie powinno być wdrażane w zestawie z platformą docelową „{0}”. Odwołania do innych platform docelowych spowodują nieprzewidywalne zachowanie kompilatora. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Typy wdrażające punkty rozszerzenia kompilatora powinny być deklarowane tylko w zestawach z odwołaniami netstandard2.0. Bardziej określone platformy docelowe są dostępne tylko w podzbiorze obsługiwanych scenariuszy kompilacji, więc odwołanie do nich może spowodować nieprzewidywalne działanie funkcji. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Rozszerzenia kompilatora powinny być wdrażane w zestawach z odwołaniami netstandard2.0 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + To rozszerzenie kompilatora nie powinno być implementowane w zestawie zawierającym odwołanie do zestawu Microsoft.CodeAnalysis.Workspaces. Zestaw Microsoft.CodeAnalysis.Workspaces nie został dostarczony podczas scenariuszy kompilacji wiersza polecenia, więc odwołania do niego mogą spowodować nieprzewidywalne działanie rozszerzenia kompilatora. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + To rozszerzenie kompilatora języka Visual Basic nie powinno być implementowane w zestawie zawierającym odwołanie do zestawu Microsoft.CodeAnalysis.CSharp. Zestaw Microsoft.CodeAnalysis.CSharp nie jest dostarczany podczas scenariuszy kompilacji w języku Visual Basic, więc odwołania do niego mogą spowodować nieprzewidywalne działanie rozszerzenia kompilatora. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Typy implementujące punkty rozszerzenia kompilatora nie powinny być deklarowane w zestawach zawierających odwołania do zestawów, które nie są dostarczane przez wszystkie scenariusze kompilacji. Wykonanie tej czynności może spowodować nieprzewidywalne zachowanie funkcji. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Rozszerzenia kompilatora powinny być implementowane w zestawach z odwołaniami dostarczonymi przez kompilator + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + Wywołanie metody „GetSemanticModel” w analizatorze diagnostycznym jest kosztowne, ponieważ powoduje utworzenie całkowicie nowego modelu semantycznego, który nie współdzieli danych kompilacji z kompilatorem ani innymi analizatorami. Wiąże się to z dodatkowym kosztem wydajności podczas analizy semantycznej. Zamiast tego rozważ zarejestrowanie innej akcji analizatora, która umożliwia użycie współdzielonego modelu „SemanticModel”, takiego jak „RegisterOperationAction”, „RegisterSyntaxNodeAction” lub „RegisterSemanticModelAction”. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Nie wywołuj metody Compilation.GetSemanticModel() w analizatorze diagnostycznym + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Nie wywołuj metody Compilation.GetSemanticModel() w analizatorze diagnostycznym + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + W elemencie DiagnosticId dla analizatorów nie należy używać zastrzeżonych identyfikatorów. + + + + '{0}' is a reserved diagnostic ID + „{0}” to zastrzeżony identyfikator diagnostyczny + + + + Do not use reserved diagnostic IDs + Nie używaj zastrzeżonych identyfikatorów diagnostycznych + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Włączenie śledzenia wydań dla pakietów analizatora ułatwia śledzenie i dokumentowanie diagnostyki analizatora dostarczanej i/lub zmienianej w poszczególnych wydaniach analizatora. Zobacz szczegóły pod adresem https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Włącz śledzenie wydań analizatora dla projektu analizatora zawierającego regułę „{0}” + + + + Enable analyzer release tracking + Włącz śledzenie wydań analizatora + + + + Enable concurrent execution + Włącz wykonywanie współbieżne + + + + Enable concurrent execution + Włącz wykonywanie współbieżne + + + + Enable concurrent execution + Włącz wykonywanie współbieżne + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Nieprawidłowy wpis w pliku wydania analizatora. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Plik wydania analizatora „{0}” ma nieprawidłowy wpis „{1}” + + + + Invalid entry in analyzer release file + Nieprawidłowy wpis w pliku wydania analizatora + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + Brak nagłówka wydania „{1}” w pliku wydania analizatora „{0}” lub jest on nieprawidłowy + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + Plik wydania analizatora „{0}” ma nieprawidłowy wpis „{1}” bez wcześniejszego dostarczonego wydania dla reguły „{2}”. Zamiast tego dodaj osobny wpis „{1}” dla reguły w niedostarczonym pliku wydania. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + Plik wydania analizatora „{0}” ma wpis z co najmniej jednym polem „Niewykryte”, które należy wypełnić ręcznie w elemencie „{1}” + + + + Missing '{0}' attribute + Brak atrybutu „{0}” + + + + Missing diagnostic analyzer attribute + Brak atrybutu analizatora diagnostycznego + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Nieabstrakcyjne podtypy atrybutu DiagnosticAnalyzer powinny być oznakowane atrybutami DiagnosticAnalyzerAttribute. Argument dla tych atrybutów, o ile istnieje, określa obsługiwane języki analizatora. Typy analizatora bez tego atrybutu będą ignorowane przez aparat analizy. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Analizator diagnostyczny „{0}” może mieć możliwość obsłużenia zarówno języka C#, jak i Visual Basic. Rozważ dodanie argumentu do atrybutu DiagnosticAnalyzerAttribute w celu obsługi języka „{1}”. + + + + Recommend adding language support to diagnostic analyzer + Zaleć dodanie obsługi języka do analizatora diagnostycznego + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Analizator diagnostyczny jest oznaczony jako obsługujący tylko jeden język, ale zestaw analizatora raczej nie odwołuje się do żadnych zestawów CodeAnalysis specyficznych dla języka, a więc najprawdopodobniej będzie działał dla więcej niż jednego języka. Rozważ dodanie argumentu dodatkowego języka do atrybutu DiagnosticAnalyzerAttribute. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Zastosuj atrybut DiagnosticAnalyzer do elementu „{0}”. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Zastosuj atrybut DiagnosticAnalyzer zarówno do elementu „{0}”, jak i elementu „{1}”. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Określ co najmniej jeden interesujący argument SymbolKind podczas rejestrowania akcji analizatora symboli + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Określ co najmniej jeden interesujący argument SyntaxKind podczas rejestrowania akcji analizatora węzła składni + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Określ co najmniej jeden interesujący argument OperationKind podczas rejestrowania akcji analizatora operacji + + + + Missing kind argument when registering an analyzer action + Brak argumentu rodzaju podczas rejestrowania akcji analizatora + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Musisz określić co najmniej jeden rodzaj składni, symbolu lub operacji podczas rejestracji odpowiednio akcji analizatora składni, symboli lub operacji. W przeciwnym razie zarejestrowana akcja nie zostanie nigdy wywołana podczas analizy. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Projekt zawierający analizatory lub generatory źródeł powinien określać właściwość „<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>” + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + „{0}”: projekt zawierający analizatory lub generatory źródeł powinien określać właściwość „<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>” + + + + Specify analyzer banned API enforcement setting + Określ ustawienie wymuszania zabronionego interfejsu API analizatora + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Preferuj formę „syntax.IsKind(kind)” zamiast „syntax.Kind() == kind” podczas sprawdzania rodzajów składni. Kod używający elementu „IsKind” jest nieco bardziej wydajny w środowisku uruchomieniowym, więc konsekwentne używanie tej formy w stosownych przypadkach pomaga zwiększyć wydajność w złożonych scenariuszach analizy. + + + + Use 'IsKind' instead of 'Kind' + Użyj elementu „IsKind” zamiast elementu „Kind” + + + + Prefer 'IsKind' for checking syntax kinds + Preferuj metodę „IsKind” do sprawdzania rodzajów składni + + + + Prefer 'IsKind' for checking syntax kinds + Preferuj metodę „IsKind” do sprawdzania rodzajów składni + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + Wartość „customTags” jest używana jako sposób włączania określonych akcji i filtrów w deskryptorach diagnostycznych na podstawie konkretnych wartości tagów. Każdy analizator Roslyn powinien zawierać co najmniej jeden tag z klasy „WellKnownDiagnosticTags”. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Rozważ podanie innej niż null wartości „customTags” w konstruktorze deskryptorów diagnostycznych, aby umożliwić filtrowanie metadanych deskryptorów diagnostycznych + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Podaj inną niż null wartość „customTags” w konstruktorze deskryptorów diagnostycznych + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Usuń zduplikowane wpisy identyfikatora diagnostycznego w różnych wydaniach analizatora. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + Reguła „{0}” ma zduplikowany wpis między wydaniami „{1}” i „{2}” + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Usuń zduplikowane wpisy identyfikatora diagnostyki w różnych wydaniach analizatora + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Usuń zduplikowane wpisy identyfikatora diagnostycznego w tym samym wydaniu analizatora. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + Reguła „{0}” ma więcej niż jeden wpis dla wydania „{1}” w pliku wydania analizatora „{2}” + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Usuń zduplikowane wpisy identyfikatora diagnostyki w tym samym wydaniu analizatora + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Dostarczone identyfikatory diagnostyczne, które nie są już zgłaszane, powinny mieć wpis w tabeli „Usunięte reguły” niedostarczonego pliku. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + Reguła „{0}” została dostarczona w wydaniu analizatora „{1}”, ale nie jest to już obsługiwana diagnostyka dla żadnego analizatora. Dodaj wpis dla tej reguły w tabeli „Usunięte reguły” do niedostarczonego pliku. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Dostarczone identyfikatory diagnostyki, które nie są już zgłaszane, powinny mieć wpis w tabeli „Usunięte reguły” niedostarczonego pliku + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Wpisy identyfikatorów diagnostycznych analizatora, które nie są już zgłaszane i nigdy nie są dostarczane, mogą zostać usunięte z niedostarczonego wydania analizatora. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + Reguła „{0}” jest częścią następnego niedostarczonego wydania analizatora, ale nie jest obsługiwaną diagnostyką dla żadnego analizatora + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Nie dodawaj usuniętych identyfikatorów diagnostyki analizatora do niedostarczonego wydania analizatora + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Wywołanie metody „SemanticModel.GetDeclaredSymbol” z określonymi typami dziedziczonymi z elementu „SyntaxNode”, na przykład „GlobalStatementSyntax” i „IncompleteMemberSyntax”, zawsze zwróci wartość „null”. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Wywołanie metody „SemanticModel.GetDeclaredSymbol” z argumentem typu „FieldDeclarationSyntax” lub „EventFieldDeclarationSyntax” zawsze zwróci wartość „null”. Wywołaj metodę „GetDeclaredSymbol” z deklaratorami zmiennych z pola. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Wywołanie metody „SemanticModel.GetDeclaredSymbol({0})” zawsze zwraca wartość „null” + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Wywołanie metody „SemanticModel.GetDeclaredSymbol({0})” zawsze zwraca wartość „null” + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + To wywołanie metody "SemanticModel.GetDeclaredSymbol()" zawsze zwróci wartość „null” + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Symbol został oznaczony jako zabroniony do użytku w analizatorze i zamiast niego powinien być używany alternatywny. + + + + The symbol '{0}' is banned for use by analyzers{1} + Symbol „{0}” jest zabroniony do użycia przez analizatory{1} + + + + Do not use APIs banned for analyzers + Nie używaj interfejsów API zablokowanych dla analizatorów + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Identyfikatory diagnostyczne oznaczone jako usunięte w pliku wydania analizatora nie powinny być zgłaszane przez analizatory. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + Reguła „{0}” jest oznaczona jako usunięta w najnowszym wydaniu analizatora, ale jest nadal zgłaszana + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Identyfikatory diagnostyki oznaczone jako usunięte w pliku wydania analizatora nie powinny być zgłaszane przez analizatory + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + Element SymbolKind „{0}” nie jest obsługiwany dla akcji analizatora symboli + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Nieobsługiwany argument SymbolKind podczas rejestrowania akcji analizatora symboli + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + Argument ReportDiagnostic wywołany z nieobsługiwanym deskryptorem DiagnosticDescriptor „{0}” + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + Argument ReportDiagnostic wywołany z nieobsługiwanym deskryptorem DiagnosticDescriptor + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + Argument ReportDiagnostic powinien być wywoływany tylko z obsługiwanymi deskryptorami DiagnosticDescriptor, które są zwracane z właściwości DiagnosticAnalyzer.SupportedDiagnostics. W przeciwnym razie zgłoszona diagnostyka zostanie odfiltrowana przez aparat analizy. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + Argument typu „{0}” dla parametru typu „{1}” metody „{2}” nie jest wyliczeniem SyntaxKind + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Nieprawidłowy argument typu dla metody Register analizatora DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Specyficzne dla języka metody Register analizatora DiagnosticAnalyzer, takie jak RegisterSyntaxNodeAction, RegisterCodeBlockStartAction i RegisterCodeBlockEndAction, oczekują specyficznego dla języka argumentu typu „SyntaxKind” dla swojego parametru typu „{0}”. W przeciwnym razie zarejestrowana akcja analizatora może nigdy nie zostać wywołana podczas analizy. + + + + Start action has no registered non-end actions + Akcja uruchamiania nie ma zarejestrowanych akcji niekończących + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + Element „{0}” nie rejestruje żadnych akcji analizatora. Rozważ przeniesienie akcji zarejestrowanych w elemencie „{1}”, które zależą od tej akcji uruchamiania, do „{0}”. + + + + Start action has no registered actions + Akcja uruchamiania nie ma zarejestrowanych akcji + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + Element „{0}” nie rejestruje żadnych akcji analizatora, chyba że dla elementu „{1}”. Rozważ zastąpienie tej pary akcji początek/koniec przez „{2}” lub przeniesienie akcji zarejestrowanych w elemencie „{3}”, które zależą od tej akcji uruchamiania, do elementu „{0}”. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Akcja uruchamiania analizatora umożliwia wykonanie analizy stanowej dla danej jednostki kodu, takiej jak blok kodu, kompilacja itd. Dokładne projektowanie jest niezbędne do uzyskania skutecznego wykonania analizatora bez przecieków pamięci. Podczas pisania takich analizatorów skorzystaj z poniższych wskazówek: +1. Określ nowy zakres dla zarejestrowanej akcji uruchamiania, być może za pomocą prywatnego typu zagnieżdżonego, do analizowania każdej jednostki kodu. +2. W razie potrzeby zdefiniuj i zainicjuj stan w akcji uruchamiania. +3. Zarejestruj co najmniej jedną akcję niekończącą, która odnosi się do tego stanu w akcji uruchamiania. Jeśli żadna taka akcja nie jest niezbędna, rozważ zastąpienie akcji uruchamiania przez akcję inną niż uruchamianie. Na przykład akcja CodeBlockStartAction bez zarejestrowanych akcji lub tylko zarejestrowana akcja CodeBlockEndAction powinny być zastąpione przez akcję CodeBlockAction. +4. W razie potrzeby zarejestruj akcję kończącą, aby zgłaszać diagnostykę na podstawie stanu końcowego. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Upewnij się, że do wydania analizatora dodano aktualny wpis identyfikatorów diagnostycznych analizatora. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + W regule „{0}” zmieniono wartość „Kategoria” lub „Ważność” w stosunku do ostatniego wydania. Przywróć aktualizacje w źródle lub dodaj nowy, aktualny wpis do niedostarczonego pliku wydania. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Upewnij się, że do wydania analizatora dodano aktualny wpis identyfikatorów diagnostyki analizatora + + + + Update rule entry in unshipped release file + Aktualizuj wpis reguły w niedostarczonym pliku wydania + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Rozważ udostępnienie umożliwiających lokalizację argumentów typu „{0}” dla konstruktora deskryptora diagnostycznego, aby zapewnić możliwość lokalizacji deskryptora + + + + Provide localizable arguments to diagnostic descriptor constructor + Zapewnij umożliwiające lokalizację argumenty dla konstruktora deskryptora diagnostycznego + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Jeśli analizator diagnostyczny i jego zgłaszana diagnostyka muszą umożliwiać lokalizację, wówczas obsługiwane deskryptory DiagnosticDescriptors użyte do konstruowania diagnostyki muszą również umożliwiać lokalizację. W takim przypadku umożliwiające lokalizację argumenty muszą zostać udostępnione dla parametru „title” (i opcjonalnie „description”) i konstruktora deskryptora diagnostycznego, aby zapewnić, że deskryptor umożliwia lokalizację. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Unikaj zapisywania danych poszczególnych kompilacji danych typu „{0}” w polach analizatora diagnostycznego + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Unikaj zapisywania danych poszczególnych kompilacji w polach analizatora diagnostycznego + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Wystąpienie analizatora diagnostycznego może istnieć dłużej niż czas życia kompilacji. Stąd przechowywanie danych poszczególnych kompilacji, takich jak symbole, w polach analizatora diagnostycznego może powodować przedłużenie istnienia nieaktualnych kompilacji i przecieki pamięci. Zamiast tego należy przechowywać te dane w oddzielnym typie, dla którego utworzono wystąpienie w akcji uruchamiania kompilacji, zarejestrowanym przy użyciu interfejsu API „{0}.{1}”. Wystąpienie tego typu zostanie utworzone dla poszczególnych kompilacji i nie będzie istnieć dłużej niż czas życia kompilacji, co pozwoli uniknąć przecieków pamięci. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + Autor niniejszego interfejsu nie przewidywał implementacji tego interfejsu przez podmioty trzecie i zastrzega sobie prawo do jego zmiany. Wdrożenie tego interfejsu może więc powodować problem ze zgodnością plików źródłowych lub binarnych z przyszłą wersją niniejszego interfejsu. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Typ {0} nie może wdrażać interfejsu {1}, ponieważ interfejs {1} nie jest dostępny do publicznego wdrażania + + + + Only internal implementations of this interface are allowed + Dozwolone są tylko wewnętrzne implementacje tego interfejsu + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Dostawca CodeFixProvider, który zamierza obsługiwać poprawki dla wszystkich wystąpień, musi sklasyfikować zarejestrowane akcje kodu do klas równoważności, przypisując im jawny, inny niż null klucz równoważności, który jest unikatowy dla każdego rodzaju akcji kodu utworzonego przez tę regułę naprawczą. Umożliwia to dostawcy FixAllProvider naprawę całej diagnostyki w wymaganym zakresie, stosując akcje kodu z tej reguły naprawczej, które są w klasie równoważności akcji wyzwalania kodu. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Podaj jawny argument dla opcjonalnego parametru „{0}”, który jest inny niż null i unikatowy dla każdego rodzaju akcji kodu utworzonego przez tę regułę naprawczą + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Utworzone akcje kodu powinny mieć unikatowy klucz EquivalenceKey do obsługi wystąpień FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + Element „{0}” ma wartość domyślną „null” dla właściwości „{1}”. Zastąp tę właściwość w elemencie „{0}”, aby zwrócić niezerową i unikatową wartość dla wszystkich akcji kodów na regułę naprawczą, albo użyj takiej akcji istniejącego kodu. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Użyj akcji kodu, które mają unikatowy klucz EquivalenceKey do obsługi wystąpień FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Wiele obiektów ujawnionych przez program Roslyn jest niezmienialnych. Wartość zwracana z wywołania metody dla tych obiektów nie powinna być ignorowana. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + Element „{0}” jest niezmienialny i element „{1}” nie będzie miał tutaj żadnego efektu. Rozważ użycie wartości zwracanej z elementu „{1}”. + + + + Do not ignore values returned by methods on immutable objects + Nie ignoruj wartości zwracanych przez metody w obiektach niezmiennych + + + + Code fix providers should provide FixAll support + Dostawcy poprawek kodu powinni dostarczać obsługę elementu FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + Element „{0}” rejestruje co najmniej jedną poprawkę kodu, ale nie przesłania metody „CodeFixProvider.GetFixAllProvider”. Przesłoń tę metodę i udostępnij element FixAllProvider o wartości innej niż null na potrzeby obsługi elementu FixAll, potencjalnie „WellKnownFixAllProviders.BatchFixer”, lub „null”, aby jawnie wyłączyć obsługę elementu FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Element CodeFixProvider powinien dostarczać obsługę elementu FixAll, aby umożliwić użytkownikom naprawienie wielu wystąpień diagnostyk bazowych za pomocą pojedynczej poprawki kodu. Zobacz dokumentację pod adresem https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md w celu uzyskania dalszych szczegółów. + + + + Override GetFixAllProvider. + Przesłoń element GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Rozważ dostarczenie wartości „helpLinkUri” innej niż null do konstruktora deskryptora diagnostyki w celu pokazania informacji, gdy ta diagnostyka pojawia się na liście błędów + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Dostarcz wartość „helpLinkUri” różną od null do konstruktora deskryptora diagnostyki + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + Wartość „helpLinkUri” służy do pokazywania informacji, gdy ta diagnostyka pojawia się na liście błędów. Każdy analizator powinien mieć określoną wartość helpLinkUri wskazującą stronę pomocy, która nie zmienia się w czasie. + + + + DiagnosticId for analyzers must be in specified format + Wartość DiagnosticId dla analizatorów musi mieć określony format + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + Identyfikator diagnostyczny „{0}” należący do kategorii „{1}” nie znajduje się w żądanym zakresie i/lub nie ma formatu „{2}” określonego w pliku „{3}” + + + + DiagnosticId for analyzers must be in specified format. + Wartość DiagnosticId dla analizatorów musi mieć określony format. + + + + DiagnosticId must be unique across analyzers + Wartość DiagnosticId musi być unikatowa między analizatorami + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + Identyfikator diagnostyczny „{0}” jest już używany przez analizator „{1}”. Użyj innego identyfikatora diagnostycznego. + + + + DiagnosticId must be unique across analyzers. + Wartość DiagnosticId musi być unikatowa między analizatorami. + + + + Category for analyzers must be from the specified values + Kategoria dla analizatora musi być jedną z określonych wartości + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + Kategoria „{0}” nie jest jedną z dozwolonych kategorii określonych w pliku „{1}” + + + + Category for analyzers must be from the specified values. + Kategoria dla analizatora musi być jedną z określonych wartości. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Nieprawidłowy wpis w pliku specyfikacji zakresu kategorii analizatora i identyfikatora diagnostycznego + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Nieprawidłowy wpis „{0}” w pliku specyfikacji zakresu kategorii analizatora i identyfikatora diagnostycznego „{1}” + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Nieprawidłowy wpis w pliku specyfikacji zakresu kategorii analizatora i identyfikatora diagnostycznego. + + + + DiagnosticId for analyzers must be a non-null constant + Element DiagnosticId dla analizatorów musi być stałą o wartości innej niż null + + + + Diagnostic Id for rule '{0}' must be a non-null constant + Identyfikator diagnostyczny dla reguły „{0}” musi być stałą o wartości innej niż null + + + + DiagnosticId for analyzers must be a non-null constant. + Element DiagnosticId dla analizatorów musi być stałą o wartości innej niż null. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Typy analizatora diagnostycznego nie mogą używać typów z zestawów Workspaces. Zestawy Workspaces są dostępne tylko wtedy, gdy analizator działa w ramach analizy na żywo w programie Visual Studio IDE, ale nie są dostępne podczas kompilacji wiersza polecenia. Odwołania do typów z zestawów Workspaces będą powodować wyjątek środowiska uruchomieniowego podczas wykonywania analizatora podczas kompilacji wiersza polecenia. + + + + Do not use types from Workspaces assembly in an analyzer + Nie używaj typów z zestawu Workspaces w analizatorze + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Zmień typ analizatora diagnostycznego „{0}”, aby usunąć cały bezpośredni i/lub pośredni dostęp do typów „{1}”, który pozwala uzyskać dostęp do typów „{2}” + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Zmień typ analizatora diagnostycznego „{0}”, aby usunąć cały bezpośredni dostęp do typów „{1}” + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + Klasa MSBuildWorkspace została przeniesiona do pakietu Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet i istnieją zmiany interfejsu API powodujące niezgodność. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Uaktualnij klasę MSBuildWorkspace przez dodanie odwołania do pakietu Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet. Zobacz https://go.microsoft.com/fwlink/?linkid=874285, aby uzyskać szczegółowe informacje na temat pomyślnego korzystania z klasy MSBuildWorkspace. + + + + Upgrade MSBuildWorkspace + Uaktualnianie klasy MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf new file mode 100644 index 0000000000000..71e68a469a2c4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.pt-BR.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + O "DiagnosticDescriptor" atribuído ao campo é usado para relatar um diagnóstico final de compilação, mas o construtor "DiagnosticDescriptor" usado para inicializá-lo não passa na marca personalizada necessária "CompilationEnd". Confira a documentação de "WellKnownDiagnosticTags.CompilationEnd" para obter detalhes. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Adicionar a marca personalizada "CompilationEnd" ao descritor de diagnóstico usado para inicializar o campo "{0}" como ele é usado para relatar um diagnóstico final de compilação + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Adicionar a marca personalizada "CompilationEnd" ao descritor de diagnóstico final de compilação + + + + Add rule entry to unshipped release file + Adicionar uma entrada de regra ao arquivo de versão não enviado + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Herdar o tipo '{0}' de DiagnosticAnalyzer ou remover os DiagnosticAnalyzerAttributes + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Os tipos marcados com DiagnosticAnalyzerAttributes devem ser herdados de DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Usar um ' SymbolEqualityComparer ' para a comparação de símbolos + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Os símbolos devem ser comparados quanto à igualdade, não quanto à identidade. Use uma sobrecarga que aceite um 'IEqualityComparer' e passe 'SymbolEqualityComparer'. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Os símbolos devem ser comparados quanto à igualdade, não quanto à identidade. Uma chamada explícita para 'GetHashCode' provavelmente resultará no comportamento errado. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Use ' SymbolEqualityComparer ' ao comparar símbolos + + + + Symbols should be compared for equality + Os símbolos devem ser comparados quanto à igualdade + + + + Configure generated code analysis + Configurar análise de código gerado + + + + Configure generated code analysis + Configurar análise de código gerado + + + + Configure generated code analysis + Configurar análise de código gerado + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Todas as IDs de diagnóstico do analisador com suporte devem fazer parte de uma versão do analisador. + + + + Rule '{0}' is not part of any analyzer release + A regra '{0}' não faz parte de nenhuma versão do analisador + + + + Add analyzer diagnostic IDs to analyzer release + Adicione as IDs de diagnóstico do analisador à versão do analisador + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + A descrição do diagnóstico deve ter uma ou várias frases que terminam com um sinal de pontuação e não deve ter espaços em branco à esquerda nem à direita + + + + Define diagnostic description correctly + Definir a descrição de diagnóstico corretamente + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + A mensagem de diagnóstico não deve conter nenhum caractere de retorno de linha nem espaços em branco à esquerda ou à direita e deve ser uma frase única sem um ponto à direita ou várias frases com um ponto à direita + + + + Define diagnostic message correctly + Definir a mensagem de diagnóstico corretamente + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + O título do diagnóstico não deve conter um ponto nem outros caracteres de retorno de linha nem espaços em branco à esquerda ou à direita + + + + Define diagnostic title correctly + Definir o título de diagnóstico corretamente + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensão de compilador C# não deve ser implementada em um assembly contendo uma referência a Microsoft.CodeAnalysis.VisualBasic. O assembly Microsoft.CodeAnalysis.VisualBasic nem sempre é fornecido durante os cenários de compilação C#, portanto, as referências a ele podem fazer com que a extensão do compilador se comporte de forma imprevisível. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Essa extensão do compilador não deve ser implementada em um assembly com estrutura de destino ''{0}''. As referências a outras estruturas de destino fará com que o compilador se comporte de forma imprevisível. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Os tipos que implementam pontos de extensão do compilador devem ser declarados somente em assemblies direcionados a netstandard2.0. As estruturas de destino mais específicas só estão disponíveis em um subconjunto de cenários de compilação com suporte, portanto, direcioná-los pode fazer com que o recurso se comporte de forma imprevisível. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + As extensões do compilador devem ser implementadas em assemblies direcionados ao netstandard2.0 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensão de compilador não deve ser implementada em um assembly contendo uma referência a Microsoft.CodeAnalysis.Workspaces. O assembly Microsoft.CodeAnalysis.Workspaces não é fornecido durante cenários de compilação de linha de comando, portanto, referências a ele podem fazer com que a extensão do compilador se comporte de forma imprevisível. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Esta extensão de compilador do Visual Basic não deve ser implementada em um assembly contendo uma referência a Microsoft.CodeAnalysis.CSharp. O assembly Microsoft.CodeAnalysis.CSharp nem sempre é fornecido durante os cenários de compilação do Visual Basic, portanto, referências a ele podem fazer com que a extensão do compilador tenha um comportamento imprevisível. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Tipos que implementam pontos de extensão do compilador não devem ser declarados em assemblies que contenham referências a assemblies que não são fornecidos por todos os cenários de compilação. Isso pode fazer com que o recurso tenha um comportamento imprevisível. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + As extensões do compilador devem ser implementadas em assemblies com referências fornecidas pelo compilador + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel' é um método caro para ser invocado em um analisador de diagnóstico porque ele cria um modelo semântico completamente novo, que não compartilha dados de compilação com o compilador ou com outros analisadores. Isso gera um custo de desempenho adicional durante a análise semântica. Nesse caso, considere o registro de uma ação do analisador diferente que permita o uso de um 'SemanticModel' compartilhado, como 'RegisterOperationAction', 'RegisterSyntaxNodeAction' ou 'RegisterSemanticModelAction'. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Não invocar o método Compilation.GetSemanticModel() em um analisador de diagnóstico + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Não invocar o método Compilation.GetSemanticModel() em um analisador de diagnóstico + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + A DiagnosticId dos analisadores não deve usar IDs reservadas. + + + + '{0}' is a reserved diagnostic ID + '{0}' é uma ID de diagnóstico reservada + + + + Do not use reserved diagnostic IDs + Não use IDs de diagnóstico reservadas + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + A habilitação do acompanhamento de versão dos pacotes do analisador ajuda a acompanhar e documentar o diagnóstico do analisador que é fornecido e/ou alterado com cada versão do analisador. Veja os detalhes em https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Habilitar o acompanhamento de versão do analisador para o projeto do analisador que contém a regra '{0}' + + + + Enable analyzer release tracking + Habilitar o acompanhamento de versão do analisador + + + + Enable concurrent execution + Habilitar execução simultânea + + + + Enable concurrent execution + Habilitar execução simultânea + + + + Enable concurrent execution + Habilitar execução simultânea + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Entrada inválida no arquivo de versão do analisador. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + O arquivo de versão do analisador '{0}' tem uma entrada inválida '{1}' + + + + Invalid entry in analyzer release file + Entrada inválida no arquivo de versão do analisador + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + O arquivo de versão do analisador '{0}' tem um cabeçalho de versão inválido ou ausente '{1}' + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + O arquivo de versão do analisador '{0}' tem uma entrada '{1}' inválida sem uma versão anterior enviada para a regra '{2}'. Nesse caso, adicione uma entrada '{1}' separada para a regra no arquivo de versão não enviado. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + O arquivo de versão do analisador '{0}' tem uma entrada com um ou mais campos 'Não detectados' que precisam ser preenchidos manualmente em '{1}' + + + + Missing '{0}' attribute + Atributo '{0}' ausente + + + + Missing diagnostic analyzer attribute + Atributo do analisador de diagnóstico ausente + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Subtipos não abstratos de DiagnosticAnalyzer devem ser marcados com DiagnosticAnalyzerAttribute(s). O argumento para estes atributos, se houver, determina as linguagens com suporte para o analisador. Os tipos de analisador sem este atributo serão ignorados pelo mecanismo de análise. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Talvez o analisador de diagnóstico '{0}' possa dar suporte a C# e a Visual Basic. Considere adicionar um argumento a DiagnosticAnalyzerAttribute para obter suporte à linguagem '{1}'. + + + + Recommend adding language support to diagnostic analyzer + Recomende adicionar suporte à linguagem ao analisador de diagnóstico + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + O analisador de diagnóstico é marcado como dando suporte a apenas uma linguagem, mas o assembly do analisador não parece referenciar nenhum assembly CodeAnalysis específico a uma linguagem e, portanto, é provável que ele funcione para mais de uma linguagem. Considere adicionar um argumento de linguagem adicional a DiagnosticAnalyzerAttribute. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Aplique o atributo DiagnosticAnalyzer para '{0}'. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Aplique o atributo DiagnosticAnalyzer para '{0}' e para '{1}'. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Especifique pelo menos um SymbolKind de interesse ao registrar uma ação de analisador de símbolo + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Especifique pelo menos um SyntaxKind de interesse ao registrar uma ação de analisador de nó de sintaxe + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Especifique pelo menos um OperationKind de interesse ao registrar uma ação do analisador de operações + + + + Missing kind argument when registering an analyzer action + Argumento de variante ausente ao registrar uma ação do analisador + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + É necessário especificar pelo menos um tipo de sintaxe, de símbolo ou de operação ao registrar uma ação de analisador de sintaxe, símbolo ou operação, respectivamente. Caso contrário, a ação registrada nunca será invocada durante a análise. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Um projeto que contém analisadores ou geradores de origem deve especificar a propriedade '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': um projeto que contém analisadores ou geradores de origem deve especificar a propriedade '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + Specify analyzer banned API enforcement setting + Especificar a configuração de imposição de API banida pelo analisador + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Preferir 'syntax.IsKind(kind)' em vez de 'syntax.Kind() == kind' ao verificar os tipos de sintaxe. O código que usa 'IsKind' é um pouco mais eficiente no runtime, portanto o uso consistente deste formulário, nas circunstâncias em que ele se aplica, ajuda a aprimorar o desempenho em cenários de análise complexos. + + + + Use 'IsKind' instead of 'Kind' + Usar 'IsKind' em vez de 'Kind' + + + + Prefer 'IsKind' for checking syntax kinds + Preferir 'IsKind' para verificar os tipos de sintaxe + + + + Prefer 'IsKind' for checking syntax kinds + Preferir 'IsKind' para verificar os tipos de sintaxe + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + O valor 'customTags' é usado como uma maneira de habilitar as ações e os filtros específicos nos descritores de diagnóstico com base nos valores específicos das marcas. Todo analisador de Roslyn deve ter pelo menos uma marca da classe 'WellKnownDiagnosticTags'. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Considere fornecer um 'customTags' não nulo ao construtor de descritor de diagnóstico para habilitar a filtragem de metadados de descritores de diagnóstico + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Forneça um valor de 'customTags' não nulo para o construtor de descritor de diagnóstico + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Remova as entradas duplicadas da ID de diagnóstico entre as versões do analisador. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + A regra '{0}' tem uma entrada duplicada entre a versão '{1}' e a versão '{2}' + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Remova as entradas duplicadas da ID de diagnóstico entre as versões do analisador + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Remova as entradas duplicadas da ID de diagnóstico na mesma versão do analisador. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + A regra '{0}' tem mais de uma entrada para a versão '{1}' no arquivo de versão do analisador '{2}' + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Remova as entradas duplicadas da ID de diagnóstico na mesma versão do analisador + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + As IDs de diagnóstico enviadas que não são mais relatadas devem ter uma entrada na tabela 'Regras Removidas' no arquivo não enviado. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + A regra '{0}' foi enviada na versão do analisador '{1}', mas não é mais um diagnóstico compatível com nenhum analisador. Adicione uma entrada para essa regra em uma tabela 'Regras Removidas' ao arquivo não enviado. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + As IDs de diagnóstico enviadas que não são mais relatadas devem ter uma entrada na tabela 'Regras Removidas' no arquivo não enviado + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + As entradas das IDs de diagnóstico do analisador que não são mais relatadas e nunca são enviadas podem ser removidas da versão do analisador não enviada. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + A regra '{0}' faz parte da próxima versão do analisador não enviada, mas não é um diagnóstico compatível com nenhum analisador + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Não adicione as IDs de diagnóstico do analisador removidas à versão do analisador não enviada + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Chamar “SemanticModel.GetDeclaredSymbol” em determinados tipos herdados de “SyntaxNode”, por exemplo, “GlobalStatementSyntax” e “IncompleteMemberSyntax” sempre retornará “null”. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Chamar “SemanticModel.GetDeclaredSymbol” com um argumento do tipo “FieldDeclarationSyntax” ou “EventFieldDeclarationSyntax” sempre retornará “null”. Em vez disso, chame “GetDeclaredSymbol” com os declaradores de variável do campo. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Uma chamada para “SemanticModel.GetDeclaredSymbol({0})” sempre retornará “null” + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Uma chamada para “SemanticModel.GetDeclaredSymbol({0})” sempre retornará “null” + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Essa chamada para “SemanticModel.GetDeclaredSymbol()” sempre retornará “null” + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + O símbolo foi marcado como proibido para uso em analisadores. É necessário usar um símbolo alternativo. + + + + The symbol '{0}' is banned for use by analyzers{1} + O símbolo '{0}' está proibido para uso pelos analisadores{1} + + + + Do not use APIs banned for analyzers + Não usar APIs proibidas para analisadores + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + As IDs de diagnóstico marcadas como removidas no arquivo de versão do analisador não devem ser relatadas pelos analisadores. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + A regra '{0}' está marcada como removida na última versão do analisador, mas ainda está sendo relatada + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + As IDs de diagnóstico marcadas como removidas no arquivo de versão do analisador não devem ser relatadas pelos analisadores + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + Não há suporte para SymbolKind '{0}' para ações de analisador de símbolo + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Argumento SymbolKind sem suporte ao registrar uma ação de analisador de símbolo + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic invocado com um DiagnosticDescriptor '{0}' sem suporte + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic invocado com um DiagnosticDescriptor sem suporte + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic só deve ser invocado com DiagnosticDescriptors com suporte retornados da propriedade DiagnosticAnalyzer.SupportedDiagnostics. Caso contrário, o diagnóstico reportado será filtrado pelo mecanismo de análise. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + O argumento de tipo '{0}' para o parâmetro de tipo '{1}' do método '{2}' não é uma enumeração SyntaxKind + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Argumento de tipo inválido para o método Register de DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Os métodos Register específicos a um idioma do DiagnosticAnalyzer, como RegisterSyntaxNodeAction, RegisterCodeBlockStartAction e RegisterCodeBlockEndAction esperam um argumento de tipo 'SyntaxKind' específico a um idioma para o parâmetro de tipo '{0}' dele. Caso contrário, a ação do analisador registrada nunca pode ser invocada durante a análise. + + + + Start action has no registered non-end actions + A ação inicial não tem ações não finais registradas + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' não registra nenhuma ação do analisador. Considere mover ações registradas em '{1}' que dependem desta ação inicial para '{0}'. + + + + Start action has no registered actions + A ação inicial não tem ações registradas + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}' não registra nenhuma ação do analisador, exceto por um '{1}'. Considere substituir este par de ações iniciais/finais com um '{2}' ou mover as ações registradas em '{3}' que dependem dessa ação inicial para '{0}'. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Uma ação inicial do analisador permite realizar análises com estado em uma determinada unidade de código, como um bloco de código, compilação etc. É necessário projetar cuidadosamente para realizar a execução eficiente do analisador sem perdas de memória. Use as diretrizes a seguir para gravar esses analisadores: +1. Defina um novo escopo para a ação inicial registrada, possivelmente com um tipo aninhado privado para analisar cada unidade de código. +2. Se necessário, defina e inicialize o estado na ação inicial. +3. Registre pelo menos uma ação não final que referencia esse estado na ação inicial. Se nenhuma ação assim for necessária, considere substituir a ação inicial por uma ação não inicial. Por exemplo, um CodeBlockStartAction com ações não registradas ou apenas um CodeBlockEndAction registrado deve ser substituído por um CodeBlockAction. +4. Se necessário, registre uma ação final para relatar o diagnóstico com base no estado final. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Verifique se a entrada atualizada das IDs de diagnóstico do analisador foi adicionada à versão do analisador. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + A regra '{0}' tem uma 'Categoria' ou uma 'Severidade' alterada em relação à última versão. Reverta as atualizações na origem ou adicione uma nova entrada atualizada ao arquivo de versão não enviado. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Verifique se a entrada atualizada das IDs de diagnóstico do analisador foi adicionada à versão do analisador + + + + Update rule entry in unshipped release file + Atualizar a entrada de regra no arquivo de versão não enviado + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Considere fornecer argumentos localizáveis do tipo '{0}' para o construtor de descritor de diagnóstico para garantir que o descritor seja localizável + + + + Provide localizable arguments to diagnostic descriptor constructor + Forneça argumentos localizáveis para o construtor de descritor de diagnóstico + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Se seu analisador de diagnóstico e o respectivo diagnóstico reportado precisarem ser localizáveis, então o DiagnosticDescriptors com suporte usado para construir o diagnóstico também deverá ser localizável. Em caso afirmativo, os argumentos localizáveis deverão ser fornecidos do "título" (e opcionalmente para a "descrição") de parâmetro para o construtor de descritor de diagnóstico para garantir que o descritor seja localizável. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Evite armazenar dados por compilação do tipo '{0}' nos campos de um analisador de diagnóstico + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Evite armazenar dados por compilação nos campos de um analisador de diagnóstico + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + A instância de um analisador de diagnóstico pode sobreviver ao tempo de vida de compilação. Portanto, armazenar dados por compilação, como símbolos, nos campos de um analisador de diagnóstico pode fazer compilações obsoletas permanecerem ativas e causar perdas de memória. Em vez disso, você deve armazenar esses dados em um tipo separado cuja instância foi criada em uma ação inicial de compilação, registrada usando a API do '{0}.{1}'. Uma instância desse tipo será criada por compilação e não sobreviverá ao tempo de vida da compilação, evitando, portanto, perdas de memória. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + O autor dessa interface não pretendia ter implementações de terceiros dessa interface e reserva-se o direito de alterá-la. Implementar essa interface poderia, portanto, resultar em um problema de compatibilidade de origem ou binária com uma versão futura dessa interface. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + O tipo {0} não pode implementar a interface {1}, porque {1} não está disponível para implementação pública + + + + Only internal implementations of this interface are allowed + São permitidas apenas implementações internas desta interface + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Um CodeFixProvider que pretende dar suporte à correção de todas as ocorrências deve classificar as ações de código registradas em classes de equivalência, atribuindo a ela uma chave de equivalência explícita e não nula, exclusiva para cada tipo de ação de código criada por este reparador. Isso permite que o FixAllProvider corrija todos os diagnósticos no escopo necessário aplicando ações de código desse reparador que estão na classe de equivalência da ação de código do gatilho. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Forneça um argumento explícito para o parâmetro '{0}' opcional, que é exclusivo e não nulo para cada tipo de ação de código criada por este reparador + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Criar ações de código deve ter um EquivalenceKey exclusivo para o suporte a ocorrências FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}' tem o valor padrão de 'null' para a propriedade '{1}'. Substitua essa propriedade em '{0}' para retornar um valor único e não nulo em todas as ações de código por reparador ou use essa ação de código existente. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Use ações de código que têm um EquivalenceKey exclusivo para o suporte a ocorrências FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Muitos objetos expostos pelo Roslyn são imutáveis. O valor retornado de uma invocação de método nesses objetos não deve ser ignorado. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' é imutável e '{1}' não terá nenhum efeito nele. Considere usar o valor retornado de '{1}'. + + + + Do not ignore values returned by methods on immutable objects + Não ignore valores retornados por métodos em objetos imutáveis. + + + + Code fix providers should provide FixAll support + Provedores de consertos (fix) de código devem dar suporte a FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' registra uma ou mais correções de código, mas não substitui o método 'CodeFixProvider.GetFixAllProvider'. Substitua este método e forneça um FixAllProvider não nulo para suporte a FixAll, potencialmente 'WellKnownFixAllProviders.BatchFixer' ou 'null' para desabilitar explicitamente o suporte a FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + Um CodeFixProvider deve dar suporte a FixAll para permitir que os usuários corrijam várias instâncias do diagnóstico subjacente com apenas uma correção de código. Confira a documentação em https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md para obter mais detalhes. + + + + Override GetFixAllProvider. + Substitua GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Considere fornecer um 'helpLinkUri' não nulo para o construtor de descritor de diagnóstico para mostrar informações quando este diagnóstico aparece na lista de erros + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Forneça um valor de 'helpLinkUri' não nulo para o construtor de descritor de diagnóstico + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + O valor de 'helpLinkUri' é usado para mostrar informações quando esse diagnóstico aparece na lista de erros. Todo analisador deve ter um helpLinkUri especificado que aponta para uma página de ajuda que não é alterada com o passar do tempo. + + + + DiagnosticId for analyzers must be in specified format + A DiagnosticId para analisadores precisa estar no formato especificado + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + A ID de diagnóstico '{0}' pertencente à categoria '{1}' não está no intervalo e/ou no formato necessário '{2}' especificado no arquivo '{3}' + + + + DiagnosticId for analyzers must be in specified format. + A DiagnosticId para analisadores deve estar no formato especificado. + + + + DiagnosticId must be unique across analyzers + A DiagnosticId precisa ser exclusiva entre os analisadores + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + A ID de diagnóstico '{0}' já está sendo usada pelo analisador '{1}'. Use uma ID de diagnóstico diferente. + + + + DiagnosticId must be unique across analyzers. + A DiagnosticId deve ser exclusiva entre os analisadores. + + + + Category for analyzers must be from the specified values + A categoria dos analisadores precisa ser um dos valores especificados + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + A categoria '{0}' não é uma das categorias permitidas especificadas no arquivo '{1}' + + + + Category for analyzers must be from the specified values. + A categoria dos analisadores deve ser um dos valores especificados. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Entrada inválida no arquivo de especificação de intervalo de ID de diagnóstico e de categoria do analisador + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Entrada inválida '{0}' no arquivo de especificação de intervalo de ID de diagnóstico e de categoria do analisador '{1}' + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Entrada inválida no arquivo de especificação de intervalo de ID de diagnóstico e de categoria do analisador. + + + + DiagnosticId for analyzers must be a non-null constant + A DiagnosticId para analisadores precisa ser uma constante não nula + + + + Diagnostic Id for rule '{0}' must be a non-null constant + A ID de diagnóstico para a regra '{0}' precisa ser uma constante não nula + + + + DiagnosticId for analyzers must be a non-null constant. + A DiagnosticId para analisadores deve ser uma constante não nula. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Os tipos do analisador de diagnóstico não devem usar tipos de Assemblies de workspaces. Esses Assemblies estão disponíveis apenas quando o analisador é executado na análise dinâmica do IDE do Visual Studio, mas não estão disponíveis durante o build da linha de comando. Referenciar tipos de Assemblies de workspaces levará a uma exceção de tempo de execução durante a execução do analisador no build de linha de comando. + + + + Do not use types from Workspaces assembly in an analyzer + Não use tipos de Assembly de workspaces em um analisador + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Altere o tipo de analisador de diagnóstico '{0}' para remover todos os acessos diretos e/ou indiretos aos tipos '{1}', que acessam os tipos '{2}' + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Altere o tipo de analisador de diagnóstico '{0}' para remover todos os acessos diretos aos tipos '{1}' + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + O MSBuildWorkspace foi movido para o pacote do NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild e há alterações da falha da API. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Atualize o MSBuildWorkspace adicionando uma referência de pacote ao pacote Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet. Confira https://go.microsoft.com/fwlink/?linkid=874285 para obter detalhes de como usar o MSBuildWorkspace com sucesso. + + + + Upgrade MSBuildWorkspace + Atualizar o MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf new file mode 100644 index 0000000000000..cc37c6bd5e8bb --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.ru.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + Дескриптор "DiagnosticDescriptor", назначенный полю, используется для передачи диагностических данных окончания компиляции, но конструктор "DiagnosticDescriptor" не передал необходимый настраиваемый тег "CompilationEnd". Дополнительные сведения см. в документации по "WellKnownDiagnosticTags.CompilationEnd". + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Добавить настраиваемый тег "CompilationEnd" к дескриптору диагностики, используемому для инициализации поля "{0}", поскольку он передает диагностические данные окончания компиляции + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Добавить настраиваемый тег "CompilationEnd" к дескриптору диагностики окончания компиляции + + + + Add rule entry to unshipped release file + Добавить запись правила в неотправленный файл выпуска + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + Наследуйте тип "{0}" от DiagnosticAnalyzer или удалите DiagnosticAnalyzerAttribute. + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + Типы, помеченные как DiagnosticAnalyzerAttribute, должны наследовать от DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Используйте "SymbolEqualityComparer" для сравнения символов + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Символы должны сравниваться на равенство, а не на идентичность. Используйте перегрузку, принимающую "IEqualityComparer", и передайте "SymbolEqualityComparer". + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Символы должны сравниваться на равенство, а не на идентичность. Явный вызов "GetHashCode" может привести к неправильному поведению. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Используйте "SymbolEqualityComparer" при сравнении символов + + + + Symbols should be compared for equality + Символы следует сравнивать на равенство + + + + Configure generated code analysis + Настройка анализа созданного кода + + + + Configure generated code analysis + Настройка анализа созданного кода + + + + Configure generated code analysis + Настройка анализа созданного кода + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Все поддерживаемые идентификаторы диагностики анализатора должны быть частью выпуска анализатора. + + + + Rule '{0}' is not part of any analyzer release + Правило "{0}" не входит ни в один из выпусков анализатора. + + + + Add analyzer diagnostic IDs to analyzer release + Добавьте идентификаторы диагностики анализатора в выпуск анализатора + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + Описание диагностики должно представлять собой одно или несколько предложений, заканчивающихся знаком препинания, и не должно содержать начальные или конечные пробелы. + + + + Define diagnostic description correctly + Укажите описание диагностики правильно + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Диагностическое сообщение не должно содержать символы возврата каретки либо начальные или конечные пробелы и должно представлять собой одно предложение без конечной точки или несколько предложений с конечной точкой. + + + + Define diagnostic message correctly + Укажите сообщение диагностики правильно + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Заголовок диагностики не должен содержать точку, любой символ возврата каретки, а также начальные или конечные пробелы. + + + + Define diagnostic title correctly + Укажите название диагностики правильно + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Это расширение компилятора C# не следует реализовывать в сборке, содержащей ссылку на Microsoft.CodeAnalysis.VisualBasic. Сборка Microsoft.CodeAnalysis.VisualBasic не всегда предоставляется в сценариях компиляции C#, поэтому ссылки на нее могут привести к непредсказуемому поведению расширения компилятора. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Это расширение компилятора не должно быть реализовано в сборке с требуемой версией .NET Framework "{0}". Ссылки на другие требуемые версии .NET Framework приведут к непредсказуемому поведению компилятора. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Типы, реализующие точки расширения компилятора, следует объявлять только в сборках, ориентированных на netstandard2.0. Более конкретные целевые платформы доступны только в подмножестве поддерживаемых сценариев компиляции, поэтому их нацеливание может привести к непредсказуемому поведению функции. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Расширения компилятора должны быть реализованы в сборках, ориентированных на netstandard2.0. + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Это расширение компилятора не следует реализовывать в сборке, содержащей ссылку на Microsoft.CodeAnalysis.Workspaces. Сборка Microsoft.CodeAnalysis.Workspaces не предоставляется в сценариях компиляции из командной строки, поэтому ссылки на нее могут привести к непредсказуемому поведению расширения компилятора. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Это расширение компилятора Visual Basic не следует реализовывать в сборке, содержащей ссылку на Microsoft.CodeAnalysis.CSharp. Сборка Microsoft.CodeAnalysis.CSharp не всегда предоставляется в сценариях компиляции Visual Basic, поэтому ссылки на нее могут привести к непредсказуемому поведению расширения компилятора. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Типы, реализующие точки расширения компилятора, не следует объявлять в сборках, содержащих ссылки на сборки, которые не предоставляются всеми сценариями компиляции. Это может привести к непредсказуемому поведению функции. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Расширения компилятора должны быть реализованы в сборках с предоставленными компилятором ссылками. + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + "GetSemanticModel" является затратным методом для вызова в диагностическом анализаторе, так как при этом создается совершенно новая семантическая модель, которая не использует данные компиляции совместно с компилятором или другими анализаторами. Это приводит к снижению производительности во время семантического анализа. Вместо этого попробуйте зарегистрировать другое действие анализатора, которое позволит использовать общую модель "SemanticModel", например "RegisterOperationAction", "RegisterSyntaxNodeAction" или "RegisterSemanticModelAction". + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Не вызывайте метод Compilation.GetSemanticModel() в диагностическом анализаторе. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Не вызывайте метод Compilation.GetSemanticModel() в диагностическом анализаторе + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + Идентификатор диагностики DiagnosticId для анализатора не должен включать зарезервированные идентификаторы. + + + + '{0}' is a reserved diagnostic ID + "{0}" является зарезервированным идентификатором диагностики. + + + + Do not use reserved diagnostic IDs + Запрет использования зарезервированных диагностических идентификаторов + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Включение отслеживания выпуска для пакетов анализатора помогает отслеживать и документировать диагностические сведения анализатора, поставляемые и (или) изменяемые с каждым выпуском анализатора. Дополнительные сведения см. на странице https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + Включить отслеживание выпуска анализатора для проекта анализатора, содержащего правило "{0}" + + + + Enable analyzer release tracking + Включить отслеживание выпуска анализатора + + + + Enable concurrent execution + Включение параллельного выполнения + + + + Enable concurrent execution + Включение параллельного выполнения + + + + Enable concurrent execution + Включение параллельного выполнения + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Недопустимая запись в файле выпуска анализатора. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + Файл выпуска анализатора "{0}" содержит недопустимую запись "{1}" + + + + Invalid entry in analyzer release file + Недопустимая запись в файле выпуска анализатора + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + В файле выпуска анализатора "{0}" отсутствует заголовок выпуска или указан недопустимый заголовок выпуска "{1}" + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + В файле выпуска анализатора "{0}" указана недопустимая запись "{1}" без ранее отправленного выпуска для правила "{2}". Вместо этого добавьте отдельную запись "{1}" для правила в неотправленном файле выпуска. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + В файле выпуска анализатора "{0}" содержится запись с одним или несколькими полями "Не обнаружено", которые необходимо заполнить вручную в "{1}". + + + + Missing '{0}' attribute + Отсутствует атрибут "{0}". + + + + Missing diagnostic analyzer attribute + Отсутствует атрибут диагностического анализатора + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Неабстрактные подтипы DiagnosticAnalyzer нужно помечать с помощью атрибутов DiagnosticAnalyzerAttribute. Аргумент этих атрибутов (при его наличии) определяет поддерживаемые анализатором языки. Типы анализатора без такого атрибута будут проигнорированы модулем анализа. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Диагностический анализатор "{0}" может поддерживать как C#, так и Visual Basic. Рекомендуется добавить в DiagnosticAnalyzerAttribute аргумент для поддержки языка "{1}". + + + + Recommend adding language support to diagnostic analyzer + Рекомендация добавить поддержку языка в диагностический анализатор + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Для диагностического анализатора указана поддержка только одного языка, однако сборка анализатора не ссылается на какие-либо языковые сборки CodeAnalysis, поэтому, вероятнее всего, она будет работать с несколькими языками. Рекомендуется добавить в DiagnosticAnalyzerAttribute аргумент дополнительного языка. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + Примените атрибут DiagnosticAnalyzer для "{0}". + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Примените атрибут DiagnosticAnalyzer для "{0}" и "{1}". + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Укажите по меньшей мере один нужный SymbolKind при регистрации действия анализатора для символов. + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Укажите по меньшей мере один нужный SyntaxKind при регистрации действия анализатора для синтаксических узлов. + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Укажите по меньшей мере один нужный OperationKind при регистрации действия анализатора для операций. + + + + Missing kind argument when registering an analyzer action + Отсутствует аргумент типа при регистрации действия анализатора + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Вам следует указать по меньшей мере один вид синтаксиса, символа или операции при регистрации соответствующего действия анализатора. В противном случае зарегистрированное действие не будет вызвано во время анализа. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Проект, содержащий анализаторы или исходные генераторы, должен указывать свойство "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>" + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + "{0}": проект, содержащий анализаторы или исходные генераторы, должен указывать свойство "<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>" + + + + Specify analyzer banned API enforcement setting + Укажите параметр принудительного применения API, запрещенного для анализатора + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + При проверке видов синтаксиса предпочтение отдается "syntax.IsKind(kind)" вместо "syntax.Kind() == kind". Код с использованием "IsKind" имеет чуть большее быстродействие во время выполнения, поэтому регулярное применение этого формата где возможно помогает повысить производительность в сложных сценариях анализа. + + + + Use 'IsKind' instead of 'Kind' + Использование "IsKind" вместо "Kind" + + + + Prefer 'IsKind' for checking syntax kinds + Предпочтение "IsKind" для проверки видов синтаксиса + + + + Prefer 'IsKind' for checking syntax kinds + Предпочтение "IsKind" для проверки видов синтаксиса + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + Значение параметра "customTags" используется для включения определенных действий и фильтров для диагностических дескрипторов на основе конкретных значений тегов. У каждого анализатора Roslyn должен быть по крайней мере один тег из класса "WellKnownDiagnosticTags". + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Рекомендуется передать в конструктор дескриптора диагностики значение параметра "customTags", отличное от NULL, чтобы включить фильтрацию метаданных дескрипторов диагностики. + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Передача значения параметра "customTags", отличного от NULL, в конструктор дескриптора диагностики + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Удалите повторяющиеся записи идентификаторов диагностики в разных выпусках анализатора. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + Правило "{0}" содержит повторяющуюся запись в выпусках "{1}" и "{2}". + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Удалите повторяющиеся записи идентификаторов диагностики в разных выпусках анализатора + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Удалите повторяющиеся записи идентификаторов диагностики в одном и том же выпуске анализатора. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + Правило "{0}" содержит несколько записей для выпуска "{1}" в файле выпуска анализатора "{2}". + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Удалите повторяющиеся записи идентификаторов диагностики в одном и том же выпуске анализатора + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Для отправленных идентификаторов диагностики, которые больше не включены в отчет, должны присутствовать записи в таблице "Удаленные правила" в неотправленном файле. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + Правило "{0}" было отправлено в выпуске анализатора "{1}", но больше не поддерживается в качестве диагностики ни для одного из анализаторов. Добавьте запись для этого правила в таблице "Удаленные правила" в неотправленном файле. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Для отправленных идентификаторов диагностики, которые больше не включены в отчет, должны присутствовать записи в таблице "Удаленные правила" в неотправленном файле. + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Записи для идентификаторов диагностики анализатора, которые больше не включены в отчет и не были отправлены, можно удалить из неотправленного выпуска анализатора. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + Правило "{0}" является частью следующего неотправленного выпуска анализатора, но не поддерживается в качестве диагностики ни для одного из анализаторов. + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Не добавляйте удаленные идентификаторы диагностики анализатора в неотправленный выпуск анализатора + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + Вызов "SemanticModel.GetDeclaredSymbol" с определенными типами, унаследованными от "SyntaxNode", например "GlobalStatementSyntax" и "IncompleteMemberSyntax", всегда возвращает "null". + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + Вызов "SemanticModel.GetDeclaredSymbol" с аргументом типа "FieldDeclarationSyntax" или "EventFieldDeclarationSyntax" всегда будет возвращать "null". Вместо этого вызовите "GetDeclaredSymbol" с операторами объявления переменных из поля. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Вызов в "SemanticModel.GetDeclaredSymbol({0})" всегда возвращает "null" + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + Вызов в "SemanticModel.GetDeclaredSymbol({0})" всегда возвращает "null" + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + Этот вызов в "SemanticModel.GetDeclaredSymbol()" всегда возвращает "null" + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Символ был помечен как запрещенный к использованию в анализаторах. Вместо него следует использовать альтернативный вариант. + + + + The symbol '{0}' is banned for use by analyzers{1} + Символ "{0}" запрещен к использованию анализаторами{1} + + + + Do not use APIs banned for analyzers + Не использовать API, запрещенные для анализаторов + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Идентификаторы диагностики, помеченные как удаленные в файле выпуска анализатора, не должны отображаться в отчетах анализаторов. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + Правило "{0}" помечено как удаленное в последнем выпуске анализатора, но по-прежнему отображается в отчете. + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Идентификаторы диагностики, помеченные как удаленные в файле выпуска анализатора, не должны отображаться в отчетах анализаторов + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + SymbolKind "{0}" не поддерживается для действий анализатора для символов. + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Неподдерживаемый аргумент SymbolKind при регистрации действия анализатора для символов + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic вызван с помощью неподдерживаемого DiagnosticDescriptor "{0}". + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic вызван с помощью неподдерживаемого DiagnosticDescriptor + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic следует вызывать только с помощью поддерживаемых DiagnosticDescriptor, возвращаемых свойством DiagnosticAnalyzer.SupportedDiagnostics. В противном случае модуль анализа отфильтрует зарегистрированные данные диагностики. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + Аргумент типа "{0}" для параметра типа "{1}" метода "{2}" не является перечислением SyntaxKind. + + + + Invalid type argument for DiagnosticAnalyzer's Register method + Недопустимый аргумент типа для метода Register DiagnosticAnalyzer + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + Языковые методы Register DiagnosticAnalyzer, такие как RegisterSyntaxNodeAction, RegisterCodeBlockStartAction и RegisterCodeBlockEndAction, ожидают языковой аргумент типа "SyntaxKind" для параметра типа "{0}". В противном случае зарегистрированное действие анализатора невозможно будет вызвать во время анализа. + + + + Start action has no registered non-end actions + Начальное действие не содержит зарегистрированные неконечные действия + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + "{0}" не регистрирует никакие действия анализатора. Рекомендуется переместить действия, зарегистрированные в "{1}", которые зависят от этого начального действия, в "{0}". + + + + Start action has no registered actions + Начальное действие не содержит зарегистрированные действия + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + "{0}" не регистрирует никакие действия анализатора, кроме "{1}". Рекомендуется заменить это начальное/конечное действие на "{2}" или переместить действия, зарегистрированные в "{3}", которые зависят от этого начального действия, в "{0}". + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Начальное действие анализатора позволяет выполнить анализ с отслеживанием состояния для заданного модуля кода, например блока кода, компиляции и т. д. Чтобы обеспечить эффективное выполнение анализатора без утечек памяти, нужно тщательно все спроектировать. При создании подобных анализаторов используйте следующие рекомендации: +1. Определите новую область для зарегистрированного начального действия, возможно, с закрытым вложенным типом, для анализа каждого из модулей кода. +2. При необходимости определите и инициализируйте состояние в начальном действии. +3. Зарегистрируйте по меньшей мере одно неконечное действие, ссылающееся на это состояние в начальном действии. Если подобное действие не требуется, рекомендуется заменить начальное действие неначальным. Например, CodeBlockStartAction без зарегистрированных действий или с единственным зарегистрированным действием CodeBlockEndAction следует заменить на CodeBlockAction. +4. При необходимости зарегистрируйте конечное действие, чтобы предоставлять диагностические сведения на основе конечного состояния. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Убедитесь, что в выпуск анализатора добавлена обновленная запись для идентификаторов диагностики анализатора. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + Категория или уровень серьезности для правила "{0}" были изменены с последнего выпуска. Отмените изменения в источнике или добавьте обновленную запись в неотправленный файл выпуска. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Убедитесь, что в выпуск анализатора добавлена последняя версия записи для идентификаторов диагностики анализатора. + + + + Update rule entry in unshipped release file + Обновить запись правила в неотправленном файле выпуска + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Рекомендуется предоставить конструктору дескриптора диагностики локализуемые аргументы типа "{0}", чтобы дескриптор можно было локализовать. + + + + Provide localizable arguments to diagnostic descriptor constructor + Предоставление конструктору дескриптора диагностики локализуемых аргументов + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Чтобы предусмотреть возможность локализации для вашего диагностического анализатора и предоставляемых им сведений, нужно сделать локализуемыми и поддерживаемые DiagnosticDescriptor, используемые для создания диагностики. Для этого нужно предоставить локализуемые аргументы для параметра "title" (и дополнительно для "description") в конструкторе дескриптора диагностики, чтобы сделать дескриптор локализуемым. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Не храните данные типа "{0}" об отдельных компиляциях в полях диагностического анализатора. + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Нежелательность хранения данных об отдельных компиляциях в полях диагностического анализатора + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Экземпляр диагностического анализатора может сохраняться и после истечения времени существования компиляции. Поэтому хранение данных об отдельных компиляциях, например символов, в полях диагностического анализатора может привести к тому, что устаревшие компиляции останутся активными и вызовут утечки памяти. Вместо этого такие данные следует хранить в отдельном типе, экземпляр которого создается в начальном действии компиляции, и зарегистрировать с помощью API "{0}.{1}". Экземпляр этого типа создается для каждой компиляции и прекращает действовать до истечения ее времени существования, что позволяет предотвратить утечки памяти. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + Разработчик этого интерфейса не предусматривал его сторонние реализации и оставляет за собой право изменять его. Поэтому реализация этого интерфейса может привести к проблемам совместимости на уровне исходного или двоичного кода с последующими версиями интерфейса. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + Тип {0} не может реализовать интерфейс {1}, так как {1} недоступен для общей реализации. + + + + Only internal implementations of this interface are allowed + Разрешение только внутренних реализаций этого интерфейса + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + CodeFixProvider, намеревающемуся поддерживать случаи полного исправления, нужно классифицировать зарегистрированные действия кода по классам эквивалентности, назначив явный и отличный от NULL ключ эквивалентности, который является уникальным для всех видов действий кода, созданных этим исправлением. Это позволяет FixAllProvider исправить всю диагностику в требуемой области, применив действия кода из этого исправления, входящие в класс эквивалентности для действия кода триггера. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + Укажите явный аргумент для необязательного параметра "{0}", который отличен от NULL и уникален для всех видов действий кода, созданных этим исправлением. + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Действия создания кода должны иметь уникальный EquivalenceKey, чтобы поддерживать FixAll + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + "{0}" имеет значение по умолчанию "Null" для свойства "{1}". Переопределите это свойство в "{0}", чтобы возвратить отличное от Null и уникальное значение для всех действий кода, относящихся к одному исправлению, или используйте уже существующее действие кода. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + Использование действий кода, имеющих уникальный EquivalenceKey, для поддержки FixAll + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Многие из предоставляемых Roslyn объектов являются неизменяемыми. Не игнорируйте значение, возвращаемое из вызова метода для этих объектов. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + "{0}" является неизменяемым, поэтому "{1}" не окажет на него никакого влияния. Рекомендуется использовать значение, возвращаемое "{1}". + + + + Do not ignore values returned by methods on immutable objects + Не игнорировать значения, возвращаемые методами для неизменяемых объектов + + + + Code fix providers should provide FixAll support + Поставщики исправлений кода должны предоставлять поддержку FixAll + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + "{0}" регистрирует одно исправление кода или несколько, но не переопределяет метод "CodeFixProvider.GetFixAllProvider". Переопределите этот метод и предоставьте отличный от NULL FixAllProvider для обеспечения поддержки FixAll, возможно "WellKnownFixAllProviders.BatchFixer", либо задайте значение NULL, чтобы явно отключить поддержку FixAll. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider должен предоставлять поддержку FixAll, чтобы пользователи могли исправить несколько экземпляров базовых диагностических данных с помощью одного исправления кода. Дополнительные сведения см. в документации по адресу https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + + + + Override GetFixAllProvider. + Переопределите GetFixAllProvider. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Рекомендуется предоставить конструктору дескриптора диагностики отличное от NULL значение "helpLinkUri", чтобы отображать информацию, когда эти диагностические данные появляются в списке ошибок. + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Передача значения параметра "helpLinkUri", отличного от NULL, в конструктор дескриптора диагностики + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + Значение "helpLinkUri" используется для отображения информации при появлении этих диагностических сведений в списке ошибок. Для каждого анализатора должен быть задан helpLinkUri, указывающий на страницу справки, которая не меняется с течением времени. + + + + DiagnosticId for analyzers must be in specified format + Диагностический идентификатор для анализаторов должен иметь заданный формат + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + Диагностический идентификатор "{0}", относящийся к категории "{1}", не соответствует требуемому диапазону и (или) формату "{2}", указанному в файле "{3}". + + + + DiagnosticId for analyzers must be in specified format. + Диагностический идентификатор для анализаторов должен иметь заданный формат. + + + + DiagnosticId must be unique across analyzers + Анализаторы должны иметь уникальный диагностический идентификатор + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + Диагностический идентификатор "{0}" уже используется анализатором "{1}". Задайте другой идентификатор. + + + + DiagnosticId must be unique across analyzers. + Анализаторы должны иметь уникальный диагностический идентификатор. + + + + Category for analyzers must be from the specified values + Категория для анализаторов должна иметь одно из заданных значений + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + Категория "{0}" не относится к разрешенным категориям, заданным в файле "{1}". + + + + Category for analyzers must be from the specified values. + Категория для анализаторов должна иметь одно из заданных значений. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Недопустимая запись в файле спецификации с категориями анализаторов и диапазоном диагностических идентификаторов + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Недопустимая запись "{0}" в файле "{1}" с категориями анализаторов и диапазоном диагностических идентификаторов. + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Недопустимая запись в файле с категориями анализаторов и диапазоном диагностических идентификаторов. + + + + DiagnosticId for analyzers must be a non-null constant + Диагностический идентификатор для анализаторов должен быть константой, отличной от NULL + + + + Diagnostic Id for rule '{0}' must be a non-null constant + Диагностический идентификатор для правила "{0}" должен быть константой, отличной от NULL. + + + + DiagnosticId for analyzers must be a non-null constant. + Диагностический идентификатор для анализаторов должен быть константой, отличной от NULL. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Типы диагностического анализатора не должны использовать типы из сборок рабочих областей. Сборки рабочих областей доступны только при запуске анализатора в интерактивном анализе среды Visual Studio, но они недоступны во время сборки из командной строки. Ссылки на типы из сборок рабочих областей приведут к возникновению исключения в среде выполнения при запуске анализаторе в сборке командной строки. + + + + Do not use types from Workspaces assembly in an analyzer + Не используйте в анализаторе типы из сборки рабочих областей + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + Измените тип диагностического анализатора "{0}" и удалите из него все возможности прямого и непрямого доступа к типам "{1}", которые обращаются к типам "{2}" + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + Измените тип диагностического анализатора "{0}" и удалите из него все возможности прямого доступа к типам "{1}" + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + Класс MSBuildWorkspace перемещен в пакет NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild, и в API внесены существенные изменения. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Обновите MSBuildWorkspace, добавив ссылку на пакет NuGet Microsoft.CodeAnalysis.Workspaces.MSBuild. Дополнительные сведения об использовании MSBuildWorkspace: https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + Обновление MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf new file mode 100644 index 0000000000000..9426ebc687cd4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.tr.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + Alana atanan 'DiagnosticDescriptor', bir derleme bitiş tanılamasını rapor etmek için kullanılır ancak bunu başlatmak için kullanılan 'DiagnosticDescriptor' oluşturucusu, gerekli "CompilationEnd" özel etiketinde geçmez. Ayrıntılar için 'WellKnownDiagnosticTags.CompilationEnd' belgesine bakın. + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + Derleme sonu tanılamasını rapor etmek için kullanıldığından, '{0}' alanını başlatmak için kullanılan tanılama tanımlayıcısına "CompilationEnd" özel etiketi ekleyin + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + Derleme sonu tanılama tanımlayıcısına "CompilationEnd" özel etiketi ekleme + + + + Add rule entry to unshipped release file + Gönderilmeyen yayın dosyasına kural girişi ekle + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + DiagnosticAnalyzer'dan '{0}' türünü devralın veya DiagnosticAnalyzerAttribute'ları kaldırın + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + DiagnosticAnalyzerAttribute'lar ile işaretlenen türler DiagnosticAnalyzer'dan devralınmalıdır + + + + Use a 'SymbolEqualityComparer' for symbol comparison + Sembol karşılaştırması için 'SymbolEqualityComparer' kullanın + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + Semboller kimlik değil, eşitlik bakımından karşılaştırılmalıdır. 'IEqualityComparer' kabul eden bir aşırı yükleme kullanın ve 'SymbolEqualityComparer' geçirin. + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + Semboller kimlik değil, eşitlik bakımından karşılaştırılmalıdır. 'GetHashCode'a yönelik açık çağrı büyük olasılıkla yanlış davranışa neden olur. + + + + Use 'SymbolEqualityComparer' when comparing symbols + Sembolleri karşılaştırırken 'SymbolEqualityComparer' kullanın + + + + Symbols should be compared for equality + Semboller eşitlik bakımından karşılaştırılmalıdır. + + + + Configure generated code analysis + Oluşturulan kod analizini yapılandır + + + + Configure generated code analysis + Oluşturulan kod analizini yapılandır + + + + Configure generated code analysis + Oluşturulan kod analizini yapılandır + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + Tüm desteklenen çözümleyici tanılama kimlikleri bir çözümleyici yayınının parçası olmalıdır. + + + + Rule '{0}' is not part of any analyzer release + '{0}' kuralı herhangi bir çözümleyici yayınının parçası değil + + + + Add analyzer diagnostic IDs to analyzer release + Çözümleyici yayınına çözümleyici tanılama kimlikleri ekleyin + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + Tanılama açıklaması, noktalama işaretiyle biten bir veya birkaç cümle olmalı ve başında veya sonunda boşluk olmamalıdır + + + + Define diagnostic description correctly + Tanılama açıklamasını doğru tanımlayın + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + Tanılama iletisi, satır başı karakteri veya başta ya da sonda boşluk içermemeli ve sonunda nokta olmayan tek bir cümle ya da sonunda nokta olan birden çok cümle olmalıdır + + + + Define diagnostic message correctly + Tanılama iletisini doğru tanımlayın + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + Tanılama başlığı nokta, satır başı karakteri veya başta ya da sonda boşluk içermemelidir + + + + Define diagnostic title correctly + Tanılama başlığını doğru tanımlayın + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Bu C# derleyici uzantısı, Microsoft.CodeAnalysis.VisualBasic başvurusu içeren bir derlemede uygulanmamalıdır. Microsoft.CodeAnalysis.VisualBasic derlemesi, C# derleme senaryoları sırasında her zaman sağlanmaz, bu nedenle ona yapılan başvurular derleyici uzantısının beklenmedik şekilde davranmasına neden olabilir. + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + Bu derleyici uzantısı, '{0}' hedef çerçevesine sahip bir derlemede uygulanmamalıdır. Diğer hedef çerçevelere yapılan başvurular derleyicinin öngörülemeyen şekilde davranmasına neden olur. + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + Derleyici uzantı noktalarını uygulayan türler yalnızca netstandard2.0'ı hedefleyen derlemelerde tanımlanabilir. Daha belirli hedef çerçeveler yalnızca desteklenen derleme senaryolarının bir alt kümesinde kullanılabilir, bu nedenle bunları hedeflemek özelliğin öngörülemeyen şekilde davranmasına neden olabilir. + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + Derleyici uzantıları, netstandard2.0’ı hedefleyen derlemelerde uygulanmalıdır. + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Bu derleyici uzantısı, Microsoft.CodeAnalysis.Çalışma Alanları başvurusunu içeren bir derlemede uygulanmamalıdır. Microsoft.CodeAnalysis.Çalışma Alanları derlemesi, komut satırı derleme senaryoları sırasında sağlanmaz, dolayısıyla buna yapılan başvurular, derleyici uzantısının beklenmedik şekilde davranmasına neden olabilir. + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + Bu Visual Basic derleyici uzantısı, Microsoft.CodeAnalysis.CSharp başvurusunu içeren bir derlemede uygulanmamalıdır. Microsoft.CodeAnalysis.CSharp derlemesi, Visual Basic derleme senaryoları sırasında her zaman sağlanmaz, bu nedenle derlemeye yapılan başvurular derleyici uzantısının beklenmedik şekilde davranmasına neden olabilir. + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + Derleyici uzantı noktalarını uygulayan türler, tüm derleme senaryoları tarafından sağlanmayan derlemelere başvurular içeren derlemelerde bildirilmemelidir. Bunu yapmak, özelliğin öngörülemeyen şekilde davranmasına neden olabilir. + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + Derleyici uzantıları, derleyici tarafından sağlanan referanslara sahip derlemelerde uygulanmalıdır. + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 'GetSemanticModel', derleme verilerini derleyici veya diğer çözümleyiciler ile paylaşmayan tamamen yeni bir anlam modeli oluşturduğundan bir tanılama çözümleyicisi içinde çağırmak için pahalı bir yöntemdir. Bu işlem, anlamsal analiz sırasında ek bir performans maliyetine neden olur. Bunun yerine, 'RegisterOperationAction', 'RegisterSyntaxNodeAction' veya 'RegisterSemanticModelAction' gibi paylaşılan bir 'SemanticModel' kullanılmasına izin veren farklı bir çözümleyici eylemini kaydetmeyi deneyin. + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Bir tanılama çözümleyicisi içinde Compilation.GetSemanticModel() yöntemini çağırma + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + Bir tanılama çözümleyicisi içinde Compilation.GetSemanticModel() yöntemini çağırma + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + Çözümleyiciler için DiagnosticId, ayrılmış kimlikler kullanmamalıdır. + + + + '{0}' is a reserved diagnostic ID + '{0}', ayrılmış bir tanılama kimliğidir + + + + Do not use reserved diagnostic IDs + Ayrılmış tanılama kimlikleri kullanmayın + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + Çözümleyici paketleri için yayın izlemeyi etkinleştirme, her çözümleyici yayınıyla birlikte gelen ve/veya değişen çözümleyici tanılamalarını izlemeye ve belgelemeye yardımcı olur. https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md adresindeki ayrıntılara bakın. + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + '{0}' kuralını içeren çözümleyici projesi için çözümleyici yayını izlemeyi etkinleştir + + + + Enable analyzer release tracking + Çözümleyici yayını izlemeyi etkinleştir + + + + Enable concurrent execution + Eşzamanlı yürütmeyi etkinleştir + + + + Enable concurrent execution + Eşzamanlı yürütmeyi etkinleştir + + + + Enable concurrent execution + Eşzamanlı yürütmeyi etkinleştir + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + Çözümleyici yayın dosyasında geçersiz girdi. + + + + Analyzer release file '{0}' has an invalid entry '{1}' + '{0}' çözümleyici yayın dosyasında geçersiz '{1}' girişi var + + + + Invalid entry in analyzer release file + Çözümleyici yayın dosyasındaki giriş geçersiz + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + '{0}' çözümleyici yayın dosyası, eksik veya geçersiz bir yayın üst bilgisine ('{1}') sahip + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + '{0}' çözümleyici yayın dosyası, '{2}' kuralı için önceden gönderilmiş bir yayın içermeyen geçersiz bir '{1}' girdisine sahip. Bunun yerine, gönderilmeyen yayın dosyasındaki kural için ayrı bir '{1}' girdisi ekleyin. + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + '{0}' çözümleyici yayın dosyasında, '{1}' içinde el ile doldurulması gereken en az bir 'Algılanmamış' alan içeren bir giriş var + + + + Missing '{0}' attribute + '{0}' özniteliği eksik + + + + Missing diagnostic analyzer attribute + Tanılama çözümleyicisi özniteliği eksik + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + Soyut olmayan DiagnosticAnalyzer alt türleri DiagnosticAnalyzerAttribute(s) ile işaretlenmelidir. Bu özniteliklerin bağımsız değişkenleri, varsa, çözümleyici için desteklenen dilleri belirler. Bu özniteliğe sahip olmayan çözümleyici türleri çözümleme altyapısı tarafından yoksayılır. + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + Tanılama çözümleyicisi '{0}' hem C# hem de Visual Basic’i destekleyebilir. '{1}' dil desteği için DiagnosticAnalyzerAttribute’a bir bağımsız değişken eklemeyi düşünün. + + + + Recommend adding language support to diagnostic analyzer + Tanılama çözümleyicisine dil desteği eklemeniz önerilir + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + Tanılama çözümleyicisi yalnızca bir dili destekliyor olarak işaretlenmiş ancak çözümleyici bütünleştirilmiş kodu herhangi bir dile özgü CodeAnalysis bütünleştirilmiş koduna başvuruyor gibi görünmediğinden birden çok dil için çalışması olasıdır. DiagnosticAnalyzerAttribute’a bir ek dil bağımsız değişkeni eklemeyi düşünün. + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + '{0}' için DiagnosticAnalyzer özniteliğini uygulayın. + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + Hem '{0}' hem de '{1}' için DiagnosticAnalyzer özniteliğini uygulayın. + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + Bir sembol çözümleyicisi eylemi kaydedilirken en az bir ilgili SymbolKind belirtin + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + Bir söz dizimi düğümü çözümleyicisi eylemi kaydedilirken en az bir ilgili SyntaxKind belirtin + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + Bir işlem çözümleyicisi eylemi kaydedilirken en az bir ilgili OperationKind belirtin + + + + Missing kind argument when registering an analyzer action + Bir çözümleyici eylemi kaydedilirken tür bağımsız değişkeni eksik + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + Bir söz dizimi, sembol veya işlem çözümleyicisi eylemi eklerken en az bir söz dizimi, sembol veya işlem türü belirtmeniz gerekir. Aksi takdirde, kayıtlı eylem çözümleme sırasında hiçbir zaman çağrılmaz. + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + Çözümleyiciler veya kaynak oluşturucular içeren bir proje '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' özelliğini belirtilmelidir. + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': Çözümleyiciler veya kaynak oluşturucular içeren bir proje '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' özelliğini belirtilmelidir + + + + Specify analyzer banned API enforcement setting + Çözümleyici yasaklı API zorlama ayarını belirtin + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + Söz dizimi çeşitleri denetlenirken 'syntax.IsKind(kind)' türünü 'syntax.Kind() == kind' türüne tercih edin. 'IsKind' kullanan kod, çalışma zamanında biraz daha etkilidir, bu nedenle bu formu kullanmak, karmaşık analiz senaryolarında performansın artırılmasına yardımcı olur. + + + + Use 'IsKind' instead of 'Kind' + 'Kind' yerine 'IsKind' kullanın + + + + Prefer 'IsKind' for checking syntax kinds + Söz dizimi türlerini denetlemek için 'IsKind' öğesini tercih et + + + + Prefer 'IsKind' for checking syntax kinds + Söz dizimi türlerini denetlemek için 'IsKind' öğesini tercih et + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + 'customTags' değeri, etiketlerin belirli değerlerine dayalı olarak, tanılama tanımlayıcılarında belirli eylemleri ve filtreleri etkinleştirmek için kullanılır. Her Roslyn çözümleyicisinin 'WellKnownDiagnosticTags' sınıfından en az bir etiketi olmalıdır. + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + Tanılama tanımlayıcılarının meta veri filtrelemesini etkinleştirmek için tanılama tanımlayıcısı oluşturucusuna null olmayan bir 'customTags' değeri sağlamayı düşünün + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + Tanılama tanımlayıcısı oluşturucusuna null olmayan 'customTags' değeri sağlayın + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + Çözümleyici yayınları arasında tanılama kimliği için yinelenen girdileri kaldırın. + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + '{0}' kuralı, '{1}' yayını ile '{2}' yayını arasında yinelenen giriş içeriyor + + + + Remove duplicate entries for diagnostic ID between analyzer releases + Çözümleyici yayınları arasında tanılama kimliği için yinelenen girişleri kaldır + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + Aynı çözümleyici yayınında tanılama kimliği için yinelenen girdileri kaldırın. + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + '{0}' kuralı, '{1}' yayını için '{2}' çözümleyici yayın dosyasında birden fazla giriş içeriyor + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + Aynı çözümleyici yayınında tanılama kimliği için yinelenen girişleri kaldır + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + Artık bildirilmeyen gönderilmiş tanılama kimliklerinin, gönderilmeyen dosyada 'Kaldırılan Kurallar' tablosunda bir girdisi olmalıdır. + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + '{0}' kuralı '{1}' çözümleyici yayınında gönderildi, ancak artık herhangi bir çözümleyici için desteklenen bir tanılama değil. Gönderilmeyen dosyada, 'Kaldırılan kurallar' tablosunda bu kural için bir girdi ekleyin. + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + Artık bildirilmeyen gönderilmiş tanılama kimliklerinin, gönderilmeyen dosyada bulunan 'Kaldırılan Kurallar' tablosunda bir girişi olmalıdır + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + Artık bildirilmeyen ve hiç gönderilmemiş çözümleyici tanılama kimliklerine yönelik girdiler, gönderilmeyen çözümleyici yayınından kaldırılabilir. + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + '{0}' kuralı, sonraki gönderilmemiş çözümleyici yayınının parçası ancak herhangi bir çözümleyici için desteklenen bir tanılama değil + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + Kaldırılmış çözümleyici tanılama kimliklerini gönderilmeyen çözümleyici sürümüne ekleme + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + 'GlobalStatementSyntax' ve 'IncompleteMemberSyntax' gibi 'SyntaxNode' öğesinden devralınan belirli türlerde 'SemanticModel.GetDeclaredSymbol' çağrısı her zaman 'null' döndürür. + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + 'FieldDeclarationSyntax' veya 'EventFieldDeclarationSyntax' bağımsız değişken türüyle 'SemanticModel.GetDeclaredSymbol' çağrısı yapmak her zaman 'null' döndürür. Bunun yerine, alandan değişken bildirimciler ile 'GetDeclaredSymbol' çağrısı yapın. + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})' çağrısı her zaman 'null' döndürür + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 'SemanticModel.GetDeclaredSymbol({0})' çağrısı her zaman 'null' döndürür + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + 'SemanticModel.GetDeclaredSymbol()' çağrısı her zaman 'null' döndürür + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + Sembol, çözümleyicilerde kullanılması yasaklı olarak işaretlendiğinden bunun yerine başka bir sembol kullanılması gerekiyor. + + + + The symbol '{0}' is banned for use by analyzers{1} + '{0}' sembolünün {1} çözümleyicileri tarafından kullanılması yasaklandı + + + + Do not use APIs banned for analyzers + Çözümleyiciler için yasaklanan API'leri kullanmayın + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + Çözümleyici yayın dosyasında kaldırılmış olarak işaretlenen tanılama kimlikleri çözümleyiciler tarafından bildirilmemelidir. + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + '{0}' kuralı en son çözümleyici yayınında kaldırılmış olarak işaretlenmiş ancak hala bildiriliyor + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + Çözümleyici yayın dosyasında kaldırılmış olarak işaretlenen tanılama kimlikleri çözümleyiciler tarafından bildirilmemelidir + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + '{0}' SymbolKind, sembol çözümleyicisi eylemleri için desteklenmiyor + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + Sembol çözümleyicisi eylemi kaydedilirken SymbolKind bağımsız değişkeni desteklenmiyor + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + ReportDiagnostic desteklenmeyen '{0}' DiagnosticDescriptor ile çağrıldı + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + ReportDiagnostic desteklenmeyen bir DiagnosticDescriptor ile çağrıldı + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + ReportDiagnostic yalnızca DiagnosticAnalyzer.SupportedDiagnostics özelliğinden döndürülen desteklenen DiagnosticDescriptors ile çağrılmalıdır. Aksi takdirde, raporlanan tanılama çözümleme altyapısı tarafından filtrelenir. + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + '{2}' metodunun '{1}' tür parametresi için '{0}' tür bağımsız değişkeni bir SyntaxKind sabit listesi değil + + + + Invalid type argument for DiagnosticAnalyzer's Register method + DiagnosticAnalyzer’ın Register metodu için tür bağımsız değişkeni geçersiz + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + DiagnosticAnalyzer’ın dile özgü Register yöntemleri (örneğin RegisterSyntaxNodeAction, RegisterCodeBlockStartAction ve RegisterCodeBlockEndAction) '{0}' tür parametresi için dile özgü bir 'SyntaxKind' tür bağımsız değişkeni bekliyor. Aksi takdirde, kayıtlı çözümleyici eylemi çözümleme sırasında hiçbir zaman çağrılamaz. + + + + Start action has no registered non-end actions + Başlangıç eyleminin kayıtlı bitiş eylemi olmayan eylemi yok + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' herhangi bir çözümleyici eylemini kaydetmez. Bu başlangıç eylemine bağımlı olan '{1}' içinde kayıtlı eylemleri '{0}' içine taşımayı düşünün. + + + + Start action has no registered actions + Başlangıç eyleminin kayıtlı eylemi yok + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + '{0}' bir '{1}' hariç herhangi bir çözümleyici eylemini kaydetmez. Bu başlangıç/bitiş eylem çiftini '{2}' ile eşlemeyi veya bu başlangıç eylemine bağımlı olan '{3}' içinde kayıtlı eylemleri '{0}' içine taşımayı düşünün. + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + Bir çözümleyici başlatma eylemi bir kod bloğu, derleme vb. belirli kod birimi üzerinde durum bilgisi olan çözümleme gerçekleştirmeye olanak sağlar. Bellek sızıntısı olmadan verimli çözümleyici yürütmesi sağlamak için dikkatli tasarım gereklidir. Bu tür çözümleyiciler yazmak için aşağıdaki kılavuzu kullanın: +1. Muhtemelen her kod birimini çözümlemek için özel bir iç içe geçmiş tür ile kayıtlı başlatma eylemi için yeni bir kapsam tanımlayın. +2. Gerekirse, durumu başlangıç eyleminde tanımlayın ve başlatın. +3. Başlangıç eyleminde bu duruma başvuran en az bir bitiş eylemi olmayan eylem kaydedin. Böyle bir eyleme gerek yoksa, başlangıç eylemini başlangıç eylemi olmayan bir eylemle değiştirmeyi düşünün. Örneğin, kayıtlı eylemi olmayan veya yalnızca kayıtlı bir CodeBlockEndAction’ı olan CodeBlockStartAction, CodeBlockAction ile değiştirilmelidir. +4. Gerekirse, son durumu temel alan tanılamaları raporlamak için bir bitiş eylemi kaydedin. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + Çözümleyici tanılama kimlikleri için güncel girdilerin çözümleyici yayınına eklendiğinden emin olun. + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + '{0}' kuralı, son yayından sonra değiştirilen bir 'Kategori' veya 'Önem Derecesi' içeriyor. Kaynaktaki güncelleştirmeleri geri alın veya gönderilmeyen yayın dosyasına yeni bir güncel girdi ekleyin. + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + Çözümleyici tanılama kimlikleri için çözümleyici yayınına güncel girişlerin eklendiğinden emin olun + + + + Update rule entry in unshipped release file + Gönderilmeyen yayın dosyasındaki kural girdisini güncelleştir + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + Tanımlayıcının yerelleştirilebilir olduğundan emin olmak için tanılama tanımlayıcısına '{0}' türünde yerelleştirilebilir bağımsız değişkenler sağlamayı düşünün + + + + Provide localizable arguments to diagnostic descriptor constructor + Tanılama tanımlayıcısı oluşturucusuna yerelleştirilebilir bağımsız değişkenler sağlayın + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + Tanılama çözümleyicinizin ve raporlanan tanılamalarının yerelleştirilebilir olması gerekiyorsa, tanılamaları oluşturmak için kullanılan desteklenen DiagnosticDescriptors’ın da yerelleştirilebilir olması gerekir. Öyleyse, tanımlayıcının yerelleştirilebilir olduğundan emin olmak için tanılama tanımlayıcısı oluşturucusuna 'title' (ve isteğe bağlı olarak 'description') parametresi için yerelleştirilebilir bağımsız değişkenlerin sağlanması gerekir. + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + Tanılama çözümleyicisinin alanlarına '{0}' türünde derleme başına veriler depolamaktan kaçının + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + Tanılama çözümleyicisinin alanlarına derleme başına veriler depolamaktan kaçının + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + Bir tanılama çözümleyicisi, derlemenin ömrüne daha uzun süre kullanılabilir. Bu nedenle, semboller gibi derleme başına simgelerin bir tanılama çözümleyicisinin alanlarında depolanması, eski derlemelerin kullanılmaya devam ederek bellek sızıntıları oluşturmasına neden olabilir. Bunun yerine, bu verileri '{0}.{1}' API’si kullanılarak kaydedilmiş bir derleme başlatma eylemi içinde örneklenen ayrı bir tür üzerinde depolamanız gerekir. Derleme başına bu türde bir örnek oluşturulur ve derlemenin ömründen daha uzun süre kullanılmayarak bellek sızıntıları önlenir. + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + Bu arabirimin yazarı bu arabirimin üçüncü taraf uygulamalarının oluşturulmasını planlamadı ve arabirimi değiştirme hakkını saklı tutuyor. Bu nedenle, bu arabirimin uygulanması bu arabirimin gelecekteki bir sürümüyle kaynak veya uyumluluk sorunlarıyla sonuçlanabilir. + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + {1} genel uygulama için kullanılabilir olmadığından {0} türü {1} arabirimini uygulayamıyor + + + + Only internal implementations of this interface are allowed + Bu arabirimin yalnızca içeride yapılan uygulamalarına izin verilir + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + Tüm örnekleri düzeltme desteği sunmayı hedefleyen bir CodeFixProvider’ın, kayıtlı kod eylemlerine bu düzeltici tarafından oluşturulan her kod eylemi türü için benzersiz olan açık, null olmayan bir eşdeğerlik anahtarı atayarak bu kod eylemlerini eşdeğerlik sınıflarına ayırması gerekir. Bu, FixAllProvider’ın bu düzelticiden tetikleyici kod eyleminin eşdeğerlik sınıfında olan kod eylemlerini uygulayarak gerekli kapsamdaki tüm tanılamaları düzeltmesine olanak sağlar. + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + İsteğe bağlı '{0}' parametresi için, null olmayan ve bu düzeltici tarafından oluşturulan her kod eylemi için benzersiz olan bir açık bağımsız değişken sağlayın + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + Kod oluşturma eylemlerinin FixAll oluşumları desteği için benzersiz bir EquivalenceKey’e sahip olması gerekir + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}' '{1}' özelliği için 'null' varsayılan değerine sahip. Null olmayan ve tüm kod eylemleri arasında benzersiz bir değer döndürmek için '{0}' üzerindeki bu özelliği geçersiz kılın veya buna benzer mevcut bir kod eylemi kullanın. + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + FixAll oluşumları desteği için benzersiz bir EquivalenceKey’e sahip kod eylemleri kullanın + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Roslyn tarafından açığa çıkarılan birçok nesne sabittir. Bu nesneler üzerinde bir yöntem çağırmadan dönüş değeri yoksayılmamalıdır. + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' sabittir ve '{1}' bu öğeyi etkilemez. '{1}' öğesinden dönüş değerini kullanmayı düşünün. + + + + Do not ignore values returned by methods on immutable objects + Sabit nesnelerdeki yöntemler tarafından döndürülen değerleri yok saymama + + + + Code fix providers should provide FixAll support + Kod düzeltme sağlayıcıları FixAll desteği sağlamalıdır + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' bir veya daha fazla kod düzeltmesi kaydediyor ancak 'CodeFixProvider.GetFixAllProvider' metodunu geçersiz kılmıyor. Bu metodu geçersiz kılın ve FixAll desteği için 'WellKnownFixAllProviders.BatchFixer' gibi null olmayan bir FixAllProvider sağlayın veya FixAll desteğini açıkça devre dışı bırakmak için 'null' sağlayın. + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider, kullanıcıların tek bir kod düzeltmesiyle temel tanılamanın birden çok örneğini düzeltebilmesi için FixAll desteği sağlamalıdır. Daha fazla ayrıntı için https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md adresindeki belgelere bakın. + + + + Override GetFixAllProvider. + GetFixAllProvider’ı geçersiz kılın. + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + Bu tanılama, hata listesinde görüntülendiğinde bilgileri göstermek için tanılama tanımlayıcısı oluşturucusuna null olmayan bir 'helpLinkUri' sağlamayı düşünün + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + Tanılama tanımlayıcısı oluşturucusuna null olmayan 'helpLinkUri' değeri sağlayın + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + 'helpLinkUri' değeri, bu tanılama hata listesinde olduğunda bilgi görüntülemek için kullanılır. Her çözümleyici için zaman içinde değişmeyen bir yardım sayfasına işaret eden bir helpLinkUri belirtilmelidir. + + + + DiagnosticId for analyzers must be in specified format + Çözümleyiciler için DiagnosticId belirtilen biçimde olmalıdır + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + '{1}' kategorisine ait '{0}' tanılama kimliği, '{3}' dosyasında belirtilen gerekli '{2}' aralığında ve/veya biçiminde değil + + + + DiagnosticId for analyzers must be in specified format. + Çözümleyiciler için DiagnosticId belirtilen biçimde olmalıdır. + + + + DiagnosticId must be unique across analyzers + DiagnosticId çözümleyiciler arasında benzersiz olmalıdır + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + '{0}' tanılama kimliği zaten '{1}' çözümleyicisi tarafından kullanılıyor. Lütfen farklı bir tanılama kimliği kullanın. + + + + DiagnosticId must be unique across analyzers. + DiagnosticId çözümleyiciler arasında benzersiz olmalıdır. + + + + Category for analyzers must be from the specified values + Çözümleyiciler için kategori, belirtilen değerlerden biri olmalıdır + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + '{0}' kategorisi '{1}' dosyasında belirtilen izin verilen kategoriler arasında değil + + + + Category for analyzers must be from the specified values. + Çözümleyiciler için kategori belirtilen değerlerden biri olmalıdır. + + + + Invalid entry in analyzer category and diagnostic ID range specification file + Çözümleyici kategorisi ve tanılama kimliği aralık belirtimi dosyasındaki girdi geçersiz + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + Çözümleyici kategorisi ve tanılama kimliği aralık belirtimi dosyasında ('{1}'), '{0}' girdisi geçersiz + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + Çözümleyici kategorisi ve tanılama kimliği aralık belirtimi dosyasında geçersiz girdi. + + + + DiagnosticId for analyzers must be a non-null constant + Çözümleyiciler için DiagnosticId null olmayan bir sabit değer olmalıdır + + + + Diagnostic Id for rule '{0}' must be a non-null constant + '{0}' kuralı için tanılama kimliği null olmayan bir sabit değer olmalıdır + + + + DiagnosticId for analyzers must be a non-null constant. + Çözümleyiciler için DiagnosticId null olmayan bir sabit değer olmalıdır. + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + Tanılama çözümleyicisi türleri, Çalışma Alanları bütünleştirilmiş kodlarından alınan türleri kullanmamalıdır. Çalışma Alanları bütünleştirilmiş kodları yalnızca çözümleyici Visual Studio IDE canlı analizinde yürütülüyorsa kullanılabilir, ancak komut satırı derlemesi sırasında kullanılamaz. Çalışma Alanları bütünleştirilmiş kodlarından türlere başvurulması, komut satırı derlemesinde çözümleyici yürütüldüğü sırada çalışma zamanı özel durumuna yol açar. + + + + Do not use types from Workspaces assembly in an analyzer + Bir çözümleyicide Çalışma Alanları bütünleştirilmiş kodundan alınan türleri kullanmayın + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + '{2}' türüne erişen '{1}' türüne doğrudan veya dolaylı tüm erişimleri kaldırmak için tanılama çözümleyici türünü ('{0}') değiştirin + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + '{1}' türüne yönelik tüm doğrudan erişimleri kaldırmak için tanılama çözümleyici türünü ('{0}') değiştirin + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace, Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet paketini taşıdı ve hataya neden olan API değişiklikleri var. + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + Lütfen Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet paketine bir paket başvurusu ekleyerek MSBuildWorkspace’i yükseltin. MSBuildWorkspace’i başarıyla kullanma hakkında ayrıntılı bilgi için bkz. https://go.microsoft.com/fwlink/?linkid=874285. + + + + Upgrade MSBuildWorkspace + MSBuildWorkspace’i yükseltir + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf new file mode 100644 index 0000000000000..7541b9fdad494 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hans.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 分配给字段的 "DiagnosticDescriptor" 用于报告编译结束诊断,但用于初始化它的 "DiagnosticDescriptor" 构造函数未传入所需的自定义标记 "CompilationEnd"。有关详细信息,请参阅 "WellKnownDiagnosticTags.CompilationEnd" 的文档。 + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + 将 "CompilationEnd" 自定义标记添加到用于初始化字段 "{0}" 的诊断描述符,因为它用于报告编译结束诊断 + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + 将 "CompilationEnd" 自定义标记添加到编译结束诊断描述符 + + + + Add rule entry to unshipped release file + 将规则项添加到未提供的版本文件 + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + 从 DiagnosticAnalyzer 继承类型“{0}”,或者删除 DiagnosticAnalyzerAttribute + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + 标有 DiagnosticAnalyzerAttribute 的类型应继承自 DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + 使用 “SymbolEqualityComparer” 进行符号比较 + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + 应比较符号是否相等,而不是比较标识。使用重载接受 "IEqualityComparer" 并传递 "SymbolEqualityComparer"。 + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + 应比较符号是否相等,而不是比较标识。对 "GetHashCode" 的显式调用可能会导致错误的行为。 + + + + Use 'SymbolEqualityComparer' when comparing symbols + 比较符号时使用 “SymbolEqualityComparer” + + + + Symbols should be compared for equality + 应比较符号是否相等 + + + + Configure generated code analysis + 配置生成的代码分析 + + + + Configure generated code analysis + 配置生成的代码分析 + + + + Configure generated code analysis + 配置生成的代码分析 + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + 所有受支持的分析器诊断 ID 都应是分析器版本的一部分。 + + + + Rule '{0}' is not part of any analyzer release + 规则“{0}”不是分析器版本的一部分 + + + + Add analyzer diagnostic IDs to analyzer release + 向分析器版本添加分析器诊断 ID + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + 诊断说明应为以标点符号结尾的一个或多个句子,且不应包含任何前导空格或尾随空格 + + + + Define diagnostic description correctly + 正确定义诊断说明 + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + 诊断消息不应包含任何行回车符,也不应包含任何前导或尾随空格,应为不带尾随句点的单个句子或带有一个尾随句点的多个句子 + + + + Define diagnostic message correctly + 正确定义诊断消息 + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + 诊断标题不应包含句点,也不应包含任何行回车符和任何前导空格或尾随空格 + + + + Define diagnostic title correctly + 正确定义诊断标题 + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 不应在包含对 Microsoft.CodeAnalysis.VisualBasic 的引用的程序集中实现此 C# 编译器扩展。在 C# 编译方案期间,不会始终提供 Microsoft.CodeAnalysis.VisualBasic 程序集,因此对它的引用可能导致编译器扩展的行为不可预测。 + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + 不应在具有目标框架“{0}”的程序集中实现此编译器扩展。引用其他目标框架将导致编译器的行为不可预测。 + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + 实现编译器扩展点的类型只应在面向 netstandard2.0 的程序集中声明。更具体的目标框架仅在受支持的编译方案的子集中可用,因此面向这些框架可能会导致该功能的行为不可预测。 + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + 应在面向 netstandard2.0 的程序集中实现编译器扩展 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 不应在包含对 Microsoft.CodeAnalysis.Workspaces 的引用的程序集中实现此编译器扩展。在命令行编译方案期间未提供 Microsoft.CodeAnalysis.Workspaces 程序集,因此对它的引用可能导致编译器扩展的行为不可预测。 + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 不应在包含对 Microsoft.CodeAnalysis.CSharp 的引用的程序集中实现此 Visual Basic 编译器扩展。Microsoft.CodeAnalysis.CSharp 程序集并非始终在 Visual Basic 编译方案中提供,因此对它的引用可能导致编译器扩展的行为不可预测。 + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + 在包含对并非由所有编译方案提供的程序集的引用的程序集中,不应声明实现编译器扩展点的类型。这样做可能会导致该功能的行为不可预测。 + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + 应在具有编译器提供的引用的程序集中实现编译器扩展 + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + "GetSemanticModel" 是在诊断分析器中调用的昂贵方法,因为它创建了一个全新的语义模型,该语义模型不与编译器或其他分析器共享编译数据。这会在语义分析期间产生额外的性能开销。请考虑改为注册允许使用共享 "SemanticModel" (如 "RegisterOperationAction"、"RegisterSyntaxNodeAction" 或 "RegisterSemanticModelAction")的其他分析器操作。 + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 请勿在诊断分析器中调用 Compilation.GetSemanticModel() 方法 + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 请勿在诊断分析器中调用 Compilation.GetSemanticModel() 方法 + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + 分析器的 DiagnosticId 不应使用保留 ID。 + + + + '{0}' is a reserved diagnostic ID + “{0}”是保留的诊断 ID + + + + Do not use reserved diagnostic IDs + 不要使用保留的诊断 ID + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + 为分析器包启用发布跟踪有助于跟踪和记录每次分析器发布时附带/或更改的分析器诊断。有关详细信息,请访问 https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md。 + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + 为包含规则“{0}”的分析器项目启用分析器发布跟踪 + + + + Enable analyzer release tracking + 启用分析器发布跟踪 + + + + Enable concurrent execution + 启用并发执行 + + + + Enable concurrent execution + 启用并发执行 + + + + Enable concurrent execution + 启用并发执行 + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + 分析器版本文件中的条目无效。 + + + + Analyzer release file '{0}' has an invalid entry '{1}' + 分析器版本文件“{0}”具有无效条目“{1}” + + + + Invalid entry in analyzer release file + 分析器版本文件中的条目无效 + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + 分析器版本文件“{0}”缺少版本标头“{1}”或该标头无效 + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + 分析器版本文件“{0}”具有不包含针对规则“{2}”的以前已提供版本的无效“{1}”项。请改为在未提供的版本文件中添加针对规则的单独的“{1}”项。 + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + 分析器版本文件“{0}”包含具有一个或多个“未检测到”字段的项,这些字段需要在“{1}”中手动填充 + + + + Missing '{0}' attribute + 缺少“{0}”特性 + + + + Missing diagnostic analyzer attribute + 缺少诊断分析器特性 + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + DiagnosticAnalyzer 的非抽象子类型应使用 DiagnosticAnalyzerAttribute 标记。此特性的参数;如果有,则可确定分析器支持的语言。分析引擎将忽略未带此特性的分析器类型。 + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + 诊断分析器“{0}”可能可以支持 C# 和 Visual Basic。请考虑向“{1}”语言支持的 DiagnosticAnalyzerAttribute 添加一个参数。 + + + + Recommend adding language support to diagnostic analyzer + 建议向诊断分析器添加语言支持 + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + 诊断分析器标记为仅支持一种语言,但分析器程序集貌似未引用语言特定的任何 CodeAnalysis 程序集,因此可能会适用于多种语言。请考虑向 DiagnosticAnalyzerAttribute 添加其他语言参数。 + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + 为“{0}”应用 DiagnosticAnalyzer 特性。 + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + 为“{0}”和“{1}”应用 DiagnosticAnalyzer 特性。 + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + 注册符号分析器操作时,至少指定一个感兴趣的 SymbolKind + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + 注册语法节点分析器操作时,至少指定一个感兴趣的 SyntaxKind + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + 注册操作分析器操作时,至少指定一个感兴趣的 OperationKind + + + + Missing kind argument when registering an analyzer action + 注册分析器操作时缺少 kind 参数 + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + 分别注册语法、符号或操作分析器动作时,必须至少指定一种语法、符号或操作类型。否则,分析过程中将不会调用已注册的操作。 + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + 包含分析器或源生成器的项目应指定属性“<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>”。 + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + “{0}”: 包含分析器或源生成器的项目应指定属性“<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>” + + + + Specify analyzer banned API enforcement setting + 指定分析器禁止的 API 强制设置 + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + 检查语法类型时,首选 "syntax.IsKind(kind)" 而不是 "syntax.Kind() == kind"。使用 "IsKind" 的代码在运行时更加高效一些,因此在适用时始终使用此形式有助于提高复杂分析方案中的性能。 + + + + Use 'IsKind' instead of 'Kind' + 使用 "IsKind" 而不是 "Kind" + + + + Prefer 'IsKind' for checking syntax kinds + 首选 "IsKind" 来检查语法类型 + + + + Prefer 'IsKind' for checking syntax kinds + 首选 "IsKind" 来检查语法类型 + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + "CustomTags" 值用作根据标记的特定值在诊断描述符上启用特定操作和筛选器的方法。每个 Roslyn 分析器都应至少有一个来自 "WellKnownDiagnosticTags" 类的标记。 + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + 请考虑向诊断描述符构造函数提供一个非 null "customTags",以启用诊断描述符的元数据筛选 + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + 向诊断描述符构造函数提供非 null "customTags" 值 + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + 删除分析器版本之间的诊断 ID 的重复条目。 + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + 规则“{0}”在版本“{1}”和版本“{2}”之间有重复项 + + + + Remove duplicate entries for diagnostic ID between analyzer releases + 删除分析器版本之间的诊断 ID 的重复条目 + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + 删除同一分析器版本中诊断 ID 的重复条目。 + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + 在分析器版本文件“{2}”中,规则“{0}”具有版本“{1}”的多个项 + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + 删除同一分析器版本中诊断 ID 的重复条目 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + 不再报告的已提供的诊断 ID 应该在未提供的文件的“已删除规则”表中有一个条目。 + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + 规则“{0}”已在分析器版本“{1}”中提供,但不再是任何分析器支持的诊断。将“已删除规则”表中此规则的项添加到未提供的文件。 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + 不再报告但已提供的诊断 ID 应该在未提供的文件的“已删除规则”表中有一个条目 + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + 可以将不再报告和从未提供的分析器诊断 ID 的项从未提供的版本中删除。 + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + 规则“{0}”是下一个未提供的分析器版本的一部分,但它不是任何分析器支持的诊断 + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + 请勿将已删除的分析器诊断 ID 添加到未提供的分析器版本中 + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + 对从“SyntaxNode”继承的某些类型(例如“GlobalStatementSyntax”和“IncompleteMemberSyntax”)调用“SemanticModel.GetDeclaredSymbol”将始终返回“null”。 + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + 使用类型为“FieldDeclarationSyntax”或“EventFieldDeclarationSyntax”的参数调用“SemanticModel.GetDeclaredSymbol”将始终返回“null”。改为使用字段中的变量声明符调用“GetDeclaredSymbol”。 + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 对“SemanticModel.GetDeclaredSymbol({0})'”的调用将始终返回“null” + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 对“SemanticModel.GetDeclaredSymbol({0})'”的调用将始终返回“null” + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + 对“SemanticModel.GetDeclaredSymbol()”的调用将始终返回“null” + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + 已将该符号标记为禁止在分析器中使用,并应改用备用符号。 + + + + The symbol '{0}' is banned for use by analyzers{1} + 已禁止分析器 {1} 使用符号“{0}” + + + + Do not use APIs banned for analyzers + 不要使用禁用于分析器的 API + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + 分析器版本文件中标记为已删除的诊断 ID 不应由分析器报告。 + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + 规则“{0}”在最新的分析器版本中标记为“已删除”,但它仍在报告中 + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + 分析器版本文件中标记为“已删除”的诊断 ID 不得由分析器报告 + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + 符号分析器操作不支持 SymbolKind“{0}” + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + 注册符号分析器操作时使用了不受支持的 SymbolKind 实参 + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + 使用不受支持的 DiagnosticDescriptor“{0}”调用了 ReportDiagnostic + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + 使用不受支持的 DiagnosticDescriptor 调用了 ReportDiagnostic + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + 仅应使用从 DiagnosticAnalyzer.SupportedDiagnostics 属性返回的受支持的 DiagnosticDescriptors 调用 ReportDiagnostic。否则,分析引擎将过滤掉所报告的诊断。 + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + 方法“{2}”的类型形参“{1}”的类型实参“{0}”不是 SyntaxKind 枚举 + + + + Invalid type argument for DiagnosticAnalyzer's Register method + DiagnosticAnalyzer 的 Register 方法的类型参数无效 + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + DiagnosticAnalyzer 的语言特定的 Register 方法,例如 RegisterSyntaxNodeAction、RegisterCodeBlockStartAction 和 RegisterCodeBlockEndAction,需要一个语言特定的 "SyntaxKind" 类型实参用于“{0}”类型形参。否则,分析过程中将不会调用已注册的分析器操作。 + + + + Start action has no registered non-end actions + 启动操作未注册有任何非结束操作 + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + “{0}”没有注册任何分析器操作。请考虑将“{1}”中注册的依赖于此启动操作的操作移动到“{0}”。 + + + + Start action has no registered actions + 启动操作未注册有任何操作 + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + “{0}”没有注册除“{1}”以外的任何分析器操作。请考虑将此启动/结束操作对替换为“{2}”,或将“{3}”中注册的依赖于此启动操作的操作移动到“{0}”。 + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + 分析器启动操作可以对给定的代码单元(如代码块、编译等)执行有状态分析。细致的设计是实现分析器高效执行和保证内存不泄露的必要条件。使用以下准则编写此类分析器: +1.为已注册的启动操作定义新的作用域,可能需要一个私有嵌套类型用于分析每个代码单元。 +2.必要时,请在启动操作中定义和初始化状态。 +3.至少在启动操作中注册一个引用此状态的非结束操作。如果不需要此类操作,请考虑将启动操作替换为非启动操作。例如,未注册有操作或仅注册了一个 CodeBlockEndAction 的 CodeBlockStartAction 应替换为 CodeBlockAction。 +4.必要时,请注册一个结束操作,根据最终状态对诊断进行报告。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + 确保将分析器诊断 ID 的最新条目添加到分析器版本中。 + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + 与上一个版本相比,规则“{0}”更改了一个“类别”或“严重级别”。请还原源中的更新或将新的最新项添加到 未提供的版本文件。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + 确保将分析器诊断 ID 的最新条目添加到分析器版本中 + + + + Update rule entry in unshipped release file + 在未提供的版本文件中更新规则项 + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + 请考虑为诊断描述符构造函数提供类型“{0}”的可本地化参数,以确保描述符可本地化 + + + + Provide localizable arguments to diagnostic descriptor constructor + 为诊断描述符构造函数提供可本地化的参数 + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + 如果诊断分析器及其报告的诊断需要能够本地化,则用于构建诊断的受支持 DiagnosticDescriptors 也必须可本地化。若是如此,必须为诊断描述符构造函数的 “title” 形参(和可选 “description”)提供可本地化的实参,确保描述符可本地化。 + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + 不要将类型为“{0}”的每次编译的数据存储到诊断分析器的字段中 + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + 不要将每次编译的数据存储到诊断分析器的字段中 + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + 诊断分析器实例可能会超出编译的生存期。因此,如果将每次编译的数据(如符号)存储到诊断分析器的字段中,这可能导致过时的编译保持活动状态并导致内存泄漏。相反,应将此数据存储在使用“{0}.{1}”API 注册且在编译启动操作中实例化的单独类型中。此类型的实例将在每次编译时创建,并且它不会超出编译的生存期,因此避免了内存泄漏。 + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + 此接口的作者不允许此接口的第三方实现,并保留更改此接口的权利。因此,实现此接口可能会导致此接口的未来版本发生源或二进制的兼容性问题。 + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + 类型 {0} 无法实现接口 {1},因为 {1} 不适用于公共实现 + + + + Only internal implementations of this interface are allowed + 只允许此接口的内部实现 + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + 支持修复所有事件的 CodeFixProvider 必须将注册的代码操作划分为等效类,方法是为其分配一个非 null 的显式等效键,且该键对于此修复程序创建的各种代码操作都是唯一的。这样,FixAllProvider 即可通过应用此修复程序的代码操作解决所有诊断问题,其中这些操作位于触发器代码操作的等效类中。 + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + 为可选形参“{0}”提供一个显式实参,该形参对于此修复程序创建的各种代码操作均为非 null 且是唯一的 + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + 创建代码操作应具备唯一 EquivalenceKey,以支持 FixAll 事件 + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + “{0}”具有适用于属性“{1}”的默认值 "null"。可重写“{0}”上的此属性,以返回每个修复程序的所有代码操作中的唯一非 null 值,也可使用此类现有代码操作。 + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + 使用具备 FixAll 事件支持的唯一 EquivalenceKey 的代码操作 + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + 由 Roslyn 公开的多个对象不可变。通过对这些对象的方法调用得到的返回值不应被忽略。 + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + “{0}”不可变且“{1}”不对它产生任何影响。请考虑使用来自“{1}”的返回值。 + + + + Do not ignore values returned by methods on immutable objects + 不要忽略通过不可变对象上的方法返回的值 + + + + Code fix providers should provide FixAll support + 代码修复提供程序应提供 FixAll 支持 + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + “{0}”注册了一个或多个代码修复程序,但是未重写方法 "CodeFixProvider.GetFixAllProvider"。请重写该方法并向 FixAll 支持提供非 null 型 FixAllProvider (可能为 "WellKnownFixAllProviders.BatchFixer")或提供 "null" 以显式启用 FixAll 支持。 + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider 应提供 FixAll 支持,以允许用户通过单个代码修补程序修复基础诊断的多个实例。有关更多详细信息,请参阅 https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md 中的文档。 + + + + Override GetFixAllProvider. + 重写 GetFixAllProvider。 + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + 请考虑向诊断描述符构造函数提供一个非 null 型 "helpLinkUri",以在错误列表中出现该诊断时显示信息 + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + 向诊断描述符构造函数提供非 null 型 "helpLinkUri" 值 + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + "helpLinkUri" 值用于在错误列表中出现该诊断时显示信息。应对每个分析器指定一个 helpLinkUri,用于指向一个不会随时间推移而更改的帮助页面。 + + + + DiagnosticId for analyzers must be in specified format + 分析器的 DiagnosticId 必须为指定格式 + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + 属于类别“{1}”的诊断 Id“{0}”不符合文件“{3}”中指定的要求范围和/或格式“{2}” + + + + DiagnosticId for analyzers must be in specified format. + 分析器的 DiagnosticId 必须为指定格式。 + + + + DiagnosticId must be unique across analyzers + 分析器的 DiagnosticId 必须唯一 + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + 诊断 Id“{0}”已被分析器“{1}”使用。请使用其他诊断 ID。 + + + + DiagnosticId must be unique across analyzers. + 分析器的 DiagnosticId 必须唯一。 + + + + Category for analyzers must be from the specified values + 分析器的类别必须源自指定的值 + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + 类别“{0}”不属于文件“{1}”中指定的允许类别 + + + + Category for analyzers must be from the specified values. + 分析器的类别必须源自指定的值。 + + + + Invalid entry in analyzer category and diagnostic ID range specification file + 分析器类别和诊断 ID 范围规定文件中的条目无效 + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + 分析器类别和诊断 ID 范围规定文件“{1}”中的条目“{0}”无效 + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + 分析器类别和诊断 ID 范围规定文件中的条目无效。 + + + + DiagnosticId for analyzers must be a non-null constant + 分析器的 DiagnosticId 必须为非 null 常量 + + + + Diagnostic Id for rule '{0}' must be a non-null constant + 规则“{0}”的诊断 ID 必须为非 null 常量 + + + + DiagnosticId for analyzers must be a non-null constant. + 分析器的 DiagnosticId 必须为非 null 常量。 + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + 诊断分析器类型不应使用 Workspaces 程序集中的类型。分析器在 Visual Studio IDE 实时分析中执行时,Workspaces 程序集才可用,但在命令行生成期间不可用。命令行生成中的分析器执行期间,从 Workspaces 程序集引用类型将导致运行时异常。 + + + + Do not use types from Workspaces assembly in an analyzer + 不要在分析器中使用来自 Workspaces 程序集的类型 + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + 更改诊断分析器类型“{0}”,删除对访问类型“{2}”的类型“{1}”的所有的直接和/或间接访问 + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + 更改诊断分析器类型“{0}”以删除对类型“{1}”的所有直接访问 + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace 已经转移到了 Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 包,并且存在中断的 API 更改。 + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + 请通过向 Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 包添加包引用来升级 MSBuildWorkspace。有关成功使用 MSBuildWorkspace 的详细信息,请参阅 https://go.microsoft.com/fwlink/?linkid=874285。 + + + + Upgrade MSBuildWorkspace + 升级 MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf new file mode 100644 index 0000000000000..83cfa3c79c537 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Core/xlf/CodeAnalysisDiagnosticsResources.zh-Hant.xlf @@ -0,0 +1,835 @@ + + + + + + 'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + 指派給欄位的 'DiagnosticDescriptor' 可用來報告編譯結束診斷,但用於初始化的 'DiagnosticDescriptor' 建構函式未傳遞到必要的自訂標籤 "CompilationEnd"。如需詳細資料,請參閱 'WellKnownDiagnosticTags.CompilationEnd' 文件。 + + + + Add "CompilationEnd" custom tag to the diagnostic descriptor used to initialize field '{0}' as it is used to report a compilation end diagnostic + 將 "CompilationEnd" 自訂標記新增至用於初始化欄位 '{0}' 的診斷描述項,因為它用來報告編譯結束診斷 + + + + Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + 將 "CompilationEnd" 自訂標記新增至編譯結束診斷描述項 + + + + Add rule entry to unshipped release file + 將規則項目新增至未送出的版本檔案 + + + + Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + 從 DiagnosticAnalyzer 繼承型別 '{0}' 或移除 DiagnosticAnalyzerAttribute + + + + Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + 標示了 DiagnosticAnalyzerAttribute 的型別應繼承自 DiagnosticAnalyzer + + + + Use a 'SymbolEqualityComparer' for symbol comparison + 使用 'SymbolEqualityComparer' 進行符號比較 + + + + Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + 應比較符號是否相等,而非相同。請使用接受 'IEqualityComparer' 的多載,並傳遞 'SymbolEqualityComparer'。 + + + + Symbols should be compared for equality, not identity. An explicit call to 'GetHashCode' will likely result in the wrong behavior. + 應比較符號是否相等,而非相同。對 'GetHashCode' 進行明確的呼叫可能會造成錯誤的行為。 + + + + Use 'SymbolEqualityComparer' when comparing symbols + 比較符號時,請使用 'SymbolEqualityComparer' + + + + Symbols should be compared for equality + 應比較符號是否相等 + + + + Configure generated code analysis + 設定產生的程式碼分析 + + + + Configure generated code analysis + 設定產生的程式碼分析 + + + + Configure generated code analysis + 設定產生的程式碼分析 + + + + All supported analyzer diagnostic IDs should be part of an analyzer release. + 所有受支援的分析器診斷識別碼都應包含在分析器版本中。 + + + + Rule '{0}' is not part of any analyzer release + 規則 '{0}' 未包含在任何分析器版本中 + + + + Add analyzer diagnostic IDs to analyzer release + 將分析器診斷識別碼新增至分析器版本 + + + + The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + 診斷描述應為具有標點符號結尾的一或多行句子,而且前後不能有空白字元 + + + + Define diagnostic description correctly + 正確定義診斷描述 + + + + The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + 診斷訊息不應包含任何換行字元或前後空白,而且應為沒有行尾句點的單一句子,或有行尾句點的多行句子 + + + + Define diagnostic message correctly + 正確定義診斷訊息 + + + + The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + 診斷標題不應包含句點或任何換行字元,前後也不能包含任何空白字元 + + + + Define diagnostic title correctly + 正確定義診斷標題 + + + + This C# compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.VisualBasic. The Microsoft.CodeAnalysis.VisualBasic assembly is not always provided during C# compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 此 C# 編譯器延伸模組不應該在包含 Microsoft.CodeAnalysis.VisualBasic 參考的組件中實作。Microsoft.CodeAnalysis.VisualBasic 組件未一律在 C# 編譯案例期間提供,因此參考它可能會導致編譯器延伸模組發生未預期的行為。 + + + + This compiler extension should not be implemented in an assembly with target framework '{0}'. References to other target frameworks will cause the compiler to behave unpredictably. + 此編譯器延伸模組不應該在目標架構為 '{0}' 的元件中實作。參考其他目標架構會導致編譯器發生無法預測的行為。 + + + + Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + 實作編譯器擴充點的類型,只應在以 netstandard2.0 為目標的組件中宣告。更特定的目標架構僅適用於支援的編譯案例子集,因此以它們為目標可能會導致功能發生無法預期的行為。 + + + + Compiler extensions should be implemented in assemblies targeting netstandard2.0 + 編譯器延伸模組應在以 netstandard2.0 為目標的組件中實作 + + + + This compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.Workspaces. The Microsoft.CodeAnalysis.Workspaces assembly is not provided during command line compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 此編譯器延伸模組不應該在包含 Microsoft.CodeAnalysis.Workspaces 參考的組件中實作。Microsoft.CodeAnalysis.Workspaces 組件未在命令列編譯案例期間提供,因此參考它可能會導致編譯器延伸模組發生未預期的行為。 + + + + This Visual Basic compiler extension should not be implemented in an assembly containing a reference to Microsoft.CodeAnalysis.CSharp. The Microsoft.CodeAnalysis.CSharp assembly is not always provided during Visual Basic compilation scenarios, so references to it could cause the compiler extension to behave unpredictably. + 此 Visual Basic 編譯器延伸模組不應該在包含 Microsoft.CodeAnalysis.CSharp 參考的組件中實作。Microsoft.CodeAnalysis.CSharp 組件未一律在 Visual Basic 編譯案例期間提供,因此參考它可能會導致編譯器延伸模組發生未預期的行為。 + + + + Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + 實作編譯器延伸模組點不應在包含未由所有編譯案例提供的組件參考的組件中宣告的類型。這樣做可能會導致功能發生無法預測的行為。 + + + + Compiler extensions should be implemented in assemblies with compiler-provided references + 編譯器延伸模組應該在具有編譯器提供參考的組件中實作 + + + + 'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + 在診斷分析器內叫用方法時,'GetSemanticModel' 是成本較高的方法,原因是其會建立全新的語意模型,而不會與編譯器或其他分析器共用編譯資料,此舉會在分析語意期間產生額外的效能成本。請改為考慮註冊不同的分析器動作,以允許使用共用的 'SemanticModel',例如 'RegisterOperationAction'、'RegisterSyntaxNodeAction' 或 'RegisterSemanticModelAction'。 + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 請不要在診斷分析器內叫用 Compilation.GetSemanticModel() 方法 + + + + Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + 請不要在診斷分析器內叫用 Compilation.GetSemanticModel() 方法 + + + + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + + + + Type '{0}' should not be marked with 'file' + Type '{0}' should not be marked with 'file' + + + + Do not use file types for implementing analyzers, generators, and code fixers + Do not use file types for implementing analyzers, generators, and code fixers + + + + DiagnosticId for analyzers should not use reserved IDs. + 分析器的診斷識別碼不應使用保留的識別碼。 + + + + '{0}' is a reserved diagnostic ID + '{0}' 是保留的診斷識別碼 + + + + Do not use reserved diagnostic IDs + 請勿使用保留的診斷識別碼 + + + + Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md. + 啟用分析器套件的版本追蹤有助於追蹤及記錄每個分析器版本所發佈及 (或) 變更的分析器診斷。如需詳細資料,請參閱 https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md。 + + + + Enable analyzer release tracking for the analyzer project containing rule '{0}' + 啟用包含規則 '{0}' 之分析器專案的分析器版本追蹤 + + + + Enable analyzer release tracking + 啟用分析器版本追蹤 + + + + Enable concurrent execution + 啟用同時執行 + + + + Enable concurrent execution + 啟用同時執行 + + + + Enable concurrent execution + 啟用同時執行 + + + + The author of this interface has deprecated implementing this interface. + The author of this interface has deprecated implementing this interface. + + + + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + Type {0} cannot implement interface {1} because {1} is obsolete for implementation. See {2} for more details. + + + + Implementations of this interface are not allowed + Implementations of this interface are not allowed + + + + Invalid entry in analyzer release file. + 分析器版本檔案中的項目無效。 + + + + Analyzer release file '{0}' has an invalid entry '{1}' + 分析器版本檔案 '{0}' 具有無效的項目 '{1}' + + + + Invalid entry in analyzer release file + 分析器版本檔案中有無效的項目 + + + + Analyzer release file '{0}' has a missing or invalid release header '{1}' + 分析器版本檔案 '{0}' 缺少版本標頭 '{1}' 或其無效 + + + + Analyzer release file '{0}' has an invalid '{1}' entry without a prior shipped release for the rule '{2}'. Instead, add a separate '{1}' entry for the rule in unshipped release file. + 分析器版本檔案 '{0}' 具有無效的 '{1}' 項目,該項目未包含先前所送出的規則 '{2}' 版本。請改為在未送出的版本檔案中針對規則新增個別的 '{1}' 項目。 + + + + Analyzer release file '{0}' has an entry with one or more 'Undetected' fields that need to be manually filled in '{1}' + 分析器版本檔案 '{0}' 具有包含一或多個「未偵測」欄位的項目,必須手動填入 '{1}' 中 + + + + Missing '{0}' attribute + 缺少 '{0}' 屬性 + + + + Missing diagnostic analyzer attribute + 缺少診斷分析器屬性 + + + + Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + DiagnosticAnalyzer 的非抽象子類型應標記為 DiagnosticAnalyzerAttribute。此屬性的引數 (若有的話) 會決定分析器支援的語言。分析引擎將會略過不含此屬性的分析器類型。 + + + + Diagnostic analyzer '{0}' may be able to support both C# and Visual Basic. Consider adding an argument to DiagnosticAnalyzerAttribute for '{1}' language support. + 診斷分析器 '{0}' 可同時支援 C# 與 Visual Basic。請考慮將對 DiagnosticAnalyzerAttribute 新增一項引數,以取得 '{1}' 語言支援。 + + + + Recommend adding language support to diagnostic analyzer + 建議為診斷分析器新增語言支援 + + + + Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + 診斷分析器標記為只支援一種語言,但分析器組件似乎未參考任何語言專屬的 CodeAnalysis 組件,因此可能適用於多種語言。請考慮對 DiagnosticAnalyzerAttribute 新增其他語言引數。 + + + + Apply DiagnosticAnalyzer attribute for '{0}'. + 為 '{0}' 套用 DiagnosticAnalyzer 屬性。 + + + + Apply DiagnosticAnalyzer attribute for both '{0}' and '{1}'. + 同時為 '{0}' 與 '{1}' 套用 DiagnosticAnalyzer 屬性。 + + + + Specify at least one SymbolKind of interest when registering a symbol analyzer action + 註冊符號分析器動作時,請至少指定一個關注的 SymbolKind + + + + Specify at least one SyntaxKind of interest when registering a syntax node analyzer action + 註冊語法節點分析器動作時,請至少指定一個關注的 SyntaxKind + + + + Specify at least one OperationKind of interest when registering an operation analyzer action + 註冊作業分析器動作時,請至少指定一個關注的 OperationKind + + + + Missing kind argument when registering an analyzer action + 註冊分析器動作時,缺少 kind 引數 + + + + You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + 註冊語法、符號或作業分析器動作時,必須個別指定至少一個語法、符號或作業種類。否則,分析期間絕不會叫用註冊的動作。 + + + + A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'. + 包含分析器或來源產生器的專案應該指定屬性 '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>'。 + + + + '{0}': A project containing analyzers or source generators should specify the property '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + '{0}': 包含分析器或來源產生器的專案應該指定屬性 '<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>' + + + + Specify analyzer banned API enforcement setting + 指定分析器禁止的 API 強制設定 + + + + Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + 檢查語法類型時,相較於 'syntax.Kind() == kind',更建議使用 'syntax.IsKind(kind)'。使用 'IsKind' 的程式碼,在執行階段的效率稍高。因此在適用情況下,一致使用此格式有助改進複雜分析案例的效能。 + + + + Use 'IsKind' instead of 'Kind' + 使用 'IsKind' 代替 'Kind' + + + + Prefer 'IsKind' for checking syntax kinds + 建議使用 'IsKind' 來檢查語法類型 + + + + Prefer 'IsKind' for checking syntax kinds + 建議使用 'IsKind' 來檢查語法類型 + + + + The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + 'customTags' 值可用來在診斷描述項上依據標籤的特定值,啟用特定的動作及篩選。每個 Roslyn 分析器至少都應該要有一個來自 'WellKnownDiagnosticTags' 類別的標籤。 + + + + Consider providing a non-null 'customTags' to diagnostic descriptor constructor to enable metatada filtering of diagnostic descriptors + 請考慮提供非 null 的 'customTags' 給診斷描述元建構函式,以啟用診斷描述元的中繼資料篩選 + + + + Provide non-null 'customTags' value to diagnostic descriptor constructor + 請提供非 null 的 'customTags' 值給診斷描述元建構函式 + + + + Remove duplicate entries for diagnostic ID between analyzer releases. + 移除分析器版本之間診斷識別碼的重複項目。 + + + + Rule '{0}' has duplicate entry between release '{1}' and release '{2}' + 規則 '{0}' 在版本 '{1}' 與版本 '{2}' 之間,出現了重複的項目 + + + + Remove duplicate entries for diagnostic ID between analyzer releases + 移除分析器版本之間重複的診斷識別碼項目 + + + + Remove duplicate entries for diagnostic ID in the same analyzer release. + 移除相同分析器版本中診斷識別碼的重複項目。 + + + + Rule '{0}' has more then one entry for release '{1}' in analyzer release file '{2}' + 規則 '{0}' 在分析器版本檔案 '{2}' 中,針對版本 '{1}' 出現了多個項目 + + + + Remove duplicate entries for diagnostic ID in the same analyzer release + 移除相同分析器版本中重複的診斷識別碼項目 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + 不再回報的已送出診斷識別碼,在未送出檔案的「已移除規則」資料表中應有一個項目。 + + + + Rule '{0}' was shipped in analyzer release '{1}', but is no longer a supported diagnostic for any analyzer. Add an entry for this rule in a 'Removed Rules' table to unshipped file. + 規則 '{0}' 已送到分析器版本 '{1}' 中,但已不再是任何分析器的支援診斷。請在未送出檔案的「已移除規則」資料表中針對此規則新增項目。 + + + + Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + 不會再回報的已送出之診斷識別碼,在未送出檔案的「已移除規則」資料表中應有一個項目 + + + + Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + 不再回報且從未送出的分析器診斷識別碼項目可以從未送出的分析器版本移除。 + + + + Rule '{0}' is part of the next unshipped analyzer release, but is not a supported diagnostic for any analyzer + 規則 '{0}' 包含在下一個未送出的分析器版本中,但其並非任何分析器的支援診斷 + + + + Do not add removed analyzer diagnostic IDs to unshipped analyzer release + 請勿將已移除的分析器診斷識別碼,新增至未送出的分析器版本 + + + + Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + 在繼承自 'SyntaxNode' 的特定類型上呼叫 'SemanticModel.GetDeclaredSymbol',例如 'GlobalStatementSyntax' 和 'IncompleteMemberSyntax' 一律會傳回 'null'。 + + + + Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + 使用類型為 'FieldDeclarationSyntax' 或 'EventFieldDeclarationSyntax' 的引數呼叫 'SemanticModel.GetDeclaredSymbol' 一律會傳回 'null'。改為使用欄位中的變數宣告子呼叫 'GetDeclaredSymbol'。 + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 呼叫 'SemanticModel.GetDeclaredSymbol({0})' 一律會傳回 'null' + + + + A call to 'SemanticModel.GetDeclaredSymbol({0})' will always return 'null' + 呼叫 'SemanticModel.GetDeclaredSymbol({0})' 一律會傳回 'null' + + + + This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + 對 'SemanticModel.GetDeclaredSymbol()' 的呼叫一律會傳回 'null' + + + + The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + 已將符號標示為禁用於分析器,因此應改用替代符號。 + + + + The symbol '{0}' is banned for use by analyzers{1} + 符號 '{0}' 已禁用於分析器{1} + + + + Do not use APIs banned for analyzers + 請勿在分析器中使用禁止的 API + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + 分析器不應回報分析器版本檔案中標示為已移除的診斷識別碼。 + + + + Rule '{0}' is marked as removed in the latest analyzer release, but is still being reported + 規則 '{0}' 在最新的分析器版本中標示為已移除,但仍回報了該規則 + + + + Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + 分析器不應回報在分析器版本檔案中標示為已移除的診斷識別碼 + + + + SymbolKind '{0}' is not supported for symbol analyzer actions + 對符號分析器動作不支援 SymbolKind '{0}' + + + + Unsupported SymbolKind argument when registering a symbol analyzer action + 註冊符號分析器動作時不支援 SymbolKind 引數 + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor '{0}' + 叫用 ReportDiagnostic 時使用了不受支援的 DiagnosticDescriptor '{0}' + + + + ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + 叫用 ReportDiagnostic 時使用了不受支援的 DiagnosticDescriptor + + + + ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + 叫用 ReportDiagnostic 時,只可使用 DiagnosticAnalyzer.SupportedDiagnostics 屬性所傳回之受支援的 DiagnosticDescriptors。否則,分析引擎將會篩選掉回報的診斷。 + + + + Type argument '{0}' for type parameter '{1}' of method '{2}' is not a SyntaxKind enum + 方法 '{2}' 之型別參數 '{1}' 的型別引數 '{0}' 並非 SyntaxKind 列舉 + + + + Invalid type argument for DiagnosticAnalyzer's Register method + DiagnosticAnalyzer 之 Register 方法的型別引數無效 + + + + DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's '{0}' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + DiagnosticAnalyzer 語言專屬的 Register 方法 (例如 RegisterSyntaxNodeAction、RegisterCodeBlockStartAction 與 RegisterCodeBlockEndAction),必須是其 '{0}' 型別參數的語言專屬 'SyntaxKind' 型別引數。否則,分析期間絕不會叫用註冊的分析器動作。 + + + + Start action has no registered non-end actions + 起始動作沒有任何註冊的非結束動作 + + + + '{0}' does not register any analyzer actions. Consider moving actions registered in '{1}' that depend on this start action to '{0}'. + '{0}' 並未註冊任何分析器動作。請考慮將註冊於 '{1}' 中且與此起始動作相依的動作,移至 '{0}'。 + + + + Start action has no registered actions + 起始動作沒有任何註冊的動作 + + + + '{0}' does not register any analyzer actions, except for a '{1}'. Consider replacing this start/end action pair with a '{2}' or moving actions registered in '{3}' that depend on this start action to '{0}'. + 除了 '{1}' 以外,'{0}' 並未註冊任何分析器動作。請考慮以 '{2}' 取代此成對的起始/結束動作,或將註冊於 '{3}' 中且與此起始動作相依的動作,移至 '{0}'。 + + + + An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. +2. If required, define and initialize state in the start action. +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. +4. If required, register an end action to report diagnostics based on the final state. + 分析器起始動作會對指定的程式碼單位 (例如程式碼區塊、編譯等) 執行具狀態的分析。請仔細設計,以便有效執行分析器而不流失記憶體。使用下列方針撰寫這類分析器: +1.為註冊的起始動作定義新範圍,可能會有私用巢狀類別以分析每個程式碼單位。 +2.如有必要,請定義並初始化起始動作中的狀態。 +3.至少註冊一個非結束動作,其參考起始動作中的這個狀態。如果不需要這類動作,請考慮以非起始動作取代起始動作。例如,CodeBlockStartAction 沒有任何註冊動作或只有一個註冊的 CodeBlockEndAction 時,應以 CodeBlockAction 取代。 +4.如有必要,請註冊結束動作,以根據最終狀態回報診斷。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + 請確認已將分析器診斷識別碼的最新項目新增至分析器版本。 + + + + Rule '{0}' has a changed 'Category' or 'Severity' from the last release. Either revert the update(s) in source or add a new up-to-date entry to unshipped release file. + 規則 '{0}' 已變更上一個版本中的「分類」或「嚴重性」。請還原來源中的更新,或將最新項目新增至未送出的版本檔案。 + + + + Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + 請確認已將分析器診斷識別碼的最新項目,新增至分析器版本 + + + + Update rule entry in unshipped release file + 更新未送出版本檔案中的規則項目 + + + + Consider providing localizable arguments of type '{0}' to diagnostic descriptor constructor to ensure the descriptor is localizable + 請考慮將型別為 '{0}' 的可當地語系化引數,提供給診斷描述元建構函式,以確保該描述元可當地語系化 + + + + Provide localizable arguments to diagnostic descriptor constructor + 為診斷描述元建構函式提供可當地語系化的引數 + + + + If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + 如果診斷分析器及其回報的診斷必須可當地語系化,則用於建構診斷之受支援的 DiagnosticDescriptors,也必須可當地語系化。若是如此,則必須為診斷描述元建構函式,對參數 'title' (及選擇性 'description') 提供可當地語系化引數,以確保該描述元可當地語系化。 + + + + Avoid storing per-compilation data of type '{0}' into the fields of a diagnostic analyzer + 避免將型別 '{0}' 的各個編譯資料,儲存至診斷分析器的欄位中 + + + + Avoid storing per-compilation data into the fields of a diagnostic analyzer + 避免將各個編譯的資料,儲存至診斷分析器的欄位中 + + + + Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using '{0}.{1}' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + 診斷分析器的執行個體可能會超過編譯的存留期。因此,將各個編譯資料 (例如符號) 儲存至診斷分析器的欄位中,可能會造成過時的編譯仍繼續運作,而導致記憶體流失。而應改為將此資料儲存為另一種在編譯起始動作內具現化,且使用 '{0}.{1}' API 註冊的類型。每個編譯都會建立一個此類型的執行個體,且該執行個體不會超過編譯的存留期,因此可避免記憶體流失。 + + + + The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + 此介面的作者並不希望第三方實作此介面,並保留變更的權利。因此,實作此介面可能會導致與此介面的未來版本,發生原始碼或二進位碼相容性問題。 + + + + Type {0} cannot implement interface {1} because {1} is not available for public implementation + 因為 {1} 無法供公用實作使用,所以型別 {0} 無法實作介面 {1} + + + + Only internal implementations of this interface are allowed + 只允許內部實作此介面 + + + + A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + CodeFixProvider 若想支援修正所有出現之處,必須將註冊的程式碼動作分類為相等類別,方法是為其指派明確且非 Null 的相等索引鍵,而其在此修正程式建立的每一種程式碼動作之間皆不重複。如此可讓 FixAllProvider 能藉由套用來自此修正程式中位於觸發程式碼動作之相等類別內的程式碼動作,來修正所需範圍內的所有診斷。 + + + + Provide an explicit argument for optional parameter '{0}', which is non-null and unique for each kind of code action created by this fixer + 為選擇性參數 '{0}' 提供非 Null 的明確引數,而其在此修正程式所建立的每一種程式碼動作間皆不重複 + + + + Create code actions should have a unique EquivalenceKey for FixAll occurrences support + 建立的程式碼動作應要有唯一的 EquivalenceKey ,才可支援出現 FixAll 之處 + + + + '{0}' has the default value of 'null' for property '{1}'. Either override this property on '{0}' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + '{0}' 的屬性 '{1}' 為預設值 'null'。請在 '{0}' 上覆寫此屬性,以傳回在每個修正程式的所有程式碼動作之間唯一的非 Null 值,或使用這類現有的程式碼動作。 + + + + Use code actions that have a unique EquivalenceKey for FixAll occurrences support + 使用具有唯一 EquivalenceKey 之程式碼動作,支援出現 FixAll 之處 + + + + Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + Roslyn 所公開的許多物件皆不可變。請勿略過來自這些物件上,方法引動過程的傳回值。 + + + + '{0}' is immutable and '{1}' will not have any effect on it. Consider using the return value from '{1}'. + '{0}' 不可變動,所以 '{1}' 對它沒有任何作用。請考慮使用來自 '{1}' 的傳回值。 + + + + Do not ignore values returned by methods on immutable objects + 請勿略過不可變物件上方法所傳回的值 + + + + Code fix providers should provide FixAll support + 程式碼修正提供者應提供 FixAll 支援 + + + + '{0}' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + '{0}' 註冊了一個或多個程式碼修正,卻沒有覆寫 'CodeFixProvider.GetFixAllProvider' 方法。請覆寫此方法並提供 FixAll 支援、也許是 'WellKnownFixAllProviders.BatchFixer',或是已明確停用 'null' 的非 null FixAllProvider。 + + + + A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details. + CodeFixProvider 應提供 FixAll 支援,讓使用者能利用單一程式碼修正來修正基礎診斷的多個執行個體。如需進一步的詳細資料,請參閱下列位置的文件 https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md。 + + + + Override GetFixAllProvider. + 覆寫 GetFixAllProvider。 + + + + Consider providing a non-null 'helpLinkUri' to diagnostic descriptor constructor to show information when this diagnostic appears in the error list + 請考慮提供非 null 的 'helpLinkUri' 到診斷描述元建構函式,使錯誤清單出現此診斷時顯示資訊 + + + + Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + 請提供非 null 的 'helpLinkUri' 值到診斷描述元建構函式 + + + + The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + 'helpLinkUri' 值可在錯誤清單中出現此診斷時用於顯示資訊。每個分析器都應指定指向說明頁面的 helpLinkUri,其不會隨時間變更。 + + + + DiagnosticId for analyzers must be in specified format + 分析器的診斷識別碼必須為指定格式 + + + + Diagnostic Id '{0}' belonging to category '{1}' is not in the required range and/or format '{2}' specified in the file '{3}' + 屬於類別 '{1}' 的診斷識別碼 '{0}' 不在必要範圍內,而且 (或) 不是檔案 '{3}' 中所指定的格式 '{2}' + + + + DiagnosticId for analyzers must be in specified format. + 分析器的診斷識別碼必須為指定格式。 + + + + DiagnosticId must be unique across analyzers + 診斷識別碼必須是唯一的橫向分析器 + + + + Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID. + 分析器 '{1}' 已使用診斷識別碼 '{0}'。請使用其他診斷識別碼。 + + + + DiagnosticId must be unique across analyzers. + 分析器的診斷識別碼必須是唯一的。 + + + + Category for analyzers must be from the specified values + 分析器的類別必須來自指定的值 + + + + Category '{0}' is not from the allowed categories specified in the file '{1}' + 類別 '{0}' 並不是來自檔案 '{1}' 中指定的允許類別 + + + + Category for analyzers must be from the specified values. + 分析器的類別必須來自指定的值。 + + + + Invalid entry in analyzer category and diagnostic ID range specification file + 分析器類別中的項目與診斷識別碼範圍規格檔案無效 + + + + Invalid entry '{0}' in analyzer category and diagnostic ID range specification file '{1}' + 分析器類別中的項目 '{0}' 與診斷識別碼範圍規格檔案 '{1}' 無效 + + + + Invalid entry in analyzer category and diagnostic ID range specification file. + 分析器類別中的項目與診斷識別碼規格檔案無效。 + + + + DiagnosticId for analyzers must be a non-null constant + 分析器之診斷識別碼必須是非 null 的常數 + + + + Diagnostic Id for rule '{0}' must be a non-null constant + 規則 '{0}' 的診斷識別碼必須是非 null 之常數 + + + + DiagnosticId for analyzers must be a non-null constant. + 分析器的診斷識別碼必須是非 null 之常數。 + + + + Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + 診斷分析器類型不應使用來自工作區組件的類型。工作區組件僅在分析器於 Visual Studio IDE 即時分析中執行時可用,而在命令列建置期間不可用。當分析器在命令列建置期間執行時,參考來自工作區組件的類型會導致執行階段例外狀況。 + + + + Do not use types from Workspaces assembly in an analyzer + 不要使用分析器中來自工作區組件的類型 + + + + Change diagnostic analyzer type '{0}' to remove all direct and/or indirect accesses to type(s) '{1}', which access type(s) '{2}' + 變更診斷分析器類型 '{0}' 會移除存取類型 '{2}' 之類型 '{1}' 的所有直接及 (或) 間接存取 + + + + Change diagnostic analyzer type '{0}' to remove all direct accesses to type(s) '{1}' + 變更診斷分析器類型 '{0}' 可移除類型 '{1}' 的所有直接存取 + + + + MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + MSBuildWorkspace 已移至 Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 套件,且目前有最新的 API 變更。 + + + + Please upgrade MSBuildWorkspace by adding a package reference to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package. See https://go.microsoft.com/fwlink/?linkid=874285 for details on using MSBuildWorkspace successfully. + 請將套件參考新增至 Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet 套件,來升級 MSBuildWorkspace。如需成功使用 MSBuildWorkspace 的詳細資料,請參閱 https://go.microsoft.com/fwlink/?linkid=874285。 + + + + Upgrade MSBuildWorkspace + 升級 MSBuildWorkspace + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Directory.Build.props b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Directory.Build.props new file mode 100644 index 0000000000000..eb8f907aeee04 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + $(DefineConstants),MICROSOFT_CODEANALYSIS_ANALYZERS + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md new file mode 100644 index 0000000000000..c63b863e7ca9e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.md @@ -0,0 +1,641 @@ +# Microsoft.CodeAnalysis.Analyzers + +## RS1001: Missing diagnostic analyzer attribute + +Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1002: Missing kind argument when registering an analyzer action + +You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1003: Unsupported SymbolKind argument when registering a symbol analyzer action + +SymbolKind '{0}' is not supported for symbol analyzer actions + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1004: Recommend adding language support to diagnostic analyzer + +Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1005: ReportDiagnostic invoked with an unsupported DiagnosticDescriptor + +ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1006: Invalid type argument for DiagnosticAnalyzer's Register method + +DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's 'TLanguageKindEnumName' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1007: Provide localizable arguments to diagnostic descriptor constructor + +If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisLocalization| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1008: Avoid storing per-compilation data into the fields of a diagnostic analyzer + +Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using 'AnalysisContext.RegisterCompilationStartAction' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1009: Only internal implementations of this interface are allowed + +The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCompatibility| +|Enabled|True| +|Severity|Error| +|CodeFix|False| +--- + +## RS1010: Create code actions should have a unique EquivalenceKey for FixAll occurrences support + +A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + +|Item|Value| +|-|-| +|Category|Correctness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1011: Use code actions that have a unique EquivalenceKey for FixAll occurrences support + +A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action. + +|Item|Value| +|-|-| +|Category|Correctness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1012: Start action has no registered actions + +An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: + +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. + +2. If required, define and initialize state in the start action. + +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. + +4. If required, register an end action to report diagnostics based on the final state. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1013: Start action has no registered non-end actions + +An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers: + +1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit. + +2. If required, define and initialize state in the start action. + +3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction. + +4. If required, register an end action to report diagnostics based on the final state. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1014: Do not ignore values returned by methods on immutable objects + +Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1015: Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor + +The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDocumentation| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1016: Code fix providers should provide FixAll support + +A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at for further details. + +|Item|Value| +|-|-| +|Category|Correctness| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1017: DiagnosticId for analyzers must be a non-null constant + +DiagnosticId for analyzers must be a non-null constant. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1018: DiagnosticId for analyzers must be in specified format + +DiagnosticId for analyzers must be in specified format. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1019: DiagnosticId must be unique across analyzers + +DiagnosticId must be unique across analyzers. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1020: Category for analyzers must be from the specified values + +Category for analyzers must be from the specified values. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1021: Invalid entry in analyzer category and diagnostic ID range specification file + +Invalid entry in analyzer category and diagnostic ID range specification file. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS1022](https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1022.md): Do not use types from Workspaces assembly in an analyzer + +Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS1023](https://go.microsoft.com/fwlink/?linkid=874285): Upgrade MSBuildWorkspace + +MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes. + +|Item|Value| +|-|-| +|Category|Library| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1024: Symbols should be compared for equality + +Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1025: Configure generated code analysis + +Configure generated code analysis + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1026: Enable concurrent execution + +Enable concurrent execution + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1027: Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer + +Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s) + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1028: Provide non-null 'customTags' value to diagnostic descriptor constructor + +The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDocumentation| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1029: Do not use reserved diagnostic IDs + +DiagnosticId for analyzers should not use reserved IDs. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1030: Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + +'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1031: Define diagnostic title correctly + +The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1032: Define diagnostic message correctly + +The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1033: Define diagnostic description correctly + +The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1034: Prefer 'IsKind' for checking syntax kinds + +Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS1035: Do not use APIs banned for analyzers + +The symbol has been marked as banned for use in analyzers, and an alternate should be used instead. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Error| +|CodeFix|False| +--- + +## RS1036: Specify analyzer banned API enforcement setting + +A project containing analyzers or source generators should specify the property '\true\'. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1037: Add "CompilationEnd" custom tag to compilation end diagnostic descriptor + +'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag "CompilationEnd". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS1038](https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md): Compiler extensions should be implemented in assemblies with compiler-provided references + +Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1039: This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + +Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1040: This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' + +Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS1041](https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1041.md): Compiler extensions should be implemented in assemblies targeting netstandard2.0 + +Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS1042: Implementations of this interface are not allowed + +The author of this interface has deprecated implementing this interface. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCompatibility| +|Enabled|True| +|Severity|Error| +|CodeFix|False| +--- + +## RS1043: Do not use file types for implementing analyzers, generators, and code fixers + +Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisCorrectness| +|Enabled|True| +|Severity|Error| +|CodeFix|False| +--- + +## [RS2000](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Add analyzer diagnostic IDs to analyzer release + +All supported analyzer diagnostic IDs should be part of an analyzer release. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS2001](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release + +Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS2002](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Do not add removed analyzer diagnostic IDs to unshipped analyzer release + +Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2003](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file + +Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2004](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers + +Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2005](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Remove duplicate entries for diagnostic ID in the same analyzer release + +Remove duplicate entries for diagnostic ID in the same analyzer release. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2006](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Remove duplicate entries for diagnostic ID between analyzer releases + +Remove duplicate entries for diagnostic ID between analyzer releases. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2007](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Invalid entry in analyzer release file + +Invalid entry in analyzer release file. + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS2008](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md): Enable analyzer release tracking + +Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at . + +|Item|Value| +|-|-| +|Category|MicrosoftCodeAnalysisReleaseTracking| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif new file mode 100644 index 0000000000000..223a94d1a1792 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.sarif @@ -0,0 +1,1157 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Microsoft.CodeAnalysis.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS1001": { + "id": "RS1001", + "shortDescription": "Missing diagnostic analyzer attribute", + "fullDescription": "Non-abstract sub-types of DiagnosticAnalyzer should be marked with DiagnosticAnalyzerAttribute(s). The argument to this attribute(s), if any, determine the supported languages for the analyzer. Analyzer types without this attribute will be ignored by the analysis engine.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "DiagnosticAnalyzerAttributeAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1004": { + "id": "RS1004", + "shortDescription": "Recommend adding language support to diagnostic analyzer", + "fullDescription": "Diagnostic analyzer is marked as supporting only one language, but the analyzer assembly doesn't seem to refer to any language specific CodeAnalysis assemblies, and so is likely to work for more than one language. Consider adding an additional language argument to DiagnosticAnalyzerAttribute.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "DiagnosticAnalyzerAttributeAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1007": { + "id": "RS1007", + "shortDescription": "Provide localizable arguments to diagnostic descriptor constructor", + "fullDescription": "If your diagnostic analyzer and it's reported diagnostics need to be localizable, then the supported DiagnosticDescriptors used for constructing the diagnostics must also be localizable. If so, then localizable argument(s) must be provided for parameter 'title' (and optionally 'description') to the diagnostic descriptor constructor to ensure that the descriptor is localizable.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisLocalization", + "isEnabledByDefault": false, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1009": { + "id": "RS1009", + "shortDescription": "Only internal implementations of this interface are allowed", + "fullDescription": "The author of this interface did not intend to have third party implementations of this interface and reserves the right to change it. Implementing this interface could therefore result in a source or binary compatibility issue with a future version of this interface.", + "defaultLevel": "error", + "properties": { + "category": "MicrosoftCodeAnalysisCompatibility", + "isEnabledByDefault": true, + "typeName": "InternalImplementationOnlyAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "RS1010": { + "id": "RS1010", + "shortDescription": "Create code actions should have a unique EquivalenceKey for FixAll occurrences support", + "fullDescription": "A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action.", + "defaultLevel": "warning", + "properties": { + "category": "Correctness", + "isEnabledByDefault": true, + "typeName": "FixerWithFixAllAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1011": { + "id": "RS1011", + "shortDescription": "Use code actions that have a unique EquivalenceKey for FixAll occurrences support", + "fullDescription": "A CodeFixProvider that intends to support fix all occurrences must classify the registered code actions into equivalence classes by assigning it an explicit, non-null equivalence key which is unique for each kind of code action created by this fixer. This enables the FixAllProvider to fix all diagnostics in the required scope by applying code actions from this fixer that are in the equivalence class of the trigger code action.", + "defaultLevel": "warning", + "properties": { + "category": "Correctness", + "isEnabledByDefault": true, + "typeName": "FixerWithFixAllAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1014": { + "id": "RS1014", + "shortDescription": "Do not ignore values returned by methods on immutable objects", + "fullDescription": "Many objects exposed by Roslyn are immutable. The return value from a method invocation on these objects should not be ignored.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "ImmutableObjectMethodAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1015": { + "id": "RS1015", + "shortDescription": "Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor", + "fullDescription": "The 'helpLinkUri' value is used to show information when this diagnostic in the error list. Every analyzer should have a helpLinkUri specified which points to a help page that does not change over time.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDocumentation", + "isEnabledByDefault": false, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1016": { + "id": "RS1016", + "shortDescription": "Code fix providers should provide FixAll support", + "fullDescription": "A CodeFixProvider should provide FixAll support to enable users to fix multiple instances of the underlying diagnostic with a single code fix. See documentation at https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for further details.", + "defaultLevel": "warning", + "properties": { + "category": "Correctness", + "isEnabledByDefault": true, + "typeName": "FixerWithFixAllAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1017": { + "id": "RS1017", + "shortDescription": "DiagnosticId for analyzers must be a non-null constant", + "fullDescription": "DiagnosticId for analyzers must be a non-null constant.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1018": { + "id": "RS1018", + "shortDescription": "DiagnosticId for analyzers must be in specified format", + "fullDescription": "DiagnosticId for analyzers must be in specified format.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1019": { + "id": "RS1019", + "shortDescription": "DiagnosticId must be unique across analyzers", + "fullDescription": "DiagnosticId must be unique across analyzers.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS1020": { + "id": "RS1020", + "shortDescription": "Category for analyzers must be from the specified values", + "fullDescription": "Category for analyzers must be from the specified values.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": false, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1021": { + "id": "RS1021", + "shortDescription": "Invalid entry in analyzer category and diagnostic ID range specification file", + "fullDescription": "Invalid entry in analyzer category and diagnostic ID range specification file.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1024": { + "id": "RS1024", + "shortDescription": "Symbols should be compared for equality", + "fullDescription": "Symbols should be compared for equality, not identity. Use an overload accepting an 'IEqualityComparer' and pass 'SymbolEqualityComparer'.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CompareSymbolsCorrectlyAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1025": { + "id": "RS1025", + "shortDescription": "Configure generated code analysis", + "fullDescription": "Configure generated code analysis", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "ConfigureGeneratedCodeAnalysisAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1026": { + "id": "RS1026", + "shortDescription": "Enable concurrent execution", + "fullDescription": "Enable concurrent execution", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "EnableConcurrentExecutionAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1027": { + "id": "RS1027", + "shortDescription": "Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer", + "fullDescription": "Inherit type '{0}' from DiagnosticAnalyzer or remove the DiagnosticAnalyzerAttribute(s)", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "ClassIsNotDiagnosticAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1028": { + "id": "RS1028", + "shortDescription": "Provide non-null 'customTags' value to diagnostic descriptor constructor", + "fullDescription": "The 'customTags' value is used as a way to enable specific actions and filters on diagnostic descriptors based on the specific values of the tags. Every Roslyn analyzer should have at least one tag from the 'WellKnownDiagnosticTags' class.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDocumentation", + "isEnabledByDefault": false, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1029": { + "id": "RS1029", + "shortDescription": "Do not use reserved diagnostic IDs", + "fullDescription": "DiagnosticId for analyzers should not use reserved IDs.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1030": { + "id": "RS1030", + "shortDescription": "Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer", + "fullDescription": "'GetSemanticModel' is an expensive method to invoke within a diagnostic analyzer because it creates a completely new semantic model, which does not share compilation data with the compiler or other analyzers. This incurs an additional performance cost during semantic analysis. Instead, consider registering a different analyzer action which allows used of a shared 'SemanticModel', such as 'RegisterOperationAction', 'RegisterSyntaxNodeAction', or 'RegisterSemanticModelAction'.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "DoNotUseCompilationGetSemanticModelAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1031": { + "id": "RS1031", + "shortDescription": "Define diagnostic title correctly", + "fullDescription": "The diagnostic title should not contain a period, nor any line return character, nor any leading or trailing whitespaces", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1032": { + "id": "RS1032", + "shortDescription": "Define diagnostic message correctly", + "fullDescription": "The diagnostic message should not contain any line return character nor any leading or trailing whitespaces and should either be a single sentence without a trailing period or a multi-sentences with a trailing period", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1033": { + "id": "RS1033", + "shortDescription": "Define diagnostic description correctly", + "fullDescription": "The diagnostic description should be one or multiple sentences ending with a punctuation sign and should not have any leading or trailing whitespaces", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1034": { + "id": "RS1034", + "shortDescription": "Prefer 'IsKind' for checking syntax kinds", + "fullDescription": "Prefer 'syntax.IsKind(kind)' to 'syntax.Kind() == kind' when checking syntax kinds. Code using 'IsKind' is slightly more efficient at runtime, so consistent use of this form where applicable helps improve performance in complex analysis scenarios.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "PreferIsKindAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1037": { + "id": "RS1037", + "shortDescription": "Add \"CompilationEnd\" custom tag to compilation end diagnostic descriptor", + "fullDescription": "'DiagnosticDescriptor' assigned to field is used to report a compilation end diagnostic, but the 'DiagnosticDescriptor' constructor used to initialize it does not pass in the required custom tag \"CompilationEnd\". See documentation for 'WellKnownDiagnosticTags.CompilationEnd' for details.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisDesign", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1038": { + "id": "RS1038", + "shortDescription": "Compiler extensions should be implemented in assemblies with compiler-provided references", + "fullDescription": "Types which implement compiler extension points should not be declared in assemblies that contain references to assemblies which are not provided by all compilation scenarios. Doing so may cause the feature to behave unpredictably.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1038.md", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CompilerExtensionStrictApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1041": { + "id": "RS1041", + "shortDescription": "Compiler extensions should be implemented in assemblies targeting netstandard2.0", + "fullDescription": "Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1041.md", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CompilerExtensionTargetFrameworkAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1042": { + "id": "RS1042", + "shortDescription": "Implementations of this interface are not allowed", + "fullDescription": "The author of this interface has deprecated implementing this interface.", + "defaultLevel": "error", + "properties": { + "category": "MicrosoftCodeAnalysisCompatibility", + "isEnabledByDefault": true, + "typeName": "ImplementationIsObsoleteAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "RS1043": { + "id": "RS1043", + "shortDescription": "Do not use file types for implementing analyzers, generators, and code fixers", + "fullDescription": "Using a 'file' type is not allowed for implementing analyzers, generators, or code fixers. This can break analyzer loading on some platforms.", + "defaultLevel": "error", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "DoNotUseFileTypesForAnalyzersOrGenerators", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "RS2000": { + "id": "RS2000", + "shortDescription": "Add analyzer diagnostic IDs to analyzer release", + "fullDescription": "All supported analyzer diagnostic IDs should be part of an analyzer release.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS2001": { + "id": "RS2001", + "shortDescription": "Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release", + "fullDescription": "Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS2002": { + "id": "RS2002", + "shortDescription": "Do not add removed analyzer diagnostic IDs to unshipped analyzer release", + "fullDescription": "Entries for analyzer diagnostic IDs that are no longer reported and never shipped can be removed from unshipped analyzer release.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS2003": { + "id": "RS2003", + "shortDescription": "Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file", + "fullDescription": "Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS2004": { + "id": "RS2004", + "shortDescription": "Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers", + "fullDescription": "Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS2005": { + "id": "RS2005", + "shortDescription": "Remove duplicate entries for diagnostic ID in the same analyzer release", + "fullDescription": "Remove duplicate entries for diagnostic ID in the same analyzer release.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS2006": { + "id": "RS2006", + "shortDescription": "Remove duplicate entries for diagnostic ID between analyzer releases", + "fullDescription": "Remove duplicate entries for diagnostic ID between analyzer releases.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS2007": { + "id": "RS2007", + "shortDescription": "Invalid entry in analyzer release file", + "fullDescription": "Invalid entry in analyzer release file.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS2008": { + "id": "RS2008", + "shortDescription": "Enable analyzer release tracking", + "fullDescription": "Enabling release tracking for analyzer packages helps in tracking and documenting the analyzer diagnostics that ship and/or change with each analyzer release. See details at https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md", + "properties": { + "category": "MicrosoftCodeAnalysisReleaseTracking", + "isEnabledByDefault": true, + "typeName": "DiagnosticDescriptorCreationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.CSharp.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS1002": { + "id": "RS1002", + "shortDescription": "Missing kind argument when registering an analyzer action", + "fullDescription": "You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpRegisterActionAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1003": { + "id": "RS1003", + "shortDescription": "Unsupported SymbolKind argument when registering a symbol analyzer action", + "fullDescription": "SymbolKind '{0}' is not supported for symbol analyzer actions", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpRegisterActionAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1005": { + "id": "RS1005", + "shortDescription": "ReportDiagnostic invoked with an unsupported DiagnosticDescriptor", + "fullDescription": "ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpReportDiagnosticAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1006": { + "id": "RS1006", + "shortDescription": "Invalid type argument for DiagnosticAnalyzer's Register method", + "fullDescription": "DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's 'TLanguageKindEnumName' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpRegisterActionAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1008": { + "id": "RS1008", + "shortDescription": "Avoid storing per-compilation data into the fields of a diagnostic analyzer", + "fullDescription": "Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using 'AnalysisContext.RegisterCompilationStartAction' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "CSharpDiagnosticAnalyzerFieldsAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1012": { + "id": "RS1012", + "shortDescription": "Start action has no registered actions", + "fullDescription": "An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers:\u000a1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit.\u000a2. If required, define and initialize state in the start action.\u000a3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction.\u000a4. If required, register an end action to report diagnostics based on the final state.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "CSharpRegisterActionAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1013": { + "id": "RS1013", + "shortDescription": "Start action has no registered non-end actions", + "fullDescription": "An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers:\u000a1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit.\u000a2. If required, define and initialize state in the start action.\u000a3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction.\u000a4. If required, register an end action to report diagnostics based on the final state.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "CSharpRegisterActionAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1022": { + "id": "RS1022", + "shortDescription": "Do not use types from Workspaces assembly in an analyzer", + "fullDescription": "Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1022.md", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpDiagnosticAnalyzerApiUsageAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS1023": { + "id": "RS1023", + "shortDescription": "Upgrade MSBuildWorkspace", + "fullDescription": "MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes.", + "defaultLevel": "warning", + "helpUri": "https://go.microsoft.com/fwlink/?linkid=874285", + "properties": { + "category": "Library", + "isEnabledByDefault": true, + "typeName": "CSharpUpgradeMSBuildWorkspaceAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1035": { + "id": "RS1035", + "shortDescription": "Do not use APIs banned for analyzers", + "fullDescription": "The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.", + "defaultLevel": "error", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpSymbolIsBannedInAnalyzersAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1036": { + "id": "RS1036", + "shortDescription": "Specify analyzer banned API enforcement setting", + "fullDescription": "A project containing analyzers or source generators should specify the property 'true'.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpSymbolIsBannedInAnalyzersAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1039": { + "id": "RS1039", + "shortDescription": "This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'", + "fullDescription": "Calling 'SemanticModel.GetDeclaredSymbol' with on certain types inheriting from 'SyntaxNode', for example 'GlobalStatementSyntax' and 'IncompleteMemberSyntax' will always return 'null'.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1040": { + "id": "RS1040", + "shortDescription": "This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null'", + "fullDescription": "Calling 'SemanticModel.GetDeclaredSymbol' with an argument of type 'FieldDeclarationSyntax' or 'EventFieldDeclarationSyntax' will always return 'null'. Call 'GetDeclaredSymbol' with the variable declarators from the field instead.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.VisualBasic.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS1002": { + "id": "RS1002", + "shortDescription": "Missing kind argument when registering an analyzer action", + "fullDescription": "You must specify at least one syntax, symbol or operation kind when registering a syntax, symbol, or operation analyzer action respectively. Otherwise, the registered action will never be invoked during analysis.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicRegisterActionAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1003": { + "id": "RS1003", + "shortDescription": "Unsupported SymbolKind argument when registering a symbol analyzer action", + "fullDescription": "SymbolKind '{0}' is not supported for symbol analyzer actions", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicRegisterActionAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1005": { + "id": "RS1005", + "shortDescription": "ReportDiagnostic invoked with an unsupported DiagnosticDescriptor", + "fullDescription": "ReportDiagnostic should only be invoked with supported DiagnosticDescriptors that are returned from DiagnosticAnalyzer.SupportedDiagnostics property. Otherwise, the reported diagnostic will be filtered out by the analysis engine.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicReportDiagnosticAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1006": { + "id": "RS1006", + "shortDescription": "Invalid type argument for DiagnosticAnalyzer's Register method", + "fullDescription": "DiagnosticAnalyzer's language-specific Register methods, such as RegisterSyntaxNodeAction, RegisterCodeBlockStartAction and RegisterCodeBlockEndAction, expect a language-specific 'SyntaxKind' type argument for it's 'TLanguageKindEnumName' type parameter. Otherwise, the registered analyzer action can never be invoked during analysis.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicRegisterActionAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1008": { + "id": "RS1008", + "shortDescription": "Avoid storing per-compilation data into the fields of a diagnostic analyzer", + "fullDescription": "Instance of a diagnostic analyzer might outlive the lifetime of compilation. Hence, storing per-compilation data, such as symbols, into the fields of a diagnostic analyzer might cause stale compilations to stay alive and cause memory leaks. Instead, you should store this data on a separate type instantiated in a compilation start action, registered using 'AnalysisContext.RegisterCompilationStartAction' API. An instance of this type will be created per-compilation and it won't outlive compilation's lifetime, hence avoiding memory leaks.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "BasicDiagnosticAnalyzerFieldsAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1012": { + "id": "RS1012", + "shortDescription": "Start action has no registered actions", + "fullDescription": "An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers:\u000a1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit.\u000a2. If required, define and initialize state in the start action.\u000a3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction.\u000a4. If required, register an end action to report diagnostics based on the final state.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "BasicRegisterActionAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1013": { + "id": "RS1013", + "shortDescription": "Start action has no registered non-end actions", + "fullDescription": "An analyzer start action enables performing stateful analysis over a given code unit, such as a code block, compilation, etc. Careful design is necessary to achieve efficient analyzer execution without memory leaks. Use the following guidelines for writing such analyzers:\u000a1. Define a new scope for the registered start action, possibly with a private nested type for analyzing each code unit.\u000a2. If required, define and initialize state in the start action.\u000a3. Register at least one non-end action that refers to this state in the start action. If no such action is necessary, consider replacing the start action with a non-start action. For example, a CodeBlockStartAction with no registered actions or only a registered CodeBlockEndAction should be replaced with a CodeBlockAction.\u000a4. If required, register an end action to report diagnostics based on the final state.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisPerformance", + "isEnabledByDefault": true, + "typeName": "BasicRegisterActionAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1022": { + "id": "RS1022", + "shortDescription": "Do not use types from Workspaces assembly in an analyzer", + "fullDescription": "Diagnostic analyzer types should not use types from Workspaces assemblies. Workspaces assemblies are only available when the analyzer executes in Visual Studio IDE live analysis, but are not available during command line build. Referencing types from Workspaces assemblies will lead to runtime exception during analyzer execution in command line build.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/roslyn-analyzers/rules/RS1022.md", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicDiagnosticAnalyzerApiUsageAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS1023": { + "id": "RS1023", + "shortDescription": "Upgrade MSBuildWorkspace", + "fullDescription": "MSBuildWorkspace has moved to the Microsoft.CodeAnalysis.Workspaces.MSBuild NuGet package and there are breaking API changes.", + "defaultLevel": "warning", + "helpUri": "https://go.microsoft.com/fwlink/?linkid=874285", + "properties": { + "category": "Library", + "isEnabledByDefault": true, + "typeName": "VisualBasicUpgradeMSBuildWorkspaceAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1035": { + "id": "RS1035", + "shortDescription": "Do not use APIs banned for analyzers", + "fullDescription": "The symbol has been marked as banned for use in analyzers, and an alternate should be used instead.", + "defaultLevel": "error", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicSymbolIsBannedInAnalyzersAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS1036": { + "id": "RS1036", + "shortDescription": "Specify analyzer banned API enforcement setting", + "fullDescription": "A project containing analyzers or source generators should specify the property 'true'.", + "defaultLevel": "warning", + "properties": { + "category": "MicrosoftCodeAnalysisCorrectness", + "isEnabledByDefault": true, + "typeName": "BasicSymbolIsBannedInAnalyzersAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md new file mode 100644 index 0000000000000..696057f8206ad --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md @@ -0,0 +1,159 @@ +# Release tracking analyzer + +## Overview + +Release tracking analyzer enables third party analyzer packages to define analyzer releases with associated versions. Each release can track the following changes: + +1. Additions: Set of new analyzer rules that shipped for the first time in this release. +2. Removals: Set of old analyzer rules that shipped in an earlier release, but are removed starting this release. +3. Changes: Set of existing analyzer rules that shipped in an earlier release, but one of the following attributes of the diagnostic changed in this release: + 1. Category of the diagnostic. + 2. Default severity of the diagnostic + 3. Enabled by default status. + +This analyzer expects that the third party analyzer project has two additional files, namely `AnalyzerReleases.Unshipped.md` and `AnalyzerReleases.Shipped.md`, to track this release specific metadata. + +1. `AnalyzerReleases.Unshipped.md`: Additional file for the upcoming or unshipped analyzer release. This file will start empty at the beginning of each release. While working on the upcoming release, it will track additions/removals/changes to analyzer rules in the repo. +2. `AnalyzerReleases.Shipped.md`: Additional file for shipped analyzer releases. This file only tracks analyzer rules for shipped releases. It cannot be changed while work is in progress for upcoming release. When a new analyzer package is released, a new release with shipped package version should be created in the shipped file, and all the entries from the unshipped file should be moved under the new shipped release. + +Note that each analyzer project that contributes an assembly to the analyzer NuGet package would require these additional files. + +Release tracking analyzer provides the diagnostics and code fixes to enable analyzer authors to maintain the shipped and unshipped files. Analyzer author only requires to perform the following two manual tasks: + +1. Create these two files and pass mark them as additional files for their analyzer project (steps explained in next section), and +2. Move all the entries from unshipped file to the shipped file after each release. + +## Example + +Consider the following example: + +1. `AnalyzerReleases.Shipped.md`: + + ```md + ## Release 1.0 + + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA1000 | Design | Warning | CA1000_AnalyzerName, [Documentation](CA1000_Documentation_Link) + CA2000 | Security | Info | CA2000_AnalyzerName, [Documentation](CA2000_Documentation_Link) + CA3000 | Usage | Disabled | CA3000_AnalyzerName, [Documentation](CA3000_Documentation_Link) + + + ## Release 2.0 + + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA4000 | Design | Warning | CA4000_AnalyzerName, [Documentation](CA4000_Documentation_Link) + + ### Removed Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA3000 | Usage | Disable | CA3000_AnalyzerName, [Documentation](CA3000_Documentation_Link) + + ### Changed Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA2000 | Security | Disabled | CA2000_AnalyzerName, [Documentation](CA2000_Documentation_Link) + ``` + +2. `AnalyzerReleases.Unshipped.md`: + + ```md + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA5000 | Security | Warning | CA5000_AnalyzerName + CA6000 | Design | Warning | CA6000_AnalyzerName + ``` + +Analyzer author has shipped 2 analyzer releases: + +1. Version 1.0 shipped three rules: CA1000, CA2000 and CA3000. +2. Version 2.0 changed the default severity of a shipped rule CA2000 from 'Warning' to 'Disabled'. It removed a shipped rule CA3000 and added a new rule CA4000. +3. Upcoming release will add 2 new rules CA5000 and CA6000. + +When the next release is shipped, say version '3.0', a new release section for 3.0 should be created at the end of the shipped file and the entries from unshipped file should be moved under it. The files will change as below: + +1. `AnalyzerReleases.Shipped.md`: + + ```md + ## Release 1.0 + + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA1000 | Design | Warning | CA1000_AnalyzerName, [Documentation](CA1000_Documentation_Link) + CA2000 | Security | Info | CA2000_AnalyzerName, [Documentation](CA2000_Documentation_Link) + CA3000 | Usage | Disabled | CA3000_AnalyzerName, [Documentation](CA3000_Documentation_Link) + + + ## Release 2.0 + + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA4000 | Design | Warning | CA4000_AnalyzerName, [Documentation](CA4000_Documentation_Link) + + ### Removed Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA3000 | Usage | Disable | CA3000_AnalyzerName, [Documentation](CA3000_Documentation_Link) + + ### Changed Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA2000 | Security | Disabled | CA2000_AnalyzerName, [Documentation](CA2000_Documentation_Link) + + + ## Release 3.0 + + ### New Rules + + Rule ID | Category | Severity | Notes + --------|----------|----------|-------------------- + CA5000 | Security | Warning | CA5000_AnalyzerName + CA6000 | Design | Warning | CA6000_AnalyzerName + ``` + +2. `AnalyzerReleases.Unshipped.md` (empty): + + ```md + ``` + +## How to enable Release Tracking analyzer + +The following additional files have to be added to any project referencing this package to enable analysis: + +- `AnalyzerReleases.Shipped.md` +- `AnalyzerReleases.Unshipped.md` + +This can be done in couple of ways, which is detailed in below sections. + +### Using the light bulb + +![Enable analyzer release tracking](./enable-analyzer-release-tracking.png) + +### Manual steps + +This can be done by: + +- In Visual Studio, right-click the project in Solution Explorer, choose "Add -> New Item...", and then select "Text File" in the "Add New Item" dialog. Then right-click each file, select "Properties", and choose "C# analyzer additional file" for "Build Action" in the "Properties" window. +- Or, create these two files at the location you desire, then add the following text to your project/target file (replace file path with its actual location): + + ```xml + + + + + ``` diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..2bb2274872a43 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/RulesMissingDocumentation.md @@ -0,0 +1,55 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +RS1001 | | Missing diagnostic analyzer attribute | +RS1002 | | Missing kind argument when registering an analyzer action | +RS1003 | | Unsupported SymbolKind argument when registering a symbol analyzer action | +RS1004 | | Recommend adding language support to diagnostic analyzer | +RS1005 | | ReportDiagnostic invoked with an unsupported DiagnosticDescriptor | +RS1006 | | Invalid type argument for DiagnosticAnalyzer's Register method | +RS1007 | | Provide localizable arguments to diagnostic descriptor constructor | +RS1008 | | Avoid storing per-compilation data into the fields of a diagnostic analyzer | +RS1009 | | Only internal implementations of this interface are allowed | +RS1010 | | Create code actions should have a unique EquivalenceKey for FixAll occurrences support | +RS1011 | | Use code actions that have a unique EquivalenceKey for FixAll occurrences support | +RS1012 | | Start action has no registered actions | +RS1013 | | Start action has no registered non-end actions | +RS1014 | | Do not ignore values returned by methods on immutable objects | +RS1015 | | Provide non-null 'helpLinkUri' value to diagnostic descriptor constructor | +RS1016 | | Code fix providers should provide FixAll support | +RS1017 | | DiagnosticId for analyzers must be a non-null constant | +RS1018 | | DiagnosticId for analyzers must be in specified format | +RS1019 | | DiagnosticId must be unique across analyzers | +RS1020 | | Category for analyzers must be from the specified values | +RS1021 | | Invalid entry in analyzer category and diagnostic ID range specification file | +RS1022 | | Do not use types from Workspaces assembly in an analyzer | +RS1024 | | Symbols should be compared for equality | +RS1025 | | Configure generated code analysis | +RS1026 | | Enable concurrent execution | +RS1027 | | Types marked with DiagnosticAnalyzerAttribute(s) should inherit from DiagnosticAnalyzer | +RS1028 | | Provide non-null 'customTags' value to diagnostic descriptor constructor | +RS1029 | | Do not use reserved diagnostic IDs | +RS1030 | | Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer | +RS1031 | | Define diagnostic title correctly | +RS1032 | | Define diagnostic message correctly | +RS1033 | | Define diagnostic description correctly | +RS1034 | | Prefer 'IsKind' for checking syntax kinds | +RS1035 | | Do not use APIs banned for analyzers | +RS1036 | | Specify analyzer banned API enforcement setting | +RS1037 | | Add "CompilationEnd" custom tag to compilation end diagnostic descriptor | +RS1038 | | Compiler extensions should be implemented in assemblies with compiler-provided references | +RS1039 | | This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' | +RS1040 | | This call to 'SemanticModel.GetDeclaredSymbol()' will always return 'null' | +RS1041 | | Compiler extensions should be implemented in assemblies targeting netstandard2.0 | +RS1042 | | Implementations of this interface are not allowed | +RS1043 | | Do not use file types for implementing analyzers, generators, and code fixers | +RS2000 | | Add analyzer diagnostic IDs to analyzer release | +RS2001 | | Ensure up-to-date entry for analyzer diagnostic IDs are added to analyzer release | +RS2002 | | Do not add removed analyzer diagnostic IDs to unshipped analyzer release | +RS2003 | | Shipped diagnostic IDs that are no longer reported should have an entry in the 'Removed Rules' table in unshipped file | +RS2004 | | Diagnostic IDs marked as removed in analyzer release file should not be reported by analyzers | +RS2005 | | Remove duplicate entries for diagnostic ID in the same analyzer release | +RS2006 | | Remove duplicate entries for diagnostic ID between analyzer releases | +RS2007 | | Invalid entry in analyzer release file | +RS2008 | | Enable analyzer release tracking | diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Microsoft.CodeAnalysis.Analyzers.Setup.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Microsoft.CodeAnalysis.Analyzers.Setup.csproj new file mode 100644 index 0000000000000..49e46246f52ee --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Microsoft.CodeAnalysis.Analyzers.Setup.csproj @@ -0,0 +1,47 @@ + + + + net472 + + + *$(MSBuildProjectFile)* + + true + false + false + false + false + false + true + + + + + Microsoft.CodeAnalysis.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Microsoft.CodeAnalysis.CSharp.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Microsoft.CodeAnalysis.VisualBasic.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Properties/launchSettings.json b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Properties/launchSettings.json new file mode 100644 index 0000000000000..183040856937b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Visual Studio Extension": { + "executablePath": "$(DevEnvDir)devenv.exe", + "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..6bb0a572df826 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,27 @@ + + + + + + .NET Compiler Platform (Roslyn) Analyzers + Analyzers for .NET Compiler Platform (Roslyn) APIs. + EULA.txt + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.Fixer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.Fixer.cs new file mode 100644 index 0000000000000..fcb42175bea21 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.Fixer.cs @@ -0,0 +1,584 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.FixAnalyzers.FixerWithFixAllAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.FixerWithFixAllFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.FixAnalyzers.FixerWithFixAllAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.FixerWithFixAllFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.FixAnalyzers +{ + public class FixerWithFixAllFixerTests + { + #region CSharp tests + + [Fact] + public async Task CSharp_VerifyFix_NonSealedTypeAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class {|RS1016:C1|} : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + return null; + } +} +"; + var fixedSource = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } + + public sealed override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task CSharp_VerifyFix_SealedTypeAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +sealed class {|RS1016:C1|} : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + return null; + } +} +"; + var fixedSource = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +sealed class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task CSharp_NoDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } +} + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C2))] +class C2 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override FixAllProvider GetFixAllProvider() + { + return null; + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } +} +"; + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task CSharp_VerifyFixAllAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +sealed class {|RS1016:C1|} : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + return null; + } +} + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C2))] +sealed class {|RS1016:C2|} : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + return null; + } +} +"; + var fixedSource = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +sealed class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } +} + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C2))] +sealed class C2 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document))|}; + return null; + } + + public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + #endregion + + #region VisualBasic tests + + [Fact()] + public async Task VisualBasic_VerifyFix_NonSealedTypeAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class {|RS1016:C1|} + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Return Nothing + End Function +End Class +"; + var fixedSource = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public NotOverridable Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer + End Function +End Class +"; + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task VisualBasic_VerifyFix_SealedTypeAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +NotInheritable Class {|RS1016:C1|} + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Return Nothing + End Function +End Class +"; + var fixedSource = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +NotInheritable Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer + End Function +End Class +"; + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer + End Function +End Class + + +Class C2 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public Overrides Function GetFixAllProvider() As FixAllProvider + Return Nothing + End Function +End Class +"; + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task VisualBasic_VerifyFixAllAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +NotInheritable Class {|RS1016:C1|} + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Return Nothing + End Function +End Class + + +NotInheritable Class {|RS1016:C2|} + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Return Nothing + End Function +End Class +"; + var fixedSource = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +NotInheritable Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer + End Function +End Class + + +NotInheritable Class C2 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = {|RS1010:CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document))|} + Return Nothing + End Function + + Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs new file mode 100644 index 0000000000000..2390c083e2d81 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/FixAnalyzers/FixerWithFixAllAnalyzerTests.cs @@ -0,0 +1,770 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Analyzers.FixAnalyzers; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.FixAnalyzers.FixerWithFixAllAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.FixerWithFixAllFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.FixAnalyzers.FixerWithFixAllAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.FixerWithFixAllFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.FixAnalyzers +{ + public class FixerWithFixAllAnalyzerTests + { + #region CSharp tests + + private const string CSharpCustomCodeActions = @" +public class MyCodeActionNoEquivalenceKey : CodeAction +{ + public override string Title + { + get + { + throw new NotImplementedException(); + } + } +} + +public class MyCodeActionWithEquivalenceKey : CodeAction +{ + public override string Title + { + get + { + throw new NotImplementedException(); + } + } + + public override string EquivalenceKey + { + get + { + return ""DummyEquivalenceKey""; + } + } +} + +public abstract class MyAbstractCodeActionWithEquivalenceKey : CodeAction +{ + public override string Title + { + get + { + throw new NotImplementedException(); + } + } + + public override string EquivalenceKey + { + get + { + return ""DummyEquivalenceKey""; + } + } +} + +public class MyDerivedCodeActionWithEquivalenceKey : MyAbstractCodeActionWithEquivalenceKey +{ +} +"; + private async Task TestCSharpCoreAsync(string source, DiagnosticResult missingGetFixAllProviderOverrideDiagnostic, + bool withCustomCodeActions = false, params DiagnosticResult[] expected) + { + var fixAllProviderString = @"public override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + }"; + + var sourceSuffix = @" +}"; + + if (withCustomCodeActions) + { + sourceSuffix += CSharpCustomCodeActions; + } + + // Verify expected diagnostics for fixer that supports FixAllProvider. + await VerifyCS.VerifyAnalyzerAsync(source + fixAllProviderString + sourceSuffix, expected); + + // Verify RS1016 (OverrideGetFixAllProviderRule) diagnostic for fixer that does not support FixAllProvider. + expected = new[] { missingGetFixAllProviderOverrideDiagnostic }; + await VerifyCS.VerifyAnalyzerAsync(source + sourceSuffix, expected); + } + + [Fact] + public async Task CSharp_CodeActionCreate_VerifyDiagnosticsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Regular cases. + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + var codeAction1_2 = CodeAction.Create(""Title1_2"", createChangedDocument: _ => Task.FromResult(context.Document)); + var codeAction1_3 = CodeAction.Create(createChangedDocument: _ => Task.FromResult(context.Document), title: ""Title1_3""); + + // Null argument for equivalenceKey. + var codeAction2_1 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), null); + var codeAction2_2 = CodeAction.Create(createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: null, title: ""Title2_2""); + var codeAction2_3 = CodeAction.Create(""Title2_3"", _ => Task.FromResult(context.Document), equivalenceKey: null); + + return null; + } +"; + + var expected = new[] + { + // Test0.cs(23,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(23, 29), + // Test0.cs(24,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(24, 29), + // Test0.cs(25,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(25, 29), + // Test0.cs(28,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(28, 29), + // Test0.cs(29,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(29, 29), + // Test0.cs(30,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(30, 29) + }; + + // Test0.cs(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + await TestCSharpCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, expected: expected); + } + + [Fact] + public async Task CSharp_CodeActionCreate_NoDiagnosticsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Overload resolution failure cases. + var codeAction1_1 = CodeAction.{|CS1501:Create|}(""Title1_1""); + var codeAction1_2 = CodeAction.{|CS7036:Create|}(createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: null); + + // Correct non-null arguments + var equivalenceKey = ""equivalenceKey""; + var codeAction2_1 = CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), equivalenceKey); + var codeAction2_2 = CodeAction.Create(title: ""Title2_2"", createChangedDocument: _ => Task.FromResult(context.Document), equivalenceKey: equivalenceKey); + var codeAction2_3 = CodeAction.Create(equivalenceKey: equivalenceKey, title: ""Title2_3"", createChangedDocument: _ => Task.FromResult(context.Document)); + + // Conservative no diagnostic cases. + string nullKey = null; + var codeAction3_1 = CodeAction.Create(""Title3_1"", _ => Task.FromResult(context.Document), nullKey); + var codeAction3_2 = CodeAction.Create(""Title3_1"", _ => Task.FromResult(context.Document), GetKey()); + + context.RegisterCodeFix(codeAction1_1, context.Diagnostics); + context.RegisterCodeFix(codeAction1_2, context.Diagnostics); + + context.RegisterCodeFix(codeAction2_1, context.Diagnostics); + context.RegisterCodeFix(codeAction2_2, context.Diagnostics); + context.RegisterCodeFix(codeAction2_3, context.Diagnostics); + + context.RegisterCodeFix(codeAction3_1, context.Diagnostics); + context.RegisterCodeFix(codeAction3_2, context.Diagnostics); + + return null; + } + + private string GetKey() + { + return null; + } +"; + + // Test0.cs(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestCSharpCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic); + } + + [Fact] + public async Task CSharp_CodeActionCreate_DiagnosticsOnExportedCodeFixProviderTypeAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C2))] +class C2 : C1 +{ +} + +abstract class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction1_1 = CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document)); + return null; + } +"; + var expected = new[] + { + // Test0.cs(26,29): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(26, 29) + }; + + // Test0.cs(10,16): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C2"); + + await TestCSharpCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, expected: expected); + } + + [Fact] + public async Task CSharp_CustomCodeAction_VerifyDiagnosticsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var codeAction = new MyCodeActionNoEquivalenceKey(); + return null; + } +"; + + var expected = new[] + { + // Test0.cs(22,26): warning RS1011: 'MyCodeActionNoEquivalenceKey' has the default value of 'null' for property 'EquivalenceKey'. Either override this property on 'MyCodeActionNoEquivalenceKey' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + GetCSharpOverrideCodeActionEquivalenceKeyExpectedDiagnostic(22, 26, "MyCodeActionNoEquivalenceKey") + }; + + // Test0.cs(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestCSharpCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, withCustomCodeActions: true, expected: expected); + } + + [Fact] + public async Task CSharp_CustomCodeAction_NoDiagnosticsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + CodeAction codeAction = new MyCodeActionWithEquivalenceKey(); + context.RegisterCodeFix(codeAction, context.Diagnostics); + + codeAction = new MyDerivedCodeActionWithEquivalenceKey(); + context.RegisterCodeFix(codeAction, context.Diagnostics); + return null; + } + + private string GetKey() + { + return null; + } +"; + // Test0.cs(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestCSharpCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, withCustomCodeActions: true); + } + + [Fact, WorkItem(3475, "https://github.com/dotnet/roslyn-analyzers/issues/3475")] + public async Task CSharp_CodeActionCreateNestedActions_NoDiagnosticsAsync() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.Default.AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.CodeAnalysis", "3.3.0"))), + TestCode = @" +using System; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeActions; + +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(C1))] +class C1 : CodeFixProvider +{ + public override ImmutableArray FixableDiagnosticIds + { + get + { + throw new NotImplementedException(); + } + } + + public override FixAllProvider GetFixAllProvider() + { + throw new NotImplementedException(); + } + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var c1 = CodeAction.Create( + ""Title1"", + ImmutableArray.Create( + CodeAction.Create(""Title1_1"", _ => Task.FromResult(context.Document), equivalenceKey: ""Title1_1""), + {|#0:CodeAction.Create(""Title1_2"", _ => Task.FromResult(context.Document))|} + ), + false); + + var c2 = CodeAction.Create( + ""Title2"", + ImmutableArray.Create( + CodeAction.Create(""Title2_1"", _ => Task.FromResult(context.Document), equivalenceKey: ""Title2_1""), + {|#1:CodeAction.Create(""Title2_2"", _ => Task.FromResult(context.Document))|} + ), + true); + + return null; + } +}", + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(FixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule).WithLocation(0).WithArguments("equivalenceKey"), + VerifyCS.Diagnostic(FixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule).WithLocation(1).WithArguments("equivalenceKey"), + } + }.RunAsync(); + } + + #endregion + + #region VisualBasic tests + + private const string VisualBasicCustomCodeActions = @" + +Public Class MyCodeActionNoEquivalenceKey + Inherits CodeAction + Public Overrides ReadOnly Property Title() As String + Get + Throw New NotImplementedException() + End Get + End Property +End Class + +Public Class MyCodeActionWithEquivalenceKey + Inherits CodeAction + Public Overrides ReadOnly Property Title() As String + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides ReadOnly Property EquivalenceKey() As String + Get + Return ""DummyEquivalenceKey"" + End Get + End Property +End Class + +Public MustInherit Class MyAbstractCodeActionWithEquivalenceKey + Inherits CodeAction + Public Overrides ReadOnly Property Title() As String + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides ReadOnly Property EquivalenceKey() As String + Get + Return ""DummyEquivalenceKey"" + End Get + End Property +End Class + +Public Class MyDerivedCodeActionWithEquivalenceKey + Inherits MyAbstractCodeActionWithEquivalenceKey +End Class +"; + private async Task TestBasicCoreAsync(string source, DiagnosticResult missingGetFixAllProviderOverrideDiagnostic, + bool withCustomCodeActions = false, params DiagnosticResult[] expected) + { + var fixAllProviderString = @"Public Overrides Function GetFixAllProvider() As FixAllProvider + Return WellKnownFixAllProviders.BatchFixer +End Function +"; + + var sourceSuffix = @" +End Class +"; + + if (withCustomCodeActions) + { + sourceSuffix += VisualBasicCustomCodeActions; + } + + // Verify expected diagnostics for fixer that supports FixAllProvider. + await VerifyVB.VerifyAnalyzerAsync(source + fixAllProviderString + sourceSuffix, expected); + + // Verify RS1016 (OverrideGetFixAllProviderRule) diagnostic for fixer that does not support FixAllProvider. + expected = new[] { missingGetFixAllProviderOverrideDiagnostic }; + await VerifyVB.VerifyAnalyzerAsync(source + sourceSuffix, expected); + } + + [Fact] + public async Task VisualBasic_CodeActionCreate_VerifyDiagnosticsAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Regular cases. + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Dim codeAction1_2 = CodeAction.Create(""Title1_2"", createChangedDocument := Function(x) Task.FromResult(context.Document)) + Dim codeAction1_3 = CodeAction.Create(createChangedDocument := Function(x) Task.FromResult(context.Document), title := ""Title1_3"") + + ' Null argument for equivalenceKey. + Dim codeAction2_1 = CodeAction.Create(""Title2_1"", Function(x) Task.FromResult(context.Document), Nothing) + Dim codeAction2_2 = CodeAction.Create(createChangedDocument := Function(x) Task.FromResult(context.Document), equivalenceKey := Nothing, title := ""Title2_2"") + Dim codeAction2_3 = CodeAction.Create(""Title2_3"", Function(x) Task.FromResult(context.Document), equivalenceKey := Nothing) + + Return Nothing + End Function +"; + + var expected = new[] + { + // Test0.vb(20,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(20, 23), + // Test0.vb(21,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(21, 23), + // Test0.vb(22,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(22, 23), + // Test0.vb(25,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(25, 23), + // Test0.vb(26,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(26, 23), + // Test0.vb(27,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(27, 23) + }; + + // Test0.vb(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetBasicOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestBasicCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, expected: expected); + } + + [Fact] + public async Task VisualBasic_CodeActionCreate_NoDiagnosticsAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + ' Overload resolution failure cases. + Dim codeAction1_1 = CodeAction.{|BC30516:Create|}(""Title1_1"") + Dim codeAction1_2 = CodeAction.{|BC30518:Create|}(createChangedDocument := Function(x) Task.FromResult(context.Document), equivalenceKey := Nothing) + + ' Correct non-null arguments + Dim equivalenceKey = ""equivalenceKey"" + Dim codeAction2_1 = CodeAction.Create(""Title2_1"", Function(x) Task.FromResult(context.Document), equivalenceKey) + Dim codeAction2_2 = CodeAction.Create(title := ""Title2_2"", createChangedDocument := Function(x) Task.FromResult(context.Document), equivalenceKey := equivalenceKey) + Dim codeAction2_3 = CodeAction.Create(equivalenceKey := equivalenceKey, title := ""Title2_3"", createChangedDocument := Function(x) Task.FromResult(context.Document)) + + ' Conservative no diagnostic cases. + Dim nullKey As String = Nothing + Dim codeAction3_1 = CodeAction.Create(""Title3_1"", Function(x) Task.FromResult(context.Document), nullKey) + Dim codeAction3_2 = CodeAction.Create(""Title3_1"", Function(x) Task.FromResult(context.Document), GetKey()) + + context.RegisterCodeFix(codeAction1_1, context.Diagnostics) + context.RegisterCodeFix(codeAction1_2, context.Diagnostics) + + context.RegisterCodeFix(codeAction2_1, context.Diagnostics) + context.RegisterCodeFix(codeAction2_2, context.Diagnostics) + context.RegisterCodeFix(codeAction2_3, context.Diagnostics) + + context.RegisterCodeFix(codeAction3_1, context.Diagnostics) + context.RegisterCodeFix(codeAction3_2, context.Diagnostics) + + Return Nothing + End Function + + Private Function GetKey() As String + Return Nothing + End Function +"; + // Test0.vb(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetBasicOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestBasicCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic); + } + + [Fact] + public async Task VisualBasic_CodeActionCreate_DiagnosticsOnExportedCodeFixProviderTypeAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C2 + Inherits C1 +End Class + +MustInherit Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + Dim codeAction1_1 = CodeAction.Create(""Title1_1"", Function(x) Task.FromResult(context.Document)) + Return Nothing + End Function + + Private Function GetKey() As String + Return Nothing + End Function +"; + var expected = new[] + { + // Test0.vb(23,23): warning RS1010: Provide an explicit argument for optional parameter 'equivalenceKey', which is non-null and unique across all code actions created by this fixer. + GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(23, 23) + }; + + // Test0.vb(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetBasicOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C2"); + + await TestBasicCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, expected: expected); + } + + [Fact] + public async Task VisualBasic_CustomCodeAction_VerifyDiagnosticsAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + Dim codeAction = New MyCodeActionNoEquivalenceKey() + Return Nothing + End Function +"; + + var expected = new[] + { + // Test0.vb(19,20): warning RS1011: 'MyCodeActionNoEquivalenceKey' has the default value of 'null' for property 'EquivalenceKey'. Either override this property on 'MyCodeActionNoEquivalenceKey' to return a non-null and unique value across all code actions per-fixer or use such an existing code action. + GetBasicOverrideCodeActionEquivalenceKeyExpectedDiagnostic(19, 20, "MyCodeActionNoEquivalenceKey") + }; + + // Test0.vb(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetBasicOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestBasicCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, withCustomCodeActions: true, expected: expected); + } + + [Fact] + public async Task VisualBasic_CustomCodeAction_NoDiagnosticsAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.CodeActions + + +Class C1 + Inherits CodeFixProvider + Public Overrides ReadOnly Property FixableDiagnosticIds() As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Function RegisterCodeFixesAsync(context As CodeFixContext) As Task + Dim codeAction As CodeAction = New MyCodeActionWithEquivalenceKey() + context.RegisterCodeFix(codeAction, context.Diagnostics) + + codeAction = New MyDerivedCodeActionWithEquivalenceKey() + context.RegisterCodeFix(codeAction, context.Diagnostics) + + Return Nothing + End Function + + Private Function GetKey() As String + Return Nothing + End Function +"; + // Test0.vb(10,7): warning RS1016: 'C1' registers one or more code fixes, but does not override the method 'CodeFixProvider.GetFixAllProvider'. Override this method and provide a non-null FixAllProvider for FixAll support, potentially 'WellKnownFixAllProviders.BatchFixer', or 'null' to explicitly disable FixAll support. + var missingGetFixAllProviderOverrideDiagnostic = GetBasicOverrideGetFixAllProviderExpectedDiagnostic(10, 7, "C1"); + + await TestBasicCoreAsync(source, missingGetFixAllProviderOverrideDiagnostic, withCustomCodeActions: true); + } + + #endregion + + private static DiagnosticResult GetCSharpOverrideCodeActionEquivalenceKeyExpectedDiagnostic(int line, int column, string customCodeActionName) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyCS.Diagnostic(FixerWithFixAllAnalyzer.OverrideCodeActionEquivalenceKeyRule).WithLocation(line, column).WithArguments(customCodeActionName, nameof(CodeAction.EquivalenceKey)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetBasicOverrideCodeActionEquivalenceKeyExpectedDiagnostic(int line, int column, string customCodeActionName) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyVB.Diagnostic(FixerWithFixAllAnalyzer.OverrideCodeActionEquivalenceKeyRule).WithLocation(line, column).WithArguments(customCodeActionName, nameof(CodeAction.EquivalenceKey)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetCSharpCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(int line, int column) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyCS.Diagnostic(FixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule).WithLocation(line, column).WithArguments("equivalenceKey"); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetBasicCreateCodeActionWithEquivalenceKeyExpectedDiagnostic(int line, int column) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyVB.Diagnostic(FixerWithFixAllAnalyzer.CreateCodeActionEquivalenceKeyRule).WithLocation(line, column).WithArguments("equivalenceKey"); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetCSharpOverrideGetFixAllProviderExpectedDiagnostic(int line, int column, string codeFixProviderTypeName) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyCS.Diagnostic(FixerWithFixAllAnalyzer.OverrideGetFixAllProviderRule).WithLocation(line, column).WithArguments(codeFixProviderTypeName); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetBasicOverrideGetFixAllProviderExpectedDiagnostic(int line, int column, string codeFixProviderTypeName) + { +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyVB.Diagnostic(FixerWithFixAllAnalyzer.OverrideGetFixAllProviderRule).WithLocation(line, column).WithArguments(codeFixProviderTypeName); +#pragma warning restore RS0030 // Do not use banned APIs + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/ImplementationIsObsoleteAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/ImplementationIsObsoleteAnalyzerTests.cs new file mode 100644 index 0000000000000..683644ee00a8e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/ImplementationIsObsoleteAnalyzerTests.cs @@ -0,0 +1,265 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests +{ + public class ImplementationIsObsoleteAnalyzerTests + { + private const string AttributeStringCSharp = """ + + namespace System.Runtime.CompilerServices + { + internal class ImplementationIsObsoleteAttribute : System.Attribute { public ImplementationIsObsoleteAttribute(string url) { } } + } + + """; + [Fact] + public async Task CSharp_VerifySameAssemblyAsync() + { + string source = $$""" + {{AttributeStringCSharp}} + + [System.Runtime.CompilerServices.ImplementationIsObsoleteAttribute("https://example.com")] + public interface IMyInterface { } + + class SomeClass : IMyInterface { } + """; + + // Verify no diagnostic since interface is in the same assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyDifferentAssemblyAsync() + { + string source1 = AttributeStringCSharp + """ + + + [System.Runtime.CompilerServices.ImplementationIsObsoleteAttribute("https://example.com")] + public interface IMyInterface { } + + public interface IMyOtherInterface : IMyInterface { } + """; + + var source2 = """ + + class SomeClass : IMyInterface { } + + class SomeOtherClass : IMyOtherInterface { } + """; + + // Verify errors since interface is not in a friend assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + ExpectedDiagnostics = + { + // /0/Test0.cs(2,7): error RS1042: Type SomeClass cannot implement interface IMyInterface because IMyInterface is obsolete for implementation. See https://example.com for more details. + VerifyCS.Diagnostic().WithSpan(2, 7, 2, 16).WithArguments("SomeClass", "IMyInterface", "https://example.com"), + // /0/Test0.cs(4,7): error RS1042: Type SomeOtherClass cannot implement interface IMyInterface because IMyInterface is obsolete for implementation. See https://example.com for more details. + VerifyCS.Diagnostic().WithSpan(4, 7, 4, 21).WithArguments("SomeOtherClass", "IMyInterface", "https://example.com"), + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyDifferentFriendAssemblyAsync() + { + string source1 = $$""" + + [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("TestProject")] + + {{AttributeStringCSharp}} + + + [System.Runtime.CompilerServices.ImplementationIsObsoleteAttribute("https://example.com")] + public interface IMyInterface { } + + public interface IMyOtherInterface : IMyInterface { } + """; + + var source2 = """ + + class SomeClass : IMyInterface { } + + class SomeOtherClass : IMyOtherInterface { } + """; + + // Verify no diagnostic since interface is in a friend assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + }, + }.RunAsync(); + } + + private const string AttributeStringBasic = """ + + Namespace System.Runtime.CompilerServices + Friend Class ImplementationIsObsoleteAttribute + Inherits System.Attribute + + Public Sub New(url As String) + End Sub + End Class + End Namespace + + """; + + [Fact] + public async Task Basic_VerifySameAssemblyAsync() + { + string source = $""" + {AttributeStringBasic} + + + Public Interface IMyInterface + End Interface + + Class SomeClass + Implements IMyInterface + End Class + """; + + // Verify no diagnostic since interface is in the same assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + } + + [Fact] + public async Task Basic_VerifyDifferentAssemblyAsync() + { + string source1 = $""" + {AttributeStringBasic} + + + Public Interface IMyInterface + End Interface + + Public Interface IMyOtherInterface + Inherits IMyInterface + End Interface + + """; + + var source2 = """ + + Class SomeClass + Implements IMyInterface + End Class + + Class SomeOtherClass + Implements IMyOtherInterface + End Class + + """; + + // Verify errors since interface is not in a friend assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + ExpectedDiagnostics = + { + // /0/Test0.vb(2,7): error RS1042: Type SomeClass cannot implement interface IMyInterface because IMyInterface is obsolete for implementation. See https://example.com for more details. + VerifyVB.Diagnostic().WithSpan(2, 7, 2, 16).WithArguments("SomeClass", "IMyInterface", "https://example.com"), + // /0/Test0.vb(6,7): error RS1042: Type SomeOtherClass cannot implement interface IMyInterface because IMyInterface is obsolete for implementation. See https://example.com for more details. + VerifyVB.Diagnostic().WithSpan(6, 7, 6, 21).WithArguments("SomeOtherClass", "IMyInterface", "https://example.com"), + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task Basic_VerifyDifferentFriendAssemblyAsync() + { + string source1 = $""" + + {AttributeStringBasic} + + + Public Interface IMyInterface + End Interface + + Public Interface IMyOtherInterface + Inherits IMyInterface + End Interface + """; + + var source2 = @" +Class SomeClass + Implements IMyInterface +End Class + +Class SomeOtherClass + Implements IMyOtherInterface +End Class +"; + + // Verify no diagnostic since interface is in a friend assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + }, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/InternalImplementationOnlyTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/InternalImplementationOnlyTests.cs new file mode 100644 index 0000000000000..b13dd2114a491 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/InternalImplementationOnlyTests.cs @@ -0,0 +1,753 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests +{ + public class InternalImplementationOnlyTests + { + private const string AttributeStringCSharp = @" +namespace System.Runtime.CompilerServices +{ + internal class InternalImplementationOnlyAttribute : System.Attribute {} +} +"; + [Fact] + public async Task CSharp_VerifySameAssemblyAsync() + { + string source = AttributeStringCSharp + @" + +[System.Runtime.CompilerServices.InternalImplementationOnly] +public interface IMyInterface { } + +class SomeClass : IMyInterface { } +"; + + // Verify no diagnostic since interface is in the same assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyDifferentAssemblyAsync() + { + string source1 = AttributeStringCSharp + @" + +[System.Runtime.CompilerServices.InternalImplementationOnly] +public interface IMyInterface { } + +public interface IMyOtherInterface : IMyInterface { } +"; + + var source2 = @" +class SomeClass : IMyInterface { } + +class SomeOtherClass : IMyOtherInterface { }"; + + // Verify errors since interface is not in a friend assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + ExpectedDiagnostics = + { + // Test0.cs(2,7): error RS1009: Type SomeClass cannot implement interface IMyInterface because IMyInterface is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(2, 7, 2, 16).WithArguments("SomeClass", "IMyInterface"), + // Test0.cs(4,7): error RS1009: Type SomeOtherClass cannot implement interface IMyInterface because IMyInterface is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(4, 7, 4, 21).WithArguments("SomeOtherClass", "IMyInterface"), + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyDifferentFriendAssemblyAsync() + { + string source1 = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""TestProject"")] +" + AttributeStringCSharp + @" + +[System.Runtime.CompilerServices.InternalImplementationOnly] +public interface IMyInterface { } + +public interface IMyOtherInterface : IMyInterface { } +"; + + var source2 = @" +class SomeClass : IMyInterface { } + +class SomeOtherClass : IMyOtherInterface { }"; + + // Verify no diagnostic since interface is in a friend assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyISymbolAsync() + { + var source = @" +// Causes many compile errors, because not all members are implemented. +class SomeClass : Microsoft.CodeAnalysis.ISymbol { } +class SomeOtherClass : Microsoft.CodeAnalysis.IAssemblySymbol { } +"; + + // Verify that ISymbol is not implementable. + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = + { + // Test0.cs(3,7): error RS1009: Type SomeClass cannot implement interface ISymbol because ISymbol is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(3, 7, 3, 16).WithArguments("SomeClass", "ISymbol"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Accept(SymbolVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Accept(Microsoft.CodeAnalysis.SymbolVisitor)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Accept(SymbolVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Accept(Microsoft.CodeAnalysis.SymbolVisitor)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.CanBeReferencedByName' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.CanBeReferencedByName"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ContainingAssembly' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ContainingAssembly"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ContainingModule' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ContainingModule"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ContainingNamespace' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ContainingNamespace"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ContainingSymbol' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ContainingSymbol"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ContainingType' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ContainingType"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.DeclaredAccessibility' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.DeclaredAccessibility"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.DeclaringSyntaxReferences' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.DeclaringSyntaxReferences"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.GetAttributes()' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.GetAttributes()"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.GetDocumentationCommentId()' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.GetDocumentationCommentId()"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.GetDocumentationCommentXml(CultureInfo, bool, CancellationToken)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.GetDocumentationCommentXml(System.Globalization.CultureInfo, bool, System.Threading.CancellationToken)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.HasUnsupportedMetadata' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.HasUnsupportedMetadata"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsAbstract' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsAbstract"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsDefinition' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsDefinition"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsExtern' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsExtern"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsImplicitlyDeclared' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsImplicitlyDeclared"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsOverride' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsOverride"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsSealed' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsSealed"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsStatic' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsStatic"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.IsVirtual' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.IsVirtual"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Kind' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Kind"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Language' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Language"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Locations' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Locations"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.MetadataName' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.MetadataName"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.Name' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.Name"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.OriginalDefinition' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.OriginalDefinition"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ToDisplayParts(SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ToDisplayParts(Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ToDisplayString(SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ToDisplayString(Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ToMinimalDisplayParts(SemanticModel, int, SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ToMinimalDisplayParts(Microsoft.CodeAnalysis.SemanticModel, int, Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'ISymbol.ToMinimalDisplayString(SemanticModel, int, SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "Microsoft.CodeAnalysis.ISymbol.ToMinimalDisplayString(Microsoft.CodeAnalysis.SemanticModel, int, Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(3,19): error CS0535: 'SomeClass' does not implement interface member 'IEquatable.Equals(ISymbol?)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 49).WithArguments("SomeClass", "System.IEquatable.Equals(Microsoft.CodeAnalysis.ISymbol?)"), + // Test0.cs(4,7): error RS1009: Type SomeOtherClass cannot implement interface ISymbol because ISymbol is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(4, 7, 4, 21).WithArguments("SomeOtherClass", "ISymbol"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.GetMetadata()' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.GetMetadata()"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.GetTypeByMetadataName(string)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.GetTypeByMetadataName(string)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.GivesAccessTo(IAssemblySymbol)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.GivesAccessTo(Microsoft.CodeAnalysis.IAssemblySymbol)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.GlobalNamespace' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.GlobalNamespace"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.Identity' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.Identity"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.IsInteractive' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.IsInteractive"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.MightContainExtensionMethods' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.MightContainExtensionMethods"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.Modules' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.Modules"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.NamespaceNames' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.NamespaceNames"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.ResolveForwardedType(string)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.ResolveForwardedType(string)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IAssemblySymbol.TypeNames' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IAssemblySymbol.TypeNames"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Accept(SymbolVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Accept(Microsoft.CodeAnalysis.SymbolVisitor)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Accept(SymbolVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Accept(Microsoft.CodeAnalysis.SymbolVisitor)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.CanBeReferencedByName' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.CanBeReferencedByName"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ContainingAssembly' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ContainingAssembly"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ContainingModule' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ContainingModule"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ContainingNamespace' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ContainingNamespace"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ContainingSymbol' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ContainingSymbol"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ContainingType' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ContainingType"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.DeclaredAccessibility' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.DeclaredAccessibility"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.DeclaringSyntaxReferences' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.DeclaringSyntaxReferences"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.GetAttributes()' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.GetAttributes()"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.GetDocumentationCommentId()' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.GetDocumentationCommentId()"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.GetDocumentationCommentXml(CultureInfo, bool, CancellationToken)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.GetDocumentationCommentXml(System.Globalization.CultureInfo, bool, System.Threading.CancellationToken)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.HasUnsupportedMetadata' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.HasUnsupportedMetadata"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsAbstract' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsAbstract"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsDefinition' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsDefinition"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsExtern' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsExtern"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsImplicitlyDeclared' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsImplicitlyDeclared"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsOverride' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsOverride"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsSealed' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsSealed"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsStatic' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsStatic"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.IsVirtual' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.IsVirtual"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Kind' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Kind"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Language' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Language"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Locations' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Locations"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.MetadataName' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.MetadataName"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.Name' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.Name"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.OriginalDefinition' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.OriginalDefinition"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ToDisplayParts(SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ToDisplayParts(Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ToDisplayString(SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ToDisplayString(Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ToMinimalDisplayParts(SemanticModel, int, SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ToMinimalDisplayParts(Microsoft.CodeAnalysis.SemanticModel, int, Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'ISymbol.ToMinimalDisplayString(SemanticModel, int, SymbolDisplayFormat)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.ISymbol.ToMinimalDisplayString(Microsoft.CodeAnalysis.SemanticModel, int, Microsoft.CodeAnalysis.SymbolDisplayFormat)"), + // Test0.cs(4,24): error CS0535: 'SomeOtherClass' does not implement interface member 'IEquatable.Equals(ISymbol?)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 62).WithArguments("SomeOtherClass", "System.IEquatable.Equals(Microsoft.CodeAnalysis.ISymbol?)") + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyIOperationAsync() + { + var source = @" +// Causes many compile errors, because not all members are implemented. +class SomeClass : Microsoft.CodeAnalysis.IOperation { } +class SomeOtherClass : Microsoft.CodeAnalysis.Operations.IInvocationOperation { } +"; + + // Verify that IOperation is not implementable. + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = + { + // Test0.cs(3,7): error RS1009: Type SomeClass cannot implement interface IOperation because IOperation is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(3, 7, 3, 16).WithArguments("SomeClass", "IOperation"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Accept(OperationVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Accept(Microsoft.CodeAnalysis.Operations.OperationVisitor)"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Accept(OperationVisitor, TArgument)' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Accept(Microsoft.CodeAnalysis.Operations.OperationVisitor, TArgument)"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Children' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Children"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.ConstantValue' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.ConstantValue"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.IsImplicit' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.IsImplicit"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Kind' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Kind"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Language' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Language"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Parent' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Parent"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.SemanticModel' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.SemanticModel"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Syntax' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Syntax"), + // Test0.cs(3,13): error CS0535: 'SomeClass' does not implement interface member 'IOperation.Type' + DiagnosticResult.CompilerError("CS0535").WithSpan(3, 19, 3, 52).WithArguments("SomeClass", "Microsoft.CodeAnalysis.IOperation.Type"), + // Test0.cs(4,7): error RS1009: Type SomeOtherClass cannot implement interface IOperation because IOperation is not available for public implementation. + VerifyCS.Diagnostic().WithSpan(4, 7, 4, 21).WithArguments("SomeOtherClass", "IOperation"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Accept(OperationVisitor)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Accept(Microsoft.CodeAnalysis.Operations.OperationVisitor)"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Accept(OperationVisitor, TArgument)' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Accept(Microsoft.CodeAnalysis.Operations.OperationVisitor, TArgument)"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Children' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Children"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.ConstantValue' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.ConstantValue"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.IsImplicit' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.IsImplicit"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Kind' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Kind"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Language' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Language"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Parent' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Parent"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.SemanticModel' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.SemanticModel"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Syntax' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Syntax"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IOperation.Type' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.IOperation.Type"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IInvocationOperation.Arguments' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.Operations.IInvocationOperation.Arguments"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IInvocationOperation.Instance' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.Operations.IInvocationOperation.Instance"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IInvocationOperation.IsVirtual' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.Operations.IInvocationOperation.IsVirtual"), + // Test0.cs(4,13): error CS0535: 'SomeOtherClass' does not implement interface member 'IInvocationOperation.TargetMethod' + DiagnosticResult.CompilerError("CS0535").WithSpan(4, 24, 4, 78).WithArguments("SomeOtherClass", "Microsoft.CodeAnalysis.Operations.IInvocationOperation.TargetMethod") + }, + }, + }.RunAsync(); + } + + private const string AttributeStringBasic = @" +Namespace System.Runtime.CompilerServices + Friend Class InternalImplementationOnlyAttribute + Inherits System.Attribute + End Class +End Namespace +"; + + [Fact] + public async Task Basic_VerifySameAssemblyAsync() + { + string source = AttributeStringBasic + @" + + +Public Interface IMyInterface +End Interface + +Class SomeClass + Implements IMyInterface +End Class +"; + + // Verify no diagnostic since interface is in the same assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + } + + [Fact] + public async Task Basic_VerifyDifferentAssemblyAsync() + { + string source1 = AttributeStringBasic + @" + + +Public Interface IMyInterface +End Interface + +Public Interface IMyOtherInterface + Inherits IMyInterface +End Interface +"; + + var source2 = @" +Class SomeClass + Implements IMyInterface +End Class + +Class SomeOtherClass + Implements IMyOtherInterface +End Class +"; + + // Verify errors since interface is not in a friend assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + ExpectedDiagnostics = + { + // Test0.vb(2,7): error RS1009: Type SomeClass cannot implement interface IMyInterface because IMyInterface is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(2, 7, 2, 16).WithArguments("SomeClass", "IMyInterface"), + // Test0.vb(6,7): error RS1009: Type SomeOtherClass cannot implement interface IMyInterface because IMyInterface is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(6, 7, 6, 21).WithArguments("SomeOtherClass", "IMyInterface"), + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task Basic_VerifyDifferentFriendAssemblyAsync() + { + string source1 = @" + +" + AttributeStringBasic + @" + + +Public Interface IMyInterface +End Interface + +Public Interface IMyOtherInterface + Inherits IMyInterface +End Interface +"; + + var source2 = @" +Class SomeClass + Implements IMyInterface +End Class + +Class SomeOtherClass + Implements IMyOtherInterface +End Class +"; + + // Verify no diagnostic since interface is in a friend assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source2 }, + AdditionalProjects = + { + ["DependencyProject"] = + { + Sources = { source1 }, + }, + }, + AdditionalProjectReferences = { "DependencyProject" }, + }, + }.RunAsync(); + } + + [Fact] + public async Task Basic_VerifyISymbolAsync() + { + var source = @" +' Causes many compile errors, because not all members are implemented. +Class C1 + Implements Microsoft.CodeAnalysis.ISymbol +End Class +Class C2 + Implements Microsoft.CodeAnalysis.IAssemblySymbol +End Class +"; + + // Verify that ISymbol is not implementable. + await VerifyVB.VerifyAnalyzerAsync(source, + // Test0.vb(3,7): error RS1009: Type C1 cannot implement interface ISymbol because ISymbol is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(3, 7, 3, 9).WithArguments("C1", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function Accept(Of TResult)(visitor As Microsoft.CodeAnalysis.SymbolVisitor(Of TResult)) As TResult", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function Equals(other As ISymbol) As Boolean' for interface 'IEquatable(Of ISymbol)'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function Equals(other As Microsoft.CodeAnalysis.ISymbol) As Boolean", "IEquatable(Of ISymbol)"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function GetAttributes() As ImmutableArray(Of AttributeData)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function GetAttributes() As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.AttributeData)", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function GetDocumentationCommentId() As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function GetDocumentationCommentId() As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function GetDocumentationCommentXml([preferredCulture As CultureInfo = Nothing], [expandIncludes As Boolean = False], [cancellationToken As CancellationToken = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function GetDocumentationCommentXml([preferredCulture As System.Globalization.CultureInfo = Nothing], [expandIncludes As Boolean = False], [cancellationToken As System.Threading.CancellationToken = Nothing]) As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function ToDisplayParts([format As SymbolDisplayFormat = Nothing]) As ImmutableArray(Of SymbolDisplayPart)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function ToDisplayParts([format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SymbolDisplayPart)", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function ToDisplayString([format As SymbolDisplayFormat = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function ToDisplayString([format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function ToMinimalDisplayParts(semanticModel As SemanticModel, position As Integer, [format As SymbolDisplayFormat = Nothing]) As ImmutableArray(Of SymbolDisplayPart)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function ToMinimalDisplayParts(semanticModel As Microsoft.CodeAnalysis.SemanticModel, position As Integer, [format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SymbolDisplayPart)", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function ToMinimalDisplayString(semanticModel As SemanticModel, position As Integer, [format As SymbolDisplayFormat = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Function ToMinimalDisplayString(semanticModel As Microsoft.CodeAnalysis.SemanticModel, position As Integer, [format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property CanBeReferencedByName As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property CanBeReferencedByName As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ContainingAssembly As IAssemblySymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property ContainingAssembly As Microsoft.CodeAnalysis.IAssemblySymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ContainingModule As IModuleSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property ContainingModule As Microsoft.CodeAnalysis.IModuleSymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ContainingNamespace As INamespaceSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property ContainingNamespace As Microsoft.CodeAnalysis.INamespaceSymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ContainingSymbol As ISymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property ContainingSymbol As Microsoft.CodeAnalysis.ISymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ContainingType As INamedTypeSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property ContainingType As Microsoft.CodeAnalysis.INamedTypeSymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property DeclaredAccessibility As Accessibility' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property DeclaredAccessibility As Microsoft.CodeAnalysis.Accessibility", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property DeclaringSyntaxReferences As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SyntaxReference)", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property HasUnsupportedMetadata As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property HasUnsupportedMetadata As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsAbstract As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsAbstract As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsDefinition As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsDefinition As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsExtern As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsExtern As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsImplicitlyDeclared As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsImplicitlyDeclared As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsOverride As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsOverride As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsSealed As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsSealed As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsStatic As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsStatic As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsVirtual As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property IsVirtual As Boolean", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Kind As SymbolKind' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property Kind As Microsoft.CodeAnalysis.SymbolKind", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Language As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property Language As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Locations As ImmutableArray(Of Location)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property Locations As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.Location)", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property MetadataName As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property MetadataName As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Name As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property Name As String", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property OriginalDefinition As ISymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "ReadOnly Property OriginalDefinition As Microsoft.CodeAnalysis.ISymbol", "ISymbol"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Sub Accept(visitor As SymbolVisitor)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 46).WithArguments("Class", "C1", "Sub Accept(visitor As Microsoft.CodeAnalysis.SymbolVisitor)", "ISymbol"), + // Test0.vb(6,7): error RS1009: Type C2 cannot implement interface ISymbol because ISymbol is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(6, 7, 6, 9).WithArguments("C2", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function Accept(Of TResult)(visitor As Microsoft.CodeAnalysis.SymbolVisitor(Of TResult)) As TResult", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function Equals(other As ISymbol) As Boolean' for interface 'IEquatable(Of ISymbol)'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function Equals(other As Microsoft.CodeAnalysis.ISymbol) As Boolean", "IEquatable(Of ISymbol)"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GetAttributes() As ImmutableArray(Of AttributeData)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GetAttributes() As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.AttributeData)", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GetDocumentationCommentId() As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GetDocumentationCommentId() As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GetDocumentationCommentXml([preferredCulture As CultureInfo = Nothing], [expandIncludes As Boolean = False], [cancellationToken As CancellationToken = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GetDocumentationCommentXml([preferredCulture As System.Globalization.CultureInfo = Nothing], [expandIncludes As Boolean = False], [cancellationToken As System.Threading.CancellationToken = Nothing]) As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GetMetadata() As AssemblyMetadata' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GetMetadata() As Microsoft.CodeAnalysis.AssemblyMetadata", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GetTypeByMetadataName(fullyQualifiedMetadataName As String) As INamedTypeSymbol' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GetTypeByMetadataName(fullyQualifiedMetadataName As String) As Microsoft.CodeAnalysis.INamedTypeSymbol", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function GivesAccessTo(toAssembly As IAssemblySymbol) As Boolean' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function GivesAccessTo(toAssembly As Microsoft.CodeAnalysis.IAssemblySymbol) As Boolean", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function ResolveForwardedType(fullyQualifiedMetadataName As String) As INamedTypeSymbol' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function ResolveForwardedType(fullyQualifiedMetadataName As String) As Microsoft.CodeAnalysis.INamedTypeSymbol", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function ToDisplayParts([format As SymbolDisplayFormat = Nothing]) As ImmutableArray(Of SymbolDisplayPart)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function ToDisplayParts([format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SymbolDisplayPart)", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function ToDisplayString([format As SymbolDisplayFormat = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function ToDisplayString([format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function ToMinimalDisplayParts(semanticModel As SemanticModel, position As Integer, [format As SymbolDisplayFormat = Nothing]) As ImmutableArray(Of SymbolDisplayPart)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function ToMinimalDisplayParts(semanticModel As Microsoft.CodeAnalysis.SemanticModel, position As Integer, [format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SymbolDisplayPart)", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function ToMinimalDisplayString(semanticModel As SemanticModel, position As Integer, [format As SymbolDisplayFormat = Nothing]) As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Function ToMinimalDisplayString(semanticModel As Microsoft.CodeAnalysis.SemanticModel, position As Integer, [format As Microsoft.CodeAnalysis.SymbolDisplayFormat = Nothing]) As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property CanBeReferencedByName As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property CanBeReferencedByName As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ContainingAssembly As IAssemblySymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property ContainingAssembly As Microsoft.CodeAnalysis.IAssemblySymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ContainingModule As IModuleSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property ContainingModule As Microsoft.CodeAnalysis.IModuleSymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ContainingNamespace As INamespaceSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property ContainingNamespace As Microsoft.CodeAnalysis.INamespaceSymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ContainingSymbol As ISymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property ContainingSymbol As Microsoft.CodeAnalysis.ISymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ContainingType As INamedTypeSymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property ContainingType As Microsoft.CodeAnalysis.INamedTypeSymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property DeclaredAccessibility As Accessibility' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property DeclaredAccessibility As Microsoft.CodeAnalysis.Accessibility", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property DeclaringSyntaxReferences As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.SyntaxReference)", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property GlobalNamespace As INamespaceSymbol' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property GlobalNamespace As Microsoft.CodeAnalysis.INamespaceSymbol", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property HasUnsupportedMetadata As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property HasUnsupportedMetadata As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Identity As AssemblyIdentity' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Identity As Microsoft.CodeAnalysis.AssemblyIdentity", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsAbstract As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsAbstract As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsDefinition As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsDefinition As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsExtern As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsExtern As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsImplicitlyDeclared As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsImplicitlyDeclared As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsInteractive As Boolean' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsInteractive As Boolean", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsOverride As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsOverride As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsSealed As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsSealed As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsStatic As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsStatic As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsVirtual As Boolean' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property IsVirtual As Boolean", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Kind As SymbolKind' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Kind As Microsoft.CodeAnalysis.SymbolKind", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Language As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Language As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Locations As ImmutableArray(Of Location)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Locations As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.Location)", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property MetadataName As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property MetadataName As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property MightContainExtensionMethods As Boolean' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property MightContainExtensionMethods As Boolean", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Modules As IEnumerable(Of IModuleSymbol)' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Modules As System.Collections.Generic.IEnumerable(Of Microsoft.CodeAnalysis.IModuleSymbol)", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Name As String' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property Name As String", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property NamespaceNames As ICollection(Of String)' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property NamespaceNames As System.Collections.Generic.ICollection(Of String)", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property OriginalDefinition As ISymbol' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property OriginalDefinition As Microsoft.CodeAnalysis.ISymbol", "ISymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property TypeNames As ICollection(Of String)' for interface 'IAssemblySymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "ReadOnly Property TypeNames As System.Collections.Generic.ICollection(Of String)", "IAssemblySymbol"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Sub Accept(visitor As SymbolVisitor)' for interface 'ISymbol'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 54).WithArguments("Class", "C2", "Sub Accept(visitor As Microsoft.CodeAnalysis.SymbolVisitor)", "ISymbol") + ); + } + + [Fact] + public async Task Basic_VerifyIOperationAsync() + { + var source = @" +' Causes many compile errors, because not all members are implemented. +Class C1 + Implements Microsoft.CodeAnalysis.IOperation +End Class +Class C2 + Implements Microsoft.CodeAnalysis.Operations.IInvocationOperation +End Class +"; + + // Verify that IOperation is not implementable. + await VerifyVB.VerifyAnalyzerAsync(source, + // Test0.vb(3,7): error RS1009: Type C1 cannot implement interface IOperation because IOperation is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(3, 7, 3, 9).WithArguments("C1", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "Function Accept(Of TArgument, TResult)(visitor As Microsoft.CodeAnalysis.Operations.OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Children As IEnumerable(Of IOperation)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Children As System.Collections.Generic.IEnumerable(Of Microsoft.CodeAnalysis.IOperation)", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property ConstantValue As [Optional](Of Object)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property ConstantValue As Microsoft.CodeAnalysis.Optional(Of Object)", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property IsImplicit As Boolean' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property IsImplicit As Boolean", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Kind As OperationKind' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Kind As Microsoft.CodeAnalysis.OperationKind", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Language As String' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Language As String", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Parent As IOperation' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Parent As Microsoft.CodeAnalysis.IOperation", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property SemanticModel As SemanticModel' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property SemanticModel As Microsoft.CodeAnalysis.SemanticModel", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Syntax As SyntaxNode' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Syntax As Microsoft.CodeAnalysis.SyntaxNode", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'ReadOnly Property Type As ITypeSymbol' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "ReadOnly Property Type As Microsoft.CodeAnalysis.ITypeSymbol", "IOperation"), + // Test0.vb(4) : error BC30149: Class 'C1' must implement 'Sub Accept(visitor As OperationVisitor)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(4, 16, 4, 49).WithArguments("Class", "C1", "Sub Accept(visitor As Microsoft.CodeAnalysis.Operations.OperationVisitor)", "IOperation"), + // Test0.vb(6,7): error RS1009: Type C2 cannot implement interface IOperation because IOperation is not available for public implementation. + VerifyVB.Diagnostic().WithSpan(6, 7, 6, 9).WithArguments("C2", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "Function Accept(Of TArgument, TResult)(visitor As Microsoft.CodeAnalysis.Operations.OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Arguments As ImmutableArray(Of IArgumentOperation)' for interface 'IInvocationOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Arguments As System.Collections.Immutable.ImmutableArray(Of Microsoft.CodeAnalysis.Operations.IArgumentOperation)", "IInvocationOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Children As IEnumerable(Of IOperation)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Children As System.Collections.Generic.IEnumerable(Of Microsoft.CodeAnalysis.IOperation)", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property ConstantValue As [Optional](Of Object)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property ConstantValue As Microsoft.CodeAnalysis.Optional(Of Object)", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Instance As IOperation' for interface 'IInvocationOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Instance As Microsoft.CodeAnalysis.IOperation", "IInvocationOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsImplicit As Boolean' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property IsImplicit As Boolean", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property IsVirtual As Boolean' for interface 'IInvocationOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property IsVirtual As Boolean", "IInvocationOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Kind As OperationKind' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Kind As Microsoft.CodeAnalysis.OperationKind", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Language As String' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Language As String", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Parent As IOperation' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Parent As Microsoft.CodeAnalysis.IOperation", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property SemanticModel As SemanticModel' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property SemanticModel As Microsoft.CodeAnalysis.SemanticModel", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Syntax As SyntaxNode' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Syntax As Microsoft.CodeAnalysis.SyntaxNode", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property TargetMethod As IMethodSymbol' for interface 'IInvocationOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property TargetMethod As Microsoft.CodeAnalysis.IMethodSymbol", "IInvocationOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'ReadOnly Property Type As ITypeSymbol' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "ReadOnly Property Type As Microsoft.CodeAnalysis.ITypeSymbol", "IOperation"), + // Test0.vb(7) : error BC30149: Class 'C2' must implement 'Sub Accept(visitor As OperationVisitor)' for interface 'IOperation'. + DiagnosticResult.CompilerError("BC30149").WithSpan(7, 16, 7, 70).WithArguments("Class", "C2", "Sub Accept(visitor As Microsoft.CodeAnalysis.Operations.OperationVisitor)", "IOperation") + ); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/AddLanguageSupportToAnalyzerRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/AddLanguageSupportToAnalyzerRuleTests.cs new file mode 100644 index 0000000000000..2344993ef8173 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/AddLanguageSupportToAnalyzerRuleTests.cs @@ -0,0 +1,219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticAnalyzerAttributeAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticAnalyzerAttributeAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class AddLanguageSupportToAnalyzerRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, ""MyLanguage"")] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; + DiagnosticResult expected = GetCSharpExpectedDiagnostic(7, 2, "MyAnalyzer", missingLanguageName: LanguageNames.VisualBasic); + + // Verify diagnostic if analyzer assembly doesn't reference C# code analysis assembly. + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + }.RunAsync(); + + // Verify no diagnostic if analyzer assembly references C# code analysis assembly. + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + DiagnosticResult expected = GetBasicExpectedDiagnostic(7, 2, "MyAnalyzer", missingLanguageName: LanguageNames.CSharp); + + // Verify diagnostic if analyzer assembly doesn't reference VB code analysis assembly. + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + }.RunAsync(); + + // Verify no diagnostic if analyzer assembly references VB code analysis assembly. + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(""MyLanguage"")] +class MyAnalyzerWithCustomLanguageAttribute : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzerWithBothLanguages : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public abstract class MyAbstractAnalyzer : DiagnosticAnalyzer +{ +} +"; + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzerWithCustomLanguageAttribute + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + + +Class MyAnalyzerWithBothLanguages + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + + +Public MustInherit Class MyAbstractAnalyzer + Inherits DiagnosticAnalyzer +End Class +"; + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithoutRoslynSymbols, + TestState = { Sources = { source } }, + }.RunAsync(); + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string analyzerTypeName, string missingLanguageName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DiagnosticAnalyzerAttributeAnalyzer.AddLanguageSupportToAnalyzerRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(analyzerTypeName, missingLanguageName); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string analyzerTypeName, string missingLanguageName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(DiagnosticAnalyzerAttributeAnalyzer.AddLanguageSupportToAnalyzerRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(analyzerTypeName, missingLanguageName); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ClassIsNotDiagnosticAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ClassIsNotDiagnosticAnalyzerTests.cs new file mode 100644 index 0000000000000..28b738e7a13a1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ClassIsNotDiagnosticAnalyzerTests.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.ClassIsNotDiagnosticAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.ClassIsNotDiagnosticAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class ClassIsNotDiagnosticAnalyzerTests + { + [Fact] + public async Task ClassNotDiagnosticAnalyzer_DiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +namespace RoslynSandbox +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class NotAnAnalyzer + { + } +}", +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(8, 20).WithArguments("NotAnAnalyzer")); +#pragma warning restore RS0030 // Do not use banned APIs + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Namespace RoslynSandbox + + Friend Class NotAnAnalyzer + End Class +End Namespace", +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic().WithLocation(7, 18).WithArguments("NotAnAnalyzer")); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task StaticClassNotDiagnosticAnalyzer_DiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +namespace RoslynSandbox +{ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal static class NotAnAnalyzer + { + } +}", +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(8, 27).WithArguments("NotAnAnalyzer")); +#pragma warning restore RS0030 // Do not use banned APIs + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Namespace RoslynSandbox + + Friend Module NotAnAnalyzer + End Module +End Namespace"); + } + + [Fact] + public async Task ClassDiagnosticAnalyzer_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +namespace RoslynSandbox +{ + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class SomeAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => throw new System.NotImplementedException(); + + public override void Initialize(AnalysisContext context) => throw new System.NotImplementedException(); + } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Namespace RoslynSandbox + + Friend Class SomeAnalyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + Throw New System.NotImplementedException() + End Sub + End Class +End Namespace"); + } + + [Fact] + public async Task ClassInheritsClassDiagnosticAnalyzer_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +namespace RoslynSandbox +{ + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + internal abstract class SomeAnalyzerBase : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => throw new System.NotImplementedException(); + + public override void Initialize(AnalysisContext context) => throw new System.NotImplementedException(); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class SomeAnalyzer : SomeAnalyzerBase + { + } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Namespace RoslynSandbox + Friend MustInherit Class SomeAnalyzerBase + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + Throw New System.NotImplementedException() + End Sub + End Class + + + + Friend Class SomeAnalyzer + Inherits SomeAnalyzerBase + End Class +End Namespace"); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs new file mode 100644 index 0000000000000..6f17d05eb0d45 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompareSymbolsCorrectlyTests.cs @@ -0,0 +1,1665 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompareSymbolsCorrectlyAnalyzer, + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers.CSharpCompareSymbolsCorrectlyFix>; +using VerifyVB = Test.Utilities.VisualBasicSecurityCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompareSymbolsCorrectlyAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.Fixers.BasicCompareSymbolsCorrectlyFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class CompareSymbolsCorrectlyTests + { + private const string MinimalSymbolImplementationCSharp = @" +using System; +using System.Collections.Immutable; +using System.Globalization; +using System.Threading; +using Microsoft.CodeAnalysis; + +class Symbol : ISymbol { + public SymbolKind Kind => throw new NotImplementedException(); + public string Language => throw new NotImplementedException(); + public string Name => throw new NotImplementedException(); + public string MetadataName => throw new NotImplementedException(); + public ISymbol ContainingSymbol => throw new NotImplementedException(); + public IAssemblySymbol ContainingAssembly => throw new NotImplementedException(); + public IModuleSymbol ContainingModule => throw new NotImplementedException(); + public INamedTypeSymbol ContainingType => throw new NotImplementedException(); + public INamespaceSymbol ContainingNamespace => throw new NotImplementedException(); + public bool IsDefinition => throw new NotImplementedException(); + public bool IsStatic => throw new NotImplementedException(); + public bool IsVirtual => throw new NotImplementedException(); + public bool IsOverride => throw new NotImplementedException(); + public bool IsAbstract => throw new NotImplementedException(); + public bool IsSealed => throw new NotImplementedException(); + public bool IsExtern => throw new NotImplementedException(); + public bool IsImplicitlyDeclared => throw new NotImplementedException(); + public bool CanBeReferencedByName => throw new NotImplementedException(); + public ImmutableArray Locations => throw new NotImplementedException(); + public ImmutableArray DeclaringSyntaxReferences => throw new NotImplementedException(); + public Accessibility DeclaredAccessibility => throw new NotImplementedException(); + public ISymbol OriginalDefinition => throw new NotImplementedException(); + public bool HasUnsupportedMetadata => throw new NotImplementedException(); + + public void Accept(SymbolVisitor visitor) => throw new NotImplementedException(); + public TResult Accept(SymbolVisitor visitor) => throw new NotImplementedException(); + public bool Equals(ISymbol other) => throw new NotImplementedException(); + public ImmutableArray GetAttributes() => throw new NotImplementedException(); + public string GetDocumentationCommentId() => throw new NotImplementedException(); + public string GetDocumentationCommentXml(CultureInfo preferredCulture, bool expandIncludes, CancellationToken cancellationToken) => throw new NotImplementedException(); + public ImmutableArray ToDisplayParts(SymbolDisplayFormat format) => throw new NotImplementedException(); + public string ToDisplayString(SymbolDisplayFormat format) => throw new NotImplementedException(); + public ImmutableArray ToMinimalDisplayParts(SemanticModel semanticModel, int position, SymbolDisplayFormat format) => throw new NotImplementedException(); + public string ToMinimalDisplayString(SemanticModel semanticModel, int position, SymbolDisplayFormat format) => throw new NotImplementedException(); + + public static bool operator ==(Symbol left, Symbol right) => throw new NotImplementedException(); + public static bool operator !=(Symbol left, Symbol right) => throw new NotImplementedException(); +} +"; + private const string MinimalSymbolImplementationVisualBasic = @" +Imports System +Imports System.Collections.Immutable +Imports System.Globalization +Imports System.Threading +Imports Microsoft.CodeAnalysis + +Class Symbol + Implements ISymbol + + Public ReadOnly Property Kind As SymbolKind Implements ISymbol.Kind + Public ReadOnly Property Language As String Implements ISymbol.Language + Public ReadOnly Property Name As String Implements ISymbol.Name + Public ReadOnly Property MetadataName As String Implements ISymbol.MetadataName + Public ReadOnly Property ContainingSymbol As ISymbol Implements ISymbol.ContainingSymbol + Public ReadOnly Property ContainingAssembly As IAssemblySymbol Implements ISymbol.ContainingAssembly + Public ReadOnly Property ContainingModule As IModuleSymbol Implements ISymbol.ContainingModule + Public ReadOnly Property ContainingType As INamedTypeSymbol Implements ISymbol.ContainingType + Public ReadOnly Property ContainingNamespace As INamespaceSymbol Implements ISymbol.ContainingNamespace + Public ReadOnly Property IsDefinition As Boolean Implements ISymbol.IsDefinition + Public ReadOnly Property IsStatic As Boolean Implements ISymbol.IsStatic + Public ReadOnly Property IsVirtual As Boolean Implements ISymbol.IsVirtual + Public ReadOnly Property IsOverride As Boolean Implements ISymbol.IsOverride + Public ReadOnly Property IsAbstract As Boolean Implements ISymbol.IsAbstract + Public ReadOnly Property IsSealed As Boolean Implements ISymbol.IsSealed + Public ReadOnly Property IsExtern As Boolean Implements ISymbol.IsExtern + Public ReadOnly Property IsImplicitlyDeclared As Boolean Implements ISymbol.IsImplicitlyDeclared + Public ReadOnly Property CanBeReferencedByName As Boolean Implements ISymbol.CanBeReferencedByName + Public ReadOnly Property Locations As ImmutableArray(Of Location) Implements ISymbol.Locations + Public ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference) Implements ISymbol.DeclaringSyntaxReferences + Public ReadOnly Property DeclaredAccessibility As Accessibility Implements ISymbol.DeclaredAccessibility + Public ReadOnly Property OriginalDefinition As ISymbol Implements ISymbol.OriginalDefinition + Public ReadOnly Property HasUnsupportedMetadata As Boolean Implements ISymbol.HasUnsupportedMetadata + + Public Sub Accept(visitor As SymbolVisitor) Implements ISymbol.Accept + Throw New NotImplementedException() + End Sub + + Public Function GetAttributes() As ImmutableArray(Of AttributeData) Implements ISymbol.GetAttributes + Throw New NotImplementedException() + End Function + + Public Function Accept(Of TResult)(visitor As SymbolVisitor(Of TResult)) As TResult Implements ISymbol.Accept + Throw New NotImplementedException() + End Function + + Public Function GetDocumentationCommentId() As String Implements ISymbol.GetDocumentationCommentId + Throw New NotImplementedException() + End Function + + Public Function GetDocumentationCommentXml(Optional preferredCulture As CultureInfo = Nothing, Optional expandIncludes As Boolean = False, Optional cancellationToken As CancellationToken = Nothing) As String Implements ISymbol.GetDocumentationCommentXml + Throw New NotImplementedException() + End Function + + Public Function ToDisplayString(Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToDisplayString + Throw New NotImplementedException() + End Function + + Public Function ToDisplayParts(Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToDisplayParts + Throw New NotImplementedException() + End Function + + Public Function ToMinimalDisplayString(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As String Implements ISymbol.ToMinimalDisplayString + Throw New NotImplementedException() + End Function + + Public Function ToMinimalDisplayParts(semanticModel As SemanticModel, position As Integer, Optional format As SymbolDisplayFormat = Nothing) As ImmutableArray(Of SymbolDisplayPart) Implements ISymbol.ToMinimalDisplayParts + Throw New NotImplementedException() + End Function + + Public Function Equals(other As ISymbol) As Boolean Implements IEquatable(Of ISymbol).Equals + Throw New NotImplementedException() + End Function + + Public Shared Operator =(left As Symbol, right As Symbol) As Boolean + Throw New NotImplementedException() + End Operator + + Public Shared Operator <>(left As Symbol, right As Symbol) As Boolean + Throw New NotImplementedException() + End Operator +End Class +"; + + private const string SymbolEqualityComparerStubVisualBasic = +@" +Imports System.Collections.Generic + +Namespace Microsoft + Namespace CodeAnalysis + Public Class SymbolEqualityComparer + Implements IEqualityComparer(Of ISymbol) + + Public Shared ReadOnly [Default] As SymbolEqualityComparer = New SymbolEqualityComparer() + + Private Sub New() + End Sub + + Public Function Equals(x As ISymbol, y As ISymbol) As Boolean Implements IEqualityComparer(Of ISymbol).Equals + Throw New System.NotImplementedException() + End Function + + Public Function GetHashCode(obj As ISymbol) As Integer Implements IEqualityComparer(Of ISymbol).GetHashCode + Throw New System.NotImplementedException() + End Function + End Class + End Namespace +End Namespace"; + + private const string SymbolEqualityComparerStubCSharp = +@" +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis +{ + public class SymbolEqualityComparer : IEqualityComparer + { + public static readonly SymbolEqualityComparer Default = new SymbolEqualityComparer(); + + private SymbolEqualityComparer() + { + } + + public bool Equals(ISymbol x, ISymbol y) + { + throw new System.NotImplementedException(); + } + + public int GetHashCode(ISymbol obj) + { + throw new System.NotImplementedException(); + } + } +}"; + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsEquals_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method({symbolType} x, {symbolType} y) {{ + return [|x == y|]; + }} +}} +"; + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method({symbolType} x, {symbolType} y) {{ + return SymbolEqualityComparer.Default.Equals(x, y); + }} +}} +"; + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsEquals_NoComparer_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method({symbolType} x, {symbolType} y) {{ + return [|x == y|]; + }} +}} +"; + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method({symbolType} x, {symbolType} y) {{ + return Equals(x, y); + }} +}} +"; + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [WorkItem(2335, "https://github.com/dotnet/roslyn-analyzers/issues/2335")] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsByIdentity_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method1({symbolType} x, {symbolType} y) {{ + return (object)x == y; + }} + bool Method2({symbolType} x, {symbolType} y) {{ + return x == (object)y; + }} + bool Method3({symbolType} x, {symbolType} y) {{ + return (object)x == (object)y; + }} +}} +"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + [WorkItem(2336, "https://github.com/dotnet/roslyn-analyzers/issues/2336")] + public async Task CompareTwoSymbolImplementations_CSharpAsync() + { + var source = $@" +class TestClass {{ + bool Method(Symbol x, Symbol y) {{ + return x == y; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, MinimalSymbolImplementationCSharp } }, + }.RunAsync(); + } + + [Theory] + [WorkItem(2336, "https://github.com/dotnet/roslyn-analyzers/issues/2336")] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareSymbolImplementationWithInterface_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(Symbol x, {symbolType} y) {{ + return [|x == y|]; + }} +}} +"; + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(Symbol x, {symbolType} y) {{ + return SymbolEqualityComparer.Default.Equals(x, y); + }} +}} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, MinimalSymbolImplementationCSharp, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, MinimalSymbolImplementationCSharp, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareSymbolImplementationWithInterface_NoComparer_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(Symbol x, {symbolType} y) {{ + return [|x == y|]; + }} +}} +"; + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(Symbol x, {symbolType} y) {{ + return Equals(x, y); + }} +}} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, MinimalSymbolImplementationCSharp } }, + FixedState = { Sources = { fixedSource, MinimalSymbolImplementationCSharp } }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CompareSymbolWithNull_CSharpAsync( + [CombinatorialValues(nameof(ISymbol), nameof(INamedTypeSymbol))] string symbolType, + [CombinatorialValues("==", "!=")] string @operator, + [CombinatorialValues("null", "default", "default(ISymbol)")] string value) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method1({symbolType} x) {{ + return x {@operator} {value}; + }} + + bool Method2({symbolType} x) {{ + return {value} {@operator} x; + }} +}} +"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareSymbolWithNullPattern_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method1({symbolType} x) {{ + return x is null; + }} +}} +"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsEquals_VisualBasicAsync(string symbolType) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method(x As {symbolType}, y As {symbolType}) As Boolean + Return [|x Is y|] + End Function +End Class +"; + var fixedSource = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method(x As {symbolType}, y As {symbolType}) As Boolean + Return SymbolEqualityComparer.Default.Equals(x, y) + End Function +End Class +"; + + await new VerifyVB.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubVisualBasic } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubVisualBasic } }, + }.RunAsync(); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsEquals_NoComparer_VisualBasicAsync(string symbolType) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method(x As {symbolType}, y As {symbolType}) As Boolean + Return [|x Is y|] + End Function +End Class +"; + var fixedSource = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method(x As {symbolType}, y As {symbolType}) As Boolean + Return Equals(x, y) + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [WorkItem(2335, "https://github.com/dotnet/roslyn-analyzers/issues/2335")] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareTwoSymbolsByIdentity_VisualBasicAsync(string symbolType) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method(x As {symbolType}, y As {symbolType}) As Boolean + Return DirectCast(x, Object) Is y + End Function +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Theory] + [WorkItem(2336, "https://github.com/dotnet/roslyn-analyzers/issues/2336")] + [CombinatorialData] + public async Task CompareTwoSymbolImplementations_VisualBasicAsync( + [CombinatorialValues("Symbol", nameof(ISymbol), nameof(INamedTypeSymbol))] string symbolType, + [CombinatorialValues("=", "<>", "Is", "IsNot")] string @operator) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method1(x As Symbol, y As {symbolType}) As Boolean + Return x {@operator} y + End Function + + Function Method2(x As Symbol, y As {symbolType}) As Boolean + Return y {@operator} x + End Function +End Class +"; + + await new VerifyVB.Test + { + TestState = { Sources = { source, MinimalSymbolImplementationVisualBasic } }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CompareSymbolWithNull_VisualBasicAsync( + [CombinatorialValues(nameof(ISymbol), nameof(INamedTypeSymbol))] string symbolType, + [CombinatorialValues("Is", "IsNot")] string @operator) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Function Method1(x As {symbolType}) As Boolean + Return x {@operator} Nothing + End Function + + Function Method2(x As {symbolType}) As Boolean + Return Nothing {@operator} x + End Function +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Theory] + [CombinatorialData] + public async Task CompareSymbolFromInstanceEquals_VisualBasicAsync( + [CombinatorialValues(nameof(ISymbol), nameof(INamedTypeSymbol))] string symbolType, + [CombinatorialValues("", "Not ")] string @operator) + { + var source = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Sub Method1(x As {symbolType}, y As {symbolType}) + If {@operator}[|x.Equals(y)|] Then Exit Sub + End Sub +End Class +"; + + var fixedSource = $@" +Imports Microsoft.CodeAnalysis +Class TestClass + Sub Method1(x As {symbolType}, y As {symbolType}) + If {@operator}SymbolEqualityComparer.Default.Equals(x, y) Then Exit Sub + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubVisualBasic } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubVisualBasic } }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CompareSymbolFromInstanceEquals_CSharpAsync( + [CombinatorialValues(nameof(ISymbol), nameof(INamedTypeSymbol))] string symbolType, + [CombinatorialValues("", "!")] string @operator) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass +{{ + void Method1({symbolType} x , {symbolType} y) + {{ + if ({@operator}[|x.Equals(y)|]) return; + }} +}} +"; + + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass +{{ + void Method1({symbolType} x , {symbolType} y) + {{ + if ({@operator}SymbolEqualityComparer.Default.Equals(x, y)) return; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Fact] + public async Task CompareSymbolFromInstanceEqualsWithConditionalAccess_VisualBasicAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Class TestClass + Sub Method1(x As ISymbol, y As ISymbol) + If x?[|.Equals(y)|] Then Exit Sub + End Sub +End Class +"; + + var fixedSource = @" +Imports Microsoft.CodeAnalysis +Class TestClass + Sub Method1(x As ISymbol, y As ISymbol) + If SymbolEqualityComparer.Default.Equals(x, y) Then Exit Sub + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubVisualBasic } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubVisualBasic } }, + }.RunAsync(); + } + + [Fact] + public async Task CompareSymbolFromInstanceEqualsWithNullConditionalAccess_CSharpAsync() + { + var source = @" +using Microsoft.CodeAnalysis; +class TestClass +{ + void Method1(ISymbol x, ISymbol y) + { + if (x?[|.Equals(y)|] == true) return; + } +} +"; + + var fixedSource = @" +using Microsoft.CodeAnalysis; +class TestClass +{ + void Method1(ISymbol x, ISymbol y) + { + if (SymbolEqualityComparer.Default.Equals(x, y) == true) return; + } +} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Fact] + public async Task CompareSymbolFromInstanceEqualsWithChainConditionalAccess_VisualBasicAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis + +Class A + Public b As B +End Class + +Class B + Public s As ISymbol +End Class + +Class TestClass + Sub Method1(a As A, b As B, s As ISymbol) + If a?.b?.s?[|.Equals(s)|] Then Exit Sub + If b?.s?[|.Equals(s)|] Then Exit Sub + End Sub +End Class +"; + + var fixedSource = @" +Imports Microsoft.CodeAnalysis + +Class A + Public b As B +End Class + +Class B + Public s As ISymbol +End Class + +Class TestClass + Sub Method1(a As A, b As B, s As ISymbol) + If SymbolEqualityComparer.Default.Equals(a?.b?.s, s) Then Exit Sub + If SymbolEqualityComparer.Default.Equals(b?.s, s) Then Exit Sub + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubVisualBasic } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubVisualBasic } }, + }.RunAsync(); + } + + [Fact] + public async Task CompareSymbolFromInstanceEqualsWithChainNullConditionalAccess_CSharpAsync() + { + var source = @" +using Microsoft.CodeAnalysis; + +class A +{ + public B b; +} + +class B +{ + public ISymbol s; +} + +class TestClass +{ + void Method1(A a, B b, ISymbol s) + { + if (a?.b?.s?[|.Equals(s)|] == true) return; + if (b?.s?[|.Equals(s)|] == true) return; + } +} +"; + + var fixedSource = @" +using Microsoft.CodeAnalysis; + +class A +{ + public B b; +} + +class B +{ + public ISymbol s; +} + +class TestClass +{ + void Method1(A a, B b, ISymbol s) + { + if (SymbolEqualityComparer.Default.Equals(a?.b?.s, s) == true) return; + if (SymbolEqualityComparer.Default.Equals(b?.s, s) == true) return; + } +} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Fact] + public async Task CompareSymbolFromInstanceEqualsWithChain_CSharpAsync() + { + var source = @" +using Microsoft.CodeAnalysis; + +class A +{ + public B b; + public B GetB() => null; +} + +class B +{ + public ISymbol s; + public ISymbol GetS() => null; +} + +class TestClass +{ + void Method1(A a, ISymbol symbol) + { + if ([|a.b.s.Equals(symbol)|] == true) return; + if ([|a.GetB().GetS().Equals(symbol)|] == true) return; + } +}"; + + var fixedSource = @" +using Microsoft.CodeAnalysis; + +class A +{ + public B b; + public B GetB() => null; +} + +class B +{ + public ISymbol s; + public ISymbol GetS() => null; +} + +class TestClass +{ + void Method1(A a, ISymbol symbol) + { + if (SymbolEqualityComparer.Default.Equals(a.b.s, symbol) == true) return; + if (SymbolEqualityComparer.Default.Equals(a.GetB().GetS(), symbol) == true) return; + } +}"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Theory] + [InlineData(nameof(ISymbol))] + [InlineData(nameof(INamedTypeSymbol))] + public async Task CompareSymbolImplementationWithInterface_EqualsComparison_CSharpAsync(string symbolType) + { + var source = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(ISymbol x, {symbolType} y) {{ + return [|Equals(x, y)|]; + }} +}} +"; + var fixedSource = $@" +using Microsoft.CodeAnalysis; +class TestClass {{ + bool Method(ISymbol x, {symbolType} y) {{ + return SymbolEqualityComparer.Default.Equals(x, y); + }} +}} +"; + + await new VerifyCS.Test + { + TestState = { Sources = { source, SymbolEqualityComparerStubCSharp } }, + FixedState = { Sources = { fixedSource, SymbolEqualityComparerStubCSharp } }, + }.RunAsync(); + } + + [Fact, WorkItem(2493, "https://github.com/dotnet/roslyn-analyzers/issues/2493")] + public async Task GetHashCode_DiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using Microsoft.CodeAnalysis; +public class C +{ + public int M(ISymbol symbol, INamedTypeSymbol namedType) + { + return [|symbol.GetHashCode()|] + [|namedType.GetHashCode()|]; + } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports Microsoft.CodeAnalysis + +Public Class C + Public Function M(ByVal symbol As ISymbol, ByVal namedType As INamedTypeSymbol) As Integer + Return [|symbol.GetHashCode()|] + [|namedType.GetHashCode()|] + End Function +End Class"); + } + + [Fact, WorkItem(2493, "https://github.com/dotnet/roslyn-analyzers/issues/2493")] + public async Task CollectionConstructorsKnownToRequireComparer_DiagnosticAsync() + { + await new VerifyCS.Test + { + TestState = + { + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + Sources = + { + @" +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void MethodWithDiagnostics(IEnumerable symbols) + { + var kvps = symbols.Select(s => new KeyValuePair(s, 0)); + + [|new Dictionary()|]; + [|new Dictionary(42)|]; + [|new Dictionary(capacity: 42)|]; + [|new Dictionary(kvps)|]; + + [|new HashSet()|]; + [|new HashSet(42)|]; + [|new HashSet(symbols)|]; + + [|new ConcurrentDictionary()|]; + [|new ConcurrentDictionary(kvps)|]; + [|new ConcurrentDictionary(1, 42)|]; + } + + public void MethodWithoutDiagnostics() + { + new Dictionary(); + new HashSet(); + new ConcurrentDictionary(); + + new Dictionary(SymbolEqualityComparer.Default); + } +}", + SymbolEqualityComparerStubCSharp, + }, + }, + FixedState = + { + Sources = + { + @" +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void MethodWithDiagnostics(IEnumerable symbols) + { + var kvps = symbols.Select(s => new KeyValuePair(s, 0)); + + new Dictionary(SymbolEqualityComparer.Default); + new Dictionary(42, SymbolEqualityComparer.Default); + new Dictionary(capacity: 42, SymbolEqualityComparer.Default); + new Dictionary(kvps, SymbolEqualityComparer.Default); + + new HashSet(SymbolEqualityComparer.Default); + new HashSet(42, SymbolEqualityComparer.Default); + new HashSet(symbols, SymbolEqualityComparer.Default); + + new ConcurrentDictionary(SymbolEqualityComparer.Default); + new ConcurrentDictionary(kvps, SymbolEqualityComparer.Default); + new ConcurrentDictionary(1, 42, SymbolEqualityComparer.Default); + } + + public void MethodWithoutDiagnostics() + { + new Dictionary(); + new HashSet(); + new ConcurrentDictionary(); + + new Dictionary(SymbolEqualityComparer.Default); + } +}", + SymbolEqualityComparerStubCSharp, + } + } + }.RunAsync(); + + await new VerifyVB.Test + { + TestState = + { + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + Sources = + { + @" +Imports System.Collections.Concurrent +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + Public Sub MethodWithDiagnostics(symbols As IEnumerable(Of ISymbol)) + Dim kvps = symbols.[Select](Function(s) New KeyValuePair(Of ISymbol, Integer)(s, 0)) + + Dim x11 = [|New Dictionary(Of ISymbol, Integer)()|] + Dim x12 = [|New Dictionary(Of ISymbol, Integer)(42)|] + Dim x13 = [|New Dictionary(Of ISymbol, Integer)(kvps)|] + + Dim x21 = [|New HashSet(Of ISymbol)()|] + Dim x22 = [|New HashSet(Of ISymbol)(42)|] + Dim x23 = [|New HashSet(Of ISymbol)(symbols)|] + + Dim x31 = [|New ConcurrentDictionary(Of ISymbol, Integer)()|] + Dim x32 = [|New ConcurrentDictionary(Of ISymbol, Integer)(kvps)|] + Dim x33 = [|New ConcurrentDictionary(Of ISymbol, Integer)(1, 42)|] + End Sub + + Public Sub MethodWithoutDiagnostics() + Dim x1 = New Dictionary(Of Integer, ISymbol)() + Dim x2 = New HashSet(Of String)() + Dim x3 = New ConcurrentDictionary(Of Integer, ISymbol)() + + Dim x4 = New Dictionary(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + End Sub +End Class", + SymbolEqualityComparerStubVisualBasic, + }, + }, + FixedState = + { + Sources = + { + @" +Imports System.Collections.Concurrent +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + Public Sub MethodWithDiagnostics(symbols As IEnumerable(Of ISymbol)) + Dim kvps = symbols.[Select](Function(s) New KeyValuePair(Of ISymbol, Integer)(s, 0)) + + Dim x11 = New Dictionary(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + Dim x12 = New Dictionary(Of ISymbol, Integer)(42, SymbolEqualityComparer.Default) + Dim x13 = New Dictionary(Of ISymbol, Integer)(kvps, SymbolEqualityComparer.Default) + + Dim x21 = New HashSet(Of ISymbol)(SymbolEqualityComparer.Default) + Dim x22 = New HashSet(Of ISymbol)(42, SymbolEqualityComparer.Default) + Dim x23 = New HashSet(Of ISymbol)(symbols, SymbolEqualityComparer.Default) + + Dim x31 = New ConcurrentDictionary(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + Dim x32 = New ConcurrentDictionary(Of ISymbol, Integer)(kvps, SymbolEqualityComparer.Default) + Dim x33 = New ConcurrentDictionary(Of ISymbol, Integer)(1, 42, SymbolEqualityComparer.Default) + End Sub + + Public Sub MethodWithoutDiagnostics() + Dim x1 = New Dictionary(Of Integer, ISymbol)() + Dim x2 = New HashSet(Of String)() + Dim x3 = New ConcurrentDictionary(Of Integer, ISymbol)() + + Dim x4 = New Dictionary(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + End Sub +End Class", + SymbolEqualityComparerStubVisualBasic, + }, + } + }.RunAsync(); + } + + [Fact, WorkItem(2493, "https://github.com/dotnet/roslyn-analyzers/issues/2493")] + public async Task CollectionMethodsKnownToRequireComparer_DiagnosticAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +public class C +{ + public void MethodWithDiagnostics(IEnumerable> kvps, IEnumerable symbols, IEnumerable symbols2, ISymbol symbol) + { + [|ImmutableHashSet.Create()|]; + [|ImmutableHashSet.CreateBuilder()|]; + [|ImmutableHashSet.CreateRange(symbols)|]; + [|symbols.ToImmutableHashSet()|]; + + [|ImmutableDictionary.Create()|]; + [|ImmutableDictionary.CreateBuilder()|]; + [|ImmutableDictionary.CreateRange(kvps)|]; + [|kvps.ToImmutableDictionary()|]; + + [|symbols.Contains(symbol)|]; + [|symbols.Distinct()|]; + [|symbols.GroupBy(x => x)|]; + [|symbols.GroupJoin(symbols2, x => x, x => x, (x, y) => x)|]; + [|symbols.Intersect(symbols2)|]; + [|symbols.Join(symbols2, x => x, x => x, (x, y) => x)|]; + [|symbols.SequenceEqual(symbols2)|]; + [|symbols.ToDictionary(x => x)|]; + [|symbols.ToLookup(x => x)|]; + [|symbols.Union(symbols2)|]; + + [|ImmutableHashSet.ToImmutableHashSet(symbols)|]; + symbols?[|.ToImmutableHashSet()|]; + } + + public void MethodWithoutDiagnostics(IEnumerable> kvps, IEnumerable integers, int integer) + { + ImmutableHashSet.Create(); + ImmutableHashSet.CreateBuilder(); + ImmutableHashSet.CreateRange(integers); + integers.ToImmutableHashSet(); + + ImmutableDictionary.Create(); + ImmutableDictionary.CreateBuilder(); + ImmutableDictionary.CreateRange(kvps); + kvps.ToImmutableDictionary(); + + integers.Contains(integer); + integers.Distinct(); + integers.GroupBy(x => x); + integers.GroupJoin(integers, x => x, x => x, (x, y) => x); + integers.Intersect(integers); + integers.Join(integers, x => x, x => x, (x, y) => x); + integers.SequenceEqual(integers); + integers.ToDictionary(x => x); + integers.ToLookup(x => x); + integers.Union(integers); + } +}", + SymbolEqualityComparerStubCSharp, + }, + }, + FixedState = + { + Sources = + { + @" +using System; +using System.Collections.Immutable; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +public class C +{ + public void MethodWithDiagnostics(IEnumerable> kvps, IEnumerable symbols, IEnumerable symbols2, ISymbol symbol) + { + ImmutableHashSet.Create(SymbolEqualityComparer.Default); + ImmutableHashSet.CreateBuilder(SymbolEqualityComparer.Default); + ImmutableHashSet.CreateRange(SymbolEqualityComparer.Default, symbols); + symbols.ToImmutableHashSet(SymbolEqualityComparer.Default); + + ImmutableDictionary.Create(SymbolEqualityComparer.Default); + ImmutableDictionary.CreateBuilder(SymbolEqualityComparer.Default); + ImmutableDictionary.CreateRange(SymbolEqualityComparer.Default, kvps); + kvps.ToImmutableDictionary(SymbolEqualityComparer.Default); + + symbols.Contains(symbol, SymbolEqualityComparer.Default); + symbols.Distinct(SymbolEqualityComparer.Default); + symbols.GroupBy(x => x, SymbolEqualityComparer.Default); + symbols.GroupJoin(symbols2, x => x, x => x, (x, y) => x, SymbolEqualityComparer.Default); + symbols.Intersect(symbols2, SymbolEqualityComparer.Default); + symbols.Join(symbols2, x => x, x => x, (x, y) => x, SymbolEqualityComparer.Default); + symbols.SequenceEqual(symbols2, SymbolEqualityComparer.Default); + symbols.ToDictionary(x => x, SymbolEqualityComparer.Default); + symbols.ToLookup(x => x, SymbolEqualityComparer.Default); + symbols.Union(symbols2, SymbolEqualityComparer.Default); + + ImmutableHashSet.ToImmutableHashSet(symbols, SymbolEqualityComparer.Default); + symbols?.ToImmutableHashSet(SymbolEqualityComparer.Default); + } + + public void MethodWithoutDiagnostics(IEnumerable> kvps, IEnumerable integers, int integer) + { + ImmutableHashSet.Create(); + ImmutableHashSet.CreateBuilder(); + ImmutableHashSet.CreateRange(integers); + integers.ToImmutableHashSet(); + + ImmutableDictionary.Create(); + ImmutableDictionary.CreateBuilder(); + ImmutableDictionary.CreateRange(kvps); + kvps.ToImmutableDictionary(); + + integers.Contains(integer); + integers.Distinct(); + integers.GroupBy(x => x); + integers.GroupJoin(integers, x => x, x => x, (x, y) => x); + integers.Intersect(integers); + integers.Join(integers, x => x, x => x, (x, y) => x); + integers.SequenceEqual(integers); + integers.ToDictionary(x => x); + integers.ToLookup(x => x); + integers.Union(integers); + } +}", + SymbolEqualityComparerStubCSharp, + }, + } + }.RunAsync(); + + await new VerifyVB.Test + { + TestState = + { + Sources = + { + @" +Imports System +Imports System.Collections.Immutable +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + Public Sub MethodWithDiagnostics(kvps As IEnumerable(Of KeyValuePair(Of ISymbol, Integer)), symbols As IEnumerable(Of ISymbol), symbols2 As IEnumerable(Of ISymbol), symbol As ISymbol) + Dim x1 = [|ImmutableHashSet.Create(Of ISymbol)()|] + Dim x2 = [|ImmutableHashSet.CreateBuilder(Of ISymbol)()|] + Dim x3 = [|ImmutableHashSet.CreateRange(symbols)|] + Dim x4 = [|symbols.ToImmutableHashSet()|] + + Dim x5 = [|ImmutableDictionary.Create(Of ISymbol, Integer)()|] + Dim x6 = [|ImmutableDictionary.CreateBuilder(Of ISymbol, Integer)()|] + Dim x7 = [|ImmutableDictionary.CreateRange(kvps)|] + Dim x8 = [|kvps.ToImmutableDictionary()|] + + Dim x9 = [|symbols.Contains(symbol)|] + Dim x10 = [|symbols.Distinct()|] + Dim x11 = [|symbols.GroupBy(Function(x) x)|] + Dim x12 = [|symbols.GroupJoin(symbols2, Function(x) x, Function(x) x, Function(x, y) x)|] + Dim x13 = [|symbols.Intersect(symbols2)|] + Dim x14 = [|symbols.Join(symbols2, Function(x) x, Function(x) x, Function(x, y) x)|] + Dim x15 = [|symbols.SequenceEqual(symbols2)|] + Dim x16 = [|symbols.ToDictionary(Function(x) x)|] + Dim x17 = [|symbols.ToLookup(Function(x) x)|] + Dim x18 = [|symbols.Union(symbols2)|] + + Dim x19 = [|ImmutableHashSet.ToImmutableHashSet(symbols)|] + End Sub + + Public Sub MethodWithoutDiagnostics(kvps As IEnumerable(Of KeyValuePair(Of Integer, ISymbol)), integers As IEnumerable(Of Integer), i As Integer) + Dim x1 = ImmutableHashSet.Create(Of Integer)() + Dim x2 = ImmutableHashSet.CreateBuilder(Of Integer)() + Dim x3 = ImmutableHashSet.CreateRange(integers) + Dim x4 = integers.ToImmutableHashSet() + + Dim x5 = ImmutableDictionary.Create(Of Integer, ISymbol)() + Dim x6 = ImmutableDictionary.CreateBuilder(Of Integer, ISymbol)() + Dim x7 = ImmutableDictionary.CreateRange(kvps) + Dim x8 = kvps.ToImmutableDictionary() + + Dim x9 = integers.Contains(i) + Dim x10 = integers.Distinct() + Dim x11 = integers.GroupBy(Function(x) x) + Dim x12 = integers.GroupJoin(integers, Function(x) x, Function(x) x, Function(x, y) x) + Dim x13 = integers.Intersect(integers) + Dim x14 = integers.Join(integers, Function(x) x, Function(x) x, Function(x, y) x) + Dim x15 = integers.SequenceEqual(integers) + Dim x16 = integers.ToDictionary(Function(x) x) + Dim x17 = integers.ToLookup(Function(x) x) + Dim x18 = integers.Union(integers) + End Sub +End Class", + SymbolEqualityComparerStubVisualBasic, + }, + }, + FixedState = + { + Sources = + { + @" +Imports System +Imports System.Collections.Immutable +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + Public Sub MethodWithDiagnostics(kvps As IEnumerable(Of KeyValuePair(Of ISymbol, Integer)), symbols As IEnumerable(Of ISymbol), symbols2 As IEnumerable(Of ISymbol), symbol As ISymbol) + Dim x1 = ImmutableHashSet.Create(Of ISymbol)(SymbolEqualityComparer.Default) + Dim x2 = ImmutableHashSet.CreateBuilder(Of ISymbol)(SymbolEqualityComparer.Default) + Dim x3 = ImmutableHashSet.CreateRange(SymbolEqualityComparer.Default, symbols) + Dim x4 = symbols.ToImmutableHashSet(SymbolEqualityComparer.Default) + + Dim x5 = ImmutableDictionary.Create(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + Dim x6 = ImmutableDictionary.CreateBuilder(Of ISymbol, Integer)(SymbolEqualityComparer.Default) + Dim x7 = ImmutableDictionary.CreateRange(SymbolEqualityComparer.Default, kvps) + Dim x8 = kvps.ToImmutableDictionary(SymbolEqualityComparer.Default) + + Dim x9 = symbols.Contains(symbol, SymbolEqualityComparer.Default) + Dim x10 = symbols.Distinct(SymbolEqualityComparer.Default) + Dim x11 = symbols.GroupBy(Function(x) x, SymbolEqualityComparer.Default) + Dim x12 = symbols.GroupJoin(symbols2, Function(x) x, Function(x) x, Function(x, y) x, SymbolEqualityComparer.Default) + Dim x13 = symbols.Intersect(symbols2, SymbolEqualityComparer.Default) + Dim x14 = symbols.Join(symbols2, Function(x) x, Function(x) x, Function(x, y) x, SymbolEqualityComparer.Default) + Dim x15 = symbols.SequenceEqual(symbols2, SymbolEqualityComparer.Default) + Dim x16 = symbols.ToDictionary(Function(x) x, SymbolEqualityComparer.Default) + Dim x17 = symbols.ToLookup(Function(x) x, SymbolEqualityComparer.Default) + Dim x18 = symbols.Union(symbols2, SymbolEqualityComparer.Default) + + Dim x19 = ImmutableHashSet.ToImmutableHashSet(symbols, SymbolEqualityComparer.Default) + End Sub + + Public Sub MethodWithoutDiagnostics(kvps As IEnumerable(Of KeyValuePair(Of Integer, ISymbol)), integers As IEnumerable(Of Integer), i As Integer) + Dim x1 = ImmutableHashSet.Create(Of Integer)() + Dim x2 = ImmutableHashSet.CreateBuilder(Of Integer)() + Dim x3 = ImmutableHashSet.CreateRange(integers) + Dim x4 = integers.ToImmutableHashSet() + + Dim x5 = ImmutableDictionary.Create(Of Integer, ISymbol)() + Dim x6 = ImmutableDictionary.CreateBuilder(Of Integer, ISymbol)() + Dim x7 = ImmutableDictionary.CreateRange(kvps) + Dim x8 = kvps.ToImmutableDictionary() + + Dim x9 = integers.Contains(i) + Dim x10 = integers.Distinct() + Dim x11 = integers.GroupBy(Function(x) x) + Dim x12 = integers.GroupJoin(integers, Function(x) x, Function(x) x, Function(x, y) x) + Dim x13 = integers.Intersect(integers) + Dim x14 = integers.Join(integers, Function(x) x, Function(x) x, Function(x, y) x) + Dim x15 = integers.SequenceEqual(integers) + Dim x16 = integers.ToDictionary(Function(x) x) + Dim x17 = integers.ToLookup(Function(x) x) + Dim x18 = integers.Union(integers) + End Sub +End Class", + SymbolEqualityComparerStubVisualBasic, + }, + } + }.RunAsync(); + } + + [Fact, WorkItem(4469, "https://github.com/dotnet/roslyn-analyzers/issues/4469")] + public async Task RS1024_SymbolEqualityComparerDefaultAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void M(IEnumerable e, ISymbol symbol, INamedTypeSymbol type) + { + e.Contains(symbol, SymbolEqualityComparer.Default); + + var asyncMethods = type.GetMembers() + .OfType() + .Where(x => x.IsAsync) + .ToLookup(x => x.ContainingType, x => x, SymbolEqualityComparer.Default); + } +}", + SymbolEqualityComparerStubCSharp, + }, + }, + }.RunAsync(); + } + + [Fact] + [WorkItem(4470, "https://github.com/dotnet/roslyn-analyzers/issues/4470")] + [WorkItem(4568, "https://github.com/dotnet/roslyn-analyzers/issues/4568")] + public async Task RS1024_InvocationArgumentTypeIsNullAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + private readonly HashSet _types = new HashSet(SymbolEqualityComparer.Default); +}", + SymbolEqualityComparerStubCSharp, + }, + }, + }.RunAsync(); + } + + [Fact, WorkItem(4413, "https://github.com/dotnet/roslyn-analyzers/issues/4413")] + public async Task RS1024_SourceCollectionIsSymbolButLambdaIsNotAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void M1(IFieldSymbol[] fields) + { + var result = fields.ToLookup(f => f.Name); + } + + public void M2(IEnumerable source, IEnumerable destination) + { + var result = source.Join(destination, p => (p.Name, p.Type.Name), p => (p.Name, p.Type.Name), (p1, p2) => p1.Name); + } +}", + SymbolEqualityComparerStubCSharp, + }, + }, + }.RunAsync(); + } + + [Fact, WorkItem(4956, "https://github.com/dotnet/roslyn-analyzers/issues/4956")] + public async Task RS1024_StringGetHashCodeAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { +@" +using System; + +class C +{ + void M() + { + ReadOnlySpan testROS = default; + int hashCode = string.GetHashCode(testROS, StringComparison.OrdinalIgnoreCase); + } +}" + , SymbolEqualityComparerStubCSharp + }, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies() + } + }.RunAsync(); + } + + [Fact] + public async Task RS1024_GetHashCodeOnInt64Async() + { + var code = @" +using System; +using Microsoft.CodeAnalysis; + +static class HashCodeHelper +{ + public static Int32 GetHashCode(Int64 x) => 0; + public static Int32 GetHashCode(ISymbol symbol) => [|symbol.GetHashCode()|]; +} + +public class C +{ + public int GetHashCode(Int64 obj) + { + return HashCodeHelper.GetHashCode(obj); + } + + public int GetHashCode(ISymbol symbol) + { + return HashCodeHelper.GetHashCode(symbol); + } + + public int GetHashCode(object o) + { + if (o is ISymbol symbol) + { + return [|HashCode.Combine(symbol)|]; + } + + return HashCode.Combine(o); + } + + public int GetHashCode(object o1, object o2) + { + if (o1 is ISymbol symbol1 && o2 is ISymbol symbol2) + { + return [|HashCode.Combine(symbol1, symbol2)|]; + } + + if (o1 is ISymbol symbolFirst) + { + return [|HashCode.Combine(symbolFirst, o2)|]; + } + + if (o2 is ISymbol symbolSecond) + { + return [|HashCode.Combine(o1, symbolSecond)|]; + } + + return HashCode.Combine(o1, o2); + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = + { + code, SymbolEqualityComparerStubCSharp + }, + }, + FixedState = + { + Sources = + { + code, SymbolEqualityComparerStubCSharp + }, + MarkupHandling = Testing.MarkupMode.Allow + }, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies() + }.RunAsync(); + } + + [Fact] + [WorkItem(5715, "https://github.com/dotnet/roslyn-analyzers/issues/5715")] + public async Task RS1024_CustomComparer_Instance_Is_InterfaceAsync() + { + var csCode = @" +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void M(IEnumerable symbols) + { + _ = new HashSet(SymbolNameComparer.Instance); + _ = symbols.ToDictionary(s => s, s => s.ToDisplayString(), SymbolNameComparer.Instance); + _ = symbols.ToDictionary(s => s, s => s.ToDisplayString(), SymbolEqualityComparer.Default); + } +} + +internal sealed class SymbolNameComparer : EqualityComparer +{ + private SymbolNameComparer() { } + + internal static IEqualityComparer Instance { get; } = new SymbolNameComparer(); + + public override bool Equals(ISymbol x, ISymbol y) => true; + + public override int GetHashCode(ISymbol obj) => 0; +} +"; + + await new VerifyCS.Test + { + TestCode = csCode, + FixedCode = csCode, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + + }.RunAsync(); + + var vbCode = @" +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + + Public Sub M(symbols As IEnumerable(Of ITypeSymbol)) + Dim x As New HashSet(Of ISymbol)(SymbolNameComparer.Instance) + Dim y = symbols.ToDictionary(Function(s) s, Function(s) s.ToDisplayString(), SymbolNameComparer.Instance) + Dim z = symbols.ToDictionary(Function(s) s, Function(s) s.ToDisplayString(), SymbolEqualityComparer.Default) + End Sub +End Class + +Class SymbolNameComparer + Inherits EqualityComparer(Of ISymbol) + + Private Sub New() + End Sub + + Friend Shared Property Instance As IEqualityComparer(Of ISymbol) = New SymbolNameComparer() + + Public Overrides Function Equals(x As ISymbol, y As ISymbol) As Boolean + Return True + End Function + + Public Overrides Function GetHashCode(obj As ISymbol) As Integer + Return 0 + End Function +End Class +"; + + await new VerifyVB.Test + { + TestCode = vbCode, + FixedCode = vbCode, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + + }.RunAsync(); + } + + [Fact] + [WorkItem(5715, "https://github.com/dotnet/roslyn-analyzers/issues/5715")] + public async Task RS1024_CustomComparer_Instance_Is_TypeAsync() + { + var csCode = @" +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; + +public class C +{ + public void M(IEnumerable symbols) + { + _ = new HashSet(SymbolNameComparer.Instance); + _ = symbols.ToDictionary(s => s, s => s.ToDisplayString(), SymbolNameComparer.Instance); + _ = symbols.ToDictionary(s => s, s => s.ToDisplayString(), SymbolEqualityComparer.Default); + } +} + +internal sealed class SymbolNameComparer : EqualityComparer +{ + private SymbolNameComparer() { } + + internal static SymbolNameComparer Instance { get; } = new SymbolNameComparer(); + + public override bool Equals(ISymbol x, ISymbol y) => true; + + public override int GetHashCode(ISymbol obj) => 0; +} +"; + + await new VerifyCS.Test + { + TestCode = csCode, + FixedCode = csCode, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + + }.RunAsync(); + + var vbCode = @" +Imports System.Collections.Generic +Imports System.Linq +Imports Microsoft.CodeAnalysis + +Public Class C + Public Sub M(symbols As IEnumerable(Of ITypeSymbol)) + Dim x As New HashSet(Of ISymbol)(SymbolNameComparer.Instance) + Dim y = symbols.ToDictionary(Function(s) s, Function(s) s.ToDisplayString(), SymbolNameComparer.Instance) + Dim z = symbols.ToDictionary(Function(s) s, Function(s) s.ToDisplayString(), SymbolEqualityComparer.Default) + End Sub +End Class + +Class SymbolNameComparer + Inherits EqualityComparer(Of ISymbol) + + Private Sub New() + End Sub + + Friend Shared Property Instance As SymbolNameComparer = New SymbolNameComparer() + + Public Overrides Function Equals(x As ISymbol, y As ISymbol) As Boolean + Return True + End Function + + Public Overrides Function GetHashCode(obj As ISymbol) As Integer + Return 0 + End Function +End Class +"; + + await new VerifyVB.Test + { + TestCode = vbCode, + FixedCode = vbCode, + ReferenceAssemblies = CreateNetCoreReferenceAssemblies(), + + }.RunAsync(); + } + + private static ReferenceAssemblies CreateNetCoreReferenceAssemblies() + => ReferenceAssemblies.NetCore.NetCoreApp31.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis", "4.0.1"), + new PackageIdentity("System.Runtime.Serialization.Formatters", "4.3.0"), + new PackageIdentity("System.Configuration.ConfigurationManager", "4.7.0"), + new PackageIdentity("System.Security.Cryptography.Cng", "4.7.0"), + new PackageIdentity("System.Security.Permissions", "4.7.0"), + new PackageIdentity("Microsoft.VisualBasic", "10.3.0"))); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionStrictApiAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionStrictApiAnalyzerTests.cs new file mode 100644 index 0000000000000..6d2dcba5a316e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionStrictApiAnalyzerTests.cs @@ -0,0 +1,334 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompilerExtensionStrictApiAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompilerExtensionStrictApiAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class CompilerExtensionStrictApiAnalyzerTests + { + private const string CompilerReferenceVersion = "4.6.0"; + + public enum ImplementationLanguage + { + CSharp, + VisualBasic, + } + + public enum SupportedLanguage + { + CSharp, + VisualBasic, + CSharpAndVisualBasic, + } + + public enum CompilerFeature + { + DiagnosticAnalyzer, + DiagnosticSuppressor, + ISourceGenerator, + IIncrementalGenerator, + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithCommonReference(CompilerFeature feature, SupportedLanguage supportedLanguage) + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.CSharp, feature, supportedLanguage), + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithMatchingLanguageReference(CompilerFeature feature, [CombinatorialValues(SupportedLanguage.CSharp, SupportedLanguage.VisualBasic)] SupportedLanguage supportedLanguage) + { + var matchingPackage = supportedLanguage switch + { + SupportedLanguage.CSharp => "Microsoft.CodeAnalysis.CSharp", + SupportedLanguage.VisualBasic => "Microsoft.CodeAnalysis.VisualBasic", + _ => throw new NotImplementedException(), + }; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity(matchingPackage, CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.CSharp, feature, supportedLanguage), + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithWorkspaceReference(CompilerFeature feature, SupportedLanguage supportedLanguage, bool relaxedValidation) + { + var test = new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Workspaces.Common", CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.CSharp, feature, supportedLanguage), + }; + + if (relaxedValidation) + { + test.TestState.AnalyzerConfigFiles.Add( + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """)); + } + else + { + test.TestState.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(CompilerExtensionStrictApiAnalyzer.DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceStrictRule).WithLocation(0)); + } + + await test.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithMismatchedLanguageReference(CompilerFeature feature, SupportedLanguage supportedLanguage) + { + var (mismatchedPackage, descriptor) = supportedLanguage switch + { + SupportedLanguage.CSharp => ("Microsoft.CodeAnalysis.VisualBasic", CompilerExtensionStrictApiAnalyzer.DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule), + SupportedLanguage.VisualBasic => ("Microsoft.CodeAnalysis.CSharp", CompilerExtensionStrictApiAnalyzer.DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule), + SupportedLanguage.CSharpAndVisualBasic => ("Microsoft.CodeAnalysis.VisualBasic", CompilerExtensionStrictApiAnalyzer.DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule), + _ => throw new NotImplementedException(), + }; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity(mismatchedPackage, CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.CSharp, feature, supportedLanguage), + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(descriptor).WithLocation(0), + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithCommonReference(CompilerFeature feature, SupportedLanguage supportedLanguage) + { + await new VerifyVB.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.VisualBasic, feature, supportedLanguage), + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithMatchingLanguageReference(CompilerFeature feature, [CombinatorialValues(SupportedLanguage.CSharp, SupportedLanguage.VisualBasic)] SupportedLanguage supportedLanguage) + { + var matchingPackage = supportedLanguage switch + { + SupportedLanguage.CSharp => "Microsoft.CodeAnalysis.CSharp", + SupportedLanguage.VisualBasic => "Microsoft.CodeAnalysis.VisualBasic", + _ => throw new NotImplementedException(), + }; + + await new VerifyVB.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity(matchingPackage, CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.VisualBasic, feature, supportedLanguage), + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithWorkspaceReference(CompilerFeature feature, SupportedLanguage supportedLanguage, bool relaxedValidation) + { + var test = new VerifyVB.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Workspaces.Common", CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.VisualBasic, feature, supportedLanguage), + }; + + if (relaxedValidation) + { + test.TestState.AnalyzerConfigFiles.Add( + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """)); + } + else + { + test.TestState.ExpectedDiagnostics.Add(VerifyVB.Diagnostic(CompilerExtensionStrictApiAnalyzer.DoNotDeclareCompilerFeatureInAssemblyWithWorkspacesReferenceStrictRule).WithLocation(0)); + } + + await test.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithMismatchedLanguageReference(CompilerFeature feature, SupportedLanguage supportedLanguage) + { + var (mismatchedPackage, descriptor) = supportedLanguage switch + { + SupportedLanguage.CSharp => ("Microsoft.CodeAnalysis.VisualBasic", CompilerExtensionStrictApiAnalyzer.DoNotDeclareCSharpCompilerFeatureInAssemblyWithVisualBasicReferenceStrictRule), + SupportedLanguage.VisualBasic => ("Microsoft.CodeAnalysis.CSharp", CompilerExtensionStrictApiAnalyzer.DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule), + SupportedLanguage.CSharpAndVisualBasic => ("Microsoft.CodeAnalysis.CSharp", CompilerExtensionStrictApiAnalyzer.DoNotDeclareVisualBasicCompilerFeatureInAssemblyWithCSharpReferenceStrictRule), + _ => throw new NotImplementedException(), + }; + + await new VerifyVB.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages(ImmutableArray.Create( + new PackageIdentity(mismatchedPackage, CompilerReferenceVersion))), + TestCode = DefineFeature(ImplementationLanguage.VisualBasic, feature, supportedLanguage), + ExpectedDiagnostics = + { + VerifyVB.Diagnostic(descriptor).WithLocation(0), + }, + }.RunAsync(); + } + + private static string DefineFeature(ImplementationLanguage languageName, CompilerFeature feature, SupportedLanguage supportedLanguage) + { + var languageApplication = (languageName, supportedLanguage) switch + { + (_, SupportedLanguage.CSharp) => "LanguageNames.CSharp", + (_, SupportedLanguage.VisualBasic) => "LanguageNames.VisualBasic", + (ImplementationLanguage.CSharp, SupportedLanguage.CSharpAndVisualBasic) => "LanguageNames.CSharp, LanguageNames.VisualBasic", + (ImplementationLanguage.VisualBasic, SupportedLanguage.CSharpAndVisualBasic) => "LanguageNames.VisualBasic, LanguageNames.CSharp", + _ => throw new NotImplementedException(), + }; + + return (languageName, feature) switch + { + (ImplementationLanguage.CSharp, CompilerFeature.DiagnosticAnalyzer) => + $$""" + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [{|#0:DiagnosticAnalyzer({{languageApplication}})|}] + public class MyAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } + public override void Initialize(AnalysisContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.DiagnosticSuppressor) => + $$""" + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [{|#0:DiagnosticAnalyzer({{languageApplication}})|}] + public class MySuppressor : DiagnosticSuppressor + { + public override ImmutableArray SupportedSuppressions { get; } + public override void ReportSuppressions(SuppressionAnalysisContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.ISourceGenerator) => + $$""" + using Microsoft.CodeAnalysis; + + [{|#0:Generator({{languageApplication}})|}] + public class MyGenerator : ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) { } + public void Execute(GeneratorExecutionContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.IIncrementalGenerator) => + $$""" + using Microsoft.CodeAnalysis; + + [{|#0:Generator({{languageApplication}})|}] + public class MyGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) { } + } + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.DiagnosticAnalyzer) => + $$""" + Imports System.Collections.Immutable + Imports Microsoft.CodeAnalysis + Imports Microsoft.CodeAnalysis.Diagnostics + + <{|#0:DiagnosticAnalyzer({{languageApplication}})|}> + Public Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.DiagnosticSuppressor) => + $$""" + Imports System.Collections.Immutable + Imports Microsoft.CodeAnalysis + Imports Microsoft.CodeAnalysis.Diagnostics + + <{|#0:DiagnosticAnalyzer({{languageApplication}})|}> + Public Class MySuppressor + Inherits DiagnosticSuppressor + + Public Overrides ReadOnly Property SupportedSuppressions As ImmutableArray(Of SuppressionDescriptor) + Public Overrides Sub ReportSuppressions(context As SuppressionAnalysisContext) + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.ISourceGenerator) => + $$""" + Imports Microsoft.CodeAnalysis + + <{|#0:Generator({{languageApplication}})|}> + Public Class MyGenerator + Implements ISourceGenerator + + Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize + End Sub + + Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.IIncrementalGenerator) => + $$""" + Imports Microsoft.CodeAnalysis + + <{|#0:Generator({{languageApplication}})|}> + Public Class MyGenerator + Implements IIncrementalGenerator + + Public Sub Initialize(context As IncrementalGeneratorInitializationContext) Implements IIncrementalGenerator.Initialize + End Sub + End Class + """, + _ => throw new NotImplementedException(), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzerTests.cs new file mode 100644 index 0000000000000..c90d13c793cc0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzerTests.cs @@ -0,0 +1,414 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompilerExtensionTargetFrameworkAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.CompilerExtensionTargetFrameworkAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class CompilerExtensionTargetFrameworkAnalyzerTests + { + public enum ImplementationLanguage + { + CSharp, + VisualBasic, + } + + public enum SupportedLanguage + { + CSharp, + VisualBasic, + CSharpAndVisualBasic, + } + + public enum CompilerFeature + { + DiagnosticAnalyzer, + DiagnosticSuppressor, + ISourceGenerator, + IIncrementalGenerator, + } + + public enum SupportedTargetFramework + { + // Excluding theoretically-supported frameworks that we can't test using Microsoft.CodeAnalysis.Testing. + //NetStandard1_0, + //NetStandard1_1, + //NetStandard1_2, + NetStandard1_3, + NetStandard1_4, + NetStandard1_5, + NetStandard1_6, + NetStandard2_0, + } + + public enum UnsupportedTargetFramework + { + NetFramework4_7_2, + NetStandard2_1, + Net6_0, + Net7_0, + Net8_0, + } + + [Theory] + [CombinatorialData] + public async Task CSharpAnalyzerDefinedWithSupportedFramework(SupportedTargetFramework supportedFramework) + { + await new VerifyCS.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(supportedFramework).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(supportedFramework)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.CSharp, CompilerFeature.DiagnosticAnalyzer, SupportedLanguage.CSharp), + GetTargetFrameworkAttribute(ImplementationLanguage.CSharp, supportedFramework), + }, + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithSupportedFramework(CompilerFeature feature) + { + await new VerifyCS.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(SupportedTargetFramework.NetStandard2_0).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(SupportedTargetFramework.NetStandard2_0)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.CSharp, feature, SupportedLanguage.CSharp), + GetTargetFrameworkAttribute(ImplementationLanguage.CSharp, SupportedTargetFramework.NetStandard2_0), + }, + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task CSharpFeatureDefinedWithUnsupportedFramework(CompilerFeature feature, UnsupportedTargetFramework framework) + { + await new VerifyCS.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(framework).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(framework)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.CSharp, feature, SupportedLanguage.CSharp), + GetTargetFrameworkAttribute(ImplementationLanguage.CSharp, framework), + }, + }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic().WithLocation(0).WithArguments(GetDisplayName(framework)), + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicAnalyzerDefinedWithSupportedFramework(SupportedTargetFramework supportedFramework) + { + await new VerifyVB.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(supportedFramework).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(supportedFramework)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.VisualBasic, CompilerFeature.DiagnosticAnalyzer, SupportedLanguage.VisualBasic), + GetTargetFrameworkAttribute(ImplementationLanguage.VisualBasic, supportedFramework), + }, + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithSupportedFramework(CompilerFeature feature) + { + await new VerifyVB.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(SupportedTargetFramework.NetStandard2_0).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(SupportedTargetFramework.NetStandard2_0)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.VisualBasic, feature, SupportedLanguage.VisualBasic), + GetTargetFrameworkAttribute(ImplementationLanguage.VisualBasic, SupportedTargetFramework.NetStandard2_0), + }, + }, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task VisualBasicFeatureDefinedWithUnsupportedFramework(CompilerFeature feature, UnsupportedTargetFramework framework) + { + await new VerifyVB.Test + { + ReferenceAssemblies = GetReferenceAssembliesForTargetFramework(framework).AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Common", GetCodeAnalysisPackageVersion(framework)))), + TestState = + { + Sources = + { + DefineFeature(ImplementationLanguage.VisualBasic, feature, SupportedLanguage.VisualBasic), + GetTargetFrameworkAttribute(ImplementationLanguage.VisualBasic, framework), + }, + }, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic().WithLocation(0).WithArguments(GetDisplayName(framework)), + }, + }.RunAsync(); + } + + private static ReferenceAssemblies GetReferenceAssembliesForTargetFramework(SupportedTargetFramework framework) + { + return framework switch + { + //SupportedTargetFramework.NetStandard1_0 => ReferenceAssemblies.NetStandard.NetStandard10, + //SupportedTargetFramework.NetStandard1_1 => ReferenceAssemblies.NetStandard.NetStandard11, + //SupportedTargetFramework.NetStandard1_2 => ReferenceAssemblies.NetStandard.NetStandard12, + SupportedTargetFramework.NetStandard1_3 => ReferenceAssemblies.NetStandard.NetStandard13, + SupportedTargetFramework.NetStandard1_4 => ReferenceAssemblies.NetStandard.NetStandard14, + SupportedTargetFramework.NetStandard1_5 => ReferenceAssemblies.NetStandard.NetStandard15, + SupportedTargetFramework.NetStandard1_6 => ReferenceAssemblies.NetStandard.NetStandard16, + SupportedTargetFramework.NetStandard2_0 => ReferenceAssemblies.NetStandard.NetStandard20, + _ => throw new ArgumentException("Unknown target framework"), + }; + } + + private static ReferenceAssemblies GetReferenceAssembliesForTargetFramework(UnsupportedTargetFramework framework) + { + return framework switch + { + UnsupportedTargetFramework.NetFramework4_7_2 => ReferenceAssemblies.NetFramework.Net472.Default, + UnsupportedTargetFramework.NetStandard2_1 => ReferenceAssemblies.NetStandard.NetStandard21, + //UnsupportedTargetFramework.NetCoreApp3_1 => ReferenceAssemblies.NetCore.NetCoreApp31, + UnsupportedTargetFramework.Net6_0 => ReferenceAssemblies.Net.Net60, + UnsupportedTargetFramework.Net7_0 => ReferenceAssemblies.Net.Net70, + UnsupportedTargetFramework.Net8_0 => ReferenceAssemblies.Net.Net80, + _ => throw new ArgumentException("Unknown target framework"), + }; + } + + private static string GetCodeAnalysisPackageVersion(SupportedTargetFramework framework) + { + return framework switch + { + //SupportedTargetFramework.NetStandard1_0 => throw new NotImplementedException(), + //SupportedTargetFramework.NetStandard1_1 => throw new NotImplementedException(), + //SupportedTargetFramework.NetStandard1_2 => throw new NotImplementedException(), + SupportedTargetFramework.NetStandard1_3 => "2.8.2", + SupportedTargetFramework.NetStandard1_4 => "2.8.2", + SupportedTargetFramework.NetStandard1_5 => "2.8.2", + SupportedTargetFramework.NetStandard1_6 => "2.8.2", + SupportedTargetFramework.NetStandard2_0 => "4.6.0", + _ => throw new ArgumentException("Unknown target framework"), + }; + } + + private static string GetTargetFrameworkAttribute(ImplementationLanguage language, SupportedTargetFramework framework) + { + var (name, displayName) = framework switch + { + SupportedTargetFramework.NetStandard1_3 => (".NETStandard,Version=v1.3", ".NET Standard 1.3"), + SupportedTargetFramework.NetStandard1_4 => (".NETStandard,Version=v1.4", ".NET Standard 1.4"), + SupportedTargetFramework.NetStandard1_5 => (".NETStandard,Version=v1.5", ".NET Standard 1.5"), + SupportedTargetFramework.NetStandard1_6 => (".NETStandard,Version=v1.6", ".NET Standard 1.6"), + SupportedTargetFramework.NetStandard2_0 => (".NETStandard,Version=v2.0", ".NET Standard 2.0"), + _ => throw new NotImplementedException(), + }; + + return language switch + { + ImplementationLanguage.CSharp => $"[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(\"{name}\", FrameworkDisplayName = \"{displayName}\")]", + ImplementationLanguage.VisualBasic => $"", + _ => throw new NotImplementedException(), + }; + } + + private static string GetTargetFrameworkAttribute(ImplementationLanguage language, UnsupportedTargetFramework framework) + { + var (name, displayName) = framework switch + { + UnsupportedTargetFramework.NetFramework4_7_2 => (".NETFramework,Version=v4.7.2", ".NET Framework 4.7.2"), + UnsupportedTargetFramework.NetStandard2_1 => (".NETStandard,Version=v2.1", ".NET Standard 2.1"), + UnsupportedTargetFramework.Net6_0 => (".NETCoreApp,Version=v6.0", ".NET 6.0"), + UnsupportedTargetFramework.Net7_0 => (".NETCoreApp,Version=v7.0", ".NET 7.0"), + UnsupportedTargetFramework.Net8_0 => (".NETCoreApp,Version=v8.0", ".NET 8.0"), + _ => throw new NotImplementedException(), + }; + + return language switch + { + ImplementationLanguage.CSharp => $"[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(\"{name}\", FrameworkDisplayName = \"{displayName}\")]", + ImplementationLanguage.VisualBasic => $"", + _ => throw new NotImplementedException(), + }; + } + + private static string GetDisplayName(UnsupportedTargetFramework framework) + { + return framework switch + { + UnsupportedTargetFramework.NetFramework4_7_2 => ".NET Framework 4.7.2", + UnsupportedTargetFramework.NetStandard2_1 => ".NET Standard 2.1", + UnsupportedTargetFramework.Net6_0 => ".NET 6.0", + UnsupportedTargetFramework.Net7_0 => ".NET 7.0", + UnsupportedTargetFramework.Net8_0 => ".NET 8.0", + _ => throw new NotImplementedException(), + }; + } + + private static string GetCodeAnalysisPackageVersion(UnsupportedTargetFramework framework) + { + return framework switch + { + _ => "4.0.0", + }; + } + + private static string DefineFeature(ImplementationLanguage languageName, CompilerFeature feature, SupportedLanguage supportedLanguage) + { + var languageApplication = (languageName, supportedLanguage) switch + { + (_, SupportedLanguage.CSharp) => "LanguageNames.CSharp", + (_, SupportedLanguage.VisualBasic) => "LanguageNames.VisualBasic", + (ImplementationLanguage.CSharp, SupportedLanguage.CSharpAndVisualBasic) => "LanguageNames.CSharp, LanguageNames.VisualBasic", + (ImplementationLanguage.VisualBasic, SupportedLanguage.CSharpAndVisualBasic) => "LanguageNames.VisualBasic, LanguageNames.CSharp", + _ => throw new NotImplementedException(), + }; + + return (languageName, feature) switch + { + (ImplementationLanguage.CSharp, CompilerFeature.DiagnosticAnalyzer) => + $$""" + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [{|#0:DiagnosticAnalyzer({{languageApplication}})|}] + public class MyAnalyzer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics { get; } + public override void Initialize(AnalysisContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.DiagnosticSuppressor) => + $$""" + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + + [{|#0:DiagnosticAnalyzer({{languageApplication}})|}] + public class MySuppressor : DiagnosticSuppressor + { + public override ImmutableArray SupportedSuppressions { get; } + public override void ReportSuppressions(SuppressionAnalysisContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.ISourceGenerator) => + $$""" + using Microsoft.CodeAnalysis; + + [{|#0:Generator({{languageApplication}})|}] + public class MyGenerator : ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) { } + public void Execute(GeneratorExecutionContext context) { } + } + """, + (ImplementationLanguage.CSharp, CompilerFeature.IIncrementalGenerator) => + $$""" + using Microsoft.CodeAnalysis; + + [{|#0:Generator({{languageApplication}})|}] + public class MyGenerator : IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) { } + } + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.DiagnosticAnalyzer) => + $$""" + Imports System.Collections.Immutable + Imports Microsoft.CodeAnalysis + Imports Microsoft.CodeAnalysis.Diagnostics + + <{|#0:DiagnosticAnalyzer({{languageApplication}})|}> + Public Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.DiagnosticSuppressor) => + $$""" + Imports System.Collections.Immutable + Imports Microsoft.CodeAnalysis + Imports Microsoft.CodeAnalysis.Diagnostics + + <{|#0:DiagnosticAnalyzer({{languageApplication}})|}> + Public Class MySuppressor + Inherits DiagnosticSuppressor + + Public Overrides ReadOnly Property SupportedSuppressions As ImmutableArray(Of SuppressionDescriptor) + Public Overrides Sub ReportSuppressions(context As SuppressionAnalysisContext) + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.ISourceGenerator) => + $$""" + Imports Microsoft.CodeAnalysis + + <{|#0:Generator({{languageApplication}})|}> + Public Class MyGenerator + Implements ISourceGenerator + + Public Sub Initialize(context As GeneratorInitializationContext) Implements ISourceGenerator.Initialize + End Sub + + Public Sub Execute(context As GeneratorExecutionContext) Implements ISourceGenerator.Execute + End Sub + End Class + """, + (ImplementationLanguage.VisualBasic, CompilerFeature.IIncrementalGenerator) => + $$""" + Imports Microsoft.CodeAnalysis + + <{|#0:Generator({{languageApplication}})|}> + Public Class MyGenerator + Implements IIncrementalGenerator + + Public Sub Initialize(context As IncrementalGeneratorInitializationContext) Implements IIncrementalGenerator.Initialize + End Sub + End Class + """, + _ => throw new NotImplementedException(), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzerTests.cs new file mode 100644 index 0000000000000..0606aa0fca6e8 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ConfigureGeneratedCodeAnalysisAnalyzerTests.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.ConfigureGeneratedCodeAnalysisAnalyzer, + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers.CSharpConfigureGeneratedCodeAnalysisFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.ConfigureGeneratedCodeAnalysisAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes.BasicConfigureGeneratedCodeAnalysisFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class ConfigureGeneratedCodeAnalysisAnalyzerTests + { + [Fact] + public async Task TestSimpleCase_CSharpAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext [|context|]) + { + } +} +"; + var fixedCode = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [Fact] + public async Task TestSimpleCase_VisualBasicAsync() + { + var code = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize([|context|] As AnalysisContext) + End Sub +End Class +"; + var fixedCode = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze Or GeneratedCodeAnalysisFlags.ReportDiagnostics) + End Sub +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(code, fixedCode); + } + + [Fact] + public async Task RenamedMethod_CSharpAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + } + + public void NotInitialize(AnalysisContext context) + { + } +} +"; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task RenamedMethod_VisualBasicAsync() + { + var code = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze Or GeneratedCodeAnalysisFlags.ReportDiagnostics) + End Sub + + Public Sub NotInitialize(context As AnalysisContext) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [Fact, WorkItem(2698, "https://github.com/dotnet/roslyn-analyzers/issues/2698")] + public async Task RS1025_ExpressionBodiedMethodAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext [|context|]) + => context.RegisterCompilationAction(x => { }); +} +"; + var fixedCode = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.RegisterCompilationAction(x => { }); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticAnalyzerApiUsageAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticAnalyzerApiUsageAnalyzerTests.cs new file mode 100644 index 0000000000000..28245b9c3561c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticAnalyzerApiUsageAnalyzerTests.cs @@ -0,0 +1,1048 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpDiagnosticAnalyzerApiUsageAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicDiagnosticAnalyzerApiUsageAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class DiagnosticAnalyzerApiUsageAnalyzerTests + { + private static async Task VerifyCSharpAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AnalyzerConfigFiles = + { + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """), + } + } + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyVisualBasicAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AnalyzerConfigFiles = + { + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """), + } + } + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + [Fact] + public async Task NoDiagnosticCasesAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +interface I { } + +internal delegate void MyDelegateGlobal(); + +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics=> throw new NotImplementedException(); + + internal delegate void MyDelegateInType(); + + public override void Initialize(AnalysisContext context) + { + var x = typeof(I<>); + } +} + +class MyFixer : CodeFixProvider +{ + public sealed override ImmutableArray FixableDiagnosticIds => throw new NotImplementedException(); + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + return null; + } +} + +class MyFixer2 : I +{ + Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider field; +}"); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports System.Threading.Tasks +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Interface I(Of T) +End Interface + +Friend Delegate Sub MyDelegateGlobal() + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Friend Delegate Sub MyDelegateInType() + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Class MyFixer + Inherits CodeFixProvider + + Public NotOverridable Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) + Get + Throw New NotImplementedException() + End Get + End Property + + Public NotOverridable Overrides Function RegisterCodeFixesAsync(ByVal context As CodeFixContext) As Task + Return Nothing + End Function +End Class + +Class MyFixer2 + Implements I(Of CodeFixProvider) + + Dim field As Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider +End Class +"); + } + + [Theory] + [CombinatorialData] + public async Task DirectlyAccessedType_InDeclaration_DiagnosticAsync(bool relaxedValidation) + { + var csTest = new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +interface I { } + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer, I +{ + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + }, + } + }; + + if (relaxedValidation) + { + csTest.TestState.AnalyzerConfigFiles.Add( + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """)); + csTest.ExpectedDiagnostics.Add( + // Test0.cs(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + await csTest.RunAsync(); + + var vbTest = new VerifyVB.Test + { + TestState = + { + Sources = + { + @" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Interface I(Of T) +End Interface + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + Implements I(Of Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + }, + } + }; + + if (relaxedValidation) + { + vbTest.TestState.AnalyzerConfigFiles.Add( + ("/.globalconfig", + """ + is_global = true + + roslyn_correctness.assembly_reference_validation = relaxed + """)); + vbTest.ExpectedDiagnostics.Add( + // Test0.vb(12,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + await vbTest.RunAsync(); + } + + [Fact] + public async Task DirectlyAccessedType_InMemberDeclaration_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + private readonly CodeFixProvider field; + FixAllContext Method(FixAllProvider param) + { + return null; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllContext, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllContext, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Dim field As CodeFixProvider + Function Method(param As FixAllProvider) As FixAllContext + Return Nothing + End Function + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllContext, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllContext, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + } + + [Fact] + public async Task DirectlyAccessedType_InMemberBody_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method() + { + CodeFixProvider c = null; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method() + Dim c As CodeFixProvider = Nothing + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + [Fact] + public async Task DirectlyAccessedType_StaticMember_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using static Microsoft.CodeAnalysis.CodeActions.WarningAnnotation; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method() + { + var c = Kind; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + // Test0.cs(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeActions.WarningAnnotation'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeActions.WarningAnnotation")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports WarningAnnotation = Microsoft.CodeAnalysis.CodeActions.WarningAnnotation + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method() + Dim c = WarningAnnotation.Kind + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + // Test0.vb(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeActions.WarningAnnotation'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeActions.WarningAnnotation")); + } + + [Fact] + public async Task DirectlyAccessedType_StaticMember_02_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using static Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method() + { + var c = BatchFixer; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + // Test0.cs(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.FixAllProvider, Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.FixAllProvider, Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports WellKnownFixAllProviders = Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method() + Dim c = WellKnownFixAllProviders.BatchFixer + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + // Test0.vb(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.FixAllProvider, Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.FixAllProvider, Microsoft.CodeAnalysis.CodeFixes.WellKnownFixAllProviders")); + } + + [Fact] + public async Task DirectlyAccessedType_CastAndTypeCheck_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method(object o) + { + var c = (CodeFixProvider)o; + var c2 = o is FixAllProvider; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +}", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method(o As Object) + Dim c = DirectCast(o, CodeFixProvider) + Dim c2 = TypeOf o Is FixAllProvider + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct accesses to type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + } + + [Fact] + public async Task IndirectlyAccessedType_StaticMember_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using static Fixer; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + private string Value => FixerValue; + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +class Fixer : CodeFixProvider +{ + internal static readonly string FixerValue = ""something""; + + public sealed override ImmutableArray FixableDiagnosticIds => throw new NotImplementedException(); + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + return null; + } + +} +", + // Test0.cs(12,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Fixer', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixContext, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Fixer", "Microsoft.CodeAnalysis.CodeFixes.CodeFixContext, Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + [Fact] + public async Task IndirectlyAccessedType_ExtensionMethod_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using static FixerExtensions; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void M() + { + this.ExtensionMethod(); + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +public static class FixerExtensions +{ + public static void ExtensionMethod(this DiagnosticAnalyzer analyzer) + { + CodeFixProvider c = null; + } +} +", + // Test0.cs(11,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'FixerExtensions', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "FixerExtensions", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports FixerExtensions + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Private Sub M() + Me.ExtensionMethod() + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Module FixerExtensions + + Sub ExtensionMethod(analyzer As DiagnosticAnalyzer) + Dim c As CodeFixProvider = Nothing + End Sub +End Module +", + // Test0.vb(12,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'FixerExtensions', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "FixerExtensions", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + [Fact] + public async Task IndirectlyAccessedType_InvokedFromAnalyzer_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method(Class2 c) + { + c.M(); + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +class Class2 +{ + public void M() + { + CodeFixProvider c = null; + } +} +", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Class2", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method(c As Class2) + c.M() + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Class Class2 + Sub M() + Dim c As CodeFixProvider = Nothing + End Sub +End Class +", + + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Class2", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + [Fact] + public async Task IndirectlyAccessedType_NotInvokedFromAnalyzer_DiagnosticAsync() + { + // We report diagnostic if there is a transitive access to a type referencing something from Workspaces. + // This is regardless of whether the transitive access is actually reachable from a possible code path or not. + + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method(Class2 c2, Class3 c3) + { + c2.M2(); + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +class Class2 +{ + public void M() + { + CodeFixProvider c = null; + } + + + public void M2() + { + } +} + +class Class3 +{ + public void M() + { + FixAllProvider c = null; + } +} +", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2, Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Class2, Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method(c2 As Class2, c3 As Class3) + c2.M2() + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Class Class2 + Sub M() + Dim c As CodeFixProvider = Nothing + End Sub + + Sub M2() + End Sub +End Class + +Class Class3 + Sub M() + Dim c As FixAllProvider = Nothing + End Sub +End Class +", + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2, Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Class2, Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + } + + [Fact] + public async Task IndirectlyAccessedType_Transitive_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method(Class2 c2) + { + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +class Class2 +{ + public void M2(Class3 c3) + { + } +} + +class Class3 +{ + public void M() + { + CodeFixProvider c = null; + } +} +", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method(c2 As Class2) + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Class Class2 + Sub M2(c3 As Class3) + End Sub +End Class + +Class Class3 + Sub M() + Dim c As CodeFixProvider = Nothing + End Sub +End Class +", + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider")); + } + + [Fact] + public async Task TypeDependencyGraphWithCycles_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class {|#0:MyAnalyzer|} : DiagnosticAnalyzer +{ + void Method(Class2 c2, Class3 c3) + { + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + } +} + +class Class2 +{ + public void M() + { + CodeFixProvider c = null; + } + + public void M2(Class3 c3) + { + } +} + +class Class3 +{ + public void M(Class2 c2) + { + FixAllProvider c = null; + } +} +", + // Test0.cs(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2, Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetCSharpExpectedDiagnostic(0, "MyAnalyzer", "Class2, Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + + await VerifyVisualBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class {|#0:MyAnalyzer|} + Inherits DiagnosticAnalyzer + + Sub Method(c2 As Class2, c3 As Class3) + End Sub + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Class Class2 + Sub M() + Dim c As CodeFixProvider = Nothing + End Sub + + Sub M2(c3 As Class3) + End Sub +End Class + +Class Class3 + Sub M(c2 As Class2) + Dim c As FixAllProvider = Nothing + End Sub +End Class +", + // Test0.vb(10,7): warning RS1022: Change diagnostic analyzer type 'MyAnalyzer' to remove all direct and/or indirect accesses to type(s) 'Class2, Class3', which access type(s) 'Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider'. + GetBasicExpectedDiagnostic(0, "MyAnalyzer", "Class2, Class3", "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider, Microsoft.CodeAnalysis.CodeFixes.FixAllProvider")); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int location, string analyzerTypeName, string violatingTypes) => + VerifyCS.Diagnostic(CSharpDiagnosticAnalyzerApiUsageAnalyzer.DoNotUseTypesFromAssemblyDirectRule) + .WithLocation(location) + .WithArguments(analyzerTypeName, violatingTypes); + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int location, string analyzerTypeName, string violatingIndirectTypes, string violatingTypes) => + VerifyCS.Diagnostic(CSharpDiagnosticAnalyzerApiUsageAnalyzer.DoNotUseTypesFromAssemblyIndirectRule) + .WithLocation(location) + .WithArguments(analyzerTypeName, violatingIndirectTypes, violatingTypes); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int location, string analyzerTypeName, string violatingTypes) => + VerifyVB.Diagnostic(BasicDiagnosticAnalyzerApiUsageAnalyzer.DoNotUseTypesFromAssemblyDirectRule) + .WithLocation(location) + .WithArguments(analyzerTypeName, violatingTypes); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int location, string analyzerTypeName, string violatingIndirectTypes, string violatingTypes) => + VerifyVB.Diagnostic(BasicDiagnosticAnalyzerApiUsageAnalyzer.DoNotUseTypesFromAssemblyIndirectRule) + .WithLocation(location) + .WithArguments(analyzerTypeName, violatingIndirectTypes, violatingTypes); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs new file mode 100644 index 0000000000000..809bd19d4d42d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DiagnosticDescriptorCreationAnalyzerTests.cs @@ -0,0 +1,4501 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticDescriptorCreationAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.DefineDiagnosticDescriptorArgumentsCorrectlyFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticDescriptorCreationAnalyzer, + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers.DefineDiagnosticDescriptorArgumentsCorrectlyFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class DiagnosticDescriptorCreationAnalyzerTests + { + #region RS1007 (UseLocalizableStringsInDescriptorRuleId) and RS1015 (ProvideHelpUriInDescriptorRuleId) + + [Fact] + public async Task RS1007_RS1015_CSharp_VerifyDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true)|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(0), + GetRS1028ResultAt(0)); + } + + [Fact] + public async Task RS1007_RS1015_VisualBasic_VerifyDiagnosticAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Private Shared ReadOnly descriptor As DiagnosticDescriptor = {|#0:new {|#1:DiagnosticDescriptor|}(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:= true)|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(1), + GetRS1028ResultAt(1)); + } + + [Fact] + public async Task RS1007_RS1015_CSharp_VerifyDiagnostic_NamedArgumentCasesAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", messageFormat: ""MyDiagnosticMessage"", title: ""MyDiagnosticTitle"", {|#2:helpLinkUri: null|}, category: ""MyDiagnosticCategory"", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true)|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", messageFormat: ""MyDiagnosticMessage"", title: ""MyDiagnosticTitle"", category: ""MyDiagnosticCategory"", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true)|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1028ResultAt(0), + GetRS1015ExpectedDiagnostic(2), + GetRS1007ExpectedDiagnostic(3), + GetRS1015ExpectedDiagnostic(3), + GetRS1028ResultAt(3)); + } + + [Fact] + public async Task RS1007_RS1015_VisualBasic_VerifyDiagnostic_NamedArgumentCasesAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Private Shared ReadOnly descriptor As DiagnosticDescriptor = {|#0:new {|#1:DiagnosticDescriptor|}(""MyDiagnosticId"", title:=""MyDiagnosticTitle"", {|#2:helpLinkUri:=Nothing|}, messageFormat:=""MyDiagnosticMessage"", category:=""MyDiagnosticCategory"", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:= true)|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#3:new {|#4:DiagnosticDescriptor|}(""MyDiagnosticId"", title:=""MyDiagnosticTitle"", messageFormat:=""MyDiagnosticMessage"", category:=""MyDiagnosticCategory"", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:= true)|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + GetRS1028ResultAt(1), + GetRS1015ExpectedDiagnostic(2), + GetRS1007ExpectedDiagnostic(3), + GetRS1015ExpectedDiagnostic(4), + GetRS1028ResultAt(4)); + } + + [Fact] + public async Task RS1007_RS1015_CSharp_NoDiagnosticCasesAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableString dummyLocalizableTitle = new LocalizableResourceString(""dummyName"", null, null); + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, true, ""MyDiagnosticDescription."", ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(helpLinkUri: ""HelpLink"", id: ""MyDiagnosticId"", messageFormat:""MyDiagnosticMessage"", title: dummyLocalizableTitle, category: ""MyDiagnosticCategory"", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true)|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +", + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2)); + } + + [Fact] + public async Task RS1007_RS1015_VisualBasic_NoDiagnosticCasesAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = new LocalizableResourceString(""dummyName"", Nothing, Nothing) + Private Shared ReadOnly descriptor As DiagnosticDescriptor = new {|#0:DiagnosticDescriptor|}(""MyDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = new {|#1:DiagnosticDescriptor|}(""MyDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""MyDiagnosticDescription."", ""HelpLink"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = new {|#2:DiagnosticDescriptor|}(helpLinkUri:=""HelpLink"", id:=""MyDiagnosticId"", title:=dummyLocalizableTitle, messageFormat:=""MyDiagnosticMessage"", category:=""MyDiagnosticCategory"", defaultSeverity:=DiagnosticSeverity.Warning, isEnabledByDefault:=true) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2)); + } + + #endregion + + #region RS1017 (DiagnosticIdMustBeAConstantRuleId) and RS1019 (UseUniqueDiagnosticIdRuleId) + + [Fact] + public async Task RS1017_RS1019_CSharp_VerifyDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly string NonConstantDiagnosticId = ""NonConstantDiagnosticId""; + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor({|#1:NonConstantDiagnosticId|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer2 : DiagnosticAnalyzer +{ + private static LocalizableString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#3:new DiagnosticDescriptor({|#4:""DuplicateDiagnosticId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1028ResultAt(0), + GetRS1017ExpectedDiagnostic(1, "descriptor"), + GetRS1028ResultAt(2), + GetRS1028ResultAt(3), + GetRS1019ExpectedDiagnostic(4, "DuplicateDiagnosticId", "MyAnalyzer")); + } + + [Fact] + public async Task RS1017_RS1019_CSharp_VerifyDiagnostic_CreateHelperAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly string NonConstantDiagnosticId = ""NonConstantDiagnosticId""; + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + DiagnosticDescriptorHelper.Create({|#0:NonConstantDiagnosticId|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory""); + + private static readonly DiagnosticDescriptor descriptor2 = + DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory""); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer2 : DiagnosticAnalyzer +{ + private static LocalizableString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + DiagnosticDescriptorHelper.Create({|#1:""DuplicateDiagnosticId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory""); + + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}" + CSharpDiagnosticDescriptorCreationHelper, + GetRS1017ExpectedDiagnostic(0, "descriptor"), + GetRS1019ExpectedDiagnostic(1, "DuplicateDiagnosticId", "MyAnalyzer")); + } + + [Fact] + public async Task RS1017_RS1019_VisualBasic_VerifyDiagnosticAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Private Shared ReadOnly NonConstantDiagnosticId = ""NonConstantDiagnosticId"" + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = new {|#0:DiagnosticDescriptor|}({|#1:NonConstantDiagnosticId|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = new {|#2:DiagnosticDescriptor|}(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + + +Class MyAnalyzer2 + Inherits DiagnosticAnalyzer + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = new {|#3:DiagnosticDescriptor|}({|#4:""DuplicateDiagnosticId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1028ResultAt(0), + GetRS1017ExpectedDiagnostic(1, "descriptor"), + GetRS1028ResultAt(2), + GetRS1028ResultAt(3), + GetRS1019ExpectedDiagnostic(4, "DuplicateDiagnosticId", "MyAnalyzer")); + } + + [Fact] + public async Task RS1017_RS1019_VisualBasic_VerifyDiagnostic_CreateHelperAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Private Shared ReadOnly NonConstantDiagnosticId = ""NonConstantDiagnosticId"" + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#0:NonConstantDiagnosticId|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + + +Class MyAnalyzer2 + Inherits DiagnosticAnalyzer + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#1:""DuplicateDiagnosticId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +" + VisualBasicDiagnosticDescriptorCreationHelper, + GetRS1017ExpectedDiagnostic(0, "descriptor"), + GetRS1019ExpectedDiagnostic(1, "DuplicateDiagnosticId", "MyAnalyzer")); + } + + [Fact] + public async Task RS1017_RS1019_CSharp_NoDiagnosticCasesAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private const string ConstantDiagnosticId = ""ConstantDiagnosticId""; + private static LocalizableString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(ConstantDiagnosticId, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + // Allow multiple descriptors with same rule ID in the same analyzer. + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage2"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +", + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2)); + } + + [Fact] + public async Task RS1017_RS1019_CSharp_NoDiagnosticCases_CreateHelperAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private const string ConstantDiagnosticId = ""ConstantDiagnosticId""; + private static LocalizableString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + DiagnosticDescriptorHelper.Create(ConstantDiagnosticId, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory""); + + private static readonly DiagnosticDescriptor descriptor2 = + DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory""); + + // Allow multiple descriptors with same rule ID in the same analyzer. + private static readonly DiagnosticDescriptor descriptor3 = + DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage2"", ""MyDiagnosticCategory""); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +" + CSharpDiagnosticDescriptorCreationHelper); + } + + [Fact] + public async Task RS1017_RS1019_VisualBasic_NoDiagnosticCasesAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Const ConstantDiagnosticId As String = ""ConstantDiagnosticId"" + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = new {|#0:DiagnosticDescriptor|}(ConstantDiagnosticId, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = new {|#1:DiagnosticDescriptor|}(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + ' Allow multiple descriptors with same rule ID in the same analyzer. + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = new {|#2:DiagnosticDescriptor|}(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage2"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault:=true, helpLinkUri:=""HelpLink"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2)); + } + + [Fact] + public async Task RS1017_RS1019_VisualBasic_NoDiagnosticCases_CreateHelperAsync() + { + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Const ConstantDiagnosticId As String = ""ConstantDiagnosticId"" + Private Shared ReadOnly dummyLocalizableTitle As LocalizableString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(ConstantDiagnosticId, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"") + ' Allow multiple descriptors with same rule ID in the same analyzer. + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""DuplicateDiagnosticId"", dummyLocalizableTitle, ""MyDiagnosticMessage2"", ""MyDiagnosticCategory"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +" + VisualBasicDiagnosticDescriptorCreationHelper); + } + + #endregion + + #region RS1018 (DiagnosticIdMustBeInSpecifiedFormatRuleId) and RS1020 (UseCategoriesFromSpecifiedRangeRuleId) + + [Fact] + public async Task RS1018_RS1020_CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", {|#1:""NotAllowedCategory""|}, DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor({|#3:""DifferentPrefixId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#4:new DiagnosticDescriptor({|#5:""Prefix200""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor4 = + {|#6:new DiagnosticDescriptor({|#7:""Prefix101""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor5 = + {|#8:new DiagnosticDescriptor({|#9:""MySecondPrefix400""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor6 = + {|#10:new DiagnosticDescriptor({|#11:""MyThirdPrefix""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor3, descriptor4, descriptor5, descriptor6); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +"; + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1028ResultAt(0), + GetRS1020ExpectedDiagnostic(1, "NotAllowedCategory", AdditionalFileName), + GetRS1028ResultAt(2), + GetRS1018ExpectedDiagnostic(3, "DifferentPrefixId", "CategoryWithPrefix", "PrefixXXXX", AdditionalFileName), + GetRS1028ResultAt(4), + GetRS1018ExpectedDiagnostic(5, "Prefix200", "CategoryWithRange", "Prefix0-Prefix99", AdditionalFileName), + GetRS1028ResultAt(6), + GetRS1018ExpectedDiagnostic(7, "Prefix101", "CategoryWithId", "Prefix100-Prefix100", AdditionalFileName), + GetRS1028ResultAt(8), + GetRS1018ExpectedDiagnostic(9, "MySecondPrefix400", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + GetRS1028ResultAt(10), + GetRS1018ExpectedDiagnostic(11, "MyThirdPrefix", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName) + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_CSharp_VerifyDiagnostic_CreateHelperAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + DiagnosticDescriptorHelper.Create(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", {|#0:""NotAllowedCategory""|}); + + private static readonly DiagnosticDescriptor descriptor2 = + DiagnosticDescriptorHelper.Create({|#1:""DifferentPrefixId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix""); + + private static readonly DiagnosticDescriptor descriptor3 = + DiagnosticDescriptorHelper.Create({|#2:""Prefix200""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange""); + + private static readonly DiagnosticDescriptor descriptor4 = + DiagnosticDescriptorHelper.Create({|#3:""Prefix101""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId""); + + private static readonly DiagnosticDescriptor descriptor5 = + DiagnosticDescriptorHelper.Create({|#4:""MySecondPrefix400""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId""); + + private static readonly DiagnosticDescriptor descriptor6 = + DiagnosticDescriptorHelper.Create({|#5:""MyThirdPrefix""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId""); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor3, descriptor4, descriptor5, descriptor6); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}" + CSharpDiagnosticDescriptorCreationHelper; + + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1020ExpectedDiagnostic(0, "NotAllowedCategory", AdditionalFileName), + GetRS1018ExpectedDiagnostic(1, "DifferentPrefixId", "CategoryWithPrefix", "PrefixXXXX", AdditionalFileName), + GetRS1018ExpectedDiagnostic(2, "Prefix200", "CategoryWithRange", "Prefix0-Prefix99", AdditionalFileName), + GetRS1018ExpectedDiagnostic(3, "Prefix101", "CategoryWithId", "Prefix100-Prefix100", AdditionalFileName), + GetRS1018ExpectedDiagnostic(4, "MySecondPrefix400", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + GetRS1018ExpectedDiagnostic(5, "MyThirdPrefix", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName) + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = New {|#0:DiagnosticDescriptor|}(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", {|#1:""NotAllowedCategory""|}, DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New {|#2:DiagnosticDescriptor|}({|#3:""DifferentPrefixId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = New {|#4:DiagnosticDescriptor|}({|#5:""Prefix200""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = New {|#6:DiagnosticDescriptor|}({|#7:""Prefix101""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = New {|#8:DiagnosticDescriptor|}({|#9:""MySecondPrefix400""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = New {|#10:DiagnosticDescriptor|}({|#11:""MyThirdPrefix""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor3, descriptor4, descriptor5, descriptor6) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class +"; + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1028ResultAt(0), + GetRS1020ExpectedDiagnostic(1, "NotAllowedCategory", AdditionalFileName), + GetRS1028ResultAt(2), + GetRS1018ExpectedDiagnostic(3, "DifferentPrefixId", "CategoryWithPrefix", "PrefixXXXX", AdditionalFileName), + GetRS1028ResultAt(4), + GetRS1018ExpectedDiagnostic(5, "Prefix200", "CategoryWithRange", "Prefix0-Prefix99", AdditionalFileName), + GetRS1028ResultAt(6), + GetRS1018ExpectedDiagnostic(7, "Prefix101", "CategoryWithId", "Prefix100-Prefix100", AdditionalFileName), + GetRS1028ResultAt(8), + GetRS1018ExpectedDiagnostic(9, "MySecondPrefix400", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + GetRS1028ResultAt(10), + GetRS1018ExpectedDiagnostic(11, "MyThirdPrefix", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_VisualBasic_VerifyDiagnostic_CreateHelperAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", {|#0:""NotAllowedCategory""|}) + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#1:""DifferentPrefixId""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#2:""Prefix200""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#3:""Prefix101""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#4:""MySecondPrefix400""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create({|#5:""MyThirdPrefix""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor3, descriptor4, descriptor5, descriptor6) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class +" + VisualBasicDiagnosticDescriptorCreationHelper; + + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1020ExpectedDiagnostic(0, "NotAllowedCategory", AdditionalFileName), + GetRS1018ExpectedDiagnostic(1, "DifferentPrefixId", "CategoryWithPrefix", "PrefixXXXX", AdditionalFileName), + GetRS1018ExpectedDiagnostic(2, "Prefix200", "CategoryWithRange", "Prefix0-Prefix99", AdditionalFileName), + GetRS1018ExpectedDiagnostic(3, "Prefix101", "CategoryWithId", "Prefix100-Prefix100", AdditionalFileName), + GetRS1018ExpectedDiagnostic(4, "MySecondPrefix400", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + GetRS1018ExpectedDiagnostic(5, "MyThirdPrefix", "CategoryWithPrefixRangeAndId", "MyFirstPrefixXXXX, MySecondPrefix0-MySecondPrefix99, MySecondPrefix300-MySecondPrefix300", AdditionalFileName), + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithNoIdRangeOrFormat"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2_2 = + {|#2:new DiagnosticDescriptor(""Prefix101"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#3:new DiagnosticDescriptor(""Prefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor4 = + {|#4:new DiagnosticDescriptor(""Prefix100"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor5 = + {|#5:new DiagnosticDescriptor(""MyFirstPrefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor6 = + {|#6:new DiagnosticDescriptor(""MySecondPrefix050"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor7 = + {|#7:new DiagnosticDescriptor(""MySecondPrefix300"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor2_2, descriptor3, descriptor4, descriptor5, descriptor6, descriptor7); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +"; + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2), + GetRS1028ResultAt(3), + GetRS1028ResultAt(4), + GetRS1028ResultAt(5), + GetRS1028ResultAt(6), + GetRS1028ResultAt(7), + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_CSharp_NoDiagnosticCases_CreateHelperAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + DiagnosticDescriptorHelper.Create(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithNoIdRangeOrFormat""); + + private static readonly DiagnosticDescriptor descriptor2 = + DiagnosticDescriptorHelper.Create(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix""); + + private static readonly DiagnosticDescriptor descriptor2_2 = + DiagnosticDescriptorHelper.Create(""Prefix101"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix""); + + private static readonly DiagnosticDescriptor descriptor3 = + DiagnosticDescriptorHelper.Create(""Prefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange""); + + private static readonly DiagnosticDescriptor descriptor4 = + DiagnosticDescriptorHelper.Create(""Prefix100"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId""); + + private static readonly DiagnosticDescriptor descriptor5 = + DiagnosticDescriptorHelper.Create(""MyFirstPrefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId""); + + private static readonly DiagnosticDescriptor descriptor6 = + DiagnosticDescriptorHelper.Create(""MySecondPrefix050"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId""); + + private static readonly DiagnosticDescriptor descriptor7 = + DiagnosticDescriptorHelper.Create(""MySecondPrefix300"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId""); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor2_2, descriptor3, descriptor4, descriptor5, descriptor6, descriptor7); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}" + CSharpDiagnosticDescriptorCreationHelper; + + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = New {|#0:DiagnosticDescriptor|}(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithNoIdRangeOrFormat"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New {|#1:DiagnosticDescriptor|}(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor2_2 As DiagnosticDescriptor = New {|#2:DiagnosticDescriptor|}(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = New {|#3:DiagnosticDescriptor|}(""Prefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = New {|#4:DiagnosticDescriptor|}(""Prefix100"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = New {|#5:DiagnosticDescriptor|}(""MyFirstPrefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = New {|#6:DiagnosticDescriptor|}(""MySecondPrefix050"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + Private Shared ReadOnly descriptor7 As DiagnosticDescriptor = New {|#7:DiagnosticDescriptor|}(""MySecondPrefix300"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault:=True, helpLinkUri:=""HelpLink"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor2_2, descriptor3, descriptor4, descriptor5, descriptor6, descriptor7) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class +"; + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2), + GetRS1028ResultAt(3), + GetRS1028ResultAt(4), + GetRS1028ResultAt(5), + GetRS1028ResultAt(6), + GetRS1028ResultAt(7), + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + [Fact] + public async Task RS1018_RS1020_VisualBasic_NoDiagnosticCases_CreateHelperAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithNoIdRangeOrFormat"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"") + Private Shared ReadOnly descriptor2_2 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Prefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Prefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""Prefix100"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""MyFirstPrefix001"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""MySecondPrefix050"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"") + Private Shared ReadOnly descriptor7 As DiagnosticDescriptor = DiagnosticDescriptorHelper.Create(""MySecondPrefix300"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor, descriptor2, descriptor2_2, descriptor3, descriptor4, descriptor5, descriptor6, descriptor7) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class +" + VisualBasicDiagnosticDescriptorCreationHelper; + + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +CategoryWithNoIdRangeOrFormat +CategoryWithPrefix: Prefix +CategoryWithRange: Prefix000-Prefix099 +CategoryWithId: Prefix100 +CategoryWithPrefixRangeAndId: MyFirstPrefix, MySecondPrefix000-MySecondPrefix099, MySecondPrefix300 +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + #endregion + + #region RS1021 (AnalyzerCategoryAndIdRangeFileInvalidRuleId) + + [Fact] + public async Task RS1021_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(""Id1"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""DifferentPrefixId"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefix"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(""Prefix200"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithRange"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor4 = + {|#3:new DiagnosticDescriptor(""Prefix101"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor5 = + {|#4:new DiagnosticDescriptor(""MySecondPrefix400"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + private static readonly DiagnosticDescriptor descriptor6 = + {|#5:new DiagnosticDescriptor(""MyThirdPrefix"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""CategoryWithPrefixRangeAndId"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor, descriptor2, descriptor3, descriptor4, descriptor5, descriptor6); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} +"; + string additionalText = @" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +# Illegal: spaces in category name +{|#6:Category with spaces|} +{|#7:Category with spaces and range: Prefix100-Prefix199|} + +# Illegal: Multiple colons +{|#8:CategoryMultipleColons: IdWithColon:100|} + +# Illegal: Duplicate category +DuplicateCategory1 +{|#9:DuplicateCategory1|} +DuplicateCategory2: Prefix100-Prefix199 +{|#10:DuplicateCategory2: Prefix200-Prefix299|} + +# Illegal: ID cannot be non-alphanumeric +{|#11:CategoryWithBadId1: Prefix_100|} +{|#12:CategoryWithBadId2: Prefix_100-Prefix_199|} + +# Illegal: Id cannot have letters after number +{|#13:CategoryWithBadId3: Prefix000NotAllowed|} +{|#14:CategoryWithBadId4: Prefix000NotAllowed-Prefix099NotAllowed|} + +# Illegal: Different prefixes in ID range +{|#15:CategoryWithBadId5: Prefix000-DifferentPrefix099|} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (AdditionalFileName, additionalText) }, + ExpectedDiagnostics = + { + GetRS1021ExpectedDiagnostic(6, "Category with spaces", AdditionalFileName), + GetRS1021ExpectedDiagnostic(7, "Category with spaces and range: Prefix100-Prefix199", AdditionalFileName), + GetRS1021ExpectedDiagnostic(8, "CategoryMultipleColons: IdWithColon:100", AdditionalFileName), + GetRS1021ExpectedDiagnostic(9, "DuplicateCategory1", AdditionalFileName), + GetRS1021ExpectedDiagnostic(10, "DuplicateCategory2: Prefix200-Prefix299", AdditionalFileName), + GetRS1021ExpectedDiagnostic(11, "CategoryWithBadId1: Prefix_100", AdditionalFileName), + GetRS1021ExpectedDiagnostic(12, "CategoryWithBadId2: Prefix_100-Prefix_199", AdditionalFileName), + GetRS1021ExpectedDiagnostic(13, "CategoryWithBadId3: Prefix000NotAllowed", AdditionalFileName), + GetRS1021ExpectedDiagnostic(14, "CategoryWithBadId4: Prefix000NotAllowed-Prefix099NotAllowed", AdditionalFileName), + GetRS1021ExpectedDiagnostic(15, "CategoryWithBadId5: Prefix000-DifferentPrefix099", AdditionalFileName), + GetRS1028ResultAt(0), + GetRS1028ResultAt(1), + GetRS1028ResultAt(2), + GetRS1028ResultAt(3), + GetRS1028ResultAt(4), + GetRS1028ResultAt(5), + } + }, + SolutionTransforms = { WithoutEnableReleaseTrackingWarning } + }.RunAsync(); + } + + #endregion + + #region RS1028 (ProvideCustomTagsInDescriptorRuleId) + [Fact] + public async Task ReportOnMissingCustomTagsAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using Microsoft.CodeAnalysis; +public class MyAnalyzer +{ + internal static DiagnosticDescriptor Rule1 = {|#0:new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false)|}; + internal static DiagnosticDescriptor Rule2 = {|#1:new DiagnosticDescriptor("""", new LocalizableResourceString("""", null, null), + new LocalizableResourceString("""", null, null), """", DiagnosticSeverity.Warning, false)|}; + public void SomeMethod() + { + var diag = new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false); + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(0), + GetRS1028ResultAt(0), + GetRS1015ExpectedDiagnostic(1), + GetRS1028ResultAt(1)); + + await VerifyBasicAnalyzerAsync(@" +Imports Microsoft.CodeAnalysis +Public Class MyAnalyzer + Friend Shared Rule1 As DiagnosticDescriptor = {|#0:New {|#1:DiagnosticDescriptor|}("""", """", """", """", DiagnosticSeverity.Warning, False)|} + Friend Shared Rule2 As DiagnosticDescriptor = New {|#2:DiagnosticDescriptor|}("""", New LocalizableResourceString("""", Nothing, Nothing), New LocalizableResourceString("""", Nothing, Nothing), """", DiagnosticSeverity.Warning, False) + Public Sub SomeMethod() + Dim diag = New DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, False) + End Sub +End Class", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(1), + GetRS1028ResultAt(1), + GetRS1015ExpectedDiagnostic(2), + GetRS1028ResultAt(2)); + } + + [Fact] + public async Task DoNotReportOnNamedCustomTagsAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using Microsoft.CodeAnalysis; +public class MyAnalyzer +{ + internal static DiagnosticDescriptor Rule1 = {|#0:new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false, customTags: """")|}; + internal static DiagnosticDescriptor Rule2 = {|#1:new DiagnosticDescriptor("""", new LocalizableResourceString("""", null, null), + new LocalizableResourceString("""", null, null), """", DiagnosticSeverity.Warning, false, customTags: """")|}; + public void SomeMethod() + { + var diag = new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false, customTags: """"); + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(1)); + + // Named arguments are incompatible with ParamArray in VB.NET + } + + [Fact] + public async Task DoNotReportOnCustomTagsAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using Microsoft.CodeAnalysis; +public class MyAnalyzer +{ + internal static DiagnosticDescriptor Rule1 = {|#0:new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false, null, {|#1:null|}, """")|}; + internal static DiagnosticDescriptor Rule2 = new DiagnosticDescriptor("""", new LocalizableResourceString("""", null, null), + new LocalizableResourceString("""", null, null), """", DiagnosticSeverity.Warning, false, new LocalizableResourceString("""", null, null), """", """"); + internal static DiagnosticDescriptor Rule3 = {|#2:new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false, null, {|#3:null|}, new[] { """", """" })|}; + internal static DiagnosticDescriptor Rule4 = new DiagnosticDescriptor("""", new LocalizableResourceString("""", null, null), + new LocalizableResourceString("""", null, null), """", DiagnosticSeverity.Warning, false, new LocalizableResourceString("""", null, null), """", new[] { """", """" }); + public void SomeMethod() + { + var diag = new DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, false, null, null, """"); + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2), + GetRS1015ExpectedDiagnostic(3)); + + await VerifyBasicAnalyzerAsync(@" +Imports Microsoft.CodeAnalysis +Public Class MyAnalyzer + Friend Shared Rule1 As DiagnosticDescriptor = {|#0:New DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, False, Nothing, {|#1:Nothing|}, """")|} + Friend Shared Rule2 As DiagnosticDescriptor = New DiagnosticDescriptor("""", New LocalizableResourceString("""", Nothing, Nothing), New LocalizableResourceString("""", Nothing, Nothing), """", DiagnosticSeverity.Warning, False, New LocalizableResourceString("""", Nothing, Nothing), """", """") + Friend Shared Rule3 As DiagnosticDescriptor = {|#2:New DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, False, Nothing, {|#3:Nothing|}, { """", """" })|} + Friend Shared Rule4 As DiagnosticDescriptor = New DiagnosticDescriptor("""", New LocalizableResourceString("""", Nothing, Nothing), New LocalizableResourceString("""", Nothing, Nothing), """", DiagnosticSeverity.Warning, False, New LocalizableResourceString("""", Nothing, Nothing), """", { """", """" }) + Public Sub SomeMethod() + Dim diag = New DiagnosticDescriptor("""", """", """", """", DiagnosticSeverity.Warning, False, Nothing, Nothing, """") + End Sub +End Class", + GetRS1007ExpectedDiagnostic(0), + GetRS1015ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2), + GetRS1015ExpectedDiagnostic(3)); + } + #endregion + + #region RS1029 (DoNotUseReservedDiagnosticIdRuleId) + + [Fact, WorkItem(1727, "https://github.com/dotnet/roslyn-analyzers/issues/1727")] + public async Task RS1029_AlreadyUsedId_DiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|#0:""CA0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor({|#1:""CS0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor3 = + new DiagnosticDescriptor({|#2:""BC0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor4 = + new DiagnosticDescriptor({|#3:""CA00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor5 = + new DiagnosticDescriptor({|#4:""CS00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor6 = + new DiagnosticDescriptor({|#5:""BC00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1029ResultAt(0, "CA0"), + GetRS1029ResultAt(1, "CS0"), + GetRS1029ResultAt(2, "BC0"), + GetRS1029ResultAt(3, "CA00000000000000000000"), + GetRS1029ResultAt(4, "CS00000000000000000000"), + GetRS1029ResultAt(5, "BC00000000000000000000")); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor({|#0:""CA0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New DiagnosticDescriptor({|#1:""CS0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = New DiagnosticDescriptor({|#2:""BC0""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = New DiagnosticDescriptor({|#3:""CA00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = New DiagnosticDescriptor({|#4:""CS00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = New DiagnosticDescriptor({|#5:""BC00000000000000000000""|}, dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class", + GetRS1029ResultAt(0, "CA0"), + GetRS1029ResultAt(1, "CS0"), + GetRS1029ResultAt(2, "BC0"), + GetRS1029ResultAt(3, "CA00000000000000000000"), + GetRS1029ResultAt(4, "CS00000000000000000000"), + GetRS1029ResultAt(5, "BC00000000000000000000")); + } + + [Fact, WorkItem(1727, "https://github.com/dotnet/roslyn-analyzers/issues/1727")] + public async Task RS1029_DiagnosticIdSimilarButNotReserved_NoDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""CAA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor(""CSA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor3 = + new DiagnosticDescriptor(""BCA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor4 = + new DiagnosticDescriptor(""CA00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor5 = + new DiagnosticDescriptor(""CS00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor6 = + new DiagnosticDescriptor(""BC00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor(""CAA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New DiagnosticDescriptor(""CSA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = New DiagnosticDescriptor(""BCA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = New DiagnosticDescriptor(""CA00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor5 As DiagnosticDescriptor = New DiagnosticDescriptor(""CS00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor6 As DiagnosticDescriptor = New DiagnosticDescriptor(""BC00A0"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class"); + } + + [Fact, WorkItem(1727, "https://github.com/dotnet/roslyn-analyzers/issues/1727")] + public async Task RS1029_DiagnosticIdSimilarButTooShort_NoDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""CA"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor(""CS"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + private static readonly DiagnosticDescriptor descriptor3 = + new DiagnosticDescriptor(""BC"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor(""CA"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New DiagnosticDescriptor(""CS"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = New DiagnosticDescriptor(""BC"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class"); + } + + [Theory, WorkItem(1727, "https://github.com/dotnet/roslyn-analyzers/issues/1727")] + [InlineData("Microsoft.CodeAnalysis.VersionCheckAnalyzer")] + [InlineData("Microsoft.CodeAnalysis.NetAnalyzers")] + [InlineData("Microsoft.CodeAnalysis.CSharp.NetAnalyzers")] + [InlineData("Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers")] + [InlineData("Microsoft.CodeQuality.Analyzers")] + [InlineData("Microsoft.CodeQuality.CSharp.Analyzers")] + [InlineData("Microsoft.CodeQuality.VisualBasic.Analyzers")] + [InlineData("Microsoft.NetCore.Analyzers")] + [InlineData("Microsoft.NetCore.CSharp.Analyzers")] + [InlineData("Microsoft.NetCore.VisualBasic.Analyzers")] + [InlineData("Microsoft.NetFramework.Analyzers")] + [InlineData("Microsoft.NetFramework.CSharp.Analyzers")] + [InlineData("Microsoft.NetFramework.VisualBasic.Analyzers")] + [InlineData("Text.Analyzers")] + [InlineData("Text.CSharp.Analyzers")] + [InlineData("Text.VisualBasic.Analyzers")] + public async Task RS1029_CADiagnosticIdOnRoslynAnalyzers_NoDiagnosticAsync(string assemblyName) + { + await new VerifyCS.Test + { + TestCode = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static LocalizableResourceString dummyLocalizableTitle = null; + + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""CA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """"); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + SolutionTransforms = + { + (solution, projectId) => solution.GetProject(projectId)!.WithAssemblyName(assemblyName).Solution, + WithoutEnableReleaseTrackingWarning, + }, + }.RunAsync(); + + await new VerifyVB.Test + { + TestCode = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared dummyLocalizableTitle As LocalizableResourceString = Nothing + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor(""CA0000"", dummyLocalizableTitle, ""MyDiagnosticMessage"", ""NotAllowedCategory"", DiagnosticSeverity.Warning, True, Nothing, ""HelpLink"", ""customTag"") + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + End Sub +End Class", + SolutionTransforms = + { + (solution, projectId) => solution.GetProject(projectId)!.WithAssemblyName(assemblyName).Solution, + WithoutEnableReleaseTrackingWarning, + } + }.RunAsync(); + } + + #endregion + + #region RS1031 (DefineDiagnosticTitleCorrectlyRule) + + [WindowsOnlyFact, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + public async Task RS1031_TitleStringEndsWithPeriod_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnosticTitle.""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = {|#4:""MyDiagnosticTitle.""|}; + private static readonly DiagnosticDescriptor descriptor3 = + {|#5:new DiagnosticDescriptor(""MyDiagnosticId"", {|#6:s_title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static string s_title = {|#7:""MyDiagnosticTitle.""|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = ""MyDiagnosticTitle""; + private static readonly DiagnosticDescriptor descriptor3 = + {|#5:new DiagnosticDescriptor(""MyDiagnosticId"", s_title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static string s_title = ""MyDiagnosticTitle""; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4), + GetRS1007ExpectedDiagnostic(5), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(6).WithLocation(7)); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnosticTitle.""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = {|#4:""MyDiagnosticTitle.""|} + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#5:new DiagnosticDescriptor(""MyDiagnosticId"", {|#6:s_title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly s_title As String = {|#7:""MyDiagnosticTitle.""|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = ""MyDiagnosticTitle"" + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#5:new DiagnosticDescriptor(""MyDiagnosticId"", s_title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly s_title As String = ""MyDiagnosticTitle"" + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4), + GetRS1007ExpectedDiagnostic(5), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(6).WithLocation(7)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + public async Task RS1031_TitleStringEndsWithPeriod_ResxFile_DiagnosticAsync( + string csharpLocalizableTitleExpression, string basicLocalizableTitleExpression, + string csharpLocalizableFieldDeclaration = "", string basicLocalizableFieldDeclaration = "") + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerTitle => string.Empty; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpLocalizableTitleExpression}|}}", csharpLocalizableFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpLocalizableTitleExpression, csharpLocalizableFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, "My Analyzer Title."), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, "My Analyzer Title"), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared Readonly Property AnalyzerTitle As String = """" +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Readonly Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Custom tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicLocalizableTitleExpression}|}}", basicLocalizableFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicLocalizableTitleExpression, basicLocalizableFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, "My Analyzer Title."), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, "My Analyzer Title"), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + } + + [WindowsOnlyFact, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + public async Task RS1031_TitleIsMultiSentence_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnostic. Title.""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = {|#4:""MyDiagnostic. Title""|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnostic"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = ""MyDiagnostic""; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4)); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnostic. Title.""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = {|#4:""MyDiagnostic. Title""|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnostic"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = ""MyDiagnostic"" + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + public async Task RS1031_TitleIsMultiSentence_ResxFile_DiagnosticAsync( + string csharpLocalizableTitleExpression, string basicLocalizableTitleExpression, + string csharpLocalizableFieldDeclaration = "", string basicLocalizableFieldDeclaration = "") + { + await VerifyTitleAsync("MyDiagnostic. Title.", "MyDiagnostic"); + await VerifyTitleAsync("MyDiagnostic. Title", "MyDiagnostic"); + return; + + // Local functions + + async Task VerifyTitleAsync(string title, string fixedTitle) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerTitle => string.Empty; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpLocalizableTitleExpression}|}}", csharpLocalizableFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpLocalizableTitleExpression, csharpLocalizableFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerTitle As String = Nothing +End Class + + +Public Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicLocalizableTitleExpression}|}}", basicLocalizableFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicLocalizableTitleExpression, basicLocalizableFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + } + } + + [WindowsOnlyFact, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + public async Task RS1031_TitleIsMultiSentence_MultipleDescriptorsUsingSameTitle_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId1"", {|#1:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId2"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = {|#4:""MyDiagnosticTitle. AnalyzerTitle.""|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId1"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId2"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Title = ""MyDiagnosticTitle""; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(4), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4) + ); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId1"", {|#1:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId2"", {|#3:Title|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = {|#4:""MyDiagnosticTitle. AnalyzerTitle.""|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId1"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId2"", Title, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Title As String = ""MyDiagnosticTitle"" + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(4), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(4)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + public async Task RS1031_TitleIsMultiSentence_MultipleDescriptorsUsingSameTitle_ResxFile_DiagnosticAsync(string csharpTitleExpression, string basicTitleExpression) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerTitle => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule1 = new DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + private static readonly DiagnosticDescriptor Rule2 = new DiagnosticDescriptor( + ""RuleId"", {1}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + private static readonly LocalizableString Title = {2}; + + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule1, Rule2); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, "{|#0:Title|}", "{|#1:Title|}", csharpTitleExpression), + fixedSource: string.Format(csharpSourceFormat, "Title", "Title", csharpTitleExpression), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, "MyDiagnostic. Title."), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, "MyDiagnostic"), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerTitle As String = Nothing +End Class + + +Public Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared ReadOnly Rule1 As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags"") + Private Shared ReadOnly Rule2 As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {1}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags"") + Private Shared ReadOnly Title As LocalizableString = {2} + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule1, Rule2) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, "{|#0:Title|}", "{|#1:Title|}", basicTitleExpression), + fixedSource: string.Format(basicSourceFormat, "Title", "Title", basicTitleExpression), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, "MyDiagnostic. Title."), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, "MyDiagnostic"), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1) + }); + } + + [WindowsOnlyFact, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + public async Task RS1031_TitleStringContainsLineReturn_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnostic\rTitle""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:""MyDiagnostic\nTitle""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", {|#5:""MyDiagnostic\r\nTitle""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnostic"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnostic"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnostic"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(3), + GetRS1007ExpectedDiagnostic(4), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(5).WithLocation(5)); + + // NOTE: Code fix does not handle binary operations. + var vbCode = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", {|#1:""MyDiagnostic"" & vbCr & ""Title""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", {|#3:""MyDiagnostic"" & vbLf & ""Title""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", {|#5:""MyDiagnostic"" & vbCrLf & ""Title""|}, ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + await VerifyBasicCodeFixAsync(vbCode, vbCode, + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(3).WithLocation(3), + GetRS1007ExpectedDiagnostic(4), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(5).WithLocation(5)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + [InlineData( + "s_localizableTitle", + "s_localizableTitle", + "private static readonly LocalizableString s_localizableTitle = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle));", + "Private Shared ReadOnly s_localizableTitle As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + public async Task RS1031_TitleStringContainsLineReturn_ResxFile_DiagnosticAsync( + string csharpTitleExpression, string basicTitleExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyTitleAsync( + @"MyDiagnostic +Title.", + @"MyDiagnostic"); + await VerifyTitleAsync( + @"MyDiagnostic +Title", + @"MyDiagnostic"); + + return; + + // Local functions + + async Task VerifyTitleAsync(string title, string fixedTitle) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerTitle => string.Empty; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpTitleExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpTitleExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared Readonly Property AnalyzerTitle As String = """" +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Readonly Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Custom tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicTitleExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicTitleExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + } + } + + [WindowsOnlyFact, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + public async Task RS1031_ValidTitleString_NoDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""Title can contain A.B qualifications"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""Title can contain 'A.B' qualifications"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""Title can contain A.B qualifications"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""Title can contain 'A.B' qualifications"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [WindowsOnlyTheory, WorkItem(3958, "https://github.com/dotnet/roslyn-analyzers/issues/3958")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerTitle), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerTitle), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerTitle))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerTitle))")] + public async Task RS1031_LeadingOrTailingWhitespace_DiagnosticAsync(string csharpTitleExpression, string basicTitleExpression) + { + await VerifyTitleAsync("Title with trailing space ", "Title with trailing space"); + await VerifyTitleAsync(" Title with leading space", "Title with leading space"); + await VerifyTitleAsync("\t Title with leading and trailing spaces/tabs \t ", "Title with leading and trailing spaces/tabs"); + await VerifyTitleAsync("Title with trailing space. ", "Title with trailing space"); + return; + + // Local functions + + async Task VerifyTitleAsync(string title, string fixedTitle) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerTitle => string.Empty; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpTitleExpression}|}}"), + fixedSource: string.Format(csharpSourceFormat, csharpTitleExpression), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerTitle As String = Nothing +End Class + + +Public Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", {0}, ""Message"", ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Tags"") + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicTitleExpression}|}}"), + fixedSource: string.Format(basicSourceFormat, basicTitleExpression), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, title), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedTitle), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticTitleCorrectlyRule).WithLocation(0) + }); + } + } + + #endregion // RS1031 (DefineDiagnosticTitleCorrectlyRule) + + #region RS1032 (DefineDiagnosticMessageCorrectlyRule) + + [WindowsOnlyFact, WorkItem(3576, "https://github.com/dotnet/roslyn-analyzers/issues/3576")] + public async Task RS1032_MessageStringEndsWithPeriodAndIsNotMultiSentence_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""MyDiagnosticMessage.""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#3:Message|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Message = {|#4:""MyDiagnostic.Message.""|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", Message, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Message = ""MyDiagnostic.Message""; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(3).WithLocation(4)); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""MyDiagnosticMessage.""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#3:Message|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Message As String = {|#4:""MyDiagnostic.Message.""|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", Message, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Const Message As String = ""MyDiagnostic.Message"" + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(3).WithLocation(4)); + } + + [WindowsOnlyTheory, WorkItem(3576, "https://github.com/dotnet/roslyn-analyzers/issues/3576")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + public async Task RS1032_MessageStringEndsWithPeriodAndIsNotMultiSentence_ResxFile_DiagnosticAsync( + string csharpMessageExpression, string basicMessageExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyMessageAsync("My diagnostic message.", "My diagnostic message"); + await VerifyMessageAsync("MyDiagnostic.Message.", "MyDiagnostic.Message"); + return; + + // Local functions + + async Task VerifyMessageAsync(string message, string fixedMessage) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerMessage => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpMessageExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpMessageExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerMessage As String = Nothing +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicMessageExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicMessageExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + } + } + + [WindowsOnlyFact, WorkItem(3576, "https://github.com/dotnet/roslyn-analyzers/issues/3576")] + public async Task RS1032_MessageStringIsMultiSentenceAndDoesNotEndWithPeriod_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""Message is. Multi-sentence""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message is. Multi-sentence."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1)); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""Message is. Multi-sentence""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message is. Multi-sentence."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1)); + } + + [WindowsOnlyFact, WorkItem(3576, "https://github.com/dotnet/roslyn-analyzers/issues/3576")] + public async Task RS1032_MessageStringContainsLineReturn_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""MyDiagnostic\rMessage""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#3:""MyDiagnostic\nMessage""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#5:""MyDiagnostic\r\nMessage""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnostic. Message."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnostic. Message."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnostic. Message."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(3).WithLocation(3), + GetRS1007ExpectedDiagnostic(4), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(5).WithLocation(5)); + + // NOTE: Code fix does not handle binary operations. + var vbCode = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#1:""MyDiagnostic"" & vbCr & ""Message""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#3:""MyDiagnostic"" & vbLf & ""Message""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#4:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", {|#5:""MyDiagnostic"" & vbCrLf & ""Message""|}, ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + await VerifyBasicCodeFixAsync(vbCode, vbCode, + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(3).WithLocation(3), + GetRS1007ExpectedDiagnostic(4), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(5).WithLocation(5)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + public async Task RS1032_MessageStringContainsLineReturn_ResxFile_DiagnostiAsync( + string csharpMessageExpression, string basicMessageExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyMessageAsync( + @"MyDiagnostic +Message1.", "MyDiagnostic. Message1."); + await VerifyMessageAsync( + @"MyDiagnostic. +Message2", "MyDiagnostic. Message2."); + return; + + // Local functions + + async Task VerifyMessageAsync(string message, string fixedMessage) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerMessage => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpMessageExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpMessageExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerMessage As String = Nothing +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicMessageExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicMessageExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + } + } + + [WindowsOnlyFact, WorkItem(3576, "https://github.com/dotnet/roslyn-analyzers/issues/3576")] + public async Task RS1032_ValidMessageString_NoDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message is a. Multi-sentence."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message can contain A.B qualifications"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor4 = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message can contain 'A.B' qualifications"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3, descriptor4); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2), + GetRS1007ExpectedDiagnostic(3)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message is a. Multi-sentence."", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message can contain A.B qualifications"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor4 As DiagnosticDescriptor = {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""Message can contain 'A.B' qualifications"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3, descriptor4) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2), + GetRS1007ExpectedDiagnostic(3)); + } + + [WindowsOnlyTheory, WorkItem(3958, "https://github.com/dotnet/roslyn-analyzers/issues/3958")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(Resources.AnalyzerMessage), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerMessage), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + [InlineData( + "s_localizableMessage", + "s_localizableMessage", + "private static readonly LocalizableString s_localizableMessage = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerMessage));", + "Private Shared ReadOnly s_localizableMessage As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerMessage))")] + public async Task RS1032_LeadingOrTrailingWhitespaces_DiagnosticAsync( + string csharpMessageExpression, string basicMessageExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyMessageAsync("Message with trailing whitespace ", "Message with trailing whitespace"); + await VerifyMessageAsync(" Message with leading whitespace", "Message with leading whitespace"); + await VerifyMessageAsync(" \t Message with leading and trailing spaces/tabs \t ", "Message with leading and trailing spaces/tabs"); + await VerifyMessageAsync("Message with period and trailing whitespace. ", "Message with period and trailing whitespace"); + return; + + // Local functions + + async Task VerifyMessageAsync(string message, string fixedMessage) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerMessage => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, true, ""Description."", ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpMessageExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpMessageExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerMessage As String = Nothing +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", ""Title"", {0}, ""Category"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicMessageExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicMessageExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, message), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedMessage), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticMessageCorrectlyRule).WithLocation(0) + }); + } + } + + #endregion // RS1032 (DefineDiagnosticMessageCorrectlyRule) + + #region RS1033 (DefineDiagnosticDescriptionCorrectlyRule) + + [WindowsOnlyFact, WorkItem(3577, "https://github.com/dotnet/roslyn-analyzers/issues/3577")] + public async Task RS1033_DescriptionStringDoesNotEndWithPunctuation_DiagnosticAsync() + { + await VerifyCSharpCodeFixAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, {|#1:description: {|#2:""MyDiagnosticDescription""|}|}, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, {|#4:description: Description|}, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Description = {|#5:""MyDiagnosticDescription""|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: ""MyDiagnosticDescription."", helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description, helpLinkUri: ""HelpLink"", customTags: """")|}; + private const string Description = ""MyDiagnosticDescription.""; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(1).WithLocation(2), + GetRS1007ExpectedDiagnostic(3), + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(4).WithLocation(5)); + + await VerifyBasicCodeFixAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, {|#1:""Description""|}, ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, {|#3:Description|}, ""HelpLinkUrl"", ""Tag"")|} + Private Const Description As String = {|#4:""Description""|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, Description, ""HelpLinkUrl"", ""Tag"")|} + Private Const Description As String = ""Description."" + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(1).WithLocation(1), + GetRS1007ExpectedDiagnostic(2), + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(3).WithLocation(4)); + } + + [WindowsOnlyTheory, WorkItem(3575, "https://github.com/dotnet/roslyn-analyzers/issues/3575")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerDescription), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerDescription), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableDescription", + "s_localizableDescription", + "private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableDescription As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerDescription), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerDescription))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerDescription))")] + [InlineData( + "s_localizableDescription", + "s_localizableDescription", + "private static readonly LocalizableString s_localizableDescription = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerDescription));", + "Private Shared ReadOnly s_localizableDescription As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerDescription))")] + public async Task RS1033_DescriptionStringDoesNotEndWithPunctuation_ResxFile_DiagnosticAsync( + string csharpDescriptionExpression, string basicDescriptionExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyDescriptionAsync("MyDiagnosticDescription", "MyDiagnosticDescription."); + await VerifyDescriptionAsync("MyDiagnostic. Description", "MyDiagnostic. Description."); + return; + + // Local functions + + async Task VerifyDescriptionAsync(string description, string fixedDescription) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerDescription => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", ""Title"", ""Message"", ""Category"", DiagnosticSeverity.Warning, true, {0}, ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpDescriptionExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpDescriptionExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, description), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedDescription), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerDescription As String = Nothing +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", ""Title"", ""Message"", ""Category"", DiagnosticSeverity.Warning, True, {0}, ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicDescriptionExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicDescriptionExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, description), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedDescription), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(0) + }); + } + } + + [WindowsOnlyFact, WorkItem(3577, "https://github.com/dotnet/roslyn-analyzers/issues/3577")] + public async Task RS1033_DescriptionEndsWithPunctuation_NoDiagnosticAsync() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, + description: ""MyDiagnosticDescription."", helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor2 = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, + description: ""MyDiagnosticDescription!"", helpLinkUri: ""HelpLink"", customTags: """")|}; + + private static readonly DiagnosticDescriptor descriptor3 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, + description: ""MyDiagnosticDescription?"", helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", + DiagnosticSeverity.Warning, True, ""Description."", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", + DiagnosticSeverity.Warning, True, ""Description!"", ""HelpLinkUrl"", ""Tag"")|} + + Private Shared ReadOnly descriptor3 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", + DiagnosticSeverity.Warning, True, ""Description?"", ""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2, descriptor3) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [WindowsOnlyTheory, WorkItem(3958, "https://github.com/dotnet/roslyn-analyzers/issues/3958")] + [InlineData( + "new LocalizableResourceString(nameof(Resources.AnalyzerDescription), null, typeof(Resources))", + "New LocalizableResourceString(NameOf(Resources.AnalyzerDescription), Nothing, GetType(Resources))")] + [InlineData( + "s_localizableDescription", + "s_localizableDescription", + "private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), null, typeof(Resources));", + "Private Shared ReadOnly s_localizableDescription As LocalizableString = New LocalizableResourceString(NameOf(Resources.AnalyzerDescription), Nothing, GetType(Resources))")] + [InlineData( + "Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerDescription))", + "Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerDescription))")] + [InlineData( + "s_localizableDescription", + "s_localizableDescription", + "private static readonly LocalizableString s_localizableDescription = Resources.CreateLocalizableResourceString(nameof(Resources.AnalyzerDescription));", + "Private Shared ReadOnly s_localizableDescription As LocalizableString = Resources.CreateLocalizableResourceString(NameOf(Resources.AnalyzerDescription))")] + public async Task RS1033_LeadingOrTrailingWhitespaces_DiagnosticAsync( + string csharpDescriptionExpression, string basicDescriptionExpression, + string csharpFieldDeclaration = "", string basicFieldDeclaration = "") + { + await VerifyDescriptionAsync("Description with trailing space ", "Description with trailing space."); + await VerifyDescriptionAsync(" Description with leading space", "Description with leading space."); + await VerifyDescriptionAsync(" \t Description with leading and trailing spaces/tabs \t ", "Description with leading and trailing spaces/tabs."); + await VerifyDescriptionAsync("Description with period and trailing space. ", "Description with period and trailing space."); + return; + + // Local functions + + async Task VerifyDescriptionAsync(string description, string fixedDescription) + { + string additionalFileName = "Resources.resx"; + string additionalFileTextFormat = @" + + + {0} + Optional comment. + +"; + + string csharpSourceFormat = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Resources +{{ + public static LocalizableResourceString CreateLocalizableResourceString(string resourceName) => default; + public static string AnalyzerDescription => default; +}} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + ""RuleId"", ""Title"", ""Message"", ""Category"", DiagnosticSeverity.Warning, true, {0}, ""HelpLinkUri"", ""Tags""); + {1} + public override ImmutableArray SupportedDiagnostics {{ get; }} = ImmutableArray.Create(Rule); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpCodeFixAsync( + source: string.Format(csharpSourceFormat, $"{{|#0:{csharpDescriptionExpression}|}}", csharpFieldDeclaration), + fixedSource: string.Format(csharpSourceFormat, csharpDescriptionExpression, csharpFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, description), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedDescription), + expected: new[] + { + VerifyCS.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(0) + }); + + string basicSourceFormat = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Resources + Public Shared Function CreateLocalizableResourceString(resourceName As String) As LocalizableResourceString + Return Nothing + End Function + Public Shared ReadOnly Property AnalyzerDescription As String = Nothing +End Class + + +Public NotInheritable Class MyAnalyzer : Inherits DiagnosticAnalyzer + Private Shared Rule As DiagnosticDescriptor = New DiagnosticDescriptor( + ""RuleId"", ""Title"", ""Message"", ""Category"", DiagnosticSeverity.Warning, True, {0}, ""HelpLinkUri"", ""Tags"") + {1} + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) = ImmutableArray.Create(Rule) + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class"; + await VerifyBasicCodeFixAsync( + source: string.Format(basicSourceFormat, $"{{|#0:{basicDescriptionExpression}|}}", basicFieldDeclaration), + fixedSource: string.Format(basicSourceFormat, basicDescriptionExpression, basicFieldDeclaration), + additionalFileName: additionalFileName, + additionalFileText: string.Format(additionalFileTextFormat, description), + fixedAdditionalFileText: string.Format(additionalFileTextFormat, fixedDescription), + expected: new[] + { + VerifyVB.Diagnostic(DiagnosticDescriptorCreationAnalyzer.DefineDiagnosticDescriptionCorrectlyRule).WithLocation(0) + }); + } + } + + #endregion // RS1033 (DefineDiagnosticDescriptionCorrectlyRule) + + #region RS1037 (AddCompilationEndCustomTagRule) + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_WithRequiredCustomTag_NoDiagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: ""CompilationEnd"")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + }); + }); + } +}", + GetRS1007ExpectedDiagnostic(0)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""CompilationEnd"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + End Function) + End Function) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_NonInlinedCustomTags_NoDiagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: tags)|}; + private static readonly string[] tags = new string[] { """" }; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + }); + }); + } +}", + GetRS1007ExpectedDiagnostic(0)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = {|#0:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", Tags)|} + Private Shared ReadOnly Tags As String() = { """" } + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + End Function) + End Function) + End Sub +End Class +", + GetRS1007ExpectedDiagnostic(0)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_WithoutRequiredCustomTag_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)); + + var diag2 = Diagnostic.Create(descriptor2, Location.None); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + compilationEndAnalysisContext.ReportDiagnostic(Diagnostic.Create(descriptor1, Location.None)) + + Dim diag2 = Diagnostic.Create(descriptor2, Location.None) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_DiagnosticStoredInLocal_WithInitializer_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor descriptor2 = + {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + var diag1 = Diagnostic.Create(descriptor1, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag1); + + var diag2 = Diagnostic.Create(descriptor2, Location.None); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = {|#2:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + Dim diag1 = Diagnostic.Create(descriptor1, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag1) + + Dim diag2 = Diagnostic.Create(descriptor2, Location.None) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1007ExpectedDiagnostic(2)); + } + + [Fact, WorkItem(6282, "https://github.com/dotnet/roslyn-analyzers/issues/6282")] + public async Task RS1037_DiagnosticStoredInLocal_WithAssignment_Diagnostic() + { + await VerifyCSharpAnalyzerAsync(@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor {|#0:descriptor1|} = + {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + private static readonly DiagnosticDescriptor {|#2:descriptor2|} = + {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1, descriptor2); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + compilationStartAnalysisContext.RegisterCompilationEndAction(compilationEndAnalysisContext => + { + var diag = Diagnostic.Create(descriptor1, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag); + + diag = Diagnostic.Create(descriptor2, Location.None); + compilationEndAnalysisContext.ReportDiagnostic(diag); + }); + }); + } +}", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1037ExpectedDiagnostic(2, "descriptor2"), + GetRS1007ExpectedDiagnostic(3)); + + await VerifyBasicAnalyzerAsync(@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly {|#0:descriptor1|} As DiagnosticDescriptor = {|#1:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + Private Shared ReadOnly {|#2:descriptor2|} As DiagnosticDescriptor = {|#3:new DiagnosticDescriptor(""MyDiagnosticId"", ""MyDiagnosticTitle"", ""MyDiagnosticMessage"", ""MyDiagnosticCategory"", DiagnosticSeverity.Warning, True, description:=Nothing, helpLinkUri:=""HelpLinkUrl"", ""Tag"")|} + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1, descriptor2) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationStartAnalysisContext) + compilationStartAnalysisContext.RegisterCompilationEndAction(Function(compilationEndAnalysisContext) + Dim diag = Diagnostic.Create(descriptor1, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag) + + diag = Diagnostic.Create(descriptor2, Location.None) + compilationEndAnalysisContext.ReportDiagnostic(diag) + End Function) + End Function) + End Sub +End Class +", + GetRS1037ExpectedDiagnostic(0, "descriptor1"), + GetRS1007ExpectedDiagnostic(1), + GetRS1037ExpectedDiagnostic(2, "descriptor2"), + GetRS1007ExpectedDiagnostic(3)); + } + + #endregion // RS1037 (AddCompilationEndCustomTagRule) + + [Fact, WorkItem(6035, "https://github.com/dotnet/roslyn-analyzers/issues/6035")] + public async Task VerifyFieldReferenceForFieldDefinedInSeparateFile() + { + var file1 = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor = + {|#0:new DiagnosticDescriptor(HelperType.Id, HelperType.Title, HelperType.Message, HelperType.Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""HelpLink"", customTags: """")|}; + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; + + var file2 = @" +public class HelperType +{ + public const string Id = ""Id""; + public const string Title = ""Title""; + public const string Message = ""Message""; + public const string Category = ""Category""; +}"; + + var expected = new[] { GetRS1007ExpectedDiagnostic(0) }; + var test = new VerifyCS.Test + { + TestState = { Sources = { file1, file2 } }, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + #region Helpers + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1007ExpectedDiagnostic(int markupKey) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.UseLocalizableStringsInDescriptorRule) + .WithLocation(markupKey) + .WithArguments(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1015ExpectedDiagnostic(int markupKey) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.ProvideHelpUriInDescriptorRule) + .WithLocation(markupKey); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1017ExpectedDiagnostic(int markupKey, string descriptorName) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.DiagnosticIdMustBeAConstantRule) + .WithLocation(markupKey) + .WithArguments(descriptorName); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1018ExpectedDiagnostic(int markupKey, string diagnosticId, string category, string format, string additionalFile) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.DiagnosticIdMustBeInSpecifiedFormatRule) + .WithLocation(markupKey) + .WithArguments(diagnosticId, category, format, additionalFile); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1019ExpectedDiagnostic(int markupKey, string duplicateId, string otherAnalyzerName) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.UseUniqueDiagnosticIdRule) + .WithLocation(markupKey) + .WithArguments(duplicateId, otherAnalyzerName); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1020ExpectedDiagnostic(int markupKey, string category, string additionalFile) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.UseCategoriesFromSpecifiedRangeRule) + .WithLocation(markupKey) + .WithArguments(category, additionalFile); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1028ResultAt(int markupKey) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.ProvideCustomTagsInDescriptorRule) + .WithLocation(markupKey); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1021ExpectedDiagnostic(int markupKey, string invalidEntry, string additionalFile) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.AnalyzerCategoryAndIdRangeFileInvalidRule) + .WithLocation(markupKey) + .WithArguments(invalidEntry, additionalFile); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1029ResultAt(int markupKey, string ruleId) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.DoNotUseReservedDiagnosticIdRule) + .WithLocation(markupKey) + .WithArguments(ruleId); + + /// + /// Creates an expected diagnostic for + /// + private static DiagnosticResult GetRS1037ExpectedDiagnostic(int markupKey, string fieldName) => + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.AddCompilationEndCustomTagRule) + .WithLocation(markupKey) + .WithArguments(fieldName); + + private static async Task VerifyCSharpAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestCode = source, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyBasicAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestCode = source, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyCSharpCodeFixAsync(string source, string fixedSource, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyBasicCodeFixAsync(string source, string fixedSource, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestCode = source, + FixedCode = fixedSource, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyCSharpCodeFixAsync(string source, string additionalFileName, string additionalFileText, string fixedSource, string fixedAdditionalFileText, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (additionalFileName, additionalFileText), }, + }, + FixedState = + { + Sources = { fixedSource }, + AdditionalFiles = { (additionalFileName, fixedAdditionalFileText), }, + }, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyBasicCodeFixAsync(string source, string additionalFileName, string additionalFileText, string fixedSource, string fixedAdditionalFileText, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (additionalFileName, additionalFileText), }, + }, + FixedState = + { + Sources = { fixedSource }, + AdditionalFiles = { (additionalFileName, fixedAdditionalFileText), }, + }, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + }; + + test.SolutionTransforms.Add(WithoutEnableReleaseTrackingWarning); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static readonly ImmutableDictionary s_enableReleaseTrackingWarningDisabled = ImmutableDictionary.Empty + .Add(DiagnosticDescriptorCreationAnalyzer.EnableAnalyzerReleaseTrackingRule.Id, ReportDiagnostic.Suppress); + + private static Solution WithoutEnableReleaseTrackingWarning(Solution solution, ProjectId projectId) + { + var compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(compilationOptions.SpecificDiagnosticOptions.SetItems(s_enableReleaseTrackingWarningDisabled)); + return solution.WithProjectCompilationOptions(projectId, compilationOptions); + } + + private const string AdditionalFileName = "DiagnosticCategoryAndIdRanges.txt"; + + private const string CSharpDiagnosticDescriptorCreationHelper = @" +internal static class DiagnosticDescriptorHelper +{ + // Dummy DiagnosticDescriptor creation helper. + public static DiagnosticDescriptor Create( + string id, + LocalizableString title, + LocalizableString messageFormat, + string category) + => null; +}"; + private const string VisualBasicDiagnosticDescriptorCreationHelper = @" +Friend Partial Module DiagnosticDescriptorHelper + ' Dummy DiagnosticDescriptor creation helper. + Function Create(id As String, title As LocalizableString, messageFormat As LocalizableString, category As String) As DiagnosticDescriptor + Return Nothing + End Function +End Module"; + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotStorePerCompilationDataOntoFieldsRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotStorePerCompilationDataOntoFieldsRuleTests.cs new file mode 100644 index 0000000000000..9a31dc99e1926 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotStorePerCompilationDataOntoFieldsRuleTests.cs @@ -0,0 +1,614 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpDiagnosticAnalyzerFieldsAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicDiagnosticAnalyzerFieldsAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class DoNotStorePerCompilationDataOntoFieldsRuleTests + { + [Fact, WorkItem(7196, "https://github.com/dotnet/roslyn-analyzers/issues/7196")] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using MyNamedType = Microsoft.CodeAnalysis.INamedTypeSymbol; + +abstract class {|CS1729:MyCompilation|} : Compilation +{ + // Compile error: no public constructor exists on Compilation. +} + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly ITypeSymbol x1; + public static readonly CSharpCompilation x2; + private readonly List x3; + private static Dictionary x4; + private static readonly IBinaryOperation x5; + private static readonly ISymbol x6; + private static readonly IOperation x7; + + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(19, 29, violatingTypeName: typeof(ITypeSymbol).FullName), + GetCSharpExpectedDiagnostic(20, 28, violatingTypeName: typeof(CSharpCompilation).FullName), + GetCSharpExpectedDiagnostic(21, 27, violatingTypeName: typeof(INamedTypeSymbol).FullName), + GetCSharpExpectedDiagnostic(22, 31, violatingTypeName: "MyCompilation"), + GetCSharpExpectedDiagnostic(23, 29, violatingTypeName: typeof(IBinaryOperation).FullName), + GetCSharpExpectedDiagnostic(24, 29, violatingTypeName: typeof(ISymbol).FullName), + GetCSharpExpectedDiagnostic(25, 29, violatingTypeName: typeof(IOperation).FullName) + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact, WorkItem(7196, "https://github.com/dotnet/roslyn-analyzers/issues/7196")] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic +Imports MyNamedType = Microsoft.CodeAnalysis.INamedTypeSymbol + +MustInherit Class {|BC31399:MyCompilation|} + Inherits Compilation ' Compile error: no public constructor exists on Compilation. +End Class + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As ITypeSymbol + Public Shared ReadOnly x2 As VisualBasicCompilation + Private ReadOnly x3 As List(Of MyNamedType) + Private Shared x4 As Dictionary(Of MyCompilation, MyNamedType) + Private Shared ReadOnly x5 As IBinaryOperation + Private Shared ReadOnly x6 As ISymbol + Private Shared ReadOnly x7 As IOperation + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(19, 35, violatingTypeName: typeof(ITypeSymbol).FullName), + GetBasicExpectedDiagnostic(20, 34, violatingTypeName: typeof(VisualBasicCompilation).FullName), + GetBasicExpectedDiagnostic(21, 36, violatingTypeName: typeof(INamedTypeSymbol).FullName), + GetBasicExpectedDiagnostic(22, 40, violatingTypeName: "MyCompilation"), + GetBasicExpectedDiagnostic(23, 35, violatingTypeName: typeof(IBinaryOperation).FullName), + GetBasicExpectedDiagnostic(24, 35, violatingTypeName: typeof(ISymbol).FullName), + GetBasicExpectedDiagnostic(25, 35, violatingTypeName: typeof(IOperation).FullName) + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using MyNamedType = Microsoft.CodeAnalysis.INamedTypeSymbol; + +abstract class {|CS1729:MyCompilation|} : Compilation +{ + // Compile error: no public constructor exists on Compilation. +} + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor x1; + private readonly List x2; + + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + var analyzer = new NestedCompilationAnalyzer(); + context.RegisterCompilationStartAction(analyzer.StartCompilation); + } + + private class NestedCompilationAnalyzer + { + // Ok to store per-compilation data here. + private readonly Dictionary x; + + internal void StartCompilation(CompilationStartAnalysisContext context) + { + } + } + + private struct NestedStructCompilationAnalyzer + { + // Ok to store per-compilation data here. + private readonly Dictionary y; + + internal void StartCompilation(CompilationStartAnalysisContext context) + { + } + } +} + +class MyAnalyzerWithoutAttribute : DiagnosticAnalyzer +{ + // Ok to store per-compilation data here. + private static ITypeSymbol x; + + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + throw new NotImplementedException(); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports MyNamedType = Microsoft.CodeAnalysis.INamedTypeSymbol + +MustInherit Class {|BC31399:MyCompilation|} + Inherits Compilation ' Compile error: no public constructor exists on Compilation. +End Class + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As DiagnosticDescriptor + Private ReadOnly x2 As List(Of LocalizableResourceString) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + Dim compilationAnalyzer = New NestedCompilationAnalyzer + context.RegisterCompilationStartAction(AddressOf compilationAnalyzer.StartCompilation) + End Sub + + Class NestedCompilationAnalyzer + ' Ok to store per-compilation data here. + Private ReadOnly x As Dictionary(Of MyCompilation, MyNamedType) + + Friend Sub StartCompilation(context As CompilationStartAnalysisContext) + End Sub + End Class + + Structure NestedStructCompilationAnalyzer + ' Ok to store per-compilation data here. + Private ReadOnly y As Dictionary(Of MyCompilation, MyNamedType) + + Friend Sub StartCompilation(context As CompilationStartAnalysisContext) + End Sub + End Structure +End Class + +Class MyAnalyzerWithoutAttribute + Inherits DiagnosticAnalyzer + + ' Ok to store per-compilation data here. + Private Shared x As ITypeSymbol + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + Throw New NotImplementedException() + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact, WorkItem(4308, "https://github.com/dotnet/roslyn-analyzers/issues/4308")] + public async Task CSharp_NestedStruct_NoDiagnosticAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace MyNamespace +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class AnyInstanceInjectionAnalyzer : DiagnosticAnalyzer + { + public struct DependencyAccess + { + public IMethodSymbol method; + public string expectedName; + } + + public override ImmutableArray SupportedDiagnostics => throw new NotImplementedException(); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterCompilationStartAction(OnCompilationStart); + } + + public void OnCompilationStart(CompilationStartAnalysisContext context) + { + var accessors = new ConcurrentBag(); + + context.RegisterSymbolAction( + symbolContext => AnalyzeSymbol(symbolContext, accessors), + SymbolKind.Property, + SymbolKind.Field + ); + + context.RegisterSemanticModelAction( + semanticModelContext => AnalyzeSemanticModel(semanticModelContext, accessors) + ); + } + + public void AnalyzeSymbol(SymbolAnalysisContext context, ConcurrentBag accessors) + { + // collect symbols for analysis + } + + public void AnalyzeSemanticModel(SemanticModelAnalysisContext context, ConcurrentBag accessors) + { + foreach (var access in accessors) + { + // analyze + } + } + } +}"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task CSharp_Func_NoDiagnostic(string delegateType) + { + return VerifyCS.VerifyAnalyzerAsync($@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly {delegateType} x; + + public override ImmutableArray SupportedDiagnostics + {{ + get + {{ + throw new NotImplementedException(); + }} + }} + + public override void Initialize(AnalysisContext context) + {{ + }} +}}"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task CSharp_NestedFunc_NoDiagnostic(string delegateType) + { + return VerifyCS.VerifyAnalyzerAsync($@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly ImmutableArray<{delegateType}> x; + + public override ImmutableArray SupportedDiagnostics + {{ + get + {{ + throw new NotImplementedException(); + }} + }} + + public override void Initialize(AnalysisContext context) + {{ + }} +}}"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task CSharp_NestedNestedFunc_NoDiagnostic(string delegateType) + { + return VerifyCS.VerifyAnalyzerAsync($@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly {delegateType}> x; + + public override ImmutableArray SupportedDiagnostics + {{ + get + {{ + throw new NotImplementedException(); + }} + }} + + public override void Initialize(AnalysisContext context) + {{ + }} +}}"); + } + + [Theory] + [CombinatorialData] + public Task CSharp_MultiFunc_NoDiagnostic([CombinatorialValues("Func", "Action")] string delegateType, [CombinatorialValues("bool", "int, string")] string types) + { + return VerifyCS.VerifyAnalyzerAsync($@" +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly {delegateType} x; + + public override ImmutableArray SupportedDiagnostics + {{ + get + {{ + throw new NotImplementedException(); + }} + }} + + public override void Initialize(AnalysisContext context) + {{ + }} +}}"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task VisualBasic_Func_NoDiagnostic(string delegateType) + { + return VerifyVB.VerifyAnalyzerAsync($@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As {delegateType}(Of IBinaryOperation) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + + End Sub +End Class"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task VisualBasic_NestedFunc_NoDiagnostic(string delegateType) + { + return VerifyVB.VerifyAnalyzerAsync($@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As ImmutableArray(Of {delegateType}(Of IBinaryOperation)) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + + End Sub +End Class"); + } + + [Theory] + [InlineData("Func")] + [InlineData("Action")] + public Task VisualBasic_NestedNestedFunc_NoDiagnostic(string delegateType) + { + return VerifyVB.VerifyAnalyzerAsync($@" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As {delegateType}(Of List(Of IBinaryOperation)) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + + End Sub +End Class"); + } + + [Theory] + [CombinatorialData] + public Task VisualBasic_MultiFunc_NoDiagnostic([CombinatorialValues("Func", "Action")] string delegateType, [CombinatorialValues("Int32", "Int32, String")] string types) + { + return VerifyVB.VerifyAnalyzerAsync($@" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly x1 As {delegateType}(Of IBinaryOperation, {types}) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + + End Sub +End Class"); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string violatingTypeName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(violatingTypeName); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string violatingTypeName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(violatingTypeName); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseCompilationGetSemanticModelTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseCompilationGetSemanticModelTests.cs new file mode 100644 index 0000000000000..3cf7a6a00ae1b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseCompilationGetSemanticModelTests.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DoNotUseCompilationGetSemanticModelAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DoNotUseCompilationGetSemanticModelAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class DoNotUseCompilationGetSemanticModelTests + { + [Fact] + public async Task CallInInitializeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => throw null; + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(csac => + { + csac.Compilation.GetSemanticModel(null); + }); + } +}", + GetCSharpExpectedDiagnostic(14, 13)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + context.RegisterCompilationStartAction( + Function(csac) + csac.Compilation.GetSemanticModel(Nothing) + End Function) + End Sub +End Class", + GetBasicExpectedDiagnostic(18, 17)); + } + + [Fact] + public async Task CallInSeparateMethodAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => throw null; + + public override void Initialize(AnalysisContext context) + { + DoSomething(context); + } + + private void DoSomething(AnalysisContext context) + { + context.RegisterOperationAction(oac => + { + var semanticModel = oac.Compilation.GetSemanticModel(null); + }, OperationKind.Invocation); + } +}", + GetCSharpExpectedDiagnostic(19, 33)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + DoSomething(context) + End Sub + + Private Sub DoSomething(ByVal context As AnalysisContext) + context.RegisterOperationAction( + Function(oac) + Dim semanticModel = oac.Compilation.GetSemanticModel(Nothing) + End Function, + OperationKind.Invocation) + End Sub +End Class", + GetBasicExpectedDiagnostic(22, 37)); + } + + [Fact] + public async Task CastedCallAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => throw null; + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(csac => + { + var csharpCompilation = csac.Compilation as CSharpCompilation; + csharpCompilation.GetSemanticModel(null); + }); + } +}", + GetCSharpExpectedDiagnostic(16, 13)); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(ByVal context As AnalysisContext) + context.RegisterCompilationStartAction( + Function(csac) + Dim basicCompilation = TryCast(csac.Compilation, VisualBasicCompilation) + basicCompilation.GetSemanticModel(Nothing) + End Function) + End Sub +End Class", + GetBasicExpectedDiagnostic(20, 17)); + } + + [Fact] + public async Task CallInNonDiagnosticAnalyzerClassAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class NotADiagnosticAnalyzer +{ + public void Foo(AnalysisContext context) + { + context.RegisterCompilationStartAction(csac => + { + csac.Compilation.GetSemanticModel(null); + }); + } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class NotADiagnosticAnalyzer + Public Sub Foo(ByVal context As AnalysisContext) + context.RegisterCompilationStartAction( + Function(csac) + csac.Compilation.GetSemanticModel(Nothing) + End Function) + End Sub +End Class"); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic().WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseFileLocalTypesForAnalyzersOrGeneratorsTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseFileLocalTypesForAnalyzersOrGeneratorsTests.cs new file mode 100644 index 0000000000000..60684ce54e4b4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/DoNotUseFileLocalTypesForAnalyzersOrGeneratorsTests.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DoNotUseFileTypesForAnalyzersOrGenerators, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.MetaAnalyzers +{ + public sealed partial class MetaAnalyzersTests + { + private const string CompilerReferenceVersion = "4.6.0"; + + [Fact] + public async Task FiresOnFileLocalType_CodeFixProvider() + { + var code = """ + using System.Collections.Immutable; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CodeFixes; + file class Type : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds => throw null!; + public override FixAllProvider GetFixAllProvider() => throw null!; + public override Task RegisterCodeFixesAsync(CodeFixContext context) => throw null!; + } + """; + await new VerifyCS.Test + { + TestCode = code, + LanguageVersion = LanguageVersion.CSharp11, + ExpectedDiagnostics = + { + // /0/Test0.cs(5,12): error RS1043: Type 'Type' should not be marked with 'file' + VerifyCS.Diagnostic().WithSpan(5, 12, 5, 16).WithArguments("Type"), + } + }.RunAsync(); + } + + [Fact] + public async Task FiresOnFileLocalType_DiagnosticAnalyzer() + { + var code = """ + using System.Collections.Immutable; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Diagnostics; + file class Type : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => throw null!; + public override void Initialize(AnalysisContext context) => throw null!; + + } + """; + await new VerifyCS.Test + { + TestCode = code, + LanguageVersion = LanguageVersion.CSharp11, + ExpectedDiagnostics = + { + // /0/Test0.cs(4,12): error RS1043: Type 'Type' should not be marked with 'file' + VerifyCS.Diagnostic().WithSpan(4, 12, 4, 16).WithArguments("Type"), + } + }.RunAsync(); + } + + [Fact] + public async Task FiresOnFileLocalType_ISourceGenerator() + { + var code = """ + using Microsoft.CodeAnalysis; + file class Type : Microsoft.CodeAnalysis.ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) => throw null!; + public void Execute(GeneratorExecutionContext context) => throw null!; + } + """; + await new VerifyCS.Test + { + TestCode = code, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages([new PackageIdentity("Microsoft.CodeAnalysis.Common", CompilerReferenceVersion)]), + ExpectedDiagnostics = + { + // /0/Test0.cs(2,12): error RS1043: Type 'Type' should not be marked with 'file' + VerifyCS.Diagnostic().WithSpan(2, 12, 2, 16).WithArguments("Type"), + }, + }.RunAsync(); + } + + [Fact] + public async Task FiresOnFileLocalType_IIncrementalGenerator() + { + var code = """ + using Microsoft.CodeAnalysis; + file class Type : Microsoft.CodeAnalysis.IIncrementalGenerator + { + public void Initialize(IncrementalGeneratorInitializationContext context) => throw null!; + } + """; + await new VerifyCS.Test + { + TestCode = code, + LanguageVersion = LanguageVersion.CSharp11, + ReferenceAssemblies = ReferenceAssemblies.NetStandard.NetStandard20.AddPackages([new PackageIdentity("Microsoft.CodeAnalysis.Common", CompilerReferenceVersion)]), + ExpectedDiagnostics = + { + // /0/Test0.cs(2,12): error RS1043: Type 'Type' should not be marked with 'file' + VerifyCS.Diagnostic().WithSpan(2, 12, 2, 16).WithArguments("Type"), + }, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/EnableConcurrentExecutionAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/EnableConcurrentExecutionAnalyzerTests.cs new file mode 100644 index 0000000000000..9338bcb9d1ec0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/EnableConcurrentExecutionAnalyzerTests.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.EnableConcurrentExecutionAnalyzer, + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers.CSharpEnableConcurrentExecutionFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.EnableConcurrentExecutionAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes.BasicEnableConcurrentExecutionFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class EnableConcurrentExecutionAnalyzerTests + { + [Fact] + public async Task TestSimpleCase_CSharpAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext [|context|]) + { + } +} +"; + var fixedCode = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + + [Fact] + public async Task TestSimpleCase_VisualBasicAsync() + { + var code = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize([|context|] As AnalysisContext) + End Sub +End Class +"; + var fixedCode = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize([|context|] As AnalysisContext) + context.EnableConcurrentExecution() + End Sub +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(code, fixedCode); + } + + [Fact] + public async Task RenamedMethod_CSharpAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + } + + public void NotInitialize(AnalysisContext context) + { + } +} +"; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task RenamedMethod_VisualBasicAsync() + { + var code = @" +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class Analyzer + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New System.Exception + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.EnableConcurrentExecution() + End Sub + + Public Sub NotInitialize(context As AnalysisContext) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [Fact, WorkItem(2698, "https://github.com/dotnet/roslyn-analyzers/issues/2698")] + public async Task RS1026_ExpressionBodiedMethodAsync() + { + var code = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext [|context|]) + => context.RegisterCompilationAction(x => { }); +} +"; + var fixedCode = @" +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class Analyzer : DiagnosticAnalyzer { + public override ImmutableArray SupportedDiagnostics => throw null; + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.RegisterCompilationAction(x => { }); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(code, fixedCode); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidReportDiagnosticRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidReportDiagnosticRuleTests.cs new file mode 100644 index 0000000000000..616b437c5278f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidReportDiagnosticRuleTests.cs @@ -0,0 +1,283 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpReportDiagnosticAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicReportDiagnosticAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class InvalidReportDiagnosticRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = new DiagnosticDescriptor(""MyDiagnosticId1"", null, null, null, DiagnosticSeverity.Warning, false); + private static readonly DiagnosticDescriptor descriptor2 = new DiagnosticDescriptor(""MyDiagnosticId2"", null, null, null, DiagnosticSeverity.Warning, false); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)); + + var diag = Diagnostic.Create(descriptor2, Location.None); + context.ReportDiagnostic(diag); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)); + + Diagnostic diag = Diagnostic.Create(descriptor2, Location.None), diag2 = Diagnostic.Create(descriptor2, Location.None); + context.ReportDiagnostic(diag); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)); + + var diag = Diagnostic.Create(descriptor2, Location.None); + context.ReportDiagnostic(diag); + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(27, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(30, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(35, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(38, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(43, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(46, 9, unsupportedDescriptorName: "descriptor2") + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact, WorkItem(1689, "https://github.com/dotnet/roslyn-analyzers/issues/1689")] + public async Task CSharp_VerifyDiagnostic_PropertyInitializerAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = new DiagnosticDescriptor(""MyDiagnosticId1"", null, null, null, DiagnosticSeverity.Warning, false); + private static readonly DiagnosticDescriptor descriptor2 = new DiagnosticDescriptor(""MyDiagnosticId2"", null, null, null, DiagnosticSeverity.Warning, false); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(descriptor1); + + public override void Initialize(AnalysisContext context) + { + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)); + + var diag = Diagnostic.Create(descriptor2, Location.None); + context.ReportDiagnostic(diag); + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(21, 9, unsupportedDescriptorName: "descriptor2"), + GetCSharpExpectedDiagnostic(24, 9, unsupportedDescriptorName: "descriptor2") + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor(""MyDiagnosticId1"", Nothing, Nothing, Nothing, DiagnosticSeverity.Warning, False) + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New DiagnosticDescriptor(""MyDiagnosticId2"", Nothing, Nothing, Nothing, DiagnosticSeverity.Warning, False) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)) + + Dim diag = Diagnostic.Create(descriptor2, Location.None) + context.ReportDiagnostic(diag) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)) + + Dim diag = Diagnostic.Create(descriptor2, Location.None), diag2 = Diagnostic.Create(descriptor2, Location.None) + context.ReportDiagnostic(diag) + End Sub + + Private Shared Sub AnalyzeOperation(context As OperationAnalysisContext) + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None)) + + Dim diag = Diagnostic.Create(descriptor2, Location.None) + context.ReportDiagnostic(diag) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(24, 9, unsupportedDescriptorName: "descriptor2"), + GetBasicExpectedDiagnostic(27, 9, unsupportedDescriptorName: "descriptor2"), + GetBasicExpectedDiagnostic(31, 9, unsupportedDescriptorName: "descriptor2"), + GetBasicExpectedDiagnostic(34, 9, unsupportedDescriptorName: "descriptor2"), + GetBasicExpectedDiagnostic(38, 9, unsupportedDescriptorName: "descriptor2"), + GetBasicExpectedDiagnostic(41, 9, unsupportedDescriptorName: "descriptor2") + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = new DiagnosticDescriptor(""MyDiagnosticId1"", null, null, null, DiagnosticSeverity.Warning, false); + private static readonly DiagnosticDescriptor descriptor2 = new DiagnosticDescriptor(""MyDiagnosticId2"", null, null, null, DiagnosticSeverity.Warning, false); + + public override ImmutableArray SupportedDiagnostics + { + get + { + return ImmutableArray.Create(descriptor1); + } + } + + public override void Initialize(AnalysisContext context) + { + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + // Overload resolution failures + context.{|CS1501:ReportDiagnostic|}(Diagnostic.Create(descriptor2, Location.None), null); + context.ReportDiagnostic(Diagnostic.{|CS0121:Create|}(descriptor2, Location.None, null)); + context.ReportDiagnostic({|CS0841:diag|}); + + // Needs flow analysis + var diag = Diagnostic.Create(descriptor2, Location.None); + var diag2 = diag; + context.ReportDiagnostic(diag2); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Generic +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly descriptor1 As DiagnosticDescriptor = New DiagnosticDescriptor(""MyDiagnosticId1"", Nothing, Nothing, Nothing, DiagnosticSeverity.Warning, False) + Private Shared ReadOnly descriptor2 As DiagnosticDescriptor = New DiagnosticDescriptor(""MyDiagnosticId2"", Nothing, Nothing, Nothing, DiagnosticSeverity.Warning, False) + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(descriptor1) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + ' Overload resolution failures + context.ReportDiagnostic(Diagnostic.Create(descriptor2, Location.None), {|BC30057:Nothing|}) + context.ReportDiagnostic(Diagnostic.{|BC30521:Create|}(descriptor2, Location.None, Nothing)) + context.ReportDiagnostic({|BC32000:diag|}) + + ' Needs flow analysis + Dim diag = Diagnostic.Create(descriptor2, Location.None) + Dim diag2 = diag + context.ReportDiagnostic(diag2) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string unsupportedDescriptorName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(unsupportedDescriptorName); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string unsupportedDescriptorName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(unsupportedDescriptorName); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidSyntaxKindTypeArgumentRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidSyntaxKindTypeArgumentRuleTests.cs new file mode 100644 index 0000000000000..4c37d2f786ad9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/InvalidSyntaxKindTypeArgumentRuleTests.cs @@ -0,0 +1,208 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class InvalidSyntaxKindTypeArgumentRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +#pragma warning disable RS1012 +#pragma warning disable RS1013 + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax, 0); + context.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(24, 9, typeArgumentName: "Int32", registerMethodName: DiagnosticWellKnownNames.RegisterSyntaxNodeActionName), + GetCSharpExpectedDiagnostic(25, 9, typeArgumentName: "Int32", registerMethodName: DiagnosticWellKnownNames.RegisterCodeBlockStartActionName) + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +#Disable Warning RS1012 +#Disable Warning RS1013 + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, 0) + context.RegisterCodeBlockStartAction(Of Int32)(AddressOf AnalyzeCodeBlockStart) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of Int32)) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(20, 9, typeArgumentName: "Int32", registerMethodName: DiagnosticWellKnownNames.RegisterSyntaxNodeActionName), + GetBasicExpectedDiagnostic(21, 9, typeArgumentName: "Int32", registerMethodName: DiagnosticWellKnownNames.RegisterCodeBlockStartActionName) + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +#pragma warning disable RS1012 +#pragma warning disable RS1013 + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +abstract class MyAnalyzer : DiagnosticAnalyzer + where T : struct +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.{|CS0411:RegisterSyntaxNodeAction|}(AnalyzeSyntax, null); // Overload resolution failure + context.RegisterSyntaxNodeAction<{|CS0246:ErrorType|}>(AnalyzeSyntax, null); // Error type argument + context.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); // NYI: Type param as a type argument + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +#Disable Warning RS1012 +#Disable Warning RS1013 + + +Class MyAnalyzer(Of T As Structure) + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.{|BC30518:RegisterSyntaxNodeAction|}(AddressOf AnalyzeSyntax, Nothing) ' Overload resolution failure + context.{|BC30521:RegisterSyntaxNodeAction(Of {|BC30002:ErrorType|})|}(AddressOf AnalyzeSyntax, Nothing) ' Error type argument + context.RegisterCodeBlockStartAction(Of T)(AddressOf AnalyzeCodeBlockStart) ' NYI: Type param as a type argument + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of T)) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string typeArgumentName, string registerMethodName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CSharpRegisterActionAnalyzer.InvalidSyntaxKindTypeArgumentRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(typeArgumentName, DiagnosticWellKnownNames.TLanguageKindEnumName, registerMethodName); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string typeArgumentName, string registerMethodName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(BasicRegisterActionAnalyzer.InvalidSyntaxKindTypeArgumentRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(typeArgumentName, DiagnosticWellKnownNames.TLanguageKindEnumName, registerMethodName); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingDiagnosticAnalyzerAttributeRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingDiagnosticAnalyzerAttributeRuleTests.cs new file mode 100644 index 0000000000000..4081a9f6d0b62 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingDiagnosticAnalyzerAttributeRuleTests.cs @@ -0,0 +1,272 @@ +// 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. + +#pragma warning disable CA1305 + +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticAnalyzerAttributeAnalyzer, + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers.CSharpApplyDiagnosticAnalyzerAttributeFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.DiagnosticAnalyzerAttributeAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes.BasicApplyDiagnosticAnalyzerAttributeFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class MissingDiagnosticAnalyzerAttributeRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAndFixesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; +#pragma warning disable RS0030 // Do not use banned APIs + DiagnosticResult expected = VerifyCS.Diagnostic(DiagnosticAnalyzerAttributeAnalyzer.MissingDiagnosticAnalyzerAttributeRule).WithLocation(7, 7).WithArguments(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); +#pragma warning restore RS0030 // Do not use banned APIs + await VerifyCS.VerifyAnalyzerAsync(source, expected); + + var fixedCode_WithCSharpAttribute = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + FixedState = { Sources = { fixedCode_WithCSharpAttribute } }, + CodeActionIndex = 0, + CodeActionEquivalenceKey = string.Format(CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_1, LanguageNames.CSharp), + }.RunAsync(); + + var fixedCode_WithCSharpAndVBAttributes = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + FixedState = { Sources = { fixedCode_WithCSharpAndVBAttributes } }, + CodeActionIndex = 2, + CodeActionEquivalenceKey = string.Format(CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_2, LanguageNames.CSharp, LanguageNames.VisualBasic), + }.RunAsync(); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAndFixesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; +#pragma warning disable RS0030 // Do not use banned APIs + DiagnosticResult expected = VerifyVB.Diagnostic(DiagnosticAnalyzerAttributeAnalyzer.MissingDiagnosticAnalyzerAttributeRule).WithLocation(7, 7).WithArguments(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); +#pragma warning restore RS0030 // Do not use banned APIs + await VerifyVB.VerifyAnalyzerAsync(source, expected); + + var fixedCode_WithVBAttribute = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + FixedState = { Sources = { fixedCode_WithVBAttribute } }, + CodeActionIndex = 1, + CodeActionEquivalenceKey = string.Format(CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_1, LanguageNames.VisualBasic), + }.RunAsync(); + + var fixedCode_WithCSharpAndVBAttributes = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + ExpectedDiagnostics = { expected }, + }, + FixedState = { Sources = { fixedCode_WithCSharpAndVBAttributes } }, + CodeActionIndex = 2, + CodeActionEquivalenceKey = string.Format(CodeAnalysisDiagnosticsResources.ApplyDiagnosticAnalyzerAttribute_2, LanguageNames.CSharp, LanguageNames.VisualBasic), + }.RunAsync(); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzerWithLanguageSpecificAttribute : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + } +} + +public abstract class MyAbstractAnalyzerWithoutAttribute : DiagnosticAnalyzer +{ +} +"; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzerWithLanguageSpecificAttribute + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + End Sub +End Class + +Public MustInherit Class MyAbstractAnalyzerWithoutAttribute + Inherits DiagnosticAnalyzer +End Class +"; + await VerifyVB.VerifyAnalyzerAsync(source); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingKindArgumentToRegisterActionRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingKindArgumentToRegisterActionRuleTests.cs new file mode 100644 index 0000000000000..58355fe23a1b0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/MissingKindArgumentToRegisterActionRuleTests.cs @@ -0,0 +1,264 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class MissingKindArgumentToRegisterActionRuleTests + { + [Fact] + public async Task CSharp_VerifyRegisterSymbolActionDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSymbolAction(AnalyzeSymbol); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } +}"; + DiagnosticResult expected = GetCSharpExpectedDiagnostic(20, 9, MissingKindArgument.SymbolKind); + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyRegisterSymbolActionDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterSymbolAction(AddressOf AnalyzeSymbol) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub +End Class +"; + DiagnosticResult expected = GetBasicExpectedDiagnostic(17, 9, MissingKindArgument.SymbolKind); + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_VerifyRegisterSyntaxActionDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } +}"; + DiagnosticResult expected = GetCSharpExpectedDiagnostic(21, 9, MissingKindArgument.SyntaxKind); + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyRegisterSyntaxActionDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterSyntaxNodeAction(Of SyntaxKind)(AddressOf AnalyzeSyntax) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub +End Class +"; + DiagnosticResult expected = GetBasicExpectedDiagnostic(18, 9, MissingKindArgument.SyntaxKind); + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_VerifyRegisterOperationActionDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterOperationAction(AnalyzeOperation); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + } +}"; + DiagnosticResult expected = GetCSharpExpectedDiagnostic(20, 9, MissingKindArgument.OperationKind); + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyRegisterOperationActionDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterOperationAction(AddressOf AnalyzeOperation) + End Sub + + Private Shared Sub AnalyzeOperation(context As OperationAnalysisContext) + End Sub +End Class +"; + DiagnosticResult expected = GetBasicExpectedDiagnostic(17, 9, MissingKindArgument.OperationKind); + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, MissingKindArgument kind) + { + var rule = kind switch + { + MissingKindArgument.SymbolKind => CSharpRegisterActionAnalyzer.MissingSymbolKindArgumentRule, + MissingKindArgument.SyntaxKind => CSharpRegisterActionAnalyzer.MissingSyntaxKindArgumentRule, + MissingKindArgument.OperationKind => CSharpRegisterActionAnalyzer.MissingOperationKindArgumentRule, + _ => throw new ArgumentException("Unsupported argument kind", nameof(kind)), + }; + +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyCS.Diagnostic(rule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, MissingKindArgument kind) + { + var rule = kind switch + { + MissingKindArgument.SymbolKind => BasicRegisterActionAnalyzer.MissingSymbolKindArgumentRule, + MissingKindArgument.SyntaxKind => BasicRegisterActionAnalyzer.MissingSyntaxKindArgumentRule, + MissingKindArgument.OperationKind => BasicRegisterActionAnalyzer.MissingOperationKindArgumentRule, + _ => throw new ArgumentException("Unsupported argument kind", nameof(kind)), + }; + +#pragma warning disable RS0030 // Do not use banned APIs + return VerifyVB.Diagnostic(rule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + } + + private enum MissingKindArgument + { + SymbolKind, + SyntaxKind, + OperationKind + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/PreferIsKindAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/PreferIsKindAnalyzerTests.cs new file mode 100644 index 0000000000000..423b371d9416d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/PreferIsKindAnalyzerTests.cs @@ -0,0 +1,523 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.PreferIsKindAnalyzer, + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.Fixers.CSharpPreferIsKindFix>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.PreferIsKindAnalyzer, + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes.BasicPreferIsKindFix>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class PreferIsKindAnalyzerTests + { + [Theory] + [InlineData("==")] + [InlineData("!=")] + public async Task TestSimpleReturn_CSAsync(string @operator) + { + var source = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return [|node.Kind()|] {@operator} SyntaxKind.None; + }} +}} +"; + + var prefix = @operator switch + { + "==" => "", + "!=" => "!", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return {prefix}node.IsKind(SyntaxKind.None); + }} +}} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [InlineData("=")] + [InlineData("<>")] + public async Task TestSimpleReturn_VBAsync(string @operator) + { + var source = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|node.Kind()|] {@operator} SyntaxKind.None + End Function +End Class +"; + + var prefix = @operator switch + { + "=" => "", + "<>" => "Not ", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return {prefix}node.IsKind(SyntaxKind.None) + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TestCompoundExpression_CSAsync() + { + var source = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + bool Method(SyntaxNode node) + { + return [|node.Kind()|] != SyntaxKind.None && [|node.Kind()|] != SyntaxKind.TrueKeyword; + } +} +"; + var fixedSource = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + bool Method(SyntaxNode node) + { + return !node.IsKind(SyntaxKind.None) && !node.IsKind(SyntaxKind.TrueKeyword); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TestCompoundExpression_VBAsync() + { + var source = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|node.Kind()|] <> SyntaxKind.None AndAlso [|node.Kind()|] <> SyntaxKind.TrueKeyword + End Function +End Class +"; + var fixedSource = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return Not node.IsKind(SyntaxKind.None) AndAlso Not node.IsKind(SyntaxKind.TrueKeyword) + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [InlineData("==")] + [InlineData("!=")] + public async Task TestCompoundExpression2_CSAsync(string @operator) + { + var source = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return [|node.Kind()|] {@operator} SyntaxKind.None && + [|node.Kind()|] {@operator} SyntaxKind.TrueKeyword; + }} +}} +"; + + var prefix = @operator switch + { + "==" => "", + "!=" => "!", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return {prefix}node.IsKind(SyntaxKind.None) && + {prefix}node.IsKind(SyntaxKind.TrueKeyword); + }} +}} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [InlineData("=")] + [InlineData("<>")] + public async Task TestCompoundExpression2_VBAsync(string @operator) + { + var source = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|node.Kind()|] {@operator} SyntaxKind.None AndAlso + [|node.Kind()|] {@operator} SyntaxKind.TrueKeyword + End Function +End Class +"; + + var prefix = @operator switch + { + "=" => "", + "<>" => "Not ", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return {prefix}node.IsKind(SyntaxKind.None) AndAlso + {prefix}node.IsKind(SyntaxKind.TrueKeyword) + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Theory] + [InlineData("==")] + [InlineData("!=")] + public async Task TestCalledAsStaticMethod_CSAsync(string @operator) + { + var source = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return [|Microsoft.CodeAnalysis.CSharp.CSharpExtensions.Kind(node)|] {@operator} SyntaxKind.None; + }} +}} +"; + + var prefix = @operator switch + { + "==" => "", + "!=" => "!", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{{ + bool Method(SyntaxNode node) + {{ + return {prefix}Microsoft.CodeAnalysis.CSharp.CSharpExtensions.{{|#0:IsKind|}}(node, SyntaxKind.None); + }} +}} +"; + + await new VerifyCS.Test + { + TestCode = source, + FixedState = + { + Sources = { fixedSource }, + ExpectedDiagnostics = + { + // /0/Test0.cs(8,63): error CS0117: 'CSharpExtensions' does not contain a definition for 'IsKind' + DiagnosticResult.CompilerError("CS0117").WithLocation(0).WithArguments("Microsoft.CodeAnalysis.CSharp.CSharpExtensions", "IsKind"), + }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("=")] + [InlineData("<>")] + public async Task TestCalledAsStaticMethod_VBAsync(string @operator) + { + var source = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions.Kind(node)|] {@operator} SyntaxKind.None + End Function +End Class +"; + + var prefix = @operator switch + { + "=" => "", + "<>" => "Not ", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return {prefix}{{|#0:Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions.IsKind|}}(node, SyntaxKind.None) + End Function +End Class +"; + + await new VerifyVB.Test + { + TestCode = source, + FixedState = + { + Sources = { fixedSource }, + ExpectedDiagnostics = + { + // /0/Test0.vb(6) : error BC30456: 'IsKind' is not a member of 'VisualBasicExtensions'. + DiagnosticResult.CompilerError("BC30456").WithLocation(0).WithArguments("IsKind", "Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions"), + }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("=")] + [InlineData("<>")] + public async Task TestVBWithoutParensAsync(string @operator) + { + var source = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|node.Kind|] {@operator} SyntaxKind.None + End Function +End Class +"; + + var prefix = @operator switch + { + "=" => "", + "<>" => "Not ", + _ => throw new InvalidOperationException(), + }; + + var fixedSource = +$@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return {prefix}node.IsKind(SyntaxKind.None) + End Function +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TestSwitchStatement_CSAsync() + { + var source = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + void Method(SyntaxNode node) + { + switch (node.Kind()) + { + case SyntaxKind.NewKeyword: + break; + + case SyntaxKind.None: + break; + } + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task TestSwitchStatement_VBAsync() + { + var source = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Sub Method(node As SyntaxNode) + Select Case node.Kind() + Case SyntaxKind.NewKeyword + Return + Case Else + Return + End Select + End Sub +End Class +"; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + [WorkItem(4946, "https://github.com/dotnet/roslyn-analyzers/issues/4946")] + public async Task TestSingleNullConditionalAccess_CSAsync() + { + var source = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + bool Method(SyntaxNode node) + { + return [|node?.Kind()|] == SyntaxKind.None; + } +} +"; + + var fixedSource = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + bool Method(SyntaxNode node) + { + return node.IsKind(SyntaxKind.None); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + [WorkItem(4946, "https://github.com/dotnet/roslyn-analyzers/issues/4946")] + public async Task TestSingleNullConditionalAccess_VBAsync() + { + var source = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return [|node?.Kind()|] = SyntaxKind.None + End Function +End Class +"; + var fixedSource = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(node As SyntaxNode) As Boolean + Return node.IsKind(SyntaxKind.None) + End Function +End Class +"; + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + [WorkItem(4946, "https://github.com/dotnet/roslyn-analyzers/issues/4946")] + public async Task TestSingleNullConditionalAccess_SyntaxToken_CSAsync() + { + var source = +@"using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +class C +{ + bool Method(SyntaxToken? token) + { + return token?.Kind() == SyntaxKind.None; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + [WorkItem(4946, "https://github.com/dotnet/roslyn-analyzers/issues/4946")] + public async Task TestSingleNullConditionalAccess_SyntaxToken_VBAsync() + { + var source = +@"Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic + +Class C + Function Method(token As SyntaxToken?) As Boolean + Return token?.Kind() = SyntaxKind.None + End Function +End Class +"; + await VerifyVB.VerifyCodeFixAsync(source, source); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ReleaseTrackingAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ReleaseTrackingAnalyzerTests.cs new file mode 100644 index 0000000000000..52753bad951cc --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/ReleaseTrackingAnalyzerTests.cs @@ -0,0 +1,1093 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; +using Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class ReleaseTrackingAnalyzerTests + { + [Fact] + public async Task TestNoDeclaredAnalyzersAsync() + { + var source = @""; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [InlineData(@"{|RS2008:""Id1""|}", null, null)] + [InlineData(@"""Id1""", "", null)] + [InlineData(@"""Id1""", null, "")] + [InlineData(@"{|RS2000:""Id1""|}", "", "")] + [Theory] + public async Task TestMissingReleasesFilesAsync(string id, string shippedText, string unshippedText) + { + var source = $@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({id}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task TestCodeFixToEnableAnalyzerReleaseTrackingAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|RS2008:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var test = new CSharpCodeFixVerifier.Test() + { + ReferenceAssemblies = AdditionalMetadataReferences.Default, + NumberOfIncrementalIterations = 2, + NumberOfFixAllIterations = 2, + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { source }, + AdditionalFiles = { + (ReleaseTrackingHelper.ShippedFileName, + AnalyzerReleaseTrackingFix.ShippedAnalyzerReleaseTrackingFileDefaultContent), + (ReleaseTrackingHelper.UnshippedFileName, + AnalyzerReleaseTrackingFix.UnshippedAnalyzerReleaseTrackingFileDefaultContent + DefaultUnshippedHeader + "Id1 | Category1 | Warning | MyAnalyzer") + } + }, + }; + + test.SolutionTransforms.Add(DisableNonReleaseTrackingWarnings); + await test.RunAsync(); + } + + // Unshipped release with existing new rules table. + [InlineData("", DefaultUnshippedHeader + "Id1 | Category1 | Warning |")] + // Shipped release with existing new rules table. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |", "")] + // Releases with separate new rules and changed rules table. + [InlineData(DefaultShippedHeader + "Id1 | Category0 | Warning |" + BlankLine + + DefaultChangedShippedHeader2 + "Id1 | Category1 | Warning | Category0 | Warning |", "")] + // Releases with separate new rules and removed rules table. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + "Id2 | Category1 | Warning |" + BlankLine + + DefaultRemovedShippedHeader2 + "Id2 | Category1 | Warning ", "")] + // Release with new rules and changed rules table. + [InlineData(DefaultShippedHeader + "Id2 | Category0 | Warning |" + BlankLine + + DefaultShippedHeader2 + "Id1 | Category1 | Warning |" + BlankLine + DefaultChangedUnshippedHeader + "Id2 | Category1 | Warning | Category0 | Warning |", + DefaultRemovedUnshippedHeader + "Id2 | Category1 | Warning |")] + // Release with new rules and removed rules table. + [InlineData(DefaultShippedHeader + "Id2 | Category0 | Warning |" + BlankLine + + DefaultShippedHeader2 + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedUnshippedHeader + "Id2 | Category0 | Warning |", "")] + // Release with all 3 tables + [InlineData(DefaultShippedHeader + "Id3 | Category1 | Warning |" + BlankLine + "Id2 | Category0 | Warning |" + BlankLine + + DefaultShippedHeader2 + "Id1 | Category1 | Warning |" + BlankLine + DefaultChangedUnshippedHeader + "Id3 | Category2 | Warning | Category1 | Warning |" + BlankLine + DefaultRemovedUnshippedHeader + "Id2 | Category1 | Warning |", + DefaultRemovedUnshippedHeader + "Id3 | Category2 | Warning |")] + [Theory] + public async Task TestReleasesFileAlreadyHasEntryAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) {{ }} +}"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task TestRemoveUnshippedDeletedDiagnosticIdRuleAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Empty; + public override void Initialize(AnalysisContext context) {{ }} +}"; + var shippedText = @""; + var unshippedText = DefaultUnshippedHeader + "Id1 | Category1 | Warning |"; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.RemoveUnshippedDeletedDiagnosticIdRule).WithArguments("Id1")); + } + + [Fact] + public async Task TestRemoveShippedDeletedDiagnosticIdRuleAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Empty; + public override void Initialize(AnalysisContext context) {{ }} +}"; + var shippedText = DefaultShippedHeader + "Id1 | Category1 | Warning |"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + new DiagnosticResult(DiagnosticDescriptorCreationAnalyzer.RemoveShippedDeletedDiagnosticIdRule).WithArguments("Id1", "1.0")); + } + + [Fact] + public async Task TestCodeFixToAddUnshippedEntriesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|RS2000:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + // Duplicate descriptor with different message. + private static readonly DiagnosticDescriptor descriptor1_dupe = + new DiagnosticDescriptor({|RS2000:""Id1""|}, ""Title1"", ""DifferentMessage"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + // Disabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor({|RS2000:""Id2""|}, ""Title2"", ""Message2"", ""Category2"", DiagnosticSeverity.Warning, isEnabledByDefault: false); + + // Descriptor with help link. + private static readonly DiagnosticDescriptor descriptor3 = + new DiagnosticDescriptor({|RS2000:""Id3""|}, ""Title3"", ""Message3"", ""Category3"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""Dummy""); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1, descriptor1_dupe, descriptor2, descriptor3); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = +$@"{DefaultUnshippedHeader}Id1 | Category1 | Warning | MyAnalyzer +Id2 | Category2 | Disabled | MyAnalyzer +Id3 | Category3 | Warning | MyAnalyzer, [Documentation](Dummy)"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestCodeFixToAddUnshippedEntries_DiagnosticDescriptorHelperAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + DiagnosticDescriptorHelper.Create({|RS2000:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", RuleLevel.BuildWarning); + + // Duplicate descriptor with different message. + private static readonly DiagnosticDescriptor descriptor1_dupe = + DiagnosticDescriptorHelper.Create({|RS2000:""Id1""|}, ""Title1"", ""DifferentMessage"", ""Category1"", RuleLevel.BuildWarning); + + // Disabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor2 = + DiagnosticDescriptorHelper.Create({|RS2000:""Id2""|}, ""Title2"", ""Message2"", ""Category2"", RuleLevel.Disabled); + + // Descriptor with help link. + private static readonly DiagnosticDescriptor descriptor3 = + DiagnosticDescriptorHelper.Create({|RS2000:""Id3""|}, ""Title3"", ""Message3"", ""Category3"", RuleLevel.BuildWarning, helpLinkUri: ""Dummy""); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1, descriptor1_dupe, descriptor2, descriptor3); + public override void Initialize(AnalysisContext context) { } +}" + CSharpDiagnosticDescriptorCreationHelper; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = +$@"{DefaultUnshippedHeader}Id1 | Category1 | Warning | MyAnalyzer +Id2 | Category2 | Disabled | MyAnalyzer +Id3 | Category3 | Warning | MyAnalyzer, [Documentation](Dummy)"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + private const string BlankLine = @" +"; + + // Comments + [InlineData(DefaultUnshippedHeader + @"; Comments are preserved" + BlankLine, + DefaultUnshippedHeader + @"; Comments are preserved" + BlankLine + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine)] + // Blank lines are preserved + [InlineData(DefaultUnshippedHeader + BlankLine, + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine)] + // Mixed + [InlineData(DefaultUnshippedHeader + BlankLine + @"; Comments are preserved" + BlankLine, + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine + @"; Comments are preserved" + BlankLine)] + [Theory] + public async Task TestCodeFixToAddUnshippedEntries_TriviaIsPreservedAsync(string unshippedText, string fixedUnshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|RS2000:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + // Added after current entry. + [InlineData("Id0", DefaultUnshippedHeader + @"Id0 | DifferentCategory | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id0 | DifferentCategory | Warning | MyAnalyzer" + BlankLine + @"Id1 | Category1 | Warning | MyAnalyzer")] + // Added before current entry. + [InlineData("Id2", DefaultUnshippedHeader + @"Id2 | DifferentCategory | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + @"Id2 | DifferentCategory | Warning | MyAnalyzer")] + // Has existing changed rules table. + [InlineData("Id2", DefaultChangedUnshippedHeader + @"Id2 | DifferentCategory | Warning | Category | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine + DefaultChangedUnshippedHeader + @"Id2 | DifferentCategory | Warning | Category | Warning | MyAnalyzer", + DefaultShippedHeader + @"Id2 | Category | Warning | MyAnalyzer")] + // Has existing removed rules table. + [InlineData("Id2", DefaultRemovedUnshippedHeader + @"Id3 | Category | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine + DefaultRemovedUnshippedHeader + @"Id3 | Category | Warning | MyAnalyzer", + DefaultShippedHeader + @"Id2 | DifferentCategory | Warning | MyAnalyzer" + BlankLine + @"Id3 | Category | Warning | MyAnalyzer")] + [Theory] + public async Task TestCodeFixToAddUnshippedEntries_AlreadyHasDifferentUnshippedEntriesAsync(string differentRuleId, string unshippedText, string fixedUnshippedText, string shippedText = "") + { + var source = $@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({{|RS2000:""Id1""|}}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor(""{differentRuleId}"", ""DifferentTitle"", ""DifferentMessage"", ""DifferentCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1, descriptor2); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + // Adds to existing new rules table and creates a new changed rules table. + [InlineData(DefaultUnshippedHeader + @"Id0 | Category0 | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id0 | Category0 | Warning | MyAnalyzer" + BlankLine + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine + DefaultChangedUnshippedHeader + @"Id2 | DifferentCategory | Warning | Category | Warning | MyAnalyzer", + DefaultShippedHeader + @"Id2 | Category | Warning | MyAnalyzer")] + // Adds to existing new rules table and changed rules table. + [InlineData(DefaultChangedUnshippedHeader + @"Id0 | Category0 | Warning | Category | Warning | MyAnalyzer", + DefaultUnshippedHeader + @"Id1 | Category1 | Warning | MyAnalyzer" + BlankLine + BlankLine + DefaultChangedUnshippedHeader + @"Id0 | Category0 | Warning | Category | Warning | MyAnalyzer" + BlankLine + @"Id2 | DifferentCategory | Warning | Category | Warning | MyAnalyzer", + DefaultShippedHeader + @"Id0 | Category | Warning | MyAnalyzer" + BlankLine + @"Id2 | Category | Warning | MyAnalyzer")] + [Theory] + public async Task TestCodeFixToAddUnshippedEntriesToMultipleTablesAsync(string unshippedText, string fixedUnshippedText, string shippedText = "") + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor0 = + new DiagnosticDescriptor(""Id0"", ""Title0"", ""Message0"", ""Category0"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|RS2000:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor({|RS2001:""Id2""|}, ""DifferentTitle"", ""DifferentMessage"", ""DifferentCategory"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor0, descriptor1, descriptor2); + public override void Initialize(AnalysisContext context) { } +}"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [InlineData("", + DefaultUnshippedHeader + "Id1 | Category1 | Warning | MyAnalyzer", + "RS2000")] + [InlineData(DefaultShippedHeader + "Id1 | DifferentCategory | Warning | MyAnalyzer", + DefaultChangedUnshippedHeader + "Id1 | Category1 | Warning | DifferentCategory | Warning | MyAnalyzer", + "RS2001")] + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Disabled | MyAnalyzer", + DefaultChangedUnshippedHeader + "Id1 | Category1 | Warning | Category1 | Disabled | MyAnalyzer", + "RS2001")] + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Info |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Info | MyAnalyzer", + DefaultUnshippedHeader + "Id1 | Category1 | Warning | MyAnalyzer", + "RS2000")] + [Theory] + public async Task TestCodeFixToAddUnshippedEntries_AlreadyHasDifferentShippedEntryAsync(string shippedText, string fixedUnshippedText, string expectedDiagnosticId) + { + var source = $@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({{|{expectedDiagnosticId}:""Id1""|}}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + + var unshippedText = @""; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestCodeFixToUpdateMultipleUnshippedEntriesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + // Enabled by default descriptor. + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({|RS2001:""Id1""|}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + // Disable by default descriptor. + private static readonly DiagnosticDescriptor descriptor2 = + new DiagnosticDescriptor({|RS2001:""Id2""|}, ""Title2"", ""Message2"", ""Category2"", DiagnosticSeverity.Warning, isEnabledByDefault: false); + + // Descriptor with help - ensure that just adding a help link does not require a new analyzer release entry. + private static readonly DiagnosticDescriptor descriptor3 = + new DiagnosticDescriptor(""Id3"", ""Title3"", ""Message3"", ""Category3"", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: ""Dummy""); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1, descriptor2, descriptor3); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + + var unshippedText = +$@"{DefaultUnshippedHeader}Id1 | DifferentCategory | Warning | MyAnalyzer +Id2 | Category2 | Warning | MyAnalyzer +Id3 | Category3 | Warning | MyAnalyzer"; + + var fixedUnshippedText = +$@"{DefaultUnshippedHeader}Id1 | Category1 | Warning | MyAnalyzer +Id2 | Category2 | Disabled | MyAnalyzer +Id3 | Category3 | Warning | MyAnalyzer"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestCodeFixToAddUnshippedEntries_UndetectedFieldsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +public static class DiagnosticDescriptorHelper +{ + public static DiagnosticDescriptor Create( + string id, + LocalizableString title, + LocalizableString messageFormat) + => null; +} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + DiagnosticDescriptorHelper.Create({|RS2000:""Id1""|}, ""Title1"", ""Message1""); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + var unshippedText = @""; + var entry = $@"Id1 | {ReleaseTrackingHelper.UndetectedText} | {ReleaseTrackingHelper.UndetectedText} | MyAnalyzer"; + var fixedUnshippedText = $@"{DefaultUnshippedHeader}{entry}"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText, additionalExpectedDiagnosticsInInput: ImmutableArray.Empty, + additionalExpectedDiagnosticsInResult: ImmutableArray.Create( + GetAdditionalFileResultAt(5, 1, + ReleaseTrackingHelper.UnshippedFileName, + DiagnosticDescriptorCreationAnalyzer.InvalidUndetectedEntryInAnalyzerReleasesFileRule, + ReleaseTrackingHelper.UnshippedFileName, + entry))); + } + + [Fact] + public async Task TestNoCodeFixToAddUnshippedEntries_UndetectedFieldsAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +public static class DiagnosticDescriptorHelper +{ + public static DiagnosticDescriptor Create( + string id, + LocalizableString title, + LocalizableString messageFormat) + => null; +} + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + DiagnosticDescriptorHelper.Create(""Id1"", ""Title1"", ""Message1""); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + var unshippedText = $@"{DefaultUnshippedHeader}Id1 | CustomCategory | Warning |"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + // No header in unshipped + [InlineData("", "Id1 | Category1 | Warning |")] + // No header in shipped + [InlineData("Id1 | Category1 | Warning |", "")] + // Missing TableTitle in unshipped + [InlineData("", ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1 + BlankLine + "Id1 | Category1 | Warning |")] + // Missing TableHeaderLine1 in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleNewRules + BlankLine + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2 + BlankLine + "Id1 | Category1 | Warning |", 2)] + // Missing TableHeaderLine1 with minimum markdown column hyphens in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleNewRules + BlankLine + @"---|---|---|---" + BlankLine + "Id1 | Category1 | Warning |", 2)] + // Missing TableHeaderLine2 in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleNewRules + BlankLine + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1 + BlankLine + "Id1 | Category1 | Warning |", 3)] + // Missing TableHeaderLine2 with extra column header text spaces in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleNewRules + BlankLine + @"Rule ID | Category | Severity | Notes " + BlankLine + "Id1 | Category1 | Warning |", 3)] + // Missing Release Version line in shipped + [InlineData(DefaultUnshippedHeader + "Id1 | Category1 | Warning |", "")] + // Missing Release Version in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + BlankLine + DefaultUnshippedHeader + "Id1 | Category1 | Warning |", "")] + // Invalid Release Version in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + " InvalidVersion" + BlankLine + DefaultUnshippedHeader + "Id1 | Category1 | Warning |", "")] + // Missing TableTitle in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + "1.0" + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine1 + BlankLine + "Id1 | Category1 | Warning |", "", 2)] + // Missing TableHeaderLine1 in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + "1.0" + BlankLine + ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine2 + BlankLine + "Id1 | Category1 | Warning |", "", 3)] + // Missing TableHeaderLine1 with minimum markdown column hyphens in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + "1.0" + BlankLine + ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + @"---|---|---|---|---|---" + BlankLine + "Id1 | Category1 | Warning |", "", 3)] + // Missing TableHeaderLine2 in shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + " 1.0" + BlankLine + ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine1 + BlankLine + "Id1 | Category1 | Warning |", "", 4)] + // Missing TableHeaderLine2 in with extra column header text spaces shipped + [InlineData(ReleaseTrackingHelper.ReleasePrefix + " 1.0" + BlankLine + ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + @"Rule ID | New Category | New Severity | Old Category | Old Severity | Notes" + BlankLine + "Id1 | Category1 | Warning |", "", 4)] + // Invalid Release Version line in unshipped + [InlineData("", DefaultShippedHeader + "Id1 | Category1 | Warning |")] + // Mismatch Table title and TableHeaderLine1 in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleNewRules + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine1 + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine2 + BlankLine + "Id1 | Category1 | Warning |", 2)] + // Mismatch Table title and TableHeaderLine2 in unshipped + [InlineData("", ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + ReleaseTrackingHelper.TableHeaderChangedRulesLine1 + BlankLine + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2 + BlankLine + "Id1 | Category1 | Warning |", 3)] + [Theory] + public async Task TestInvalidHeaderDiagnosticAsync(string shippedText, string unshippedText, int line = 1) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var fileWithDiagnostics = shippedText.Length > 0 ? ReleaseTrackingHelper.ShippedFileName : ReleaseTrackingHelper.UnshippedFileName; + var diagnosticText = (shippedText.Length > 0 ? shippedText : unshippedText).Split(new[] { Environment.NewLine }, StringSplitOptions.None).ElementAt(line - 1); + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(line, 1, + fileWithDiagnostics, + DiagnosticDescriptorCreationAnalyzer.InvalidHeaderInAnalyzerReleasesFileRule, + fileWithDiagnostics, + diagnosticText)); + } + + // Undetected category + [InlineData("Id1 | " + ReleaseTrackingHelper.UndetectedText + " | Warning |", true)] + // Undetected severity + [InlineData("Id1 | Category1 | " + ReleaseTrackingHelper.UndetectedText + " |", true)] + // Undetected category and severity + [InlineData("Id1 | " + ReleaseTrackingHelper.UndetectedText + " | " + ReleaseTrackingHelper.UndetectedText + " |", true)] + // Invalid severity + [InlineData("Id1 | Category1 | Invalid |", false)] + // Missing required fields - category + severity + [InlineData("Id1", false)] + // Missing required field - category + [InlineData("Id1 | Warning ", false)] + // Missing required field - severity + [InlineData("Id1 | Category1 |", false)] + // Extra fields + [InlineData("Id1 | Category1 | Warning | Notes | InvalidField", false)] + [Theory] + public async Task TestInvalidEntryDiagnosticAsync(string entry, bool hasUndetectedField) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + var rule = hasUndetectedField ? + DiagnosticDescriptorCreationAnalyzer.InvalidUndetectedEntryInAnalyzerReleasesFileRule : + DiagnosticDescriptorCreationAnalyzer.InvalidEntryInAnalyzerReleasesFileRule; + + var shippedText = @""; + var unshippedText = DefaultUnshippedHeader + entry; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(5, 1, + ReleaseTrackingHelper.UnshippedFileName, + rule, + ReleaseTrackingHelper.UnshippedFileName, + entry)); + } + + // Undetected category + [InlineData("Id1 | " + ReleaseTrackingHelper.UndetectedText + " | Warning | Category1 | Warning |", true)] + // Undetected old category + [InlineData("Id1 | Category1 | Warning | " + ReleaseTrackingHelper.UndetectedText + " | Warning |", true)] + // Undetected severity + [InlineData("Id1 | Category1 | " + ReleaseTrackingHelper.UndetectedText + " | Category1 | Warning |", true)] + // Undetected old severity + [InlineData("Id1 | Category1 | Warning | Category1 | " + ReleaseTrackingHelper.UndetectedText + " |", true)] + // Undetected category and severity + [InlineData("Id1 | " + ReleaseTrackingHelper.UndetectedText + " | " + ReleaseTrackingHelper.UndetectedText + " | Category1 | Warning |", true)] + // Invalid severity + [InlineData("Id1 | Category1 | Invalid | Category1 | Warning |", false)] + // Invalid old severity + [InlineData("Id1 | Category1 | Warning | Category1 | Invalid |", false)] + // Unchanged old category and old severity + [InlineData("Id1 | Category1 | Warning | Category1 | Warning |", false)] + // Missing required fields - category + severity + old category + old severity + [InlineData("Id1", false)] + // Missing required fields - category + old category + old severity + [InlineData("Id1 | Warning ", false)] + // Missing required fields - old category + old severity + [InlineData("Id1 | Category1 | Warning ", false)] + // Missing required fields - old severity + [InlineData("Id1 | Category1 | Warning | OldCategory ", false)] + // Missing required field - severity + [InlineData("Id1 | Category1 |", false)] + // Extra fields + [InlineData("Id1 | Category1 | Warning | Category2 | Warning | Notes | InvalidField", false)] + [Theory] + public async Task TestInvalidEntryDiagnostic_ChangedRulesAsync(string entry, bool hasUndetectedField) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + var rule = hasUndetectedField ? + DiagnosticDescriptorCreationAnalyzer.InvalidUndetectedEntryInAnalyzerReleasesFileRule : + DiagnosticDescriptorCreationAnalyzer.InvalidEntryInAnalyzerReleasesFileRule; + + var shippedText = @""; + var unshippedText = DefaultChangedUnshippedHeader + entry; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(5, 1, + ReleaseTrackingHelper.UnshippedFileName, + rule, + ReleaseTrackingHelper.UnshippedFileName, + entry)); + } + + // Duplicate entries in shipped. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + "{|RS2005:Id1 | Category1 | Warning ||}", "")] + // Duplicate entries in unshipped. + [InlineData("", DefaultUnshippedHeader + "Id1 | Category1 | Warning |" + BlankLine + "{|RS2005:Id1 | Category1 | Warning ||}")] + // Duplicate changed entries in unshipped. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |", DefaultChangedUnshippedHeader + "Id1 | Category1 | Info | Category1 | Warning |" + BlankLine + DefaultChangedUnshippedHeader + "{|RS2005:Id1 | Category1 | Info | Category1 | Warning ||}")] + // Duplicate entries with changed field in shipped. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + "{|RS2005:Id1 | Category2 | Warning ||}", "")] + // Duplicate entries with changed field in unshipped. + [InlineData("", DefaultUnshippedHeader + "Id1 | Category1 | Warning |" + BlankLine + "{|RS2005:Id1 | Category1 | Info ||}")] + // Duplicate entries with in shipped with first removed entry. + [InlineData(DefaultRemovedShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultUnshippedHeader + "{|RS2005:Id1 | Category2 | Warning ||}", "")] + // Duplicate entries with in shipped with second removed entry. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedUnshippedHeader + "{|RS2005:Id1 | Category2 | Warning ||}", "")] + [Theory] + public async Task TestDuplicateEntryInReleaseDiagnosticAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + // Duplicate entries across shipped and unshipped. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |", DefaultUnshippedHeader + "{|RS2006:Id1 | Category1 | Warning ||}")] + // Duplicate entries across consecutive shipped releases. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultShippedHeader2 + "{|RS2006:Id1 | Category1 | Warning ||}", "")] + // Duplicate entries across non-consecutive shipped releases. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultShippedHeader2 + BlankLine + DefaultShippedHeader3 + "{|RS2006:Id1 | Category1 | Warning ||}", "")] + // Duplicate entries across shipped and unshipped, but with an intermediate entry with changed category - no diagnostic expected. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultShippedHeader2 + "Id1 | Category2 | Warning |", DefaultUnshippedHeader + "Id1 | Category1 | Warning |")] + // Duplicate entries across shipped and unshipped, but with an intermediate entry with changed severity - no diagnostic expected. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultShippedHeader2 + "Id1 | Category1 | Info |", DefaultUnshippedHeader + "Id1 | Category1 | Warning |")] + // Duplicate entries across shipped and unshipped, but with an intermediate entry with removed prefix - no diagnostic expected. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Warning |", DefaultUnshippedHeader + "Id1 | Category1 | Warning |")] + // Duplicate changed entries across consecutive shipped releases. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultChangedShippedHeader2 + "Id1 | Category1 | Warning | Category1 | Info |" + BlankLine + DefaultChangedShippedHeader3 + "{|RS2006:Id1 | Category1 | Warning | Category1 | Info ||}", "")] + [Theory] + public async Task TestDuplicateEntryBetweenReleasesDiagnosticAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + // Remove entry in unshipped for already shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |", DefaultRemovedUnshippedHeader + "Id1 | Category1 | Warning |", "RS2004")] + // Remove entry in shipped for a prior shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Warning |", "", "RS2000")] + // Remove entry with changed severity in shipped for a prior shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Info |", "", "RS2000")] + [Theory] + public async Task TestRemoveEntryInReleaseFile_DiagnosticCasesAsync(string shippedText, string unshippedText, string expectedDiagnosticId) + { + var source = $@" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor({{|{expectedDiagnosticId}:""Id1""|}}, ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) {{ }} +}}"; + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + // Invalid remove entry without prior shipped entry in shipped. + [InlineData(DefaultRemovedShippedHeader + "Id1 | Category1 | Warning |", "")] + // Invalid remove entry without prior shipped entry in unshipped. + [InlineData("", DefaultRemovedUnshippedHeader + "Id1 | Category1 | Warning |")] + [Theory] + public async Task TestInvalidRemoveWithoutShippedEntryInReleaseFileAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Empty; + public override void Initialize(AnalysisContext context) { } +}"; + var fileWithDiagnostics = shippedText.Length > 0 ? ReleaseTrackingHelper.ShippedFileName : ReleaseTrackingHelper.UnshippedFileName; + var lineCount = (shippedText.Length > 0 ? shippedText : unshippedText).Split(new[] { Environment.NewLine }, StringSplitOptions.None).Length; + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(lineCount, 1, + fileWithDiagnostics, + DiagnosticDescriptorCreationAnalyzer.InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule, + fileWithDiagnostics, + "Removed", + "Id1")); + } + + // Invalid changed entry without prior shipped entry in shipped. + [InlineData(DefaultChangedShippedHeader + "Id1 | Category1 | Hidden | Category1 | Warning |", "")] + // Invalid changed entry without prior shipped entry in unshipped. + [InlineData("", DefaultChangedUnshippedHeader + "Id1 | Category1 | Hidden | Category1 | Warning |")] + [Theory] + public async Task TestInvalidChangedWithoutShippedEntryInReleaseFileAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Hidden, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + var fileWithDiagnostics = shippedText.Length > 0 ? ReleaseTrackingHelper.ShippedFileName : ReleaseTrackingHelper.UnshippedFileName; + var lineCount = (shippedText.Length > 0 ? shippedText : unshippedText).Split(new[] { Environment.NewLine }, StringSplitOptions.None).Length; + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(lineCount, 1, + fileWithDiagnostics, + DiagnosticDescriptorCreationAnalyzer.InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule, + fileWithDiagnostics, + "Changed", + "Id1")); + } + + // Invalid remove entry without prior shipped entry in shipped, followed by a shipped entry. + [InlineData(DefaultRemovedShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultShippedHeader2 + "Id1 | Category1 | Warning |", "")] + // Invalid remove entry without prior shipped entry in shipped, followed by an unshipped entry. + [InlineData(DefaultRemovedShippedHeader + "Id1 | Category1 | Warning |", DefaultUnshippedHeader + "Id1 | Category1 | Warning |")] + [Theory] + public async Task TestInvalidRemoveWithoutShippedEntryInReleaseFile_02Async(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new DiagnosticDescriptor(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + var fileWithDiagnostics = shippedText.Length > 0 ? ReleaseTrackingHelper.ShippedFileName : ReleaseTrackingHelper.UnshippedFileName; + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetAdditionalFileResultAt(7, 1, + fileWithDiagnostics, + DiagnosticDescriptorCreationAnalyzer.InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule, + fileWithDiagnostics, + "Removed", + "Id1")); + } + + // Remove entry in unshipped for already shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |", DefaultRemovedUnshippedHeader + "Id1 | Category1 | Warning |")] + // Remove entry in shipped for a prior shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Warning |", "")] + // Remove entry with changed severity in shipped for a prior shipped release. + [InlineData(DefaultShippedHeader + "Id1 | Category1 | Warning |" + BlankLine + DefaultRemovedShippedHeader2 + "Id1 | Category1 | Info |", "")] + [Theory] + public async Task TestRemoveEntryInReleaseFile_NoDiagnosticCasesAsync(string shippedText, string unshippedText) + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Empty; + public override void Initialize(AnalysisContext context) { } +}"; + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(5828, "https://github.com/dotnet/roslyn-analyzers/issues/5828")] + public async Task TestTargetTypedNew() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +class MyAnalyzer : DiagnosticAnalyzer +{ + private static readonly DiagnosticDescriptor descriptor1 = + new(""Id1"", ""Title1"", ""Message1"", ""Category1"", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(descriptor1); + public override void Initialize(AnalysisContext context) { } +}"; + + var shippedText = @""; + var unshippedText = $@"{DefaultUnshippedHeader}Id1 | Category1 | Warning |"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + #region Helpers + + private const string DefaultUnshippedHeader = ReleaseTrackingHelper.TableTitleNewRules + BlankLine + BlankLine + + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1 + BlankLine + + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2 + BlankLine; + + private const string DefaultRemovedUnshippedHeader = ReleaseTrackingHelper.TableTitleRemovedRules + BlankLine + BlankLine + + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine1 + BlankLine + + ReleaseTrackingHelper.TableHeaderNewOrRemovedRulesLine2 + BlankLine; + + private const string DefaultChangedUnshippedHeader = ReleaseTrackingHelper.TableTitleChangedRules + BlankLine + BlankLine + + ReleaseTrackingHelper.TableHeaderChangedRulesLine1 + BlankLine + + ReleaseTrackingHelper.TableHeaderChangedRulesLine2 + BlankLine; + + private const string DefaultShippedHeader = ReleaseTrackingHelper.ReleasePrefix + " 1.0" + BlankLine + BlankLine + DefaultUnshippedHeader; + private const string DefaultRemovedShippedHeader = ReleaseTrackingHelper.ReleasePrefix + " 1.0" + BlankLine + BlankLine + DefaultRemovedUnshippedHeader; + private const string DefaultChangedShippedHeader = ReleaseTrackingHelper.ReleasePrefix + " 1.0" + BlankLine + BlankLine + DefaultChangedUnshippedHeader; + + private const string DefaultShippedHeader2 = ReleaseTrackingHelper.ReleasePrefix + " 2.0" + BlankLine + BlankLine + DefaultUnshippedHeader; + private const string DefaultRemovedShippedHeader2 = ReleaseTrackingHelper.ReleasePrefix + " 2.0" + BlankLine + BlankLine + DefaultRemovedUnshippedHeader; + private const string DefaultChangedShippedHeader2 = ReleaseTrackingHelper.ReleasePrefix + " 2.0" + BlankLine + BlankLine + DefaultChangedUnshippedHeader; + + private const string DefaultShippedHeader3 = ReleaseTrackingHelper.ReleasePrefix + " 3.0" + BlankLine + BlankLine + DefaultUnshippedHeader; + private const string DefaultChangedShippedHeader3 = ReleaseTrackingHelper.ReleasePrefix + " 3.0" + BlankLine + BlankLine + DefaultChangedUnshippedHeader; + + private static DiagnosticResult GetAdditionalFileResultAt(int line, int column, string path, DiagnosticDescriptor descriptor, params object[] arguments) + { +#pragma warning disable RS0030 // Do not use banned APIs + return new DiagnosticResult(descriptor) + .WithLocation(path, line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(arguments); + } + + private static readonly ImmutableDictionary s_nonReleaseTrackingWarningsDisabled = ImmutableDictionary.Empty + .Add(DiagnosticDescriptorCreationAnalyzer.DiagnosticIdMustBeInSpecifiedFormatRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.DoNotUseReservedDiagnosticIdRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.ProvideCustomTagsInDescriptorRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.ProvideHelpUriInDescriptorRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.UseCategoriesFromSpecifiedRangeRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.UseLocalizableStringsInDescriptorRule.Id, ReportDiagnostic.Suppress) + .Add(DiagnosticDescriptorCreationAnalyzer.UseUniqueDiagnosticIdRule.Id, ReportDiagnostic.Suppress); + + private static Solution DisableNonReleaseTrackingWarnings(Solution solution, ProjectId projectId) + { + var compilationOptions = solution.GetProject(projectId)!.CompilationOptions!; + compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(compilationOptions.SpecificDiagnosticOptions.SetItems(s_nonReleaseTrackingWarningsDisabled)); + return solution.WithProjectCompilationOptions(projectId, compilationOptions); + } + + private async Task VerifyCSharpAsync(string source, string shippedText, string unshippedText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + }, + ReferenceAssemblies = AdditionalMetadataReferences.Default, + }; + + if (shippedText != null) + { + test.TestState.AdditionalFiles.Add((ReleaseTrackingHelper.ShippedFileName, shippedText)); + } + + if (unshippedText != null) + { + test.TestState.AdditionalFiles.Add((ReleaseTrackingHelper.UnshippedFileName, unshippedText)); + } + + test.SolutionTransforms.Add(DisableNonReleaseTrackingWarnings); + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private async Task VerifyCSharpAdditionalFileFixAsync(string source, string shippedText, string oldUnshippedText, string newUnshippedText) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedText, oldUnshippedText, newUnshippedText, ImmutableArray.Empty, ImmutableArray.Empty); + } + + private async Task VerifyCSharpAdditionalFileFixAsync(string source, string shippedText, string oldUnshippedText, string newUnshippedText, + ImmutableArray additionalExpectedDiagnosticsInInput, ImmutableArray additionalExpectedDiagnosticsInResult) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedText, oldUnshippedText, newUnshippedText, additionalExpectedDiagnosticsInInput, additionalExpectedDiagnosticsInResult); + } + + private async Task VerifyAdditionalFileFixAsync(string language, string source, string shippedText, string oldUnshippedText, string newUnshippedText, + ImmutableArray additionalExpectedDiagnosticsInInput, ImmutableArray additionalExpectedDiagnosticsInResult) + { + var test = language == LanguageNames.CSharp + ? new CSharpCodeFixTest() + : (CodeFixTest)new VisualBasicCodeFixTest(); + + test.ReferenceAssemblies = AdditionalMetadataReferences.Default; + test.SolutionTransforms.Add(DisableNonReleaseTrackingWarnings); + + test.TestState.Sources.Add(source); + test.TestState.AdditionalFiles.Add((ReleaseTrackingHelper.ShippedFileName, shippedText)); + test.TestState.AdditionalFiles.Add((ReleaseTrackingHelper.UnshippedFileName, oldUnshippedText)); + test.TestState.ExpectedDiagnostics.AddRange(additionalExpectedDiagnosticsInInput); + + test.FixedState.AdditionalFiles.Add((ReleaseTrackingHelper.ShippedFileName, shippedText)); + test.FixedState.AdditionalFiles.Add((ReleaseTrackingHelper.UnshippedFileName, newUnshippedText)); + test.FixedState.ExpectedDiagnostics.AddRange(additionalExpectedDiagnosticsInResult); + + await test.RunAsync(); + } + + private const string CSharpDiagnosticDescriptorCreationHelper = @" +internal static class DiagnosticDescriptorHelper +{ + // Dummy DiagnosticDescriptor creation helper. + public static DiagnosticDescriptor Create( + string id, + LocalizableString title, + LocalizableString messageFormat, + string category, + RuleLevel ruleLevel, + string helpLinkUri = null) + => null; +} + +namespace Microsoft.CodeAnalysis +{ + internal enum RuleLevel + { + BuildWarning = 1, + IdeSuggestion = 2, + IdeHidden_BulkConfigurable = 3, + Disabled = 4, + CandidateForRemoval = 5, + } +}"; + #endregion + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzerTests.cs new file mode 100644 index 0000000000000..7a81cb09a8127 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzerTests.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.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public sealed class SemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzerTests + { + [Theory] + [InlineData("AccessorListSyntax")] + [InlineData("AwaitExpressionSyntax")] + [InlineData("ForStatementSyntax")] + [InlineData("GenericNameSyntax")] + [InlineData("ParameterListSyntax")] + [InlineData("LockStatementSyntax")] + public Task Diagnostic(string type) + { + var code = $@" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +public class Test {{ + public void M(SemanticModel semanticModel, {type} syntax) {{ + var x = {{|#0:semanticModel.GetDeclaredSymbol(syntax)|}}; + }} +}}"; + + return new VerifyCS.Test + { + TestCode = code, + ExpectedDiagnostics = { new DiagnosticResult(CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer.DiagnosticDescriptor).WithLocation(0).WithArguments(type) } + }.RunAsync(); + } + + [Theory] + [InlineData("BaseFieldDeclarationSyntax")] + [InlineData("FieldDeclarationSyntax")] + [InlineData("EventFieldDeclarationSyntax")] + public Task Field_Diagnostic(string type) + { + var code = $@" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +public class Test {{ + public void M(SemanticModel semanticModel, {type} syntax) {{ + var x = {{|#0:semanticModel.GetDeclaredSymbol(syntax)|}}; + }} +}}"; + + return new VerifyCS.Test + { + TestCode = code, + ExpectedDiagnostics = { new DiagnosticResult(CSharpSemanticModelGetDeclaredSymbolAlwaysReturnsNullAnalyzer.FieldDiagnosticDescriptor).WithLocation(0).WithArguments(type) } + }.RunAsync(); + } + + [Theory] + [InlineData("SyntaxNode")] + [InlineData("TypeDeclarationSyntax")] + [InlineData("ClassDeclarationSyntax")] + [InlineData("EnumMemberDeclarationSyntax")] + [InlineData("NamespaceDeclarationSyntax")] + public Task NoDiagnostic(string type) + { + var code = $@" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +public class Test {{ + public void M(SemanticModel semanticModel, {type} syntax) {{ + var x = semanticModel.GetDeclaredSymbol(syntax); + }} +}}"; + + return VerifyCS.VerifyAnalyzerAsync(code); + } + + [Fact] + public Task NoDiagnosticForCompilationError() + { + const string code = @" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +public class Test { + public void M(SemanticModel semanticModel) { + var x = semanticModel.{|CS7036:GetDeclaredSymbol|}(); + } +}"; + + return VerifyCS.VerifyAnalyzerAsync(code); + } + + [Fact, WorkItem(7061, "https://github.com/dotnet/roslyn-analyzers/issues/7061")] + public Task LocalFunctionStatement_NoDiagnostic() + { + const string code = """ + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + public class Test + { + public void M(SemanticModel semanticModel, LocalFunctionStatementSyntax syntax) + { + var x = semanticModel.GetDeclaredSymbol(syntax); + } + } + """; + + return VerifyCS.VerifyAnalyzerAsync(code); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithNoRegisteredActionsRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithNoRegisteredActionsRuleTests.cs new file mode 100644 index 0000000000000..8b1ed69a0742c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithNoRegisteredActionsRuleTests.cs @@ -0,0 +1,539 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class StartActionWithNoRegisteredActionsRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + }); + + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + context.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); + context.RegisterOperationBlockStartAction(AnalyzeOperationBlockStart); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext codeBlockContext) + { + } + + private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext operationBlockContext) + { + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(21, 48, parameterName: "compilationContext", kind: StartActionKind.CompilationStartAction), + GetCSharpExpectedDiagnostic(34, 47, parameterName: "codeBlockContext", kind: StartActionKind.CodeBlockStartAction), + GetCSharpExpectedDiagnostic(38, 52, parameterName: "operationBlockContext", kind: StartActionKind.OperationBlockStartAction) + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + End Sub + ) + + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + context.RegisterCodeBlockStartAction(Of SyntaxKind)(AddressOf AnalyzeCodeBlockStart) + context.RegisterOperationBlockStartAction(AddressOf AnalyzeOperationBlockStart) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(codeBlockContext As CodeBlockStartAnalysisContext(Of SyntaxKind)) + End Sub + + Private Shared Sub AnalyzeOperationBlockStart(operationBlockContext As OperationBlockStartAnalysisContext) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(19, 17, parameterName: "compilationContext", kind: StartActionKind.CompilationStartAction), + GetBasicExpectedDiagnostic(31, 46, parameterName: "codeBlockContext", kind: StartActionKind.CodeBlockStartAction), + GetBasicExpectedDiagnostic(34, 51, parameterName: "operationBlockContext", kind: StartActionKind.OperationBlockStartAction) + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +abstract class MyAnalyzer : DiagnosticAnalyzer + where T : struct +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); + }); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_NoDiagnosticCases_2Async() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +abstract class MyAnalyzer : DiagnosticAnalyzer + where T : struct +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterCodeBlockStartAction(codeBlockContext => + { + AnalyzeCodeBlockStart(codeBlockContext); + }); + }); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_NoDiagnosticCases_OperationAnalyzerRegistrationAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + } +} + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer2 : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterOperationBlockAction(AnalyzeOperationBlock); + } + + private static void AnalyzeOperationBlock(OperationBlockAnalysisContext context) + { + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_NoDiagnosticCases_NestedOperationAnalyzerRegistrationAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterOperationBlockStartAction(operationBlockContext => + { + AnalyzeOperationBlockStart(operationBlockContext); + }); + }); + + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation); + }); + + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterOperationBlockAction(AnalyzeOperationBlock); + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + } + + private static void AnalyzeOperationBlock(OperationBlockAnalysisContext context) + { + } + + private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext context) + { + context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer(Of T As Structure) + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + compilationContext.RegisterCodeBlockStartAction(Of SyntaxKind)(AddressOf AnalyzeCodeBlockStart) + End Sub + ) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of SyntaxKind)) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCases_2Async() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer(Of T As Structure) + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + compilationContext.RegisterCodeBlockStartAction(Of SyntaxKind)( + Sub(codeBlockContext As CodeBlockStartAnalysisContext(Of SyntaxKind)) + AnalyzeCodeBlockStart(codeBlockContext) + End Sub + ) + End Sub + ) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of SyntaxKind)) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCases_OperationAnalyzerRegistrationAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + _ +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterOperationAction(AddressOf AnalyzeOperation, OperationKind.Invocation) + End Sub + + Private Shared Sub AnalyzeOperation(context As OperationAnalysisContext) + End Sub +End Class + + _ +Class MyAnalyzer2 + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterOperationBlockAction(AddressOf AnalyzeOperationBlock) + End Sub + + Private Shared Sub AnalyzeOperationBlock(context As OperationBlockAnalysisContext) + End Sub +End Class +"; + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCases_NestedOperationAnalyzerRegistrationAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + _ +MustInherit Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction(Function(compilationContext) + compilationContext.RegisterOperationBlockStartAction(Function(operationBlockContext) + AnalyzeOperationBlockStart(operationBlockContext) + End Function) + End Function) + + context.RegisterCompilationStartAction(Function(compilationContext) + compilationContext.RegisterOperationAction(AddressOf AnalyzeOperation, OperationKind.Invocation) + End Function) + + context.RegisterCompilationStartAction(Function(compilationContext) + compilationContext.RegisterOperationBlockAction(AddressOf AnalyzeOperationBlock) + End Function) + End Sub + + Private Shared Sub AnalyzeOperation(context As OperationAnalysisContext) + End Sub + + Private Shared Sub AnalyzeOperationBlock(context As OperationBlockAnalysisContext) + End Sub + + Private Shared Sub AnalyzeOperationBlockStart(context As OperationBlockStartAnalysisContext) + context.RegisterOperationAction(AddressOf AnalyzeOperation, OperationKind.Invocation) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string parameterName, StartActionKind kind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CSharpRegisterActionAnalyzer.StartActionWithNoRegisteredActionsRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(GetExpectedArguments(parameterName, kind)); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string parameterName, StartActionKind kind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(BasicRegisterActionAnalyzer.StartActionWithNoRegisteredActionsRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(GetExpectedArguments(parameterName, kind)); + + private static string[] GetExpectedArguments(string parameterName, StartActionKind kind) + { + var arg2 = kind switch + { + StartActionKind.CompilationStartAction => "Initialize", + StartActionKind.CodeBlockStartAction + or StartActionKind.OperationBlockStartAction => "Initialize, CompilationStartAction", + _ => throw new ArgumentException("Unsupported action kind", nameof(kind)), + }; + + return new[] { parameterName, arg2 }; + } + + private enum StartActionKind + { + CompilationStartAction, + CodeBlockStartAction, + OperationBlockStartAction + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithOnlyEndActionRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithOnlyEndActionRuleTests.cs new file mode 100644 index 0000000000000..0fa2b7abedcdd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/StartActionWithOnlyEndActionRuleTests.cs @@ -0,0 +1,388 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class StartActionWithOnlyEndActionRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterCompilationEndAction(null); + }); + + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + context.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); + context.RegisterOperationBlockStartAction(AnalyzeOperationBlockStart); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext codeBlockContext) + { + codeBlockContext.RegisterCodeBlockEndAction(null); + } + + private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext operationBlockContext) + { + operationBlockContext.RegisterOperationBlockEndAction(null); + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(21, 48, parameterName: "compilationContext", kind: StartActionKind.CompilationStartAction), + GetCSharpExpectedDiagnostic(35, 47, parameterName: "codeBlockContext", kind: StartActionKind.CodeBlockStartAction), + GetCSharpExpectedDiagnostic(40, 52, parameterName: "operationBlockContext", kind: StartActionKind.OperationBlockStartAction) + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + compilationContext.RegisterCompilationEndAction(Nothing) + End Sub + ) + + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + context.RegisterCodeBlockStartAction(Of SyntaxKind)(AddressOf AnalyzeCodeBlockStart) + context.RegisterOperationBlockStartAction(AddressOf AnalyzeOperationBlockStart) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(codeBlockContext As CodeBlockStartAnalysisContext(Of SyntaxKind)) + codeBlockContext.RegisterCodeBlockEndAction(Nothing) + End Sub + + Private Shared Sub AnalyzeOperationBlockStart(operationBlockContext As OperationBlockStartAnalysisContext) + operationBlockContext.RegisterOperationBlockEndAction(Nothing) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(19, 17, parameterName: "compilationContext", kind: StartActionKind.CompilationStartAction), + GetBasicExpectedDiagnostic(32, 46, parameterName: "codeBlockContext", kind: StartActionKind.CodeBlockStartAction), + GetBasicExpectedDiagnostic(36, 51, parameterName: "operationBlockContext", kind: StartActionKind.OperationBlockStartAction) + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +abstract class MyAnalyzer : DiagnosticAnalyzer + where T : struct +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterCodeBlockStartAction(AnalyzeCodeBlockStart); + compilationContext.RegisterOperationBlockStartAction(AnalyzeOperationBlockStart); + compilationContext.RegisterCompilationEndAction(null); + }); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + context.RegisterCodeBlockEndAction(null); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + } + + private static void AnalyzeOperationBlockStart(OperationBlockStartAnalysisContext context) + { + context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation); + context.RegisterOperationBlockEndAction(null); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_NoDiagnosticCases_2Async() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +abstract class MyAnalyzer : DiagnosticAnalyzer + where T : struct +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterCompilationStartAction(compilationContext => + { + compilationContext.RegisterCodeBlockStartAction(codeBlockContext => + { + AnalyzeCodeBlockStart(codeBlockContext); + }); + + compilationContext.RegisterCompilationEndAction(null); + }); + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } + + private static void AnalyzeCodeBlockStart(CodeBlockStartAnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression); + context.RegisterCodeBlockEndAction(null); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer(Of T As Structure) + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + compilationContext.RegisterCodeBlockStartAction(Of SyntaxKind)(AddressOf AnalyzeCodeBlockStart) + compilationContext.RegisterOperationBlockStartAction(AddressOf AnalyzeOperationBlockStart) + compilationContext.RegisterCompilationEndAction(Nothing) + End Sub + ) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of SyntaxKind)) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + context.RegisterCodeBlockEndAction(Nothing) + End Sub + + Private Shared Sub AnalyzeOperation(context As OperationAnalysisContext) + End Sub + + Private Shared Sub AnalyzeOperationBlockStart(context As OperationBlockStartAnalysisContext) + context.RegisterOperationAction(AddressOf AnalyzeOperation, OperationKind.Invocation) + context.RegisterOperationBlockEndAction(Nothing) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCases_2Async() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic + + +Class MyAnalyzer(Of T As Structure) + Inherits DiagnosticAnalyzer + + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterCompilationStartAction( + Sub(compilationContext As CompilationStartAnalysisContext) + compilationContext.RegisterCodeBlockStartAction(Of SyntaxKind)( + Sub(codeBlockContext As CodeBlockStartAnalysisContext(Of SyntaxKind)) + AnalyzeCodeBlockStart(codeBlockContext) + End Sub + ) + + compilationContext.RegisterCompilationEndAction(Nothing) + End Sub + ) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub + + Private Shared Sub AnalyzeCodeBlockStart(context As CodeBlockStartAnalysisContext(Of SyntaxKind)) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeSyntax, SyntaxKind.InvocationExpression) + context.RegisterCodeBlockEndAction(Nothing) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, string parameterName, StartActionKind kind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CSharpRegisterActionAnalyzer.StartActionWithOnlyEndActionRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(GetExpectedArguments(parameterName, kind)); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, string parameterName, StartActionKind kind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(BasicRegisterActionAnalyzer.StartActionWithOnlyEndActionRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(GetExpectedArguments(parameterName, kind)); + + private static string[] GetExpectedArguments(string parameterName, StartActionKind kind) + { + string endActionName; + string statelessActionName; + string arg4; + switch (kind) + { + case StartActionKind.CompilationStartAction: + endActionName = "CompilationEndAction"; + statelessActionName = "RegisterCompilationAction"; + arg4 = "Initialize"; + break; + + case StartActionKind.CodeBlockStartAction: + endActionName = "CodeBlockEndAction"; + statelessActionName = "RegisterCodeBlockAction"; + arg4 = "Initialize, CompilationStartAction"; + break; + + case StartActionKind.OperationBlockStartAction: + endActionName = "OperationBlockEndAction"; + statelessActionName = "RegisterOperationBlockAction"; + arg4 = "Initialize, CompilationStartAction"; + break; + + default: + throw new ArgumentException("Unsupported argument kind", nameof(kind)); + } + + return new[] { parameterName, endActionName, statelessActionName, arg4 }; + } + + private enum StartActionKind + { + CompilationStartAction, + CodeBlockStartAction, + OperationBlockStartAction + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SymbolIsBannedInAnalyzersTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SymbolIsBannedInAnalyzersTests.cs new file mode 100644 index 0000000000000..d3790578a0b54 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/SymbolIsBannedInAnalyzersTests.cs @@ -0,0 +1,311 @@ +// 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 enable + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.CSharpSymbolIsBannedInAnalyzersAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.BasicSymbolIsBannedInAnalyzersAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests +{ + public class SymbolIsBannedInAnalyzersTests + { + [Fact] + public async Task UseBannedApi_EnforcementEnabled_CSharp() + { + await new VerifyCS.Test + { + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9, + TestCode = @" +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer +{ +} + +class C +{ + void M() + { + _ = File.Exists(""something""); + } +} +", + ExpectedDiagnostics = { + // /0/Test0.cs(15,13): error RS1035: The symbol 'File' is banned for use by analyzers: Do not do file IO in analyzers + VerifyCS.Diagnostic("RS1035").WithSpan(15, 13, 15, 37).WithArguments("File", ": Do not do file IO in analyzers"), + }, + TestState = { + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +build_property.EnforceExtendedAnalyzerRules = true +"), }, + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementEnabled_Generator_CSharp() + { + await new VerifyCS.Test + { + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9, + TestCode = @" +using System.IO; +using Microsoft.CodeAnalysis; + +namespace Microsoft.CodeAnalysis +{ + public class GeneratorAttribute : System.Attribute { } +} + +[Generator] +class MyAnalyzer +{ +} + +class C +{ + void M() + { + _ = File.Exists(""something""); + } +} +", + ExpectedDiagnostics = { + // /0/Test0.cs(19,13): error RS1035: The symbol 'File' is banned for use by analyzers: Do not do file IO in analyzers + VerifyCS.Diagnostic("RS1035").WithSpan(19, 13, 19, 37).WithArguments("File", ": Do not do file IO in analyzers"), + }, + TestState = { + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +build_property.EnforceExtendedAnalyzerRules = true +"), }, + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementNotSpecified_CSharp() + { + await new VerifyCS.Test + { + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9, + TestCode = @" +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer +{ +} + +class C +{ + void M() + { + _ = File.Exists(""something""); + } +} +", + ExpectedDiagnostics = { + // /0/Test0.cs(7,7): warning RS1036: 'MyAnalyzer': A project containing analyzers or source generators should specify the property 'true' + VerifyCS.Diagnostic("RS1036").WithSpan(7, 7, 7, 17).WithArguments("MyAnalyzer"), + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementDisabled_CSharp() + { + await new VerifyCS.Test + { + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9, + TestCode = @" +using System.IO; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer +{ +} + +class C +{ + void M() + { + _ = File.Exists(""something""); + } +} +", + TestState = { + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +build_property.EnforceExtendedAnalyzerRules = false +"), + }, + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementEnabled_Basic() + { + await new VerifyVB.Test + { + LanguageVersion = Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Latest, + TestCode = @" +Imports System.IO +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyDiagnosticAnalyzer +End Class + +Class C + Function M() + File.Exists(""something"") + End Function +End Class +", + ExpectedDiagnostics = + { + // /0/Test0.vb(12,9,12,33): error RS1035: The symbol 'File' is banned for use by analyzers: Do not do file IO in analyzers + VerifyVB.Diagnostic("RS1035").WithSpan(12, 9, 12, 33).WithArguments("File", ": Do not do file IO in analyzers"), + }, + TestState = { + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +build_property.EnforceExtendedAnalyzerRules = true +"), + }, + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementNotSpecified_Basic() + { + await new VerifyVB.Test + { + LanguageVersion = Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Latest, + TestCode = @" +Imports System.IO +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyDiagnosticAnalyzer +End Class + +Class C + Function M() + File.Exists(""something"") + End Function +End Class +", + ExpectedDiagnostics = + { + // /0/Test0.vb(7,7): warning RS1036: 'MyDiagnosticAnalyzer': A project containing analyzers or source generators should specify the property 'true' + VerifyVB.Diagnostic("RS1036").WithSpan(7, 7, 7, 27).WithArguments("MyDiagnosticAnalyzer"), + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_EnforcementDisabled_Basic() + { + await new VerifyVB.Test + { + LanguageVersion = Microsoft.CodeAnalysis.VisualBasic.LanguageVersion.Latest, + TestCode = @" +Imports System.IO +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyDiagnosticAnalyzer +End Class + +Class C + Function M() + File.Exists(""something"") + End Function +End Class +", + TestState = { + AnalyzerConfigFiles = { ("/.editorconfig", $@"root = true + +[*] +build_property.EnforceExtendedAnalyzerRules = false +"), + }, + } + }.RunAsync(); + } + + [Fact] + public async Task UseBannedApi_ISourceGenerator() + { + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.Default.WithPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.CodeAnalysis.Common", "4.5.0"))), + TestCode = """ + using Microsoft.CodeAnalysis; + + [Generator] + class MyGenerator : ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) + { + {|#0:context.RegisterForPostInitialization(_ => { })|}; + } + public void Execute(GeneratorExecutionContext context) + { + {|#1:context.AddSource("Generated.cs", "// ")|}; + } + } + """, + TestState = + { + AnalyzerConfigFiles = + { + ("/.editorconfig", """ + root = true + + [*] + build_property.EnforceExtendedAnalyzerRules = true + """), + }, + }, + ExpectedDiagnostics = + { + // /0/Test0.cs(8,9): error RS1035: The symbol 'GeneratorInitializationContext' is banned for use by analyzers: Non-incremental source generators should not be used, implement IIncrementalGenerator instead + VerifyCS.Diagnostic("RS1035").WithLocation(0).WithArguments("GeneratorInitializationContext", ": Non-incremental source generators should not be used, implement IIncrementalGenerator instead"), + // /0/Test0.cs(14,9): error RS1035: The symbol 'GeneratorExecutionContext' is banned for use by analyzers: Non-incremental source generators should not be used, implement IIncrementalGenerator instead + VerifyCS.Diagnostic("RS1035").WithLocation(1).WithArguments("GeneratorExecutionContext", ": Non-incremental source generators should not be used, implement IIncrementalGenerator instead"), + } + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/UnsupportedSymbolKindArgumentRuleTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/UnsupportedSymbolKindArgumentRuleTests.cs new file mode 100644 index 0000000000000..dc435024867e7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/MetaAnalyzers/UnsupportedSymbolKindArgumentRuleTests.cs @@ -0,0 +1,268 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.MetaAnalyzers.CSharpRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.BasicRegisterActionAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests.MetaAnalyzers +{ + public class UnsupportedSymbolKindArgumentRuleTests + { + [Fact] + public async Task CSharp_VerifyDiagnosticAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + context.RegisterSymbolAction(AnalyzeSymbol, + SymbolKind.Alias, + SymbolKind.ArrayType, + SymbolKind.Assembly, + SymbolKind.Discard, + SymbolKind.DynamicType, + SymbolKind.ErrorType, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Label, + SymbolKind.Local, + SymbolKind.Method, + SymbolKind.NetModule, + SymbolKind.NamedType, + SymbolKind.Namespace, + SymbolKind.Parameter, + SymbolKind.PointerType, + SymbolKind.Property, + SymbolKind.Preprocessing, + SymbolKind.RangeVariable, + SymbolKind.TypeParameter); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + } +}"; + DiagnosticResult[] expected = new[] + { + GetCSharpExpectedDiagnostic(21, 13, unsupportedSymbolKind: SymbolKind.Alias), + GetCSharpExpectedDiagnostic(22, 13, unsupportedSymbolKind: SymbolKind.ArrayType), + GetCSharpExpectedDiagnostic(23, 13, unsupportedSymbolKind: SymbolKind.Assembly), + GetCSharpExpectedDiagnostic(24, 13, unsupportedSymbolKind: SymbolKind.Discard), + GetCSharpExpectedDiagnostic(25, 13, unsupportedSymbolKind: SymbolKind.DynamicType), + GetCSharpExpectedDiagnostic(26, 13, unsupportedSymbolKind: SymbolKind.ErrorType), + GetCSharpExpectedDiagnostic(29, 13, unsupportedSymbolKind: SymbolKind.Label), + GetCSharpExpectedDiagnostic(30, 13, unsupportedSymbolKind: SymbolKind.Local), + GetCSharpExpectedDiagnostic(32, 13, unsupportedSymbolKind: SymbolKind.NetModule), + GetCSharpExpectedDiagnostic(36, 13, unsupportedSymbolKind: SymbolKind.PointerType), + GetCSharpExpectedDiagnostic(38, 13, unsupportedSymbolKind: SymbolKind.Preprocessing), + GetCSharpExpectedDiagnostic(39, 13, unsupportedSymbolKind: SymbolKind.RangeVariable), + GetCSharpExpectedDiagnostic(40, 13, unsupportedSymbolKind: SymbolKind.TypeParameter), + }; + + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyRegisterSymbolActionDiagnosticAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.RegisterSymbolAction(AddressOf AnalyzeSymbol, + SymbolKind.Alias, + SymbolKind.ArrayType, + SymbolKind.Assembly, + SymbolKind.Discard, + SymbolKind.DynamicType, + SymbolKind.ErrorType, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Label, + SymbolKind.Local, + SymbolKind.Method, + SymbolKind.NetModule, + SymbolKind.NamedType, + SymbolKind.Namespace, + SymbolKind.Parameter, + SymbolKind.PointerType, + SymbolKind.Property, + SymbolKind.Preprocessing, + SymbolKind.RangeVariable, + SymbolKind.TypeParameter) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + End Sub +End Class +"; + DiagnosticResult[] expected = new[] + { + GetBasicExpectedDiagnostic(18, 13, unsupportedSymbolKind: SymbolKind.Alias), + GetBasicExpectedDiagnostic(19, 13, unsupportedSymbolKind: SymbolKind.ArrayType), + GetBasicExpectedDiagnostic(20, 13, unsupportedSymbolKind: SymbolKind.Assembly), + GetBasicExpectedDiagnostic(21, 13, unsupportedSymbolKind: SymbolKind.Discard), + GetBasicExpectedDiagnostic(22, 13, unsupportedSymbolKind: SymbolKind.DynamicType), + GetBasicExpectedDiagnostic(23, 13, unsupportedSymbolKind: SymbolKind.ErrorType), + GetBasicExpectedDiagnostic(26, 13, unsupportedSymbolKind: SymbolKind.Label), + GetBasicExpectedDiagnostic(27, 13, unsupportedSymbolKind: SymbolKind.Local), + GetBasicExpectedDiagnostic(29, 13, unsupportedSymbolKind: SymbolKind.NetModule), + GetBasicExpectedDiagnostic(33, 13, unsupportedSymbolKind: SymbolKind.PointerType), + GetBasicExpectedDiagnostic(35, 13, unsupportedSymbolKind: SymbolKind.Preprocessing), + GetBasicExpectedDiagnostic(36, 13, unsupportedSymbolKind: SymbolKind.RangeVariable), + GetBasicExpectedDiagnostic(37, 13, unsupportedSymbolKind: SymbolKind.TypeParameter), + }; + + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_NoDiagnosticCasesAsync() + { + var source = @" +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +class MyAnalyzer : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics + { + get + { + throw new NotImplementedException(); + } + } + + public override void Initialize(AnalysisContext context) + { + // Valid symbol kinds. + context.RegisterSymbolAction(AnalyzeSymbol, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Method, + SymbolKind.NamedType, + SymbolKind.Namespace, + SymbolKind.Property); + + // Overload resolution failure + context.RegisterSymbolAction({|CS1503:AnalyzeSyntax|}, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Method, + SymbolKind.NamedType, + SymbolKind.Namespace, + SymbolKind.Property); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context) + { + } + + private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_NoDiagnosticCasesAsync() + { + var source = @" +Imports System +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics + + +Class MyAnalyzer + Inherits DiagnosticAnalyzer + Public Overrides ReadOnly Property SupportedDiagnostics() As ImmutableArray(Of DiagnosticDescriptor) + Get + Throw New NotImplementedException() + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + + ' Valid symbol kinds + context.RegisterSymbolAction(AddressOf AnalyzeSymbol, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Method, + SymbolKind.NamedType, + SymbolKind.Namespace, + SymbolKind.Property) + + ' Overload resolution failure + context.{|BC30518:RegisterSymbolAction|}(AddressOf AnalyzeSyntax, + SymbolKind.Alias) + End Sub + + Private Shared Sub AnalyzeSymbol(context As SymbolAnalysisContext) + End Sub + + Private Shared Sub AnalyzeSyntax(context As SyntaxNodeAnalysisContext) + End Sub +End Class +"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int line, int column, SymbolKind unsupportedSymbolKind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CSharpRegisterActionAnalyzer.UnsupportedSymbolKindArgumentRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(unsupportedSymbolKind); + + private static DiagnosticResult GetBasicExpectedDiagnostic(int line, int column, SymbolKind unsupportedSymbolKind) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(BasicRegisterActionAnalyzer.UnsupportedSymbolKindArgumentRule) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(unsupportedSymbolKind); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Microsoft.CodeAnalysis.Analyzers.UnitTests.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Microsoft.CodeAnalysis.Analyzers.UnitTests.csproj new file mode 100644 index 0000000000000..90a2c99ec1d86 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Microsoft.CodeAnalysis.Analyzers.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + $(NetRoslyn) + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Properties/launchSettings.json b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000000..4e3b720d74ec4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "TestSetup": { + "commandName": "Executable", + "executablePath": "$(NuGetPackageRoot)\\xunit.runner.console\\$(XUnitVersion)\\tools\\xunit.console.x86.exe", + "commandLineArgs": "$(AssemblyName).dll -noshadow -wait", + "workingDirectory": "$(OutDir)" + } + } +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UpgradeMSBuildWorkspaceAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UpgradeMSBuildWorkspaceAnalyzerTests.cs new file mode 100644 index 0000000000000..7371f32772647 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UpgradeMSBuildWorkspaceAnalyzerTests.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.Analyzers.CSharpUpgradeMSBuildWorkspaceAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.Analyzers.VisualBasicUpgradeMSBuildWorkspaceAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests +{ + public class UpgradeMSBuildWorkspaceAnalyzerTests + { + private static readonly ReferenceAssemblies s_withDesktopWorkspaces = ReferenceAssemblies.NetFramework.Net46.Default.AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.CodeAnalysis.Workspaces.Common", "2.9.0"))); + + private static readonly ReferenceAssemblies s_withMSBuildWorkspaces = ReferenceAssemblies.NetFramework.Net461.Default.AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis.Workspaces.MSBuild", "2.9.0"), + new PackageIdentity("Microsoft.CodeAnalysis.CSharp.Workspaces", "2.9.0"), + new PackageIdentity("Microsoft.Build.Locator", "1.0.18"))); + + private static async Task VerifyCSharpAsync(string source, ReferenceAssemblies referenceAssemblies) + { + await new VerifyCS.Test() + { + TestCode = source, + FixedCode = source, + ReferenceAssemblies = referenceAssemblies, + }.RunAsync(); + } + + private static async Task VerifyVisualBasicAsync(string source, ReferenceAssemblies referenceAssemblies) + { + await new VerifyVB.Test() + { + TestCode = source, + FixedCode = source, + ReferenceAssemblies = referenceAssemblies, + }.RunAsync(); + } + + [Fact] + public async Task CSharp_VerifyWithMSBuildWorkspaceAsync() + { + const string source = @" +using Microsoft.CodeAnalysis.MSBuild; + +class Usage +{ + void M() + { + var workspace = MSBuildWorkspace.Create(); + } +}"; + + await VerifyCSharpAsync(source, s_withMSBuildWorkspaces); + } + + [Fact] + public async Task CSharp_VerifyWithoutMSBuildWorkspaceAsync() + { + const string source = @" +using Microsoft.CodeAnalysis.{|CS0234:MSBuild|}; + +class Usage +{ + void M() + { + var workspace = [|{|CS0103:MSBuildWorkspace|}|].Create(); + } +}"; + await VerifyCSharpAsync(source, s_withDesktopWorkspaces); + } + + [Fact] + public async Task VisualBasic_VerifyWithMSBuildWorkspaceAsync() + { + const string source = @" +Imports Microsoft.CodeAnalysis.MSBuild + +Class Usage + Sub M() + Dim workspace = MSBuildWorkspace.Create() + End Sub +End Class"; + await VerifyVisualBasicAsync(source, s_withMSBuildWorkspaces); + } + + [Fact] + public async Task VisualBasic_VerifyWithoutMSBuildWorkspaceAsync() + { + const string source = @" +Imports Microsoft.CodeAnalysis.MSBuild + +Class Usage + Sub M() + Dim workspace = [|{|BC30451:MSBuildWorkspace|}|].Create() + End Sub +End Class"; + await VerifyVisualBasicAsync(source, s_withDesktopWorkspaces); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs new file mode 100644 index 0000000000000..b9a1c038d45df --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/UnitTests/UseReturnValueFromImmutableObjectMethodTests.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.ImmutableObjectMethodAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.Analyzers.ImmutableObjectMethodAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.Analyzers.UnitTests +{ + public class UseReturnValueFromImmutableObjectMethodTests + { + [Fact] + public async Task CSharpVerifyDiagnosticsAsync() + { + var source = @" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +class TestSimple +{ + void M() + { + Document document = default(Document); + {|#0:document.WithText(default(SourceText))|}; + + Project project = default(Project); + {|#1:project.AddDocument(""Sample.cs"", default(SourceText))|}; + + Solution solution = default(Solution); + {|#2:solution.AddProject(""Sample"", ""Sample"", ""CSharp"")|}; + + Compilation compilation = default(Compilation); + {|#3:compilation.RemoveAllSyntaxTrees()|}; + } +} +"; + DiagnosticResult documentExpected = GetCSharpExpectedDiagnostic(0, "Document", "WithText"); + DiagnosticResult projectExpected = GetCSharpExpectedDiagnostic(1, "Project", "AddDocument"); + DiagnosticResult solutionExpected = GetCSharpExpectedDiagnostic(2, "Solution", "AddProject"); + DiagnosticResult compilationExpected = GetCSharpExpectedDiagnostic(3, "Compilation", "RemoveAllSyntaxTrees"); + + await VerifyCS.VerifyAnalyzerAsync(source, documentExpected, projectExpected, solutionExpected, compilationExpected); + } + + [Fact] + public async Task VisualBasicVerifyDiagnosticsAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Class TestSimple + Sub M() + Dim document As Document = Nothing + {|#0:document.WithText(Nothing)|} + + Dim project As Project = Nothing + {|#1:project.AddDocument(""Sample.cs"", CType(Nothing, SourceText))|} + + Dim solution As Solution = Nothing + {|#2:solution.AddProject(""Sample"", ""Sample"", ""CSharp"")|} + + Dim compilation As Compilation = Nothing + {|#3:compilation.RemoveAllSyntaxTrees()|} + End Sub +End Class +"; + DiagnosticResult documentExpected = GetVisualBasicExpectedDiagnostic(0, "Document", "WithText"); + DiagnosticResult projectExpected = GetVisualBasicExpectedDiagnostic(1, "Project", "AddDocument"); + DiagnosticResult solutionExpected = GetVisualBasicExpectedDiagnostic(2, "Solution", "AddProject"); + DiagnosticResult compilationExpected = GetVisualBasicExpectedDiagnostic(3, "Compilation", "RemoveAllSyntaxTrees"); + + await VerifyVB.VerifyAnalyzerAsync(source, documentExpected, projectExpected, solutionExpected, compilationExpected); + } + + [Fact] + public async Task CSharp_VerifyDiagnosticOnExtensionMethodAsync() + { + var source = @" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +class TestExtensionMethodTrivia +{ + void M() + { + SyntaxNode node = default(SyntaxNode); + {|#0:node.WithLeadingTrivia()|}; + } +}"; + DiagnosticResult expected = GetCSharpExpectedDiagnostic(0, "SyntaxNode", "WithLeadingTrivia"); + await VerifyCS.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task VisualBasic_VerifyDiagnosticOnExtensionMethodAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Class TestExtensionMethodTrivia + Sub M() + Dim node As SyntaxNode = Nothing + {|#0:node.WithLeadingTrivia()|} + End Sub +End Class"; + DiagnosticResult expected = GetVisualBasicExpectedDiagnostic(0, "SyntaxNode", "WithLeadingTrivia"); + await VerifyVB.VerifyAnalyzerAsync(source, expected); + } + + [Fact] + public async Task CSharp_VerifyNoDiagnosticAsync() + { + var source = @" +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace ConsoleApplication1 +{ + class TestNoDiagnostic + { + public Document M() + { + Document document = default(Document); + var newDocument = document.WithText(default(SourceText)); + document = document.WithText(default(SourceText)); + + OtherMethod(document.WithText(default(SourceText))); + return document.WithText(default(SourceText)); + } + + public void OtherMethod(Document document) + { + } + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task VisualBasic_VerifyNoDiagnosticAsync() + { + var source = @" +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text + +Namespace ConsoleApplication1 + Class TestNoDiagnostic + Public Function M() As Document + Dim document As Document = Nothing + Dim newDocument = document.WithText(Nothing) + document = document.WithText(Nothing) + + OtherMethod(document.WithText(Nothing)) + Return document.WithText(Nothing) + End Function + + Public Sub OtherMethod(document As Document) + End Sub + End Class +End Namespace"; + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task CSharp_ReturnsVoid() + { + var source = @" +namespace Microsoft.CodeAnalysis +{ + public class Compilation + { + internal void AddSomething() + { + } + + internal void M() => AddSomething(); + } +}"; + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task VisualBasic_ReturnsVoid() + { + var source = @" +Namespace Microsoft.CodeAnalysis + Public Class Compilation + Friend Sub AddSomething() + End Sub + + Friend Sub M() + AddSomething() + End Sub + End Class +End Namespace"; + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + private static DiagnosticResult GetCSharpExpectedDiagnostic(int markupKey, string objectName, string methodName) => + VerifyCS.Diagnostic().WithLocation(markupKey).WithArguments(objectName, methodName); + + private static DiagnosticResult GetVisualBasicExpectedDiagnostic(int markupKey, string objectName, string methodName) => + VerifyVB.Diagnostic().WithLocation(markupKey).WithArguments(objectName, methodName); + } +} diff --git a/src/Tools/ExternalAccess/Razor/PublicAPI.Shipped.txt b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md similarity index 100% rename from src/Tools/ExternalAccess/Razor/PublicAPI.Shipped.txt rename to src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb new file mode 100644 index 0000000000000..10e6384925e7c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/BasicSymbolIsBannedInAnalyzersAnalyzer.vb @@ -0,0 +1,48 @@ +' 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. + +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.Analyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers + + Public Class BasicSymbolIsBannedInAnalyzersAnalyzer + Inherits SymbolIsBannedInAnalyzersAnalyzer(Of SyntaxKind) + + Protected Overrides ReadOnly Property XmlCrefSyntaxKind As SyntaxKind + Get + Return SyntaxKind.XmlCrefAttribute + End Get + End Property + + Protected Overrides ReadOnly Property BaseTypeSyntaxKinds As ImmutableArray(Of SyntaxKind) + Get + Return ImmutableArray.Create(SyntaxKind.InheritsStatement, SyntaxKind.ImplementsStatement) + End Get + End Property + + Protected Overrides ReadOnly Property SymbolDisplayFormat As SymbolDisplayFormat + Get + Return SymbolDisplayFormat.VisualBasicShortErrorMessageFormat + End Get + End Property + + Protected Overrides Function GetReferenceSyntaxNodeFromXmlCref(syntaxNode As SyntaxNode) As SyntaxNode + Return CType(syntaxNode, XmlCrefAttributeSyntax).Reference + End Function + + Protected Overrides Function GetTypeSyntaxNodesFromBaseType(syntaxNode As SyntaxNode) As IEnumerable(Of SyntaxNode) + If syntaxNode.IsKind(SyntaxKind.InheritsStatement) Then + Return CType(syntaxNode, InheritsStatementSyntax).Types + ElseIf syntaxNode.IsKind(SyntaxKind.ImplementsStatement) Then + Return CType(syntaxNode, ImplementsStatementSyntax).Types + Else + Return ImmutableArray(Of SyntaxNode).Empty + End If + End Function + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerAPIUsageAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerAPIUsageAnalyzer.vb new file mode 100644 index 0000000000000..62afdf4e49a3a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerAPIUsageAnalyzer.vb @@ -0,0 +1,25 @@ +' 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. + +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers + + Public Class BasicDiagnosticAnalyzerApiUsageAnalyzer + Inherits DiagnosticAnalyzerApiUsageAnalyzer(Of TypeSyntax) + + Protected Overrides Function IsNamedTypeDeclarationBlock(syntax As SyntaxNode) As Boolean + Select Case syntax.Kind() + Case SyntaxKind.ModuleBlock, SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.EnumBlock, SyntaxKind.InterfaceBlock + Return True + + Case Else + Return False + End Select + End Function + End Class +End Namespace + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerFieldsAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerFieldsAnalyzer.vb new file mode 100644 index 0000000000000..e20d2f148d844 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicDiagnosticAnalyzerFieldsAnalyzer.vb @@ -0,0 +1,15 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers + + Public Class BasicDiagnosticAnalyzerFieldsAnalyzer + Inherits DiagnosticAnalyzerFieldsAnalyzer(Of ClassBlockSyntax, StructureBlockSyntax, FieldDeclarationSyntax, TypeSyntax, SimpleAsClauseSyntax, TypeArgumentListSyntax, GenericNameSyntax) + End Class +End Namespace + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicRegisterActionAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicRegisterActionAnalyzer.vb new file mode 100644 index 0000000000000..f2cdcc6986a90 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicRegisterActionAnalyzer.vb @@ -0,0 +1,93 @@ +' 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. + +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers + + Public Class BasicRegisterActionAnalyzer + Inherits RegisterActionAnalyzer(Of InvocationExpressionSyntax, ArgumentSyntax, SyntaxKind) + + Private Const BasicSyntaxKindFullName As String = "Microsoft.CodeAnalysis.VisualBasic.SyntaxKind" + Private Const CSharpSyntaxKindFullName As String = "Microsoft.CodeAnalysis.CSharp.SyntaxKind" + + Protected Overrides Function GetCodeBlockAnalyzer(compilation As Compilation, + analysisContext As INamedTypeSymbol, + compilationStartAnalysisContext As INamedTypeSymbol, + codeBlockStartAnalysisContext As INamedTypeSymbol, + operationBlockStartAnalysisContext As INamedTypeSymbol, + symbolKind As INamedTypeSymbol) As RegisterActionCodeBlockAnalyzer + Dim basicSyntaxKind = compilation.GetOrCreateTypeByMetadataName(BasicSyntaxKindFullName) + Dim csharpSyntaxKind = compilation.GetOrCreateTypeByMetadataName(CSharpSyntaxKindFullName) + Return New BasicRegisterActionCodeBlockAnalyzer(basicSyntaxKind, csharpSyntaxKind, analysisContext, compilationStartAnalysisContext, codeBlockStartAnalysisContext, + operationBlockStartAnalysisContext, symbolKind) + End Function + + Private NotInheritable Class BasicRegisterActionCodeBlockAnalyzer + Inherits RegisterActionCodeBlockAnalyzer + + Private ReadOnly _csharpSyntaxKind As ITypeSymbol + Private ReadOnly _basicSyntaxKind As ITypeSymbol + + Public Sub New(basicSyntaxKind As INamedTypeSymbol, + csharpSyntaxKind As INamedTypeSymbol, + analysisContext As INamedTypeSymbol, + compilationStartAnalysisContext As INamedTypeSymbol, + codeBlockStartAnalysisContext As INamedTypeSymbol, + operationBlockStartAnalysisContext As INamedTypeSymbol, + symbolKind As INamedTypeSymbol) + MyBase.New(analysisContext, compilationStartAnalysisContext, codeBlockStartAnalysisContext, operationBlockStartAnalysisContext, symbolKind) + + Me._basicSyntaxKind = basicSyntaxKind + Me._csharpSyntaxKind = csharpSyntaxKind + End Sub + + Protected Overrides ReadOnly Property InvocationExpressionKind As SyntaxKind + Get + Return SyntaxKind.InvocationExpression + End Get + End Property + + Protected Overrides ReadOnly Property ArgumentSyntaxKind As SyntaxKind + Get + Return SyntaxKind.SimpleArgument + End Get + End Property + + Protected Overrides ReadOnly Property ParameterSyntaxKind As SyntaxKind + Get + Return SyntaxKind.Parameter + End Get + End Property + + Protected Overrides Function GetArgumentExpressions(invocation As InvocationExpressionSyntax) As IEnumerable(Of SyntaxNode) + If invocation.ArgumentList IsNot Nothing Then + Return invocation.ArgumentList.Arguments.Select(Function(a) a.GetExpression) + End If + + Return Nothing + End Function + + Protected Overrides Function GetArgumentExpression(argument As ArgumentSyntax) As SyntaxNode + Return argument.GetExpression + End Function + + Protected Overrides Function GetInvocationExpression(invocation As InvocationExpressionSyntax) As SyntaxNode + Return invocation.Expression + End Function + + Protected Overrides Function GetInvocationReceiver(invocation As InvocationExpressionSyntax) As SyntaxNode + Return TryCast(invocation.Expression, MemberAccessExpressionSyntax)?.Expression + End Function + + Protected Overrides Function IsSyntaxKind(type As ITypeSymbol) As Boolean + Return (Me._basicSyntaxKind IsNot Nothing AndAlso type.Equals(Me._basicSyntaxKind)) OrElse + (Me._csharpSyntaxKind IsNot Nothing AndAlso type.Equals(Me._csharpSyntaxKind)) + End Function + End Class + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicReportDiagnosticAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicReportDiagnosticAnalyzer.vb new file mode 100644 index 0000000000000..d664eed97b611 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/BasicReportDiagnosticAnalyzer.vb @@ -0,0 +1,57 @@ +' 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. + +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers + + Public Class BasicReportDiagnosticAnalyzer + Inherits ReportDiagnosticAnalyzer(Of ClassBlockSyntax, StructureBlockSyntax, InvocationExpressionSyntax, IdentifierNameSyntax, VariableDeclaratorSyntax) + + Protected Overrides Function GetAnalyzer(contextTypes As ImmutableHashSet(Of INamedTypeSymbol), + diagnosticType As INamedTypeSymbol, + diagnosticDescriptorType As INamedTypeSymbol, + diagnosticAnalyzer As INamedTypeSymbol, + diagnosticAnalyzerAttribute As INamedTypeSymbol) As ReportDiagnosticCompilationAnalyzer + Return New BasicReportDiagnosticCompilationAnalyzer(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute) + End Function + + Private NotInheritable Class BasicReportDiagnosticCompilationAnalyzer + Inherits ReportDiagnosticCompilationAnalyzer + + Public Sub New(contextTypes As ImmutableHashSet(Of INamedTypeSymbol), + diagnosticType As INamedTypeSymbol, + diagnosticDescriptorType As INamedTypeSymbol, + diagnosticAnalyzer As INamedTypeSymbol, + diagnosticAnalyzerAttribute As INamedTypeSymbol) + MyBase.New(contextTypes, diagnosticType, diagnosticDescriptorType, diagnosticAnalyzer, diagnosticAnalyzerAttribute) + End Sub + + Protected Overrides Function GetArgumentExpressions(invocation As InvocationExpressionSyntax) As IEnumerable(Of SyntaxNode) + If invocation.ArgumentList IsNot Nothing Then + Return invocation.ArgumentList.Arguments.Select(Function(a) a.GetExpression) + End If + + Return Nothing + End Function + + Protected Overrides Function GetPropertyGetterBlockSyntax(declaringSyntaxRefNode As SyntaxNode) As SyntaxNode + Select Case declaringSyntaxRefNode.Kind + Case SyntaxKind.GetAccessorBlock + Return declaringSyntaxRefNode + + Case SyntaxKind.GetAccessorStatement + Return declaringSyntaxRefNode.Parent + + Case Else + Return Nothing + End Select + End Function + End Class + End Class +End Namespace + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicApplyDiagnosticAnalyzerAttributeFix.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicApplyDiagnosticAnalyzerAttributeFix.vb new file mode 100644 index 0000000000000..ebd65f576cf0a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicApplyDiagnosticAnalyzerAttributeFix.vb @@ -0,0 +1,24 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Simplification + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes + + Public Class BasicApplyDiagnosticAnalyzerAttributeFix + Inherits ApplyDiagnosticAnalyzerAttributeFix + + + + Public Sub New() + End Sub + + Protected Overrides Function ParseExpression(expression As String) As SyntaxNode + Return SyntaxFactory.ParseExpression(expression).WithAdditionalAnnotations(Simplifier.Annotation) + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicCompareSymbolsCorrectlyFix.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicCompareSymbolsCorrectlyFix.vb new file mode 100644 index 0000000000000..bcca872c78864 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicCompareSymbolsCorrectlyFix.vb @@ -0,0 +1,31 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.Fixers + + Public Class BasicCompareSymbolsCorrectlyFix + Inherits CompareSymbolsCorrectlyFix + + + + Public Sub New() + End Sub + + Protected Overrides Function CreateConditionalAccessExpression(expression As SyntaxNode, whenNotNull As SyntaxNode) As SyntaxNode + Return SyntaxFactory.ConditionalAccessExpression( + DirectCast(expression, ExpressionSyntax), + DirectCast(whenNotNull, ExpressionSyntax)) + End Function + + Protected Overrides Function GetExpression(invocationOperation As Operations.IInvocationOperation) As SyntaxNode + Dim invocation = DirectCast(invocationOperation.Syntax, InvocationExpressionSyntax) + Return invocation.Expression + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicConfigureGeneratedCodeAnalysisFix.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicConfigureGeneratedCodeAnalysisFix.vb new file mode 100644 index 0000000000000..352c13347a889 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicConfigureGeneratedCodeAnalysisFix.vb @@ -0,0 +1,25 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes + + Public Class BasicConfigureGeneratedCodeAnalysisFix + Inherits ConfigureGeneratedCodeAnalysisFix + + + + Public Sub New() + End Sub + + Protected Overrides Function GetStatements(methodDeclaration As SyntaxNode) As IEnumerable(Of SyntaxNode) + Dim method = TryCast(methodDeclaration, MethodBlockSyntax) + Return method.Statements + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicEnableConcurrentExecutionFix.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicEnableConcurrentExecutionFix.vb new file mode 100644 index 0000000000000..ae8d72b635ce3 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicEnableConcurrentExecutionFix.vb @@ -0,0 +1,25 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes + + Public Class BasicEnableConcurrentExecutionFix + Inherits EnableConcurrentExecutionFix + + + + Public Sub New() + End Sub + + Protected Overrides Function GetStatements(methodDeclaration As SyntaxNode) As IEnumerable(Of SyntaxNode) + Dim method = TryCast(methodDeclaration, MethodBlockSyntax) + Return method.Statements + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicPreferIsKindFix.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicPreferIsKindFix.vb new file mode 100644 index 0000000000000..00a8cb5ac9ddd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/MetaAnalyzers/Fixers/BasicPreferIsKindFix.vb @@ -0,0 +1,91 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Fixers +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Editing +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers.MetaAnalyzers.CodeFixes + + <[Shared]> + Public NotInheritable Class BasicPreferIsKindFix + Inherits PreferIsKindFix + + + + Public Sub New() + End Sub + + Protected Overrides Function TryGetNodeToFix(root As SyntaxNode, span As TextSpan) As SyntaxNode + Dim binaryExpression = root.FindNode(span, getInnermostNodeForTie:=True).FirstAncestorOrSelf(Of BinaryExpressionSyntax)() + If binaryExpression.Left.IsKind(SyntaxKind.InvocationExpression) OrElse + binaryExpression.Left.IsKind(SyntaxKind.SimpleMemberAccessExpression) OrElse + binaryExpression.Left.IsKind(SyntaxKind.ConditionalAccessExpression) Then + Return binaryExpression + End If + + Return Nothing + End Function + + Protected Overrides Sub FixDiagnostic(editor As DocumentEditor, nodeToFix As SyntaxNode) + editor.ReplaceNode( + nodeToFix, + Function(nodeToFix2, generator) + Dim binaryExpression = DirectCast(nodeToFix2, BinaryExpressionSyntax) + Dim invocation = TryCast(binaryExpression.Left, InvocationExpressionSyntax) + invocation = If(invocation, TryConvertMemberAccessToInvocation(binaryExpression.Left)) + invocation = If(invocation, TryConvertConditionalAccessToInvocation(binaryExpression.Left)) + If invocation Is Nothing Then + Return binaryExpression + End If + + Dim newInvocation = invocation _ + .WithExpression(ConvertKindNameToIsKind(invocation.Expression)) _ + .AddArgumentListArguments(SyntaxFactory.SimpleArgument(binaryExpression.Right.WithoutTrailingTrivia())) _ + .WithTrailingTrivia(binaryExpression.Right.GetTrailingTrivia()) + Dim negate = binaryExpression.OperatorToken.IsKind(SyntaxKind.LessThanGreaterThanToken) + If negate Then + Return SyntaxFactory.NotExpression(newInvocation.WithoutLeadingTrivia()).WithLeadingTrivia(newInvocation.GetLeadingTrivia()) + Else + Return newInvocation + End If + End Function) + End Sub + + Private Shared Function TryConvertMemberAccessToInvocation(expression As ExpressionSyntax) As InvocationExpressionSyntax + Dim memberAccessExpression = TryCast(expression, MemberAccessExpressionSyntax) + If memberAccessExpression IsNot Nothing Then + Return SyntaxFactory.InvocationExpression(memberAccessExpression.WithoutTrailingTrivia()) _ + .WithTrailingTrivia(memberAccessExpression.GetTrailingTrivia()) + Else + Return Nothing + End If + End Function + + Private Shared Function TryConvertConditionalAccessToInvocation(expression As ExpressionSyntax) As InvocationExpressionSyntax + Dim conditionalAccessExpression = TryCast(expression, ConditionalAccessExpressionSyntax) + If conditionalAccessExpression IsNot Nothing Then + Dim simpleMemberAccess = SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + conditionalAccessExpression.Expression.WithoutTrailingTrivia(), + SyntaxFactory.Token(SyntaxKind.DotToken), SyntaxFactory.IdentifierName("Kind")) + Return SyntaxFactory.InvocationExpression(simpleMemberAccess.WithTrailingTrivia(conditionalAccessExpression.GetTrailingTrivia())) + Else + Return Nothing + End If + End Function + + Private Shared Function ConvertKindNameToIsKind(expression As ExpressionSyntax) As ExpressionSyntax + Dim memberAccessExpression = TryCast(expression, MemberAccessExpressionSyntax) + If memberAccessExpression IsNot Nothing Then + Return memberAccessExpression.WithName(SyntaxFactory.IdentifierName(SyntaxFactory.Identifier("IsKind"))) + Else + Return expression + End If + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Analyzers.vbproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Analyzers.vbproj new file mode 100644 index 0000000000000..f88c691046368 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.Analyzers.vbproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + $(MicrosoftCodeAnalysisVersionForCodeAnalysisAnalyzers) + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/VisualBasicUpgradeMSBuildWorkspaceAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/VisualBasicUpgradeMSBuildWorkspaceAnalyzer.vb new file mode 100644 index 0000000000000..9ab5739c8aedc --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/VisualBasic/VisualBasicUpgradeMSBuildWorkspaceAnalyzer.vb @@ -0,0 +1,35 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis.Analyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Analyzers + + Public Class VisualBasicUpgradeMSBuildWorkspaceAnalyzer + Inherits UpgradeMSBuildWorkspaceAnalyzer + + Protected Overrides Sub RegisterIdentifierAnalysis(context As CompilationStartAnalysisContext) + context.RegisterSyntaxNodeAction(AddressOf AnalyzeIdentifier, SyntaxKind.IdentifierName) + End Sub + + Private Sub AnalyzeIdentifier(context As SyntaxNodeAnalysisContext) + Dim identifierName = TryCast(context.Node, IdentifierNameSyntax) + If identifierName Is Nothing Then + Return + End If + + If Not CaseInsensitiveComparison.Equals(identifierName.Identifier.ToString(), MSBuildWorkspace) Then + Return + End If + + Dim symbolInfo = context.SemanticModel.GetSymbolInfo(identifierName, context.CancellationToken) + If symbolInfo.Symbol Is Nothing Then + context.ReportDiagnostic(identifierName.CreateDiagnostic(UpgradeMSBuildWorkspaceDiagnosticRule)) + End If + End Sub + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/enable-analyzer-release-tracking.png b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/enable-analyzer-release-tracking.png new file mode 100644 index 0000000000000..a15e0d6d9cbe0 Binary files /dev/null and b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/enable-analyzer-release-tracking.png differ diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/readme.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/readme.md new file mode 100644 index 0000000000000..4fc038bf4578e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/readme.md @@ -0,0 +1,5 @@ +# Microsoft.CodeAnalysis.Analyzers + +Contains rules for correct usage of APIs from the [Microsoft.CodeAnalysis](https://www.nuget.org/packages/Microsoft.CodeAnalysis) NuGet package, i.e. .NET Compiler Platform ("Roslyn") APIs. These are primarily aimed towards helping authors of diagnostic analyzers, code fix providers and other tools built on top of Microsoft.CodeAnalysis to invoke the Microsoft.CodeAnalysis APIs in a recommended manner. This package is included as a development dependency of [Microsoft.CodeAnalysis](https://www.nuget.org/packages/Microsoft.CodeAnalysis) NuGet package, and does not need to be installed separately if you are referencing Microsoft.CodeAnalysis NuGet package. + +[More info about rules in this package](./Microsoft.CodeAnalysis.Analyzers.md) diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md new file mode 100644 index 0000000000000..cf031e98eb1bd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md @@ -0,0 +1,71 @@ +# How to use Microsoft.CodeAnalysis.BannedApiAnalyzers + +The following file or files have to be added to any project referencing this package to enable analysis: + +- BannedSymbols.txt +- BannedSymbols.\*.txt + +This can be done by: + +- In Visual Studio, right click project in Solution Explorer, and choose "Add -> New Items", then select "Text File" in "Add new item" dialog. +- Or, create the file at the location you desire, then add the following text to your project/target file (replace file path with its actual location): + + ```xml + + + + ``` + +To add a symbol to the banned list, just add an entry in the format below to one of the configuration files (Description Text will be displayed as description in diagnostics, which is optional): + +```txt +{Documentation Comment ID string for the symbol}[;Description Text] +``` + +Comments can be indicated with `//`, in the same way that they work in C#. + +For details on ID string format, please refer to ["ID string format"](https://github.com/dotnet/csharpstandard/blob/standard-v6/standard/documentation-comments.md#d42-id-string-format). + +Examples of BannedSymbols.txt entries for symbols declared in the source below: + +```cs +namespace N +{ + class BannedType + { + public BannedType() {} + + public int BannedMethod() {} + + public void BannedMethod(int i) {} + + public void BannedMethod(T t) {} + + public void BannedMethod(Func f) {} + + public string BannedField; + + public string BannedProperty { get; } + + public event EventHandler BannedEvent; + } + + class BannedType + { + } +} +``` + +| Symbol in Source | Sample Entry in BannedSymbols.txt +| ----------- | ----------- +| `class BannedType` | `T:N.BannedType;Don't use BannedType` +| `class BannedType` | ``T:N.BannedType`1;Don't use BannedType`` +| `BannedType()` | `M:N.BannedType.#ctor` +| `int BannedMethod()` | `M:N.BannedType.BannedMethod` +| `void BannedMethod(int i)` | `M:N.BannedType.BannedMethod(System.Int32);Don't use BannedMethod` +| `void BannedMethod(T t)` | ```M:N.BannedType.BannedMethod`1(``0)``` +| `void BannedMethod(Func f)` | ```M:N.BannedType.BannedMethod`1(System.Func{``0})``` +| `string BannedField` | `F:N.BannedType.BannedField` +| `string BannedProperty { get; }` | `P:N.BannedType.BannedProperty` +| `event EventHandler BannedEvent;` | `E:N.BannedType.BannedEvent` +| `namespace N` | `N:N` diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpRestrictedInternalsVisibleToAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpRestrictedInternalsVisibleToAnalyzer.cs new file mode 100644 index 0000000000000..1f3606afca233 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpRestrictedInternalsVisibleToAnalyzer.cs @@ -0,0 +1,25 @@ +// 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.BannedApiAnalyzers; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpRestrictedInternalsVisibleToAnalyzer : RestrictedInternalsVisibleToAnalyzer + { + protected override ImmutableArray NameSyntaxKinds => + ImmutableArray.Create( + SyntaxKind.IdentifierName, + SyntaxKind.GenericName, + SyntaxKind.QualifiedName, + SyntaxKind.AliasQualifiedName); + + protected override bool IsInTypeOnlyContext(NameSyntax node) + => SyntaxFacts.IsInTypeOnlyContext(node); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.cs new file mode 100644 index 0000000000000..dde086c94da26 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/CSharpSymbolIsBannedAnalyzer.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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.BannedApiAnalyzers; +using System.Collections.Immutable; +using System.Linq; +using System.Collections.Generic; + +namespace Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpSymbolIsBannedAnalyzer : SymbolIsBannedAnalyzer + { + protected override SyntaxKind XmlCrefSyntaxKind => SyntaxKind.XmlCrefAttribute; + + protected override ImmutableArray BaseTypeSyntaxKinds => ImmutableArray.Create(SyntaxKind.BaseList); + + protected override SymbolDisplayFormat SymbolDisplayFormat => SymbolDisplayFormat.CSharpShortErrorMessageFormat; + + protected override SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode) => ((XmlCrefAttributeSyntax)syntaxNode).Cref; + + protected override IEnumerable GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode) => ((BaseListSyntax)syntaxNode).Types.Select(t => (SyntaxNode)t.Type); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.csproj new file mode 100644 index 0000000000000..b181b16055b0c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/CSharp/Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForBannedApiAnalyzers) + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..68d773c8afe5b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Shipped.md @@ -0,0 +1,9 @@ +## Release 2.9.8 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS0030 | ApiDesign | Warning | SymbolIsBannedAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md) | +| RS0031 | ApiDesign | Warning | SymbolIsBannedAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md) | +| RS0035 | ApiDesign | Error | RestrictedInternalsVisibleToAnalyzer | diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.cs new file mode 100644 index 0000000000000..1287e1c7ec890 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers +{ + internal partial class BannedApiAnalyzerResources + { + private static readonly Type s_resourcesType = typeof(BannedApiAnalyzerResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.resx b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.resx new file mode 100644 index 0000000000000..a6bb44d7cb72f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/BannedApiAnalyzerResources.resx @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The list of banned symbols contains a duplicate. + + + The symbol '{0}' is listed multiple times in the list of banned APIs + + + The list of banned symbols contains a duplicate + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + + + The symbol '{0}' is banned in this project{1} + + + Do not use banned APIs + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + + + External access to internal symbols outside the restricted namespace(s) is prohibited + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/DocumentationCommentIdParser.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/DocumentationCommentIdParser.cs new file mode 100644 index 0000000000000..9aef41eb17ece --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/DocumentationCommentIdParser.cs @@ -0,0 +1,109 @@ +// 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.BannedApiAnalyzers +{ + /// + /// Stripped down port of the code in roslyn. Responsible only for determining the and + /// name of the for a given xml doc comment symbol id. + /// + internal static class DocumentationCommentIdParser + { + private static readonly char[] s_nameDelimiters = { ':', '.', '(', ')', '{', '}', '[', ']', ',', '\'', '@', '*', '`', '~' }; + + public static (string ParentName, string SymbolName)? ParseDeclaredSymbolId(string id) + { + if (id == null) + return null; + + if (id.Length < 2) + return null; + + int index = 0; + return ParseDeclaredId(id, ref index); + } + + private static (string ParentName, string SymbolName)? ParseDeclaredId(string id, ref int index) + { + var kindChar = PeekNextChar(id, index); + + switch (kindChar) + { + case 'E': // Events + case 'F': // Fields + case 'M': // Methods + case 'P': // Properties + case 'T': // Types + case 'N': // Namespaces + break; + default: + // Documentation comment id must start with E, F, M, N, P or T. + return null; + } + + index++; + if (PeekNextChar(id, index) == ':') + index++; + + string parentName = ""; + + // process dotted names + while (true) + { + var symbolName = ParseName(id, ref index); + + // has type parameters? + if (PeekNextChar(id, index) == '`') + { + index++; + + // method type parameters? + if (PeekNextChar(id, index) == '`') + index++; + + ReadNextInteger(id, ref index); + } + + if (PeekNextChar(id, index) == '.') + { + index++; + parentName = symbolName; + continue; + } + else + { + return (parentName, symbolName); + } + } + } + + private static char PeekNextChar(string id, int index) + => index >= id.Length ? '\0' : id[index]; + + private static string ParseName(string id, ref int index) + { + string name; + + int delimiterOffset = id.IndexOfAny(s_nameDelimiters, index); + if (delimiterOffset >= 0) + { + name = id[index..delimiterOffset]; + index = delimiterOffset; + } + else + { + name = id[index..]; + index = id.Length; + } + + return name.Replace('#', '.'); + } + + private static void ReadNextInteger(string id, ref int index) + { + while (index < id.Length && char.IsDigit(id[index])) + index++; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/Microsoft.CodeAnalysis.BannedApiAnalyzers.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/Microsoft.CodeAnalysis.BannedApiAnalyzers.csproj new file mode 100644 index 0000000000000..31b150e7e6e62 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/Microsoft.CodeAnalysis.BannedApiAnalyzers.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForBannedApiAnalyzers) + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/RestrictedInternalsVisibleToAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/RestrictedInternalsVisibleToAnalyzer.cs new file mode 100644 index 0000000000000..259d3e271ab88 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/RestrictedInternalsVisibleToAnalyzer.cs @@ -0,0 +1,266 @@ +// 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.Concurrent; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +using DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers +{ + using static BannedApiAnalyzerResources; + + /// + /// RS0035: + /// + public abstract class RestrictedInternalsVisibleToAnalyzer : DiagnosticAnalyzer + where TNameSyntax : SyntaxNode + where TSyntaxKind : struct + { + public static readonly DiagnosticDescriptor Rule = new( + id: DiagnosticIds.RestrictedInternalsVisibleToRuleId, + title: CreateLocalizableResourceString(nameof(RestrictedInternalsVisibleToTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RestrictedInternalsVisibleToMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Error, // Force build break on invalid external access. + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RestrictedInternalsVisibleToDescription)), + helpLinkUri: null, // TODO: Add help link + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public sealed override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + protected abstract ImmutableArray NameSyntaxKinds { get; } + + protected abstract bool IsInTypeOnlyContext(TNameSyntax node); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // Analyzer needs to get callbacks for generated code, and might report diagnostics in generated code. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext compilationContext) + { + var restrictedInternalsVisibleToMap = GetRestrictedInternalsVisibleToMap(compilationContext.Compilation); + if (restrictedInternalsVisibleToMap.IsEmpty) + { + return; + } + + var namespaceToIsBannedMap = new ConcurrentDictionary(); + + // Verify all explicit type name specifications in declarations and executable code. + compilationContext.RegisterSyntaxNodeAction( + context => + { + var name = (TNameSyntax)context.Node; + if (!IsInTypeOnlyContext(name) || + name.Parent is TNameSyntax) + { + // Bail out if we are not in type only context or the parent is also a name + // which will be analyzed separately. + return; + } + + var typeInfo = context.SemanticModel.GetTypeInfo(name, context.CancellationToken); + VerifySymbol(typeInfo.Type as INamedTypeSymbol, name, + context.ReportDiagnostic, restrictedInternalsVisibleToMap, namespaceToIsBannedMap); + }, + NameSyntaxKinds); + + // Verify all member usages in executable code. + compilationContext.RegisterOperationAction( + context => + { + var symbol = context.Operation switch + { + IObjectCreationOperation objectCreation => objectCreation.Constructor, + IInvocationOperation invocation => invocation.TargetMethod, + IMemberReferenceOperation memberReference => memberReference.Member, + IConversionOperation conversion => conversion.OperatorMethod, + IUnaryOperation unary => unary.OperatorMethod, + IBinaryOperation binary => binary.OperatorMethod, + IIncrementOrDecrementOperation incrementOrDecrement => incrementOrDecrement.OperatorMethod, + _ => throw new NotImplementedException($"Unhandled OperationKind: {context.Operation.Kind}"), + }; + + VerifySymbol(symbol, context.Operation.Syntax, + context.ReportDiagnostic, restrictedInternalsVisibleToMap, namespaceToIsBannedMap); + }, + OperationKind.ObjectCreation, + OperationKind.Invocation, + OperationKind.EventReference, + OperationKind.FieldReference, + OperationKind.MethodReference, + OperationKind.PropertyReference, + OperationKind.Conversion, + OperationKind.UnaryOperator, + OperationKind.BinaryOperator, + OperationKind.Increment, + OperationKind.Decrement); + } + + private static ImmutableDictionary> GetRestrictedInternalsVisibleToMap(Compilation compilation) + { + var restrictedInternalsVisibleToAttribute = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesRestrictedInternalsVisibleToAttribute); + if (restrictedInternalsVisibleToAttribute == null) + { + return ImmutableDictionary>.Empty; + } + + var builder = ImmutableDictionary.CreateBuilder>(); + foreach (var referencedAssemblySymbol in compilation.References.Select(compilation.GetAssemblyOrModuleSymbol).OfType()) + { + // Check IVT + if (!referencedAssemblySymbol.GivesAccessTo(compilation.Assembly)) + { + continue; + } + + var namespaceNameComparer = compilation.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; + var namespaceBuilder = ImmutableSortedSet.CreateBuilder(namespaceNameComparer); + foreach (var assemblyAttribute in referencedAssemblySymbol.GetAttributes(restrictedInternalsVisibleToAttribute)) + { + // Look for ctor: "RestrictedInternalsVisibleToAttribute(string assemblyName, params string[] namespaces)" + if (assemblyAttribute.AttributeConstructor is null || + assemblyAttribute.AttributeConstructor.Parameters.Length != 2 || + assemblyAttribute.AttributeConstructor.Parameters[0].Type.SpecialType != SpecialType.System_String || + assemblyAttribute.AttributeConstructor.Parameters[1].Type is not IArrayTypeSymbol arrayType || + arrayType.Rank != 1 || + arrayType.ElementType.SpecialType != SpecialType.System_String || + !assemblyAttribute.AttributeConstructor.Parameters[1].IsParams) + { + continue; + } + + // Ensure the Restricted IVT is for the current compilation's assembly. + if (assemblyAttribute.ConstructorArguments.Length != 2 || + assemblyAttribute.ConstructorArguments[0].Kind != TypedConstantKind.Primitive || + assemblyAttribute.ConstructorArguments[0].Value is not string assemblyName || + !string.Equals(assemblyName, compilation.Assembly.Name, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + // Ensure second constructor argument is string array. + if (assemblyAttribute.ConstructorArguments[1].Kind != TypedConstantKind.Array || + !(assemblyAttribute.ConstructorArguments[1].Values is var namespaceConstants)) + { + continue; + } + + // Add namespaces specified in the second constructor argument. + foreach (TypedConstant namespaceConstant in namespaceConstants) + { + if (namespaceConstant.Kind == TypedConstantKind.Primitive && + namespaceConstant.Value is string namespaceName) + { + namespaceBuilder.Add(namespaceName); + } + } + } + + if (namespaceBuilder.Count > 0) + { + builder.Add(referencedAssemblySymbol, namespaceBuilder.ToImmutable()); + } + } + + return builder.ToImmutable(); + } + + private static void VerifySymbol( + ISymbol? symbol, + SyntaxNode node, + Action reportDiagnostic, + ImmutableDictionary> restrictedInternalsVisibleToMap, + ConcurrentDictionary namespaceToIsBannedMap) + { + if (symbol != null && + IsBannedSymbol(symbol, restrictedInternalsVisibleToMap, namespaceToIsBannedMap)) + { + var bannedSymbolDisplayString = symbol.ToDisplayString(SymbolDisplayFormats.QualifiedTypeAndNamespaceSymbolDisplayFormat); + var assemblyName = symbol.ContainingAssembly.Name; + var restrictedNamespaces = string.Join(", ", restrictedInternalsVisibleToMap[symbol.ContainingAssembly]); + var diagnostic = node.CreateDiagnostic(Rule, bannedSymbolDisplayString, assemblyName, restrictedNamespaces); + reportDiagnostic(diagnostic); + } + } + + private static bool IsBannedSymbol( + ISymbol symbol, + ImmutableDictionary> restrictedInternalsVisibleToMap, + ConcurrentDictionary namespaceToIsBannedMap) + { + // Check if the symbol belongs to an assembly to which this compilation has restricted internals access + // and it is an internal symbol. + if (symbol.ContainingAssembly == null || + !restrictedInternalsVisibleToMap.TryGetValue(symbol.ContainingAssembly, out var allowedNamespaces) || + symbol.GetResultantVisibility() != SymbolVisibility.Internal) + { + return false; + } + + // Walk up containing namespace chain to explicitly look for an allowed namespace + // with restricted internals access. + var currentNamespace = symbol.ContainingNamespace; + while (currentNamespace != null && !currentNamespace.IsGlobalNamespace) + { + // Check if we have already computed whether this namespace is banned or not. + if (namespaceToIsBannedMap.TryGetValue(currentNamespace, out var isBanned)) + { + return isBanned; + } + + // Check if this namespace is explicitly marked as allowed through restricted IVT. + if (allowedNamespaces.Contains(currentNamespace.ToDisplayString())) + { + MarkIsBanned(symbol.ContainingNamespace, currentNamespace, namespaceToIsBannedMap, banned: false); + return false; + } + + currentNamespace = currentNamespace.ContainingNamespace; + } + + // Otherwise, mark all the containing namespace names of the given symbol as banned + // and consider the given symbol as banned. + MarkIsBanned(symbol.ContainingNamespace, currentNamespace, namespaceToIsBannedMap, banned: true); + return true; + } + + private static void MarkIsBanned( + INamespaceSymbol? startNamespace, + INamespaceSymbol? uptoNamespace, + ConcurrentDictionary namespaceToIsBannedMap, + bool banned) + { + var currentNamespace = startNamespace; + while (currentNamespace != null) + { + var saved = namespaceToIsBannedMap.GetOrAdd(currentNamespace, banned); + Debug.Assert(saved == banned); + + if (Equals(currentNamespace, uptoNamespace)) + { + break; + } + + currentNamespace = currentNamespace.ContainingNamespace; + } + } + } +} + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzer.cs new file mode 100644 index 0000000000000..7f1eadf360731 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzer.cs @@ -0,0 +1,146 @@ +// 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.IO; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +using DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers +{ + using static BannedApiAnalyzerResources; + + internal static class SymbolIsBannedAnalyzer + { + public static readonly DiagnosticDescriptor SymbolIsBannedRule = new( + id: DiagnosticIds.SymbolIsBannedRuleId, + title: CreateLocalizableResourceString(nameof(SymbolIsBannedTitle)), + messageFormat: CreateLocalizableResourceString(nameof(SymbolIsBannedMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(SymbolIsBannedDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public static readonly DiagnosticDescriptor DuplicateBannedSymbolRule = new( + id: DiagnosticIds.DuplicateBannedSymbolRuleId, + title: CreateLocalizableResourceString(nameof(DuplicateBannedSymbolTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DuplicateBannedSymbolMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DuplicateBannedSymbolDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + } + + public abstract class SymbolIsBannedAnalyzer : SymbolIsBannedAnalyzerBase + where TSyntaxKind : struct + { + public sealed override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create(SymbolIsBannedAnalyzer.SymbolIsBannedRule, SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule); + + protected sealed override DiagnosticDescriptor SymbolIsBannedRule => SymbolIsBannedAnalyzer.SymbolIsBannedRule; + +#pragma warning disable RS1013 // 'compilationContext' does not register any analyzer actions, except for a 'CompilationEndAction'. Consider replacing this start/end action pair with a 'RegisterCompilationAction' or moving actions registered in 'Initialize' that depend on this start action to 'compilationContext'. + protected sealed override Dictionary<(string ContainerName, string SymbolName), ImmutableArray>? ReadBannedApis( + CompilationStartAnalysisContext compilationContext) + { + var compilation = compilationContext.Compilation; + + var query = + from additionalFile in compilationContext.Options.AdditionalFiles + let fileName = Path.GetFileName(additionalFile.Path) + where fileName != null && fileName.StartsWith("BannedSymbols.", StringComparison.Ordinal) && fileName.EndsWith(".txt", StringComparison.Ordinal) + orderby additionalFile.Path // Additional files are sorted by DocumentId (which is a GUID), make the file order deterministic + let sourceText = additionalFile.GetText(compilationContext.CancellationToken) + where sourceText != null + from line in sourceText.Lines + let text = line.ToString() + let commentIndex = text.IndexOf("//", StringComparison.Ordinal) + let textWithoutComment = commentIndex == -1 ? text : text[..commentIndex] + where !string.IsNullOrWhiteSpace(textWithoutComment) + let trimmedTextWithoutComment = textWithoutComment.TrimEnd() + let span = commentIndex == -1 ? line.Span : new Text.TextSpan(line.Span.Start, trimmedTextWithoutComment.Length) + let entry = new BanFileEntry(compilation, trimmedTextWithoutComment, span, sourceText, additionalFile.Path) + where !string.IsNullOrWhiteSpace(entry.DeclarationId) + select entry; + + var entries = query.ToList(); + + if (entries.Count == 0) + return null; + + var errors = new List(); + + // Report any duplicates. + var groups = entries.GroupBy(e => TrimForErrorReporting(e.DeclarationId)); + foreach (var group in groups) + { + if (group.Count() >= 2) + { + var groupList = group.ToList(); + var firstEntry = groupList[0]; + for (int i = 1; i < groupList.Count; i++) + { + var nextEntry = groupList[i]; + errors.Add(Diagnostic.Create( + SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule, + nextEntry.Location, new[] { firstEntry.Location }, + firstEntry.Symbols.FirstOrDefault()?.ToDisplayString() ?? "")); + } + } + } + + if (errors.Count != 0) + { + compilationContext.RegisterCompilationEndAction( + endContext => + { + foreach (var error in errors) + endContext.ReportDiagnostic(error); + }); + } + + var result = new Dictionary<(string ContainerName, string SymbolName), List>(); + + foreach (var entry in entries) + { + var parsed = DocumentationCommentIdParser.ParseDeclaredSymbolId(entry.DeclarationId); + if (parsed is null) + continue; + + if (!result.TryGetValue(parsed.Value, out var existing)) + { + existing = new(); + result.Add(parsed.Value, existing); + } + + existing.Add(entry); + } + + return result.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToImmutableArray()); + + static string TrimForErrorReporting(string declarationId) + { +#pragma warning disable format + return declarationId switch + { + // Remove the prefix and colon if there. + [_, ':', .. var rest] => rest, + // Colon is technically optional. So remove just the first character if not there. + [_, .. var rest] => rest, + _ => declarationId, + }; +#pragma warning restore format + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs new file mode 100644 index 0000000000000..844bda1a5c5cc --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/SymbolIsBannedAnalyzerBase.cs @@ -0,0 +1,460 @@ +// 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.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers +{ + public abstract class SymbolIsBannedAnalyzerBase : DiagnosticAnalyzer + where TSyntaxKind : struct + { + protected abstract Dictionary<(string ContainerName, string SymbolName), ImmutableArray>? ReadBannedApis(CompilationStartAnalysisContext compilationContext); + + protected abstract DiagnosticDescriptor SymbolIsBannedRule { get; } + + protected abstract TSyntaxKind XmlCrefSyntaxKind { get; } + + protected abstract SyntaxNode GetReferenceSyntaxNodeFromXmlCref(SyntaxNode syntaxNode); + + protected abstract ImmutableArray BaseTypeSyntaxKinds { get; } + + protected abstract IEnumerable GetTypeSyntaxNodesFromBaseType(SyntaxNode syntaxNode); + + protected abstract SymbolDisplayFormat SymbolDisplayFormat { get; } + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // Analyzer needs to get callbacks for generated code, and might report diagnostics in generated code. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext compilationContext) + { + var bannedApis = ReadBannedApis(compilationContext); + if (bannedApis == null || bannedApis.Count == 0) + return; + + if (ShouldAnalyzeAttributes()) + { + compilationContext.RegisterCompilationEndAction( + context => + { + VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.Assembly.GetAttributes(), context.CancellationToken); + VerifyAttributes(context.ReportDiagnostic, compilationContext.Compilation.SourceModule.GetAttributes(), context.CancellationToken); + }); + + compilationContext.RegisterSymbolAction( + context => VerifyAttributes(context.ReportDiagnostic, context.Symbol.GetAttributes(), context.CancellationToken), + SymbolKind.NamedType, + SymbolKind.Method, + SymbolKind.Field, + SymbolKind.Property, + SymbolKind.Event); + } + + compilationContext.RegisterOperationAction( + context => + { + context.CancellationToken.ThrowIfCancellationRequested(); + switch (context.Operation) + { + case IObjectCreationOperation objectCreation: + if (objectCreation.Constructor != null) + VerifySymbol(context.ReportDiagnostic, objectCreation.Constructor, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, objectCreation.Type, context.Operation.Syntax); + break; + + case IInvocationOperation invocation: + VerifySymbol(context.ReportDiagnostic, invocation.TargetMethod, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, invocation.TargetMethod.ContainingType, context.Operation.Syntax); + break; + + case IMemberReferenceOperation memberReference: + VerifySymbol(context.ReportDiagnostic, memberReference.Member, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, memberReference.Member.ContainingType, context.Operation.Syntax); + break; + + case IArrayCreationOperation arrayCreation: + VerifyType(context.ReportDiagnostic, arrayCreation.Type, context.Operation.Syntax); + break; + + case IAddressOfOperation addressOf: + VerifyType(context.ReportDiagnostic, addressOf.Type, context.Operation.Syntax); + break; + + case IConversionOperation conversion: + if (conversion.OperatorMethod != null) + { + VerifySymbol(context.ReportDiagnostic, conversion.OperatorMethod, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, conversion.OperatorMethod.ContainingType, context.Operation.Syntax); + } + + break; + + case IUnaryOperation unary: + if (unary.OperatorMethod != null) + { + VerifySymbol(context.ReportDiagnostic, unary.OperatorMethod, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, unary.OperatorMethod.ContainingType, context.Operation.Syntax); + } + + break; + + case IBinaryOperation binary: + if (binary.OperatorMethod != null) + { + VerifySymbol(context.ReportDiagnostic, binary.OperatorMethod, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, binary.OperatorMethod.ContainingType, context.Operation.Syntax); + } + + break; + + case IIncrementOrDecrementOperation incrementOrDecrement: + if (incrementOrDecrement.OperatorMethod != null) + { + VerifySymbol(context.ReportDiagnostic, incrementOrDecrement.OperatorMethod, context.Operation.Syntax); + VerifyType(context.ReportDiagnostic, incrementOrDecrement.OperatorMethod.ContainingType, context.Operation.Syntax); + } + + break; + case ITypeOfOperation typeOfOperation: + VerifyType(context.ReportDiagnostic, typeOfOperation.TypeOperand, context.Operation.Syntax); + break; + } + }, + OperationKind.ObjectCreation, + OperationKind.Invocation, + OperationKind.EventReference, + OperationKind.FieldReference, + OperationKind.MethodReference, + OperationKind.PropertyReference, + OperationKind.ArrayCreation, + OperationKind.AddressOf, + OperationKind.Conversion, + OperationKind.UnaryOperator, + OperationKind.BinaryOperator, + OperationKind.Increment, + OperationKind.Decrement, + OperationKind.TypeOf); + + compilationContext.RegisterSyntaxNodeAction( + context => VerifyDocumentationSyntax(context.ReportDiagnostic, GetReferenceSyntaxNodeFromXmlCref(context.Node), context), + XmlCrefSyntaxKind); + + compilationContext.RegisterSyntaxNodeAction( + context => VerifyBaseTypesSyntax(context.ReportDiagnostic, GetTypeSyntaxNodesFromBaseType(context.Node), context), + BaseTypeSyntaxKinds); + + return; + + bool IsBannedSymbol([NotNullWhen(true)] ISymbol? symbol, [NotNullWhen(true)] out BanFileEntry? entry) + { + if (symbol is { ContainingSymbol.Name: string parentName } && + bannedApis.TryGetValue((parentName, symbol.Name), out var entries)) + { + foreach (var bannedFileEntry in entries) + { + foreach (var bannedSymbol in bannedFileEntry.Symbols) + { + if (SymbolEqualityComparer.Default.Equals(symbol, bannedSymbol)) + { + entry = bannedFileEntry; + return true; + } + } + } + } + + entry = null; + return false; + } + + bool ShouldAnalyzeAttributes() + { + // We want to avoid realizing symbols here as that can be very expensive. So we instead use a simple + // heuristic which works thanks to .net coding conventions. Specifically, we look to see if the banned + // api contains a type that ends in 'Attribute'. In that case, we do the work to try to get the real symbol. + foreach (var kvp in bannedApis) + { + if (!kvp.Key.SymbolName.EndsWith("Attribute", StringComparison.InvariantCulture) && + !kvp.Key.ContainerName.EndsWith("Attribute", StringComparison.InvariantCulture)) + { + continue; + } + + foreach (var entry in kvp.Value) + { + if (entry.Symbols.Any(ContainsAttributeSymbol)) + { + return true; + } + } + } + + return false; + } + + bool ContainsAttributeSymbol(ISymbol symbol) + { + return symbol switch + { + INamedTypeSymbol namedType => namedType.IsAttribute(), + IMethodSymbol method => method.ContainingType.IsAttribute() && method.IsConstructor(), + _ => false + }; + } + + void VerifyAttributes(Action reportDiagnostic, ImmutableArray attributes, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + foreach (var attribute in attributes) + { + if (IsBannedSymbol(attribute.AttributeClass, out var entry)) + { + var node = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken); + if (node != null) + { + reportDiagnostic( + node.CreateDiagnostic( + SymbolIsBannedRule, + attribute.AttributeClass.ToDisplayString(), + string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); + } + } + + if (attribute.AttributeConstructor != null) + { + var syntaxNode = attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken); + + if (syntaxNode != null) + { + VerifySymbol(reportDiagnostic, attribute.AttributeConstructor, syntaxNode); + } + } + } + } + + bool VerifyType(Action reportDiagnostic, ITypeSymbol? type, SyntaxNode syntaxNode) + { + do + { + if (!VerifyTypeArguments(reportDiagnostic, type, syntaxNode, out type)) + { + return false; + } + + if (type == null) + { + // Type will be null for arrays and pointers. + return true; + } + + if (IsBannedSymbol(type, out var entry)) + { + reportDiagnostic( + syntaxNode.CreateDiagnostic( + SymbolIsBannedRule, + type.ToDisplayString(SymbolDisplayFormat), + string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); + return false; + } + + foreach (var currentNamespace in GetContainingNamespaces(type)) + { + if (IsBannedSymbol(currentNamespace, out entry)) + { + reportDiagnostic( + syntaxNode.CreateDiagnostic( + SymbolIsBannedRule, + currentNamespace.ToDisplayString(), + string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); + return false; + } + } + + type = type.ContainingType; + } + while (!(type is null)); + + return true; + + static IEnumerable GetContainingNamespaces(ISymbol symbol) + { + INamespaceSymbol? currentNamespace = symbol.ContainingNamespace; + + while (currentNamespace is { IsGlobalNamespace: false }) + { + foreach (var constituent in currentNamespace.ConstituentNamespaces) + yield return constituent; + + currentNamespace = currentNamespace.ContainingNamespace; + } + } + } + + bool VerifyTypeArguments(Action reportDiagnostic, ITypeSymbol? type, SyntaxNode syntaxNode, out ITypeSymbol? originalDefinition) + { + switch (type) + { + case INamedTypeSymbol namedTypeSymbol: + originalDefinition = namedTypeSymbol.ConstructedFrom; + foreach (var typeArgument in namedTypeSymbol.TypeArguments) + { + if (typeArgument.TypeKind != TypeKind.TypeParameter && + typeArgument.TypeKind != TypeKind.Error && + !VerifyType(reportDiagnostic, typeArgument, syntaxNode)) + { + return false; + } + } + + break; + + case IArrayTypeSymbol arrayTypeSymbol: + originalDefinition = null; + return VerifyType(reportDiagnostic, arrayTypeSymbol.ElementType, syntaxNode); + + case IPointerTypeSymbol pointerTypeSymbol: + originalDefinition = null; + return VerifyType(reportDiagnostic, pointerTypeSymbol.PointedAtType, syntaxNode); + + default: + originalDefinition = type?.OriginalDefinition; + break; + + } + + return true; + } + + void VerifySymbol(Action reportDiagnostic, ISymbol symbol, SyntaxNode syntaxNode) + { + foreach (var currentSymbol in GetSymbolAndOverridenSymbols(symbol)) + { + if (IsBannedSymbol(currentSymbol, out var entry)) + { + reportDiagnostic( + syntaxNode.CreateDiagnostic( + SymbolIsBannedRule, + currentSymbol.ToDisplayString(SymbolDisplayFormat), + string.IsNullOrWhiteSpace(entry.Message) ? "" : ": " + entry.Message)); + return; + } + } + + static IEnumerable GetSymbolAndOverridenSymbols(ISymbol symbol) + { + ISymbol? currentSymbol = symbol.OriginalDefinition; + + while (currentSymbol != null) + { + yield return currentSymbol; + + // It's possible to have `IsOverride` true and yet have `GetOverriddeMember` returning null when the code is invalid + // (e.g. base symbol is not marked as `virtual` or `abstract` and current symbol has the `overrides` modifier). + currentSymbol = currentSymbol.IsOverride + ? currentSymbol.GetOverriddenMember()?.OriginalDefinition + : null; + } + } + } + + void VerifyDocumentationSyntax(Action reportDiagnostic, SyntaxNode syntaxNode, SyntaxNodeAnalysisContext context) + { + var symbol = context.SemanticModel.GetSymbolInfo(syntaxNode, context.CancellationToken).Symbol; + + if (symbol is ITypeSymbol typeSymbol) + { + VerifyType(reportDiagnostic, typeSymbol, syntaxNode); + } + else if (symbol != null) + { + VerifySymbol(reportDiagnostic, symbol, syntaxNode); + } + } + + void VerifyBaseTypesSyntax(Action reportDiagnostic, IEnumerable typeSyntaxNodes, SyntaxNodeAnalysisContext context) + { + foreach (var typeSyntaxNode in typeSyntaxNodes) + { + var symbol = context.SemanticModel.GetSymbolInfo(typeSyntaxNode, context.CancellationToken).Symbol; + + if (symbol is ITypeSymbol typeSymbol) + { + VerifyType(reportDiagnostic, typeSymbol, typeSyntaxNode); + } + } + } + } + + protected sealed class BanFileEntry + { + public TextSpan Span { get; } + public SourceText SourceText { get; } + public string Path { get; } + public string DeclarationId { get; } + public string Message { get; } + + private readonly Lazy> _lazySymbols; + public ImmutableArray Symbols => _lazySymbols.Value; + + public BanFileEntry(Compilation compilation, string text, TextSpan span, SourceText sourceText, string path) + { + // Split the text on semicolon into declaration ID and message + var index = text.IndexOf(';'); + + if (index == -1) + { + DeclarationId = text.Trim(); + Message = ""; + } + else if (index == text.Length - 1) + { + DeclarationId = text[0..^1].Trim(); + Message = ""; + } + else + { + DeclarationId = text[..index].Trim(); + Message = text[(index + 1)..].Trim(); + } + + Span = span; + SourceText = sourceText; + Path = path; + + _lazySymbols = new Lazy>( + () => DocumentationCommentId.GetSymbolsForDeclarationId(DeclarationId, compilation) + .SelectMany(ExpandConstituentNamespaces).ToImmutableArray()); + + static IEnumerable ExpandConstituentNamespaces(ISymbol symbol) + { + if (symbol is not INamespaceSymbol namespaceSymbol) + { + yield return symbol; + yield break; + } + + foreach (var constituent in namespaceSymbol.ConstituentNamespaces) + yield return constituent; + } + } + + public Location Location => Location.Create(Path, Span, SourceText.Lines.GetLinePositionSpan(Span)); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.cs.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.cs.xlf new file mode 100644 index 0000000000000..214d271e051a1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.cs.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + Seznam zakázaných symbolů obsahuje duplicitu. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Symbol {0} se v seznamu zakázaných rozhraní API nachází několikrát. + + + + The list of banned symbols contains a duplicate + Seznam zakázaných symbolů obsahuje duplicitu + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute povoluje omezenou verzi InternalsVisibleToAttribute, která omezuje přístup k interním symbolům na symboly v zadaném oboru názvů. Každé odkazující sestavení má přístup jen k interním symbolům definovaným v omezeném oboru názvů, který dané sestavení povoluje. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + Externí přístup k internímu symbolu {0} je zakázaný. Sestavení {1} povoluje přístup jen k interním symbolům definovaným v následujících oborech názvů: {2} + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + Externí přístup k interním symbolům mimo omezené obory názvů je zakázaný + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Symbol se v tomto projektu označil jako zakázaný, měla by se místo něj použít nějaká alternativa. + + + + The symbol '{0}' is banned in this project{1} + Symbol {0} je v tomto projektu {1} zakázaný. + + + + Do not use banned APIs + Nepoužívat zakázaná rozhraní API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.de.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.de.xlf new file mode 100644 index 0000000000000..4f7f38e641cd6 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.de.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + Die Liste der gesperrten Symbole enthält ein Duplikat. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Das Symbol "{0}" ist in der Liste der gesperrten APIs mehrmals aufgeführt. + + + + The list of banned symbols contains a duplicate + Liste gesperrter Symbole enthält ein Duplikat + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute aktiviert eine eingeschränkte Version von InternalsVisibleToAttribute, die den Zugriff auf interne Symbole auf diejenigen innerhalb der angegebenen Namespaces beschränkt. Jede verweisende Assembly kann nur auf interne Symbole zugreifen, die in den eingeschränkten Namespaces definiert sind, die von der referenzierten Assembly zugelassen werden. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + Der externe Zugriff auf das interne Symbol "{0}" ist unzulässig. Die Assembly "{1}" erlaubt nur den Zugriff auf interne Symbole, die in den folgenden Namespaces definiert sind: {2} + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + Externer Zugriff auf interne Symbole außerhalb der eingeschränkten Namespaces ist unzulässig + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Das Symbol wurde in diesem Projekt als gesperrt gekennzeichnet, und es muss stattdessen eine Alternative verwendet werden. + + + + The symbol '{0}' is banned in this project{1} + Das Symbol "{0}" ist in diesem Projekt gesperrt ({1}). + + + + Do not use banned APIs + Gesperrte APIs nicht verwenden + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.es.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.es.xlf new file mode 100644 index 0000000000000..83ed254ec147d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.es.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + La lista de símbolos prohibidos contiene un duplicado. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + El símbolo "{0}" aparece varias veces en la lista de las API prohibidas. + + + + The list of banned symbols contains a duplicate + La lista de símbolos prohibidos contiene un duplicado + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute habilita una versión restringida de InternalsVisibleToAttribute que limita el acceso a los símbolos internos a aquellos incluidos en los espacios de nombres especificados. Cada ensamblado de referencia solo puede acceder a los símbolos internos definidos en los espacios de nombres restringidos que permite el ensamblado al que se hace referencia. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + Se prohíbe el acceso externo al símbolo "{0}" interno. El ensamblado "{1}" solo permite el acceso a los símbolos internos definidos en los espacios de nombres siguientes: "{2}". + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + Se prohíbe el acceso externo a los símbolos internos fuera de los espacios de nombres restringidos + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + El símbolo se ha marcado como prohibido en el proyecto y se debe usar uno alternativo en su lugar. + + + + The symbol '{0}' is banned in this project{1} + El símbolo "{0}" está prohibido en el proyecto ({1}) + + + + Do not use banned APIs + No usar API prohibidas + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.fr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.fr.xlf new file mode 100644 index 0000000000000..f38e7d0cbba30 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.fr.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + La liste des symboles interdits contient un doublon. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Le symbole '{0}' est listé plusieurs fois dans la liste des API interdites + + + + The list of banned symbols contains a duplicate + La liste des symboles interdits contient un doublon + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute active une version restreinte de InternalsVisibleToAttribute qui limite l'accès aux symboles internes présents dans les espaces de noms spécifiés. Chaque assembly de référence peut uniquement accéder aux symboles internes définis dans les espaces de noms restreints autorisés par l'assembly référencé. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + L'accès externe au symbole interne '{0}' est interdit. L'assembly '{1}' autorise uniquement l'accès aux symboles internes définis dans les espaces de noms suivants : '{2}'. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + L'accès externe aux symboles internes situés en dehors des espaces de noms restreints est interdit + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Le symbole a été marqué comme étant interdit dans ce projet. Un autre symbole doit être utilisé à la place. + + + + The symbol '{0}' is banned in this project{1} + Le symbole '{0}' est interdit dans ce projet {1} + + + + Do not use banned APIs + Ne pas utiliser d’API interdites + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.it.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.it.xlf new file mode 100644 index 0000000000000..469a884008457 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.it.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + L'elenco dei simboli vietati contiene un duplicato. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Il simbolo '{0}' è indicato più volte nell'elenco delle API vietate + + + + The list of banned symbols contains a duplicate + L'elenco dei simboli vietati contiene un duplicato + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute abilita una versione con restrizioni di InternalsVisibleToAttribute che limita l'accesso ai simboli interni a quelli inclusi negli spazi dei nomi specificati. Ogni assembly a cui viene fatto riferimento può accedere solo ai simboli interni definiti negli spazi dei nomi con restrizioni consentiti dall'assembly di riferimento. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + L'accesso esterno al simbolo interno '{0}' non è consentito. L'assembly '{1}' consente solo l'accesso ai simboli interni definiti negli spazi dei nomi seguenti: '{2}'. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + L'accesso esterno ai simboli interni esterni agli spazi dei nomi con restrizioni non è consentito + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Il simbolo è stato contrassegnato come vietato in questo progetto ed è necessario usare un'alternativa. + + + + The symbol '{0}' is banned in this project{1} + Il simbolo '{0}' è vietato in questo progetto{1} + + + + Do not use banned APIs + Non usare API vietate + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ja.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ja.xlf new file mode 100644 index 0000000000000..27353a109fed7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ja.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + 禁止されたシンボルの一覧に重複が含まれています。 + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + シンボル '{0}' が、禁止された API の一覧に複数含まれています + + + + The list of banned symbols contains a duplicate + 禁止されたシンボルの一覧に重複が含まれています + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute では、内部シンボルへのアクセスを指定された名前空間内のシンボルに制限する InternalsVisibleToAttribute の制限付きバージョンを有効にします。参照している各アセンブリは、参照しているアセンブリが許可する制限された名前空間で定義されている内部シンボルにのみアクセスできます。 + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + 内部シンボル '{0}' への外部アクセスは禁止されています。アセンブリ '{1}' では、次の名前空間で定義されている内部シンボルへのアクセスのみが許可されています: '{2}'。 + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + 制限された名前空間外の内部シンボルへの外部アクセス禁止 + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + シンボルはこのプロジェクトで禁止とマークされているため、ほかのものを使用する必要があります。 + + + + The symbol '{0}' is banned in this project{1} + シンボル '{0}' は、このプロジェクト {1} では禁止されています + + + + Do not use banned APIs + 禁止 API を使用しない + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ko.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ko.xlf new file mode 100644 index 0000000000000..d59be1226fb17 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ko.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + 금지된 기호 목록에 중복이 있습니다. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + '{0}' 기호가 금지된 API 목록에 여러 번 나열됩니다. + + + + The list of banned symbols contains a duplicate + 금지된 기호 목록에 중복이 있습니다. + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute로 지정된 네임스페이스 내에서 내부 기호에 대한 액세스를 제한하는 제한된 버전의 InternalsVisibleToAttribute를 사용할 수 있습니다. 각 참조 어셈블리는 참조된 어셈블리에서 허용하는 제한된 네임스페이스에 정의된 내부 기호에만 액세스할 수 있습니다. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + 내부 기호 '{0}'에 대한 외부 액세스가 허용되지 않습니다. '{1}' 어셈블리는 다음 네임스페이스에 정의된 내부 기호에 대한 액세스만 허용합니다. '{2}'. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + 제한된 네임스페이스 외부에서 내부 기호에 대한 외부 액세스는 허용되지 않습니다. + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + 해당 기호는 이 프로젝트에서 금지됨으로 표시되었고 대체 항목을 대신 사용해야 합니다. + + + + The symbol '{0}' is banned in this project{1} + '{0}' 기호는 이 프로젝트{1}에서 금지됩니다. + + + + Do not use banned APIs + 금지된 API를 사용하지 마세요. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pl.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pl.xlf new file mode 100644 index 0000000000000..e03885ad7896c --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pl.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + Lista zabronionych symboli zawiera duplikat. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Symbol „{0}” jest uwzględniony wiele razy na liście zabronionych interfejsów API + + + + The list of banned symbols contains a duplicate + Lista zabronionych symboli zawiera duplikat + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + Parametr RestrictedInternalsVisibleToAttribute włącza ograniczoną wersję parametru InternalsVisibleToAttribute, który ogranicza dostęp do symboli wewnętrznych do tych, które są w określonych przestrzeniach nazw. Każdy odwołujący się zestaw może uzyskać dostęp tylko do symboli wewnętrznych zdefiniowanych w przestrzeniach nazw z ograniczeniami, na które zezwala przywoływany zestaw. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + Zewnętrzny dostęp do symbolu wewnętrznego „{0}” jest zabroniony. Zestaw „{1}” umożliwia dostęp tylko do wewnętrznych symboli zdefiniowanych w następujących przestrzeniach nazw: „{2}”. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + Zewnętrzny dostęp do symboli wewnętrznych poza przestrzeniami nazw z ograniczeniami jest zabroniony. + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Symbol został oznaczony jako zabroniony w tym projekcie i zamiast niego powinien być używany alternatywny. + + + + The symbol '{0}' is banned in this project{1} + Symbol „{0}” jest zabroniony w projekcie {1} + + + + Do not use banned APIs + Nie używaj zablokowanych interfejsów API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pt-BR.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pt-BR.xlf new file mode 100644 index 0000000000000..6af075bc05b6f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.pt-BR.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + A lista de símbolos banidos contém uma duplicata. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + O símbolo '{0}' está listado várias vezes na lista de APIs banidas + + + + The list of banned symbols contains a duplicate + A lista de símbolos banidos contém uma duplicata + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute habilita uma versão restrita do InternalsVisibleToAttribute que limita o acesso a símbolos internos aos que estão nos namespaces especificados. Cada assembly de referência só pode acessar os símbolos internos definidos nos namespaces restritos que o assembly referenciado permite. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + O acesso externo ao símbolo interno '{0}' é proibido. O assembly '{1}' permite acesso somente aos símbolos internos definidos nos seguintes namespaces: '{2}'. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + O acesso externo a símbolos internos fora dos namespaces restritos é proibido + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + O símbolo foi marcado como banido neste projeto. Nesse caso, é necessário usar um símbolo alternativo. + + + + The symbol '{0}' is banned in this project{1} + O símbolo '{0}' foi banido deste projeto {1} + + + + Do not use banned APIs + Não use APIs banidas + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ru.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ru.xlf new file mode 100644 index 0000000000000..7620e61e1b675 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.ru.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + Список запрещенных символов содержит дубликат. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + Символ "{0}" указан несколько раз в списке запрещенных API + + + + The list of banned symbols contains a duplicate + Список запрещенных символов содержит дубликат + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute включает ограниченную версию InternalsVisibleToAttribute, которая разрешает доступ только к внутренним символам, входящим в заданные пространства имен. Каждая ссылочная сборка может обращаться только к внутренним символам, определенным в ограниченных пространствах имен, допускаемых сборкой, на которую указывает ссылка. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + Внешний доступ к внутреннему символу "{0}" запрещен. Сборка "{1}" разрешает доступ только к внутренним символам, определенным в следующих пространствах имен: "{2}". + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + Внешний доступ к внутренним символам за пределами ограниченных пространств имен запрещен + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Символ был помечен как запрещенный в этом проекте, вместо него следует использовать альтернативный вариант. + + + + The symbol '{0}' is banned in this project{1} + Символ "{0}" не может использоваться в этом проекте {1} + + + + Do not use banned APIs + Не использовать запрещенные API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.tr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.tr.xlf new file mode 100644 index 0000000000000..4d57262b864e4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.tr.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + Yasaklanan semboller listesi yinelenen bir öğe içeriyor. + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + '{0}' sembolü yasaklanan API'ler listesinde birden çok kez listelenmiş + + + + The list of banned symbols contains a duplicate + Yasaklanan semboller listesi yinelenen bir öğe içeriyor + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute, iç simgelere erişimi belirtilen ad alanlarındakilerle sınırlayan kısıtlanmış bir InternalsVisibleToAttribute sürümünü etkinleştirir. Başvuru yapan her derleme yalnızca başvurulan derlemenin izin verdiği kısıtlanmış ad alanlarında tanımlı iç simgelere erişebilir. + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + '{0}' iç sembolüne dışarıdan erişime izin verilmez. '{1}' bütünleştirilmiş kodunda yalnızca şu ad alanlarında tanımlanan iç sembollere erişime izin verilir: '{2}'. + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + İç simgelere kısıtlı ad alanları dışından erişime izin verilmez + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + Sembol bu projede yasaklanan olarak işaretlendiğinden bunun yerine başka bir sembol kullanılması gerekiyor. + + + + The symbol '{0}' is banned in this project{1} + '{0}' sembolü bu {1} projesinde yasaklı + + + + Do not use banned APIs + Yasaklanmış API'leri kullanmayın + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf new file mode 100644 index 0000000000000..f65aac3943bdf --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hans.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + 禁用符号列表中包含重复项。 + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + 符号“{0}”在禁用的 API 列表中多次列出 + + + + The list of banned symbols contains a duplicate + 禁用符号列表中包含重复项 + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute 启用了受限版本的 InternalsVisibleToAttribute,它仅限特定命名空间中的程序集访问内部符号。每个引用程序集都只能访问在被引用程序集允许的受限命名空间中定义的内部符号。 + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + 禁止外部访问内部符号“{0}”。程序集“{1}”仅允许访问在以下命名空间中定义的内部符号:“{2}”。 + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + 禁止外部访问未在受限命名空间中的内部符号 + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + 此项目中已将该符号标记为禁用,应改为使用替换符号。 + + + + The symbol '{0}' is banned in this project{1} + 此项目{1}中禁用符号“{0}” + + + + Do not use banned APIs + 请勿使用禁止的 API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf new file mode 100644 index 0000000000000..7754ebba7e9fb --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Core/xlf/BannedApiAnalyzerResources.zh-Hant.xlf @@ -0,0 +1,52 @@ + + + + + + The list of banned symbols contains a duplicate. + 禁止符號清單出現重複的項目。 + + + + The symbol '{0}' is listed multiple times in the list of banned APIs + 在禁止的 API 清單中,列出了多次符號 '{0}' + + + + The list of banned symbols contains a duplicate + 禁止的符號清單包含重複項目 + + + + RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + RestrictedInternalsVisibleToAttribute 是受限版的 InternalsVisibleToAttribute,會限制對指定命名空間中內部符號的存取。參考的每一個組件都只能存取所參考組件允許之受限命名空間中定義的內部符號。 + + + + External access to internal symbol '{0}' is prohibited. Assembly '{1}' only allows access to internal symbols defined in the following namespace(s): '{2}'. + 禁止從外部存取內部符號 '{0}'。組件 '{1}' 只得存取下列命名空間中所定義的內部符號: '{2}'。 + + + + External access to internal symbols outside the restricted namespace(s) is prohibited + 外部無法存取受限命名空間外部的內部符號 + + + + The symbol has been marked as banned in this project, and an alternate should be used instead. + 符號已標示為在此專案中禁用,因此應改用替代符號。 + + + + The symbol '{0}' is banned in this project{1} + 此專案中禁用符號 '{0}'{1} + + + + Do not use banned APIs + 請勿使用禁止的 API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Directory.Build.props b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Directory.Build.props new file mode 100644 index 0000000000000..9dd0ddea9df26 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Directory.Build.props @@ -0,0 +1,6 @@ + + + + true + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.md new file mode 100644 index 0000000000000..7489b7e7db1e0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.md @@ -0,0 +1,37 @@ +# Microsoft.CodeAnalysis.BannedApiAnalyzers + +## [RS0030](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md): Do not use banned APIs + +The symbol has been marked as banned in this project, and an alternate should be used instead. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0031](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md): The list of banned symbols contains a duplicate + +The list of banned symbols contains a duplicate. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0035: External access to internal symbols outside the restricted namespace(s) is prohibited + +RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Error| +|CodeFix|False| +--- diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.sarif b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.sarif new file mode 100644 index 0000000000000..cd83b907906f2 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.sarif @@ -0,0 +1,141 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Microsoft.CodeAnalysis.BannedApiAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0030": { + "id": "RS0030", + "shortDescription": "Do not use banned APIs", + "fullDescription": "The symbol has been marked as banned in this project, and an alternate should be used instead.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "CSharpSymbolIsBannedAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0031": { + "id": "RS0031", + "shortDescription": "The list of banned symbols contains a duplicate", + "fullDescription": "The list of banned symbols contains a duplicate.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "CSharpSymbolIsBannedAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0035": { + "id": "RS0035", + "shortDescription": "External access to internal symbols outside the restricted namespace(s) is prohibited", + "fullDescription": "RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows.", + "defaultLevel": "error", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "CSharpRestrictedInternalsVisibleToAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0030": { + "id": "RS0030", + "shortDescription": "Do not use banned APIs", + "fullDescription": "The symbol has been marked as banned in this project, and an alternate should be used instead.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "BasicSymbolIsBannedAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0031": { + "id": "RS0031", + "shortDescription": "The list of banned symbols contains a duplicate", + "fullDescription": "The list of banned symbols contains a duplicate.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "BasicSymbolIsBannedAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0035": { + "id": "RS0035", + "shortDescription": "External access to internal symbols outside the restricted namespace(s) is prohibited", + "fullDescription": "RestrictedInternalsVisibleToAttribute enables a restricted version of InternalsVisibleToAttribute that limits access to internal symbols to those within specified namespaces. Each referencing assembly can only access internal symbols defined in the restricted namespaces that the referenced assembly allows.", + "defaultLevel": "error", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "BasicRestrictedInternalsVisibleToAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..cf07dc392a0bc --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/RulesMissingDocumentation.md @@ -0,0 +1,7 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +RS0030 | | Do not use banned APIs | +RS0031 | | The list of banned symbols contains a duplicate | +RS0035 | | External access to internal symbols outside the restricted namespace(s) is prohibited | diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/Microsoft.CodeAnalysis.BannedApiAnalyzers.Setup.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/Microsoft.CodeAnalysis.BannedApiAnalyzers.Setup.csproj new file mode 100644 index 0000000000000..60bd69ad7b691 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/Microsoft.CodeAnalysis.BannedApiAnalyzers.Setup.csproj @@ -0,0 +1,26 @@ + + + + net472 + + true + false + false + false + false + false + true + + + + + + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..f3cac2f7c462b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,27 @@ + + + + + + Banned API Analyzers + Banned API Analyzers + EULA.txt + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests.csproj new file mode 100644 index 0000000000000..7315c34369dd6 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests.csproj @@ -0,0 +1,23 @@ + + + + $(NetRoslyn) + true + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/RestrictedInternalsVisibleToAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/RestrictedInternalsVisibleToAnalyzerTests.cs new file mode 100644 index 0000000000000..bdf1aa0ee4287 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/RestrictedInternalsVisibleToAnalyzerTests.cs @@ -0,0 +1,2232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; + +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.CSharpRestrictedInternalsVisibleToAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.BasicRestrictedInternalsVisibleToAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests +{ + public class RestrictedInternalsVisibleToAnalyzerTests + { + private static DiagnosticResult GetCSharpResultAt(int markupId, string bannedSymbolName, string restrictedNamespaces) => + VerifyCS.Diagnostic() + .WithLocation(markupId) + .WithArguments(bannedSymbolName, ApiProviderProjectName, restrictedNamespaces); + + private static DiagnosticResult GetBasicResultAt(int markupId, string bannedSymbolName, string restrictedNamespaces) => + VerifyVB.Diagnostic() + .WithLocation(markupId) + .WithArguments(bannedSymbolName, ApiProviderProjectName, restrictedNamespaces); + + private static DiagnosticResult GetBasicResultAt(int startLine, int startColumn, int endLine, int endColumn, string bannedSymbolName, string restrictedNamespaces) => + VerifyVB.Diagnostic() + .WithSpan(startLine, startColumn, endLine, endColumn) + .WithArguments(bannedSymbolName, ApiProviderProjectName, restrictedNamespaces); + + private const string ApiProviderProjectName = nameof(ApiProviderProjectName); + private const string ApiConsumerProjectName = nameof(ApiConsumerProjectName); + + private const string CSharpApiProviderFileName = "ApiProviderFileName.cs"; + private const string VisualBasicApiProviderFileName = "ApiProviderFileName.vb"; + + private const string CSharpRestrictedInternalsVisibleToAttribute = @" +namespace System.Runtime.CompilerServices +{ + [System.AttributeUsage(System.AttributeTargets.Assembly, AllowMultiple = true)] + internal class RestrictedInternalsVisibleToAttribute : System.Attribute + { + public RestrictedInternalsVisibleToAttribute(string assemblyName, params string[] restrictedNamespaces) + { + } + } +}"; + private const string VisualBasicRestrictedInternalsVisibleToAttribute = @" +Namespace System.Runtime.CompilerServices + + Friend Class RestrictedInternalsVisibleToAttribute + Inherits System.Attribute + + Public Sub New(ByVal assemblyName As String, ParamArray restrictedNamespaces As String()) + End Sub + End Class +End Namespace"; + + private async Task VerifyCSharpAsync(string apiProviderSource, string apiConsumerSource, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { apiConsumerSource }, + AdditionalProjects = + { + [ApiProviderProjectName] = + { + Sources = + { + (CSharpApiProviderFileName, apiProviderSource + CSharpRestrictedInternalsVisibleToAttribute), + }, + }, + }, + AdditionalProjectReferences = { ApiProviderProjectName }, + }, + SolutionTransforms = + { + ApplySolutionTransforms, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private async Task VerifyBasicAsync(string apiProviderSource, string apiConsumerSource, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestState = + { + Sources = { apiConsumerSource }, + AdditionalProjects = + { + [ApiProviderProjectName] = + { + Sources = + { + (VisualBasicApiProviderFileName, apiProviderSource + VisualBasicRestrictedInternalsVisibleToAttribute), + }, + }, + }, + AdditionalProjectReferences = { ApiProviderProjectName }, + }, + SolutionTransforms = + { + ApplySolutionTransforms, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static Solution ApplySolutionTransforms(Solution solution, ProjectId apiConsumerProjectId) + { + return solution.WithProjectAssemblyName(apiConsumerProjectId, ApiConsumerProjectName); + } + + [Fact] + public async Task CSharp_NoIVT_NoRestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.{|CS0122:C1|} c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_NoIVT_NoRestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As {|BC30389:N1.C1|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_NoRestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_NoRestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_NoIVT_RestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""NonExistentNamespace"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.{|CS0122:C1|} c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_NoIVT_RestrictedIVT_NoDiagnosticAsync() + { + var apiProviderSource = @" + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As {|BC30389:N1.C1|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_BasicScenario_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_BasicScenario_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_BasicScenario_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M({|#0:N1.C1|} c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_BasicScenario_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As {|#0:N1.C1|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_MultipleAttributes_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 { } +} + +namespace N2 +{ + internal class C2 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c1, N2.C2 c2) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_MultipleAttributes_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace + +Namespace N2 + Friend Class C2 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c1 As N1.C1, c2 As N2.C2) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_MultipleAttributes_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 { } +} + +namespace N2 +{ + internal class C2 { } +} + +namespace N3 +{ + internal class C3 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c1, N2.C2 c2, {|#0:N3.C3|} c3) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N3.C3", "N1, N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_MultipleAttributes_DiagnosticAsync() + { + var apiProviderSource = @" + + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace + +Namespace N2 + Friend Class C2 + End Class +End Namespace + +Namespace N3 + Friend Class C3 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c1 As N1.C1, c2 As N2.C2, c3 As {|#0:N3.C3|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N3.C3", "N1, N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_ProjectNameMismatch_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""XYZ"", ""NonExistentNamespace"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_ProjectNameMismatch_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_ProjectNameMismatch_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""XYZ"", ""N1"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M({|#0:N1.C1|} c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_ProjectNameMismatch_DiagnosticAsync() + { + var apiProviderSource = @" + + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As {|#0:N1.C1|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_NoRestrictedNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_NoRestrictedNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_QualifiedNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1.N2"")] + +namespace N1 +{ + namespace N2 + { + internal class C1 { } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.N2.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_QualifiedNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Namespace N2 + Friend Class C1 + End Class + End Namespace +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.N2.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_QualifiedNamespace_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1.N2"")] + +namespace N1 +{ + namespace N2 + { + internal class C1 { } + } + + internal class C3 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.N2.C1 c1, {|#0:N1.C3|} c3) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C3", "N1.N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_QualifiedNamespace_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Namespace N2 + Friend Class C1 + End Class + End Namespace + + Friend Class C3 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c1 As N1.N2.C1, c3 As {|#0:N1.C3|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C3", "N1.N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_AncestorNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + namespace N2 + { + internal class C1 { } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.N2.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_AncestorNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Namespace N2 + Friend Class C1 + End Class + End Namespace +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.N2.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_AncestorNamespace_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1.N2"")] + +namespace N1 +{ + namespace N2 + { + internal class C1 { } + } + + internal class C2 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.N2.C1 c1, {|#0:N1.C2|} c2) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C2", "N1.N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_AncestorNamespace_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Namespace N2 + Friend Class C1 + End Class + End Namespace + + Friend Class C2 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c1 As N1.N2.C1, c2 As {|#0:N1.C2|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C2", "N1.N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_QualifiedAndAncestorNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"", ""N1.N2"")] + +namespace N1 +{ + namespace N2 + { + internal class C1 { } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.N2.C1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_QualifiedAndAncestorNamespace_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Namespace N2 + Friend Class C1 + End Class + End Namespace +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.N2.C1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_NestedType_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 { internal class Nested { } } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c, N1.C1.Nested nested) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_NestedType_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + Friend Class Nested + End Class + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1, nested As N1.C1.Nested) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_NestedType_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 { internal class Nested { } } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c, {|#0:N1.C1.Nested|} nested) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Nested", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_NestedType_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend Class Nested + End Class + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1, nested As {|#0:N1.C1.Nested|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.Nested", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInAttributes_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 : System.Attribute + { + public C1(object o) { } + } +}"; + + var apiConsumerSource = @" +[N1.C1(typeof(N1.C1))] +class C2 +{ + [N1.C1(typeof(N1.C1))] + private readonly int field; + + [N1.C1(typeof(N1.C1))] + private int Property { [N1.C1(typeof(N1.C1))] get; } + + [N1.C1(typeof(N1.C1))] + private event System.EventHandler X; + + [N1.C1(typeof(N1.C1))] + [return: N1.C1(typeof(N1.C1))] + int M([N1.C1(typeof(N1.C1))]object c) + { + return 0; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInAttributes_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + Inherits System.Attribute + Public Sub New(obj As Object) + End Sub + End Class +End Namespace"; + + var apiConsumerSource = @" + +Class C2 + + Private ReadOnly field As Integer + + + Private ReadOnly Property [Property] As Integer + + + Private Event X As System.EventHandler + + + Private Function M( ByVal c As Object) As Integer + Return 0 + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInAttributes_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 : System.Attribute + { + public C1(object o) { } + } +}"; + + var apiConsumerSource = @" +[{|#16:{|#0:N1.C1|}(typeof({|#1:N1.C1|}))|}] +class C2 +{ + [{|#17:{|#2:N1.C1|}(typeof({|#3:N1.C1|}))|}] + private readonly int field; + + [{|#18:{|#4:N1.C1|}(typeof({|#5:N1.C1|}))|}] + private int Property { [{|#19:{|#6:N1.C1|}(typeof({|#7:N1.C1|}))|}] get; } + + [{|#20:{|#8:N1.C1|}(typeof({|#9:N1.C1|}))|}] + private event System.EventHandler X; + + [{|#21:{|#10:N1.C1|}(typeof({|#11:N1.C1|}))|}] + [return: {|#22:{|#12:N1.C1|}(typeof({|#13:N1.C1|}))|}] + int M([{|#23:{|#14:N1.C1|}(typeof({|#15:N1.C1|}))|}]object c) + { + return 0; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1", "N2"), + GetCSharpResultAt(1, "N1.C1", "N2"), + GetCSharpResultAt(2, "N1.C1", "N2"), + GetCSharpResultAt(3, "N1.C1", "N2"), + GetCSharpResultAt(4, "N1.C1", "N2"), + GetCSharpResultAt(5, "N1.C1", "N2"), + GetCSharpResultAt(6, "N1.C1", "N2"), + GetCSharpResultAt(7, "N1.C1", "N2"), + GetCSharpResultAt(8, "N1.C1", "N2"), + GetCSharpResultAt(9, "N1.C1", "N2"), + GetCSharpResultAt(10, "N1.C1", "N2"), + GetCSharpResultAt(11, "N1.C1", "N2"), + GetCSharpResultAt(12, "N1.C1", "N2"), + GetCSharpResultAt(13, "N1.C1", "N2"), + GetCSharpResultAt(14, "N1.C1", "N2"), + GetCSharpResultAt(15, "N1.C1", "N2"), + GetCSharpResultAt(16, "N1.C1.C1", "N2"), + GetCSharpResultAt(17, "N1.C1.C1", "N2"), + GetCSharpResultAt(18, "N1.C1.C1", "N2"), + GetCSharpResultAt(19, "N1.C1.C1", "N2"), + GetCSharpResultAt(20, "N1.C1.C1", "N2"), + GetCSharpResultAt(21, "N1.C1.C1", "N2"), + GetCSharpResultAt(22, "N1.C1.C1", "N2"), + GetCSharpResultAt(23, "N1.C1.C1", "N2") + ); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInAttributes_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + Inherits System.Attribute + Public Sub New(obj As Object) + End Sub + End Class +End Namespace"; + + var apiConsumerSource = @" +<{|#14:{|#0:N1.C1|}(GetType({|#1:N1.C1|}))|}> +Class C2 + <{|#15:{|#2:N1.C1|}(GetType({|#3:N1.C1|}))|}> + Private ReadOnly field As Integer + + <{|#16:{|#4:N1.C1|}(GetType({|#5:N1.C1|}))|}> + Private ReadOnly Property [Property] As Integer + + <{|#17:{|#6:N1.C1|}(GetType({|#7:N1.C1|}))|}> + Private Event X As System.EventHandler + + <{|#18:{|#8:N1.C1|}(GetType({|#9:N1.C1|}))|}> + Private Function M(<{|#19:{|#10:N1.C1|}(GetType({|#11:N1.C1|}))|}> ByVal c As Object) As <{|#20:{|#12:N1.C1|}(GetType({|#13:N1.C1|}))|}> Integer + Return 0 + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1", "N2"), + GetBasicResultAt(1, "N1.C1", "N2"), + GetBasicResultAt(2, "N1.C1", "N2"), + GetBasicResultAt(3, "N1.C1", "N2"), + GetBasicResultAt(4, "N1.C1", "N2"), + GetBasicResultAt(5, "N1.C1", "N2"), + GetBasicResultAt(6, "N1.C1", "N2"), + GetBasicResultAt(7, "N1.C1", "N2"), + GetBasicResultAt(8, "N1.C1", "N2"), + GetBasicResultAt(9, "N1.C1", "N2"), + GetBasicResultAt(10, "N1.C1", "N2"), + GetBasicResultAt(11, "N1.C1", "N2"), + GetBasicResultAt(12, "N1.C1", "N2"), + GetBasicResultAt(13, "N1.C1", "N2"), + GetBasicResultAt(14, "N1.C1.New", "N2"), + GetBasicResultAt(15, "N1.C1.New", "N2"), + GetBasicResultAt(16, "N1.C1.New", "N2"), + GetBasicResultAt(17, "N1.C1.New", "N2"), + GetBasicResultAt(18, "N1.C1.New", "N2"), + GetBasicResultAt(19, "N1.C1.New", "N2"), + GetBasicResultAt(20, "N1.C1.New", "N2") + ); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInDeclaration_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @" +class C2 : N1.C1 +{ + private readonly N1.C1 field; + private N1.C1 Property { get; } + private N1.C1 this[N1.C1 index] { get => null; } + N1.C1 M(N1.C1 c) + { + return null; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInDeclaration_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Inherits N1.C1 + + Private ReadOnly field As N1.C1 + Private ReadOnly Property [Property] As N1.C1 + + Private ReadOnly Property Item(index As N1.C1) As N1.C1 + Get + Return Nothing + End Get + End Property + + Private Function M(c As N1.C1) As N1.C1 + Return Nothing + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInDeclaration_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 { } + internal class C2 { } + internal class C3 { } + internal class C4 { } + internal class C5 { } + internal class C6 { } + internal class C7 { } +}"; + + var apiConsumerSource = @" +class B : {|#0:N1.C1|} +{ + private readonly {|#1:N1.C2|} field; + private {|#2:N1.C3|} Property { get; } + private {|#3:N1.C4|} this[{|#4:N1.C5|} index] { get => null; } + {|#5:N1.C6|} M({|#6:N1.C7|} c) + { + return null; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1", "N2"), + GetCSharpResultAt(1, "N1.C2", "N2"), + GetCSharpResultAt(2, "N1.C3", "N2"), + GetCSharpResultAt(3, "N1.C4", "N2"), + GetCSharpResultAt(4, "N1.C5", "N2"), + GetCSharpResultAt(5, "N1.C6", "N2"), + GetCSharpResultAt(6, "N1.C7", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInDeclaration_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class + Friend Class C2 + End Class + Friend Class C3 + End Class + Friend Class C4 + End Class + Friend Class C5 + End Class + Friend Class C6 + End Class + Friend Class C7 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class B + Inherits {|#0:N1.C1|} + + Private ReadOnly field As {|#1:N1.C2|} + Private ReadOnly Property [Property] As {|#2:N1.C3|} + + Private ReadOnly Property Item(index As {|#3:N1.C4|}) As {|#4:N1.C5|} + Get + Return Nothing + End Get + End Property + + Private Function M(c As {|#5:N1.C6|}) As {|#6:N1.C7|} + Return Nothing + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1", "N2"), + GetBasicResultAt(1, "N1.C2", "N2"), + GetBasicResultAt(2, "N1.C3", "N2"), + GetBasicResultAt(3, "N1.C4", "N2"), + GetBasicResultAt(4, "N1.C5", "N2"), + GetBasicResultAt(5, "N1.C6", "N2"), + GetBasicResultAt(6, "N1.C7", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInExecutableCode_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 : System.Attribute + { + public C1(object o) { } + public C1 GetC() => this; + public C1 GetC(C1 c) => c; + public object GetObject() => this; + public C1 Field; + public C1 Property { get; set; } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + // Field initializer + private readonly N1.C1 field = new N1.C1(null); + + // Property initializer + private N1.C1 Property { get; } = new N1.C1(null); + + void M(object c) + { + var x = new N1.C1(null); // Object creation + N1.C1 y = x.GetC(); // Invocation + var z = y.GetC(x); // Parameter type + _ = (N1.C1)z.GetObject(); // Conversion + _ = z.Field; // Field reference + _ = z.Property; // Property reference + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInExecutableCode_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + Inherits System.Attribute + + Public Sub New(ByVal o As Object) + End Sub + + Public Function GetC() As C1 + Return Me + End Function + + Public Function GetC(ByVal c As C1) As C1 + Return c + End Function + + Public Function GetObject() As Object + Return Me + End Function + + Public Field As C1 + Public Property [Property] As C1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private ReadOnly field As N1.C1 = New N1.C1(Nothing) + Private ReadOnly Property [Property] As N1.C1 = New N1.C1(Nothing) + + Private Sub M(ByVal c As Object) + Dim x = New N1.C1(Nothing) + Dim y As N1.C1 = x.GetC() + Dim z = y.GetC(x) + Dim unused1 = CType(z.GetObject(), N1.C1) + Dim unused2 = z.Field + Dim unused3 = z.[Property] + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalMember_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + public class C1 + { + internal int Field; + } +}"; + + var apiConsumerSource = @" +class C2 +{ + int M(N1.C1 c) + { + return c.Field; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalMember_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend Field As Integer + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Function M(c As N1.C1) As Integer + Return c.Field + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalField_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + internal int Field; + } +}"; + + var apiConsumerSource = @" +class C2 +{ + int M(N1.C1 c) + { + return {|#0:c.Field|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Field", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalField_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend Field As Integer + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Function M(c As N1.C1) As Integer + Return {|#0:c.Field|} + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.Field", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalMethod_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + internal int Method() => 0; + } +}"; + + var apiConsumerSource = @" +class C2 +{ + int M(N1.C1 c) + { + return {|#0:c.Method()|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Method", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalMethod_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend Function Method() As Integer + Return 0 + End Function + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Function M(c As N1.C1) As Integer + Return {|#0:c.Method()|} + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.Method", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalExtensionMethod_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + public class C1 + { + } + + public static class C1Extensions + { + internal static int Method(this C1 c1) => 0; + } +}"; + + var apiConsumerSource = @"using static N1.C1Extensions; +class C2 +{ + int M(N1.C1 c) + { + return c.Method(); + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalExtensionMethod_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + End Class + + Public Module C1Extensions + + Friend Function Method(c As C1) As Integer + Return 0 + End Function + End Module +End Namespace"; + + var apiConsumerSource = @"Imports N1.C1Extensions +Class C2 + Private Function M(c As N1.C1) As Integer + Return c.Method() + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalExtensionMethod_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + } + + public static class C1Extensions + { + internal static int Method(this C1 c1) => 0; + } +}"; + + var apiConsumerSource = @"using static N1.C1Extensions; +class C2 +{ + int M(N1.C1 c) + { + return {|#0:c.Method()|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Method", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalExtensionMethod_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + End Class + + Public Module C1Extensions + + Friend Function Method(c As C1) As Integer + Return 0 + End Function + End Module +End Namespace"; + + var apiConsumerSource = @"Imports N1.C1Extensions +Class C2 + Private Function M(c As N1.C1) As Integer + Return {|#0:c.Method()|} + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.Method", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalProperty_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + internal int Property { get => 0; } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + int M(N1.C1 c) + { + return {|#0:c.Property|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Property", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalProperty_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend ReadOnly Property [Property] As Integer + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Function M(c As N1.C1) As Integer + Return {|#0:c.[Property]|} + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.Property", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalEvent_DiagnosticAsync() + { + var apiProviderSource = @"using System; + +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + internal event EventHandler Event; + } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + _ = {|#0:c.{|CS0070:Event|}|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.Event", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalEvent_DiagnosticAsync() + { + var apiProviderSource = @"Imports System + + + + +Namespace N1 + Public Class C1 + Friend Event [Event] As EventHandler + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M(c As N1.C1) + Dim unused = {|BC32022:c.[Event]|} + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(4, 22, 4, 31, "N1.C1.Event", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_PublicTypeInternalConstructor_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + public class C1 + { + internal C1() { } + } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M() + { + var c = {|#0:new N1.C1()|}; + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1.C1", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_PublicTypeInternalConstructor_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Public Class C1 + Friend Sub New() + End Sub + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Sub M() + Dim c = {|#0:New N1.C1()|} + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1.New", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_Conversions_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal interface I1 { } + public class C1 : I1 { } +}"; + + var apiConsumerSource = @" +class C2 +{ + void M(N1.C1 c) + { + _ = ({|#0:N1.I1|})c; // Explicit + {|#1:N1.I1|} y = c; // Implicit + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.I1", "N2"), + GetCSharpResultAt(1, "N1.I1", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_Conversions_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Interface I1 + End Interface + + Public Class C1 + Implements I1 + End Class +End Namespace"; + + var apiConsumerSource = @" +Class C2 + Private Function M(c As N1.C1) As Integer + Dim x = CType(c, {|#0:N1.I1|}) + Dim y As {|#1:N1.I1|} = c + End Function +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.I1", "N2"), + GetCSharpResultAt(1, "N1.I1", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInTypeArgument_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 + { + public C1 GetC1() => null; + } + + internal class C3 { } +}"; + + var apiConsumerSource = @" +using N1; +class C2 : C1 +{ + void M(C1 c1, C1 c2) + { + _ = c2.GetC1(); + _ = c2.GetC1>(); + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInTypeArgument_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1(Of T) + Public Function GetC1(Of U)() As C1(Of Object) + Return Nothing + End Function + End Class + + Friend Class C3 + End Class +End Namespace"; + + var apiConsumerSource = @" +Imports N1 + +Class C2 + Inherits C1(Of C3) + + Private Sub M(ByVal c1 As C1(Of C3), ByVal c2 As C1(Of Object)) + Dim unused1 = c2.GetC1(Of C3)() + Dim unused2 = c2.GetC1(Of C1(Of C3))() + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsageInTypeArgument_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C3 { } + internal class C4 { } + internal class C5 { } + internal class C6 { } + +} + +namespace N2 +{ + internal class C1 + { + public C1 GetC1() => null; + } +} +"; + + var apiConsumerSource = @" +using N1; +using N2; + +class C2 : C1<{|#0:C3|}> +{ + void M(C1<{|#1:C4|}> c1, C1 c2) + { + _ = c2.GetC1<{|#2:C5|}>(); + _ = c2.GetC1>(); + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C3", "N2"), + GetCSharpResultAt(1, "N1.C4", "N2"), + GetCSharpResultAt(2, "N1.C5", "N2"), + GetCSharpResultAt(3, "N1.C6", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsageInTypeArgument_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C3 + End Class + Friend Class C4 + End Class + Friend Class C5 + End Class + Friend Class C6 + End Class +End Namespace + +Namespace N2 + Friend Class C1(Of T) + Public Function GetC1(Of U)() As C1(Of Object) + Return Nothing + End Function + End Class +End Namespace"; + + var apiConsumerSource = @" +Imports N1 +Imports N2 + +Class C2 + Inherits C1(Of {|#0:C3|}) + + Private Sub M(ByVal c1 As C1(Of {|#1:C4|}), ByVal c2 As C1(Of Object)) + Dim unused1 = c2.GetC1(Of {|#2:C5|})() + Dim unused2 = c2.GetC1(Of C1(Of {|#3:C6|}))() + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C3", "N2"), + GetCSharpResultAt(1, "N1.C4", "N2"), + GetCSharpResultAt(2, "N1.C5", "N2"), + GetCSharpResultAt(3, "N1.C6", "N2")); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsingAlias_NoDiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N1"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @"using ImportedC1 = N1.C1; +class C2 +{ + void M(ImportedC1 c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsingAlias_NoDiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @"Imports ImportedC1 = N1.C1 +Class C2 + Private Sub M(c As ImportedC1) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource); + } + + [Fact] + public async Task CSharp_IVT_RestrictedIVT_UsingAlias_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C1 { } +}"; + + var apiConsumerSource = @"using ImportedC1 = N1.C1; +class C2 +{ + void M({|#0:ImportedC1|} c) + { + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C1", "N2")); + } + + [Fact] + public async Task Basic_IVT_RestrictedIVT_UsingAlias_DiagnosticAsync() + { + var apiProviderSource = @" + + + +Namespace N1 + Friend Class C1 + End Class +End Namespace"; + + var apiConsumerSource = @"Imports ImportedC1 = N1.C1 +Class C2 + Private Sub M(c As {|#0:ImportedC1|}) + End Sub +End Class"; + + await VerifyBasicAsync(apiProviderSource, apiConsumerSource, + GetBasicResultAt(0, "N1.C1", "N2")); + } + + [Fact] + [WorkItem(2655, "https://github.com/dotnet/roslyn-analyzers/issues/2655")] + public async Task CSharp_IVT_RestrictedIVT_InternalOperators_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal class C + { + public static implicit operator C(int i) => new C(); + public static explicit operator C(float f) => new C(); + public static C operator +(C c, int i) => c; + public static C operator ++(C c) => c; + public static C operator -(C c) => c; + } +}"; + + var apiConsumerSource = @" +using N1; +class C2 +{ + void M() + { + {|#0:C|} c = {|#1:0|}; // implicit conversion. + c = {|#2:({|#3:C|})1.0f|}; // Explicit conversion. + c = {|#4:c + 1|}; // Binary operator. + {|#5:c++|}; // Increment or decrement. + c = {|#6:-c|}; // Unary operator. + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + GetCSharpResultAt(0, "N1.C", "N2"), + GetCSharpResultAt(1, "N1.C.implicit operator N1.C", "N2"), + GetCSharpResultAt(2, "N1.C.explicit operator N1.C", "N2"), + GetCSharpResultAt(3, "N1.C", "N2"), + GetCSharpResultAt(4, "N1.C.operator +", "N2"), + GetCSharpResultAt(5, "N1.C.operator ++", "N2"), + GetCSharpResultAt(6, "N1.C.operator -", "N2")); + } + + [Fact] + [WorkItem(2655, "https://github.com/dotnet/roslyn-analyzers/issues/2655")] + public async Task CSharp_IVT_RestrictedIVT_TypeArgumentUsage_DiagnosticAsync() + { + var apiProviderSource = @" +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(""ApiConsumerProjectName"")] +[assembly: System.Runtime.CompilerServices.RestrictedInternalsVisibleTo(""ApiConsumerProjectName"", ""N2"")] + +namespace N1 +{ + internal struct C {} +}"; + + var apiConsumerSource = @" +using N1; +namespace N2 +{ + class G + { + class N + { } + + unsafe void M() + { + var b = new G<{|#0:C|}>(); + var c = new G<{|#1:C|}>.N(); + var d = new G.N<{|#2:C|}>(); + var e = new G.N<{|#3:C|}>>.N(); + var f = new G.N>.N(); + var g = new {|#5:C|}[42]; + var h = new G<{|#6:C|}[]>(); + fixed ({|#7:C|}* i = &g[0]) { } + } + } +}"; + + await VerifyCSharpAsync(apiProviderSource, apiConsumerSource, + // Test0.cs(12,27): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(0, "N1.C", "N2"), + // Test0.cs(13,27): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(1, "N1.C", "N2"), + // Test0.cs(14,34): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(2, "N1.C", "N2"), + // Test0.cs(15,36): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(3, "N1.C", "N2"), + // Test0.cs(16,29): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(4, "N1.C", "N2"), + // Test0.cs(17,25): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(5, "N1.C", "N2"), + // Test0.cs(18,27): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(6, "N1.C", "N2"), + // Test0.cs(19,20): error RS0035: External access to internal symbol 'N1.C' is prohibited. Assembly 'ApiProviderProjectName' only allows access to internal symbols defined in the following namespace(s): 'N2' + GetCSharpResultAt(7, "N1.C", "N2")); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs new file mode 100644 index 0000000000000..7aa267b0d2238 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/UnitTests/SymbolIsBannedAnalyzerTests.cs @@ -0,0 +1,2409 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; + +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.BannedApiAnalyzers.CSharpSymbolIsBannedAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.BasicSymbolIsBannedAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.BannedApiAnalyzers.UnitTests +{ + // For specification of document comment IDs see https://learn.microsoft.com/dotnet/csharp/language-reference/language-specification/documentation-comments#processing-the-documentation-file + + public class SymbolIsBannedAnalyzerTests + { + private const string BannedSymbolsFileName = "BannedSymbols.txt"; + + private static DiagnosticResult GetCSharpResultAt(int markupKey, DiagnosticDescriptor descriptor, string bannedMemberName, string message) + => VerifyCS.Diagnostic(descriptor) + .WithLocation(markupKey) + .WithArguments(bannedMemberName, message); + + private static DiagnosticResult GetBasicResultAt(int markupKey, DiagnosticDescriptor descriptor, string bannedMemberName, string message) + => VerifyVB.Diagnostic(descriptor) + .WithLocation(markupKey) + .WithArguments(bannedMemberName, message); + + private static async Task VerifyBasicAnalyzerAsync(string source, string bannedApiText, params DiagnosticResult[] expected) + { + var test = new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (BannedSymbolsFileName, bannedApiText) }, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private static async Task VerifyCSharpAnalyzerAsync(string source, string bannedApiText, params DiagnosticResult[] expected) + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { (BannedSymbolsFileName, bannedApiText) }, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + #region Diagnostic tests + + [Fact] + public async Task NoDiagnosticForNoBannedTextAsync() + { + await VerifyCS.VerifyAnalyzerAsync("class C { }"); + await VerifyVB.VerifyAnalyzerAsync(@"Class C +End Class"); + } + + [Fact] + public async Task NoDiagnosticReportedForEmptyBannedTextAsync() + { + var source = @""; + + var bannedText = @""; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task NoDiagnosticReportedForMultilineBlankBannedTextAsync() + { + var source = @" + + +"; + + var bannedText = @""; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task NoDiagnosticReportedForMultilineMessageOnlyBannedTextAsync() + { + var source = @""; + var bannedText = @" +;first + ;second +;third // comment"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task DiagnosticReportedForDuplicateBannedApiLinesAsync() + { + var source = @""; + var bannedText = @" +{|#0:T:System.Console|} +{|#1:T:System.Console|}"; + + var expected = new DiagnosticResult(SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule) + .WithLocation(1) + .WithLocation(0) + .WithArguments("System.Console"); + await VerifyCSharpAnalyzerAsync(source, bannedText, expected); + } + + [Fact] + public async Task DiagnosticReportedForDuplicateBannedApiLinesWithDifferentIdsAsync() + { + // The colon in the documentation ID is optional. + // Verify that it doesn't cause exceptions when building look ups. + + var source = @""; + var bannedText = @" +{|#0:T:System.Console;Message 1|} +{|#1:TSystem.Console;Message 2|}"; + + var expected = new DiagnosticResult(SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule) + .WithLocation(1) + .WithLocation(0) + .WithArguments("System.Console"); + await VerifyCSharpAnalyzerAsync(source, bannedText, expected); + } + + [Fact] + public async Task DiagnosticReportedForDuplicateBannedApiLinesInDifferentFiles() + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { @"" }, + AdditionalFiles = + { + ("BannedSymbols.txt", @"{|#0:T:System.Console|}") , + ("BannedSymbols.Other.txt", @"{|#1:T:System.Console|}") + }, + }, + ExpectedDiagnostics = + { + new DiagnosticResult(SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule) + .WithLocation(0) + .WithLocation(1) + .WithArguments("System.Console") + } + }; + + await test.RunAsync(); + } + + #region Comments in BannedSymbols.txt tests + + [Fact] + public async Task DiagnosticReportedForDuplicateBannedApiLinesWithCommentsAsync() + { + var source = @""; + var bannedText = @" +{|#0:T:System.Console|} " + '\t' + @" //comment here with whitespace before it +{|#1:T:System.Console|}//should not affect the reported duplicate"; + + var expected = new DiagnosticResult(SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule) + .WithLocation(1) + .WithLocation(0) + .WithArguments("System.Console"); + await VerifyCSharpAnalyzerAsync(source, bannedText, expected); + } + + [Fact] + public async Task DiagnosticReportedForDuplicateBannedApiLinesWithCommentsInDifferentFiles() + { + var test = new VerifyCS.Test + { + TestState = + { + Sources = { @"" }, + AdditionalFiles = + { + ("BannedSymbols.txt", @"{|#0:T:System.Console|} //ignore this") , + ("BannedSymbols.Other.txt", @"{|#1:T:System.Console|} //comment will not affect result") + }, + }, + ExpectedDiagnostics = + { + new DiagnosticResult(SymbolIsBannedAnalyzer.DuplicateBannedSymbolRule) + .WithLocation(0) + .WithLocation(1) + .WithArguments("System.Console") + } + }; + + await test.RunAsync(); + } + + [Fact] + public async Task NoDiagnosticReportedForCommentLine() + { + var source = @""; + var bannedText = @"// this comment line will be ignored"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + await VerifyBasicAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task NoDiagnosticReportedForCommentedOutBannedApiLineAsync() + { + var bannedText = @"//T:N.Banned"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = new Banned(); + } + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As New Banned() + End Sub + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText); + } + + [Fact] + public async Task NoDiagnosticReportedForCommentLineThatBeginsWithWhitespaceAsync() + { + var bannedText = @" // comment here"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = new Banned(); + } + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As New Banned() + End Sub + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText); + } + + [Fact] + public async Task NoDiagnosticReportedForCommentedOutDuplicateBannedApiLinesAsync() + { + var source = @""; + var bannedText = @" +//T:System.Console +//T:System.Console"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + await VerifyBasicAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task DiagnosticReportedForBannedApiLineWithCommentAtTheEndAsync() + { + var bannedText = @"T:N.Banned//comment here"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As {|#0:New Banned()|} + End Sub + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task DiagnosticReportedForInterfaceImplementationAsync() + { + var bannedText = "T:N.IBanned//comment here"; + + var csharpSource = @" +namespace N +{ + interface IBanned { } + class C : {|#0:IBanned|} + { + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "IBanned", "")); + + var visualBasicSource = @" +Namespace N + Interface IBanned : End Interface + Class C + Implements {|#0:IBanned|} + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "IBanned", "")); + + } + + [Fact] + public async Task DiagnosticReportedForClassInheritanceAsync() + { + var bannedText = "T:N.Banned//comment here"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C : {|#0:Banned|} + { + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Inherits {|#0:Banned|} + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + + } + + [Fact] + public async Task DiagnosticReportedForBannedApiLineWithWhitespaceThenCommentAtTheEndAsync() + { + var bannedText = "T:N.Banned \t //comment here"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As {|#0:New Banned()|} + End Sub + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task DiagnosticReportedForBannedApiLineWithMessageThenWhitespaceFollowedByCommentAtTheEndMustNotIncludeWhitespaceInMessageAsync() + { + var bannedText = "T:N.Banned;message \t //comment here"; + + var csharpSource = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + await VerifyCSharpAnalyzerAsync(csharpSource, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", ": message")); + + var visualBasicSource = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As {|#0:New Banned()|} + End Sub + End Class +End Namespace"; + + await VerifyBasicAnalyzerAsync(visualBasicSource, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", ": message")); + } + + #endregion Comments in BannedSymbols.txt tests + + [Fact] + public async Task CSharp_BannedApiFile_MessageIncludedInDiagnosticAsync() + { + var source = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + var bannedText = @"T:N.Banned;Use NonBanned instead"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", ": Use NonBanned instead")); + } + + [Fact] + public async Task CSharp_BannedApiFile_WhiteSpaceAsync() + { + var source = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + var bannedText = @" + T:N.Banned "; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task CSharp_BannedApiFile_WhiteSpaceWithMessageAsync() + { + var source = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + var bannedText = @"T:N.Banned ; Use NonBanned instead "; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", ": Use NonBanned instead")); + } + + [Fact] + public async Task CSharp_BannedApiFile_EmptyMessageAsync() + { + var source = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + var bannedText = @"T:N.Banned;"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task CSharp_BannedApi_MultipleFiles() + { + var source = @" +namespace N +{ + class BannedA { } + class BannedB { } + class NotBanned { } + class C + { + void M() + { + var a = {|#0:new BannedA()|}; + var b = {|#1:new BannedB()|}; + var c = new NotBanned(); + } + } +}"; + + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = + { + ("BannedSymbols.txt", @"T:N.BannedA") , + ("BannedSymbols.Other.txt", @"T:N.BannedB"), + ("OtherFile.txt", @"T:N.NotBanned") + }, + }, + ExpectedDiagnostics = + { + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedA", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedB", "") + } + }; + + await test.RunAsync(); + } + + [Fact] + public async Task CSharp_BannedType_ConstructorAsync() + { + var source = @" +namespace N +{ + class Banned { } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + } + } +}"; + + var bannedText = @" +T:N.Banned"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_ConstructorAsync() + { + var source = @" +namespace N +{ + class C + { + void M() + { + var c = {|#0:new N.C()|}; + } + } +} +"; + + var bannedText = @" +N:N"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_Parent_ConstructorAsync() + { + var source = @" +namespace N.NN +{ + class C + { + void M() + { + var a = {|#0:new N.NN.C()|}; + } + } +} +"; + + var bannedText = @" +N:N"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_MethodGroupAsync() + { + var source = @" +namespace N +{ + delegate void D(); + class C + { + void M() + { + D d = {|#0:M|}; + } + } +} +"; + + var bannedText = @" +N:N"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_PropertyAsync() + { + var source = @" +namespace N +{ + class C + { + public int P { get; set; } + void M() + { + {|#0:P|} = {|#1:P|}; + } + } +} +"; + + var bannedText = @" +N:N"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_MethodAsync() + { + var source = @" +namespace N +{ + interface I + { + void M(); + } +} + +class C +{ + void M() + { + N.I i = null; + {|#0:i.M()|}; + } +}"; + var bannedText = @"N:N"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_TypeOfArgument() + { + var source = @" +namespace N +{ + class Banned { } +} +class C +{ + void M() + { + var type = {|#0:typeof(N.Banned)|}; + } +} +"; + var bannedText = @"N:N"; + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "N", "")); + } + + [Fact] + public async Task CSharp_BannedNamespace_Constituent() + { + var source = @" +class C +{ + void M() + { + var thread = {|#0:new System.Threading.Thread((System.Threading.ThreadStart)null)|}; + } +} +"; + var bannedText = @"N:System.Threading"; + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "System.Threading", "")); + } + + [Fact] + public async Task CSharp_BannedGenericType_ConstructorAsync() + { + var source = @" +class C +{ + void M() + { + var c = {|#0:new System.Collections.Generic.List()|}; + } +}"; + + var bannedText = @" +T:System.Collections.Generic.List`1"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "List", "")); + } + + [Fact] + public async Task CSharp_BannedType_AsTypeArgumentAsync() + { + var source = @" +struct C {} + +class G +{ + class N + { } + + unsafe void M() + { + var b = {|#0:new G()|}; + var c = {|#1:new G.N()|}; + var d = {|#2:new G.N()|}; + var e = {|#3:new G.N>.N()|}; + var f = {|#4:new G.N>.N()|}; + var g = {|#5:new C[42]|}; + var h = {|#6:new G()|}; + fixed (C* i = {|#7:&g[0]|}) { } + } +}"; + + var bannedText = @" +T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(3, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(4, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(5, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(6, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(7, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedNestedType_ConstructorAsync() + { + var source = @" +class C +{ + class Nested { } + void M() + { + var n = {|#0:new Nested()|}; + } +}"; + + var bannedText = @" +T:C.Nested"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Nested", "")); + } + + [Fact] + public async Task CSharp_BannedType_MethodOnNestedTypeAsync() + { + var source = @" +class C +{ + public static class Nested + { + public static void M() { } + } +} + +class D +{ + void M2() + { + {|#0:C.Nested.M()|}; + } +}"; + var bannedText = @" +T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedInterface_MethodAsync() + { + var source = @" +interface I +{ + void M(); +} + +class C +{ + void M() + { + I i = null; + {|#0:i.M()|}; + } +}"; + var bannedText = @"T:I"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "I", "")); + } + + [Fact] + public async Task CSharp_BannedClass_OperatorsAsync() + { + var source = @" +class C +{ + public static implicit operator C(int i) => {|#0:new C()|}; + public static explicit operator C(float f) => {|#1:new C()|}; + public static C operator +(C c, int i) => c; + public static C operator ++(C c) => c; + public static C operator -(C c) => c; + + void M() + { + C c = {|#2:0|}; // implicit conversion. + c = {|#3:(C)1.0f|}; // Explicit conversion. + c = {|#4:c + 1|}; // Binary operator. + {|#5:c++|}; // Increment or decrement. + c = {|#6:-c|}; // Unary operator. + } +}"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(3, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(4, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(5, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(6, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedClass_PropertyAsync() + { + var source = @" +class C +{ + public int P { get; set; } + void M() + { + {|#0:P|} = {|#1:P|}; + } +}"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedClass_FieldAsync() + { + var source = @" +class C +{ + public int F; + void M() + { + {|#0:F|} = {|#1:F|}; + } +}"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedClass_EventAsync() + { + var source = @" +using System; + +class C +{ + public event EventHandler E; + void M() + { + {|#0:E|} += null; + {|#1:E|} -= null; + {|#2:E|}(null, EventArgs.Empty); + } +}"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedClass_MethodGroupAsync() + { + var source = @" +delegate void D(); +class C +{ + void M() + { + D d = {|#0:M|}; + } +} +"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedClass_DocumentationReferenceAsync() + { + var source = @" +class C { } + +/// +class D { } +"; + var bannedText = @"T:C"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task CSharp_BannedAttribute_UsageOnTypeAsync() + { + var source = @" +using System; + +[AttributeUsage(AttributeTargets.All, Inherited = true)] +class BannedAttribute : Attribute { } + +[{|#0:Banned|}] +class C { } +class D : C { } +"; + var bannedText = @"T:BannedAttribute"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task CSharp_BannedAttribute_UsageOnMemberAsync() + { + var source = @" +using System; + +[AttributeUsage(AttributeTargets.All, Inherited = true)] +class BannedAttribute : Attribute { } + +class C +{ + [{|#0:Banned|}] + public int SomeProperty { get; } +} +"; + var bannedText = @"T:BannedAttribute"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task CSharp_BannedAttribute_UsageOnAssemblyAsync() + { + var source = @" +using System; + +[assembly: {|#0:BannedAttribute|}] + +[AttributeUsage(AttributeTargets.All, Inherited = true)] +class BannedAttribute : Attribute { } +"; + + var bannedText = @"T:BannedAttribute"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task CSharp_BannedAttribute_UsageOnModuleAsync() + { + var source = @" +using System; + +[module: {|#0:BannedAttribute|}] + +[AttributeUsage(AttributeTargets.All, Inherited = true)] +class BannedAttribute : Attribute { } +"; + + var bannedText = @"T:BannedAttribute"; + + await VerifyCSharpAnalyzerAsync(source, bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task CSharp_BannedConstructorAsync() + { + var source = @" +namespace N +{ + class Banned + { + public Banned() {} + public Banned(int i) {} + } + class C + { + void M() + { + var c = {|#0:new Banned()|}; + var d = {|#1:new Banned(1)|}; + } + } +}"; + + var bannedText1 = @"M:N.Banned.#ctor"; + var bannedText2 = @"M:N.Banned.#ctor(System.Int32)"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText1, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned.Banned()", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText2, + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned.Banned(int)", "")); + } + + [Fact] + public async Task Csharp_BannedConstructor_AttributeAsync() + { + var source = @" +using System; + +[AttributeUsage(AttributeTargets.All, Inherited = true)] +class BannedAttribute : Attribute +{ + public BannedAttribute() {} + public BannedAttribute(int banned) {} + public BannedAttribute(string notBanned) {} +} + +class C +{ + [{|#0:Banned|}] + public int SomeProperty { get; } + + [{|#1:Banned(1)|}] + public void SomeMethod() {} + + [Banned("""")] + class D {} +} +"; + var bannedText1 = @"M:BannedAttribute.#ctor"; + var bannedText2 = @"M:BannedAttribute.#ctor(System.Int32)"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText1, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute.BannedAttribute()", ""), + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute.BannedAttribute()", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText2, + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute.BannedAttribute(int)", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute.BannedAttribute(int)", "")); + } + + [Fact] + public async Task CSharp_BannedMethodAsync() + { + var source = @" +namespace N +{ + class C + { + public void Banned() {} + public void Banned(int i) {} + public void Banned(T t) {} + + void M() + { + {|#0:Banned()|}; + {|#1:Banned(1)|}; + {|#2:Banned("""")|}; + } + } + + class D + { + public void Banned() {} + public void Banned(int i) {} + public void Banned(U u) {} + + void M() + { + {|#3:Banned()|}; + {|#4:Banned(1)|}; + {|#5:Banned("""")|}; + } + } +}"; + + var bannedText1 = @"M:N.C.Banned"; + var bannedText2 = @"M:N.C.Banned(System.Int32)"; + var bannedText3 = @"M:N.C.Banned``1(``0)"; + var bannedText4 = @"M:N.D`1.Banned()"; + var bannedText5 = @"M:N.D`1.Banned(System.Int32)"; + var bannedText6 = @"M:N.D`1.Banned``1(``0)"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText1, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned()", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText2, + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned(int)", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText3, + GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned(T)", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText4, + GetCSharpResultAt(3, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "D.Banned()", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText5, + GetCSharpResultAt(4, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "D.Banned(int)", "")); + + await VerifyCSharpAnalyzerAsync( + source, + bannedText6, + GetCSharpResultAt(5, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "D.Banned(U)", "")); + } + + [Fact] + public async Task CSharp_BannedPropertyAsync() + { + var source = @" +namespace N +{ + class C + { + public int Banned { get; set; } + + void M() + { + {|#0:Banned|} = {|#1:Banned|}; + } + } +}"; + + var bannedText = @"P:N.C.Banned"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", "")); + } + + [Fact] + public async Task CSharp_BannedFieldAsync() + { + var source = @" +namespace N +{ + class C + { + public int Banned; + + void M() + { + {|#0:Banned|} = {|#1:Banned|}; + } + } +}"; + + var bannedText = @"F:N.C.Banned"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", "")); + } + + [Fact] + public async Task CSharp_BannedEventAsync() + { + var source = @" +namespace N +{ + class C + { + public event System.Action Banned; + + void M() + { + {|#0:Banned|} += null; + {|#1:Banned|} -= null; + {|#2:Banned|}(); + } + } +}"; + + var bannedText = @"E:N.C.Banned"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", ""), + GetCSharpResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", ""), + GetCSharpResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned", "")); + } + + [Fact] + public async Task CSharp_BannedMethodGroupAsync() + { + var source = @" +namespace N +{ + class C + { + public void Banned() {} + + void M() + { + System.Action b = {|#0:Banned|}; + } + } +}"; + var bannedText = @"M:N.C.Banned"; + + await VerifyCSharpAnalyzerAsync( + source, + bannedText, + GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Banned()", "")); + } + + [Fact] + public async Task CSharp_NoDiagnosticClass_TypeOfArgument() + { + var source = @" +class Banned { } +class C +{ + void M() + { + var type = {|#0:typeof(C)|}; + } +} +"; + var bannedText = @"T:Banned"; + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task CSharp_BannedClass_TypeOfArgument() + { + var source = @" +class Banned { } +class C +{ + void M() + { + var type = {|#0:typeof(Banned)|}; + } +} +"; + var bannedText = @"T:Banned"; + await VerifyCSharpAnalyzerAsync(source, bannedText, GetCSharpResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task CSharp_BannedAbstractVirtualMemberAlsoBansOverrides_RootLevelIsBannedAsync() + { + var source = @" +using System; + +namespace N +{ + public abstract class C1 + { + public abstract void Method1(); + public abstract int Property1 { get; set; } + public abstract event Action Event1; + + public virtual void Method2() {} + public virtual int Property2 { get; set; } + public virtual event Action Event2; + } + + public class C2 : C1 + { + public override void Method1() {} + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + {|RS0030:base.Method2()|}; + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M1() + { + {|RS0030:Method1()|}; + if ({|RS0030:Property1|} == 42 && {|RS0030:Event1|} != null) {} + + {|RS0030:Method2()|}; + if ({|RS0030:Property2|} == 42 && {|RS0030:Event2|} != null) {} + } + } + + public class C3 : C2 + { + public override void Method1() + { + {|RS0030:base.Method1()|}; + } + + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + {|RS0030:base.Method2()|}; + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M2() + { + {|RS0030:Method1()|}; + if ({|RS0030:Property1|} == 42 && {|RS0030:Event1|} != null) {} + + {|RS0030:Method2()|}; + if ({|RS0030:Property2|} == 42 && {|RS0030:Event2|} != null) {} + } + } +}"; + + var bannedText = @"M:N.C1.Method1 +P:N.C1.Property1 +E:N.C1.Event1 +M:N.C1.Method2 +P:N.C1.Property2 +E:N.C1.Event2"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task CSharp_BannedAbstractVirtualMemberBansCorrectOverrides_MiddleLevelIsBannedAsync() + { + var source = @" +using System; + +namespace N +{ + public abstract class C1 + { + public abstract void Method1(); + public abstract int Property1 { get; set; } + public abstract event Action Event1; + + public virtual void Method2() {} + public virtual int Property2 { get; set; } + public virtual event Action Event2; + } + + public class C2 : C1 + { + public override void Method1() {} + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + base.Method2(); + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M1() + { + {|RS0030:Method1()|}; + if ({|RS0030:Property1|} == 42 && {|RS0030:Event1|} != null) {} + + {|RS0030:Method2()|}; + if ({|RS0030:Property2|} == 42 && {|RS0030:Event2|} != null) {} + } + } + + public class C3 : C2 + { + public override void Method1() + { + {|RS0030:base.Method1()|}; + } + + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + {|RS0030:base.Method2()|}; + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M2() + { + {|RS0030:Method1()|}; + if ({|RS0030:Property1|} == 42 && {|RS0030:Event1|} != null) {} + + {|RS0030:Method2()|}; + if ({|RS0030:Property2|} == 42 && {|RS0030:Event2|} != null) {} + } + } +}"; + + var bannedText = @"M:N.C2.Method1 +P:N.C2.Property1 +E:N.C2.Event1 +M:N.C2.Method2 +P:N.C2.Property2 +E:N.C2.Event2"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task CSharp_BannedAbstractVirtualMemberBansCorrectOverrides_LeafLevelIsBannedAsync() + { + var source = @" +using System; + +namespace N +{ + public abstract class C1 + { + public abstract void Method1(); + public abstract int Property1 { get; set; } + public abstract event Action Event1; + + public virtual void Method2() {} + public virtual int Property2 { get; set; } + public virtual event Action Event2; + } + + public class C2 : C1 + { + public override void Method1() {} + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + base.Method2(); + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M1() + { + Method1(); + if (Property1 == 42 && Event1 != null) {} + + Method2(); + if (Property2 == 42 && Event2 != null) {} + } + } + + public class C3 : C2 + { + public override void Method1() + { + base.Method1(); + } + + public override int Property1 { get; set; } + public override event Action Event1; + + public override void Method2() + { + base.Method2(); + } + + public override int Property2 { get; set; } + public override event Action Event2; + + void M2() + { + {|RS0030:Method1()|}; + if ({|RS0030:Property1|} == 42 && {|RS0030:Event1|} != null) {} + + {|RS0030:Method2()|}; + if ({|RS0030:Property2|} == 42 && {|RS0030:Event2|} != null) {} + } + } +}"; + + var bannedText = @"M:N.C3.Method1 +P:N.C3.Property1 +E:N.C3.Event1 +M:N.C3.Method2 +P:N.C3.Property2 +E:N.C3.Event2"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task CSharp_InvalidOverrideDefinitionAsync() + { + var source = @" +using System; + +namespace N +{ + public class C1 + { + public void Method1() {} + } + + public class C2 : C1 + { + public override void {|CS0506:Method1|}() {} + + void M1() + { + Method1(); + } + } +}"; + + var bannedText = @"M:N.C1.Method1"; + + await VerifyCSharpAnalyzerAsync(source, bannedText); + } + + [Fact] + public async Task VisualBasic_BannedApi_MultipleFiles() + { + var source = @" +Namespace N + Class BannedA : End Class + Class BannedB : End Class + Class NotBanned : End Class + Class C + Sub M() + Dim a As {|#0:New BannedA()|} + Dim b As {|#1:New BannedB()|} + Dim c As New NotBanned() + End Sub + End Class +End Namespace"; + + var test = new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = + { + ("BannedSymbols.txt", @"T:N.BannedA") , + ("BannedSymbols.Other.txt", @"T:N.BannedB"), + ("OtherFile.txt", @"T:N.NotBanned") + }, + }, + ExpectedDiagnostics = + { + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedA", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedB", "") + } + }; + + await test.RunAsync(); + } + + [Fact] + public async Task VisualBasic_BannedType_ConstructorAsync() + { + var source = @" +Namespace N + Class Banned : End Class + Class C + Sub M() + Dim c As {|#0:New Banned()|} + End Sub + End Class +End Namespace"; + + var bannedText = @"T:N.Banned"; + + await VerifyBasicAnalyzerAsync(source, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Banned", "")); + } + + [Fact] + public async Task VisualBasic_BannedGenericType_ConstructorAsync() + { + var source = @" +Class C + Sub M() + Dim c = {|#0:New System.Collections.Generic.List(Of String)()|} + End Sub +End Class"; + + var bannedText = @" +T:System.Collections.Generic.List`1"; + + await VerifyBasicAnalyzerAsync(source, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "List(Of T)", "")); + } + + [Fact] + public async Task VisualBasic_BannedNestedType_ConstructorAsync() + { + var source = @" +Class C + Class Nested : End Class + Sub M() + Dim n As {|#0:New Nested()|} + End Sub +End Class"; + + var bannedText = @" +T:C.Nested"; + + await VerifyBasicAnalyzerAsync(source, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C.Nested", "")); + } + + [Fact] + public async Task VisualBasic_BannedType_MethodOnNestedTypeAsync() + { + var source = @" +Class C + Public Class Nested + Public Shared Sub M() : End Sub + End Class +End Class + +Class D + Sub M2() + {|#0:C.Nested.M()|} + End Sub +End Class +"; + var bannedText = @" +T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task VisualBasic_BannedInterface_MethodAsync() + { + var source = @" +Interface I + Sub M() +End Interface + +Class C + Sub M() + Dim i As I = Nothing + {|#0:i.M()|} + End Sub +End Class"; + var bannedText = @"T:I"; + + await VerifyBasicAnalyzerAsync(source, bannedText, GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "I", "")); + } + + [Fact] + public async Task VisualBasic_BannedClass_PropertyAsync() + { + var source = @" +Class C + Public Property P As Integer + Sub M() + {|#0:P|} = {|#1:P|} + End Sub +End Class"; + var bannedText = @"T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task VisualBasic_BannedClass_FieldAsync() + { + var source = @" +Class C + Public F As Integer + Sub M() + {|#0:F|} = {|#1:F|} + End Sub +End Class"; + var bannedText = @"T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task VisualBasic_BannedClass_EventAsync() + { + var source = @" +Imports System + +Class C + public Event E As EventHandler + Sub M() + AddHandler {|#0:E|}, Nothing + RemoveHandler {|#1:E|}, Nothing + RaiseEvent {|#2:E|}(Me, EventArgs.Empty) + End Sub +End Class"; + var bannedText = @"T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", ""), + GetBasicResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task VisualBasic_BannedClass_MethodGroupAsync() + { + var source = @" +Delegate Sub D() +Class C + Sub M() + Dim d as D = {|#0:AddressOf M|} + End Sub +End Class"; + var bannedText = @"T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact] + public async Task VisualBasic_BannedAttribute_UsageOnTypeAsync() + { + var source = @" +Imports System + + +Class BannedAttribute + Inherits Attribute +End Class + +<{|#0:Banned|}> +Class C +End Class +Class D + Inherits C +End Class +"; + var bannedText = @"T:BannedAttribute"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task VisualBasic_BannedAttribute_UsageOnMemberAsync() + { + var source = @" +Imports System + + +Class BannedAttribute + Inherits System.Attribute +End Class + +Class C + <{|#0:Banned|}> + Public ReadOnly Property SomeProperty As Integer +End Class +"; + var bannedText = @"T:BannedAttribute"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task VisualBasic_BannedAttribute_UsageOnAssemblyAsync() + { + var source = @" +Imports System + +<{|#0:Assembly:BannedAttribute|}> + + +Class BannedAttribute + Inherits Attribute +End Class +"; + + var bannedText = @"T:BannedAttribute"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task VisualBasic_BannedAttribute_UsageOnModuleAsync() + { + var source = @" +Imports System + +<{|#0:Module:BannedAttribute|}> + + +Class BannedAttribute + Inherits Attribute +End Class +"; + + var bannedText = @"T:BannedAttribute"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", ""), + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "BannedAttribute", "")); + } + + [Fact] + public async Task VisualBasic_BannedConstructorAsync() + { + var source = @" +Namespace N + Class Banned + Sub New : End Sub + Sub New(ByVal I As Integer) : End Sub + End Class + Class C + Sub M() + Dim c As {|#0:New Banned()|} + Dim d As {|#1:New Banned(1)|} + End Sub + End Class +End Namespace"; + + var bannedText1 = @"M:N.Banned.#ctor"; + var bannedText2 = @"M:N.Banned.#ctor(System.Int32)"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText1, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New()", "")); + + await VerifyBasicAnalyzerAsync( + source, + bannedText2, + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New(I As Integer)", "")); + } + + [Fact] + public async Task VisualBasic_BannedConstructor_AttributeAsync() + { + var source = @" +Imports System + + +Class BannedAttribute + Inherits System.Attribute + + Sub New : End Sub + Sub New(ByVal Banned As Integer) : End Sub + Sub New(ByVal NotBanned As String) : End Sub +End Class + +Class C + <{|#0:Banned|}> + Public ReadOnly Property SomeProperty As Integer + + <{|#1:Banned(1)|}> + Public Sub SomeMethod : End Sub + + + Class D : End Class +End Class +"; + var bannedText1 = @"M:BannedAttribute.#ctor"; + var bannedText2 = @"M:BannedAttribute.#ctor(System.Int32)"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText1, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New()", ""), + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New()", "")); + + await VerifyBasicAnalyzerAsync( + source, + bannedText2, + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New(Banned As Integer)", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub New(Banned As Integer)", "")); + } + + [Fact] + public async Task VisualBasic_BannedMethodAsync() + { + var source = @" +Namespace N + Class C + Sub Banned : End Sub + Sub Banned(ByVal I As Integer) : End Sub + Sub M() + {|#0:Me.Banned()|} + {|#1:Me.Banned(1)|} + End Sub + End Class +End Namespace"; + + var bannedText1 = @"M:N.C.Banned"; + var bannedText2 = @"M:N.C.Banned(System.Int32)"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText1, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub Banned()", "")); + + await VerifyBasicAnalyzerAsync( + source, + bannedText2, + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub Banned(I As Integer)", "")); + } + + [Fact] + public async Task VisualBasic_BannedPropertyAsync() + { + var source = @" +Namespace N + Class C + Public Property Banned As Integer + Sub M() + {|#0:Banned|} = {|#1:Banned|} + End Sub + End Class +End Namespace"; + + var bannedText = @"P:N.C.Banned"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Property Banned As Integer", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Property Banned As Integer", "")); + } + + [Fact] + public async Task VisualBasic_BannedFieldAsync() + { + var source = @" +Namespace N + Class C + Public Banned As Integer + Sub M() + {|#0:Banned|} = {|#1:Banned|} + End Sub + End Class +End Namespace"; + + var bannedText = @"F:N.C.Banned"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Banned As Integer", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Banned As Integer", "")); + } + + [Fact] + public async Task VisualBasic_BannedEventAsync() + { + var source = @" +Namespace N + Class C + Public Event Banned As System.Action + Sub M() + AddHandler {|#0:Banned|}, Nothing + RemoveHandler {|#1:Banned|}, Nothing + RaiseEvent {|#2:Banned|}() + End Sub + End Class +End Namespace"; + + var bannedText = @"E:N.C.Banned"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Event Banned As Action", ""), + GetBasicResultAt(1, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Event Banned As Action", ""), + GetBasicResultAt(2, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Event Banned As Action", "")); + } + + [Fact] + public async Task VisualBasic_BannedMethodGroupAsync() + { + var source = @" +Namespace N + Class C + Public Sub Banned() : End Sub + Sub M() + Dim b As System.Action = {|#0:AddressOf Banned|} + End Sub + End Class +End Namespace"; + + var bannedText = @"M:N.C.Banned"; + + await VerifyBasicAnalyzerAsync( + source, + bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "Public Sub Banned()", "")); + } + + [Fact] + public async Task VisualBasic_BannedClass_DocumentationReferenceAsync() + { + var source = @" +Class C : End Class + +''' +Class D : End Class +"; + var bannedText = @"T:C"; + + await VerifyBasicAnalyzerAsync(source, bannedText, + GetBasicResultAt(0, SymbolIsBannedAnalyzer.SymbolIsBannedRule, "C", "")); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task VisualBasic_BannedAbstractVirtualMemberAlsoBansOverrides_RootLevelIsBannedAsync() + { + var source = @" +Imports System + +Namespace N + Public MustInherit Class C1 + Public MustOverride Sub Method1() + Public MustOverride Property Property1 As Integer + + Public Overridable Sub Method2() + End Sub + + Public Overridable Property Property2 As Integer + End Class + + Public Class C2 + Inherits C1 + + Public Overrides Sub Method1() + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + {|RS0030:MyBase.Method2()|} + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M1() + {|RS0030:Method1()|} + + If {|RS0030:Property1|} = 42 Then + End If + + {|RS0030:Method2()|} + + If {|RS0030:Property2|} = 42 Then + End If + End Sub + End Class + + Public Class C3 + Inherits C2 + + Public Overrides Sub Method1() + {|RS0030:MyBase.Method1()|} + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + {|RS0030:MyBase.Method2()|} + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M2() + {|RS0030:Method1()|} + + If {|RS0030:Property1|} = 42 Then + End If + + {|RS0030:Method2()|} + + If {|RS0030:Property2|} = 42 Then + End If + End Sub + End Class +End Namespace +"; + + var bannedText = @"M:N.C1.Method1 +P:N.C1.Property1 +E:N.C1.Event1 +M:N.C1.Method2 +P:N.C1.Property2 +E:N.C1.Event2"; + + await VerifyBasicAnalyzerAsync(source, bannedText); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task VisualBasic_BannedAbstractVirtualMemberAlsoBansOverrides_MiddleLevelIsBannedAsync() + { + var source = @" +Imports System + +Namespace N + Public MustInherit Class C1 + Public MustOverride Sub Method1() + Public MustOverride Property Property1 As Integer + + Public Overridable Sub Method2() + End Sub + + Public Overridable Property Property2 As Integer + End Class + + Public Class C2 + Inherits C1 + + Public Overrides Sub Method1() + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + MyBase.Method2() + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M1() + {|RS0030:Method1()|} + + If {|RS0030:Property1|} = 42 Then + End If + + {|RS0030:Method2()|} + + If {|RS0030:Property2|} = 42 Then + End If + End Sub + End Class + + Public Class C3 + Inherits C2 + + Public Overrides Sub Method1() + {|RS0030:MyBase.Method1()|} + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + {|RS0030:MyBase.Method2()|} + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M2() + {|RS0030:Method1()|} + + If {|RS0030:Property1|} = 42 Then + End If + + {|RS0030:Method2()|} + + If {|RS0030:Property2|} = 42 Then + End If + End Sub + End Class +End Namespace +"; + + var bannedText = @"M:N.C2.Method1 +P:N.C2.Property1 +E:N.C2.Event1 +M:N.C2.Method2 +P:N.C2.Property2 +E:N.C2.Event2"; + + await VerifyBasicAnalyzerAsync(source, bannedText); + } + + [Fact, WorkItem(3295, "https://github.com/dotnet/roslyn-analyzers/issues/3295")] + public async Task VisualBasic_BannedAbstractVirtualMemberAlsoBansOverrides_LeafLevelIsBannedAsync() + { + var source = @" +Imports System + +Namespace N + Public MustInherit Class C1 + Public MustOverride Sub Method1() + Public MustOverride Property Property1 As Integer + + Public Overridable Sub Method2() + End Sub + + Public Overridable Property Property2 As Integer + End Class + + Public Class C2 + Inherits C1 + + Public Overrides Sub Method1() + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + MyBase.Method2() + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M1() + Method1() + + If Property1 = 42 Then + End If + + Method2() + + If Property2 = 42 Then + End If + End Sub + End Class + + Public Class C3 + Inherits C2 + + Public Overrides Sub Method1() + MyBase.Method1() + End Sub + + Public Overrides Property Property1 As Integer + + Public Overrides Sub Method2() + MyBase.Method2() + End Sub + + Public Overrides Property Property2 As Integer + + Private Sub M2() + {|RS0030:Method1()|} + + If {|RS0030:Property1|} = 42 Then + End If + + {|RS0030:Method2()|} + + If {|RS0030:Property2|} = 42 Then + End If + End Sub + End Class +End Namespace +"; + + var bannedText = @"M:N.C3.Method1 +P:N.C3.Property1 +E:N.C3.Event1 +M:N.C3.Method2 +P:N.C3.Property2 +E:N.C3.Event2"; + + await VerifyBasicAnalyzerAsync(source, bannedText); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicRestrictedInternalsVisibleToAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicRestrictedInternalsVisibleToAnalyzer.vb new file mode 100644 index 0000000000000..9b240937749cd --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicRestrictedInternalsVisibleToAnalyzer.vb @@ -0,0 +1,28 @@ +' 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. + +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.BannedApiAnalyzers +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers + + Public Class BasicRestrictedInternalsVisibleToAnalyzer + Inherits RestrictedInternalsVisibleToAnalyzer(Of NameSyntax, SyntaxKind) + + Protected Overrides ReadOnly Property NameSyntaxKinds As ImmutableArray(Of SyntaxKind) + Get + Return ImmutableArray.Create( + SyntaxKind.IdentifierName, + SyntaxKind.GenericName, + SyntaxKind.QualifiedName) + End Get + End Property + + Protected Overrides Function IsInTypeOnlyContext(node As NameSyntax) As Boolean + Return SyntaxFacts.IsInTypeOnlyContext(node) + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb new file mode 100644 index 0000000000000..79db7b962f4fb --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/BasicSymbolIsBannedAnalyzer.vb @@ -0,0 +1,48 @@ +' 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. + +Imports System.Collections.Immutable +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.CodeAnalysis.BannedApiAnalyzers + +Namespace Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers + + Public Class BasicSymbolIsBannedAnalyzer + Inherits SymbolIsBannedAnalyzer(Of SyntaxKind) + + Protected Overrides ReadOnly Property XmlCrefSyntaxKind As SyntaxKind + Get + Return SyntaxKind.XmlCrefAttribute + End Get + End Property + + Protected Overrides ReadOnly Property BaseTypeSyntaxKinds As ImmutableArray(Of SyntaxKind) + Get + Return ImmutableArray.Create(SyntaxKind.InheritsStatement, SyntaxKind.ImplementsStatement) + End Get + End Property + + Protected Overrides ReadOnly Property SymbolDisplayFormat As SymbolDisplayFormat + Get + Return SymbolDisplayFormat.VisualBasicShortErrorMessageFormat + End Get + End Property + + Protected Overrides Function GetReferenceSyntaxNodeFromXmlCref(syntaxNode As SyntaxNode) As SyntaxNode + Return CType(syntaxNode, XmlCrefAttributeSyntax).Reference + End Function + + Protected Overrides Function GetTypeSyntaxNodesFromBaseType(syntaxNode As SyntaxNode) As IEnumerable(Of SyntaxNode) + If syntaxNode.IsKind(SyntaxKind.InheritsStatement) Then + Return CType(syntaxNode, InheritsStatementSyntax).Types + ElseIf syntaxNode.IsKind(SyntaxKind.ImplementsStatement) Then + Return CType(syntaxNode, ImplementsStatementSyntax).Types + Else + Return ImmutableArray(Of SyntaxNode).Empty + End If + End Function + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.vbproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.vbproj new file mode 100644 index 0000000000000..088467a5ffa68 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers/VisualBasic/Microsoft.CodeAnalysis.VisualBasic.BannedApiAnalyzers.vbproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForBannedApiAnalyzers) + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/CSharpResxGenerator.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/CSharpResxGenerator.cs new file mode 100644 index 0000000000000..b17e67d4517f8 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/CSharpResxGenerator.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp; + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp +{ + [Generator(LanguageNames.CSharp)] + internal sealed class CSharpResxGenerator : AbstractResxGenerator + { + protected override bool SupportsNullable(Compilation compilation) + { + return ((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp8; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp.csproj new file mode 100644 index 0000000000000..1c42ab3fde764 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp/Microsoft.CodeAnalysis.ResxSourceGenerator.CSharp.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + false + true + + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForResxSourceGenerators) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests.csproj new file mode 100644 index 0000000000000..7bbb158306cd4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests.csproj @@ -0,0 +1,44 @@ + + + + $(NetRoslyn) + + true + true + $(MicrosoftCodeAnalysisVersionForResxSourceGenerators) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_False/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_True/Resources.Designer.cs new file mode 100644 index 0000000000000..c7688ac134b46 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsCSharpAsync_True/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public const string @Name = "Name"; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_False/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_False/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_False/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_True/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_True/Resources.Designer.vb new file mode 100644 index 0000000000000..3a7ff2a36a27a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_AsConstantsVisualBasicAsync_True/Resources.Designer.vb @@ -0,0 +1,29 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Const [Name] As String = "Name" + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS/Resources.Designer.cs new file mode 100644 index 0000000000000..88c522856854a --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS/Resources.Designer.cs @@ -0,0 +1,23 @@ +// + +#nullable enable +using System.Reflection; + +namespace TestProject +{ + internal static class Resources { } +} + +internal static partial class NS +{ + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(TestProject.Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + +} + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS1.NS2/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS1.NS2/Resources.Designer.cs new file mode 100644 index 0000000000000..4ded4b004c555 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameCSharpAsync_NS1.NS2/Resources.Designer.cs @@ -0,0 +1,24 @@ +// + +#nullable enable +using System.Reflection; + +namespace TestProject +{ + internal static class Resources { } +} +namespace NS1 +{ + internal static partial class NS2 + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(TestProject.Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS/Resources.Designer.vb new file mode 100644 index 0000000000000..84a8ca15a650d --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS/Resources.Designer.vb @@ -0,0 +1,36 @@ +' + +Imports System.Reflection + +Namespace TestProject + Friend Class Resources + End Class +End Namespace + +Friend Partial Class NS + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(TestProject.Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + +End Class + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS1.NS2/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS1.NS2/Resources.Designer.vb new file mode 100644 index 0000000000000..866c481c2e8f9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_ClassNameVisualBasicAsync_NS1.NS2/Resources.Designer.vb @@ -0,0 +1,36 @@ +' + +Imports System.Reflection + +Namespace TestProject + Friend Class Resources + End Class +End Namespace +Namespace Global.NS1 + Friend Partial Class NS2 + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(TestProject.Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp5/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp5/Resources.Designer.cs new file mode 100644 index 0000000000000..d50bcc48b5b83 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp5/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + + +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager s_resourceManager; + internal static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + internal static global::System.Globalization.CultureInfo Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string GetResourceString(string resourceKey, string defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + internal static string @Name => GetResourceString("Name"); + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp6/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp6/Resources.Designer.cs new file mode 100644 index 0000000000000..ff7d2796857ea --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp6/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + + +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string GetResourceString(string resourceKey, string defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name"); + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp7/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp7/Resources.Designer.cs new file mode 100644 index 0000000000000..ff7d2796857ea --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp7/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + + +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string GetResourceString(string resourceKey, string defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name"); + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp8/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp8/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp8/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp9/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp9/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultCSharpAsync_CSharp9/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_DefaultVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_False/Resources.Designer.cs new file mode 100644 index 0000000000000..5d380b3db1666 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value {0} + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_True/Resources.Designer.cs new file mode 100644 index 0000000000000..b3919a15c4f5b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_0_True/Resources.Designer.cs @@ -0,0 +1,39 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + + private static string GetResourceString(string resourceKey, string[]? formatterNames) + { + var value = GetResourceString(resourceKey) ?? ""; + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + return value; + } + + /// value {0} + public static string @Name => GetResourceString("Name")!; + /// value {0} + internal static string FormatName(object? p0) + => string.Format(Culture, GetResourceString("Name") ?? "", p0); + + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_False/Resources.Designer.cs new file mode 100644 index 0000000000000..4feef75d44d61 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value {replacement} + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_True/Resources.Designer.cs new file mode 100644 index 0000000000000..fa77240ec63c1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_replacement_True/Resources.Designer.cs @@ -0,0 +1,39 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + + private static string GetResourceString(string resourceKey, string[]? formatterNames) + { + var value = GetResourceString(resourceKey) ?? ""; + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + return value; + } + + /// value {replacement} + public static string @Name => GetResourceString("Name")!; + /// value {replacement} + internal static string FormatName(object? replacement) + => string.Format(Culture, GetResourceString("Name", new[] { "replacement" }), replacement); + + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_False/Resources.Designer.cs new file mode 100644 index 0000000000000..19b20ba8116e2 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value {x} + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_True/Resources.Designer.cs new file mode 100644 index 0000000000000..fe057e9cbfcb4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsCSharpAsync_x_True/Resources.Designer.cs @@ -0,0 +1,39 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + + private static string GetResourceString(string resourceKey, string[]? formatterNames) + { + var value = GetResourceString(resourceKey) ?? ""; + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + return value; + } + + /// value {x} + public static string @Name => GetResourceString("Name")!; + /// value {x} + internal static string FormatName(object? x) + => string.Format(Culture, GetResourceString("Name", new[] { "x" }), x); + + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_False/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_False/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_False/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_True/Resources.Error.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_True/Resources.Error.vb new file mode 100644 index 0000000000000..79bba70604e54 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_EmitFormatMethodsVisualBasicAsync_True/Resources.Error.vb @@ -0,0 +1,4 @@ +#error System.NotImplementedException: The method or operation is not implemented. +#error at Microsoft.CodeAnalysis.ResxSourceGenerator.AbstractResxGenerator.Impl.Execute(CancellationToken cancellationToken) in C:\dev\roslyn-analyzers\src\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator\AbstractResxGenerator.cs:line 328 +#error at Microsoft.CodeAnalysis.ResxSourceGenerator.AbstractResxGenerator.ProcessResourceFile(SourceProductionContext context, ResourceInformation resourceFile) in C:\dev\roslyn-analyzers\src\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator\AbstractResxGenerator.cs:line 166 +#error at Microsoft.CodeAnalysis.ResxSourceGenerator.AbstractResxGenerator.<>c.b__1_3(SourceProductionContext context, ResourceInformation resourceInformation) in C:\dev\roslyn-analyzers\src\Microsoft.CodeAnalysis.ResxSourceGenerator\Microsoft.CodeAnalysis.ResxSourceGenerator\AbstractResxGenerator.cs:line 117 diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_False/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_True/Resources.Designer.cs new file mode 100644 index 0000000000000..1473a8f809be9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesCSharpAsync_True/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name", @"value"); + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_False/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_False/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_False/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_True/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_True/Resources.Designer.vb new file mode 100644 index 0000000000000..a7d121e8de99e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_IncludeDefaultValuesVisualBasicAsync_True/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name", "value") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591/Resources.Designer.cs new file mode 100644 index 0000000000000..2de335ef9122b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591/Resources.Designer.cs @@ -0,0 +1,25 @@ +// + +#pragma warning disable CS1591 + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} + +#pragma warning restore CS1591 diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591_IDE0010/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591_IDE0010/Resources.Designer.cs new file mode 100644 index 0000000000000..62cee7c3c5d04 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsCSharpAsync_CS1591_IDE0010/Resources.Designer.cs @@ -0,0 +1,25 @@ +// + +#pragma warning disable CS1591, IDE0010 + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} + +#pragma warning restore CS1591, IDE0010 diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591/Resources.Designer.vb new file mode 100644 index 0000000000000..17132d5e6825b --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591/Resources.Designer.vb @@ -0,0 +1,37 @@ +' + +#Disable Warning CS1591 + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace + +#Enable Warning CS1591 diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591_IDE0010/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591_IDE0010/Resources.Designer.vb new file mode 100644 index 0000000000000..72d82353a1840 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_NoWarnsVisualBasicAsync_CS1591_IDE0010/Resources.Designer.vb @@ -0,0 +1,37 @@ +' + +#Disable Warning CS1591, IDE0010 + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace + +#Enable Warning CS1591, IDE0010 diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_False/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_True/Resources.Designer.cs new file mode 100644 index 0000000000000..82e188c979458 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringCSharpAsync_True/Resources.Designer.cs @@ -0,0 +1,18 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_False/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_False/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_False/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_True/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_True/Resources.Designer.vb new file mode 100644 index 0000000000000..2a310c4b2fc5f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_OmitGetResourceStringVisualBasicAsync_True/Resources.Designer.vb @@ -0,0 +1,29 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_False/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_False/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_False/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_True/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_True/Resources.Designer.cs new file mode 100644 index 0000000000000..5aec8c10768e0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicCSharpAsync_True/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + public static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_False/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_False/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_False/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_True/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_True/Resources.Designer.vb new file mode 100644 index 0000000000000..64be47f98a099 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_PublicVisualBasicAsync_True/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Public Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync/Resources.Designer.cs new file mode 100644 index 0000000000000..c1c925afa5ad4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS/Resources.Designer.cs new file mode 100644 index 0000000000000..1f3c879f30782 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject +{ + internal static partial class NSResources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(NSResources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS1.NS2/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS1.NS2/Resources.Designer.cs new file mode 100644 index 0000000000000..f32431e787727 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirCSharpAsync_NS1.NS2/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject.NS1 +{ + internal static partial class NS2Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(NS2Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..7e5e0abe937db --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS/Resources.Designer.vb new file mode 100644 index 0000000000000..8bd90c0fae28f --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject + Friend Partial Class NSResources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(NSResources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS1.NS2/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS1.NS2/Resources.Designer.vb new file mode 100644 index 0000000000000..c0a22ef9916df --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RelativeDirVisualBasicAsync_NS1.NS2/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject.NS1 + Friend Partial Class NS2Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(NS2Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync/Resources.Designer.cs new file mode 100644 index 0000000000000..4110d659e6761 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + internal static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + internal static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + internal static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS/Resources.Designer.cs new file mode 100644 index 0000000000000..e92f3ef2aded2 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace NS +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS1.NS2/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS1.NS2/Resources.Designer.cs new file mode 100644 index 0000000000000..9bdce5dfefde8 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceCSharpAsync_NS1.NS2/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace NS1.NS2 +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..8032199f048d9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global. + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Friend Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Friend Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Friend Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS/Resources.Designer.vb new file mode 100644 index 0000000000000..1d7d203bc1fb3 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.NS + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS1.NS2/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS1.NS2/Resources.Designer.vb new file mode 100644 index 0000000000000..25ea9cf6a93f6 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/SingleString_RootNamespaceVisualBasicAsync_NS1.NS2/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.NS1.NS2 + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources.Designer.cs new file mode 100644 index 0000000000000..ec47ea64240a4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject.First +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources0.Designer.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources0.Designer.cs new file mode 100644 index 0000000000000..26e3d38af1550 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultCSharpAsync/Resources0.Designer.cs @@ -0,0 +1,21 @@ +// + +#nullable enable +using System.Reflection; + + +namespace TestProject.Second +{ + internal static partial class Resources + { + private static global::System.Resources.ResourceManager? s_resourceManager; + public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof(Resources))); + public static global::System.Globalization.CultureInfo? Culture { get; set; } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + [return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull("defaultValue")] + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue; + /// value + public static string @Name => GetResourceString("Name")!; + + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources.Designer.vb new file mode 100644 index 0000000000000..dd7fa8fdeb5ee --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject.First + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources0.Designer.vb b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources0.Designer.vb new file mode 100644 index 0000000000000..b4a921a70e1f1 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Resources/TwoResourcesSameName_DefaultVisualBasicAsync/Resources0.Designer.vb @@ -0,0 +1,33 @@ +' + +Imports System.Reflection + + +Namespace Global.TestProject.Second + Friend Partial Class Resources + Private Sub New + End Sub + + Private Shared s_resourceManager As Global.System.Resources.ResourceManager + Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager + Get + If s_resourceManager Is Nothing Then + s_resourceManager = New Global.System.Resources.ResourceManager(GetType(Resources)) + End If + Return s_resourceManager + End Get + End Property + Public Shared Property Culture As Global.System.Globalization.CultureInfo + + Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String + Return ResourceManager.GetString(resourceKey, Culture) + End Function + ''' value + Public Shared ReadOnly Property [Name] As String + Get + Return GetResourceString("Name") + End Get + End Property + + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/ResxGeneratorTests.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/ResxGeneratorTests.cs new file mode 100644 index 0000000000000..0016d77fd3c54 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/ResxGeneratorTests.cs @@ -0,0 +1,815 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using CSharpLanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion; +using VerifyCS = Microsoft.CodeAnalysis.ResxSourceGenerator.Test.CSharpSourceGeneratorVerifier; +using VerifyVB = Microsoft.CodeAnalysis.ResxSourceGenerator.Test.VisualBasicSourceGeneratorVerifier; + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + public class ResxGeneratorTests + { + private const string ResxHeader = @" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + +"; + private const string ResxFooter = @" +"; + + [Theory] + [InlineData(CSharpLanguageVersion.CSharp5, Skip = "Expression-bodied members are not supported in C# 5")] + [InlineData(CSharpLanguageVersion.CSharp6)] + [InlineData(CSharpLanguageVersion.CSharp7)] + [InlineData(CSharpLanguageVersion.CSharp8)] + [InlineData(CSharpLanguageVersion.CSharp9)] + public async Task SingleString_DefaultCSharpAsync(CSharpLanguageVersion languageVersion) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: languageVersion.ToString()) + { + LanguageVersion = languageVersion, + TestState = + { + Sources = { "" }, + AdditionalFiles = { ("/0/Resources.resx", code) }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Fact] + public async Task TwoResourcesSameName_DefaultCSharpAsync() + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test() + { + TestState = + { + Sources = { "" }, + AdditionalFiles = + { + ("/0/First/Resources.resx", code), + ("/0/Second/Resources.resx", code), + }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +build_property.RootNamespace = TestProject + +[/0/First/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = First/ + +[/0/Second/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = Second/ +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Fact] + public async Task TwoResourcesSameName_DefaultVisualBasicAsync() + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test() + { + TestState = + { + Sources = { "" }, + AdditionalFiles = + { + ("/0/First/Resources.resx", code), + ("/0/Second/Resources.resx", code), + }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +build_property.RootNamespace = TestProject + +[/0/First/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = First/ + +[/0/Second/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = Second/ +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Fact] + public async Task SingleString_DefaultVisualBasicAsync() + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Fact] + public async Task SingleString_DisableCodeGenAsync() + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test + { + TestState = + { + Sources = { "" }, + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", @" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.GenerateSource = false +"), + }, + }, + }.RunAsync(); + + await new VerifyVB.Test + { + TestState = + { + Sources = { "" }, + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", @" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.GenerateSource = false +"), + }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("", Skip = "Empty root namespaces are not supported")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_RootNamespaceCSharpAsync(string rootNamespace) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: rootNamespace) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +build_property.RootNamespace = {rootNamespace} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("", Skip = "Empty root namespaces are not supported")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_RootNamespaceVisualBasicAsync(string rootNamespace) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: rootNamespace) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +build_property.RootNamespace = {rootNamespace} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_RelativeDirCSharpAsync(string relativeDir) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: relativeDir) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = {relativeDir} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_RelativeDirVisualBasicAsync(string relativeDir) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: relativeDir) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.RelativeDir = {relativeDir} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_ClassNameCSharpAsync(string className) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: className) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.ClassName = {className} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("NS")] + [InlineData("NS1.NS2")] + public async Task SingleString_ClassNameVisualBasicAsync(string className) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: className) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.ClassName = {className} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_OmitGetResourceStringCSharpAsync(bool omitGetResourceString) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + var customGetResourceString = @"#nullable enable + +namespace TestProject +{ + internal static partial class Resources + { + internal static string? GetResourceString(string resourceKey, string? defaultValue = null) => throw null!; + } +} +"; + + await new VerifyCS.Test(identifier: omitGetResourceString.ToString()) + { + TestState = + { + Sources = { omitGetResourceString ? customGetResourceString : "" }, + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.OmitGetResourceString = {(omitGetResourceString ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_OmitGetResourceStringVisualBasicAsync(bool omitGetResourceString) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + var customGetResourceString = @" +Namespace Global.TestProject + Friend Partial Class Resources + Friend Shared Function GetResourceString(resourceKey As String, Optional defaultValue As String = Nothing) + Return """" + End Function + End Class +End Namespace +"; + + await new VerifyVB.Test(identifier: omitGetResourceString.ToString()) + { + TestState = + { + Sources = { omitGetResourceString ? customGetResourceString : "" }, + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.OmitGetResourceString = {(omitGetResourceString ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_AsConstantsCSharpAsync(bool asConstants) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: asConstants.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.AsConstants = {(asConstants ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_AsConstantsVisualBasicAsync(bool asConstants) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: asConstants.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.AsConstants = {(asConstants ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_IncludeDefaultValuesCSharpAsync(bool includeDefaultValues) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: includeDefaultValues.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.IncludeDefaultValues = {(includeDefaultValues ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_IncludeDefaultValuesVisualBasicAsync(bool includeDefaultValues) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: includeDefaultValues.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.IncludeDefaultValues = {(includeDefaultValues ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_EmitFormatMethodsCSharpAsync( + [CombinatorialValues("0", "x", "replacement")] string placeholder, + bool emitFormatMethods) + { + var code = ResxHeader + + $@" + value {{{placeholder}}} + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: $"{placeholder}_{emitFormatMethods}") + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.EmitFormatMethods = {(emitFormatMethods ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData(true, Skip = "Not yet supported")] + [InlineData(false)] + public async Task SingleString_EmitFormatMethodsVisualBasicAsync(bool emitFormatMethods) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: emitFormatMethods.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.EmitFormatMethods = {(emitFormatMethods ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_PublicCSharpAsync(bool publicResource) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyCS.Test(identifier: publicResource.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.Public = {(publicResource ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task SingleString_PublicVisualBasicAsync(bool publicResource) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + await new VerifyVB.Test(identifier: publicResource.ToString()) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.Public = {(publicResource ? "true" : "false")} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("CS1591")] + [InlineData("CS1591, IDE0010")] + [InlineData(" , CS1591, IDE0010 ")] + public async Task SingleString_NoWarnsCSharpAsync(string noWarn) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + var id = string.Join("_", noWarn.Replace(" ", "").Split(",", System.StringSplitOptions.TrimEntries | System.StringSplitOptions.RemoveEmptyEntries)); + + await new VerifyCS.Test(identifier: id) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.NoWarn = {noWarn} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("CS1591")] + [InlineData("CS1591, IDE0010")] + [InlineData(" , CS1591, IDE0010 ")] + public async Task SingleString_NoWarnsVisualBasicAsync(string noWarn) + { + var code = ResxHeader + + @" + value + comment + " + + ResxFooter; + + var id = string.Join("_", noWarn.Replace(" ", "").Split(",", System.StringSplitOptions.TrimEntries | System.StringSplitOptions.RemoveEmptyEntries)); + + await new VerifyVB.Test(identifier: id) + { + TestState = + { + AdditionalFiles = { ("/0/Resources.resx", code) }, + AnalyzerConfigFiles = + { + ("/.globalconfig", $@" +is_global = true + +[/0/Resources.resx] +build_metadata.AdditionalFiles.NoWarn = {noWarn} +"), + }, + }, + }.AddGeneratedSources().RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs new file mode 100644 index 0000000000000..9bab32bead5da --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1+Test.cs @@ -0,0 +1,145 @@ +// 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 enable + +// Uncomment the following line to write expected files to disk +////#define WRITE_EXPECTED + +#if WRITE_EXPECTED +#warning WRITE_EXPECTED is fine for local builds, but should not be merged to the main branch. +#endif + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + public static partial class CSharpSourceGeneratorVerifier + where TSourceGenerator : IIncrementalGenerator, new() + { + public class Test : CSharpSourceGeneratorTest + { + private readonly string _identifier; + private readonly string? _testFile; + private readonly string? _testMethod; + + public Test([CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null) + : this(string.Empty, testFile, testMethod) + { + } + + public Test(string identifier, [CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null) + { + _identifier = identifier; + _testFile = testFile; + _testMethod = testMethod; + +#if WRITE_EXPECTED + TestBehaviors |= TestBehaviors.SkipGeneratedSourcesCheck; +#endif + } + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.Default; + + private string ResourceName + { + get + { + if (string.IsNullOrEmpty(_identifier)) + return _testMethod ?? ""; + + return $"{_testMethod}_{_identifier}"; + } + } + + protected override CompilationOptions CreateCompilationOptions() + { + var compilationOptions = base.CreateCompilationOptions(); + return compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings)); + } + + protected override ParseOptions CreateParseOptions() + { + return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + + protected override async Task<(Compilation compilation, ImmutableArray generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken) + { + var resourceDirectory = Path.Combine(Path.GetDirectoryName(_testFile)!, "Resources", ResourceName); + + var (compilation, generatorDiagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken); + var expectedNames = new HashSet(); + foreach (var tree in compilation.SyntaxTrees.Skip(project.DocumentIds.Count)) + { + WriteTreeToDiskIfNecessary(tree, resourceDirectory); + expectedNames.Add(Path.GetFileName(tree.FilePath)); + } + + var currentTestPrefix = $"{Assembly.GetExecutingAssembly().GetName().Name}.Resources.{ResourceName}."; + foreach (var name in GetType().Assembly.GetManifestResourceNames()) + { + if (!name.StartsWith(currentTestPrefix, StringComparison.Ordinal)) + { + continue; + } + + if (!expectedNames.Contains(name.Substring(currentTestPrefix.Length))) + { + throw new InvalidOperationException($"Unexpected test resource: {name.Substring(currentTestPrefix.Length)}"); + } + } + + return (compilation, generatorDiagnostics); + } + + public Test AddGeneratedSources() + { + var expectedPrefix = $"{Assembly.GetExecutingAssembly().GetName().Name}.Resources.{ResourceName}."; + foreach (var resourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames()) + { + if (!resourceName.StartsWith(expectedPrefix, StringComparison.Ordinal)) + { + continue; + } + + using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException(); + using var reader = new StreamReader(resourceStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4096, leaveOpen: true); + var name = resourceName.Substring(expectedPrefix.Length); + TestState.GeneratedSources.Add((typeof(TSourceGenerator), name, SourceText.From(reader.ReadToEnd(), Encoding.UTF8, SourceHashAlgorithm.Sha256))); + } + + return this; + } + + [Conditional("WRITE_EXPECTED")] + private static void WriteTreeToDiskIfNecessary(SyntaxTree tree, string resourceDirectory) + { + if (tree.Encoding is null) + { + throw new ArgumentException("Syntax tree encoding was not specified"); + } + + var name = Path.GetFileName(tree.FilePath); + var filePath = Path.Combine(resourceDirectory, name); + Directory.CreateDirectory(resourceDirectory); + File.WriteAllText(filePath, tree.GetText().ToString(), tree.Encoding); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1.cs new file mode 100644 index 0000000000000..981cda8d6c8c7 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpSourceGeneratorVerifier`1.cs @@ -0,0 +1,13 @@ +// 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 enable + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + public static partial class CSharpSourceGeneratorVerifier + where TSourceGenerator : IIncrementalGenerator, new() + { + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs new file mode 100644 index 0000000000000..69ff503c1aa74 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/CSharpVerifierHelper.cs @@ -0,0 +1,31 @@ +// 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 enable + +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CSharp; + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + internal static class CSharpVerifierHelper + { + /// + /// By default, the compiler reports diagnostics for nullable reference types at + /// , and the analyzer test framework defaults to only validating + /// diagnostics at . This map contains all compiler diagnostic IDs + /// related to nullability mapped to , which is then used to enable all + /// of these warnings for default validation during analyzer and code fix tests. + /// + internal static ImmutableDictionary NullableWarnings { get; } = GetNullableWarningsFromCompiler(); + + private static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = { "/warnaserror:nullable" }; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1+Test.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1+Test.cs new file mode 100644 index 0000000000000..038cab71fb387 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1+Test.cs @@ -0,0 +1,130 @@ +// 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 enable + +// Uncomment the following line to write expected files to disk +////#define WRITE_EXPECTED + +#if WRITE_EXPECTED +#warning WRITE_EXPECTED is fine for local builds, but should not be merged to the main branch. +#endif + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + public static partial class VisualBasicSourceGeneratorVerifier + where TSourceGenerator : IIncrementalGenerator, new() + { + public class Test : VisualBasicSourceGeneratorTest + { + private readonly string _identifier; + private readonly string? _testFile; + private readonly string? _testMethod; + + public Test([CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null) + : this(string.Empty, testFile, testMethod) + { + } + + public Test(string identifier, [CallerFilePath] string? testFile = null, [CallerMemberName] string? testMethod = null) + { + _identifier = identifier; + _testFile = testFile; + _testMethod = testMethod; + +#if WRITE_EXPECTED + TestBehaviors |= TestBehaviors.SkipGeneratedSourcesCheck; +#endif + } + + private string ResourceName + { + get + { + if (string.IsNullOrEmpty(_identifier)) + return _testMethod ?? ""; + + return $"{_testMethod}_{_identifier}"; + } + } + + protected override async Task<(Compilation compilation, ImmutableArray generatorDiagnostics)> GetProjectCompilationAsync(Project project, IVerifier verifier, CancellationToken cancellationToken) + { + var resourceDirectory = Path.Combine(Path.GetDirectoryName(_testFile)!, "Resources", ResourceName); + + var (compilation, generatorDiagnostics) = await base.GetProjectCompilationAsync(project, verifier, cancellationToken); + var expectedNames = new HashSet(); + foreach (var tree in compilation.SyntaxTrees.Skip(project.DocumentIds.Count)) + { + WriteTreeToDiskIfNecessary(tree, resourceDirectory); + expectedNames.Add(Path.GetFileName(tree.FilePath)); + } + + var currentTestPrefix = $"{typeof(ResxGeneratorTests).Assembly.GetName().Name}.Resources.{ResourceName}."; + foreach (var name in GetType().Assembly.GetManifestResourceNames()) + { + if (!name.StartsWith(currentTestPrefix, StringComparison.Ordinal)) + { + continue; + } + + if (!expectedNames.Contains(name.Substring(currentTestPrefix.Length))) + { + throw new InvalidOperationException($"Unexpected test resource: {name.Substring(currentTestPrefix.Length)}"); + } + } + + return (compilation, generatorDiagnostics); + } + + public Test AddGeneratedSources() + { + var expectedPrefix = $"{typeof(ResxGeneratorTests).Assembly.GetName().Name}.Resources.{ResourceName}."; + foreach (var resourceName in typeof(Test).Assembly.GetManifestResourceNames()) + { + if (!resourceName.StartsWith(expectedPrefix, StringComparison.Ordinal)) + { + continue; + } + + using var resourceStream = typeof(ResxGeneratorTests).Assembly.GetManifestResourceStream(resourceName) ?? throw new InvalidOperationException(); + using var reader = new StreamReader(resourceStream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 4096, leaveOpen: true); + var name = resourceName.Substring(expectedPrefix.Length); + TestState.GeneratedSources.Add((typeof(VisualBasicResxGenerator), name, SourceText.From(reader.ReadToEnd(), Encoding.UTF8, SourceHashAlgorithm.Sha256))); + } + + return this; + } + + [Conditional("WRITE_EXPECTED")] + private static void WriteTreeToDiskIfNecessary(SyntaxTree tree, string resourceDirectory) + { + if (tree.Encoding is null) + { + throw new ArgumentException("Syntax tree encoding was not specified"); + } + + var name = Path.GetFileName(tree.FilePath); + var filePath = Path.Combine(resourceDirectory, name); + Directory.CreateDirectory(resourceDirectory); + File.WriteAllText(filePath, tree.GetText().ToString(), tree.Encoding); + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1.cs new file mode 100644 index 0000000000000..f39ec2ac2df47 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.UnitTests/Verifiers/VisualBasicSourceGeneratorVerifier`1.cs @@ -0,0 +1,13 @@ +// 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 enable + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.Test +{ + public static partial class VisualBasicSourceGeneratorVerifier + where TSourceGenerator : IIncrementalGenerator, new() + { + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic.csproj new file mode 100644 index 0000000000000..eed9ed1024961 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic.csproj @@ -0,0 +1,26 @@ + + + + netstandard2.0 + false + true + + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForResxSourceGenerators) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/VisualBasicResxGenerator.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/VisualBasicResxGenerator.cs new file mode 100644 index 0000000000000..7d9b1997392ef --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic/VisualBasicResxGenerator.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator.VisualBasic +{ + [Generator(LanguageNames.VisualBasic)] + internal sealed class VisualBasicResxGenerator : AbstractResxGenerator + { + protected override bool SupportsNullable(Compilation compilation) + { + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/AbstractResxGenerator.cs b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/AbstractResxGenerator.cs new file mode 100644 index 0000000000000..f6d8e1f796002 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/AbstractResxGenerator.cs @@ -0,0 +1,910 @@ +// 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.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Xml.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +#pragma warning disable IDE0010 // Add missing cases (noise) +#pragma warning disable IDE0057 // Use range operator (incorrectly reported when Range is not defined) +#pragma warning disable IDE0058 // Expression value is never used (not sure why this is enabled) +#pragma warning disable IDE0066 // Convert switch statement to expression (not always better) + +namespace Microsoft.CodeAnalysis.ResxSourceGenerator +{ + internal abstract class AbstractResxGenerator : IIncrementalGenerator + { + protected abstract bool SupportsNullable(Compilation compilation); + + [SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Standard practice for diagnosing source generator failures.")] + public void Initialize(IncrementalGeneratorInitializationContext context) + { + var resourceFiles = context.AdditionalTextsProvider.Where(static file => file.Path.EndsWith(".resx", StringComparison.OrdinalIgnoreCase)); + var compilationInformation = context.CompilationProvider.Select( + (compilation, cancellationToken) => + { + var methodImplOptions = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesMethodImplOptions); + var hasAggressiveInlining = methodImplOptions?.MemberNames.Contains(nameof(MethodImplOptions.AggressiveInlining)) ?? false; + var hasNotNullIfNotNull = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsCodeAnalysisNotNullIfNotNullAttribute) is not null; + + return new CompilationInformation( + AssemblyName: compilation.AssemblyName, + CodeLanguage: compilation.Language, + SupportsNullable: SupportsNullable(compilation), + HasAggressiveInlining: hasAggressiveInlining, + HasNotNullIfNotNull: hasNotNullIfNotNull); + }); + var resourceFilesToGenerateSource = resourceFiles.Combine(context.AnalyzerConfigOptionsProvider.Combine(compilationInformation)).SelectMany( + static (resourceFileAndOptions, cancellationToken) => + { + var (resourceFile, (optionsProvider, compilationInfo)) = resourceFileAndOptions; + var options = optionsProvider.GetOptions(resourceFile); + + // Use the GenerateSource property if provided. Otherwise, the value of GenerateSource defaults to + // true for resources without an explicit culture. + var explicitGenerateSource = IsGenerateSource(options); + if (explicitGenerateSource == false) + { + // Source generation is explicitly disabled for this resource file + return Array.Empty(); + } + else if (explicitGenerateSource != true) + { + var implicitGenerateSource = !IsExplicitWithCulture(options); + if (!implicitGenerateSource) + { + // Source generation is disabled for this resource file + return Array.Empty(); + } + } + + if (!optionsProvider.GlobalOptions.TryGetValue("build_property.RootNamespace", out var rootNamespace)) + { + rootNamespace = compilationInfo.AssemblyName; + } + + var resourceHintName = Path.GetFileNameWithoutExtension(resourceFile.Path); + var resourceName = resourceHintName; + if (options.TryGetValue("build_metadata.AdditionalFiles.RelativeDir", out var relativeDir)) + { + resourceName = relativeDir.Replace(Path.DirectorySeparatorChar, '.').Replace(Path.AltDirectorySeparatorChar, '.') + resourceName; + } + + options.TryGetValue("build_metadata.AdditionalFiles.ClassName", out var resourceClassName); + + if (!options.TryGetValue("build_metadata.AdditionalFiles.OmitGetResourceString", out var omitGetResourceStringText) + || !bool.TryParse(omitGetResourceStringText, out var omitGetResourceString)) + { + omitGetResourceString = false; + } + + if (!options.TryGetValue("build_metadata.AdditionalFiles.AsConstants", out var asConstantsText) + || !bool.TryParse(asConstantsText, out var asConstants)) + { + asConstants = false; + } + + if (!options.TryGetValue("build_metadata.AdditionalFiles.IncludeDefaultValues", out var includeDefaultValuesText) + || !bool.TryParse(includeDefaultValuesText, out var includeDefaultValues)) + { + includeDefaultValues = false; + } + + if (!options.TryGetValue("build_metadata.AdditionalFiles.EmitFormatMethods", out var emitFormatMethodsText) + || !bool.TryParse(emitFormatMethodsText, out var emitFormatMethods)) + { + emitFormatMethods = false; + } + + if (!options.TryGetValue("build_metadata.AdditionalFiles.Public", out var publicText) + || !bool.TryParse(publicText, out var publicResource)) + { + publicResource = false; + } + + var noWarn = Array.Empty(); + if (options.TryGetValue("build_metadata.AdditionalFiles.NoWarn", out var noWarnText)) + { + noWarn = noWarnText.Split([',', ';'], StringSplitOptions.RemoveEmptyEntries).Select(i => i.Trim()).ToArray() ?? []; + } + + return new[] + { + new ResourceInformation( + CompilationInformation: compilationInfo, + ResourceFile: resourceFile, + ResourceName: string.Join(".", rootNamespace, resourceName), + ResourceHintName: resourceHintName, + ResourceClassName: resourceClassName, + OmitGetResourceString: omitGetResourceString, + AsConstants: asConstants, + IncludeDefaultValues: includeDefaultValues, + EmitFormatMethods: emitFormatMethods, + Public: publicResource, + NoWarn: noWarn) + }; + }); + var renameMapping = resourceFilesToGenerateSource + .Select(static (resourceFile, cancellationToken) => + { + return (resourceFile.ResourceName, resourceFile.ResourceHintName); + }) + .Collect() + .Select(static (resourceNames, cancellationToken) => + { + var names = new HashSet(); + var remappedNames = ImmutableDictionary.Empty; + foreach (var (resourceName, resourceHintName) in resourceNames.OrderBy(x => x.ResourceName, StringComparer.Ordinal)) + { + for (var i = -1; true; i++) + { + if (i == -1) + { + if (names.Add(resourceHintName)) + break; + } + else + { + var candidateName = resourceHintName + i; + if (names.Add(candidateName)) + { + remappedNames = remappedNames.Add(resourceName, candidateName); + break; + } + } + } + } + + return remappedNames; + }) + .WithComparer(ImmutableDictionaryEqualityComparer.Instance); + var resourceFilesToGenerateSourceWithNames = resourceFilesToGenerateSource.Combine(renameMapping).Select( + static (resourceFileAndRenameMapping, cancellationToken) => + { + var (resourceFile, renameMapping) = resourceFileAndRenameMapping; + if (renameMapping.TryGetValue(resourceFile.ResourceName, out var newHintName)) + { + return resourceFile with { ResourceHintName = newHintName }; + } + + return resourceFile; + }); + + context.RegisterSourceOutput( + resourceFilesToGenerateSourceWithNames, + static (context, resourceInformation) => + { + try + { + var impl = new Impl(resourceInformation); + if (impl.Execute(context.CancellationToken)) + { + context.AddSource(impl.OutputTextHintName, impl.OutputText); + } + } + catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested) + { + throw; + } + catch (Exception ex) + { + var exceptionLines = ex.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None); + var text = string.Join("", exceptionLines.Select(line => "#error " + line + Environment.NewLine)); + var errorText = SourceText.From(text, Encoding.UTF8, SourceHashAlgorithm.Sha256); + context.AddSource($"{resourceInformation.ResourceHintName}.Error", errorText); + } + }); + } + + private static bool? IsGenerateSource(AnalyzerConfigOptions options) + { + if (!options.TryGetValue("build_metadata.AdditionalFiles.GenerateSource", out var generateSourceText) + || !bool.TryParse(generateSourceText, out var generateSource)) + { + // This resource did not explicitly set GenerateSource to true or false + return null; + } + + return generateSource; + } + + private static bool IsExplicitWithCulture(AnalyzerConfigOptions options) + { + if (!options.TryGetValue("build_metadata.AdditionalFiles.WithCulture", out var withCultureText) + || !bool.TryParse(withCultureText, out var withCulture)) + { + // Assume the resource does not have a culture when there is no indication otherwise + return false; + } + + return withCulture; + } + + /// + /// + /// + /// + /// Language of source file to generate. Supported languages: CSharp, VisualBasic. + /// + private sealed record CompilationInformation( + string? AssemblyName, + string CodeLanguage, + bool SupportsNullable, + bool HasAggressiveInlining, + bool HasNotNullIfNotNull); + + /// + /// + /// + /// Information about the compilation. + /// Resources (resx) file. + /// Name of the embedded resources to generate accessor class for. + /// Unique identifying name for the generated resource file within the compilation. This will be the same as the last segment of (after the final .) except in the case of duplicates. + /// Optionally, a namespace.type name for the generated Resources accessor class. Defaults to if unspecified. + /// If set to , the GetResourceString method is not included in the generated class and must be specified in a separate source file. + /// If set to , emits constant key strings instead of properties that retrieve values. + /// If set to , calls to GetResourceString receive a default resource string value. + /// If set to , the generated code will include .FormatXYZ(...) methods. + /// If set to , the generated class will be declared ; otherwise, it will be declared . + /// List of of disabled warnings, adding a #pragma warning disable. + private sealed record ResourceInformation( + CompilationInformation CompilationInformation, + AdditionalText ResourceFile, + string ResourceName, + string ResourceHintName, + string? ResourceClassName, + bool OmitGetResourceString, + bool AsConstants, + bool IncludeDefaultValues, + bool EmitFormatMethods, + bool Public, + string[] NoWarn); + + private sealed class ImmutableDictionaryEqualityComparer : IEqualityComparer?> + where TKey : notnull + { + public static readonly ImmutableDictionaryEqualityComparer Instance = new(); + + public bool Equals(ImmutableDictionary? x, ImmutableDictionary? y) + { + if (ReferenceEquals(x, y)) + return true; + + if (x is null || y is null) + return false; + + if (!Equals(x.KeyComparer, y.KeyComparer)) + return false; + + if (!Equals(x.ValueComparer, y.ValueComparer)) + return false; + + foreach (var (key, value) in x) + { + if (!y.TryGetValue(key, out var other) + || !x.ValueComparer.Equals(value, other)) + { + return false; + } + } + + return true; + } + + public int GetHashCode(ImmutableDictionary? obj) + { + return obj?.Count ?? 0; + } + } + + private sealed class Impl + { + private const int maxDocCommentLength = 256; + + public Impl(ResourceInformation resourceInformation) + { + ResourceInformation = resourceInformation; + OutputText = SourceText.From("", Encoding.UTF8); + } + + public ResourceInformation ResourceInformation { get; } + public CompilationInformation CompilationInformation => ResourceInformation.CompilationInformation; + + public string? OutputTextHintName { get; private set; } + public SourceText OutputText { get; private set; } + + private enum Lang + { + CSharp, + VisualBasic, + } + + private void LogError(Lang language, string message) + { + var result = language switch + { + Lang.CSharp => $"#error {message}", + Lang.VisualBasic => $"#Error \"{message}\"", + _ => message, + }; + + OutputText = SourceText.From(result, Encoding.UTF8, SourceHashAlgorithm.Sha256); + } + + [MemberNotNullWhen(true, nameof(OutputTextHintName), nameof(OutputText))] + public bool Execute(CancellationToken cancellationToken) + { + Lang language; + switch (CompilationInformation.CodeLanguage) + { + case LanguageNames.CSharp: + language = Lang.CSharp; + break; + + case LanguageNames.VisualBasic: + language = Lang.VisualBasic; + break; + + default: + LogError(Lang.CSharp, $"GenerateResxSource doesn't support language: '{CompilationInformation.CodeLanguage}'"); + return false; + } + + var extension = language switch + { + Lang.CSharp => "cs", + Lang.VisualBasic => "vb", + _ => "cs", + }; + + OutputTextHintName = ResourceInformation.ResourceHintName + $".Designer.{extension}"; + + if (string.IsNullOrEmpty(ResourceInformation.ResourceName)) + { + LogError(language, "ResourceName not specified"); + return false; + } + + var resourceAccessName = RoslynString.IsNullOrEmpty(ResourceInformation.ResourceClassName) ? ResourceInformation.ResourceName : ResourceInformation.ResourceClassName; + SplitName(resourceAccessName, out var namespaceName, out var className); + + var classIndent = namespaceName == null ? "" : " "; + var memberIndent = classIndent + " "; + + var text = ResourceInformation.ResourceFile.GetText(cancellationToken); + if (text is null) + { + LogError(language, "ResourceFile was null"); + return false; + } + + var strings = new StringBuilder(); + foreach (var node in XDocument.Load(new SourceTextReader(text)).Descendants("data")) + { + var name = node.Attribute("name")?.Value; + if (name == null) + { + LogError(language, "Missing resource name"); + return false; + } + + var value = node.Elements("value").FirstOrDefault()?.Value.Trim(); + if (value == null) + { + LogError(language, $"Missing resource value: '{name}'"); + return false; + } + + if (name.Length == 0) + { + LogError(language, $"Empty resource name"); + return false; + } + + var docCommentString = value.Length > maxDocCommentLength ? value.Substring(0, maxDocCommentLength) + " ..." : value; + + RenderDocComment(language, memberIndent, strings, docCommentString); + + var identifier = GetIdentifierFromResourceName(name); + + var defaultValue = ResourceInformation.IncludeDefaultValues ? ", " + CreateStringLiteral(value, language) : string.Empty; + + switch (language) + { + case Lang.CSharp: + if (ResourceInformation.AsConstants) + { + strings.AppendLine($"{memberIndent}public const string @{identifier} = \"{name}\";"); + } + else + { + var needSuppression = false; + if (CompilationInformation.SupportsNullable) + { + // We need a suppression unless default values are included and the NotNullIfNotNull + // attribute has been applied to eliminated the need for a suppression + if (!ResourceInformation.IncludeDefaultValues || !CompilationInformation.HasNotNullIfNotNull) + needSuppression = true; + } + + strings.AppendLine($"{memberIndent}public static string @{identifier} => GetResourceString(\"{name}\"{defaultValue}){(needSuppression ? "!" : "")};"); + } + + if (ResourceInformation.EmitFormatMethods) + { + var resourceString = new ResourceString(name, value); + + if (resourceString.HasArguments) + { + RenderDocComment(language, memberIndent, strings, docCommentString); + RenderFormatMethod(memberIndent, language, CompilationInformation.SupportsNullable, strings, resourceString); + } + } + + break; + + case Lang.VisualBasic: + if (ResourceInformation.AsConstants) + { + strings.AppendLine($"{memberIndent}Public Const [{identifier}] As String = \"{name}\""); + } + else + { + strings.AppendLine($"{memberIndent}Public Shared ReadOnly Property [{identifier}] As String"); + strings.AppendLine($"{memberIndent} Get"); + strings.AppendLine($"{memberIndent} Return GetResourceString(\"{name}\"{defaultValue})"); + strings.AppendLine($"{memberIndent} End Get"); + strings.AppendLine($"{memberIndent}End Property"); + } + + if (ResourceInformation.EmitFormatMethods) + { + throw new NotImplementedException(); + } + + break; + + default: + throw new InvalidOperationException(); + } + } + + string? getStringMethod; + if (ResourceInformation.OmitGetResourceString) + { + getStringMethod = null; + } + else + { + switch (language) + { + case Lang.CSharp: + var getResourceStringAttributes = new List(); + if (CompilationInformation.HasAggressiveInlining) + { + getResourceStringAttributes.Add("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + } + + if (CompilationInformation.HasNotNullIfNotNull) + { + getResourceStringAttributes.Add("[return: global::System.Diagnostics.CodeAnalysis.NotNullIfNotNull(\"defaultValue\")]"); + } + + getStringMethod = $@"{memberIndent}public static global::System.Globalization.CultureInfo{(CompilationInformation.SupportsNullable ? "?" : "")} Culture {{ get; set; }} +{string.Join(Environment.NewLine, getResourceStringAttributes.Select(attr => memberIndent + attr))} +{memberIndent}internal static {(CompilationInformation.SupportsNullable ? "string?" : "string")} GetResourceString(string resourceKey, {(CompilationInformation.SupportsNullable ? "string?" : "string")} defaultValue = null) => ResourceManager.GetString(resourceKey, Culture) ?? defaultValue;"; + if (ResourceInformation.EmitFormatMethods) + { + getStringMethod += $@" + +{memberIndent}private static string GetResourceString(string resourceKey, string[]? formatterNames) +{memberIndent}{{ +{memberIndent} var value = GetResourceString(resourceKey) ?? """"; +{memberIndent} if (formatterNames != null) +{memberIndent} {{ +{memberIndent} for (var i = 0; i < formatterNames.Length; i++) +{memberIndent} {{ +{memberIndent} value = value.Replace(""{{"" + formatterNames[i] + ""}}"", ""{{"" + i + ""}}""); +{memberIndent} }} +{memberIndent} }} +{memberIndent} return value; +{memberIndent}}} +"; + } + + break; + + case Lang.VisualBasic: + getStringMethod = $@"{memberIndent}Public Shared Property Culture As Global.System.Globalization.CultureInfo +{memberIndent} +{memberIndent}Friend Shared Function GetResourceString(ByVal resourceKey As String, Optional ByVal defaultValue As String = Nothing) As String +{memberIndent} Return ResourceManager.GetString(resourceKey, Culture) +{memberIndent}End Function"; + if (ResourceInformation.EmitFormatMethods) + { + throw new NotImplementedException(); + } + + break; + + default: + throw new InvalidOperationException(); + } + } + + string? namespaceStart, namespaceEnd; + if (namespaceName == null) + { + namespaceStart = namespaceEnd = null; + } + else + { + switch (language) + { + case Lang.CSharp: + namespaceStart = $@"namespace {namespaceName}{Environment.NewLine}{{"; + namespaceEnd = "}"; + break; + + case Lang.VisualBasic: + namespaceStart = $"Namespace Global.{namespaceName}"; + namespaceEnd = "End Namespace"; + break; + + default: + throw new InvalidOperationException(); + } + } + + string resourceTypeName; + string? resourceTypeDefinition; + if (string.IsNullOrEmpty(ResourceInformation.ResourceClassName) || ResourceInformation.ResourceName == ResourceInformation.ResourceClassName) + { + // resource name is same as accessor, no need for a second type. + resourceTypeName = className; + resourceTypeDefinition = null; + } + else + { + // resource name differs from the access class, need a type for specifying the resources + // this empty type must remain as it is required by the .NETNative toolchain for locating resources + // once assemblies have been merged into the application + resourceTypeName = ResourceInformation.ResourceName; + + SplitName(resourceTypeName, out var resourceNamespaceName, out var resourceClassName); + var resourceClassIndent = resourceNamespaceName == null ? "" : " "; + + switch (language) + { + case Lang.CSharp: + resourceTypeDefinition = $"{resourceClassIndent}internal static class {resourceClassName} {{ }}"; + if (resourceNamespaceName != null) + { + resourceTypeDefinition = $@"namespace {resourceNamespaceName} +{{ +{resourceTypeDefinition} +}}"; + } + + break; + + case Lang.VisualBasic: + resourceTypeDefinition = $@"{resourceClassIndent}Friend Class {resourceClassName} +{resourceClassIndent}End Class"; + if (resourceNamespaceName != null) + { + resourceTypeDefinition = $@"Namespace {resourceNamespaceName} +{resourceTypeDefinition} +End Namespace"; + } + + break; + + default: + throw new InvalidOperationException(); + } + } + + // List of NoWarn + string? noWarnDisabled = null; + string? noWarnRestored = null; + if (ResourceInformation.NoWarn.Length > 0) + { + var noWarnList = string.Join(", ", ResourceInformation.NoWarn); + var crLf = Environment.NewLine; + + switch (language) + { + case Lang.CSharp: + noWarnDisabled = $"{crLf}{crLf}#pragma warning disable {noWarnList}"; + noWarnRestored = $"{crLf}{crLf}#pragma warning restore {noWarnList}"; + break; + + case Lang.VisualBasic: + noWarnDisabled = $"{crLf}{crLf}#Disable Warning {noWarnList}"; + noWarnRestored = $"{crLf}{crLf}#Enable Warning {noWarnList}"; + break; + + default: + throw new InvalidOperationException(); + } + } + + // The ResourceManager property being initialized lazily is an important optimization that lets .NETNative + // completely remove the ResourceManager class if the disk space saving optimization to strip resources + // (/DisableExceptionMessages) is turned on in the compiler. + string result; + switch (language) + { + case Lang.CSharp: + result = $@"// {noWarnDisabled} + +{(CompilationInformation.SupportsNullable ? "#nullable enable" : "")} +using System.Reflection; + +{resourceTypeDefinition} +{namespaceStart} +{classIndent}{(ResourceInformation.Public ? "public" : "internal")} static partial class {className} +{classIndent}{{ +{memberIndent}private static global::System.Resources.ResourceManager{(CompilationInformation.SupportsNullable ? "?" : "")} s_resourceManager; +{memberIndent}public static global::System.Resources.ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new global::System.Resources.ResourceManager(typeof({resourceTypeName}))); +{getStringMethod} +{strings} +{classIndent}}} +{namespaceEnd}{noWarnRestored} +"; + break; + + case Lang.VisualBasic: + result = $@"' {noWarnDisabled} + +Imports System.Reflection + +{resourceTypeDefinition} +{namespaceStart} +{classIndent}{(ResourceInformation.Public ? "Public" : "Friend")} Partial Class {className} +{memberIndent}Private Sub New +{memberIndent}End Sub +{memberIndent} +{memberIndent}Private Shared s_resourceManager As Global.System.Resources.ResourceManager +{memberIndent}Public Shared ReadOnly Property ResourceManager As Global.System.Resources.ResourceManager +{memberIndent} Get +{memberIndent} If s_resourceManager Is Nothing Then +{memberIndent} s_resourceManager = New Global.System.Resources.ResourceManager(GetType({resourceTypeName})) +{memberIndent} End If +{memberIndent} Return s_resourceManager +{memberIndent} End Get +{memberIndent}End Property +{getStringMethod} +{strings} +{classIndent}End Class +{namespaceEnd}{noWarnRestored} +"; + break; + + default: + throw new InvalidOperationException(); + } + + OutputText = SourceText.From(result, Encoding.UTF8, SourceHashAlgorithm.Sha256); + return true; + } + + internal static string GetIdentifierFromResourceName(string name) + { + if (name.All(IsIdentifierPartCharacter)) + { + return IsIdentifierStartCharacter(name[0]) ? name : "_" + name; + } + + var builder = new StringBuilder(name.Length); + + var f = name[0]; + if (IsIdentifierPartCharacter(f) && !IsIdentifierStartCharacter(f)) + { + builder.Append('_'); + } + + foreach (var c in name) + { + builder.Append(IsIdentifierPartCharacter(c) ? c : '_'); + } + + return builder.ToString(); + + static bool IsIdentifierStartCharacter(char ch) + => ch == '_' || IsLetterChar(CharUnicodeInfo.GetUnicodeCategory(ch)); + + static bool IsIdentifierPartCharacter(char ch) + { + var cat = CharUnicodeInfo.GetUnicodeCategory(ch); + return IsLetterChar(cat) + || cat == UnicodeCategory.DecimalDigitNumber + || cat == UnicodeCategory.ConnectorPunctuation + || cat == UnicodeCategory.Format + || cat == UnicodeCategory.NonSpacingMark + || cat == UnicodeCategory.SpacingCombiningMark; + } + + static bool IsLetterChar(UnicodeCategory cat) + { + switch (cat) + { + case UnicodeCategory.UppercaseLetter: + case UnicodeCategory.LowercaseLetter: + case UnicodeCategory.TitlecaseLetter: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.OtherLetter: + case UnicodeCategory.LetterNumber: + return true; + } + + return false; + } + } + + private static void RenderDocComment(Lang language, string memberIndent, StringBuilder strings, string value) + { + var docCommentStart = language == Lang.CSharp + ? "///" + : "'''"; + + var escapedTrimmedValue = new XElement("summary", value).ToString(); + + foreach (var line in escapedTrimmedValue.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)) + { + strings.Append(memberIndent).Append(docCommentStart).Append(' '); + strings.AppendLine(line); + } + } + + private static string CreateStringLiteral(string original, Lang lang) + { + var stringLiteral = new StringBuilder(original.Length + 3); + if (lang == Lang.CSharp) + { + stringLiteral.Append('@'); + } + + stringLiteral.Append('\"'); + for (var i = 0; i < original.Length; i++) + { + // duplicate '"' for VB and C# + if (original[i] == '\"') + { + stringLiteral.Append('"'); + } + + stringLiteral.Append(original[i]); + } + + stringLiteral.Append('\"'); + + return stringLiteral.ToString(); + } + + private static void SplitName(string fullName, out string? namespaceName, out string className) + { + var lastDot = fullName.LastIndexOf('.'); + if (lastDot == -1) + { + namespaceName = null; + className = fullName; + } + else + { + namespaceName = fullName.Substring(0, lastDot); + className = fullName.Substring(lastDot + 1); + } + } + + private static void RenderFormatMethod(string indent, Lang language, bool supportsNullable, StringBuilder strings, ResourceString resourceString) + { + strings.AppendLine($"{indent}internal static string Format{resourceString.Name}({resourceString.GetMethodParameters(language, supportsNullable)})"); + if (resourceString.UsingNamedArgs) + { + strings.AppendLine($@"{indent} => string.Format(Culture, GetResourceString(""{resourceString.Name}"", new[] {{ {resourceString.GetArgumentNames()} }}), {resourceString.GetArguments()});"); + } + else + { + strings.AppendLine($@"{indent} => string.Format(Culture, GetResourceString(""{resourceString.Name}"") ?? """", {resourceString.GetArguments()});"); + } + + strings.AppendLine(); + } + + private class ResourceString + { + private static readonly Regex _namedParameterMatcher = new(@"\{([a-z]\w*)\}", RegexOptions.IgnoreCase | RegexOptions.Compiled); + private static readonly Regex _numberParameterMatcher = new(@"\{(\d+)\}", RegexOptions.Compiled); + private readonly IReadOnlyList _arguments; + + public ResourceString(string name, string value) + { + Name = name; + Value = value; + + var match = _namedParameterMatcher.Matches(value); + UsingNamedArgs = match.Count > 0; + + if (!UsingNamedArgs) + { + match = _numberParameterMatcher.Matches(value); + } + + var arguments = match.Cast() + .Select(m => m.Groups[1].Value) + .Distinct(); + if (!UsingNamedArgs) + { + arguments = arguments.OrderBy(Convert.ToInt32); + } + + _arguments = arguments.ToList(); + } + + public string Name { get; } + + public string Value { get; } + + public bool UsingNamedArgs { get; } + + public bool HasArguments => _arguments.Count > 0; + + public string GetArgumentNames() => string.Join(", ", _arguments.Select(a => "\"" + a + "\"")); + + public string GetArguments() => string.Join(", ", _arguments.Select(GetArgName)); + + public string GetMethodParameters(Lang language, bool supportsNullable) + { + switch (language) + { + case Lang.CSharp: + return string.Join(", ", _arguments.Select(a => $"object{(supportsNullable ? "?" : "")} " + GetArgName(a))); + case Lang.VisualBasic: + return string.Join(", ", _arguments.Select(GetArgName)); + default: + throw new NotImplementedException(); + } + } + + private string GetArgName(string name) => UsingNamedArgs ? name : 'p' + name; + } + } + + private sealed class SourceTextReader : TextReader + { + private readonly SourceText _text; + private int _position; + + public SourceTextReader(SourceText text) + { + _text = text; + } + + public override int Read(char[] buffer, int index, int count) + { + var remaining = _text.Length - _position; + var charactersToRead = Math.Min(remaining, count); + _text.CopyTo(_position, buffer, index, charactersToRead); + _position += charactersToRead; + return charactersToRead; + } + } + } +} diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.csproj b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.csproj new file mode 100644 index 0000000000000..bf61522d0acb8 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + false + true + + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForResxSourceGenerators) + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Resources.resx b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Resources.resx new file mode 100644 index 0000000000000..d58980a38d714 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/Resources.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.cs.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.cs.xlf new file mode 100644 index 0000000000000..99bb7f1d7ed61 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.cs.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.de.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.de.xlf new file mode 100644 index 0000000000000..2b1fd700ef3da --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.de.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.es.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.es.xlf new file mode 100644 index 0000000000000..a4f8872fd3b16 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.es.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.fr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.fr.xlf new file mode 100644 index 0000000000000..8231293ba6bf0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.fr.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.it.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.it.xlf new file mode 100644 index 0000000000000..86dbb08ee69e0 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.it.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ja.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ja.xlf new file mode 100644 index 0000000000000..737849421dac8 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ja.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ko.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ko.xlf new file mode 100644 index 0000000000000..4997d851652a4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ko.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pl.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pl.xlf new file mode 100644 index 0000000000000..b0181a3f58617 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pl.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pt-BR.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pt-BR.xlf new file mode 100644 index 0000000000000..56a8322a36be4 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.pt-BR.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ru.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ru.xlf new file mode 100644 index 0000000000000..7f33dd37bec4e --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.ru.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.tr.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.tr.xlf new file mode 100644 index 0000000000000..cb11b99f6a741 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.tr.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hans.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hans.xlf new file mode 100644 index 0000000000000..d831e742d4dc9 --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hans.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hant.xlf b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hant.xlf new file mode 100644 index 0000000000000..a0c04874458aa --- /dev/null +++ b/src/RoslynAnalyzers/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator/xlf/Resources.zh-Hant.xlf @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Directory.Build.targets b/src/RoslynAnalyzers/NuGet/Directory.Build.targets new file mode 100644 index 0000000000000..62ab211f7dd95 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Directory.Build.targets @@ -0,0 +1,12 @@ + + + + + + + true + false + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.Package.csproj new file mode 100644 index 0000000000000..ae976160be1ce --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.AnalyzerUtilities/Microsoft.CodeAnalysis.AnalyzerUtilities.Package.csproj @@ -0,0 +1,35 @@ + + + + netstandard2.0 + true + false + Microsoft.CodeAnalysis.AnalyzerUtilities + Analyzer utilities for various analyses, including Dataflow analysis based on ControlFlowGraph API in Microsoft.CodeAnalysis. + Analyzer utilities for various analyses, including Dataflow analysis + Analyzer utilities for various analyses, including Dataflow analysis based on ControlFlowGraph API in Microsoft.CodeAnalysis. + Roslyn Analyzer Utilities CodeAnalysis Dataflow ControlFlowGraph FlowAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + true + + + *$(MSBuildProjectFile)* + false + + + $(AnalyzerUtilitiesVersionPrefix) + + $(NoWarn);NU5128 + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.Package.csproj new file mode 100644 index 0000000000000..419b0192beb72 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Analyzers/Microsoft.CodeAnalysis.Analyzers.Package.csproj @@ -0,0 +1,34 @@ + + + + netstandard2.0 + true + false + Microsoft.CodeAnalysis.Analyzers + Analyzers for consumers of "Microsoft.CodeAnalysis" NuGet package, i.e. extensions and applications built on top of .NET Compiler Platform ("Roslyn"). This package is included as a development dependency of "Microsoft.CodeAnalysis" NuGet package and does not need to be installed separately if you are referencing "Microsoft.CodeAnalysis" NuGet package. + Analyzers for .NET Compiler Platform ("Roslyn") + Diagnostic analyzers for the Microsoft .NET Compiler Platform (Roslyn) + Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + documentation\readme.md + true + + + *$(MSBuildProjectFile)* + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.Package.csproj new file mode 100644 index 0000000000000..ff9cc50cd97c2 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.BannedApiAnalyzers/Microsoft.CodeAnalysis.BannedApiAnalyzers.Package.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + + true + true + false + Microsoft.CodeAnalysis.BannedApiAnalyzers + Banned API Analyzers + Banned API Analyzers + Banned API Analyzers + Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics BannedApi ApiAnalyzer + true + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj new file mode 100644 index 0000000000000..e197e3e509867 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.Package.csproj @@ -0,0 +1,53 @@ + + + + net472 + + true + true + false + Microsoft.CodeAnalysis.Metrics + Report source based Code Metrics + Microsoft.CodeAnalysis.Metrics + Tool to report source based code metrics + Roslyn CodeAnalysis CodeMetrics Metrics Compiler CSharp VB VisualBasic Syntax Semantics + false + false + true + $(NoWarn);NU5100 + $(MetricsVersionPrefix) + + $(MicrosoftCodeAnalysisVersionForMetrics) + 1.1.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.targets b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.targets new file mode 100644 index 0000000000000..001ea39016509 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.Metrics/Microsoft.CodeAnalysis.Metrics.targets @@ -0,0 +1,19 @@ + + + + $(MSBuildThisFileDirectory)\..\Metrics + Metrics + $(MSBuildProjectName).Metrics.xml + + + + $(MetricsExeFolder).Legacy + $(MetricsExeName).Legacy + + + + + + + diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.Package.csproj new file mode 100644 index 0000000000000..249051463d996 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.ResxSourceGenerator/Microsoft.CodeAnalysis.ResxSourceGenerator.Package.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + + true + true + false + Microsoft.CodeAnalysis.ResxSourceGenerator + Source generator for resource files + Source generator for resource files + Source generator for resource files + Microsoft.CodeAnalysis.ResxSourceGenerator, analyzers + true + true + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter.Package.csproj b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter.Package.csproj new file mode 100644 index 0000000000000..290ffc46d8b05 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter/Microsoft.CodeAnalysis.RulesetToEditorconfigConverter.Package.csproj @@ -0,0 +1,38 @@ + + + + net472 + + true + false + Microsoft.CodeAnalysis.RulesetToEditorconfigConverter + Utility to convert ruleset files to equivalent .editorconfig files. Editorconfig files are respected by C# and VB compilers on VS2019 16.3 or later. See https://learn.microsoft.com/visualstudio/code-quality/use-roslyn-analyzers#set-rule-severity-in-an-editorconfig-file for details. + Utility to convert ruleset files to equivalent .editorconfig files + Utility to convert ruleset files to equivalent .editorconfig files + Roslyn CodeAnalysis Ruleset EditorConfig Compiler FxCop CSharp VB VisualBasic Syntax Semantics + false + false + true + + $(NoWarn);NU5100 + + $(MicrosoftCodeAnalysisVersionForExecution) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Package.csproj new file mode 100644 index 0000000000000..73b944fbf7de1 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Package.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0 + + true + true + false + Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers + PerformanceSensitive Analyzers + PerformanceSensitive Analyzers + PerformanceSensitive Analyzers + Roslyn CodeAnalysis Compiler CSharp Diagnostic Analyzers Syntax Semantics Performance + $(RepoRoot)src\RoslynAnalyzers\PerformanceSensitiveAnalyzers + $(RepoRoot)src\RoslynAnalyzers\PerformanceSensitiveAnalyzers + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.cs b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.cs new file mode 100644 index 0000000000000..47f2139618b2b --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.cs @@ -0,0 +1,117 @@ +// +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +#pragma warning disable CS1574 // XML comment has cref attribute that could not be resolved (not all builds have all types) + +using System; +using System.Diagnostics; + +namespace Roslyn.Utilities +{ + /// + /// Indicates that a code element is performance sensitive under a known scenario. + /// + /// + /// When applying this attribute, only explicitly set the values for properties specifically indicated by the + /// test/measurement technique described in the associated . + /// + [Conditional("EMIT_CODE_ANALYSIS_ATTRIBUTES")] + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] + internal sealed class PerformanceSensitiveAttribute : Attribute + { + public PerformanceSensitiveAttribute(string uri) + { + Uri = uri; + } + + /// + /// Gets the location where the original problem is documented, likely with steps to reproduce the issue and/or + /// validate performance related to a change in the method. + /// + public string Uri + { + get; + } + + /// + /// Gets or sets a description of the constraint imposed by the original performance issue. + /// + /// + /// Constraints are normally specified by other specific properties that allow automated validation of the + /// constraint. This property supports documenting constraints which cannot be described in terms of other + /// constraint properties. + /// + public string Constraint + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether captures are allowed. + /// + public bool AllowCaptures + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether implicit boxing of value types is allowed. + /// + public bool AllowImplicitBoxing + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether enumeration of a generic + /// is allowed. + /// + public bool AllowGenericEnumeration + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether locks are allowed. + /// + public bool AllowLocks + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether the asynchronous state machine typically completes synchronously. + /// + /// + /// When , validation of this performance constraint typically involves analyzing + /// the method to ensure synchronous completion of the state machine does not require the allocation of a + /// , either through caching the result or by using + /// . + /// + public bool OftenCompletesSynchronously + { + get; + set; + } + + /// + /// Gets or sets a value indicating whether this is an entry point to a parallel algorithm. + /// + /// + /// Parallelization APIs and algorithms, e.g. Parallel.ForEach, may be efficient for parallel entry + /// points (few direct calls but large amounts of iterative work), but are problematic when called inside the + /// iterations themselves. Performance-sensitive code should avoid the use of heavy parallelization APIs except + /// for known entry points to the parallel portion of code. + /// + public bool IsParallelEntry + { + get; + set; + } + } +} diff --git a/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.vb b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.vb new file mode 100644 index 0000000000000..675f371ed6171 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/PerformanceSensitiveAnalyzers/PerformanceSensitiveAttribute.vb @@ -0,0 +1,83 @@ +' +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System +Imports System.Diagnostics + +Namespace Global.Roslyn.Utilities + ''' + ''' Indicates that a code element is performance sensitive under a known scenario. + ''' + ''' + ''' When applying this attribute, only explicitly set the values for properties specifically indicated by the + ''' test/measurement technique described in the associated . + ''' + + + Friend NotInheritable Class PerformanceSensitiveAttribute + Inherits Attribute + + Public Sub New(uri As String) + Me.Uri = uri + End Sub + + ''' + ''' Gets the location where the original problem is documented, likely with steps to reproduce the issue and/or + ''' validate performance related to a change in the method. + ''' + Public ReadOnly Property Uri As String + + ''' + ''' Gets or sets a description of the constraint imposed by the original performance issue. + ''' + ''' + ''' Constraints are normally specified by other specific properties that allow automated validation of the + ''' constraint. This property supports documenting constraints which cannot be described in terms of other + ''' constraint properties. + ''' + Public Property Constraint As String + + ''' + ''' Gets or sets a value indicating whether captures are allowed. + ''' + Public Property AllowCaptures As Boolean + + ''' + ''' Gets or sets a value indicating whether implicit boxing of value types is allowed. + ''' + Public Property AllowImplicitBoxing As Boolean + + ''' + ''' Gets or sets a value indicating whether enumeration of a generic + ''' is allowed. + ''' + Public Property AllowGenericEnumeration As Boolean + + ''' + ''' Gets or sets a value indicating whether locks are allowed. + ''' + Public Property AllowLocks As Boolean + + ''' + ''' Gets or sets a value indicating whether the asynchronous state machine typically completes synchronously. + ''' + ''' + ''' When , validation of this performance constraint typically involves analyzing + ''' the method to ensure synchronous completion of the state machine does not require the allocation of a + ''' , either through caching the result or by using + ''' . + ''' + Public Property OftenCompletesSynchronously As Boolean + + ''' + ''' Gets or sets a value indicating whether this is an entry point to a parallel algorithm. + ''' + ''' + ''' Parallelization APIs and algorithms, e.g. Parallel.ForEach, may be efficient for parallel entry + ''' points (few direct calls but large amounts of iterative work), but are problematic when called inside the + ''' iterations themselves. Performance-sensitive code should avoid the use of heavy parallelization APIs except + ''' for known entry points to the parallel portion of code. + ''' + Public Property IsParallelEntry As Boolean + End Class +End Namespace diff --git a/src/RoslynAnalyzers/NuGet/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj new file mode 100644 index 0000000000000..ce5baf829698a --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.Package.csproj @@ -0,0 +1,36 @@ + + + + netstandard2.0 + + true + true + false + Microsoft.CodeAnalysis.PublicApiAnalyzers + Public API Analyzers + Public API Analyzer + Public API Analyzers + Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics PublicApi ApiAnalyzer + $(RepoRoot)src\RoslynAnalyzers\PublicApiAnalyzers + $(RepoRoot)src\RoslynAnalyzers\PublicApiAnalyzers + true + + + + + + portable-net45+win8 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj new file mode 100644 index 0000000000000..0bd27bf6bdba8 --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.Package.csproj @@ -0,0 +1,38 @@ + + + + netstandard2.0 + + true + false + Roslyn.Diagnostics.Analyzers + Private analyzers specific to Roslyn repo. These analyzers are not intended for public consumptions outside of the Roslyn repo. + Roslyn.Diagnostics Analyzers + Roslyn.Diagnostics Analyzers + Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + true + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/NuGet/Text.Analyzers/Text.Analyzers.Package.csproj b/src/RoslynAnalyzers/NuGet/Text.Analyzers/Text.Analyzers.Package.csproj new file mode 100644 index 0000000000000..91a3d03a350ab --- /dev/null +++ b/src/RoslynAnalyzers/NuGet/Text.Analyzers/Text.Analyzers.Package.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.0 + + true + true + false + Text.Analyzers + Text Analyzers + Text Analyzers + Text Analyzers + Roslyn CodeAnalysis Compiler CSharp VB VisualBasic Diagnostic Analyzers Syntax Semantics + true + + true + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..da24a7daa8df0 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzerReleases.Unshipped.md @@ -0,0 +1,18 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +HAA0101 | Performance | Warning | CallSiteImplicitAllocationAnalyzer +HAA0102 | Performance | Warning | CallSiteImplicitAllocationAnalyzer +HAA0201 | Performance | Warning | ConcatenationAllocationAnalyzer, [Documentation](http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.110).aspx) +HAA0202 | Performance | Warning | ConcatenationAllocationAnalyzer, [Documentation](http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx) +HAA0301 | Performance | Warning | DisplayClassAllocationAnalyzer +HAA0302 | Performance | Warning | DisplayClassAllocationAnalyzer +HAA0303 | Performance | Warning | DisplayClassAllocationAnalyzer +HAA0401 | Performance | Warning | EnumeratorAllocationAnalyzer +HAA0601 | Performance | Warning | TypeConversionAllocationAnalyzer +HAA0602 | Performance | Warning | TypeConversionAllocationAnalyzer +HAA0603 | Performance | Warning | TypeConversionAllocationAnalyzer +HAA0604 | Performance | Info | TypeConversionAllocationAnalyzer diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.cs new file mode 100644 index 0000000000000..dc02812379c12 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + internal partial class AnalyzersResources + { + private static readonly Type s_resourcesType = typeof(AnalyzersResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.resx b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.resx new file mode 100644 index 0000000000000..32e87d57035ed --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/AnalyzersResources.resx @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + + + Display class allocation to capture closure + + + Heap allocation of closure Captures: {0} + + + Closure Allocation Source + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + + + Delegate on struct instance caused a boxing allocation + + + Considering moving this out of the generic method + + + Lambda or anonymous method in a generic method allocates a delegate instance + + + This will allocate a delegate instance + + + Delegate allocation from a method group + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + + + Array allocation for params parameter + + + This will allocate a delegate instance + + + Delegate allocation from a method group + + + Non-ValueType enumerator may result in a heap allocation + + + Possible allocation of reference type enumerator + + + Considering using StringBuilder + + + Implicit string concatenation allocation + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + + + Non-overridden virtual method call on value type + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + + + Value type to reference type conversion causing boxing allocation + + + Value type ({0}) is being boxed to a reference type for a string concatenation + + + Value type to reference type conversion allocation for string concatenation + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/CallSiteImplicitAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/CallSiteImplicitAllocationAnalyzer.cs new file mode 100644 index 0000000000000..bfdb7afaa5c87 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/CallSiteImplicitAllocationAnalyzer.cs @@ -0,0 +1,96 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + using static AnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class CallSiteImplicitAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string ParamsParameterRuleId = "HAA0101"; + public const string ValueTypeNonOverridenCallRuleId = "HAA0102"; + + internal static readonly DiagnosticDescriptor ParamsParameterRule = new( + ParamsParameterRuleId, + CreateLocalizableResourceString(nameof(ParamsParameterRuleTitle)), + CreateLocalizableResourceString(nameof(ParamsParameterRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor ValueTypeNonOverridenCallRule = new( + ValueTypeNonOverridenCallRuleId, + CreateLocalizableResourceString(nameof(ValueTypeNonOverridenCallRuleTitle)), + CreateLocalizableResourceString(nameof(ValueTypeNonOverridenCallRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(ParamsParameterRule, ValueTypeNonOverridenCallRule); + + protected override ImmutableArray Expressions { get; } = ImmutableArray.Create(SyntaxKind.InvocationExpression); + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info) + { + var node = context.Node; + var semanticModel = context.SemanticModel; + Action reportDiagnostic = context.ReportDiagnostic; + var cancellationToken = context.CancellationToken; + + if (semanticModel.GetOperation(node, cancellationToken) is not IInvocationOperation invocationOperation) + { + return; + } + + var targetMethod = invocationOperation.TargetMethod; + + if (targetMethod.IsOverride) + { + CheckNonOverridenMethodOnStruct(targetMethod, reportDiagnostic, node); + } + + bool compilationHasSystemArrayEmpty = !semanticModel.Compilation.GetSpecialType(SpecialType.System_Array).GetMembers("Empty").IsEmpty; + + // Loop on every argument because params argument may not be the last one. + // static void Fun1() => Fun2(args: "", i: 5); + // static void Fun2(int i = 0, params object[] args) {} + foreach (var argument in invocationOperation.Arguments) + { + if (argument.ArgumentKind == ArgumentKind.ParamArray) + { + // Up to net45 the System.Array.Empty singleton didn't existed so an empty params array was still causing some memory allocation. + if (argument.IsImplicit && + (!compilationHasSystemArrayEmpty || (argument.Value as IArrayCreationOperation)?.Initializer?.ElementValues.IsEmpty != true)) + { + reportDiagnostic(node.CreateDiagnostic(ParamsParameterRule)); + } + + break; + } + } + } + + private static void CheckNonOverridenMethodOnStruct(IMethodSymbol methodInfo, Action reportDiagnostic, SyntaxNode node) + { + if (methodInfo.ContainingType != null) + { + // hack? Hmmm. + var containingType = methodInfo.ContainingType.ToString(); + if (string.Equals(containingType, "System.ValueType", StringComparison.OrdinalIgnoreCase) || string.Equals(containingType, "System.Enum", StringComparison.OrdinalIgnoreCase)) + { + reportDiagnostic(node.CreateDiagnostic(ValueTypeNonOverridenCallRule)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/ConcatenationAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/ConcatenationAllocationAnalyzer.cs new file mode 100644 index 0000000000000..d542deb050ac2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/ConcatenationAllocationAnalyzer.cs @@ -0,0 +1,109 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + using static AnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class ConcatenationAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string StringConcatenationAllocationRuleId = "HAA0201"; + public const string ValueTypeToReferenceTypeInAStringConcatenationRuleId = "HAA0202"; + + internal static readonly DiagnosticDescriptor StringConcatenationAllocationRule = new( + StringConcatenationAllocationRuleId, + CreateLocalizableResourceString(nameof(StringConcatenationAllocationRuleTitle)), + CreateLocalizableResourceString(nameof(StringConcatenationAllocationRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.110).aspx"); + + internal static readonly DiagnosticDescriptor ValueTypeToReferenceTypeInAStringConcatenationRule = new( + ValueTypeToReferenceTypeInAStringConcatenationRuleId, + CreateLocalizableResourceString(nameof(ValueTypeToReferenceTypeInAStringConcatenationRuleTitle)), + CreateLocalizableResourceString(nameof(ValueTypeToReferenceTypeInAStringConcatenationRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx"); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(StringConcatenationAllocationRule, ValueTypeToReferenceTypeInAStringConcatenationRule); + + protected override ImmutableArray Expressions { get; } = ImmutableArray.Create(SyntaxKind.AddExpression, SyntaxKind.AddAssignmentExpression); + + private static readonly object[] EmptyMessageArgs = Array.Empty(); + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info) + { + var node = context.Node; + var semanticModel = context.SemanticModel; + Action reportDiagnostic = context.ReportDiagnostic; + var cancellationToken = context.CancellationToken; + var binaryExpressions = node.DescendantNodesAndSelf().OfType().Reverse(); // need inner most expressions + + int stringConcatenationCount = 0; + foreach (var binaryExpression in binaryExpressions) + { + if (binaryExpression.Left == null || binaryExpression.Right == null) + { + continue; + } + + bool isConstant = semanticModel.GetConstantValue(binaryExpression, cancellationToken).HasValue; + if (isConstant) + { + continue; + } + + var left = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken); + var leftConversion = semanticModel.GetConversion(binaryExpression.Left, cancellationToken); + CheckTypeConversion(left, leftConversion, reportDiagnostic, binaryExpression.Left); + + var right = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); + var rightConversion = semanticModel.GetConversion(binaryExpression.Right, cancellationToken); + CheckTypeConversion(right, rightConversion, reportDiagnostic, binaryExpression.Right); + + // regular string allocation + if (left.Type?.SpecialType == SpecialType.System_String || right.Type?.SpecialType == SpecialType.System_String) + { + stringConcatenationCount++; + } + } + + if (stringConcatenationCount > 3) + { + reportDiagnostic(node.CreateDiagnostic(StringConcatenationAllocationRule, EmptyMessageArgs)); + } + } + + private static void CheckTypeConversion(TypeInfo typeInfo, Conversion conversionInfo, Action reportDiagnostic, ExpressionSyntax expression) + { + if (conversionInfo.IsBoxing && typeInfo.Type != null && !IsOptimizedValueType(typeInfo.Type)) + { + reportDiagnostic(expression.CreateDiagnostic(ValueTypeToReferenceTypeInAStringConcatenationRule, typeInfo.Type.ToDisplayString())); + } + + return; + + static bool IsOptimizedValueType(ITypeSymbol type) + { + return type.SpecialType is SpecialType.System_Boolean or + SpecialType.System_Char or + SpecialType.System_IntPtr or + SpecialType.System_UIntPtr; + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/DisplayClassAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/DisplayClassAllocationAnalyzer.cs new file mode 100644 index 0000000000000..28c28eaaa27b0 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/DisplayClassAllocationAnalyzer.cs @@ -0,0 +1,120 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + using static AnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class DisplayClassAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string ClosureDriverRuleId = "HAA0301"; + public const string ClosureCaptureRuleId = "HAA0302"; + public const string LambaOrAnonymousMethodInGenericMethodRuleId = "HAA0303"; + + internal static readonly DiagnosticDescriptor ClosureDriverRule = new( + ClosureDriverRuleId, + CreateLocalizableResourceString(nameof(ClosureDriverRuleTitle)), + CreateLocalizableResourceString(nameof(ClosureDriverRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor ClosureCaptureRule = new( + ClosureCaptureRuleId, + CreateLocalizableResourceString(nameof(ClosureCaptureRuleTitle)), + CreateLocalizableResourceString(nameof(ClosureCaptureRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor LambaOrAnonymousMethodInGenericMethodRule = new( + LambaOrAnonymousMethodInGenericMethodRuleId, + CreateLocalizableResourceString(nameof(LambaOrAnonymousMethodInGenericMethodRuleTitle)), + CreateLocalizableResourceString(nameof(LambaOrAnonymousMethodInGenericMethodRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(ClosureCaptureRule, ClosureDriverRule, LambaOrAnonymousMethodInGenericMethodRule); + + protected override ImmutableArray Expressions { get; } = ImmutableArray.Create(SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.SimpleLambdaExpression, SyntaxKind.AnonymousMethodExpression); + + private static readonly object[] EmptyMessageArgs = Array.Empty(); + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info) + { + var node = context.Node; + var semanticModel = context.SemanticModel; + var cancellationToken = context.CancellationToken; + Action reportDiagnostic = context.ReportDiagnostic; + + var anonExpr = node as AnonymousMethodExpressionSyntax; + if (anonExpr?.Block?.ChildNodes() != null && anonExpr.Block.ChildNodes().Any()) + { + GenericMethodCheck(semanticModel, node, anonExpr.DelegateKeyword.GetLocation(), reportDiagnostic, cancellationToken); + ClosureCaptureDataFlowAnalysis(semanticModel.AnalyzeDataFlow(anonExpr.Block.ChildNodes().First(), anonExpr.Block.ChildNodes().Last()), reportDiagnostic, anonExpr.DelegateKeyword.GetLocation()); + return; + } + + if (node is SimpleLambdaExpressionSyntax lambdaExpr) + { + GenericMethodCheck(semanticModel, node, lambdaExpr.ArrowToken.GetLocation(), reportDiagnostic, cancellationToken); + ClosureCaptureDataFlowAnalysis(semanticModel.AnalyzeDataFlow(lambdaExpr), reportDiagnostic, lambdaExpr.ArrowToken.GetLocation()); + return; + } + + if (node is ParenthesizedLambdaExpressionSyntax parenLambdaExpr) + { + GenericMethodCheck(semanticModel, node, parenLambdaExpr.ArrowToken.GetLocation(), reportDiagnostic, cancellationToken); + ClosureCaptureDataFlowAnalysis(semanticModel.AnalyzeDataFlow(parenLambdaExpr), reportDiagnostic, parenLambdaExpr.ArrowToken.GetLocation()); + return; + } + } + + private static void ClosureCaptureDataFlowAnalysis(DataFlowAnalysis? flow, Action reportDiagnostic, Location location) + { + if (flow == null || + flow.Captured.IsEmpty) + { + return; + } + + foreach (var capture in flow.Captured) + { + if (capture.Name != null && capture.Locations != null) + { + foreach (var l in capture.Locations) + { + reportDiagnostic(Diagnostic.Create(ClosureCaptureRule, l, EmptyMessageArgs)); + } + } + } + + reportDiagnostic(Diagnostic.Create(ClosureDriverRule, location, new[] { string.Join(",", flow.Captured.Select(x => x.Name)) })); + } + + private static void GenericMethodCheck(SemanticModel semanticModel, SyntaxNode node, Location location, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (semanticModel.GetSymbolInfo(node, cancellationToken).Symbol is { } symbol) + { + var containingSymbol = symbol.ContainingSymbol; + if (containingSymbol is IMethodSymbol methodSymbol && methodSymbol.Arity > 0) + { + reportDiagnostic(Diagnostic.Create(LambaOrAnonymousMethodInGenericMethodRule, location, EmptyMessageArgs)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/EnumeratorAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/EnumeratorAllocationAnalyzer.cs new file mode 100644 index 0000000000000..b17213b7ae247 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/EnumeratorAllocationAnalyzer.cs @@ -0,0 +1,108 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + using static AnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class EnumeratorAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string ReferenceTypeEnumeratorRuleId = "HAA0401"; + + internal static readonly DiagnosticDescriptor ReferenceTypeEnumeratorRule = new( + ReferenceTypeEnumeratorRuleId, + CreateLocalizableResourceString(nameof(ReferenceTypeEnumeratorRuleTitle)), + CreateLocalizableResourceString(nameof(ReferenceTypeEnumeratorRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(ReferenceTypeEnumeratorRule); + + protected override ImmutableArray Expressions { get; } = ImmutableArray.Create(SyntaxKind.ForEachStatement, SyntaxKind.InvocationExpression); + + private static readonly object[] EmptyMessageArgs = Array.Empty(); + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info) + { + var node = context.Node; + var semanticModel = context.SemanticModel; + Action reportDiagnostic = context.ReportDiagnostic; + var cancellationToken = context.CancellationToken; + if (node is ForEachStatementSyntax foreachExpression) + { + var typeInfo = semanticModel.GetTypeInfo(foreachExpression.Expression, cancellationToken); + if (typeInfo.Type == null) + return; + + if (typeInfo.Type.Name == "String" && typeInfo.Type.ContainingNamespace.Name == "System") + { + // Special case for System.String which is optmizined by + // the compiler and does not result in an allocation. + return; + } + + // Regular way of getting the enumerator + ImmutableArray enumerator = typeInfo.Type.GetMembers("GetEnumerator"); + if ((enumerator == null || enumerator.IsEmpty) && typeInfo.ConvertedType != null) + { + // 1st we try and fallback to using the ConvertedType + enumerator = typeInfo.ConvertedType.GetMembers("GetEnumerator"); + } + + if ((enumerator == null || enumerator.IsEmpty) && typeInfo.Type.Interfaces != null) + { + // 2nd fallback, now we try and find the IEnumerable Interface explicitly + var iEnumerable = typeInfo.Type.Interfaces.Where(i => i.Name == "IEnumerable").ToImmutableArray(); + if (iEnumerable != null && !iEnumerable.IsEmpty) + { + enumerator = iEnumerable[0].GetMembers("GetEnumerator"); + } + } + + if (enumerator != null && !enumerator.IsEmpty) + { + // probably should do something better here, hack. + if (enumerator[0] is IMethodSymbol methodSymbol) + { + if (methodSymbol.ReturnType.IsReferenceType && methodSymbol.ReturnType.SpecialType != SpecialType.System_Collections_IEnumerator) + { + reportDiagnostic(foreachExpression.InKeyword.CreateDiagnostic(ReferenceTypeEnumeratorRule, EmptyMessageArgs)); + } + } + } + + return; + } + + if (node is InvocationExpressionSyntax invocationExpression) + { + var methodInfo = semanticModel.GetSymbolInfo(invocationExpression, cancellationToken).Symbol as IMethodSymbol; + if (methodInfo?.ReturnType != null && methodInfo.ReturnType.IsReferenceType) + { + if (methodInfo.ReturnType.AllInterfaces != null) + { + foreach (var @interface in methodInfo.ReturnType.AllInterfaces) + { + if (@interface.SpecialType is SpecialType.System_Collections_Generic_IEnumerator_T or SpecialType.System_Collections_IEnumerator) + { + reportDiagnostic(invocationExpression.CreateDiagnostic(ReferenceTypeEnumeratorRule, EmptyMessageArgs)); + } + } + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.csproj b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.csproj new file mode 100644 index 0000000000000..8ec1f07ca3b8a --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.csproj @@ -0,0 +1,21 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForPerfSensitiveAnalyzers) + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/TypeConversionAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/TypeConversionAllocationAnalyzer.cs new file mode 100644 index 0000000000000..9592bf4853f7a --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/TypeConversionAllocationAnalyzer.cs @@ -0,0 +1,336 @@ +// 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.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers +{ + using static AnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class TypeConversionAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string ValueTypeToReferenceTypeConversionRuleId = "HAA0601"; + public const string DelegateOnStructInstanceRuleId = "HAA0602"; + public const string MethodGroupAllocationRuleId = "HAA0603"; + public const string ReadonlyMethodGroupAllocationRuleId = "HAA0604"; + + internal static readonly DiagnosticDescriptor ValueTypeToReferenceTypeConversionRule = new( + ValueTypeToReferenceTypeConversionRuleId, + CreateLocalizableResourceString(nameof(ValueTypeToReferenceTypeConversionRuleTitle)), + CreateLocalizableResourceString(nameof(ValueTypeToReferenceTypeConversionRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor DelegateOnStructInstanceRule = new( + DelegateOnStructInstanceRuleId, + CreateLocalizableResourceString(nameof(DelegateOnStructInstanceRuleTitle)), + CreateLocalizableResourceString(nameof(DelegateOnStructInstanceRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor MethodGroupAllocationRule = new( + MethodGroupAllocationRuleId, + CreateLocalizableResourceString(nameof(MethodGroupAllocationRuleTitle)), + CreateLocalizableResourceString(nameof(MethodGroupAllocationRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor ReadonlyMethodGroupAllocationRule = new( + ReadonlyMethodGroupAllocationRuleId, + CreateLocalizableResourceString(nameof(ReadonlyMethodGroupAllocationRuleTitle)), + CreateLocalizableResourceString(nameof(ReadonlyMethodGroupAllocationRuleMessage)), + DiagnosticCategory.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(ValueTypeToReferenceTypeConversionRule, DelegateOnStructInstanceRule, MethodGroupAllocationRule, ReadonlyMethodGroupAllocationRule); + + protected override ImmutableArray Expressions { get; } = ImmutableArray.Create( + SyntaxKind.SimpleAssignmentExpression, + SyntaxKind.ReturnStatement, + SyntaxKind.YieldReturnStatement, + SyntaxKind.CastExpression, + SyntaxKind.AsExpression, + SyntaxKind.CoalesceExpression, + SyntaxKind.ConditionalExpression, + SyntaxKind.ForEachStatement, + SyntaxKind.EqualsValueClause, + SyntaxKind.Argument, + SyntaxKind.ArrowExpressionClause, + SyntaxKind.Interpolation); + + private static readonly object[] EmptyMessageArgs = Array.Empty(); + + protected override void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info) + { + var node = context.Node; + var semanticModel = context.SemanticModel; + var cancellationToken = context.CancellationToken; + Action reportDiagnostic = context.ReportDiagnostic; + bool assignedToReadonlyFieldOrProperty = + context.ContainingSymbol is IFieldSymbol { IsReadOnly: true } or IPropertySymbol { IsReadOnly: true }; + + // this.fooObjCall(10); + // new myobject(10); + if (node is ArgumentSyntax argumentSyntax) + { + ArgumentSyntaxCheck(argumentSyntax, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, cancellationToken); + } + + // object foo { get { return 0; } } + if (node is ReturnStatementSyntax returnStatementSyntax) + { + ReturnStatementExpressionCheck(returnStatementSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + + // yield return 0 + if (node is YieldStatementSyntax yieldStatementSyntax) + { + YieldReturnStatementExpressionCheck(yieldStatementSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + + // object a = x ?? 0; + // var a = 10 as object; + if (node is BinaryExpressionSyntax binaryExpressionSyntax) + { + BinaryExpressionCheck(binaryExpressionSyntax, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, cancellationToken); + return; + } + + // for (object i = 0;;) + if (node is EqualsValueClauseSyntax equalsValueClauseSyntax) + { + EqualsValueClauseCheck(equalsValueClauseSyntax, semanticModel, assignedToReadonlyFieldOrProperty, reportDiagnostic, cancellationToken); + return; + } + + // object = true ? 0 : obj + if (node is ConditionalExpressionSyntax conditionalExpressionSyntax) + { + ConditionalExpressionCheck(conditionalExpressionSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + + // string a = $"{1}"; + if (node is InterpolationSyntax interpolationSyntax) + { + InterpolationCheck(interpolationSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + + // var f = (object) + if (node is CastExpressionSyntax castExpressionSyntax) + { + CastExpressionCheck(castExpressionSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + + // object Foo => 1 + if (node is ArrowExpressionClauseSyntax arrowExpressionClauseSyntax) + { + ArrowExpressionCheck(arrowExpressionClauseSyntax, semanticModel, reportDiagnostic, cancellationToken); + return; + } + } + + private static void ReturnStatementExpressionCheck(ReturnStatementSyntax returnStatementExpression, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (returnStatementExpression.Expression != null) + { + var returnConversionInfo = semanticModel.GetConversion(returnStatementExpression.Expression, cancellationToken); + CheckTypeConversion(returnConversionInfo, reportDiagnostic, returnStatementExpression.Expression.GetLocation()); + } + } + + private static void YieldReturnStatementExpressionCheck(YieldStatementSyntax yieldExpression, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (yieldExpression.Expression != null) + { + var returnConversionInfo = semanticModel.GetConversion(yieldExpression.Expression, cancellationToken); + CheckTypeConversion(returnConversionInfo, reportDiagnostic, yieldExpression.Expression.GetLocation()); + } + } + + private static void ArgumentSyntaxCheck(ArgumentSyntax argument, SemanticModel semanticModel, bool isAssignmentToReadonly, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (argument.Expression != null) + { + var argumentTypeInfo = semanticModel.GetTypeInfo(argument.Expression, cancellationToken); + var argumentConversionInfo = semanticModel.GetConversion(argument.Expression, cancellationToken); + CheckTypeConversion(argumentConversionInfo, reportDiagnostic, argument.Expression.GetLocation()); + CheckDelegateCreation(argument.Expression, argumentTypeInfo, semanticModel, isAssignmentToReadonly, reportDiagnostic, argument.Expression.GetLocation(), cancellationToken); + } + } + + private static void BinaryExpressionCheck(BinaryExpressionSyntax binaryExpression, SemanticModel semanticModel, bool isAssignmentToReadonly, Action reportDiagnostic, CancellationToken cancellationToken) + { + // as expression + if (binaryExpression.IsKind(SyntaxKind.AsExpression) && binaryExpression.Left != null && binaryExpression.Right != null) + { + var leftT = semanticModel.GetTypeInfo(binaryExpression.Left, cancellationToken); + var rightT = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); + + if (leftT.Type?.IsValueType == true && rightT.Type?.IsReferenceType == true) + { + reportDiagnostic(binaryExpression.Left.CreateDiagnostic(ValueTypeToReferenceTypeConversionRule, EmptyMessageArgs)); + } + + return; + } + + if (binaryExpression.Right != null) + { + var assignmentExprTypeInfo = semanticModel.GetTypeInfo(binaryExpression.Right, cancellationToken); + var assignmentExprConversionInfo = semanticModel.GetConversion(binaryExpression.Right, cancellationToken); + CheckTypeConversion(assignmentExprConversionInfo, reportDiagnostic, binaryExpression.Right.GetLocation()); + CheckDelegateCreation(binaryExpression.Right, assignmentExprTypeInfo, semanticModel, isAssignmentToReadonly, reportDiagnostic, binaryExpression.Right.GetLocation(), cancellationToken); + return; + } + } + + private static void InterpolationCheck(InterpolationSyntax interpolation, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + var typeInfo = semanticModel.GetTypeInfo(interpolation.Expression, cancellationToken); + if (typeInfo.Type?.IsValueType == true) + { + reportDiagnostic(interpolation.Expression.CreateDiagnostic(ValueTypeToReferenceTypeConversionRule, EmptyMessageArgs)); + } + } + + private static void CastExpressionCheck(CastExpressionSyntax castExpression, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (castExpression.Expression != null) + { + var castTypeInfo = semanticModel.GetTypeInfo(castExpression, cancellationToken); + var expressionTypeInfo = semanticModel.GetTypeInfo(castExpression.Expression, cancellationToken); + + if (castTypeInfo.Type?.IsReferenceType == true && expressionTypeInfo.Type?.IsValueType == true) + { + reportDiagnostic(castExpression.Expression.CreateDiagnostic(ValueTypeToReferenceTypeConversionRule, EmptyMessageArgs)); + } + } + } + + private static void ConditionalExpressionCheck(ConditionalExpressionSyntax conditionalExpression, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + var trueExp = conditionalExpression.WhenTrue; + var falseExp = conditionalExpression.WhenFalse; + + if (trueExp != null) + { + CheckTypeConversion(semanticModel.GetConversion(trueExp, cancellationToken), reportDiagnostic, trueExp.GetLocation()); + } + + if (falseExp != null) + { + CheckTypeConversion(semanticModel.GetConversion(falseExp, cancellationToken), reportDiagnostic, falseExp.GetLocation()); + } + } + + private static void EqualsValueClauseCheck(EqualsValueClauseSyntax initializer, SemanticModel semanticModel, bool isAssignmentToReadonly, Action reportDiagnostic, CancellationToken cancellationToken) + { + if (initializer.Value != null) + { + var typeInfo = semanticModel.GetTypeInfo(initializer.Value, cancellationToken); + var conversionInfo = semanticModel.GetConversion(initializer.Value, cancellationToken); + CheckTypeConversion(conversionInfo, reportDiagnostic, initializer.Value.GetLocation()); + CheckDelegateCreation(initializer.Value, typeInfo, semanticModel, isAssignmentToReadonly, reportDiagnostic, initializer.Value.GetLocation(), cancellationToken); + } + } + + private static void ArrowExpressionCheck(ArrowExpressionClauseSyntax syntax, SemanticModel semanticModel, Action reportDiagnostic, CancellationToken cancellationToken) + { + var typeInfo = semanticModel.GetTypeInfo(syntax.Expression, cancellationToken); + var conversionInfo = semanticModel.GetConversion(syntax.Expression, cancellationToken); + CheckTypeConversion(conversionInfo, reportDiagnostic, syntax.Expression.GetLocation()); + CheckDelegateCreation(syntax, typeInfo, semanticModel, false, reportDiagnostic, + syntax.Expression.GetLocation(), cancellationToken); + } + + private static void CheckTypeConversion(Conversion conversionInfo, Action reportDiagnostic, Location location) + { + if (conversionInfo.IsBoxing) + { + reportDiagnostic(Diagnostic.Create(ValueTypeToReferenceTypeConversionRule, location, EmptyMessageArgs)); + } + } + + private static void CheckDelegateCreation(SyntaxNode node, TypeInfo typeInfo, SemanticModel semanticModel, bool isAssignmentToReadonly, Action reportDiagnostic, Location location, CancellationToken cancellationToken) + { + // special case: method groups + if (typeInfo.ConvertedType?.TypeKind == TypeKind.Delegate) + { + // new Action(MethodGroup); should skip this one + var insideObjectCreation = node?.Parent?.Parent?.Parent?.Kind() == SyntaxKind.ObjectCreationExpression; + if (node is ParenthesizedLambdaExpressionSyntax || node is SimpleLambdaExpressionSyntax || + node is AnonymousMethodExpressionSyntax || node is ObjectCreationExpressionSyntax || + insideObjectCreation) + { + // skip this, because it's intended. + } + else + { + if (node.IsKind(SyntaxKind.IdentifierName)) + { + if (semanticModel.GetSymbolInfo(node, cancellationToken).Symbol is IMethodSymbol) + { + reportDiagnostic(Diagnostic.Create(MethodGroupAllocationRule, location, EmptyMessageArgs)); + } + } + else if (node.IsKind(SyntaxKind.SimpleMemberAccessExpression)) + { + var memberAccess = (MemberAccessExpressionSyntax)node!; + if (semanticModel.GetSymbolInfo(memberAccess.Name, cancellationToken).Symbol is IMethodSymbol) + { + if (isAssignmentToReadonly) + { + reportDiagnostic(Diagnostic.Create(ReadonlyMethodGroupAllocationRule, location, EmptyMessageArgs)); + } + else + { + reportDiagnostic(Diagnostic.Create(MethodGroupAllocationRule, location, EmptyMessageArgs)); + } + } + } + else if (node is ArrowExpressionClauseSyntax arrowClause) + { + if (semanticModel.GetSymbolInfo(arrowClause.Expression, cancellationToken).Symbol is IMethodSymbol) + { + reportDiagnostic(Diagnostic.Create(MethodGroupAllocationRule, location, EmptyMessageArgs)); + } + } + } + + if (IsStructInstanceMethod(node!, semanticModel, cancellationToken)) + { + reportDiagnostic(Diagnostic.Create(DelegateOnStructInstanceRule, location, EmptyMessageArgs)); + } + } + } + + private static bool IsStructInstanceMethod(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) + { + if (node.IsKind(SyntaxKind.AnonymousMethodExpression) || node.IsKind(SyntaxKind.ParenthesizedLambdaExpression) || node.IsKind(SyntaxKind.SimpleLambdaExpression)) + { + return false; + } + + var symbolInfo = semanticModel.GetSymbolInfo(node, cancellationToken).Symbol; + return symbolInfo?.Kind == SymbolKind.Method && !symbolInfo.IsStatic && symbolInfo.ContainingType?.IsValueType == true; + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.cs.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.cs.xlf new file mode 100644 index 0000000000000..0e52e79f0bcb6 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.cs.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Kompilátor vygeneruje třídu, která toto bude uchovávat jako pole, aby se toto uzavření dalo zachytit. + + + + Display class allocation to capture closure + Zobrazit přidělení třídy, aby se zachytilo uzavření + + + + Heap allocation of closure Captures: {0} + Přidělení haldy zachycení uzavření: {0} + + + + Closure Allocation Source + Zdroj přidělení uzavření + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Instanční metoda struct, pomocí které se deleguje vytváření. Toto způsobí instrukci zabalení. + + + + Delegate on struct instance caused a boxing allocation + Delegát v instanci struktury způsobil přidělení zabalení + + + + Considering moving this out of the generic method + Zvažte možnost přesunout to mimo obecnou metodu. + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Lambda nebo anonymní metoda v obecné metodě přidělují instanci delegáta + + + + This will allocate a delegate instance + Tato operace přidělí instanci delegáta. + + + + Delegate allocation from a method group + Přidělení delegáta ze skupiny metod + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Tato lokalita volání volá funkci s parametrem params. Výsledkem je přidělení pole. + + + + Array allocation for params parameter + Přidělení pole pro parametr params + + + + This will allocate a delegate instance + Tato operace přidělí instanci delegáta. + + + + Delegate allocation from a method group + Přidělení delegáta ze skupiny metod + + + + Non-ValueType enumerator may result in a heap allocation + Enumerátor, který není ValueType, může způsobit přidělení haldy. + + + + Possible allocation of reference type enumerator + Možné přidělení enumerátoru typů odkazů + + + + Considering using StringBuilder + Zvažte možnost použít StringBuilder. + + + + Implicit string concatenation allocation + Implicitní přidělení zřetězení řetězců + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Volání nepřepsané virtuální metody pro typ hodnoty přidává instrukci zabalení nebo omezenou instrukci. + + + + Non-overridden virtual method call on value type + Volání nepřepsané virtuální metody pro typ hodnoty + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + Typ hodnoty pro převod typu odkazu způsobuje zabalení v lokalitě volání (tady) a rozbalení v lokalitě volajícího. Zvažte možnost použít obecné typy, pokud je to možné. + + + + Value type to reference type conversion causing boxing allocation + Typ hodnoty pro převod typů odkazů způsobuje přidělení zabalení + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Typ hodnoty ({0}) se balí do typu odkazu pro zřetězení řetězců. + + + + Value type to reference type conversion allocation for string concatenation + Přidělení typu hodnoty pro převod typů odkazů pro řetězení řetězců + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.de.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.de.xlf new file mode 100644 index 0000000000000..431f70d3dd352 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.de.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Der Compiler gibt eine Klasse aus, in der diese Daten als Feld gespeichert werden, um eine Erfassung dieses Abschlusses zuzulassen. + + + + Display class allocation to capture closure + Klassenzuordnung zu Abschlusserfassung anzeigen + + + + Heap allocation of closure Captures: {0} + Heapzuordnung von Abschlusserfassungen: {0} + + + + Closure Allocation Source + Quelle für Abschlusszuordnung + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Strukturinstanzmethode wird zur Delegaterstellung verwendet. Dies führt zu einer Boxinganweisung. + + + + Delegate on struct instance caused a boxing allocation + Boxingzuordnung verursacht durch Delegat in Strukturinstanz + + + + Considering moving this out of the generic method + Es wird erwogen, dieses Element aus der generischen Methode zu verschieben. + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Lambdafunktion oder anonyme Methode in generischer Methode ordnet eine Delegatinstanz zu + + + + This will allocate a delegate instance + Hiermit wird eine Delegatinstanz zugeordnet. + + + + Delegate allocation from a method group + Delegatzuordnung aus einer Methodengruppe + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Diese Aufrufsite ruft eine Funktion mit einem params-Parameter auf. Dies führt zu einer Arrayzuordnung. + + + + Array allocation for params parameter + Arrayzuordnung für params-Parameter + + + + This will allocate a delegate instance + Hiermit wird eine Delegatinstanz zugeordnet. + + + + Delegate allocation from a method group + Delegatzuordnung aus einer Methodengruppe + + + + Non-ValueType enumerator may result in a heap allocation + Nicht-ValueType-Enumerator kann zu einer Heapzuweisung führen. + + + + Possible allocation of reference type enumerator + Mögliche Zuordnung von Verweistypenumerator + + + + Considering using StringBuilder + Die Verwendung von StringBuilder wird in Betracht gezogen. + + + + Implicit string concatenation allocation + Implizite Zuordnung von Zeichenfolgenverkettungen + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Durch einen nicht außer Kraft gesetzten Aufruf einer virtuellen Methode für einen Werttyp wird eine Boxing- oder eingeschränkte Anweisung hinzugefügt. + + + + Non-overridden virtual method call on value type + Nicht außer Kraft gesetzter Aufruf einer virtuellen Methode für Werttyp + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + Der Werttyp für die Verweistypkonvertierung verursacht Boxing in der Aufrufsite (hier) und Unboxing in der Site der aufgerufenen Funktion. Verwenden Sie ggf. Generics. + + + + Value type to reference type conversion causing boxing allocation + Werttyp für Verweistypkonvertierung verursacht Boxingzuordnung + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Für den Werttyp ({0}) wird ein Boxing in einen Verweistyp für eine Zeichenfolgenverkettung durchgeführt. + + + + Value type to reference type conversion allocation for string concatenation + Werttyp für Zuordnung der Verweistypkonvertierung für Zeichenfolgenverkettung + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.es.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.es.xlf new file mode 100644 index 0000000000000..5169d83b3f078 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.es.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + El compilador emitirá una clase que lo contiene como campo para permitir la captura de esta clausura. + + + + Display class allocation to capture closure + Mostrar la asignación de clase para capturar la clausura + + + + Heap allocation of closure Captures: {0} + Asignación del montón de las capturas de clausura: {0} + + + + Closure Allocation Source + Origen de la asignación de clausura + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Método de instancia de struct que se usa para la creación de delegado, lo que dará como resultado una instrucción de conversión boxing. + + + + Delegate on struct instance caused a boxing allocation + El delegado en la instancia de struct causó una asignación de conversión boxing + + + + Considering moving this out of the generic method + Considere la posibilidad de extraerlo del método genérico + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Una expresión lambda o un método anónimo en un método genérico asigna una instancia de delegado + + + + This will allocate a delegate instance + Asignará una instancia de delegado + + + + Delegate allocation from a method group + Asignación de delegado desde un grupo de métodos + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Este sitio de llamada invoca a una función con un parámetro "params", lo que da lugar a una asignación de matriz. + + + + Array allocation for params parameter + Asignación de matriz para el parámetro params + + + + This will allocate a delegate instance + Asignará una instancia de delegado + + + + Delegate allocation from a method group + Asignación de delegado desde un grupo de métodos + + + + Non-ValueType enumerator may result in a heap allocation + Un enumerador distinto de ValueType puede producir una asignación del montón + + + + Possible allocation of reference type enumerator + Posible asignación del enumerador de tipo de referencia + + + + Considering using StringBuilder + Considere la posibilidad de usar StringBuilder + + + + Implicit string concatenation allocation + Asignación implícita de concatenación de cadenas + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + La llamada de método virtual no reemplazado en un tipo de valor agrega una instrucción de conversión boxing o restringida. + + + + Non-overridden virtual method call on value type + Llamada de método virtual no reemplazado en el tipo de valor + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + La conversión del tipo de valor al tipo de referencia genera una conversión boxing en el sitio de llamada (aquí) y otra de tipo unboxing en el sitio del destinatario. Considere la posibilidad de usar valores genéricos, si procede. + + + + Value type to reference type conversion causing boxing allocation + La conversión del tipo de valor al tipo de referencia causa una asignación de conversión boxing + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Se está aplicando una conversión boxing del tipo de valor ({0}) a un tipo de referencia para una concatenación de cadenas. + + + + Value type to reference type conversion allocation for string concatenation + Asignación de conversión de tipo de valor a tipo de referencia para la concatenación de cadenas + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.fr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.fr.xlf new file mode 100644 index 0000000000000..259dda9bb482d --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.fr.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Le compilateur va émettre une classe qui conservera ceci en tant que champ pour permettre la capture de cette fermeture + + + + Display class allocation to capture closure + Afficher l'allocation de classe pour capturer la fermeture + + + + Heap allocation of closure Captures: {0} + Allocation de tas des captures de fermeture : {0} + + + + Closure Allocation Source + Source d'allocation de fermeture + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Dans la mesure où la méthode d'instance de struct est utilisée pour la création de délégué, cela va donner lieu à une instruction boxing + + + + Delegate on struct instance caused a boxing allocation + Le délégué sur l'instance de struct a entraîné une allocation boxing + + + + Considering moving this out of the generic method + Envisager de sortir ceci de la méthode générique + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Une expression lambda ou une méthode anonyme dans une méthode générique alloue une instance de délégué + + + + This will allocate a delegate instance + Ceci va allouer une instance de délégué + + + + Delegate allocation from a method group + Allocation de délégué à partir d'un groupe de méthodes + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Ce site d'appel appelle une fonction avec un paramètre 'params'. Cela se traduit par une allocation de tableau. + + + + Array allocation for params parameter + Allocation de tableau pour le paramètre params + + + + This will allocate a delegate instance + Ceci va allouer une instance de délégué + + + + Delegate allocation from a method group + Allocation de délégué à partir d'un groupe de méthodes + + + + Non-ValueType enumerator may result in a heap allocation + Un énumérateur non ValueType peut entraîner une allocation de tas + + + + Possible allocation of reference type enumerator + Allocation possible d'un énumérateur de type référence + + + + Considering using StringBuilder + Utilisation envisagée de StringBuilder + + + + Implicit string concatenation allocation + Allocation implicite de concaténation de chaînes + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Un appel de méthode virtuelle non substituée sur un type valeur ajoute une instruction boxing ou contrainte + + + + Non-overridden virtual method call on value type + Appel de méthode virtuelle non substituée sur le type valeur + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + La conversion d'un type valeur en type référence entraîne un boxing sur le site d'appel (ici), et une conversion unboxing sur le site appelé. Utiliser des génériques, le cas échéant. + + + + Value type to reference type conversion causing boxing allocation + La conversion d'un type valeur en type référence entraîne une allocation boxing + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Le type valeur ({0}) est converti (boxed) en type référence pour une concaténation de chaîne + + + + Value type to reference type conversion allocation for string concatenation + Allocation de conversion de type valeur en type référence pour la concaténation de chaînes + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.it.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.it.xlf new file mode 100644 index 0000000000000..aac609610b3d2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.it.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Il compilatore creerà una classe che conterrà questo valore come campo per consentire l'acquisizione di questa chiusura + + + + Display class allocation to capture closure + Visualizza allocazione della classe per l'acquisizione della chiusura + + + + Heap allocation of closure Captures: {0} + Allocazione heap di acquisizioni chiusura: {0} + + + + Closure Allocation Source + Origine allocazione chiusura + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Per la creazione di delegati viene usato il metodo di istanza struct. Verrà generata un'istruzione di conversione boxing + + + + Delegate on struct instance caused a boxing allocation + Il delegato sull'istanza struct ha causato un'allocazione di conversione boxing + + + + Considering moving this out of the generic method + Verrà spostato all'esterno del metodo generico + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Il metodo anonimo o l'espressione lambda in un metodo generico implica l'allocazione di un'istanza di delegato + + + + This will allocate a delegate instance + Verrà allocata un'istanza di delegato + + + + Delegate allocation from a method group + Allocazione del delegato da un gruppo di metodi + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Questo sito di chiamata chiama una funzione con un parametro 'params', di conseguenza viene generata un'allocazione di matrice. + + + + Array allocation for params parameter + Allocazione di matrice per parametro params + + + + This will allocate a delegate instance + Verrà allocata un'istanza di delegato + + + + Delegate allocation from a method group + Allocazione del delegato da un gruppo di metodi + + + + Non-ValueType enumerator may result in a heap allocation + L'enumeratore non ValueType può causare un'allocazione heap + + + + Possible allocation of reference type enumerator + Possibile allocazione di enumeratore di tipo riferimento + + + + Considering using StringBuilder + Verrà usato StringBuilder + + + + Implicit string concatenation allocation + Allocazione implicita di concatenazioni di stringhe + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + La chiamata a un metodo virtuale non sottoposto a override su un tipo valore implica l'aggiunta di un'istruzione di conversione boxing o vincolata + + + + Non-overridden virtual method call on value type + Chiamata a un metodo virtuale non sottoposta a override sul tipo valore + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + La conversione da tipo valore a tipo riferimento causa la conversione boxing nel sito di chiamata (qui) e la conversione unboxing nel sito chiamato. Se applicabile, provare a usare generics. + + + + Value type to reference type conversion causing boxing allocation + Conversione da tipo valore a tipo riferimento che causa l'allocazione della conversione boxing + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Verrà eseguita la conversione boxing del tipo valore ({0}) in un tipo riferimento per una concatenazione di stringhe + + + + Value type to reference type conversion allocation for string concatenation + Allocazione della conversione da tipo valore a tipo riferimento per la concatenazione di stringhe + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ja.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ja.xlf new file mode 100644 index 0000000000000..6920b2357a880 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ja.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + コンパイラは、このクロージャのキャプチャを可能にするフィールドとしてこれを保持するクラスを生成します + + + + Display class allocation to capture closure + クラスの割り当てを表示してクロージャをキャプチャする + + + + Heap allocation of closure Captures: {0} + クロージャのヒープ割り当ては次をキャプチャします: {0} + + + + Closure Allocation Source + クロージャ割り当てソース + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + デリゲートの作成に使用されている構造体インスタンス メソッドで、これはボックス化命令になります + + + + Delegate on struct instance caused a boxing allocation + 構造体インスタンスでのデリゲートが、ボックス化の割り当てを引き起こしました + + + + Considering moving this out of the generic method + これをジェネリック メソッドから移行することを検討します + + + + Lambda or anonymous method in a generic method allocates a delegate instance + ジェネリック メソッド内のラムダ式または匿名メソッドは、デリゲート インスタンスを割り当てます + + + + This will allocate a delegate instance + これにより、デリゲート インスタンスが割り当てられます + + + + Delegate allocation from a method group + メソッド グループからのデリゲート割り当て + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + この呼び出しサイトは 'params' パラメーターを持つ関数を呼び出しています。結果として、配列が割り当てられます。 + + + + Array allocation for params parameter + params パラメーターの配列の割り当て + + + + This will allocate a delegate instance + これにより、デリゲート インスタンスが割り当てられます + + + + Delegate allocation from a method group + メソッド グループからのデリゲート割り当て + + + + Non-ValueType enumerator may result in a heap allocation + 非 ValueType 列挙子は、ヒープの割り当てを発生させる可能性があります + + + + Possible allocation of reference type enumerator + 参照型列挙子の可能な割り当て + + + + Considering using StringBuilder + StringBuilder の使用を検討します + + + + Implicit string concatenation allocation + 暗黙的な文字列連結の割り当て + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + 値の型へのオーバーロードされていない仮想メソッド呼び出しにより、ボックス化または制限付きの命令が追加されます + + + + Non-overridden virtual method call on value type + 値の型へのオーバーライドされていない仮想メソッド呼び出し + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + 値の型から参照型への変換は、呼び出しサイト (ここ) でのボックス化と、呼び出し先サイトでのボックス化解除につながります。該当する場合は、ジェネリックの使用を検討してください。 + + + + Value type to reference type conversion causing boxing allocation + ボックス化割り当ての原因となる、値の型から参照型への変換 + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + 値の型 ({0}) は、文字列連結のための参照型にボックス化されています + + + + Value type to reference type conversion allocation for string concatenation + 文字列連結のための、値の型から参照型への変換割り当て + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ko.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ko.xlf new file mode 100644 index 0000000000000..f057d32060631 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ko.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + 컴파일러는 이 Closure의 캡처를 허용하도록 이 항목을 필드로 포함할 클래스를 내보냅니다. + + + + Display class allocation to capture closure + Closure를 캡처하기 위한 클래스 할당 표시 + + + + Heap allocation of closure Captures: {0} + Closure 캡처의 힙 할당: {0} + + + + Closure Allocation Source + Closure 할당 소스 + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + 대리자 생성에 사용되는 구조체 인스턴스 메서드, 이로 인해 boxing 지침이 발생함 + + + + Delegate on struct instance caused a boxing allocation + 구조체 인스턴스의 대리자로 인해 Boxing 할당이 발생함 + + + + Considering moving this out of the generic method + 제네릭 메서드 외부로 이동 고려 + + + + Lambda or anonymous method in a generic method allocates a delegate instance + 제네릭 메서드의 람다 또는 무명 메서드는 대리자 인스턴스를 할당합니다. + + + + This will allocate a delegate instance + 이렇게 하면 대리자 인스턴스가 할당됩니다. + + + + Delegate allocation from a method group + 메서드 그룹의 대리자 할당 + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + 이 호출 사이트가 'params' 매개 변수가 있는 함수를 호출하고 있습니다. 이 작업으로 인해 배열 할당이 발생합니다. + + + + Array allocation for params parameter + params 매개 변수의 배열 할당 + + + + This will allocate a delegate instance + 이렇게 하면 대리자 인스턴스가 할당됩니다. + + + + Delegate allocation from a method group + 메서드 그룹의 대리자 할당 + + + + Non-ValueType enumerator may result in a heap allocation + 비 ValueType 열거자를 사용하면 힙 할당이 발생할 수 있습니다. + + + + Possible allocation of reference type enumerator + 참조 형식 열거자의 가능한 할당 + + + + Considering using StringBuilder + StringBuilder 사용 고려 + + + + Implicit string concatenation allocation + 암시적 문자열 연결 할당 + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + 값 형식에 대한 재정의되지 않은 가상 메서드 호출은 boxing 또는 제한된 지침을 추가합니다. + + + + Non-overridden virtual method call on value type + 값 형식에 대한 재정의되지 않은 가상 메서드 호출 + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + 값 형식에서 참조 형식으로 변환하면 호출 사이트(여기)에서 boxing되고 호출 수신자 사이트에서 unboxing됩니다. 해당하는 경우 제네릭을 사용하는 것이 좋습니다. + + + + Value type to reference type conversion causing boxing allocation + boxing 할당을 초래하는 값 형식에서 참조 형식으로 변환 + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + 값 형식({0})이 문자열 연결을 위해 참조 형식으로 boxing됩니다. + + + + Value type to reference type conversion allocation for string concatenation + 문자열 연결을 위한 값 형식에서 참조 형식으로 변환 할당 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pl.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pl.xlf new file mode 100644 index 0000000000000..ff29c779ca0d4 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pl.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Kompilator będzie emitował klasę, która będzie przechowywała ten element jako pole, aby zezwolić na przechwytywanie tego zamknięcia + + + + Display class allocation to capture closure + Wyświetl alokację klasy do przechwytywania zamknięcia + + + + Heap allocation of closure Captures: {0} + Alokacja sterty przechwyceń zamknięcia: {0} + + + + Closure Allocation Source + Źródło alokacji zamknięcia + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Metoda wystąpienia struktury jest używana do utworzenia delegata, co spowoduje instrukcję konwersji boxing + + + + Delegate on struct instance caused a boxing allocation + Delegat w wystąpieniu struktury spowodował alokację z konwersją boxing + + + + Considering moving this out of the generic method + Rozważanie przeniesienia tego poza metodę ogólną + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Wyrażenie lambda lub metoda anonimowa w metodzie ogólnej alokuje wystąpienie delegata + + + + This will allocate a delegate instance + Spowoduje to alokowanie wystąpienia delegata + + + + Delegate allocation from a method group + Alokacja delegata z grupy metod + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + To miejsce wywołania wywołuje funkcję z parametrem „params”. Powoduje to alokację tablicy. + + + + Array allocation for params parameter + Alokacja tablicy dla parametru params + + + + This will allocate a delegate instance + Spowoduje to alokowanie wystąpienia delegata + + + + Delegate allocation from a method group + Alokacja delegata z grupy metod + + + + Non-ValueType enumerator may result in a heap allocation + Moduł wyliczający inny niż ValueType może skutkować alokacją sterty + + + + Possible allocation of reference type enumerator + Możliwa alokacja modułu wyliczającego typ referencyjny + + + + Considering using StringBuilder + Rozważanie używania klasy StringBuilder + + + + Implicit string concatenation allocation + Niejawna alokacja łączenia ciągów + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Nieprzesłonięte wywołanie metody wirtualnej dla typu wartości dodaje konwersję boxing instrukcji z ograniczeniami + + + + Non-overridden virtual method call on value type + Nieprzesłonięte wywołanie metody wirtualnej dla typu wartości + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + Konwersja typu wartości na typ referencyjny powoduje konwersję boxing w lokacji wywołania (tutaj) i w konwersję unboxing w lokacji wywoływanej. Rozważ użycie typów ogólnych, jeśli mają zastosowanie. + + + + Value type to reference type conversion causing boxing allocation + Konwersja typu wartości na typ referencyjny powoduje alokację z konwersją boxing + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Typ wartości ({0}) jest poddawany konwersji boxing do typu referencyjnego na potrzeby łączenia ciągów + + + + Value type to reference type conversion allocation for string concatenation + Alokacja konwersji typu wartości na typ referencyjny dla łączenia ciągów + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pt-BR.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pt-BR.xlf new file mode 100644 index 0000000000000..e403a01a75737 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.pt-BR.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + O compilador emitirá uma classe que manterá isso como um campo para permitir a captura desse fechamento + + + + Display class allocation to capture closure + Exibir a alocação de classe para capturar o fechamento + + + + Heap allocation of closure Captures: {0} + Alocação de heap de Capturas de fechamento: {0} + + + + Closure Allocation Source + Origem da Alocação de Fechamento + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Um método de instância de struct está sendo usado para a criação de delegado. Isso resultará em uma instrução de conversão boxing + + + + Delegate on struct instance caused a boxing allocation + O delegado na instância do struct causou uma alocação de conversão boxing + + + + Considering moving this out of the generic method + Considerar a retirada disso do método genérico + + + + Lambda or anonymous method in a generic method allocates a delegate instance + O método lambda ou anônimo em um método genérico aloca uma instância delegada + + + + This will allocate a delegate instance + Essa opção alocará uma instância delegada + + + + Delegate allocation from a method group + Alocação de delegado de um grupo de métodos + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Este site de chamada está chamando uma função com um parâmetro 'params'. Isso resulta em uma alocação de matriz. + + + + Array allocation for params parameter + Alocação de matriz para o parâmetro params + + + + This will allocate a delegate instance + Essa opção alocará uma instância delegada + + + + Delegate allocation from a method group + Alocação de delegado de um grupo de métodos + + + + Non-ValueType enumerator may result in a heap allocation + Um enumerador que não seja ValueType poderá resultar em uma alocação de heap + + + + Possible allocation of reference type enumerator + Alocação possível do enumerador de tipo de referência + + + + Considering using StringBuilder + Considerar o uso de StringBuilder + + + + Implicit string concatenation allocation + Alocação implícita de concatenação de cadeia de caracteres + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + A chamada de método virtual não substituído em um tipo de valor adiciona uma instrução restrita ou de conversão boxing + + + + Non-overridden virtual method call on value type + Chamada de método virtual não substituído no tipo de valor + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + O tipo de valor para a conversão de tipo de referência causa a conversão boxing no site de chamada (aqui) e a conversão unboxing no site do receptor. Considere o uso de genéricos, caso seja aplicável. + + + + Value type to reference type conversion causing boxing allocation + Tipo de valor para a conversão de tipo de referência causando uma alocação de conversão boxing + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + O tipo de valor ({0}) está passando por uma conversão boxing, tornando-se um tipo de referência de uma concatenação de cadeia de caracteres + + + + Value type to reference type conversion allocation for string concatenation + Tipo de valor para a alocação de conversão de tipo de referência de uma concatenação de cadeia de caracteres + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ru.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ru.xlf new file mode 100644 index 0000000000000..a7fa8763702b1 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.ru.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Компилятор будет выводить класс, содержащий этот объект в качестве поля, чтобы разрешить захват такого замыкания + + + + Display class allocation to capture closure + Отображение выделения класса для захвата замыкания + + + + Heap allocation of closure Captures: {0} + Выделение кучи для захватов замыкания: {0} + + + + Closure Allocation Source + Источник выделения замыкания + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Метод экземпляра структуры используется для создания делегата, это приведет к инструкции упаковки-преобразования + + + + Delegate on struct instance caused a boxing allocation + Делегат в экземпляре структуры вызвал выделение упаковки-преобразования + + + + Considering moving this out of the generic method + Учитывая его перемещение из универсального метода + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Лямбда-выражение или анонимный метод в универсальном методе выделяет экземпляр делегата + + + + This will allocate a delegate instance + Эта операция выделит экземпляр делегата + + + + Delegate allocation from a method group + Выделение делегата из группы методов + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Это место вызова направляет в функцию вызов с параметром "params". Это приводит к выделению массива. + + + + Array allocation for params parameter + Выделение массива для параметра params + + + + This will allocate a delegate instance + Эта операция выделит экземпляр делегата + + + + Delegate allocation from a method group + Выделение делегата из группы методов + + + + Non-ValueType enumerator may result in a heap allocation + Перечислитель, отличный от ValueType, может привести к выделению кучи + + + + Possible allocation of reference type enumerator + Возможное выделение перечислителя ссылочного типа + + + + Considering using StringBuilder + Учитывая использование StringBuilder + + + + Implicit string concatenation allocation + Неявное выделение объединения строк + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Непереопределенный вызов виртуального метода для типа значения добавляет инструкцию упаковки-преобразования или ограниченную инструкцию + + + + Non-overridden virtual method call on value type + Непереопределенный вызов виртуального метода для типа значения + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + Преобразование типа значения в ссылочный тип приводит к упаковке-преобразованию в месте вызова (здесь) и распаковке в месте вызываемого объекта. Рекомендуется по возможности использовать универсальные шаблоны. + + + + Value type to reference type conversion causing boxing allocation + Преобразование типа значения в ссылочный тип приводит к выделению упаковки-преобразования + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + Тип значения ({0}) подвергается упаковке-преобразованию в ссылочный тип для объединения строк. + + + + Value type to reference type conversion allocation for string concatenation + Выделение преобразования типа значения в ссылочный тип для объединения строк + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.tr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.tr.xlf new file mode 100644 index 0000000000000..290194c84118e --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.tr.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + Derleyici, bu kapanışın yakalanmasını sağlamak için bunu bir alan olarak tutacak bir sınıf üretir + + + + Display class allocation to capture closure + Kapanış yakalamak için sınıf ayırmayı görüntüle + + + + Heap allocation of closure Captures: {0} + Kapanış Yakalamalarının yığın ayırması: {0} + + + + Closure Allocation Source + Kapanış Ayırma Kaynağı + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + Yapı örneği metodu temsilci oluşturmak için kullanılıyor. Bu durum kutulama yönergesine neden olur + + + + Delegate on struct instance caused a boxing allocation + Yapı örneğindeki temsilci kutulama ayırmasına neden oldu + + + + Considering moving this out of the generic method + Bunun genel metodun dışına taşınması düşünülüyor + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Genel bir metottaki lambda veya anonim metot bir temsilci örneği ayırır + + + + This will allocate a delegate instance + Bu bir temsilci örneğini ayırır + + + + Delegate allocation from a method group + Bir metot grubundan temsilci ayırma + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + Bu çağrı sitesi 'params' parametresi ile bir işleve çağrı yapıyor. Bu durum bir dizi ayırmasına neden olur. + + + + Array allocation for params parameter + params parametresi için dizi ayırma + + + + This will allocate a delegate instance + Bu bir temsilci örneğini ayırır + + + + Delegate allocation from a method group + Bir metot grubundan temsilci ayırma + + + + Non-ValueType enumerator may result in a heap allocation + ValueType olmayan numaralandırıcı bir yığın ayırmaya neden olabilir + + + + Possible allocation of reference type enumerator + Başvuru türü numaralandırıcısının olası ayırması + + + + Considering using StringBuilder + StringBuilder kullanılması düşünülüyor + + + + Implicit string concatenation allocation + Örtük dize birleştirmesini ayırma + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + Bir değer türü üzerindeki geçersiz kılınmayan sanal metot çağrısı bir kutulama veya kısıtlanmış yönerge ekler + + + + Non-overridden virtual method call on value type + Değer türü üzerindeki geçersiz kılınmayan sanal metot çağrısı + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + Değer türünden başvuru türüne dönüştürme, çağrı sitesinde (burada) kutulamaya ve çağrılan sitede ise kutudan çıkarmaya neden olur. Varsa genel türleri kullanmayı deneyin. + + + + Value type to reference type conversion causing boxing allocation + Değer türünden başvuru türüne dönüştürme, kutulama ayırmasına neden oluyor + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + {0} değer türü, dize birleştirmesi için bir başvuru türüne kutulanıyor + + + + Value type to reference type conversion allocation for string concatenation + Dize birleştirmesi için değer türünden başvuru türüne dönüştürme ayırması + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf new file mode 100644 index 0000000000000..220c4bd104864 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hans.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + 编译器将发出将此内容作为字段保存的类,以允许捕获此闭包 + + + + Display class allocation to capture closure + 显示类分配以捕获闭包 + + + + Heap allocation of closure Captures: {0} + 闭包捕获的堆分配: {0} + + + + Closure Allocation Source + 闭包分配源 + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + 结构实例方法正用于委托创建,这将导致装箱指令 + + + + Delegate on struct instance caused a boxing allocation + 导致装箱分配的结构实例上的委托 + + + + Considering moving this out of the generic method + 考虑将此项移出泛型方法范围 + + + + Lambda or anonymous method in a generic method allocates a delegate instance + Lambda 或泛型方法中的匿名方法会分配委托实例 + + + + This will allocate a delegate instance + 这将会分配一个委托实例 + + + + Delegate allocation from a method group + 从方法组委托分配 + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + 此调用站点正在调用带有 "params" 参数的函数。这会导致数组分配。 + + + + Array allocation for params parameter + Params 参数的数组分配 + + + + This will allocate a delegate instance + 这将会分配一个委托实例 + + + + Delegate allocation from a method group + 从方法组委托分配 + + + + Non-ValueType enumerator may result in a heap allocation + 非 ValueType 枚举器可能会导致堆分配 + + + + Possible allocation of reference type enumerator + 引用类型枚举器的可能分配 + + + + Considering using StringBuilder + 考虑使用 StringBuilder + + + + Implicit string concatenation allocation + 隐式字符串串联分配 + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + 值类型上的非替代虚拟方法调用会添加装箱或受约束的指令 + + + + Non-overridden virtual method call on value type + 值类型上的非替代虚拟方法调用 + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + 值类型到引用类型的转换将导致在调用站点处(此处)装箱和在被调用方站点处取消装箱。如果适用,请考虑使用泛型 + + + + Value type to reference type conversion causing boxing allocation + 引用类型转换的值类型导致装箱分配 + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + 值类型({0})正在装箱到引用类型以便进行字符串串联 + + + + Value type to reference type conversion allocation for string concatenation + 字符串串联的引用类型转换分配的值类型 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf new file mode 100644 index 0000000000000..a2861a06d9562 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/Analyzers/xlf/AnalyzersResources.zh-Hant.xlf @@ -0,0 +1,127 @@ + + + + + + The compiler will emit a class that will hold this as a field to allow capturing of this closure + 編譯器會發出類別,其會保留該項目做為欄位以允許擷取此結束 + + + + Display class allocation to capture closure + 顯示類別配置以擷取結束 + + + + Heap allocation of closure Captures: {0} + 結束擷取的堆積配置: {0} + + + + Closure Allocation Source + 結束配置來源 + + + + Struct instance method being used for delegate creation, this will result in a boxing instruction + 用於建立委派的結構執行個體方法,其會產生 Boxing 指示 + + + + Delegate on struct instance caused a boxing allocation + 在結構執行個體上進行委派導致了 Boxing 配置 + + + + Considering moving this out of the generic method + 考慮將此移出泛型方法 + + + + Lambda or anonymous method in a generic method allocates a delegate instance + 泛型方法中的 Lambda 或匿名方法,配置了委派執行個體 + + + + This will allocate a delegate instance + 如此將會配置委派執行個體 + + + + Delegate allocation from a method group + 從方法群組委派配置 + + + + This call site is calling into a function with a 'params' parameter. This results in an array allocation. + 此呼叫位置正在呼叫具有 'params' 參數的函式。這會產生陣列配置。 + + + + Array allocation for params parameter + params 參數的陣列配置 + + + + This will allocate a delegate instance + 如此將會配置委派執行個體 + + + + Delegate allocation from a method group + 從方法群組委派配置 + + + + Non-ValueType enumerator may result in a heap allocation + 在堆積配置中會產生非 ValueType 列舉程式 + + + + Possible allocation of reference type enumerator + 參考型別列舉程式的可能配置情況 + + + + Considering using StringBuilder + 考慮使用 StringBuilder + + + + Implicit string concatenation allocation + 隱含字串串連配置 + + + + Non-overridden virtual method call on a value type adds a boxing or constrained instruction + 在實值型別上呼叫非覆寫虛擬方法,會新增 Boxing 或限制指令 + + + + Non-overridden virtual method call on value type + 在實值型別上呼叫非覆寫虛擬方法 + + + + Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + 將實值型別轉換為參考型別,導致在呼叫站台 (此處) 發生 Boxing,而在被呼叫站台則出現 Unboxing。如果可行的話,請考慮使用泛型。 + + + + Value type to reference type conversion causing boxing allocation + 將實值型別轉換為參考型別,會導致 Boxing 配置 + + + + Value type ({0}) is being boxed to a reference type for a string concatenation + 實值型別 ({0}) 正 Box 為參考型別,以進行字串串連 + + + + Value type to reference type conversion allocation for string concatenation + 將實值型別轉換為參考型別的配置,以進行字串串連 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/AvoidAllocationWithArrayEmptyCodeFix.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/AvoidAllocationWithArrayEmptyCodeFix.cs new file mode 100644 index 0000000000000..229feeb5492fd --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/AvoidAllocationWithArrayEmptyCodeFix.cs @@ -0,0 +1,236 @@ +// 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.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; + +namespace Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AvoidAllocationWithArrayEmptyCodeFix)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + internal sealed class AvoidAllocationWithArrayEmptyCodeFix() : CodeFixProvider + { + private readonly string _title = CodeFixesResources.AvoidAllocationByUsingArrayEmpty; + + public sealed override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(ExplicitAllocationAnalyzer.ObjectCreationRuleId, ExplicitAllocationAnalyzer.ArrayCreationRuleId); + + public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var diagnostic = context.Diagnostics.First(); + var diagnosticSpan = diagnostic.Location.SourceSpan; + var node = root.FindNode(diagnosticSpan); + + if (IsReturnStatement(node)) + { + await TryToRegisterCodeFixesForReturnStatementAsync(context, node, diagnostic).ConfigureAwait(false); + return; + } + + if (IsMethodInvocationParameter(node)) + { + await TryToRegisterCodeFixesForMethodInvocationParameterAsync(context, node, diagnostic).ConfigureAwait(false); + return; + } + } + + private async Task TryToRegisterCodeFixesForMethodInvocationParameterAsync(CodeFixContext context, SyntaxNode node, Diagnostic diagnostic) + { + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (IsExpectedParameterReadonlySequence(node, semanticModel) && node is ArgumentSyntax argument) + { + TryRegisterCodeFix(context, node, diagnostic, argument.Expression, semanticModel); + } + } + + private async Task TryToRegisterCodeFixesForReturnStatementAsync(CodeFixContext context, SyntaxNode node, Diagnostic diagnostic) + { + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + if (IsInsideMemberReturningEnumerable(node, semanticModel)) + { + TryRegisterCodeFix(context, node, diagnostic, node, semanticModel); + } + } + + private void TryRegisterCodeFix(CodeFixContext context, SyntaxNode node, Diagnostic diagnostic, SyntaxNode creationExpression, SemanticModel semanticModel) + { + switch (creationExpression) + { + case ObjectCreationExpressionSyntax objectCreation: + { + if (CanBeReplaceWithEnumerableEmpty(objectCreation, semanticModel) && + objectCreation.Type is GenericNameSyntax genericName) + { + var codeAction = CodeAction.Create(_title, + token => TransformAsync(context.Document, node, genericName.TypeArgumentList.Arguments[0], token), + _title); + context.RegisterCodeFix(codeAction, diagnostic); + } + } + + break; + case ArrayCreationExpressionSyntax arrayCreation: + { + if (CanBeReplaceWithEnumerableEmpty(arrayCreation)) + { + var codeAction = CodeAction.Create(_title, + token => TransformAsync(context.Document, node, arrayCreation.Type.ElementType, token), + _title); + context.RegisterCodeFix(codeAction, diagnostic); + } + } + + break; + } + } + + private static bool IsMethodInvocationParameter(SyntaxNode node) => node is ArgumentSyntax; + + private static bool IsReturnStatement(SyntaxNode node) + => node.Parent is ReturnStatementSyntax or YieldStatementSyntax or ArrowExpressionClauseSyntax; + + private static bool IsInsideMemberReturningEnumerable(SyntaxNode node, SemanticModel semanticModel) + => IsInsideMethodReturningEnumerable(node, semanticModel) || IsInsidePropertyDeclaration(node, semanticModel); + + private static bool IsInsidePropertyDeclaration(SyntaxNode node, SemanticModel semanticModel) + { + var propertyDeclaration = node.FirstAncestorOrSelf(); + return propertyDeclaration != null && + IsPropertyTypeReadonlySequence(semanticModel, propertyDeclaration) && + (IsAutoPropertyWithGetter(node) || IsArrowExpression(node)); + } + + private static bool IsAutoPropertyWithGetter(SyntaxNode node) + { + var accessorDeclaration = node.FirstAncestorOrSelf(); + return accessorDeclaration != null && accessorDeclaration.Keyword.Text == "get"; + } + + private static bool IsArrowExpression(SyntaxNode node) + => node.FirstAncestorOrSelf() != null; + + private static bool CanBeReplaceWithEnumerableEmpty(ArrayCreationExpressionSyntax arrayCreation) + => IsInitializationBlockEmpty(arrayCreation.Initializer); + + private static bool CanBeReplaceWithEnumerableEmpty(ObjectCreationExpressionSyntax objectCreation, SemanticModel semanticModel) + => IsCollectionType(semanticModel, objectCreation) && + IsInitializationBlockEmpty(objectCreation.Initializer) && + IsCopyConstructor(semanticModel, objectCreation) == false; + + private static bool IsInsideMethodReturningEnumerable(SyntaxNode node, SemanticModel semanticModel) + { + var methodDeclaration = node.FirstAncestorOrSelf(); + return methodDeclaration != null && IsReturnTypeReadonlySequence(semanticModel, methodDeclaration); + } + + private static async Task TransformAsync(Document contextDocument, SyntaxNode node, TypeSyntax typeArgument, CancellationToken cancellationToken) + { + var noAllocation = SyntaxFactory.ParseExpression($"Array.Empty<{typeArgument}>()"); + var newNode = ReplaceExpression(node, noAllocation); + if (newNode == null) + { + return contextDocument; + } + + var syntaxRootAsync = await contextDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var newSyntaxRoot = syntaxRootAsync.ReplaceNode(node.Parent!, newNode); + return contextDocument.WithSyntaxRoot(newSyntaxRoot); + } + + private static SyntaxNode? ReplaceExpression(SyntaxNode node, ExpressionSyntax newExpression) + { + switch (node.Parent) + { + case ReturnStatementSyntax parentReturn: + return parentReturn.WithExpression(newExpression); + case ArrowExpressionClauseSyntax arrowStatement: + return arrowStatement.WithExpression(newExpression); + case ArgumentListSyntax argumentList: + var newArguments = argumentList.Arguments.Select(x => x == node ? SyntaxFactory.Argument(newExpression) : x); + return argumentList.WithArguments(SyntaxFactory.SeparatedList(newArguments)); + default: + return null; + } + } + + private static bool IsCopyConstructor(SemanticModel semanticModel, ObjectCreationExpressionSyntax objectCreation) + => objectCreation.ArgumentList?.Arguments.Count > 0 && + semanticModel.GetSymbolInfo(objectCreation).Symbol is IMethodSymbol methodSymbol && + methodSymbol.Parameters.Any(x => x.Type is INamedTypeSymbol namedType && ImplementsGenericICollectionInterface(namedType)); + + private static bool IsInitializationBlockEmpty(InitializerExpressionSyntax? initializer) + => initializer == null || initializer.Expressions.Count == 0; + + private static bool IsCollectionType(SemanticModel semanticModel, ObjectCreationExpressionSyntax objectCreationExpressionSyntax) + => semanticModel.GetTypeInfo(objectCreationExpressionSyntax).Type is INamedTypeSymbol createdType && + (createdType.TypeKind == TypeKind.Array || ImplementsGenericICollectionInterface(createdType)); + + private static bool ImplementsGenericICollectionInterface(INamedTypeSymbol typeSymbol) + { + return typeSymbol.ConstructedFrom.Interfaces.Any(x => x.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_ICollection_T); + } + + private static bool IsPropertyTypeReadonlySequence(SemanticModel semanticModel, PropertyDeclarationSyntax propertyDeclaration) + => IsTypeReadonlySequence(semanticModel, propertyDeclaration.Type); + + private static bool IsReturnTypeReadonlySequence(SemanticModel semanticModel, MethodDeclarationSyntax methodDeclarationSyntax) + => IsTypeReadonlySequence(semanticModel, methodDeclarationSyntax.ReturnType); + + private static bool IsExpectedParameterReadonlySequence(SyntaxNode node, SemanticModel semanticModel) + { + if (node is ArgumentSyntax argument && node.Parent is ArgumentListSyntax argumentList) + { + var argumentIndex = argumentList.Arguments.IndexOf(argument); + if (semanticModel.GetSymbolInfo(argumentList.Parent!).Symbol is IMethodSymbol methodSymbol && + methodSymbol.Parameters.Length > argumentIndex) + { + var parameterType = methodSymbol.Parameters[argumentIndex].Type; + if (IsTypeReadonlySequence(parameterType)) + { + return true; + } + } + } + + return false; + } + + private static bool IsTypeReadonlySequence(SemanticModel semanticModel, TypeSyntax typeSyntax) + { + var returnType = ModelExtensions.GetTypeInfo(semanticModel, typeSyntax).Type!; + return IsTypeReadonlySequence(returnType); + } + + private static bool IsTypeReadonlySequence(ITypeSymbol type) + { + if (type.Kind == SymbolKind.ArrayType) + { + return true; + } + + return type is INamedTypeSymbol namedType && + namedType.IsGenericType && + _readonlySequenceSpecialTypes.Any(readonlySequence => namedType.ConstructedFrom.SpecialType == readonlySequence); + } + + private static readonly ImmutableArray _readonlySequenceSpecialTypes = ImmutableArray.Create( + SpecialType.System_Collections_Generic_IEnumerable_T, + SpecialType.System_Collections_Generic_IReadOnlyList_T, + SpecialType.System_Collections_Generic_IReadOnlyCollection_T); + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/CodeFixesResources.resx b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/CodeFixesResources.resx new file mode 100644 index 0000000000000..719fa69ce5551 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/CodeFixesResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Avoid allocation by using 'Array.Empty<T>()' + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes.csproj b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes.csproj new file mode 100644 index 0000000000000..2bf000de837ff --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.0 + true + $(MicrosoftCodeAnalysisVersionForPerfSensitiveAnalyzers) + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.cs.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.cs.xlf new file mode 100644 index 0000000000000..0f444e639d015 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.cs.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Nepoužívejte přidělování pomocí Array.Empty<T>(). + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.de.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.de.xlf new file mode 100644 index 0000000000000..7bbd5bff55e9a --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.de.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Zuordnung unter Verwendung von "Array.Empty<T>()" vermeiden + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.es.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.es.xlf new file mode 100644 index 0000000000000..b8dda990ef8bd --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.es.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Evitar la asignación mediante "Array.Empty<T>()" + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.fr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.fr.xlf new file mode 100644 index 0000000000000..551fe4d60e55e --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.fr.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Éviter l'allocation à l'aide de 'Array.Empty<T>()' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.it.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.it.xlf new file mode 100644 index 0000000000000..5fba4537d4263 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.it.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Evitare l'allocazione usando 'Array.Empty<T>()' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ja.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ja.xlf new file mode 100644 index 0000000000000..ee06e31220b91 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ja.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + 'Array.Empty<T>()' を使用して割り当てないでください + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ko.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ko.xlf new file mode 100644 index 0000000000000..3df6f299b48ac --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ko.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + 'Array.Empty<T>()'를 사용하여 할당하지 마세요. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pl.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pl.xlf new file mode 100644 index 0000000000000..4567bc0f3ca90 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pl.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Unikaj alokacji za pomocą elementu „Array.Empty<T>()” + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pt-BR.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pt-BR.xlf new file mode 100644 index 0000000000000..1151a8651a125 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.pt-BR.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Evite a alocação usando 'Array.Empty<T>()' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ru.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ru.xlf new file mode 100644 index 0000000000000..f3e185d1914e2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.ru.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + Избегайте выделения с помощью "Array.Empty<T>()" + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.tr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.tr.xlf new file mode 100644 index 0000000000000..65a5e310497cb --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.tr.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + 'Array.Empty<T>()' kullanarak ayırmadan kaçının + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf new file mode 100644 index 0000000000000..934aa1810852e --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hans.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + 避免使用 "Array.Empty<T>()" 进行分配 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf new file mode 100644 index 0000000000000..d911d7fbf81f2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/CSharp/CodeFixes/xlf/CodeFixesResources.zh-Hant.xlf @@ -0,0 +1,12 @@ + + + + + + Avoid allocation by using 'Array.Empty<T>()' + 使用 'Array.Empty<T>()' 來避免配置 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer.cs new file mode 100644 index 0000000000000..d971069b0b3f7 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer.cs @@ -0,0 +1,149 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers +{ + internal abstract class AbstractAllocationAnalyzer : DiagnosticAnalyzer + { + protected abstract ImmutableArray Operations { get; } + + protected abstract void AnalyzeNode(OperationAnalysisContext context, in PerformanceSensitiveInfo info); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // This analyzer is triggered by an attribute, even if it appears in generated code + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + if (Operations.IsEmpty) + { + return; + } + + context.RegisterCompilationStartAction(compilationStartContext => + { + var compilation = compilationStartContext.Compilation; + var attributeSymbol = compilation.GetOrCreateTypeByMetadataName(AllocationRules.PerformanceSensitiveAttributeName); + + // Bail if PerformanceSensitiveAttribute is not declared in the compilation. + if (attributeSymbol == null) + { + return; + } + + compilationStartContext.RegisterOperationBlockStartAction(blockStartContext => + { + var checker = new AttributeChecker(attributeSymbol); + RegisterOperationAnalysis(blockStartContext, checker); + }); + }); + } + + private void RegisterOperationAnalysis(OperationBlockStartAnalysisContext operationBlockStartAnalysisContext, AttributeChecker performanceSensitiveAttributeChecker) + { + var owningSymbol = operationBlockStartAnalysisContext.OwningSymbol; + if (!performanceSensitiveAttributeChecker.TryGetContainsPerformanceSensitiveInfo(owningSymbol, out var info)) + { + return; + } + + operationBlockStartAnalysisContext.RegisterOperationAction( + syntaxNodeContext => + { + AnalyzeNode(syntaxNodeContext, in info); + }, + Operations); + } + + protected sealed class AttributeChecker + { + private INamedTypeSymbol PerfSensitiveAttributeSymbol { get; } + + public AttributeChecker(INamedTypeSymbol perfSensitiveAttributeSymbol) + { + PerfSensitiveAttributeSymbol = perfSensitiveAttributeSymbol; + } + + public bool TryGetContainsPerformanceSensitiveInfo(ISymbol symbol, out PerformanceSensitiveInfo info) + { + if (TryGet(symbol, out info)) + { + return true; + } + + // The attribute might be applied to a property declaration, instead of its accessor declaration. + if (symbol is IMethodSymbol methodSymbol && + (methodSymbol.MethodKind == MethodKind.PropertyGet || methodSymbol.MethodKind == MethodKind.PropertySet) && + TryGet(methodSymbol.AssociatedSymbol!, out info)) + { + return true; + } + + info = default; + return false; + + bool TryGet(ISymbol s, out PerformanceSensitiveInfo i) + { + if (s.GetAttribute(PerfSensitiveAttributeSymbol) is { } attribute) + { + i = CreatePerformanceSensitiveInfo(attribute); + return true; + } + + i = default; + return false; + } + } + + private static PerformanceSensitiveInfo CreatePerformanceSensitiveInfo(AttributeData data) + { + var allowCaptures = true; + var allowGenericEnumeration = true; + var allowLocks = true; + + foreach (var namedArgument in data.NamedArguments) + { + switch (namedArgument.Key) + { + case "AllowCaptures": + allowCaptures = (bool)namedArgument.Value.Value!; + break; + case "AllowGenericEnumeration": + allowGenericEnumeration = (bool)namedArgument.Value.Value!; + break; + case "AllowLocks": + allowLocks = (bool)namedArgument.Value.Value!; + break; + } + } + + return new PerformanceSensitiveInfo(allowCaptures, allowGenericEnumeration, allowLocks); + } + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types. This type is never used for comparison + protected readonly struct PerformanceSensitiveInfo +#pragma warning restore CA1815 + { + public bool AllowCaptures { get; } + public bool AllowGenericEnumeration { get; } + public bool AllowLocks { get; } + + public PerformanceSensitiveInfo( + bool allowCaptures = true, + bool allowGenericEnumeration = true, + bool allowLocks = true) + { + AllowCaptures = allowCaptures; + AllowGenericEnumeration = allowGenericEnumeration; + AllowLocks = allowLocks; + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer`1.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer`1.cs new file mode 100644 index 0000000000000..a76192354a4aa --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AbstractAllocationAnalyzer`1.cs @@ -0,0 +1,66 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers +{ + internal abstract class AbstractAllocationAnalyzer + : AbstractAllocationAnalyzer + where TLanguageKindEnum : struct + { + protected abstract ImmutableArray Expressions { get; } + + protected sealed override ImmutableArray Operations => ImmutableArray.Empty; + + protected abstract void AnalyzeNode(SyntaxNodeAnalysisContext context, in PerformanceSensitiveInfo info); + + protected override void AnalyzeNode(OperationAnalysisContext context, in PerformanceSensitiveInfo info) { } + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // This analyzer is triggered by an attribute, even if it appears in generated code + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(compilationStartContext => + { + var compilation = compilationStartContext.Compilation; + var attributeSymbol = compilation.GetOrCreateTypeByMetadataName(AllocationRules.PerformanceSensitiveAttributeName); + + // Bail if PerformanceSensitiveAttribute is not declared in the compilation. + if (attributeSymbol == null) + { + return; + } + + compilationStartContext.RegisterCodeBlockStartAction(blockStartContext => + { + var checker = new AttributeChecker(attributeSymbol); + RegisterSyntaxAnalysis(blockStartContext, checker); + }); + }); + } + + private void RegisterSyntaxAnalysis(CodeBlockStartAnalysisContext codeBlockStartAnalysisContext, AttributeChecker performanceSensitiveAttributeChecker) + { + var owningSymbol = codeBlockStartAnalysisContext.OwningSymbol; + + if (!performanceSensitiveAttributeChecker.TryGetContainsPerformanceSensitiveInfo(owningSymbol, out var info)) + { + return; + } + + codeBlockStartAnalysisContext.RegisterSyntaxNodeAction( + syntaxNodeContext => + { + AnalyzeNode(syntaxNodeContext, in info); + }, + Expressions); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AllocationRules.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AllocationRules.cs new file mode 100644 index 0000000000000..9571546dd7036 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AllocationRules.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. + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers +{ + internal static class AllocationRules + { + public const string PerformanceSensitiveAttributeName = "Roslyn.Utilities.PerformanceSensitiveAttribute"; + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..4458b84d8ecd2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -0,0 +1,10 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +HAA0501 | Performance | Info | ExplicitAllocationAnalyzer +HAA0502 | Performance | Info | ExplicitAllocationAnalyzer +HAA0503 | Performance | Info | ExplicitAllocationAnalyzer, [Documentation](http://msdn.microsoft.com/en-us/library/bb397696.aspx) +HAA0506 | Performance | Info | ExplicitAllocationAnalyzer diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/ExplicitAllocationAnalyzer.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/ExplicitAllocationAnalyzer.cs new file mode 100644 index 0000000000000..9cf63be92a69f --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/ExplicitAllocationAnalyzer.cs @@ -0,0 +1,144 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers +{ + using static PerformanceSensitiveAnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + internal sealed class ExplicitAllocationAnalyzer : AbstractAllocationAnalyzer + { + public const string ArrayCreationRuleId = "HAA0501"; + public const string ObjectCreationRuleId = "HAA0502"; + public const string AnonymousObjectCreationRuleId = "HAA0503"; + // HAA0504 is retired and should not be reused + // HAA0505 is retired and should not be reused + public const string LetCauseRuleId = "HAA0506"; + + private static readonly LocalizableString s_localizableArrayCreationRuleTitleAndMessage = CreateLocalizableResourceString(nameof(NewArrayRuleTitleAndMessage)); + private static readonly LocalizableString s_localizableObjectCreationRuleTitleAndMessage = CreateLocalizableResourceString(nameof(NewObjectRuleTitleAndMessage)); + private static readonly LocalizableString s_localizablAnonymousObjectCreationRuleTitleAndMessage = CreateLocalizableResourceString(nameof(AnonymousNewObjectRuleTitleAndMessage)); + private static readonly LocalizableString s_localizableLetCauseRuleTitleAndMessage = CreateLocalizableResourceString(nameof(LetCauseRuleTitleAndMessage)); + + internal static readonly DiagnosticDescriptor ArrayCreationRule = new( + ArrayCreationRuleId, + s_localizableArrayCreationRuleTitleAndMessage, + s_localizableArrayCreationRuleTitleAndMessage, + DiagnosticCategory.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor ObjectCreationRule = new( + ObjectCreationRuleId, + s_localizableObjectCreationRuleTitleAndMessage, + s_localizableObjectCreationRuleTitleAndMessage, + DiagnosticCategory.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + internal static readonly DiagnosticDescriptor AnonymousObjectCreationRule = new( + AnonymousObjectCreationRuleId, + s_localizablAnonymousObjectCreationRuleTitleAndMessage, + s_localizablAnonymousObjectCreationRuleTitleAndMessage, + DiagnosticCategory.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true, + helpLinkUri: "http://msdn.microsoft.com/en-us/library/bb397696.aspx"); + + internal static readonly DiagnosticDescriptor LetCauseRule = new( + LetCauseRuleId, + s_localizableLetCauseRuleTitleAndMessage, + s_localizableLetCauseRuleTitleAndMessage, + DiagnosticCategory.Performance, + DiagnosticSeverity.Info, + isEnabledByDefault: true); + + private static readonly object[] EmptyMessageArgs = Array.Empty(); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + ArrayCreationRule, + ObjectCreationRule, + AnonymousObjectCreationRule, + LetCauseRule); + + protected override ImmutableArray Operations { get; } = ImmutableArray.Create( + OperationKind.ArrayCreation, + OperationKind.ObjectCreation, + OperationKind.AnonymousObjectCreation, + OperationKind.DelegateCreation, + OperationKind.TypeParameterObjectCreation); + + protected override void AnalyzeNode(OperationAnalysisContext context, in PerformanceSensitiveInfo info) + { + if (context.Operation is IArrayCreationOperation arrayCreation) + { + // The implicit case is handled by HAA0101 + if (!arrayCreation.IsImplicit) + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(ArrayCreationRule, EmptyMessageArgs)); + } + + return; + } + + if (context.Operation is IObjectCreationOperation or ITypeParameterObjectCreationOperation) + { + if (context.Operation.Parent?.Kind == OperationKindEx.Attribute) + { + // Don't report attribute usage as creating a new instance + return; + } + + if (context.Operation.Type?.IsReferenceType == true) + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(ObjectCreationRule, EmptyMessageArgs)); + return; + } + + if (context.Operation.Parent is IConversionOperation conversion) + { + if (conversion.Type?.IsReferenceType == true && conversion.Operand.Type?.IsValueType == true) + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(ObjectCreationRule, EmptyMessageArgs)); + } + + return; + } + } + + if (context.Operation is IAnonymousObjectCreationOperation) + { + if (context.Operation.IsImplicit) + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(LetCauseRule, EmptyMessageArgs)); + } + else + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(AnonymousObjectCreationRule, EmptyMessageArgs)); + } + + return; + } + + if (context.Operation is IDelegateCreationOperation delegateCreationOperation) + { + // The implicit case is handled by HAA0603 + if (!delegateCreationOperation.IsImplicit) + { + context.ReportDiagnostic(context.Operation.CreateDiagnostic(ObjectCreationRule, EmptyMessageArgs)); + } + + return; + } + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.csproj b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.csproj new file mode 100644 index 0000000000000..1e9de5ea52f9d --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.csproj @@ -0,0 +1,24 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForPerfSensitiveAnalyzers) + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.cs new file mode 100644 index 0000000000000..c08f6e061cb38 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers +{ + internal partial class PerformanceSensitiveAnalyzersResources + { + private static readonly Type s_resourcesType = typeof(PerformanceSensitiveAnalyzersResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.resx b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.resx new file mode 100644 index 0000000000000..7f943a4ae32c2 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/PerformanceSensitiveAnalyzersResources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Explicit new anonymous object allocation + + + Let clause induced allocation + + + Explicit new array type allocation + + + Explicit new reference type allocation + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.cs.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.cs.xlf new file mode 100644 index 0000000000000..9eeba267560c5 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.cs.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Explicitní přidělení nového anonymního objektu + + + + Let clause induced allocation + Přidělení vyvolané klauzulí let + + + + Explicit new array type allocation + Explicitní přidělení typu nového pole + + + + Explicit new reference type allocation + Explicitní přidělení typu nového odkazu + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.de.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.de.xlf new file mode 100644 index 0000000000000..35d296252bb28 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.de.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Explizite Zuordnung neuer anonymer Objekte + + + + Let clause induced allocation + Let-Klausel hat Zuordnung ausgelöst + + + + Explicit new array type allocation + Explizite Zuordnung neuer Arraytypen + + + + Explicit new reference type allocation + Explizite Zuordnung neuer Verweistypen + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.es.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.es.xlf new file mode 100644 index 0000000000000..ca6fbc6a04edf --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.es.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Asignación explícita de objeto anónimo nuevo + + + + Let clause induced allocation + La cláusula let provocó la asignación + + + + Explicit new array type allocation + Asignación explícita de tipo de matriz nuevo + + + + Explicit new reference type allocation + Asignación explícita de tipo de referencia nuevo + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.fr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.fr.xlf new file mode 100644 index 0000000000000..522495625123d --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.fr.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Allocation explicite de nouvel objet anonyme + + + + Let clause induced allocation + Allocation induite de clause Let + + + + Explicit new array type allocation + Allocation explicite d'un nouveau type de tableau + + + + Explicit new reference type allocation + Allocation explicite d'un nouveau type référence + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.it.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.it.xlf new file mode 100644 index 0000000000000..f46722d41c563 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.it.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Allocazione esplicita di nuovi oggetti anonimi + + + + Let clause induced allocation + La clausola Let ha indotto l'allocazione + + + + Explicit new array type allocation + Allocazione esplicita di nuovi tipi matrice + + + + Explicit new reference type allocation + Allocazione esplicita di nuovi tipi riferimento + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ja.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ja.xlf new file mode 100644 index 0000000000000..91024cda1f960 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ja.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + 明示的な新しい匿名オブジェクトの割り当て + + + + Let clause induced allocation + let 句が割り当てを発生させました + + + + Explicit new array type allocation + 明示的な新しい配列型の割り当て + + + + Explicit new reference type allocation + 明示的な新しい参照型の割り当て + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ko.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ko.xlf new file mode 100644 index 0000000000000..b4f85f35df971 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ko.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + 명시적 새 익명 개체 할당 + + + + Let clause induced allocation + Let 절 발생 할당 + + + + Explicit new array type allocation + 명시적 새 배열 형식 할당 + + + + Explicit new reference type allocation + 명시적 새 참조 형식 할당 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pl.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pl.xlf new file mode 100644 index 0000000000000..e0ac41204e6da --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pl.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Jawna alokacja nowego obiektu anonimowego + + + + Let clause induced allocation + Alokacja wywołana przez klauzulę let + + + + Explicit new array type allocation + Jawna alokacja nowego typu tablicy + + + + Explicit new reference type allocation + Jawna alokacja nowego typu referencyjnego + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pt-BR.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pt-BR.xlf new file mode 100644 index 0000000000000..8150028992f15 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.pt-BR.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Alocação explícita de novo objeto anônimo + + + + Let clause induced allocation + Alocação induzida da cláusula Let + + + + Explicit new array type allocation + Alocação explícita de novo tipo de matriz + + + + Explicit new reference type allocation + Alocação explícita de novo tipo de referência + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ru.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ru.xlf new file mode 100644 index 0000000000000..1c2e453b3f153 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.ru.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Явное выделение нового анонимного объекта + + + + Let clause induced allocation + Предложение let вызвало выделение + + + + Explicit new array type allocation + Явное выделение нового типа массива + + + + Explicit new reference type allocation + Явное выделение нового ссылочного типа + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.tr.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.tr.xlf new file mode 100644 index 0000000000000..0a3c92ea33a97 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.tr.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + Açık yeni anonim nesne ayırma + + + + Let clause induced allocation + Yan tümcenin başlattığı ayırmaya izin ver + + + + Explicit new array type allocation + Açık yeni dizi türü ayırma + + + + Explicit new reference type allocation + Açık yeni başvuru türü ayırma + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf new file mode 100644 index 0000000000000..36ca2a3d1a5b9 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hans.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + 显式的新匿名对象分配 + + + + Let clause induced allocation + 引发分配的 Let 子句 + + + + Explicit new array type allocation + 显式的新数组类型分配 + + + + Explicit new reference type allocation + 显式的新引用类型分配 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf new file mode 100644 index 0000000000000..8aeb788a6dec3 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Core/xlf/PerformanceSensitiveAnalyzersResources.zh-Hant.xlf @@ -0,0 +1,27 @@ + + + + + + Explicit new anonymous object allocation + 明確的新匿名物件配置 + + + + Let clause induced allocation + Let 子句引發了配置 + + + + Explicit new array type allocation + 明確的新陣列類型配置 + + + + Explicit new reference type allocation + 明確的新參考型別配置 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Directory.Build.props b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Directory.Build.props new file mode 100644 index 0000000000000..9dd0ddea9df26 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Directory.Build.props @@ -0,0 +1,6 @@ + + + + true + + diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.md new file mode 100644 index 0000000000000..8d1c74876d2e0 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.md @@ -0,0 +1,193 @@ +# Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers + +## HAA0101: Array allocation for params parameter + +This call site is calling into a function with a 'params' parameter. This results in an array allocation. + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0102: Non-overridden virtual method call on value type + +Non-overridden virtual method call on a value type adds a boxing or constrained instruction + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [HAA0201](http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.110).aspx): Implicit string concatenation allocation + +Considering using StringBuilder + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [HAA0202](http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx): Value type to reference type conversion allocation for string concatenation + +Value type ({0}) is being boxed to a reference type for a string concatenation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0301: Closure Allocation Source + +Heap allocation of closure Captures: {0} + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0302: Display class allocation to capture closure + +The compiler will emit a class that will hold this as a field to allow capturing of this closure + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0303: Lambda or anonymous method in a generic method allocates a delegate instance + +Considering moving this out of the generic method + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0401: Possible allocation of reference type enumerator + +Non-ValueType enumerator may result in a heap allocation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0501: Explicit new array type allocation + +Explicit new array type allocation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + +## HAA0502: Explicit new reference type allocation + +Explicit new reference type allocation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + +## [HAA0503](http://msdn.microsoft.com/en-us/library/bb397696.aspx): Explicit new anonymous object allocation + +Explicit new anonymous object allocation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|False| +--- + +## HAA0506: Let clause induced allocation + +Let clause induced allocation + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|False| +--- + +## HAA0601: Value type to reference type conversion causing boxing allocation + +Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable. + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0602: Delegate on struct instance caused a boxing allocation + +Struct instance method being used for delegate creation, this will result in a boxing instruction + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0603: Delegate allocation from a method group + +This will allocate a delegate instance + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## HAA0604: Delegate allocation from a method group + +This will allocate a delegate instance + +|Item|Value| +|-|-| +|Category|Performance| +|Enabled|True| +|Severity|Info| +|CodeFix|False| +--- diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.sarif b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.sarif new file mode 100644 index 0000000000000..d9cd37eaa58ac --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.sarif @@ -0,0 +1,264 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "HAA0101": { + "id": "HAA0101", + "shortDescription": "Array allocation for params parameter", + "fullDescription": "This call site is calling into a function with a 'params' parameter. This results in an array allocation.", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "CallSiteImplicitAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0102": { + "id": "HAA0102", + "shortDescription": "Non-overridden virtual method call on value type", + "fullDescription": "Non-overridden virtual method call on a value type adds a boxing or constrained instruction", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "CallSiteImplicitAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0201": { + "id": "HAA0201", + "shortDescription": "Implicit string concatenation allocation", + "fullDescription": "Considering using StringBuilder", + "defaultLevel": "warning", + "helpUri": "http://msdn.microsoft.com/en-us/library/2839d5h5(v=vs.110).aspx", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ConcatenationAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0202": { + "id": "HAA0202", + "shortDescription": "Value type to reference type conversion allocation for string concatenation", + "fullDescription": "Value type ({0}) is being boxed to a reference type for a string concatenation", + "defaultLevel": "warning", + "helpUri": "http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ConcatenationAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0301": { + "id": "HAA0301", + "shortDescription": "Closure Allocation Source", + "fullDescription": "Heap allocation of closure Captures: {0}", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "DisplayClassAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0302": { + "id": "HAA0302", + "shortDescription": "Display class allocation to capture closure", + "fullDescription": "The compiler will emit a class that will hold this as a field to allow capturing of this closure", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "DisplayClassAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0303": { + "id": "HAA0303", + "shortDescription": "Lambda or anonymous method in a generic method allocates a delegate instance", + "fullDescription": "Considering moving this out of the generic method", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "DisplayClassAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0401": { + "id": "HAA0401", + "shortDescription": "Possible allocation of reference type enumerator", + "fullDescription": "Non-ValueType enumerator may result in a heap allocation", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "EnumeratorAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0601": { + "id": "HAA0601", + "shortDescription": "Value type to reference type conversion causing boxing allocation", + "fullDescription": "Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable.", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "TypeConversionAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0602": { + "id": "HAA0602", + "shortDescription": "Delegate on struct instance caused a boxing allocation", + "fullDescription": "Struct instance method being used for delegate creation, this will result in a boxing instruction", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "TypeConversionAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0603": { + "id": "HAA0603", + "shortDescription": "Delegate allocation from a method group", + "fullDescription": "This will allocate a delegate instance", + "defaultLevel": "warning", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "TypeConversionAllocationAnalyzer", + "languages": [ + "C#" + ] + } + }, + "HAA0604": { + "id": "HAA0604", + "shortDescription": "Delegate allocation from a method group", + "fullDescription": "This will allocate a delegate instance", + "defaultLevel": "note", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "TypeConversionAllocationAnalyzer", + "languages": [ + "C#" + ] + } + } + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "HAA0501": { + "id": "HAA0501", + "shortDescription": "Explicit new array type allocation", + "fullDescription": "Explicit new array type allocation", + "defaultLevel": "note", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ExplicitAllocationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "HAA0502": { + "id": "HAA0502", + "shortDescription": "Explicit new reference type allocation", + "fullDescription": "Explicit new reference type allocation", + "defaultLevel": "note", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ExplicitAllocationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "HAA0503": { + "id": "HAA0503", + "shortDescription": "Explicit new anonymous object allocation", + "fullDescription": "Explicit new anonymous object allocation", + "defaultLevel": "note", + "helpUri": "http://msdn.microsoft.com/en-us/library/bb397696.aspx", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ExplicitAllocationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + }, + "HAA0506": { + "id": "HAA0506", + "shortDescription": "Let clause induced allocation", + "fullDescription": "Let clause induced allocation", + "defaultLevel": "note", + "properties": { + "category": "Performance", + "isEnabledByDefault": true, + "typeName": "ExplicitAllocationAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/PerformanceSensitiveAnalyzers.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/PerformanceSensitiveAnalyzers.md new file mode 100644 index 0000000000000..3c4d74bc800b7 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/PerformanceSensitiveAnalyzers.md @@ -0,0 +1,39 @@ +This is forked from [01c6dd2bf7dc509de289da60b78df8440ce4c11d@Microsoft/RoslynClrHeapAllocationAnalyzer](https://github.com/Microsoft/RoslynClrHeapAllocationAnalyzer/commit/01c6dd2bf7dc509de289da60b78df8440ce4c11d) + +How to avoid conflicts of `PerformanceSensitiveAttribute` in projects contain `InternalsVisibleTo` (IVT) +-------------------------------- + +Because we inject the source of `PerformanceSensitiveAttribute` into projects referencing PerformanceSensitive analyzer package by default, which is declared as `internal`, you may run into warning CS0436 if you have IVTs defined in your projects. + +One way to resolve this issue is setting `GeneratePerformanceSensitiveAttribute` property to true in the project at the root of your IVT tree, and false otherwise. + +For example, given the dependency graph below, if project A has IVT for project B, and project C has IVT for project D and project E. You need to set `GeneratePerformanceSensitiveAttribute` to true in A and C, and false in B, D and E. + +```text + A + | + / \ + B C + / \ + D E +``` + +Internally, this is implemented in the `Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.targets` file included in the nuget package. + +```xml + + + + true + $(MSBuildThisFileDirectory)PerformanceSensitiveAttribute$(DefaultLanguageSourceExtension) + + + + + + + + + + +``` diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..49b40aeecc088 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/RulesMissingDocumentation.md @@ -0,0 +1,17 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +HAA0101 | | Array allocation for params parameter | +HAA0102 | | Non-overridden virtual method call on value type | +HAA0301 | | Closure Allocation Source | +HAA0302 | | Display class allocation to capture closure | +HAA0303 | | Lambda or anonymous method in a generic method allocates a delegate instance | +HAA0401 | | Possible allocation of reference type enumerator | +HAA0501 | | Explicit new array type allocation | +HAA0502 | | Explicit new reference type allocation | +HAA0506 | | Let clause induced allocation | +HAA0601 | | Value type to reference type conversion causing boxing allocation | +HAA0602 | | Delegate on struct instance caused a boxing allocation | +HAA0603 | | Delegate allocation from a method group | +HAA0604 | | Delegate allocation from a method group | diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Setup.csproj b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Setup.csproj new file mode 100644 index 0000000000000..013ec582cf7d1 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.Setup.csproj @@ -0,0 +1,26 @@ + + + + net472 + + true + false + false + false + false + false + true + + + + + + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..19b93f4fc5344 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,25 @@ + + + + + + PerformanceSensitive Analyzers + PerformanceSensitive Analyzers + EULA.txt + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/AvoidAllocationWithArrayEmptyCodeFixTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/AvoidAllocationWithArrayEmptyCodeFixTests.cs new file mode 100644 index 0000000000000..fc9e515a2d0d6 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/AvoidAllocationWithArrayEmptyCodeFixTests.cs @@ -0,0 +1,545 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.ExplicitAllocationAnalyzer, + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CodeFixes.AvoidAllocationWithArrayEmptyCodeFix>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitive.Analyzers.UnitTests +{ + public class AvoidAllocationWithArrayEmptyCodeFixTests + { + [Theory] + [InlineData("IEnumerable")] + [InlineData("IReadOnlyList")] + [InlineData("IReadOnlyCollection")] + public async Task ShouldReplaceEmptyListCreationWithArrayEmptyWithReturnTypeAsync(string type) + { + var initial = $@" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{{ + class SampleClass + {{ + [PerformanceSensitive(""uri"")] + public {type} DoSomething() + {{ + return new List(); + }} + }} +}}"; + + var expected = $@" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{{ + class SampleClass + {{ + [PerformanceSensitive(""uri"")] + public {type} DoSomething() + {{ + return Array.Empty(); + }} + }} +}}"; + await VerifyCS.VerifyCodeFixAsync(initial, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(13, 20, 13, 35), expected); + } + + [Fact] + public async Task ShouldReplaceEmptyListCreationWithArrayEmptyWhenReturnFromMethodArrayAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public int[] DoSomething() + { + return new int[0]; + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public int[] DoSomething() + { + return Array.Empty(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 20, 13, 30), after); + } + + [Fact] + public async Task ShouldReplaceEmptyLisCreationWithArrayEmptyForArrowExpressionAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething => new List(); + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething => Array.Empty(); + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(11, 48, 11, 63), after); + } + [Fact] + public async Task ShouldReplaceEmptyListCreationWithArrayEmptyForReadonlyPropertyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething { get {return new List();}} + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething { get {return Array.Empty();}} + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(11, 59, 11, 74), after); + } + [Fact] + public async Task ShouldReplaceEmptyListWithCreationWithPredefinedSizeWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new List(10); + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return Array.Empty(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(13, 20, 13, 37), after); + } + [Fact] + public async Task ShouldNotProposeCodeFixWhenNonEmptyListCreatedAsync() + { + var code = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new List(){1, 2}; + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(13, 20, 13, 41), code); + } + [Fact] + public async Task ShouldNotProposeCodeFixWhenReturnTypeInheritFormEnumerableAsync() + { + var code = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public List DoSomething() + { + return new List(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(13, 20, 13, 35), code); + } + [Fact] + public async Task ShouldNotProposeCodeFixWhenForCollectionCreationUsingCopyConstructorAsync() + { + var code = @" +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + var innerList = new List(){1, 2}; + return new ReadOnlyCollection(innerList); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(code, + new[] + { + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(14, 29, 14, 50), + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(15, 20, 15, 58), + }, code); + } + [Fact] + public async Task ShouldReplacEmptyCollectionCreationWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new Collection(); + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return Array.Empty(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(14, 20, 14, 41), after); + } + [Fact] + public async Task ShouldReplaceEmptyArrayCreationWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new int[0]; + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return Array.Empty(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 20, 13, 30), after); + } + [Fact] + public async Task ShouldNotProposeCodeFixWhenNonEmptyArrayCreationAsync() + { + var code = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new int[]{1, 2}; + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(code, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 20, 13, 35), code); + } + [Fact] + public async Task ShouldReplaceEmptyArrayCreationWithInitBlockWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return new int[] { }; + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public IEnumerable DoSomething() + { + return Array.Empty(); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 20, 13, 33), after); + } + [Fact] + public async Task ShouldReplaceListCreationAsMethodInvocationParameterWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething() + { + Do(new List()); + } + + private void Do(IEnumerable a) + { + + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething() + { + Do(Array.Empty()); + } + + private void Do(IEnumerable a) + { + + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithSpan(13, 16, 13, 31), after); + } + [Fact] + public async Task ShouldReplaceArrayCreationAsMethodInvocationParameterWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething() + { + Do(new int[0]); + } + + private void Do(IEnumerable a) + { + + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething() + { + Do(Array.Empty()); + } + + private void Do(IEnumerable a) + { + + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 16, 13, 26), after); + } + [Fact] + public async Task ShouldReplaceArrayCreationAsDelegateInvocationParameterWithArrayEmptyAsync() + { + var before = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething(Action> doSth) + { + doSth(new int[0]); + } + } +}"; + var after = @" +using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +namespace SampleNamespace +{ + class SampleClass + { + [PerformanceSensitive(""uri"")] + public void DoSomething(Action> doSth) + { + doSth(Array.Empty()); + } + } +}"; + + await VerifyCS.VerifyCodeFixAsync(before, VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithSpan(13, 19, 13, 29), after); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..9ec92e6f5b9ef --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2+Test.cs @@ -0,0 +1,20 @@ +// 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.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests +{ + public static partial class CSharpPerformanceCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + internal sealed class Test : CSharpCodeFixTest + { + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..df37773c89e70 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CSharpPerformanceCodeFixVerifier`2.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.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests +{ + public static partial class CSharpPerformanceCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + internal const string PerformanceSensitiveAttributeSource = @" +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace Roslyn.Utilities +{ + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] + internal sealed class PerformanceSensitiveAttribute : Attribute + { + public PerformanceSensitiveAttribute(string uri) + { + Uri = uri; + } + + public string Uri { get; } + public string Constraint { get; set; } + public bool AllowCaptures { get; set; } + public bool AllowGenericEnumeration { get; set; } + public bool AllowLocks { get; set; } + public bool OftenCompletesSynchronously { get; set; } + public bool IsParallelEntry { get; set; } + } +}"; + + public static DiagnosticResult Diagnostic() + => CSharpCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestState = + { + Sources = + { + source, + ("PerformanceSensitiveAttribute.cs", PerformanceSensitiveAttributeSource) + }, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + public static Task VerifyCodeFixAsync(string source, string fixedSource) + => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestState = + { + Sources = + { + source, + ("PerformanceSensitiveAttribute.cs", PerformanceSensitiveAttributeSource) + }, + }, + FixedState = + { + Sources = + { + fixedSource, + ("PerformanceSensitiveAttribute.cs", PerformanceSensitiveAttributeSource) + }, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CallSiteImplicitAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CallSiteImplicitAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..e55f42813baee --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/CallSiteImplicitAllocationAnalyzerTests.cs @@ -0,0 +1,226 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.CallSiteImplicitAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysisPerformanceSensitiveAnalyzers.UnitTests +{ + public class CallSiteImplicitAllocationAnalyzerTests + { + [Fact] + public async Task CallSiteImplicitAllocation_ParamAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + + Params(); //no allocation, because compiler will implicitly substitute Array.Empty() + Params(1, 2); + Params(new [] { 1, 2}); // explicit, so no warning + ParamsWithObjects(new [] { 1, 2}); // explicit, but converted to objects, so stil la warning?! + + // Only 4 args and above use the params overload of String.Format + var test = String.Format(""Testing {0}, {1}, {2}, {3}"", 1, ""blah"", 2.0m, 'c'); + } + + public void Params(params int[] args) + { + } + + public void ParamsWithObjects(params object[] args) + { + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(11,9): warning HAA0101: This call site is calling into a function with a 'params' parameter. This results in an array allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ParamsParameterRule).WithLocation(11, 9), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(13,9): warning HAA0101: This call site is calling into a function with a 'params' parameter. This results in an array allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ParamsParameterRule).WithLocation(13, 9), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(16,20): warning HAA0101: This call site is calling into a function with a 'params' parameter. This results in an array allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ParamsParameterRule).WithLocation(16, 20)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact, WorkItem(3272, "https://github.com/dotnet/roslyn-analyzers/issues/3272")] + public async Task EmptyParamsWithNetFramework45Async() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetFramework.Net45.Default, + TestState = + { + Sources = + { + @" +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Params(); // allocation + } + + public void Params(params int[] args) + { + } +}", + ("PerformanceSensitiveAttribute.cs", VerifyCS.PerformanceSensitiveAttributeSource) + }, + ExpectedDiagnostics = + { +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ParamsParameterRule).WithLocation(10, 9), +#pragma warning restore RS0030 // Do not use banned APIs + }, + }, + }.RunAsync(); + } + + [Fact] + public async Task CallSiteImplicitAllocation_NonOverridenMethodOnStructAsync() + { + var sampleProgram = @" +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var normal = new Normal().GetHashCode(); + var overridden = new OverrideToHashCode().GetHashCode(); + } +} + +public struct Normal +{ +} + +public struct OverrideToHashCode +{ + public override int GetHashCode() + { + return -1; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(10,22): warning HAA0102: Non-overridden virtual method call on a value type adds a boxing or constrained instruction +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ValueTypeNonOverridenCallRule).WithLocation(10, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task CallSiteImplicitAllocation_DoNotReportNonOverriddenMethodCallForStaticCallsAsync() + { + var sampleProgram = @" +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var t = System.Enum.GetUnderlyingType(typeof(System.StringComparison)); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task CallSiteImplicitAllocation_DoNotReportNonOverriddenMethodCallForNonVirtualCallsAsync() + { + var sampleProgram = @" +using System.IO; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + FileAttributes attr = FileAttributes.System; + attr.HasFlag (FileAttributes.Directory); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ParamsIsPrecededByOptionalParametersAsync() + { + var sampleProgram = @" +using System.IO; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + void Fun1() + { + Fun2(); + {|#0:Fun2(args: """", i: 5)|}; + } + + void Fun2(int i = 0, params object[] args) + { + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ParamsParameterRule).WithLocation(0)); + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Calling_non_overridden_virtual_methods_on_value_typesAsync() + { + var source = @" +using System; +using Roslyn.Utilities; + +enum E { A } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + E.A.GetHashCode(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(12,9): warning HAA0102: Non-overridden virtual method call on a value type adds a boxing or constrained instruction +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(CallSiteImplicitAllocationAnalyzer.ValueTypeNonOverridenCallRule).WithLocation(12, 9)); +#pragma warning restore RS0030 // Do not use banned APIs + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ConcatenationAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ConcatenationAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..9553de2ab5d4b --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ConcatenationAllocationAnalyzerTests.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.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers; +using Test.Utilities; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.ConcatenationAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests +{ + public class ConcatenationAllocationAnalyzerTests + { + [Fact] + public async Task ConcatenationAllocation_Basic1Async() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + string s0 = ""hello"" + 0.ToString() + ""world"" + 1.ToString(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ConcatenationAllocation_Basic2Async() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + string s2 = ""ohell"" + 2.ToString() + ""world"" + 3.ToString() + 4.ToString(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,21): warning HAA0201: Considering using StringBuilder +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ConcatenationAllocationAnalyzer.StringConcatenationAllocationRule).WithLocation(9, 21)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Theory] + [InlineData("string s0 = nameof(System.String) + '-';")] + [InlineData("string s0 = nameof(System.String) + true;")] + [InlineData("string s0 = nameof(System.String) + new System.IntPtr();")] + [InlineData("string s0 = nameof(System.String) + new System.UIntPtr();")] + public async Task ConcatenationAllocation_DoNotWarnForOptimizedValueTypesAsync(string statement) + { + var source = $@"using System; +using Roslyn.Utilities; + +public class MyClass +{{ + [PerformanceSensitive(""uri"")] + public void Testing() + {{ + {statement} + }} +}}"; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory] + [InlineData(@"const string s0 = nameof(System.String) + ""."" + nameof(System.String);")] + [InlineData(@"const string s0 = nameof(System.String) + ""."";")] + [InlineData(@"string s0 = nameof(System.String) + ""."" + nameof(System.String);")] + [InlineData(@"string s0 = nameof(System.String) + ""."";")] + public async Task ConcatenationAllocation_DoNotWarnForConstAsync(string statement) + { + var source = $@"using System; +using Roslyn.Utilities; + +public class MyClass +{{ + [PerformanceSensitive(""uri"")] + public void Testing() + {{ + {statement} + }} +}}"; + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Non_constant_value_types_in_CSharp_string_concatenationAsync() + { + var source = @" +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + System.DateTime c = System.DateTime.Now; + string s1 = ""char value will box"" + c; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(11,45): warning HAA0202: Value type (System.DateTime) is being boxed to a reference type for a string concatenation. +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ConcatenationAllocationAnalyzer.ValueTypeToReferenceTypeInAStringConcatenationRule).WithLocation(11, 45).WithArguments("System.DateTime")); +#pragma warning restore RS0030 // Do not use banned APIs + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/DisplayClassAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/DisplayClassAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..6e43176009d5f --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/DisplayClassAllocationAnalyzerTests.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.DisplayClassAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitive.Analyzers.UnitTests +{ + public class DisplayClassAllocationAnalyzerTests + { + [Fact] + public async Task DisplayClassAllocation_AnonymousMethodExpressionSyntaxAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +class Test +{ + static void Main() + { + Action action = CreateAction(5); + } + + [PerformanceSensitive(""uri"")] + static Action CreateAction(T item) + { + T test = default(T); + int counter = 0; + return delegate + { + counter++; + Console.WriteLine(""counter={0}"", counter); + }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(15,13): warning HAA0302: The compiler will emit a class that will hold this as a field to allow capturing of this closure +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureCaptureRule).WithLocation(15, 13), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(16,16): warning HAA0303: Considering moving this out of the generic method +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.LambaOrAnonymousMethodInGenericMethodRule).WithLocation(16, 16), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(16,16): warning HAA0301: Heap allocation of closure Captures: counter +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureDriverRule).WithLocation(16, 16).WithArguments("counter")); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task DisplayClassAllocation_SimpleLambdaExpressionSyntaxAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System; +using System.Linq; +using Roslyn.Utilities; + +public class Testing +{ + [PerformanceSensitive(""uri"")] + public Testing() + { + int[] intData = new[] { 123, 32, 4 }; + int min = 31; + var results = intData.Where(i => i > min).ToList(); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(12,13): warning HAA0302: The compiler will emit a class that will hold this as a field to allow capturing of this closure +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureCaptureRule).WithLocation(12, 13), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(13,39): warning HAA0301: Heap allocation of closure Captures: min +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureDriverRule).WithLocation(13, 39).WithArguments("min")); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task DisplayClassAllocation_ParenthesizedLambdaExpressionSyntaxAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System; +using System.Linq; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + var words = new[] { ""aaaa"", ""bbbb"", ""cccc"", ""ddd"" }; + var actions = new List(); + foreach (string word in words) // <-- captured closure + { + actions.Add(() => Console.WriteLine(word)); // <-- reason for closure capture + } + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(13,25): warning HAA0302: The compiler will emit a class that will hold this as a field to allow capturing of this closure +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureCaptureRule).WithLocation(13, 25), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(15,28): warning HAA0301: Heap allocation of closure Captures: word +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureDriverRule).WithLocation(15, 28).WithArguments("word")); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task DisplayClassAllocation_DoNotReportForNonCapturingAnonymousMethodAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Sorter(int[] arr) + { + System.Array.Sort(arr, delegate(int x, int y) { return x - y; }); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task DisplayClassAllocation_DoNotReportForNonCapturingLambdaAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Sorter(int[] arr) + { + System.Array.Sort(arr, (x, y) => x - y); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task DisplayClassAllocation_ReportForCapturingAnonymousMethodAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Sorter(int[] arr) + { + int z = 2; + System.Array.Sort(arr, delegate(int x, int y) { return x - z; }); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,13): warning HAA0302: The compiler will emit a class that will hold this as a field to allow capturing of this closure +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureCaptureRule).WithLocation(9, 13), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(10,32): warning HAA0301: Heap allocation of closure Captures: z +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(DisplayClassAllocationAnalyzer.ClosureDriverRule).WithLocation(10, 32).WithArguments("z")); +#pragma warning restore RS0030 // Do not use banned APIs + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/EnumeratorAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/EnumeratorAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..d248a59333474 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/EnumeratorAllocationAnalyzerTests.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.EnumeratorAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitive.Analyzers.UnitTests +{ + public class EnumeratorAllocationAnalyzerTests + { + [Fact] + public async Task EnumeratorAllocation_BasicAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System; +using System.Linq; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + int[] intData = new[] { 123, 32, 4 }; + IList iListData = new[] { 123, 32, 4 }; + List listData = new[] { 123, 32, 4 }.ToList(); + + foreach (var i in intData) + { + Console.WriteLine(i); + } + + foreach (var i in listData) + { + Console.WriteLine(i); + } + + foreach (var i in iListData) // Allocations (line 19) + { + Console.WriteLine(i); + } + + foreach (var i in (IEnumerable)intData) // Allocations (line 24) + { + Console.WriteLine(i); + } + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(25,24): warning HAA0401: Non-ValueType enumerator may result in a heap allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(25, 24), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(30,24): warning HAA0401: Non-ValueType enumerator may result in a heap allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(30, 24)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task EnumeratorAllocation_AdvancedAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + // These next 3 are from the YouTube video + foreach (object a in new[] { 1, 2, 3}) // Allocations 'new [] { 1. 2, 3}' + { + Console.WriteLine(a.ToString()); + } + + IEnumerable fx1 = default(IEnumerable); + foreach (var f in fx1) // Allocations 'in' + { + } + + List fx2 = default(List); + foreach (var f in fx2) // NO Allocations + { + } + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(17,24): warning HAA0401: Non-ValueType enumerator may result in a heap allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(17, 24)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task EnumeratorAllocation_Via_InvocationExpressionSyntaxAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System.Collections; +using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + var enumeratorRaw = GetIEnumerableRaw(); + while (enumeratorRaw.MoveNext()) + { + Console.WriteLine(enumeratorRaw.Current.ToString()); + } + + var enumeratorRawViaIEnumerable = GetIEnumeratorViaIEnumerable(); + while (enumeratorRawViaIEnumerable.MoveNext()) + { + Console.WriteLine(enumeratorRawViaIEnumerable.Current.ToString()); + } + } + + private IEnumerator GetIEnumerableRaw() + { + return new[] { 123, 32, 4 }.GetEnumerator(); + } + + private IEnumerator GetIEnumeratorViaIEnumerable() + { + int[] intData = new[] { 123, 32, 4 }; + return (IEnumerator)intData.GetEnumerator(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(17,43): warning HAA0401: Non-ValueType enumerator may result in a heap allocation +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic().WithLocation(17, 43)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task EnumeratorAllocation_IterateOverString_NoWarningAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + foreach (char c in ""aaa"") { }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ExplicitAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ExplicitAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..840d31534ffd5 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/ExplicitAllocationAnalyzerTests.cs @@ -0,0 +1,866 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers; +using Test.Utilities; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.ExplicitAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.VisualBasicPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.ExplicitAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitive.Analyzers.UnitTests +{ + public class ExplicitAllocationAnalyzerTests + { + [Fact] + public async Task ExplicitAllocation_ObjectInitializerAsync() + { + var sampleProgram = + @"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @class = new TestClass { Name = ""Bob"" }; + } +} + +public class TestClass +{ + public string Name { get; set; } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(9, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ObjectInitializer_VisualBasicAsync() + { + var code = + @"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim instance = New TestClass With {.Name = ""Bob""} + End Sub +End Class + +Public Class TestClass + Public Property Name As String +End Class"; + + await VerifyVB.VerifyAnalyzerAsync( + code, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 24)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ObjectInitializerStruct_NoWarningAsync() + { + var sampleProgram = + @"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @struct = new TestStruct { Name = ""Bob"" }; + } +} + +public struct TestStruct +{ + public string Name { get; set; } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ObjectInitializerStruct_NoWarning_VisualBasicAsync() + { + var code = + @"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim instance = New TestClass With {.Name = ""Bob""} + End Sub +End Class + +Public Structure TestClass + Public Property Name As String +End Structure"; + + await VerifyVB.VerifyAnalyzerAsync(code); + } + + [Fact] + public async Task ExplicitAllocation_ImplicitArrayCreationAsync() + { + var sampleProgram = + @"using System.Collections.Generic; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + int[] intData = new[] { 123, 32, 4 }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(9, 25)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ImplicitArrayCreation_VisualBasicAsync() + { + var sampleProgram = + @"Imports System.Collections.Generic +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim intData() As Integer = {123, 32, 4} + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(7, 36)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_AnonymousObjectCreationAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var temp = new { A = 123, Name = ""Test"", }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.AnonymousObjectCreationRule).WithLocation(9, 20)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_AnonymousObjectCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim temp = New With {Key .B = 123, .Name = ""Test""} + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.AnonymousObjectCreationRule).WithLocation(7, 20)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ArrayCreationAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + int[] intData = new int[] { 123, 32, 4 }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(9, 25)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ArrayCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System.Collections.Generic +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim intData = New Integer() {123, 32, 4} + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(7, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ObjectCreationAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var allocation = new String('a', 10); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(9, 26)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ObjectCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim allocation = New String(""a""c, 10) + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 26)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_LetClauseAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using System.Linq; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + int[] intData = new[] { 123, 32, 4 }; + var result = (from a in intData + let b = a * 3 + select b).ToList(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(10, 25), +#pragma warning restore RS0030 // Do not use banned APIs +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.LetCauseRule).WithLocation(12, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_LetClause_VisualBasicAsync() + { + var sampleProgram = +@"Imports System.Collections.Generic +Imports System.Linq +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim intData() As Integer = {123, 32, 4} + Dim result = (From x In intData + Let b = x * 3 + Select b).ToList() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ArrayCreationRule).WithLocation(8, 36), +#pragma warning restore RS0030 // Do not use banned APIs +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.LetCauseRule).WithLocation(10, 27)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_to_System_Object_typeAsync() + { + var source = @" +using Roslyn.Utilities; + +public struct S { } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + object box = new S(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(11, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_to_System_Object_type_VisualBasicAsync() + { + var source = @" +Imports Roslyn.Utilities + +Public Structure S +End Structure + +Public Class A + + Public Sub SomeMethod() + Dim box As Object = new S() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(10, 29)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_to_System_ValueType_typeAsync() + { + var source = @" +using Roslyn.Utilities; + +public struct S { } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + System.ValueType box = new S(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(11, 32)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_to_System_ValueType_type_VisualBasicAsync() + { + var source = @" +Imports Roslyn.Utilities + +Public Structure S +End Structure + +Public Class A + + Public Sub SomeMethod() + Dim box As System.ValueType = new S() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(10, 39)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_into_interface_referenceAsync() + { + var source = @" +using Roslyn.Utilities; + +interface I { } + +public struct S : I { } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + I box = new S(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(13, 17)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_value_type_into_interface_reference_VisualBasicAsync() + { + var source = @" +Imports Roslyn.Utilities + +Interface I +End Interface + +Public Structure S + Implements I +End Structure + +Public Class A + + Public Sub SomeMethod() + Dim box As I = new S() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(source, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(14, 24)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_StructCreation_NoWarningAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public struct S { } + +public class MyClass +{ + + [PerformanceSensitive(""uri"")] + public void Testing() + { + var noBox1 = new DateTime(); + S noBox2 = new S(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_StructCreation_NoWarning_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Structure S +End Structure + +Public Class A + + Public Sub Testing() + Dim noBox1 = new DateTime() + Dim noBox2 As S = new S() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_PrimitiveTypeConversion_NoWarningAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + double x = new int(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_PrimitiveTypeConversion_NoWarning_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim x As Double = New Integer() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ImplicitValueTypeConversion_NoWarningAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +struct A +{ + public static implicit operator A(B other) + { + return new A(); + } +} + +struct B +{ +} + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + A a = new B(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ImplicitValueTypeConversion_NoWarning_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Structure A + Public Shared Widening Operator CType(other As B) As A + Return New A() + End Operator +End Structure + +Structure B +End Structure + +Public Class C + + Public Sub Testing() + Dim a As A = New B() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_NoParamsArrayCreationAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing(params int[] values) + { + Testing(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_NoParamsArrayCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System.Collections.Generic +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(ParamArray values() As Integer) + Testing() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ExplicitDelegateCreationAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing(object sender, EventArgs e) + { + var handler = new EventHandler(Testing); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(9, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ExplicitDelegateCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(sender As Object, e As EventArgs) + Dim handler = new EventHandler(AddressOf Testing) + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ImplicitDelegateCreationAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing(object sender, EventArgs e) + { + EventHandler handler = Testing; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ImplicitDelegateCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(sender As Object, e As EventArgs) + Dim handler As EventHandler = AddressOf Testing + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_ListInitializerCreationAsync() + { + var sampleProgram = +@"using System.Collections.Generic; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var intData = new List { 3, 4 }; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(9, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_ListInitializerCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System.Collections.Generic +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing() + Dim intData = New List(Of Integer) From {3, 4} + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreationAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + where T : class, new() + { + var allocation = new T(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(10, 26)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreation_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(Of T As {Class, New})() + Dim allocation = New T() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 26)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreation2Async() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + where T : struct + { + object allocation = new T(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(10, 29)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreation2_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(Of T As Structure)() + Dim allocation As Object = New T() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram, +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(ExplicitAllocationAnalyzer.ObjectCreationRule).WithLocation(7, 36)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreation3Async() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + where T : struct + { + T value = new T(); + } +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task ExplicitAllocation_GenericObjectCreation3_VisualBasicAsync() + { + var sampleProgram = +@"Imports System +Imports Roslyn.Utilities + +Public Class A + + Public Sub Testing(Of T As Structure)() + Dim value As T = new T() + End Sub +End Class"; + await VerifyVB.VerifyAnalyzerAsync(sampleProgram); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.csproj b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.csproj new file mode 100644 index 0000000000000..3d20c3fae6ddc --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.csproj @@ -0,0 +1,28 @@ + + + + $(NetRoslyn) + true + $(NoWarn);xUnit1000;CA1812 + + + $(NoWarn);CA2007 + + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/TypeConversionAllocationAnalyzerTests.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/TypeConversionAllocationAnalyzerTests.cs new file mode 100644 index 0000000000000..b4a9794971548 --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/TypeConversionAllocationAnalyzerTests.cs @@ -0,0 +1,860 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers; +using Test.Utilities; +using Xunit; +using VerifyCS = Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests.CSharpPerformanceCodeFixVerifier< + Microsoft.CodeAnalysis.CSharp.PerformanceSensitiveAnalyzers.TypeConversionAllocationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Microsoft.CodeAnalysis.PerformanceSensitive.Analyzers.UnitTests +{ + public class TypeConversionAllocationAnalyzerTests + { + [Fact] + public async Task TypeConversionAllocation_ArgumentSyntaxAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +using Roslyn.Utilities; + +public class MyObject +{ + public MyObject(object obj) + { + } + + private void ObjCall(object obj) + { + } + + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + ObjCall(10); // Allocation + _ = new MyObject(10); // Allocation + } +}", +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(18, 17), +#pragma warning restore RS0030 // Do not use banned APIs +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(19, 26) +#pragma warning restore RS0030 // Do not use banned APIs + ); + } + + [Fact] + public async Task TypeConversionAllocation_ArgumentSyntax_WithDelegatesAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @class = new MyClass(); + @class.ProcessFunc(someObjCall); // implicit, so Allocation + @class.ProcessFunc(new Func(someObjCall)); // Explicit, so NO Allocation + } + + public void ProcessFunc(Func func) + { + } + + private string someObjCall(object obj) => null; +} + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @struct = new MyStruct(); + @struct.ProcessFunc(someObjCall); // implicit allocation + boxing + @struct.ProcessFunc(new Func(someObjCall)); // Explicit allocation + boxing + } + + public void ProcessFunc(Func func) + { + } + + private string someObjCall(object obj) => null; +}"; + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(10,28): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(10, 28, 10, 39), + // Test0.cs(27,29): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(27, 29, 27, 40), + // Test0.cs(27,29): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(27, 29, 27, 40), + // Test0.cs(28,54): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(28, 54, 28, 65)); + } + + [Fact] + public async Task TypeConversionAllocation_ReturnStatementSyntaxAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyObject +{ + public Object Obj1 + { + [PerformanceSensitive(""uri"")] + get { return 0; } + } + + [PerformanceSensitive(""uri"")] + public Object Obj2 + { + get { return 0; } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,22): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(9, 22), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(15,22): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(15, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ReturnStatementSyntax_NoAllocAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyObject +{ + [PerformanceSensitive(""uri"")] + public Object ObjNoAllocation1 { get { return 0.ToString(); } } + + public Object ObjNoAllocation2 + { + [PerformanceSensitive(""uri"")] + get { return 0.ToString(); } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram); + } + + [Fact] + public async Task TypeConversionAllocation_YieldStatementSyntaxAsync() + { + var sampleProgram = +@"using System; +using System.Collections.Generic; +using Roslyn.Utilities; + +public class MyClass +{ + public void SomeMethod() + { + foreach (var item in GetItems()) + { + } + + foreach (var item in GetItemsNoAllocation()) + { + } + } + + [PerformanceSensitive(""uri"")] + public IEnumerable GetItems() + { + yield return 0; // Allocation + yield break; + } + + [PerformanceSensitive(""uri"")] + public IEnumerable GetItemsNoAllocation() + { + yield return 0; // NO Allocation (IEnumerable) + yield break; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(21,22): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(21, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_BinaryExpressionSyntaxAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + object x = ""blah""; + object a1 = x ?? 0; // Allocation + object a2 = x ?? 0.ToString(); // No Allocation + + var b1 = 10 as object; // Allocation + var b2 = 10.ToString() as object; // No Allocation + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(10,26): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(10, 26), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(13,18): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(13, 18)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_BinaryExpressionSyntax_WithDelegatesAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Func temp = null; + var result1 = temp ?? someObjCall; // implicit, so Allocation + var result2 = temp ?? new Func(someObjCall); // Explicit, so NO Allocation + } + + private string someObjCall(object obj) + { + return obj.ToString(); + } +} + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Func temp = null; + var result1 = temp ?? someObjCall; // implicit allocation + boxing + var result2 = temp ?? new Func(someObjCall); // Explicit allocation + boxing + } + + private string someObjCall(object obj) + { + return obj.ToString(); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(10,31): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(10, 31, 10, 42), + // Test0.cs(26,31): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(26, 31, 26, 42), + // Test0.cs(26,31): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(26, 31, 26, 42), + // Test0.cs(27,56): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(27, 56, 27, 67)); + } + + [Fact] + public async Task TypeConversionAllocation_EqualsValueClauseSyntaxAsync() + { + // for (object i = 0;;) + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + for (object i = 0;;) // Allocation + { + } + + for (int i = 0;;) // NO Allocation + { + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,25): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(9, 25)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_EqualsValueClauseSyntax_WithDelegatesAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Func func2 = someObjCall; // implicit, so Allocation + Func func1 = new Func(someObjCall); // Explicit, so NO Allocation + } + + private string someObjCall(object obj) + { + return obj.ToString(); + } +} + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Func func2 = someObjCall; // implicit allocation + boxing + Func func1 = new Func(someObjCall); // Explicit allocation + boxing + } + + private string someObjCall(object obj) + { + return obj.ToString(); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,38): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(9, 38, 9, 49), + // Test0.cs(24,38): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(24, 38, 24, 49), + // Test0.cs(24,38): warning HAA0603: This will allocate a delegate instance + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithSpan(24, 38, 24, 49), + // Test0.cs(25,63): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(25, 63, 25, 74)); + } + + [Fact] + [WorkItem(2, "https://github.com/mjsabby/RoslynClrHeapAllocationAnalyzer/issues/2")] + public async Task TypeConversionAllocation_EqualsValueClause_ExplicitMethodGroupAllocation_BugAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Action methodGroup = this.Method; + } + + private void Method() + { + } +} + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + Action methodGroup = this.Method; + } + + private void Method() + { + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,30): warning HAA0603: This will allocate a delegate instance +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithLocation(9, 30), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(22,30): warning HAA0603: This will allocate a delegate instance +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithLocation(22, 30), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(22,30): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithLocation(22, 30)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ConditionalExpressionSyntaxAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + object obj = ""test""; + object test1 = true ? 0 : obj; // Allocation + object test2 = true ? 0.ToString() : obj; // NO Allocation + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(10,31): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(10, 31)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_CastExpressionSyntaxAsync() + { + var sampleProgram = +@"using System; +using Roslyn.Utilities; + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var f1 = (object)5; // Allocation + var f2 = (object)""5""; // NO Allocation + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(sampleProgram, + // Test0.cs(9,26): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(9, 26)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ArgumentWithImplicitStringCastOperatorAsync() + { + const string programWithoutImplicitCastOperator = @" +using System; +using Roslyn.Utilities; + +public struct AStruct +{ + [PerformanceSensitive(""uri"")] + public static void Dump(AStruct astruct) + { + System.Console.WriteLine(astruct); + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(programWithoutImplicitCastOperator, + // Test0.cs(10,34): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(10, 34)); +#pragma warning restore RS0030 // Do not use banned APIs + + const string programWithImplicitCastOperator = @" +using System; +using Roslyn.Utilities; + +public struct AStruct +{ + public readonly string WrappedString; + + public AStruct(string s) + { + WrappedString = s ?? """"; + } + + [PerformanceSensitive(""uri"")] + public static void Dump(AStruct astruct) + { + System.Console.WriteLine(astruct); + } + + [PerformanceSensitive(""uri"")] + public static implicit operator string(AStruct astruct) + { + return astruct.WrappedString; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(programWithImplicitCastOperator); + } + + [Fact] + public async Task TypeConversionAllocation_YieldReturnImplicitStringCastOperatorAsync() + { + const string programWithoutImplicitCastOperator = @" +using System; +using Roslyn.Utilities; + +public struct AStruct +{ + [PerformanceSensitive(""uri"")] + public System.Collections.Generic.IEnumerator GetEnumerator() + { + yield return this; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(programWithoutImplicitCastOperator, + // Test0.cs(10,22): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(10, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + + const string programWithImplicitCastOperator = @" +using System; +using Roslyn.Utilities; + +public struct AStruct +{ + [PerformanceSensitive(""uri"")] + public System.Collections.Generic.IEnumerator GetEnumerator() + { + yield return this; + } + + public static implicit operator string(AStruct astruct) + { + return """"; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(programWithImplicitCastOperator); + } + + [Fact] + public async Task TypeConversionAllocation_InterpolatedStringWithInt_BoxingWarningAsync() + { + var source = @" +using System; +using Roslyn.Utilities; + +class Program +{ + [PerformanceSensitive(""uri"")] + void SomeMethod() + { + string s = $""{1}""; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(10,23): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(10, 23)); +#pragma warning restore RS0030 // Do not use banned APIs + } + +#if false + [Fact] + public void TypeConversionAllocation_InterpolatedStringWithString_NoWarning() + { + var sampleProgram = @"string s = $""{1.ToString()}"";"; + + var analyser = new TypeConversionAllocationAnalyzer(); + var info = ProcessCode(analyser, sampleProgram, ImmutableArray.Create(SyntaxKind.Interpolation)); + + Assert.Empty(info.Allocations); + } +#endif + + [Theory] + [InlineData(@"private readonly System.Func fileExists = System.IO.File.Exists;")] + [InlineData(@"private System.Func fileExists { get; } = System.IO.File.Exists;")] + [InlineData(@"private static System.Func fileExists { get; } = System.IO.File.Exists;")] + [InlineData(@"private static readonly System.Func fileExists = System.IO.File.Exists;")] + public async Task TypeConversionAllocation_DelegateAssignmentToReadonly_DoNotWarnAsync(string snippet) + { + var source = $@" +using System; +using Roslyn.Utilities; + +class Program +{{ + [PerformanceSensitive(""uri"")] + {snippet} +}}"; + + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(8,68): info HeapAnalyzerReadonlyMethodGroupAllocationRule: This will allocate a delegate instance +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ReadonlyMethodGroupAllocationRule).WithLocation(8, 68)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ExpressionBodiedPropertyBoxing_WithBoxingAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +class Program +{ + [PerformanceSensitive(""uri"")] + object Obj => 1; +}"; + + await VerifyCS.VerifyAnalyzerAsync(snippet, + // Test0.cs(8,19): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(8, 19)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ExpressionBodiedPropertyBoxing_WithoutBoxingAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +class Program +{ + [PerformanceSensitive(""uri"")] + object Obj => 1.ToString(); +}"; + + await VerifyCS.VerifyAnalyzerAsync(snippet); + } + + [Fact] + public async Task TypeConversionAllocation_ExpressionBodiedPropertyDelegateAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +class Program +{ + void Function(int i) { } + + [PerformanceSensitive(""uri"")] + Action Obj => Function; +}"; + + await VerifyCS.VerifyAnalyzerAsync(snippet, + // Test0.cs(10,24): warning HAA0603: This will allocate a delegate instance +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithLocation(10, 24)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_ExpressionBodiedPropertyExplicitDelegate_NoWarningAsync() + { + // Tests that an explicit delegate creation does not trigger HAA0603. It should be handled by HAA0502. + const string snippet = @" +using System; +using Roslyn.Utilities; + +class Program +{ + void Function(int i) { } + + [PerformanceSensitive(""uri"")] + Action Obj => new Action(Function); +}"; + + await VerifyCS.VerifyAnalyzerAsync(snippet); + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Converting_any_enumeration_type_to_System_Enum_typeAsync() + { + var source = @" +using Roslyn.Utilities; + +enum E { A } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + System.Enum box = E.A; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(11,27): warning HAA0601: Value type to reference type conversion causes boxing at call site (here), and unboxing at the callee-site. Consider using generics if applicable +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.ValueTypeToReferenceTypeConversionRule).WithLocation(11, 27)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + [WorkItem(7995606, "http://stackoverflow.com/questions/7995606/boxing-occurrence-in-c-sharp")] + public async Task Creating_delegate_from_value_type_instance_methodAsync() + { + var source = @" +using System; +using Roslyn.Utilities; + +struct S { public void M() {} } + +public class MyClass +{ + [PerformanceSensitive(""uri"")] + public void SomeMethod() + { + Action box = new S().M; + } +}"; + await VerifyCS.VerifyAnalyzerAsync(source, + // Test0.cs(12,22): warning HAA0603: This will allocate a delegate instance +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.MethodGroupAllocationRule).WithLocation(12, 22), +#pragma warning restore RS0030 // Do not use banned APIs + // Test0.cs(12,22): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithLocation(12, 22)); +#pragma warning restore RS0030 // Do not use banned APIs + } + + [Fact] + public async Task TypeConversionAllocation_NoDiagnosticWhenPassingDelegateAsArgumentAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +struct Foo +{ + [PerformanceSensitive(""uri"")] + void Do(Action process) + { + DoMore(process); + } + + void DoMore(Action process) + { + process(); + } +} + "; + await VerifyCS.VerifyAnalyzerAsync(snippet); + } + + [Fact] + public async Task TypeConversionAllocation_ReportBoxingAllocationForPassingStructInstanceMethodForDelegateConstructorAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @struct = new MyStruct(); + @struct.ProcessFunc(new Func(FooObjCall)); + } + + public void ProcessFunc(Func func) + { + } + + private string FooObjCall(object obj) + { + return obj.ToString(); + } +} + "; + + await VerifyCS.VerifyAnalyzerAsync(snippet, + // Test0.cs(11,54): warning HAA0602: Struct instance method being used for delegate creation, this will result in a boxing instruction + VerifyCS.Diagnostic(TypeConversionAllocationAnalyzer.DelegateOnStructInstanceRule).WithSpan(11, 54, 11, 64)); + } + + [Fact] + public async Task TypeConversionAllocation_DoNotReportBoxingAllocationForPassingStructStaticMethodForDelegateConstructorAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var @struct = new MyStruct(); + @struct.ProcessFunc(new Func(FooObjCall)); + } + + public void ProcessFunc(Func func) + { + } + + private static string FooObjCall(object obj) + { + return obj.ToString(); + } +} + "; + + await VerifyCS.VerifyAnalyzerAsync(snippet); + } + + [Fact] + public async Task TypeConversionAllocation_DoNotReportInlineDelegateAsStructInstanceMethodsAsync() + { + const string snippet = @" +using System; +using Roslyn.Utilities; + +public struct MyStruct +{ + [PerformanceSensitive(""uri"")] + public void Testing() + { + var ints = new[] { 5, 4, 3, 2, 1 }; + Array.Sort(ints, delegate(int x, int y) { return x - y; }); + Array.Sort(ints, (x, y) => x - y); + DoSomething(() => throw new Exception()); + DoSomething(delegate() { throw new Exception(); }); + + DoSomething2(x => throw new Exception()); + } + + private static void DoSomething(Action action) + { + } + + private static void DoSomething2(Action action) + { + } +} + "; + + await VerifyCS.VerifyAnalyzerAsync(snippet); + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..e68c95159aa2d --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2+Test.cs @@ -0,0 +1,20 @@ +// 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.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests +{ + public static partial class VisualBasicPerformanceCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + internal sealed class Test : VisualBasicCodeFixTest + { + } + } +} diff --git a/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2.cs b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..e13b2a47d0c1e --- /dev/null +++ b/src/RoslynAnalyzers/PerformanceSensitiveAnalyzers/UnitTests/VisualBasicPerformanceCodeFixVerifier`2.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.UnitTests +{ + public static partial class VisualBasicPerformanceCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + private const string PerformanceSensitiveAttributeSource = @" +Imports System +Imports System.Collections.Generic +Imports System.Diagnostics +Imports System.Threading.Tasks + +Namespace Global.Roslyn.Utilities + + Friend NotInheritable Class PerformanceSensitiveAttribute + Inherits Attribute + + Public Sub New(uri As String) + Me.Uri = uri + End Sub + + Public ReadOnly Property Uri As String + Public Property Constraint As String + Public Property AllowCaptures As Boolean + Public Property AllowGenericEnumeration As Boolean + Public Property AllowLocks As Boolean + Public Property OftenCompletesSynchronously As Boolean + Public Property IsParallelEntry As Boolean + End Class +End Namespace +"; + + public static DiagnosticResult Diagnostic() + => VisualBasicCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestState = + { + Sources = + { + source, + ("PerformanceSensitiveAttribute.vb", PerformanceSensitiveAttributeSource) + }, + }, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + public static Task VerifyCodeFixAsync(string source, string fixedSource) + => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestState = + { + Sources = + { + source, + ("PerformanceSensitiveAttribute.vb", PerformanceSensitiveAttributeSource) + }, + }, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..44310606648e4 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Shipped.md @@ -0,0 +1,50 @@ +## Release 2.9.8 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS0016 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0017 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0022 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0024 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0025 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0026 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md) | +| RS0027 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md) | + +## Release 3.3.0 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS0036 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0037 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0041 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0048 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | + +## Release 3.3.3 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS0050 | ApiDesign | Warning | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | + +## Release 3.3.4 + +### New Rules + +| Rule ID | Category | Severity | Notes | +| ------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| RS0051 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0052 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0053 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0054 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0055 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0056 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0057 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0058 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | +| RS0059 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md) | +| RS0060 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md) | +| RS0061 | ApiDesign | Disabled | DeclarePublicApiAnalyzer, [Documentation](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) | diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs new file mode 100644 index 0000000000000..42b49434fba8a --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.Impl.cs @@ -0,0 +1,1061 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.Lightup; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + public partial class DeclarePublicApiAnalyzer : DiagnosticAnalyzer + { + private sealed record AdditionalFileInfo(SourceText SourceText, bool IsShippedApi) + { + public string GetPath(ImmutableDictionary additionalFiles) + { + foreach (var (additionalText, sourceText) in additionalFiles) + { + if (SourceText == sourceText) + return additionalText.Path; + } + + throw new InvalidOperationException(); + } + } + + private readonly record struct ApiLine(string Text, TextSpan Span, AdditionalFileInfo FileInfo) + { + public bool IsDefault => FileInfo == null; + + public SourceText SourceText => FileInfo.SourceText; + public bool IsShippedApi => FileInfo.IsShippedApi; + + public string GetPath(ImmutableDictionary additionalFiles) + => FileInfo.GetPath(additionalFiles); + + public Location GetLocation(ImmutableDictionary additionalFiles) + { + LinePositionSpan linePositionSpan = SourceText.Lines.GetLinePositionSpan(Span); + return Location.Create(GetPath(additionalFiles), Span, linePositionSpan); + } + } + + private readonly record struct RemovedApiLine(string Text, ApiLine ApiLine); + + private readonly record struct ApiName(string Name, string NameWithNullability); + + /// Number for the max line where #nullable enable was found (-1 otherwise) + private sealed record ApiData(ImmutableArray ApiList, ImmutableArray RemovedApiList, int NullableLineNumber) + { + public static readonly ApiData Empty = new(ImmutableArray.Empty, ImmutableArray.Empty, NullableLineNumber: -1); + } + + private sealed class Impl + { + private static readonly ImmutableArray s_ignorableMethodKinds + = ImmutableArray.Create(MethodKind.EventAdd, MethodKind.EventRemove); + + private static readonly SymbolDisplayFormat s_namespaceFormat = new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces); + + private readonly Compilation _compilation; + private readonly ImmutableDictionary _additionalFiles; + private readonly ApiData _unshippedData; + private readonly bool _useNullability; + private readonly bool _isPublic; + private readonly ConcurrentDictionary<(ITypeSymbol Type, bool IsPublic), bool> _typeCanBeExtendedCache = new(); + private readonly ConcurrentDictionary _visitedApiList = new(StringComparer.Ordinal); + private readonly ConcurrentDictionary> _skippedNamespacesCache = new(); + private readonly Lazy> _apiMap; + private readonly AnalyzerOptions _analyzerOptions; + + internal Impl(Compilation compilation, ImmutableDictionary additionalFiles, ApiData shippedData, ApiData unshippedData, bool isPublic, AnalyzerOptions analyzerOptions) + { + _compilation = compilation; + _additionalFiles = additionalFiles; + _useNullability = shippedData.NullableLineNumber >= 0 || unshippedData.NullableLineNumber >= 0; + _unshippedData = unshippedData; + + _apiMap = new Lazy>(() => CreateApiMap(shippedData, unshippedData)); + _isPublic = isPublic; + _analyzerOptions = analyzerOptions; + + static IReadOnlyDictionary CreateApiMap(ApiData shippedData, ApiData unshippedData) + { + // Defer allocating/creating the apiMap until it's needed as there are many cases where it's never used + // and can be fairly large. + var publicApiMap = new Dictionary(shippedData.ApiList.Length + unshippedData.ApiList.Length, StringComparer.Ordinal); + foreach (ApiLine cur in shippedData.ApiList) + { + publicApiMap.Add(cur.Text, cur); + } + + foreach (ApiLine cur in unshippedData.ApiList) + { + publicApiMap.Add(cur.Text, cur); + } + + return publicApiMap; + } + } + + internal void OnSymbolAction(SymbolAnalysisContext symbolContext) + { + var obsoleteAttribute = symbolContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute); + OnSymbolActionCore(symbolContext.Symbol, symbolContext.ReportDiagnostic, obsoleteAttribute, symbolContext.CancellationToken); + } + + internal void OnPropertyAction(SymbolAnalysisContext symbolContext) + { + // If a property is non-implicit, but it's accessors *are* implicit, + // then we will not get called back for the accessor methods. Add + // those methods explicitly in this case. This happens, for example, + // in VB with properties like: + // + // public readonly property A as Integer + // + // In this case, the getter/setters are both implicit, and will not + // trigger the callback to analyze them. So we manually do it ourselves. + var property = (IPropertySymbol)symbolContext.Symbol; + if (!property.IsImplicitlyDeclared) + { + this.CheckPropertyAccessor(symbolContext, property.GetMethod); + this.CheckPropertyAccessor(symbolContext, property.SetMethod); + } + } + + private void CheckPropertyAccessor(SymbolAnalysisContext symbolContext, IMethodSymbol accessor) + { + if (accessor == null) + { + return; + } + + // Only process implicit accessors. We won't get callbacks for them + // normally with RegisterSymbolAction. + if (!accessor.IsImplicitlyDeclared) + { + return; + } + + if (!this.IsTrackedAPI(accessor, symbolContext.CancellationToken)) + { + return; + } + + var obsoleteAttribute = symbolContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute); + this.OnSymbolActionCore(accessor, symbolContext.ReportDiagnostic, isImplicitlyDeclaredConstructor: false, obsoleteAttribute, symbolContext.CancellationToken); + } + + /// The symbol to analyze. Will also analyze implicit constructors too. + /// Action called to actually report a diagnostic. + /// A location to report the diagnostics for a symbol at. If null, then + /// the location of the symbol will be used. + private void OnSymbolActionCore(ISymbol symbol, Action reportDiagnostic, INamedTypeSymbol? obsoleteAttribute, CancellationToken cancellationToken, Location? explicitLocation = null) + { + if (!IsTrackedAPI(symbol, cancellationToken)) + { + return; + } + + Debug.Assert(!symbol.IsImplicitlyDeclared); + OnSymbolActionCore(symbol, reportDiagnostic, isImplicitlyDeclaredConstructor: false, obsoleteAttribute, cancellationToken, explicitLocation: explicitLocation); + + // Handle implicitly declared public constructors. + if (symbol is INamedTypeSymbol namedType) + { + IMethodSymbol? implicitConstructor = null; + if (namedType is { TypeKind: TypeKind.Class, InstanceConstructors.Length: 1 } or { TypeKind: TypeKind.Struct }) + { + implicitConstructor = namedType.InstanceConstructors.FirstOrDefault(x => x.IsImplicitlyDeclared); + if (implicitConstructor != null) + OnSymbolActionCore(implicitConstructor, reportDiagnostic, isImplicitlyDeclaredConstructor: true, obsoleteAttribute, cancellationToken, explicitLocation: explicitLocation); + } + + // Ensure that any implicitly declared members of a record are emitted as well. + foreach (var member in namedType.GetMembers()) + { + // Handled above. + if (member.Equals(implicitConstructor)) + continue; + + if (IsTrackedAPI(member, cancellationToken) && member is IMethodSymbol { IsImplicitlyDeclared: true } method) + { + // Record property accessors (for `record X(int P)`) are considered implicitly declared. + // However, we still handle the normal property symbol for that through our standard symbol + // callbacks. So we don't need to process those here. + // + // We do, however, need to process any implicit accessors for *implicit* properties. For + // example, for the implicit `virtual Type EqualityContract { get; }` member + if (method.MethodKind is not (MethodKind.PropertyGet or MethodKind.PropertySet) || + method is { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet, AssociatedSymbol.IsImplicitlyDeclared: true }) + { + OnSymbolActionCore(member, reportDiagnostic, isImplicitlyDeclaredConstructor: false, obsoleteAttribute, cancellationToken, explicitLocation: explicitLocation); + } + } + } + } + } + + private static string WithObliviousMarker(string name) + { + return ObliviousMarker + name; + } + + /// The symbol to analyze. + /// Action called to actually report a diagnostic. + /// If the symbol is an implicitly declared constructor. + /// A location to report the diagnostics for a symbol at. If null, then + /// the location of the symbol will be used. + private void OnSymbolActionCore(ISymbol symbol, Action reportDiagnostic, bool isImplicitlyDeclaredConstructor, INamedTypeSymbol? obsoleteAttribute, CancellationToken cancellationToken, Location? explicitLocation = null) + { + Debug.Assert(IsTrackedAPI(symbol, cancellationToken)); + + ApiName publicApiName = GetApiName(symbol); + _visitedApiList.TryAdd(publicApiName.Name, default); + _visitedApiList.TryAdd(WithObliviousMarker(publicApiName.Name), default); + _visitedApiList.TryAdd(publicApiName.NameWithNullability, default); + _visitedApiList.TryAdd(WithObliviousMarker(publicApiName.NameWithNullability), default); + + List locationsToReport = new List(); + IReadOnlyDictionary apiMap = _apiMap.Value; + + if (explicitLocation != null) + { + locationsToReport.Add(explicitLocation); + } + else + { + var locations = isImplicitlyDeclaredConstructor ? symbol.ContainingType.Locations : symbol.Locations; + locationsToReport.AddRange(locations.Where(l => l.IsInSource)); + } + + ApiLine foundApiLine; + bool symbolUsesOblivious = false; + if (_useNullability) + { + symbolUsesOblivious = UsesOblivious(symbol); + if (symbolUsesOblivious && !symbol.IsImplicitlyDeclared) + { + reportObliviousApi(symbol); + } + + var hasApiEntryWithNullability = apiMap.TryGetValue(publicApiName.NameWithNullability, out foundApiLine); + + var hasApiEntryWithNullabilityAndOblivious = + !hasApiEntryWithNullability && + symbolUsesOblivious && + apiMap.TryGetValue(WithObliviousMarker(publicApiName.NameWithNullability), out foundApiLine); + + if (!hasApiEntryWithNullability && !hasApiEntryWithNullabilityAndOblivious) + { + var hasApiEntryWithoutNullability = apiMap.TryGetValue(publicApiName.Name, out foundApiLine); + + var hasApiEntryWithoutNullabilityButOblivious = + !hasApiEntryWithoutNullability && + apiMap.TryGetValue(WithObliviousMarker(publicApiName.Name), out foundApiLine); + + if (!hasApiEntryWithoutNullability && !hasApiEntryWithoutNullabilityButOblivious) + { + reportDeclareNewApi(symbol, isImplicitlyDeclaredConstructor, withObliviousIfNeeded(publicApiName.NameWithNullability)); + } + else + { + reportAnnotateApi(symbol, isImplicitlyDeclaredConstructor, publicApiName, foundApiLine.IsShippedApi, foundApiLine.GetPath(_additionalFiles)); + } + } + else if (hasApiEntryWithNullability && symbolUsesOblivious) + { + reportAnnotateApi(symbol, isImplicitlyDeclaredConstructor, publicApiName, foundApiLine.IsShippedApi, foundApiLine.GetPath(_additionalFiles)); + } + } + else + { + var hasApiEntryWithoutNullability = apiMap.TryGetValue(publicApiName.Name, out foundApiLine); + if (!hasApiEntryWithoutNullability) + { + reportDeclareNewApi(symbol, isImplicitlyDeclaredConstructor, publicApiName.Name); + } + + if (publicApiName.Name != publicApiName.NameWithNullability) + { + // '#nullable enable' would be useful and should be set + reportDiagnosticAtLocations(GetDiagnostic(ShouldAnnotatePublicApiFilesRule, ShouldAnnotateInternalApiFilesRule), ImmutableDictionary.Empty); + } + } + + if (symbol.Kind == SymbolKind.Method) + { + var method = (IMethodSymbol)symbol; + var isMethodShippedApi = !foundApiLine.IsDefault && foundApiLine.IsShippedApi; + + // Check if a public API is a constructor that makes this class instantiable, even though the base class + // is not instantiable. That API pattern is not allowed, because it causes protected members of + // the base class, which are not considered public APIs, to be exposed to subclasses of this class. + if (!isMethodShippedApi && + method.MethodKind == MethodKind.Constructor && + method.ContainingType.TypeKind == TypeKind.Class && + !method.ContainingType.IsSealed && + method.ContainingType.BaseType != null && + IsTrackedApiCore(method.ContainingType.BaseType, cancellationToken) && + !CanTypeBeExtended(method.ContainingType.BaseType)) + { + string errorMessageName = GetErrorMessageName(method, isImplicitlyDeclaredConstructor); + ImmutableDictionary propertyBag = ImmutableDictionary.Empty; + var locations = isImplicitlyDeclaredConstructor ? method.ContainingType.Locations : method.Locations; + reportDiagnostic(Diagnostic.Create(GetDiagnostic(ExposedNoninstantiableTypePublic, ExposedNoninstantiableTypeInternal), locations[0], propertyBag, errorMessageName)); + } + + // Flag public API with optional parameters that violate backcompat requirements: https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md. + if (method.HasOptionalParameters()) + { + foreach (var overload in method.GetOverloads()) + { + var symbolAccessibility = overload.GetResultantVisibility(); + var minAccessibility = _isPublic ? SymbolVisibility.Public : SymbolVisibility.Internal; + if (symbolAccessibility > minAccessibility) + { + continue; + } + + // Don't flag overloads which have identical params (e.g. overloading a generic and non-generic method with same parameter types). + if (overload.Parameters.Length == method.Parameters.Length && + overload.Parameters.Select(p => p.Type).SequenceEqual(method.Parameters.Select(p => p.Type))) + { + continue; + } + + // Don't flag obsolete overloads + if (overload.HasAnyAttribute(obsoleteAttribute)) + { + continue; + } + + // RS0026: Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + var overloadHasOptionalParams = overload.HasOptionalParameters(); + // Flag only if 'method' is a new unshipped API with optional parameters. + if (overloadHasOptionalParams && !isMethodShippedApi) + { + string errorMessageName = GetErrorMessageName(method, isImplicitlyDeclaredConstructor); + var diagnostic = GetDiagnostic(AvoidMultipleOverloadsWithOptionalParametersPublic, AvoidMultipleOverloadsWithOptionalParametersInternal); + reportDiagnosticAtLocations(diagnostic, ImmutableDictionary.Empty, errorMessageName, diagnostic.HelpLinkUri); + break; + } + + // RS0027: Symbol '{0}' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + if (method.Parameters.Length <= overload.Parameters.Length) + { + // 'method' is unshipped: Flag regardless of whether the overload is shipped/unshipped. + // 'method' is shipped: Flag only if overload is unshipped and has no optional parameters (overload will already be flagged with RS0026) + if (!isMethodShippedApi) + { + string errorMessageName = GetErrorMessageName(method, isImplicitlyDeclaredConstructor); + var diagnostic = GetDiagnostic(OverloadWithOptionalParametersShouldHaveMostParametersPublic, OverloadWithOptionalParametersShouldHaveMostParametersInternal); + reportDiagnosticAtLocations(diagnostic, ImmutableDictionary.Empty, errorMessageName, diagnostic.HelpLinkUri); + break; + } + else if (!overloadHasOptionalParams) + { + var overloadPublicApiName = GetApiName(overload); + var isOverloadUnshipped = !lookupPublicApi(overloadPublicApiName, out ApiLine overloadPublicApiLine) || + !overloadPublicApiLine.IsShippedApi; + if (isOverloadUnshipped) + { + string errorMessageName = GetErrorMessageName(method, isImplicitlyDeclaredConstructor); + var diagnostic = GetDiagnostic(OverloadWithOptionalParametersShouldHaveMostParametersPublic, OverloadWithOptionalParametersShouldHaveMostParametersInternal); + reportDiagnosticAtLocations(diagnostic, ImmutableDictionary.Empty, errorMessageName, diagnostic.HelpLinkUri); + break; + } + } + } + } + } + } + + return; + + // local functions + void reportDiagnosticAtLocations(DiagnosticDescriptor descriptor, ImmutableDictionary propertyBag, params object[] args) + { + foreach (var location in locationsToReport) + { + reportDiagnostic(Diagnostic.Create(descriptor, location, propertyBag, args)); + } + } + + void reportDeclareNewApi(ISymbol symbol, bool isImplicitlyDeclaredConstructor, string publicApiName) + { + // TODO: workaround for https://github.com/dotnet/wpf/issues/2690 + if (publicApiName is "XamlGeneratedNamespace.GeneratedInternalTypeHelper" or + "XamlGeneratedNamespace.GeneratedInternalTypeHelper.GeneratedInternalTypeHelper() -> void") + { + return; + } + + // Unshipped public API with no entry in public API file - report diagnostic. + string errorMessageName = GetErrorMessageName(symbol, isImplicitlyDeclaredConstructor); + // Compute public API names for any stale siblings to remove from unshipped text (e.g. during signature change of unshipped public API). + var siblingPublicApiNamesToRemove = GetSiblingNamesToRemoveFromUnshippedText(symbol, cancellationToken); + ImmutableDictionary propertyBag = ImmutableDictionary.Empty + .Add(ApiNamePropertyBagKey, publicApiName) + .Add(MinimalNamePropertyBagKey, errorMessageName) + .Add(ApiNamesOfSiblingsToRemovePropertyBagKey, siblingPublicApiNamesToRemove); + + reportDiagnosticAtLocations(GetDiagnostic(DeclareNewPublicApiRule, DeclareNewInternalApiRule), propertyBag, publicApiName); + } + + void reportAnnotateApi(ISymbol symbol, bool isImplicitlyDeclaredConstructor, ApiName publicApiName, bool isShipped, string filename) + { + // Public API missing annotations in public API file - report diagnostic. + string errorMessageName = GetErrorMessageName(symbol, isImplicitlyDeclaredConstructor); + ImmutableDictionary propertyBag = ImmutableDictionary.Empty + .Add(ApiNamePropertyBagKey, publicApiName.Name) + .Add(ApiNameWithNullabilityPropertyBagKey, withObliviousIfNeeded(publicApiName.NameWithNullability)) + .Add(MinimalNamePropertyBagKey, errorMessageName) + .Add(ApiIsShippedPropertyBagKey, isShipped ? "true" : "false") + .Add(FileName, filename); + + reportDiagnosticAtLocations(GetDiagnostic(AnnotatePublicApiRule, AnnotateInternalApiRule), propertyBag, publicApiName.NameWithNullability); + } + + string withObliviousIfNeeded(string name) + { + return symbolUsesOblivious ? WithObliviousMarker(name) : name; + } + + void reportObliviousApi(ISymbol symbol) + { + // Public API using oblivious types in public API file - report diagnostic. + string errorMessageName = GetErrorMessageName(symbol, isImplicitlyDeclaredConstructor); + + reportDiagnosticAtLocations(GetDiagnostic(ObliviousPublicApiRule, ObliviousInternalApiRule), ImmutableDictionary.Empty, errorMessageName); + } + + bool lookupPublicApi(ApiName overloadPublicApiName, out ApiLine overloadPublicApiLine) + { + if (_useNullability) + { + return apiMap.TryGetValue(overloadPublicApiName.NameWithNullability, out overloadPublicApiLine) || + apiMap.TryGetValue(WithObliviousMarker(overloadPublicApiName.NameWithNullability), out overloadPublicApiLine) || + apiMap.TryGetValue(overloadPublicApiName.Name, out overloadPublicApiLine); + } + else + { + return apiMap.TryGetValue(overloadPublicApiName.Name, out overloadPublicApiLine); + } + } + } + + private static string GetErrorMessageName(ISymbol symbol, bool isImplicitlyDeclaredConstructor) + { + if (symbol.IsImplicitlyDeclared && + symbol is IMethodSymbol methodSymbol && + methodSymbol.AssociatedSymbol is IPropertySymbol property) + { + var formatString = symbol.Equals(property.GetMethod) + ? PublicApiAnalyzerResources.ImplicitGetAccessor + : PublicApiAnalyzerResources.ImplicitSetAccessor; + + return string.Format(CultureInfo.CurrentCulture, formatString, property.Name); + } + + return isImplicitlyDeclaredConstructor ? + string.Format(CultureInfo.CurrentCulture, PublicApiAnalyzerResources.ImplicitConstructorErrorMessageName, symbol.ContainingSymbol.ToDisplayString(ShortSymbolNameFormat)) : + symbol.ToDisplayString(ShortSymbolNameFormat); + } + + private string GetSiblingNamesToRemoveFromUnshippedText(ISymbol symbol, CancellationToken cancellationToken) + { + // Don't crash the analyzer if we are unable to determine stale entries to remove in public API text. + try + { + return GetSiblingNamesToRemoveFromUnshippedTextCore(symbol, cancellationToken); + } +#pragma warning disable CA1031 // Do not catch general exception types - https://github.com/dotnet/roslyn-analyzers/issues/2181 + catch (Exception ex) + { + Debug.Assert(false, ex.Message); + return string.Empty; + } +#pragma warning restore CA1031 // Do not catch general exception types + } + + private string GetSiblingNamesToRemoveFromUnshippedTextCore(ISymbol symbol, CancellationToken cancellationToken) + { + // Compute all sibling names that must be removed from unshipped text, as they are no longer public or have been changed. + if (symbol.ContainingSymbol is INamespaceOrTypeSymbol containingSymbol) + { + // First get the lines in the unshipped text for siblings of the symbol: + // (a) Contains API name of containing symbol. + // (b) Doesn't contain API name of nested types/namespaces of containing symbol. + var containingSymbolApiName = GetApiName(containingSymbol); + + var nestedNamespaceOrTypeMembers = containingSymbol.GetMembers().OfType().ToImmutableArray(); + var nestedNamespaceOrTypesApiNames = new List(nestedNamespaceOrTypeMembers.Length); + foreach (var nestedNamespaceOrType in nestedNamespaceOrTypeMembers) + { + var nestedNamespaceOrTypeApiName = GetApiName(nestedNamespaceOrType).Name; + nestedNamespaceOrTypesApiNames.Add(nestedNamespaceOrTypeApiName); + } + + var publicApiLinesForSiblingsOfSymbol = new HashSet(); + foreach (var apiLine in _unshippedData.ApiList) + { + var apiLineText = apiLine.Text; + if (apiLineText == containingSymbolApiName.Name) + { + // Not a sibling of symbol. + continue; + } + + if (!ContainsPublicApiName(apiLineText, containingSymbolApiName.Name + ".")) + { + // Doesn't contain containingSymbol public API name - not a sibling of symbol. + continue; + } + + var containedInNestedMember = false; + foreach (var nestedNamespaceOrTypePublicApiName in nestedNamespaceOrTypesApiNames) + { + if (ContainsPublicApiName(apiLineText, nestedNamespaceOrTypePublicApiName + ".")) + { + // Belongs to a nested type/namespace in containingSymbol - not a sibling of symbol. + containedInNestedMember = true; + break; + } + } + + if (containedInNestedMember) + { + continue; + } + + publicApiLinesForSiblingsOfSymbol.Add(apiLineText); + } + + // Now remove the lines for siblings which are still public APIs - we don't want to remove those. + if (publicApiLinesForSiblingsOfSymbol.Count > 0) + { + var siblings = containingSymbol.GetMembers(); + foreach (var sibling in siblings) + { + if (sibling.IsImplicitlyDeclared) + { + if (sibling is not IMethodSymbol { MethodKind: MethodKind.Constructor or MethodKind.PropertyGet or MethodKind.PropertySet }) + { + continue; + } + } + else if (!IsTrackedAPI(sibling, cancellationToken)) + { + continue; + } + + var siblingPublicApiName = GetApiName(sibling); + publicApiLinesForSiblingsOfSymbol.Remove(siblingPublicApiName.Name); + publicApiLinesForSiblingsOfSymbol.Remove(siblingPublicApiName.NameWithNullability); + publicApiLinesForSiblingsOfSymbol.Remove(WithObliviousMarker(siblingPublicApiName.NameWithNullability)); + } + + // Join all the symbols names with a special separator. + return string.Join(ApiNamesOfSiblingsToRemovePropertyBagValueSeparator, publicApiLinesForSiblingsOfSymbol); + } + } + + return string.Empty; + } + + private static bool UsesOblivious(ISymbol symbol) + { + if (symbol.Kind == SymbolKind.NamedType) + { + return ObliviousDetector.VisitNamedTypeDeclaration((INamedTypeSymbol)symbol); + } + + return ObliviousDetector.Instance.Visit(symbol); + } + + private ApiName GetApiName(ISymbol symbol) + { + var experimentName = getExperimentName(symbol); + + return new ApiName( + getApiString(_compilation, symbol, experimentName, s_publicApiFormat), + getApiString(_compilation, symbol, experimentName, s_publicApiFormatWithNullability)); + + static string? getExperimentName(ISymbol symbol) + { + for (var current = symbol; current is not null; current = current.ContainingSymbol) + { + foreach (var attribute in current.GetAttributes()) + { + if (attribute.AttributeClass is { Name: "ExperimentalAttribute", ContainingSymbol: INamespaceSymbol { Name: nameof(System.Diagnostics.CodeAnalysis), ContainingNamespace: { Name: nameof(System.Diagnostics), ContainingNamespace: { Name: nameof(System), ContainingNamespace.IsGlobalNamespace: true } } } }) + { + if (attribute.ConstructorArguments is not [{ Kind: TypedConstantKind.Primitive, Type.SpecialType: SpecialType.System_String, Value: string diagnosticId }]) + return "???"; + + return diagnosticId; + + } + } + } + + return null; + } + + static string getApiString(Compilation compilation, ISymbol symbol, string? experimentName, SymbolDisplayFormat format) + { + string publicApiName = symbol.ToDisplayString(format); + + ITypeSymbol? memberType = null; + if (symbol is IMethodSymbol method) + { + memberType = method.ReturnType; + } + else if (symbol is IPropertySymbol property) + { + memberType = property.Type; + } + else if (symbol is IEventSymbol @event) + { + memberType = @event.Type; + } + else if (symbol is IFieldSymbol field) + { + memberType = field.Type; + } + + if (memberType != null) + { + publicApiName = publicApiName + " -> " + memberType.ToDisplayString(format); + } + + if (((symbol as INamespaceSymbol)?.IsGlobalNamespace).GetValueOrDefault()) + { + return string.Empty; + } + + if (symbol.ContainingAssembly != null && !symbol.ContainingAssembly.Equals(compilation.Assembly)) + { + publicApiName += $" (forwarded, contained in {symbol.ContainingAssembly.Name})"; + } + + if (experimentName != null) + { + publicApiName = "[" + experimentName + "]" + publicApiName; + } + + return publicApiName; + } + } + + private static bool ContainsPublicApiName(string apiLineText, string publicApiNameToSearch) + { + apiLineText = apiLineText.TrimStart(ObliviousMarkerArray); + + // Ensure we don't search in parameter list/return type. + var indexOfParamsList = apiLineText.IndexOf('('); + if (indexOfParamsList > 0) + { + apiLineText = apiLineText[..indexOfParamsList]; + } + else + { + var indexOfReturnType = apiLineText.IndexOf("->", StringComparison.Ordinal); + if (indexOfReturnType > 0) + { + apiLineText = apiLineText[..indexOfReturnType]; + } + } + + // Ensure that we don't have any leading characters in matched substring, apart from whitespace. + var index = apiLineText.IndexOf(publicApiNameToSearch, StringComparison.Ordinal); + return index == 0 || (index > 0 && apiLineText[index - 1] == ' '); + } + + internal void OnCompilationEnd(CompilationAnalysisContext context) + { + ProcessTypeForwardedAttributes(context.Compilation, context.ReportDiagnostic, context.CancellationToken); + ReportDeletedApiList(context.ReportDiagnostic); + ReportMarkedAsRemovedButNotActuallyRemovedApiList(context.ReportDiagnostic); + } + + private void ProcessTypeForwardedAttributes(Compilation compilation, Action reportDiagnostic, CancellationToken cancellationToken) + { + var typeForwardedToAttribute = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesTypeForwardedToAttribute); + + if (typeForwardedToAttribute != null) + { + foreach (var attribute in compilation.Assembly.GetAttributes(typeForwardedToAttribute)) + { + if (attribute.AttributeConstructor.Parameters.Length == 1 && + attribute.ConstructorArguments.Length == 1 && + attribute.ConstructorArguments[0].Value is INamedTypeSymbol forwardedType) + { + var obsoleteAttribute = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute); + if (forwardedType.IsUnboundGenericType) + { + forwardedType = forwardedType.ConstructedFrom; + } + + VisitForwardedTypeRecursively(forwardedType, reportDiagnostic, obsoleteAttribute, attribute.ApplicationSyntaxReference.GetSyntax(cancellationToken).GetLocation(), cancellationToken); + } + } + } + } + + private void VisitForwardedTypeRecursively(ISymbol symbol, Action reportDiagnostic, INamedTypeSymbol? obsoleteAttribute, Location typeForwardedAttributeLocation, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + OnSymbolActionCore(symbol, reportDiagnostic, obsoleteAttribute, cancellationToken, typeForwardedAttributeLocation); + + if (symbol is INamedTypeSymbol namedTypeSymbol) + { + foreach (var nestedType in namedTypeSymbol.GetTypeMembers()) + { + VisitForwardedTypeRecursively(nestedType, reportDiagnostic, obsoleteAttribute, typeForwardedAttributeLocation, cancellationToken); + } + + foreach (var member in namedTypeSymbol.GetMembers()) + { + if (!(member.IsImplicitlyDeclared && member.IsDefaultConstructor())) + { + VisitForwardedTypeRecursively(member, reportDiagnostic, obsoleteAttribute, typeForwardedAttributeLocation, cancellationToken); + } + } + } + } + + /// + /// Report diagnostics to the set of APIs which have been deleted but not yet documented. + /// + internal void ReportDeletedApiList(Action reportDiagnostic) + { + IReadOnlyDictionary apiMap = _apiMap.Value; + foreach (KeyValuePair pair in apiMap) + { + if (_visitedApiList.ContainsKey(pair.Key)) + { + continue; + } + + if (_unshippedData.RemovedApiList.Any(x => x.Text == pair.Key)) + { + continue; + } + + Location location = pair.Value.GetLocation(_additionalFiles); + ImmutableDictionary propertyBag = ImmutableDictionary.Empty.Add(ApiNamePropertyBagKey, pair.Value.Text); + reportDiagnostic(Diagnostic.Create(GetDiagnostic(RemoveDeletedPublicApiRule, RemoveDeletedInternalApiRule), location, propertyBag, pair.Value.Text)); + } + } + + /// + /// Report diagnostics to the set of APIs which have been marked with *REMOVED* but still exists in source code. + /// + internal void ReportMarkedAsRemovedButNotActuallyRemovedApiList(Action reportDiagnostic) + { + foreach (var markedAsRemoved in _unshippedData.RemovedApiList) + { + if (_visitedApiList.ContainsKey(markedAsRemoved.Text)) + { + Location location = markedAsRemoved.ApiLine.GetLocation(_additionalFiles); + reportDiagnostic(Diagnostic.Create(RemovedApiIsNotActuallyRemovedRule, location, messageArgs: markedAsRemoved.Text)); + } + } + } + + private bool IsTrackedAPI(ISymbol symbol, CancellationToken cancellationToken) + { + if (symbol is IMethodSymbol methodSymbol) + { + if (s_ignorableMethodKinds.Contains(methodSymbol.MethodKind)) + return false; + + if (methodSymbol is { MethodKind: MethodKind.Constructor, ContainingType.TypeKind: TypeKind.Enum }) + return false; + + // include a delegate's 'Invoke' method so we encode its signature (it would be a breaking change to + // change that). All other delegate methods can be ignored though. + if (methodSymbol is { ContainingType.TypeKind: TypeKind.Delegate, MethodKind: not MethodKind.DelegateInvoke }) + return false; + } + + // We don't consider properties to be public APIs. Instead, property getters and setters + // (which are IMethodSymbols) are considered as public APIs. + if (symbol is IPropertySymbol) + { + return false; + } + + if (IsNamespaceSkipped(symbol, cancellationToken)) + { + return false; + } + + return IsTrackedApiCore(symbol, cancellationToken); + } + + private bool IsNamespaceSkipped(ISymbol symbol, CancellationToken cancellationToken) + { + var @namespace = symbol as INamespaceSymbol ?? symbol.ContainingNamespace; + + PooledHashSet? skippedNamespaces = null; + + try + { + foreach (var location in symbol.Locations) + { + if (!location.IsInSource) + { + continue; + } + + var syntaxTree = location.SourceTree; + var currentSkippedNamespaces = _skippedNamespacesCache.GetOrAdd(syntaxTree, GetSkippedNamespacesForTree); + if (currentSkippedNamespaces.Length == 0) + { + continue; + } + + (skippedNamespaces ??= PooledHashSet.GetInstance()).AddRange(currentSkippedNamespaces); + } + + if (skippedNamespaces == null) + { + return false; + } + + var namespaceString = @namespace.ToDisplayString(s_namespaceFormat); + return skippedNamespaces.Any(n => namespaceString.StartsWith(n, StringComparison.Ordinal)); + } + finally + { + skippedNamespaces?.Free(cancellationToken); + } + } + + private ImmutableArray GetSkippedNamespacesForTree(SyntaxTree tree) + { + if (TryGetEditorConfigOptionForSkippedNamespaces(_analyzerOptions, tree, out var skippedNamespaces)) + { + return skippedNamespaces; + } + + return ImmutableArray.Empty; + } + + private bool IsTrackedApiCore(ISymbol symbol, CancellationToken cancellationToken) + { + var resultantVisibility = symbol.GetResultantVisibility(); + +#pragma warning disable IDE0047 // Remove unnecessary parentheses + if (resultantVisibility == SymbolVisibility.Private + || ((resultantVisibility == SymbolVisibility.Public) != _isPublic)) + { + return false; + } +#pragma warning restore IDE0047 // Remove unnecessary parentheses + + cancellationToken.ThrowIfCancellationRequested(); + + for (var current = symbol; current != null; current = current.ContainingType) + { + switch (current.DeclaredAccessibility) + { + case Accessibility.Protected: + case Accessibility.ProtectedOrInternal when _isPublic: + // Can't have top-level protected or protected internal members + if (!CanTypeBeExtended(current.ContainingType)) + { + return false; + } + + break; + } + } + + return true; + } + + private bool CanTypeBeExtended(ITypeSymbol type) + { + return _typeCanBeExtendedCache.GetOrAdd((type, _isPublic), CanTypeBeExtendedImpl); + } + + private static bool CanTypeBeExtendedImpl((ITypeSymbol Type, bool IsPublic) key) + { + // a type can be extended publicly if (1) it isn't sealed, and (2) it has some constructor that is + // not internal, private or protected&internal + return !key.Type.IsSealed && + key.Type.GetMembers(WellKnownMemberNames.InstanceConstructorName).Any( + m => m.DeclaredAccessibility switch + { + Accessibility.Internal or Accessibility.ProtectedAndInternal => !key.IsPublic, + Accessibility.Private => false, + _ => true, + } + ); + } + + private DiagnosticDescriptor GetDiagnostic(DiagnosticDescriptor publicDiagnostic, DiagnosticDescriptor privateDiagnostic) + => _isPublic ? publicDiagnostic : privateDiagnostic; + + /// + /// Various Visit* methods return true if an oblivious reference type is detected. + /// + private sealed class ObliviousDetector : SymbolVisitor + { + // We need to ignore top-level nullability for outer types: `Outer<...>.Inner` + private static readonly ObliviousDetector IgnoreTopLevelNullabilityInstance = new(ignoreTopLevelNullability: true); + + public static readonly ObliviousDetector Instance = new(ignoreTopLevelNullability: false); + + private readonly bool _ignoreTopLevelNullability; + + private ObliviousDetector(bool ignoreTopLevelNullability) + { + _ignoreTopLevelNullability = ignoreTopLevelNullability; + } + + public override bool VisitField(IFieldSymbol symbol) + { + return Visit(symbol.Type); + } + + public override bool VisitMethod(IMethodSymbol symbol) + { + if (Visit(symbol.ReturnType)) + { + return true; + } + + foreach (var parameter in symbol.Parameters) + { + if (Visit(parameter.Type)) + { + return true; + } + } + + foreach (var typeParameter in symbol.TypeParameters) + { + if (CheckTypeParameterConstraints(typeParameter)) + { + return true; + } + } + + return false; + } + + /// This is visiting type references, not type definitions (that's done elsewhere). + public override bool VisitNamedType(INamedTypeSymbol symbol) + { + if (!_ignoreTopLevelNullability && + symbol.IsReferenceType && + symbol.NullableAnnotation() == NullableAnnotation.None) + { + return true; + } + + if (symbol.ContainingType is INamedTypeSymbol containing && + IgnoreTopLevelNullabilityInstance.Visit(containing)) + { + return true; + } + + foreach (var typeArgument in symbol.TypeArguments) + { + if (Instance.Visit(typeArgument)) + { + return true; + } + } + + return false; + } + + public override bool VisitArrayType(IArrayTypeSymbol symbol) + { + if (symbol.NullableAnnotation() == NullableAnnotation.None) + { + return true; + } + + return Visit(symbol.ElementType); + } + + public override bool VisitPointerType(IPointerTypeSymbol symbol) + { + return Visit(symbol.PointedAtType); + } + + /// This only checks the use of a type parameter. We're checking their definition (looking at type constraints) elsewhere. + public override bool VisitTypeParameter(ITypeParameterSymbol symbol) + { + if (symbol.IsReferenceType && + symbol.NullableAnnotation() == NullableAnnotation.None) + { + // Example: + // I + return true; + } + + return false; + } + + /// This is checking the definition of a type (as opposed to its usage). + public static bool VisitNamedTypeDeclaration(INamedTypeSymbol symbol) + { + foreach (var typeParameter in symbol.TypeParameters) + { + if (CheckTypeParameterConstraints(typeParameter)) + { + return true; + } + } + + return false; + } + + private static bool CheckTypeParameterConstraints(ITypeParameterSymbol symbol) + { + if (symbol.HasReferenceTypeConstraint() && + symbol.ReferenceTypeConstraintNullableAnnotation() == NullableAnnotation.None) + { + // where T : class~ + return true; + } + + foreach (var constraintType in symbol.ConstraintTypes) + { + if (Instance.Visit(constraintType)) + { + // Examples: + // where T : SomeReferenceType~ + // where T : I + return true; + } + } + + return false; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs new file mode 100644 index 0000000000000..79ecd049f7116 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer.cs @@ -0,0 +1,423 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed partial class DeclarePublicApiAnalyzer : DiagnosticAnalyzer + { + private static readonly SourceTextValueProvider s_shippingApiDataProvider = new(static text => ReadApiData(text, isShippedApi: true)); + private static readonly SourceTextValueProvider s_nonShippingApiDataProvider = new(static text => ReadApiData(text, isShippedApi: false)); + + internal const string Extension = ".txt"; + internal const string PublicShippedFileNamePrefix = "PublicAPI.Shipped"; + internal const string PublicShippedFileName = PublicShippedFileNamePrefix + Extension; + internal const string InternalShippedFileNamePrefix = "InternalAPI.Shipped"; + internal const string InternalShippedFileName = InternalShippedFileNamePrefix + Extension; + internal const string PublicUnshippedFileNamePrefix = "PublicAPI.Unshipped"; + internal const string PublicUnshippedFileName = PublicUnshippedFileNamePrefix + Extension; + internal const string InternalUnshippedFileNamePrefix = "InternalAPI.Unshipped"; + internal const string InternalUnshippedFileName = InternalUnshippedFileNamePrefix + Extension; + internal const string ApiNamePropertyBagKey = "APIName"; + internal const string ApiNameWithNullabilityPropertyBagKey = "APINameWithNullability"; + internal const string MinimalNamePropertyBagKey = "MinimalName"; + internal const string ApiNamesOfSiblingsToRemovePropertyBagKey = "ApiNamesOfSiblingsToRemove"; + internal const string ApiNamesOfSiblingsToRemovePropertyBagValueSeparator = ";;"; + internal const string RemovedApiPrefix = "*REMOVED*"; + internal const string NullableEnable = "#nullable enable"; + internal const string InvalidReasonShippedCantHaveRemoved = "The shipped API file can't have removed members"; + internal const string InvalidReasonMisplacedNullableEnable = "The '#nullable enable' marker can only appear as the first line in the shipped API file"; + internal const string ApiIsShippedPropertyBagKey = "APIIsShipped"; + internal const string FileName = "FileName"; + + private const char ObliviousMarker = '~'; + private static readonly char[] ObliviousMarkerArray = { ObliviousMarker }; + + /// + /// Boolean option to configure if public API analyzer should bail out silently if public API files are missing. + /// + private const string BaseEditorConfigPath = "dotnet_public_api_analyzer"; + private const string BailOnMissingPublicApiFilesEditorConfigOptionName = $"{BaseEditorConfigPath}.require_api_files"; + private const string NamespaceToIgnoreInTrackingEditorConfigOptionName = $"{BaseEditorConfigPath}.skip_namespaces"; + + internal static readonly SymbolDisplayFormat ShortSymbolNameFormat = + new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + memberOptions: + SymbolDisplayMemberOptions.None, + parameterOptions: + SymbolDisplayParameterOptions.None, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.None); + + private const int IncludeNullableReferenceTypeModifier = 1 << 6; + private const int IncludeNonNullableReferenceTypeModifier = 1 << 8; + + private static readonly SymbolDisplayFormat s_publicApiFormat = + new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.ShowReadWriteDescriptor, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + memberOptions: + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeContainingType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeConstantValue, + parameterOptions: + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeType | + SymbolDisplayParameterOptions.IncludeName | + SymbolDisplayParameterOptions.IncludeDefaultValue, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + private static readonly SymbolDisplayFormat s_publicApiFormatWithNullability = + s_publicApiFormat.WithMiscellaneousOptions( + SymbolDisplayMiscellaneousOptions.UseSpecialTypes | + (SymbolDisplayMiscellaneousOptions)IncludeNullableReferenceTypeModifier | + (SymbolDisplayMiscellaneousOptions)IncludeNonNullableReferenceTypeModifier); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // Analyzer needs to get callbacks for generated code, and might report diagnostics in generated code. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext context) + { + CheckAndRegisterImplementation(isPublic: true); + CheckAndRegisterImplementation(isPublic: false); + + void CheckAndRegisterImplementation(bool isPublic) + { + var errors = new List(); + // Switch to "RegisterAdditionalFileAction" available in Microsoft.CodeAnalysis "3.8.x" to report additional file diagnostics: https://github.com/dotnet/roslyn-analyzers/issues/3918 + if (!TryGetAndValidateApiFiles(context, isPublic, errors, out var additionalFiles, out var shippedData, out var unshippedData)) + { + context.RegisterCompilationEndAction(context => + { + foreach (Diagnostic cur in errors) + { + context.ReportDiagnostic(cur); + } + }); + + return; + } + + Debug.Assert(errors.Count == 0); + + RegisterImplActions(context, new Impl(context.Compilation, additionalFiles, shippedData, unshippedData, isPublic, context.Options)); + return; + + bool TryGetAndValidateApiFiles(CompilationStartAnalysisContext context, bool isPublic, List errors, [NotNullWhen(true)] out ImmutableDictionary? additionalFiles, [NotNullWhen(true)] out ApiData? shippedData, [NotNullWhen(true)] out ApiData? unshippedData) + { + return TryGetApiData(context, isPublic, errors, out additionalFiles, out shippedData, out unshippedData) + && ValidateApiFiles(additionalFiles, shippedData, unshippedData, isPublic, errors); + } + + static void RegisterImplActions(CompilationStartAnalysisContext compilationContext, Impl impl) + { + compilationContext.RegisterSymbolAction( + impl.OnSymbolAction, + SymbolKind.NamedType, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Method); + compilationContext.RegisterSymbolAction( + impl.OnPropertyAction, + SymbolKind.Property); + compilationContext.RegisterCompilationEndAction(impl.OnCompilationEnd); + } + } + } + + private static ApiData ReadApiData(SourceText sourceText, bool isShippedApi) + { + var apiBuilder = ArrayBuilder.GetInstance(); + var removedBuilder = ArrayBuilder.GetInstance(); + var lastNullableLineNumber = -1; + + // current line we're on. Note: we ignore whitespace lines when computing this. + var lineNumber = -1; + + var additionalFileInfo = new AdditionalFileInfo(sourceText, isShippedApi); + + foreach (var line in sourceText.Lines) + { + // Skip whitespace. + var text = line.ToString(); + if (string.IsNullOrWhiteSpace(text)) + continue; + + lineNumber++; + + if (text == NullableEnable) + { + lastNullableLineNumber = lineNumber; + continue; + } + + var apiLine = new ApiLine(text, line.Span, additionalFileInfo); + if (text.StartsWith(RemovedApiPrefix, StringComparison.Ordinal)) + { + var removedText = text[RemovedApiPrefix.Length..]; + removedBuilder.Add(new RemovedApiLine(removedText, apiLine)); + } + else + { + apiBuilder.Add(apiLine); + } + } + + return new ApiData(apiBuilder.ToImmutableAndFree(), removedBuilder.ToImmutableAndFree(), lastNullableLineNumber); + } + + private static bool TryGetApiData(CompilationStartAnalysisContext context, bool isPublic, List errors, [NotNullWhen(true)] out ImmutableDictionary? additionalFiles, [NotNullWhen(true)] out ApiData? shippedData, [NotNullWhen(true)] out ApiData? unshippedData) + { + using var allShippedData = ArrayBuilder.GetInstance(); + using var allUnshippedData = ArrayBuilder.GetInstance(); + + AddApiTexts(context, isPublic, out additionalFiles, allShippedData, allUnshippedData); + + // Both missing. + if (allShippedData.Count == 0 && allUnshippedData.Count == 0) + { + if (TryGetEditorConfigOptionForMissingFiles(context.Options, context.Compilation, out var silentlyBailOutOnMissingApiFiles) && + silentlyBailOutOnMissingApiFiles) + { + shippedData = null; + unshippedData = null; + return false; + } + + // Bootstrapping public API files. + (shippedData, unshippedData) = (ApiData.Empty, ApiData.Empty); + return true; + } + + // Both there. Succeed and return what was found. + if (allShippedData.Count > 0 && allUnshippedData.Count > 0) + { + shippedData = Flatten(allShippedData); + unshippedData = Flatten(allUnshippedData); + return true; + } + + // One missing. Give custom error message depending on which it was. + var missingFileName = (allShippedData.Count == 0, isPublic) switch + { + (true, isPublic: true) => PublicShippedFileName, + (true, isPublic: false) => InternalShippedFileName, + (false, isPublic: true) => PublicUnshippedFileName, + (false, isPublic: false) => InternalUnshippedFileName + }; + + errors.Add(Diagnostic.Create(isPublic ? PublicApiFileMissing : InternalApiFileMissing, Location.None, missingFileName)); + shippedData = null; + unshippedData = null; + return false; + + // Takes potentially multiple ApiData instances, corresponding to different additional text files, and + // flattens them into the final instance we will use when analyzing the compilation. + static ApiData Flatten(ArrayBuilder allData) + { + Debug.Assert(allData.Count > 0); + + // The common case is that we will have one file corresponding to the shipped data, and one for the + // unshipped data. In that case, just return the instance directly. + if (allData.Count == 1) + return allData[0]; + + var apiBuilder = ArrayBuilder.GetInstance(); + var removedBuilder = ArrayBuilder.GetInstance(); + + for (int i = 0, n = allData.Count; i < n; i++) + { + var data = allData[i]; + apiBuilder.AddRange(data.ApiList); + removedBuilder.AddRange(data.RemovedApiList); + } + + return new ApiData( + apiBuilder.ToImmutableAndFree(), + removedBuilder.ToImmutableAndFree(), + allData.Max(static d => d.NullableLineNumber)); + } + } + + private static bool TryGetEditorConfigOption(AnalyzerOptions analyzerOptions, SyntaxTree tree, string optionName, out string optionValue) + { + optionValue = ""; + try + { + var provider = analyzerOptions.GetType().GetRuntimeProperty("AnalyzerConfigOptionsProvider")?.GetValue(analyzerOptions); + if (provider == null) + { + return false; + } + + var getOptionsMethod = provider.GetType().GetRuntimeMethods().FirstOrDefault(m => m.Name == "GetOptions"); + if (getOptionsMethod == null) + { + return false; + } + + var options = getOptionsMethod.Invoke(provider, new object[] { tree }); + var tryGetValueMethod = options.GetType().GetRuntimeMethods().FirstOrDefault(m => m.Name == "TryGetValue"); + if (tryGetValueMethod == null) + { + return false; + } + + // bool TryGetValue(string key, out string value); + var parameters = new object?[] { optionName, null }; + if (tryGetValueMethod.Invoke(options, parameters) is not bool hasOption || + !hasOption) + { + return false; + } + + if (parameters[1] is not string value) + { + return false; + } + + optionValue = value; + return true; + } +#pragma warning disable CA1031 // Do not catch general exception types + catch +#pragma warning restore CA1031 // Do not catch general exception types + { + // Gracefully handle any exception from reflection. + return false; + } + } + + private static bool TryGetEditorConfigOptionForMissingFiles(AnalyzerOptions analyzerOptions, Compilation compilation, out bool optionValue) + { + optionValue = false; + + return compilation.SyntaxTrees.FirstOrDefault() is { } tree + && TryGetEditorConfigOption(analyzerOptions, tree, BailOnMissingPublicApiFilesEditorConfigOptionName, out string value) + && bool.TryParse(value, out optionValue); + } + + private static bool TryGetEditorConfigOptionForSkippedNamespaces(AnalyzerOptions analyzerOptions, SyntaxTree tree, out ImmutableArray skippedNamespaces) + { + skippedNamespaces = ImmutableArray.Empty; + if (!TryGetEditorConfigOption(analyzerOptions, tree, NamespaceToIgnoreInTrackingEditorConfigOptionName, out var namespacesString) || string.IsNullOrWhiteSpace(namespacesString)) + { + return false; + } + + var namespaceStrings = namespacesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + if (namespaceStrings.Length == 0) + { + return false; + } + + skippedNamespaces = namespaceStrings.ToImmutableArray(); + return true; + } + + [SuppressMessage("MicrosoftCodeAnalysisPerformance", "RS1012:Start action has no registered actions", Justification = "This is not a start action")] + private static void AddApiTexts( + CompilationStartAnalysisContext context, + bool isPublic, + out ImmutableDictionary additionalFiles, + ArrayBuilder allShippedData, + ArrayBuilder allUnshippedData) + { + additionalFiles = ImmutableDictionary.Empty; + + foreach (var additionalText in context.Options.AdditionalFiles) + { + context.CancellationToken.ThrowIfCancellationRequested(); + + var file = new PublicApiFile(additionalText.Path, isPublic); + + // if it's not an api file (quick filename check), we can just immediately ignore. + if (!file.IsApiFile) + continue; + + var apiDataProvider = file.IsShipping ? s_shippingApiDataProvider : s_nonShippingApiDataProvider; + var text = additionalText.GetText(context.CancellationToken); + additionalFiles = additionalFiles.Add(additionalText, text); + if (!context.TryGetValue(text, apiDataProvider, out var apiData)) + continue; + + var resultList = file.IsShipping ? allShippedData : allUnshippedData; + resultList.Add(apiData); + } + } + + private static bool ValidateApiFiles(ImmutableDictionary additionalFiles, ApiData shippedData, ApiData unshippedData, bool isPublic, List errors) + { + var descriptor = isPublic ? PublicApiFilesInvalid : InternalApiFilesInvalid; + if (!shippedData.RemovedApiList.IsEmpty) + { + errors.Add(Diagnostic.Create(descriptor, Location.None, InvalidReasonShippedCantHaveRemoved)); + } + + if (shippedData.NullableLineNumber > 0) + { + // '#nullable enable' must be on the first line + errors.Add(Diagnostic.Create(descriptor, Location.None, InvalidReasonMisplacedNullableEnable)); + } + + if (unshippedData.NullableLineNumber > 0) + { + // '#nullable enable' must be on the first line + errors.Add(Diagnostic.Create(descriptor, Location.None, InvalidReasonMisplacedNullableEnable)); + } + + using var publicApiMap = PooledDictionary.GetInstance(StringComparer.Ordinal); + ValidateApiList(additionalFiles, publicApiMap, shippedData.ApiList, isPublic, errors); + ValidateApiList(additionalFiles, publicApiMap, unshippedData.ApiList, isPublic, errors); + + return errors.Count == 0; + } + + private static void ValidateApiList(ImmutableDictionary additionalFiles, Dictionary publicApiMap, ImmutableArray apiList, bool isPublic, List errors) + { + foreach (ApiLine cur in apiList) + { + string textWithoutOblivious = cur.Text.TrimStart(ObliviousMarkerArray); + if (publicApiMap.TryGetValue(textWithoutOblivious, out ApiLine existingLine)) + { + Location existingLocation = existingLine.GetLocation(additionalFiles); + Location duplicateLocation = cur.GetLocation(additionalFiles); + errors.Add(Diagnostic.Create(isPublic ? DuplicateSymbolInPublicApiFiles : DuplicateSymbolInInternalApiFiles, duplicateLocation, new[] { existingLocation }, cur.Text)); + } + else + { + publicApiMap.Add(textWithoutOblivious, cur); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer_Diagnostics.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer_Diagnostics.cs new file mode 100644 index 0000000000000..5ba902bcbf2eb --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/DeclarePublicApiAnalyzer_Diagnostics.cs @@ -0,0 +1,305 @@ +// 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 DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + using static PublicApiAnalyzerResources; + + /// + /// RS0016: + /// RS0017: + /// RS0022: + /// RS0024: + /// RS0025: + /// RS0026: + /// RS0027: + /// RS0036: + /// RS0037: + /// RS0041: + /// RS0048: + /// RS0050: + /// RS0051: + /// RS0052: + /// RS0053: + /// RS0054: + /// RS0055: + /// RS0056: + /// RS0057: + /// RS0058: + /// RS0059: + /// RS0060: + /// RS0061: + /// + public partial class DeclarePublicApiAnalyzer + { + internal static readonly DiagnosticDescriptor DeclareNewPublicApiRule = new( + id: DiagnosticIds.DeclarePublicApiRuleId, + title: CreateLocalizableResourceString(nameof(DeclarePublicApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DeclarePublicApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DeclarePublicApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor DeclareNewInternalApiRule = new( + id: DiagnosticIds.DeclareInternalApiRuleId, + title: CreateLocalizableResourceString(nameof(DeclareInternalApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DeclareInternalApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(DeclareInternalApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor AnnotatePublicApiRule = new( + id: DiagnosticIds.AnnotatePublicApiRuleId, + title: CreateLocalizableResourceString(nameof(AnnotatePublicApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(AnnotatePublicApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AnnotatePublicApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor AnnotateInternalApiRule = new( + id: DiagnosticIds.AnnotateInternalApiRuleId, + title: CreateLocalizableResourceString(nameof(AnnotateInternalApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(AnnotateInternalApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(AnnotateInternalApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor ObliviousPublicApiRule = new( + id: DiagnosticIds.ObliviousPublicApiRuleId, + title: CreateLocalizableResourceString(nameof(ObliviousPublicApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ObliviousPublicApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(ObliviousPublicApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor ObliviousInternalApiRule = new( + id: DiagnosticIds.ObliviousInternalApiRuleId, + title: CreateLocalizableResourceString(nameof(ObliviousInternalApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ObliviousInternalApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(ObliviousInternalApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor RemoveDeletedPublicApiRule = new( + id: DiagnosticIds.RemoveDeletedPublicApiRuleId, + title: CreateLocalizableResourceString(nameof(RemoveDeletedPublicApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveDeletedPublicApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(RemoveDeletedPublicApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor RemoveDeletedInternalApiRule = new( + id: DiagnosticIds.RemoveDeletedInternalApiRuleId, + title: CreateLocalizableResourceString(nameof(RemoveDeletedInternalApiTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemoveDeletedInternalApiMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(RemoveDeletedInternalApiDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor RemovedApiIsNotActuallyRemovedRule = new( + id: DiagnosticIds.RemovedApiIsNotActuallyRemovedRuleId, + title: CreateLocalizableResourceString(nameof(RemovedApiIsNotActuallyRemovedTitle)), + messageFormat: CreateLocalizableResourceString(nameof(RemovedApiIsNotActuallyRemovedMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor ExposedNoninstantiableTypePublic = new( + id: DiagnosticIds.ExposedNoninstantiableTypeRuleIdPublic, + title: CreateLocalizableResourceString(nameof(ExposedNoninstantiableTypeTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ExposedNoninstantiableTypeMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor ExposedNoninstantiableTypeInternal = new( + id: DiagnosticIds.ExposedNoninstantiableTypeRuleIdInternal, + title: CreateLocalizableResourceString(nameof(ExposedNoninstantiableTypeTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ExposedNoninstantiableTypeMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor PublicApiFilesInvalid = new( + id: DiagnosticIds.PublicApiFilesInvalid, + title: CreateLocalizableResourceString(nameof(PublicApiFilesInvalidTitle)), + messageFormat: CreateLocalizableResourceString(nameof(PublicApiFilesInvalidMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InternalApiFilesInvalid = new( + id: DiagnosticIds.InternalApiFilesInvalid, + title: CreateLocalizableResourceString(nameof(InternalApiFilesInvalidTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InternalApiFilesInvalidMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor PublicApiFileMissing = new( + id: DiagnosticIds.PublicApiFileMissing, + title: CreateLocalizableResourceString(nameof(PublicApiFileMissingTitle)), + messageFormat: CreateLocalizableResourceString(nameof(PublicApiFileMissingMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor InternalApiFileMissing = new( + id: DiagnosticIds.InternalApiFileMissing, + title: CreateLocalizableResourceString(nameof(InternalApiFileMissingTitle)), + messageFormat: CreateLocalizableResourceString(nameof(InternalApiFileMissingMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor DuplicateSymbolInPublicApiFiles = new( + id: DiagnosticIds.DuplicatedSymbolInPublicApiFiles, + title: CreateLocalizableResourceString(nameof(DuplicateSymbolsInPublicApiFilesTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DuplicateSymbolsInPublicApiFilesMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor DuplicateSymbolInInternalApiFiles = new( + id: DiagnosticIds.DuplicatedSymbolInInternalApiFiles, + title: CreateLocalizableResourceString(nameof(DuplicateSymbolsInInternalApiFilesTitle)), + messageFormat: CreateLocalizableResourceString(nameof(DuplicateSymbolsInInternalApiFilesMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + internal static readonly DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParametersPublic = new( + id: DiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersPublic, + title: CreateLocalizableResourceString(nameof(AvoidMultipleOverloadsWithOptionalParametersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(AvoidMultipleOverloadsWithOptionalParametersMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: @"https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParametersInternal = new( + id: DiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersInternal, + title: CreateLocalizableResourceString(nameof(AvoidMultipleOverloadsWithOptionalParametersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(AvoidMultipleOverloadsWithOptionalParametersMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: @"https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParametersPublic = new( + id: DiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersPublic, + title: CreateLocalizableResourceString(nameof(OverloadWithOptionalParametersShouldHaveMostParametersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(OverloadWithOptionalParametersShouldHaveMostParametersMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: @"https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParametersInternal = new( + id: DiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersInternal, + title: CreateLocalizableResourceString(nameof(OverloadWithOptionalParametersShouldHaveMostParametersTitle)), + messageFormat: CreateLocalizableResourceString(nameof(OverloadWithOptionalParametersShouldHaveMostParametersMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + helpLinkUri: @"https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor ShouldAnnotatePublicApiFilesRule = new( + id: DiagnosticIds.ShouldAnnotatePublicApiFilesRuleId, + title: CreateLocalizableResourceString(nameof(ShouldAnnotatePublicApiFilesTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ShouldAnnotatePublicApiFilesMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(ShouldAnnotatePublicApiFilesDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor ShouldAnnotateInternalApiFilesRule = new( + id: DiagnosticIds.ShouldAnnotateInternalApiFilesRuleId, + title: CreateLocalizableResourceString(nameof(ShouldAnnotateInternalApiFilesTitle)), + messageFormat: CreateLocalizableResourceString(nameof(ShouldAnnotateInternalApiFilesMessage)), + category: "ApiDesign", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(ShouldAnnotateInternalApiFilesDescription)), + helpLinkUri: "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = + ImmutableArray.Create( + DeclareNewPublicApiRule, + DeclareNewInternalApiRule, + AnnotatePublicApiRule, + AnnotateInternalApiRule, + ObliviousPublicApiRule, + ObliviousInternalApiRule, + RemoveDeletedPublicApiRule, + RemoveDeletedInternalApiRule, + ExposedNoninstantiableTypePublic, + ExposedNoninstantiableTypeInternal, + PublicApiFilesInvalid, + InternalApiFilesInvalid, + PublicApiFileMissing, + InternalApiFileMissing, + DuplicateSymbolInPublicApiFiles, + DuplicateSymbolInInternalApiFiles, + AvoidMultipleOverloadsWithOptionalParametersPublic, + AvoidMultipleOverloadsWithOptionalParametersInternal, + OverloadWithOptionalParametersShouldHaveMostParametersPublic, + OverloadWithOptionalParametersShouldHaveMostParametersInternal, + ShouldAnnotatePublicApiFilesRule, + ShouldAnnotateInternalApiFilesRule, + RemovedApiIsNotActuallyRemovedRule); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeParameterSymbolExtensions.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeParameterSymbolExtensions.cs new file mode 100644 index 0000000000000..dfc2573b8e92d --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeParameterSymbolExtensions.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + internal static class ITypeParameterSymbolExtensions + { + private static readonly Func s_hasReferenceTypeConstraint + = LightupHelpers.CreateSymbolPropertyAccessor(typeof(ITypeParameterSymbol), nameof(HasReferenceTypeConstraint), fallbackResult: false); + + private static readonly Func s_referenceTypeConstraintNullableAnnotation + = LightupHelpers.CreateSymbolPropertyAccessor(typeof(ITypeParameterSymbol), nameof(ReferenceTypeConstraintNullableAnnotation), fallbackResult: Lightup.NullableAnnotation.None); + + public static bool HasReferenceTypeConstraint(this ITypeParameterSymbol typeParameterSymbol) + => s_hasReferenceTypeConstraint(typeParameterSymbol); + + public static NullableAnnotation ReferenceTypeConstraintNullableAnnotation(this ITypeParameterSymbol typeParameterSymbol) + => s_referenceTypeConstraintNullableAnnotation(typeParameterSymbol); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeSymbolExtensions.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeSymbolExtensions.cs new file mode 100644 index 0000000000000..5a55a4c0c6769 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/ITypeSymbolExtensions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + internal static class ITypeSymbolExtensions + { + private static readonly Func s_nullableAnnotation + = LightupHelpers.CreateSymbolPropertyAccessor(typeof(ITypeSymbol), nameof(NullableAnnotation), fallbackResult: Lightup.NullableAnnotation.None); + + public static NullableAnnotation NullableAnnotation(this ITypeSymbol typeSymbol) + => s_nullableAnnotation(typeSymbol); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.csproj b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.csproj new file mode 100644 index 0000000000000..11ffac36df0ab --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForPublicApiAnalyzers) + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableAnnotation.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableAnnotation.cs new file mode 100644 index 0000000000000..ce8d83bc6402d --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableAnnotation.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + /// + /// Represents the nullability of values that can be assigned + /// to an expression used as an lvalue. + /// + [SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "Underlying type must match the original underlying type.")] + internal enum NullableAnnotation : byte + { + /// + /// The expression has not been analyzed, or the syntax is + /// not an expression (such as a statement). + /// + /// + /// There are a few different reasons the expression could + /// have not been analyzed: + /// 1) The symbol producing the expression comes from + /// a method that has not been annotated, such as + /// invoking a C# 7.3 or earlier method, or a + /// method in this compilation that is in a disabled + /// context. + /// 2) Nullable is completely disabled in this + /// compilation. + /// + None = 0, + + /// + /// The expression is not annotated (does not have a ?). + /// + NotAnnotated, + + /// + /// The expression is annotated (does have a ?). + /// + Annotated, + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContext.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContext.cs new file mode 100644 index 0000000000000..aa96d9a6524cc --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContext.cs @@ -0,0 +1,75 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + /// + /// Represents the state of the nullable analysis at a specific point in a file. Bits one and + /// two correspond to whether the nullable feature is enabled. Bits three and four correspond + /// to whether the context was inherited from the global context. + /// + [Flags] + [SuppressMessage("Naming", "CA1714:Flags enums should have plural names", Justification = "Intentionally uses the name from Roslyn.")] + internal enum NullableContext + { + /// + /// Nullable warnings and annotations are explicitly turned off at this location. + /// +#pragma warning disable CA1008 // Enums should have zero value - Rename zero valued field to 'None' + Disabled = 0, +#pragma warning restore CA1008 // Enums should have zero value + + /// + /// Nullable warnings are enabled and will be reported at this file location. + /// + WarningsEnabled = 1, + + /// + /// Nullable annotations are enabled and will be shown when APIs defined at + /// this location are used in other contexts. + /// + AnnotationsEnabled = 1 << 1, + + /// + /// The nullable feature is fully enabled. + /// + Enabled = WarningsEnabled | AnnotationsEnabled, + + /// + /// The nullable warning state is inherited from the project default. + /// + /// + /// The project default can change depending on the file type. Generated + /// files have nullable off by default, regardless of the project-level + /// default setting. + /// + WarningsContextInherited = 1 << 2, + + /// + /// The nullable annotation state is inherited from the project default. + /// + /// + /// The project default can change depending on the file type. Generated + /// files have nullable off by default, regardless of the project-level + /// default setting. + /// + AnnotationsContextInherited = 1 << 3, + + /// + /// The current state of both warnings and annotations are inherited from + /// the project default. + /// + /// + /// This flag is set by default at the start of all files. + /// + /// The project default can change depending on the file type. Generated + /// files have nullable off by default, regardless of the project-level + /// default setting. + /// + ContextInherited = WarningsContextInherited | AnnotationsContextInherited + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContextExtensions.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContextExtensions.cs new file mode 100644 index 0000000000000..f725f798b3720 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/NullableContextExtensions.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.Lightup +{ + internal static class NullableContextExtensions + { + private static bool IsFlagSet(NullableContext context, NullableContext flag) => + (context & flag) == flag; + + /// + /// Returns whether nullable warnings are enabled for this context. + /// + public static bool WarningsEnabled(this NullableContext context) => + IsFlagSet(context, NullableContext.WarningsEnabled); + + /// + /// Returns whether nullable annotations are enabled for this context. + /// + public static bool AnnotationsEnabled(this NullableContext context) => + IsFlagSet(context, NullableContext.AnnotationsEnabled); + + /// + /// Returns whether the nullable warning state was inherited from the project default for this file type. + /// + public static bool WarningsInherited(this NullableContext context) => + IsFlagSet(context, NullableContext.WarningsContextInherited); + + /// + /// Returns whether the nullable annotation state was inherited from the project default for this file type. + /// + public static bool AnnotationsInherited(this NullableContext context) => + IsFlagSet(context, NullableContext.AnnotationsContextInherited); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.cs new file mode 100644 index 0000000000000..bf96660a9afae --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + internal partial class PublicApiAnalyzerResources + { + private static readonly Type s_resourcesType = typeof(PublicApiAnalyzerResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.resx b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.resx new file mode 100644 index 0000000000000..a763f9f5bab3e --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiAnalyzerResources.resx @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Add public types and members to the declared API + + + Symbol '{0}' is not part of the declared public API + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + + + Annotate nullability of public types and members in the declared API + + + Symbol '{0}' is missing nullability annotations in the declared API + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + + + Enable tracking of nullability of reference types in the declared API + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + + + Public members should not use oblivious types + + + Symbol '{0}' uses some oblivious reference types + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + + + Remove deleted types and members from the declared API + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + {Locked="*REMOVED*"} + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + + + Constructor make noninheritable base class inheritable + + + The contents of the public API files are invalid + + + The contents of the public API files are invalid: {0} + + + Missing shipped or unshipped public API file + + + Public API file '{0}' is missing or not marked as an additional analyzer file + + + Do not duplicate symbols in public API files + + + The symbol '{0}' appears more than once in the public API files + + + implicit constructor for '{0}' + + + Do not add multiple public overloads with optional parameters + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + + + API with optional parameter(s) should have the most parameters amongst its public overloads + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + + + implicit get-accessor for '{0}' + + + implicit set-accessor for '{0}' + + + Add all items in document '{0}' to the API + + + Add all items in project '{0}' to the API + + + Add all items in the solution to the API + + + Enable nullability annotations in the API for project '{0}' + + + Enable nullability annotations in the API for the solution + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + + + API is marked as removed but it exists in source code + + + Add internal types and members to the declared API + + + Symbol '{0}' is not part of the declared API + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + + + Annotate nullability of internal types and members in the declared API + + + Symbol '{0}' is missing nullability annotations in the declared API + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + + + Enable tracking of nullability of reference types in the declared API + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + + + Internal members should not use oblivious types + + + Symbol '{0}' uses some oblivious reference types + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + + + Remove deleted types and members from the declared internal API + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + {Locked="*REMOVED*"} + + + The contents of the internal API files are invalid + + + The contents of the internal API files are invalid: {0} + + + Missing shipped or unshipped internal API file + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + + + Do not duplicate symbols in internal API files + + + The symbol '{0}' appears more than once in the internal API files + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiFile.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiFile.cs new file mode 100644 index 0000000000000..dc4d5f1a5fbd4 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/PublicApiFile.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ +#pragma warning disable CA1815 // Override equals and operator equals on value types + public readonly struct PublicApiFile +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + public PublicApiFile(string path, bool isPublic) + { + var fileName = Path.GetFileName(path); + + IsShipping = IsFile(fileName, isPublic ? DeclarePublicApiAnalyzer.PublicShippedFileNamePrefix : DeclarePublicApiAnalyzer.InternalShippedFileNamePrefix); + var isUnshippedFile = IsFile(fileName, isPublic ? DeclarePublicApiAnalyzer.PublicUnshippedFileNamePrefix : DeclarePublicApiAnalyzer.InternalUnshippedFileNamePrefix); + + IsApiFile = IsShipping || isUnshippedFile; + } + + public bool IsShipping { get; } + + public bool IsApiFile { get; } + + private static bool IsFile(string path, string prefix) + => path.StartsWith(prefix, StringComparison.Ordinal) && path.EndsWith(DeclarePublicApiAnalyzer.Extension, StringComparison.Ordinal); + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf new file mode 100644 index 0000000000000..ccb1321157473 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.cs.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Přidat všechny položky v dokumentu {0} do rozhraní API + + + + Add all items in project '{0}' to the API + Přidat všechny položky v projektu {0} do rozhraní API + + + + Add all items in the solution to the API + Přidat všechny položky v řešení do rozhraní API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Všechny interní typy a členy by se měly deklarovat s anotacemi o možnosti použití hodnoty null v souboru InternalAPI.txt. To přitáhne pozornost ke změnám možnosti použití hodnoty null u rozhraní API při revizích a v historii správy zdrojového kódu a pomůže předejít změnám způsobujícím chybu. + + + + Symbol '{0}' is missing nullability annotations in the declared API + U symbolu {0} chybí v deklarovaném rozhraní API anotace o možnosti použití hodnoty null. + + + + Annotate nullability of internal types and members in the declared API + Přidat anotace o možnosti použití hodnoty null u interních typů a členů v deklarovaném rozhraní API + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Všechny veřejné typy a členy by se měly deklarovat s anotacemi o možnosti použití hodnoty null v souboru PublicAPI.txt. To přitáhne pozornost ke změnám možnosti použití hodnoty null u rozhraní API při revizích a v historii správy zdrojového kódu a pomůže předejít změnám způsobujícím chybu. + + + + Symbol '{0}' is missing nullability annotations in the declared API + U symbolu {0} chybí v deklarovaném rozhraní API anotace o možnosti použití hodnoty null. + + + + Annotate nullability of public types and members in the declared API + Přidat anotace o možnosti použití hodnoty null u veřejných typů a členů v deklarovaném rozhraní API + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Symbol {0} porušuje požadavek na zpětnou kompatibilitu: Nepřidávat více přetížení s nepovinnými parametry. Podrobnosti najdete tady: {1} + + + + Do not add multiple public overloads with optional parameters + Nepřidávat více přetížení s nepovinnými parametry + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Všechny interní typy a členy by se měly deklarovat v souboru InternalAPI.txt. To přivádí pozornost ke změnám rozhraní API v revizích a historii správy zdrojového kódu a pomáhá předcházet změnám způsobujícím chybu. + + + + Symbol '{0}' is not part of the declared API + Symbol {0} není součástí deklarovaného rozhraní API. + + + + Add internal types and members to the declared API + Přidat interní typy a členy do deklarovaného rozhraní API + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Všechny veřejné typy a členy by se měly deklarovat v souboru PublicAPI.txt. To přivádí pozornost ke změnám rozhraní API v revizích a historii správy zdrojového kódu a pomáhá předcházet změnám způsobujícím chybu. + + + + Symbol '{0}' is not part of the declared public API + Symbol {0} není součástí deklarovaného veřejné rozhraní API. + + + + Add public types and members to the declared API + Přidat veřejné typy a členy do deklarovaného rozhraní API + + + + The symbol '{0}' appears more than once in the internal API files + Symbol {0} se v souborech interního rozhraní API vyskytuje více než jednou. + + + + Do not duplicate symbols in internal API files + Neduplikovat symboly v souborech interního rozhraní API + + + + The symbol '{0}' appears more than once in the public API files + Symbol {0} se v souborech veřejného rozhraní API vyskytuje více než jednou. + + + + Do not duplicate symbols in public API files + Neduplikovat symboly v souborech veřejných rozhraní API + + + + Enable nullability annotations in the API for project '{0}' + Povolit anotace o možnosti použití hodnoty null v rozhraní API pro projekt {0} + + + + Enable nullability annotations in the API for the solution + Povolit anotace o možnosti použití hodnoty null v rozhraní API pro řešení + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Konstruktor nastavuje základní třídu, ze které se nedá dědit, na třídu, ze které se dědit dá, čímž zpřístupňuje své chráněné členy. + + + + Constructor make noninheritable base class inheritable + Konstruktor činí z nedědičné základní třídy dědičnou. + + + + implicit constructor for '{0}' + implicitní konstruktor pro {0} + + + + implicit get-accessor for '{0}' + implicitní přístupový objekt get pro {0} + + + + implicit set-accessor for '{0}' + implicitní přístupový objekt set pro {0} + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Soubor interního rozhraní API {0} chybí nebo není označený jako doplňkový soubor analyzátoru. + + + + Missing shipped or unshipped internal API file + Chybí vydaný nebo nevydaný soubor interního rozhraní API + + + + The contents of the internal API files are invalid: {0} + Obsah souborů interních rozhraní API není platný: {0} + + + + The contents of the internal API files are invalid + Obsah souborů interních rozhraní API není platný + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Všechny interní členové by měly používat odkazové typy s možnou hodnotou null, nebo bez ní, ale ne odkazové typy bez určení možnosti použití hodnoty null. + + + + Symbol '{0}' uses some oblivious reference types + Symbol {0} používá některé odkazové typy bez určení možnosti použití hodnoty null. + + + + Internal members should not use oblivious types + Interní členové by neměly používat typy bez určení možnosti použití hodnoty null + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Všechny veřejné členy by měly používat odkazové typy s možnou hodnotou null, nebo bez ní, ale ne odkazové typy bez určení možnosti použití hodnoty null. + + + + Symbol '{0}' uses some oblivious reference types + Symbol {0} používá některé odkazové typy bez určení možnosti použití hodnoty null. + + + + Public members should not use oblivious types + Veřejné členy by neměly používat typy bez určení možnosti použití hodnoty null + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + {0} porušuje požadavek na zpětnou kompatibilitu: Rozhraní API s nepovinnými parametry by mělo mít ze všech veřejných přetížení nejvíce parametrů. Podrobnosti najdete tady: {1} + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + Rozhraní API s nepovinnými parametry by mělo mít ze všech veřejných přetížení nejvíce parametrů + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Soubor veřejného rozhraní API {0} chybí nebo není označený jako doplňkový soubor analyzátoru. + + + + Missing shipped or unshipped public API file + Chybí vydaný nebo nevydaný soubor veřejného rozhraní API + + + + The contents of the public API files are invalid: {0} + Obsah souborů veřejných rozhraní API není platný: {0} + + + + The contents of the public API files are invalid + Obsah souborů veřejných rozhraní API není platný + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Když se odebírá interní typ nebo člen, vložte tuto položku do souboru InternalAPI.Unshipped.txt s předponou *REMOVED*. To upozorňuje na změny rozhraní API v revizích kódu a historii správy zdrojového kódu a pomáhá předcházet změnám způsobujícím chybu. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Symbol {0} je součástí deklarovaného interního rozhraní API, ale buď není vnitřní, nebo se ho nepovedlo najít. + + + + Remove deleted types and members from the declared internal API + Odebrat odstraněné typy a členy z deklarovaného interního rozhraní API + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Když se odebírá veřejný typ nebo člen, vložte tuto položku do souboru PublicAPI.Unshipped.txt s předponou *REMOVED*. To upozorňuje na změny rozhraní API v revizích kódu a historii správy zdrojového kódu a pomáhá předcházet změnám způsobujícím chybu. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Symbol {0} je součástí deklarovaného rozhraní API, ale buď není veřejný, nebo se ho nepovedlo najít. + + + + Remove deleted types and members from the declared API + Odebrat odstraněné typy a členy z deklarovaného rozhraní API + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Symbol {0} se označil jako odebraný, ale neodstranil se ze zdrojového kódu. + + + + API is marked as removed but it exists in source code + Rozhraní API se označilo jako odebrané, ale existuje ve zdrojovém kódu. + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Soubory InternalAPI.txt by měly mít #nullable enable, aby se sledovala informace o možné hodnotě null, nebo by se tato diagnostika měla potlačit. Když se možnost nastavit hodnotu null povolí, InternalAPI.txt bude uchovávat informace o tom, které typy bude možné nastavit na null (přípona typu ?) a které ne (přípona !). Kromě toho sleduje všechna rozhraní API, která stále používá typ odkazu, který se nepoužívá (předpona ~na řádku). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + V souboru InternalAPI.txt chybí #nullable enable, takže se nezaznamenají anotace o možnosti použití hodnoty null u rozhraní API. Doporučuje se toto sledování povolit. + + + + Enable tracking of nullability of reference types in the declared API + Povolit sledování možnosti použití hodnoty null pro typy odkazů v deklarovaném rozhraní API + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Soubory PublicAPI.txt by měly mít #nullable enable, aby se sledovala informace o možné hodnotě null, nebo by se tato diagnostika měla potlačit. Když se možnost nastavit hodnotu null povolí, PublicAPI.txt bude uchovávat informace o tom, které typy bude možné nastavit na null (přípona typu ?) a které ne (přípona !). Kromě toho sleduje všechna rozhraní API, která stále používají typy bez určení možnosti použití hodnoty null (předpona ~ na řádku). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + V souboru PublicAPI.txt chybí #nullable enable, takže se nezaznamenají anotace o možnosti použití hodnoty null u rozhraní API. Doporučuje se toto sledování povolit. + + + + Enable tracking of nullability of reference types in the declared API + Povolit sledování možnosti použití hodnoty null pro typy odkazů v deklarovaném rozhraní API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf new file mode 100644 index 0000000000000..65ff7eaac3fcc --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.de.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Alle Elemente im Dokument „{0}“ der API hinzufügen + + + + Add all items in project '{0}' to the API + Alle Elemente im Projekt „{0}“ der API hinzufügen + + + + Add all items in the solution to the API + Alle Elemente in der Lösung der API hinzufügen + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Alle internen Typen und Member müssen in InternalAPI.txt mit Anmerkungen zur NULL-Zulässigkeit deklariert werden. Dadurch wird die Aufmerksamkeit auf Änderungen an der NULL-Zulässigkeit der API in den Code Reviews und im Quellcodeverwaltungsverlauf gelenkt, und Breaking Changes werden verhindert. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Für das Symbol "{0}" fehlen Anmerkungen zur NULL-Zulässigkeit in der deklarierten API. + + + + Annotate nullability of internal types and members in the declared API + NULL-Zulässigkeit von internen Typen und Member in der deklarierten API anmerken + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Alle öffentlichen Typen und Member müssen in "PublicAPI.txt" mit Anmerkungen zur NULL-Zulässigkeit deklariert werden. Dadurch wird die Aufmerksamkeit auf Änderungen an der NULL-Zulässigkeit der API in den Code Reviews und der Versionsgeschichte der Quellcodeverwaltung gelenkt, und Breaking Changes werden verhindert. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Für das Symbol "{0}" fehlen Anmerkungen zur NULL-Zulässigkeit in der deklarierten API. + + + + Annotate nullability of public types and members in the declared API + NULL-Zulässigkeit öffentlicher Typen und Member in der deklarierten API anmerken + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Das Symbol "{0}" verletzt die backcompat-Anforderung: Nicht mehrere Überladungen mit optionalen Parametern hinzufügen. Details finden Sie unter "{1}". + + + + Do not add multiple public overloads with optional parameters + Nicht mehrere Überladungen mit optionalen Parametern hinzufügen + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Alle internen Typen und Member müssen in InternalAPI.txt deklariert werden. Dadurch wird die Aufmerksamkeit auf API-Änderungen in den Code Reviews und im Quellcodeverwaltungsverlauf gelenkt, und Breaking Changes werden verhindert. + + + + Symbol '{0}' is not part of the declared API + Das Symbol "{0}" ist nicht Teil der deklarierten API. + + + + Add internal types and members to the declared API + Interne Typen und Member der deklarierten API hinzufügen + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Alle öffentlichen Typen und Member müssen in "PublicAPI.txt" deklariert werden. Dadurch wird die Aufmerksamkeit auf API-Änderungen in den Codeüberprüfungen und der Versionsgeschichte der Quellcodeverwaltung gelenkt, und Breaking Changes werden verhindert. + + + + Symbol '{0}' is not part of the declared public API + Das Symbol „{0}“ ist nicht Teil der deklarierten öffentlichen API. + + + + Add public types and members to the declared API + Öffentliche Typen und Member der deklarierten API hinzufügen + + + + The symbol '{0}' appears more than once in the internal API files + Das Symbol „{0}“ erscheint mehr als einmal in den internen API-Dateien + + + + Do not duplicate symbols in internal API files + Symbole in internen API-Dateien nicht duplizieren + + + + The symbol '{0}' appears more than once in the public API files + Das Symbol "{0}" wird in den öffentlichen API-Dateien mehrmals aufgeführt. + + + + Do not duplicate symbols in public API files + Symbole in öffentlichen API-Dateien nicht duplizieren + + + + Enable nullability annotations in the API for project '{0}' + Anmerkungen zur NULL-Zulässigkeit in der API für das Projekt „{0}“ aktivieren + + + + Enable nullability annotations in the API for the solution + Anmerkungen zur NULL-Zulässigkeit in der API für die Lösung aktivieren + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Der Konstruktor legt die nicht vererbbare Basisklasse als vererbbar fest und macht damit deren geschützte Member verfügbar. + + + + Constructor make noninheritable base class inheritable + Konstruktor macht nicht vererbbare Basisklasse vererbbar + + + + implicit constructor for '{0}' + Impliziter Konstruktor für "{0}" + + + + implicit get-accessor for '{0}' + Impliziter get-Accessor für "{0}" + + + + implicit set-accessor for '{0}' + Impliziter set-Accessor für "{0}" + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Die interne API-Datei „{0}“ fehlt oder ist nicht als zusätzliche Analysetooldatei gekennzeichnet. + + + + Missing shipped or unshipped internal API file + Fehlende versandte oder nicht versandte interne API-Datei + + + + The contents of the internal API files are invalid: {0} + Die Inhalte der internen API-Dateien sind ungültig: {0} + + + + The contents of the internal API files are invalid + Die Inhalte der internen API-Dateien sind ungültig + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Alle internen Member sollten Verweistypen verwenden, die Nullwerte zulassen oder nicht zulassen, aber keine nicht bewussten Verweistypen. + + + + Symbol '{0}' uses some oblivious reference types + Das Symbol "{0}" verwendet einige nicht beachtende Verweistypen. + + + + Internal members should not use oblivious types + Interne Member dürfen keine nicht bewussten Typen verwenden. + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Alle öffentlichen Member sollten Verweistypen, die NULL-Werte zulassen oder nicht zulassen, aber keine nicht beachtenden Verweistypen verwenden. + + + + Symbol '{0}' uses some oblivious reference types + Das Symbol "{0}" verwendet einige nicht beachtende Verweistypen. + + + + Public members should not use oblivious types + Öffentliche Member dürfen keine nicht beachtenden Typen verwenden. + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + „{0}“ verletzt die Anforderungen der Rückwärtskompatibilität: Die API mit optionalen Parametern sollte die meisten Parameter bei den öffentlichen Überladungen aufweisen. Details finden Sie unter „{1}“. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + Die API mit optionalen Parametern sollte die meisten Parameter bei den öffentlichen Überladungen aufweisen + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Die öffentliche API-Datei "{0}" fehlt oder ist nicht als zusätzliche Analysedatei gekennzeichnet. + + + + Missing shipped or unshipped public API file + Fehlende versandte oder nicht versandte öffentliche API-Datei + + + + The contents of the public API files are invalid: {0} + Die Inhalte der öffentlichen API-Dateien sind ungültig: {0} + + + + The contents of the public API files are invalid + Inhalte der öffentlichen API-Dateien sind ungültig + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Wenn Sie einen internen Typ oder ein internes Member entfernen, fügen Sie diesen Eintrag in InternalAPI.Unshipped.txt mit dem Präfix „*REMOVED*“ ein. Dadurch wird die Aufmerksamkeit auf API-Änderungen in den Code Reviews und dem Quellcodeverwaltungsverlauf gelenkt,und Breaking Changes werden verhindert. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Das Symbol „{0}“ ist Teil der deklarierten internen API, ist jedoch entweder nicht intern oder wurde nicht gefunden. + + + + Remove deleted types and members from the declared internal API + Gelöschte Typen und Member aus der deklarierten internen API entfernen + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Wenn Sie einen öffentlichen Typ oder Member entfernen, fügen Sie diesen Eintrag in "PublicAPI.Unshipped.txt" mit dem Präfix "*REMOVED*" ein. Dadurch wird die Aufmerksamkeit auf API-Änderungen in den Codeüberprüfungen und dem Quellcodeverwaltungsverlauf gelenkt und trägt dazu bei, Breaking Changes zu verhindern. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Das Symbol "{0}" ist Teil der deklarierten API, aber entweder nicht öffentlich oder nicht auffindbar. + + + + Remove deleted types and members from the declared API + Gelöschte Typen und Member aus der deklarierten API entfernen + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Das Symbol ‚{0}‘ ist als entfernt markiert, wird jedoch im Quellcode nicht gelöscht. + + + + API is marked as removed but it exists in source code + Die API ist als entfernt markiert, ist jedoch im Quellcode vorhanden. + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Für InternalAPI.txt-Dateien muss „#nullable enable“ festgelegt sein, um Informationen zur NULL-Zulässigkeit nachzuverfolgen. Andernfalls sollte diese Diagnose unterdrückt werden. Bei aktivierter NULL-Zulässigkeit zeichnet InternalAPI.txt auf, welche Typen Nullwerte zulassen (Suffix „?“ für den Typ) bzw. keine Nullwerte zulassen (Suffix „!“). Zudem werden alle APIs verfolgt, die noch immer einen nicht bewussten Verweistyp verwenden (Präfix „~“ in der Zeile). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + In InternalAPI.txt fehlt „#nullable enable“, daher werden die Anmerkungen zur NULL-Zulässigkeit der API nicht aufgezeichnet. Es wird empfohlen, diese Nachverfolgung zu aktivieren. + + + + Enable tracking of nullability of reference types in the declared API + Nachverfolgung der NULL-Zulässigkeit von Verweistypen in der deklarierten API aktivieren + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Für "PublicAPI.txt"-Dateien muss "#nullable enable" festgelegt sein, um Informationen zur NULL-Zulässigkeit nachzuverfolgen. Andernfalls sollte diese Diagnose unterdrückt werden. Bei aktivierter NULL-Zulässigkeit zeichnet "PublicAPI.txt" auf, welche Typen NULL-Werte zulassen (Suffix "?" für den Typ) bzw. keine NULL-Werte zulassen (Suffix "!"). Zudem werden alle APIs verfolgt, die noch immer einen Verweistyp ohne Beachtung verwenden (Präfix "~" in der Zeile). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + In "PublicAPI.txt" fehlt "#nullable enable", daher werden die Anmerkungen zur NULL-Zulässigkeit der API nicht aufgezeichnet. Es wird empfohlen, diese Nachverfolgung zu aktivieren. + + + + Enable tracking of nullability of reference types in the declared API + Nachverfolgung der NULL-Zulässigkeit von Verweistypen in der deklarierten API aktivieren + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf new file mode 100644 index 0000000000000..396e5f991e00a --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.es.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Agregue todos los elementos del documento "{0}" a la API + + + + Add all items in project '{0}' to the API + Agregue todos los elementos del proyecto "{0}" a la API + + + + Add all items in the solution to the API + Agregar todos los elementos de la solución a la API pública + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Todos los tipos y miembros internos deben declararse con anotaciones de nulabilidad en InternalAPI.txt. Esto atrae la atención a los cambios de nulabilidad de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + + + + Symbol '{0}' is missing nullability annotations in the declared API + El símbolo "{0}" no tiene anotaciones de nulabilidad en la API declarada. + + + + Annotate nullability of internal types and members in the declared API + Anotar la nulabilidad de los miembros y tipos en la API declarada + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Todos los tipos y miembros públicos deben declararse con anotaciones de nulabilidad en PublicAPI.txt. Esto atrae la atención a los cambios de nulabilidad de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + + + + Symbol '{0}' is missing nullability annotations in the declared API + El símbolo "{0}" no tiene anotaciones de nulabilidad en la API declarada. + + + + Annotate nullability of public types and members in the declared API + Anotar la nulabilidad de los miembros y tipos públicos en la API declarada + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + El símbolo "{0}" infringe el requisito de compatibilidad con versiones anteriores: "No agregar varias sobrecargas con los parámetros opcionales". Consulte "{1}" para obtener más detalles. + + + + Do not add multiple public overloads with optional parameters + No agregar varias sobrecargas públicas con los parámetros opcionales + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Todos los tipos y miembros internos deben declararse en PublicAPI.txt. Esto atrae la atención a los cambios de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + + + + Symbol '{0}' is not part of the declared API + El símbolo "{0}" no forma parte de la API declarada. + + + + Add internal types and members to the declared API + Agregue miembros y tipos internos a la API declarada + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Todos los tipos y miembros públicos deben declararse en PublicAPI.txt. Esto atrae la atención a los cambios de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + + + + Symbol '{0}' is not part of the declared public API + El símbolo "{0}" no forma parte de la API pública declarada. + + + + Add public types and members to the declared API + Agregar miembros y tipos públicos a la API declarada + + + + The symbol '{0}' appears more than once in the internal API files + El símbolo "{0}" aparece más de una vez en los archivos internos de la API. + + + + Do not duplicate symbols in internal API files + No duplique símbolos en los archivos internos de la API + + + + The symbol '{0}' appears more than once in the public API files + El símbolo "{0}" aparece más de una vez en los archivos de la API pública. + + + + Do not duplicate symbols in public API files + No duplicar símbolos en los archivos de la API pública + + + + Enable nullability annotations in the API for project '{0}' + Habilite las anotaciones de nulabilidad en la API para el proyecto "{0}" + + + + Enable nullability annotations in the API for the solution + Habilite las anotaciones de nulabilidad en la API para la solución + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + El constructor convierte su clase base no heredable en heredable, por lo que expone sus miembros protegidos. + + + + Constructor make noninheritable base class inheritable + El constructor convierte en heredable su clase base no heredable + + + + implicit constructor for '{0}' + constructor implícito para "{0}" + + + + implicit get-accessor for '{0}' + descriptor de acceso get implícito para "{0}" + + + + implicit set-accessor for '{0}' + descriptor de acceso set implícito para "{0}" + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + El archivo interno de la API "{0}" falta o no está marcado como archivo de analizador adicional. + + + + Missing shipped or unshipped internal API file + Falta un archivo interno de la API pública enviado o con envío anulado + + + + The contents of the internal API files are invalid: {0} + El contenido de los archivos internos de la API no es válido: {0} + + + + The contents of the internal API files are invalid + El contenido de los archivos internos de la API no es válido + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Todos los miembros públicos deben usar tipos de referencia que se puedan o no anular, pero no tipos de referencia inconscientes. + + + + Symbol '{0}' uses some oblivious reference types + El símbolo "{0}" usa algunos tipos de referencia ajenos. + + + + Internal members should not use oblivious types + Los miembros internos no deben usar tipos inconscientes + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Todos los miembros públicos deben usar tipos de referencia que acepten o no valores NULL, pero no tipos de referencia inconscientes. + + + + Symbol '{0}' uses some oblivious reference types + El símbolo "{0}" usa algunos tipos de referencia ajenos. + + + + Public members should not use oblivious types + Los miembros públicos no deben usar tipos inconscientes + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + "{0}" infringe el requisito de compatibilidad con versiones anteriores: "La API con parámetros opcionales debe tener la mayoría de los parámetros entre sus sobrecargas públicas". Consulte "{1}" para conocer más detalles. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + La API con parámetros opcionales debe tener la mayoría de los parámetros entre sus sobrecargas públicas + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + El archivo de API pública "{0}" falta o no está marcado como archivo de analizador adicional. + + + + Missing shipped or unshipped public API file + Falta un archivo de API pública enviado o con envío anulado + + + + The contents of the public API files are invalid: {0} + El contenido de los archivos de la API pública no es válido: {0} + + + + The contents of the public API files are invalid + El contenido de los archivos de la API pública no es válido + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Al quitar un tipo o miembro público, coloque esa entrada en InternalAPI.Unshipped.txt con el prefijo "*REMOVED*". Esto atrae la atención a los cambios de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + El símbolo "{0}" forma parte de la API interna declarada, pero no es interna o no se encontró. + + + + Remove deleted types and members from the declared internal API + Quitar tipos y miembros eliminados de la API interna declarada + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Al quitar un tipo o miembro público, coloque esa entrada en PublicAPI.Unshipped.txt con el prefijo "*REMOVED*". Esto atrae la atención a los cambios de la API en las revisiones de código y el historial de control de código fuente; además, ayuda a evitar cambios importantes. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + El símbolo "{0}" forma parte de la API declarada, pero no es pública o no se encontró. + + + + Remove deleted types and members from the declared API + Quitar tipos y miembros eliminados de la API declarada + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + El símbolo ' {0} ' está marcado como retirado, pero no se elimina en el código fuente + + + + API is marked as removed but it exists in source code + La API se marcó como quitada, pero existe en el código fuente + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Los archivos InternalAPI.txt deben tener "#nullable enable" para hacer un seguimiento de la información de nulabilidad, o bien se debe suprimir este diagnóstico. Con la nulabilidad habilitada, PublicAPI.txt registra los tipos se pueden anular (sufijo "?" en el tipo) o que no se pueden anular (sufijo "!"). También realiza el seguimiento de cualquier API que aún use un tipo de referencia obvio (prefijo "~" en la línea). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + Falta "#nullable enable" en InternalAPI.txt, por lo que las anotaciones de nulabilidad de la API no se registran. Se recomienda habilitar este seguimiento. + + + + Enable tracking of nullability of reference types in the declared API + Habilitar el seguimiento de la nulabilidad de los tipos de referencia en la API declarada + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Los archivos PublicAPI.txt deben tener "#nullable Enable" para hacer un seguimiento de la información de nulabilidad, o bien se debe suprimir este diagnóstico. Con la nulabilidad habilitada, PublicAPI.txt registra los tipos que aceptan valores NULL (sufijo "?" en el tipo) o que no admiten valores NULL (sufijo "!"). También realiza el seguimiento de cualquier API que aún use un tipo de referencia obvio (prefijo "~" en la línea). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + Falta "#nullable enable" en PublicAPI.txt, por lo que las anotaciones de nulabilidad de la API no se registran. Se recomienda habilitar este seguimiento. + + + + Enable tracking of nullability of reference types in the declared API + Habilitar el seguimiento de la nulabilidad de los tipos de referencia en la API declarada + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf new file mode 100644 index 0000000000000..8565484183bdb --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.fr.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Ajouter tous les éléments du document «{0}» à l’API + + + + Add all items in project '{0}' to the API + Ajouter tous les éléments du projet «{0}» à l’API + + + + Add all items in the solution to the API + Ajouter tous les éléments de la solution à l’API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tous les types et membres internes doivent être déclarés avec des annotations de nullabilité dans InternalAPI.txt. Cela attire l’attention sur les modifications de nullabilité de l’API dans les révisions de code et l’historique du contrôle de code source, et permet d’éviter les changements cassants. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Il manque des annotations de nullabilité pour le symbole '{0}' dans l'API déclarée + + + + Annotate nullability of internal types and members in the declared API + Annoter la nullabilité des types internes et des membres dans l’API déclarée + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tous les types et membres publics doivent être déclarés avec des annotations de nullabilité dans PublicAPI.txt. Cela permet d'attirer l'attention sur les changements apportés à la nullabilité de l'API dans les revues de code et l'historique de contrôle de code source. Cela permet également d'éviter les changements cassants. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Il manque des annotations de nullabilité pour le symbole '{0}' dans l'API déclarée + + + + Annotate nullability of public types and members in the declared API + Annoter la nullabilité des types et des membres publics dans l'API déclarée + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Le symbole '{0}' viole l'exigence de compatibilité descendante : 'Ne pas ajouter plusieurs surcharges publiques avec des paramètres optionnels'. Pour plus d'informations, consultez '{1}'. + + + + Do not add multiple public overloads with optional parameters + Ne pas ajouter plusieurs surcharges publiques avec des paramètres optionnels + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tous les types et membres internes doivent être déclarés dans InternalAPI.txt. Cela attire l’attention sur les modifications d’API dans les révisions de code et l’historique du contrôle de code source, et permet d’éviter les changements cassants. + + + + Symbol '{0}' is not part of the declared API + Le symbole '{0}' ne fait pas partie de l'API déclarée + + + + Add internal types and members to the declared API + Ajouter des types et des membres internes à l’API déclarée + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tous les types et membres publics doivent être déclarés dans PublicAPI.txt. Cela permet d'attirer l'attention sur les changements apportés à l'API dans les revues de code et l'historique de contrôle de code source. Cela permet également d'éviter les changements cassants. + + + + Symbol '{0}' is not part of the declared public API + Le symbole '{0}' ne fait pas partie de l’API publique déclarée + + + + Add public types and members to the declared API + Ajouter des types et membres publics à l'API déclarée + + + + The symbol '{0}' appears more than once in the internal API files + Le symbole «{0}» apparaît plusieurs fois dans les fichiers d’API internes + + + + Do not duplicate symbols in internal API files + Ne pas dupliquer les symboles dans les fichiers d’API internes + + + + The symbol '{0}' appears more than once in the public API files + Le symbole '{0}' apparaît plusieurs fois dans les fichiers de l'API publique + + + + Do not duplicate symbols in public API files + Ne pas dupliquer les symboles dans les fichiers d'une API publique + + + + Enable nullability annotations in the API for project '{0}' + Activer les annotations de nullabilité dans l’API pour le projet «{0}» + + + + Enable nullability annotations in the API for the solution + Activer les annotations de nullabilité dans l’API pour la solution + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Le constructeur fait de sa classe de base non héritable une classe héritable, exposant ainsi ses membres protégés + + + + Constructor make noninheritable base class inheritable + Le constructeur fait de la classe de base noninheritable une classe inheritable + + + + implicit constructor for '{0}' + constructeur implicite pour '{0}' + + + + implicit get-accessor for '{0}' + accesseur get implicite pour '{0}' + + + + implicit set-accessor for '{0}' + accesseur set implicite pour '{0}' + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Le fichier d’API interne «{0}» est manquant ou n’est pas marqué comme fichier d’analyseur supplémentaire + + + + Missing shipped or unshipped internal API file + Fichier d’API interne non expédié ou non expédié manquant + + + + The contents of the internal API files are invalid: {0} + Le contenu des fichiers d’API internes n’est pas valide : {0} + + + + The contents of the internal API files are invalid + Le contenu des fichiers d’API internes n’est pas valide + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tous les membres internes doivent utiliser des types référence nullables ou non nullables, mais aucun type référence inconscient. + + + + Symbol '{0}' uses some oblivious reference types + Le symbole '{0}' utilise des types référence inconscients + + + + Internal members should not use oblivious types + Les membres internes ne doivent pas utiliser de types inconscients + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tous les membres publics doivent utiliser des types référence nullables ou non nullables, mais aucun type référence inconscient. + + + + Symbol '{0}' uses some oblivious reference types + Le symbole '{0}' utilise des types référence inconscients + + + + Public members should not use oblivious types + Les membres publics ne doivent pas utiliser de types inconscients + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + «{0}» ne respecte pas l’exigence de compatibilité descendante : « L’API avec le ou les paramètres facultatifs doit avoir le plus de paramètres parmi ses surcharges publiques ». Pour plus d’informations, consultez «{1}». + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + L’API avec des paramètres facultatifs doit avoir le plus de paramètres parmi ses surcharges publiques + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Le fichier d'API publique '{0}' est manquant ou n'est pas marqué en tant que fichier d'analyseur supplémentaire + + + + Missing shipped or unshipped public API file + Absence du fichier d'API publique fournie ou non fournie + + + + The contents of the public API files are invalid: {0} + Le contenu des fichiers de l'API publique est non valide : {0} + + + + The contents of the public API files are invalid + Le contenu des fichiers de l'API publique est non valide + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Lors de la suppression d’un type ou d’un membre interne, placez cette entrée dans InternalAPI.Unshipped.txt avec le préfixe '*REMOVED*'. Cela attire l’attention sur les modifications d’API dans les révisions de code et l’historique du contrôle de code source, et permet d’éviter les changements cassants. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Le symbole '{0}' fait partie de l’API interne déclarée, mais n’est pas interne ou est introuvable + + + + Remove deleted types and members from the declared internal API + Supprimer les types et membres supprimés de l’API interne déclarée + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Lorsque vous supprimez un type ou un membre public, placez cette entrée dans PublicAPI.Unshipped.txt avec le préfixe « *REMOVED* ». Cela permet d'attirer l'attention sur les modifications de l'API dans les revues de code et l'historique du contrôle de la source, et d'éviter les modifications de rupture. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Le symbole '{0}' fait partie de l'API déclarée, mais il n'est pas public ou est introuvable + + + + Remove deleted types and members from the declared API + Enlever les types et membres supprimés de l'API déclarée + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Le symbole « {0} » est marqué comme supprimé, mais il n’est pas supprimé dans le code source + + + + API is marked as removed but it exists in source code + L’API est marquée comme supprimée, mais elle existe dans le code source + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Les fichiers InternalAPI.txt doivent avoir « #nullable activé » pour suivre les informations de nullabilité, ou ce diagnostic doit être supprimé. Une fois la possibilité de valeur Null activée, InternalAPI.txt enregistre les types qui sont nullables (suffixe « ? » sur le type) ou non Nullable (suffixe « ! »). Il effectue également le suivi de toute API qui utilise toujours un type référence obondant (préfixe « ~ » sur la ligne). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt ne contient pas '#nullable enable', donc les annotations de nullabilité de l’API ne sont pas enregistrées. Il est recommandé d’activer ce suivi. + + + + Enable tracking of nullability of reference types in the declared API + Activer le suivi de la possibilité de valeur null des types référence dans l'API déclarée + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Les fichiers PublicAPI.txt doivent avoir '#nullable enable' pour effectuer le suivi des informations de possibilité de valeur null ou ce diagnostic doit être supprimé. Si l'option de possibilité de valeur null est activée, PublicAPI.txt enregistre les types nullable (suffixe '?' sur le type) ou non-nullable (suffixe '!'). Il effectue également le suivi des API qui utilisent encore un type référence inconscient (préfixe '~' sur la ligne). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt ne contient pas '#nullable enable'. Les annotations de nullabilité de l'API ne sont donc pas enregistrées. Il est recommandé d'activer ce suivi. + + + + Enable tracking of nullability of reference types in the declared API + Activer le suivi de la possibilité de valeur null des types référence dans l'API déclarée + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf new file mode 100644 index 0000000000000..c48bac46d8b2f --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.it.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Aggiungi tutti gli elementi del documento '{0}' all'API + + + + Add all items in project '{0}' to the API + Aggiungi tutti gli elementi del progetto '{0}' all'API + + + + Add all items in the solution to the API + Aggiungi tutti gli elementi della soluzione all'API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tutti i tipi e i membri interni devono essere dichiarati con annotazioni di supporto dei valori Null in InternalAPI.txt. In questo modo si richiama l'attenzione sulle modifiche del supporto dei valori Null all'API nelle revisioni del codice e nella cronologia del controllo del codice sorgente e si contribuisce a evitare modifiche che causano un'interruzione. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Nel simbolo '{0}' mancano le annotazioni di supporto dei valori Null nell'API dichiarata + + + + Annotate nullability of internal types and members in the declared API + Annota il supporto dei valori Null di tipi interni e membri nell'API dichiarata + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tutti i tipi e i membri public devono essere dichiarati con annotazioni di supporto dei valori Null in PublicAPI.txt. In questo modo si richiama l'attenzione sulle modifiche del supporto dei valori Null all'API nelle revisioni del codice e nella cronologia del controllo del codice sorgente e si contribuisce a evitare modifiche che causano un'interruzione. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Nel simbolo '{0}' mancano le annotazioni di supporto dei valori Null nell'API dichiarata + + + + Annotate nullability of public types and members in the declared API + Annota il supporto dei valori Null di tipi e membri public nell'API dichiarata + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Il simbolo '{0}' viola il requisito di compatibilità con le versioni precedenti: 'Non aggiungere più overload public con parametri facoltativi'. Per dettagli, vedere '{1}'. + + + + Do not add multiple public overloads with optional parameters + Non aggiungere più overload public con parametri facoltativi + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tutti i tipi e i membri interni devono essere dichiarati in InternalAPI.txt. In questo modo si richiama l'attenzione sulle modifiche all'API nelle revisioni del codice e nella cronologia del controllo del codice sorgente e contribuisce a evitare modifiche che causano un'interruzione. + + + + Symbol '{0}' is not part of the declared API + Il simbolo '{0}' non fa parte dell'API dichiarata + + + + Add internal types and members to the declared API + Aggiungere tipi e membri interni all'API dichiarata + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tutti i tipi e i membri public devono essere dichiarati in PublicAPI.txt. In questo modo si richiama l'attenzione sulle modifiche all'API nelle revisioni del codice e nella cronologia del controllo del codice sorgente e contribuisce a evitare modifiche che causano un'interruzione. + + + + Symbol '{0}' is not part of the declared public API + Il simbolo '{0}' non fa parte dell'API pubblica dichiarata + + + + Add public types and members to the declared API + Aggiungere tipi e membri public all'API dichiarata + + + + The symbol '{0}' appears more than once in the internal API files + Il simbolo '{0}' è presente più volte nei file dell'API interna + + + + Do not duplicate symbols in internal API files + Non duplicare simboli nei file API interni + + + + The symbol '{0}' appears more than once in the public API files + Il simbolo '{0}' è presente più volte nei file dell'API pubblica + + + + Do not duplicate symbols in public API files + Non duplicare simboli nei file dell'API pubblica + + + + Enable nullability annotations in the API for project '{0}' + Abilita le annotazioni di supporto dei valori Null nell'API per il progetto '{0}' + + + + Enable nullability annotations in the API for the solution + Abilita le annotazioni di supporto dei valori Null nell'API per la soluzione + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Il costruttore rende ereditabile la relativa classe di base non ereditabile, esponendone in tal modo i membri protetti + + + + Constructor make noninheritable base class inheritable + Il costruttore rende ereditabile la classe di base non ereditabile + + + + implicit constructor for '{0}' + costruttore implicito per '{0}' + + + + implicit get-accessor for '{0}' + funzione di accesso get implicita per '{0}' + + + + implicit set-accessor for '{0}' + funzione di accesso set implicita per '{0}' + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Il file di API interna '{0}' manca o non è contrassegnato come file aggiuntivo dell'analizzatore + + + + Missing shipped or unshipped internal API file + File di API interno distribuito o non distribuito mancante + + + + The contents of the internal API files are invalid: {0} + Il contenuto dei file dell'API interna non è valido: {0} + + + + The contents of the internal API files are invalid + Il contenuto dei file dell'API interna non è valido: + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tutti i membri interni devono usare tipi riferimento nullable o non nullable, ma non tipi riferimento oblivious. + + + + Symbol '{0}' uses some oblivious reference types + Il simbolo '{0}' usa alcuni tipi di riferimento oblivious + + + + Internal members should not use oblivious types + I membri interni non devono usare tipi oblivious + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tutti i membri pubblici devono usare tipi riferimento nullable o non nullable, ma non tipi riferimento oblivious. + + + + Symbol '{0}' uses some oblivious reference types + Il simbolo '{0}' usa alcuni tipi di riferimento oblivious + + + + Public members should not use oblivious types + I membri pubblici non devono usare tipi oblivious + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}' viola il requisito di compatibilità con le versioni precedenti: 'L'API con parametri facoltativi deve includere la maggior parte dei parametri tra i relativi overload public'. Per dettagli, vedere '{1}'. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + L'API con parametri facoltativi deve includere la maggior parte dei parametri tra i relativi overload public + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Il file di API pubblica '{0}' manca o non è contrassegnato come file aggiuntivo dell'analizzatore + + + + Missing shipped or unshipped public API file + File di API pubblica distribuito o non distribuito mancante + + + + The contents of the public API files are invalid: {0} + Il contenuto dei file dell'API pubblica non è valido: {0} + + + + The contents of the public API files are invalid + Il contenuto dei file dell'API pubblica non è valido + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Quando si rimuove un tipo o un membro interno, è necessario inserire questa voce in InternalAPI.Unshipped.txt con prefisso '*REMOVED*’. In questo modo si richiama l'attenzione sulle modifiche all'API nelle revisioni del codice e nella cronologia dei controlli del codice sorgente, e si contribuisce a evitare modifiche che causano un'interruzione. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Il simbolo '{0}' fa parte dell'API interna dichiarata, ma non è interno oppure non è stato possibile trovarlo + + + + Remove deleted types and members from the declared internal API + Rimuovere tipi e membri eliminati dall'API interna dichiarata + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Quando si rimuove un tipo o un membro pubblico, è necessario inserire questa voce in PublicAPI.Unshipped.txt con prefisso '*REMOVED*’. In questo modo si richiama l'attenzione sulle modifiche all'API nelle revisioni del codice e nella cronologia dei controlli del codice sorgente, e si contribuisce a evitare modifiche che causano un'interruzione. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Il simbolo '{0}' fa parte dell'API dichiarata, ma non è public oppure non è stato possibile trovarlo + + + + Remove deleted types and members from the declared API + Rimuovere tipi e membri eliminati dall'API dichiarata + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Il simbolo '{0}' è contrassegnato come rimosso, ma non è stato eliminato nel codice sorgente + + + + API is marked as removed but it exists in source code + L'API è contrassegnata come rimossa, ma è presente nel codice sorgente + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + I file InternalAPI.txt devono includere `#nullable enable` per tenere traccia delle informazioni sul supporto dei valori Null, in caso contrario questa diagnostica deve essere eliminata. Se il supporto dei valori Null è abilitato, InternalAPI.txt registra i tipi nullable (suffisso `?` nel tipo) o non nullable (suffisso `!`). Tiene inoltre traccia di eventuali API che usano ancora un tipo riferimento senza annotazioni nullable (prefisso `~` nella riga). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + In InternalAPI.txt manca '#nullable enable', di conseguenza le annotazioni di supporto dei valori Null dell'API non sono registrate. È consigliabile abilitare questa verifica. + + + + Enable tracking of nullability of reference types in the declared API + Abilita il rilevamento del supporto dei valori Null dei tipi riferimento nell'API dichiarata + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + I file PublicAPI.txt devono includere `#nullable enable` per tenere traccia delle informazioni sul supporto dei valori Null, in caso contrario questa diagnostica deve essere eliminata. Se il supporto dei valori Null è abilitato, PublicAPI.txt registra i tipi nullable (suffisso `?` nel tipo) o non nullable (suffisso `!`). Tiene inoltre traccia di eventuali API che usano ancora un tipo riferimento senza annotazioni nullable (prefisso `~` nella riga). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + In PublicAPI.txt manca '#nullable enable', di conseguenza le annotazioni di supporto dei valori Null dell'API non sono registrate. È consigliabile abilitare questa verifica. + + + + Enable tracking of nullability of reference types in the declared API + Abilita il rilevamento del supporto dei valori Null dei tipi riferimento nell'API dichiarata + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf new file mode 100644 index 0000000000000..cd4054f86da77 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ja.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + ドキュメント '{0}' 内のすべての項目を API へ追加 + + + + Add all items in project '{0}' to the API + プロジェクト '{0}' 内のすべての項目を API へ追加 + + + + Add all items in the solution to the API + ソリューション内のすべての項目を API へ追加 + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + すべての内部の型とメンバーは、NULL 値許容の注釈を付けて、InternalAPI.txt で宣言する必要があります。これにより、コード レビューとソース管理履歴で API の NULL 値の許容の変更に注意を引くことができ、破壊的変更を防ぐことができます。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + シンボル '{0}' では、宣言された API に NULL 許容の注釈がありません + + + + Annotate nullability of internal types and members in the declared API + 宣言された内部 API の型とメンバーの NULL 値許容の注釈を付ける + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + すべてのパブリックの型とメンバーは、NULL 許容の注釈を付けて、PublicAPI.txt で宣言する必要があります。これにより、コード レビューとソース管理履歴で API の NULL 許容の変更に注意を引くことができ、破壊的変更を防ぐことができます。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + シンボル '{0}' では、宣言された API に NULL 許容の注釈がありません + + + + Annotate nullability of public types and members in the declared API + 宣言された API のパブリックの型とメンバーの NULL 許容に注釈を付ける + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + シンボル '{0}' は下位互換性の要件、「省略可能なパラメーターを持つ複数のオーバーロードを追加しない」に違反しています。詳細は '{1}' を参照してください。 + + + + Do not add multiple public overloads with optional parameters + 省略可能なパラメーターを持つ複数のパブリック オーバーロードを追加しない + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + すべての内部の型とメンバーは、 InternalAPI.txt で宣言する必要があります。これにより、コード レビューとソース管理履歴で API 変更に注意を引くことができ、破壊的変更を防ぐことができます。 + + + + Symbol '{0}' is not part of the declared API + シンボル '{0}' は宣言された API の一部ではありません + + + + Add internal types and members to the declared API + 宣言された API に内部の型とメンバーを追加する + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + すべてのパブリックの型とメンバーは、PublicAPI.txt で宣言する必要があります。これにより、コード レビューとソース管理履歴で API 変更に注意を引くことができ、破壊的変更を防ぐことができます。 + + + + Symbol '{0}' is not part of the declared public API + シンボル '{0}' は宣言されたパブリック API の一部ではありません + + + + Add public types and members to the declared API + 宣言された API にパブリックの型とメンバーを追加する + + + + The symbol '{0}' appears more than once in the internal API files + シンボル '{0}' が、内部 API ファイルに複数含まれています + + + + Do not duplicate symbols in internal API files + 内部 API ファイル内でシンボルを重複させない + + + + The symbol '{0}' appears more than once in the public API files + シンボル '{0}' が、パブリック API ファイルに複数含まれています + + + + Do not duplicate symbols in public API files + パブリック API ファイル内でシンボルを重複させない + + + + Enable nullability annotations in the API for project '{0}' + プロジェクト '{0}' の API で、NULL 値許容の注釈を有効にする + + + + Enable nullability annotations in the API for the solution + ソリューションの API で、NULL 値許容の注釈を有効にする + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + コンストラクターは、その継承不可能な基底クラスを継承可能にすることで、保護されたメンバーを公開します + + + + Constructor make noninheritable base class inheritable + コンストラクターにより、継承不可能な基底クラスが継承可能になります + + + + implicit constructor for '{0}' + '{0}' の暗黙的なコンストラクター + + + + implicit get-accessor for '{0}' + '{0}' の暗黙的な get アクセサー + + + + implicit set-accessor for '{0}' + '{0}' の暗黙的な set アクセサー + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + 内部 API ファイル '{0}' が存在しないか、追加のアナライザー ファイルとしてマークされていません + + + + Missing shipped or unshipped internal API file + 出荷済みまたは未出荷の内部 API ファイルがない + + + + The contents of the internal API files are invalid: {0} + 内部 API ファイルのコンテンツが無効です: {0} + + + + The contents of the internal API files are invalid + 内部 API ファイルのコンテンツが無効 + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + すべての内部メンバーは、Null 許容参照型と null 非許容参照型のいずれかを使用する必要がありますが、oblivious 参照型は使用しません。 + + + + Symbol '{0}' uses some oblivious reference types + シンボル '{0}' ではいくつかの oblivious 参照型が使用されています + + + + Internal members should not use oblivious types + 内部メンバーによる oblivious 型の使用禁止 + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + すべてのパブリック メンバーは、Null 許容参照型と null 非許容参照型のいずれかを使用する必要がありますが、oblivious 参照型は使用しません。 + + + + Symbol '{0}' uses some oblivious reference types + シンボル '{0}' ではいくつかの oblivious 参照型が使用されています + + + + Public members should not use oblivious types + パブリック メンバーによる oblivious 型の使用禁止 + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}' は下位互換性の要件、「省略可能なパラメーターを持つ API は、そのパブリック オーバーロード内のほとんどのパラメーターを持つ必要がある」に違反しています。詳細は '{1}' を参照してください。 + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + 省略可能なパラメーターを持つ API は、そのパブリック オーバーロード内のほとんどのパラメーターを持つ必要がある + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + パブリック API ファイル '{0}' が存在しないか、追加のアナライザー ファイルとしてマークされていません + + + + Missing shipped or unshipped public API file + 出荷済みまたは未出荷のパブリック API ファイルがありません + + + + The contents of the public API files are invalid: {0} + パブリック API ファイルのコンテンツが無効です: {0} + + + + The contents of the public API files are invalid + パブリック API ファイルのコンテンツが無効です + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 内部の型またはメンバーを削除する際、その項目を InternalAPI.Unshipped.txt に '*REMOVED*' プレフィックスを付けて記入する必要があります。これにより、コード レビューとソース管理履歴で API の変更に注意を引くことができ、破壊的変更を防ぐことができます。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + シンボル '{0}' は宣言された内部 API の一部ですが、内部ではないか、見つかりませんでした + + + + Remove deleted types and members from the declared internal API + 削除された型とメンバーを、宣言された内部 API から削除する + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + パブリックの型またはメンバーを削除する際、その項目を PublicAPI.Unshipped.txt に '*REMOVED*' プレフィックスを付けて記入する必要があります。これにより、コード レビューとソース管理履歴で API の変更に注意を引くことができ、破壊的変更を防ぐことができます。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + シンボル '{0}' は宣言された API の一部ですが、パブリックではないか、見つかりませんでした + + + + Remove deleted types and members from the declared API + 削除された型とメンバーを、宣言された API から削除する + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + 記号 '{0}' が削除済みとしてマークされていますが、ソース コードでは削除されていません + + + + API is marked as removed but it exists in source code + API が削除済みとマークされているが、ソース コード内に存在する + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + InternalAPI.txt ファイルでは、NULL 値の許容情報を追跡するために '#nullable enable' を指定する必要があります。指定されていない場合は、この診断を中止する必要があります。NULL 値の許容を有効にすると、InternalAPI.txt は、どの型が null 許容 (型にサフィックス '?' を含む) または null 非許容 (サフィックス '!') かを記録します。また、oblivious 参照型 (行にプレフィックス '~' を含む) を使用している API を追跡します。 + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt に '#nullable enable' がありません。そのため、API の NULL 値許容の注釈は記録されていません。この追跡を有効にすることをお勧めします。 + + + + Enable tracking of nullability of reference types in the declared API + 宣言された API 内で参照型の NULL 値の許容を追跡できるようにする + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + PublicAPI.txt ファイルでは、NULL 値の許容情報を追跡するために '#nullable enable' を指定する必要があります。指定されていない場合は、この診断を中止する必要があります。NULL 値の許容を有効にすると、PublicAPI.txt は、どの型が null 許容 (型にサフィックス '?' を含む) または null 非許容 (サフィックス '!') かを記録します。また、oblivious 参照型 (行にプレフィックス '~' を含む) を使用している API を追跡します。 + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt に '#nullable enable' がありません。そのため、API の NULL 許容の注釈は記録されていません。この追跡を有効にすることをお勧めします。 + + + + Enable tracking of nullability of reference types in the declared API + 宣言された API 内で参照型の NULL 値の許容を追跡できるようにする + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf new file mode 100644 index 0000000000000..dac317675838a --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ko.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + 문서 '{0}'의 모든 항목을 API에 추가 + + + + Add all items in project '{0}' to the API + 프로젝트 '{0}'의 모든 항목을 API에 추가 + + + + Add all items in the solution to the API + API에 솔루션의 모든 항목 추가 + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 모든 내부 형식 및 구성원은 InternalAPI.txt에서 Null 허용 여부 주석을 포함하여 선언되어야 합니다. 그러면 코드 검토 및 소스 제어 기록에서 API Null 허용 여부 변경 내용을 쉽게 확인할 수 있고 호환성이 손상되는 변경을 방지할 수 있습니다. + + + + Symbol '{0}' is missing nullability annotations in the declared API + 선언된 API에서 '{0}' 기호에 Null 허용 여부 주석이 없습니다. + + + + Annotate nullability of internal types and members in the declared API + 선언된 API에서 내부 형식 및 구성원의 Null 허용 여부 주석 추가 + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 모든 public 형식 및 멤버는 PublicAPI.txt에서 Null 허용 여부 주석을 포함하여 선언되어야 합니다. 그러면 코드 검토 및 소스 제어 기록에서 API Null 허용 여부 변경 내용을 쉽게 확인할 수 있고 호환성이 손상되는 변경을 방지할 수 있습니다. + + + + Symbol '{0}' is missing nullability annotations in the declared API + 선언된 API에서 '{0}' 기호에 Null 허용 여부 주석이 없습니다. + + + + Annotate nullability of public types and members in the declared API + 선언된 API에서 public 형식 및 멤버의 Null 허용 여부 주석 추가 + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + '{0}' 기호가 backcompat 요구 사항인 '선택적 매개 변수가 있는 여러 오버로드를 추가하지 마세요.'를 위반합니다. 자세한 내용은 '{1}'을(를) 참조하세요. + + + + Do not add multiple public overloads with optional parameters + 선택적 매개 변수가 있는 여러 공용 오버로드를 추가하지 마세요. + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 모든 내부 형식 및 구성원은 InternalAPI.txt에서 선언해야 합니다. 이로 인해 코드 검토 및 소스 제어 기록에서 API 변경 내용을 쉽게 확인할 수 있고 큰 변경을 방지할 수 있습니다. + + + + Symbol '{0}' is not part of the declared API + '{0}' 기호가 선언된 API에 포함되지 않습니다. + + + + Add internal types and members to the declared API + 선언된 API에 내부 형식 및 구성원 추가 + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 모든 public 형식 및 멤버는 PublicAPI.txt에서 선언해야 합니다. 이로 인해 코드 검토 및 소스 제어 기록에서 API 변경 내용을 쉽게 확인할 수 있고 큰 변경을 방지할 수 있습니다. + + + + Symbol '{0}' is not part of the declared public API + 기호 '{0}'은(는) 선언된 공개 API의 일부가 아닙니다. + + + + Add public types and members to the declared API + 선언된 API에 공용 형식 및 멤버 추가 + + + + The symbol '{0}' appears more than once in the internal API files + 내부 API 파일에 '{0}' 기호가 두 번 이상 나타납니다. + + + + Do not duplicate symbols in internal API files + 내부 API 파일에서 기호를 복제하지 마세요. + + + + The symbol '{0}' appears more than once in the public API files + '{0}' 기호가 퍼블릭 API 파일에 두 번 이상 나타납니다. + + + + Do not duplicate symbols in public API files + 공용 API 파일에서 기호를 복제하지 마세요. + + + + Enable nullability annotations in the API for project '{0}' + API에서 프로젝트 '{0}'에 Null 허용 여부 주석 사용 + + + + Enable nullability annotations in the API for the solution + 솔루션에 대한 API에서 null 허용 여부 주석 사용 + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + 생성자는 상속할 수 없는 기본 클래스를 상속할 수 있도록 만들어 보호된 멤버를 공개합니다. + + + + Constructor make noninheritable base class inheritable + 생성자가 상속할 수 없는 기본 클래스를 상속할 수 있도록 만듭니다. + + + + implicit constructor for '{0}' + '{0}'의 암시적 생성자 + + + + implicit get-accessor for '{0}' + '{0}'의 암시적 get 접근자 + + + + implicit set-accessor for '{0}' + '{0}'의 암시적 set 접근자 + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + 내부 API 파일 '{0}'이(가) 누락되었거나 추가 분석기 파일로 표시되지 않았습니다. + + + + Missing shipped or unshipped internal API file + 배송되거나 배송되지 않은 내부 API 파일 누락 + + + + The contents of the internal API files are invalid: {0} + 내부 API 파일의 내용이 잘못되었습니다. {0} + + + + The contents of the internal API files are invalid + 내부 API 파일의 내용이 잘못되었습니다. + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + 모든 내부 구성원은 null을 허용하거나 null을 허용하지 않는 참조 형식을 사용해야 하지만, 인식 불가능한 참조 형식은 사용하면 안 됩니다. + + + + Symbol '{0}' uses some oblivious reference types + '{0}' 기호는 일부 인식 불가능한 참조 형식을 사용합니다. + + + + Internal members should not use oblivious types + 내부 구성원은 인식 불가능한 형식을 사용하면 안 됩니다. + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + 모든 public 멤버는 nullable 또는 nullable이 아닌 참조 형식을 사용해야 하지만, 인식 불가능한 참조 형식은 사용하면 안 됩니다. + + + + Symbol '{0}' uses some oblivious reference types + '{0}' 기호는 일부 인식 불가능한 참조 형식을 사용합니다. + + + + Public members should not use oblivious types + public 멤버는 인식 불가능한 형식을 사용하면 안 됨 + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}'이(가) backcompat 요구 사항인 '선택적 매개 변수가 있는 API는 공용 오버로드 중 가장 많은 매개 변수를 보유해야 합니다'를 위반합니다. 자세한 내용은 '{1}'을(를) 참조하세요. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + 선택적 매개 변수가 있는 API에는 공개 오버로드 중에서 가장 많은 매개 변수가 있어야 합니다. + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + 퍼블릭 API 파일 '{0}'이(가) 없거나 추가 분석기 파일로 표시되지 않았습니다. + + + + Missing shipped or unshipped public API file + 제공되거나 제공되지 않은 퍼블릭 API 파일이 없음 + + + + The contents of the public API files are invalid: {0} + 공용 API 파일의 콘텐츠가 유효하지 않습니다. {0} + + + + The contents of the public API files are invalid + 공용 API 파일의 콘텐츠가 유효하지 않습니다. + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 내부 형식 또는 구성원을 제거할 때 해당 항목을 InternalAPI.Unshipped.txt에 '*REMOVED*' 접두어를 추가합니다. 이는 코드 검토 및 소스 제어 기록의 API 변경 내용에 주의를 기울이고 주요 변경 내용을 방지하는 데 도움이 됩니다. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + 기호 '{0}'은(는) 선언된 내부 API의 일부이지만 내부가 아니거나 찾을 수 없습니다. + + + + Remove deleted types and members from the declared internal API + 선언된 내부 API에서 삭제된 형식 및 구성원 제거 + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + public 형식 및 멤버를 제거할 경우 해당 항목을 '*REMOVED*' 접두사를 사용해 PublicAPI.Unshipped.txt에 넣습니다. 이렇게 하면 코드 검토와 원본 제어 기록에서 API 변경 내용에 주의를 기울이게 하고, 호환성이 손상되는 변경을 방지할 수 있습니다. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + '{0}' 기호가 선언된 API의 일부이지만, 공용이 아니거나 찾을 수 없습니다. + + + + Remove deleted types and members from the declared API + 선언된 API에서 삭제된 형식 및 멤버 제거 + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + '{0}' 기호가 제거됨으로 표시되지만 소스 코드에서는 삭제되지 않습니다. + + + + API is marked as removed but it exists in source code + API가 제거된 것으로 표시되지만 소스 코드에 존재합니다. + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + InternalAPI.txt 파일에는 Null 허용 여부 정보를 추적하기 위해 `#nullable enable`이 있어야 합니다. 그렇지 않으면 이 진단이 억제되어야 합니다. Null 허용 여부를 사용하도록 설정하면 InternalAPI.txt는 Null을 허용(형식의 접미사 `?`)하거나 null을 허용하지 않는(접미사 `!`) 형식을 기록합니다. 또한 여전히 인식 불가능한 참조 형식(접두사 `~` 온라인)을 사용하는 모든 API를 추적합니다. + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt에 '#nullable enable'이 없으므로 API의 Null 허용 여부 주석이 기록되지 않았습니다. 이 추적을 사용하도록 설정하는 것이 좋습니다. + + + + Enable tracking of nullability of reference types in the declared API + 선언된 API에서 참조 형식의 Null 허용 여부 추적 사용 + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Null 허용 여부 정보를 추적하려면 PublicAPI.txt 파일에 '#nullable enable'이 있어야 합니다. 또는 이 진단이 표시되지 않아야 합니다. Null 허용 여부를 사용하는 경우 PublicAPI.txt는 nullable 형식(형식에 접미사 '?') 또는 nullable이 아닌 형식(접미사 '!')을 기록합니다. 또한 null을 허용하지 않는 참조 형식(줄에 접두사 '~')을 계속 사용하는 모든 API를 추적합니다. + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt에 '#nullable enable'이 없으므로 API의 Null 허용 여부 주석이 기록되지 않았습니다. 이 추적을 사용하도록 설정하는 것이 좋습니다. + + + + Enable tracking of nullability of reference types in the declared API + 선언된 API에서 참조 형식의 Null 허용 여부 추적 사용 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf new file mode 100644 index 0000000000000..ecc2859dd58b2 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pl.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Dodaj wszystkie elementy w dokumencie „{0}” do interfejsu API + + + + Add all items in project '{0}' to the API + Dodaj wszystkie elementy w projekcie „{0}” do interfejsu API + + + + Add all items in the solution to the API + Dodaj wszystkie elementy w rozwiązaniu interfejsu API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Wszystkie wewnętrzne typy i składowe powinny być zadeklarowane w pliku PublicAPI.txt z adnotacjami opcji dopuszczania wartości null. To zwraca uwagę na zmiany opcji dopuszczania wartości null interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym nieprawidłowe działanie. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Symbol „{0}” nie ma adnotacji opcji dopuszczania wartości null w zadeklarowanym interfejsie API + + + + Annotate nullability of internal types and members in the declared API + Dodawanie adnotacji opcji dopuszczania wartości null typów i składowych w zadeklarowanym interfejsie API + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Wszystkie publiczne typy i składowe powinny być zadeklarowane w pliku PublicAPI.txt z adnotacjami opcji dopuszczania wartości null. To przyciąga uwagę do zmian opcji dopuszczania wartości null interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym nieprawidłowe działanie. + + + + Symbol '{0}' is missing nullability annotations in the declared API + Symbol „{0}” nie ma adnotacji opcji dopuszczania wartości null w zadeklarowanym interfejsie API + + + + Annotate nullability of public types and members in the declared API + Dodawanie adnotacji opcji dopuszczania wartości null publicznych typów i składowych w zadeklarowanym interfejsie API + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Symbol „{0}” narusza wymaganie wstecznej zgodności: „Nie dodawaj wielu przeciążeń z opcjonalnymi parametrami”. Zobacz „{1}”, aby uzyskać szczegółowe informacje. + + + + Do not add multiple public overloads with optional parameters + Nie dodawaj wielu przeciążeń publicznych z opcjonalnymi parametrami + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Wszystkie typy wewnętrzne i składowe powinny być zadeklarowane w pliku InternalAPI.txt. Zwraca to uwagę na zmiany interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym niezgodność. + + + + Symbol '{0}' is not part of the declared API + Symbol „{0}” nie jest częścią zadeklarowanego interfejsu API + + + + Add internal types and members to the declared API + Dodaj publiczne typy i składowe do zadeklarowanego interfejsu API + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Wszystkie publiczne typy i składowe powinny być zadeklarowane w pliku PublicAPI.txt. To przyciąga uwagę do zmian interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym nieprawidłowe działanie. + + + + Symbol '{0}' is not part of the declared public API + Symbol „{0}” nie jest częścią zadeklarowanego publicznego interfejsu API + + + + Add public types and members to the declared API + Dodaj publiczne typy i składowe do zadeklarowanego interfejsu API + + + + The symbol '{0}' appears more than once in the internal API files + Symbol „{0}” występuje więcej niż raz w plikach wewnętrznego interfejsu API + + + + Do not duplicate symbols in internal API files + Nie duplikuj symboli w plikach wewnętrznego interfejsu API + + + + The symbol '{0}' appears more than once in the public API files + Symbol „{0}” występuje więcej niż raz w plikach publicznego interfejsu API + + + + Do not duplicate symbols in public API files + Nie duplikuj symboli w plikach publicznego interfejsu API + + + + Enable nullability annotations in the API for project '{0}' + Włącz adnotacje dopuszczające wartość null w interfejsie API dla projektu „{0}” + + + + Enable nullability annotations in the API for the solution + Włącz adnotacje opcji dopuszczania wartości null w publicznym interfejsie API dla rozwiązania + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Konstruktor umożliwia dziedziczenie klasy podstawowej nieprzeznaczonej do dziedziczenia, tym samym ujawniając jej składowe chronione + + + + Constructor make noninheritable base class inheritable + Konstruktor umożliwia dziedziczenie klasy podstawowej nieprzeznaczonej do dziedziczenia + + + + implicit constructor for '{0}' + niejawny konstruktor dla elementu „{0}” + + + + implicit get-accessor for '{0}' + niejawna metoda dostępu get dla elementu „{0}” + + + + implicit set-accessor for '{0}' + niejawna metoda dostępu set dla elementu „{0}” + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Brak pliku wewnętrznego interfejsu API „{0}” lub nie jest on oznaczony jako dodatkowy plik analizatora + + + + Missing shipped or unshipped internal API file + Brak dostarczonego lub niewysłanego pliku wewnętrznego interfejsu API + + + + The contents of the internal API files are invalid: {0} + Zawartość plików wewnętrznego interfejsu API jest nieprawidłowa: {0} + + + + The contents of the internal API files are invalid + Zawartość plików wewnętrznego interfejsu API jest nieprawidłowa + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Wszystkie publiczne składowe powinny używać typów referencyjnych dopuszczających wartość null lub nie dopuszczających jej, ale nie nieświadomych typów referencyjnych. + + + + Symbol '{0}' uses some oblivious reference types + Symbol „{0}” używa pewnych nieświadomych typów referencyjnych + + + + Internal members should not use oblivious types + Publiczne składowe nie powinny używać typów obojętnych + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Wszystkie publiczne składowe powinny używać typów referencyjnych dopuszczających wartość null lub nie dopuszczających jej, ale nie nieświadomych typów referencyjnych. + + + + Symbol '{0}' uses some oblivious reference types + Symbol „{0}” używa pewnych nieświadomych typów referencyjnych + + + + Public members should not use oblivious types + Publiczne składowe nie powinny używać typów nieświadomych + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + „{0}” narusza wymaganie dotyczące zgodności z kopią zapasową: „Interfejs API z opcjonalnymi parametrami powinien mieć najwięcej parametrów spośród przeciążeń publicznych”. Aby uzyskać szczegółowe informacje, zobacz „{1}”. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + Publiczny interfejs API z opcjonalnymi parametrami powinien mieć najwięcej parametrów spośród jego przeciążeń publicznych + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Brak pliku publicznego interfejsu API „{0}” lub nie jest on oznaczony jako dodatkowy plik analizatora + + + + Missing shipped or unshipped public API file + Brak wysłanego lub niewysłanego pliku publicznego interfejsu API + + + + The contents of the public API files are invalid: {0} + Zawartość plików publicznego interfejsu API jest nieprawidłowa: {0} + + + + The contents of the public API files are invalid + Zawartość plików publicznego interfejsu API jest nieprawidłowa + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + W przypadku usunięcia typu wewnętrznego lub składowej należy usunąć także powiązany wpis w pliku PublicAPI.Unshipped.txt. z prefiksem ’*REMOVED*’. To przyciąga uwagę do zmian interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym nieprawidłowe działanie. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Symbol „{0}” jest częścią zadeklarowanego wewnętrznego interfejsu API, ale nie jest wewnętrzny lub nie można go znaleźć + + + + Remove deleted types and members from the declared internal API + Usuń skasowane typy i składowe ze zadeklarowanego wewnętrznego interfejsu API + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + W przypadku usunięcia typu publicznego lub składowej należy usunąć także powiązany wpis w pliku PublicAPI.Unshipped.txt. z prefiksem ’*REMOVED*’. To przyciąga uwagę do zmian interfejsu API w przeglądach kodu i historii kontroli źródła oraz pomaga zapobiegać zmianom powodującym nieprawidłowe działanie. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Symbol „{0}” jest częścią zadeklarowanego interfejsu API, ale nie jest publiczny lub nie można go odnaleźć + + + + Remove deleted types and members from the declared API + Usuń usunięte typy i składowe z zadeklarowanego interfejsu API + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Symbol „{0}” został oznaczony jako usunięty, ale nie jest usuwany w kodzie źródłowym + + + + API is marked as removed but it exists in source code + Publiczny interfejs API został oznaczony jako usunięty, ale istnieje w kodzie źródłowym + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Pliki PublicAPI.txt powinny zawierać wartość „#nullable enable” na potrzeby śledzenia informacji o dopuszczaniu wartości null lub należy pominąć tę diagnostykę. W przypadku włączenia informacji o dopuszczaniu wartości null plik PublicAPI.txt rejestruje typy dopuszczające wartość null (sufiks „?” dla typu) i niedopuszczające wartości null (sufiks „!”). Śledzi on również wszelkie interfejsy API, które nadal korzystają z nieświadomego typu odwołania (prefiks „~” w wierszu). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + W pliku PublicAPI.txt brakuje elementu „#nullable enable”, dlatego adnotacje opcji dopuszczania wartości null nie są rejestrowane. Zaleca się włączenie tego śledzenia. + + + + Enable tracking of nullability of reference types in the declared API + Włącz śledzenie wartości null typów referencyjnych w zadeklarowanym interfejsie API + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Pliki PublicAPI.txt powinny zawierać wartość „#nullable enable” w celu śledzenia informacji o dopuszczaniu wartości null lub należy pominąć tę diagnostykę. W przypadku włączenia informacji o dopuszczaniu wartości null plik PublicAPI.txt rejestruje typy dopuszczające wartość null (sufiks „?” dla typu) i niedopuszczające wartości null (sufiks „!”). Śledzi on również wszelkie interfejsy API, które nadal korzystają z nieświadomego typu odwołania (prefiks „~” w wierszu). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + W pliku PublicAPI.txt brakuje elementu „#nullable enable”, dlatego adnotacje opcji dopuszczania wartości null nie są rejestrowane. Zaleca się włączenie tego śledzenia. + + + + Enable tracking of nullability of reference types in the declared API + Włącz śledzenie wartości null typów referencyjnych w zadeklarowanym interfejsie API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf new file mode 100644 index 0000000000000..891b40eb41e4d --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.pt-BR.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Adicione todos os itens do documento '{0}' à API + + + + Add all items in project '{0}' to the API + Adicione todos os itens do projeto '{0}' à API + + + + Add all items in the solution to the API + Adicione todos os itens da solução à API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Todos os tipos e membros internos devem ser declarados com anotações de nulidade em InternalAPI.txt. Isso chama a atenção para as alterações de nulidade da API nas revisões de código e no histórico de controle do código-fonte e ajuda a evitar alterações significativas. + + + + Symbol '{0}' is missing nullability annotations in the declared API + O símbolo '{0}' não tem anotações de nulidade na API declarada + + + + Annotate nullability of internal types and members in the declared API + Anote a nulidade de tipos e membros internos na API declarada + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Todos os tipos e membros públicos devem ser declarados com anotações de nulidade no PublicAPI.txt. Isso chama a atenção para as alterações de nulidade da API no histórico de revisões de código e de controle do código-fonte e ajuda a evitar alterações interruptivas. + + + + Symbol '{0}' is missing nullability annotations in the declared API + O símbolo '{0}' não tem anotações de nulidade na API declarada + + + + Annotate nullability of public types and members in the declared API + Anotar a nulidade de tipos e membros públicos na API declarada + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + O símbolo '{0}' viola o requisito de backcompat: 'Não adicione várias sobrecargas com parâmetros opcionais'. Confira '{1}' para obter detalhes. + + + + Do not add multiple public overloads with optional parameters + Não adicione várias sobrecargas públicas com parâmetros opcionais + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Todos os tipos e membros internos devem ser declarados em InternalAPI.txt. Isso chama a atenção para as alterações da API nas revisões de código e no histórico de controle do código-fonte e ajuda a evitar alterações significativas. + + + + Symbol '{0}' is not part of the declared API + O símbolo '{0}' não faz parte da API declarada + + + + Add internal types and members to the declared API + Adicionar tipos e membros internos à API declarada + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Todos os tipos e membros públicos devem ser declarados no PublicAPI.txt. Isso chama a atenção para as alterações da API no histórico de revisões de código e de controle do código-fonte e ajuda a evitar alterações interruptivas. + + + + Symbol '{0}' is not part of the declared public API + O símbolo '{0}' não faz parte da API pública declarada + + + + Add public types and members to the declared API + Adicionar membros e tipos públicos à API declarada + + + + The symbol '{0}' appears more than once in the internal API files + O símbolo '{0}' aparece mais de uma vez nos arquivos internos da API + + + + Do not duplicate symbols in internal API files + Não duplique símbolos em arquivos de API internos + + + + The symbol '{0}' appears more than once in the public API files + O símbolo '{0}' aparece mais de uma vez nos arquivos da API pública + + + + Do not duplicate symbols in public API files + Não duplique símbolos em arquivos de API pública + + + + Enable nullability annotations in the API for project '{0}' + Habilitar anotações de nulidade na API para o projeto '{0}' + + + + Enable nullability annotations in the API for the solution + Ativar anotações de nulidade na API para a solução + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + O construtor faz com que sua classe base não herdável seja herdável, expondo seus membros protegidos + + + + Constructor make noninheritable base class inheritable + O construtor transforma a classe base não herdável em herdável + + + + implicit constructor for '{0}' + construtor implícito para '{0}' + + + + implicit get-accessor for '{0}' + acessador get implícito para '{0}' + + + + implicit set-accessor for '{0}' + acessador set implícito para '{0}' + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + O arquivo de API interno '{0}' está ausente ou não está marcado como um arquivo de analisador adicional + + + + Missing shipped or unshipped internal API file + Arquivo de API interno enviado ou não enviado ausente + + + + The contents of the internal API files are invalid: {0} + O conteúdo dos arquivos internos da API é inválido: {0} + + + + The contents of the internal API files are invalid + O conteúdo dos arquivos internos da API é inválido + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Todos os membros internos devem usar tipos de referência anuláveis ​​ou não anuláveis, mas nenhum tipo de referência inconsciente. + + + + Symbol '{0}' uses some oblivious reference types + O símbolo '{0}' usa alguns tipos de referência vagos + + + + Internal members should not use oblivious types + Membros internos não devem usar tipos alheios + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Todos os membros públicos devem usar tipos de referência que permitem valor nulo ou não anuláveis, mas nenhum tipo de referência inconsistente. + + + + Symbol '{0}' uses some oblivious reference types + O símbolo '{0}' usa alguns tipos de referência vagos + + + + Public members should not use oblivious types + Os membros públicos não devem usar tipos inconsistentes + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}' viola o requisito de backcompat: 'API com parâmetro(s) opcional deve ter o maior número de parâmetros entre suas sobrecargas públicas'. Veja '{1}' para detalhes. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + API com parâmetro(s) opcional deve ter o maior número de parâmetros entre suas sobrecargas públicas + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + O arquivo de API pública '{0}' está ausente ou não está marcado como um arquivo do analisador adicional + + + + Missing shipped or unshipped public API file + Arquivo de API pública enviado ou não enviado ausente + + + + The contents of the public API files are invalid: {0} + O conteúdo dos arquivos da API pública é inválido: {0} + + + + The contents of the public API files are invalid + O conteúdo dos arquivos da API pública é inválido + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Ao remover um tipo ou membro interno, coloque essa entrada em InternalAPI.Unshipped.txt com o prefixo '*REMOVED*'. Isso chama a atenção para as alterações da API nas revisões de código e no histórico de controle do código-fonte e ajuda a evitar alterações significativas. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + O símbolo '{0}' faz parte da API interna declarada, mas não é interno ou não foi encontrado + + + + Remove deleted types and members from the declared internal API + Remova tipos e membros excluídos da API interna declarada + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Ao remover um tipo ou membro público, coloque essa entrada em PublicAPI.Unshipped.txt com o prefixo '*REMOVED*'. Isso chama a atenção para as alterações da API nas revisões de código e no histórico de controle de origem e ajuda a evitar alterações significativas. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + O símbolo '{0}' faz parte da API declarada, mas não é público ou não pôde ser encontrado + + + + Remove deleted types and members from the declared API + Remova os tipos e membros excluídos da API declarada + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Símbolo "{0}" está marcado como removido, mas não é excluído no código-fonte + + + + API is marked as removed but it exists in source code + A API está marcada como removida, mas existe no código-fonte + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Os arquivos InternalAPI.txt devem ter `#nullable enable` para rastrear informações de nulidade ou esse diagnóstico deve ser suprimido. Com a nulidade habilitada, InternalAPI.txt registra quais tipos são anuláveis ​​(sufixo `?` no tipo) ou não anuláveis ​​(sufixo `!`). Ele também rastreia qualquer API que ainda esteja usando um tipo de referência inconsciente (prefixo `~` on line). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt está faltando '#nullable enable', então as anotações de nulidade da API não são registradas. Recomenda-se habilitar esse rastreamento. + + + + Enable tracking of nullability of reference types in the declared API + Habilitar o acompanhamento de nulidade de tipos de referência na API declarada + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Os arquivos PublicAPI.txt devem ter `#nullable enable` para controlar as informações de nulidade ou esse diagnóstico deve ser suprimido. Com a nulidade habilitada, o PublicAPI.txt registra quais tipos são anuláveis (sufixo `?` no tipo) ou não anuláveis (sufixo `!`). Ele também rastreia APIs que ainda estejam usando um tipo de referência alheio (prefixo `~` na linha). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + O PublicAPI.txt está sem '#nullable enable', portanto, as anotações de nulidade da API não estão sendo registradas. É recomendado que esse controle seja habilitado. + + + + Enable tracking of nullability of reference types in the declared API + Habilitar o acompanhamento de nulidade de tipos de referência na API declarada + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf new file mode 100644 index 0000000000000..8c9cea27358ff --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.ru.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + Добавить все элементы документа "{0}" в API + + + + Add all items in project '{0}' to the API + Добавить все элементы проекта "{0}" в API + + + + Add all items in the solution to the API + Добавить все элементы решения в API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Все внутренние типы и элементы должны быть объявлены в InternalAPI.txt. с заметками допустимости значений NULL. Это привлекает внимание к изменениям допустимости значений NULL в API при проверках кода и в журнале системы управления версиями и помогает предотвратить критические изменения. + + + + Symbol '{0}' is missing nullability annotations in the declared API + В объявленном API для символа "{0}" отсутствуют заметки о допустимости значений NULL. + + + + Annotate nullability of internal types and members in the declared API + Добавлять заметки о допустимости значений NULL для типов и элементов в объявленном API + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Все открытые типы и элементы должны быть объявлены в PublicAPI.txt с заметками допустимости значений NULL. Это привлекает внимание к изменениям допустимости значений NULL в API при проверках кода и в журнале системы управления версиями и помогает предотвратить критические изменения. + + + + Symbol '{0}' is missing nullability annotations in the declared API + В объявленном API для символа "{0}" отсутствуют заметки о допустимости значений NULL. + + + + Annotate nullability of public types and members in the declared API + Аннотирование допустимости значений NULL для открытых типов и элементов в объявленном API + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + Символ "{0}" нарушает требование обратной совместимости: "Не добавляйте несколько перегрузок с необязательными параметрами". Дополнительные сведения см. в "{1}" . + + + + Do not add multiple public overloads with optional parameters + Не добавляйте несколько общедоступных перегрузок с необязательными параметрами + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Все внутренние типы и элементы должны быть объявлены в InternalAPI.txt.. Это привлекает внимание к изменениям API при проверках кода и в журнале системы управления версиями и помогает предотвратить критические изменения. + + + + Symbol '{0}' is not part of the declared API + Символ "{0}" не является частью объявленного API. + + + + Add internal types and members to the declared API + Добавить открытые типы и элементы в объявленный API + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Все открытые типы и элементы должны быть объявлены в PublicAPI.txt. Это привлекает внимание к изменениям API при проверках кода и в журнале системы управления версиями и помогает предотвратить критические изменения. + + + + Symbol '{0}' is not part of the declared public API + Символ "{0}" не является частью объявленного общедоступного API. + + + + Add public types and members to the declared API + Добавьте открытые типы и элементы в объявленный API + + + + The symbol '{0}' appears more than once in the internal API files + Символ "{0}" встречается больше одного раза в файлах внутреннего API. + + + + Do not duplicate symbols in internal API files + Не дублировать символы во внутренних файлах API + + + + The symbol '{0}' appears more than once in the public API files + Символ "{0}" встречается больше одного раза в файлах открытого API. + + + + Do not duplicate symbols in public API files + Не дублируйте символы в файлах открытого API + + + + Enable nullability annotations in the API for project '{0}' + Включить заметки о допустимости значений NULL в API для проекта "{0}" + + + + Enable nullability annotations in the API for the solution + Включить аннотации, допускающие значение NULL, в API для решения. + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Конструктор делает свой ненаследуемый базовый класс наследуемым, ставя под угрозу его защищенные элементы. + + + + Constructor make noninheritable base class inheritable + Конструктор делает ненаследуемый базовый класс наследуемым + + + + implicit constructor for '{0}' + неявный конструктор для "{0}" + + + + implicit get-accessor for '{0}' + неявный метод доступа get для "{0}" + + + + implicit set-accessor for '{0}' + неявный метод доступа set для "{0}" + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + Файл внутреннего API "{0}" отсутствует или не помечен как дополнительный файл анализатора. + + + + Missing shipped or unshipped internal API file + Отсутствует отправленный или неотправленный файл общедоступного API + + + + The contents of the internal API files are invalid: {0} + Недопустимое содержимое внутренних файлов API: {0} + + + + The contents of the internal API files are invalid + Недопустимое содержимое внутренних файлов API + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Все внутренние элементы должны использовать ссылочные типы, допускающие или не допускающие значение NULL, но не игнорируемые ссылочные типы. + + + + Symbol '{0}' uses some oblivious reference types + Символ "{0}" использует некоторые игнорируемые ссылочные типы. + + + + Internal members should not use oblivious types + Внутренние элементы не должны использовать игнорируемые типы + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Все открытые элементы должны использовать ссылочные типы, допускающие или не допускающие значение null, но не игнорируемые ссылочные типы. + + + + Symbol '{0}' uses some oblivious reference types + Символ "{0}" использует некоторые игнорируемые ссылочные типы. + + + + Public members should not use oblivious types + Открытые элементы не должны использовать игнорируемые типы + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + "{0}" нарушает требование обратной совместимости: "большинство параметров открытого API с необязательными параметрами должны находиться среди его общедоступных перегрузок". Дополнительные сведения см. в "{1}". + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + Большинство параметров открытого API с необязательными параметрами должны находиться среди его общедоступных перегрузок. + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + Файл общедоступного API "{0}" отсутствует или не помечен как дополнительный файл анализатора. + + + + Missing shipped or unshipped public API file + Отсутствует отправленный или неотправленный файл общедоступного API + + + + The contents of the public API files are invalid: {0} + Недопустимое содержимое файлов открытого API: {0} + + + + The contents of the public API files are invalid + Недопустимое содержимое файлов открытого API + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + При удалении внутреннего типа или элемента поместите эту запись в файл InternalAPI.Unshipped.txt с префиксом "*REMOVED*". Это привлекает внимание к изменениям API в проверках кода и журнала управления версиями и помогает предотвратить критические изменения. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + Символ "{0}" является частью объявленного внутреннего API, но не является внутренним или не найден + + + + Remove deleted types and members from the declared internal API + Удалить удаленные типы и элементы из объявленного API + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + При удалении открытого типа или элемента поместите эту запись в файл PublicAPI.Unshipped.txt с префиксом "*REMOVED*". Это привлечет внимание к изменениями API при проверке кода и в истории управления версиями, а также поможет предотвратить критические изменения. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + Символ "{0}" является частью объявленного API, однако не является открытым либо не был найден + + + + Remove deleted types and members from the declared API + Удалите удаленные типы и элементы из объявленного API + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + Символ "{0}" помечен как удаленный, но он не удален в исходном коде + + + + API is marked as removed but it exists in source code + API помечен как удаленный, но существует в исходном коде + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Для файлов InternalAPI.txt необходимо установить директиву "#nullable enable", чтобы отслеживать сведения о допустимости значений NULL или отключить этот параметр диагностики. Если значения NULL являются допустимыми, в файлах InternalAPI.txt записываются типы, допускающие значения NULL (суффикс "?" типа) или не допускают значения NULL (суффикс "!"). Также отслеживаются все API, в которых все еще используется ссылочный тип без заметок о допустимости значений NULL (префикс "~" в строке). + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + В файле PublicAPI.txt отсутствует "#nullable enable", поэтому заметки о допустимости значений NULL для API не записываются. Рекомендуется включить это отслеживание. + + + + Enable tracking of nullability of reference types in the declared API + Включение отслеживания допустимости значений NULL ссылочных типов в объявленном API + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + Для файлов PublicAPI.txt необходимо установить директиву #nullable enable, чтобы отслеживать сведения о допустимости значений NULL, либо отключить этот параметр диагностики. Если значения NULL являются допустимыми, в файлах PublicAPI.txt записываются типы, которые допускают значения NULL (суффикс "?" типа) или не допускают значения NULL (суффикс "!"). Также отслеживаются все API, в которых все еще используется ссылочный тип без заметок о допустимости значений NULL (префикс "~" в строке). + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + В файле PublicAPI.txt отсутствует "#nullable enable", поэтому заметки о допустимости значений NULL для API не записываются. Рекомендуется включить их отслеживание. + + + + Enable tracking of nullability of reference types in the declared API + Включение отслеживания допустимости значений NULL ссылочных типов в объявленном API + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf new file mode 100644 index 0000000000000..468345d6785bb --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.tr.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + '{0}' belgesindeki tüm öğeleri API'ye ekle + + + + Add all items in project '{0}' to the API + '{0}' projesindeki tüm öğeleri API'ye ekle + + + + Add all items in the solution to the API + Çözümdeki tüm öğeleri API'ye ekleyin + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tüm genel türler ve üyeler PublicAPI.txt içinde null atanabilirlik ek açıklamalarıyla tanımlanmalıdır. Bu işlem, kod incelemeleri ve kaynak denetimi geçmişindeki API null atanabilirlik değişikliklerine dikkat çeker ve hataya neden olan değişikliklerin önlenmesine yardımcı olur. + + + + Symbol '{0}' is missing nullability annotations in the declared API + '{0}' sembolünde, bildirilen API'deki null atanabilirlik ek açıklamaları eksik + + + + Annotate nullability of internal types and members in the declared API + Tanımlanan API'deki dahili türlerin ve üyelerin null atanabilirliğine not ekleyin + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + Tüm genel türler ve üyeler PublicAPI.txt içinde null atanabilirlik ek açıklamalarıyla bildirilmelidir. Bu işlem, kod incelemeleri ve kaynak denetimi geçmişindeki API null atanabilirlik değişikliklerine dikkat çeker ve hataya neden olan değişikliklerin önlenmesine yardımcı olur. + + + + Symbol '{0}' is missing nullability annotations in the declared API + '{0}' sembolünde, bildirilen API'deki null atanabilirlik ek açıklamaları eksik + + + + Annotate nullability of public types and members in the declared API + Bildirilen API'deki genel türlerin ve üyelerin null atanabilirliğine not ekle + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + '{0}' sembolü geriye dönük uyumluluk gereksinimini ihlal ediyor: 'İsteğe bağlı parametreleri olan birden fazla aşırı yükleme eklemeyin'. Ayrıntılar için bkz. '{1}'. + + + + Do not add multiple public overloads with optional parameters + İsteğe bağlı parametreleri olan birden fazla genel aşırı yükleme eklemeyin + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tüm dahili türler ve üyeler InternalAPI.txt içinde tanımlanmalıdır. Bu, kod incelemelerindeki ve kaynak kontrol geçmişindeki API değişikliklerine dikkat çeker ve hataya neden olan değişiklikleri önlemeye yardımcı olur. + + + + Symbol '{0}' is not part of the declared API + '{0}' sembolü bildirilen API'nin bir parçası değil + + + + Add internal types and members to the declared API + Tanımlanan API'ye dahili türler ve üyeler ekleyin + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Tüm genel türler ve üyeler PublicAPI.txt içinde bildirilmelidir. Bu işlem, kod incelemeleri ve kaynak denetimi geçmişindeki API değişikliklerine dikkat çeker ve hataya neden olan değişikliklerin önlenmesine yardımcı olur. + + + + Symbol '{0}' is not part of the declared public API + '{0}' sembolü, tanımlanan genel API'nin bir parçası değil + + + + Add public types and members to the declared API + Genel türleri ve üyeleri bildirilen API'ye ekleyin + + + + The symbol '{0}' appears more than once in the internal API files + '{0}' sembolü, dahili API dosyalarında birden çok kez görünüyor + + + + Do not duplicate symbols in internal API files + Sembolleri dahili API dosyalarında çoğaltmayın + + + + The symbol '{0}' appears more than once in the public API files + '{0}' sembolü, genel API dosyalarında birden çok kez görünüyor + + + + Do not duplicate symbols in public API files + Genel API dosyalarında sembolleri yinelemeyin + + + + Enable nullability annotations in the API for project '{0}' + '{0}' projesi için API'de null atanabilirlik ek açıklamalarını etkinleştir + + + + Enable nullability annotations in the API for the solution + Çözüm için API'de geçersizlik ek açıklamalarını etkinleştirin + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + Oluşturucu, devralınamaz temel sınıfını devralınabilir hale getirir, böylece sınıfın korumalı üyelerini kullanıma sunar + + + + Constructor make noninheritable base class inheritable + Oluşturucu, devralınamayan temel sınıfı devralınabilir hale getirir + + + + implicit constructor for '{0}' + '{0}' için örtük oluşturucu + + + + implicit get-accessor for '{0}' + '{0}' için örtük get erişimcisi + + + + implicit set-accessor for '{0}' + '{0}' için örtük set erişimcisi + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + '{0}' dahili API dosyası eksik veya ek bir çözümleyici dosyası olarak işaretlenmemiş + + + + Missing shipped or unshipped internal API file + Gönderilen veya gönderilmeyen dahili API dosyası eksik + + + + The contents of the internal API files are invalid: {0} + Dahili API dosyalarının içerikleri geçersiz: {0} + + + + The contents of the internal API files are invalid + Dahili API dosyalarının içeriği geçersiz + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tüm dahili üyeler, null atanabilir veya null atanamaz başvuru türleri kullanmalı, ancak habersiz başvuru türleri kullanmamalıdır. + + + + Symbol '{0}' uses some oblivious reference types + '{0}' sembolü uyarısız atanabilen bazı başvuru türlerini kullanıyor + + + + Internal members should not use oblivious types + Dahili üyeler habersiz türleri kullanmamalıdır + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + Tüm genel üyeler null atanabilir veya null atanamaz başvuru türleri kullanmalıdır ancak uyarısız atanabilen başvuru türü içermemelidir. + + + + Symbol '{0}' uses some oblivious reference types + '{0}' sembolü uyarısız atanabilen bazı başvuru türlerini kullanıyor + + + + Public members should not use oblivious types + Genel üyeler uyarısız atanabilen türler kullanmamalıdır + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}', geri uyumluluk gereksinimini ihlal ediyor: 'İsteğe bağlı parametre(ler) içeren API, genel aşırı yüklemeleri arasında en fazla parametreye sahip olmalıdır'. Ayrıntılar için '{1}' bölümüne bakın. + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + İsteğe bağlı parametrelere sahip API, genel aşırı yüklemeleri arasında en fazla parametreye sahip olmalıdır + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + '{0}' genel API dosyası eksik veya ek bir çözümleyici dosyası olarak işaretlenmemiş + + + + Missing shipped or unshipped public API file + Gönderilmiş veya gönderilmemiş genel API dosyası eksik + + + + The contents of the public API files are invalid: {0} + Genel API dosyalarının içerikleri geçersiz: {0} + + + + The contents of the public API files are invalid + Genel API dosyalarının içerikleri geçersiz + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Bir dahili türü veya üyeyi kaldırırken, o girişi '*REMOVED*' önekiyle InternalAPI.Unshipped.txt dosyasına koyun. Bu, kod incelemelerindeki ve kaynak denetimi geçmişindeki API değişikliklerine dikkat çeker ve değişikliklerin bozulmasını önlemeye yardımcı olur. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + '{0}' sembolü tanımlanan dahili API'nin parçası, ancak dahili değil veya bulunamadı + + + + Remove deleted types and members from the declared internal API + Silinen türleri ve üyeleri tanımlanan dahili API'den kaldırın + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + Bir genel türü veya üyeyi kaldırırken, bu girişi PublicAPI.Unshipped.txt dosyasına '*REMOVED*' önekiyle birlikte yerleştirin. Bu işlem, kod incelemeleri ve kaynak denetimi geçmişindeki API değişikliklerine dikkat çeker ve hataya neden olan değişikliklerin önlenmesine yardımcı olur. + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + '{0}' sembolü bildirilen API'nin parçası, ancak genel değil veya bulunamadı + + + + Remove deleted types and members from the declared API + Silinen türleri ve üyeleri bildirilen API'den kaldırın + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + '{0}' sembolü kaldırıldı olarak işaretlenmiş ancak kaynak kodunda silinmemiş + + + + API is marked as removed but it exists in source code + API kaldırıldı olarak işaretlendi ancak kaynak kodunda mevcut + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + InternalAPI.txt dosyalarında null atanabilirlik bilgilerini izlemek için `#nullable enable` bulunmalıdır veya bu tanılama gizlenmelidir. Null atanabilirlik etkin olduğunda InternalAPI.txt, null atanabilir (türde `?` soneki) veya null atanamaz (`!` soneki) türleri kaydeder. Ayrıca, hala bir habersiz başvuru türünü (satırda `~` ön eki) kullanan tüm API'leri de izler. + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt'de '#nullable enable' eksik, bu nedenle API'nin null atanabilirlik ek açıklamaları kaydedilmiyor. Bu izlemeyi etkinleştirmeniz önerilir. + + + + Enable tracking of nullability of reference types in the declared API + Bildirilen API'de başvuru türlerinin null olduğunu izlemeyi etkinleştirin + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + PublicAPI.txt dosyalarında null atanabilirlik bilgilerini izlemek için `#nullable enable` bulunmalıdır veya bu tanılama gizlenmelidir. Null atanabilirlik etkin olduğunda PublicAPI.txt, null atanabilir (türde `?` soneki) veya null atanamaz (`!` soneki) türleri kaydeder. Ayrıca, hala bir kayıtsız başvuru türünü (satırda `~` ön eki) kullanan tüm API'leri de izler. + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt dosyasında '#nullable enable' eksik olduğundan API'nin null atanabilirlik ek açıklamaları kaydedilmiyor. Bu izlemenin etkinleştirilmesi önerilir. + + + + Enable tracking of nullability of reference types in the declared API + Bildirilen API'de başvuru türlerinin null olduğunu izlemeyi etkinleştirin + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf new file mode 100644 index 0000000000000..2780621110e2a --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hans.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + 将文档“{0}”中的所有项添加到 API + + + + Add all items in project '{0}' to the API + 将项目“{0}”中的所有项添加到 API + + + + Add all items in the solution to the API + 将解决方案中的所有项添加到 API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 应在 InternalAPI.txt 中使用为 Null 性注释声明所有内部类型和成员。这将关注代码评审和源代码管理历史记录中的 API 为 Null 性更改,并有助于防止中断性变更。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + 符号“{0}”在声明的 API 中缺少为 Null 性注释 + + + + Annotate nullability of internal types and members in the declared API + 在声明的 API 中对内部类型和成员的为 Null 性进行批注 + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 应在 PublicAPI.txt 中使用为 Null 性注释声明所有公共类型和成员。这将关注代码评审和源代码管理历史记录中的 API 为 Null 性更改,并有助于防止中断性变更。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + 符号“{0}”在声明的 API 中缺少为 Null 性注释 + + + + Annotate nullability of public types and members in the declared API + 在声明的 API 中对公共类型和成员的为 Null 性进行批注 + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + 符号“{0}”违反了 backcompat 要求:“请勿添加具有可选参数的多个重载”。有关详细信息,请参阅“{1}”。 + + + + Do not add multiple public overloads with optional parameters + 请勿添加具有可选参数的多个公共重载 + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 应在 InternalAPI.txt 中声明所有内部类型和成员。这将关注代码评审和源代码管理历史记录中的 API 更改,并有助于防止重大更改。 + + + + Symbol '{0}' is not part of the declared API + 符号“{0}”不是已声明 API 的一部分 + + + + Add internal types and members to the declared API + 向已内部 API 添加公共类型和成员 + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 应在 PublicAPI.txt 中声明所有公共类型和成员。这将关注代码评审和源代码管理历史记录中的 API 更改,并有助于防止重大更改。 + + + + Symbol '{0}' is not part of the declared public API + 符号“{0}”不是已声明公共 API 的一部分 + + + + Add public types and members to the declared API + 向已声明 API 添加公共类型和成员 + + + + The symbol '{0}' appears more than once in the internal API files + 符号“{0}”多次出现在内部 API 文件中 + + + + Do not duplicate symbols in internal API files + 请勿在内部 API 文件中复制符号 + + + + The symbol '{0}' appears more than once in the public API files + 符号“{0}”多次出现在公共 API 文件中 + + + + Do not duplicate symbols in public API files + 请勿在公共 API 文件中复制符号 + + + + Enable nullability annotations in the API for project '{0}' + 在项目“{0}”的 API 中启用为 Null 性注释 + + + + Enable nullability annotations in the API for the solution + 在解决方案的 API 中启用为 Null 性注释 + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + 构造函数使其不可继承的基类变为可继承,从而暴露其受保护的成员 + + + + Constructor make noninheritable base class inheritable + 构造函数使不可继承的基类可以继承 + + + + implicit constructor for '{0}' + “{0}”的隐式构造函数 + + + + implicit get-accessor for '{0}' + “{0}”的隐式 get 访问器 + + + + implicit set-accessor for '{0}' + “{0}”的隐式 set 访问器 + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + 缺少内部 API 文件“{0}”,或者未将它标记为附加分析器文件 + + + + Missing shipped or unshipped internal API file + 缺少附带/未附带的内部 API 文件 + + + + The contents of the internal API files are invalid: {0} + 内部 API 文件的内容无效: {0} + + + + The contents of the internal API files are invalid + 内部 API 文件的内容无效 + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + 所有内部成员都应使用可以为 null 或不可为 null 的引用类型,但不能使用未知引用类型。 + + + + Symbol '{0}' uses some oblivious reference types + 符号“{0}”使用了一些未知引用类型 + + + + Internal members should not use oblivious types + 内部成员不得使用未知类型 + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + 所有公共成员都应使用可以为 null 或不可为 null 的引用类型,但不能使用未知引用类型。 + + + + Symbol '{0}' uses some oblivious reference types + 符号“{0}”使用了一些未知引用类型 + + + + Public members should not use oblivious types + 公共成员不得使用未知类型 + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + “{0}”违反了 backcompat 要求:“具有可选参数的 API 应在其公共重载中具有最多参数”。有关详细信息,请参阅“{1}”。 + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + 具有可选参数的 API 应在其公共重载中具有最多参数 + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + 缺少公共 API 文件“{0}”,或者未将它标记为附加分析器文件 + + + + Missing shipped or unshipped public API file + 缺少附带/未附带的公共 API 文件 + + + + The contents of the public API files are invalid: {0} + 公共 API 文件的内容无效: {0} + + + + The contents of the public API files are invalid + 公共 API 文件的内容无效 + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 删除内部类型或成员时,将其项输入 InternalAPI.Unshipped.txt 中,并附上前缀 "*REMOVED*"。这样能够在代码评审和源代码管理历史记录中引起对 API 更改的注意,有助于防止中断性更改。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + 符号“{0}”是已声明内部 API 的一部分,但此符号不是内部符号或无法找到 + + + + Remove deleted types and members from the declared internal API + 从已声明内部 API 中删除已删除的类型和成员 + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 删除公共类型或成员时,将其项输入 PublicAPI.Unshipped.txt 中,并附上前缀 "*REMOVED*"。这样能够在代码评审和源代码管理历史记录中引起对 API 更改的注意,有助于防止中断性更改。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + 符号“{0}”是已声明 API 的一部分,但此符号不是公共符号或无法找到 + + + + Remove deleted types and members from the declared API + 从已声明 API 中删除已删除的类型和成员 + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + 符号“{0}”标记为已删除,但未在源代码中删除 + + + + API is marked as removed but it exists in source code + API 标记为已删除,但它存在于源代码中 + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + InternalAPI.txt 文件应具有 "#nullable enable" 以跟踪为 Null 性信息,或者应取消显示此诊断。如果启用了为 Null 性,则 InternalAPI.txt 会记录哪些类型可为 Null (类型上带后缀 "?")而哪些不可为 Null (带后缀 "!")。它还会跟踪所有仍在使用不带此信息的引用类型(行上带前缀 "~007E;")的 API。 + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt 缺少 "#nullable enable",因此不会记录 API 的为 Null 性注释。建议启用此跟踪。 + + + + Enable tracking of nullability of reference types in the declared API + 在已声明 API 中启用对引用类型的为 Null 性的跟踪 + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + PublicAPI.txt 文件应具有 "#nullable enable" 以跟踪为 Null 性信息,或者应取消显示此诊断。如果启用了为 Null 性,则 PublicAPI.txt 会记录哪些类型可为 Null (类型上带后缀 "?")而哪些不可为 Null (带后缀 "!")。它还会跟踪所有仍在使用不带此信息的引用类型(行上带前缀 "~")的 API。 + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt 缺少 "#nullable enable",因此不会记录 API 的为 Null 性注释。建议启用此跟踪。 + + + + Enable tracking of nullability of reference types in the declared API + 在已声明 API 中启用对引用类型的为 Null 性的跟踪 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf new file mode 100644 index 0000000000000..8690ef0236738 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/Analyzers/xlf/PublicApiAnalyzerResources.zh-Hant.xlf @@ -0,0 +1,297 @@ + + + + + + Add all items in document '{0}' to the API + 將文件 '{0}' 中的所有項目新增至 API + + + + Add all items in project '{0}' to the API + 將專案 '{0}' 中的所有項目新增至 API + + + + Add all items in the solution to the API + 將解決方案中的所有項目新增至 API + + + + All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 應在 InternalAPI.txt 中宣告所有內部類型與成員並加上可 NULL 性註釋。這麼做會讓您注意到程式碼檢閱與原始檔控制歷程記錄中的 API 可 NULL 性變更,同時有助於避免重大變更。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + 在宣告的 API 中,符號 '{0}' 缺少可為 Null 的註釋 + + + + Annotate nullability of internal types and members in the declared API + 標註已宣告 API 中內部類型和成員的可 NULL 性 + + + + All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + 在 PublicAPI.txt 中宣告所有公用類型與成員時,應提供可 NULL 性註釋。此動作會引起對程式碼檢閱與原始檔控制記錄中 API 可 NULL 性變更的注意,有助於避免發生中斷性變更。 + + + + Symbol '{0}' is missing nullability annotations in the declared API + 在宣告的 API 中,符號 '{0}' 缺少可為 Null 的註釋 + + + + Annotate nullability of public types and members in the declared API + 標註已宣告 API 中公用類型和成員的可 NULL 性 + + + + Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + 符號 '{0}' 違反回溯相容性需求:「請勿新增具備選擇性參數的多項多載」。請參閱 '{1}' 以取得詳細資料。 + + + + Do not add multiple public overloads with optional parameters + 無法新增多個具有選擇性參數的公用多載 + + + + All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 應在 InternalAPI.txt 中宣告所有內部類型與成員。這麼做會讓您注意到程式碼檢閱與原始檔控制歷程記錄中的 API 變更,同時有助於避免重大變更。 + + + + Symbol '{0}' is not part of the declared API + 符號 '{0}' 並非宣告 API 的一部分 + + + + Add internal types and members to the declared API + 為宣告的 API 新增公用類型與成員 + + + + All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 應在 PublicAPI.txt 中宣告所有公用類型與成員。此舉會讓您注意到程式碼檢閱與原始檔控制歷程記錄中的 API 變更,同時有助於避免發生中斷性變更。 + + + + Symbol '{0}' is not part of the declared public API + 符號 '{0}' 並非宣告的公用 API 的一部分 + + + + Add public types and members to the declared API + 為宣告的 API 新增公用類型與成員 + + + + The symbol '{0}' appears more than once in the internal API files + 內部 API 檔案中出現多次符號 '{0}' + + + + Do not duplicate symbols in internal API files + 不要複製內部 API 檔案中的符號 + + + + The symbol '{0}' appears more than once in the public API files + 公用 API 檔案中出現多次符號 '{0}' + + + + Do not duplicate symbols in public API files + 請勿在公用 API 檔案中使用重複的符號 + + + + Enable nullability annotations in the API for project '{0}' + 啟用 API 中專案 '{0}' 的可 NULL 性註釋 + + + + Enable nullability annotations in the API for the solution + 啟用 API 中解決方案的可 NULL 性註釋 + + + + Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + 建構函式會讓無法繼承的基底類別變成可繼承,進而會公開其受保護的成員 + + + + Constructor make noninheritable base class inheritable + 建構函式可將無法繼承的基底類別變成為可繼承 + + + + implicit constructor for '{0}' + '{0}' 的隱含建構函式 + + + + implicit get-accessor for '{0}' + '{0}' 的隱含 get 存取子 + + + + implicit set-accessor for '{0}' + '{0}' 的隱含 set 存取子 + + + + Internal API file '{0}' is missing or not marked as an additional analyzer file + 遺失內部 API 檔案 '{0}',或未將其標示為額外的分析器檔案 + + + + Missing shipped or unshipped internal API file + 遺失隨附或未隨附的內部 API 檔案 + + + + The contents of the internal API files are invalid: {0} + 內部 API 檔案的內容無效: {0} + + + + The contents of the internal API files are invalid + 內部 API 檔案的內容無效 + + + + All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + 所有內部成員都應使用可為 Null 或不可為 Null 的參考類型,但不可使用無警示的參考類型。 + + + + Symbol '{0}' uses some oblivious reference types + 符號 '{0}' 使用了部分無必要的參考型別 + + + + Internal members should not use oblivious types + 內部成員不應使用無警示的類型 + + + + All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + 所有公開成員都應使用可為 Null 或不可為 Null 的參考型別,但不可使用無警示的參考型別。 + + + + Symbol '{0}' uses some oblivious reference types + 符號 '{0}' 使用了部分無必要的參考型別 + + + + Public members should not use oblivious types + 公開成員不應使用無警示類型 + + + + '{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + '{0}' 違反回溯相容性需求:「具有選擇性參數的 API,大部分的參數應位於其公用多載之間」。請參閱 '{1}' 以取得詳細資料。 + + + + API with optional parameter(s) should have the most parameters amongst its public overloads + 具有選擇性參數的 API,大部分的參數應位於其公用多載之間 + + + + Public API file '{0}' is missing or not marked as an additional analyzer file + 遺失公用 API 檔案 '{0}',或未將其標示為額外的分析器檔案 + + + + Missing shipped or unshipped public API file + 遺失隨附或未隨附的公用 API 檔案 + + + + The contents of the public API files are invalid: {0} + 公用 API 檔案的內容無效: {0} + + + + The contents of the public API files are invalid + 公用 API 檔案的內容無效 + + + + When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 移除內部類型或成員時,請將該項目放在 InternalAPI.Unshipped.txt 中並包含 '*REMOVED*' 前置詞。這麼做會讓您注意到程式碼檢閱與原始檔控制歷程記錄中的 API 變更,同時有助於避免重大變更。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared internal API, but is either not internal or could not be found + 符號 '{0}' 是宣告的內部 API 的一部分,但可能非內部或找不到 + + + + Remove deleted types and members from the declared internal API + 從宣告的內部 API 移除已刪除的類型和成員 + + + + When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + 移除公用類型或成員時,請將該項目放在 PublicAPI.Unshipped.txt 中並包含 '*REMOVED*' 前置詞。如此會讓您注意到在程式碼檢閱以及原始檔控制歷程記錄中 API 的變更,同時有助於避免發生中斷性變更。 + {Locked="*REMOVED*"} + + + Symbol '{0}' is part of the declared API, but is either not public or could not be found + 符號 '{0}' 是宣告 API 的一部分,但可能找不到或並非公用 + + + + Remove deleted types and members from the declared API + 從宣告的 API 移除已刪除的類型與成員 + + + + Symbol '{0}' is marked as removed but it isn't deleted in source code + 符號 '{0}' 標記為已移除,但未在原始程式碼中刪除 + + + + API is marked as removed but it exists in source code + API 標記為已移除,但存在於原始程式碼中 + + + + InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + InternalAPI.txt 檔案應該具備 `#nullable enable`,才能追蹤可為 NULL 的資訊,否則應隱藏此診斷。若啟用可為 NULL,InternalAPI.txt 便能記錄可為 Null (類型的尾碼為 `?`) 或不可為 Null (尾碼為 `!`) 的類型。其也會追蹤仍在使用無警示參考類型的任何 API (行的首碼為 `~`)。 + + + + InternalAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + InternalAPI.txt 遺失 '#nullable enable',因此未記錄 API 的可 NULL 性註釋。建議啟用此追蹤。 + + + + Enable tracking of nullability of reference types in the declared API + 在宣告 API 中啟用參考型別的可為 Null 追蹤 + + + + PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + PublicAPI.txt 檔案必須具備 '#nullable enable',才能追蹤可為 NULL 的資訊,否則應隱藏此診斷。若啟用可為 NULL,PublicAPI.txt 便能記錄型可為 Null (類型的尾碼為 '?') 或不可為 Null (尾碼為 '!') 的類型。其也會追蹤仍在使用未經察覺之參考型別的任何 API (行的首碼為 '~')。 + + + + PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + PublicAPI.txt 缺少 '#nullable enable',因此未記錄 API 的可 NULL 性註釋。建議啟用此追蹤。 + + + + Enable tracking of nullability of reference types in the declared API + 在宣告 API 中啟用參考型別的可為 Null 追蹤 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/AnnotatePublicApiFix.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/AnnotatePublicApiFix.cs new file mode 100644 index 0000000000000..2e489681e8b35 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/AnnotatePublicApiFix.cs @@ -0,0 +1,232 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; + +using DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = "AnnotatePublicApiFix"), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class AnnotatePublicApiFix() : CodeFixProvider + { + private const char ObliviousMarker = '~'; + + public sealed override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(DiagnosticIds.AnnotatePublicApiRuleId, DiagnosticIds.AnnotateInternalApiRuleId); + + public sealed override FixAllProvider GetFixAllProvider() + => new PublicSurfaceAreaFixAllProvider(); + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + Project project = context.Document.Project; + + foreach (Diagnostic diagnostic in context.Diagnostics) + { + string minimalSymbolName = diagnostic.Properties[DeclarePublicApiAnalyzer.MinimalNamePropertyBagKey]; + string publicSymbolName = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamePropertyBagKey]; + string publicSymbolNameWithNullability = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNameWithNullabilityPropertyBagKey]; + string fileName = diagnostic.Properties[DeclarePublicApiAnalyzer.FileName]; + + TextDocument? document = project.GetPublicApiDocument(fileName); + + if (document != null) + { + context.RegisterCodeFix( + new DeclarePublicApiFix.AdditionalDocumentChangeAction( + $"Annotate {minimalSymbolName} in public API", + document.Id, + isPublic: diagnostic.Id == DiagnosticIds.AnnotatePublicApiRuleId, + c => GetFixAsync(document, publicSymbolName, publicSymbolNameWithNullability, c)), + diagnostic); + } + } + + return Task.CompletedTask; + + static async Task GetFixAsync(TextDocument publicSurfaceAreaDocument, string oldSymbolName, string newSymbolName, CancellationToken cancellationToken) + { + SourceText sourceText = await publicSurfaceAreaDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + SourceText newSourceText = AnnotateSymbolNamesInSourceText(sourceText, new Dictionary { [oldSymbolName] = newSymbolName }); + + return publicSurfaceAreaDocument.Project.Solution.WithAdditionalDocumentText(publicSurfaceAreaDocument.Id, newSourceText); + } + } + + private static SourceText AnnotateSymbolNamesInSourceText(SourceText sourceText, Dictionary changes) + { + if (changes.Count == 0) + { + return sourceText; + } + + List lines = DeclarePublicApiFix.GetLinesFromSourceText(sourceText); + + for (int i = 0; i < lines.Count; i++) + { + if (changes.TryGetValue(lines[i].Trim(ObliviousMarker), out string newLine)) + { + lines.Insert(i, newLine); + lines.RemoveAt(i + 1); + } + } + + var endOfLine = sourceText.GetEndOfLine(); + SourceText newSourceText = sourceText.Replace(new TextSpan(0, sourceText.Length), string.Join(endOfLine, lines) + sourceText.GetEndOfFileText(endOfLine)); + return newSourceText; + } + + private class FixAllAdditionalDocumentChangeAction : CodeAction + { + private readonly List>> _diagnosticsToFix; + private readonly Solution _solution; + + public FixAllAdditionalDocumentChangeAction(string title, Solution solution, List>> diagnosticsToFix) + { + this.Title = title; + _solution = solution; + _diagnosticsToFix = diagnosticsToFix; + } + + public override string Title { get; } + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + var updatedPublicSurfaceAreaText = new List<(DocumentId, SourceText)>(); + + foreach (var (project, diagnostics) in _diagnosticsToFix) + { + IEnumerable> groupedDiagnostics = + diagnostics + .Where(d => d.Location.IsInSource) + .GroupBy(d => d.Location.SourceTree); + + var allChanges = new Dictionary>(); + + foreach (IGrouping grouping in groupedDiagnostics) + { + Document document = project.GetDocument(grouping.Key); + + if (document is null) + { + continue; + } + + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (Diagnostic diagnostic in grouping) + { + switch (diagnostic.Id) + { + case DiagnosticIds.AnnotateInternalApiRuleId: + case DiagnosticIds.AnnotatePublicApiRuleId: + break; + default: + continue; + } + + string oldName = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamePropertyBagKey]; + string newName = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNameWithNullabilityPropertyBagKey]; + bool isShipped = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiIsShippedPropertyBagKey] == "true"; + string fileName = diagnostic.Properties[DeclarePublicApiAnalyzer.FileName]; + + if (!allChanges.TryGetValue(fileName, out var mapToUpdate)) + { + mapToUpdate = new(); + allChanges.Add(fileName, mapToUpdate); + } + + mapToUpdate[oldName] = newName; + } + } + + foreach (var (path, changes) in allChanges) + { + var doc = project.GetPublicApiDocument(path); + + if (doc is not null) + { + var text = await doc.GetTextAsync(cancellationToken).ConfigureAwait(false); + SourceText newShippedSourceText = AnnotateSymbolNamesInSourceText(text, changes); + updatedPublicSurfaceAreaText.Add((doc.Id, newShippedSourceText)); + } + } + } + + Solution newSolution = _solution; + + foreach (var (docId, text) in updatedPublicSurfaceAreaText) + { + newSolution = newSolution.WithAdditionalDocumentText(docId, text); + } + + return newSolution; + } + } + + private class PublicSurfaceAreaFixAllProvider : FixAllProvider + { + public override async Task GetFixAsync(FixAllContext fixAllContext) + { + var diagnosticsToFix = new List>>(); + string? title; + switch (fixAllContext.Scope) + { + case FixAllScope.Document: + { + ImmutableArray diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + title = string.Format(CultureInfo.InvariantCulture, PublicApiAnalyzerResources.AddAllItemsInDocumentToTheApiTitle, fixAllContext.Document.Name); + break; + } + + case FixAllScope.Project: + { + Project project = fixAllContext.Project; + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + title = string.Format(CultureInfo.InvariantCulture, PublicApiAnalyzerResources.AddAllItemsInProjectToTheApiTitle, fixAllContext.Project.Name); + break; + } + + case FixAllScope.Solution: + { + foreach (Project project in fixAllContext.Solution.Projects) + { + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(project, diagnostics)); + } + + title = PublicApiAnalyzerResources.AddAllItemsInTheSolutionToTheApiTitle; + break; + } + + case FixAllScope.Custom: + return null; + + default: + Debug.Fail($"Unknown FixAllScope '{fixAllContext.Scope}'"); + return null; + } + + return new FixAllAdditionalDocumentChangeAction(title, fixAllContext.Solution, diagnosticsToFix); + } + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs new file mode 100644 index 0000000000000..8021b30c5974b --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/DeclarePublicApiFix.cs @@ -0,0 +1,395 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Diagnostics.Analyzers; +using DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = "DeclarePublicApiFix"), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class DeclarePublicApiFix() : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIds.DeclarePublicApiRuleId, DiagnosticIds.DeclareInternalApiRuleId); + + public sealed override FixAllProvider GetFixAllProvider() + { + return new PublicSurfaceAreaFixAllProvider(); + } + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + var project = context.Document.Project; + + foreach (Diagnostic diagnostic in context.Diagnostics) + { + bool isPublic = diagnostic.Id == DiagnosticIds.DeclarePublicApiRuleId; + string minimalSymbolName = diagnostic.Properties[DeclarePublicApiAnalyzer.MinimalNamePropertyBagKey]; + string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamePropertyBagKey]; + ImmutableHashSet siblingSymbolNamesToRemove = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamesOfSiblingsToRemovePropertyBagKey] + .Split(DeclarePublicApiAnalyzer.ApiNamesOfSiblingsToRemovePropertyBagValueSeparator.ToCharArray()) + .ToImmutableHashSet(); + + foreach (var file in GetUnshippedPublicApiFiles(context.Document.Project, isPublic)) + { + context.RegisterCodeFix( + new AdditionalDocumentChangeAction( + $"Add {minimalSymbolName} to API file {file?.Name}", + file?.Id, + isPublic, + c => GetFixAsync(file, isPublic, project, publicSurfaceAreaSymbolName, siblingSymbolNamesToRemove, c)), + diagnostic); + } + } + + return Task.CompletedTask; + } + + private static IEnumerable GetUnshippedPublicApiFiles(Project project, bool isPublic) + { + var count = 0; + + foreach (var additional in project.AdditionalDocuments) + { + var file = new PublicApiFile(additional.FilePath, isPublic); + + if (file.IsApiFile && !file.IsShipping) + { + yield return additional; + count++; + } + } + + if (count == 0) + { + yield return null; + } + } + + private static async Task GetFixAsync(TextDocument? surfaceAreaDocument, bool isPublic, Project project, string newSymbolName, ImmutableHashSet siblingSymbolNamesToRemove, CancellationToken cancellationToken) + { + if (surfaceAreaDocument == null) + { + var newSourceText = AddSymbolNamesToSourceText(sourceText: null, new[] { newSymbolName }); + return AddPublicApiFiles(project, newSourceText, isPublic); + } + else + { + var sourceText = await surfaceAreaDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + var newSourceText = AddSymbolNamesToSourceText(sourceText, new[] { newSymbolName }); + newSourceText = RemoveSymbolNamesFromSourceText(newSourceText, siblingSymbolNamesToRemove); + + return surfaceAreaDocument.Project.Solution.WithAdditionalDocumentText(surfaceAreaDocument.Id, newSourceText); + } + } + + private static Solution AddPublicApiFiles(Project project, SourceText unshippedText, bool isPublic) + { + Debug.Assert(unshippedText.Length > 0); + project = AddAdditionalDocument(project, isPublic ? DeclarePublicApiAnalyzer.PublicShippedFileName : DeclarePublicApiAnalyzer.InternalShippedFileName, SourceText.From(string.Empty)); + project = AddAdditionalDocument(project, isPublic ? DeclarePublicApiAnalyzer.PublicUnshippedFileName : DeclarePublicApiAnalyzer.InternalUnshippedFileName, unshippedText); + return project.Solution; + + // Local functions. + static Project AddAdditionalDocument(Project project, string name, SourceText text) + { + TextDocument? additionalDocument = project.AdditionalDocuments.FirstOrDefault(doc => string.Equals(doc.Name, name, StringComparison.OrdinalIgnoreCase)); + if (additionalDocument == null) + { + project = project.AddAdditionalDocument(name, text).Project; + } + + return project; + } + } + + private static SourceText AddSymbolNamesToSourceText(SourceText? sourceText, IEnumerable newSymbolNames) + { + List lines = GetLinesFromSourceText(sourceText); + + foreach (string name in newSymbolNames) + { + insertInList(lines, name); + } + + var endOfLine = sourceText.GetEndOfLine(); + + var newText = string.Join(endOfLine, lines) + sourceText.GetEndOfFileText(endOfLine); + return sourceText?.Replace(new TextSpan(0, sourceText.Length), newText) ?? SourceText.From(newText); + + // Insert name at the first suitable position + static void insertInList(List list, string name) + { + for (int i = 0; i < list.Count; i++) + { + if (IgnoreCaseWhenPossibleComparer.Instance.Compare(name, list[i]) < 0) + { + list.Insert(i, name); + return; + } + } + + list.Add(name); + } + } + + private static SourceText RemoveSymbolNamesFromSourceText(SourceText sourceText, ImmutableHashSet linesToRemove) + { + if (linesToRemove.IsEmpty) + { + return sourceText; + } + + List lines = GetLinesFromSourceText(sourceText); + IEnumerable newLines = lines.Where(line => !linesToRemove.Contains(line)); + + var endOfLine = sourceText.GetEndOfLine(); + SourceText newSourceText = sourceText.Replace(new TextSpan(0, sourceText.Length), string.Join(endOfLine, newLines) + sourceText.GetEndOfFileText(endOfLine)); + return newSourceText; + } + + internal static List GetLinesFromSourceText(SourceText? sourceText) + { + if (sourceText == null) + { + return new List(); + } + + var lines = new List(); + + foreach (TextLine textLine in sourceText.Lines) + { + string text = textLine.ToString(); + if (!string.IsNullOrWhiteSpace(text)) + { + lines.Add(text); + } + } + + return lines; + } + + internal class AdditionalDocumentChangeAction : CodeAction + { + private readonly Func> _createChangedAdditionalDocument; + + public AdditionalDocumentChangeAction(string title, DocumentId? apiDocId, bool isPublic, Func> createChangedAdditionalDocument) + { + this.Title = title; + EquivalenceKey = apiDocId.CreateEquivalenceKey(isPublic); + _createChangedAdditionalDocument = createChangedAdditionalDocument; + } + + public override string Title { get; } + + public override string EquivalenceKey { get; } + + protected override Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + return _createChangedAdditionalDocument(cancellationToken); + } + } + + private class FixAllAdditionalDocumentChangeAction : CodeAction + { + private readonly List>> _diagnosticsToFix; + private readonly bool _isPublic; + private readonly DocumentId? _apiDocId; + private readonly Solution _solution; + + public FixAllAdditionalDocumentChangeAction(string title, DocumentId? apiDocId, Solution solution, List>> diagnosticsToFix, bool isPublic) + { + this.Title = title; + _apiDocId = apiDocId; + _solution = solution; + _diagnosticsToFix = diagnosticsToFix; + this._isPublic = isPublic; + } + + public override string Title { get; } + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + var updatedPublicSurfaceAreaText = new List>(); + var addedPublicSurfaceAreaText = new List>(); + + foreach (KeyValuePair> pair in _diagnosticsToFix) + { + Project project = pair.Key; + ImmutableArray diagnostics = pair.Value; + + var publicSurfaceAreaAdditionalDocument = _apiDocId is not null ? project.GetAdditionalDocument(_apiDocId) : null; + var sourceText = publicSurfaceAreaAdditionalDocument != null ? + await publicSurfaceAreaAdditionalDocument.GetTextAsync(cancellationToken).ConfigureAwait(false) : + null; + + IEnumerable> groupedDiagnostics = + diagnostics + .Where(d => d.Location.IsInSource) + .GroupBy(d => d.Location.SourceTree); + + var newSymbolNames = new SortedSet(IgnoreCaseWhenPossibleComparer.Instance); + var symbolNamesToRemoveBuilder = PooledHashSet.GetInstance(); + + foreach (IGrouping grouping in groupedDiagnostics) + { + Document document = project.GetDocument(grouping.Key); + + if (document == null) + { + continue; + } + + SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + foreach (Diagnostic diagnostic in grouping) + { + if (diagnostic.Id is RoslynDiagnosticIds.ShouldAnnotatePublicApiFilesRuleId + or RoslynDiagnosticIds.ShouldAnnotateInternalApiFilesRuleId + or RoslynDiagnosticIds.ObliviousPublicApiRuleId + or RoslynDiagnosticIds.ObliviousInternalApiRuleId) + { + continue; + } + + string publicSurfaceAreaSymbolName = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamePropertyBagKey]; + + newSymbolNames.Add(publicSurfaceAreaSymbolName); + + string siblingNamesToRemove = diagnostic.Properties[DeclarePublicApiAnalyzer.ApiNamesOfSiblingsToRemovePropertyBagKey]; + if (siblingNamesToRemove.Length > 0) + { + var namesToRemove = siblingNamesToRemove.Split(DeclarePublicApiAnalyzer.ApiNamesOfSiblingsToRemovePropertyBagValueSeparator.ToCharArray()); + foreach (var nameToRemove in namesToRemove) + { + symbolNamesToRemoveBuilder.Add(nameToRemove); + } + } + } + } + + var symbolNamesToRemove = symbolNamesToRemoveBuilder.ToImmutableAndFree(); + + // We shouldn't be attempting to remove any symbol name, while also adding it. + Debug.Assert(newSymbolNames.All(newSymbolName => !symbolNamesToRemove.Contains(newSymbolName))); + + SourceText newSourceText = AddSymbolNamesToSourceText(sourceText, newSymbolNames); + newSourceText = RemoveSymbolNamesFromSourceText(newSourceText, symbolNamesToRemove); + + if (publicSurfaceAreaAdditionalDocument != null) + { + updatedPublicSurfaceAreaText.Add(new KeyValuePair(publicSurfaceAreaAdditionalDocument.Id, newSourceText)); + } + else if (newSourceText.Length > 0) + { + addedPublicSurfaceAreaText.Add(new KeyValuePair(project.Id, newSourceText)); + } + } + + Solution newSolution = _solution; + + foreach (KeyValuePair pair in updatedPublicSurfaceAreaText) + { + newSolution = newSolution.WithAdditionalDocumentText(pair.Key, pair.Value); + } + + // NOTE: We need to avoid creating duplicate files for multi-tfm projects. See https://github.com/dotnet/roslyn-analyzers/issues/3952. + using var uniqueProjectPaths = PooledHashSet.GetInstance(); + foreach (KeyValuePair pair in addedPublicSurfaceAreaText) + { + var project = newSolution.GetProject(pair.Key); + if (uniqueProjectPaths.Add(project.FilePath ?? project.Name)) + { + newSolution = AddPublicApiFiles(project, pair.Value, _isPublic); + } + } + + return newSolution; + } + } + + private class PublicSurfaceAreaFixAllProvider : FixAllProvider + { + public override async Task GetFixAsync(FixAllContext fixAllContext) + { + var diagnosticsToFix = new List>>(); + string? title; + switch (fixAllContext.Scope) + { + case FixAllScope.Document: + { + ImmutableArray diagnostics = await fixAllContext.GetDocumentDiagnosticsAsync(fixAllContext.Document).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + title = string.Format(CultureInfo.InvariantCulture, PublicApiAnalyzerResources.AddAllItemsInDocumentToTheApiTitle, fixAllContext.Document.Name); + break; + } + + case FixAllScope.Project: + { + Project project = fixAllContext.Project; + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(fixAllContext.Project, diagnostics)); + title = string.Format(CultureInfo.InvariantCulture, PublicApiAnalyzerResources.AddAllItemsInProjectToTheApiTitle, fixAllContext.Project.Name); + break; + } + + case FixAllScope.Solution: + { + foreach (Project project in fixAllContext.Solution.Projects) + { + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + diagnosticsToFix.Add(new KeyValuePair>(project, diagnostics)); + } + + title = PublicApiAnalyzerResources.AddAllItemsInTheSolutionToTheApiTitle; + break; + } + + case FixAllScope.Custom: + return null; + + default: + Debug.Fail($"Unknown FixAllScope '{fixAllContext.Scope}'"); + return null; + } + + return new FixAllAdditionalDocumentChangeAction(title, fixAllContext.CreateDocIdFromEquivalenceKey(out bool isPublic), fixAllContext.Solution, diagnosticsToFix, isPublic); + } + } + + private sealed class IgnoreCaseWhenPossibleComparer : IComparer + { + public static readonly IgnoreCaseWhenPossibleComparer Instance = new(); + + private IgnoreCaseWhenPossibleComparer() + { + } + + public int Compare(string x, string y) + { + var result = StringComparer.OrdinalIgnoreCase.Compare(x, y); + if (result == 0) + result = StringComparer.Ordinal.Compare(x, y); + + return result; + } + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes.csproj b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes.csproj new file mode 100644 index 0000000000000..1796777fca98b --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes.csproj @@ -0,0 +1,28 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForPublicApiAnalyzers) + true + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/NullableEnablePublicApiFix.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/NullableEnablePublicApiFix.cs new file mode 100644 index 0000000000000..16cb4f0293ffc --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/NullableEnablePublicApiFix.cs @@ -0,0 +1,162 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; +using DiagnosticIds = Roslyn.Diagnostics.Analyzers.RoslynDiagnosticIds; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = "NullableEnablePublicApiFix"), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class NullableEnablePublicApiFix() : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = + ImmutableArray.Create(DiagnosticIds.ShouldAnnotatePublicApiFilesRuleId, DiagnosticIds.ShouldAnnotateInternalApiFilesRuleId); + + public sealed override FixAllProvider GetFixAllProvider() + => new PublicSurfaceAreaFixAllProvider(); + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + Project project = context.Document.Project; + + foreach (Diagnostic diagnostic in context.Diagnostics) + { + var isPublic = diagnostic.Id == DiagnosticIds.ShouldAnnotatePublicApiFilesRuleId; + TextDocument? document = project.GetShippedDocument(isPublic); + + if (document != null) + { + context.RegisterCodeFix( + new DeclarePublicApiFix.AdditionalDocumentChangeAction( + $"Add '#nullable enable' to {(isPublic ? "public" : "internal")} API", + document.Id, + isPublic, + c => GetFixAsync(document, c)), + diagnostic); + } + } + + return Task.CompletedTask; + } + + private static async Task GetFixAsync(TextDocument surfaceAreaDocument, CancellationToken cancellationToken) + { + SourceText sourceText = await surfaceAreaDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + SourceText newSourceText = AddNullableEnable(sourceText); + + return surfaceAreaDocument.Project.Solution.WithAdditionalDocumentText(surfaceAreaDocument.Id, newSourceText); + } + + private static SourceText AddNullableEnable(SourceText sourceText) + { + string extraLine = "#nullable enable" + sourceText.GetEndOfLine(); + SourceText newSourceText = sourceText.WithChanges(new TextChange(new TextSpan(0, 0), extraLine)); + return newSourceText; + } + + private class FixAllAdditionalDocumentChangeAction : CodeAction + { + private readonly List _projectsToFix; + private readonly Solution _solution; + + public FixAllAdditionalDocumentChangeAction(string title, Solution solution, List projectsToFix) + { + this.Title = title; + _solution = solution; + _projectsToFix = projectsToFix; + } + + public override string Title { get; } + + protected override async Task GetChangedSolutionAsync(CancellationToken cancellationToken) + { + var updatedSurfaceAreaText = new List<(DocumentId, SourceText)>(); + + using var uniqueShippedDocuments = PooledHashSet.GetInstance(); + foreach (var project in _projectsToFix) + { + foreach (var isPublic in new[] { true, false }) + { + TextDocument? shippedDocument = project.GetShippedDocument(isPublic); + if (shippedDocument == null || + shippedDocument.FilePath != null && !uniqueShippedDocuments.Add(shippedDocument.FilePath)) + { + // Skip past duplicate shipped documents. + // Multi-tfm projects can likely share the same api files, and we want to avoid duplicate code fix application. + continue; + } + + var shippedSourceText = await shippedDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + SourceText newShippedSourceText = AddNullableEnable(shippedSourceText); + updatedSurfaceAreaText.Add((shippedDocument!.Id, newShippedSourceText)); + } + } + + Solution newSolution = _solution; + foreach (var (document, text) in updatedSurfaceAreaText) + { + newSolution = newSolution.WithAdditionalDocumentText(document, text); + } + + return newSolution; + } + } + + private class PublicSurfaceAreaFixAllProvider : FixAllProvider + { + public override async Task GetFixAsync(FixAllContext fixAllContext) + { + var projectsToFix = new List(); + string? title; + switch (fixAllContext.Scope) + { + case FixAllScope.Document: + case FixAllScope.Project: + { + projectsToFix.Add(fixAllContext.Project); + title = string.Format(CultureInfo.InvariantCulture, PublicApiAnalyzerResources.EnableNullableInProjectToTheApiTitle, fixAllContext.Project.Name); + break; + } + + case FixAllScope.Solution: + { + foreach (Project project in fixAllContext.Solution.Projects) + { + ImmutableArray diagnostics = await fixAllContext.GetAllDiagnosticsAsync(project).ConfigureAwait(false); + if (!diagnostics.IsEmpty) + { + projectsToFix.Add(project); + } + } + + title = PublicApiAnalyzerResources.EnableNullableInTheSolutionToTheApiTitle; + break; + } + + case FixAllScope.Custom: + return null; + + default: + Debug.Fail($"Unknown FixAllScope '{fixAllContext.Scope}'"); + return null; + } + + return new FixAllAdditionalDocumentChangeAction(title, fixAllContext.Solution, projectsToFix); + } + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/PublicApiFixHelpers.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/PublicApiFixHelpers.cs new file mode 100644 index 0000000000000..542bae97e2562 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Core/CodeFixes/PublicApiFixHelpers.cs @@ -0,0 +1,89 @@ +// 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 Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers +{ + internal static class PublicApiFixHelpers + { + private static readonly char[] SemicolonSplit = new[] { ';' }; + + internal static void Deconstruct(this KeyValuePair kv, out TKey key, out TValue value) + { + key = kv.Key; + value = kv.Value; + } + + private const string ApiDocEquivalenceKeyPrefix = "__ApiDoc__"; + + internal static string CreateEquivalenceKey(this DocumentId? doc, bool isPublic) + { + if (doc is null) + { + return $"{ApiDocEquivalenceKeyPrefix};;;{isPublic}"; + } + else + { + return $"{ApiDocEquivalenceKeyPrefix};{doc.ProjectId.Id};{doc.Id};{isPublic}"; + + } + } + + internal static DocumentId? CreateDocIdFromEquivalenceKey(this FixAllContext fixAllContext, out bool isPublic) + { + var split = fixAllContext.CodeActionEquivalenceKey.Split(SemicolonSplit, StringSplitOptions.RemoveEmptyEntries); + + isPublic = bool.Parse(split[^1]); + + if (split is [ApiDocEquivalenceKeyPrefix, var projectGuidString, var docGuidString, _] + && Guid.TryParse(projectGuidString, out var projectGuid) && projectGuid != Guid.Empty && + Guid.TryParse(docGuidString, out var docGuid) && docGuid != Guid.Empty) + { + var projectId = ProjectId.CreateFromSerialized(projectGuid); + return DocumentId.CreateFromSerialized(projectId, docGuid); + } + + return null; + } + + internal static TextDocument? GetPublicApiDocument(this Project project, string name) + { + return project.AdditionalDocuments.FirstOrDefault(doc => doc.Name.Equals(name, StringComparison.Ordinal)); + } + + internal static TextDocument? GetShippedDocument(this Project project, bool isPublic) + => project.GetPublicApiDocument(isPublic ? DeclarePublicApiAnalyzer.PublicShippedFileName : DeclarePublicApiAnalyzer.InternalShippedFileName); + + /// + /// Returns the trailing newline from the end of , if one exists. + /// + /// The source text. + /// if ends with a trailing newline; + /// otherwise, . + internal static string GetEndOfFileText(this SourceText? sourceText, string endOfLine) + { + if (sourceText == null || sourceText.Length == 0) + return string.Empty; + + var lastLine = sourceText.Lines[^1]; + return lastLine.Span.IsEmpty ? endOfLine : string.Empty; + } + + internal static string GetEndOfLine(this SourceText? sourceText) + { + if (sourceText?.Lines.Count > 1) + { + var firstLine = sourceText.Lines[0]; + return sourceText.ToString(new TextSpan(firstLine.End, firstLine.EndIncludingLineBreak - firstLine.End)); + } + + return Environment.NewLine; + } + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Directory.Build.props b/src/RoslynAnalyzers/PublicApiAnalyzers/Directory.Build.props new file mode 100644 index 0000000000000..92851f0e86467 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Directory.Build.props @@ -0,0 +1,8 @@ + + + + true + $(NoWarn);NU1701 + $(DefineConstants),MICROSOFT_CODEANALYSIS_PUBLIC_API_ANALYZERS + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md b/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md new file mode 100644 index 0000000000000..a5c26c6c5d228 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md @@ -0,0 +1,277 @@ +# Microsoft.CodeAnalysis.PublicApiAnalyzers + +## [RS0016](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Add public types and members to the declared API + +All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0017](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Remove deleted types and members from the declared API + +When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '\*REMOVED\*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0022](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Constructor make noninheritable base class inheritable + +Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0024](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): The contents of the public API files are invalid + +The contents of the public API files are invalid: {0} + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0025](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Do not duplicate symbols in public API files + +The symbol '{0}' appears more than once in the public API files + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0026](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md): Do not add multiple public overloads with optional parameters + +Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0027](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md): API with optional parameter(s) should have the most parameters amongst its public overloads + +'{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0036](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Annotate nullability of public types and members in the declared API + +All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0037](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Enable tracking of nullability of reference types in the declared API + +PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0041](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Public members should not use oblivious types + +All public members should use either nullable or non-nullable reference types, but no oblivious reference types. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0048](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Missing shipped or unshipped public API file + +Public API file '{0}' is missing or not marked as an additional analyzer file + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0050](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): API is marked as removed but it exists in source code + +Symbol '{0}' is marked as removed but it isn't deleted in source code + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0051](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Add internal types and members to the declared API + +All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0052](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Remove deleted types and members from the declared internal API + +When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '\*REMOVED\*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0053](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): The contents of the internal API files are invalid + +The contents of the internal API files are invalid: {0} + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0054](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Do not duplicate symbols in internal API files + +The symbol '{0}' appears more than once in the internal API files + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0055](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Annotate nullability of internal types and members in the declared API + +All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0056](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Enable tracking of nullability of reference types in the declared API + +InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line). + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- + +## [RS0057](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Internal members should not use oblivious types + +All internal members should use either nullable or non-nullable reference types, but no oblivious reference types. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0058](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Missing shipped or unshipped internal API file + +Internal API file '{0}' is missing or not marked as an additional analyzer file + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0059](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md): Do not add multiple public overloads with optional parameters + +Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0060](https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md): API with optional parameter(s) should have the most parameters amongst its public overloads + +'{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details. + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [RS0061](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md): Constructor make noninheritable base class inheritable + +Constructor makes its noninheritable base class inheritable, thereby exposing its protected members + +|Item|Value| +|-|-| +|Category|ApiDesign| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.sarif b/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.sarif new file mode 100644 index 0000000000000..ec800473771af --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.sarif @@ -0,0 +1,470 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Microsoft.CodeAnalysis.PublicApiAnalyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0016": { + "id": "RS0016", + "shortDescription": "Add public types and members to the declared API", + "fullDescription": "All public types and members should be declared in PublicAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0017": { + "id": "RS0017", + "shortDescription": "Remove deleted types and members from the declared API", + "fullDescription": "When removing a public type or member, put that entry in PublicAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0022": { + "id": "RS0022", + "shortDescription": "Constructor make noninheritable base class inheritable", + "fullDescription": "Constructor makes its noninheritable base class inheritable, thereby exposing its protected members", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0024": { + "id": "RS0024", + "shortDescription": "The contents of the public API files are invalid", + "fullDescription": "The contents of the public API files are invalid: {0}", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0025": { + "id": "RS0025", + "shortDescription": "Do not duplicate symbols in public API files", + "fullDescription": "The symbol '{0}' appears more than once in the public API files", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0026": { + "id": "RS0026", + "shortDescription": "Do not add multiple public overloads with optional parameters", + "fullDescription": "Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0027": { + "id": "RS0027", + "shortDescription": "API with optional parameter(s) should have the most parameters amongst its public overloads", + "fullDescription": "'{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0036": { + "id": "RS0036", + "shortDescription": "Annotate nullability of public types and members in the declared API", + "fullDescription": "All public types and members should be declared with nullability annotations in PublicAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0037": { + "id": "RS0037", + "shortDescription": "Enable tracking of nullability of reference types in the declared API", + "fullDescription": "PublicAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, PublicAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line).", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0041": { + "id": "RS0041", + "shortDescription": "Public members should not use oblivious types", + "fullDescription": "All public members should use either nullable or non-nullable reference types, but no oblivious reference types.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0048": { + "id": "RS0048", + "shortDescription": "Missing shipped or unshipped public API file", + "fullDescription": "Public API file '{0}' is missing or not marked as an additional analyzer file", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0050": { + "id": "RS0050", + "shortDescription": "API is marked as removed but it exists in source code", + "fullDescription": "Symbol '{0}' is marked as removed but it isn't deleted in source code", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": true, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0051": { + "id": "RS0051", + "shortDescription": "Add internal types and members to the declared API", + "fullDescription": "All internal types and members should be declared in InternalAPI.txt. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0052": { + "id": "RS0052", + "shortDescription": "Remove deleted types and members from the declared internal API", + "fullDescription": "When removing a internal type or member, put that entry in InternalAPI.Unshipped.txt with '*REMOVED*' prefix. This draws attention to API changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0053": { + "id": "RS0053", + "shortDescription": "The contents of the internal API files are invalid", + "fullDescription": "The contents of the internal API files are invalid: {0}", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0054": { + "id": "RS0054", + "shortDescription": "Do not duplicate symbols in internal API files", + "fullDescription": "The symbol '{0}' appears more than once in the internal API files", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0055": { + "id": "RS0055", + "shortDescription": "Annotate nullability of internal types and members in the declared API", + "fullDescription": "All internal types and members should be declared with nullability annotations in InternalAPI.txt. This draws attention to API nullability changes in the code reviews and source control history, and helps prevent breaking changes.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0056": { + "id": "RS0056", + "shortDescription": "Enable tracking of nullability of reference types in the declared API", + "fullDescription": "InternalAPI.txt files should have `#nullable enable` to track nullability information, or this diagnostic should be suppressed. With nullability enabled, InternalAPI.txt records which types are nullable (suffix `?` on type) or non-nullable (suffix `!`). It also tracks any API that is still using an oblivious reference type (prefix `~` on line).", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0057": { + "id": "RS0057", + "shortDescription": "Internal members should not use oblivious types", + "fullDescription": "All internal members should use either nullable or non-nullable reference types, but no oblivious reference types.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0058": { + "id": "RS0058", + "shortDescription": "Missing shipped or unshipped internal API file", + "fullDescription": "Internal API file '{0}' is missing or not marked as an additional analyzer file", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0059": { + "id": "RS0059", + "shortDescription": "Do not add multiple public overloads with optional parameters", + "fullDescription": "Symbol '{0}' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See '{1}' for details.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0060": { + "id": "RS0060", + "shortDescription": "API with optional parameter(s) should have the most parameters amongst its public overloads", + "fullDescription": "'{0}' violates the backcompat requirement: 'API with optional parameter(s) should have the most parameters amongst its public overloads'. See '{1}' for details.", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0061": { + "id": "RS0061", + "shortDescription": "Constructor make noninheritable base class inheritable", + "fullDescription": "Constructor makes its noninheritable base class inheritable, thereby exposing its protected members", + "defaultLevel": "warning", + "helpUri": "https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md", + "properties": { + "category": "ApiDesign", + "isEnabledByDefault": false, + "typeName": "DeclarePublicApiAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Microsoft.CodeAnalysis.PublicApiAnalyzers.CodeFixes", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md b/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md new file mode 100644 index 0000000000000..1cb2dd116484d --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/PublicApiAnalyzers.Help.md @@ -0,0 +1,79 @@ +# How to use Microsoft.CodeAnalysis.PublicApiAnalyzers + +To get started with the Public API Analyzer: + +1. Add a package reference to [Microsoft.CodeAnalysis.PublicApiAnalyzers](https://www.nuget.org/packages/Microsoft.CodeAnalysis.PublicApiAnalyzers) to your project. +2. You will have `RS0016` diagnostics on all your public APIs. +3. Invoke the codefix on any `RS0016` to add the public APIs to the documented set. You can apply the codefix across the entire project or solution to easily document all APIs at once. Text files `PublicAPI.Shipped.txt` and `PublicAPI.Unshipped.txt` will be added to each project in scope, if they do not already exist. + +**Configuration:** If you would prefer the public API analyzer to bail out silently for projects with missing public API files, you can do so by setting the following .editorconfig option: + +```ini +[*.cs] +dotnet_public_api_analyzer.require_api_files = true +``` + +See [Analyzer Configuration.md](../../docs/Analyzer%20Configuration.md) for more details on how to setup editorconfig based configuration. + +## Package version earlier than 3.3.x + +If you are using a `Microsoft.CodeAnalysis.PublicApiAnalyzers` package with version prior to 3.3.x, then you will need to manually create the following public API files in each project directory that needs to be analyzed. Additionally, you will need to mark the above files as analyzer additional files to enable analysis. + +- `PublicAPI.Shipped.txt` +- `PublicAPI.Unshipped.txt` + +This can be done by: + +- In Visual Studio, right-click the project in Solution Explorer, choose "Add -> New Item...", and then select "Text File" in the "Add New Item" dialog. Then right-click each file, select "Properties", and choose "C# analyzer additional file" for "Build Action" in the "Properties" window. +- Or, create these two files at the location you desire, then add the following text to your project/target file (replace file path with its actual location): + +```xml + + + + +``` + +## Nullable reference type support + +To enable support for [nullable reference types](https://learn.microsoft.com/dotnet/csharp/nullable-references), make sure that you are using a Roslyn compiler version 3.5 (or newer) in your build process and then add the following at the top of each `PublicAPI.*.txt` file: + +```xml +#nullable enable +``` + +One way of checking the version of your compiler is to add `#error version` in a source file, then looking at the error message output in your build logs. + +At that point, reference types in annotated code will need to be annotated with either a `?` (nullable) or a `!` (non-nullable). For instance, `C.AnnotatedMethod(string! nonNullableParameter, string? nullableParameter, int valueTypeParameter) -> void`. + +Any public API that haven't been annotated (i.e. uses an oblivious reference type) will be tracked with a `~` marker. The marker lets you track how many public APIs still lack annotations. For instance, `~C.ObliviousMethod() -> string`. + +We recommend to enable [RS0041 warning](https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/PublicApiAnalyzers/Microsoft.CodeAnalysis.PublicApiAnalyzers.md) if you start with a fresh project or your project has reached 100% annotation on your public API to ensure that all public APIs remain annotated. +If you are in the process of annotating an existing project, we recommended to disable this warning until you complete the annotation. The rule can be disabled via `.editorconfig` with `dotnet_diagnostic.RS0041.severity = none`. + +## Conditional API Differences + +Sometimes APIs vary by compilation symbol such as target framework. + +For example when using the [`#if` preprocessor directive](https://learn.microsoft.com/dotnet/csharp/language-reference/preprocessor-directives/preprocessor-if): + +```c# + public void Foo(string s) + {} + +#if NETCOREAPP3_0 + public void Foo(ReadOnlySpan s) + {} +#else +``` + +To correctly model the API differences between target frameworks (or any other property), use multiple instances of the `PublicAPI.*.txt` files. + +If you have multiple target frameworks and APIs differ between them, use the following in your project file: + +```xml + + + + +``` diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/PublicApiAnalyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..baf6a2f1f7f6b --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/RulesMissingDocumentation.md @@ -0,0 +1,23 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +RS0016 | | Add public types and members to the declared API | +RS0017 | | Remove deleted types and members from the declared API | +RS0022 | | Constructor make noninheritable base class inheritable | +RS0024 | | The contents of the public API files are invalid | +RS0025 | | Do not duplicate symbols in public API files | +RS0036 | | Annotate nullability of public types and members in the declared API | +RS0037 | | Enable tracking of nullability of reference types in the declared API | +RS0041 | | Public members should not use oblivious types | +RS0048 | | Missing shipped or unshipped public API file | +RS0050 | | API is marked as removed but it exists in source code | +RS0051 | | Add internal types and members to the declared API | +RS0052 | | Remove deleted types and members from the declared internal API | +RS0053 | | The contents of the internal API files are invalid | +RS0054 | | Do not duplicate symbols in internal API files | +RS0055 | | Annotate nullability of internal types and members in the declared API | +RS0056 | | Enable tracking of nullability of reference types in the declared API | +RS0057 | | Internal members should not use oblivious types | +RS0058 | | Missing shipped or unshipped internal API file | +RS0061 | | Constructor make noninheritable base class inheritable | diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/Microsoft.CodeAnalysis.PublicApiAnalyzers.Setup.csproj b/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/Microsoft.CodeAnalysis.PublicApiAnalyzers.Setup.csproj new file mode 100644 index 0000000000000..463974e9c0925 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/Microsoft.CodeAnalysis.PublicApiAnalyzers.Setup.csproj @@ -0,0 +1,25 @@ + + + + netstandard2.0 + + true + false + false + false + false + false + true + + + + + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..48985cd14ab1f --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,25 @@ + + + + + + Public API Analyzers + Public API Analyzers + EULA.txt + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsBase.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsBase.cs new file mode 100644 index 0000000000000..fc3a32d6dc09f --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsBase.cs @@ -0,0 +1,574 @@ +// 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. + +#pragma warning disable CA1305 + +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public abstract class AnnotatePublicApiAnalyzerTestsBase + { + protected abstract bool IsInternalTest { get; } + protected abstract string EnabledModifier { get; } + protected abstract string ShippedFileName { get; } + protected abstract string UnshippedFileName { get; } + protected abstract string UnshippedFileNamePrefix { get; } + protected abstract string AnnotateApiId { get; } + protected abstract string ShouldAnnotateApiFilesId { get; } + protected abstract string ObliviousApiId { get; } + + protected abstract IEnumerable DisabledDiagnostics { get; } + + #region Utilities + private async Task VerifyCSharpAsync(string source, string shippedApiText, string unshippedApiText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + }, + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAdditionalFileFixAsync(string source, string oldShippedApiText, string oldUnshippedApiText, string newShippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(source, oldShippedApiText, oldUnshippedApiText, newShippedApiText, newUnshippedApiText); + } + + private async Task VerifyAdditionalFileFixAsync(string source, string oldShippedApiText, string oldUnshippedApiText, string newShippedApiText, string newUnshippedApiText) + { + var test = new CSharpCodeFixTest(); + + test.TestState.Sources.Add(source); + test.TestState.AdditionalFiles.Add((ShippedFileName, oldShippedApiText)); + test.TestState.AdditionalFiles.Add((UnshippedFileName, oldUnshippedApiText)); + + test.FixedState.AdditionalFiles.Add((ShippedFileName, newShippedApiText)); + test.FixedState.AdditionalFiles.Add((UnshippedFileName, newUnshippedApiText)); + + await test.RunAsync(); + } + #endregion + + #region Fix tests + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task NoObliviousWhenUnannotatedClassConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C where T : class + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +C +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task NoObliviousWhenAnnotatedClassConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C where T : class? + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +C +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task NoObliviousWhenAnnotatedClassConstraintMultipleFiles() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C where T : class? + { + } + """; + + var shippedText = @"#nullable enable"; + var unshippedText1 = @"#nullable enable +C.C() -> void +"; + var unshippedText2 = @"#nullable enable +C +"; + + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = + { + (ShippedFileName, shippedText), + (UnshippedFileName, unshippedText1), + (UnshippedFileNamePrefix + "test" + DeclarePublicApiAnalyzer.Extension, unshippedText2), + }, + }, + }; + + await test.RunAsync(); + } + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task ObliviousWhenObliviousClassConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class {|{{ObliviousApiId}}:C|} // oblivious + #nullable disable + where T : class + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +~C +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task NoObliviousWhenUnannotatedReferenceTypeConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class D { } + {{EnabledModifier}} class C where T : D + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +C +D +D.D() -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task NoObliviousWhenAnnotatedReferenceTypeConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class D { } + {{EnabledModifier}} class C where T : D? + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +C +D +D.D() -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(4040, "https://github.com/dotnet/roslyn-analyzers/issues/4040")] + public async Task ObliviousWhenObliviousReferenceTypeConstraintAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class D { } + + {{EnabledModifier}} class {|{{ObliviousApiId}}:C|} // oblivious + #nullable disable + where T : D + { + } + """; + + var shippedText = @""; + var unshippedText = @"#nullable enable +C.C() -> void +~C +D +D.D() -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task DoNotAnnotateMemberInUnannotatedUnshippedAPI_NullableAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? {|{{ShouldAnnotateApiFilesId}}:Field|}; + } + """; + + var shippedText = @""; + var unshippedText = @"C +C.C() -> void +C.Field -> string"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task DoNotAnnotateMemberInUnannotatedUnshippedAPI_NonNullableAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string {|{{ShouldAnnotateApiFilesId}}:Field2|}; + } + """; + + var shippedText = @""; + var unshippedText = @"C +C.C() -> void +C.Field2 -> string"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task DoNotAnnotateMemberInUnannotatedShippedAPIAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? {|{{ShouldAnnotateApiFilesId}}:Field|}; + {{EnabledModifier}} string {|{{ShouldAnnotateApiFilesId}}:Field2|}; + } + """; + + var shippedText = @"C +C.C() -> void +C.Field -> string +C.Field2 -> string"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task AnnotatedMemberInAnnotatedShippedAPIAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? OldField; + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field2|}; + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string +C.Field2 -> string"; + + var unshippedText = @""; + + var fixedShippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string? +C.Field2 -> string!"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task AnnotatedMemberInAnnotatedUnshippedAPI_EnabledViaUnshippedAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? OldField; + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field2|}; + } + """; + + var unshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string +C.Field2 -> string"; + + var shippedText = @""; + + var fixedUnshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string? +C.Field2 -> string!"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, shippedText, fixedUnshippedText); + } + + [Fact] + public async Task AnnotatedMemberInAnnotatedUnshippedAPI_EnabledViaMultipleUnshippedAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? OldField; + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field2|}; + } + """; + + var unshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string +C.Field2 -> string"; + + var shippedText = @""; + + var fixedUnshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string? +C.Field2 -> string!"; + + var test = new CSharpCodeFixTest(); + + test.TestState.Sources.Add(source); + + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedText)); + test.TestState.AdditionalFiles.Add((UnshippedFileName, string.Empty)); + test.TestState.AdditionalFiles.Add((UnshippedFileNamePrefix + "test" + DeclarePublicApiAnalyzer.Extension, unshippedText)); + + test.FixedState.AdditionalFiles.Add((ShippedFileName, shippedText)); + test.FixedState.AdditionalFiles.Add((UnshippedFileName, string.Empty)); + test.FixedState.AdditionalFiles.Add((UnshippedFileNamePrefix + "test" + DeclarePublicApiAnalyzer.Extension, fixedUnshippedText)); + + await test.RunAsync(); + } + + [Fact] + public async Task AnnotatedMemberInAnnotatedUnshippedAPI_EnabledViaShippedAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? OldField; + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field2|}; + } + """; + + var shippedText = @"#nullable enable"; + var unshippedText = @"C +C.C() -> void +C.OldField -> string? +C.Field -> string +C.Field2 -> string"; + + var fixedUnshippedText = @"C +C.C() -> void +C.OldField -> string? +C.Field -> string? +C.Field2 -> string!"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newShippedApiText: shippedText, fixedUnshippedText); + } + + [Fact] + public async Task AnnotatedMemberInAnnotatedUnshippedAPI_EnabledViaBothAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? OldField; + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field2|}; + } + """; + + var shippedText = @"#nullable enable"; + var unshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string +C.Field2 -> string"; + + var fixedUnshippedText = @"#nullable enable +C +C.C() -> void +C.OldField -> string? +C.Field -> string? +C.Field2 -> string!"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newShippedApiText: shippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestAddAndRemoveMembers_CSharp_Fix_WithAddedNullability_WithoutObliviousAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? {|{{AnnotateApiId}}:ChangedField|}; + } + """; + var shippedText = $@"#nullable enable"; + var unshippedText = @"C +C.C() -> void +C.ChangedField -> string"; + var fixedUnshippedText = @"C +C.C() -> void +C.ChangedField -> string?"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newShippedApiText: shippedText, fixedUnshippedText); + } + + [Fact] + public async Task LegacyAPIShouldBeAnnotatedWithObliviousMarkerAsync() + { + var source = $$""" + {{EnabledModifier}} class C + { + {{EnabledModifier}} string {|{{AnnotateApiId}}:{|{{ObliviousApiId}}:Field|}|}; // oblivious + } + """; + var shippedText = $@"#nullable enable"; + var unshippedText = @"C +C.C() -> void +C.Field -> string"; + var fixedUnshippedText = @"C +C.C() -> void +~C.Field -> string"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newShippedApiText: shippedText, fixedUnshippedText); + } + + [Fact] + public async Task LegacyAPIShouldBeAnnotatedWithObliviousMarker_ShippedFileAsync() + { + var source = $$""" + {{EnabledModifier}} class C + { + {{EnabledModifier}} string {|{{AnnotateApiId}}:{|{{ObliviousApiId}}:Field|}|}; // oblivious + } + """; + var shippedText = $@"#nullable enable +C +C.C() -> void +C.Field -> string"; + var unshippedText = @""; + var fixedShippedText = $@"#nullable enable +C +C.C() -> void +~C.Field -> string"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task LegacyAPIWithObliviousMarkerGetsAnnotatedAsNullableAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string? {|{{AnnotateApiId}}:Field|}; + } + """; + var shippedText = $@"#nullable enable +C +C.C() -> void +~C.Field -> string"; + var unshippedText = @""; + var fixedShippedText = $@"#nullable enable +C +C.C() -> void +C.Field -> string?"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task LegacyAPIWithObliviousMarkerGetsAnnotatedAsNotNullableAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifier}} class C + { + {{EnabledModifier}} string {|{{AnnotateApiId}}:Field|}; + } + """; + var shippedText = $@"#nullable enable +C +C.C() -> void +~C.Field -> string"; + var unshippedText = @""; + var fixedShippedText = $@"#nullable enable +C +C.C() -> void +C.Field -> string!"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedShippedText, newUnshippedApiText: unshippedText); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsInternal.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsInternal.cs new file mode 100644 index 0000000000000..e0f45c68d40dd --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsInternal.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Roslyn.Diagnostics.Analyzers; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public class AnnotatePublicApiAnalyzerTestsInternal : AnnotatePublicApiAnalyzerTestsBase + { + protected override bool IsInternalTest => true; + protected override string EnabledModifier => "internal"; + protected override string ShippedFileName => DeclarePublicApiAnalyzer.InternalShippedFileName; + protected override string UnshippedFileName => DeclarePublicApiAnalyzer.InternalUnshippedFileName; + protected override string UnshippedFileNamePrefix => DeclarePublicApiAnalyzer.InternalUnshippedFileNamePrefix; + protected override string AnnotateApiId => RoslynDiagnosticIds.AnnotateInternalApiRuleId; + protected override string ShouldAnnotateApiFilesId => RoslynDiagnosticIds.ShouldAnnotateInternalApiFilesRuleId; + protected override string ObliviousApiId => RoslynDiagnosticIds.ObliviousInternalApiRuleId; + + protected override IEnumerable DisabledDiagnostics => new[] { + RoslynDiagnosticIds.DeclarePublicApiRuleId, + RoslynDiagnosticIds.RemoveDeletedPublicApiRuleId, + RoslynDiagnosticIds.PublicApiFilesInvalid, + RoslynDiagnosticIds.DuplicatedSymbolInPublicApiFiles, + RoslynDiagnosticIds.AnnotatePublicApiRuleId, + RoslynDiagnosticIds.ShouldAnnotatePublicApiFilesRuleId, + RoslynDiagnosticIds.ObliviousPublicApiRuleId, + RoslynDiagnosticIds.PublicApiFileMissing, + RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersPublic, + RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersPublic, + RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleIdPublic, + }; + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsPublic.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsPublic.cs new file mode 100644 index 0000000000000..c6e81578b1f1c --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/AnnotatePublicApiAnalyzerTestsPublic.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Roslyn.Diagnostics.Analyzers; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public class AnnotatePublicApiAnalyzerTestsPublic : AnnotatePublicApiAnalyzerTestsBase + { + protected override bool IsInternalTest => false; + protected override string EnabledModifier => "public"; + protected override string ShippedFileName => DeclarePublicApiAnalyzer.PublicShippedFileName; + protected override string UnshippedFileName => DeclarePublicApiAnalyzer.PublicUnshippedFileName; + protected override string UnshippedFileNamePrefix => DeclarePublicApiAnalyzer.PublicUnshippedFileNamePrefix; + protected override string AnnotateApiId => RoslynDiagnosticIds.AnnotatePublicApiRuleId; + protected override string ShouldAnnotateApiFilesId => RoslynDiagnosticIds.ShouldAnnotatePublicApiFilesRuleId; + protected override string ObliviousApiId => RoslynDiagnosticIds.ObliviousPublicApiRuleId; + + protected override IEnumerable DisabledDiagnostics => new[] { + RoslynDiagnosticIds.DeclareInternalApiRuleId, + RoslynDiagnosticIds.RemoveDeletedInternalApiRuleId, + RoslynDiagnosticIds.InternalApiFilesInvalid, + RoslynDiagnosticIds.DuplicatedSymbolInInternalApiFiles, + RoslynDiagnosticIds.AnnotateInternalApiRuleId, + RoslynDiagnosticIds.ShouldAnnotateInternalApiFilesRuleId, + RoslynDiagnosticIds.ObliviousInternalApiRuleId, + RoslynDiagnosticIds.InternalApiFileMissing, + RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersInternal, + RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersInternal, + RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleIdInternal, + }; + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs new file mode 100644 index 0000000000000..d90694b131f11 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsBase.cs @@ -0,0 +1,3301 @@ +// 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 enable + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; +using Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public abstract class DeclarePublicApiAnalyzerTestsBase + { + protected abstract bool IsInternalTest { get; } + protected abstract string EnabledModifierCSharp { get; } + protected abstract string DisabledModifierCSharp { get; } + protected abstract string EnabledModifierVB { get; } + protected abstract string DisabledModifierVB { get; } + protected abstract string ShippedFileName { get; } + protected abstract string UnshippedFileName { get; } + protected abstract string UnshippedFileNamePrefix { get; } + protected abstract string AddNewApiId { get; } + protected abstract string RemoveApiId { get; } + protected abstract string DuplicatedSymbolInApiFileId { get; } + protected abstract string ShouldAnnotateApiFilesId { get; } + protected abstract string ObliviousApiId { get; } + + protected abstract DiagnosticDescriptor DeclareNewApiRule { get; } + protected abstract DiagnosticDescriptor RemoveDeletedApiRule { get; } + protected abstract DiagnosticDescriptor DuplicateSymbolInApiFiles { get; } + protected abstract DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParameters { get; } + protected abstract DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParameters { get; } + protected abstract DiagnosticDescriptor AnnotateApiRule { get; } + protected abstract DiagnosticDescriptor ObliviousApiRule { get; } + protected abstract DiagnosticDescriptor ApiFilesInvalid { get; } + protected abstract DiagnosticDescriptor ApiFileMissing { get; } + protected abstract IEnumerable DisabledDiagnostics { get; } + + #region Helpers + private static DiagnosticResult GetAdditionalFileResultAt(int line, int column, string path, DiagnosticDescriptor descriptor, params object[] arguments) + { +#pragma warning disable RS0030 // Do not use banned APIs + return new DiagnosticResult(descriptor) + .WithLocation(path, line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(arguments); + } + + private static DiagnosticResult GetCSharpResultAt(int line, int column, DiagnosticDescriptor descriptor, params object[] arguments) + { +#pragma warning disable RS0030 // Do not use banned APIs + return new DiagnosticResult(descriptor) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(arguments); + } + + private static DiagnosticResult GetBasicResultAt(int line, int column, DiagnosticDescriptor descriptor, params object[] arguments) + { +#pragma warning disable RS0030 // Do not use banned APIs + return new DiagnosticResult(descriptor) + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(arguments); + } + + private async Task VerifyBasicAsync(string source, string shippedApiText, string unshippedApiText, params DiagnosticResult[] expected) + { + var test = new VisualBasicCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + }, + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAsync(string source, string? shippedApiText, string? unshippedApiText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + }, + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAsync(string source, string? shippedApiText, string? unshippedApiText, string editorConfigText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixVerifier.Test + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + AnalyzerConfigFiles = { ("/.editorconfig", editorConfigText) }, + }, + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAsync(Action addSourcesAction, string? shippedApiText, string? unshippedApiText, string editorConfigText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixVerifier.Test + { + TestState = + { + AdditionalFiles = { }, + AnalyzerConfigFiles = { ("/.editorconfig", editorConfigText) }, + }, + }; + + addSourcesAction(test.TestState.Sources); + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAsync(string source, string shippedApiText, string unshippedApiText, string shippedApiFilePath, string unshippedApiFilePath, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + } + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((shippedApiFilePath, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((unshippedApiFilePath, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + await test.RunAsync(); + } + + private async Task VerifyCSharpAdditionalFileFixAsync(string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedApiText, oldUnshippedApiText, newUnshippedApiText); + } + + private async Task VerifyNet50CSharpAdditionalFileFixAsync(string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedApiText, oldUnshippedApiText, newUnshippedApiText, ReferenceAssemblies.Net.Net50); + } + + private async Task VerifyNet80CSharpAdditionalFileFixAsync(string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(LanguageNames.CSharp, source, shippedApiText, oldUnshippedApiText, newUnshippedApiText, ReferenceAssemblies.Net.Net80); + } + + private async Task VerifyAdditionalFileFixAsync(string language, string source, string? shippedApiText, string? oldUnshippedApiText, string newUnshippedApiText, + ReferenceAssemblies? referenceAssemblies = null) + { + var test = language == LanguageNames.CSharp + ? new CSharpCodeFixTest() + : (CodeFixTest)new VisualBasicCodeFixTest(); + + if (referenceAssemblies is not null) + { + test.ReferenceAssemblies = referenceAssemblies; + } + + test.TestState.Sources.Add(source); + if (shippedApiText != null) + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedApiText)); + if (oldUnshippedApiText != null) + test.TestState.AdditionalFiles.Add((UnshippedFileName, oldUnshippedApiText)); + + test.FixedState.AdditionalFiles.Add((ShippedFileName, shippedApiText ?? string.Empty)); + test.FixedState.AdditionalFiles.Add((UnshippedFileName, newUnshippedApiText)); + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + + await test.RunAsync(); + } + #endregion + + #region Diagnostic tests + + [Fact] + [WorkItem(2622, "https://github.com/dotnet/roslyn-analyzers/issues/2622")] + public async Task AnalyzerFileMissing_ShippedAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + string? shippedText = null; + string? unshippedText = @""; + + var expected = new DiagnosticResult(ApiFileMissing) + .WithArguments(ShippedFileName); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + [WorkItem(2622, "https://github.com/dotnet/roslyn-analyzers/issues/2622")] + public async Task AnalyzerFileMissing_UnshippedAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + string? shippedText = @""; + string? unshippedText = null; + + var expected = new DiagnosticResult(ApiFileMissing) + .WithArguments(UnshippedFileName); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Theory] + [InlineData("")] + [InlineData("dotnet_public_api_analyzer.require_api_files = false")] + [InlineData("dotnet_public_api_analyzer.require_api_files = true")] + [WorkItem(2622, "https://github.com/dotnet/roslyn-analyzers/issues/2622")] + public async Task AnalyzerFileMissing_BothAsync(string editorconfigText) + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + string? shippedText = null; + string? unshippedText = null; + + var expectedDiagnostics = Array.Empty(); + if (!editorconfigText.EndsWith("true", StringComparison.OrdinalIgnoreCase)) + { + expectedDiagnostics = new[] { GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C") }; + } + + await VerifyCSharpAsync(source, shippedText, unshippedText, $"[*]\r\n{editorconfigText}", expectedDiagnostics); + } + + [Fact] + public async Task AnalyzerFilePresent_MissingNonEnabledText() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + string? shippedText = ""; + string? unshippedText = ""; + + var expectedDiagnostics = new[] { GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C") }; + + await VerifyCSharpAsync(source, shippedText, unshippedText, $"[*]\r\ndotnet_public_api_analyzer.require_api_files = true", expectedDiagnostics); + } + + [Fact] + public async Task EmptyPublicAPIFilesAsync() + { + var source = @""; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task SimpleMissingTypeAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C")); + } + + [Fact, WorkItem(2690, "https://github.com/dotnet/wpf/issues/2690")] + public async Task XamlGeneratedNamespaceWorkaroundAsync() + { + var source = $$""" + + namespace XamlGeneratedNamespace { + {{EnabledModifierCSharp}} sealed class GeneratedInternalTypeHelper + { + } + } + """; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task SimpleMissingMember_CSharpAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + {{EnabledModifierCSharp}} int ArrowExpressionProperty => 0; + } + """; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // Test0.cs(2,14): error RS0016: Symbol 'C' is not part of the declared API. + GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C"), + // Test0.cs(2,14): warning RS0016: Symbol 'C.C() -> void' is not part of the declared API. + GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.C() -> void"), + // Test0.cs(4,16): error RS0016: Symbol 'C.Field -> int' is not part of the declared API. + GetCSharpResultAt(4, 10 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Field -> int"), + // Test0.cs(5,27): error RS0016: Symbol 'C.Property.get -> int' is not part of the declared API. + GetCSharpResultAt(5, 21 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Property.get -> int"), + // Test0.cs(5,32): error RS0016: Symbol 'C.Property.set -> void' is not part of the declared API. + GetCSharpResultAt(5, 26 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Property.set -> void"), + // Test0.cs(6,17): error RS0016: Symbol 'C.Method() -> void' is not part of the declared API. + GetCSharpResultAt(6, 11 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Method() -> void"), + // Test0.cs(7,43): error RS0016: Symbol 'C.ArrowExpressionProperty.get -> int' is not part of the declared API. + GetCSharpResultAt(7, 37 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.ArrowExpressionProperty.get -> int")); + } + + [Theory] + [InlineData("string ", "string!")] + [InlineData("string?", "string?")] + [InlineData("int ", "int")] + [InlineData("int? ", "int?")] + public async Task SimpleMissingMember_CSharp_NullableTypes(string csharp, string message) + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} {{csharp}} Field; + {{EnabledModifierCSharp}} {{csharp}} Property { get; set; } + {{EnabledModifierCSharp}} void Method({{csharp}} p) { } + {{EnabledModifierCSharp}} {{csharp}} ArrowExpressionProperty => default; + } + """; + + var shippedText = "#nullable enable"; + var unshippedText = "#nullable enable"; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // Test0.cs(2,14): error RS0016: Symbol 'C' is not part of the declared API. + GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C"), + // Test0.cs(2,14): warning RS0016: Symbol 'C.C() -> void' is not part of the declared API. + GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.C() -> void"), + // Test0.cs(4,16): error RS0016: Symbol 'C.Field -> int' is not part of the declared API. + GetCSharpResultAt(4, 14 + EnabledModifierCSharp.Length, DeclareNewApiRule, $"C.Field -> {message}"), + // Test0.cs(5,27): error RS0016: Symbol 'C.Property.get -> int' is not part of the declared API. + GetCSharpResultAt(5, 25 + EnabledModifierCSharp.Length, DeclareNewApiRule, $"C.Property.get -> {message}"), + // Test0.cs(5,32): error RS0016: Symbol 'C.Property.set -> void' is not part of the declared API. + GetCSharpResultAt(5, 30 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Property.set -> void"), + // Test0.cs(6,17): error RS0016: Symbol 'C.Method() -> void' is not part of the declared API. + GetCSharpResultAt(6, 11 + EnabledModifierCSharp.Length, DeclareNewApiRule, $"C.Method({message} p) -> void"), + // Test0.cs(7,43): error RS0016: Symbol 'C.ArrowExpressionProperty.get -> int' is not part of the declared API. + GetCSharpResultAt(7, 41 + EnabledModifierCSharp.Length, DeclareNewApiRule, $"C.ArrowExpressionProperty.get -> {message}")); + } + + [Fact, WorkItem(821, "https://github.com/dotnet/roslyn-analyzers/issues/821")] + public async Task SimpleMissingMember_BasicAsync() + { + var source = $""" + + Imports System + + {EnabledModifierVB} Class C + {EnabledModifierVB} Field As Integer + + {EnabledModifierVB} Property [Property]() As Integer + Get + Return m_Property + End Get + Set + m_Property = Value + End Set + End Property + Private m_Property As Integer + + {EnabledModifierVB} Sub Method() + End Sub + + {EnabledModifierVB} ReadOnly Property ReadOnlyAutoProperty As Integer = 0 + {EnabledModifierVB} Property NormalAutoProperty As Integer = 0 + End Class + """; + + var shippedText = @""; + var unshippedText = @""; + + await VerifyBasicAsync(source, shippedText, unshippedText, + // Test0.vb(4,14): warning RS0016: Symbol 'C' is not part of the declared API. + GetBasicResultAt(4, 14, DeclareNewApiRule, "C"), + // Test0.cs(2,14): warning RS0016: Symbol 'C.New() -> Void' is not part of the declared API. + GetBasicResultAt(4, 14, DeclareNewApiRule, "C.New() -> Void"), + // Test0.vb(5,12): warning RS0016: Symbol 'C.Field -> Integer' is not part of the declared API. + GetBasicResultAt(5, 12, DeclareNewApiRule, "C.Field -> Integer"), + // Test0.vb(8,9): warning RS0016: Symbol 'C.Property() -> Integer' is not part of the declared API. + GetBasicResultAt(8, 9, DeclareNewApiRule, "C.Property() -> Integer"), + // Test0.vb(11,9): warning RS0016: Symbol 'C.Property(Value As Integer) -> Void' is not part of the declared API. + GetBasicResultAt(11, 9, DeclareNewApiRule, "C.Property(Value As Integer) -> Void"), + // Test0.vb(17,16): warning RS0016: Symbol 'C.Method() -> Void' is not part of the declared API. + GetBasicResultAt(17, 16, DeclareNewApiRule, "C.Method() -> Void"), + // Test0.vb(20,30): warning RS0016: Symbol 'C.ReadOnlyAutoProperty() -> Integer' is not part of the declared API. + GetBasicResultAt(20, 30, DeclareNewApiRule, "C.ReadOnlyAutoProperty() -> Integer"), + // Test0.vb(21,21): warning RS0016: Symbol 'C.NormalAutoProperty() -> Integer' is not part of the declared API. + GetBasicResultAt(21, 21, DeclareNewApiRule, "C.NormalAutoProperty() -> Integer"), + // Test0.vb(21,21): warning RS0016: Symbol 'C.NormalAutoProperty(AutoPropertyValue As Integer) -> Void' is not part of the declared API. + GetBasicResultAt(21, 21, DeclareNewApiRule, "C.NormalAutoProperty(AutoPropertyValue As Integer) -> Void")); + } + + [Fact(), WorkItem(821, "https://github.com/dotnet/roslyn-analyzers/issues/821")] + public async Task SimpleMissingMember_Basic1Async() + { + var source = $""" + + Imports System + {EnabledModifierVB} Class C + Private m_Property As Integer + {EnabledModifierVB} Property [Property]() As Integer + ' Get + ' Return m_Property + ' End Get + ' Set + ' m_Property = Value + ' End Set + ' End Property + {EnabledModifierVB} ReadOnly Property ReadOnlyProperty0() As Integer + Get + Return m_Property + End Get + End Property + {EnabledModifierVB} WriteOnly Property WriteOnlyProperty0() As Integer + Set + m_Property = Value + End Set + End Property + {EnabledModifierVB} ReadOnly Property ReadOnlyProperty1 As Integer = 0 + {EnabledModifierVB} ReadOnly Property ReadOnlyProperty2 As Integer + {EnabledModifierVB} Property Property1 As Integer + End Class + + """; + + var shippedText = @" +C +C.New() -> Void +C.Property() -> Integer +C.Property(AutoPropertyValue As Integer) -> Void +C.Property1() -> Integer +C.Property1(AutoPropertyValue As Integer) -> Void +C.ReadOnlyProperty0() -> Integer +C.ReadOnlyProperty1() -> Integer +C.ReadOnlyProperty2() -> Integer +C.WriteOnlyProperty0(Value As Integer) -> Void +"; + var unshippedText = @""; + await VerifyBasicAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(806, "https://github.com/dotnet/roslyn-analyzers/issues/806")] + public async Task ShippedTextWithImplicitConstructorAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + var shippedText = @" +C +C -> void()"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // PublicAPI.Shipped.txt(3,1): warning RS0017: Symbol 'C -> void()' is part of the declared API, but is either not public or could not be found + GetAdditionalFileResultAt(3, 1, ShippedFileName, RemoveDeletedApiRule, "C -> void()")); + } + + [Fact, WorkItem(806, "https://github.com/dotnet/roslyn-analyzers/issues/806")] + public async Task ShippedTextForImplicitConstructorAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + } + """; + + var shippedText = @" +C +C.C() -> void"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(806, "https://github.com/dotnet/roslyn-analyzers/issues/806")] + public async Task UnshippedTextForImplicitConstructorAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + } + """; + + var shippedText = @" +C"; + var unshippedText = @" +C.C() -> void"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(806, "https://github.com/dotnet/roslyn-analyzers/issues/806")] + public async Task ShippedTextWithMissingImplicitConstructorAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + } + """; + + var shippedText = @" +C"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // Test0.cs(2,14): warning RS0016: Symbol 'C.C() -> void' is not part of the declared API. + GetCSharpResultAt(2, 8 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.C() -> void")); + } + + [Fact, WorkItem(806, "https://github.com/dotnet/roslyn-analyzers/issues/806")] + public async Task ShippedTextWithImplicitConstructorAndBreakingCodeChangeAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + private C() { } + } + """; + + var shippedText = @" +C +C.C() -> void"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // PublicAPI.Shipped.txt(3,1): warning RS0017: Symbol 'C.C() -> void' is part of the declared API, but is either not public or could not be found + GetAdditionalFileResultAt(3, 1, ShippedFileName, RemoveDeletedApiRule, "C.C() -> void")); + } + + [Fact] + public async Task SimpleMemberAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + } + + """; + + var shippedText = @" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +C.Method() -> void +"; + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task SplitBetweenShippedUnshippedAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + } + """; + + var shippedText = @" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +"; + var unshippedText = @" +C.Method() -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task EnumSplitBetweenFilesAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} enum E + { + V1 = 1, + V2 = 2, + V3 = 3, + } + + """; + + var shippedText = @" +E +E.V1 = 1 -> E +E.V2 = 2 -> E +"; + + var unshippedText = @" +E.V3 = 3 -> E +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task SimpleRemovedMemberAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + public int Field; + public int Property { get; set; } + } + """; + + var shippedText = @" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +C.Method() -> void +"; + + string unshippedText = $@" +{DeclarePublicApiAnalyzer.RemovedApiPrefix}C.Method() -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Theory] + [CombinatorialData] + [WorkItem(3329, "https://github.com/dotnet/roslyn-analyzers/issues/3329")] + public async Task RemovedPrefixForNonRemovedApiAsync(bool includeInShipped) + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + } + """; + + var shippedText = @" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +"; + + if (includeInShipped) + { + shippedText += @"C.Method() -> void +"; + } + + string unshippedText = $@" +{DeclarePublicApiAnalyzer.RemovedApiPrefix}C.Method() -> void +"; + + var diagnostics = new[] { + // PublicAPI.Unshipped.txt(2,1): warning RS0050: Symbol 'C.Method() -> void' is marked as removed but it isn't deleted in source code + GetAdditionalFileResultAt(2, 1, UnshippedFileName, DeclarePublicApiAnalyzer.RemovedApiIsNotActuallyRemovedRule, "C.Method() -> void") + }; + if (includeInShipped) + { + await VerifyCSharpAsync(source, shippedText, unshippedText, diagnostics); + } + else + { + // /0/Test0.cs(6,17): warning RS0016: Symbol 'C.Method() -> void' is not part of the declared API + var secondDiagnostic = new[] { GetCSharpResultAt(6, 11 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C.Method() -> void") }; + + await VerifyCSharpAsync(source, shippedText, unshippedText, diagnostics.Concat(secondDiagnostic).ToArray()); + } + } + + [Fact] + public async Task ApiFileShippedWithRemovedAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + """; + + string shippedText = $@" +C +C.Field -> int +C.Property.get -> int +C.Property.set -> void +{DeclarePublicApiAnalyzer.RemovedApiPrefix}C.Method() -> void +"; + + string unshippedText = $@""; + + var expected = new DiagnosticResult(ApiFilesInvalid) + .WithArguments(DeclarePublicApiAnalyzer.InvalidReasonShippedCantHaveRemoved); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + [WorkItem(312, "https://github.com/dotnet/roslyn-analyzers/issues/312")] + public async Task DuplicateSymbolInSameAPIFileAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + """; + + var shippedText = @" +C +C.Field -> int +C.Property.get -> int +C.Property.set -> void +C.Property.get -> int +"; + + var unshippedText = @""; + +#pragma warning disable RS0030 // Do not use banned APIs +#pragma warning disable RS0030 // Do not use banned APIs + var expected = new DiagnosticResult(DuplicateSymbolInApiFiles) + .WithLocation(ShippedFileName, 6, 1) +#pragma warning restore RS0030 // Do not use banned APIs + .WithLocation(ShippedFileName, 4, 1) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments("C.Property.get -> int"); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + [WorkItem(312, "https://github.com/dotnet/roslyn-analyzers/issues/312")] + public async Task DuplicateSymbolInDifferentAPIFilesAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + + """; + + var shippedText = @" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +"; + + var unshippedText = @" +C.Property.get -> int"; + +#pragma warning disable RS0030 // Do not use banned APIs +#pragma warning disable RS0030 // Do not use banned APIs + var expected = new DiagnosticResult(DuplicateSymbolInApiFiles) + .WithLocation(UnshippedFileName, 2, 1) +#pragma warning restore RS0030 // Do not use banned APIs + .WithLocation(ShippedFileName, 5, 1) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments("C.Property.get -> int"); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + [WorkItem(4584, "https://github.com/dotnet/roslyn-analyzers/issues/4584")] + public async Task DuplicateObliviousSymbolsInSameApiFileAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + """; + + var shippedText = $$""" + #nullable enable + C + C.C() -> void + C.Field -> int + C.Property.set -> void + ~C.Property.get -> int + {|{{DuplicatedSymbolInApiFileId}}:~C.Property.get -> int|} + """; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + [WorkItem(4584, "https://github.com/dotnet/roslyn-analyzers/issues/4584")] + public async Task DuplicateSymbolUsingObliviousInSameApiFilesAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + """; + + var shippedText = $$""" + #nullable enable + C + C.C() -> void + C.Field -> int + C.Property.get -> int + C.Property.set -> void + {|{{DuplicatedSymbolInApiFileId}}:~C.Property.get -> int|} + + """; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + [WorkItem(4584, "https://github.com/dotnet/roslyn-analyzers/issues/4584")] + public async Task DuplicateSymbolUsingObliviousInDifferentApiFilesAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Field -> int +~C.Property.get -> int +C.Property.set -> void +"; + + var unshippedText = $$""" + #nullable enable + {|{{DuplicatedSymbolInApiFileId}}:C.Property.get -> int|} + """; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + [WorkItem(4584, "https://github.com/dotnet/roslyn-analyzers/issues/4584")] + public async Task MultipleDuplicateSymbolsUsingObliviousInDifferentApiFilesAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + } + + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +"; + + var unshippedText = $$""" + #nullable enable + {|{{DuplicatedSymbolInApiFileId}}:~C.Property.get -> int|} + {|{{DuplicatedSymbolInApiFileId}}:C.Property.get -> int|} + {|{{DuplicatedSymbolInApiFileId}}:~C.Property.set -> void|} + """; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(773, "https://github.com/dotnet/roslyn-analyzers/issues/773")] + public async Task ApiFileShippedWithNonExistentMembersAsync() + { + // Type C has no public member "Method", but the shipped API has an entry for it. + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + private void Method() { } + } + + """; + + string shippedText = $@" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +C.Method() -> void +"; + string unshippedText = $@""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // PublicAPI.Shipped.txt(7,1): warning RS0017: Symbol 'C.Method() -> void' is part of the declared API, but is either not public or could not be found + GetAdditionalFileResultAt(7, 1, ShippedFileName, RemoveDeletedApiRule, "C.Method() -> void")); + } + + [Fact, WorkItem(773, "https://github.com/dotnet/roslyn-analyzers/issues/773")] + public async Task ApiFileShippedWithNonExistentMembers_TestFullPathAsync() + { + // Type C has no public member "Method", but the shipped API has an entry for it. + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + private void Method() { } + } + """; + + var tempPath = Path.GetTempPath(); + string shippedText = $@" +C +C.C() -> void +C.Field -> int +C.Property.get -> int +C.Property.set -> void +C.Method() -> void +"; + var shippedFilePath = Path.Combine(tempPath, ShippedFileName); + + string unshippedText = $@""; + var unshippedFilePath = Path.Combine(tempPath, UnshippedFileName); + + await VerifyCSharpAsync(source, shippedText, unshippedText, shippedFilePath, unshippedFilePath, + // <%TEMP_PATH%>\PublicAPI.Shipped.txt(7,1): warning RS0017: Symbol 'C.Method() -> void' is part of the declared API, but is either not public or could not be found + GetAdditionalFileResultAt(7, 1, shippedFilePath, RemoveDeletedApiRule, "C.Method() -> void")); + } + + [Fact] + public async Task TypeForwardsAreProcessed1Async() + { + if (IsInternalTest) + { + return; + } + + var source = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.StringComparison))] +"; + +#if NETCOREAPP + var containingAssembly = "System.Runtime"; +#else + var containingAssembly = "mscorlib"; +#endif + string shippedText = $@" +System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.CurrentCulture = 0 -> System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.CurrentCultureIgnoreCase = 1 -> System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.InvariantCulture = 2 -> System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.InvariantCultureIgnoreCase = 3 -> System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.Ordinal = 4 -> System.StringComparison (forwarded, contained in {containingAssembly}) +System.StringComparison.OrdinalIgnoreCase = 5 -> System.StringComparison (forwarded, contained in {containingAssembly}) +"; + string unshippedText = $@""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task TypeForwardsAreProcessed2Async() + { + if (IsInternalTest) + { + return; + } + + var source = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.StringComparer))] +"; + +#if NETCOREAPP + var containingAssembly = "System.Runtime.Extensions"; + const string NonNullSuffix = "!"; + const string NullableSuffix = "?"; +#else + var containingAssembly = "mscorlib"; + const string NonNullSuffix = ""; + const string NullableSuffix = ""; +#endif + string shippedText = $@" +System.StringComparer (forwarded, contained in {containingAssembly}) +static System.StringComparer.InvariantCulture.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.InvariantCultureIgnoreCase.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.CurrentCulture.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.CurrentCultureIgnoreCase.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.Ordinal.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.OrdinalIgnoreCase.get -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.Create(System.Globalization.CultureInfo{NonNullSuffix} culture, bool ignoreCase) -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +System.StringComparer.Compare(object{NullableSuffix} x, object{NullableSuffix} y) -> int (forwarded, contained in {containingAssembly}) +System.StringComparer.Equals(object{NullableSuffix} x, object{NullableSuffix} y) -> bool (forwarded, contained in {containingAssembly}) +System.StringComparer.GetHashCode(object{NonNullSuffix} obj) -> int (forwarded, contained in {containingAssembly}) +abstract System.StringComparer.Compare(string{NullableSuffix} x, string{NullableSuffix} y) -> int (forwarded, contained in {containingAssembly}) +abstract System.StringComparer.Equals(string{NullableSuffix} x, string{NullableSuffix} y) -> bool (forwarded, contained in {containingAssembly}) +abstract System.StringComparer.GetHashCode(string{NonNullSuffix} obj) -> int (forwarded, contained in {containingAssembly}) +System.StringComparer.StringComparer() -> void (forwarded, contained in {containingAssembly}) +"; + +#if NETCOREAPP + shippedText = $@" +#nullable enable +{shippedText} +static System.StringComparer.Create(System.Globalization.CultureInfo{NonNullSuffix} culture, System.Globalization.CompareOptions options) -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +static System.StringComparer.FromComparison(System.StringComparison comparisonType) -> System.StringComparer{NonNullSuffix} (forwarded, contained in {containingAssembly}) +"; +#endif + + string unshippedText = $@""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(1192, "https://github.com/dotnet/roslyn-analyzers/issues/1192")] + public async Task OpenGenericTypeForwardsAreProcessedAsync() + { + if (IsInternalTest) + { + return; + } + + var source = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.IEnumerable<>))] +"; + + string shippedText = ""; + string unshippedText = ""; + +#if NETCOREAPP + var containingAssembly = "System.Runtime"; +#else + var containingAssembly = "mscorlib"; +#endif + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(2,12): warning RS0016: Symbol 'System.Collections.Generic.IEnumerable (forwarded, contained in System.Runtime)' is not part of the declared API + GetCSharpResultAt(2, 12, DeclareNewApiRule, $"System.Collections.Generic.IEnumerable (forwarded, contained in {containingAssembly})"), + // /0/Test0.cs(2,12): warning RS0016: Symbol 'System.Collections.Generic.IEnumerable.GetEnumerator() -> System.Collections.Generic.IEnumerator (forwarded, contained in System.Runtime)' is not part of the declared API + GetCSharpResultAt(2, 12, DeclareNewApiRule, $"System.Collections.Generic.IEnumerable.GetEnumerator() -> System.Collections.Generic.IEnumerator (forwarded, contained in {containingAssembly})") +#if NETCOREAPP + // /0/Test0.cs(2,12): warning RS0037: PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + , GetCSharpResultAt(2, 12, DeclarePublicApiAnalyzer.ShouldAnnotatePublicApiFilesRule) +#endif + ); + } + + [Fact, WorkItem(1192, "https://github.com/dotnet/roslyn-analyzers/issues/1192")] + public async Task GenericTypeForwardsAreProcessedAsync() + { + if (IsInternalTest) + { + return; + } + + var source = @" +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Collections.Generic.IEnumerable))] +"; + + string shippedText = ""; + string unshippedText = ""; + +#if NETCOREAPP + var containingAssembly = "System.Runtime"; +#else + var containingAssembly = "mscorlib"; +#endif + + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(2,12): warning RS0016: Symbol 'System.Collections.Generic.IEnumerable (forwarded, contained in System.Runtime)' is not part of the declared API + GetCSharpResultAt(2, 12, DeclareNewApiRule, $"System.Collections.Generic.IEnumerable (forwarded, contained in {containingAssembly})"), + // /0/Test0.cs(2,12): warning RS0016: Symbol 'System.Collections.Generic.IEnumerable.GetEnumerator() -> System.Collections.Generic.IEnumerator (forwarded, contained in System.Runtime)' is not part of the declared API + GetCSharpResultAt(2, 12, DeclareNewApiRule, $"System.Collections.Generic.IEnumerable.GetEnumerator() -> System.Collections.Generic.IEnumerator (forwarded, contained in {containingAssembly})") +#if NETCOREAPP + // /0/Test0.cs(2,12): warning RS0037: PublicAPI.txt is missing '#nullable enable', so the nullability annotations of API isn't recorded. It is recommended to enable this tracking. + , GetCSharpResultAt(2, 12, DeclarePublicApiAnalyzer.ShouldAnnotatePublicApiFilesRule) +#endif + ); + } + + [Fact, WorkItem(851, "https://github.com/dotnet/roslyn-analyzers/issues/851")] + public async Task TestAvoidMultipleOverloadsWithOptionalParametersAsync() + { + var source = $$""" + + public class C + { + // ok - single overload with optional params, 2 overloads have no public API entries. + {{EnabledModifierCSharp}} void Method1(int p1, int p2, int p3 = 0) { } + {{EnabledModifierCSharp}} void Method1() { } + {{EnabledModifierCSharp}} void Method1(int p1, int p2) { } + {{EnabledModifierCSharp}} void Method1(char p1, params int[] p2) { } + + // ok - multiple overloads with optional params, but only one is public. + {{EnabledModifierCSharp}} void Method2(int p1 = 0) { } + {{DisabledModifierCSharp}} void Method2(char p1 = '0') { } + private void Method2(string p1 = null) { } + + // ok - multiple overloads with optional params, but all are shipped. + {{EnabledModifierCSharp}} void Method3(int p1 = 0) { } + {{EnabledModifierCSharp}} void Method3(string p1 = null) { } + + // fire on unshipped (1) - multiple overloads with optional params, all but first are shipped. + {{EnabledModifierCSharp}} void Method4(int p1 = 0) { } + {{EnabledModifierCSharp}} void Method4(char p1 = 'a') { } + {{EnabledModifierCSharp}} void Method4(string p1 = null) { } + + // fire on all unshipped (3) - multiple overloads with optional params, all are unshipped, 2 have unshipped entries. + {{EnabledModifierCSharp}} void Method5(int p1 = 0) { } + {{EnabledModifierCSharp}} void Method5(char p1 = 'a') { } + {{EnabledModifierCSharp}} void Method5(string p1 = null) { } + + // ok - multiple overloads with optional params, but all have same params (differ only by generic vs non-generic). + {{EnabledModifierCSharp}} object Method6(int p1 = 0) { return Method6(p1); } + {{EnabledModifierCSharp}} T Method6(int p1 = 0) { return default(T); } + } + """; + + string shippedText = """ + C.Method3(int p1 = 0) -> void + C.Method3(string p1 = null) -> void + C.Method4(char p1 = 'a') -> void + C.Method4(string p1 = null) -> void + """; + string unshippedText = + (IsInternalTest ? "" : + """ + C + C.C() -> void + + """) + + """ + C.Method1() -> void + C.Method1(int p1, int p2) -> void + C.Method2(int p1 = 0) -> void + C.Method4(int p1 = 0) -> void + C.Method5(char p1 = 'a') -> void + C.Method5(string p1 = null) -> void + C.Method6(int p1 = 0) -> object + C.Method6(int p1 = 0) -> T + """; + + // The error on Method2 is the difference between internal and public results + var result = IsInternalTest + ? new DiagnosticResult[] + { + // Test0.cs(5,17): warning RS0016: Symbol 'C.Method1(int p1, int p2, int p3 = 0) -> void' is not part of the declared API. + GetCSharpResultAt(5, 19, DeclareNewApiRule, "C.Method1(int p1, int p2, int p3 = 0) -> void"), + // Test0.cs(8,17): warning RS0016: Symbol 'C.Method1(char p1, params int[] p2) -> void' is not part of the declared API. + GetCSharpResultAt(8, 19, DeclareNewApiRule, "C.Method1(char p1, params int[] p2) -> void"), + // /0/Test0.cs(11,19): warning RS0059: Symbol 'Method2' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(11, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method2", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(20,17): warning RS0026: Symbol 'Method4' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(20, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method4", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(25,17): warning RS0016: Symbol 'C.Method5(int p1 = 0) -> void' is not part of the declared API. + GetCSharpResultAt(25, 19, DeclareNewApiRule, "C.Method5(int p1 = 0) -> void"), + // Test0.cs(25,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(25, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(26,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(26, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(27,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(27, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri) + } + : new[] { + // Test0.cs(5,17): warning RS0016: Symbol 'C.Method1(int p1, int p2, int p3 = 0) -> void' is not part of the declared API. + GetCSharpResultAt(5, 17, DeclareNewApiRule, "C.Method1(int p1, int p2, int p3 = 0) -> void"), + // Test0.cs(8,17): warning RS0016: Symbol 'C.Method1(char p1, params int[] p2) -> void' is not part of the declared API. + GetCSharpResultAt(8, 17, DeclareNewApiRule, "C.Method1(char p1, params int[] p2) -> void"), + // Test0.cs(20,17): warning RS0026: Symbol 'Method4' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(20, 17, AvoidMultipleOverloadsWithOptionalParameters, "Method4", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(25,17): warning RS0016: Symbol 'C.Method5(int p1 = 0) -> void' is not part of the declared API. + GetCSharpResultAt(25, 17, DeclareNewApiRule, "C.Method5(int p1 = 0) -> void"), + // Test0.cs(25,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(25, 17, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(26,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(26, 17, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + // Test0.cs(27,17): warning RS0026: Symbol 'Method5' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(27, 17, AvoidMultipleOverloadsWithOptionalParameters, "Method5", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri) + }; + + await VerifyCSharpAsync(source, shippedText, unshippedText, result); + } + + [Fact, WorkItem(851, "https://github.com/dotnet/roslyn-analyzers/issues/851")] + public async Task TestOverloadWithOptionalParametersShouldHaveMostParametersAsync() + { + var source = $$""" + + public class C + { + // ok - single overload with optional params has most parameters. + {{EnabledModifierCSharp}} void Method1(int p1, int p2, int p3 = 0) { } + {{EnabledModifierCSharp}} void Method1() { } + {{EnabledModifierCSharp}} void Method1(int p1, int p2) { } + {{EnabledModifierCSharp}} void Method1(char p1, params int[] p2) { } + + // ok - multiple overloads with optional params violating most params requirement, but only one is public. + {{EnabledModifierCSharp}} void Method2(int p1 = 0) { } + {{DisabledModifierCSharp}} void Method2(int p1, char p2 = '0') { } + private void Method2(string p1 = null) { } + + // ok - multiple overloads with optional params violating most params requirement, but all are shipped. + {{EnabledModifierCSharp}} void Method3(int p1 = 0) { } + {{EnabledModifierCSharp}} void Method3(string p1 = null) { } + {{EnabledModifierCSharp}} void Method3(int p1, int p2) { } + + // fire on unshipped (1) - single overload with optional params and violating most params requirement. + {{EnabledModifierCSharp}} void Method4(int p1 = 0) { } // unshipped + {{EnabledModifierCSharp}} void Method4(char p1, int p2) { } // unshipped + {{EnabledModifierCSharp}} void Method4(string p1, int p2) { } // unshipped + + // fire on shipped (1) - single shipped overload with optional params and violating most params requirement due to a new unshipped API. + {{EnabledModifierCSharp}} void Method5(int p1 = 0) { } // shipped + {{EnabledModifierCSharp}} void Method5(char p1) { } // shipped + {{EnabledModifierCSharp}} void Method5(string p1) { } // unshipped + + // fire on multiple shipped (2) - multiple shipped overloads with optional params and violating most params requirement due to a new unshipped API + {{EnabledModifierCSharp}} void Method6(int p1 = 0) { } // shipped + {{EnabledModifierCSharp}} void Method6(char p1 = 'a') { } // shipped + {{EnabledModifierCSharp}} void Method6(string p1) { } // unshipped + } + """; + + string shippedText = """ + C.Method3(int p1 = 0) -> void + C.Method3(int p1, int p2) -> void + C.Method3(string p1 = null) -> void + C.Method5(char p1) -> void + C.Method5(int p1 = 0) -> void + C.Method6(char p1 = 'a') -> void + C.Method6(int p1 = 0) -> void + """; + string unshippedText = (IsInternalTest ? "" : + """ + C + C.C() -> void + + """) + """ + C.Method1() -> void + C.Method1(char p1, params int[] p2) -> void + C.Method1(int p1, int p2) -> void + C.Method1(int p1, int p2, int p3 = 0) -> void + C.Method2(int p1 = 0) -> void + C.Method4(char p1, int p2) -> void + C.Method4(int p1 = 0) -> void + C.Method4(string p1, int p2) -> void + C.Method5(string p1) -> void + C.Method6(string p1) -> void + """; + + var diagnostics = IsInternalTest ? new DiagnosticResult[] { + // /0/Test0.cs(11,19): warning RS0059: Symbol 'Method2' violates the backcompat requirement: 'Do not add multiple overloads with optional parameters'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(11, 19, AvoidMultipleOverloadsWithOptionalParameters, "Method2", AvoidMultipleOverloadsWithOptionalParameters.HelpLinkUri), + } : Array.Empty(); + + diagnostics = diagnostics.Concat(new[] { + // Test0.cs(21,17): warning RS0027: Symbol 'Method4' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(21, 11 + EnabledModifierCSharp.Length, OverloadWithOptionalParametersShouldHaveMostParameters, "Method4", OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri), + // Test0.cs(26,17): warning RS0027: Symbol 'Method5' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(26, 11 + EnabledModifierCSharp.Length, OverloadWithOptionalParametersShouldHaveMostParameters, "Method5", OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri), + // Test0.cs(31,17): warning RS0027: Symbol 'Method6' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(31, 11 + EnabledModifierCSharp.Length, OverloadWithOptionalParametersShouldHaveMostParameters, "Method6", OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri), + // Test0.cs(32,17): warning RS0027: Symbol 'Method6' violates the backcompat requirement: 'Public API with optional parameter(s) should have the most parameters amongst its public overloads'. See 'https://github.com/dotnet/roslyn/blob/main/docs/Adding%20Optional%20Parameters%20in%20Public%20API.md' for details. + GetCSharpResultAt(32, 11 + EnabledModifierCSharp.Length, OverloadWithOptionalParametersShouldHaveMostParameters, "Method6", OverloadWithOptionalParametersShouldHaveMostParameters.HelpLinkUri) + }).ToArray(); + + await VerifyCSharpAsync(source, shippedText, unshippedText, diagnostics); + } + + [Fact, WorkItem(4766, "https://github.com/dotnet/roslyn-analyzers/issues/4766")] + public async Task TestObsoleteOverloadWithOptionalParameters_NoDiagnosticAsync() + { + var source = $$""" + + using System; + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} void M(int p1 = 0) { } + + [Obsolete] + {{EnabledModifierCSharp}} void M(char p1, int p2) { } + } + """; + + string shippedText = string.Empty; + string unshippedText = @" +C +C.C() -> void + +C.M(char p1, int p2) -> void +C.M(int p1 = 0) -> void +"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact, WorkItem(4766, "https://github.com/dotnet/roslyn-analyzers/issues/4766")] + public async Task TestMultipleOverloadsWithOptionalParameter_OneIsObsoleteAsync() + { + var source = $$""" + + using System; + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} void M(int p1 = 0) { } + + [Obsolete] + {{EnabledModifierCSharp}} void M(char p1 = '0') { } + } + """; + + string shippedText = @"C +C.C() -> void +C.M(char p1 = '0') -> void"; + string unshippedText = "C.M(int p1 = 0) -> void"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task ObliviousMember_SimpleAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string Field; + {{EnabledModifierCSharp}} string Property { get; set; } + {{EnabledModifierCSharp}} string Method(string x) => throw null!; + {{EnabledModifierCSharp}} string ArrowExpressionProperty => throw null!; + } + """; + + var shippedText = @"#nullable enable +C +C.ArrowExpressionProperty.get -> string +C.C() -> void +C.Field -> string +C.Method(string x) -> string +C.Property.get -> string +C.Property.set -> void"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(4, 13 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.Field -> string"), + GetCSharpResultAt(4, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "Field"), + GetCSharpResultAt(5, 24 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.Property.get -> string"), + GetCSharpResultAt(5, 24 + EnabledModifierCSharp.Length, ObliviousApiRule, "Property.get"), + GetCSharpResultAt(5, 29 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.Property.set -> void"), + GetCSharpResultAt(5, 29 + EnabledModifierCSharp.Length, ObliviousApiRule, "Property.set"), + GetCSharpResultAt(6, 13 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.Method(string x) -> string"), + GetCSharpResultAt(6, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "Method"), + GetCSharpResultAt(7, 40 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.ArrowExpressionProperty.get -> string"), + GetCSharpResultAt(7, 40 + EnabledModifierCSharp.Length, ObliviousApiRule, "ArrowExpressionProperty.get") + ); + } + + [Theory] + [InlineData("string ", "string", "string!")] + [InlineData("string?", "string", "string?")] + public async Task ObliviousMember_Simple_NullableTypes(string csharp, string unannotated, string annotated) + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} {{csharp}} Field; + {{EnabledModifierCSharp}} {{csharp}} Property { get; set; } + {{EnabledModifierCSharp}} {{csharp}} Method({{csharp}} x) => throw null!; + {{EnabledModifierCSharp}} {{csharp}} ArrowExpressionProperty => throw null!; + } + """; + + var shippedText = $""" + #nullable enable + C + C.ArrowExpressionProperty.get -> {unannotated} + C.C() -> void + C.Field -> {unannotated} + C.Method({unannotated} x) -> {unannotated} + C.Property.get -> {unannotated} + C.Property.set -> void + """; + + var unshippedText = ""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(4, 14 + EnabledModifierCSharp.Length, AnnotateApiRule, $"C.Field -> {annotated}"), + GetCSharpResultAt(5, 25 + EnabledModifierCSharp.Length, AnnotateApiRule, $"C.Property.get -> {annotated}"), + GetCSharpResultAt(6, 14 + EnabledModifierCSharp.Length, AnnotateApiRule, $"C.Method({annotated} x) -> {annotated}"), + GetCSharpResultAt(7, 41 + EnabledModifierCSharp.Length, AnnotateApiRule, $"C.ArrowExpressionProperty.get -> {annotated}")); + } + + [Fact] + public async Task ObliviousMember_AlreadyMarkedAsObliviousAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string Field; + {{EnabledModifierCSharp}} D Field2; + #nullable disable + {{EnabledModifierCSharp}} string Property { get; set; } + {{EnabledModifierCSharp}} void Method(string x) => throw null!; + {{EnabledModifierCSharp}} string Method2() => throw null!; + {{EnabledModifierCSharp}} string ArrowExpressionProperty => throw null!; + #nullable enable + {{EnabledModifierCSharp}} D.E< + #nullable disable + string + #nullable enable + > Method3() => throw null!; + #nullable disable + {{EnabledModifierCSharp}} string this[string x] { get => throw null!; set => throw null!; } + } + {{EnabledModifierCSharp}} class D { public class E { } } + + """; + + var shippedText = @"#nullable enable +C +~C.ArrowExpressionProperty.get -> string +C.C() -> void +~C.Field -> string +~C.Field2 -> D! +~C.Method(string x) -> void +~C.Method2() -> string +~C.Property.get -> string +~C.Property.set -> void +~C.Method3() -> D.E! +~C.this[string x].set -> void +~C.this[string x].get -> string +D +D.D() -> void +D.E +D.E.E() -> void"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(4, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "Field"), + GetCSharpResultAt(7, 11, ObliviousApiRule, "Field2"), + GetCSharpResultAt(9, 24 + EnabledModifierCSharp.Length, ObliviousApiRule, "Property.get"), + GetCSharpResultAt(9, 29 + EnabledModifierCSharp.Length, ObliviousApiRule, "Property.set"), + GetCSharpResultAt(10, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "Method"), + GetCSharpResultAt(11, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "Method2"), + GetCSharpResultAt(12, 40 + EnabledModifierCSharp.Length, ObliviousApiRule, "ArrowExpressionProperty.get"), + GetCSharpResultAt(18, 15, ObliviousApiRule, "Method3"), + GetCSharpResultAt(20, 30 + EnabledModifierCSharp.Length, ObliviousApiRule, "this.get"), + GetCSharpResultAt(20, 50 + EnabledModifierCSharp.Length, ObliviousApiRule, "this.set") + ); + } + + [Fact] + public async Task ObliviousMember_AlreadyMarkedAsOblivious_TypeParametersWithClassConstraintAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} void M(T t) where T : class { } + + #nullable enable + {{EnabledModifierCSharp}} void M2(T t) where T : class { } + {{EnabledModifierCSharp}} void M3(T t) where T : class? { } + #nullable disable + } + {{EnabledModifierCSharp}} class D where T : class { } + {{EnabledModifierCSharp}} class E { public class F where T : class { } } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +~C.M(T t) -> void +C.M2(T! t) -> void +C.M3(T t) -> void +~D +D.D() -> void +E +E.E() -> void +~E.F +E.F.F() -> void +"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(4, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "M"), + GetCSharpResultAt(11, 8 + EnabledModifierCSharp.Length, ObliviousApiRule, "D"), + GetCSharpResultAt(12, 25 + EnabledModifierCSharp.Length, ObliviousApiRule, "F") + ); + } + + [Fact] + public async Task ObliviousMember_AlreadyMarkedAsOblivious_TypeParametersWithNotNullConstraintAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} void M(T t) where T : notnull { } + + #nullable enable + {{EnabledModifierCSharp}} void M2(T t) where T : notnull { } + #nullable disable + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.M(T t) -> void +C.M2(T t) -> void +"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task ObliviousMember_AlreadyMarkedAsOblivious_TypeParametersWithMiscConstraintsAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} interface I { } + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} void M1() where T : I { } + {{EnabledModifierCSharp}} void M2() where T : C { } + {{EnabledModifierCSharp}} void M3() where T : U where U : class { } + + #nullable enable + {{EnabledModifierCSharp}} void M1b() where T : I { } + {{EnabledModifierCSharp}} void M2b() where T : C? { } + {{EnabledModifierCSharp}} void M3b() where T : U where U : class { } + #nullable disable + } + """; + + var shippedText = @"#nullable enable +I +C +C.C() -> void +~C.M1() -> void +~C.M2() -> void +~C.M3() -> void +C.M1b() -> void +C.M2b() -> void +C.M3b() -> void +"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(5, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "M1"), + GetCSharpResultAt(6, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "M2"), + GetCSharpResultAt(7, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "M3") + ); + } + + [Fact] + public async Task ObliviousMember_AlreadyMarkedAsOblivious_TypeParametersWithMiscConstraints2Async() + { + var source = $$""" + + {{EnabledModifierCSharp}} interface I { } + {{EnabledModifierCSharp}} class C + { + #nullable enable + {{EnabledModifierCSharp}} void M1() where T : I< + #nullable disable + string + #nullable enable + > { } + #nullable disable + } + + """; + + var shippedText = @"#nullable enable +I +C +C.C() -> void +~C.M1() -> void +"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(6, 11 + EnabledModifierCSharp.Length, ObliviousApiRule, "M1") + ); + } + + [Fact] + public async Task ObliviousMember_NestedEnumIsNotObliviousAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} enum E + { + None, + Some + } + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.E +C.E.None = 0 -> C.E +C.E.Some = 1 -> C.E"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task NestedEnumIsNotObliviousAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} enum E + { + None, + Some + } + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.E +C.E.None = 0 -> C.E +C.E.Some = 1 -> C.E"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task ObliviousTypeArgumentInContainingTypeAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} struct Nested { } + + {{EnabledModifierCSharp}} C< + #nullable disable + string + #nullable enable + >.Nested field; + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Nested +C.Nested.Nested() -> void +~C.field -> C.Nested"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText, + GetCSharpResultAt(11, 22, ObliviousApiRule, "field") + ); + } + + [Fact] + public async Task ImplicitContainingType_TClassAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} class C where T : class + { + {{EnabledModifierCSharp}} struct Nested { } + + {{EnabledModifierCSharp}} Nested field; + {{EnabledModifierCSharp}} C.Nested field2; + } + + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Nested +C.Nested.Nested() -> void +~C.field -> C.Nested +C.field2 -> C.Nested"; + + var unshippedText = @""; + + // Note: although the code is entirely nullable-enabled, the compiler uses a containing type that is + // `C` so there is an oblivious symbol. This only happens when the type parameter is constrained + // such that it could be annotated in C# 8 (`T?` would have been allowed). + // + // One recourse is to use a suppression around such APIs: + // #pragma warning disable RS0041 // uses oblivious reference types + // + // Another recourse is to make the containing type explicit: `C.Nested` + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(7,19): warning RS0041: Symbol 'field' uses some oblivious reference types. + GetCSharpResultAt(7, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "field") + ); + } + + [Fact] + public async Task ImplicitContainingType_TOpenAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} struct Nested { } + + {{EnabledModifierCSharp}} Nested field; + {{EnabledModifierCSharp}} Nested field2; + } + """; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Nested +C.Nested.Nested() -> void +C.field -> C.Nested +C.field2 -> C.Nested"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task SkippedNamespace_ExactMatches() + { + var source = $$""" + namespace My.Namespace + { + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} C() { } + } + } + namespace Other.Namespace + { + {{EnabledModifierCSharp}} class D + { + {{EnabledModifierCSharp}} D() { } + } + } + """; + + string? shippedText = null; + string? unshippedText = null; + + await VerifyCSharpAsync(source, shippedText, unshippedText, $"[*]\r\ndotnet_public_api_analyzer.skip_namespaces = My.Namespace", + // /0/Test0.cs(10,18): warning RS0016: Symbol 'Other.Namespace.D' is not part of the declared public API + GetCSharpResultAt(10, 12 + EnabledModifierCSharp.Length, DeclareNewApiRule, "Other.Namespace.D"), + // /0/Test0.cs(12,16): warning RS0016: Symbol 'Other.Namespace.D.D() -> void' is not part of the declared public API + GetCSharpResultAt(12, 10 + EnabledModifierCSharp.Length, DeclareNewApiRule, "Other.Namespace.D.D() -> void")); + } + + [Fact] + public async Task SkippedNamespace_ShorterSpecifiedNamespace() + { + var source = $$""" + namespace My.Namespace + { + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} C() { } + } + } + """; + + string? shippedText = null; + string? unshippedText = null; + + await VerifyCSharpAsync(source, shippedText, unshippedText, $"[*]\r\ndotnet_public_api_analyzer.skip_namespaces = My"); + } + + [Fact] + public async Task SkippedNamespace_MoreDerivedNamespace() + { + var source = $$""" + namespace My.Namespace + { + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} C() { } + } + } + """; + + string? shippedText = null; + string? unshippedText = null; + + await VerifyCSharpAsync(source, shippedText, unshippedText, $"[*]\r\ndotnet_public_api_analyzer.skip_namespaces = My.Namespace.Longer", + // /0/Test0.cs(3,18): warning RS0016: Symbol 'My.Namespace.C' is not part of the declared public API + GetCSharpResultAt(line: 3, 12 + EnabledModifierCSharp.Length, DeclareNewApiRule, "My.Namespace.C"), + // /0/Test0.cs(5,16): warning RS0016: Symbol 'My.Namespace.C.C() -> void' is not part of the declared public API + GetCSharpResultAt(5, 10 + EnabledModifierCSharp.Length, DeclareNewApiRule, "My.Namespace.C.C() -> void")); + } + + [Fact] + public async Task SkippedNamespace_PartialLocations() + { + var source = $$""" + namespace My.Namespace + { + {{EnabledModifierCSharp}} partial class C + { + } + } + """; + + var addSources = (SourceFileList sources) => + { + sources.Add((filename: $"/path1/Test1.cs", source)); + sources.Add((filename: $"/path2/Test2.cs", source)); + }; + + string? shippedText = null; + string? unshippedText = null; + + await VerifyCSharpAsync(addSources, shippedText, unshippedText, $"[path1/**.cs]\r\ndotnet_public_api_analyzer.skip_namespaces = My.Namespace"); + } + + [Fact] + public async Task ShippedTextWithMissingImplicitRecordMembers() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} record C(int X, string Y) + { + } + + namespace System.Runtime.CompilerServices + { + internal static class IsExternalInit + { + } + } + """; + + var shippedText = """ + #nullable enable + C + C.C(C! original) -> void + C.C(int X, string! Y) -> void + virtual C.$() -> C! + C.Deconstruct(out int X, out string! Y) -> void + override C.Equals(object? obj) -> bool + override C.GetHashCode() -> int + override C.ToString() -> string! + static C.operator !=(C? left, C? right) -> bool + static C.operator ==(C? left, C? right) -> bool + virtual C.EqualityContract.get -> System.Type! + virtual C.Equals(C? other) -> bool + virtual C.PrintMembers(System.Text.StringBuilder! builder) -> bool + C.X.get -> int + C.X.init -> void + C.Y.get -> string! + C.Y.init -> void + """; + + if (EnabledModifierCSharp == "internal") + { + shippedText += $"\r\nSystem.Runtime.CompilerServices.IsExternalInit"; + } + + var unshippedText = string.Empty; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task ShippedTextWithMissingDelegate() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} delegate void D(int X, string Y); + """; + + var shippedText = """ + #nullable enable + D + virtual D.Invoke(int X, string! Y) -> void + """; + + var unshippedText = string.Empty; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + #endregion + + #region Fix tests + + [Fact] + public async Task ShippedTextWithMissingImplicitStructConstructorAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} struct {|{{AddNewApiId}}:C|} + { + } + """; + + var shippedText = @" +C"; + var unshippedText = string.Empty; + var fixedUnshippedText = "C.C() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task ShippedTextWithMissingImplicitStructConstructorWithExplicitPrivateCtorWithParametersAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} struct {|{{AddNewApiId}}:C|} + { + private C(string x) {} + } + """; + + var shippedText = @" +C"; + var unshippedText = string.Empty; + var fixedUnshippedText = "C.C() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task ShippedTextWithMissingImplicitStructConstructorWithOtherOverloadsAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} struct {|{{AddNewApiId}}:C|} + { + {{EnabledModifierCSharp}} C(int value) + { + } + } + """; + + var shippedText = @" +C +C.C(int value) -> void"; + var unshippedText = string.Empty; + var fixedUnshippedText = "C.C() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + [WorkItem(2622, "https://github.com/dotnet/roslyn-analyzers/issues/2622")] + public async Task AnalyzerFileMissing_Both_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + } + """; + + string? shippedText = null; + string? unshippedText = null; + var fixedUnshippedText = @"C"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestSimpleMissingMember_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + {{EnabledModifierCSharp}} int ArrowExpressionProperty => 0; + + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:NewField|}; // Newly added field, not in current public API. + } + """; + + var shippedText = @""; + var unshippedText = @"C +C.ArrowExpressionProperty.get -> int +C.C() -> void +C.Field -> int +C.Method() -> void +C.Property.get -> int +C.Property.set -> void"; + var fixedUnshippedText = @"C +C.ArrowExpressionProperty.get -> int +C.C() -> void +C.Field -> int +C.Method() -> void +C.NewField -> int +C.Property.get -> int +C.Property.set -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Theory] + [WorkItem(4749, "https://github.com/dotnet/roslyn-analyzers/issues/4749")] + [InlineData("\r\n")] // Windows line ending. + [InlineData("\n")] // Linux line ending. + public async Task TestUseExistingLineEndingsAsync(string lineEnding) + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} int Field1; + {{EnabledModifierCSharp}} int Field2; + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field3|}; // Newly added field, not in current public API. + } + """; + + var shippedText = @""; + var unshippedText = $"C{lineEnding}C.Field1 -> int{lineEnding}C.Field2 -> int"; + var fixedUnshippedText = $"C{lineEnding}C.Field1 -> int{lineEnding}C.Field2 -> int{lineEnding}C.Field3 -> int"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + [WorkItem(4749, "https://github.com/dotnet/roslyn-analyzers/issues/4749")] + public async Task TestUseOSLineEndingAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field1|}; // Newly added field, not in current public API. + } + """; + var shippedText = @""; + var unshippedText = $"C"; + var fixedUnshippedText = $"C{Environment.NewLine}C.Field1 -> int"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestSimpleMissingMember_Fix_WithoutNullabilityAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string? {|{{ShouldAnnotateApiFilesId}}:{|{{AddNewApiId}}:NewField|}|}; // Newly added field, not in current public API. + } + """; + + var shippedText = @""; + var unshippedText = @"C +C.C() -> void"; + var fixedUnshippedText = @"C +C.C() -> void +C.NewField -> string"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [InlineData(0)] + [InlineData(1)] + [Theory] + public async Task TestSimpleMissingMember_Fix_WithoutNullability_MultipleFilesAsync(int index) + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string? {|{{ShouldAnnotateApiFilesId}}:{|{{AddNewApiId}}:NewField|}|}; // Newly added field, not in current public API. + } + """; + + var shippedText = @""; + var unshippedText1 = @"C +C.C() -> void"; + var unshippedText2 = @""; + var fixedUnshippedText1_index0 = @"C +C.C() -> void +C.NewField -> string"; + var fixedUnshippedText1_index1 = "C.NewField -> string"; + + var unshippedTextName2 = UnshippedFileNamePrefix + "test" + DeclarePublicApiAnalyzer.Extension; + + var test = new CSharpCodeFixTest(); + + test.TestState.Sources.Add(source); + test.TestState.AdditionalFiles.Add((ShippedFileName, shippedText)); + test.TestState.AdditionalFiles.Add((UnshippedFileName, unshippedText1)); + test.TestState.AdditionalFiles.Add((unshippedTextName2, unshippedText2)); + + test.CodeActionIndex = index; + test.FixedState.AdditionalFiles.Add((ShippedFileName, shippedText)); + + if (index == 0) + { + test.FixedState.AdditionalFiles.Add((UnshippedFileName, fixedUnshippedText1_index0)); + test.FixedState.AdditionalFiles.Add((unshippedTextName2, unshippedText2)); + } + else if (index == 1) + { + test.FixedState.AdditionalFiles.Add((UnshippedFileName, unshippedText1)); + test.FixedState.AdditionalFiles.Add((unshippedTextName2, fixedUnshippedText1_index1)); + } + else + { + throw new NotSupportedException(); + } + + await test.RunAsync(); + } + + [Fact] + public async Task TestSimpleMissingMember_Fix_WithNullabilityAsync() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string? {|{{AddNewApiId}}:NewField|}; // Newly added field, not in current public API. + } + """; + + var shippedText = $@"#nullable enable"; + var unshippedText = @"C +C.C() -> void"; + var fixedUnshippedText = @"C +C.C() -> void +C.NewField -> string?"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestSimpleMissingMember_Fix_WithNullability2Async() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string? OldField; + {{EnabledModifierCSharp}} string? {|{{AddNewApiId}}:NewField|}; // Newly added field, not in current public API. + } + """; + var shippedText = $@"#nullable enable"; + var unshippedText = @"C +C.C() -> void +C.OldField -> string?"; + var fixedUnshippedText = @"C +C.C() -> void +C.NewField -> string? +C.OldField -> string?"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestSimpleMissingMember_Fix_WithNullability3Async() + { + var source = $$""" + #nullable enable + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string? OldField; + {{EnabledModifierCSharp}} string? NewField; + } + """; + var shippedText = $@"#nullable enable +C +C.C() -> void +C.NewField -> string? +C.OldField -> string?"; + + var unshippedText = ""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task TestAddAndRemoveMembers_CSharp_Fix_WithRemovedNullabilityAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string {|{{ObliviousApiId}}:{|{{AddNewApiId}}:ChangedField|}|}; // oblivious + } + """; + var shippedText = $@"#nullable enable"; + var unshippedText = $$""" + C + C.C() -> void + {|{{RemoveApiId}}:C.ChangedField -> string?|} + """; + var fixedUnshippedText = @"C +C.C() -> void +~C.ChangedField -> string"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact, WorkItem(3793, "https://github.com/dotnet/roslyn-analyzers/issues/3793")] + public async Task ObliviousApiDiagnosticInGeneratedFileStillWarnAsync() + { + // We complain about oblivious APIs in generated files too (no special treatment) + var source = $$""" + + // + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} string ObliviousField; + } + """; + var shippedText = "#nullable enable"; + var unshippedText = @"C +C.C() -> void +C.ObliviousField -> string"; + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(5,19): warning RS0036: Symbol 'C.ObliviousField -> string' is missing nullability annotations in the declared API. + GetCSharpResultAt(5, 13 + EnabledModifierCSharp.Length, AnnotateApiRule, "C.ObliviousField -> string"), + // /0/Test0.cs(5,19): warning RS0041: Symbol 'ObliviousField' uses some oblivious reference types. + GetCSharpResultAt(5, 13 + EnabledModifierCSharp.Length, ObliviousApiRule, "ObliviousField") + ); + } + + [Fact, WorkItem(3672, "https://github.com/dotnet/roslyn-analyzers/issues/3672")] + public async Task TypeArgumentRefersToTypeParameter_OnMethodAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} static class C + { + {{EnabledModifierCSharp}} static void M() + where T : System.IComparable + { + } + } + """; + var shippedText = "#nullable enable"; + var unshippedText = @""; + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(3,21): warning RS0016: Symbol 'C' is not part of the declared API. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C"), + // /0/Test0.cs(5,24): warning RS0016: Symbol 'static C.M() -> void' is not part of the declared API. + GetCSharpResultAt(5, 18 + EnabledModifierCSharp.Length, DeclareNewApiRule, "static C.M() -> void") + ); + } + + [Fact, WorkItem(3672, "https://github.com/dotnet/roslyn-analyzers/issues/3672")] + public async Task TypeArgumentRefersToTypeParameter_OnTypeAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} static class C + where T : System.IComparable + { + } + """; + var shippedText = "#nullable enable"; + var unshippedText = @""; + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(3,21): warning RS0016: Symbol 'C' is not part of the declared API. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, DeclareNewApiRule, "C") + ); + } + + [Fact, WorkItem(3672, "https://github.com/dotnet/roslyn-analyzers/issues/3672")] + public async Task TypeArgumentRefersToTypeParameter_OnType_SecondTypeArgumentAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} static class C + where T1 : class + where T2 : System.IComparable< + #nullable disable + T1 + #nullable enable + > + { + } + """; + var shippedText = "#nullable enable"; + var unshippedText = @""; + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(3,21): warning RS0016: Symbol '~C' is not part of the declared API. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, DeclareNewApiRule, "~C"), + // /0/Test0.cs(3,21): warning RS0041: Symbol 'C' uses some oblivious reference types. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, ObliviousApiRule, "C") + ); + } + + [Fact, WorkItem(3672, "https://github.com/dotnet/roslyn-analyzers/issues/3672")] + public async Task TypeArgumentRefersToTypeParameter_OnType_ObliviousReferenceAsync() + { + var source = $$""" + + #nullable enable + {{EnabledModifierCSharp}} static class C + where T : class, System.IComparable< + #nullable disable + T + #nullable enable + > + { + } + + """; + var shippedText = "#nullable enable"; + var unshippedText = @""; + await VerifyCSharpAsync(source, shippedText, unshippedText, + // /0/Test0.cs(3,21): warning RS0016: Symbol '~C' is not part of the declared API. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, DeclareNewApiRule, "~C"), + // /0/Test0.cs(3,21): warning RS0041: Symbol 'C' uses some oblivious reference types. + GetCSharpResultAt(3, 15 + EnabledModifierCSharp.Length, ObliviousApiRule, "C") + ); + } + + [Fact] + public async Task ApiFileShippedWithDuplicateNullableEnableAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + } + + """; + + string shippedText = $@" +#nullable enable +#nullable enable +"; + + string unshippedText = $@""; + + var expected = new DiagnosticResult(ApiFilesInvalid) + .WithArguments(DeclarePublicApiAnalyzer.InvalidReasonMisplacedNullableEnable); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + public async Task ApiFileUnshippedWithDuplicateNullableEnableAsync() + { + var source = $$""" + + {{EnabledModifierCSharp}} class C + { + } + """; + + string shippedText = $@""; + + string unshippedText = $@" +#nullable enable +#nullable enable +"; + + var expected = new DiagnosticResult(ApiFilesInvalid) + .WithArguments(DeclarePublicApiAnalyzer.InvalidReasonMisplacedNullableEnable); + await VerifyCSharpAsync(source, shippedText, unshippedText, expected); + } + + [Fact] + public async Task ApiFileShippedWithoutNullableEnable_AvoidUnnecessaryDiagnosticAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + } + """; + + string shippedText = $@"C +C.C() -> void"; + + string unshippedText = $@""; + + // Only oblivious APIs, so no need to warn about lack of '#nullable enable' + await VerifyCSharpAsync(source, shippedText, unshippedText, System.Array.Empty()); + } + + [Fact] + public async Task TestAddAndRemoveMembers_CSharp_FixAsync() + { + // Unshipped file has a state 'ObsoleteField' entry and a missing 'NewField' entry. + var source = $$""" + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Field; + {{EnabledModifierCSharp}} int Property { get; set; } + {{EnabledModifierCSharp}} void Method() { } + {{EnabledModifierCSharp}} int ArrowExpressionProperty => 0; + + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:NewField|}; + } + """; + var shippedText = @""; + var unshippedText = $$""" + C + C.ArrowExpressionProperty.get -> int + C.C() -> void + C.Field -> int + C.Method() -> void + {|{{RemoveApiId}}:C.ObsoleteField -> int|} + C.Property.get -> int + C.Property.set -> void + """; + var fixedUnshippedText = @"C +C.ArrowExpressionProperty.get -> int +C.C() -> void +C.Field -> int +C.Method() -> void +C.NewField -> int +C.Property.get -> int +C.Property.set -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestSimpleMissingType_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"C"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestMultipleMissingTypeAndMember_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"C +C.Field -> int +C2 +C2.C2() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestMultipleMissingTypeAndMember_CaseSensitiveFixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field_A|}; + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field_b|}; + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field_C|}; + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field_d|}; + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"C +C.Field_A -> int +C.Field_b -> int +C.Field_C -> int +C.Field_d -> int +C2 +C2.C2() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestChangingMethodSignatureForAnUnshippedMethod_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} void {|{{AddNewApiId}}:Method|}(int p1){ } + } + """; + + var shippedText = @"C"; + // previously method had no params, so the fix should remove the previous overload. + var unshippedText = $$"""{|{{RemoveApiId}}:C.Method() -> void|}"""; + var fixedUnshippedText = @"C.Method(int p1) -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestChangingMethodSignatureForAnUnshippedMethod_Fix_WithNullabilityAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} void {|{{AddNewApiId}}:Method|}(object? p1){ } + } + """; + + var shippedText = $@"#nullable enable +C"; + // previously method had no params, so the fix should remove the previous overload. + var unshippedText = $$"""{|{{RemoveApiId}}:C.Method(string p1) -> void|}"""; + var fixedUnshippedText = @"C.Method(object? p1) -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestChangingMethodSignatureForAnUnshippedMethodWithShippedOverloads_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} void Method(int p1){ } + {{EnabledModifierCSharp}} void Method(int p1, int p2){ } + {{EnabledModifierCSharp}} void {|{{AddNewApiId}}:Method|}(char p1){ } + } + """; + + var shippedText = @"C +C.Method(int p1) -> void +C.Method(int p1, int p2) -> void"; + // previously method had no params, so the fix should remove the previous overload. + var unshippedText = $$"""{|{{RemoveApiId}}:C.Method() -> void|}"""; + var fixedUnshippedText = @"C.Method(char p1) -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestAddingNewPublicOverload_FixAsync() + { + var source = $$""" + public class C + { + private C() { } + {{EnabledModifierCSharp}} void {|{{AddNewApiId}}:Method|}(){ } + {{DisabledModifierCSharp}} void Method(int p1){ } + {{DisabledModifierCSharp}} void Method(int p1, int p2){ } + {{EnabledModifierCSharp}} void Method(char p1){ } + } + """; + + var shippedText = @""; + var unshippedText = IsInternalTest + ? """ + C.Method(char p1) -> void + """ + : """ + C + C.Method(char p1) -> void + """; + var fixedUnshippedText = IsInternalTest + ? """ + C.Method() -> void + C.Method(char p1) -> void + """ + : """ + C + C.Method() -> void + C.Method(char p1) -> void + """; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestMissingTypeAndMemberAndNestedMembers_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + + {{EnabledModifierCSharp}} class CC + { + public int {|{{AddNewApiId}}:Field|}; + } + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @"C.CC +C.CC.CC() -> void"; + var unshippedText = @""; + var fixedUnshippedText = @"C +C.CC.Field -> int +C.Field -> int +C2 +C2.C2() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestMissingNestedGenericMembersAndStaleMembers_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} CC {|{{AddNewApiId}}:Field|}; + private C3.C4 Field2; + private C3.C4 Method(C3.C4 p1) { throw new System.NotImplementedException(); } + + {{EnabledModifierCSharp}} class CC + { + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + {{EnabledModifierCSharp}} CC {|{{AddNewApiId}}:Field2|}; + } + + {{EnabledModifierCSharp}} class C3 + { + {{EnabledModifierCSharp}} class C4 { } + } + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @""; + var unshippedText = $$""" + C.C3 + C.C3.C3() -> void + C.C3.C4 + C.C3.C4.C4() -> void + C.CC + C.CC.CC() -> void + {|{{RemoveApiId}}:C.Field2 -> C.C3.C4|} + {|{{RemoveApiId}}:C.Method(C.C3.C4 p1) -> C.C3.C4|} + """; + var fixedUnshippedText = """ + C + C.C3 + C.C3.C3() -> void + C.C3.C4 + C.C3.C4.C4() -> void + C.CC + C.CC.CC() -> void + C.CC.Field -> int + C.CC.Field2 -> C.CC + C.Field -> C.CC + C2 + C2.C2() -> void + """; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestWithExistingUnshippedNestedMembers_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + + {{EnabledModifierCSharp}} class CC + { + {{EnabledModifierCSharp}} int Field; + } + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @""; + var unshippedText = @"C.CC +C.CC.CC() -> void +C.CC.Field -> int"; + var fixedUnshippedText = @"C +C.CC +C.CC.CC() -> void +C.CC.Field -> int +C.Field -> int +C2 +C2.C2() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestWithExistingUnshippedNestedGenericMembers_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + private C() { } + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:CC|} + { + {{EnabledModifierCSharp}} int Field; + } + + {{EnabledModifierCSharp}} class CC + { + private CC() { } + {{EnabledModifierCSharp}} int Field; + } + } + """; + + var shippedText = @""; + var unshippedText = @"C +C.CC +C.CC.Field -> int +C.CC +C.CC.Field -> int"; + var fixedUnshippedText = @"C +C.CC +C.CC.CC() -> void +C.CC.Field -> int +C.CC +C.CC.Field -> int"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestWithExistingShippedNestedMembers_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + + {{EnabledModifierCSharp}} class CC + { + {{EnabledModifierCSharp}} int Field; + } + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @"C.CC +C.CC.CC() -> void +C.CC.Field -> int"; + var unshippedText = @""; + var fixedUnshippedText = @"C +C.Field -> int +C2 +C2.C2() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + public async Task TestOnlyRemoveStaleSiblingEntries_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:C|} + { + private C() { } + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:Field|}; + + {{EnabledModifierCSharp}} class CC + { + private int Field; // This has a stale public API entry, but this shouldn't be removed unless we attempt to add a public API entry for a sibling. + } + } + + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C2|}|} { } + """; + + var shippedText = @""; + var unshippedText = $$""" + + C.CC + C.CC.CC() -> void + {|{{RemoveApiId}}:C.CC.Field -> int|} + """; + var fixedUnshippedText = $$""" + C + C.CC + C.CC.CC() -> void + {|{{RemoveApiId}}:C.CC.Field -> int|} + C.Field -> int + C2 + C2.C2() -> void + """; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Theory] + [InlineData("", "")] + [InlineData("\r\n", "\r\n")] + [InlineData("\r\n\r\n", "\r\n")] + public async Task TestPreserveTrailingNewlineAsync(string originalEndOfFile, string expectedEndOfFile) + { + var source = $$""" + {{EnabledModifierCSharp}} class C + { + {{EnabledModifierCSharp}} int Property { get; } + + {{EnabledModifierCSharp}} int {|{{AddNewApiId}}:NewField|}; // Newly added field, not in current public API. + } + """; + + var shippedText = @""; + var unshippedText = $@"C +C.C() -> void +C.Property.get -> int{originalEndOfFile}"; + var fixedUnshippedText = $@"C +C.C() -> void +C.NewField -> int +C.Property.get -> int{expectedEndOfFile}"; + + await VerifyCSharpAdditionalFileFixAsync( + source.ReplaceLineEndings("\r\n"), + shippedText, + unshippedText.ReplaceLineEndings("\r\n"), + fixedUnshippedText.ReplaceLineEndings("\r\n")); + } + + [Fact] + public async Task MissingType_AAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:A|}|} { } + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class D { } + """; + + var unshippedText = @"B +B.B() -> void +D +D.D() -> void"; + + var expectedUnshippedText = @"A +A.A() -> void +B +B.B() -> void +D +D.D() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact] + public async Task MissingType_CAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} { } + {{EnabledModifierCSharp}} class D { } + """; + + var unshippedText = @"B +B.B() -> void +D +D.D() -> void"; + + var expectedUnshippedText = @"B +B.B() -> void +C +C.C() -> void +D +D.D() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact] + public async Task MissingType_EAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class D { } + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:E|}|} { } + """; + + var unshippedText = @"B +B.B() -> void +D +D.D() -> void"; + + var expectedUnshippedText = @"B +B.B() -> void +D +D.D() -> void +E +E.E() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact] + public async Task MissingType_Unordered_AAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:A|}|} { } + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class D { } + """; + + var unshippedText = @"D +D.D() -> void +B +B.B() -> void"; + + var expectedUnshippedText = @"A +A.A() -> void +D +D.D() -> void +B +B.B() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact] + public async Task MissingType_Unordered_CAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} { } + {{EnabledModifierCSharp}} class D { } + """; + + var unshippedText = @"D +D.D() -> void +B +B.B() -> void"; + + var expectedUnshippedText = @"C +C.C() -> void +D +D.D() -> void +B +B.B() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact] + public async Task MissingType_Unordered_EAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} class B { } + {{EnabledModifierCSharp}} class D { } + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:E|}|} { } + """; + + var unshippedText = @"D +D.D() -> void +B +B.B() -> void"; + + var expectedUnshippedText = @"D +D.D() -> void +B +B.B() -> void +E +E.E() -> void"; + await VerifyCSharpAdditionalFileFixAsync(source, shippedApiText: "", oldUnshippedApiText: unshippedText, newUnshippedApiText: expectedUnshippedText); + } + + [Fact, WorkItem(2195, "https://github.com/dotnet/roslyn-analyzers/issues/2195")] + public async Task TestPartialTypeAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} partial class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} + { + } + + {{EnabledModifierCSharp}} partial class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} + { + } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"C +C.C() -> void"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact, WorkItem(4133, "https://github.com/dotnet/roslyn-analyzers/issues/4133")] + public async Task Record_ImplicitProperty_FixAsync() + { + var source = $$""" + {{EnabledModifierCSharp}} record R(int {|{{AddNewApiId}}:P|}); + """; + + var shippedText = """ + #nullable enable + R.R(R! original) -> void + virtual R.$() -> R! + R.Deconstruct(out int P) -> void + override R.Equals(object? obj) -> bool + override R.GetHashCode() -> int + override R.ToString() -> string! + static R.operator !=(R? left, R? right) -> bool + static R.operator ==(R? left, R? right) -> bool + virtual R.EqualityContract.get -> System.Type! + virtual R.Equals(R? other) -> bool + virtual R.PrintMembers(System.Text.StringBuilder! builder) -> bool + """; + var unshippedText = """ + #nullable enable + R + R.R(int P) -> void + R.P.get -> int + """; + var fixedUnshippedText = """ + #nullable enable + R + R.P.init -> void + R.R(int P) -> void + R.P.get -> int + """; + + await VerifyNet50CSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Fact] + [WorkItem(6759, "https://github.com/dotnet/roslyn-analyzers/issues/6759")] + public async Task TestExperimentalApiAsync() + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + [Experimental("ID1")] + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} + { + } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"[ID1]C +[ID1]C.C() -> void"; + + await VerifyNet80CSharpAdditionalFileFixAsync(source, shippedText, unshippedText, fixedUnshippedText); + } + + [Theory] + [InlineData("")] + [InlineData("null")] + [InlineData("1")] + [InlineData("1, 2")] + [WorkItem(6759, "https://github.com/dotnet/roslyn-analyzers/issues/6759")] + public async Task TestExperimentalApiWithInvalidArgumentAsync(string invalidArgument) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + [Experimental({{invalidArgument}})] + {{EnabledModifierCSharp}} class {|{{AddNewApiId}}:{|{{AddNewApiId}}:C|}|} + { + } + """; + + var shippedText = @""; + var unshippedText = @""; + var fixedUnshippedText = @"[???]C +[???]C.C() -> void"; + + var test = new CSharpCodeFixTest() + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net80, + CompilerDiagnostics = CompilerDiagnostics.None, + TestState = + { + Sources = { source }, + AdditionalFiles = + { + (ShippedFileName, shippedText), + (UnshippedFileName, unshippedText), + }, + }, + FixedState = + { + AdditionalFiles = + { + (ShippedFileName, shippedText), + (UnshippedFileName, fixedUnshippedText), + }, + }, + }; + + test.DisabledDiagnostics.AddRange(DisabledDiagnostics); + + await test.RunAsync(); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsInternal.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsInternal.cs new file mode 100644 index 0000000000000..89129590ca05e --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsInternal.cs @@ -0,0 +1,49 @@ +// 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 Roslyn.Diagnostics.Analyzers; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public class DeclarePublicApiAnalyzerTestsInternal : DeclarePublicApiAnalyzerTestsBase + { + protected override bool IsInternalTest => true; + protected override string EnabledModifierCSharp => "internal"; + protected override string DisabledModifierCSharp => "public"; + protected override string EnabledModifierVB => "Friend"; + protected override string DisabledModifierVB => "Public"; + protected override string ShippedFileName => DeclarePublicApiAnalyzer.InternalShippedFileName; + protected override string UnshippedFileName => DeclarePublicApiAnalyzer.InternalUnshippedFileName; + protected override string UnshippedFileNamePrefix => DeclarePublicApiAnalyzer.InternalUnshippedFileNamePrefix; + protected override string AddNewApiId => RoslynDiagnosticIds.DeclareInternalApiRuleId; + protected override string RemoveApiId => RoslynDiagnosticIds.RemoveDeletedInternalApiRuleId; + protected override string DuplicatedSymbolInApiFileId => RoslynDiagnosticIds.DuplicatedSymbolInInternalApiFiles; + protected override string ShouldAnnotateApiFilesId => RoslynDiagnosticIds.ShouldAnnotateInternalApiFilesRuleId; + protected override string ObliviousApiId => RoslynDiagnosticIds.ObliviousInternalApiRuleId; + protected override DiagnosticDescriptor DeclareNewApiRule => DeclarePublicApiAnalyzer.DeclareNewInternalApiRule; + protected override DiagnosticDescriptor RemoveDeletedApiRule => DeclarePublicApiAnalyzer.RemoveDeletedInternalApiRule; + protected override DiagnosticDescriptor DuplicateSymbolInApiFiles => DeclarePublicApiAnalyzer.DuplicateSymbolInInternalApiFiles; + protected override DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParameters => DeclarePublicApiAnalyzer.AvoidMultipleOverloadsWithOptionalParametersInternal; + protected override DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParameters => DeclarePublicApiAnalyzer.OverloadWithOptionalParametersShouldHaveMostParametersInternal; + protected override DiagnosticDescriptor AnnotateApiRule => DeclarePublicApiAnalyzer.AnnotateInternalApiRule; + protected override DiagnosticDescriptor ObliviousApiRule => DeclarePublicApiAnalyzer.ObliviousInternalApiRule; + protected override DiagnosticDescriptor ApiFilesInvalid => DeclarePublicApiAnalyzer.InternalApiFilesInvalid; + protected override DiagnosticDescriptor ApiFileMissing => DeclarePublicApiAnalyzer.InternalApiFileMissing; + + protected override IEnumerable DisabledDiagnostics => new[] { + RoslynDiagnosticIds.DeclarePublicApiRuleId, + RoslynDiagnosticIds.RemoveDeletedPublicApiRuleId, + RoslynDiagnosticIds.PublicApiFilesInvalid, + RoslynDiagnosticIds.DuplicatedSymbolInPublicApiFiles, + RoslynDiagnosticIds.AnnotatePublicApiRuleId, + RoslynDiagnosticIds.ShouldAnnotatePublicApiFilesRuleId, + RoslynDiagnosticIds.ObliviousPublicApiRuleId, + RoslynDiagnosticIds.PublicApiFileMissing, + RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersPublic, + RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersPublic, + RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleIdPublic, + }; + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsPublic.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsPublic.cs new file mode 100644 index 0000000000000..5a7b5143722ca --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/DeclarePublicAPIAnalyzerTestsPublic.cs @@ -0,0 +1,49 @@ +// 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 Roslyn.Diagnostics.Analyzers; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public class DeclarePublicApiAnalyzerTestsPublic : DeclarePublicApiAnalyzerTestsBase + { + protected override bool IsInternalTest => false; + protected override string EnabledModifierCSharp => "public"; + protected override string DisabledModifierCSharp => "internal"; + protected override string EnabledModifierVB => "Public"; + protected override string DisabledModifierVB => "Friend"; + protected override string ShippedFileName => DeclarePublicApiAnalyzer.PublicShippedFileName; + protected override string UnshippedFileName => DeclarePublicApiAnalyzer.PublicUnshippedFileName; + protected override string UnshippedFileNamePrefix => DeclarePublicApiAnalyzer.PublicUnshippedFileNamePrefix; + protected override string AddNewApiId => RoslynDiagnosticIds.DeclarePublicApiRuleId; + protected override string RemoveApiId => RoslynDiagnosticIds.RemoveDeletedPublicApiRuleId; + protected override string DuplicatedSymbolInApiFileId => RoslynDiagnosticIds.DuplicatedSymbolInPublicApiFiles; + protected override string ShouldAnnotateApiFilesId => RoslynDiagnosticIds.ShouldAnnotatePublicApiFilesRuleId; + protected override string ObliviousApiId => RoslynDiagnosticIds.ObliviousPublicApiRuleId; + protected override DiagnosticDescriptor DeclareNewApiRule => DeclarePublicApiAnalyzer.DeclareNewPublicApiRule; + protected override DiagnosticDescriptor RemoveDeletedApiRule => DeclarePublicApiAnalyzer.RemoveDeletedPublicApiRule; + protected override DiagnosticDescriptor DuplicateSymbolInApiFiles => DeclarePublicApiAnalyzer.DuplicateSymbolInPublicApiFiles; + protected override DiagnosticDescriptor AvoidMultipleOverloadsWithOptionalParameters => DeclarePublicApiAnalyzer.AvoidMultipleOverloadsWithOptionalParametersPublic; + protected override DiagnosticDescriptor OverloadWithOptionalParametersShouldHaveMostParameters => DeclarePublicApiAnalyzer.OverloadWithOptionalParametersShouldHaveMostParametersPublic; + protected override DiagnosticDescriptor AnnotateApiRule => DeclarePublicApiAnalyzer.AnnotatePublicApiRule; + protected override DiagnosticDescriptor ObliviousApiRule => DeclarePublicApiAnalyzer.ObliviousPublicApiRule; + protected override DiagnosticDescriptor ApiFilesInvalid => DeclarePublicApiAnalyzer.PublicApiFilesInvalid; + protected override DiagnosticDescriptor ApiFileMissing => DeclarePublicApiAnalyzer.PublicApiFileMissing; + + protected override IEnumerable DisabledDiagnostics => new[] { + RoslynDiagnosticIds.DeclareInternalApiRuleId, + RoslynDiagnosticIds.RemoveDeletedInternalApiRuleId, + RoslynDiagnosticIds.InternalApiFilesInvalid, + RoslynDiagnosticIds.DuplicatedSymbolInInternalApiFiles, + RoslynDiagnosticIds.AnnotateInternalApiRuleId, + RoslynDiagnosticIds.ShouldAnnotateInternalApiFilesRuleId, + RoslynDiagnosticIds.ObliviousInternalApiRuleId, + RoslynDiagnosticIds.InternalApiFileMissing, + RoslynDiagnosticIds.AvoidMultipleOverloadsWithOptionalParametersInternal, + RoslynDiagnosticIds.OverloadWithOptionalParametersShouldHaveMostParametersInternal, + RoslynDiagnosticIds.ExposedNoninstantiableTypeRuleIdInternal, + }; + } +} diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests.csproj b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests.csproj new file mode 100644 index 0000000000000..a7ec0aba1e615 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests.csproj @@ -0,0 +1,21 @@ + + + + $(NetRoslyn) + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/NullableEnablePublicApiAnalyzerTests.cs b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/NullableEnablePublicApiAnalyzerTests.cs new file mode 100644 index 0000000000000..d8b805e7af438 --- /dev/null +++ b/src/RoslynAnalyzers/PublicApiAnalyzers/UnitTests/NullableEnablePublicApiAnalyzerTests.cs @@ -0,0 +1,205 @@ +// 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. + +#pragma warning disable CA1305 + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; +using Xunit; + +namespace Microsoft.CodeAnalysis.PublicApiAnalyzers.UnitTests +{ + public class NullableEnablePublicApiAnalyzerTests + { + #region Utilities + private async Task VerifyCSharpAsync(string source, string shippedApiText, string unshippedApiText, params DiagnosticResult[] expected) + { + var test = new CSharpCodeFixTest + { + TestState = + { + Sources = { source }, + AdditionalFiles = { }, + }, + }; + + if (shippedApiText != null) + { + test.TestState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicShippedFileName, shippedApiText)); + } + + if (unshippedApiText != null) + { + test.TestState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicUnshippedFileName, unshippedApiText)); + } + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + private async Task VerifyCSharpAdditionalFileFixAsync(string source, string oldShippedApiText, string oldUnshippedApiText, string newSource, string newShippedApiText, string newUnshippedApiText) + { + await VerifyAdditionalFileFixAsync(source, oldShippedApiText, oldUnshippedApiText, newSource, newShippedApiText, newUnshippedApiText); + } + + private async Task VerifyAdditionalFileFixAsync(string source, string oldShippedApiText, string oldUnshippedApiText, string newSource, string newShippedApiText, string newUnshippedApiText) + { + var test = new CSharpCodeFixTest(); + + test.TestState.Sources.Add(source); + test.TestState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicShippedFileName, oldShippedApiText)); + test.TestState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicUnshippedFileName, oldUnshippedApiText)); + + test.FixedState.Sources.Add(newSource); + test.FixedState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicShippedFileName, newShippedApiText)); + test.FixedState.AdditionalFiles.Add((DeclarePublicApiAnalyzer.PublicUnshippedFileName, newUnshippedApiText)); + + await test.RunAsync(); + } + #endregion + + #region Fix tests + + [Fact] + public async Task NullableEnableShippedAPI_NullableMemberAsync() + { + var source = @" +#nullable enable +public class C +{ + public string? {|RS0037:Field|}; +} +"; + + var shippedText = @""; + var unshippedText = @"C +C.C() -> void +C.Field -> string"; + + // The source is unchanged, but a new diagnostic appears + var newSource = @" +#nullable enable +public class C +{ + public string? {|RS0036:Field|}; +} +"; + var fixedShippedText = @"#nullable enable +"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newSource, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task NullableEnableShippedAPI_NonNullableMemberAsync() + { + var source = @" +#nullable enable +public class C +{ + public string {|RS0037:Field2|}; +} +"; + + var shippedText = @""; + var unshippedText = @"C +C.C() -> void +C.Field2 -> string"; + + // The source is unchanged, but a new diagnostic appears + var newSource = @" +#nullable enable +public class C +{ + public string {|RS0036:Field2|}; +} +"; + + var fixedShippedText = @"#nullable enable +"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newSource, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task NullableEnableShippedAPI_NonEmptyFileAsync() + { + var source = @" +#nullable enable +public class C +{ + public string? {|RS0037:Field|}; +} +"; + + var shippedText = @"C +C.C() -> void +C.Field -> string"; + var unshippedText = @""; + + var newSource = @" +#nullable enable +public class C +{ + public string? {|RS0036:Field|}; +} +"; + + var fixedShippedText = @"#nullable enable +C +C.C() -> void +C.Field -> string"; + + await VerifyCSharpAdditionalFileFixAsync(source, shippedText, unshippedText, newSource, fixedShippedText, newUnshippedApiText: unshippedText); + } + + [Fact] + public async Task DoNotWarnIfAlreadyEnabled_ViaUnshippedFileAsync() + { + var source = @" +#nullable enable +public class C +{ + public string? {|RS0036:Field|}; + public string {|RS0036:Field2|}; +} +"; + + var shippedText = @"C +C.C() -> void +C.Field -> string +C.Field2 -> string"; + + var unshippedText = @"#nullable enable"; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + [Fact] + public async Task DoNotWarnIfAlreadyEnabled_ViaShippedFileAsync() + { + var source = @" +#nullable enable +public class C +{ + public string? {|RS0036:Field|}; + public string {|RS0036:Field2|}; +} +"; + + var shippedText = @"#nullable enable +C +C.C() -> void +C.Field -> string +C.Field2 -> string"; + + var unshippedText = @""; + + await VerifyCSharpAsync(source, shippedText, unshippedText); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..065d2f0762d09 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Shipped.md @@ -0,0 +1,19 @@ +## Release 3.3.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0038 | RoslynDiagnosticsMaintainability | Warning | PreferNullLiteral +RS0046 | RoslynDiagnosticsDesign | Warning | CSharpAvoidOptSuffixForNullableEnableCode +RS0100 | RoslynDiagnosticsMaintainability | Warning | CSharpWrapStatementsDiagnosticAnalyzer +RS0102 | RoslynDiagnosticsMaintainability | Warning | CSharpBracePlacementDiagnosticAnalyzer + +## Release 3.3.3 + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0100 | RoslynDiagnosticsMaintainability | Warning | CSharpWrapStatementsDiagnosticAnalyzer +RS0102 | RoslynDiagnosticsMaintainability | Warning | CSharpBracePlacementDiagnosticAnalyzer diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..9d0ded92a0f46 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -0,0 +1,8 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0062 | RoslynDiagnosticsMaintainability | Disabled | DoNotCapturePrimaryConstructorParametersAnalyzer +RS0063 | RoslynDiagnosticsPerformance | Disabled | CSharpDoNotUseDebugAssertForInterpolatedStrings diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpApplyTraitToClass.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpApplyTraitToClass.cs new file mode 100644 index 0000000000000..a59fdb2194c32 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpApplyTraitToClass.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpApplyTraitToClass))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpApplyTraitToClass() : AbstractApplyTraitToClass + { + private protected override IRefactoringHelpers RefactoringHelpers => CSharpRefactoringHelpers.Instance; + + protected override SyntaxNode? GetTypeDeclarationForNode(SyntaxNode reportedNode) + => reportedNode.FirstAncestorOrSelf(); + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.cs new file mode 100644 index 0000000000000..565a3cec4a8f2 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCode.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. + +#nullable disable warnings + +using System; +using System.Collections.Immutable; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0046: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpAvoidOptSuffixForNullableEnableCode : DiagnosticAnalyzer + { + internal const string OptSuffix = "Opt"; + + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.AvoidOptSuffixForNullableEnableCodeRuleId, + CreateLocalizableResourceString(nameof(AvoidOptSuffixForNullableEnableCodeTitle)), + CreateLocalizableResourceString(nameof(AvoidOptSuffixForNullableEnableCodeMessage)), + DiagnosticCategory.RoslynDiagnosticsDesign, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(AvoidOptSuffixForNullableEnableCodeDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public sealed override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterSyntaxNodeAction(context => + { + var parameter = (ParameterSyntax)context.Node; + ReportOnInvalidIdentifier(parameter.Identifier, context.SemanticModel, context.ReportDiagnostic, context.CancellationToken); + }, SyntaxKind.Parameter); + + context.RegisterSyntaxNodeAction(context => + { + var variableDeclarator = (VariableDeclaratorSyntax)context.Node; + ReportOnInvalidIdentifier(variableDeclarator.Identifier, context.SemanticModel, context.ReportDiagnostic, context.CancellationToken); + }, SyntaxKind.VariableDeclarator); + + context.RegisterSyntaxNodeAction(context => + { + var propertyDeclaration = (PropertyDeclarationSyntax)context.Node; + ReportOnInvalidIdentifier(propertyDeclaration.Identifier, context.SemanticModel, context.ReportDiagnostic, context.CancellationToken); + }, SyntaxKind.PropertyDeclaration); + } + + private static void ReportOnInvalidIdentifier(SyntaxToken identifier, SemanticModel semanticModel, Action reportAction, CancellationToken cancellationToken) + { + if (!identifier.Text.EndsWith(OptSuffix, StringComparison.Ordinal) || + !semanticModel.GetNullableContext(identifier.SpanStart).AnnotationsEnabled()) + { + return; + } + + var symbol = semanticModel.GetDeclaredSymbol(identifier.Parent, cancellationToken); + + if (ShouldReport(symbol)) + { + reportAction(identifier.CreateDiagnostic(Rule)); + } + } + + private static bool ShouldReport(ISymbol symbol) + { + if (symbol?.GetMemberOrLocalOrParameterType()?.NullableAnnotation != NullableAnnotation.Annotated) + { + // Not in a nullable context, bail-out + return false; + } + + return symbol.Kind switch + { + SymbolKind.Property => !symbol.IsOverride + && !symbol.IsImplementationOfAnyInterfaceMember(), + + SymbolKind.Parameter => symbol.ContainingSymbol != null + && !symbol.ContainingSymbol.IsOverride + && !symbol.ContainingSymbol.IsImplementationOfAnyInterfaceMember(), + + _ => true + }; + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs new file mode 100644 index 0000000000000..28de224bf2595 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider.cs @@ -0,0 +1,70 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Rename; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp)] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(CSharpAvoidOptSuffixForNullableEnableCode.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var title = RoslynDiagnosticsAnalyzersResources.AvoidOptSuffixForNullableEnableCodeCodeFixTitle; + + foreach (var diagnostic in context.Diagnostics) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var variable = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + if (variable == null) + { + continue; + } + + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var variableSymbol = semanticModel.GetDeclaredSymbol(variable, context.CancellationToken); + if (variableSymbol == null || variableSymbol.Name.Length <= CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length) + { + continue; + } + + var newName = variableSymbol.Name[..^CSharpAvoidOptSuffixForNullableEnableCode.OptSuffix.Length]; + + // There is no symbol matching the new name so we can register the codefix + if (semanticModel.LookupSymbols(diagnostic.Location.SourceSpan.Start, variableSymbol.ContainingType, newName).IsEmpty) + { + context.RegisterCodeFix( + CodeAction.Create( + title, + cancellationToken => RemoveOptSuffixOnVariableAsync(context.Document, variableSymbol, newName, cancellationToken), + equivalenceKey: title), + diagnostic); + } + } + } + + private static async Task RemoveOptSuffixOnVariableAsync(Document document, ISymbol variableSymbol, string newName, CancellationToken cancellationToken) + => await Renamer.RenameSymbolAsync(document.Project.Solution, variableSymbol, newName, document.Project.Solution.Options, cancellationToken) + .ConfigureAwait(false); + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpCreateTestAccessor.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpCreateTestAccessor.cs new file mode 100644 index 0000000000000..623bd9673dbc6 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpCreateTestAccessor.cs @@ -0,0 +1,34 @@ +// 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 warnings + +using System; +using System.Composition; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpCreateTestAccessor))] + [Shared] + public sealed class CSharpCreateTestAccessor : AbstractCreateTestAccessor + { + [ImportingConstructor] + [Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public CSharpCreateTestAccessor() + { + } + + private protected override IRefactoringHelpers RefactoringHelpers => CSharpRefactoringHelpers.Instance; + + protected override SyntaxNode GetTypeDeclarationForNode(SyntaxNode reportedNode) + { + return reportedNode.FirstAncestorOrSelf(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCapturePrimaryContructorParameters.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCapturePrimaryContructorParameters.cs new file mode 100644 index 0000000000000..090e3667290ed --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCapturePrimaryContructorParameters.cs @@ -0,0 +1,63 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Roslyn.Diagnostics.Analyzers; +using System.Collections.Immutable; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class DoNotCapturePrimaryConstructorParametersAnalyzer : DiagnosticAnalyzer + { + private static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.DoNotCapturePrimaryConstructorParametersRuleId, + CreateLocalizableResourceString(nameof(DoNotCapturePrimaryConstructorParametersTitle)), + CreateLocalizableResourceString(nameof(DoNotCapturePrimaryConstructorParametersMessage)), + DiagnosticCategory.RoslynDiagnosticsMaintainability, + DiagnosticSeverity.Error, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(DoNotCapturePrimaryConstructorParametersDescription))); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + context.RegisterOperationAction(AnalyzeOperation, OperationKind.ParameterReference); + } + + private static void AnalyzeOperation(OperationAnalysisContext context) + { + var operation = (IParameterReferenceOperation)context.Operation; + + if (operation.Parameter.ContainingSymbol == context.ContainingSymbol || operation.Parameter.ContainingSymbol is not IMethodSymbol { MethodKind: MethodKind.Constructor }) + { + // We're in the primary constructor itself, so no capture. + // Or, this isn't a primary constructor parameter at all. + return; + } + + IOperation rootOperation = operation; + for (; rootOperation.Parent != null; rootOperation = rootOperation.Parent) + { + } + + if (rootOperation is IPropertyInitializerOperation or IFieldInitializerOperation) + { + // This is an explicit capture into member state. That's fine. + return; + } + + // This must be a capture. Error + context.ReportDiagnostic(Diagnostic.Create(Rule, operation.Syntax.GetLocation(), operation.Parameter.Name)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCopyValue.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCopyValue.cs new file mode 100644 index 0000000000000..119e1b8aa1be6 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotCopyValue.cs @@ -0,0 +1,64 @@ +// 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 warnings + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpDoNotCopyValue : AbstractDoNotCopyValue + { + protected override NonCopyableWalker CreateWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) + => new CSharpNonCopyableWalker(context, cache); + + protected override NonCopyableSymbolWalker CreateSymbolWalker(SymbolAnalysisContext context, NonCopyableTypesCache cache) + => new CSharpNonCopyableSymbolWalker(context, cache); + + private sealed class CSharpNonCopyableWalker : NonCopyableWalker + { + public CSharpNonCopyableWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) + : base(context, cache) + { + } + + protected override bool CheckForEachGetEnumerator(IForEachLoopOperation operation, [DisallowNull] ref IConversionOperation? conversion, [DisallowNull] ref IOperation? instance) + { + if (operation.Syntax is CommonForEachStatementSyntax syntax + && operation.SemanticModel.GetForEachStatementInfo(syntax).GetEnumeratorMethod is { } getEnumeratorMethod) + { + CheckMethodSymbolInUnsupportedContext(operation, getEnumeratorMethod); + + if (instance is not null + && Cache.IsNonCopyableType(getEnumeratorMethod.ReceiverType) + && !getEnumeratorMethod.IsReadOnly + && Acquire(instance) == RefKind.In) + { + // mark the instance as not checked by this method + instance = null; + } + + return true; + } + + return false; + } + } + + private sealed class CSharpNonCopyableSymbolWalker : NonCopyableSymbolWalker + { + public CSharpNonCopyableSymbolWalker(SymbolAnalysisContext context, NonCopyableTypesCache cache) + : base(context, cache) + { + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs new file mode 100644 index 0000000000000..a555a598856ef --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + /// + /// RS0006: Do not mix attributes from different versions of MEF + /// + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class CSharpDoNotMixAttributesFromDifferentVersionsOfMEFFixer() : DoNotMixAttributesFromDifferentVersionsOfMEFFixer + { + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStrings.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStrings.cs new file mode 100644 index 0000000000000..f66f2f8ef7c99 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStrings.cs @@ -0,0 +1,76 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpDoNotUseDebugAssertForInterpolatedStrings : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.DoNotUseInterpolatedStringsWithDebugAssertRuleId, + CreateLocalizableResourceString(nameof(DoNotUseInterpolatedStringsWithDebugAssertTitle)), + CreateLocalizableResourceString(nameof(DoNotUseInterpolatedStringsWithDebugAssertMessage)), + DiagnosticCategory.RoslynDiagnosticsPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(DoNotUseInterpolatedStringsWithDebugAssertDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + var debugType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsDebug); + + if (debugType is null) + { + return; + } + + IMethodSymbol? assertMethod = null; + + foreach (var member in debugType.GetMembers("Assert")) + { + if (member is IMethodSymbol { Parameters: [{ Type.SpecialType: SpecialType.System_Boolean }, { Type.SpecialType: SpecialType.System_String }] } method) + { + assertMethod = method; + break; + } + } + + if (assertMethod is null) + { + return; + } + + context.RegisterOperationAction(context => + { + var invocation = (IInvocationOperation)context.Operation; + + if (invocation.TargetMethod.Equals(assertMethod) && + invocation.Arguments is [_, IArgumentOperation { Value: IInterpolatedStringOperation { ConstantValue.HasValue: false } }]) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(Rule)); + } + }, OperationKind.Invocation); + }); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStringsFixer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStringsFixer.cs new file mode 100644 index 0000000000000..705d65842f9ac --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpDoNotUseDebugAssertForInterpolatedStringsFixer.cs @@ -0,0 +1,85 @@ +// 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.Composition; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(CSharpDoNotUseDebugAssertForInterpolatedStringsFixer))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpDoNotUseDebugAssertForInterpolatedStringsFixer() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(RoslynDiagnosticIds.DoNotUseInterpolatedStringsWithDebugAssertRuleId); + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var compilation = await context.Document.Project.GetCompilationAsync(context.CancellationToken).ConfigureAwait(false); + + if (compilation is null) + { + return; + } + + var roslynDebugSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.RoslynDebug); + + if (roslynDebugSymbol is null) + { + return; + } + + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.DoNotUseInterpolatedStringsWithDebugAssertCodeFix, + ct => ReplaceWithDebugAssertAsync(context.Document, diagnostic.Location, roslynDebugSymbol, ct), + equivalenceKey: nameof(CSharpDoNotUseDebugAssertForInterpolatedStringsFixer)), + diagnostic); + } + } + + private static async Task ReplaceWithDebugAssertAsync(Document document, Location location, INamedTypeSymbol roslynDebugSymbol, CancellationToken cancellationToken) + { + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntax = root.FindNode(location.SourceSpan, getInnermostNodeForTie: true); + var generator = SyntaxGenerator.GetGenerator(document); + + if (syntax is not InvocationExpressionSyntax + { + Expression: MemberAccessExpressionSyntax + { + Expression: IdentifierNameSyntax { Identifier.ValueText: "Debug" } debugIdentifierNode, + Name.Identifier.ValueText: "Assert" + }, + }) + { + return document; + } + + var roslynDebugNode = generator.TypeExpression(roslynDebugSymbol) + .WithAddImportsAnnotation() + .WithLeadingTrivia(debugIdentifierNode.GetLeadingTrivia()) + .WithTrailingTrivia(debugIdentifierNode.GetTrailingTrivia()); + + var newRoot = root.ReplaceNode(debugIdentifierNode, roslynDebugNode); + return document.WithSyntaxRoot(newRoot); + } + + public override FixAllProvider? GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpExposeMemberForTesting.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpExposeMemberForTesting.cs new file mode 100644 index 0000000000000..82ff4db303fd3 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpExposeMemberForTesting.cs @@ -0,0 +1,55 @@ +// 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 warnings + +using System; +using System.Composition; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpExposeMemberForTesting))] + [Shared] + public sealed class CSharpExposeMemberForTesting : AbstractExposeMemberForTesting + { + [ImportingConstructor] + [Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public CSharpExposeMemberForTesting() + { + } + + private protected override IRefactoringHelpers RefactoringHelpers => CSharpRefactoringHelpers.Instance; + + protected override bool HasRefReturns => true; + + protected override SyntaxNode GetTypeDeclarationForNode(SyntaxNode reportedNode) + { + return reportedNode.FirstAncestorOrSelf(); + } + + protected override SyntaxNode GetByRefType(SyntaxNode type, RefKind refKind) + { + var refKeyword = SyntaxFactory.Token(SyntaxKind.RefKeyword); + var readOnlyKeyword = refKind switch + { + RefKind.Ref => default, + RefKind.RefReadOnly => SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword), + _ => throw new ArgumentOutOfRangeException(nameof(refKind)), + }; + + return SyntaxFactory.RefType(refKeyword, readOnlyKeyword, (TypeSyntax)type); + } + + protected override SyntaxNode GetByRefExpression(SyntaxNode expression) + { + return SyntaxFactory.RefExpression((ExpressionSyntax)expression); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs new file mode 100644 index 0000000000000..a7a08ab03ac7e --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs @@ -0,0 +1,23 @@ +// 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.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + /// + /// RS0023: Parts exported with MEFv2 must be marked as Shared + /// + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class CSharpPartsExportedWithMEFv2MustBeMarkedAsSharedFixer() : PartsExportedWithMEFv2MustBeMarkedAsSharedFixer + { + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpRunIterations.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpRunIterations.cs new file mode 100644 index 0000000000000..f242b5129ab93 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpRunIterations.cs @@ -0,0 +1,23 @@ +// 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.Composition; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpRunIterations))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class CSharpRunIterations() : AbstractRunIterations + { + private protected override IRefactoringHelpers RefactoringHelpers => CSharpRefactoringHelpers.Instance; + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSpecializedEnumerableCreationAnalyzer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSpecializedEnumerableCreationAnalyzer.cs new file mode 100644 index 0000000000000..929377730c003 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSpecializedEnumerableCreationAnalyzer.cs @@ -0,0 +1,106 @@ +// 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 warnings + +using System; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpSpecializedEnumerableCreationAnalyzer : SpecializedEnumerableCreationAnalyzer + { + protected override void GetCodeBlockStartedAnalyzer(CompilationStartAnalysisContext context, INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + { + context.RegisterCodeBlockStartAction(new CodeBlockStartedAnalyzer(genericEnumerableSymbol, genericEmptyEnumerableSymbol).Initialize); + } + + private sealed class CodeBlockStartedAnalyzer : AbstractCodeBlockStartedAnalyzer + { + public CodeBlockStartedAnalyzer(INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + : base(genericEnumerableSymbol, genericEmptyEnumerableSymbol) + { + } + + protected override void GetSyntaxAnalyzer(CodeBlockStartAnalysisContext context, INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + { + context.RegisterSyntaxNodeAction(new SyntaxAnalyzer(genericEnumerableSymbol, genericEmptyEnumerableSymbol).AnalyzeNode, SyntaxKind.ReturnStatement); + } + } + + private sealed class SyntaxAnalyzer : AbstractSyntaxAnalyzer + { + public SyntaxAnalyzer(INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + : base(genericEnumerableSymbol, genericEmptyEnumerableSymbol) + { + } + + public void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + System.Collections.Generic.IEnumerable expressionsToAnalyze = context.Node.DescendantNodes().Where(n => ShouldAnalyzeExpression(n, context.SemanticModel, context.CancellationToken)); + + foreach (SyntaxNode expression in expressionsToAnalyze) + { + switch (expression.Kind()) + { + case SyntaxKind.ArrayCreationExpression: + AnalyzeArrayCreationExpression((ArrayCreationExpressionSyntax)expression, context.ReportDiagnostic); + break; + case SyntaxKind.ImplicitArrayCreationExpression: + AnalyzeInitializerExpression(((ImplicitArrayCreationExpressionSyntax)expression).Initializer, context.ReportDiagnostic); + break; + case SyntaxKind.SimpleMemberAccessExpression: + AnalyzeMemberAccessName(((MemberAccessExpressionSyntax)expression).Name, context.SemanticModel, context.ReportDiagnostic, context.CancellationToken); + break; + } + } + } + + private bool ShouldAnalyzeExpression(SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken) + { + return expression.Kind() switch + { + SyntaxKind.ArrayCreationExpression + or SyntaxKind.ImplicitArrayCreationExpression => ShouldAnalyzeArrayCreationExpression(expression, semanticModel, cancellationToken), + SyntaxKind.SimpleMemberAccessExpression => true, + _ => false, + }; + } + + private static void AnalyzeArrayCreationExpression(ArrayCreationExpressionSyntax arrayCreationExpression, Action addDiagnostic) + { + ArrayTypeSyntax arrayType = arrayCreationExpression.Type; + if (arrayType.RankSpecifiers.Count == 1) + { + // Check for explicit specification of empty or singleton array + + if (arrayType.RankSpecifiers[0].ChildNodes() + .FirstOrDefault(n => n.IsKind(SyntaxKind.NumericLiteralExpression)) is LiteralExpressionSyntax literalRankSpecifier) + { + AnalyzeArrayLength((int)literalRankSpecifier.Token.Value, arrayCreationExpression, addDiagnostic); + return; + } + } + + AnalyzeInitializerExpression(arrayCreationExpression.Initializer, addDiagnostic); + } + + private static void AnalyzeInitializerExpression(InitializerExpressionSyntax initializer, Action addDiagnostic) + { + // Check length of initializer list for empty or singleton array + if (initializer != null) + { + AnalyzeArrayLength(initializer.Expressions.Count, initializer.Parent, addDiagnostic); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs new file mode 100644 index 0000000000000..8cadeefb2c48e --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/CSharpSymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs @@ -0,0 +1,66 @@ +// 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 warnings + +using System.Collections.Immutable; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class CSharpSymbolDeclaredEventAnalyzer : SymbolDeclaredEventAnalyzer + { + private static readonly ImmutableHashSet s_symbolTypesWithExpectedSymbolDeclaredEvent = ImmutableHashSet.Create( + "SourceNamespaceSymbol", + "SourceNamedTypeSymbol", + "SourceEventSymbol", + "SourceFieldSymbol", + "SourceMethodSymbol", + "SourcePropertySymbol"); + + protected override CompilationAnalyzer? GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol symbolType) + { + INamedTypeSymbol? compilationType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpCompilation); + if (compilationType == null) + { + return null; + } + + return new CSharpCompilationAnalyzer(symbolType, compilationType); + } + + protected override SyntaxKind InvocationExpressionSyntaxKind => SyntaxKind.InvocationExpression; + + private sealed class CSharpCompilationAnalyzer : CompilationAnalyzer + { + public CSharpCompilationAnalyzer(INamedTypeSymbol symbolType, INamedTypeSymbol compilationType) + : base(symbolType, compilationType) + { } + + protected override ImmutableHashSet SymbolTypesWithExpectedSymbolDeclaredEvent => s_symbolTypesWithExpectedSymbolDeclaredEvent; + + protected override SyntaxNode? GetFirstArgumentOfInvocation(SyntaxNode invocation) + { + var invocationExpression = (InvocationExpressionSyntax)invocation; + if (invocationExpression.ArgumentList != null) + { + ArgumentSyntax argument = invocationExpression.ArgumentList.Arguments.FirstOrDefault(); + if (argument != null) + { + return argument.Expression; + } + } + + return null; + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/NumberCommentsRefactoring.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/NumberCommentsRefactoring.cs new file mode 100644 index 0000000000000..0d37add0a4aca --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/NumberCommentsRefactoring.cs @@ -0,0 +1,255 @@ +// 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.Composition; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; + +namespace Roslyn.Diagnostics.Analyzers +{ + /// + /// This refactoring looks for numbered comments `// N` or `// N, N+1, ...` on non-empty lines within string literals + /// and checks that the numbers are sequential. If they are not, the refactoring is offered. + /// + /// This pattern is commonly used by compiler tests. + /// Comments that don't look like numbered comments are left alone. For instance, any comment that contains alpha characters. + /// + [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(NumberCommentsRefactoring)), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + internal sealed class NumberCommentsRefactoring() : CodeRefactoringProvider + { + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var literal = await context.TryGetRelevantNodeAsync(CSharpRefactoringHelpers.Instance).ConfigureAwait(false); + if (literal is null) + return; + + if (literal.Kind() == SyntaxKind.StringLiteralExpression && + !IsProperlyNumbered(literal.Token.ValueText)) + { + var action = CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.FixNumberedComments, + c => FixCommentsAsync(context.Document, literal, c)); + context.RegisterRefactoring(action); + } + } + + private static async Task FixCommentsAsync(Document document, LiteralExpressionSyntax stringLiteral, CancellationToken c) + { + var newValueText = FixComments(stringLiteral.Token.ValueText, prefix: null); + var oldText = stringLiteral.Token.Text; + var newText = FixComments(oldText, getPrefix(oldText)); + + var oldToken = stringLiteral.Token; + var newToken = SyntaxFactory.Token(oldToken.LeadingTrivia, kind: oldToken.Kind(), text: newText, valueText: newValueText, oldToken.TrailingTrivia); + var newStringLiteral = stringLiteral.Update(newToken); + + var editor = await DocumentEditor.CreateAsync(document, c).ConfigureAwait(false); + editor.ReplaceNode(stringLiteral, newStringLiteral); + return editor.GetChangedDocument(); + + static string? getPrefix(string text) + { + if (text.StartsWith("@\"", StringComparison.OrdinalIgnoreCase)) + { + return "@\""; + } + + if (text.StartsWith("\"", StringComparison.OrdinalIgnoreCase)) + { + return "\""; + } + + return null; + } + } + + public static bool IsProperlyNumbered(string text) + { + int exptectedNumber = 1; + int cursor = 0; + do + { + var (eolOrEofIndex, newLine) = FindNewLineOrEndOfFile(cursor, text, hasPrefix: false); + + // find the last comment between cursor and newLineIndex + (int commentStartIndex, _) = FindNumberComment(cursor, eolOrEofIndex, text); + if (commentStartIndex > 0) + { + var separatedNumbers = text[commentStartIndex..eolOrEofIndex]; + var numbers = separatedNumbers.Split(',').Select(removeWhiteSpace); + foreach (var number in numbers) + { + if (string.IsNullOrEmpty(number)) + { + return false; + } + + if (!int.TryParse(number, out var actualNumber) || exptectedNumber != actualNumber) + { + return false; + } + + exptectedNumber++; + } + } + + cursor = eolOrEofIndex + newLine.Length; + } + while (cursor < text.Length); + + return true; + + static string removeWhiteSpace(string self) + => new(self.Where(c => !char.IsWhiteSpace(c)).ToArray()); + } + + /// + /// Returns the index of the end of line terminator or the end of file. + /// If hasPrefix, we'll consider the terminating double-quotes (") to be the end of file. + /// + internal static (int Index, string NewLine) FindNewLineOrEndOfFile(int index, string comment, bool hasPrefix) + { + int length = GetStringLengthIgnoringQuote(comment, hasPrefix); + for (int i = index; i < length; i++) + { + var current = comment[i]; + if (current == '\n') + { + return (i, "\n"); + } + else if (current == '\r') + { + if (i + 1 < length && comment[i + 1] == '\n') + { + return (i, "\r\n"); + } + + return (i, "\r"); + } + } + + return (length, ""); // EOF + } + + internal static (int FoundIndex, int CommaCount) FindNumberComment(int cursor, int newLineIndex, string comment) + { + int commaCount = 0; + for (int index = newLineIndex - 1; index > cursor + 2; index--) + { + var current = comment[index]; + if (current == ',') + { + commaCount++; + } + + if (current == '/' && comment[index - 1] == '/' && comment[index - 2] == ' ') + { + // found start of comment + return (index + 1, commaCount); + } + + if (!IsDigitOrComma(current)) + { + break; + } + } + + return (-1, 0); + } + + internal static bool IsDigitOrComma(char c) + { + if (c is >= '0' and <= '9') + { + return true; + } + + if (c is ' ' or ',') + { + return true; + } + + return false; + } + + internal static int GetStringLengthIgnoringQuote(string text, bool hasPrefix) + { + if (hasPrefix) + { + return text.Length - 1; + } + + return text.Length; + } + + private static string FixComments(string text, string? prefix) + { + var builder = new StringBuilder(); + int nextNumber = 1; + int cursor = 0; + + if (prefix != null) + { + builder.Append(prefix); + cursor += prefix.Length; + } + + int length = GetStringLengthIgnoringQuote(text, prefix != null); + + do + { + var (eolOrEofIndex, newLine) = FindNewLineOrEndOfFile(cursor, text, prefix != null); + // find the last comment between cursor and newLineIndex + (int commentStartIndex, int commaCount) = FindNumberComment(cursor, eolOrEofIndex, text); + if (commentStartIndex > 0) + { + builder.Append(text, cursor, commentStartIndex - cursor); + appendFixedNumberComment(builder, commaCount, ref nextNumber); + builder.Append(newLine); + } + else + { + builder.Append(text, cursor, eolOrEofIndex + newLine.Length - cursor); + } + + cursor = eolOrEofIndex + newLine.Length; + } + while (cursor < length); + + if (prefix != null) + { + builder.Append('"'); + } + + return builder.ToString(); + + static void appendFixedNumberComment(StringBuilder builder, int commaCount, ref int nextNumber) + { + for (int commaIndex = 0; commaIndex <= commaCount; commaIndex++) + { + builder.Append(' '); + builder.Append(nextNumber.ToString(CultureInfo.InvariantCulture)); + nextNumber++; + if (commaIndex < commaCount) + { + builder.Append(','); + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteral.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteral.cs new file mode 100644 index 0000000000000..8deb34b46f356 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteral.cs @@ -0,0 +1,81 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0038: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class PreferNullLiteral : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.PreferNullLiteralRuleId, + CreateLocalizableResourceString(nameof(PreferNullLiteralTitle)), + CreateLocalizableResourceString(nameof(PreferNullLiteralMessage)), + DiagnosticCategory.RoslynDiagnosticsMaintainability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(PreferNullLiteralDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterOperationAction(HandleDefaultValueOperation, OperationKind.DefaultValue); + } + + private void HandleDefaultValueOperation(OperationAnalysisContext context) + { + if (context.Operation.IsImplicit) + { + // Ignore implicit operations since they don't appear in source code. + return; + } + + var type = context.Operation.Type; + if (type is null) + { + return; + } + + if (type.TypeKind == TypeKind.Pointer) + { + // Pointers can use 'null' + } + else if (type.TypeKind == TypeKind.Error) + { + return; + } + else if (type.IsValueType) + { + if (type is not INamedTypeSymbol namedType + || namedType.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T) + { + return; + } + } + else if (!type.IsReferenceType) + { + return; + } + + context.ReportDiagnostic(context.Operation.CreateDiagnostic(Rule)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteralCodeFixProvider.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteralCodeFixProvider.cs new file mode 100644 index 0000000000000..a57e1c259ac68 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/PreferNullLiteralCodeFixProvider.cs @@ -0,0 +1,80 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Simplification; +using Roslyn.Diagnostics.Analyzers; + +namespace Roslyn.Diagnostics.CSharp.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(PreferNullLiteralCodeFixProvider))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class PreferNullLiteralCodeFixProvider() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(PreferNullLiteral.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.PreferNullLiteralCodeFix, + cancellationToken => ReplaceWithNullLiteralAsync(context.Document, diagnostic.Location, cancellationToken), + equivalenceKey: nameof(PreferNullLiteralCodeFixProvider)), + diagnostic); + } + + return Task.CompletedTask; + } + + private static async Task ReplaceWithNullLiteralAsync(Document document, Location location, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var syntax = root.FindNode(location.SourceSpan, getInnermostNodeForTie: true); + + ExpressionSyntax newSyntax = SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression); + if (syntax is DefaultExpressionSyntax defaultExpression) + { + var type = defaultExpression.Type; + if (!type.IsKind(SyntaxKind.NullableType) && !type.IsKind(SyntaxKind.PointerType)) + { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var nullableContext = semanticModel.GetNullableContext(type.SpanStart); + if (nullableContext.AnnotationsEnabled()) + { + type = SyntaxFactory.NullableType(type.WithoutTrivia()).WithTriviaFrom(type); + } + } + + var castExpression = SyntaxFactory.CastExpression(type, newSyntax.WithTrailingTrivia(defaultExpression.Keyword.TrailingTrivia)); + castExpression = castExpression + .WithOpenParenToken(castExpression.OpenParenToken.WithTriviaFrom(defaultExpression.OpenParenToken)) + .WithCloseParenToken(castExpression.CloseParenToken.WithLeadingTrivia(defaultExpression.CloseParenToken.LeadingTrivia)); + + newSyntax = SyntaxFactory.ParenthesizedExpression(castExpression.WithAdditionalAnnotations(Simplifier.Annotation)) + .WithAdditionalAnnotations(Simplifier.Annotation); + } + + newSyntax = newSyntax.WithTriviaFrom(syntax); + return document.WithSyntaxRoot(root.ReplaceNode(syntax, newSyntax)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/Roslyn.Diagnostics.CSharp.Analyzers.csproj b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/Roslyn.Diagnostics.CSharp.Analyzers.csproj new file mode 100644 index 0000000000000..d35499746d6ae --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/CSharp/Roslyn.Diagnostics.CSharp.Analyzers.csproj @@ -0,0 +1,20 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisForRoslynDiagnosticsAnalyzersVersion) + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractApplyTraitToClass`1.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractApplyTraitToClass`1.cs new file mode 100644 index 0000000000000..7504d7d45c2ee --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractApplyTraitToClass`1.cs @@ -0,0 +1,122 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; + +namespace Roslyn.Diagnostics.Analyzers +{ + public abstract class AbstractApplyTraitToClass : CodeRefactoringProvider + where TAttributeSyntax : SyntaxNode + { + private protected abstract IRefactoringHelpers RefactoringHelpers { get; } + + protected abstract SyntaxNode? GetTypeDeclarationForNode(SyntaxNode reportedNode); + + private record State( + Document Document, + SemanticModel SemanticModel, + INamedTypeSymbol TraitAttribute, + TAttributeSyntax AttributeSyntax); + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var attribute = await context.TryGetRelevantNodeAsync(RefactoringHelpers).ConfigureAwait(false); + if (attribute is null) + { + // No attribute in context + return; + } + + var syntaxGenerator = SyntaxGenerator.GetGenerator(context.Document); + if (syntaxGenerator.TryGetContainingDeclaration(attribute, DeclarationKind.Method) is null) + { + // The attribute is not applied to a method + return; + } + + var semanticModel = (await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false))!; + if (semanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.XunitTraitAttribute) is not { } traitAttribute) + { + // Xunit.TraitAttribute not found in compilation + return; + } + + var attributeType = semanticModel.GetTypeInfo(attribute, context.CancellationToken); + if (!SymbolEqualityComparer.Default.Equals(attributeType.Type, traitAttribute)) + return; + + var state = new State(context.Document, semanticModel, traitAttribute, attribute); + context.RegisterRefactoring( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.ApplyTraitToContainingType, + cancellationToken => ApplyTraitToClassAsync(state, cancellationToken), + nameof(AbstractApplyTraitToClass))); + } + + private async Task ApplyTraitToClassAsync(State state, CancellationToken cancellationToken) + { + var syntaxRoot = await state.SemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var typeDeclaration = GetTypeDeclarationForNode(state.AttributeSyntax); + if (typeDeclaration is null) + return state.Document; + + var syntaxGenerator = SyntaxGenerator.GetGenerator(state.Document); + if (syntaxGenerator.TryGetContainingDeclaration(state.AttributeSyntax, DeclarationKind.Attribute) is not { } attribute) + { + throw new InvalidOperationException("Failed to obtain the attribute declaration."); + } + + if (syntaxGenerator.TryGetContainingDeclaration(attribute, DeclarationKind.Method) is not { } method) + { + throw new InvalidOperationException("Failed to obtain the method syntax to which the attribute is applied."); + } + + var expectedAttributeData = state.SemanticModel.GetDeclaredSymbol(method, cancellationToken)!.GetAttributes() + .Single(attributeData => attributeData.ApplicationSyntaxReference is not null && attribute.Span.Contains(attributeData.ApplicationSyntaxReference.Span)); + + var newTypeDeclaration = typeDeclaration.ReplaceNodes( + syntaxGenerator.GetMembers(typeDeclaration), + (originalNode, replacementNode) => + { + foreach (var attribute in syntaxGenerator.GetAttributes(originalNode)) + { + var attributeType = state.SemanticModel.GetTypeInfo(attribute, cancellationToken); + if (attributeType.Type is null) + { + // In this case, 'attribute' is an attribute list syntax containing a single attribute + // syntax. SyntaxGenerator treats this case differently from SemanticModel. + attributeType = state.SemanticModel.GetTypeInfo(attribute.ChildNodes().First(), cancellationToken); + } + + if (!SymbolEqualityComparer.Default.Equals(attributeType.Type, state.TraitAttribute)) + continue; + + var actualAttributeData = state.SemanticModel.GetDeclaredSymbol(originalNode, cancellationToken)!.GetAttributes() + .Single(attributeData => attributeData.ApplicationSyntaxReference is not null && attribute.Span.Contains(attributeData.ApplicationSyntaxReference.Span)); + + if (!expectedAttributeData.ConstructorArguments.SequenceEqual(actualAttributeData.ConstructorArguments)) + continue; + + return syntaxGenerator.RemoveNode(originalNode, attribute); + } + + return originalNode; + }); + + newTypeDeclaration = syntaxGenerator.AddAttributes(newTypeDeclaration, attribute.WithAdditionalAnnotations(Formatter.Annotation)); + + return state.Document.WithSyntaxRoot(syntaxRoot.ReplaceNode(typeDeclaration, newTypeDeclaration)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractCreateTestAccessor`1.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractCreateTestAccessor`1.cs new file mode 100644 index 0000000000000..54dcd6456817b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractCreateTestAccessor`1.cs @@ -0,0 +1,100 @@ +// 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 warnings + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Roslyn.Diagnostics.Analyzers +{ + public abstract class AbstractCreateTestAccessor : CodeRefactoringProvider + where TTypeDeclarationSyntax : SyntaxNode + { + protected AbstractCreateTestAccessor() + { + } + + private protected abstract IRefactoringHelpers RefactoringHelpers { get; } + + protected abstract SyntaxNode GetTypeDeclarationForNode(SyntaxNode reportedNode); + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var type = await context.TryGetRelevantNodeAsync(RefactoringHelpers).ConfigureAwait(false); + if (type is null) + return; + + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var typeSymbol = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(type, context.CancellationToken); + if (!IsClassOrStruct(typeSymbol)) + return; + + if (typeSymbol.GetTypeMembers(TestAccessorHelper.TestAccessorTypeName).Any()) + return; + + var location = typeSymbol.Locations.FirstOrDefault(location => location.IsInSource && Equals(location.SourceTree, semanticModel.SyntaxTree)); + if (location is null) + return; + + context.RegisterRefactoring( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.CreateTestAccessorMessage, + cancellationToken => CreateTestAccessorAsync(context.Document, location.SourceSpan, cancellationToken), + nameof(AbstractCreateTestAccessor))); + } + + private static bool IsClassOrStruct(ITypeSymbol typeSymbol) + => typeSymbol.TypeKind is TypeKind.Class or TypeKind.Struct; + + private async Task CreateTestAccessorAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var syntaxRoot = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var reportedNode = syntaxRoot.FindNode(sourceSpan, getInnermostNodeForTie: true); + var typeDeclaration = GetTypeDeclarationForNode(reportedNode); + var type = (ITypeSymbol)semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken); + + var syntaxGenerator = SyntaxGenerator.GetGenerator(document); + var newTestAccessorExpression = syntaxGenerator.ObjectCreationExpression( + syntaxGenerator.IdentifierName(TestAccessorHelper.TestAccessorTypeName), + syntaxGenerator.ThisExpression()); + var getTestAccessorMethod = syntaxGenerator.MethodDeclaration( + TestAccessorHelper.GetTestAccessorMethodName, + returnType: syntaxGenerator.IdentifierName(TestAccessorHelper.TestAccessorTypeName), + accessibility: Accessibility.Internal, + statements: new[] { syntaxGenerator.ReturnStatement(newTestAccessorExpression) }); + + var parameterName = "instance"; + var fieldName = "_" + parameterName; + var testAccessorField = syntaxGenerator.FieldDeclaration( + fieldName, + syntaxGenerator.TypeExpression(type), + Accessibility.Private, + DeclarationModifiers.ReadOnly); + var testAccessorConstructor = syntaxGenerator.ConstructorDeclaration( + containingTypeName: TestAccessorHelper.TestAccessorTypeName, + parameters: new[] { syntaxGenerator.ParameterDeclaration(parameterName, syntaxGenerator.TypeExpression(type)) }, + accessibility: Accessibility.Internal, + statements: new[] { syntaxGenerator.AssignmentStatement(syntaxGenerator.IdentifierName(fieldName), syntaxGenerator.IdentifierName(parameterName)) }); + var testAccessorType = syntaxGenerator.StructDeclaration( + TestAccessorHelper.TestAccessorTypeName, + accessibility: Accessibility.Internal, + modifiers: DeclarationModifiers.ReadOnly, + members: new[] { testAccessorField, testAccessorConstructor }); + + var newTypeDeclaration = syntaxGenerator.AddMembers(typeDeclaration, getTestAccessorMethod, testAccessorType); + return document.WithSyntaxRoot(syntaxRoot.ReplaceNode(typeDeclaration, newTypeDeclaration)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractDoNotCopyValue.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractDoNotCopyValue.cs new file mode 100644 index 0000000000000..4f735e5c8cb43 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractDoNotCopyValue.cs @@ -0,0 +1,1721 @@ +// 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 warnings + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0042: + /// + public abstract class AbstractDoNotCopyValue : DiagnosticAnalyzer + { + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(DoNotCopyValueTitle)); + + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor UnsupportedUseRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueUnsupportedUseMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueUnsupportedUseDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor AvoidNullableWrapperRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueAvoidNullableWrapperMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueAvoidNullableWrapperDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoAssignValueFromReferenceRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoAssignValueFromReferenceMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoAssignValueFromReferenceDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoReturnValueFromReferenceRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoReturnValueFromReferenceMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoReturnValueFromReferenceDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoBoxingRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoBoxingMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoBoxingDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoUnboxingRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoUnboxingMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoUnboxingDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoFieldOfCopyableTypeRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoFieldOfCopyableTypeMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoFieldOfCopyableTypeDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor NoAutoPropertyRule = new( + RoslynDiagnosticIds.DoNotCopyValueRuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(DoNotCopyValueNoAutoPropertyMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCopyValueNoAutoPropertyDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule, UnsupportedUseRule, NoBoxingRule, NoUnboxingRule, NoFieldOfCopyableTypeRule, NoAutoPropertyRule); + + protected abstract NonCopyableWalker CreateWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache); + + protected abstract NonCopyableSymbolWalker CreateSymbolWalker(SymbolAnalysisContext context, NonCopyableTypesCache cache); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + var cache = new NonCopyableTypesCache(context.Compilation); + context.RegisterOperationBlockAction(context => AnalyzeOperationBlock(context, cache)); + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, cache), + //SymbolKind.Alias, + //SymbolKind.ArrayType, + //SymbolKind.Assembly, + //SymbolKind.Discard, + //SymbolKind.DynamicType, + //SymbolKind.ErrorType, + SymbolKind.Event, + SymbolKind.Field, + //SymbolKind.FunctionPointerType, + //SymbolKind.Label, + //SymbolKind.Local, + SymbolKind.Method, + SymbolKind.NamedType, + SymbolKind.Namespace, + //SymbolKind.NetModule, + SymbolKind.Parameter, + //SymbolKind.PointerType, + //SymbolKind.Preprocessing, + SymbolKind.Property + //SymbolKind.RangeVariable, + //SymbolKind.TypeParameter + ); + }); + } + + private void AnalyzeOperationBlock(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) + { + var walker = CreateWalker(context, cache); + foreach (var operation in context.OperationBlocks) + { + walker.Visit(operation); + } + } + + private void AnalyzeSymbol(SymbolAnalysisContext context, NonCopyableTypesCache cache) + { + var walker = CreateSymbolWalker(context, cache); + walker.Visit(context.Symbol); + } + + private static VisitReleaser TryAddForVisit(HashSet set, T? value, out bool added) + where T : class + { + if (value is null) + { + added = false; + return default; + } + + added = set.Add(value); + if (!added) + return default; + + return new VisitReleaser(set, value); + } + + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "This type is never compared.")] + private readonly struct VisitReleaser : IDisposable + where T : class + { + private readonly HashSet _set; + private readonly T _value; + + public VisitReleaser(HashSet set, T value) + { + _set = set; + _value = value; + } + + public void Dispose() + { + _set?.Remove(_value); + } + } + + protected abstract class NonCopyableSymbolWalker : SymbolVisitor + { + private readonly SymbolAnalysisContext _context; + //private readonly HashSet _handledSymbols = new(); + + protected NonCopyableSymbolWalker(SymbolAnalysisContext context, NonCopyableTypesCache cache) + { + _context = context; + Cache = cache; + } + + protected NonCopyableTypesCache Cache { get; } + + public sealed override void Visit(ISymbol? symbol) + { + base.Visit(symbol); + } + + public override void DefaultVisit(ISymbol symbol) + { + base.DefaultVisit(symbol); + } + + public override void VisitAlias(IAliasSymbol symbol) + { + base.VisitAlias(symbol); + } + + public override void VisitArrayType(IArrayTypeSymbol symbol) + { + base.VisitArrayType(symbol); + } + + public override void VisitAssembly(IAssemblySymbol symbol) + { + base.VisitAssembly(symbol); + } + + public override void VisitDiscard(IDiscardSymbol symbol) + { + base.VisitDiscard(symbol); + } + + public override void VisitDynamicType(IDynamicTypeSymbol symbol) + { + base.VisitDynamicType(symbol); + } + + public override void VisitEvent(IEventSymbol symbol) + { + base.VisitEvent(symbol); + } + + public override void VisitField(IFieldSymbol symbol) + { + // Fields of copyable value types must be copyable. Copying a value type makes a shallow copy of the + // fields, which implicitly copies any value type fields. + if (Cache.IsNonCopyableType(symbol.Type) + && !Cache.IsNonCopyableType(symbol.ContainingType) + && symbol.ContainingType.IsValueType) + { + _context.ReportDiagnostic(symbol.CreateDiagnostic(NoFieldOfCopyableTypeRule, symbol.Type, symbol)); + } + + base.VisitField(symbol); + } + + public override void VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) + { + base.VisitFunctionPointerType(symbol); + } + + public override void VisitLabel(ILabelSymbol symbol) + { + base.VisitLabel(symbol); + } + + public override void VisitLocal(ILocalSymbol symbol) + { + base.VisitLocal(symbol); + } + + public override void VisitMethod(IMethodSymbol symbol) + { + base.VisitMethod(symbol); + } + + public override void VisitModule(IModuleSymbol symbol) + { + base.VisitModule(symbol); + } + + public override void VisitNamedType(INamedTypeSymbol symbol) + { + base.VisitNamedType(symbol); + } + + public override void VisitNamespace(INamespaceSymbol symbol) + { + base.VisitNamespace(symbol); + } + + public override void VisitParameter(IParameterSymbol symbol) + { + base.VisitParameter(symbol); + } + + public override void VisitPointerType(IPointerTypeSymbol symbol) + { + base.VisitPointerType(symbol); + } + + public override void VisitProperty(IPropertySymbol symbol) + { + // Auto-properties cannot have non-copyable types. The getter always returns the backing field by value, + // which requires making a copy. + if (symbol.IsAutoProperty() + && Cache.IsNonCopyableType(symbol.Type)) + { + _context.ReportDiagnostic(symbol.CreateDiagnostic(NoAutoPropertyRule, symbol.Type, symbol)); + } + + base.VisitProperty(symbol); + } + + public override void VisitRangeVariable(IRangeVariableSymbol symbol) + { + base.VisitRangeVariable(symbol); + } + + public override void VisitTypeParameter(ITypeParameterSymbol symbol) + { + base.VisitTypeParameter(symbol); + } + } + + protected abstract class NonCopyableWalker : OperationWalker + { + private readonly OperationBlockAnalysisContext _context; + private readonly HashSet _handledOperations = new(); + + protected NonCopyableWalker(OperationBlockAnalysisContext context, NonCopyableTypesCache cache) + { + _context = context; + Cache = cache; + } + + protected NonCopyableTypesCache Cache { get; } + + protected abstract bool CheckForEachGetEnumerator(IForEachLoopOperation operation, [DisallowNull] ref IConversionOperation? conversion, [DisallowNull] ref IOperation? instance); + + public override void VisitAddressOf(IAddressOfOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitAddressOf(operation); + } + + public override void VisitAnonymousFunction(IAnonymousFunctionOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.Symbol); + CheckTypeInUnsupportedContext(operation); + base.VisitAnonymousFunction(operation); + } + + public override void VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitAnonymousObjectCreation(operation); + } + + public override void VisitArgument(IArgumentOperation operation) + { + CheckParameterSymbolInUnsupportedContext(operation, operation.Parameter); + CheckConversionInUnsupportedContext(operation, operation.InConversion); + CheckConversionInUnsupportedContext(operation, operation.OutConversion); + + var value = operation.Value; + var parameterRefKind = operation.Parameter.RefKind; + var sourceRefKind = Acquire(operation.Value); + if (!CanAssign(sourceRefKind, parameterRefKind)) + { + // Mark the value as not handled + value = null; + } + + using var releaser = TryAddForVisit(_handledOperations, value, out _); + + base.VisitArgument(operation); + } + + public override void VisitArrayCreation(IArrayCreationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitArrayCreation(operation); + } + + public override void VisitArrayElementReference(IArrayElementReferenceOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitArrayElementReference(operation); + } + + public override void VisitArrayInitializer(IArrayInitializerOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitArrayInitializer(operation); + } + + public override void VisitAwait(IAwaitOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitAwait(operation); + } + + public override void VisitBinaryOperator(IBinaryOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.OperatorMethod); + CheckTypeInUnsupportedContext(operation); + base.VisitBinaryOperator(operation); + } + + public override void VisitBlock(IBlockOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitBlock(operation); + } + + public override void VisitBranch(IBranchOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitBranch(operation); + } + + public override void VisitCatchClause(ICatchClauseOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeSymbolInUnsupportedContext(operation, operation.ExceptionType); + CheckTypeInUnsupportedContext(operation); + base.VisitCatchClause(operation); + } + + public override void VisitCaughtException(ICaughtExceptionOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitCaughtException(operation); + } + + public override void VisitCoalesce(ICoalesceOperation operation) + { + CheckConversionInUnsupportedContext(operation, operation.ValueConversion); + CheckTypeInUnsupportedContext(operation); + base.VisitCoalesce(operation); + } + + public override void VisitCoalesceAssignment(ICoalesceAssignmentOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitCoalesceAssignment(operation); + } + + public override void VisitCompoundAssignment(ICompoundAssignmentOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.OperatorMethod); + CheckConversionInUnsupportedContext(operation, operation.InConversion); + CheckConversionInUnsupportedContext(operation, operation.OutConversion); + CheckTypeInUnsupportedContext(operation); + base.VisitCompoundAssignment(operation); + } + + public override void VisitConditional(IConditionalOperation operation) + { + if (!operation.IsRef && Acquire(operation) != RefKind.None) + { + CheckTypeInUnsupportedContext(operation); + } + + var currentIsHandled = _handledOperations.Contains(operation); + var handledWhenTrue = currentIsHandled ? operation.WhenTrue : null; + var handledWhenFalse = currentIsHandled ? operation.WhenFalse : null; + using var _1 = TryAddForVisit(_handledOperations, handledWhenTrue, out _); + using var _2 = TryAddForVisit(_handledOperations, handledWhenFalse, out _); + + base.VisitConditional(operation); + } + + public override void VisitConditionalAccess(IConditionalAccessOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitConditionalAccess(operation); + } + + public override void VisitConditionalAccessInstance(IConditionalAccessInstanceOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitConditionalAccessInstance(operation); + } + + public override void VisitConstantPattern(IConstantPatternOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.InputType); + CheckTypeInUnsupportedContext(operation); + base.VisitConstantPattern(operation); + } + + public override void VisitConstructorBodyOperation(IConstructorBodyOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitConstructorBodyOperation(operation); + } + + public override void VisitConversion(IConversionOperation operation) + { + if (!IsSupportedConversion()) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.OperatorMethod); + CheckConversionInUnsupportedContext(operation, operation.Conversion); + CheckTypeInUnsupportedContext(operation); + } + + base.VisitConversion(operation); + return; + + // Local functions + bool IsSupportedConversion() + { + if (!operation.Conversion.Exists) + { + // The compiler will warn or error about this case + return true; + } + + if (operation.Conversion.MethodSymbol is object) + { + // Not yet handled + return false; + } + + switch (Acquire(operation.Operand)) + { + case RefKind.None: + case RefKind.Ref or RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter when operation.Conversion.IsIdentity: + return true; + + default: + break; + } + + return false; + } + } + + public override void VisitDeclarationExpression(IDeclarationExpressionOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDeclarationExpression(operation); + } + + public override void VisitDeclarationPattern(IDeclarationPatternOperation operation) + { + CheckSymbolInUnsupportedContext(operation, operation.DeclaredSymbol); + CheckTypeSymbolInUnsupportedContext(operation, operation.InputType); + CheckTypeSymbolInUnsupportedContext(operation, operation.MatchedType); + CheckTypeInUnsupportedContext(operation); + base.VisitDeclarationPattern(operation); + } + + public override void VisitDeconstructionAssignment(IDeconstructionAssignmentOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDeconstructionAssignment(operation); + } + + public override void VisitDefaultCaseClause(IDefaultCaseClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDefaultCaseClause(operation); + } + + public override void VisitDefaultValue(IDefaultValueOperation operation) + { + // default(T) is a valid way to acquire a 'T'. Non-defaultable type analysis is handled separately. + base.VisitDefaultValue(operation); + } + + public override void VisitDelegateCreation(IDelegateCreationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDelegateCreation(operation); + } + + public override void VisitDiscardOperation(IDiscardOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDiscardOperation(operation); + } + + public override void VisitDiscardPattern(IDiscardPatternOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.InputType); + CheckTypeInUnsupportedContext(operation); + base.VisitDiscardPattern(operation); + } + + public override void VisitDynamicIndexerAccess(IDynamicIndexerAccessOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDynamicIndexerAccess(operation); + } + + public override void VisitDynamicInvocation(IDynamicInvocationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDynamicInvocation(operation); + } + + public override void VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation) + { + foreach (var type in operation.TypeArguments) + { + CheckTypeSymbolInUnsupportedContext(operation, type); + } + + CheckTypeSymbolInUnsupportedContext(operation, operation.ContainingType); + CheckTypeInUnsupportedContext(operation); + base.VisitDynamicMemberReference(operation); + } + + public override void VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitDynamicObjectCreation(operation); + } + + public override void VisitEmpty(IEmptyOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitEmpty(operation); + } + + public override void VisitEnd(IEndOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitEnd(operation); + } + + public override void VisitEventAssignment(IEventAssignmentOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitEventAssignment(operation); + } + + public override void VisitEventReference(IEventReferenceOperation operation) + { + CheckEventSymbolInUnsupportedContext(operation, operation.Event); + CheckTypeInUnsupportedContext(operation); + base.VisitEventReference(operation); + } + + public override void VisitExpressionStatement(IExpressionStatementOperation operation) + { + CheckTypeInUnsupportedContext(operation); + + // The result of the top-most operation in an expression statement is not used + using var releaser = TryAddForVisit(_handledOperations, operation.Operation, out _); + + base.VisitExpressionStatement(operation); + } + + public override void VisitFieldInitializer(IFieldInitializerOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + foreach (var field in operation.InitializedFields) + { + CheckFieldSymbolInUnsupportedContext(operation, field); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitFieldInitializer(operation); + } + + public override void VisitFieldReference(IFieldReferenceOperation operation) + { + // An instance never needs to be copied to read or write a field value. Even in cases where the instance + // is read-only, attempts to write to the field would be reported as compiler errors so analyzer + // diagnostics are not required. + using var releaser = TryAddForVisit(_handledOperations, operation.Instance, out _); + + CheckFieldSymbolInUnsupportedContext(operation, operation.Field); + CheckTypeInUnsupportedContext(operation); + base.VisitFieldReference(operation); + } + + public override void VisitFlowAnonymousFunction(IFlowAnonymousFunctionOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.Symbol); + CheckTypeInUnsupportedContext(operation); + base.VisitFlowAnonymousFunction(operation); + } + + public override void VisitFlowCapture(IFlowCaptureOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitFlowCapture(operation); + } + + public override void VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitFlowCaptureReference(operation); + } + + public override void VisitForEachLoop(IForEachLoopOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + // 'foreach' operations have an identity conversion for the collection property, and then invoke the + // GetEnumerator method. + var instance = operation.Collection as IConversionOperation; + var instance2 = (operation.Collection as IConversionOperation)?.Operand; + + if (instance2 is null) + { + // Didn't match the known pattern + instance = null; + } + else if (instance?.Conversion is not { IsIdentity: true, MethodSymbol: null }) + { + // Not a supported conversion + instance = null; + instance2 = null; + } + else + { + // Treat this as an invocation of the GetEnumerator method. + if (!CheckForEachGetEnumerator(operation, ref instance, ref instance2)) + { + // Not supported + instance = null; + instance2 = null; + } + } + + using var releaser = TryAddForVisit(_handledOperations, instance, out _); + using var releaser2 = TryAddForVisit(_handledOperations, instance2, out _); + + CheckTypeInUnsupportedContext(operation); + base.VisitForEachLoop(operation); + } + + public override void VisitForLoop(IForLoopOperation operation) + { + foreach (var local in operation.ConditionLocals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitForLoop(operation); + } + + public override void VisitForToLoop(IForToLoopOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitForToLoop(operation); + } + + public override void VisitIncrementOrDecrement(IIncrementOrDecrementOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.OperatorMethod); + CheckTypeInUnsupportedContext(operation); + base.VisitIncrementOrDecrement(operation); + } + + public override void VisitInstanceReference(IInstanceReferenceOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitInstanceReference(operation); + } + + public override void VisitInterpolatedString(IInterpolatedStringOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitInterpolatedString(operation); + } + + public override void VisitInterpolatedStringText(IInterpolatedStringTextOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitInterpolatedStringText(operation); + } + + public override void VisitInterpolation(IInterpolationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitInterpolation(operation); + } + + public override void VisitInvalid(IInvalidOperation operation) + { + // This is already a compiler error. No need to report more diagnostics on it. + base.VisitInvalid(operation); + } + + public override void VisitInvocation(IInvocationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + + var instance = operation.Instance; + if (instance is object + && Cache.IsNonCopyableType(operation.TargetMethod.ReceiverType) + && !operation.TargetMethod.IsReadOnly + && Acquire(instance) is RefKind.In or RefKindEx.RefReadOnlyParameter) + { + // mark the instance as not checked by this method + instance = null; + } + + using var releaser = TryAddForVisit(_handledOperations, instance, out _); + + // No need to check the method signature further. Parameters will be checked by the IArgumentOperation. + base.VisitInvocation(operation); + } + + public override void VisitIsNull(IIsNullOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitIsNull(operation); + } + + public override void VisitIsPattern(IIsPatternOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitIsPattern(operation); + } + + public override void VisitIsType(IIsTypeOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.TypeOperand); + CheckTypeInUnsupportedContext(operation); + base.VisitIsType(operation); + } + + public override void VisitLabeled(ILabeledOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitLabeled(operation); + } + + public override void VisitLiteral(ILiteralOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitLiteral(operation); + } + + public override void VisitLocalFunction(ILocalFunctionOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.Symbol); + CheckTypeInUnsupportedContext(operation); + base.VisitLocalFunction(operation); + } + + public override void VisitLocalReference(ILocalReferenceOperation operation) + { + CheckLocalSymbolInUnsupportedContext(operation, operation.Local); + CheckTypeInUnsupportedContext(operation); + base.VisitLocalReference(operation); + } + + public override void VisitLock(ILockOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitLock(operation); + } + + public override void VisitMemberInitializer(IMemberInitializerOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitMemberInitializer(operation); + } + + public override void VisitMethodBodyOperation(IMethodBodyOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitMethodBodyOperation(operation); + } + + public override void VisitMethodReference(IMethodReferenceOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.Method); + CheckTypeInUnsupportedContext(operation); + base.VisitMethodReference(operation); + } + + public override void VisitNameOf(INameOfOperation operation) + { + // A 'nameof' operation does not copy anything in the value + return; + } + + public override void VisitObjectCreation(IObjectCreationOperation operation) + { + // 'new T()' is a valid way to acquire a 'T'. Non-defaultable type analysis is handled separately. + // No need to check the method signature further. Parameters will be checked by the IArgumentOperation. + // Also mark the initializer (if any) as handled. + using var releaser = TryAddForVisit(_handledOperations, operation.Initializer, out _); + base.VisitObjectCreation(operation); + } + + public override void VisitObjectOrCollectionInitializer(IObjectOrCollectionInitializerOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitObjectOrCollectionInitializer(operation); + } + + public override void VisitOmittedArgument(IOmittedArgumentOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitOmittedArgument(operation); + } + + public override void VisitParameterInitializer(IParameterInitializerOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckParameterSymbolInUnsupportedContext(operation, operation.Parameter); + CheckTypeInUnsupportedContext(operation); + base.VisitParameterInitializer(operation); + } + + public override void VisitParameterReference(IParameterReferenceOperation operation) + { + CheckParameterSymbolInUnsupportedContext(operation, operation.Parameter); + CheckTypeInUnsupportedContext(operation); + base.VisitParameterReference(operation); + } + + public override void VisitParenthesized(IParenthesizedOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitParenthesized(operation); + } + + public override void VisitPatternCaseClause(IPatternCaseClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitPatternCaseClause(operation); + } + + public override void VisitPropertyInitializer(IPropertyInitializerOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + foreach (var property in operation.InitializedProperties) + { + CheckPropertySymbolInUnsupportedContext(operation, property); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitPropertyInitializer(operation); + } + + public override void VisitPropertyReference(IPropertyReferenceOperation operation) + { + // Allow a property to return a non-copyable type by value + if (Acquire(operation) != RefKind.None) + { + CheckTypeInUnsupportedContext(operation); + } + + var instance = operation.Instance; + if (instance is object + && Cache.IsNonCopyableType(operation.Property.ContainingType) + && Acquire(instance) is RefKind.In or RefKindEx.RefReadOnlyParameter) + { + if (operation.IsSetMethodInvocation()) + { + if (operation.Property.SetMethod is { IsReadOnly: false }) + { + // mark the instance as not checked by this method + instance = null; + } + } + else + { + if (operation.Property.GetMethod is { IsReadOnly: false }) + { + // mark the instance as not checked by this method + instance = null; + } + } + } + + using var releaser = TryAddForVisit(_handledOperations, instance, out _); + + // No need to check the method signature further. Parameters will be checked by the IArgumentOperation. + base.VisitPropertyReference(operation); + } + + public override void VisitRaiseEvent(IRaiseEventOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitRaiseEvent(operation); + } + + public override void VisitRangeCaseClause(IRangeCaseClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitRangeCaseClause(operation); + } + + public override void VisitRangeOperation(IRangeOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.Method); + CheckTypeInUnsupportedContext(operation); + base.VisitRangeOperation(operation); + } + + public override void VisitReDim(IReDimOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitReDim(operation); + } + + public override void VisitReDimClause(IReDimClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitReDimClause(operation); + } + + public override void VisitRelationalCaseClause(IRelationalCaseClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitRelationalCaseClause(operation); + } + + public override void VisitReturn(IReturnOperation operation) + { + var returnedValue = operation.ReturnedValue; + if (returnedValue is not null && Acquire(returnedValue) != RefKind.None) + { + if (returnedValue is ILocalReferenceOperation { Local.IsRef: false }) + { + // Returning a by-value local is allowed + } + else if (returnedValue is IParameterReferenceOperation { Parameter.RefKind: RefKind.None }) + { + // Returning a by-value parameter is allowed + } + else if (operation.GetRefKind(_context.OwningSymbol) == RefKind.None && Cache.IsNonCopyableType(returnedValue.Type)) + { + _context.ReportDiagnostic(returnedValue.CreateDiagnostic(NoReturnValueFromReferenceRule, returnedValue.Type)); + } + } + + using var releaser = TryAddForVisit(_handledOperations, returnedValue, out _); + + CheckTypeInUnsupportedContext(operation); + base.VisitReturn(operation); + } + + public override void VisitSimpleAssignment(ISimpleAssignmentOperation operation) + { + var target = operation.Target; + var value = operation.Value; + + var sourceRefKind = Acquire(value); + if (Cache.IsNonCopyableType(value.Type) + && !operation.IsRef + && target.Kind != OperationKind.Discard + && sourceRefKind != RefKind.None) + { + _context.ReportDiagnostic(value.CreateDiagnostic(NoAssignValueFromReferenceRule, value.Type)); + } + + using var releaser1 = TryAddForVisit(_handledOperations, target, out _); + using var releaser2 = TryAddForVisit(_handledOperations, value, out _); + + base.VisitSimpleAssignment(operation); + } + + public override void VisitSingleValueCaseClause(ISingleValueCaseClauseOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitSingleValueCaseClause(operation); + } + + public override void VisitSizeOf(ISizeOfOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.TypeOperand); + CheckTypeInUnsupportedContext(operation); + base.VisitSizeOf(operation); + } + + public override void VisitStaticLocalInitializationSemaphore(IStaticLocalInitializationSemaphoreOperation operation) + { + CheckLocalSymbolInUnsupportedContext(operation, operation.Local); + CheckTypeInUnsupportedContext(operation); + base.VisitStaticLocalInitializationSemaphore(operation); + } + + public override void VisitStop(IStopOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitStop(operation); + } + + public override void VisitSwitch(ISwitchOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitSwitch(operation); + } + + public override void VisitSwitchCase(ISwitchCaseOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitSwitchCase(operation); + } + + public override void VisitSwitchExpression(ISwitchExpressionOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitSwitchExpression(operation); + } + + public override void VisitSwitchExpressionArm(ISwitchExpressionArmOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitSwitchExpressionArm(operation); + } + + public override void VisitThrow(IThrowOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitThrow(operation); + } + + public override void VisitTranslatedQuery(ITranslatedQueryOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitTranslatedQuery(operation); + } + + public override void VisitTry(ITryOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitTry(operation); + } + + public override void VisitTuple(ITupleOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.NaturalType); + CheckTypeInUnsupportedContext(operation); + base.VisitTuple(operation); + } + + public override void VisitTupleBinaryOperator(ITupleBinaryOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitTupleBinaryOperator(operation); + } + + public override void VisitTypeOf(ITypeOfOperation operation) + { + CheckTypeSymbolInUnsupportedContext(operation, operation.TypeOperand); + CheckTypeInUnsupportedContext(operation); + base.VisitTypeOf(operation); + } + + public override void VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitTypeParameterObjectCreation(operation); + } + + public override void VisitUnaryOperator(IUnaryOperation operation) + { + CheckMethodSymbolInUnsupportedContext(operation, operation.OperatorMethod); + CheckTypeInUnsupportedContext(operation); + base.VisitUnaryOperator(operation); + } + + public override void VisitUsing(IUsingOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + + var resource = operation.Resources; + if (Acquire(resource) != RefKind.None) + { + // Not yet handled + resource = null; + } + + using var releaser = TryAddForVisit(_handledOperations, resource, out _); + base.VisitUsing(operation); + } + + public override void VisitVariableDeclaration(IVariableDeclarationOperation operation) + { + CheckTypeInUnsupportedContext(operation); + + var initializer = operation.Initializer; + if (initializer is not null) + { + var symbol = operation.Declarators.Single().Symbol; + var localRefKind = symbol.RefKind; + var sourceRefKind = Acquire(operation.Initializer?.Value); + if (Cache.IsNonCopyableType(symbol.Type) + && !CanAssign(sourceRefKind, localRefKind)) + { + _context.ReportDiagnostic(initializer.CreateDiagnostic(NoAssignValueFromReferenceRule, symbol.Type)); + } + } + + using var releaser1 = TryAddForVisit(_handledOperations, initializer, out _); + using var releaser2 = TryAddForVisit(_handledOperations, initializer?.Value, out _); + + base.VisitVariableDeclaration(operation); + } + + public override void VisitVariableDeclarationGroup(IVariableDeclarationGroupOperation operation) + { + CheckTypeInUnsupportedContext(operation); + base.VisitVariableDeclarationGroup(operation); + } + + public override void VisitVariableDeclarator(IVariableDeclaratorOperation operation) + { + CheckLocalSymbolInUnsupportedContext(operation, operation.Symbol); + + var initializer = operation.Initializer; + var localRefKind = operation.Symbol.RefKind; + var sourceRefKind = Acquire(operation.Initializer?.Value); + if (initializer is not null + && Cache.IsNonCopyableType(operation.Symbol.Type) + && !CanAssign(sourceRefKind, localRefKind)) + { + _context.ReportDiagnostic(initializer.CreateDiagnostic(NoAssignValueFromReferenceRule, operation.Symbol.Type)); + } + + using var releaser1 = TryAddForVisit(_handledOperations, initializer, out _); + using var releaser2 = TryAddForVisit(_handledOperations, initializer?.Value, out _); + + base.VisitVariableDeclarator(operation); + } + + public override void VisitVariableInitializer(IVariableInitializerOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitVariableInitializer(operation); + } + + public override void VisitWhileLoop(IWhileLoopOperation operation) + { + foreach (var local in operation.Locals) + { + CheckLocalSymbolInUnsupportedContext(operation, local); + } + + CheckTypeInUnsupportedContext(operation); + base.VisitWhileLoop(operation); + } + + [Obsolete("ICollectionElementInitializerOperation has been replaced with IInvocationOperation and IDynamicInvocationOperation", error: true)] + public override void VisitCollectionElementInitializer(ICollectionElementInitializerOperation operation) + => throw new NotSupportedException(); + + private static bool CanAssign(RefKind sourceRefKind, RefKind targetRefKind) + { + return (sourceRefKind, targetRefKind) switch + { + (RefKind.None, _) => true, + (RefKind.Ref, RefKind.Ref or RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter) => true, + (RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter, RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter) => true, + _ => false, + }; + } + + protected RefKind Acquire(IOperation? operation) + { + if (operation is null) + return RefKind.RefReadOnly; + + switch (operation.Kind) + { + case OperationKind.ArrayElementReference: + return RefKind.Ref; + + case OperationKind.Await: + return ((IAwaitOperation)operation).Operation switch + { + { Type: INamedTypeSymbol { OriginalDefinition: var taskType } } + when SymbolEqualityComparer.Default.Equals(taskType, Cache.ValueTaskT) || SymbolEqualityComparer.Default.Equals(taskType, Cache.ConfiguredValueTaskAwaitableT) + => RefKind.None, + var awaited => Acquire(awaited), + }; + + case OperationKind.Conditional: + var conditional = (IConditionalOperation)operation; + return CombineRestrictions(Acquire(conditional.WhenTrue ?? conditional.Condition), Acquire(conditional.WhenFalse)); + + case OperationKind.Conversion: + var conversion = (IConversionOperation)operation; + return conversion.OperatorMethod switch + { + null => Acquire(conversion.Operand), + { ReturnsByRefReadonly: true } => RefKind.RefReadOnly, + { ReturnsByRef: true } => RefKind.Ref, + _ => RefKind.None, + }; + + case OperationKind.DefaultValue: + return RefKind.None; + + case OperationKind.FieldReference: + var field = ((IFieldReferenceOperation)operation).Field; + return field.IsReadOnly ? RefKind.RefReadOnly : RefKind.Ref; + + case OperationKind.InstanceReference: + return _context.OwningSymbol.IsReadOnly() ? RefKind.RefReadOnly : RefKind.Ref; + + case OperationKind.Invocation: + return ((IInvocationOperation)operation).TargetMethod switch + { + { ReturnsByRefReadonly: true } => RefKind.RefReadOnly, + { ReturnsByRef: true } => RefKind.Ref, + _ => RefKind.None, + }; + + case OperationKind.Literal: + return RefKind.None; + + case OperationKind.LocalReference: + var local = ((ILocalReferenceOperation)operation).Local; + return local.RefKind == RefKind.RefReadOnly ? RefKind.RefReadOnly : RefKind.Ref; + + case OperationKind.ObjectCreation: + return RefKind.None; + + case OperationKind.ParameterReference: + var parameter = ((IParameterReferenceOperation)operation).Parameter; + return parameter.RefKind is RefKind.In or RefKindEx.RefReadOnlyParameter ? parameter.RefKind : RefKind.Ref; + + case OperationKind.Parenthesized: + return Acquire(((IParenthesizedOperation)operation).Operand); + + case OperationKind.PropertyReference: + var property = ((IPropertyReferenceOperation)operation).Property; + return property switch + { + { ReturnsByRefReadonly: true } => RefKind.RefReadOnly, + { ReturnsByRef: true } => RefKind.Ref, + _ => RefKind.None, + }; + + case OperationKind.Throw: + return RefKind.None; + + case OperationKindEx.CollectionExpression: + return RefKind.None; + + default: + return RefKind.RefReadOnly; + } + + // Local functions + static RefKind CombineRestrictions(RefKind first, RefKind second) + { + return (first, second) switch + { + (RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter, _) => RefKind.RefReadOnly, + (_, RefKind.RefReadOnly or RefKindEx.RefReadOnlyParameter) => RefKind.RefReadOnly, + (RefKind.Out, _) => RefKind.Out, + (_, RefKind.Out) => RefKind.Out, + (RefKind.None, RefKind.None) => RefKind.None, + _ => RefKind.Ref, + }; + } + } + + private void CheckEventSymbolInUnsupportedContext(IOperation operation, IEventSymbol? @event) + { + if (@event is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, @event.Type); + CheckMethodSymbolInUnsupportedContext(operation, @event.AddMethod); + CheckMethodSymbolInUnsupportedContext(operation, @event.RemoveMethod); + CheckMethodSymbolInUnsupportedContext(operation, @event.RaiseMethod); + } + + private void CheckFieldSymbolInUnsupportedContext(IOperation operation, IFieldSymbol? field) + { + if (field is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, field.Type); + } + + private void CheckPropertySymbolInUnsupportedContext(IOperation operation, IPropertySymbol? property) + { + if (property is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, property.Type); + foreach (var parameter in property.Parameters) + { + CheckParameterSymbolInUnsupportedContext(operation, parameter); + } + + CheckMethodSymbolInUnsupportedContext(operation, property.GetMethod); + CheckMethodSymbolInUnsupportedContext(operation, property.SetMethod); + } + + private void CheckSymbolInUnsupportedContext(IOperation operation, ISymbol? symbol) + { + if (symbol is null) + return; + + switch (symbol.Kind) + { + case SymbolKind.Alias: + CheckSymbolInUnsupportedContext(operation, ((IAliasSymbol)symbol).Target); + break; + + case SymbolKind.ArrayType: + case SymbolKind.DynamicType: + case SymbolKind.ErrorType: + case SymbolKind.NamedType: + case SymbolKind.PointerType: + CheckTypeSymbolInUnsupportedContext(operation, (ITypeSymbol)symbol); + break; + + case SymbolKind.Event: + CheckEventSymbolInUnsupportedContext(operation, (IEventSymbol)symbol); + break; + + case SymbolKind.Field: + CheckFieldSymbolInUnsupportedContext(operation, (IFieldSymbol)symbol); + break; + + case SymbolKind.Local: + CheckLocalSymbolInUnsupportedContext(operation, (ILocalSymbol)symbol); + break; + + case SymbolKind.Method: + CheckMethodSymbolInUnsupportedContext(operation, (IMethodSymbol)symbol); + break; + + case SymbolKind.Parameter: + CheckParameterSymbolInUnsupportedContext(operation, (IParameterSymbol)symbol); + break; + + case SymbolKind.Property: + CheckPropertySymbolInUnsupportedContext(operation, (IPropertySymbol)symbol); + break; + + case SymbolKind.TypeParameter: + CheckTypeParameterSymbolInUnsupportedContext(operation, (ITypeParameterSymbol)symbol); + break; + + case SymbolKind.Assembly: + case SymbolKind.Discard: + case SymbolKind.Label: + case SymbolKind.Namespace: + case SymbolKind.NetModule: + case SymbolKind.Preprocessing: + case SymbolKind.RangeVariable: + // Nothing to check for these symbols + break; + + default: + break; + } + } + + private void CheckTypeSymbolInUnsupportedContext(IOperation operation, ITypeSymbol? type) + { + if (type is null) + return; + + if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) + { + var nullableUnderlyingType = ((INamedTypeSymbol)type).TypeArguments.FirstOrDefault(); + if (Cache.IsNonCopyableType(nullableUnderlyingType)) + { + _context.ReportDiagnostic(operation.Syntax.CreateDiagnostic(AvoidNullableWrapperRule, type, operation.Kind)); + } + } + + _ = operation; + _ = type; + } + + private void CheckParameterSymbolInUnsupportedContext(IOperation operation, IParameterSymbol? parameter) + { + if (parameter is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, parameter.Type); + } + + private void CheckTypeParameterSymbolInUnsupportedContext(IOperation operation, ITypeParameterSymbol? typeParameter) + { + if (typeParameter is null) + return; + + foreach (var constraint in typeParameter.ConstraintTypes) + { + CheckTypeSymbolInUnsupportedContext(operation, constraint); + } + } + + private void CheckConversionInUnsupportedContext(IOperation operation, CommonConversion conversion) + { + CheckMethodSymbolInUnsupportedContext(operation, conversion.MethodSymbol); + } + + private void CheckLocalSymbolInUnsupportedContext(IOperation operation, ILocalSymbol? local) + { + if (local is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, local.Type); + } + + protected void CheckMethodSymbolInUnsupportedContext(IOperation operation, IMethodSymbol? symbol) + { + if (symbol is null) + return; + + CheckTypeSymbolInUnsupportedContext(operation, symbol.ReturnType); + + foreach (var parameter in symbol.Parameters) + { + CheckParameterSymbolInUnsupportedContext(operation, parameter); + } + + foreach (var typeArguments in symbol.TypeArguments) + { + CheckTypeSymbolInUnsupportedContext(operation, typeArguments); + } + + foreach (var typeParameter in symbol.TypeParameters) + { + CheckTypeParameterSymbolInUnsupportedContext(operation, typeParameter); + } + } + + /// + /// Non-copyable types are only allowed in cases explicitly handled by this analyzer. This call handles + /// unrecognized coding patterns (not known to be safe or unsafe), and reports a generic message if a + /// non-copyable type is used in this context. + /// + /// The operation being analyzed. + private void CheckTypeInUnsupportedContext(IOperation operation) + { + if (_handledOperations.Contains(operation)) + return; + + var node = operation.Syntax; + var symbol = operation.Type; + var operationKind = operation.Kind; + + if (symbol is null) + { + // This operation did not have a type. + return; + } + + if (!Cache.IsNonCopyableType(symbol)) + { + // Copies of this type are allowed + return; + } + + _context.ReportDiagnostic(node.CreateDiagnostic(UnsupportedUseRule, symbol, operationKind)); + } + } + + protected sealed class NonCopyableTypesCache + { + private readonly ConcurrentDictionary _typesToNonCopyable + = new(); + + public INamedTypeSymbol? ValueTaskT { get; } + public INamedTypeSymbol? ConfiguredValueTaskAwaitableT { get; } + + public NonCopyableTypesCache(Compilation compilation) + { + if (compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingSpinLock) is { } spinLock) + _typesToNonCopyable[spinLock] = true; + + if (compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesGCHandle) is { } gcHandle) + _typesToNonCopyable[gcHandle] = true; + + ValueTaskT = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask1); + ConfiguredValueTaskAwaitableT = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable1); + } + + internal bool IsNonCopyableType([NotNullWhen(true)] ITypeSymbol? symbol) + { + if (symbol is not { IsValueType: true }) + { + return false; + } + + if (symbol is not INamedTypeSymbol namedTypeSymbol) + { + return false; + } + + if (_typesToNonCopyable.TryGetValue(namedTypeSymbol, out var noncopyable)) + { + return noncopyable; + } + + return IsNonCopyableTypeSlow(namedTypeSymbol); + } + + private bool IsNonCopyableTypeSlow(INamedTypeSymbol symbol) + { + return _typesToNonCopyable.GetOrAdd( + symbol, + symbol => + { + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass.Name == "NonCopyableAttribute") + return true; + } + + return false; + }); + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractExposeMemberForTesting`1.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractExposeMemberForTesting`1.cs new file mode 100644 index 0000000000000..2dba18c74bdc6 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractExposeMemberForTesting`1.cs @@ -0,0 +1,198 @@ +// 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 warnings + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Roslyn.Diagnostics.Analyzers +{ + public abstract class AbstractExposeMemberForTesting : CodeRefactoringProvider + where TTypeDeclarationSyntax : SyntaxNode + { + protected AbstractExposeMemberForTesting() + { + } + + private protected abstract IRefactoringHelpers RefactoringHelpers { get; } + + protected abstract bool HasRefReturns { get; } + + protected abstract SyntaxNode GetTypeDeclarationForNode(SyntaxNode reportedNode); + + protected abstract SyntaxNode GetByRefType(SyntaxNode type, RefKind refKind); + + protected abstract SyntaxNode GetByRefExpression(SyntaxNode expression); + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var type = await GetRelevantTypeFromHeaderAsync(context).ConfigureAwait(false); + if (type is null) + return; + + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var testAccessorType = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(type, context.CancellationToken); + if (!IsClassOrStruct(testAccessorType)) + return; + + if (testAccessorType.Name != TestAccessorHelper.TestAccessorTypeName) + return; + + var location = testAccessorType.Locations.FirstOrDefault(location => location.IsInSource && Equals(location.SourceTree, semanticModel.SyntaxTree)); + if (location is null) + return; + + if (testAccessorType.ContainingSymbol is not ITypeSymbol containingType) + return; + + foreach (var member in containingType.GetMembers()) + { + var memberName = member.Name; + if (testAccessorType.GetMembers(GetTestAccessorName(member)).Any()) + { + continue; + } + + switch (member) + { + case IFieldSymbol: + case IPropertySymbol: + context.RegisterRefactoring( + CodeAction.Create( + memberName, + cancellationToken => AddMemberToTestAccessorAsync(context.Document, location.SourceSpan, memberName, member.GetDocumentationCommentId(), cancellationToken), + member.GetDocumentationCommentId())); + break; + + default: + break; + } + } + } + + private async Task GetRelevantTypeFromHeaderAsync(CodeRefactoringContext context) + { + var type = await context.TryGetRelevantNodeAsync(RefactoringHelpers).ConfigureAwait(false); + if (type is null) + return null; + + return type; + } + + private static bool IsClassOrStruct(ITypeSymbol typeSymbol) + => typeSymbol.TypeKind is TypeKind.Class or TypeKind.Struct; + + private static string GetTestAccessorName(ISymbol symbol) + { + var name = symbol.Name.TrimStart('_'); + return char.ToUpperInvariant(name[0]) + name[1..]; + } + + private async Task AddMemberToTestAccessorAsync(Document document, TextSpan sourceSpan, string memberName, string memberDocumentationCommentId, CancellationToken cancellationToken) + { + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var syntaxRoot = await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + var reportedNode = syntaxRoot.FindNode(sourceSpan, getInnermostNodeForTie: true); + var testAccessorTypeDeclaration = GetTypeDeclarationForNode(reportedNode); + var testAccessorType = (ITypeSymbol)semanticModel.GetDeclaredSymbol(testAccessorTypeDeclaration, cancellationToken); + var containingType = (ITypeSymbol)testAccessorType.ContainingSymbol; + var member = containingType.GetMembers(memberName).First(m => m.GetDocumentationCommentId() == memberDocumentationCommentId); + + var accessorField = testAccessorType.GetMembers().OfType().FirstOrDefault(field => field.Type.Equals(containingType)); + if (accessorField is null) + { + return document.Project.Solution; + } + + var syntaxGenerator = SyntaxGenerator.GetGenerator(document); + SyntaxNode newMember; + switch (member) + { + case IFieldSymbol fieldSymbol: + newMember = GenerateTestAccessorForField(fieldSymbol, memberName, syntaxGenerator, accessorField); + break; + + case IPropertySymbol propertySymbol: + SyntaxNode? getAccessor = null; + SyntaxNode? setAccessor = null; + if (!propertySymbol.IsWriteOnly) + { + getAccessor = syntaxGenerator.ReturnStatement(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(accessorField.Name), syntaxGenerator.IdentifierName(memberName))); + } + + if (!propertySymbol.IsReadOnly) + { + setAccessor = syntaxGenerator.AssignmentStatement(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(accessorField.Name), syntaxGenerator.IdentifierName(memberName)), syntaxGenerator.IdentifierName("value")); + } + + DeclarationModifiers modifiers; + if (propertySymbol.IsWriteOnly) + { + modifiers = DeclarationModifiers.WriteOnly; + } + else if (propertySymbol.IsReadOnly) + { + modifiers = DeclarationModifiers.ReadOnly; + } + else + { + modifiers = DeclarationModifiers.None; + } + + newMember = syntaxGenerator.PropertyDeclaration( + GetTestAccessorName(member), + syntaxGenerator.TypeExpression(propertySymbol.Type), + Accessibility.Internal, + modifiers, + getAccessorStatements: getAccessor != null ? new[] { getAccessor } : null, + setAccessorStatements: setAccessor != null ? new[] { setAccessor } : null); + break; + + default: + return document.Project.Solution; + } + + var newTypeDeclaration = syntaxGenerator.AddMembers(testAccessorTypeDeclaration, newMember); + return document.WithSyntaxRoot(syntaxRoot.ReplaceNode(testAccessorTypeDeclaration, newTypeDeclaration)).Project.Solution; + } + + private SyntaxNode GenerateTestAccessorForField(IFieldSymbol fieldSymbol, string memberName, SyntaxGenerator syntaxGenerator, IFieldSymbol accessorField) + { + var getAccessor = syntaxGenerator.ReturnStatement(GetByRefExpression(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(accessorField.Name), syntaxGenerator.IdentifierName(memberName)))); + SyntaxNode? setAccessor = null; + if (!fieldSymbol.IsReadOnly && !HasRefReturns) + { + setAccessor = syntaxGenerator.AssignmentStatement(syntaxGenerator.MemberAccessExpression(syntaxGenerator.IdentifierName(accessorField.Name), syntaxGenerator.IdentifierName(memberName)), syntaxGenerator.IdentifierName("value")); + } + + DeclarationModifiers modifiers; + if (setAccessor is null) + { + modifiers = DeclarationModifiers.ReadOnly; + } + else + { + modifiers = DeclarationModifiers.None; + } + + return syntaxGenerator.PropertyDeclaration( + GetTestAccessorName(fieldSymbol), + GetByRefType(syntaxGenerator.TypeExpression(fieldSymbol.Type), fieldSymbol.IsReadOnly ? RefKind.RefReadOnly : RefKind.Ref), + Accessibility.Internal, + modifiers, + getAccessorStatements: new[] { getAccessor }, + setAccessorStatements: setAccessor != null ? new[] { setAccessor } : null); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractRunIterations`1.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractRunIterations`1.cs new file mode 100644 index 0000000000000..6f8e7deb2177c --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AbstractRunIterations`1.cs @@ -0,0 +1,149 @@ +// 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.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Editing; + +namespace Roslyn.Diagnostics.Analyzers +{ + public abstract class AbstractRunIterations : CodeRefactoringProvider + where TMethodDeclarationSyntax : SyntaxNode + { + private protected abstract IRefactoringHelpers RefactoringHelpers { get; } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + var method = await context.TryGetRelevantNodeAsync(RefactoringHelpers).ConfigureAwait(false); + if (method is null) + return; + + var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + RoslynDebug.Assert(semanticModel is not null); + + if (!semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.XunitFactAttribute, out var factAttribute) + || !semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.XunitSdkDataAttribute, out var dataAttribute) + || !semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.XunitCombinatorialDataAttribute, out var combinatorialDataAttribute)) + { + return; + } + + var knownTestAttributes = new ConcurrentDictionary(); + var methodSymbol = (IMethodSymbol)semanticModel.GetDeclaredSymbol(method, context.CancellationToken)!; + if (!methodSymbol.IsBenchmarkOrXUnitTestMethod(knownTestAttributes, benchmarkAttribute: null, factAttribute)) + return; + + foreach (var parameter in methodSymbol.Parameters) + { + // This is already a test with iterations + if (parameter.Name == "iteration") + return; + } + + // When true, this method is a [Fact] (or related) test which requires conversion to [Theory] with + // application of [CombinatorialData] as part of the refactoring. Otherwise, this test is already a [Theory] + // and only needs an additional parameter added. + bool convertToTheory = true; + + foreach (var attribute in methodSymbol.GetAttributes()) + { + if (!attribute.AttributeClass.DerivesFrom(dataAttribute)) + continue; + + if (!attribute.AttributeClass.DerivesFrom(combinatorialDataAttribute)) + { + // The test is already a theory, but doesn't use [CombinatorialData]. It's not known how this test + // can be automatically converted to run iterations. + return; + } + + convertToTheory = false; + break; + } + + context.RegisterRefactoring( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.Run_iterations, + cancellationToken => AbstractRunIterations.RunIterationsAsync(context.Document, method, convertToTheory, cancellationToken), + equivalenceKey: nameof(RoslynDiagnosticsAnalyzersResources.Run_iterations))); + } + + private static async Task RunIterationsAsync(Document document, TMethodDeclarationSyntax method, bool convertToTheory, CancellationToken cancellationToken) + { + var syntaxGenerator = SyntaxGenerator.GetGenerator(document); + SyntaxNode updatedMethod = method; + + if (convertToTheory) + { + foreach (var attribute in syntaxGenerator.GetAttributes(method)) + { + var name = syntaxGenerator.GetName(attribute); + if (name.EndsWith("Fact", StringComparison.Ordinal)) + { + updatedMethod = updatedMethod.ReplaceNode( + attribute, + ReplaceName(syntaxGenerator, attribute, name[0..^4] + "Theory")); + break; + } + else if (name.EndsWith("FactAttribute", StringComparison.Ordinal)) + { + updatedMethod = updatedMethod.ReplaceNode( + attribute, + ReplaceName(syntaxGenerator, attribute, name[0..^"FactAttribute".Length] + "TheoryAttribute")); + break; + } + } + + updatedMethod = syntaxGenerator.AddAttributes(updatedMethod, syntaxGenerator.Attribute(WellKnownTypeNames.XunitCombinatorialDataAttribute).WithAddImportsAnnotation()); + } + + updatedMethod = syntaxGenerator.AddParameters( + updatedMethod, + new[] + { + syntaxGenerator.AddAttributes( + syntaxGenerator.ParameterDeclaration( + "iteration", + syntaxGenerator.TypeExpression(SpecialType.System_Int32)), + syntaxGenerator.Attribute( + WellKnownTypeNames.XunitCombinatorialRangeAttribute, + syntaxGenerator.LiteralExpression(0), + syntaxGenerator.LiteralExpression(10))), + }); + + // For C# test projects, add a discard assignment to avoid xunit warnings about unused theory parameters + if (document.Project.Language == LanguageNames.CSharp) + { + var assignment = syntaxGenerator.AssignmentStatement(syntaxGenerator.IdentifierName("_"), syntaxGenerator.IdentifierName("iteration")); + var statements = syntaxGenerator.GetStatements(updatedMethod); + updatedMethod = syntaxGenerator.WithStatements(updatedMethod, new[] { assignment }.Concat(statements)); + } + + var root = await method.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); + return document.WithSyntaxRoot(root.ReplaceNode(method, updatedMethod)); + } + + private static SyntaxNode ReplaceName(SyntaxGenerator syntaxGenerator, SyntaxNode node, string name) + { + var newNode = syntaxGenerator.WithName(node, name); + if (newNode.RawKind != node.RawKind + && newNode.ChildNodes().FirstOrDefault()?.RawKind == node.RawKind) + { + // The call to WithName may have converted AttributeSyntax to AttributeListSyntax; we only want the + // AttributeSyntax portion. + newNode = newNode.ChildNodes().First(); + } + + return newNode; + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..b08d045f710c3 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Shipped.md @@ -0,0 +1,69 @@ +## Release 2.9.8 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0001 | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer +RS0002 | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer +RS0005 | RoslyDiagnosticsPerformance | Warning | CodeActionCreateAnalyzer +RS0006 | RoslyDiagnosticsReliability | Warning | DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer +RS0013 | RoslyDiagnosticsPerformance | Disabled | DiagnosticDescriptorAccessAnalyzer +RS0019 | RoslyDiagnosticsReliability | Disabled | SymbolDeclaredEventAnalyzer +RS0023 | RoslyDiagnosticsReliability | Warning | PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer +RS0032 | RoslyDiagnosticsReliability | Disabled | TestExportsShouldNotBeDiscoverable +RS0033 | RoslyDiagnosticsReliability | Warning | ImportingConstructorShouldBeObsolete +RS0034 | RoslyDiagnosticsReliability | Warning | ExportedPartsShouldHaveImportingConstructor + +## Release 3.3.0 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0040 | RoslynDiagnosticsReliability | Warning | DefaultableTypeShouldHaveDefaultableFieldsAnalyzer +RS0042 | RoslynDiagnosticsReliability | Warning | DoNotCopyValue +RS0043 | RoslynDiagnosticsMaintainability | Warning | DoNotCallGetTestAccessor +RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer + +### Changed Rules + +Rule ID | New Category | New Severity | Old Category | Old Severity | Notes +--------|--------------|--------------|--------------|--------------|------- +RS0001 | RoslynDiagnosticsPerformance | Warning | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer +RS0002 | RoslynDiagnosticsPerformance | Warning | RoslyDiagnosticsPerformance | Warning | SpecializedEnumerableCreationAnalyzer +RS0005 | RoslynDiagnosticsPerformance | Warning | RoslyDiagnosticsPerformance | Warning | CodeActionCreateAnalyzer +RS0006 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | Warning | DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer +RS0019 | RoslynDiagnosticsReliability | Disabled | RoslyDiagnosticsReliability | Disabled | SymbolDeclaredEventAnalyzer +RS0023 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | Warning | PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer +RS0032 | RoslynDiagnosticsReliability | Disabled | RoslyDiagnosticsReliability | Disabled | TestExportsShouldNotBeDiscoverable +RS0033 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | Warning | ImportingConstructorShouldBeObsolete +RS0034 | RoslynDiagnosticsReliability | Warning | RoslyDiagnosticsReliability | Warning | ExportedPartsShouldHaveImportingConstructor + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0013 | RoslyDiagnosticsPerformance | Disabled | DiagnosticDescriptorAccessAnalyzer + +## Release 3.3.3 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0049 | RoslynDiagnosticsReliability | Warning | TemporaryArrayAsRefAnalyzer + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0101 | RoslynDiagnosticsMaintainability | Warning | AbstractBlankLinesDiagnosticAnalyzer + +## Release 3.3.4 + +### Removed Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0005 | RoslyDiagnosticsPerformance | Warning | CodeActionCreateAnalyzer diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DefaultableTypeShouldHaveDefaultableFieldsAnalyzer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DefaultableTypeShouldHaveDefaultableFieldsAnalyzer.cs new file mode 100644 index 0000000000000..1b2bd26d8152b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DefaultableTypeShouldHaveDefaultableFieldsAnalyzer.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable warnings + +using System.Collections.Concurrent; +using System.Collections.Immutable; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0040: + /// +#pragma warning disable RS1004 // Recommend adding language support to diagnostic analyzer + [DiagnosticAnalyzer(LanguageNames.CSharp)] +#pragma warning restore RS1004 // Recommend adding language support to diagnostic analyzer + public class DefaultableTypeShouldHaveDefaultableFieldsAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.DefaultableTypeShouldHaveDefaultableFieldsRuleId, + CreateLocalizableResourceString(nameof(DefaultableTypeShouldHaveDefaultableFieldsTitle)), + CreateLocalizableResourceString(nameof(DefaultableTypeShouldHaveDefaultableFieldsMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DefaultableTypeShouldHaveDefaultableFieldsDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + var nonDefaultableAttribute = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.RoslynUtilitiesNonDefaultableAttribute); + if (nonDefaultableAttribute is null) + return; + + var knownNonDefaultableTypes = new ConcurrentDictionary(); + context.RegisterSymbolAction(context => AnalyzeField(context, nonDefaultableAttribute, knownNonDefaultableTypes), SymbolKind.Field); + context.RegisterSymbolAction(context => AnalyzeNamedType(context, nonDefaultableAttribute, knownNonDefaultableTypes), SymbolKind.NamedType); + }); + } + + private static void AnalyzeField(SymbolAnalysisContext context, INamedTypeSymbol nonDefaultableAttribute, ConcurrentDictionary knownNonDefaultableTypes) + { + AnalyzeField(context, (IFieldSymbol)context.Symbol, nonDefaultableAttribute, knownNonDefaultableTypes); + } + + private static void AnalyzeNamedType(SymbolAnalysisContext context, INamedTypeSymbol nonDefaultableAttribute, ConcurrentDictionary knownNonDefaultableTypes) + { + var namedType = (INamedTypeSymbol)context.Symbol; + foreach (var member in namedType.GetMembers()) + { + if (member.Kind != SymbolKind.Field) + continue; + + if (!member.IsImplicitlyDeclared) + continue; + + AnalyzeField(context, (IFieldSymbol)member, nonDefaultableAttribute, knownNonDefaultableTypes); + } + } + + private static void AnalyzeField(SymbolAnalysisContext originalContext, IFieldSymbol field, INamedTypeSymbol nonDefaultableAttribute, ConcurrentDictionary knownNonDefaultableTypes) + { + if (field.IsStatic) + return; + + var containingType = field.ContainingType; + if (containingType.TypeKind != TypeKind.Struct) + return; + + if (!IsDefaultable(containingType, nonDefaultableAttribute, knownNonDefaultableTypes)) + { + // A non-defaultable type is allowed to have fields of any type + return; + } + + if (IsDefaultable(field.Type, nonDefaultableAttribute, knownNonDefaultableTypes)) + { + // Any type is allowed to have defaultable fields + return; + } + +#pragma warning disable RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + var semanticModel = originalContext.Compilation.GetSemanticModel(field.Locations[0].SourceTree); +#pragma warning restore RS1030 // Do not invoke Compilation.GetSemanticModel() method within a diagnostic analyzer + if (!semanticModel.GetNullableContext(field.Locations[0].SourceSpan.Start).WarningsEnabled()) + { + // Warnings are not enabled for this field + return; + } + + var sourceSymbol = (field.IsImplicitlyDeclared ? field.AssociatedSymbol : null) ?? field; + originalContext.ReportDiagnostic(field.CreateDiagnostic(Rule, field.ContainingType, sourceSymbol.Name)); + } + + private static bool IsDefaultable(ITypeSymbol type, INamedTypeSymbol nonDefaultableAttribute, ConcurrentDictionary knownNonDefaultableTypes) + { + switch (type.TypeKind) + { + case TypeKind.Class: + case TypeKind.Interface: + case TypeKind.Delegate: + return type.NullableAnnotation != NullableAnnotation.NotAnnotated; + + case TypeKind.Enum: + return true; + + case TypeKind.Struct: + if (type is not INamedTypeSymbol namedType) + return true; + + if (knownNonDefaultableTypes.TryGetValue(namedType, out var isNonDefaultable)) + return !isNonDefaultable; + + isNonDefaultable = namedType.HasAnyAttribute(nonDefaultableAttribute); + return !knownNonDefaultableTypes.GetOrAdd(namedType, isNonDefaultable); + + case TypeKind.TypeParameter: + if (knownNonDefaultableTypes.TryGetValue(type, out isNonDefaultable)) + return !isNonDefaultable; + + isNonDefaultable = type.HasAnyAttribute(nonDefaultableAttribute); + return !knownNonDefaultableTypes.GetOrAdd(type, isNonDefaultable); + + case TypeKind.Unknown: + case TypeKind.Array: + case TypeKind.Dynamic: + case TypeKind.Error: + case TypeKind.Module: + case TypeKind.Pointer: + case TypeKind.Submission: + default: + return true; + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DiagnosticExtensions.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DiagnosticExtensions.cs new file mode 100644 index 0000000000000..2e9e681e4a4a7 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DiagnosticExtensions.cs @@ -0,0 +1,39 @@ +// 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 System.Linq; +using System.Threading; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; + +namespace Roslyn.Diagnostics.Analyzers +{ + public static class DiagnosticExtensions + { + public static Diagnostic CreateDiagnostic( + this SyntaxReference syntaxReference, + DiagnosticDescriptor rule, + CancellationToken cancellationToken, + params object[] args) + => syntaxReference.GetSyntax(cancellationToken).CreateDiagnostic(rule, args); + + public static Diagnostic CreateDiagnostic( + this SyntaxReference syntaxReference, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + CancellationToken cancellationToken, + params object[] args) + => syntaxReference.GetSyntax(cancellationToken).CreateDiagnostic(rule, properties, args); + + public static Diagnostic CreateDiagnostic( + this IEnumerable syntaxReferences, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + CancellationToken cancellationToken, + params object[] args) + => syntaxReferences.Select(s => s.GetSyntax(cancellationToken).GetLocation()).CreateDiagnostic(rule, properties, args); + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotCallGetTestAccessor.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotCallGetTestAccessor.cs new file mode 100644 index 0000000000000..876e7082449c7 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotCallGetTestAccessor.cs @@ -0,0 +1,86 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0043: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DoNotCallGetTestAccessor : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor DoNotCallGetTestAccessorRule = new( + RoslynDiagnosticIds.DoNotCallGetTestAccessorRuleId, + CreateLocalizableResourceString(nameof(DoNotCallGetTestAccessorTitle)), + CreateLocalizableResourceString(nameof(DoNotCallGetTestAccessorMessage)), + DiagnosticCategory.RoslynDiagnosticsMaintainability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotCallGetTestAccessorDescription)), + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public sealed override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(DoNotCallGetTestAccessorRule); + + public sealed override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterOperationBlockStartAction(context => + { + if (!string.Equals(context.OwningSymbol.Name, TestAccessorHelper.GetTestAccessorMethodName, StringComparison.Ordinal) + && !string.Equals(context.OwningSymbol.ContainingType?.Name, TestAccessorHelper.TestAccessorTypeName, StringComparison.Ordinal)) + { + context.RegisterOperationAction(HandleMemberReference, OperationKinds.MemberReference); + context.RegisterOperationAction(HandleInvocation, OperationKind.Invocation); + context.RegisterOperationAction(HandleObjectCreation, OperationKind.ObjectCreation); + } + }); + } + + private void HandleMemberReference(OperationAnalysisContext context) + { + var memberReference = (IMemberReferenceOperation)context.Operation; + if (string.Equals(memberReference.Member.ContainingType?.Name, TestAccessorHelper.TestAccessorTypeName, StringComparison.Ordinal)) + { + context.ReportDiagnostic(memberReference.Syntax.CreateDiagnostic(DoNotCallGetTestAccessorRule)); + } + } + + private void HandleInvocation(OperationAnalysisContext context) + { + var invocation = (IInvocationOperation)context.Operation; + if (invocation.TargetMethod.Name.Equals(TestAccessorHelper.GetTestAccessorMethodName, StringComparison.Ordinal)) + { + // Calling a type's GetTestAccessor method + context.ReportDiagnostic(invocation.Syntax.CreateDiagnostic(DoNotCallGetTestAccessorRule)); + } + else if (string.Equals(invocation.TargetMethod.ContainingType?.Name, TestAccessorHelper.TestAccessorTypeName, StringComparison.Ordinal)) + { + // Calling a static method of a TestAccessor type + context.ReportDiagnostic(invocation.Syntax.CreateDiagnostic(DoNotCallGetTestAccessorRule)); + } + } + + private void HandleObjectCreation(OperationAnalysisContext context) + { + var objectCreation = (IObjectCreationOperation)context.Operation; + if (objectCreation.Type!.Name.Equals(TestAccessorHelper.TestAccessorTypeName, StringComparison.Ordinal)) + { + // Directly constructing a TestAccessor instance + context.ReportDiagnostic(objectCreation.Syntax.CreateDiagnostic(DoNotCallGetTestAccessorRule)); + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs new file mode 100644 index 0000000000000..86f697fd00ec9 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.cs @@ -0,0 +1,31 @@ +// 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 System.Collections.Immutable; +using System.Threading.Tasks; + +namespace Roslyn.Diagnostics.Analyzers +{ + /// + /// RS0006: Do not mix attributes from different versions of MEF + /// + public abstract class DoNotMixAttributesFromDifferentVersionsOfMEFFixer : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Empty; + + public sealed override FixAllProvider GetFixAllProvider() + { + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Fixer not yet implemented. + return Task.CompletedTask; + + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.cs new file mode 100644 index 0000000000000..804ec9fb918c4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/DoNotMixAttributesFromDifferentVersionsOfMEF.cs @@ -0,0 +1,134 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable warnings + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0006: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.MixedVersionsOfMefAttributesRuleId, + CreateLocalizableResourceString(nameof(DoNotMixAttributesFromDifferentVersionsOfMEFTitle)), + CreateLocalizableResourceString(nameof(DoNotMixAttributesFromDifferentVersionsOfMEFMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(DoNotMixAttributesFromDifferentVersionsOfMEFDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var mefV1ExportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionExportAttribute); + var mefV2ExportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionExportAttribute); + if (mefV1ExportAttribute == null || mefV2ExportAttribute == null) + { + // We don't need to check assemblies unless they're referencing both versions of MEF, so we're done + return; + } + + var attributeUsageAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemAttributeUsageAttribute); + + var exportAttributes = new List() { mefV1ExportAttribute, mefV2ExportAttribute }; + compilationContext.RegisterSymbolAction(c => AnalyzeSymbol(c, exportAttributes, attributeUsageAttribute), SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext symbolContext, IEnumerable exportAttributes, INamedTypeSymbol? attributeUsageAttribute) + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + var namedTypeAttributes = namedType.GetApplicableAttributes(attributeUsageAttribute); + + // Figure out which export attributes are being used here + var appliedExportAttributes = exportAttributes.Where(e => namedTypeAttributes.Any(ad => ad.AttributeClass.DerivesFrom(e))).ToList(); + + // If we have no exports we're done + if (appliedExportAttributes.Count == 0) + { + return; + } + + var badNamespaces = exportAttributes.Except(appliedExportAttributes).Select(s => s.ContainingNamespace).ToSet(); + var goodNamespaces = appliedExportAttributes.Select(s => s.ContainingNamespace).ToSet(); + + // Now look at all attributes and see if any are metadata attributes from badNamespaces, but none from good namepaces. + foreach (var namedTypeAttribute in namedTypeAttributes) + { + var appliedMetadataAttributes = namedTypeAttribute.AttributeClass.GetApplicableAttributes(attributeUsageAttribute) + .Where(ad => ad.AttributeClass.Name.Equals("MetadataAttributeAttribute", StringComparison.Ordinal)); + if (appliedMetadataAttributes.Any(ad => badNamespaces.Contains(ad.AttributeClass.ContainingNamespace)) && + !appliedMetadataAttributes.Any(ad => goodNamespaces.Contains(ad.AttributeClass.ContainingNamespace))) + { + ReportDiagnostic(symbolContext, namedType, namedTypeAttribute); + } + } + + // Also look through all members and their attributes, and see if any are using from bad places + foreach (var member in namedType.GetMembers()) + { + foreach (var attribute in member.GetAttributes()) + { + if (badNamespaces.Contains(attribute.AttributeClass.ContainingNamespace)) + { + ReportDiagnostic(symbolContext, namedType, attribute); + } + } + + // if it's a constructor, we should also check parameters since they may have [ImportMany] + + if (member is IMethodSymbol methodSymbol && methodSymbol.MethodKind == MethodKind.Constructor) + { + foreach (var parameter in methodSymbol.Parameters) + { + foreach (var attribute in parameter.GetAttributes()) + { + if (badNamespaces.Contains(attribute.AttributeClass.ContainingNamespace)) + { + ReportDiagnostic(symbolContext, namedType, attribute); + } + } + } + } + } + } + + private static void ReportDiagnostic(SymbolAnalysisContext symbolContext, INamedTypeSymbol exportedType, AttributeData problematicAttribute) + { + if (problematicAttribute.ApplicationSyntaxReference == null) + { + symbolContext.ReportDiagnostic(symbolContext.Symbol.CreateDiagnostic(Rule, problematicAttribute.AttributeClass.Name, exportedType.Name)); + } + else + { + // Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + var diagnostic = problematicAttribute.ApplicationSyntaxReference.CreateDiagnostic( + Rule, symbolContext.CancellationToken, problematicAttribute.AttributeClass.Name, exportedType.Name); + symbolContext.ReportDiagnostic(diagnostic); + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructor.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructor.cs new file mode 100644 index 0000000000000..cd770f06c342a --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructor.cs @@ -0,0 +1,170 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable warnings + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0034: + /// MEF-exported types should have exactly one constructor, which should be explicitly defined and marked with + /// . + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class ExportedPartsShouldHaveImportingConstructor : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.ExportedPartsShouldHaveImportingConstructorRuleId, + CreateLocalizableResourceString(nameof(ExportedPartsShouldHaveImportingConstructorTitle)), + CreateLocalizableResourceString(nameof(ExportedPartsShouldHaveImportingConstructorMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(ExportedPartsShouldHaveImportingConstructorDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var exportAttributeV1 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionExportAttribute); + var importingConstructorAttributeV1 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionImportingConstructorAttribute); + var exportAttributeV2 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionExportAttribute); + var inheritedExportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionInheritedExportAttribute); + var importingConstructorAttributeV2 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionImportingConstructorAttribute); + + if (exportAttributeV1 is null && exportAttributeV2 is null) + { + // We don't need to check assemblies unless they're referencing MEF, so we're done + return; + } + + if (exportAttributeV1 is object && importingConstructorAttributeV1 is null) + { + throw new InvalidOperationException("Found MEF v1 ExportAttribute, but could not find the corresponding ImportingConstructorAttribute."); + } + + if (exportAttributeV2 is object && importingConstructorAttributeV2 is null) + { + throw new InvalidOperationException("Found MEF v2 ExportAttribute, but could not find the corresponding ImportingConstructorAttribute."); + } + + compilationContext.RegisterSymbolAction(symbolContext => + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + var exportAttributes = namedType.GetApplicableExportAttributes(exportAttributeV1, exportAttributeV2, inheritedExportAttribute); + + AnalyzeSymbolForAttribute(ref symbolContext, exportAttributeV1, importingConstructorAttributeV1, namedType, exportAttributes); + AnalyzeSymbolForAttribute(ref symbolContext, exportAttributeV2, importingConstructorAttributeV2, namedType, exportAttributes); + }, SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbolForAttribute(ref SymbolAnalysisContext context, INamedTypeSymbol? exportAttribute, INamedTypeSymbol? importingConstructorAttribute, INamedTypeSymbol namedType, IEnumerable exportAttributes) + { + if (exportAttribute is null) + { + return; + } + + var exportAttributeApplication = exportAttributes.FirstOrDefault(ad => ad.AttributeClass.DerivesFrom(exportAttribute)); + if (exportAttributeApplication is null) + { + return; + } + + IMethodSymbol? importingConstructor = null; + var nonImportingConstructors = ImmutableArray.Empty; + foreach (var constructor in namedType.Constructors) + { + if (constructor.IsStatic) + { + // Ignore static constructors + continue; + } + + if (constructor.IsImplicitlyDeclared) + { + if (exportAttributeApplication.ApplicationSyntaxReference is object) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic( + exportAttributeApplication.ApplicationSyntaxReference.CreateDiagnostic( + Rule, ScenarioProperties.ImplicitConstructor, context.CancellationToken, namedType.Name)); + } + + continue; + } + + var constructorAttributes = constructor.GetAttributes(); + var appliedImportingConstructorAttribute = constructorAttributes.FirstOrDefault(ad => ad.AttributeClass.DerivesFrom(importingConstructorAttribute)); + if (appliedImportingConstructorAttribute is null) + { + nonImportingConstructors = nonImportingConstructors.Add(constructor); + continue; + } + + importingConstructor = constructor; + if (constructor.DeclaredAccessibility != Accessibility.Public) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic( + appliedImportingConstructorAttribute.ApplicationSyntaxReference.CreateDiagnostic( + Rule, ScenarioProperties.NonPublicConstructor, context.CancellationToken, namedType.Name)); + continue; + } + } + + IMethodSymbol? missingImportingConstructor = null; + if (importingConstructor is null) + { + missingImportingConstructor = nonImportingConstructors.FirstOrDefault(constructor => constructor.DeclaredAccessibility == Accessibility.Public) + ?? nonImportingConstructors.FirstOrDefault(); + } + + foreach (var constructor in nonImportingConstructors) + { + var properties = Equals(constructor, missingImportingConstructor) ? ScenarioProperties.MissingAttribute : ScenarioProperties.MultipleConstructors; + + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(constructor.DeclaringSyntaxReferences.CreateDiagnostic(Rule, properties, context.CancellationToken, namedType.Name)); + continue; + } + } + + internal static class Scenario + { + public const string ImplicitConstructor = nameof(ImplicitConstructor); + public const string NonPublicConstructor = nameof(NonPublicConstructor); + public const string MissingAttribute = nameof(MissingAttribute); + public const string MultipleConstructors = nameof(MultipleConstructors); + } + + private static class ScenarioProperties + { + public static readonly ImmutableDictionary ImplicitConstructor = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(ImplicitConstructor)); + public static readonly ImmutableDictionary NonPublicConstructor = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(NonPublicConstructor)); + public static readonly ImmutableDictionary MissingAttribute = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(MissingAttribute)); + public static readonly ImmutableDictionary MultipleConstructors = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(MultipleConstructors)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructorCodeFixProvider.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructorCodeFixProvider.cs new file mode 100644 index 0000000000000..294c725da11c4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ExportedPartsShouldHaveImportingConstructorCodeFixProvider.cs @@ -0,0 +1,207 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Roslyn.Diagnostics.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(ExportedPartsShouldHaveImportingConstructorCodeFixProvider))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class ExportedPartsShouldHaveImportingConstructorCodeFixProvider() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(ExportedPartsShouldHaveImportingConstructor.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!diagnostic.Properties.TryGetValue(nameof(ExportedPartsShouldHaveImportingConstructor.Scenario), out var scenario)) + { + continue; + } + + string title; + Func> createChangedDocument; + switch (scenario) + { + case ExportedPartsShouldHaveImportingConstructor.Scenario.ImplicitConstructor: + title = RoslynDiagnosticsAnalyzersResources.ExportedPartsShouldHaveImportingConstructorCodeFix_ImplicitConstructor; + createChangedDocument = cancellationToken => AddExplicitImportingConstructorAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ExportedPartsShouldHaveImportingConstructor.Scenario.NonPublicConstructor: + title = RoslynDiagnosticsAnalyzersResources.ExportedPartsShouldHaveImportingConstructorCodeFix_NonPublicConstructor; + createChangedDocument = cancellationToken => MakeConstructorPublicAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ExportedPartsShouldHaveImportingConstructor.Scenario.MissingAttribute: + title = RoslynDiagnosticsAnalyzersResources.ExportedPartsShouldHaveImportingConstructorCodeFix_MissingAttribute; + createChangedDocument = cancellationToken => AddImportingConstructorAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ExportedPartsShouldHaveImportingConstructor.Scenario.MultipleConstructors: + default: + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + title, + createChangedDocument, + equivalenceKey: scenario), + diagnostic); + } + + return Task.CompletedTask; + } + + private static async Task AddExplicitImportingConstructorAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var exportAttribute = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + var exportAttributeSymbol = semanticModel.GetSymbolInfo(exportAttribute, cancellationToken).Symbol?.ContainingType; + INamedTypeSymbol? importingConstructorAttributeSymbol = null; + while (exportAttributeSymbol is object) + { + importingConstructorAttributeSymbol = exportAttributeSymbol.ContainingNamespace?.GetTypeMembers(nameof(ImportingConstructorAttribute)).FirstOrDefault(); + if (importingConstructorAttributeSymbol is object) + { + break; + } + + exportAttributeSymbol = exportAttributeSymbol.BaseType; + } + + if (importingConstructorAttributeSymbol is null) + { + return document; + } + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(exportAttribute, DeclarationKind.Class); + if (declaration is null) + { + return document; + } + + var importingConstructor = generator.ConstructorDeclaration( + containingTypeName: generator.GetName(declaration), + parameters: Enumerable.Empty(), + Accessibility.Public, + DeclarationModifiers.None, + baseConstructorArguments: null, + statements: Enumerable.Empty()); + importingConstructor = generator.AddAttributes(importingConstructor, generator.Attribute(generator.TypeExpression(importingConstructorAttributeSymbol).WithAddImportsAnnotation())); + + var index = 0; + var existingMembers = generator.GetMembers(declaration); + while (index < existingMembers.Count) + { + switch (generator.GetDeclarationKind(existingMembers[index])) + { + case DeclarationKind.Field: + index++; + continue; + + default: + break; + } + + break; + } + + var newDeclaration = generator.InsertMembers(declaration, index, importingConstructor); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + + private static async Task MakeConstructorPublicAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var importingConstructorAttribute = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(importingConstructorAttribute, DeclarationKind.Constructor); + if (declaration is null) + { + return document; + } + + var newDeclaration = generator.WithAccessibility(declaration, Accessibility.Public); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + + private static async Task AddImportingConstructorAttributeAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var constructor = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(constructor, DeclarationKind.Constructor); + if (declaration is null) + { + return document; + } + + var exportedType = semanticModel.GetDeclaredSymbol(declaration, cancellationToken)?.ContainingType; + if (exportedType is null) + { + return document; + } + + INamedTypeSymbol? importingConstructorAttributeSymbol = null; + foreach (var attributeData in exportedType.GetAttributes()) + { + INamedTypeSymbol? exportAttributeSymbol = null; + foreach (var attributeClass in attributeData.AttributeClass.GetBaseTypesAndThis()) + { + if (attributeClass.Name == nameof(ExportAttribute)) + { + exportAttributeSymbol = attributeClass; + break; + } + } + + if (exportAttributeSymbol is null) + { + continue; + } + + importingConstructorAttributeSymbol = exportAttributeSymbol.ContainingNamespace.GetTypeMembers(nameof(ImportingConstructorAttribute)).FirstOrDefault(); + if (importingConstructorAttributeSymbol is object) + { + break; + } + } + + var newDeclaration = generator.AddAttributes(declaration, generator.Attribute(generator.TypeExpression(importingConstructorAttributeSymbol).WithAddImportsAnnotation())); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsolete.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsolete.cs new file mode 100644 index 0000000000000..41779caa0a25d --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsolete.cs @@ -0,0 +1,186 @@ +// 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 warnings + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0033: + /// + /// The importing constructor for a MEF-exported type should be marked obsolete. + /// + /// + /// [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + /// + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class ImportingConstructorShouldBeObsolete : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.ImportingConstructorShouldBeObsoleteRuleId, + CreateLocalizableResourceString(nameof(ImportingConstructorShouldBeObsoleteTitle)), + CreateLocalizableResourceString(nameof(ImportingConstructorShouldBeObsoleteMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(ImportingConstructorShouldBeObsoleteDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var obsoleteAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute); + var exportAttributeV1 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionExportAttribute); + var importingConstructorAttributeV1 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionImportingConstructorAttribute); + var exportAttributeV2 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionExportAttribute); + var inheritedExportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionInheritedExportAttribute); + var importingConstructorAttributeV2 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionImportingConstructorAttribute); + + if (exportAttributeV1 is null && exportAttributeV2 is null) + { + // We don't need to check assemblies unless they're referencing MEF, so we're done + return; + } + + compilationContext.RegisterSymbolAction(symbolContext => + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + var exportAttributes = namedType.GetApplicableExportAttributes(exportAttributeV1, exportAttributeV2, inheritedExportAttribute); + + AnalyzeSymbolForAttribute(ref symbolContext, obsoleteAttribute, exportAttributeV1, importingConstructorAttributeV1, namedType, exportAttributes); + AnalyzeSymbolForAttribute(ref symbolContext, obsoleteAttribute, exportAttributeV2, importingConstructorAttributeV2, namedType, exportAttributes); + }, SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbolForAttribute(ref SymbolAnalysisContext context, INamedTypeSymbol? obsoleteAttribute, INamedTypeSymbol? exportAttribute, INamedTypeSymbol? importingConstructorAttribute, INamedTypeSymbol namedType, IEnumerable exportAttributes) + { + if (exportAttribute is null) + { + return; + } + + if (!exportAttributes.Any(ad => ad.AttributeClass.DerivesFrom(exportAttribute))) + { + return; + } + + foreach (var constructor in namedType.Constructors) + { + if (constructor.IsImplicitlyDeclared) + { + continue; + } + + var constructorAttributes = constructor.GetAttributes(); + AttributeData? importingConstructorAttributeData = null; + foreach (var attributeData in constructorAttributes) + { + if (attributeData.AttributeClass.DerivesFrom(importingConstructorAttribute)) + { + importingConstructorAttributeData = attributeData; + break; + } + } + + if (importingConstructorAttributeData is null) + { + // This constructor is not marked [ImportingConstructor] + continue; + } + + var foundObsoleteAttribute = false; + foreach (var attributeData in constructorAttributes) + { + if (!attributeData.AttributeClass.Equals(obsoleteAttribute)) + { + continue; + } + + foundObsoleteAttribute = true; + if (attributeData.ConstructorArguments.Length != 2) + { + if (attributeData.ConstructorArguments.IsEmpty) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(attributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, ScenarioProperties.MissingDescription, context.CancellationToken, namedType.Name)); + break; + } + else if (attributeData.ConstructorArguments.Length == 1) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(attributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, ScenarioProperties.MissingError, context.CancellationToken, namedType.Name)); + break; + } + else + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(attributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, context.CancellationToken, namedType.Name)); + break; + } + } + + if (!Equals(attributeData.ConstructorArguments[0].Value, "This exported object must be obtained through the MEF export provider.")) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(attributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, ScenarioProperties.IncorrectDescription, context.CancellationToken, namedType.Name)); + break; + } + + if (!Equals(attributeData.ConstructorArguments[1].Value, true)) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(attributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, ScenarioProperties.ErrorSetToFalse, context.CancellationToken, namedType.Name)); + break; + } + + break; + } + + if (!foundObsoleteAttribute) + { + // '{0}' is MEF-exported and should have a single importing constructor of the correct form + context.ReportDiagnostic(importingConstructorAttributeData.ApplicationSyntaxReference.CreateDiagnostic(Rule, ScenarioProperties.MissingAttribute, context.CancellationToken, namedType.Name)); + break; + } + } + } + + internal static class Scenario + { + public const string MissingAttribute = nameof(MissingAttribute); + public const string MissingDescription = nameof(MissingDescription); + public const string IncorrectDescription = nameof(IncorrectDescription); + public const string MissingError = nameof(MissingError); + public const string ErrorSetToFalse = nameof(ErrorSetToFalse); + } + + private static class ScenarioProperties + { + public static readonly ImmutableDictionary MissingAttribute = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(MissingAttribute)); + public static readonly ImmutableDictionary MissingDescription = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(MissingDescription)); + public static readonly ImmutableDictionary IncorrectDescription = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(IncorrectDescription)); + public static readonly ImmutableDictionary MissingError = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(MissingError)); + public static readonly ImmutableDictionary ErrorSetToFalse = ImmutableDictionary.Create().Add(nameof(Scenario), nameof(ErrorSetToFalse)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsoleteCodeFixProvider.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsoleteCodeFixProvider.cs new file mode 100644 index 0000000000000..dcd7231a565b5 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/ImportingConstructorShouldBeObsoleteCodeFixProvider.cs @@ -0,0 +1,240 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Roslyn.Diagnostics.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(ImportingConstructorShouldBeObsoleteCodeFixProvider))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class ImportingConstructorShouldBeObsoleteCodeFixProvider() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(ImportingConstructorShouldBeObsolete.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + if (!diagnostic.Properties.TryGetValue(nameof(ImportingConstructorShouldBeObsolete.Scenario), out var scenario)) + { + continue; + } + + string title; + Func> createChangedDocument; + switch (scenario) + { + case ImportingConstructorShouldBeObsolete.Scenario.MissingAttribute: + title = RoslynDiagnosticsAnalyzersResources.ImportingConstructorShouldBeObsoleteCodeFix_MissingAttribute; + createChangedDocument = cancellationToken => AddObsoleteAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ImportingConstructorShouldBeObsolete.Scenario.MissingDescription: + title = RoslynDiagnosticsAnalyzersResources.ImportingConstructorShouldBeObsoleteCodeFix_MissingDescription; + createChangedDocument = cancellationToken => AddDescriptionAndErrorAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ImportingConstructorShouldBeObsolete.Scenario.IncorrectDescription: + title = RoslynDiagnosticsAnalyzersResources.ImportingConstructorShouldBeObsoleteCodeFix_IncorrectDescription; + createChangedDocument = cancellationToken => UpdateDescriptionAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ImportingConstructorShouldBeObsolete.Scenario.MissingError: + title = RoslynDiagnosticsAnalyzersResources.ImportingConstructorShouldBeObsoleteCodeFix_MissingError; + createChangedDocument = cancellationToken => AddErrorAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + case ImportingConstructorShouldBeObsolete.Scenario.ErrorSetToFalse: + title = RoslynDiagnosticsAnalyzersResources.ImportingConstructorShouldBeObsoleteCodeFix_ErrorSetToFalse; + createChangedDocument = cancellationToken => SetErrorToTrueAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken); + break; + + default: + continue; + } + + context.RegisterCodeFix( + CodeAction.Create( + title, + createChangedDocument, + equivalenceKey: scenario), + diagnostic); + } + + return Task.CompletedTask; + } + + private static async Task AddObsoleteAttributeAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var obsoleteAttributeSymbol = semanticModel.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemObsoleteAttribute); + if (obsoleteAttributeSymbol is null) + { + return document; + } + + var constructor = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(constructor, DeclarationKind.Constructor); + if (declaration is null) + { + return document; + } + + var obsoleteAttribute = generator.Attribute( + generator.TypeExpression(obsoleteAttributeSymbol).WithAddImportsAnnotation(), + new[] + { + GenerateDescriptionArgument(generator, semanticModel), + GenerateErrorArgument(generator, allowNamedArgument: document.Project.Language == LanguageNames.CSharp), + }); + + var newDeclaration = generator.AddAttributes(declaration, obsoleteAttribute); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + + private static async Task AddDescriptionAndErrorAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var obsoleteAttributeApplication = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(obsoleteAttributeApplication, DeclarationKind.Attribute); + if (declaration is null) + { + return document; + } + + var descriptionArgument = GenerateDescriptionArgument(generator, semanticModel); + var errorArgument = GenerateErrorArgument(generator, allowNamedArgument: document.Project.Language == LanguageNames.CSharp); + var newDeclaration = generator.AddAttributeArguments(declaration, new[] { descriptionArgument, errorArgument }); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + + private static async Task UpdateDescriptionAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var obsoleteAttributeApplication = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(obsoleteAttributeApplication, DeclarationKind.Attribute); + if (declaration is null) + { + return document; + } + + var argumentToReplace = generator.GetAttributeArguments(declaration).ElementAtOrDefault(0); + if (argumentToReplace is null) + { + return document; + } + + var descriptionArgument = GenerateDescriptionArgument(generator, semanticModel); + return document.WithSyntaxRoot(root.ReplaceNode(argumentToReplace, descriptionArgument)); + } + + private static async Task AddErrorAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var obsoleteAttributeApplication = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(obsoleteAttributeApplication, DeclarationKind.Attribute); + if (declaration is null) + { + return document; + } + + var errorArgument = GenerateErrorArgument(generator, allowNamedArgument: document.Project.Language == LanguageNames.CSharp); + var newDeclaration = generator.AddAttributeArguments(declaration, new[] { errorArgument }); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + + private static async Task SetErrorToTrueAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + var obsoleteAttributeApplication = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(obsoleteAttributeApplication, DeclarationKind.Attribute); + if (declaration is null) + { + return document; + } + + var argumentToReplace = generator.GetAttributeArguments(declaration).ElementAtOrDefault(1); + if (argumentToReplace is null) + { + return document; + } + + var errorArgument = GenerateErrorArgument(generator, allowNamedArgument: document.Project.Language == LanguageNames.CSharp); + return document.WithSyntaxRoot(root.ReplaceNode(argumentToReplace, errorArgument)); + } + + private static SyntaxNode GenerateDescriptionArgument(SyntaxGenerator generator, SemanticModel semanticModel) + { + SyntaxNode attributeArgument; + if (semanticModel.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisHostMefMefConstruction, out var mefConstructionType) && + mefConstructionType.GetMembers("ImportingConstructorMessage").OfType().Any()) + { + attributeArgument = generator.MemberAccessExpression( + generator.TypeExpression(mefConstructionType).WithAddImportsAnnotation(), + generator.IdentifierName("ImportingConstructorMessage")); + } + else + { + attributeArgument = generator.LiteralExpression("This exported object must be obtained through the MEF export provider."); + } + + return generator.AttributeArgument(attributeArgument); + } + + private static SyntaxNode GenerateErrorArgument(SyntaxGenerator generator, bool allowNamedArgument) + { + if (allowNamedArgument) + { + var argument = generator.Argument("error", RefKind.None, generator.TrueLiteralExpression()); + var attribute = generator.Attribute("ignored", argument); + return generator.GetAttributeArguments(attribute)[0]; + } + else + { + return generator.AttributeArgument(generator.TrueLiteralExpression()); + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/NamedTypeFullNameNotNullSuppressor.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/NamedTypeFullNameNotNullSuppressor.cs new file mode 100644 index 0000000000000..b613ab324c8ca --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/NamedTypeFullNameNotNullSuppressor.cs @@ -0,0 +1,68 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class NamedTypeFullNameNotNullSuppressor : DiagnosticSuppressor + { + private const string Id = RoslynDiagnosticIds.NamedTypeFullNameNotNullSuppressionRuleId; + + // CS8600: Converting null literal or possible null value to non-nullable type + private const string CS8600 = nameof(CS8600); + + // CS8603: Possible null reference return + private const string CS8603 = nameof(CS8603); + + // CS8604: Possible null reference argument for parameter 'name' in 'method' + private const string CS8604 = nameof(CS8604); + + private static readonly LocalizableString s_localizableJustification = CreateLocalizableResourceString(nameof(NamedTypeFullNameNotNullSuppressorJustification)); + + internal static readonly SuppressionDescriptor CS8600Rule = new(Id, CS8600, s_localizableJustification); + internal static readonly SuppressionDescriptor CS8603Rule = new(Id, CS8603, s_localizableJustification); + internal static readonly SuppressionDescriptor CS8604Rule = new(Id, CS8604, s_localizableJustification); + + public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(CS8600Rule, CS8603Rule, CS8604Rule); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + foreach (var diagnostic in context.ReportedDiagnostics) + { + if (diagnostic.Location.SourceTree is not { } tree) + { + continue; + } + + var root = tree.GetRoot(context.CancellationToken); + var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + var semanticModel = context.GetSemanticModel(tree); + var operation = semanticModel.GetOperation(node, context.CancellationToken); + if (operation is IPropertyReferenceOperation { Property.Name: nameof(Type.FullName), Instance: ITypeOfOperation { } }) + { + context.ReportSuppression(Suppression.Create(GetDescriptor(diagnostic), diagnostic)); + } + } + } + + private static SuppressionDescriptor GetDescriptor(Diagnostic diagnostic) + { + return diagnostic.Id switch + { + CS8600 => CS8600Rule, + CS8603 => CS8603Rule, + CS8604 => CS8604Rule, + _ => throw new NotSupportedException(), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs new file mode 100644 index 0000000000000..ba2a682a7b2c2 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.cs @@ -0,0 +1,68 @@ +// 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.Composition; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; + +namespace Roslyn.Diagnostics.Analyzers +{ + /// + /// RS0023: Parts exported with MEFv2 must be marked as Shared + /// + public abstract class PartsExportedWithMEFv2MustBeMarkedAsSharedFixer : CodeFixProvider + where TTypeSyntax : SyntaxNode + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(RoslynDiagnosticIds.MissingSharedAttributeRuleId); + + public sealed override FixAllProvider GetFixAllProvider() + { + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var document = context.Document; + var cancellationToken = context.CancellationToken; + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + foreach (var diagnostic in context.Diagnostics) + { + // The following doesn't seem to work correctly due to a possible Roslyn bug. + //var symbol = semanticModel!.GetEnclosingSymbol(diagnostic.Location.SourceSpan.Start); + //if (symbol.DeclaringSyntaxReferences.IsEmpty) + //{ + // continue; + //} + //var declaration = await symbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); + + if (TryGetDeclaration(root!.FindNode(diagnostic.Location.SourceSpan), out var declaration)) + { + context.RegisterCodeFix( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.AddSharedAttribute, + _ => AddSharedAttributeAsync(document, root!, declaration), + equivalenceKey: nameof(RoslynDiagnosticsAnalyzersResources.AddSharedAttribute)), + diagnostic); + } + } + } + + private static bool TryGetDeclaration(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? declaration) + => (declaration = node.FirstAncestorOrSelf()) is not null; + + private static Task AddSharedAttributeAsync(Document document, SyntaxNode root, SyntaxNode declaration) + { + var generator = SyntaxGenerator.GetGenerator(document); + var newDeclaration = generator.AddAttributes(declaration, generator.Attribute(typeof(SharedAttribute).FullName)); + return Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration))); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.cs new file mode 100644 index 0000000000000..5c741fe7e9316 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/PartsExportedWithMEFv2MustBeMarkedAsShared.cs @@ -0,0 +1,79 @@ +// 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 warnings + +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0023: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.MissingSharedAttributeRuleId, + CreateLocalizableResourceString(nameof(PartsExportedWithMEFv2MustBeMarkedAsSharedTitle)), + CreateLocalizableResourceString(nameof(PartsExportedWithMEFv2MustBeMarkedAsSharedMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(PartsExportedWithMEFv2MustBeMarkedAsSharedDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var exportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionExportAttribute); + var attributeUsageAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemAttributeUsageAttribute); + + if (exportAttribute == null) + { + // We don't need to check assemblies unless they're referencing both MEFv2, so we're done + return; + } + + compilationContext.RegisterSymbolAction(symbolContext => + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + var namedTypeAttributes = namedType.GetApplicableAttributes(attributeUsageAttribute); + var exportAttributes = namedType.GetApplicableExportAttributes(exportAttributeV1: null, exportAttribute, inheritedExportAttribute: null); + + var exportAttributeApplication = exportAttributes.FirstOrDefault(); + + if (exportAttributeApplication != null && + !namedTypeAttributes.Any(ad => ad.AttributeClass.Name == "SharedAttribute" && + ad.AttributeClass.ContainingNamespace.Equals(exportAttribute.ContainingNamespace))) + { + if (exportAttributeApplication.ApplicationSyntaxReference == null) + { + symbolContext.ReportDiagnostic(symbolContext.Symbol.CreateDiagnostic(Rule, namedType.Name)); + } + else + { + // '{0}' is exported with MEFv2 and hence must be marked as Shared + symbolContext.ReportDiagnostic(exportAttributeApplication.ApplicationSyntaxReference.CreateDiagnostic(Rule, symbolContext.CancellationToken, namedType.Name)); + } + } + }, SymbolKind.NamedType); + }); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RefKindEx.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RefKindEx.cs new file mode 100644 index 0000000000000..6b2403bbc1176 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RefKindEx.cs @@ -0,0 +1,13 @@ +// 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; + +namespace Roslyn.Diagnostics.Analyzers +{ + internal static class RefKindEx + { + public const RefKind RefReadOnlyParameter = (RefKind)4; + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RelaxTestNamingSuppressor.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RelaxTestNamingSuppressor.cs new file mode 100644 index 0000000000000..24d5d7ca55f3f --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RelaxTestNamingSuppressor.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; +using System.Collections.Immutable; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class RelaxTestNamingSuppressor : DiagnosticSuppressor + { + private const string Id = RoslynDiagnosticIds.RelaxTestNamingSuppressionRuleId; + + // VSTHRD200: Use Async suffix for async methods + // https://github.com/microsoft/vs-threading/blob/main/doc/analyzers/VSTHRD200.md + private const string SuppressedDiagnosticId = "VSTHRD200"; + + internal static readonly SuppressionDescriptor Rule = + new(Id, SuppressedDiagnosticId, CreateLocalizableResourceString(nameof(RelaxTestNamingSuppressorJustification))); + + public override ImmutableArray SupportedSuppressions { get; } = ImmutableArray.Create(Rule); + + public override void ReportSuppressions(SuppressionAnalysisContext context) + { + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.XunitFactAttribute, out var factAttribute); + context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.BenchmarkDotNetAttributesBenchmarkAttribute, out var benchmarkAttribute); + if (factAttribute is null && benchmarkAttribute is null) + { + return; + } + + var knownTestAttributes = new ConcurrentDictionary(); + + foreach (var diagnostic in context.ReportedDiagnostics) + { + // The diagnostic is reported on the test method + if (diagnostic.Location.SourceTree is not { } tree) + { + continue; + } + + var root = tree.GetRoot(context.CancellationToken); + var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true); + + var semanticModel = context.GetSemanticModel(tree); + var declaredSymbol = semanticModel.GetDeclaredSymbol(node, context.CancellationToken); + if (declaredSymbol is IMethodSymbol method + && method.IsBenchmarkOrXUnitTestMethod(knownTestAttributes, benchmarkAttribute, factAttribute)) + { + context.ReportSuppression(Suppression.Create(Rule, diagnostic)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/Roslyn.Diagnostics.Analyzers.csproj b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/Roslyn.Diagnostics.Analyzers.csproj new file mode 100644 index 0000000000000..8dc9a7acd3efc --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/Roslyn.Diagnostics.Analyzers.csproj @@ -0,0 +1,27 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisForRoslynDiagnosticsAnalyzersVersion) + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs new file mode 100644 index 0000000000000..d57c263fab4a0 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticIds.cs @@ -0,0 +1,79 @@ +// 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 Roslyn.Diagnostics.Analyzers +{ + internal static class RoslynDiagnosticIds + { + public const string UseEmptyEnumerableRuleId = "RS0001"; + public const string UseSingletonEnumerableRuleId = "RS0002"; + // public const string DirectlyAwaitingTaskAnalyzerRuleId = "RS0003"; // Now CA2007 => Microsoft.ApiDesignGuidelines.Analyzers.DoNotDirectlyAwaitATaskAnalyzer + public const string UseSiteDiagnosticsCheckerRuleId = "RS0004"; + //public const string DoNotUseCodeActionCreateRuleId = "RS0005"; // Removed (see https://github.com/dotnet/roslyn-analyzers/issues/5947) + public const string MixedVersionsOfMefAttributesRuleId = "RS0006"; + // public const string UseArrayEmptyRuleId = "RS0007"; // Now CA1825 => System.Runtime.Analyzers.AvoidZeroLengthArrayAllocationsAnalyzer + // public const string ImplementIEquatableRuleId = "RS0008"; // Now CA1067 => Microsoft.ApiDesignGuidelines.Analyzers.EquatableAnalyzer + // public const string OverrideObjectEqualsRuleId = "RS0009"; // Now CA1815 => Microsoft.ApiDesignGuidelines.Analyzers.OverrideEqualsAndOperatorEqualsOnValueTypesAnalyzer + // public const string DoNotUseVerbatimCrefsRuleId = "RS0010"; // Now RS0010 => XmlDocumentationComments.Analyzers.AvoidUsingCrefTagsWithAPrefixAnalyzer + // public const string CancellationTokenMustBeLastRuleId = "RS0011"; // Now CA1068 => Microsoft.ApiDesignGuidelines.Analyzers.CancellationTokenParametersMustComeLastAnalyzer + // public const string DoNotCallToImmutableArrayRuleId = "RS0012"; // Now CA2009 => System.Collections.Immutable.Analyzers.DoNotCallToImmutableCollectionOnAnImmutableCollectionValueAnalyzer + //public const string DoNotAccessDiagnosticDescriptorRuleId = "RS0013"; // Removed (see https://github.com/dotnet/roslyn-analyzers/issues/3560) + // public const string DoNotCallLinqOnIndexable = "RS0014"; // Now RS0014 => System.Runtime.Analyzers.DoNotUseEnumerableMethodsOnIndexableCollectionsInsteadUseTheCollectionDirectlyAnalyzer + // public const string ConsumePreserveSigRuleId = "RS0015"; // Now CA2010 => System.Runtime.InteropServices.Analyzers.AlwaysConsumeTheValueReturnedByMethodsMarkedWithPreserveSigAttributeAnalyzer + public const string DeclarePublicApiRuleId = "RS0016"; + public const string RemoveDeletedPublicApiRuleId = "RS0017"; + // public const string DoNotCreateTasksWithoutTaskSchedulerRuleId = "RS0018"; // Now CA2008 => System.Threading.Tasks.Analyzers.DoNotCreateTasksWithoutPassingATaskSchedulerAnalyzer + public const string SymbolDeclaredEventRuleId = "RS0019"; + // public const string DeadCodeRuleId = "RS0020"; // Now ??? + // public const string DeadCodeTriggerRuleId = "RS0021"; // Now ??? + public const string ExposedNoninstantiableTypeRuleIdPublic = "RS0022"; + public const string MissingSharedAttributeRuleId = "RS0023"; + public const string PublicApiFilesInvalid = "RS0024"; + public const string DuplicatedSymbolInPublicApiFiles = "RS0025"; + public const string AvoidMultipleOverloadsWithOptionalParametersPublic = "RS0026"; + public const string OverloadWithOptionalParametersShouldHaveMostParametersPublic = "RS0027"; + public const string RoslynAnalyzerMustUseIdInSpecifiedRangeRuleId = "RS0028"; + public const string RoslynAnalyzerMustUseCategoriesFromSpecifiedRangeRuleId = "RS0029"; + public const string SymbolIsBannedRuleId = "RS0030"; + public const string DuplicateBannedSymbolRuleId = "RS0031"; + public const string TestExportsShouldNotBeDiscoverableRuleId = "RS0032"; + public const string ImportingConstructorShouldBeObsoleteRuleId = "RS0033"; + public const string ExportedPartsShouldHaveImportingConstructorRuleId = "RS0034"; + public const string RestrictedInternalsVisibleToRuleId = "RS0035"; + public const string AnnotatePublicApiRuleId = "RS0036"; + public const string ShouldAnnotatePublicApiFilesRuleId = "RS0037"; + public const string PreferNullLiteralRuleId = "RS0038"; + public const string RelaxTestNamingSuppressionRuleId = "RS0039"; + public const string DefaultableTypeShouldHaveDefaultableFieldsRuleId = "RS0040"; + public const string ObliviousPublicApiRuleId = "RS0041"; + public const string DoNotCopyValueRuleId = "RS0042"; + public const string DoNotCallGetTestAccessorRuleId = "RS0043"; + // public const string CreateTestAccessorRuleId = "RS0044"; // Now converted to a refactoring + // public const string ExposeMemberForTestingRuleId = "RS0045"; // Now converted to a refactoring + public const string AvoidOptSuffixForNullableEnableCodeRuleId = "RS0046"; + public const string NamedTypeFullNameNotNullSuppressionRuleId = "RS0047"; + public const string PublicApiFileMissing = "RS0048"; + public const string TemporaryArrayAsRefRuleId = "RS0049"; + + public const string RemovedApiIsNotActuallyRemovedRuleId = "RS0050"; + + public const string DeclareInternalApiRuleId = "RS0051"; + public const string RemoveDeletedInternalApiRuleId = "RS0052"; + public const string InternalApiFilesInvalid = "RS0053"; + public const string DuplicatedSymbolInInternalApiFiles = "RS0054"; + public const string AnnotateInternalApiRuleId = "RS0055"; + public const string ShouldAnnotateInternalApiFilesRuleId = "RS0056"; + public const string ObliviousInternalApiRuleId = "RS0057"; + public const string InternalApiFileMissing = "RS0058"; + public const string AvoidMultipleOverloadsWithOptionalParametersInternal = "RS0059"; + public const string OverloadWithOptionalParametersShouldHaveMostParametersInternal = "RS0060"; + public const string ExposedNoninstantiableTypeRuleIdInternal = "RS0061"; + public const string DoNotCapturePrimaryConstructorParametersRuleId = "RS0062"; + public const string DoNotUseInterpolatedStringsWithDebugAssertRuleId = "RS0063"; + + //public const string WrapStatementsRuleId = "RS0100"; // Now ported to dotnet/roslyn https://github.com/dotnet/roslyn/pull/50358 + //public const string BlankLinesRuleId = "RS0101"; // Now ported to dotnet/roslyn https://github.com/dotnet/roslyn/pull/50358 + //public const string BracePlacementRuleId = "RS0102"; // Now ported to dotnet/roslyn https://github.com/dotnet/roslyn/pull/50358 + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.cs new file mode 100644 index 0000000000000..f768dbdb0baa3 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.cs @@ -0,0 +1,20 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Roslyn.Diagnostics.Analyzers +{ + internal partial class RoslynDiagnosticsAnalyzersResources + { + private static readonly Type s_resourcesType = typeof(RoslynDiagnosticsAnalyzersResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx new file mode 100644 index 0000000000000..abc385b50f627 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/RoslynDiagnosticsAnalyzersResources.resx @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Use 'SpecializedCollections.EmptyEnumerable()' + + + #N/A + + + Use 'SpecializedCollections.EmptyEnumerable()' + + + Use 'SpecializedCollections.SingletonEnumerable()' + + + #N/A + + + Use 'SpecializedCollections.SingletonEnumerable()' + + + Invoke the correct property to ensure correct use site diagnostics + + + #N/A + + + Invoke the correct property to ensure correct use site diagnostics + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + + + #N/A + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + + + #N/A + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + + + 'SymbolDeclaredEvent' must be generated for source symbols + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + + + 'SymbolDeclaredEvent' must be generated for source symbols + + + Do not mix attributes from different versions of MEF + + + Do not mix attributes from different versions of MEF. + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + + + Importing constructor should be marked with 'ObsoleteAttribute'. + + + Importing constructor should be marked with 'ObsoleteAttribute' + + + Importing constructor should be marked with 'ObsoleteAttribute' + + + Test exports should not be discoverable. + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + + + Test exports should not be discoverable + + + Explicitly define the importing constructor + + + Add 'ImportingConstructor' attribute + + + Make constructor public + + + Set 'ObsoleteAttribute.Error' to 'true' + + + Use correct 'ObsoleteAttribute' message. + + + Add 'ObsoleteAttribute' + + + Configure 'ObsoleteAttribute' for importing constructor. + + + Set 'ObsoleteAttribute.Error' to 'true' + + + Apply 'PartNotDiscoverableAttribute' + + + Fix numbered comments + + + Use 'null' instead of 'default' for nullable types. + + + Use 'null' instead of 'default' for nullable types + + + Prefer null literal + + + Use 'null' instead of 'default' + + + Asynchronous test methods do not require the 'Async' suffix + + + Defaultable types should have defaultable fields. + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + + + Defaultable types should have defaultable fields + + + Do not copy value. + + + Do not copy value + + + Do not copy value + + + Do not box non-copyable value types. + + + Do not box non-copyable type '{0}' + + + Unsupported use of non-copyable type. + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + + + Do not unbox non-copyable value types. + + + Do not unbox non-copyable type '{0}' + + + Avoid nullable wrapper. + + + Do not wrap non-copyable type '{0}' in '{1}' operation + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + + + Do not call 'GetTestAccessor()' from production code + + + Do not call 'GetTestAccessor()' + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + + + Create test accessor + + + Create test accessor + + + Expose member for testing. + + + Expose member for testing + + + Expose member for testing + + + Avoid the 'Opt' suffix in a nullable-enabled code. + + + Avoid the 'Opt' suffix in a nullable-enabled code + + + Avoid the 'Opt' suffix + + + Place statement on following line + + + Remove the 'Opt' suffix + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Cannot assign a value from a reference to a non-copyable type. + + + Cannot assign a value from a reference to non-copyable type '{0}' + + + Cannot return a value from a reference to a non-copyable type. + + + Cannot return a value from a reference to non-copyable type '{0}' + + + Apply trait to containing type + + + Add 'Shared' attribute + {Locked="Shared"} + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + + + Copyable field '{1}' cannot have non-copyable type '{0}' + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + + + Auto-property '{1}' cannot have non-copyable type '{0}' + + + Run iterations + + + Do not capture primary constructor parameters + + + Primary constructor parameter '{0}' should not be implicitly captured + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + + + Do not use interpolated strings with 'Debug.Assert' + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + Use 'RoslynDebug.Assert'. + + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SpecializedEnumerableCreationAnalyzer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SpecializedEnumerableCreationAnalyzer.cs new file mode 100644 index 0000000000000..64eaff054c6d7 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SpecializedEnumerableCreationAnalyzer.cs @@ -0,0 +1,164 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + // TODO: This should be updated to follow the flow of array creation expressions + // that are eventually converted to and leave a given method as IEnumerable once we have + // the ability to do more thorough data-flow analysis in diagnostic analyzers. + /// + /// RS0001: + /// RS0002: + /// + public abstract class SpecializedEnumerableCreationAnalyzer : DiagnosticAnalyzer + { + internal const string SpecializedCollectionsMetadataName = "Roslyn.Utilities.SpecializedCollections"; + internal const string LinqEnumerableMetadataName = "System.Linq.Enumerable"; + internal const string EmptyMethodName = "Empty"; + + internal static readonly DiagnosticDescriptor UseEmptyEnumerableRule = new( + RoslynDiagnosticIds.UseEmptyEnumerableRuleId, + CreateLocalizableResourceString(nameof(UseSpecializedCollectionsEmptyEnumerableTitle)), + CreateLocalizableResourceString(nameof(UseSpecializedCollectionsEmptyEnumerableMessage)), + DiagnosticCategory.RoslynDiagnosticsPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + internal static readonly DiagnosticDescriptor UseSingletonEnumerableRule = new( + RoslynDiagnosticIds.UseSingletonEnumerableRuleId, + CreateLocalizableResourceString(nameof(UseSpecializedCollectionsSingletonEnumerableTitle)), + CreateLocalizableResourceString(nameof(UseSpecializedCollectionsSingletonEnumerableMessage)), + DiagnosticCategory.RoslynDiagnosticsPerformance, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(UseEmptyEnumerableRule, UseSingletonEnumerableRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + INamedTypeSymbol? specializedCollectionsSymbol = context.Compilation.GetOrCreateTypeByMetadataName(SpecializedCollectionsMetadataName); + if (specializedCollectionsSymbol == null) + { + // TODO: In the future, we may want to run this analyzer even if the SpecializedCollections + // type cannot be found in this compilation. In some cases, we may want to add a reference + // to SpecializedCollections as a linked file or an assembly that contains it. With this + // check, we will not warn where SpecializedCollections is not yet referenced. + return; + } + + INamedTypeSymbol? genericEnumerableSymbol = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIEnumerable1); + if (genericEnumerableSymbol == null) + { + return; + } + + INamedTypeSymbol? linqEnumerableSymbol = context.Compilation.GetOrCreateTypeByMetadataName(LinqEnumerableMetadataName); + if (linqEnumerableSymbol == null) + { + return; + } + + if (linqEnumerableSymbol.GetMembers(EmptyMethodName).FirstOrDefault() is not IMethodSymbol genericEmptyEnumerableSymbol || + genericEmptyEnumerableSymbol.Arity != 1 || + !genericEmptyEnumerableSymbol.Parameters.IsEmpty) + { + return; + } + + GetCodeBlockStartedAnalyzer(context, genericEnumerableSymbol, genericEmptyEnumerableSymbol); + }); + } + + protected abstract void GetCodeBlockStartedAnalyzer(CompilationStartAnalysisContext context, INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol); + + protected abstract class AbstractCodeBlockStartedAnalyzer where TLanguageKindEnum : struct + { + private readonly INamedTypeSymbol _genericEnumerableSymbol; + private readonly IMethodSymbol _genericEmptyEnumerableSymbol; + + protected AbstractCodeBlockStartedAnalyzer(INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + { + _genericEnumerableSymbol = genericEnumerableSymbol; + _genericEmptyEnumerableSymbol = genericEmptyEnumerableSymbol; + } + + protected abstract void GetSyntaxAnalyzer(CodeBlockStartAnalysisContext context, INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol); + + public void Initialize(CodeBlockStartAnalysisContext context) + { + if (context.OwningSymbol is IMethodSymbol methodSymbol && + Equals(methodSymbol.ReturnType.OriginalDefinition, _genericEnumerableSymbol)) + { + GetSyntaxAnalyzer(context, _genericEnumerableSymbol, _genericEmptyEnumerableSymbol); + } + } + } + + protected abstract class AbstractSyntaxAnalyzer + { + protected INamedTypeSymbol GenericEnumerableSymbol { get; } + private readonly IMethodSymbol _genericEmptyEnumerableSymbol; + + protected AbstractSyntaxAnalyzer(INamedTypeSymbol genericEnumerableSymbol, IMethodSymbol genericEmptyEnumerableSymbol) + { + this.GenericEnumerableSymbol = genericEnumerableSymbol; + _genericEmptyEnumerableSymbol = genericEmptyEnumerableSymbol; + } + + public static ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(UseEmptyEnumerableRule, UseSingletonEnumerableRule); + + protected bool ShouldAnalyzeArrayCreationExpression(SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken) + { + TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken); + + return typeInfo.ConvertedType != null && + Equals(typeInfo.ConvertedType.OriginalDefinition, GenericEnumerableSymbol) && + typeInfo.Type is IArrayTypeSymbol arrayType && + arrayType.Rank == 1; + } + + protected void AnalyzeMemberAccessName(SyntaxNode name, SemanticModel semanticModel, Action addDiagnostic, CancellationToken cancellationToken) + { + if (semanticModel.GetSymbolInfo(name, cancellationToken).Symbol is IMethodSymbol methodSymbol && + Equals(methodSymbol.OriginalDefinition, _genericEmptyEnumerableSymbol)) + { + addDiagnostic(name.Parent.CreateDiagnostic(UseEmptyEnumerableRule)); + } + } + + protected static void AnalyzeArrayLength(int length, SyntaxNode arrayCreationExpression, Action addDiagnostic) + { + if (length == 0) + { + addDiagnostic(arrayCreationExpression.CreateDiagnostic(UseEmptyEnumerableRule)); + } + else if (length == 1) + { + addDiagnostic(arrayCreationExpression.CreateDiagnostic(UseSingletonEnumerableRule)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs new file mode 100644 index 0000000000000..26c3e663e7b7d --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/SymbolDeclaredEventMustBeGeneratedForSourceSymbols.cs @@ -0,0 +1,190 @@ +// 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 warnings + +using System; +using System.Collections.Concurrent; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0019: + /// + /// + public abstract class SymbolDeclaredEventAnalyzer : DiagnosticAnalyzer + where TSyntaxKind : struct + { + private static readonly string s_fullNameOfSymbol = typeof(ISymbol).FullName; + + internal static readonly DiagnosticDescriptor SymbolDeclaredEventRule = new( + RoslynDiagnosticIds.SymbolDeclaredEventRuleId, + CreateLocalizableResourceString(nameof(SymbolDeclaredEventMustBeGeneratedForSourceSymbolsTitle)), + CreateLocalizableResourceString(nameof(SymbolDeclaredEventMustBeGeneratedForSourceSymbolsMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Error, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(SymbolDeclaredEventMustBeGeneratedForSourceSymbolsDescription)), + customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry); + + public sealed override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(SymbolDeclaredEventRule); + + public sealed override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + + // We need to analyze generated code, but don't intend to report diagnostics on generated code. + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterCompilationStartAction(compilationContext => + { + INamedTypeSymbol? symbolType = compilationContext.Compilation.GetOrCreateTypeByMetadataName(s_fullNameOfSymbol); + if (symbolType != null) + { + CompilationAnalyzer? compilationAnalyzer = GetCompilationAnalyzer(compilationContext.Compilation, symbolType); + if (compilationAnalyzer != null) + { + compilationContext.RegisterSyntaxNodeAction(compilationAnalyzer.AnalyzeNode, InvocationExpressionSyntaxKind); + compilationContext.RegisterSymbolAction(compilationAnalyzer.AnalyzeNamedType, SymbolKind.NamedType); + compilationContext.RegisterCompilationEndAction(compilationAnalyzer.AnalyzeCompilationEnd); + } + } + }); + } + + protected abstract TSyntaxKind InvocationExpressionSyntaxKind { get; } + protected abstract CompilationAnalyzer? GetCompilationAnalyzer(Compilation compilation, INamedTypeSymbol symbolType); + + protected abstract class CompilationAnalyzer + { + private readonly INamedTypeSymbol _symbolType; + private readonly INamedTypeSymbol _compilationType; + private readonly ConcurrentDictionary _sourceSymbolsToCheck = new(); + private readonly ConcurrentDictionary _typesWithSymbolDeclaredEventInvoked = new(); + private readonly bool _hasMemberNamedSymbolDeclaredEvent; + + private const string SymbolDeclaredEventName = "SymbolDeclaredEvent"; + + protected CompilationAnalyzer(INamedTypeSymbol symbolType, INamedTypeSymbol compilationType) + { + _symbolType = symbolType; + _compilationType = compilationType; + + ISymbol symbolDeclaredEvent = compilationType.GetMembers(SymbolDeclaredEventName).FirstOrDefault(); + if (symbolDeclaredEvent == null) + { + // Likely indicates compilation with errors, where we could not find the required symbol. + _hasMemberNamedSymbolDeclaredEvent = false; + } + else + { + _hasMemberNamedSymbolDeclaredEvent = true; + + // If the below assert fire then probably the definition of "SymbolDeclaredEvent" has changed and we need to fix this analyzer. + Debug.Assert(symbolDeclaredEvent.GetParameters().HasExactly(1)); + } + } + + protected abstract SyntaxNode? GetFirstArgumentOfInvocation(SyntaxNode invocation); + protected abstract ImmutableHashSet SymbolTypesWithExpectedSymbolDeclaredEvent { get; } + + internal void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + ISymbol invocationSymbol = context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken).Symbol; + if (invocationSymbol != null && + invocationSymbol.Kind == SymbolKind.Method) + { + AnalyzeMethodInvocation((IMethodSymbol)invocationSymbol, context); + } + } + + internal virtual void AnalyzeMethodInvocation(IMethodSymbol invocationSymbol, SyntaxNodeAnalysisContext context) + { + if (invocationSymbol.Name.Equals(SymbolDeclaredEventName, StringComparison.Ordinal) && + _compilationType.Equals(invocationSymbol.ContainingType)) + { + SyntaxNode? argument = GetFirstArgumentOfInvocation(context.Node); + AnalyzeSymbolDeclaredEventInvocation(argument, context); + } + } + + protected bool AnalyzeSymbolDeclaredEventInvocation(SyntaxNode? argument, SyntaxNodeAnalysisContext context) + { + if (argument != null) + { + ITypeSymbol argumentType = context.SemanticModel.GetTypeInfo(argument, context.CancellationToken).Type; + return AnalyzeSymbolDeclaredEventInvocation(argumentType); + } + + return false; + } + + private bool AnalyzeSymbolDeclaredEventInvocation(ISymbol type) + { + if (type != null && + type.Kind == SymbolKind.NamedType && + !type.Name.Equals("Symbol", StringComparison.Ordinal)) + { + var namedType = (INamedTypeSymbol)type; + if (namedType.AllInterfaces.Contains(_symbolType)) + { + _typesWithSymbolDeclaredEventInvoked.TryAdd(namedType, default); + return true; + } + } + + return false; + } + + internal void AnalyzeNamedType(SymbolAnalysisContext context) + { + var namedType = (INamedTypeSymbol)context.Symbol; + if (!namedType.IsAbstract && + namedType.Name.StartsWith("Source", StringComparison.Ordinal) && + !namedType.Name.Contains("Backing") && + namedType.AllInterfaces.Contains(_symbolType) && + namedType.GetBaseTypesAndThis().Any(b => SymbolTypesWithExpectedSymbolDeclaredEvent.Contains(b.Name, StringComparer.Ordinal))) + { + _sourceSymbolsToCheck.TryAdd(namedType, default); + } + } + + internal void AnalyzeCompilationEnd(CompilationAnalysisContext context) + { + if (!_hasMemberNamedSymbolDeclaredEvent) + { + return; + } + + foreach ((INamedTypeSymbol sourceSymbol, _) in _sourceSymbolsToCheck) + { + var found = false; + foreach (INamedTypeSymbol type in sourceSymbol.GetBaseTypesAndThis()) + { + if (_typesWithSymbolDeclaredEventInvoked.ContainsKey(type)) + { + found = true; + break; + } + } + + if (!found) + { + Diagnostic diagnostic = Diagnostic.Create(SymbolDeclaredEventRule, sourceSymbol.Locations[0], sourceSymbol.Name, _compilationType.Name, SymbolDeclaredEventName); + context.ReportDiagnostic(diagnostic); + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TemporaryArrayAsRefAnalyzer.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TemporaryArrayAsRefAnalyzer.cs new file mode 100644 index 0000000000000..f48bb55e5c5dc --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TemporaryArrayAsRefAnalyzer.cs @@ -0,0 +1,77 @@ +// 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.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0049: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class TemporaryArrayAsRefAnalyzer : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.TemporaryArrayAsRefRuleId, + CreateLocalizableResourceString(nameof(TemporaryArrayAsRefTitle)), + CreateLocalizableResourceString(nameof(TemporaryArrayAsRefMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: CreateLocalizableResourceString(nameof(TemporaryArrayAsRefDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction(context => + { + var temporaryArrayExtensions = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisSharedCollectionsTemporaryArrayExtensions); + if (temporaryArrayExtensions is null) + return; + + var temporaryArrayAsRef = (IMethodSymbol?)temporaryArrayExtensions.GetMembers("AsRef").SingleOrDefault(); + if (temporaryArrayAsRef is null) + return; + + context.RegisterOperationAction(context => AnalyzeInvocation(context, temporaryArrayAsRef), OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocation(OperationAnalysisContext context, IMethodSymbol temporaryArrayAsRef) + { + var invocation = (IInvocationOperation)context.Operation; + var targetMethod = invocation.TargetMethod.ReducedFrom ?? invocation.TargetMethod; + if (!Equals(targetMethod.OriginalDefinition, temporaryArrayAsRef)) + return; + + var instance = invocation.Instance ?? invocation.Arguments.FirstOrDefault()?.Value; + if (instance is not ILocalReferenceOperation localReference) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(Rule)); + return; + } + + var declaration = invocation.SemanticModel!.GetOperation(localReference.Local.DeclaringSyntaxReferences.Single().GetSyntax(context.CancellationToken), context.CancellationToken); + if (declaration is not { Parent: IVariableDeclarationOperation { Parent: IVariableDeclarationGroupOperation { Parent: IUsingOperation or IUsingDeclarationOperation } } }) + { + context.ReportDiagnostic(invocation.CreateDiagnostic(Rule)); + return; + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestAccessorHelper.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestAccessorHelper.cs new file mode 100644 index 0000000000000..19a4d166d5068 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestAccessorHelper.cs @@ -0,0 +1,12 @@ +// 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 Roslyn.Diagnostics.Analyzers +{ + internal static class TestAccessorHelper + { + internal static string GetTestAccessorMethodName => "GetTestAccessor"; + internal static string TestAccessorTypeName => "TestAccessor"; + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverable.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverable.cs new file mode 100644 index 0000000000000..b5a76bbda1813 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverable.cs @@ -0,0 +1,101 @@ +// 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 warnings + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Roslyn.Diagnostics.Analyzers +{ + using static RoslynDiagnosticsAnalyzersResources; + + /// + /// RS0032: + /// MEF-exported types defined in test assemblies should be marked with + /// to avoid polluting the container(s) created for testing. These parts should be explicitly added to the container + /// when required for specific tests. + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class TestExportsShouldNotBeDiscoverable : DiagnosticAnalyzer + { + internal static readonly DiagnosticDescriptor Rule = new( + RoslynDiagnosticIds.TestExportsShouldNotBeDiscoverableRuleId, + CreateLocalizableResourceString(nameof(TestExportsShouldNotBeDiscoverableTitle)), + CreateLocalizableResourceString(nameof(TestExportsShouldNotBeDiscoverableMessage)), + DiagnosticCategory.RoslynDiagnosticsReliability, + DiagnosticSeverity.Warning, + isEnabledByDefault: false, + description: CreateLocalizableResourceString(nameof(TestExportsShouldNotBeDiscoverableDescription)), + helpLinkUri: null, + customTags: WellKnownDiagnosticTagsExtensions.Telemetry); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + var exportAttributeV1 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionExportAttribute); + var exportAttributeV2 = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCompositionExportAttribute); + var inheritedExportAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemComponentModelCompositionInheritedExportAttribute); + var attributeUsageAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemAttributeUsageAttribute); + + if (exportAttributeV1 is null && exportAttributeV2 is null) + { + // We don't need to check assemblies unless they're referencing MEF, so we're done + return; + } + + compilationContext.RegisterSymbolAction(symbolContext => + { + var namedType = (INamedTypeSymbol)symbolContext.Symbol; + var exportAttributes = namedType.GetApplicableExportAttributes(exportAttributeV1, exportAttributeV2, inheritedExportAttribute); + var namedTypeAttributes = namedType.GetApplicableAttributes(attributeUsageAttribute); + + AnalyzeSymbolForAttribute(ref symbolContext, exportAttributeV1, namedType, exportAttributes, namedTypeAttributes); + AnalyzeSymbolForAttribute(ref symbolContext, exportAttributeV2, namedType, exportAttributes, namedTypeAttributes); + }, SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbolForAttribute(ref SymbolAnalysisContext context, INamedTypeSymbol? exportAttribute, INamedTypeSymbol namedType, IEnumerable exportAttributes, IEnumerable namedTypeAttributes) + { + if (exportAttribute is null) + { + return; + } + + var exportAttributeApplication = exportAttributes.FirstOrDefault(ad => ad.AttributeClass.DerivesFrom(exportAttribute)); + if (exportAttributeApplication is null) + { + return; + } + + if (!namedTypeAttributes.Any(ad => + ad.AttributeClass.Name == nameof(PartNotDiscoverableAttribute) + && Equals(ad.AttributeClass.ContainingNamespace, exportAttribute.ContainingNamespace))) + { + if (exportAttributeApplication.ApplicationSyntaxReference == null) + { + context.ReportDiagnostic(context.Symbol.CreateDiagnostic(Rule, namedType.Name)); + } + else + { + // '{0}' is exported for test purposes and should be marked PartNotDiscoverable + context.ReportDiagnostic(exportAttributeApplication.ApplicationSyntaxReference.CreateDiagnostic(Rule, context.CancellationToken, namedType.Name)); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverableCodeFixProvider.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverableCodeFixProvider.cs new file mode 100644 index 0000000000000..805af31ccc30e --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/TestExportsShouldNotBeDiscoverableCodeFixProvider.cs @@ -0,0 +1,105 @@ +// 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 warnings + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Text; + +namespace Roslyn.Diagnostics.Analyzers +{ + [ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = nameof(TestExportsShouldNotBeDiscoverableCodeFixProvider))] + [Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public class TestExportsShouldNotBeDiscoverableCodeFixProvider() : CodeFixProvider + { + public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(TestExportsShouldNotBeDiscoverable.Rule.Id); + + public override FixAllProvider GetFixAllProvider() + => WellKnownFixAllProviders.BatchFixer; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + foreach (var diagnostic in context.Diagnostics) + { + context.RegisterCodeFix( + CodeAction.Create( + RoslynDiagnosticsAnalyzersResources.TestExportsShouldNotBeDiscoverableCodeFix, + cancellationToken => AddPartNotDiscoverableAttributeAsync(context.Document, diagnostic.Location.SourceSpan, cancellationToken), + equivalenceKey: nameof(TestExportsShouldNotBeDiscoverable)), + diagnostic); + } + + return Task.CompletedTask; + } + + private static async Task AddPartNotDiscoverableAttributeAsync(Document document, TextSpan sourceSpan, CancellationToken cancellationToken) + { + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var exportingAttribute = root.FindNode(sourceSpan, getInnermostNodeForTie: true); + + var generator = SyntaxGenerator.GetGenerator(document); + + var declaration = generator.TryGetContainingDeclaration(exportingAttribute, DeclarationKind.Class); + if (declaration is null) + { + return document; + } + + var exportedType = semanticModel.GetDeclaredSymbol(declaration, cancellationToken); + if (exportedType is null) + { + return document; + } + + INamedTypeSymbol? partNotDiscoverableAttributeSymbol = null; + foreach (var attributeData in exportedType.GetAttributes()) + { + INamedTypeSymbol? exportAttributeSymbol = null; + foreach (var attributeClass in attributeData.AttributeClass.GetBaseTypesAndThis()) + { + if (attributeClass.Name == nameof(ExportAttribute)) + { + exportAttributeSymbol = attributeClass; + break; + } + } + + if (exportAttributeSymbol is null) + { + continue; + } + + partNotDiscoverableAttributeSymbol = exportAttributeSymbol.ContainingNamespace.GetTypeMembers(nameof(PartNotDiscoverableAttribute)).FirstOrDefault(); + if (partNotDiscoverableAttributeSymbol is object) + { + break; + } + } + + if (partNotDiscoverableAttributeSymbol is null) + { + // This can only get hit if ExportAttribute is available but PartNotDiscoverableAttribute is missing. + return document; + } + + var newDeclaration = generator.AddAttributes(declaration, generator.Attribute(generator.TypeExpression(partNotDiscoverableAttributeSymbol).WithAddImportsAnnotation())); + return document.WithSyntaxRoot(root.ReplaceNode(declaration, newDeclaration)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf new file mode 100644 index 0000000000000..2b8342e56b6f4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.cs.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Přidat atribut Shared + {Locked="Shared"} + + + Apply trait to containing type + Použít vlastnost pro obsahující typ + + + + Remove the 'Opt' suffix + Odeberte příponu Opt + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Nepoužívejte příponu Opt v kódu s povolenou hodnotou null. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Nepoužívejte příponu Opt v kódu s povolenou hodnotou null. + + + + Avoid the 'Opt' suffix + Nepoužívejte příponu Opt + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Toto je refaktoring, který zjednodušuje proces vytváření testovacích přístupových objektů pomocí vzoru TestAccessor. + + + + Create test accessor + Vytvořit testovací přístupový objekt + + + + Create test accessor + Vytvořit testovací přístupový objekt + + + + Defaultable types should have defaultable fields. + Typy, které se dají nastavit jako výchozí, by měly mít pole, která se tak dají nastavit taky. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Typ {0}, který se dá nastavit jako výchozí, má pole nebo automatickou vlastnost {1}, které se jako výchozí nastavit nedají. + + + + Defaultable types should have defaultable fields + Typy, které se dají nastavit jako výchozí, by měly mít pole, která se tak dají nastavit taky + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + GetTestAccessor() je pomocná metoda vyhrazená pro testování. Produkční kód tento člen nesmí volat. + + + + Do not call 'GetTestAccessor()' from production code + Nevolejte GetTestAccessor() z produkčního kódu. + + + + Do not call 'GetTestAccessor()' + Nevolat GetTestAccessor() + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Parametry primárního konstruktoru by se neměly implicitně zachytávat. Ručně je přiřaďte k polím na začátku typu. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Parametr primárního konstruktoru {0} by se neměl implicitně zachytávat. + + + + Do not capture primary constructor parameters + Nezachytávejte parametry primárního konstruktoru + + + + Avoid nullable wrapper. + Nepoužívejte obálky s možnou hodnotou null. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Neobalujte nekopírovatelný typ {0} do operace {1}. + + + + Do not copy value. + Nekopírujte hodnotu. + + + + Do not copy value + Nekopírujte hodnotu. + + + + Cannot assign a value from a reference to a non-copyable type. + Hodnota z odkazu se nedá přiřadit typu, který se nedá kopírovat. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Hodnota z odkazu se nedá přiřadit typu {0}, který se nedá kopírovat. + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Automatické vlastnosti vždy kopírují hodnoty, proto se nedají deklarovat s nekopírovatelnými typy. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + Automatická vlastnost {1} nemůže mít nekopírovatelný typ {0}. + + + + Do not box non-copyable value types. + Nebalte nekopírovatelné typy hodnot. + + + + Do not box non-copyable type '{0}' + Nebalte nekopírovatelný typ hodnoty {0}. + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Pole s nekopírovatelným typem nemůže být členem kopírovatelného typu. Obsahující typ se dá nastavit jako nekopírovatelný nebo se dá převést na odkazový typ, nebo se pole může odebrat nebo převést na kopírovatelný typ. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + Kopírovatelné pole {1} nemůže mít nekopírovatelný typ {0}. + + + + Cannot return a value from a reference to a non-copyable type. + Hodnota z odkazu se nedá vrátit typu, který se nedá kopírovat. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Hodnota z odkazu se nedá vrátit typu {0}, který se nedá kopírovat. + + + + Do not unbox non-copyable value types. + Nerozbalujte nekopírovatelné typy hodnot. + + + + Do not unbox non-copyable type '{0}' + Nerozbalujte nekopírovatelný typ hodnoty {0}. + + + + Do not copy value + Nekopírovat hodnotu + + + + Unsupported use of non-copyable type. + Nepodporované použití nekopírovatelného typu + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Nepodporované použití nekopírovatelného typu {0} v operaci {1} + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Explicitně definujte importující konstruktor. + + + + Add 'ImportingConstructor' attribute + Přidejte atribut ImportingConstructor. + + + + Make constructor public + Nastavit konstruktor jako veřejný + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Exportované cesty by se měly označit atributem ImportingConstructorAttribute. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + {0} se exportovala pomocí MEF a měla by mít jeden veřejný importující konstruktor ve správném tvaru. + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Exportované části by měly mít veřejný konstruktor označený atributem ImportingConstructorAttribute. + + + + Expose member for testing. + Zpřístupnit člen pro testování + + + + Expose member for testing + Zpřístupnit člen pro testování + + + + Expose member for testing + Zpřístupnit člen pro testování + + + + Fix numbered comments + Opravit očíslované komentáře + + + + Set 'ObsoleteAttribute.Error' to 'true' + Nastavte ObsoleteAttribute.Error na true. + + + + Use correct 'ObsoleteAttribute' message. + Použijte správnou zprávu ObsoleteAttribute. + + + + Add 'ObsoleteAttribute' + Přidejte ObsoleteAttribute. + + + + Configure 'ObsoleteAttribute' for importing constructor. + Nakonfigurujte ObsoleteAttribute pro importující konstruktor. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Nastavte ObsoleteAttribute.Error na true. + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Importující konstruktor by se měl označit atributem ObsoleteAttribute. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Importující konstruktor by se měl označit atributem ObsoleteAttribute. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Importující konstruktor by se měl označit atributem ObsoleteAttribute + + + + Place statement on following line + Umístit příkaz na následující řádek + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + Když se používá syntaxe typeof(T).FullName, není vlastnost Type.FullName nikdy null. + + + + Use 'null' instead of 'default' + Místo default použijte null. + + + + Use 'null' instead of 'default' for nullable types. + Pro typy s možnou hodnotou null místo default použijte null. + + + + Use 'null' instead of 'default' for nullable types + Pro typy s možnou hodnotou null místo default použijte null. + + + + Prefer null literal + Upřednostňovat literál null + + + + Asynchronous test methods do not require the 'Async' suffix + Asynchronní testovací metody nevyžadují příponu Async. + + + + Run iterations + Spustit iterace + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + Instance TemporaryArray<T>.AsRef() musí být proměnná using. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Instance TemporaryArray<T>.AsRef() musí být proměnná using. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Instance TemporaryArray<T>.AsRef() musí být proměnná using + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Použijte PartNotDiscoverableAttribute. + + + + Test exports should not be discoverable. + Testovací exportování by se neměla dát zjistit. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + {0} se exportovalo pro účely testování a mělo by se označit atributem PartNotDiscoverableAttribute. + + + + Test exports should not be discoverable + Testovací exportování by se neměla dát zjistit + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Použít SpecializedCollections.EmptyEnumerable() + + + + #N/A + Není k dispozici + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Použijte SpecializedCollections.EmptyEnumerable(). + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Použít SpecializedCollections.SingletonEnumerable() + + + + #N/A + Není k dispozici + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Použijte SpecializedCollections.SingletonEnumerable(). + + + + Invoke the correct property to ensure correct use site diagnostics + Zavolat správnou vlastnost, aby se zajistilo správné použití diagnostiky webu + + + + #N/A + Není k dispozici + + + + Invoke the correct property to ensure correct use site diagnostics + Vyvoláním správné vlastnosti zajistěte správné použití diagnostiky webu. + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + K vytvoření CodeAction nepoužívat obecnou metodu CodeAction.Create + + + + #N/A + Není k dispozici + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + K vytvoření CodeAction nepoužívejte obecnou metodu CodeAction.Create. + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Při implementaci IEquatable přepsat Object.Equals(object) + + + + #N/A + Není k dispozici + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Při implementaci IEquatable přepište Object.Equals(object). + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Pro zdrojové symboly musí být vygenerována událost SymbolDeclaredEvent + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Vyžaduje se, aby fronta událostí kompilace vygenerovala symbolem deklarované události pro všechny deklarované zdrojové symboly. Každý typ zdrojového symbolu nebo jeden z jeho základních typů proto musí vygenerovat symbolem deklarovanou událost. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Pro zdrojové symboly musí být vygenerována událost SymbolDeclaredEvent. + + + + Do not mix attributes from different versions of MEF + Nekombinujte atributy z různých verzí rozhraní MEF. + + + + Do not mix attributes from different versions of MEF. + Nekombinujte atributy z různých verzí rozhraní MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + Atribut {0} pochází z jiné verze rozhraní MEF než atribut exportu u {1}. + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Části exportované pomocí rozhraní MEFv2 musí být označené atributem SharedAttribute + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + Část exportovaná pomocí rozhraní MEFv2 musí být označená atributem SharedAttribute. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + Část {0} je exportovaná pomocí rozhraní MEFv2, musí být proto označená atributem SharedAttribute. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf new file mode 100644 index 0000000000000..17832a97f9c54 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.de.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Attribut "Shared" hinzufügen + {Locked="Shared"} + + + Apply trait to containing type + Merkmal auf enthaltenden Typ anwenden + + + + Remove the 'Opt' suffix + Suffix "Opt" entfernen + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Hiermit wird das Suffix "Opt" in einem Code vermieden, der NULL-Werte zulässt. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Vermeiden Sie das Suffix "Opt" in einem Code, der NULL-Werte zulässt. + + + + Avoid the 'Opt' suffix + Suffix "Opt" vermeiden + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Dies ist ein Refactoring, das den Prozess zum Erstellen von Testzugriffsmethoden mithilfe des Musters "TestAccessor" vereinfacht. + + + + Create test accessor + Testzugriffsmethode erstellen + + + + Create test accessor + Testzugriffsmethode erstellen + + + + Defaultable types should have defaultable fields. + Typen, für die ein Standardwert verfügbar ist, müssen auf den Standardwert festlegbare Felder aufweisen. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Der den Standard festlegbare Typ "{0}" weist ein nicht auf den Standard festlegbares Feld oder die automatische Eigenschaft "{1}" auf. + + + + Defaultable types should have defaultable fields + Auf den Standard festlegbare Typen müssen auf den Standard festlegbare Felder aufweisen. + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + "GetTestAccessor()" ist eine für Tests reservierte Hilfsmethode. Der Produktionscode darf diesen Member nicht aufrufen. + + + + Do not call 'GetTestAccessor()' from production code + Rufen Sie "GetTestAccessor()" nicht über den Produktionscode auf. + + + + Do not call 'GetTestAccessor()' + "GetTestAccessor()" nicht aufrufen + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Primäre Konstruktorparameter sollten nicht implizit erfasst werden. Weisen Sie sie Feldern am Anfang des Typs manuell zu. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Der primäre Konstruktorparameter „{0}“ sollte nicht implizit erfasst werden. + + + + Do not capture primary constructor parameters + Primäre Konstruktorparameter nicht erfassen + + + + Avoid nullable wrapper. + Hiermit wird der Nullable-Wrapper vermieden. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Umschließen Sie den nicht kopierbaren Typ "{0}" nicht im Vorgang "{1}". + + + + Do not copy value. + Hiermit wird der Wert nicht kopiert. + + + + Do not copy value + Wert nicht kopieren + + + + Cannot assign a value from a reference to a non-copyable type. + Es ist nicht möglich, einen Wert aus einem Verweis einem nicht kopierbaren Typ zuzuweisen. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Ein Wert kann aus einem Verweis kann dem nicht kopierbaren Typ "{0}" nicht zugewiesen werden. + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Automatische Eigenschaften kopieren immer Werte und können daher nicht mit nicht kopierbaren Typen deklariert werden. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + Die automatische Eigenschaft "{1}" darf nicht den nicht kopierbaren Typ "{0}" aufweisen. + + + + Do not box non-copyable value types. + Für nicht kopierbare Werttypen darf kein Boxing ausgeführt werden. + + + + Do not box non-copyable type '{0}' + Für den nicht kopierbaren Typ "{0}" darf kein Boxing ausgeführt werden. + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Ein Feld mit einem nicht kopierbaren Typ kann kein Member eines kopierbaren Typs sein. Der enthaltende Typ kann als nicht kopierbar festgelegt oder in einen Verweistyp konvertiert werden, oder das Feld kann entfernt oder in einen kopierbaren Typ konvertiert werden. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + Das kopierbare Feld "{1}" darf nicht den nicht kopierbaren Typ "{0}" aufweisen. + + + + Cannot return a value from a reference to a non-copyable type. + Ein Wert aus einem Verweis kann nicht an einen nicht kopierbaren Typ zurückgegeben werden. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Ein Wert aus einem Verweis kann nicht an den nicht kopierbaren Typ "{0}" zurückgegeben werden. + + + + Do not unbox non-copyable value types. + Für nicht kopierbare Werttypen darf kein Unboxing ausgeführt werden. + + + + Do not unbox non-copyable type '{0}' + Für den nicht kopierbaren Typ "{0}" darf kein Unboxing ausgeführt werden. + + + + Do not copy value + Wert nicht kopieren + + + + Unsupported use of non-copyable type. + Nicht unterstützte Verwendung eines nicht kopierbaren Typs. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Nicht unterstützte Verwendung des nicht kopierbaren Typs "{0}" im Vorgang "{1}". + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Importierenden Konstruktor explizit definieren + + + + Add 'ImportingConstructor' attribute + Attribut "ImportingConstructor" hinzufügen + + + + Make constructor public + Konstruktor als öffentlich festlegen + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Exportierte Teile müssen mit "ImportingConstructorAttribute" gekennzeichnet werden. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + „{0}“ ist MEF-exportiert und muss einen einzelnen importierenden Konstruktor der richtigen Form aufweisen. + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Exportierte Teile müssen einen öffentlichen Konstruktor aufweisen, der mit „ImportingConstructorAttribute“ markiert ist. + + + + Expose member for testing. + Hiermit wird der Member für Tests verfügbar gemacht. + + + + Expose member for testing + Member für Tests verfügbar machen + + + + Expose member for testing + Member für Tests verfügbar machen + + + + Fix numbered comments + Nummerierte Kommentare korrigieren + + + + Set 'ObsoleteAttribute.Error' to 'true' + Legen Sie "ObsoleteAttribute.Error" auf TRUE fest. + + + + Use correct 'ObsoleteAttribute' message. + Hiermit wird die richtige Meldung "ObsoleteAttribute" verwendet. + + + + Add 'ObsoleteAttribute' + "ObsoleteAttribute" hinzufügen + + + + Configure 'ObsoleteAttribute' for importing constructor. + Hiermit wird "ObsoleteAttribute" für den importierenden Konstruktor konfiguriert. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Legen Sie "ObsoleteAttribute.Error" auf TRUE fest. + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Der importierende Konstruktor muss mit "ObsoleteAttribute" gekennzeichnet werden. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Der importierende Konstruktor muss mit "ObsoleteAttribute" gekennzeichnet werden. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Importierender Konstruktor muss mit "ObsoleteAttribute" gekennzeichnet werden + + + + Place statement on following line + Anweisung in folgender Zeile platzieren + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + Die Type.FullName-Eigenschaft ist bei Verwendung der Syntax "typeof(T).FullName" nie NULL. + + + + Use 'null' instead of 'default' + "null" anstelle von "default" verwenden + + + + Use 'null' instead of 'default' for nullable types. + Verwenden Sie für Nullable-Typen "null" anstelle von "default". + + + + Use 'null' instead of 'default' for nullable types + Für Nullable-Typen "null" anstelle von "default" verwenden + + + + Prefer null literal + NULL-Literal bevorzugen + + + + Asynchronous test methods do not require the 'Async' suffix + Für asynchrone Testmethoden ist das Suffix "Async" nicht erforderlich. + + + + Run iterations + Iterationen ausführen + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + Die Instanz von TemporaryArray<T>.AsRef() muss eine using-Variable sein. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Instanz von TemporaryArray<T>.AsRef() muss eine using-Variable sein + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Instanz von TemporaryArray<T>.AsRef() muss eine using-Variable sein + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + "PartNotDiscoverableAttribute" anwenden + + + + Test exports should not be discoverable. + Testexporte dürfen nicht ermittelbar sein. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + "{0}" wird zu Testzwecken exportiert und muss mit "PartNotDiscoverableAttribute" gekennzeichnet werden. + + + + Test exports should not be discoverable + Testexporte dürfen nicht ermittelbar sein + + + + Use 'SpecializedCollections.EmptyEnumerable()' + "SpecializedCollections.EmptyEnumerable()" verwenden + + + + #N/A + #Nicht zutreffend + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Verwenden Sie "SpecializedCollections.EmptyEnumerable()". + + + + Use 'SpecializedCollections.SingletonEnumerable()' + "SpecializedCollections.SingletonEnumerable()" verwenden + + + + #N/A + #Nicht zutreffend + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Verwenden Sie "SpecializedCollections.SingletonEnumerable()". + + + + Invoke the correct property to ensure correct use site diagnostics + Richtige Eigenschaft zum Sicherstellen einer korrekten Nutzung der Websitediagnose aufrufen + + + + #N/A + #Nicht zutreffend + + + + Invoke the correct property to ensure correct use site diagnostics + Rufen Sie die richtige Eigenschaft auf, um eine korrekte Nutzung der Websitediagnose sicherzustellen. + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Generische Methode "CodeAction.Create" nicht zum Erstellen von "CodeAction" verwenden + + + + #N/A + #Nicht zutreffend + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Verwenden Sie die generische Methode "CodeAction.Create" nicht zum Erstellen von "CodeAction". + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + "Object.Equals(object)" bei Implementierung von "IEquatable" überschreiben + + + + #N/A + #Nicht zutreffend + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Überschreiben Sie "Object.Equals(object)" bei Implementierung von "IEquatable". + + + + 'SymbolDeclaredEvent' must be generated for source symbols + "SymbolDeclaredEvent" muss für Quellsymbole generiert werden + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Die Warteschlange für das Kompilierungsereignis ist erforderlich, um für alle deklarierten Quellsymbole Symboldeklarationsereignisse zu generieren. Daher muss jeder Quellsymboltyp oder einer der zugehörigen Basistypen ein Symboldeklarationsereignis generieren. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + "SymbolDeclaredEvent" muss für Quellsymbole generiert werden. + + + + Do not mix attributes from different versions of MEF + Keine Attribute aus verschiedenen Versionen des MEF kombinieren + + + + Do not mix attributes from different versions of MEF. + Hiermit werden keine Attribute aus verschiedenen Versionen des MEF kombiniert. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + Das Attribut "{0}" stammt aus einer anderen Version des MEF als das Exportattribut für "{1}". + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Mit MEFv2 exportierte Teile müssen mit "SharedAttribute" gekennzeichnet werden + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + Ein mit MEFv2 exportiertes Teil muss mit "SharedAttribute" gekennzeichnet werden. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + "{0}" wurde mit MEFv2 exportiert und muss daher mit "SharedAttribute" gekennzeichnet werden. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf new file mode 100644 index 0000000000000..59f444be8c8ff --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.es.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Agregar el atributo "Shared" + {Locked="Shared"} + + + Apply trait to containing type + Aplicar rasgo al tipo contenedor + + + + Remove the 'Opt' suffix + Quitar el sufijo "Opt" + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Evite el sufijo "Opt" en un código que admita valores NULL. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Evitar el sufijo "Opt" en un código que admita valores NULL + + + + Avoid the 'Opt' suffix + Evitar el sufijo "Opt" + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Esta es una refactorización que simplifica el proceso de creación de descriptores de acceso de pruebas con el patrón "TestAccessor". + + + + Create test accessor + Crear un descriptor de acceso de prueba + + + + Create test accessor + Crear un descriptor de acceso de prueba + + + + Defaultable types should have defaultable fields. + Los tipos que aceptan valores predeterminados deben tener campos que también los acepten. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + El tipo "{0}" defaultable tiene un campo que no es defaultable o la propiedad automática "{1}" + + + + Defaultable types should have defaultable fields + Los tipos defaultable deben tener campos defaultable + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + "GetTestAccessor()" es un método auxiliar reservado para las pruebas. El código de producción no debe llamar a este miembro. + + + + Do not call 'GetTestAccessor()' from production code + No llamar a "GetTestAccessor()" desde el código de producción + + + + Do not call 'GetTestAccessor()' + No llamar a "GetTestAccessor()" + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Los parámetros del constructor principal no deben capturarse implícitamente. Asígnelos manualmente a campos al principio del tipo. + + + + Primary constructor parameter '{0}' should not be implicitly captured + El parámetro de constructor principal "{0}" no se debe capturar implícitamente + + + + Do not capture primary constructor parameters + No capturar parámetros del constructor principal + + + + Avoid nullable wrapper. + Evita un contenedor que acepte valores NULL. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + No ajustar el tipo "{0}" que no se puede copiar en la operación "{1}" + + + + Do not copy value. + No se copia el valor. + + + + Do not copy value + No copiar el valor + + + + Cannot assign a value from a reference to a non-copyable type. + No se puede asignar un valor de una referencia a un tipo que no se puede copiar. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + No se puede asignar un valor de una referencia a un tipo "{0}" que no se puede copiar. + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Las propiedades automáticas siempre copian los valores, por lo que no pueden declararse con tipos que no se puedan copiar. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + La propiedad automática "{1}" no puede tener el tipo "{0}" que no se puede copiar. + + + + Do not box non-copyable value types. + No aplique conversión boxing a los tipos de valores que no se pueden copiar. + + + + Do not box non-copyable type '{0}' + No aplicar conversión boxing al tipo "{0}" que no se puede copiar + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Un campo con un tipo que no se pueda copiar no puede ser miembro de un tipo que pueda copiarse. El tipo contenedor puede hacerse no copiable o convertirse en un tipo de referencia, o bien el campo puede quitarse o convertirse en un tipo que se pueda copiar. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + El campo "{1}" que se puede copiar no puede tener el tipo "{0}" que no puede copiarse. + + + + Cannot return a value from a reference to a non-copyable type. + No se puede devolver un valor de una referencia a un tipo que no se puede copiar. + + + + Cannot return a value from a reference to non-copyable type '{0}' + No se puede devolver un valor de una referencia a un tipo "{0}" que no se puede copiar. + + + + Do not unbox non-copyable value types. + No aplique conversión unboxing a los tipos de valores que no se pueden copiar. + + + + Do not unbox non-copyable type '{0}' + No aplicar conversión unboxing al tipo "{0}" que no se puede copiar + + + + Do not copy value + No copiar el valor + + + + Unsupported use of non-copyable type. + Uso no admitido de un tipo que no se puede copiar. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Uso no admitido del tipo "{0}" que no se puede copiar en la operación "{1}" + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Definir explícitamente el constructor de importación + + + + Add 'ImportingConstructor' attribute + Agregar el atributo "ImportingConstructor" + + + + Make constructor public + Convertir el constructor en público + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Las partes exportadas deben marcarse con "ImportingConstructorAttribute". + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' se exporta mediante MEF y debe tener un único constructor de importación público con el formato correcto + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Las partes exportadas deben tener un constructor público marcado con 'ImportingConstructorAttribute' + + + + Expose member for testing. + Expone el miembro para las pruebas. + + + + Expose member for testing + Exponer miembro para pruebas + + + + Expose member for testing + Exponer miembro para pruebas + + + + Fix numbered comments + Corregir comentarios numerados + + + + Set 'ObsoleteAttribute.Error' to 'true' + Establecer "ObsoleteAttribute.Error" en "true" + + + + Use correct 'ObsoleteAttribute' message. + Usa el mensaje "ObsoleteAttribute" correcto. + + + + Add 'ObsoleteAttribute' + Agregar "ObsoleteAttribute" + + + + Configure 'ObsoleteAttribute' for importing constructor. + Configura "ObsoleteAttribute" para el constructor de importación. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Establecer "ObsoleteAttribute.Error" en "true" + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + El constructor de importación debe marcarse con "ObsoleteAttribute". + + + + Importing constructor should be marked with 'ObsoleteAttribute' + El constructor de importación debe marcarse con "ObsoleteAttribute" + + + + Importing constructor should be marked with 'ObsoleteAttribute' + El constructor de importación debe marcarse con "ObsoleteAttribute" + + + + Place statement on following line + Instrucción de colocación en la línea siguiente + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + La propiedad "Type.FullName" nunca es NULL cuando se usa la sintaxis "typeof(T).FullName" + + + + Use 'null' instead of 'default' + Usar "null" en lugar de "default" + + + + Use 'null' instead of 'default' for nullable types. + Use "null" en lugar de "default" para los tipos que aceptan valores NULL. + + + + Use 'null' instead of 'default' for nullable types + Usar "null" en lugar de "default" para los tipos que aceptan valores NULL + + + + Prefer null literal + Preferir literal NULL + + + + Asynchronous test methods do not require the 'Async' suffix + Los métodos de prueba asincrónicos no requieren el sufijo "Async". + + + + Run iterations + Ejecutar iteraciones + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + La instancia de TemporaryArray<T>.AsRef() debe ser una variable "using". + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + La instancia de TemporaryArray<T>.AsRef() debe ser una variable "using" + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + La instancia de TemporaryArray<T>.AsRef() debe ser una variable "using" + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Aplicar "PartNotDiscoverableAttribute" + + + + Test exports should not be discoverable. + Las exportaciones de prueba no deben poder detectarse. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + "{0}" se exporta con fines de prueba y debe marcarse con "PartNotDiscoverableAttribute". + + + + Test exports should not be discoverable + Las exportaciones de prueba no deben poder detectarse + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usar "SpecializedCollections.EmptyEnumerable()" + + + + #N/A + #N/D + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usar "SpecializedCollections.EmptyEnumerable()" + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usar "SpecializedCollections.SingletonEnumerable()" + + + + #N/A + #N/D + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usar "SpecializedCollections.SingletonEnumerable()" + + + + Invoke the correct property to ensure correct use site diagnostics + Invocar la propiedad correcta para garantizar el diagnóstico de uso de sitio correcto + + + + #N/A + #N/D + + + + Invoke the correct property to ensure correct use site diagnostics + Invocar la propiedad correcta para garantizar el diagnóstico de uso de sitio correcto + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + No usar "CodeAction.Create" genérico para crear "CodeAction" + + + + #N/A + #N/D + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + No usar "CodeAction.Create" genérico para crear "CodeAction" + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Reemplazar "Object.Equals(object)" al implementar "IEquatable" + + + + #N/A + #N/D + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Reemplazar "Object.Equals(object)" al implementar "IEquatable" + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Debe generarse "SymbolDeclaredEvent" para los símbolos de origen + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Se requiere la cola del evento de compilación para generar eventos declarados de símbolos para todos los símbolos de origen declarados. Por lo tanto, todos los tipos de símbolos de origen de uno de sus tipos base debe generar un evento declarado de símbolos. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Debe generarse "SymbolDeclaredEvent" para los símbolos de origen + + + + Do not mix attributes from different versions of MEF + No se deben mezclar atributos de distintas versiones de MEF. + + + + Do not mix attributes from different versions of MEF. + No se deben mezclar atributos de distintas versiones de MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + El atributo “{0}” procede de una versión de MEF distinta a la del atributo de exportación en “{1}”. + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Las partes exportadas con MEFv2 deben marcarse con "SharedAttribute" + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + La parte exportada con MEFv2 debe marcarse con "SharedAttribute". + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + “{0}” se exporta con MEFv2 y, por lo tanto, debe marcarse con "SharedAttribute". + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf new file mode 100644 index 0000000000000..76c303a5e994e --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.fr.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Ajouter l'attribut 'Shared' + {Locked="Shared"} + + + Apply trait to containing type + Appliquer la caractéristique au type conteneur + + + + Remove the 'Opt' suffix + Supprimer le suffixe 'Opt' + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Évitez le suffixe 'Opt' dans du code pouvant accepter des valeurs null. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Éviter le suffixe 'Opt' dans du code pouvant accepter des valeurs null + + + + Avoid the 'Opt' suffix + Éviter le suffixe 'Opt' + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Il s'agit d'une refactorisation qui simplifie le processus de création d'accesseurs de test à l'aide du modèle 'TestAccessor'. + + + + Create test accessor + Créer un accesseur de test + + + + Create test accessor + Créer un accesseur de test + + + + Defaultable types should have defaultable fields. + Les types par défaut doivent avoir des champs par défaut. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Le type par défaut '{0}' a un champ sans valeur par défaut ou une propriété automatique '{1}' + + + + Defaultable types should have defaultable fields + Les types par défaut doivent avoir des champs par défaut + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()' est une méthode d'assistance réservée aux tests. Le code de production ne doit pas appeler ce membre. + + + + Do not call 'GetTestAccessor()' from production code + N'appelez pas 'GetTestAccessor()' à partir du code de production + + + + Do not call 'GetTestAccessor()' + Ne pas appeler 'GetTestAccessor()' + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Les paramètres du constructeur principal ne doivent pas être capturés implicitement. Affectez-les manuellement aux champs au début du type. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Le paramètre de constructeur principal '{0}' ne doit pas être capturé implicitement + + + + Do not capture primary constructor parameters + Ne pas capturer les paramètres du constructeur principal + + + + Avoid nullable wrapper. + Évitez le wrapper nullable. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Ne pas inclure dans un wrapper le type '{0}' non copiable dans l'opération '{1}' + + + + Do not copy value. + Ne copiez pas de valeur. + + + + Do not copy value + Ne pas copier la valeur + + + + Cannot assign a value from a reference to a non-copyable type. + Impossible d'affecter une valeur à partir d'une référence à un type non copiable. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Impossible d'affecter une valeur à partir d'une référence au type non copiable '{0}' + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Les propriétés automatiques copient toujours les valeurs, elles ne peuvent donc pas être déclarées avec des types non copiables. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + La propriété automatique '{1}' ne peut pas avoir le type non copiable '{0}' + + + + Do not box non-copyable value types. + N'effectue pas de conversion boxing des types valeur non copiables. + + + + Do not box non-copyable type '{0}' + Ne pas effectuer de conversion boxing du type non copiable '{0}' + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Un champ avec un type non copiable ne peut pas être membre d'un type copiable. Le type conteneur peut être rendu non copiable ou être converti en type référence. De même, le champ peut être supprimé ou converti en type copiable. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + La champ copiable '{1}' ne peut pas avoir le type non copiable '{0}' + + + + Cannot return a value from a reference to a non-copyable type. + Impossible de retourner une valeur à partir d'une référence à un type non copiable. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Impossible de retourner une valeur à partir d'une référence au type non copiable '{0}' + + + + Do not unbox non-copyable value types. + N'effectue pas de conversion unboxing des types valeur non copiables. + + + + Do not unbox non-copyable type '{0}' + Ne pas effectuer de conversion unboxing du type '{0}' non copiable + + + + Do not copy value + Ne pas copier la valeur + + + + Unsupported use of non-copyable type. + Utilisation non prise en charge d'un type non copiable. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Utilisation non prise en charge du type '{0}' non copiable dans l'opération '{1}' + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Définir explicitement le constructeur d'importation + + + + Add 'ImportingConstructor' attribute + Ajouter l'attribut 'ImportingConstructor' + + + + Make constructor public + Rendre le constructeur public + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Les parties exportées doivent être marquées avec 'ImportingConstructorAttribute'. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' est exporté avec MEF et doit avoir un seul constructeur d'importation de la forme appropriée. + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Les parties exportées doivent avoir un constructeur public marqué avec 'ImportingConstructorAttribute'. + + + + Expose member for testing. + Exposez le membre à des fins de test. + + + + Expose member for testing + Exposer un membre à des fins de test + + + + Expose member for testing + Exposer un membre à des fins de test + + + + Fix numbered comments + Corriger les commentaires numérotés + + + + Set 'ObsoleteAttribute.Error' to 'true' + Affectez la valeur 'true' à 'ObsoleteAttribute.Error' + + + + Use correct 'ObsoleteAttribute' message. + Utilisez le message 'ObsoleteAttribute' approprié. + + + + Add 'ObsoleteAttribute' + Ajouter 'ObsoleteAttribute' + + + + Configure 'ObsoleteAttribute' for importing constructor. + Configurez 'ObsoleteAttribute' pour le constructeur d'importation. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Affectez la valeur 'true' à 'ObsoleteAttribute.Error' + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Le constructeur d'importation doit être marqué avec 'ObsoleteAttribute'. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Le constructeur d'importation doit être marqué avec 'ObsoleteAttribute' + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Le constructeur d'importation doit être marqué avec 'ObsoleteAttribute' + + + + Place statement on following line + Placer l'instruction sur la ligne suivante + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + La propriété 'Type.FullName' n'a jamais de valeur null quand la syntaxe 'typeof(T).FullName' est utilisée + + + + Use 'null' instead of 'default' + Utilisez 'null' à la place de 'default' + + + + Use 'null' instead of 'default' for nullable types. + Utilisez 'null' à la place de 'default' pour les types Nullable. + + + + Use 'null' instead of 'default' for nullable types + Utiliser 'null' à la place de 'default' pour les types Nullable + + + + Prefer null literal + Préférez un littéral ayant une valeur null + + + + Asynchronous test methods do not require the 'Async' suffix + Les méthodes de test asynchrones ne nécessitent pas le suffixe 'Async' + + + + Run iterations + Exécuter les itérations + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + L'instance de TemporaryArray<T>.AsRef() doit être une variable 'using'. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + L'instance de TemporaryArray<T>.AsRef() doit être une variable 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + L'instance de TemporaryArray<T>.AsRef() doit être une variable 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Appliquer 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable. + Les exportations de test ne doivent pas être détectables. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}' est exporté à des fins de test et doit être marqué avec 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable + Les exportations de test ne doivent pas être découvrables + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Utiliser 'SpecializedCollections.EmptyEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Utiliser 'SpecializedCollections.EmptyEnumerable()' + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Utiliser 'SpecializedCollections.SingletonEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Utiliser 'SpecializedCollections.SingletonEnumerable()' + + + + Invoke the correct property to ensure correct use site diagnostics + Appeler la propriété appropriée pour garantir une utilisation adéquate des diagnostics de site + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + Appeler la propriété appropriée pour garantir une utilisation adéquate des diagnostics de site + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + N'utilisez pas de 'CodeAction.Create' générique pour créer 'CodeAction' + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + N'utilisez pas de 'CodeAction.Create' générique pour créer 'CodeAction' + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Substituer 'Object.Equals(object)' au moment de l'implémentation de 'IEquatable' + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Substituer 'Object.Equals(object)' au moment de l'implémentation de 'IEquatable' + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent' doit être généré pour les symboles sources + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Une file d'attente d'événements de compilation est nécessaire afin de générer les événements de déclaration de symbole pour tous les symboles sources déclarés. Ainsi, chaque type de symbole source ou l'un de ses types de base doit générer un événement de déclaration de symbole. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent' doit être généré pour les symboles sources + + + + Do not mix attributes from different versions of MEF + Ne pas mélanger les attributs de différentes versions de MEF + + + + Do not mix attributes from different versions of MEF. + Ne mélangez pas les attributs de différentes versions de MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + L'attribut '{0}' provient d'une autre version de MEF que l'attribut d'exportation sur '{1}' + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Les parties exportées avec MEFv2 doivent être marquées avec 'SharedAttribute' + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + La partie exportée avec MEFv2 doit être marquée avec 'SharedAttribute'. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}' est exporté avec MEFv2 et doit donc être marqué avec 'SharedAttribute' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf new file mode 100644 index 0000000000000..5b1c9cc68863f --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.it.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Aggiungere l'attributo 'Shared' + {Locked="Shared"} + + + Apply trait to containing type + Applicare la caratteristica al tipo contenitore + + + + Remove the 'Opt' suffix + Rimuovere il suffisso 'Opt' + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Evitare il suffisso 'Opt' in codice abilitato per i valori Null. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Evitare il suffisso 'Opt' in codice abilitato per i valori Null + + + + Avoid the 'Opt' suffix + Evitare il suffisso 'Opt' + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Si tratta di un refactoring che semplifica il processo di creazione delle funzioni di accesso ai test con il criterio 'TestAccessor'. + + + + Create test accessor + Creare la funzione di accesso di test + + + + Create test accessor + Crea funzione di accesso di test + + + + Defaultable types should have defaultable fields. + I tipi per cui è disponibile un valore predefinito devono avere campi per cui è disponibile un valore predefinito. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Il tipo defaultable '{0}' ha un campo non defaultable o la proprietà automatica '{1}' + + + + Defaultable types should have defaultable fields + I tipi defaultable devono avere campi defaultable + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()' è un metodo helper riservato per il test. Il codice di produzione non deve chiamare questo membro. + + + + Do not call 'GetTestAccessor()' from production code + Non chiamare 'GetTestAccessor()' dal codice di produzione + + + + Do not call 'GetTestAccessor()' + Non chiamare 'GetTestAccessor()' + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + I parametri del costruttore primario non devono essere acquisiti in modo implicito. Assegnarli manualmente ai campi all'inizio del tipo. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Il parametro del costruttore primario "{0}" non deve essere acquisito in modo implicito + + + + Do not capture primary constructor parameters + Non acquisire parametri del costruttore primario + + + + Avoid nullable wrapper. + Evitare il wrapper nullable. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Non eseguire il wrapping del tipo non copiabile '{0}' nell'operazione '{1}' + + + + Do not copy value. + Non copiare il valore. + + + + Do not copy value + Non copiare il valore + + + + Cannot assign a value from a reference to a non-copyable type. + Non è possibile assegnare un valore da un riferimento a un tipo non copiabile. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Non è possibile assegnare un valore da un riferimento al tipo non copiabile '{0}' + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Le proprietà automatiche copiano sempre i valori, quindi non possono essere dichiarate con tipi non copiabili. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + La proprietà automatica '{1}' non può includere il tipo non copiabile '{0}' + + + + Do not box non-copyable value types. + Non eseguire la conversione boxing di tipi valore non copiabili. + + + + Do not box non-copyable type '{0}' + Non eseguire la conversione boxing del tipo non copiabile '{0}' + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Un campo con un tipo non copiabile non può essere membro di un tipo copiabile. Il tipo contenitore può essere reso non copiabile o convertito in un tipo riferimento oppure il campo può essere rimosso o convertito in un tipo copiabile. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + Il campo copiabile '{1}' non può includere il tipo non copiabile '{0}' + + + + Cannot return a value from a reference to a non-copyable type. + Non è possibile restituire un valore da un riferimento a un tipo non copiabile. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Non è possibile restituire un valore da un riferimento al tipo non copiabile '{0}' + + + + Do not unbox non-copyable value types. + Non eseguire la conversione unboxing di tipi valore non copiabili. + + + + Do not unbox non-copyable type '{0}' + Non eseguire la conversione unboxing del tipo non copiabile '{0}' + + + + Do not copy value + Non copiare il valore + + + + Unsupported use of non-copyable type. + Uso non supportato di un tipo non copiabile. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Uso non supportato del tipo non copiabile '{0}' nell'operazione '{1}' + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Definire esplicitamente il costruttore di importazione + + + + Add 'ImportingConstructor' attribute + Aggiungere l'attributo 'ImportingConstructor' + + + + Make constructor public + Impostare il costruttore come public + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Le parti esportate devono essere contrassegnate con 'ImportingConstructorAttribute'. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' è esportato da MEF e deve avere un singolo costruttore di importazione pubblico nel formato corretto + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Le parti esportate devono avere un costruttore pubblico contrassegnato da 'ImportingConstructorAttribute'. + + + + Expose member for testing. + Espone il membro per il test. + + + + Expose member for testing + Esporre il membro per il test + + + + Expose member for testing + Esponi il membro per il test + + + + Fix numbered comments + Correggi i commenti numerati + + + + Set 'ObsoleteAttribute.Error' to 'true' + Impostare 'ObsoleteAttribute.Error' su 'true' + + + + Use correct 'ObsoleteAttribute' message. + Usare il messaggio 'ObsoleteAttribute' corretto. + + + + Add 'ObsoleteAttribute' + Aggiungere 'ObsoleteAttribute' + + + + Configure 'ObsoleteAttribute' for importing constructor. + Configurare 'ObsoleteAttribute' per il costruttore di importazione. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Impostare 'ObsoleteAttribute.Error' su 'true' + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Il costruttore di importazione deve essere contrassegnato con 'ObsoleteAttribute'. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Il costruttore di importazione deve essere contrassegnato con 'ObsoleteAttribute' + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Il costruttore di importazione deve essere contrassegnato con 'ObsoleteAttribute' + + + + Place statement on following line + Inserire l'istruzione nella riga seguente + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + La proprietà 'Type.FullName' non è mai Null quando si usa la sintassi 'typeof(T).FullName' + + + + Use 'null' instead of 'default' + Usare 'null' invece di 'default' + + + + Use 'null' instead of 'default' for nullable types. + Usare 'null' invece di 'default' per i tipi nullable. + + + + Use 'null' instead of 'default' for nullable types + Usare 'null' invece di 'default' per i tipi nullable + + + + Prefer null literal + Preferire il valore letterale Null + + + + Asynchronous test methods do not require the 'Async' suffix + I metodi di test asincroni non richiedono il suffisso 'Async' + + + + Run iterations + Eseguire iterazioni + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + L'istanza di TemporaryArray<T>.AsRef() deve essere una variabile 'using'. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + L'istanza di TemporaryArray<T>.AsRef() deve essere una variabile 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + L'istanza di TemporaryArray<T>.AsRef() deve essere una variabile 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Applicare 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable. + Le esportazioni di test non devono essere individuabili. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}' viene esportato a scopo di test e deve essere contrassegnato con 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable + Le esportazioni di test non devono essere individuabili + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usare 'SpecializedCollections.EmptyEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usare 'SpecializedCollections.EmptyEnumerable()' + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usare 'SpecializedCollections.SingletonEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usare 'SpecializedCollections.SingletonEnumerable()' + + + + Invoke the correct property to ensure correct use site diagnostics + Richiamare la proprietà corretta per garantire l'uso corretto della diagnostica del sito + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + Richiamare la proprietà corretta per garantire l'uso corretto della diagnostica del sito + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Non usare il metodo 'CodeAction.Create' per creare l'elemento 'CodeAction' + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Non usare il metodo 'CodeAction.Create' per creare l'elemento 'CodeAction' + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Eseguire l'override di 'Object.Equals(object)' durante l'implementazione di 'IEquatable' + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Eseguire l'override di 'Object.Equals(object)' durante l'implementazione di 'IEquatable' + + + + 'SymbolDeclaredEvent' must be generated for source symbols + È necessario generare 'SymbolDeclaredEvent' per i simboli di origine + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Per generare eventi dichiarati da simboli per tutti i simboli di origine dichiarati, è necessaria la coda degli eventi di compilazione. Di conseguenza ogni tipo di simbolo di origine o uno dei relativi tipi di base deve generare un evento dichiarato da simboli. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + È necessario generare 'SymbolDeclaredEvent' per i simboli di origine + + + + Do not mix attributes from different versions of MEF + Non combinare attributi di versioni diverse di MEF + + + + Do not mix attributes from different versions of MEF. + Non combinare attributi di versioni diverse di MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + L'attributo '{0}' proviene da una versione di MEF diversa rispetto all'attributo export su '{1}' + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Le parti esportate con MEFv2 devono essere contrassegnate con 'SharedAttribute' + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + La parte esportata con MEFv2 deve essere contrassegnata con 'SharedAttribute'. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}' viene esportato con MEFv2 e di conseguenza deve essere contrassegnato con 'SharedAttribute' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf new file mode 100644 index 0000000000000..f2cc7c374ab54 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ja.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + 'Shared' 属性の追加 + {Locked="Shared"} + + + Apply trait to containing type + 包含する側の型に特性を適用する + + + + Remove the 'Opt' suffix + 'Opt' サフィックスの削除 + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + null 許容型のコードでは 'Opt' サフィックスを使用しないでください。 + + + + Avoid the 'Opt' suffix in a nullable-enabled code + null 許容型のコードでは 'Opt' サフィックスを使用しません + + + + Avoid the 'Opt' suffix + 'Opt' サフィックスを使用しない + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + これは、'TestAccessor' パターンを使用してテスト アクセサーを作成するプロセスを簡略化するリファクタリングです。 + + + + Create test accessor + テスト アクセサーの作成 + + + + Create test accessor + テスト アクセサーの作成 + + + + Defaultable types should have defaultable fields. + 既定値を使用可能な型には、既定に設定可能なフィールドが必要です。 + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + 既定に設定可能な型 '{0}' に、既定に設定できないフィールドまたは自動プロパティ '{1}' が含まれています + + + + Defaultable types should have defaultable fields + 既定に設定可能な型には、既定に設定可能なフィールドが必要です + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()' は、テスト用に予約されているヘルパー メソッドです。運用コードでこのメンバーを呼び出さないでください。 + + + + Do not call 'GetTestAccessor()' from production code + 'GetTestAccessor()' を運用コードから呼び出さないでください + + + + Do not call 'GetTestAccessor()' + 'GetTestAccessor()' を呼び出さないでください + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + プライマリ コンストラクター パラメーターを暗黙的にキャプチャしてはいけません。型の先頭にあるフィールドに手動で割り当てます。 + + + + Primary constructor parameter '{0}' should not be implicitly captured + プライマリ コンストラクター パラメーター '{0}' を暗黙的にキャプチャすることはできません + + + + Do not capture primary constructor parameters + プライマリ コンストラクター パラメーターをキャプチャしない + + + + Avoid nullable wrapper. + Null 許容ラッパーを回避します。 + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + '{1}' 操作でコピー不可の型 '{0}' をラップしないでください + + + + Do not copy value. + 値をコピーしないでください。 + + + + Do not copy value + 値をコピーしないでください + + + + Cannot assign a value from a reference to a non-copyable type. + 参照からコピー不可の型に値を割り当てることはできません。 + + + + Cannot assign a value from a reference to non-copyable type '{0}' + 参照からコピー不可の型 '{0}' に値を割り当てることはできません + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + 自動プロパティでは常に値がコピーされるため、コピー不可の型を使用して宣言することはできません。 + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + 自動プロパティ '{1}' にコピー不可の型 '{0}' を指定することはできません + + + + Do not box non-copyable value types. + コピー不可の値の型をボックス化しません。 + + + + Do not box non-copyable type '{0}' + コピー不可の型 '{0}' をボックス化しないでください + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + コピー不可の型のフィールドをコピー可能な型のメンバーにすることはできません。包含する側の型をコピー不可にすることも参照型に変換することもできます。あるいは、フィールドを削除することも、コピー可能な型に変換することもできます。 + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + コピー可能なフィールド '{1}' にコピー不可の型 '{0}' を指定することはできません + + + + Cannot return a value from a reference to a non-copyable type. + 参照からコピー不可の型に値を返すことはできません。 + + + + Cannot return a value from a reference to non-copyable type '{0}' + 参照からコピー不可の型 '{0}' に値を返すことはできません + + + + Do not unbox non-copyable value types. + コピー不可の値の型をボックス化解除しないでください。 + + + + Do not unbox non-copyable type '{0}' + コピー不可の型 '{0}' をボックス化解除しないでください + + + + Do not copy value + 値をコピーしない + + + + Unsupported use of non-copyable type. + コピー不可の型の使用はサポートされていません。 + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + '{1}' 操作でコピー不可の型 '{0}' の使用はサポートされていません + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + インポート コンストラクターを明示的に定義します + + + + Add 'ImportingConstructor' attribute + 'ImportingConstructor' 属性の追加 + + + + Make constructor public + コンストラクターを公開します + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + エクスポートされたパーツには 'ImportingConstructorAttribute' を設定する必要があります。 + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' は MEF でエクスポートされており、正しい形式の単一のインポート コンストラクターが含まれている必要があります + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + エクスポートされたパーツには、'ImportingConstructorAttribute' でマークされたパブリック コンストラクターが必要です + + + + Expose member for testing. + テストのためにメンバーを公開します。 + + + + Expose member for testing + テストのためにメンバーを公開します + + + + Expose member for testing + テストのためにメンバーを公開します + + + + Fix numbered comments + 番号付きのコメントの修正 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error' を 'true' に設定してください + + + + Use correct 'ObsoleteAttribute' message. + 正しい 'ObsoleteAttribute' メッセージを使用します。 + + + + Add 'ObsoleteAttribute' + 'ObsoleteAttribute' の追加 + + + + Configure 'ObsoleteAttribute' for importing constructor. + インポート コンストラクターの 'ObsoleteAttribute' を構成します。 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error' を 'true' に設定してください + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + インポート コンストラクターには 'ObsoleteAttribute' を設定する必要があります。 + + + + Importing constructor should be marked with 'ObsoleteAttribute' + インポート コンストラクターには 'ObsoleteAttribute' を設定する必要があります + + + + Importing constructor should be marked with 'ObsoleteAttribute' + インポート コンストラクターには 'ObsoleteAttribute' を設定する必要があります + + + + Place statement on following line + 次の行にステートメントを配置する + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + 構文 'typeof(T).FullName' を使用している場合、'Type.FullName' プロパティが null になることはありません + + + + Use 'null' instead of 'default' + 'default' の代わりに 'null' を使用する + + + + Use 'null' instead of 'default' for nullable types. + Null 許容型には、'default' の代わりに 'null' を使用してください。 + + + + Use 'null' instead of 'default' for nullable types + Null 許容型には、'default' の代わりに 'null' をお使いください + + + + Prefer null literal + null リテラルを優先 + + + + Asynchronous test methods do not require the 'Async' suffix + 非同期テスト メソッドには 'Async' サフィックスは不要です + + + + Run iterations + イテレーションの実行 + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + TemporaryArray<T>.AsRef() のインスタンスは 'using' 変数でなければなりません。 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() のインスタンスは 'using' 変数でなければなりません + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() のインスタンスは 'using' 変数でなければなりません + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + 'PartNotDiscoverableAttribute' を適用します + + + + Test exports should not be discoverable. + テスト エクスポートは検出可能であってはなりません。 + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}' はテスト用にエクスポートされており、'PartNotDiscoverableAttribute' と設定する必要があります + + + + Test exports should not be discoverable + テスト エクスポートは検出可能であってはなりません + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()' を使用します + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()' を使用します + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()' を使用します + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()' を使用します + + + + Invoke the correct property to ensure correct use site diagnostics + 正しいサイト診断が使用されるように、正しいプロパティを呼び出してください + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + 正しいサイト診断が使用されるように、正しいプロパティを呼び出してください + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 'CodeAction' を作成するために、ジェネリック 'CodeAction.Create' を使用しないでください + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 'CodeAction' を作成するために、ジェネリック 'CodeAction.Create' を使用しないでください + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + IEquatable' を実装するときに 'Object.Equals(object)' をオーバーライドしてください + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + IEquatable' を実装するときに 'Object.Equals(object)' をオーバーライドしてください + + + + 'SymbolDeclaredEvent' must be generated for source symbols + ソース シンボルに対して 'SymbolDeclaredEvent' を生成する必要があります + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + すべての宣言済みソース シンボルのシンボル宣言イベントを生成するには、コンパイル イベント キューが必要です。そのため、各ソース シンボル型、またはその基本型の 1 つは、シンボル宣言イベントを生成する必要があります。 + + + + 'SymbolDeclaredEvent' must be generated for source symbols + ソース シンボルに対して 'SymbolDeclaredEvent' を生成する必要があります + + + + Do not mix attributes from different versions of MEF + 複数のバージョンの MEF の属性を混用しないでください + + + + Do not mix attributes from different versions of MEF. + 複数のバージョンの MEF の属性を混用しないでください。 + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + 属性 '{0}' は、'{1}' のエクスポート属性とは別のバージョンの MEF の属性です + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + MEFv2 でエクスポートされたパーツには 'SharedAttribute' を設定する必要があります + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + MEFv2 でエクスポートされたパーツには 'SharedAttribute' を設定する必要があります。 + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}' は MEFv2 でエクスポートされるため、'SharedAttribute' と設定する必要があります + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf new file mode 100644 index 0000000000000..2397e3754f59a --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ko.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + 'Shared' 특성 추가 + {Locked="Shared"} + + + Apply trait to containing type + 포함하는 형식에 특성 적용 + + + + Remove the 'Opt' suffix + 'Opt' 접미사 제거 + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + nullable 지원 코드에서는 'Opt' 접미사를 사용하지 마세요. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + nullable 지원 코드에서는 'Opt' 접미사를 사용하지 마세요. + + + + Avoid the 'Opt' suffix + 'Opt' 접미사 사용 방지 + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + 'TestAccessor' 패턴을 사용하여 테스트 접근자를 만드는 프로세스를 간소화하는 리팩터링입니다. + + + + Create test accessor + 테스트 접근자 만들기 + + + + Create test accessor + 테스트 접근자 만들기 + + + + Defaultable types should have defaultable fields. + 기본값 설정 가능 형식에는 기본값 설정 가능 필드가 있어야 합니다. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + 기본값 설정 가능 형식 '{0}'에 기본값 설정 불가능 필드 또는 자동 속성 '{1}'이(가) 있습니다. + + + + Defaultable types should have defaultable fields + 기본값 설정 가능 형식에는 기본값 설정 가능 필드가 있어야 함 + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()'는 테스트용으로 예약된 도우미 메서드입니다. 프로덕션 코드는 이 멤버를 호출해서는 안 됩니다. + + + + Do not call 'GetTestAccessor()' from production code + 프로덕션 코드에서 'GetTestAccessor()'를 호출하지 마세요. + + + + Do not call 'GetTestAccessor()' + 'GetTestAccessor()' 호출 금지 + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + 기본 생성자 매개 변수는 암시적으로 캡처하면 안 됩니다. 형식이 시작될 때 필드에 수동으로 할당합니다. + + + + Primary constructor parameter '{0}' should not be implicitly captured + 기본 생성자 매개 변수 '{0}'을(를) 암시적으로 캡처하면 안 됩니다. + + + + Do not capture primary constructor parameters + 기본 생성자 매개 변수 캡처 안 함 + + + + Avoid nullable wrapper. + nullable 래퍼를 사용하지 마세요. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + '{1}' 작업에서 복사할 수 없는 형식 '{0}' 래핑 안 함 + + + + Do not copy value. + 값을 복사하지 마세요. + + + + Do not copy value + 값 복사 안 함 + + + + Cannot assign a value from a reference to a non-copyable type. + 참조에서 복사할 수 없는 형식으로 값을 할당할 수 없습니다. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + 참조에서 복사할 수 없는 '{0}' 형식으로 값을 할당할 수 없습니다. + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + 자동 속성은 항상 값을 복사하므로 복사할 수 없는 형식으로 선언할 수 없습니다. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + 자동 속성 '{1}'은(는) 복사할 수 없는 형식 '{0}'을(를) 포함할 수 없음 + + + + Do not box non-copyable value types. + 복사할 수 없는 값 형식을 boxing하지 않습니다. + + + + Do not box non-copyable type '{0}' + 복사할 수 없는 형식 '{0}' boxing 안 함 + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + 복사할 수 없는 형식의 필드는 복사할 수 있는 형식의 멤버일 수 없습니다. 포함하는 형식은 복사할 수 없는 형식이 되거나 참조 형식으로 변환되거나, 필드가 제거되거나 복사할 수 있는 형식으로 변환될 수 있습니다. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + 복사할 수 있는 필드 '{1}'은(는) 복사할 수 없는 형식 '{0}'을(를) 포함할 수 없음 + + + + Cannot return a value from a reference to a non-copyable type. + 참조에서 복사할 수 없는 형식으로 값을 반환할 수 없습니다. + + + + Cannot return a value from a reference to non-copyable type '{0}' + 참조에서 복사할 수 없는 '{0}' 형식으로 값을 반환할 수 없습니다. + + + + Do not unbox non-copyable value types. + 복사할 수 없는 값 형식을 unboxing하지 않습니다. + + + + Do not unbox non-copyable type '{0}' + 복사할 수 없는 형식 '{0}' unboxing 안 함 + + + + Do not copy value + 값 복사 안 함 + + + + Unsupported use of non-copyable type. + 복사할 수 없는 형식의 사용이 지원되지 않습니다. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + '{1}' 작업에서 복사할 수 없는 형식 '{0}'의 사용이 지원되지 않습니다. + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + 명시적으로 가져오기 생성자 정의 + + + + Add 'ImportingConstructor' attribute + 'ImportingConstructor' 특성 추가 + + + + Make constructor public + 생성자를 public으로 만들기 + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + 내보낸 파트는 'ImportingConstructorAttribute'로 표시해야 합니다. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}'은(는) MEF 내보내기이며 올바른 형식의 단일 공용 가져오기 생성자가 있어야 합니다. + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + 내보낸 파트에는 'ImportingConstructorAttribute'로 표시된 공용 생성자가 있어야 합니다. + + + + Expose member for testing. + 테스트를 위해 멤버를 노출합니다. + + + + Expose member for testing + 테스트용 멤버 노출 + + + + Expose member for testing + 테스트용 멤버 노출 + + + + Fix numbered comments + 번호가 매겨진 주석 수정 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error'를 'true'로 설정 + + + + Use correct 'ObsoleteAttribute' message. + 올바른 'ObsoleteAttribute' 메시지를 사용하세요. + + + + Add 'ObsoleteAttribute' + 'ObsoleteAttribute' 추가 + + + + Configure 'ObsoleteAttribute' for importing constructor. + 가져오기 생성자를 위해 'ObsoleteAttribute'를 구성합니다. + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error'를 'true'로 설정 + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + 가져오기 생성자는 'ObsoleteAttribute'로 표시해야 합니다. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 가져오기 생성자는 'ObsoleteAttribute'로 표시해야 합니다. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 가져오기 생성자는 'ObsoleteAttribute'로 표시해야 합니다. + + + + Place statement on following line + 다음 줄에 문 배치 + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + 'typeof(T).FullName' 구문을 사용하는 경우 'Type.FullName' 속성은 null일 수 없습니다. + + + + Use 'null' instead of 'default' + 'default' 대신 'null'을 사용합니다. + + + + Use 'null' instead of 'default' for nullable types. + Null 허용 형식에 'default' 대신 'null'을 사용합니다. + + + + Use 'null' instead of 'default' for nullable types + Null 허용 형식에 'default' 대신 'null'을 사용합니다. + + + + Prefer null literal + null 리터럴을 기본으로 사용 + + + + Asynchronous test methods do not require the 'Async' suffix + 비동기 테스트 메서드에는 'Async' 접미사가 필요하지 않습니다. + + + + Run iterations + 반복 실행 + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + TemporaryArray<T>.AsRef()의 인스턴스는 'using' 변수여야 합니다. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef()의 인스턴스는 'using' 변수여야 합니다. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef()의 인스턴스는 'using' 변수여야 합니다. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + 'PartNotDiscoverableAttribute' 적용 + + + + Test exports should not be discoverable. + 테스트 내보내기는 검색할 수 없습니다. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}'은(는) 테스트용으로 내보냈으며 'PartNotDiscoverableAttribute'로 표시해야 합니다. + + + + Test exports should not be discoverable + 테스트 내보내기는 검색 가능해서는 안 됨 + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()'을 사용하세요. + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()'을 사용하세요. + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()'을 사용하세요. + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()'을 사용하세요. + + + + Invoke the correct property to ensure correct use site diagnostics + 올바른 속성을 호출하여 사이트 진단을 올바르게 사용하는지 확인하세요. + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + 올바른 속성을 호출하여 사이트 진단을 올바르게 사용하는지 확인하세요. + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 제네릭 'CodeAction.Create'를 사용하여 'CodeAction'을 만들지 마세요. + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 제네릭 'CodeAction.Create'를 사용하여 'CodeAction'을 만들지 마세요. + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + IEquatable을 구현할 때 Object.Equals(개체)를 재정의하세요. + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + IEquatable을 구현할 때 Object.Equals(개체)를 재정의하세요. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent'는 소스 기호에 대해 생성되어야 합니다. + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + 선언된 모든 소스 기호에 대해 기호가 선언된 이벤트를 생성하는 데 컴파일 이벤트 대기열이 필요합니다. 그러므로 모든 소스 기호 형식 또는 소스 기호의 기본 형식 중 하나는 기호가 선언된 이벤트를 생성해야 합니다. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent'는 소스 기호에 대해 생성되어야 합니다. + + + + Do not mix attributes from different versions of MEF + 다른 버전의 MEF 특성을 조합하지 마세요. + + + + Do not mix attributes from different versions of MEF. + 다른 버전의 MEF 특성을 조합하지 마세요. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + '{0}' 특성은 '{1}'의 export 특성이 아닌 다른 버전의 MEF에서 가져온 것입니다. + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + MEFv2를 통해 내보낸 파트는 'SharedAttribute'로 표시해야 합니다. + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + MEFv2를 통해 내보낸 파트는 'SharedAttribute'로 표시해야 합니다. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}'을(를) MEFv2를 통해 내보냈으므로 'SharedAttribute'로 표시해야 합니다. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf new file mode 100644 index 0000000000000..f4f3f15703ec6 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pl.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Dodaj atrybut „Shared” + {Locked="Shared"} + + + Apply trait to containing type + Zastosuj cechę do typu zawierającego + + + + Remove the 'Opt' suffix + Usuń sufiks „Opt” + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Unikaj sufiksu „Opt” w kodzie z możliwością nullowania. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Unikaj sufiksu „Opt” w kodzie z możliwością nullowania + + + + Avoid the 'Opt' suffix + Unikaj sufiksu „Opt” + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Jest to refaktoryzacja upraszczająca proces tworzenia testowych metod dostępu przy użyciu wzorca „TestAccessor” + + + + Create test accessor + Utwórz testową metodę dostępu + + + + Create test accessor + Utwórz testową metodę dostępu + + + + Defaultable types should have defaultable fields. + Typy dopuszczające wartość domyślną powinny mieć pola z wartościami domyślnymi. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Typ „{0}” dopuszczający wartość domyślną ma pole niedopuszczające wartości domyślnej lub automatyczną właściwość „{1}” + + + + Defaultable types should have defaultable fields + Typy dopuszczające wartość domyślną powinny mieć pola z wartościami domyślnymi + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + „GetTestAccessor()” to metoda pomocnicza zarezerwowana do testowania. Kod produkcyjny nie może wywoływać tej składowej. + + + + Do not call 'GetTestAccessor()' from production code + Nie wywołuj metody „GetTestAccessor()” z kodu produkcyjnego + + + + Do not call 'GetTestAccessor()' + Nie wywołuj metody „GetTestAccessor()” + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Podstawowe parametry konstruktora nie powinny być przechwytywane niejawnie. Przypisz je ręcznie do pól na początku typu. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Podstawowy parametr konstruktora „{0}” nie powinien być przechwytywany niejawnie + + + + Do not capture primary constructor parameters + Nie przechwytuj parametrów konstruktora podstawowego + + + + Avoid nullable wrapper. + Unikaj otoki dopuszczającej wartość null. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Nie opakowuj typu bez możliwości kopiowania „{0}” w operacji „{1}” + + + + Do not copy value. + Nie kopiuj wartości. + + + + Do not copy value + Nie kopiuj wartości + + + + Cannot assign a value from a reference to a non-copyable type. + Nie można przypisać wartości z odwołania do typu bez możliwości kopiowania. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Nie można przypisać wartości z odwołania do typu bez możliwości kopiowania „{0}” + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Właściwości automatyczne zawsze kopiują wartości, więc nie można ich zadeklarować z typami bez możliwości kopiowania. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + Właściwość automatyczna „{1}” nie może mieć typu bez możliwości kopiowania „{0}” + + + + Do not box non-copyable value types. + Nie opakowuj typów wartości bez możliwości kopiowania. + + + + Do not box non-copyable type '{0}' + Nie opakowuj typu bez możliwości kopiowania „{0}” + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Pole typie bez możliwości kopiowania nie może być składową typu z możliwością kopiowania. Typ zawierający można ustawić jako typ z możliwością kopiowania lub przekonwertować na typ referencyjny, ewentualnie można usunąć pole lub przekonwertować je na typ z możliwością kopiowania. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + Pole z możliwością kopiowania „{1}” nie może mieć typu bez możliwości kopiowania „{0}” + + + + Cannot return a value from a reference to a non-copyable type. + Nie można zwrócić wartości z odwołania do typu bez możliwości kopiowania. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Nie można zwrócić wartości z odwołania do typu bez możliwości kopiowania „{0}” + + + + Do not unbox non-copyable value types. + Nie rozpakowuj typów wartości bez możliwości kopiowania. + + + + Do not unbox non-copyable type '{0}' + Nie rozpakowuj typu bez możliwości kopiowania „{0}” + + + + Do not copy value + Nie kopiuj wartości + + + + Unsupported use of non-copyable type. + Nieobsługiwane użycie typu bez możliwości kopiowania. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Nieobsługiwane użycie typu bez możliwości kopiowania „{0}” w operacji „{1}” + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Jawnie zdefiniuj konstruktor importujący + + + + Add 'ImportingConstructor' attribute + Dodaj atrybut „ImportingConstructor” + + + + Make constructor public + Ustaw konstruktor jako publiczny + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Wyeksportowane części powinny być oznaczone za pomocą atrybutu „ImportingConstructorAttribute”. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + Element „{0}” jest eksportowany przez strukturę MEF i powinien mieć pojedynczy konstruktor importujący o właściwej postaci + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Wyeksportowane części powinny mieć konstruktora publicznego oznaczonego atrybutem „ImportingConstructorAttribute” + + + + Expose member for testing. + Udostępnij składową do testowania. + + + + Expose member for testing + Udostępnij element członkowski do testowania + + + + Expose member for testing + Udostępnij element członkowski do testowania + + + + Fix numbered comments + Popraw numerowane komentarze + + + + Set 'ObsoleteAttribute.Error' to 'true' + Ustaw parametr „ObsoleteAttribute.Error” na wartość „true” + + + + Use correct 'ObsoleteAttribute' message. + Użyj poprawnego komunikatu „ObsoleteAttribute”. + + + + Add 'ObsoleteAttribute' + Dodaj atrybut „ObsoleteAttribute” + + + + Configure 'ObsoleteAttribute' for importing constructor. + Skonfiguruj atrybut „ObsoleteAttribute” dla konstruktora importującego. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Ustaw parametr „ObsoleteAttribute.Error” na wartość „true” + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Konstruktor importu powinien być oznaczony za pomocą atrybutu „ObsoleteAttribute”. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Konstruktor importu powinien być oznaczony atrybutem „ObsoleteAttribute” + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Konstruktor importu powinien być oznaczony atrybutem „ObsoleteAttribute” + + + + Place statement on following line + Umieść instrukcję w następnym wierszu + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + Właściwość „Type.FullName” nigdy nie ma wartości null, gdy jest używana składnia „typeof(T).FullName” + + + + Use 'null' instead of 'default' + Użyj wartości „null” zamiast „default” + + + + Use 'null' instead of 'default' for nullable types. + Użyj wartości „null” zamiast „default” dla typów dopuszczających wartość null. + + + + Use 'null' instead of 'default' for nullable types + Użyj wartości „null” zamiast „default” dla typów dopuszczających wartość null + + + + Prefer null literal + Preferuj literał o wartości null + + + + Asynchronous test methods do not require the 'Async' suffix + Asynchroniczne metody testowe nie wymagają sufiksu „Async” + + + + Run iterations + Uruchom iteracje + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + Wystąpienie elementu TemporaryArray<T>.AsRef() musi być zmienną „using”. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Wystąpienie elementu TemporaryArray<T>.AsRef() musi być zmienną „using” + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Wystąpienie elementu TemporaryArray<T>.AsRef() musi być zmienną „using” + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Zastosuj atrybut „PartNotDiscoverableAttribute” + + + + Test exports should not be discoverable. + Odnajdywanie eksportów testowych nie powinno być możliwe. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + Element „{0}” jest eksportowany do celów testowych i powinien być oznaczony za pomocą atrybutu „PartNotDiscoverableAttribute” + + + + Test exports should not be discoverable + Odnajdywanie eksportów testowych nie powinno być możliwe + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Użyj metody „SpecializedCollections.EmptyEnumerable()” + + + + #N/A + ND + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Użyj metody „SpecializedCollections.EmptyEnumerable()” + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Użyj metody „SpecializedCollections.SingletonEnumerable()” + + + + #N/A + ND + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Użyj metody „SpecializedCollections.SingletonEnumerable()” + + + + Invoke the correct property to ensure correct use site diagnostics + Wywołaj odpowiednią właściwość, aby upewnić się, że diagnostyka witryny jest używana prawidłowo + + + + #N/A + ND + + + + Invoke the correct property to ensure correct use site diagnostics + Wywołaj odpowiednią właściwość, aby upewnić się, że diagnostyka witryny jest używana prawidłowo + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Nie używaj ogólnej metody „CodeAction.Create” do tworzenia elementu „CodeAction” + + + + #N/A + ND + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Nie używaj ogólnej metody „CodeAction.Create” do tworzenia elementu „CodeAction” + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Przesłoń metodę „Object.Equals(object)” przy implementowaniu interfejsu „IEquatable” + + + + #N/A + ND + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Przesłoń metodę „Object.Equals(object)” przy implementowaniu interfejsu „IEquatable” + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Element „SymbolDeclaredEvent” musi być generowany dla symboli źródłowych + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Kolejka zdarzeń kompilacji jest wymagana do generowania zdarzeń deklarowanych przy użyciu symboli dla wszystkich deklarowanych symboli źródłowych. Dlatego każdy typ symbolu źródłowego lub jeden z jego typów bazowych musi generować zdarzenie deklarowane przy użyciu symbolu. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Element „SymbolDeclaredEvent” musi być generowany dla symboli źródłowych + + + + Do not mix attributes from different versions of MEF + Nie mieszaj atrybutów z różnych wersji MEF + + + + Do not mix attributes from different versions of MEF. + Nie mieszaj atrybutów z różnych wersji warstwy MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + Atrybut „{0}” pochodzi z innej wersji MEF niż atrybut eksportu „{1}” + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Części wyeksportowane z warstwy MEFv2 muszą być oznaczone za pomocą atrybutu „SharedAttribute” + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + Część wyeksportowana z warstwy MEFv2 musi być oznaczona za pomocą atrybutu „Shared”. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + Element „{0}” jest eksportowany jako warstwa MEFv2, a więc musi być oznaczony za pomocą atrybutu „SharedAttribute” + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf new file mode 100644 index 0000000000000..79145b0733f54 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.pt-BR.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Adicionar o atributo 'Shared' + {Locked="Shared"} + + + Apply trait to containing type + Aplicar a característica ao tipo contentor + + + + Remove the 'Opt' suffix + Remover o sufixo 'Opt' + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Evite o sufixo 'Opt' em um código habilitado para anulável. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Evite o sufixo 'Opt' em um código habilitado para anulável + + + + Avoid the 'Opt' suffix + Evite o sufixo 'Opt' + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Esta é uma refatoração que simplifica o processo de criação de acessadores de teste usando o padrão 'TestAccessor'. + + + + Create test accessor + Criar acessador de teste + + + + Create test accessor + Criar acessador de teste + + + + Defaultable types should have defaultable fields. + Os tipos usados como padrão devem ter campos usados como padrão. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + O tipo usado como padrão '{0}' tem um campo não usado como padrão ou a propriedade automática '{1}' + + + + Defaultable types should have defaultable fields + Os tipos usados como padrão devem ter campos usados como padrão + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()' é um método auxiliar reservado para testes. O código de produção não deve chamar este membro. + + + + Do not call 'GetTestAccessor()' from production code + Não chamar o 'GetTestAccessor()' no código de produção + + + + Do not call 'GetTestAccessor()' + Não chamar o 'GetTestAccessor()' + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Os parâmetros do construtor primário não devem ser capturados implicitamente. Atribua-os manualmente aos campos no início do tipo. + + + + Primary constructor parameter '{0}' should not be implicitly captured + O parâmetro do construtor primário '{0}' não deve ser capturado implicitamente + + + + Do not capture primary constructor parameters + Não capturar parâmetros de construtor primário + + + + Avoid nullable wrapper. + Evite o wrapper anulável. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Não encapsular o tipo não copiável '{0}' na operação '{1}' + + + + Do not copy value. + Não copie o valor. + + + + Do not copy value + Não copiar o valor + + + + Cannot assign a value from a reference to a non-copyable type. + Não é possível atribuir um valor de uma referência a um tipo não copiável. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Não é possível atribuir um valor de uma referência ao tipo não copiável '{0}' + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + As propriedades automáticas sempre copiam valores, portanto, elas não podem ser declarados com tipos não copiáveis. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + A propriedade automática '{1}' não pode ter o tipo não copiável '{0}' + + + + Do not box non-copyable value types. + Não demarque tipos de valor não copiáveis. + + + + Do not box non-copyable type '{0}' + Não demarcar o tipo não copiável '{0}' + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Um campo com um tipo não copiável não pode ser membro de um tipo copiável. O tipo contentor pode ser convertido em não copiável ou em um tipo de referência ou o campo pode ser removido ou convertido em um tipo copiável. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + O campo copiável '{1}' não pode ter o tipo não copiável '{0}' + + + + Cannot return a value from a reference to a non-copyable type. + Não é possível retornar um valor de uma referência a um tipo não copiável. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Não é possível retornar um valor de uma referência ao tipo não copiável '{0}' + + + + Do not unbox non-copyable value types. + Não converta tipos de valor não copiáveis. + + + + Do not unbox non-copyable type '{0}' + Não converter o tipo não copiável '{0}' + + + + Do not copy value + Não copiar o valor + + + + Unsupported use of non-copyable type. + Não há suporte para o uso do tipo não copiável. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Não há suporte para o uso do tipo não copiável '{0}' na operação '{1}' + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Definir explicitamente o construtor de importação + + + + Add 'ImportingConstructor' attribute + Adicionar atributo 'ImportingConstructor' + + + + Make constructor public + Tornar o construtor público + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + As partes exportadas devem ser marcadas com 'ImportingConstructorAttribute'. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' é exportado pelo MEF e deve ter um único construtor de importação público da forma correta + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + As peças exportadas devem ter um construtor público marcado com 'ImportingConstructorAttribute' + + + + Expose member for testing. + Exponha o membro para teste. + + + + Expose member for testing + Expor membro para teste + + + + Expose member for testing + Expor membro para teste + + + + Fix numbered comments + Corrigir comentários numerados + + + + Set 'ObsoleteAttribute.Error' to 'true' + Defina 'ObsoleteAttribute.Error' como 'true' + + + + Use correct 'ObsoleteAttribute' message. + Use a mensagem 'ObsoleteAttribute' correta. + + + + Add 'ObsoleteAttribute' + Adicionar 'ObsoleteAttribute' + + + + Configure 'ObsoleteAttribute' for importing constructor. + Configure 'ObsoleteAttribute' para o construtor de importação. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Defina 'ObsoleteAttribute.Error' como 'true' + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + O construtor de importação deve ser marcado com 'ObsoleteAttribute'. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + O construtor de importação deve ser marcado com 'ObsoleteAttribute' + + + + Importing constructor should be marked with 'ObsoleteAttribute' + O construtor de importação deve ser marcado com 'ObsoleteAttribute' + + + + Place statement on following line + Colocar a instrução na linha seguinte + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + A propriedade 'Type.FullName' nunca é nula quando a sintaxe 'typeof(T).FullName' é usada + + + + Use 'null' instead of 'default' + Use 'null' em vez de 'default' + + + + Use 'null' instead of 'default' for nullable types. + Use 'null' em vez de 'default' para tipos que permitem valor nulo. + + + + Use 'null' instead of 'default' for nullable types + Use 'null' em vez de 'default' para tipos que permitem valor nulo + + + + Prefer null literal + Preferir literal nulo + + + + Asynchronous test methods do not require the 'Async' suffix + Os métodos de teste assíncronos não exigem o sufixo 'Async' + + + + Run iterations + Executar iterações + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + A instância de TemporaryArray<T>.AsRef() precisa ser uma variável 'using'. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + A instância de TemporaryArray<T>.AsRef() precisa ser uma variável 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + A instância de TemporaryArray<T>.AsRef() precisa ser uma variável 'using' + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Aplicar 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable. + As exportações de teste não devem ser detectáveis. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}' é exportado para fins de teste e deve ser marcado como 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable + As exportações de teste não devem ser detectáveis + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usar 'SpecializedCollections.EmptyEnumerable()' + + + + #N/A + #N/D + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Usar 'SpecializedCollections.EmptyEnumerable()' + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usar 'SpecializedCollections.SingletonEnumerable()' + + + + #N/A + #N/D + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Usar 'SpecializedCollections.SingletonEnumerable()' + + + + Invoke the correct property to ensure correct use site diagnostics + Invoque a propriedade correta para assegurar o uso correto de diagnósticos do site + + + + #N/A + #N/D + + + + Invoke the correct property to ensure correct use site diagnostics + Invoque a propriedade correta para assegurar o uso correto de diagnósticos do site + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Não use um 'CodeAction.Create' genérico para criar o 'CodeAction' + + + + #N/A + #N/D + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Não use um 'CodeAction.Create' genérico para criar o 'CodeAction' + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Substitua 'Object.Equals(object)' ao implementar 'IEquatable' + + + + #N/A + #N/D + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Substitua 'Object.Equals(object)' ao implementar 'IEquatable' + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent' precisa ser gerado para símbolos de origem + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + A fila de eventos de compilação deve gerar eventos declarados por símbolo para todos os símbolos de origem declarados. Portanto, cada tipo de símbolo de origem ou um de seus tipos base deve gerar um evento declarado por símbolo. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 'SymbolDeclaredEvent' precisa ser gerado para símbolos de origem + + + + Do not mix attributes from different versions of MEF + Não combinar atributos de versões diferentes de MEF + + + + Do not mix attributes from different versions of MEF. + Não combine os atributos de versões diferentes do MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + O atributo '{0}' vem de uma versão diferente de MEF do que o atributo de exportação em '{1}' + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + As partes exportadas com MEFv2 precisam ser marcadas como 'SharedAttribute' + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + A parte exportada com MEFv2 precisa ser marcada com 'SharedAttribute'. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}' é exportado com MEFv2 e, portanto, precisa ser marcado com 'SharedAttribute' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf new file mode 100644 index 0000000000000..1c0064b524fbc --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.ru.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + Добавить атрибут "Shared" + {Locked="Shared"} + + + Apply trait to containing type + Применить признак к содержащему типу + + + + Remove the 'Opt' suffix + Удалите суффикс "Opt" + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Не используйте суффикс "Opt" в коде, допускающем значения null. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Не используйте суффикс "Opt" в коде, допускающем значения null. + + + + Avoid the 'Opt' suffix + Не используйте суффикс "Opt" + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Это рефакторинг, который упрощает процесс создания методов доступа к тесту с помощью шаблона TestAccessor. + + + + Create test accessor + Создать метод доступа к тесту + + + + Create test accessor + Создать метод доступа к тесту + + + + Defaultable types should have defaultable fields. + Типы, для которых имеются значения по умолчанию, должны иметь соответствующие поля. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Тип "{0}", допускающий значения по умолчанию, имеет поле, не допускающее значений по умолчанию, или автоматическое свойство "{1}". + + + + Defaultable types should have defaultable fields + Типы, допускающие значения по умолчанию, должны иметь соответствующие поля + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + GetTestAccessor() — это вспомогательный метод, используемый только для тестирования. Не вызывайте этот метод из рабочего кода. + + + + Do not call 'GetTestAccessor()' from production code + Не вызывайте метод GetTestAccessor() из рабочего кода + + + + Do not call 'GetTestAccessor()' + Не вызывайте метод GetTestAccessor() + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Параметры первичного конструктора не должны записываться неявным образом. Вручную назначьте их полям в начале типа. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Параметр первичного конструктора "{0}" не должен записываться неявным образом + + + + Do not capture primary constructor parameters + Не записывать параметры первичного конструктора + + + + Avoid nullable wrapper. + Избегайте программы-оболочки, допускающей значение NULL. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + Не переносите в операцию "{1}" тип "{0}", не допускающий копирование. + + + + Do not copy value. + Не копируйте значение. + + + + Do not copy value + Не копировать значение + + + + Cannot assign a value from a reference to a non-copyable type. + Невозможно присвоить значение из ссылки типу, который не может быть скопирован. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + Невозможно присвоить значение из ссылки типу "{0}", который не может быть скопирован + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Автоматические свойства всегда копируют значения, поэтому их нельзя объявлять с типами, не допускающими копирование. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + Автоматическое свойство "{1}" не может иметь тип "{0}", не допускающий копирование. + + + + Do not box non-copyable value types. + Не упаковывайте типы значений, не допускающие копирование. + + + + Do not box non-copyable type '{0}' + Не следует упаковывать тип "{0}", не допускающий копирование. + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Поле с типом, не допускающим копирование, не может быть элементом типа, допускающего копирование. Содержащий тип можно сделать не допускающим копирование или преобразовать в ссылочный тип, либо можно удалить поле или преобразовать поле в тип, допускающий копирование. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + Поле "{1}", допускающее копирование, не может иметь тип "{0}", не допускающий копирование. + + + + Cannot return a value from a reference to a non-copyable type. + Невозможно вернуть значение из ссылки в тип, который не может быть скопирован. + + + + Cannot return a value from a reference to non-copyable type '{0}' + Невозможно вернуть значение из ссылки в тип "{0}", который не может быть скопирован + + + + Do not unbox non-copyable value types. + Не распаковывайте типы значений, не допускающие копирование. + + + + Do not unbox non-copyable type '{0}' + Не следует распаковывать тип "{0}", не допускающий копирование. + + + + Do not copy value + Не копировать значение + + + + Unsupported use of non-copyable type. + Неподдерживаемое использование типа, не допускающего копирование. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + Неподдерживаемое использование типа "{0}", не допускающего копирование, в операции "{1}" + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + Явно определите конструктор импорта. + + + + Add 'ImportingConstructor' attribute + Добавить атрибут "ImportingConstructor" + + + + Make constructor public + Сделать конструктор открытым + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Экспортированные части должны быть помечены как ImportingConstructorAttribute. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + "{0}" экспортируется в MEF и должен содержать единственный общедоступный конструктор импорта правильной формы. + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Экспортированные части должны содержать общедоступный конструктор, помеченный как "ImportingConstructorAttribute" + + + + Expose member for testing. + Предоставьте элемент для тестирования + + + + Expose member for testing + Предоставить элемент для тестирования + + + + Expose member for testing + Предоставление элемента для тестирования + + + + Fix numbered comments + Исправление нумерованных комментариев + + + + Set 'ObsoleteAttribute.Error' to 'true' + Присвойте ObsoleteAttribute.Error значение true + + + + Use correct 'ObsoleteAttribute' message. + Используйте правильное сообщение ObsoleteAttribute. + + + + Add 'ObsoleteAttribute' + Добавьте ObsoleteAttribute + + + + Configure 'ObsoleteAttribute' for importing constructor. + Настройте ObsoleteAttribute для конструктора импорта. + + + + Set 'ObsoleteAttribute.Error' to 'true' + Присвойте ObsoleteAttribute.Error значение true + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + Конструктор импорта должен быть помечен как ObsoleteAttribute. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Конструктор импорта должен быть помечен как ObsoleteAttribute + + + + Importing constructor should be marked with 'ObsoleteAttribute' + Конструктор импорта должен быть помечен как ObsoleteAttribute + + + + Place statement on following line + Размещайте оператор на отдельной строке + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + Свойство "Type.FullName" не может иметь значение null при использовании синтаксиса "typeof(T).FullName". + + + + Use 'null' instead of 'default' + Используйте "null" вместо "default". + + + + Use 'null' instead of 'default' for nullable types. + Используйте "null" вместо "default" для типов, допускающих значение NULL. + + + + Use 'null' instead of 'default' for nullable types + Используйте "null" вместо "default" для типов, допускающих значение NULL. + + + + Prefer null literal + Используйте литерал null + + + + Asynchronous test methods do not require the 'Async' suffix + Асинхронные методы теста не требуют суффикса Async + + + + Run iterations + Выполнить итерации + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + Экземпляр TemporaryArray<T>.AsRef() должен представлять собой переменную "using". + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Экземпляр TemporaryArray<T>.AsRef() должен представлять собой переменную "using". + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + Экземпляр TemporaryArray<T>.AsRef() должен представлять собой переменную "using". + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + Применить PartNotDiscoverableAttribute + + + + Test exports should not be discoverable. + Тестовые экспорты не должны быть доступными для обнаружения. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + "{0}" экспортируется в целях тестирования и должен быть помечен как PartNotDiscoverableAttribute + + + + Test exports should not be discoverable + Тестовые экспорты не должны быть доступными для обнаружения + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Используйте SpecializedCollections.EmptyEnumerable() + + + + #N/A + #Н/Д + + + + Use 'SpecializedCollections.EmptyEnumerable()' + Используйте SpecializedCollections.EmptyEnumerable() + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Используйте SpecializedCollections.SingletonEnumerable() + + + + #N/A + #Н/Д + + + + Use 'SpecializedCollections.SingletonEnumerable()' + Используйте SpecializedCollections.SingletonEnumerable() + + + + Invoke the correct property to ensure correct use site diagnostics + Вызовите подходящее свойство, чтобы обеспечить правильное использование диагностики сайта + + + + #N/A + #Н/Д + + + + Invoke the correct property to ensure correct use site diagnostics + Вызовите подходящее свойство, чтобы обеспечить правильное использование диагностики сайта + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Не используйте универсальный метод CodeAction.Create для создания CodeAction + + + + #N/A + #Н/Д + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + Не используйте универсальный метод CodeAction.Create для создания CodeAction + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Переопределите Object.Equals(object) при реализации IEquatable + + + + #N/A + #Н/Д + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + Переопределите Object.Equals(object) при реализации IEquatable + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Для исходных символов необходимо создать SymbolDeclaredEvent + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Чтобы создать объявляемые символом события для всех объявленных исходных символов, нужна очередь событий компиляции. Поэтому каждый тип исходных символов или один из его базовых типов должны создавать объявляемое символом событие. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Для исходных символов необходимо создать SymbolDeclaredEvent + + + + Do not mix attributes from different versions of MEF + Не смешивайте атрибуты из разных версий MEF + + + + Do not mix attributes from different versions of MEF. + Не смешивайте атрибуты из разных версий MEF. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + Атрибут "{0}" отличается версией MEF от атрибута экспорта в "{1}" + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + Части, экспортированные с помощью MEF версии 2, должны быть помечены как SharedAttribute + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + Часть, экспортированная с помощью MEF версии 2, должна быть помечена как SharedAttribute. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + Часть "{0}" экспортирована с помощью MEF версии 2 и поэтому должна быть помечена как SharedAttribute + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf new file mode 100644 index 0000000000000..545fc59c7af0f --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.tr.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + 'Shared' özniteliği ekleme + {Locked="Shared"} + + + Apply trait to containing type + İçeren türe özellik uygulama + + + + Remove the 'Opt' suffix + 'Opt' sonekini kaldır + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + Null atanabilir özellikli bir kodda 'Opt' sonekinden kaçının. + + + + Avoid the 'Opt' suffix in a nullable-enabled code + Null atanabilir özellikli bir kodda 'Opt' sonekinden kaçının + + + + Avoid the 'Opt' suffix + 'Opt' sonekinden kaçının + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + Bu, 'TestAccessor' desenini kullanarak test erişimcileri oluşturma işlemini basitleştiren bir yeniden düzenlemedir. + + + + Create test accessor + Test erişimcisi oluştur + + + + Create test accessor + Test erişimcisi oluştur + + + + Defaultable types should have defaultable fields. + Varsayılan değer atanabilir türler, varsayılan değer atanabilir alanlara sahip olmalıdır. + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + Varsayılan değer atanabilir '{0}' türü, varsayılan değer atanamayan alana veya otomatik '{1}' özelliğine sahip + + + + Defaultable types should have defaultable fields + Varsayılan değer atanabilir türler varsayılan değer atanabilir alanlara sahip olmalıdır + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()', test için ayrılmış bir yardımcı metottur. Üretim kodu bu üyeyi çağırmamalıdır. + + + + Do not call 'GetTestAccessor()' from production code + Üretim kodundan 'GetTestAccessor()' metodunu çağırmayın + + + + Do not call 'GetTestAccessor()' + 'GetTestAccessor()' metodunu çağırmayın + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + Birincil oluşturucu parametreleri örtük olarak yakalanmamalıdır. Bunları türün başındaki alanlara el ile atayın. + + + + Primary constructor parameter '{0}' should not be implicitly captured + Birincil oluşturucu parametre '{0}' örtük olarak yakalanmamalıdır + + + + Do not capture primary constructor parameters + Birincil oluşturucu parametrelerini yakalama + + + + Avoid nullable wrapper. + Null atanabilir sarmalayıcıdan kaçının. + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + '{1}' işleminde kopyalanabilir olmayan '{0}' türünü sarmalamayın + + + + Do not copy value. + Değeri kopyalamayın. + + + + Do not copy value + Değeri kopyalamayın + + + + Cannot assign a value from a reference to a non-copyable type. + Kopyalanamayan türe yönelik bir başvurudan değer atanamaz. + + + + Cannot assign a value from a reference to non-copyable type '{0}' + '{0}' kopyalanamayan türüne yönelik bir başvurudan değer atanamaz + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + Otomatik özellikler her zaman değerleri kopyalar, bu nedenle kopyalanabilir olmayan türlerle bildirilemez. + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + '{1}' otomatik özelliği kopyalanabilir olmayan '{0}' türüne sahip olamaz + + + + Do not box non-copyable value types. + Kopyalanabilir olmayan değer türlerini kutuya eklemeyin. + + + + Do not box non-copyable type '{0}' + Kopyalanabilir olmayan '{0}' türünü kutuya eklemeyin + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + Kopyalanabilir olmayan bir tür içeren alan, kopyalanabilir türün bir üyesi olamaz. Kapsayan tür, kopyalanabilir olmayan hale getirilebilir veya bir başvuru türüne dönüştürülebilir ya da alan kaldırılabilir ya da kopyalanabilir bir türe dönüştürülebilir. + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + '{1}' kopyalanabilir alanı, kopyalanabilir olmayan '{0}' türüne sahip olamaz + + + + Cannot return a value from a reference to a non-copyable type. + Kopyalanamayan türe yönelik bir başvurudan değer döndürülemez. + + + + Cannot return a value from a reference to non-copyable type '{0}' + '{0}' kopyalanamayan türüne yönelik bir başvurudan değer döndürülemez + + + + Do not unbox non-copyable value types. + Kopyalanabilir olmayan değer türlerini kutuya eklemeyin. + + + + Do not unbox non-copyable type '{0}' + Kopyalanabilir olmayan '{0}' türünü kutudan çıkarmayın + + + + Do not copy value + Değeri kopyalamayın + + + + Unsupported use of non-copyable type. + Kopyalanabilir olmayan türün kullanımı desteklenmiyor. + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + '{1}' işleminde kopyalanabilir olmayan '{0}' türünün kullanımı desteklenmiyor + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + İçeri aktarma oluşturucusunu açıkça tanımlayın + + + + Add 'ImportingConstructor' attribute + 'ImportingConstructor' özniteliği ekle + + + + Make constructor public + Oluşturucuyu genel yapın + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + Dışarı aktarılan bölümler 'ImportingConstructorAttribute' ile işaretlenmelidir. + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}', MEF ile dışarı aktarıldı ve doğru biçimde tek bir ortak içeri aktarma oluşturucusuna sahip olmalıdır + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + Dışarı aktarılan bölümler 'ImportingConstructorAttribute' ile işaretlenmiş bir ortak oluşturucuya sahip olmalıdır + + + + Expose member for testing. + Test için üyeyi kullanıma sunun. + + + + Expose member for testing + Test için üyeyi kullanıma sunun + + + + Expose member for testing + Test için üyeyi kullanıma sunun + + + + Fix numbered comments + Numaralı açıklamaları düzelt + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error' özelliğini 'true' olarak ayarlayın + + + + Use correct 'ObsoleteAttribute' message. + Doğru 'ObsoleteAttribute' iletisini kullanın. + + + + Add 'ObsoleteAttribute' + 'ObsoleteAttribute' ekle + + + + Configure 'ObsoleteAttribute' for importing constructor. + İçeri aktarma oluşturucusu için 'ObsoleteAttribute' özniteliğini yapılandırın. + + + + Set 'ObsoleteAttribute.Error' to 'true' + 'ObsoleteAttribute.Error' özelliğini 'true' olarak ayarlayın + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + İçeri aktarma oluşturucusunun 'ObsoleteAttribute' ile işaretlenmesi gerekir. + + + + Importing constructor should be marked with 'ObsoleteAttribute' + İçeri aktarma oluşturucusunun 'ObsoleteAttribute' ile işaretlenmesi gerekir + + + + Importing constructor should be marked with 'ObsoleteAttribute' + İçeri aktarma oluşturucusunun 'ObsoleteAttribute' ile işaretlenmesi gerekir + + + + Place statement on following line + Aşağıdaki satıra ifadeyi yerleştirin + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + 'typeof(T).FullName' söz dizimi kullanılırken 'Type.FullName' özelliği hiçbir zaman null olmaz. + + + + Use 'null' instead of 'default' + 'default' yerine 'null' kullanın + + + + Use 'null' instead of 'default' for nullable types. + Başvuru türleri için 'default' yerine 'null' kullanın. + + + + Use 'null' instead of 'default' for nullable types + Başvuru türleri için 'default' yerine 'null' kullan + + + + Prefer null literal + Null sabit değerini tercih et + + + + Asynchronous test methods do not require the 'Async' suffix + Asenkron test metotları için 'Async' soneki gerekmez + + + + Run iterations + Yinelemeleri çalıştır + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + TemporaryArray<T>.AsRef() örneği bir 'using' değişkeni olmalıdır. + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() örneği bir 'using' değişkeni olmalıdır + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() örneği bir 'using' değişkeni olmalıdır + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + 'PartNotDiscoverableAttribute' uygulayın + + + + Test exports should not be discoverable. + Test dışarı aktarmaları bulunabilir olmamalıdır. + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + '{0}', test amaçları doğrultusunda dışarı aktarıldığından 'PartNotDiscoverableAttribute' ile işaretlenmelidir + + + + Test exports should not be discoverable + Test dışarı aktarmaları bulunabilir olmamalıdır + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()' kullanın + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 'SpecializedCollections.EmptyEnumerable()' kullanın + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()' kullanın + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 'SpecializedCollections.SingletonEnumerable()' kullanın + + + + Invoke the correct property to ensure correct use site diagnostics + Doğru site tanılamalarının kullanıldığından emin olmak için doğru özelliği çağırın + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + Doğru site tanılamalarının kullanıldığından emin olmak için doğru özelliği çağırın + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 'CodeAction' oluşturmak için genel 'CodeAction.Create' kullanmayın + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 'CodeAction' oluşturmak için genel 'CodeAction.Create' kullanmayın + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 'IEquatable' uygularken 'Object.Equals(object)' özniteliğini geçersiz kılın + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 'IEquatable' uygularken 'Object.Equals(object)' özniteliğini geçersiz kılın + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Kaynak semboller için 'SymbolDeclaredEvent' oluşturulmalıdır + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + Tüm bildirilen kaynak semboller için sembolle bildirilen olaylar oluşturmak üzere derleme olay kuyruğu gereklidir. Bu nedenle, her kaynak sembol türünün veya temel türlerinden birinin sembolle bildirilen bir olay oluşturması gerekir. + + + + 'SymbolDeclaredEvent' must be generated for source symbols + Kaynak semboller için 'SymbolDeclaredEvent' oluşturulmalıdır + + + + Do not mix attributes from different versions of MEF + Farklı MEF sürümlerine ait öznitelikleri karışık kullanmayın + + + + Do not mix attributes from different versions of MEF. + Farklı MEF sürümlerindeki öznitelikleri karışık olarak kullanmayın. + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + '{0}' özniteliği, '{1}' üzerindeki dışarı aktarma özniteliğinden farklı bir MEF sürümüne ait + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + MEFv2 ile dışarı aktarılan parçalar 'SharedAttribute' ile işaretlenmelidir + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + MEFv2 ile dışarı aktarılan parça 'SharedAttribute' ile işaretlenmelidir. + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + '{0}', MEFv2 ile dışarı aktarıldığından 'SharedAttribute' ile işaretlenmelidir + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf new file mode 100644 index 0000000000000..fbc3771d05223 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hans.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + 添加 "Shared" 属性 + {Locked="Shared"} + + + Apply trait to containing type + 将特征应用于包含类型 + + + + Remove the 'Opt' suffix + 请删除 "Opt" 后缀 + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + 请避免在可为 null 的代码中使用 "Opt" 后缀。 + + + + Avoid the 'Opt' suffix in a nullable-enabled code + 请避免在启用了 null 的代码中使用 "Opt" 后缀 + + + + Avoid the 'Opt' suffix + 请避免使用 "Opt" 后缀 + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + 这是一种重构,它简化了使用 "TestAccessor" 模式创建测试访问器的过程。 + + + + Create test accessor + 创建测试访问器 + + + + Create test accessor + 创建测试访问器 + + + + Defaultable types should have defaultable fields. + 默认值可用的类型应具有默认值可用的字段。 + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + 可默认类型“{0}”具有不可默认字段或自动属性“{1}” + + + + Defaultable types should have defaultable fields + 可默认类型应具有可默认字段。 + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + "GetTestAccessor()" 是保留用于测试的 helper 方法。生产代码不得调用此成员。 + + + + Do not call 'GetTestAccessor()' from production code + 请勿从生产代码中调用 "GetTestAccessor()" + + + + Do not call 'GetTestAccessor()' + 请勿调用 "GetTestAccessor()" + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + 不应隐式捕获主构造函数参数。手动将它们分配给类型开头的字段。 + + + + Primary constructor parameter '{0}' should not be implicitly captured + 不应隐式捕获主构造函数参数"{0}" + + + + Do not capture primary constructor parameters + 不要捕获主构造函数参数 + + + + Avoid nullable wrapper. + 避免可为 null 的包装器。 + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + 请勿包装“{1}”操作中不可复制的类型“{0}” + + + + Do not copy value. + 不复制值。 + + + + Do not copy value + 不复制值 + + + + Cannot assign a value from a reference to a non-copyable type. + 无法从对不可复制类型的引用中分配值。 + + + + Cannot assign a value from a reference to non-copyable type '{0}' + 无法从对不可复制类型“{0}”的引用中分配值 + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + 自动属性始终会复制值,因此不能使用不可复制的类型声明它们。 + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + 自动属性“{1}”不能具有不可复制的类型“{0}” + + + + Do not box non-copyable value types. + 请勿对不可复制的值类型装箱。 + + + + Do not box non-copyable type '{0}' + 请勿对不可复制的类型“{0}”装箱 + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + 具有不可复制的类型的字段不能是可复制的类型的成员。可将包含类型设置为不可复制或转换为引用类型,或者可将该字段删除或转换为可复制的类型。 + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + 可复制的字段“{1}”不能具有不可复制的类型“{0}” + + + + Cannot return a value from a reference to a non-copyable type. + 无法从对不可复制类型的引用中返回值。 + + + + Cannot return a value from a reference to non-copyable type '{0}' + 无法从对不可复制类型“{0}”的引用中返回值 + + + + Do not unbox non-copyable value types. + 请勿对不可复制的值类型取消装箱。 + + + + Do not unbox non-copyable type '{0}' + 请勿对不可复制的类型“{0}”取消装箱 + + + + Do not copy value + 不复制值 + + + + Unsupported use of non-copyable type. + 不支持使用不可复制的类型。 + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + 不支持在“{1}”操作中使用不可复制的类型“{0}” + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + 显式定义导入构造函数 + + + + Add 'ImportingConstructor' attribute + 添加 "ImportingConstructor" 特性 + + + + Make constructor public + 使构造函数成为公共构造函数 + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + 导出的部件应使用 "ImportingConstructorAttribute" 标记。 + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + “{0}”是 MEF 导出的部件,应具有正确形式的单个公共导入构造函数 + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + 导出的部件应具有标记为“ImportingConstructorAttribute”的公共构造函数 + + + + Expose member for testing. + 公开要进行测试的成员。 + + + + Expose member for testing + 公开要进行测试的成员 + + + + Expose member for testing + 公开要进行测试的成员 + + + + Fix numbered comments + 修复带编号的注释 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 将 "ObsoleteAttribute.Error" 设置为 "true" + + + + Use correct 'ObsoleteAttribute' message. + 使用正确的 "ObsoleteAttribute" 消息。 + + + + Add 'ObsoleteAttribute' + 添加 "ObsoleteAttribute" + + + + Configure 'ObsoleteAttribute' for importing constructor. + 配置 "ObsoleteAttribute" 以导入构造函数。 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 将 "ObsoleteAttribute.Error" 设置为 "true" + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + 导入构造函数应使用 "System.obsoleteattribute" 标记。 + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 导入构造函数应使用 "ObsoleteAttribute" 标记 + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 导入构造函数应使用 "ObsoleteAttribute" 标记 + + + + Place statement on following line + 将语句另起一行 + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + 使用语法 "typeof(T).FullName" 时,"Type.FullName" 属性不会为 null + + + + Use 'null' instead of 'default' + 使用 "null" 而不是“默认值” + + + + Use 'null' instead of 'default' for nullable types. + 对可为 null 类型使用 "null" 而不是默认值。 + + + + Use 'null' instead of 'default' for nullable types + 对可为 null 类型使用 "null" 而不是默认值 + + + + Prefer null literal + 首选 null 文本 + + + + Asynchronous test methods do not require the 'Async' suffix + 异步测试方法不需要 "Async" 后缀 + + + + Run iterations + 运行迭代 + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + TemporaryArray<T>.AsRef() 的实例必须是 "using" 变量。 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() 的实例必须是 "using" 变量 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() 的实例必须是 "using" 变量 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + 应用 "PartNotDiscoverableAttribute" + + + + Test exports should not be discoverable. + 测试导出应该不可发现。 + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + 导出“{0}”以用于测试,并且应使用 "PartNotDiscoverableAttribute" 将其进行标记 + + + + Test exports should not be discoverable + 测试导出应该不可被发现 + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 使用 "SpecializedCollections.EmptyEnumerable()" + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 使用 "SpecializedCollections.EmptyEnumerable()" + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 使用 "SpecializedCollections.SingletonEnumerable()" + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 使用 "SpecializedCollections.SingletonEnumerable()" + + + + Invoke the correct property to ensure correct use site diagnostics + 调用正确的属性以确保正确使用站点诊断 + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + 调用正确的属性以确保正确使用站点诊断 + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 不要使用通用的 "CodeAction.Create" 来创建 "CodeAction" + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 不要使用通用的 "CodeAction.Create" 来创建 "CodeAction" + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 当实现 "IEquatable" 时,重写 "Object.Equals(object)" + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 当实现 "IEquatable" 时,重写 "Object.Equals(object)" + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 必须为源符号生成 "SymbolDeclaredEvent" + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + 要为声明的所有源符号生成符号声明事件,需使用编译事件队列。因此,每个源符号类型或其任一基类型必须生成符号声明事件。 + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 必须为源符号生成 "SymbolDeclaredEvent" + + + + Do not mix attributes from different versions of MEF + 切勿混合不同版本 MEF 中的属性 + + + + Do not mix attributes from different versions of MEF. + 切勿混合不同版本 MEF 中的属性。 + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + 属性“{0}”来自与“{1}”上的导出属性不同的 MEF 版本 + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + 通过 MEFv2 导出的部件必须使用 "SharedAttribute" 标记 + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + 通过 MEFv2 导出的部件必须使用 "SharedAttribute" 标记。 + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + “{0}”是通过 MEFv2 导出的,因此必须使用 "SharedAttribute" 标记 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf new file mode 100644 index 0000000000000..00ebde2127ee5 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Core/xlf/RoslynDiagnosticsAnalyzersResources.zh-Hant.xlf @@ -0,0 +1,497 @@ + + + + + + Add 'Shared' attribute + 新增 'Shared' 屬性 + {Locked="Shared"} + + + Apply trait to containing type + 將特徵套用至包含的型別 + + + + Remove the 'Opt' suffix + 移除 'Opt' 尾碼 + + + + Avoid the 'Opt' suffix in a nullable-enabled code. + 避免在可為 null 的程式碼中使用 'Opt' 尾碼。 + + + + Avoid the 'Opt' suffix in a nullable-enabled code + 避免在可為 null 的程式碼中使用 'Opt' 尾碼 + + + + Avoid the 'Opt' suffix + 避免 'Opt' 尾碼 + + + + This is a refactoring which simplifies the process of creating test accessors using the 'TestAccessor' pattern. + 此為重構作業,可簡化使用 'TestAccessor' 模式建立測試存取子的流程。 + + + + Create test accessor + 建立測試存取子 + + + + Create test accessor + 建立測試存取子 + + + + Defaultable types should have defaultable fields. + 可設為預設的類型應具有可設為預設的欄位。 + + + + Defaultable type '{0}' has a non-defaultable field or auto-property '{1}' + 可設定預設值的類型 '{0}',有無法設定預設值的欄位或自動屬性 '{1}' + + + + Defaultable types should have defaultable fields + 可設定預設值的類型應要有可設定預設值的欄位 + + + + 'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + 'GetTestAccessor()' 是保留供測試的協助程式方法。實際執行程式碼不得呼叫此成員。 + + + + Do not call 'GetTestAccessor()' from production code + 請勿從實際執行程式碼呼叫 'GetTestAccessor()' + + + + Do not call 'GetTestAccessor()' + 請勿呼叫 'GetTestAccessor()' + + + + Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + 不應隱含擷取主要建構函式參數。在型別的開頭,手動將它們指派給欄位。 + + + + Primary constructor parameter '{0}' should not be implicitly captured + 不應隱含擷取主要建構函式參數 '{0}'。 + + + + Do not capture primary constructor parameters + 請勿擷取主要建構函式參數 + + + + Avoid nullable wrapper. + 避免使用可為 Null 的包裝函式。 + + + + Do not wrap non-copyable type '{0}' in '{1}' operation + 請勿在 '{1}' 作業中包裝無法複製的類型 '{0}' + + + + Do not copy value. + 請勿複製值。 + + + + Do not copy value + 請勿複製值 + + + + Cannot assign a value from a reference to a non-copyable type. + 無法將參考的值指派到無法複製的類型。 + + + + Cannot assign a value from a reference to non-copyable type '{0}' + 無法將參考的值指派到無法複製的類型 '{0}' + + + + Auto-properties always copy values, so they cannot be declared with non-copyable types. + 自動屬性一律會複製值,因此不得以不可複製型別宣告。 + + + + Auto-property '{1}' cannot have non-copyable type '{0}' + 自動屬性 '{1}' 不得具有不可複製型別 '{0}' + + + + Do not box non-copyable value types. + 請勿對無法複製的值類型進行 Box 處理。 + + + + Do not box non-copyable type '{0}' + 請勿對無法複製的類型 '{0}' 進行 Box 處理 + + + + A field with a non-copyable type cannot be a member of a copyable type. The containing type can be made non-copyable or converted to a reference type, or the field can be removed or converted to a copyable type. + 具有不可複製型別的欄位,不能是可複製型別的成員。可將包含的型別設為不可複製或轉換為參考型別,或將欄位移除或轉換為可複製型別。 + + + + Copyable field '{1}' cannot have non-copyable type '{0}' + 可複製欄位 '{1}' 不能具有不可複製型別 '{0}' + + + + Cannot return a value from a reference to a non-copyable type. + 無法將參考的值傳回無法複製的類型。 + + + + Cannot return a value from a reference to non-copyable type '{0}' + 無法將參考的值傳回無法複製的類型 '{0}' + + + + Do not unbox non-copyable value types. + 請勿對無法複製的值類型進行 Unbox 處理。 + + + + Do not unbox non-copyable type '{0}' + 請勿對無法複製的類型 '{0}' 進行 Unbox 處理 + + + + Do not copy value + 請勿複製值 + + + + Unsupported use of non-copyable type. + 不支援使用無法複製的類型。 + + + + Unsupported use of non-copyable type '{0}' in '{1}' operation + 不支援在 '{1}' 作業中使用無法複製的類型 '{0}' + + + + Use 'RoslynDebug.Assert'. + Use 'RoslynDebug.Assert'. + + + + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + 'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + + + + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + Do not use interpolated strings with 'Debug.Assert'. Use 'RoslynDebug.Assert' instead. + + + + Do not use interpolated strings with 'Debug.Assert' + Do not use interpolated strings with 'Debug.Assert' + + + + Explicitly define the importing constructor + 明確定義匯入建構函式 + + + + Add 'ImportingConstructor' attribute + 新增 'ImportingConstructor' 屬性 + + + + Make constructor public + 將建構函式設為公用 + + + + Exported parts should be marked with 'ImportingConstructorAttribute'. + 匯出的組件應標記為 'ImportingConstructorAttribute'。 + + + + '{0}' is MEF-exported and should have a single, public importing constructor of the correct form + '{0}' 屬於 MEF 匯出,應有格式正確的單一且公用之匯入建構函式 + + + + Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + 匯出的組件應擁有標記為 'ImportingConstructorAttribute' 的公用建構函式 + + + + Expose member for testing. + 公開要進行測試的成員。 + + + + Expose member for testing + 公開要測試的成員 + + + + Expose member for testing + 公開要測試的成員 + + + + Fix numbered comments + 修正已編號的註解 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 將 'ObsoleteAttribute.Error' 設定為 'true' + + + + Use correct 'ObsoleteAttribute' message. + 使用正確的 'ObsoleteAttribute' 訊息。 + + + + Add 'ObsoleteAttribute' + 新增 'ObsoleteAttribute' + + + + Configure 'ObsoleteAttribute' for importing constructor. + 請設定 'ObsoleteAttribute' 以匯入建構函式。 + + + + Set 'ObsoleteAttribute.Error' to 'true' + 將 'ObsoleteAttribute.Error' 設定為 'true' + + + + Importing constructor should be marked with 'ObsoleteAttribute'. + 匯入的建構函式應標記為 'ObsoleteAttribute'。 + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 匯入的建構函式應標記為 'ObsoleteAttribute' + + + + Importing constructor should be marked with 'ObsoleteAttribute' + 匯入的建構函式應標記為 'ObsoleteAttribute' + + + + Place statement on following line + 將陳述式放在下一行 + + + + The 'Type.FullName' property is never null when using the syntax 'typeof(T).FullName' + 使用語法 'typeof(T).FullName' 時,'Type.FullName' 屬性絕不會為 Null + + + + Use 'null' instead of 'default' + 使用 'null',而不要使用 'default' + + + + Use 'null' instead of 'default' for nullable types. + 對可為 Null 的型別使用 'null',而不要使用 'default'。 + + + + Use 'null' instead of 'default' for nullable types + 對可為 Null 的型別使用 'null',而不要使用 'default' + + + + Prefer null literal + 建議使用 Null 常值 + + + + Asynchronous test methods do not require the 'Async' suffix + 非同步測試方法不需要 'Async' 尾碼 + + + + Run iterations + 執行反覆項目 + + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable. + TemporaryArray<T>.AsRef() 的執行個體必須是 'using' 變數。 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() 的執行個體必須是 'using' 變數 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Instance of TemporaryArray<T>.AsRef() must be a 'using' variable + TemporaryArray<T>.AsRef() 的執行個體必須是 'using' 變數 + {Locked="TemporaryArray<T>.AsRef()"}{Locked="using"} + + + Apply 'PartNotDiscoverableAttribute' + 套用 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable. + 應無法搜尋到測試匯出。 + + + + '{0}' is exported for test purposes and should be marked with 'PartNotDiscoverableAttribute' + 匯出 '{0}' 之目的僅供測試之用,因此應標記為 'PartNotDiscoverableAttribute' + + + + Test exports should not be discoverable + 測試匯出不應為可搜尋的 + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 使用 'SpecializedCollections.EmptyEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.EmptyEnumerable()' + 使用 'SpecializedCollections.EmptyEnumerable()' + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 使用 'SpecializedCollections.SingletonEnumerable()' + + + + #N/A + #N/A + + + + Use 'SpecializedCollections.SingletonEnumerable()' + 使用 'SpecializedCollections.SingletonEnumerable()' + + + + Invoke the correct property to ensure correct use site diagnostics + 請叫用正確的屬性,確保網站診斷的使用正確 + + + + #N/A + #N/A + + + + Invoke the correct property to ensure correct use site diagnostics + 請叫用正確的屬性,確保網站診斷的使用正確 + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 請勿使用泛型 'CodeAction.Create' 來建立 'CodeAction' + + + + #N/A + #N/A + + + + Do not use generic 'CodeAction.Create' to create 'CodeAction' + 請勿使用泛型 'CodeAction.Create' 來建立 'CodeAction' + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 實作 'IEquatable' 時覆寫 'Object.Equals(object)' + + + + #N/A + #N/A + + + + Override 'Object.Equals(object)' when implementing 'IEquatable' + 實作 'IEquatable' 時覆寫 'Object.Equals(object)' + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 必須為來源符號產生 'SymbolDeclaredEvent' + + + + Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + 必須要有編譯事件佇列,才可為所有宣告的來源符號產生符號宣告事件。因此,每個來源符號類型或它的其中一個基底類型,必須產生符號宣告事件。 + + + + 'SymbolDeclaredEvent' must be generated for source symbols + 必須為來源符號產生 'SymbolDeclaredEvent' + + + + Do not mix attributes from different versions of MEF + 請勿混合來自不同 MEF 版本的屬性 + + + + Do not mix attributes from different versions of MEF. + 請勿混合來自不同 MEF 版本的屬性。 + + + + Attribute '{0}' comes from a different version of MEF than the export attribute on '{1}' + 屬性 '{0}' 來自與 '{1}' 上的匯出屬性不同的 MEF 版本 + + + + Parts exported with MEFv2 must be marked with 'SharedAttribute' + 使用 MEFv2 匯出的組件,必須標記為 'SharedAttribute' + + + + Part exported with MEFv2 must be marked with the 'SharedAttribute'. + 使用 MEFv2 匯出的組件,必須標記為 'SharedAttribute'。 + + + + '{0}' is exported with MEFv2 and hence must be marked with 'SharedAttribute' + 匯出 '{0}' 時使用的是 MEFv2,因此必須標記為 'SharedAttribute' + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.md new file mode 100644 index 0000000000000..e38b6e036b9ed --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.md @@ -0,0 +1,205 @@ +# Roslyn.Diagnostics.Analyzers + +## RS0001: Use 'SpecializedCollections.EmptyEnumerable()' + +Use 'SpecializedCollections.EmptyEnumerable()' + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0002: Use 'SpecializedCollections.SingletonEnumerable()' + +Use 'SpecializedCollections.SingletonEnumerable()' + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsPerformance| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0004: Invoke the correct property to ensure correct use site diagnostics + +Invoke the correct property to ensure correct use site diagnostics + +|Item|Value| +|-|-| +|Category|Usage| +|Enabled|False| +|Severity|Error| +|CodeFix|False| +--- + +## RS0006: Do not mix attributes from different versions of MEF + +Do not mix attributes from different versions of MEF. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0019: 'SymbolDeclaredEvent' must be generated for source symbols + +Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|False| +|Severity|Error| +|CodeFix|False| +--- + +## RS0023: Parts exported with MEFv2 must be marked with 'SharedAttribute' + +Part exported with MEFv2 must be marked with the 'SharedAttribute'. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0032: Test exports should not be discoverable + +Test exports should not be discoverable. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0033: Importing constructor should be marked with 'ObsoleteAttribute' + +Importing constructor should be marked with 'ObsoleteAttribute'. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0034: Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' + +Exported parts should be marked with 'ImportingConstructorAttribute'. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0038: Prefer null literal + +Use 'null' instead of 'default' for nullable types. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsMaintainability| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0040: Defaultable types should have defaultable fields + +Defaultable types should have defaultable fields. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0042: Do not copy value + +Auto-properties always copy values, so they cannot be declared with non-copyable types. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0043: Do not call 'GetTestAccessor()' + +'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsMaintainability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0046: Avoid the 'Opt' suffix + +Avoid the 'Opt' suffix in a nullable-enabled code. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsDesign| +|Enabled|True| +|Severity|Warning| +|CodeFix|True| +--- + +## RS0049: Instance of TemporaryArray\.AsRef() must be a 'using' variable + +Instance of TemporaryArray\.AsRef() must be a 'using' variable. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsReliability| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## RS0062: Do not capture primary constructor parameters + +Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsMaintainability| +|Enabled|False| +|Severity|Error| +|CodeFix|False| +--- + +## RS0063: Do not use interpolated strings with 'Debug.Assert' + +'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required. + +|Item|Value| +|-|-| +|Category|RoslynDiagnosticsPerformance| +|Enabled|False| +|Severity|Warning| +|CodeFix|True| +--- diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.sarif b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.sarif new file mode 100644 index 0000000000000..b2cef5b726eea --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Roslyn.Diagnostics.Analyzers.sarif @@ -0,0 +1,396 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Roslyn.Diagnostics.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0006": { + "id": "RS0006", + "shortDescription": "Do not mix attributes from different versions of MEF", + "fullDescription": "Do not mix attributes from different versions of MEF.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0023": { + "id": "RS0023", + "shortDescription": "Parts exported with MEFv2 must be marked with 'SharedAttribute'", + "fullDescription": "Part exported with MEFv2 must be marked with the 'SharedAttribute'.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0032": { + "id": "RS0032", + "shortDescription": "Test exports should not be discoverable", + "fullDescription": "Test exports should not be discoverable.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": false, + "typeName": "TestExportsShouldNotBeDiscoverable", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0033": { + "id": "RS0033", + "shortDescription": "Importing constructor should be marked with 'ObsoleteAttribute'", + "fullDescription": "Importing constructor should be marked with 'ObsoleteAttribute'.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "ImportingConstructorShouldBeObsolete", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0034": { + "id": "RS0034", + "shortDescription": "Exported parts should have a public constructor marked with 'ImportingConstructorAttribute'", + "fullDescription": "Exported parts should be marked with 'ImportingConstructorAttribute'.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "ExportedPartsShouldHaveImportingConstructor", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0040": { + "id": "RS0040", + "shortDescription": "Defaultable types should have defaultable fields", + "fullDescription": "Defaultable types should have defaultable fields.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "DefaultableTypeShouldHaveDefaultableFieldsAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0043": { + "id": "RS0043", + "shortDescription": "Do not call 'GetTestAccessor()'", + "fullDescription": "'GetTestAccessor()' is a helper method reserved for testing. Production code must not call this member.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsMaintainability", + "isEnabledByDefault": true, + "typeName": "DoNotCallGetTestAccessor", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0049": { + "id": "RS0049", + "shortDescription": "Instance of TemporaryArray.AsRef() must be a 'using' variable", + "fullDescription": "Instance of TemporaryArray.AsRef() must be a 'using' variable.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "TemporaryArrayAsRefAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Roslyn.Diagnostics.CSharp.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0001": { + "id": "RS0001", + "shortDescription": "Use 'SpecializedCollections.EmptyEnumerable()'", + "fullDescription": "Use 'SpecializedCollections.EmptyEnumerable()'", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsPerformance", + "isEnabledByDefault": true, + "typeName": "CSharpSpecializedEnumerableCreationAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0002": { + "id": "RS0002", + "shortDescription": "Use 'SpecializedCollections.SingletonEnumerable()'", + "fullDescription": "Use 'SpecializedCollections.SingletonEnumerable()'", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsPerformance", + "isEnabledByDefault": true, + "typeName": "CSharpSpecializedEnumerableCreationAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0019": { + "id": "RS0019", + "shortDescription": "'SymbolDeclaredEvent' must be generated for source symbols", + "fullDescription": "Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event.", + "defaultLevel": "error", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": false, + "typeName": "CSharpSymbolDeclaredEventAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0038": { + "id": "RS0038", + "shortDescription": "Prefer null literal", + "fullDescription": "Use 'null' instead of 'default' for nullable types.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsMaintainability", + "isEnabledByDefault": true, + "typeName": "PreferNullLiteral", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0042": { + "id": "RS0042", + "shortDescription": "Do not copy value", + "fullDescription": "Auto-properties always copy values, so they cannot be declared with non-copyable types.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "CSharpDoNotCopyValue", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0046": { + "id": "RS0046", + "shortDescription": "Avoid the 'Opt' suffix", + "fullDescription": "Avoid the 'Opt' suffix in a nullable-enabled code.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsDesign", + "isEnabledByDefault": true, + "typeName": "CSharpAvoidOptSuffixForNullableEnableCode", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0062": { + "id": "RS0062", + "shortDescription": "Do not capture primary constructor parameters", + "fullDescription": "Primary constructor parameters should not be implicitly captured. Manually assign them to fields at the start of the type.", + "defaultLevel": "error", + "properties": { + "category": "RoslynDiagnosticsMaintainability", + "isEnabledByDefault": false, + "typeName": "DoNotCapturePrimaryConstructorParametersAnalyzer", + "languages": [ + "C#" + ] + } + }, + "RS0063": { + "id": "RS0063", + "shortDescription": "Do not use interpolated strings with 'Debug.Assert'", + "fullDescription": "'Debug.Assert' on .NET Framework eagerly creates the string value. This can cause OOMs in tests, particularly for strings that involve syntax nodes. Use 'RoslynDebug.Assert' instead, which will only create the string if required.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsPerformance", + "isEnabledByDefault": false, + "typeName": "CSharpDoNotUseDebugAssertForInterpolatedStrings", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry" + ] + } + } + } + }, + { + "tool": { + "name": "Roslyn.Diagnostics.VisualBasic.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "RS0001": { + "id": "RS0001", + "shortDescription": "Use 'SpecializedCollections.EmptyEnumerable()'", + "fullDescription": "Use 'SpecializedCollections.EmptyEnumerable()'", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsPerformance", + "isEnabledByDefault": true, + "typeName": "BasicSpecializedEnumerableCreationAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0002": { + "id": "RS0002", + "shortDescription": "Use 'SpecializedCollections.SingletonEnumerable()'", + "fullDescription": "Use 'SpecializedCollections.SingletonEnumerable()'", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsPerformance", + "isEnabledByDefault": true, + "typeName": "BasicSpecializedEnumerableCreationAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0004": { + "id": "RS0004", + "shortDescription": "Invoke the correct property to ensure correct use site diagnostics", + "fullDescription": "Invoke the correct property to ensure correct use site diagnostics", + "defaultLevel": "error", + "properties": { + "category": "Usage", + "isEnabledByDefault": false, + "typeName": "BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + }, + "RS0019": { + "id": "RS0019", + "shortDescription": "'SymbolDeclaredEvent' must be generated for source symbols", + "fullDescription": "Compilation event queue is required to generate symbol declared events for all declared source symbols. Hence, every source symbol type or one of its base types must generate a symbol declared event.", + "defaultLevel": "error", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": false, + "typeName": "BasicSymbolDeclaredEventAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "CompilationEnd", + "Telemetry" + ] + } + }, + "RS0042": { + "id": "RS0042", + "shortDescription": "Do not copy value", + "fullDescription": "Auto-properties always copy values, so they cannot be declared with non-copyable types.", + "defaultLevel": "warning", + "properties": { + "category": "RoslynDiagnosticsReliability", + "isEnabledByDefault": true, + "typeName": "VisualBasicDoNotCopyValue", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry" + ] + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..2ff7360c64ea0 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/RulesMissingDocumentation.md @@ -0,0 +1,21 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +RS0001 | | Use 'SpecializedCollections.EmptyEnumerable()' | +RS0002 | | Use 'SpecializedCollections.SingletonEnumerable()' | +RS0004 | | Invoke the correct property to ensure correct use site diagnostics | +RS0006 | | Do not mix attributes from different versions of MEF | +RS0019 | | 'SymbolDeclaredEvent' must be generated for source symbols | +RS0023 | | Parts exported with MEFv2 must be marked with 'SharedAttribute' | +RS0032 | | Test exports should not be discoverable | +RS0033 | | Importing constructor should be marked with 'ObsoleteAttribute' | +RS0034 | | Exported parts should have a public constructor marked with 'ImportingConstructorAttribute' | +RS0038 | | Prefer null literal | +RS0040 | | Defaultable types should have defaultable fields | +RS0042 | | Do not copy value | +RS0043 | | Do not call 'GetTestAccessor()' | +RS0046 | | Avoid the 'Opt' suffix | +RS0049 | | Instance of TemporaryArray\.AsRef() must be a 'using' variable | +RS0062 | | Do not capture primary constructor parameters | +RS0063 | | Do not use interpolated strings with 'Debug.Assert' | diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Properties/launchSettings.json b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Properties/launchSettings.json new file mode 100644 index 0000000000000..183040856937b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Visual Studio Extension": { + "executablePath": "$(DevEnvDir)devenv.exe", + "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Roslyn.Diagnostics.Analyzers.Setup.csproj b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Roslyn.Diagnostics.Analyzers.Setup.csproj new file mode 100644 index 0000000000000..535e9c21bd296 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/Roslyn.Diagnostics.Analyzers.Setup.csproj @@ -0,0 +1,41 @@ + + + + net472 + + true + false + false + false + false + false + true + + + + + Roslyn.Diagnostics.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Roslyn.Diagnostics.CSharp.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Roslyn.Diagnostics.VisualBasic.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..0fea7db0b7636 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,27 @@ + + + + + + Roslyn.Diagnostics Analyzers + Roslyn.Diagnostics Analyzers + EULA.txt + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ApplyTraitToClassTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ApplyTraitToClassTests.cs new file mode 100644 index 0000000000000..65cefa6849b2a --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ApplyTraitToClassTests.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeRefactoringVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpApplyTraitToClass>; +using VerifyVB = Test.Utilities.VisualBasicCodeRefactoringVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.VisualBasicApplyTraitToClass>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class ApplyTraitToClassTests + { + [Theory] + [InlineData("A", "")] + [InlineData("", "A")] + [InlineData("A", "A")] + public async Task MoveTraitToType_MovesSecond_CSharpAsync(string name, string value) + { + var source = $@" +using Xunit; + +class C +{{ + [$$Trait(""{name}"", ""{value}"")] + public void Method() {{ }} + + [Fact, Trait(""{name}"", ""{value}"")] + public void Method2() {{ }} +}} +"; + var fixedSource = $@" +using Xunit; + +[Trait(""{name}"", ""{value}"")] +class C +{{ + public void Method() {{ }} + + [Fact] + public void Method2() {{ }} +}} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Theory] + [InlineData("A", "")] + [InlineData("", "A")] + [InlineData("A", "A")] + public async Task MoveTraitToType_MovesSecond_VisualBasicAsync(string name, string value) + { + var source = $@" +Imports Xunit + +Class C + <$$Trait(""{name}"", ""{value}"")> + Public Sub Method() + End Sub + + + Public Sub Method2() + End Sub +End Class +"; + var fixedSource = $@" +Imports Xunit + + +Class C + Public Sub Method() + End Sub + + + Public Sub Method2() + End Sub +End Class +"; + + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Theory] + [InlineData("A", "")] + [InlineData("", "A")] + [InlineData("A", "A")] + public async Task MoveTraitToType_MovesOnlyFirst_CSharpAsync(string name, string value) + { + var source = $@" +using Xunit; + +class C +{{ + [$$Trait("""", """")] + public void Method() {{ }} + + [Fact, Trait(""{name}"", ""{value}"")] + public void Method2() {{ }} +}} +"; + var fixedSource = $@" +using Xunit; + +[Trait("""", """")] +class C +{{ + public void Method() {{ }} + + [Fact, Trait(""{name}"", ""{value}"")] + public void Method2() {{ }} +}} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Theory] + [InlineData("A", "")] + [InlineData("", "A")] + [InlineData("A", "A")] + public async Task MoveTraitToType_MovesOnlyFirst_VisualBasicAsync(string name, string value) + { + var source = $@" +Imports Xunit + +Class C + <$$Trait("""", """")> + Public Sub Method() + End Sub + + + Public Sub Method2() + End Sub +End Class +"; + var fixedSource = $@" +Imports Xunit + + +Class C + Public Sub Method() + End Sub + + + Public Sub Method2() + End Sub +End Class +"; + + await new VerifyVB.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs new file mode 100644 index 0000000000000..03ad33e84a8fa --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpAvoidOptSuffixForNullableEnableCodeTests.cs @@ -0,0 +1,407 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCode, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpAvoidOptSuffixForNullableEnableCodeCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class CSharpAvoidOptSuffixForNullableEnableCodeTests + { + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCode_DiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], [|otherInstanceOpt|]; + + public Class1? [|PropertyOpt|] { get; set; } + + public void Method1(string? [|sOpt|]) + { + string? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}, {5}"", _instanceOpt, otherInstanceOpt, PropertyOpt, sOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private Class1? _instance, otherInstance; + + public Class1? Property { get; set; } + + public void Method1(string? s) + { + string? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}, {5}"", _instance, otherInstance, Property, s, local, otherLocal); + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeNonNullableType_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1 _instanceOpt = new Class1(), otherInstanceOpt = new Class1(); + + public Class1 PropertyOpt { get; set; } = new Class1(); + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeValueType_DiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum? [|_instanceOpt|], [|otherInstanceOpt|]; + + public MyEnum? [|PropertyOpt|] { get; set; } + + public void Method1(MyEnum? [|eOpt|]) + { + MyEnum? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}, {5}"", _instanceOpt, otherInstanceOpt, PropertyOpt, eOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum? _instance, otherInstance; + + public MyEnum? Property { get; set; } + + public void Method1(MyEnum? e) + { + MyEnum? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}, {4}, {5}"", _instance, otherInstance, Property, e, local, otherLocal); + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableEnabledCodeNonNullableValueType_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public enum MyEnum { A, } + +public class Class1 +{ + private MyEnum _instanceOpt = MyEnum.A, otherInstanceOpt = MyEnum.A; + + public MyEnum PropertyOpt { get; set; } + + public void Method1(MyEnum eOpt) + { + MyEnum localOpt, otherLocalOpt; + } +}", + + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NonNullableEnabledCode_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = + @" +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public Class1 PropertyOpt { get; set; } + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_NullableDisabledCode_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = +@" +#nullable disable + +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public Class1 PropertyOpt { get; set; } + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_PriorToCSharp8_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp7_3, + TestCode = + @" +public class Class1 +{ + private Class1 _instanceOpt, otherInstanceOpt; + + public Class1 PropertyOpt { get; set; } + + public void Method1(string sOpt) + { + string localOpt, otherLocalOpt; + } +}", + }.RunAsync(); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/3707")] + public async Task RS0046_CSharp8_VariableWithoutOptAlreadyExists_DiagnosticButNoCodeFixAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], _instance; + + public Class1? [|PropertyOpt|] { get; set; } + public Class1? Property { get; set; } + + public void Method1(string? [|sOpt|], string? s) + { + string? [|localOpt|], local; + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private Class1? [|_instanceOpt|], _instance; + + public Class1? [|PropertyOpt|] { get; set; } + public Class1? Property { get; set; } + + public void Method1(string? [|sOpt|], string? s) + { + string? [|localOpt|], local; + } +}", + }.RunAsync(); + } + + [Fact] + public async Task RS0046_CSharp8_UnknownType_DiagnosticAndCodeFixAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Class1 +{ + private {|CS0246:Class2|}? [|_instanceOpt|]; + + public {|CS0246:Class2|}? [|PropertyOpt|] { get; set; } + + public void Method1({|CS0246:Class2|}? [|sOpt|]) + { + {|CS0246:Class2|}? [|localOpt|]; + } +}", + FixedCode = @" +#nullable enable + +public class Class1 +{ + private {|CS0246:Class2|}? _instance; + + public {|CS0246:Class2|}? Property { get; set; } + + public void Method1({|CS0246:Class2|}? s) + { + {|CS0246:Class2|}? local; + } +}", + }.RunAsync(); + } + + [Fact, WorkItem(3813, "https://github.com/dotnet/roslyn-analyzers/issues/3813")] + public async Task RS0046_CSharp8_NullableEnabledCode_InterfaceImplementation_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public interface ISomething +{ + ISomething? [|PropertyOpt|] { get; set; } + void Method1(string? [|sOpt|]); +} + +public class Class1 : ISomething +{ + public ISomething? PropertyOpt { get; set; } + + public void Method1(string? sOpt) + { + string? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}"", PropertyOpt, sOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public interface ISomething +{ + ISomething? Property { get; set; } + void Method1(string? s); +} + +public class Class1 : ISomething +{ + public ISomething? Property { get; set; } + + public void Method1(string? sOpt) + { + string? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}"", Property, sOpt, local, otherLocal); + } +}", + + }.RunAsync(); + } + + [Fact, WorkItem(3813, "https://github.com/dotnet/roslyn-analyzers/issues/3813")] + public async Task RS0046_CSharp8_NullableEnabledCode_Override_NoDiagnosticAsync() + { + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestCode = @" +#nullable enable + +public class Base +{ + public virtual Base? [|PropertyOpt|] { get; set; } + + public virtual void Method1(string? [|sOpt|]) + { + System.Console.WriteLine(""{0}"", sOpt); + } +} + +public class Derived : Base +{ + public override Base? PropertyOpt { get; set; } + + public override void Method1(string? sOpt) + { + string? [|localOpt|] = null, [|otherLocalOpt|] = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}"", PropertyOpt, sOpt, localOpt, otherLocalOpt); + } +}", + FixedCode = @" +#nullable enable + +public class Base +{ + public virtual Base? Property { get; set; } + + public virtual void Method1(string? s) + { + System.Console.WriteLine(""{0}"", s); + } +} + +public class Derived : Base +{ + public override Base? Property { get; set; } + + public override void Method1(string? sOpt) + { + string? local = null, otherLocal = null; + + System.Console.WriteLine(""{0}, {1}, {2}, {3}"", Property, sOpt, local, otherLocal); + } +}", + + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotCapturePrimaryConstructorParametersTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotCapturePrimaryConstructorParametersTests.cs new file mode 100644 index 0000000000000..c93b01c550b98 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotCapturePrimaryConstructorParametersTests.cs @@ -0,0 +1,215 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.DoNotCapturePrimaryConstructorParametersAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class CSharpDoNotCapturePrimaryConstructorParametersTests + { + [Fact] + public async Task ErrorOnCapture_InMethod() + { + var source = """ + class C(int i) + { + private int M() => [|i|]; + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task ErrorOnCapture_InProperty() + { + var source = """ + class C(int i) + { + private int P + { + get => [|i|]; + set => [|i|] = value; + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task ErrorOnCapture_InIndexer() + { + var source = """ + class C(int i) + { + private int this[int param] + { + get => [|i|]; + set => [|i|] = value; + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task ErrorOnCapture_InEvent() + { + var source = """ + class C(int i) + { + public event System.Action E + { + add => _ = [|i|]; + remove => _ = [|i|]; + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task ErrorOnCapture_UseInSubsequentConstructor() + { + var source = """ + class C(int i) + { + C(bool b) : this(1) + { + _ = i; + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + ExpectedDiagnostics = { + // /0/Test0.cs(5,13): error RS0103: Primary constructor parameter 'i' should not be implicitly captured + VerifyCS.Diagnostic().WithSpan(5, 13, 5, 14).WithArguments("i"), + // /0/Test0.cs(5,13): error CS9105: Cannot use primary constructor parameter 'int i' in this context. + DiagnosticResult.CompilerError("CS9105").WithSpan(5, 13, 5, 14).WithArguments("int i"), + } + }.RunAsync(); + } + + [Fact] + public async Task NoError_PassToBase() + { + var source = """ + class Base(int i); + class Derived(int i) : Base(i); + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task NoError_FieldInitializer() + { + var source = """ + class C(int i) + { + public int I = i; + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task NoError_PropertyInitializer() + { + var source = """ + class C(int i) + { + public int I { get; set; } = i; + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task NoError_CapturedInLambda() + { + var source = """ + using System; + public class Base(Action action); + public class Derived(int i) : Base(() => Console.WriteLine(i)); + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task NoError_LocalFunctionParameterReference() + { + var source = """ + using System; + class C + { + void M() + { + Nested1(1); + + void Nested1(int i) + { + Nested2(); + + void Nested2() => Console.WriteLine(i); + } + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotUseDebugAssertForInterpolatedStringsTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotUseDebugAssertForInterpolatedStringsTests.cs new file mode 100644 index 0000000000000..aa4056f0d4975 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CSharpDoNotUseDebugAssertForInterpolatedStringsTests.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpDoNotUseDebugAssertForInterpolatedStrings, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpDoNotUseDebugAssertForInterpolatedStringsFixer>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class CSharpDoNotUseInterpolatedStringsForDebugAssertTests + { + private const string RoslynDebug = + """ + namespace Roslyn.Utilities + { + public static class RoslynDebug + { + public static void Assert(bool condition, string message) { } + } + } + """; + + [Theory] + [InlineData(""" + $"{0}" + """)] + [InlineData(""" + $@"{0}" + """)] + [InlineData(""" + @$"{0}" + """)] + [InlineData("""" + $"""{0}""" + """")] + public async Task InterpolatedString(string @string) + { + var source = $$""" + using System.Diagnostics; + + class C + { + void M() + { + [|Debug.Assert(false, {{@string}})|]; + } + } + + {{RoslynDebug}} + """; + + var @fixed = $$""" + using System.Diagnostics; + using Roslyn.Utilities; + + class C + { + void M() + { + RoslynDebug.Assert(false, {{@string}}); + } + } + + {{RoslynDebug}} + """; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultNetFramework, + TestCode = source, + FixedCode = @fixed, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task NoCrashOnUsingStaticedAssert() + { + var source = $$""" + using static System.Diagnostics.Debug; + + class C + { + void M() + { + [|Assert(false, $"{0}")|]; + } + } + + {{RoslynDebug}} + """; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultNetFramework, + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Theory] + [InlineData(""" + $"{"0"}" + """)] + [InlineData(""" + $@"{"0"}" + """)] + [InlineData(""" + @$"{"0"}" + """)] + [InlineData("""" + $"""{"0"}""" + """")] + public async Task NoAssertForConstantString(string @string) + { + var source = $$""" + using System.Diagnostics; + + class C + { + void M() + { + Debug.Assert(false, {{@string}}); + } + } + + {{RoslynDebug}} + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CreateTestAccessorTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CreateTestAccessorTests.cs new file mode 100644 index 0000000000000..edc930222e24b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/CreateTestAccessorTests.cs @@ -0,0 +1,234 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis; +using Xunit; +using CSharpLanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion; +using VerifyCS = Test.Utilities.CSharpCodeRefactoringVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpCreateTestAccessor>; +using VerifyVB = Test.Utilities.VisualBasicCodeRefactoringVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.VisualBasicCreateTestAccessor>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class CreateTestAccessorTests + { + [Theory] + [InlineData("$$class TestClass ")] + [InlineData("class $$TestClass ")] + [InlineData("class TestClass$$ ")] + [InlineData("class [|TestClass|] ")] + [InlineData("[|class TestClass|] ")] + public async Task CreateTestAccessorCSharpAsync(string typeHeader) + { + var source = typeHeader + @"{ +}"; + var fixedSourceBody = @"{ + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestClass _instance; + + internal TestAccessor(TestClass instance) + { + _instance = instance; + } + } +}"; + + var fixedSource = "class TestClass " + fixedSourceBody; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + + // Applying the refactoring a second time does not produce any changes + fixedSource = typeHeader + fixedSourceBody; + await VerifyCS.VerifyRefactoringAsync(fixedSource, fixedSource); + } + + [Theory] + [InlineData("$$struct TestStruct ")] + [InlineData("struct $$TestStruct ")] + [InlineData("struct TestStruct$$ ")] + [InlineData("struct [|TestStruct|] ")] + [InlineData("[|struct TestStruct|] ")] + public async Task CreateTestAccessorStructCSharpAsync(string typeHeader) + { + var source = typeHeader + @"{ +}"; + var fixedSourceBody = @"{ + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestStruct _instance; + + internal TestAccessor(TestStruct instance) + { + _instance = instance; + } + } +}"; + + var fixedSource = "struct TestStruct " + fixedSourceBody; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + + // Applying the refactoring a second time does not produce any changes + fixedSource = typeHeader + fixedSourceBody; + await VerifyCS.VerifyRefactoringAsync(fixedSource, fixedSource); + } + + [Theory(Skip = "Needs Roslyn 16.9 Preview 1: https://github.com/dotnet/roslyn/pull/48096")] + [InlineData("$$record TestRecord ")] + [InlineData("record $$TestRecord ")] + [InlineData("record TestRecord$$ ")] + [InlineData("record [|TestRecord|] ")] + [InlineData("[|record TestRecord|] ")] + public async Task CreateTestAccessorRecordCSharpAsync(string typeHeader) + { + var source = typeHeader + @"{ +}"; + var fixedSourceBody = @"{ + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestRecord _instance; + + internal TestAccessor(TestRecord instance) + { + _instance = instance; + } + } +}"; + + var fixedSource = "record TestRecord " + fixedSourceBody; + await new VerifyCS.Test + { + LanguageVersion = CSharpLanguageVersion.CSharp9, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + + // Applying the refactoring a second time does not produce any changes + fixedSource = typeHeader + fixedSourceBody; + await new VerifyCS.Test + { + LanguageVersion = CSharpLanguageVersion.CSharp9, + TestCode = fixedSource, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Theory] + [InlineData(TypeKind.Delegate)] + [InlineData(TypeKind.Enum)] + [InlineData(TypeKind.Interface)] + public async Task UnsupportedTypeCSharpAsync(TypeKind typeKind) + { + var declaration = typeKind switch + { + TypeKind.Delegate => "delegate void $$Method();", + TypeKind.Enum => "public enum $$SomeType { }", + TypeKind.Interface => "public interface $$SomeType { }", + _ => throw new NotSupportedException(), + }; + + await VerifyCS.VerifyRefactoringAsync(declaration, declaration); + } + + [Theory] + [InlineData("$$Class TestClass")] + [InlineData("Class $$TestClass")] + [InlineData("Class TestClass$$")] + [InlineData("Class [|TestClass|]")] + [InlineData("[|Class TestClass|]")] + public async Task CreateTestAccessorVisualBasicAsync(string typeHeader) + { + var source = $@"{typeHeader} +End Class"; + var fixedSourceBody = @" + Friend Function GetTestAccessor() As TestAccessor + Return New TestAccessor(Me) + End Function + + Friend Structure TestAccessor + Private ReadOnly _instance As TestClass + + Friend Sub New(instance As TestClass) + _instance = instance + End Sub + End Structure +End Class"; + + var fixedSource = "Class TestClass" + fixedSourceBody; + await VerifyVB.VerifyRefactoringAsync(source, fixedSource); + + // Applying the refactoring a second time does not produce any changes + fixedSource = typeHeader + fixedSourceBody; + await VerifyVB.VerifyRefactoringAsync(fixedSource, fixedSource); + } + + [Theory] + [InlineData("$$Structure TestStructure")] + [InlineData("Structure $$TestStructure")] + [InlineData("Structure TestStructure$$")] + [InlineData("Structure [|TestStructure|]")] + [InlineData("[|Structure TestStructure|]")] + public async Task CreateTestAccessorStructureVisualBasicAsync(string typeHeader) + { + var source = $@"{typeHeader} +End Structure"; + var fixedSourceBody = @" + Friend Function GetTestAccessor() As TestAccessor + Return New TestAccessor(Me) + End Function + + Friend Structure TestAccessor + Private ReadOnly _instance As TestStructure + + Friend Sub New(instance As TestStructure) + _instance = instance + End Sub + End Structure +End Structure"; + + var fixedSource = "Structure TestStructure" + fixedSourceBody; + await VerifyVB.VerifyRefactoringAsync(source, fixedSource); + + // Applying the refactoring a second time does not produce any changes + fixedSource = typeHeader + fixedSourceBody; + await VerifyVB.VerifyRefactoringAsync(fixedSource, fixedSource); + } + + [Theory] + [InlineData(TypeKind.Delegate)] + [InlineData(TypeKind.Enum)] + [InlineData(TypeKind.Interface)] + [InlineData(TypeKind.Module)] + public async Task UnsupportedTypeVisualBasicAsync(TypeKind typeKind) + { + var declaration = typeKind switch + { + TypeKind.Delegate => "Delegate Function $$SomeType() As Integer", + TypeKind.Enum => "Enum $$SomeType\r\n Member\r\nEnd Enum", + TypeKind.Interface => "Interface $$SomeType\r\nEnd Interface", + TypeKind.Module => "Module $$SomeType\r\nEnd Module", + _ => throw new NotSupportedException(), + }; + + await VerifyVB.VerifyRefactoringAsync(declaration, declaration); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DefaultableTypeShouldHaveDefaultableFieldsTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DefaultableTypeShouldHaveDefaultableFieldsTests.cs new file mode 100644 index 0000000000000..275a79db9201b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DefaultableTypeShouldHaveDefaultableFieldsTests.cs @@ -0,0 +1,312 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.DefaultableTypeShouldHaveDefaultableFieldsAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class DefaultableTypeShouldHaveDefaultableFieldsTests + { + private const string NonDefaultableAttribute = @"// +using System; + +namespace Roslyn.Utilities { + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.GenericParameter)] + sealed class NonDefaultableAttribute : Attribute { } +} +"; + + public static IEnumerable DefaultableTypes + { + get + { + yield return new object[] { "int" }; + yield return new object[] { "int?" }; + yield return new object[] { "DateTime" }; + yield return new object[] { "object?" }; + yield return new object[] { "IFormattable?" }; + yield return new object[] { "EventHandler?" }; + yield return new object[] { "StringComparison" }; + } + } + + public static IEnumerable DefaultableTypesNullableDisableContext + { + get + { + yield return new object[] { "int" }; + yield return new object[] { "int?" }; + yield return new object[] { "DateTime" }; + yield return new object[] { "object" }; + yield return new object[] { "IFormattable" }; + yield return new object[] { "EventHandler" }; + yield return new object[] { "StringComparison" }; + } + } + + public static IEnumerable NonDefaultableTypes + { + get + { + yield return new object[] { "object" }; + yield return new object[] { "IFormattable" }; + yield return new object[] { "EventHandler" }; + yield return new object[] { "NonDefaultableStruct" }; + } + } + + [Theory] + [MemberData(nameof(NonDefaultableTypes))] + public async Task TestNonDefaultableStructWithNonDefaultableTypeFieldAsync(string nonDefaultableType) + { + var code = $@" +#nullable enable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableStruct {{ }} + +[NonDefaultable] +struct NonDefaultableTestStruct {{ + {nonDefaultableType} field; + {nonDefaultableType} Property {{ get; }} + + static {nonDefaultableType} staticField = default!; + static {nonDefaultableType} StaticProperty {{ get; }} = default!; +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(NonDefaultableTypes))] + public async Task TestNonDefaultableStructWithNonDefaultableTypeNullableDisableFieldAsync(string nonDefaultableType) + { + var code = $@" +#nullable disable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableStruct {{ }} + +[NonDefaultable] +struct NonDefaultableTestStruct {{ + {nonDefaultableType} field; + {nonDefaultableType} Property {{ get; }} + + static {nonDefaultableType} staticField; + static {nonDefaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DefaultableTypes))] + public async Task TestNonDefaultableStructWithDefaultableTypeFieldAsync(string defaultableType) + { + var code = $@" +#nullable enable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableTestStruct {{ + {defaultableType} field; + {defaultableType} Property {{ get; }} + + static {defaultableType} staticField; + static {defaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DefaultableTypesNullableDisableContext))] + public async Task TestNonDefaultableStructWithDefaultableTypeNullableDisableFieldAsync(string defaultableType) + { + var code = $@" +#nullable disable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableTestStruct {{ + {defaultableType} field; + {defaultableType} Property {{ get; }} + + static {defaultableType} staticField; + static {defaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DefaultableTypes))] + public async Task TestDefaultableStructWithDefaultableTypeFieldAsync(string defaultableType) + { + var code = $@" +#nullable enable + +using System; + +struct DefaultableStruct {{ + {defaultableType} field; + {defaultableType} Property {{ get; }} + + static {defaultableType} staticField; + static {defaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DefaultableTypes))] + public async Task TestClassWithDefaultableTypeFieldAsync(string defaultableType) + { + var code = $@" +#nullable enable + +using System; + +class TestClass {{ + {defaultableType} field; + {defaultableType} Property {{ get; }} + + static {defaultableType} staticField; + static {defaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(DefaultableTypesNullableDisableContext))] + public async Task TestClassWithDefaultableTypeNullableDisableFieldAsync(string defaultableType) + { + var code = $@" +#nullable disable + +using System; + +class TestClass {{ + {defaultableType} field; + {defaultableType} Property {{ get; }} + + static {defaultableType} staticField; + static {defaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(NonDefaultableTypes))] + public async Task TestDefaultableStructWithNonDefaultableTypeFieldAsync(string nonDefaultableType) + { + var code = $@" +#nullable enable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableStruct {{ }} + +struct DefaultableStruct {{ + {nonDefaultableType} [|field|]; + {nonDefaultableType} [|Property|] {{ get; }} + + static {nonDefaultableType} StaticField = default!; + static {nonDefaultableType} StaticProperty {{ get; }} = default!; +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + + [Theory] + [MemberData(nameof(NonDefaultableTypes))] + public async Task TestDefaultableStructWithNonDefaultableTypeNullableDisableFieldAsync(string nonDefaultableType) + { + var code = $@" +#nullable disable + +using System; +using Roslyn.Utilities; + +[NonDefaultable] +struct NonDefaultableStruct {{ }} + +struct DefaultableStruct {{ + {nonDefaultableType} field; + {nonDefaultableType} Property {{ get; }} + + static {nonDefaultableType} StaticField; + static {nonDefaultableType} StaticProperty {{ get; }} +}} +"; + + await new VerifyCS.Test + { + LanguageVersion = LanguageVersion.CSharp8, + TestState = { Sources = { code, NonDefaultableAttribute } }, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCallGetTestAccessorTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCallGetTestAccessorTests.cs new file mode 100644 index 0000000000000..8c0a1e6bd7385 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCallGetTestAccessorTests.cs @@ -0,0 +1,223 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.DoNotCallGetTestAccessor, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicSecurityCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.DoNotCallGetTestAccessor, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class DoNotCallGetTestAccessorTests + { + [Fact] + public async Task DoNotCallGetTestAccessor_CSharpAsync() + { + var source = @"class TestClass { + internal void Method() + { + _ = [|GetTestAccessor()|]; + _ = [|this.GetTestAccessor()|]; + _ = [|new TestClass().GetTestAccessor()|]; + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestClass _testClass; + + internal TestAccessor(TestClass testClass) + { + _testClass = testClass; + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task DoNotCallGetTestAccessor_VisualBasicAsync() + { + var source = @"Class TestClass + Friend Sub Method() + Dim a = [|GetTestAccessor()|] + Dim b = [|Me.GetTestAccessor()|] + Dim c = [|New TestClass().GetTestAccessor()|] + End Sub + + Friend Function GetTestAccessor() As TestAccessor + Return New TestAccessor(Me) + End Function + + Friend Structure TestAccessor + Private ReadOnly _testClass As TestClass + + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task DoNotConstructTestAccessor_CSharpAsync() + { + var source = @"class TestClass { + internal void Method() + { + _ = [|new TestAccessor(this)|]; + _ = [|new TestAccessor(new TestClass())|]; + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestClass _testClass; + + internal TestAccessor(TestClass testClass) + { + _testClass = testClass; + } + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task DoNotConstructTestAccessor_VisualBasicAsync() + { + var source = @"Class TestClass + Friend Sub Method() + Dim a = [|New TestAccessor(Me)|] + Dim b = [|New TestAccessor(New TestClass())|] + End Sub + + Friend Function GetTestAccessor() As TestAccessor + Return New TestAccessor(Me) + End Function + + Friend Structure TestAccessor + Private ReadOnly _testClass As TestClass + + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task DoNotAccessTestAccessorStaticMember_CSharpAsync() + { + var source = @" +using System; + +class TestClass { + internal void Method() + { + _ = [|TestAccessor.ExposedField|]; + _ = [|TestAccessor.ExposedProperty|]; + [|TestAccessor.ExposedEvent|] += delegate { }; + [|TestAccessor.ExposedMethod()|]; + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + internal readonly struct TestAccessor + { + private readonly TestClass _testClass; + + internal TestAccessor(TestClass testClass) + { + _testClass = testClass; + } + + public static int ExposedField; + public static int ExposedProperty => 0; + public static event EventHandler ExposedEvent { add { } remove { } } + public static void ExposedMethod() { } + } +} + +class OtherClass { + internal void Method() + { + _ = [|TestClass.TestAccessor.ExposedField|]; + _ = [|TestClass.TestAccessor.ExposedProperty|]; + [|TestClass.TestAccessor.ExposedEvent|] += delegate { }; + [|TestClass.TestAccessor.ExposedMethod()|]; + } +}"; + + await VerifyCS.VerifyAnalyzerAsync(source); + } + + [Fact] + public async Task DoNotAccessTestAccessorStaticMember_VisualBasicAsync() + { + var source = @" +Imports System + +Class TestClass + Friend Sub Method() + Dim a = [|TestAccessor.ExposedField|] + Dim b = [|TestAccessor.ExposedProperty|] + AddHandler [|TestAccessor.ExposedEvent|], Sub(_1, _2) Return + [|TestAccessor.ExposedMethod()|] + End Sub + + Friend Function GetTestAccessor() As TestAccessor + Return New TestAccessor(Me) + End Function + + Friend Structure TestAccessor + Private ReadOnly _testClass As TestClass + + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Public Shared ExposedField As Integer + Public Shared ReadOnly Property ExposedProperty As Integer = 0 + Public Shared Event ExposedEvent As EventHandler + + Public Shared Sub ExposedMethod() + End Sub + End Structure +End Class + +Class OtherClass + Friend Sub Method() + Dim a = [|TestClass.TestAccessor.ExposedField|] + Dim b = [|TestClass.TestAccessor.ExposedProperty|] + AddHandler [|TestClass.TestAccessor.ExposedEvent|], Sub(_1, _2) Return + [|TestClass.TestAccessor.ExposedMethod()|] + End Sub +End Class"; + + await VerifyVB.VerifyAnalyzerAsync(source); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCopyValueTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCopyValueTests.cs new file mode 100644 index 0000000000000..7dfef6e3e9188 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotCopyValueTests.cs @@ -0,0 +1,1524 @@ +// 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 enable + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpDoNotCopyValue, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicSecurityCodeFixVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.VisualBasicDoNotCopyValue, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class DoNotCopyValueTests + { + [Fact] + public async Task TestSliceOfStringAsync() + { + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp9, + TestCode = @" +using System.Runtime.InteropServices; + +class C +{ + void M() + { + var local = """"[..]; + local = """"[..]; + } +} +", + }.RunAsync(); + } + + [Fact] + public async Task TestAcquireFromReturnByValueAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + void M() + { + var local = GCHandle.Alloc(new object()); + } +} +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Runtime.InteropServices + +Class C + Sub M() + Dim local = GCHandle.Alloc(New Object()) + End Sub +End Class"); + } + + [Theory] + [InlineData("Func>", "Func(Of ValueTask(Of GCHandle))")] + [InlineData("Func>", "Func(Of ConfiguredValueTaskAwaitable(Of GCHandle))")] + [InlineData("Func>", "Func(Of Task(Of GCHandle))")] + [InlineData("Func>", "Func(Of ConfiguredTaskAwaitable(Of GCHandle))")] + public async Task TestAcquireFromAwaitInvocationAsync(string csharpInvokeType, string visualBasicInvokeType) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +class C +{{ + async Task M({csharpInvokeType} d) + {{ + var local = await d(); + local = await d(); + }} +}} +"); + + await VerifyVB.VerifyAnalyzerAsync($@" +Imports System +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Imports System.Threading.Tasks + +Class C + Async Function M(d as {visualBasicInvokeType}) As Task + Dim local = Await d() + local = Await d() + End Function +End Class"); + } + + [Theory] + [InlineData("ValueTask", "ValueTask(Of GCHandle)")] + [InlineData("ConfiguredValueTaskAwaitable", "ConfiguredValueTaskAwaitable(Of GCHandle)")] + public async Task TestAcquireFromAwaitAsync(string csharpAwaitableType, string visualBasicAwaitableType) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +class C +{{ + async Task M({csharpAwaitableType} task) + {{ + var local = await task; + }} +}} +"); + + await VerifyVB.VerifyAnalyzerAsync($@" +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Imports System.Threading.Tasks + +Class C + Async Function M(task as {visualBasicAwaitableType}) As Task + Dim local = Await task + End Function +End Class"); + } + + [Theory] + [InlineData("Task", "Task(Of GCHandle)")] + [InlineData("ConfiguredTaskAwaitable", "ConfiguredTaskAwaitable(Of GCHandle)")] + public async Task TestFailedAcquireFromUnsupportedAwaitAsync(string csharpAwaitableType, string visualBasicAwaitableType) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +class C +{{ + async Task M({csharpAwaitableType} task) + {{ + var local {{|#0:= await task|}}; + }} +}} +", + // /0/Test0.cs(10,19): warning RS0042: Cannot assign a value from a reference to non-copyable type 'System.Runtime.InteropServices.GCHandle' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAssignValueFromReferenceRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle")); + + await VerifyVB.VerifyAnalyzerAsync($@" +Imports System.Runtime.CompilerServices +Imports System.Runtime.InteropServices +Imports System.Threading.Tasks + +Class C + Async Function M(task as {visualBasicAwaitableType}) As Task + Dim local {{|#0:= Await task|}} + End Function +End Class", + // /0/Test0.vb(8,19): warning RS0042: Cannot assign a value from a reference to non-copyable type 'System.Runtime.InteropServices.GCHandle' + VerifyVB.Diagnostic(AbstractDoNotCopyValue.NoAssignValueFromReferenceRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle")); + } + + [Theory] + [InlineData("field", "field")] + [InlineData("(field)", null)] + [InlineData("this.field", "Me.field")] + [InlineData("(this).field", null)] + [InlineData("((C)this).field", "DirectCast(Me, C).field")] + public async Task TestAcquireIntoFieldFromReturnByValueAsync(string csharpFieldReference, string? visualBasicFieldReference) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System.Runtime.InteropServices; + +class C +{{ + GCHandle field; + + void M(bool condition) + {{ + {csharpFieldReference} = GCHandle.Alloc(new object()); + {csharpFieldReference} = condition ? GCHandle.Alloc(new object()) : GCHandle.Alloc(new object()); + }} +}} +"); + + if (visualBasicFieldReference is object) + { + await VerifyVB.VerifyAnalyzerAsync($@" +Imports System.Runtime.InteropServices + +Class C + Dim field As GCHandle + + Sub M(condition As Boolean) + {visualBasicFieldReference} = GCHandle.Alloc(New Object()) + {visualBasicFieldReference} = If(condition, GCHandle.Alloc(New Object()), GCHandle.Alloc(New Object())) + End Sub +End Class"); + } + } + + [Theory] + [InlineData("field", "field")] + [InlineData("(field)", null)] + [InlineData("this.field", "Me.field")] + [InlineData("(this).field", null)] + [InlineData("((C)this).field", "DirectCast(Me, C).field")] + public async Task TestAcquireIntoArrayFieldFromReturnByValueAsync(string csharpFieldReference, string? visualBasicFieldReference) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System.Runtime.InteropServices; + +class C +{{ + GCHandle[] field; + + void M() + {{ + {csharpFieldReference}[0] = GCHandle.Alloc(new object()); + }} +}} +"); + + if (visualBasicFieldReference is object) + { + await VerifyVB.VerifyAnalyzerAsync($@" +Imports System.Runtime.InteropServices + +Class C + Dim field As GCHandle() + + Sub M() + {visualBasicFieldReference}(0) = GCHandle.Alloc(New Object()) + End Sub +End Class"); + } + } + + [Fact] + public async Task TestDoNotAcquireFromReturnByReferenceAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + void M() + { + var local {|#0:= GetRef()|}; + } + + ref GCHandle GetRef() => throw null; +} +", + // /0/Test0.cs(8,19): warning RS0042: Cannot assign a value from a reference to non-copyable type 'System.Runtime.InteropServices.GCHandle' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAssignValueFromReferenceRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle")); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/77925")] + public async Task TestPassToInstancePropertyGetterAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + GCHandle field; + readonly GCHandle readonlyfield; + + void M() + { + var local = new GCHandle(); + ref var reflocal = ref local; + ref readonly var refreadonlylocal = ref local; + + // Call each proprety twice to ensure the analyzer didn't silently treat one like a move + _ = field.Target; + _ = field.Target; + _ = local.Target; + _ = local.Target; + _ = reflocal.Target; + _ = reflocal.Target; + + _ = {|#0:readonlyfield|}.Target; + _ = {|#1:refreadonlylocal|}.Target; + } +} +", + // /0/Test0.cs(23,13): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle", "FieldReference"), + // /0/Test0.cs(17,13): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(1).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference")); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/77925")] + public async Task TestPassToInstanceMethodAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + GCHandle field; + readonly GCHandle readonlyfield; + + void M() + { + var local = new GCHandle(); + ref var reflocal = ref local; + ref readonly var refreadonlylocal = ref local; + + // Call each method twice to ensure the analyzer didn't silently treat one like a move + _ = field.AddrOfPinnedObject(); + _ = field.AddrOfPinnedObject(); + _ = local.AddrOfPinnedObject(); + _ = local.AddrOfPinnedObject(); + _ = reflocal.AddrOfPinnedObject(); + _ = reflocal.AddrOfPinnedObject(); + + _ = {|#0:readonlyfield|}.AddrOfPinnedObject(); + _ = {|#1:refreadonlylocal|}.AddrOfPinnedObject(); + } +} +", + // /0/Test0.cs(23,13): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle", "FieldReference"), + // /0/Test0.cs(17,13): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(1).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference")); + } + + [Fact] + public async Task TestPassToExtensionMethodAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + GCHandle field; + readonly GCHandle readonlyfield; + + void M() + { + var local = new GCHandle(); + ref var reflocal = ref local; + ref readonly var refreadonlylocal = ref local; + + // Success cases. Call each method twice to ensure the analyzer didn't silently treat one like a move. + field.XRef(); + field.XRef(); + local.XRef(); + local.XRef(); + reflocal.XRef(); + reflocal.XRef(); + + readonlyfield.XIn(); + readonlyfield.XIn(); + reflocal.XIn(); + reflocal.XIn(); + local.XIn(); + local.XIn(); + + // Failure cases. + {|#0:{|CS0192:readonlyfield|}|}.XRef(); + {|#1:{|CS1510:refreadonlylocal|}|}.XRef(); + {|#2:field|}.X(); + {|#3:readonlyfield|}.X(); + {|#4:local|}.X(); + {|#5:reflocal|}.X(); + {|#6:refreadonlylocal|}.X(); + } +} + +static class E +{ + public static void X(this GCHandle handle) { } + public static void XRef(this ref GCHandle handle) { } + public static void XIn(this in GCHandle handle) { } +} +", + // /0/Test0.cs(31,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle", "FieldReference"), + // /0/Test0.cs(32,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(1).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference"), + // /0/Test0.cs(33,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(2).WithArguments("System.Runtime.InteropServices.GCHandle", "FieldReference"), + // /0/Test0.cs(34,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(3).WithArguments("System.Runtime.InteropServices.GCHandle", "FieldReference"), + // /0/Test0.cs(35,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(4).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference"), + // /0/Test0.cs(36,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(5).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference"), + // /0/Test0.cs(37,9): warning RS0042: Unsupported use of non-copyable type 'System.Runtime.InteropServices.GCHandle' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(6).WithArguments("System.Runtime.InteropServices.GCHandle", "LocalReference")); + } + + [Theory] + [InlineData("throw null")] + [InlineData("(true ? throw null : default(GCHandle))")] + [InlineData("(false ? new GCHandle() : throw null)")] + public async Task TestConversionFromThrowNullAsync(string throwExpression) + { + await VerifyCS.VerifyAnalyzerAsync($@" +using System.Runtime.InteropServices; + +class C +{{ + GCHandle Get() => {throwExpression}; +}} +"); + } + + [Fact] + public async Task TestPassByReferenceAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + void Get(ref GCHandle handle) => Get(ref handle); +} +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Runtime.InteropServices + +Class C + Sub Method(ByRef handle As GCHandle) + Method(handle) + End Sub +End Class"); + } + + [Fact] + public async Task TestPassByReadOnlyReferenceAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + void Get(in GCHandle handle) => Get(in handle); +} +"); + } + + [Fact] + public async Task TestAssignToMemberAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + CannotCopy _field; + + void Method(CannotCopy parameter) + { + CannotCopy local = new CannotCopy(); + + _field.Field = 0; + parameter.Field = 0; + local.Field = 0; + + _field.Property = 0; + parameter.Property = 0; + local.Property = 0; + } +} + +[NonCopyable] +struct CannotCopy +{ + public int Field; + public int Property { get; set; } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"); + } + + [Fact] + public async Task ReturnLocalByValueAsync() + { + var source = @" +using System.Runtime.InteropServices; + +class C +{ + GCHandle Method() + { + GCHandle handle = default; + return handle; + } +} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task TestReturnMemberAsync() + { + var source = @" +using System.Runtime.InteropServices; + +class C +{ + CannotCopy _field; + readonly CannotCopy _readonlyField; + + int ReturnFieldMemberField() + { + return _field.Field; + } + + int ReturnReadonlyFieldMemberField() + { + return _readonlyField.Field; + } + + int ReturnParameterMemberField(CannotCopy parameter) + { + return parameter.Field; + } + + int ReturnArrayParameterMemberField(CannotCopy[] parameter) + { + return parameter[0].Field; + } + + int ReturnLocalMemberField() + { + CannotCopy local = new CannotCopy(); + return local.Field; + } + + int ReturnFieldMemberProperty() + { + return _field.Property; + } + + int ReturnReadonlyFieldMemberProperty() + { + return {|#0:_readonlyField|}.Property; + } + + int ReturnParameterMemberProperty(CannotCopy parameter) + { + return parameter.Property; + } + + int ReturnArrayParameterMemberProperty(CannotCopy[] parameter) + { + return parameter[0].Property; + } + + int ReturnLocalMemberProperty() + { + CannotCopy local = new CannotCopy(); + return local.Property; + } + + int ReturnFieldMemberReadonlyProperty() + { + return _field.ReadonlyProperty; + } + + int ReturnReadonlyFieldMemberReadonlyProperty() + { + return _readonlyField.ReadonlyProperty; + } + + int ReturnParameterMemberReadonlyProperty(CannotCopy parameter) + { + return parameter.ReadonlyProperty; + } + + int ReturnArrayParameterMemberReadonlyProperty(CannotCopy[] parameter) + { + return parameter[0].ReadonlyProperty; + } + + int ReturnLocalMemberReadonlyProperty() + { + CannotCopy local = new CannotCopy(); + return local.ReadonlyProperty; + } +} + +[NonCopyable] +struct CannotCopy +{ + public int Field; + public int Property { get { return 0; } } + public int ReadonlyProperty { readonly get { return 0; } set { } } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = + { + // The only reported diagnostic occurs for the invocation of a non-readonly getter of a readonly + // non-copyable field. + // + // /0/Test0.cs(42,16): warning RS0042: Unsupported use of non-copyable type 'CannotCopy' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("CannotCopy", "FieldReference"), + }, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task NonReadonlyMemberPropertiesAsync() + { + // Verify that a non-readonly member of a non-copyable type can reference another non-readonly member of the + // same type. + var source = @" +using System.Runtime.InteropServices; + +[NonCopyable] +struct CannotCopy +{ + public int First { get { return 0; } } + public int Second { get { return First; } } + public int Third => First; +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task NonReadonlyMemberMethodsAsync() + { + // Verify that a non-readonly member of a non-copyable type can reference another non-readonly member of the + // same type. + var source = @" +using System.Runtime.InteropServices; + +[NonCopyable] +struct CannotCopy +{ + public int First() { return 0; } + public int Second() { return First(); } + public int Third() => First(); +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task AllowObjectInitializerAsync() + { + var source = @" +using System.Runtime.InteropServices; + +class C +{ + CannotCopy Method() + { + return new CannotCopy() { First = 0, Second = 1 }; + } +} + +[NonCopyable] +struct CannotCopy +{ + public int First { get; set; } + public int Second { get; set; } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task AllowCustomForeachEnumeratorAsync() + { + var source = @" +using System.Runtime.InteropServices; + +class C +{ + void Method() + { + var cannotCopy = new CannotCopy(); + foreach (var obj in cannotCopy) + { + } + } +} + +[NonCopyable] +struct CannotCopy +{ + public Enumerator GetEnumerator() => throw null; + + public struct Enumerator + { + public object Current => throw null; + public bool MoveNext() => throw null; + } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task AllowCustomForeachEnumeratorParameterReferenceAsync( + [CombinatorialValues("", "ref", "in")] string parameterModifiers, + [CombinatorialValues("", "readonly")] string getEnumeratorModifiers) + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + void Method({parameterModifiers} CannotCopy cannotCopy) + {{ + foreach (var obj in {{|#0:cannotCopy|}}) + {{ + }} + }} +}} + +[NonCopyable] +struct CannotCopy +{{ + public {getEnumeratorModifiers} Enumerator GetEnumerator() => throw null; + + public struct Enumerator + {{ + public object Current => throw null; + public bool MoveNext() => throw null; + }} +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + var expected = (parameterModifiers, getEnumeratorModifiers) switch + { + // /0/Test0.cs(8,29): warning RS0042: Unsupported use of non-copyable type 'CannotCopy' in 'ParameterReference' operation + ("in", "") => new[] { VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("CannotCopy", "ParameterReference") }, + + _ => DiagnosticResult.EmptyDiagnosticResults, + }; + + var test = new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + [Fact] + public async Task AllowCustomForeachEnumeratorDisposableObject1Async() + { + var source = @" +using System; +using System.Runtime.InteropServices; + +class C +{ + void Method() + { + using var cannotCopy = new CannotCopy(); + foreach (var obj in cannotCopy) + { + } + } +} + +[NonCopyable] +struct CannotCopy : IDisposable +{ + public void Dispose() => throw null; + public Enumerator GetEnumerator() => throw null; + + public struct Enumerator + { + public object Current => throw null; + public bool MoveNext() => throw null; + } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task AllowCustomForeachEnumeratorDisposableObject2Async() + { + var source = @" +using System; +using System.Runtime.InteropServices; + +class C +{ + void Method() + { + using (var cannotCopy = new CannotCopy()) + { + foreach (var obj in cannotCopy) + { + } + } + } +} + +[NonCopyable] +struct CannotCopy : IDisposable +{ + public void Dispose() => throw null; + public Enumerator GetEnumerator() => throw null; + + public struct Enumerator + { + public object Current => throw null; + public bool MoveNext() => throw null; + } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [InlineData("new CannotCopy()")] + [InlineData("default(CannotCopy)")] + [InlineData("CannotCopy.Create()")] + [InlineData("CannotCopy.Empty")] + public async Task AllowDisposableObjectAsync(string creation) + { + var source = $@" +using System; +using System.Runtime.InteropServices; + +class C +{{ + void UsingStatement() + {{ + using ({creation}) + {{ + }} + }} + + void UsingStatementWithVariable() + {{ + using (var cannotCopy = {creation}) + {{ + }} + }} + + void UsingStatementWithDiscard() + {{ + using (_ = {creation}) + {{ + }} + }} + + void UsingDeclarationStatement() + {{ + using var cannotCopy = {creation}; + }} +}} + +[NonCopyable] +struct CannotCopy : IDisposable +{{ + public static CannotCopy Empty => throw null; + public static CannotCopy Create() => throw null; + + public void Dispose() => throw null; +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task AllowCustomForeachReadonlyEnumeratorAsync() + { + var source = @" +using System.Runtime.InteropServices; + +class C +{ + void Method() + { + var cannotCopy = new CannotCopy(); + foreach (var obj in cannotCopy) + { + } + } +} + +[NonCopyable] +struct CannotCopy +{ + public readonly Enumerator GetEnumerator() => throw null; + + public struct Enumerator + { + public object Current => throw null; + public bool MoveNext() => throw null; + } +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [InlineData("")] + [InlineData("ref")] + [InlineData("in")] + public async Task AllowNameOfParameterReferenceAsync(string parameterModifiers) + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + void Method({parameterModifiers} CannotCopy value) + {{ + _ = nameof(CannotCopy); + _ = nameof(value); + _ = nameof(value.ToString); + }} +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [InlineData("ref")] + [InlineData("in")] + public async Task AllowUnsafeAsRefParameterReferenceAsync(string parameterModifiers) + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + ref CannotCopy Method({parameterModifiers} CannotCopy cannotCopy) + {{ + return ref AsRef(in cannotCopy); + }} + + ref T AsRef(in T value) + => throw null; +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [CombinatorialData] + public async Task AllowUnsafeAsRefParameterReference2Async( + [CombinatorialValues("ref", "in", "ref readonly")] string parameterModifiers, + [CombinatorialValues("in", "scoped in", "ref readonly", "scoped ref readonly")] string asRefParameterModifiers) + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + ref CannotCopy Method({parameterModifiers} CannotCopy cannotCopy) + {{ + return ref AsRef(in cannotCopy); + }} + + ref T AsRef({asRefParameterModifiers} T value) + => throw null; +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Theory] + [InlineData("ref")] + [InlineData("in")] + public async Task StoreUnsafeAsRefParameterReferenceToLocalAsync(string parameterModifiers) + { + var localModifiers = parameterModifiers switch + { + "in" => "ref readonly", + _ => parameterModifiers, + }; + + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + void Method({parameterModifiers} CannotCopy cannotCopy) + {{ + {localModifiers} var local = ref AsRef(in cannotCopy); + + local = ref AsRef(in cannotCopy); + }} + + ref T AsRef(in T value) + => throw null; +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task CannotStoreRefReturnByValueAsync() + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + void Method(in CannotCopy cannotCopy) + {{ + // Test with initializer + var local {{|#0:= AsRef(in cannotCopy)|}}; + + // Test with assignment to local + local = {{|#1:AsRef(in cannotCopy)|}}; + + // Implicit and explicit discard is acceptable + AsRef(in cannotCopy); + _ = AsRef(in cannotCopy); + }} + + ref T AsRef(in T value) + => throw null; +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = + { + // /0/Test0.cs(8,19): warning RS0042: Cannot assign a value from a reference to non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAssignValueFromReferenceRule).WithLocation(0).WithArguments("CannotCopy"), + // /0/Test0.cs(11,17): warning RS0042: Cannot assign a value from a reference to non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAssignValueFromReferenceRule).WithLocation(1).WithArguments("CannotCopy"), + }, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task CannotReturnRefReturnByValueAsync() + { + var source = $@" +using System.Runtime.InteropServices; + +class C +{{ + CannotCopy Method(in CannotCopy cannotCopy) + {{ + return {{|#0:AsRef(in cannotCopy)|}}; + }} + + ref T AsRef(in T value) + => throw null; +}} + +[NonCopyable] +struct CannotCopy +{{ +}} + +internal sealed class NonCopyableAttribute : System.Attribute {{ }} +"; + + await new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = + { + // /0/Test0.cs(8,16): warning RS0042: Cannot return a value from a reference to non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoReturnValueFromReferenceRule).WithLocation(0).WithArguments("CannotCopy"), + }, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task TestNonCopyableAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + CanCopy _canCopy; + CannotCopy _cannotCopy; + + void Method(object value) + { + Method(_canCopy); + Method({|#0:_cannotCopy|}); + } +} + +struct CanCopy +{ +} + +[NonCopyable] +struct CannotCopy +{ +} + +internal sealed class NonCopyableAttribute : System.Attribute { } +", + // /0/Test0.cs(12,16): warning RS0042: Unsupported use of non-copyable type 'CannotCopy' in 'FieldReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("CannotCopy", "FieldReference")); + } + + [Fact] + public async Task DoNotWrapNonCopyableTypeInNullableTAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Runtime.InteropServices; + +class C +{ + GCHandle? field {|#0:= null|}; +} +", + // /0/Test0.cs(6,21): warning RS0042: Do not wrap non-copyable type 'System.Runtime.InteropServices.GCHandle?' in 'FieldInitializer' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.AvoidNullableWrapperRule).WithLocation(0).WithArguments("System.Runtime.InteropServices.GCHandle?", "FieldInitializer")); + } + + [Fact] + public async Task DoNotDefineNonCopyableFieldInCopyableTypeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +class C1 +{ + CanCopy field1; + CannotCopy field2; +} + +struct C2 +{ + CanCopy field1; + CannotCopy {|#0:field2|}; +} + +[NonCopyable] +struct C3 +{ + CanCopy field1; + CannotCopy field2; +} + +struct CanCopy { } +[NonCopyable] struct CannotCopy { } +internal sealed class NonCopyableAttribute : System.Attribute { } +", + // /0/Test0.cs(11,16): warning RS0042: Copyable field 'C2.field2' cannot have non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoFieldOfCopyableTypeRule).WithLocation(0).WithArguments("CannotCopy", "C2.field2")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Class C1 + Private field1 As CanCopy + Private field2 As CannotCopy +End Class + +Structure C2 + Private field1 As CanCopy + Private {|#0:field2|} As CannotCopy +End Structure + + +Structure C3 + Private field1 As CanCopy + Private field2 As CannotCopy +End Structure + +Structure CanCopy : End Structure + Structure CannotCopy : End Structure +Public NotInheritable Class NonCopyableAttribute : Inherits System.Attribute : End Class +", + // /0/Test0.vb(9,13): warning RS0042: Copyable field 'Private field2 As CannotCopy' cannot have non-copyable type 'CannotCopy' + VerifyVB.Diagnostic(AbstractDoNotCopyValue.NoFieldOfCopyableTypeRule).WithLocation(0).WithArguments("CannotCopy", "Private field2 As CannotCopy")); + } + + [Fact] + public async Task DoNotDefineNonCopyableAutoPropertyAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +class C1 +{ + CanCopy Property1 { get; set; } + CanCopy Property2 { get => throw null; set => throw null; } + CannotCopy {|#0:Property3|} { get; set; } + CannotCopy Property4 { get => throw null; set => throw null; } +} + +struct C2 +{ + CanCopy Property1 { get; set; } + CanCopy Property2 { get => throw null; set => throw null; } + CannotCopy {|#1:Property3|} { get; set; } + CannotCopy Property4 { get => throw null; set => throw null; } +} + +struct CanCopy { } +[NonCopyable] struct CannotCopy { } +internal sealed class NonCopyableAttribute : System.Attribute { } +", + // /0/Test0.cs(6,16): warning RS0042: Auto-property 'C1.Property3' cannot have non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAutoPropertyRule).WithLocation(0).WithArguments("CannotCopy", "C1.Property3"), + // /0/Test0.cs(14,16): warning RS0042: Auto-property 'C2.Property3' cannot have non-copyable type 'CannotCopy' + VerifyCS.Diagnostic(AbstractDoNotCopyValue.NoAutoPropertyRule).WithLocation(1).WithArguments("CannotCopy", "C2.Property3")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Class C1 + Private Property Property1 As CanCopy + + Private Property Property2 As CanCopy + Get + Throw DirectCast(Nothing, System.Exception) + End Get + Set + Throw DirectCast(Nothing, System.Exception) + End Set + End Property + + Private Property {|#0:Property3|} As CannotCopy + + Private Property Property4 As CannotCopy + Get + Throw DirectCast(Nothing, System.Exception) + End Get + Set + Throw DirectCast(Nothing, System.Exception) + End Set + End Property +End Class + +Structure C2 + Private Property Property1 As CanCopy + + Private Property Property2 As CanCopy + Get + Throw DirectCast(Nothing, System.Exception) + End Get + Set + Throw DirectCast(Nothing, System.Exception) + End Set + End Property + + Private Property {|#1:Property3|} As CannotCopy + + Private Property Property4 As CannotCopy + Get + Throw DirectCast(Nothing, System.Exception) + End Get + Set + Throw DirectCast(Nothing, System.Exception) + End Set + End Property +End Structure + +Structure CanCopy : End Structure + Structure CannotCopy : End Structure +Public NotInheritable Class NonCopyableAttribute : Inherits System.Attribute : End Class +", + // /0/Test0.vb(14,22): warning RS0042: Auto-property 'Private Property Property3 As CannotCopy' cannot have non-copyable type 'CannotCopy' + VerifyVB.Diagnostic(AbstractDoNotCopyValue.NoAutoPropertyRule).WithLocation(0).WithArguments("CannotCopy", "Private Property Property3 As CannotCopy"), + // /0/Test0.vb(38,22): warning RS0042: Auto-property 'Private Property Property3 As CannotCopy' cannot have non-copyable type 'CannotCopy' + VerifyVB.Diagnostic(AbstractDoNotCopyValue.NoAutoPropertyRule).WithLocation(1).WithArguments("CannotCopy", "Private Property Property3 As CannotCopy")); + } + + [Fact] + public async Task AllowCopyFromCollectionExpression() + { + var source = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [NonCopyable] + [CollectionBuilder(typeof(MyCollection), nameof(Create))] + partial struct MyCollection : IEnumerable + { + public IEnumerator GetEnumerator() => null; + IEnumerator IEnumerable.GetEnumerator() => null; + + public static MyCollection Create(ReadOnlySpan r) => throw null; + } + + class C + { + void M() + { + MyCollection m = [1, 2, 3]; + m = []; + } + } + + internal sealed class NonCopyableAttribute : System.Attribute { } + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] + internal sealed class CollectionBuilderAttribute : Attribute + { + public CollectionBuilderAttribute(Type builderType, string methodName) + { + BuilderType = builderType; + MethodName = methodName; + } + + public Type BuilderType { get; } + + public string MethodName { get; } + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + }.RunAsync(); + } + + [Fact] + public async Task DoNotAllowCopyInCollectionExpressionElement() + { + var source = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + [NonCopyable] + struct S + { + } + + [CollectionBuilder(typeof(MyCollection), nameof(Create))] + partial struct MyCollection : IEnumerable + { + public IEnumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + + public static MyCollection Create(ReadOnlySpan r) => throw null; + } + + class C + { + void M() + { + S s = new(); + MyCollection m = [{|#0:s|}, new S()]; + } + } + + internal sealed class NonCopyableAttribute : System.Attribute { } + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] + internal sealed class CollectionBuilderAttribute : Attribute + { + public CollectionBuilderAttribute(Type builderType, string methodName) + { + BuilderType = builderType; + MethodName = methodName; + } + + public Type BuilderType { get; } + + public string MethodName { get; } + } + } + """; + + await new VerifyCS.Test + { + TestCode = source, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp12, + ExpectedDiagnostics = { + // /0/Test0.cs(25,27): warning RS0042: Unsupported use of non-copyable type 'S' in 'LocalReference' operation + VerifyCS.Diagnostic(AbstractDoNotCopyValue.UnsupportedUseRule).WithLocation(0).WithArguments("S", "LocalReference") + } + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotMixAttributesFromDifferentVersionsOfMEFTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotMixAttributesFromDifferentVersionsOfMEFTests.cs new file mode 100644 index 0000000000000..0c72b7819fe5d --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotMixAttributesFromDifferentVersionsOfMEFTests.cs @@ -0,0 +1,608 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpDoNotMixAttributesFromDifferentVersionsOfMEFFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.DoNotMixAttributesFromDifferentVersionsOfMEFAnalyzer, + Roslyn.Diagnostics.VisualBasic.Analyzers.BasicDoNotMixAttributesFromDifferentVersionsOfMEFFixer>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class DoNotMixAttributesFromDifferentVersionsOfMEFTests + { + private const string CSharpWellKnownAttributesDefinition = @" +namespace System.Composition +{ + public class ExportAttribute : System.Attribute + { + public ExportAttribute(System.Type contractType){ } + } + + public class MetadataAttributeAttribute : System.Attribute + { + public MetadataAttributeAttribute() { } + } + + public class ImportAttribute : System.Attribute + { + public ImportAttribute() { } + } + + public class ImportingConstructorAttribute : System.Attribute + { + public ImportingConstructorAttribute() { } + } +} + +[System.Composition.MetadataAttribute] +public class SystemCompositionMetadataAttribute : System.Attribute +{ + public class ExportAttribute : System.Attribute + { + public ExportAttribute(System.Type contractType){ } + } + + public class MetadataAttributeAttribute : System.Attribute + { + public MetadataAttributeAttribute() { } + } + + public class ImportAttribute : System.Attribute + { + public ImportAttribute() { } + } + + public class ImportingConstructorAttribute : System.Attribute + { + public ImportingConstructorAttribute() { } + } +} + +namespace System.ComponentModel.Composition +{ + public class ExportAttribute : System.Attribute + { + public ExportAttribute(System.Type contractType){ } + } + + public class MetadataAttributeAttribute : System.Attribute + { + public MetadataAttributeAttribute() { } + } + + public class ImportAttribute : System.Attribute + { + public ImportAttribute() { } + } + + public class ImportingConstructorAttribute : System.Attribute + { + public ImportingConstructorAttribute() { } + } +} + +[System.ComponentModel.Composition.MetadataAttribute] +public class SystemComponentModelCompositionMetadataAttribute : System.Attribute +{ +} +"; + private const string BasicWellKnownAttributesDefinition = @" +Namespace System.Composition + Public Class ExportAttribute + Inherits System.Attribute + Public Sub New(contractType As System.Type) + End Sub + End Class + + Public Class MetadataAttributeAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class + + Public Class ImportAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class + + Public Class ImportingConstructorAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class +End Namespace + + _ +Public Class SystemCompositionMetadataAttribute + Inherits System.Attribute +End Class + +Namespace System.ComponentModel.Composition + Public Class ExportAttribute + Inherits System.Attribute + Public Sub New(contractType As System.Type) + End Sub + End Class + + Public Class MetadataAttributeAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class + + Public Class ImportAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class + + Public Class ImportingConstructorAttribute + Inherits System.Attribute + Public Sub New() + End Sub + End Class +End Namespace + + _ +Public Class SystemComponentModelCompositionMetadataAttribute + Inherits System.Attribute +End Class +"; + + #region No Diagnostic Tests + + [Fact] + public async Task NoDiagnosticCases_SingleMefAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +[System.Composition.Export(typeof(C))] +public class C +{ +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +public class C2 +{ +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + + _ +Public Class C +End Class + + _ +Public Class C2 +End Class +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task NoDiagnosticCases_SingleMefAttributeAndValidMetadataAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +[System.Composition.Export(typeof(C))] +[SystemCompositionMetadataAttribute] +public class C +{ +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +[SystemComponentModelCompositionMetadataAttribute] +public class C2 +{ +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + + _ + _ +Public Class C +End Class + + _ + _ +Public Class C2 +End Class +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task NoDiagnosticCases_SingleMefAttributeAndAnotherExportAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +[System.Composition.Export(typeof(C)), MyNamespace.Export(typeof(C))] +public class C +{ +} + +[System.ComponentModel.Composition.Export(typeof(C2)), MyNamespace.Export(typeof(C2))] +public class C2 +{ +} + +namespace MyNamespace +{ + public class ExportAttribute : System.Attribute + { + public ExportAttribute(System.Type contractType){ } + } +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + + _ +Public Class C +End Class + + _ +Public Class C2 +End Class + +Namespace MyNamespace + Public Class ExportAttribute + Inherits System.Attribute + Public Sub New(contractType As System.Type) + End Sub + End Class +End Namespace +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task NoDiagnosticCases_SingleMefAttributeOnTypeAndValidMefAttributeOnMemberAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class B { } + +[System.Composition.Export(typeof(C))] +public class C +{ + [System.Composition.ImportingConstructor] + public C([System.Composition.Import]B b) { } + + [System.Composition.Import] + public B PropertyB { get; } +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +public class C2 +{ + [System.ComponentModel.Composition.ImportingConstructor] + public C2([System.ComponentModel.Composition.Import]B b) { } + + [System.ComponentModel.Composition.Import] + public B PropertyB { get; } +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Public Class B +End Class + + _ +Public Class C + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class + + _ +Public Class C2 + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task NoDiagnosticCases_UnresolvedTypesAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; + +public class B { } + +[System.{|CS0234:Composition|}.Export(typeof(C))] +public class C +{ + [System.ComponentModel.{|CS0234:Composition|}.Import] + public B PropertyB { get; } +} +" + }, + }, + ReferenceAssemblies = ReferenceAssemblies.Default, + }.RunAsync(); + + await new VerifyVB.Test + { + TestState = + { + Sources = + { + @" +Public Class B +End Class + +<{|BC30002:System.Composition.Export|}(GetType(C))> _ +Public Class C + <{|BC30002:System.ComponentModel.Composition.Import|}> _ + Public ReadOnly Property PropertyB() As B +End Class +" + }, + }, + ReferenceAssemblies = ReferenceAssemblies.Default, + }.RunAsync(); + } + + [Fact] + public async Task NoDiagnosticCases_MultiMefMetadataAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +[System.ComponentModel.Composition.Export(typeof(C)), MyNamespace.MultiMefMetadataAttribute] +public class C +{ +} + +namespace MyNamespace +{ + [System.ComponentModel.Composition.MetadataAttribute, System.Composition.MetadataAttribute] + public class MultiMefMetadataAttribute : System.Attribute + { + } +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + + _ +Public Class C +End Class + +Namespace MyNamespace + _ + Public Class MultiMefMetadataAttribute + Inherits System.Attribute + End Class +End Namespace +" + BasicWellKnownAttributesDefinition); + } + + #endregion + + #region Diagnostic Tests + + [Fact] + public async Task DiagnosticCases_BadMetadataAttributeAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +[System.Composition.Export(typeof(C))] +[SystemComponentModelCompositionMetadataAttribute] +public class C +{ +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +[SystemCompositionMetadataAttribute] +public class C2 +{ +} +" + CSharpWellKnownAttributesDefinition, + // Test0.cs(5,2): warning RS0006: Attribute 'SystemComponentModelCompositionMetadataAttribute' comes from a different version of MEF than the export attribute on 'C' + GetCSharpResultAt(5, 2, "SystemComponentModelCompositionMetadataAttribute", "C"), + // Test0.cs(11,2): warning RS0006: Attribute 'SystemCompositionMetadataAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetCSharpResultAt(11, 2, "SystemCompositionMetadataAttribute", "C2")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System + + _ + _ +Public Class C +End Class + + _ + _ +Public Class C2 +End Class +" + BasicWellKnownAttributesDefinition, + // Test0.vb(5,2): warning RS0006: Attribute 'SystemComponentModelCompositionMetadataAttribute' comes from a different version of MEF than the export attribute on 'C' + GetBasicResultAt(5, 2, "SystemComponentModelCompositionMetadataAttribute", "C"), + // Test0.vb(10,2): warning RS0006: Attribute 'SystemCompositionMetadataAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetBasicResultAt(10, 2, "SystemCompositionMetadataAttribute", "C2")); + } + + [Fact] + public async Task DiagnosticCases_BadMefAttributeOnMemberAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class B { } + +[System.Composition.Export(typeof(C))] +public class C +{ + [System.ComponentModel.Composition.ImportingConstructor] + public C([System.Composition.Import]B b) { } + + [System.ComponentModel.Composition.Import] + public B PropertyB { get; } +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +public class C2 +{ + [System.Composition.ImportingConstructor] + public C2([System.ComponentModel.Composition.Import]B b) { } + + [System.Composition.Import] + public B PropertyB { get; } +} +" + CSharpWellKnownAttributesDefinition, + // Test0.cs(9,6): warning RS0006: Attribute 'ImportingConstructorAttribute' comes from a different version of MEF than the export attribute on 'C' + GetCSharpResultAt(9, 6, "ImportingConstructorAttribute", "C"), + // Test0.cs(12,6): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C' + GetCSharpResultAt(12, 6, "ImportAttribute", "C"), + // Test0.cs(19,6): warning RS0006: Attribute 'ImportingConstructorAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetCSharpResultAt(19, 6, "ImportingConstructorAttribute", "C2"), + // Test0.cs(22,6): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetCSharpResultAt(22, 6, "ImportAttribute", "C2") +); + + await VerifyVB.VerifyAnalyzerAsync(@" +Public Class B +End Class + + _ +Public Class C + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class + + _ +Public Class C2 + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class +" + BasicWellKnownAttributesDefinition, + // Test0.vb(7,3): warning RS0006: Attribute 'ImportingConstructorAttribute' comes from a different version of MEF than the export attribute on 'C' + GetBasicResultAt(7, 3, "ImportingConstructorAttribute", "C"), + // Test0.vb(11,3): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C' + GetBasicResultAt(11, 3, "ImportAttribute", "C"), + // Test0.vb(17,3): warning RS0006: Attribute 'ImportingConstructorAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetBasicResultAt(17, 3, "ImportingConstructorAttribute", "C2"), + // Test0.vb(21,3): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetBasicResultAt(21, 3, "ImportAttribute", "C2") +); + } + + [Fact] + public async Task DiagnosticCases_BadMefAttributeOnParameterAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; + +public class B { } + +[System.Composition.Export(typeof(C))] +public class C +{ + [System.Composition.ImportingConstructor] + public C([System.ComponentModel.Composition.Import]B b) { } + + [System.Composition.Import] + public B PropertyB { get; } +} + +[System.ComponentModel.Composition.Export(typeof(C2))] +public class C2 +{ + [System.ComponentModel.Composition.ImportingConstructor] + public C2([System.Composition.Import]B b) { } + + [System.ComponentModel.Composition.Import] + public B PropertyB { get; } +} +" + CSharpWellKnownAttributesDefinition, + // Test0.cs(10,15): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C' + GetCSharpResultAt(10, 15, "ImportAttribute", "C"), + // Test0.cs(20,16): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetCSharpResultAt(20, 16, "ImportAttribute", "C2")); + + await VerifyVB.VerifyAnalyzerAsync(@" +Public Class B +End Class + + _ +Public Class C + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class + + _ +Public Class C2 + _ + Public Sub New( b As B) + End Sub + + _ + Public ReadOnly Property PropertyB() As B +End Class +" + BasicWellKnownAttributesDefinition, + // Test0.vb(8,18): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C' + GetBasicResultAt(8, 18, "ImportAttribute", "C"), + // Test0.vb(18,18): warning RS0006: Attribute 'ImportAttribute' comes from a different version of MEF than the export attribute on 'C2' + GetBasicResultAt(18, 18, "ImportAttribute", "C2")); + } + + #endregion + + private static DiagnosticResult GetCSharpResultAt(int line, int column, string attributeName, string typeName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(attributeName, typeName); + + private static DiagnosticResult GetBasicResultAt(int line, int column, string attributeName, string typeName) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic() + .WithLocation(line, column) +#pragma warning restore RS0030 // Do not use banned APIs + .WithArguments(attributeName, typeName); + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotUseGenericCodeActionCreateToCreateCodeActionTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotUseGenericCodeActionCreateToCreateCodeActionTests.cs new file mode 100644 index 0000000000000..c792bc9e16928 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/DoNotUseGenericCodeActionCreateToCreateCodeActionTests.cs @@ -0,0 +1,10 @@ +// 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 Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class DoNotUseGenericCodeActionCreateToCreateCodeActionTests + { + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExportedPartsShouldHaveImportingConstructorTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExportedPartsShouldHaveImportingConstructorTests.cs new file mode 100644 index 0000000000000..036df74a9b1c4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExportedPartsShouldHaveImportingConstructorTests.cs @@ -0,0 +1,972 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.ExportedPartsShouldHaveImportingConstructor, + Roslyn.Diagnostics.Analyzers.ExportedPartsShouldHaveImportingConstructorCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.ExportedPartsShouldHaveImportingConstructor, + Roslyn.Diagnostics.Analyzers.ExportedPartsShouldHaveImportingConstructorCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class ExportedPartsShouldHaveImportingConstructorTests + { + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task SingleExpectedConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task SingleExpectedConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition", true)] + [InlineData("System.Composition", false)] + [InlineData("System.ComponentModel.Composition", true)] + [InlineData("System.ComponentModel.Composition", false)] + public async Task NotInheritedAttribute_CSharpAsync(string mefNamespace, bool reflectionInherited) + { + var source = $@" +using {mefNamespace}; + +[System.AttributeUsage(System.AttributeTargets.All, Inherited = {(reflectionInherited ? "true" : "false")})] +class NotInheritedExportAttribute : System.Attribute {{ }} + +[NotInheritedExport] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} + +class D : C {{ +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition", true)] + [InlineData("System.Composition", false)] + [InlineData("System.ComponentModel.Composition", true)] + [InlineData("System.ComponentModel.Composition", false)] + public async Task NotInheritedAttribute_VisualBasicAsync(string mefNamespace, bool reflectionInherited) + { + var source = $@" +Imports {mefNamespace} + + +Class NotInheritedExportAttribute + Inherits System.Attribute +End Class + + +Class C + + Public Sub New() + End Sub +End Class + +Class D + Inherits C +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/2490")] + [InlineData("System.ComponentModel.Composition")] + public async Task InheritedExportAttribute_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[InheritedExport] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} + +class D : C {{ +}} +"; + var fixedSource = $@" +using {mefNamespace}; + +[InheritedExport] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} + +class D : C {{ + [ImportingConstructor] + public D() + {{ + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(4, 2, 4, 17).WithArguments("D") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/2490")] + [InlineData("System.ComponentModel.Composition")] + public async Task InheritedExportAttribute_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class + +Class D + Inherits C +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class + +Class D + Inherits C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(4, 2, 4, 17).WithArguments("D") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ExportAttributeNotInherited_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} + +class D : C {{ }} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ExportAttributeNotInherited_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class + +Class D + Inherits C +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task InstanceAndImplicitStaticConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + private static readonly object _gate = new object(); + + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task InstanceAndImplicitStaticConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + Private Shared ReadOnly _gate As Object = New Object() + + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task InstanceAndExplicitStaticConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + private static readonly object _gate; + + static C() {{ _gate = new object(); }} + + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task InstanceAndExplicitStaticConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + Private Shared ReadOnly _gate As Object + + Shared Sub New() + _gate = New Object() + End Sub + + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ }} +"; + var fixedSource = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() + {{ + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructorAddImport_CSharpAsync(string mefNamespace) + { + var source = $@" +[{mefNamespace}.Export] +class C {{ }} +"; + var fixedSource = $@" +using {mefNamespace}; + +[{mefNamespace}.Export] +class C {{ + [ImportingConstructor] + public C() + {{ + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(2, 2, 2, mefNamespace.Length + 9).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructorAddImport_VisualBasicAsync(string mefNamespace) + { + var source = $@" +<{mefNamespace}.Export> +Class C +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + +<{mefNamespace}.Export> +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(2, 2, 2, mefNamespace.Length + 9).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructorPlacement_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + private readonly int _value = 0; + + private int Value => _value; +}} +"; + var fixedSource = $@" +using {mefNamespace}; + +[Export] +class C {{ + private readonly int _value = 0; + + [ImportingConstructor] + public C() + {{ + }} + + private int Value => _value; +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ImplicitConstructorPlacement_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + Private ReadOnly _value1 As Integer = 0 + + Private ReadOnly Property Value + Get + return _value1 + End Get + End Property +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + +Class C + Private ReadOnly _value1 As Integer = 0 + + + Public Sub New() + End Sub + + Private ReadOnly Property Value + Get + return _value1 + End Get + End Property +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MissingAttributeConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + public C() {{ }} +}} +"; + var fixedSource = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(6, 5, 6, 19).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MissingAttributeConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + Public Sub New() + End Sub +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(6, 5, 6, 21).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MissingAttributeConstructorAddImport_CSharpAsync(string mefNamespace) + { + var source = $@" +[{mefNamespace}.Export] +class C {{ + public C() {{ }} +}} +"; + var fixedSource = $@" +using {mefNamespace}; + +[{mefNamespace}.Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(4, 5, 4, 19).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MissingAttributeConstructorAddImport_VisualBasicAsync(string mefNamespace) + { + var source = $@" +<{mefNamespace}.Export> +Class C + Public Sub New() + End Sub +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + +<{mefNamespace}.Export> +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(4, 5, 4, 21).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NonPublicConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + internal C() {{ }} +}} +"; + var fixedSource = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(6, 6, 6, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NonPublicConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Friend Sub New() + End Sub +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(6, 6, 6, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MultipleConstructors_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} + + internal C(string x) {{ }} + + private C(int x) {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic().WithSpan(9, 5, 9, 29).WithArguments("C"), + VerifyCS.Diagnostic().WithSpan(11, 5, 11, 25).WithArguments("C"), + }, + }, + FixedState = + { + // No code fix is offered for this case + Sources = { source }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MultipleConstructors_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub + + Friend Sub New(x as String) + End Sub + + Private Sub New(x as Integer) + End Sub +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = + { + VerifyVB.Diagnostic().WithSpan(10, 5, 10, 32).WithArguments("C"), + VerifyVB.Diagnostic().WithSpan(13, 5, 13, 34).WithArguments("C"), + }, + }, + FixedState = + { + // No code fix is offered for this case + Sources = { source }, + }, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExposeMemberForTestingTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExposeMemberForTestingTests.cs new file mode 100644 index 0000000000000..adf2fa9bde2d2 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ExposeMemberForTestingTests.cs @@ -0,0 +1,443 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeRefactoringVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpExposeMemberForTesting>; +using VerifyVB = Test.Utilities.VisualBasicCodeRefactoringVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.VisualBasicExposeMemberForTesting>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class ExposeMemberForTestingTests + { + [Fact] + public async Task ExposeFieldCSharpAsync() + { + var source = @"class TestClass { + private int _field; + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + } +}"; + var fixedSource = @"class TestClass { + private int _field; + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + + internal ref int Field + { + get + { + return ref _testClass._field; + } + } + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "F:TestClass._field", + }.RunAsync(); + } + + [Fact] + public async Task ExposeFieldVisualBasicAsync() + { + var source = @"Class TestClass + Private Dim _field As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + var fixedSource = @"Class TestClass + Private Dim _field As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Friend Property Field As Integer + Get + Return _testClass._field + End Get + Set(value As Integer) + _testClass._field = value + End Set + End Property + End Structure +End Class"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "F:TestClass._field", + }.RunAsync(); + } + + [Fact] + public async Task ExposeReadOnlyFieldCSharpAsync() + { + var source = @"class TestClass { + private readonly int _field; + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + } +}"; + var fixedSource = @"class TestClass { + private readonly int _field; + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + + internal ref readonly int Field + { + get + { + return ref _testClass._field; + } + } + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "F:TestClass._field", + }.RunAsync(); + } + + [Fact] + public async Task ExposeReadOnlyFieldVisualBasicAsync() + { + var source = @"Class TestClass + Private ReadOnly _field As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + var fixedSource = @"Class TestClass + Private ReadOnly _field As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Friend ReadOnly Property Field As Integer + Get + Return _testClass._field + End Get + End Property + End Structure +End Class"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "F:TestClass._field", + }.RunAsync(); + } + + [Fact] + public async Task ExposePropertyCSharpAsync() + { + var source = @"class TestClass { + private int Property { get; set; } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + } +}"; + var fixedSource = @"class TestClass { + private int Property { get; set; } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + + internal int Property + { + get + { + return _testClass.Property; + } + + set + { + _testClass.Property = value; + } + } + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.Property", + }.RunAsync(); + } + + [Fact] + public async Task ExposePropertyVisualBasicAsync() + { + var source = @"Class TestClass + Private Property TestProperty As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + var fixedSource = @"Class TestClass + Private Property TestProperty As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Friend Property TestProperty As Integer + Get + Return _testClass.TestProperty + End Get + Set(value As Integer) + _testClass.TestProperty = value + End Set + End Property + End Structure +End Class"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.TestProperty", + }.RunAsync(); + } + + [Fact] + public async Task ExposeReadOnlyPropertyCSharpAsync() + { + var source = @"class TestClass { + private int Property { get; } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + } +}"; + var fixedSource = @"class TestClass { + private int Property { get; } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + + internal int Property + { + get + { + return _testClass.Property; + } + } + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.Property", + }.RunAsync(); + } + + [Fact] + public async Task ExposeReadOnlyPropertyVisualBasicAsync() + { + var source = @"Class TestClass + Private ReadOnly Property TestProperty As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + var fixedSource = @"Class TestClass + Private ReadOnly Property TestProperty As Integer + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Friend ReadOnly Property TestProperty As Integer + Get + Return _testClass.TestProperty + End Get + End Property + End Structure +End Class"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.TestProperty", + }.RunAsync(); + } + + [Fact] + public async Task ExposeWriteOnlyPropertyCSharpAsync() + { + var source = @"class TestClass { + private int Property { set { } } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + } +}"; + var fixedSource = @"class TestClass { + private int Property { set { } } + internal readonly struct [|TestAccessor|] { + private readonly TestClass _testClass; + internal TestAccessor(TestClass testClass) => _testClass = testClass; + + internal int Property + { + set + { + _testClass.Property = value; + } + } + } +}"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.Property", + }.RunAsync(); + } + + [Fact] + public async Task ExposeWriteOnlyPropertyVisualBasicAsync() + { + var source = @"Class TestClass + Private WriteOnly Property TestProperty As Integer + Set(value As Integer) + End Set + End Property + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + End Structure +End Class"; + var fixedSource = @"Class TestClass + Private WriteOnly Property TestProperty As Integer + Set(value As Integer) + End Set + End Property + Friend Structure [|TestAccessor|] + Private Dim ReadOnly _testClass As TestClass + Friend Sub New(testClass As TestClass) + _testClass = testClass + End Sub + + Friend WriteOnly Property TestProperty As Integer + Set(value As Integer) + _testClass.TestProperty = value + End Set + End Property + End Structure +End Class"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + CodeActionEquivalenceKey = "P:TestClass.TestProperty", + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs new file mode 100644 index 0000000000000..90917dc75d345 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/ImportingConstructorShouldBeObsoleteTests.cs @@ -0,0 +1,770 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.ImportingConstructorShouldBeObsolete, + Roslyn.Diagnostics.Analyzers.ImportingConstructorShouldBeObsoleteCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.ImportingConstructorShouldBeObsolete, + Roslyn.Diagnostics.Analyzers.ImportingConstructorShouldBeObsoleteCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class ImportingConstructorShouldBeObsoleteTests + { + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task SingleExpectedConstructor_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task SingleExpectedConstructor_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef.MefConstruction + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ObsoleteButNotError_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: false)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(9, 6, 9, 73).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ObsoleteButNotError_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef.MefConstruction + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef.MefConstruction + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(9, 6, 9, 66).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotMarkedObsolete_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(7, 6, 7, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotMarkedObsolete_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(7, 6, 7, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotMarkedObsoleteAddImports_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + public C() {{ }} +}} +"; + var helperSource = $@" +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source, helperSource }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(6, 6, 6, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource, helperSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotMarkedObsoleteAddImports_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System + +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(6, 6, 6, 26).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MessageArgumentOmitted_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(8, 6, 8, 14).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task MessageArgumentOmitted_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(8, 6, 8, 14).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ErrorArgumentOmitted_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(9, 6, 9, 59).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task ErrorArgumentOmitted_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef.MefConstruction + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef.MefConstruction + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(9, 6, 9, 59).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task IncorrectMessage_CSharpAsync(string mefNamespace) + { + var source = $@" +using System; +using {mefNamespace}; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(""INCORRECT MESSAGE"")] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + var fixedSource = $@" +using System; +using {mefNamespace}; +using Microsoft.CodeAnalysis.Host.Mef; + +[Export] +class C {{ + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public C() {{ }} +}} + +namespace Microsoft.CodeAnalysis.Host.Mef {{ + static class MefConstruction {{ + internal const string ImportingConstructorMessage = ""This exported object must be obtained through the MEF export provider.""; + }} +}} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(8, 6, 8, 35).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + NumberOfIncrementalIterations = 2, + NumberOfFixAllIterations = 2, + }.RunAsync(); + } + + [WindowsOnlyTheory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task IncorrectMessage_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports System +Imports {mefNamespace} + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + var fixedSource = $@" +Imports System +Imports {mefNamespace} +Imports Microsoft.CodeAnalysis.Host.Mef + + +Class C + + + Public Sub New() + End Sub +End Class + +Namespace Global.Microsoft.CodeAnalysis.Host.Mef + Module MefConstruction + Friend Const ImportingConstructorMessage As String = ""This exported object must be obtained through the MEF export provider."" + End Module +End Namespace +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(8, 6, 8, 35).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + NumberOfIncrementalIterations = 2, + NumberOfFixAllIterations = 2, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsTests.cs new file mode 100644 index 0000000000000..4d77c1d8a6aa7 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsTests.cs @@ -0,0 +1,10 @@ +// 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 Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsTests + { + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/NumberCommentsRefactoringTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/NumberCommentsRefactoringTests.cs new file mode 100644 index 0000000000000..95b6e0663789a --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/NumberCommentsRefactoringTests.cs @@ -0,0 +1,309 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeRefactoringVerifier; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class NumberCommentsRefactoringTests + { + [Fact] + public async Task TestAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]class D { } // +""; +}"; + const string fixedSource = @" +public class C +{ + string s = @"" +class D { } // 1 +""; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task TestAsync_RawStringLiteral() + { + const string source = """" +public class C +{ + string s = """ +[||]class D { } // +"""; +} +""""; + const string fixedSource = """" +public class C +{ + string s = """ +class D { } // 1 +"""; +} +""""; + await VerifyCSharp11Async(source, fixedSource); + } + + [Fact] + public async Task TestAsync_RawStringLiteral_Indented() + { + const string source = """" +public class C +{ + string s = """ + [||]class D { } // + """; +} +""""; + const string fixedSource = """" +public class C +{ + string s = """ + class D { } // 1 + """; +} +""""; + await VerifyCSharp11Async(source, fixedSource); + } + + [Fact] + public async Task TestAsync_RawStringLiteral_Indented_Multiple() + { + const string source = """" +public class C +{ + string s = """ + [||]class D { } // + class E { } //, + """; +} +""""; + const string fixedSource = """" +public class C +{ + string s = """ + class D { } // 1 + class E { } // 2, 3 + """; +} +""""; + await VerifyCSharp11Async(source, fixedSource); + } + + [Fact] + public async Task CSharp_VerifyFix_WithTriviaAsync() + { + const string source = @" +public class C +{ + string s = +[||]/*before*/ @"" +class D { } // +"" /*after*/ ; +}"; + const string fixedSource = @" +public class C +{ + string s = +/*before*/ @"" +class D { } // 1 +"" /*after*/ ; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task CSharp_VerifyFix_NonNumberCommentsLeftAloneAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]// +class D // +{// +} // test +""; +}"; + const string fixedSource = @" +public class C +{ + string s = @"" +// +class D // 1 +{// +} // test +""; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task CSharp_VerifyFix_MultipleCommasAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]class D //1 +{ //, +} // +""; +}"; + const string fixedSource = @" +public class C +{ + string s = @"" +class D // 1 +{ // 2, 3 +} // 4 +""; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task CSharp_VerifyFix_LastLineAsync() + { + const string source = @" +public class C +{ + string s = @""[||]class D { } //""; +}"; + const string fixedSource = @" +public class C +{ + string s = @""class D { } // 1""; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task CountOverTenAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]class D { } // ,,,,,,,,,,,, +""; +}"; + const string fixedSource = @" +public class C +{ + string s = @"" +class D { } // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 +""; +}"; + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task EmptyNumberIsImproperAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]class D // 1 +{ // 2, 3 +} // +""; +}"; + + const string fixedSource = @" +public class C +{ + string s = @"" +class D // 1 +{ // 2, 3 +} // 4 +""; +}"; + + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task EmptyNumberBeforeCommaIsImproperAsync() + { + const string source = @" +public class C +{ + string s = @"" +[||]class C // 1 +{ // , 3 +} +""; +}"; + + const string fixedSource = @" +public class C +{ + string s = @"" +class C // 1 +{ // 2, 3 +} +""; +}"; + + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + [Fact] + public async Task EmptyCommentOnEmptyLineIsProperAsync() + { + const string source = @" +public class C +{ + string s = @"" +// much stuff +// +// more stuff +[||]class C // 1 +{ // 2, 3 +} // +""; +}"; + + const string fixedSource = @" +public class C +{ + string s = @"" +// much stuff +// +// more stuff +class C // 1 +{ // 2, 3 +} // 4 +""; +}"; + + await VerifyCS.VerifyRefactoringAsync(source, fixedSource); + } + + #region Utilities + private async Task VerifyCSharp11Async(string source, string fixedSource) + { + var test = new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion.CSharp11, + }; + + test.ExpectedDiagnostics.AddRange(DiagnosticResult.EmptyDiagnosticResults); + await test.RunAsync(); + } + #endregion + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PartsExportedWithMEFv2MustBeMarkedAsSharedTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PartsExportedWithMEFv2MustBeMarkedAsSharedTests.cs new file mode 100644 index 0000000000000..fcc7154869167 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PartsExportedWithMEFv2MustBeMarkedAsSharedTests.cs @@ -0,0 +1,213 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer, + Roslyn.Diagnostics.CSharp.Analyzers.CSharpPartsExportedWithMEFv2MustBeMarkedAsSharedFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.PartsExportedWithMEFv2MustBeMarkedAsSharedAnalyzer, + Roslyn.Diagnostics.VisualBasic.Analyzers.BasicPartsExportedWithMEFv2MustBeMarkedAsSharedFixer>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class PartsExportedWithMEFv2MustBeMarkedAsSharedTests + { + private const string CSharpWellKnownAttributesDefinition = @" +namespace System.Composition +{ + public class ExportAttribute : System.Attribute + { + public ExportAttribute(System.Type contractType){ } + } + + public class SharedAttribute : System.Attribute + { + } +} +"; + private const string BasicWellKnownAttributesDefinition = @" +Namespace System.Composition + Public Class ExportAttribute + Inherits System.Attribute + Public Sub New(contractType As System.Type) + End Sub + End Class + + Public Class SharedAttribute + Inherits System.Attribute + End Class +End Namespace + +"; + + #region No Diagnostic Tests + + [Fact] + public async Task NoDiagnosticCases_ResolvedTypesAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +using System.Composition; + +[Export(typeof(C)), Shared] +public class C +{ +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System +Imports System.Composition + + _ +Public Class C +End Class +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task NoDiagnosticCases_UnresolvedTypesAsync() + { + await new VerifyCS.Test + { + TestState = + { + Sources = + { + @" +using System; +using System.{|CS0234:Composition|}; + +[{|CS0246:{|CS0246:Export|}|}(typeof(C)), {|CS0246:{|CS0246:Shared|}|}] +public class C +{ +} +", + }, + }, + ReferenceAssemblies = ReferenceAssemblies.Default, + }.RunAsync(); + + await new VerifyVB.Test + { + TestState = + { + Sources = + { + @" +Imports System +Imports System.Composition + +<{|BC30002:Export|}(GetType(C)), {|BC30002:[Shared]|}> _ +Public Class C +End Class +" + }, + }, + ReferenceAssemblies = ReferenceAssemblies.Default, + }.RunAsync(); + } + + #endregion + + #region Diagnostic Tests + + [Fact] + public async Task DiagnosticCases_NoSharedAttributeAsync() + { + await VerifyCS.VerifyCodeFixAsync(@" +using System; +using System.Composition; + +[[|Export(typeof(C))|]] +public class C +{ +} +" + CSharpWellKnownAttributesDefinition, @" +using System; +using System.Composition; + +[Export(typeof(C))] +[Shared] +public class C +{ +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System +Imports System.Composition + +<[|Export(GetType(C))|]> _ +Public Class C +End Class +" + BasicWellKnownAttributesDefinition, @" +Imports System +Imports System.Composition + + _ +<[Shared]> +Public Class C +End Class +" + BasicWellKnownAttributesDefinition); + } + + [Fact] + public async Task DiagnosticCases_DifferentSharedAttributeAsync() + { + await VerifyCS.VerifyCodeFixAsync(@" +using System; + +[[|System.Composition.Export(typeof(C))|], Shared] +public class C +{ +} + +public class SharedAttribute: Attribute +{ +} +" + CSharpWellKnownAttributesDefinition, @" +using System; + +[System.Composition.Export(typeof(C)), Shared] +[System.Composition.Shared] +public class C +{ +} + +public class SharedAttribute: Attribute +{ +} +" + CSharpWellKnownAttributesDefinition); + + await VerifyVB.VerifyCodeFixAsync(@" +Imports System + +<[|System.Composition.Export(GetType(C))|], [Shared]> _ +Public Class C +End Class + +Public Class SharedAttribute + Inherits Attribute +End Class +" + BasicWellKnownAttributesDefinition, @" +Imports System + + _ + +Public Class C +End Class + +Public Class SharedAttribute + Inherits Attribute +End Class +" + BasicWellKnownAttributesDefinition); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PreferNullLiteralTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PreferNullLiteralTests.cs new file mode 100644 index 0000000000000..030091c2792dc --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/PreferNullLiteralTests.cs @@ -0,0 +1,508 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.PreferNullLiteral, + Roslyn.Diagnostics.CSharp.Analyzers.PreferNullLiteralCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class PreferNullLiteralTests + { + [Theory] + [InlineData("default")] + [InlineData("default(object)")] + public async Task PreferNullLiteral_ClassAsync(string defaultValueExpression) + { + var source = $@" +class Type +{{ + object Method() + {{ + return [|{defaultValueExpression}|]; + }} +}} +"; + var fixedSource = @" +class Type +{ + object Method() + { + return null; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task UnresolvedTypeAsync() + { + var source = @" +class Type +{ + void Method() + { + {|CS0411:Method2|}(default); + } + + void Method2(T value) + { + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Theory] + [InlineData("default", "null")] + [InlineData("default(object)", "null")] + [InlineData("default(object?)", "null")] + public async Task ReturnFromNullableContextAsync(string defaultValueExpression, string fixedExpression) + { + var source = $@" +#nullable enable + +class Type +{{ + object Method() + {{ + return [|{defaultValueExpression}|]!; + }} +}} +"; + var fixedSource = $@" +#nullable enable + +class Type +{{ + object Method() + {{ + return {fixedExpression}!; + }} +}} +"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [InlineData("[|default(object)|]!", "((object?)null)!")] + [InlineData("[|default(object?)|]!", "((object?)null)!")] + [InlineData("[|default(object)|]", "(object?)null")] + [InlineData("[|default(object?)|]", "(object?)null")] + public async Task InvocationInNullableContextAsync(string defaultValueExpression, string fixedExpression) + { + var source = $@" +#nullable enable + +class Type +{{ + void Method() + {{ + Method2({defaultValueExpression}); + }} + + void Method2(T value) + {{ + }} +}} +"; + var fixedSource = $@" +#nullable enable + +class Type +{{ + void Method() + {{ + Method2({fixedExpression}); + }} + + void Method2(T value) + {{ + }} +}} +"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Fact] + public async Task NullPointerAsync() + { + var source = @" +unsafe class Type +{ + void Method() + { + Method2([|default(int*)|]); + } + + void Method2(int* value) { } + void Method2(byte* value) { } +} +"; + var fixedSource = @" +unsafe class Type +{ + void Method() + { + Method2((int*)null); + } + + void Method2(int* value) { } + void Method2(byte* value) { } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PointerInNullableContextAsync() + { + var source = @" +#nullable enable + +unsafe class Type +{ + void Method() + { + Method2([|default(int*)|]); + } + + void Method2(int* value) { } + void Method2(byte* value) { } +} +"; + var fixedSource = @" +#nullable enable + +unsafe class Type +{ + void Method() + { + Method2((int*)null); + } + + void Method2(int* value) { } + void Method2(byte* value) { } +} +"; + + await new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = LanguageVersion.CSharp8, + }.RunAsync(); + } + + [Theory] + [InlineData("default")] + [InlineData("default(object)")] + public async Task PreferNullLiteral_DefaultParameterValueAsync(string defaultValueExpression) + { + var source = $@" +class Type +{{ + void Method(object value = [|{defaultValueExpression}|]) + {{ + }} +}} +"; + var fixedSource = @" +class Type +{ + void Method(object value = null) + { + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PreferNullLiteral_ArgumentFormattingAsync() + { + var source = $@" +class Type +{{ + void Method() + {{ + Method2( + 0, + [|default|], + /*1*/ [|default|] /*2*/, + [|default(object)|], + /*1*/ [|default /*2*/ ( /*3*/ object /*4*/ )|] /*5*/, + """"); + }} + + void Method2(params object[] values) + {{ + }} +}} +"; + var fixedSource = @" +class Type +{ + void Method() + { + Method2( + 0, + null, + /*1*/ null /*2*/, + null, + /*1*/ /*3*/ /*4*/ null /*2*/ /*5*/, + """"); + } + + void Method2(params object[] values) + { + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PreferNullLiteral_OverloadResolutionAsync() + { + var source = @" +using System; + +class Type +{ + void Method() + { + Method2([|default(object)|]); + Method2([|default(string)|]); + Method2([|default(IComparable)|]); + Method2([|default(int?)|]); + Method2(default(int)); + } + + void Method2(T value) + { + } +} +"; + var fixedSource = @" +using System; + +class Type +{ + void Method() + { + Method2((object)null); + Method2((string)null); + Method2((IComparable)null); + Method2((int?)null); + Method2(default(int)); + } + + void Method2(T value) + { + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PreferNullLiteral_ParenthesizeWhereNecessaryAsync() + { + var source = @" +using System; + +class Type +{ + void Method() + { + Method2([|default(object)|]?.ToString()); + Method2([|default(string)|]?.ToString()); + Method2([|default(IComparable)|]?.ToString()); + Method2([|default(int?)|]?.ToString()); + Method2(default(int).ToString()); + } + + void Method2(string value) + { + } +} +"; + var fixedSource = @" +using System; + +class Type +{ + void Method() + { + Method2(((object)null)?.ToString()); + Method2(((string)null)?.ToString()); + Method2(((IComparable)null)?.ToString()); + Method2(((int?)null)?.ToString()); + Method2(default(int).ToString()); + } + + void Method2(string value) + { + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PreferNullLiteral_StructAsync() + { + var source = @" +class Type +{ + int Method() + { + return default; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task PreferNullLiteral_StructConvertedToReferenceTypeAsync() + { + var source = @" +class Type +{ + object Method() + { + return default(int); + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task PreferNullLiteral_UnconstrainedGenericAsync() + { + var source = @" +class Type +{ + T Method() + { + return default; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task PreferNullLiteral_GenericConstrainedToReferenceTypeAsync() + { + var source = @" +class Type +{ + T Method() + where T : class + { + return [|default|]; + } +} +"; + var fixedSource = @" +class Type +{ + T Method() + where T : class + { + return null; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task PreferNullLiteral_GenericConstrainedToInterfaceAsync() + { + var source = @" +class Type +{ + T Method() + where T : System.IComparable + { + return default; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task PreferNullLiteral_GenericConstrainedToValueTypeAsync() + { + var source = @" +class Type +{ + T Method() + where T : struct + { + return default; + } +} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Theory] + [InlineData("object")] + [InlineData("int?")] + public async Task IgnoreDefaultParametersAsync(string defaultParameterType) + { + var source = $@" +class Type +{{ + void Method1() + {{ + Method2(0); + }} + + void Method2(int first, {defaultParameterType} value = null) + {{ + }} +}} +"; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Properties/launchSettings.json b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000000..4e3b720d74ec4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "TestSetup": { + "commandName": "Executable", + "executablePath": "$(NuGetPackageRoot)\\xunit.runner.console\\$(XUnitVersion)\\tools\\xunit.console.x86.exe", + "commandLineArgs": "$(AssemblyName).dll -noshadow -wait", + "workingDirectory": "$(OutDir)" + } + } +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RelaxTestNamingSuppressorTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RelaxTestNamingSuppressorTests.cs new file mode 100644 index 0000000000000..569484312a8fa --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RelaxTestNamingSuppressorTests.cs @@ -0,0 +1,149 @@ +// 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.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.UnitTests.RelaxTestNamingSuppressorTests.WarnForMissingAsyncSuffix, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class RelaxTestNamingSuppressorTests + { + private static Solution WithoutSuppressedDiagnosticsTransform(Solution solution, ProjectId projectId) + { + var compilationOptions = solution.GetProject(projectId)!.CompilationOptions; + return solution.WithProjectCompilationOptions(projectId, compilationOptions!.WithReportSuppressedDiagnostics(false)); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/41584")] + public async Task TestClassWithFactAsync() + { + var code = @" +using System.Threading.Tasks; +using Xunit; + +public class SomeClass { +[Fact] +public async Task [|TestMethod|]() { } +} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestState = { Sources = { code } }, + SolutionTransforms = { WithoutSuppressedDiagnosticsTransform }, + }.RunAsync(); + + await new TestWithSuppressor + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestState = { Sources = { code }, MarkupHandling = MarkupMode.Ignore, }, + SolutionTransforms = { WithoutSuppressedDiagnosticsTransform }, + }.RunAsync(); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/41584")] + public async Task TestClassWithTheoryAsync() + { + var code = @" +using Xunit; + +public class [|SomeClass|] { +[Theory, InlineData(0)] +public void TestMethod(int arg) { } +} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestState = { Sources = { code } }, + SolutionTransforms = { WithoutSuppressedDiagnosticsTransform }, + }.RunAsync(); + + await new TestWithSuppressor + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestState = { Sources = { code }, MarkupHandling = MarkupMode.Ignore, }, + SolutionTransforms = { WithoutSuppressedDiagnosticsTransform }, + }.RunAsync(); + } + + [Fact] + public async Task TestAlreadyHasAsyncSuffixAsync() + { + var code = @" +using System.Threading.Tasks; +using Xunit; + +public class SomeClass { +[Fact] +public async Task TestMethodAsync() { } +} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = AdditionalMetadataReferences.DefaultWithXUnit, + TestState = { Sources = { code } }, + }.RunAsync(); + } + + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class WarnForMissingAsyncSuffix : DiagnosticAnalyzer + { + [SuppressMessage("MicrosoftCodeAnalysisDesign", "RS1017:DiagnosticId for analyzers must be a non-null constant.", Justification = "For suppression test only.")] + public static readonly DiagnosticDescriptor Rule = new(RelaxTestNamingSuppressor.Rule.SuppressedDiagnosticId, "Title", "Message", "Category", DiagnosticSeverity.Warning, isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method); + } + + private void AnalyzeSymbol(SymbolAnalysisContext context) + { + var method = (IMethodSymbol)context.Symbol; + if (method.Name.EndsWith("Async", StringComparison.Ordinal)) + { + return; + } + + if (method.ReturnType.MetadataName != "Task") + { + // Not asynchronous (incomplete checking is sufficient for this test) + return; + } + + context.ReportDiagnostic(Diagnostic.Create(Rule, method.Locations[0])); + } + } + + internal sealed class TestWithSuppressor : VerifyCS.Test + { + protected override IEnumerable GetDiagnosticAnalyzers() + { + foreach (var analyzer in base.GetDiagnosticAnalyzers()) + yield return analyzer; + + yield return new RelaxTestNamingSuppressor(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Roslyn.Diagnostics.Analyzers.UnitTests.csproj b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Roslyn.Diagnostics.Analyzers.UnitTests.csproj new file mode 100644 index 0000000000000..55d73f813b55f --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/Roslyn.Diagnostics.Analyzers.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + $(NetRoslyn) + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RunIterationsTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RunIterationsTests.cs new file mode 100644 index 0000000000000..01f12682fb471 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/RunIterationsTests.cs @@ -0,0 +1,415 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeRefactoringVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpRunIterations>; +using VerifyVB = Test.Utilities.VisualBasicCodeRefactoringVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.VisualBasicRunIterations>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class RunIterationsTests + { + private static readonly ReferenceAssemblies xunitWithCombinatorial = + AdditionalMetadataReferences.DefaultWithXUnit.AddPackages( + ImmutableArray.Create(new PackageIdentity("Xunit.Combinatorial", "1.4.1"))); + + [Theory] + [InlineData("Fact")] + [InlineData("FactAttribute")] + [InlineData("CustomFact")] + [InlineData("CustomFactAttribute")] + public async Task RunIterationsOfFact_CSharp(string attributeName) + { + var updatedName = attributeName switch + { + "Fact" => "Theory", + "FactAttribute" => "TheoryAttribute", + "CustomFact" => "CustomTheory", + "CustomFactAttribute" => "CustomTheoryAttribute", + _ => throw new ArgumentException("Unexpected argument", nameof(attributeName)), + }; + + await new VerifyCS.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}] + public void $$Method() + {{ + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + FixedCode = $@"using Xunit; + +class TestClass +{{ + [{updatedName}] + [CombinatorialData] + public void $$Method([CombinatorialRange(0, 10)] int iteration) + {{ + _ = iteration; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + }.RunAsync(); + } + + [Theory] + [InlineData("Fact")] + [InlineData("FactAttribute")] + [InlineData("CustomFact")] + [InlineData("CustomFactAttribute")] + public async Task RunIterationsOfFact_VisualBasic(string attributeName) + { + // Visual Basic syntax generator, formatter, and/or simplifier drops the Attribute suffix automatically + var updatedName = attributeName switch + { + "Fact" => "Theory", + "FactAttribute" => "Theory", + "CustomFact" => "CustomTheory", + "CustomFactAttribute" => "CustomTheory", + _ => throw new ArgumentException("Unexpected argument", nameof(attributeName)), + }; + + await new VerifyVB.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"Imports Xunit + +Class TestClass + <{attributeName}> + Public Sub $$Method() + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + FixedCode = $@"Imports Xunit + +Class TestClass + <{updatedName}> + + Public Sub $$Method( iteration As Integer) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + }.RunAsync(); + } + + [Theory] + [InlineData("Fact")] + [InlineData("FactAttribute")] + [InlineData("CustomFact")] + [InlineData("CustomFactAttribute")] + public async Task RunIterationsOfFactWithTrait_CSharp(string attributeName) + { + var updatedName = attributeName switch + { + "Fact" => "Theory", + "FactAttribute" => "TheoryAttribute", + "CustomFact" => "CustomTheory", + "CustomFactAttribute" => "CustomTheoryAttribute", + _ => throw new ArgumentException("Unexpected argument", nameof(attributeName)), + }; + + await new VerifyCS.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}, Trait(""Key"", ""Value"")] + public void $$Method() + {{ + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + FixedCode = $@"using Xunit; + +class TestClass +{{ + [{updatedName}, Trait(""Key"", ""Value"")] + [CombinatorialData] + public void $$Method([CombinatorialRange(0, 10)] int iteration) + {{ + _ = iteration; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + }.RunAsync(); + } + + [Theory] + [InlineData("Fact")] + [InlineData("FactAttribute")] + [InlineData("CustomFact")] + [InlineData("CustomFactAttribute")] + public async Task RunIterationsOfFactWithTrait_VisualBasic(string attributeName) + { + // Visual Basic syntax generator, formatter, and/or simplifier drops the Attribute suffix automatically + var updatedName = attributeName switch + { + "Fact" => "Theory", + "FactAttribute" => "Theory", + "CustomFact" => "CustomTheory", + "CustomFactAttribute" => "CustomTheory", + _ => throw new ArgumentException("Unexpected argument", nameof(attributeName)), + }; + + await new VerifyVB.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"Imports Xunit + +Class TestClass + <{attributeName}, Trait(""Key"", ""Value"")> + Public Sub $$Method() + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + FixedCode = $@"Imports Xunit + +Class TestClass + <{updatedName}, Trait(""Key"", ""Value"")> + + Public Sub $$Method( iteration As Integer) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task NoIterationsForTheoryWithInlineData_CSharp(string attributeName) + { + var testCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}] + [InlineData(true)] + public void $$Method(bool arg) + {{ + _ = arg; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = testCode, + FixedCode = testCode, + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task NoIterationsForTheoryWithInlineData_VisualBasic(string attributeName) + { + var testCode = $@"Imports Xunit + +Class TestClass + <{attributeName}> + + Public Sub $$Method(arg As Boolean) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +"; + + await new VerifyVB.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = testCode, + FixedCode = testCode, + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task NoIterationsForTheoryWithIterations_CSharp(string attributeName) + { + var testCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}] + [CombinatorialData] + public void $$Method([CombinatorialRange(0, 10)] int iteration) + {{ + _ = iteration; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +"; + + await new VerifyCS.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = testCode, + FixedCode = testCode, + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task NoIterationsForTheoryWithIterations_VisualBasic(string attributeName) + { + var testCode = $@"Imports Xunit + +Class TestClass + <{attributeName}> + + Public Sub $$Method( iteration As Integer) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +"; + + await new VerifyVB.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = testCode, + FixedCode = testCode, + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task RunIterationsOfTheory_CSharp(string attributeName) + { + await new VerifyCS.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}] + [CombinatorialData] + public void $$Method(bool arg) + {{ + _ = arg; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + FixedCode = $@"using Xunit; + +class TestClass +{{ + [{attributeName}] + [CombinatorialData] + public void $$Method(bool arg, [CombinatorialRange(0, 10)] int iteration) + {{ + _ = iteration; + _ = arg; + }} +}} + +class CustomFactAttribute : FactAttribute {{ }} +class CustomTheoryAttribute : TheoryAttribute {{ }} +", + }.RunAsync(); + } + + [Theory] + [InlineData("Theory")] + [InlineData("TheoryAttribute")] + [InlineData("CustomTheory")] + [InlineData("CustomTheoryAttribute")] + public async Task RunIterationsOfTheory_VisualBasic(string attributeName) + { + await new VerifyVB.Test + { + ReferenceAssemblies = xunitWithCombinatorial, + TestCode = $@"Imports Xunit + +Class TestClass + <{attributeName}> + + Public Sub $$Method(arg As Boolean) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + FixedCode = $@"Imports Xunit + +Class TestClass + <{attributeName}> + + Public Sub $$Method(arg As Boolean, iteration As Integer) + End Sub +End Class + +Class CustomFactAttribute : Inherits FactAttribute : End Class +Class CustomTheoryAttribute : Inherits TheoryAttribute : End Class +", + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SpecializedEnumerableCreationAnalyzerTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SpecializedEnumerableCreationAnalyzerTests.cs new file mode 100644 index 0000000000000..8b6d222fc06ab --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SpecializedEnumerableCreationAnalyzerTests.cs @@ -0,0 +1,332 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.CSharp.Analyzers.CSharpSpecializedEnumerableCreationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.VisualBasic.Analyzers.BasicSpecializedEnumerableCreationAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class SpecializedEnumerableCreationAnalyzerTests + { + private readonly string _csharpSpecializedCollectionsDefinition = @" +namespace Roslyn.Utilities +{ + public class SpecializedCollections { } +} +"; + private readonly string _basicSpecializedCollectionsDefinition = @" +Namespace Roslyn.Utilities + Public Class SpecializedCollections + End Class +End Namespace +"; + + private static DiagnosticResult GetCSharpEmptyEnumerableResultAt(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetBasicEmptyEnumerableResultAt(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(SpecializedEnumerableCreationAnalyzer.UseEmptyEnumerableRule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetCSharpSingletonEnumerableResultAt(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyCS.Diagnostic(SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetBasicSingletonEnumerableResultAt(int line, int column) => +#pragma warning disable RS0030 // Do not use banned APIs + VerifyVB.Diagnostic(SpecializedEnumerableCreationAnalyzer.UseSingletonEnumerableRule).WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + [Fact] + public async Task ReturnEmptyArrayCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable M1() { return new int[0]; } + IEnumerable M2() { return new int[0] { }; } + int[] M3() { return new int[0]; } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpEmptyEnumerableResultAt(6, 36), + GetCSharpEmptyEnumerableResultAt(7, 36)); + } + + [Fact] + public async Task ReturnSingletonArrayCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable M1() { return new int[1]; } + IEnumerable M2() { return new int[1] { 1 }; } + IEnumerable M3() { return new[] { 1 }; } + int[] M4() { return new[] { 1 }; } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpSingletonEnumerableResultAt(6, 36), + GetCSharpSingletonEnumerableResultAt(7, 36), + GetCSharpSingletonEnumerableResultAt(8, 36)); + } + + [Fact] + public async Task ReturnLinqEmptyEnumerableCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; +using System.Linq; + +class C +{ + IEnumerable M1() { return Enumerable.Empty(); } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpEmptyEnumerableResultAt(7, 36)); + } + + [Fact(Skip = "855425")] + public async Task ReturnArrayWithinExpressionCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable M1() { return 0 == 1 ? new[] { 1 } : new[] { 2 }; } + IEnumerable M2() { return null ?? new int[0]; } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpSingletonEnumerableResultAt(6, 45), + GetCSharpSingletonEnumerableResultAt(6, 59), + GetCSharpEmptyEnumerableResultAt(7, 44)); + } + + [Fact] + public async Task ReturnLinqEmptyEnumerableWithinExpressionCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; +using System.Linq; + +class C +{ + IEnumerable M1() { return 0 == 1 ? Enumerable.Empty() : null; } + IEnumerable M2() { return null ?? Enumerable.Empty(); } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpEmptyEnumerableResultAt(7, 45), + GetCSharpEmptyEnumerableResultAt(8, 44)); + } + + [Fact] + public async Task ReturnMultiElementArrayCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable M1() { return new int[2]; } + IEnumerable M2() { return new int[2] { 1, 2 }; } + IEnumerable M3() { return new[] { 1, 2 }; } + int[] M4() { return new[] { 1, 2 }; } +} +" + _csharpSpecializedCollectionsDefinition); + } + + [Fact] + public async Task ReturnJaggedArrayCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable M1() { return new int[2][] { new int[0], new int[0] }; } + IEnumerable M2() { return new[] { new[] { 1 } }; } + IEnumerable M3() { return new[] { new[] { 1, 2, 3 }, new[] { 1 } }; } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpSingletonEnumerableResultAt(7, 38)); + } + + [Fact(Skip = "855425")] + public async Task ImplicitConversionToNestedEnumerableCSharpAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System.Collections.Generic; + +class C +{ + IEnumerable> M1() { return new[] { new[] { 1 } }; } +} +" + _csharpSpecializedCollectionsDefinition, + GetCSharpSingletonEnumerableResultAt(5, 49), + GetCSharpSingletonEnumerableResultAt(5, 57)); + } + + [Fact] + public async Task ReturnEmptyArrayBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of Integer) + Return New Integer(0) {} + End Function + Function M2() As IEnumerable(Of Integer) + Return {} + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicEmptyEnumerableResultAt(6, 16), + GetBasicEmptyEnumerableResultAt(9, 16)); + } + + [Fact] + public async Task ReturnLinqEmptyEnumerableBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic +Imports System.Linq + +Class C + Function M1() As IEnumerable(Of Integer) + Return Enumerable.Empty(Of Integer)() + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicEmptyEnumerableResultAt(7, 16)); + } + + [Fact] + public async Task ReturnSingletonArrayBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of Integer) + Return New Integer(0) {1} + End Function + Function M2() As IEnumerable(Of Integer) + Return {1} + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicSingletonEnumerableResultAt(6, 16), + GetBasicSingletonEnumerableResultAt(9, 16)); + } + + [Fact(Skip = "855425")] + public async Task ReturnArrayWithinExpressionBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of Integer) + Return If(True, {1}, {2}) + End Function + Function M2() As IEnumerable(Of Integer) + Return If(True, {1}) + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicSingletonEnumerableResultAt(6, 25), + GetBasicSingletonEnumerableResultAt(6, 30), + GetBasicSingletonEnumerableResultAt(9, 25)); + } + + [Fact] + public async Task ReturnLinqEmptyEnumerableWithinExpressionBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic +Imports System.Linq + +Class C + Function M1() As IEnumerable(Of Integer) + Return If(True, Enumerable.Empty(Of Integer)(), Nothing) + End Function + Function M2() As IEnumerable(Of Integer) + Return If({|BC33107:True|}, Enumerable.Empty(Of Integer)()) + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicEmptyEnumerableResultAt(7, 25), + GetBasicEmptyEnumerableResultAt(10, 25)); + } + + [Fact] + public async Task ReturnMultiElementArrayBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of Integer) + Return New Integer(1) {1, 2} + End Function + Function M2() As IEnumerable(Of Integer) + Return {1, 2} + End Function +End Class +" + _basicSpecializedCollectionsDefinition); + } + + [Fact] + public async Task ReturnJaggedArrayBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of Integer()) + Return New Integer(1)() {New Integer() {}, New Integer() {}} + End Function + Function M2() As IEnumerable(Of Integer()) + Return {({1})} + End Function + Function M3() As IEnumerable(Of Integer()) + Return {({1, 2, 3}), ({1})} + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicSingletonEnumerableResultAt(9, 16)); + } + + [Fact(Skip = "855425")] + public async Task ImplicitConversionToNestedEnumerableBasicAsync() + { + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System.Collections.Generic + +Class C + Function M1() As IEnumerable(Of IEnumerable(Of Integer)) + Return {({1})} + End Function +End Class +" + _basicSpecializedCollectionsDefinition, + GetBasicSingletonEnumerableResultAt(6, 16), + GetBasicSingletonEnumerableResultAt(6, 17)); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SymbolDeclaredEventMustBeGeneratedForSourceSymbolsTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SymbolDeclaredEventMustBeGeneratedForSourceSymbolsTests.cs new file mode 100644 index 0000000000000..4f93b80ec17ba --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/SymbolDeclaredEventMustBeGeneratedForSourceSymbolsTests.cs @@ -0,0 +1,10 @@ +// 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 Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class SymbolDeclaredEventMustBeGeneratedForSourceSymbolsTests + { + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TemporaryArrayAsRefTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TemporaryArrayAsRefTests.cs new file mode 100644 index 0000000000000..78df26ae8a452 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TemporaryArrayAsRefTests.cs @@ -0,0 +1,176 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Xunit; +using CSharpLanguageVersion = Microsoft.CodeAnalysis.CSharp.LanguageVersion; +using VerifyCS = Test.Utilities.CSharpSecurityCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.TemporaryArrayAsRefAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicSecurityCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.TemporaryArrayAsRefAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VisualBasicLanguageVersion = Microsoft.CodeAnalysis.VisualBasic.LanguageVersion; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class TemporaryArrayAsRefTests + { + public const string TemporaryArraySource_CSharp = @" +namespace Microsoft.CodeAnalysis.Shared.Collections +{ + internal struct TemporaryArray : System.IDisposable + { + public void Dispose() { } + } + + internal static class TemporaryArrayExtensions + { + public static ref TemporaryArray AsRef(this in TemporaryArray array) => throw null; + } +} +"; + public const string TemporaryArraySource_VisualBasic = @" +Namespace Microsoft.CodeAnalysis.Shared.Collections + Friend Structure TemporaryArray(Of T) + Implements System.IDisposable + + Public Sub Dispose() Implements System.IDisposable.Dispose + End Sub + End Structure + + Friend Module TemporaryArrayExtensions + + Public Function AsRef(Of T)(array As TemporaryArray(Of T)) As TemporaryArray(Of T) + Return Nothing + End Function + End Module +End Namespace +"; + + [Fact] + public async Task TestUsingVariable_CSharpAsync() + { + var code = @" +using Microsoft.CodeAnalysis.Shared.Collections; + +class C +{ + void Method() + { + using (var array = new TemporaryArray()) + { + ref var arrayRef1 = ref array.AsRef(); + ref var arrayRef2 = ref TemporaryArrayExtensions.AsRef(in array); + } + } +} + +" + TemporaryArraySource_CSharp; + + await new VerifyCS.Test + { + LanguageVersion = CSharpLanguageVersion.CSharp9, + TestCode = code, + }.RunAsync(); + } + + [Fact] + public async Task TestUsingVariable_VisualBasicAsync() + { + var code = @" +Imports Microsoft.CodeAnalysis.Shared.Collections + +Class C + Sub Method() + Using array = New TemporaryArray(Of Integer)() + Dim arrayRef1 = array.AsRef() + Dim arrayRef2 = TemporaryArrayExtensions.AsRef(array) + End Using + End Sub +End Class + +" + TemporaryArraySource_VisualBasic; + + await new VerifyVB.Test + { + LanguageVersion = VisualBasicLanguageVersion.VisualBasic16, + TestCode = code, + }.RunAsync(); + } + + [Fact] + public async Task TestUsingDeclarationVariable_CSharpAsync() + { + var code = @" +using Microsoft.CodeAnalysis.Shared.Collections; + +class C +{ + void Method() + { + using var array = new TemporaryArray(); + ref var arrayRef1 = ref array.AsRef(); + ref var arrayRef2 = ref TemporaryArrayExtensions.AsRef(in array); + } +} + +" + TemporaryArraySource_CSharp; + + await new VerifyCS.Test + { + LanguageVersion = CSharpLanguageVersion.CSharp9, + TestCode = code, + }.RunAsync(); + } + + [Fact] + public async Task TestNonUsingVariable_CSharpAsync() + { + var code = @" +using Microsoft.CodeAnalysis.Shared.Collections; + +class C +{ + void Method() + { + var array = new TemporaryArray(); + ref var arrayRef1 = ref [|array.AsRef()|]; + ref var arrayRef2 = ref [|TemporaryArrayExtensions.AsRef(in array)|]; + } +} + +" + TemporaryArraySource_CSharp; + + await new VerifyCS.Test + { + LanguageVersion = CSharpLanguageVersion.CSharp9, + TestCode = code, + }.RunAsync(); + } + + [Fact] + public async Task TestNonUsingVariable_VisualBasicAsync() + { + var code = @" +Imports Microsoft.CodeAnalysis.Shared.Collections + +Class C + Sub Method() + Dim array = New TemporaryArray(Of Integer)() + Dim arrayRef1 = [|array.AsRef()|] + Dim arrayRef2 = [|TemporaryArrayExtensions.AsRef(array)|] + End Sub +End Class + +" + TemporaryArraySource_VisualBasic; + + await new VerifyVB.Test + { + LanguageVersion = VisualBasicLanguageVersion.VisualBasic16, + TestCode = code, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TestExportsShouldNotBeDiscoverableTests.cs b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TestExportsShouldNotBeDiscoverableTests.cs new file mode 100644 index 0000000000000..3574e2895dc9c --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/UnitTests/TestExportsShouldNotBeDiscoverableTests.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.TestExportsShouldNotBeDiscoverable, + Roslyn.Diagnostics.Analyzers.TestExportsShouldNotBeDiscoverableCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Roslyn.Diagnostics.Analyzers.TestExportsShouldNotBeDiscoverable, + Roslyn.Diagnostics.Analyzers.TestExportsShouldNotBeDiscoverableCodeFixProvider>; + +namespace Roslyn.Diagnostics.Analyzers.UnitTests +{ + public class TestExportsShouldNotBeDiscoverableTests + { + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task Discoverable_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +class C {{ }} +"; + var fixedSource = $@" +using {mefNamespace}; + +[Export] +[PartNotDiscoverable] +class C {{ }} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task DiscoverableAddImport_CSharpAsync(string mefNamespace) + { + var source = $@" +[{mefNamespace}.Export] +class C {{ }} +"; + var fixedSource = $@" +using {mefNamespace}; + +[{mefNamespace}.Export] +[PartNotDiscoverable] +class C {{ }} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyCS.Diagnostic().WithSpan(2, 2, 2, mefNamespace.Length + 9).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotDiscoverable_CSharpAsync(string mefNamespace) + { + var source = $@" +using {mefNamespace}; + +[Export] +[PartNotDiscoverable] +class C {{ }} +"; + + await new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task Discoverable_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + +Class C +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + + + +Class C +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(4, 2, 4, 8).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task DiscoverableAddImport_VisualBasicAsync(string mefNamespace) + { + var source = $@" +<{mefNamespace}.Export> +Class C +End Class +"; + var fixedSource = $@" +Imports {mefNamespace} + +<{mefNamespace}.Export> + +Class C +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + ExpectedDiagnostics = { VerifyVB.Diagnostic().WithSpan(2, 2, 2, mefNamespace.Length + 9).WithArguments("C") }, + }, + FixedState = + { + Sources = { fixedSource }, + }, + }.RunAsync(); + } + + [Theory] + [InlineData("System.Composition")] + [InlineData("System.ComponentModel.Composition")] + public async Task NotDiscoverable_VisualBasicAsync(string mefNamespace) + { + var source = $@" +Imports {mefNamespace} + + + +Class C +End Class +"; + + await new VerifyVB.Test + { + TestState = + { + Sources = { source }, + AdditionalReferences = { AdditionalMetadataReferences.SystemComponentModelCompositionReference }, + }, + }.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..3392211be36e3 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md @@ -0,0 +1,7 @@ +## Release 2.9.8 + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +RS0004 | Usage | Disabled | BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsAnalyzer diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.vb new file mode 100644 index 0000000000000..d649d49fa0b0e --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicDoNotMixAttributesFromDifferentVersionsOfMEF.Fixer.vb @@ -0,0 +1,23 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + ''' + ''' RS0006: Do not mix attributes from different versions of MEF + ''' + + Public NotInheritable Class BasicDoNotMixAttributesFromDifferentVersionsOfMEFFixer + Inherits DoNotMixAttributesFromDifferentVersionsOfMEFFixer + + + + Public Sub New() + End Sub + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnostics.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnostics.vb new file mode 100644 index 0000000000000..7233ce2b509dd --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnostics.vb @@ -0,0 +1,78 @@ +' 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. + +Imports System.Collections.Immutable +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers +Imports Roslyn.Diagnostics.Analyzers.RoslynDiagnosticsAnalyzersResources + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + Public Class BasicInvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsAnalyzer + Inherits DiagnosticAnalyzer + + Private Shared ReadOnly s_descriptor As New DiagnosticDescriptor( + RoslynDiagnosticIds.UseSiteDiagnosticsCheckerRuleId, + CreateLocalizableResourceString(NameOf(InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsTitle)), + CreateLocalizableResourceString(NameOf(InvokeTheCorrectPropertyToEnsureCorrectUseSiteDiagnosticsMessage)), + "Usage", + DiagnosticSeverity.Error, + False, + Nothing, + Nothing, + WellKnownDiagnosticTagsExtensions.Telemetry) + + Private Shared ReadOnly s_propertiesToValidateMap As ImmutableDictionary(Of String, String) = New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase) From + { + {s_baseTypeString, s_typeSymbolFullyQualifiedName}, + {s_interfacesString, s_typeSymbolFullyQualifiedName}, + {s_allInterfacesString, s_typeSymbolFullyQualifiedName}, + {s_typeArgumentsString, s_namedTypeSymbolFullyQualifiedName}, + {s_constraintTypesString, s_typeParameterSymbolFullyQualifiedName} + }.ToImmutableDictionary() + + Private Const s_baseTypeString = "BaseType" + Private Const s_interfacesString = "Interfaces" + Private Const s_allInterfacesString = "AllInterfaces" + Private Const s_typeArgumentsString = "TypeArguments" + Private Const s_constraintTypesString = "ConstraintTypes" + + Private Const s_typeSymbolFullyQualifiedName = "Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeSymbol" + Private Const s_namedTypeSymbolFullyQualifiedName = "Microsoft.CodeAnalysis.VisualBasic.Symbols.NamedTypeSymbol" + Private Const s_typeParameterSymbolFullyQualifiedName = "Microsoft.CodeAnalysis.VisualBasic.Symbols.TypeParameterSymbol" + + Public Overrides ReadOnly Property SupportedDiagnostics As ImmutableArray(Of DiagnosticDescriptor) + Get + Return ImmutableArray.Create(s_descriptor) + End Get + End Property + + Public Overrides Sub Initialize(context As AnalysisContext) + context.EnableConcurrentExecution() + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None) + + context.RegisterSyntaxNodeAction(AddressOf AnalyzeNode, SyntaxKind.SimpleMemberAccessExpression) + End Sub + + Private Shared Sub AnalyzeNode(context As SyntaxNodeAnalysisContext) + Dim name = DirectCast(context.Node, MemberAccessExpressionSyntax).Name + If name.Kind = SyntaxKind.IdentifierName Then + Dim identifier = DirectCast(name, IdentifierNameSyntax) + Dim containingTypeName As String = Nothing + If s_propertiesToValidateMap.TryGetValue(identifier.ToString(), containingTypeName) Then + Dim sym As ISymbol = context.SemanticModel.GetSymbolInfo(identifier, context.CancellationToken).Symbol + If sym IsNot Nothing AndAlso sym.Kind = SymbolKind.Property Then + If containingTypeName = sym.ContainingType.ToDisplayString() Then + context.ReportDiagnostic(identifier.CreateDiagnostic(s_descriptor, identifier.ToString())) + End If + End If + End If + End If + End Sub + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.vb new file mode 100644 index 0000000000000..cdb18e9e540d4 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicPartsExportedWithMEFv2MustBeMarkedAsShared.Fixer.vb @@ -0,0 +1,24 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + ''' + ''' RS0023: Parts exported with MEFv2 must be marked as Shared + ''' + + Public NotInheritable Class BasicPartsExportedWithMEFv2MustBeMarkedAsSharedFixer + Inherits PartsExportedWithMEFv2MustBeMarkedAsSharedFixer(Of TypeBlockSyntax) + + + + Public Sub New() + End Sub + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSpecializedEnumerableCreationAnalyzer.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSpecializedEnumerableCreationAnalyzer.vb new file mode 100644 index 0000000000000..60ba5053e07b6 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSpecializedEnumerableCreationAnalyzer.vb @@ -0,0 +1,109 @@ +' 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. + +Imports System.Threading +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + Public Class BasicSpecializedEnumerableCreationAnalyzer + Inherits SpecializedEnumerableCreationAnalyzer + + Protected Overrides Sub GetCodeBlockStartedAnalyzer(context As CompilationStartAnalysisContext, genericEnumerableSymbol As INamedTypeSymbol, genericEmptyEnumerableSymbol As IMethodSymbol) + context.RegisterCodeBlockStartAction(Of SyntaxKind)(AddressOf New CodeBlockStartedAnalyzer(genericEnumerableSymbol, genericEmptyEnumerableSymbol).Initialize) + End Sub + + Private NotInheritable Class CodeBlockStartedAnalyzer + Inherits AbstractCodeBlockStartedAnalyzer(Of SyntaxKind) + + Public Sub New(genericEnumerableSymbol As INamedTypeSymbol, genericEmptyEnumerableSymbol As IMethodSymbol) + MyBase.New(genericEnumerableSymbol, genericEmptyEnumerableSymbol) + End Sub + + Protected Overrides Sub GetSyntaxAnalyzer(context As CodeBlockStartAnalysisContext(Of SyntaxKind), genericEnumerableSymbol As INamedTypeSymbol, genericEmptyEnumerableSymbol As IMethodSymbol) + context.RegisterSyntaxNodeAction(AddressOf New SyntaxAnalyzer(genericEnumerableSymbol, genericEmptyEnumerableSymbol).AnalyzeNode, SyntaxKind.ReturnStatement) + End Sub + End Class + + Private NotInheritable Class SyntaxAnalyzer + Inherits AbstractSyntaxAnalyzer + + Public Sub New(genericEnumerableSymbol As INamedTypeSymbol, genericEmptyEnumerableSymbol As IMethodSymbol) + MyBase.New(genericEnumerableSymbol, genericEmptyEnumerableSymbol) + End Sub + + Public Sub AnalyzeNode(context As SyntaxNodeAnalysisContext) + Dim expressionsToAnalyze = context.Node.DescendantNodes().Where(Function(n) ShouldAnalyzeExpression(n, context.SemanticModel, context.CancellationToken)) + + For Each expression In expressionsToAnalyze + Select Case expression.Kind() + Case SyntaxKind.ArrayCreationExpression + AnalyzeArrayCreationExpression(DirectCast(expression, ArrayCreationExpressionSyntax), AddressOf context.ReportDiagnostic) + Case SyntaxKind.CollectionInitializer + AnalyzeCollectionInitializerExpression(DirectCast(expression, CollectionInitializerSyntax), expression, AddressOf context.ReportDiagnostic) + Case SyntaxKind.SimpleMemberAccessExpression + AnalyzeMemberAccessName(DirectCast(expression, MemberAccessExpressionSyntax).Name, context.SemanticModel, AddressOf context.ReportDiagnostic, context.CancellationToken) + End Select + Next + End Sub + + Private Function ShouldAnalyzeExpression(expression As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean + Select Case expression.Kind() + Case SyntaxKind.ArrayCreationExpression + Return ShouldAnalyzeArrayCreationExpression(expression, semanticModel, cancellationToken) + + Case SyntaxKind.CollectionInitializer + Dim typeInfo = semanticModel.GetTypeInfo(expression, cancellationToken) + + If typeInfo.Type IsNot Nothing Then + Return ShouldAnalyzeArrayCreationExpression(expression, semanticModel, cancellationToken) + End If + + ' Get TypeInfo of the array literal in context without the target type + Dim speculativeTypeInfo = semanticModel.GetSpeculativeTypeInfo(expression.SpanStart, expression, SpeculativeBindingOption.BindAsExpression) + Dim arrayType = TryCast(speculativeTypeInfo.ConvertedType, IArrayTypeSymbol) + + Return arrayType IsNot Nothing AndAlso + arrayType.Rank = 1 AndAlso + typeInfo.ConvertedType IsNot Nothing AndAlso + typeInfo.ConvertedType.OriginalDefinition.Equals(Me.GenericEnumerableSymbol) + + Case SyntaxKind.SimpleMemberAccessExpression + Return True + End Select + + Return False + End Function + + Private Shared Sub AnalyzeArrayCreationExpression(arrayCreationExpression As ArrayCreationExpressionSyntax, addDiagnostic As Action(Of Diagnostic)) + If arrayCreationExpression.RankSpecifiers.Count = 1 Then + + ' Check for explicit specification of empty or singleton array + Dim literalRankSpecifier = DirectCast(arrayCreationExpression.RankSpecifiers(0).ChildNodes() _ + .FirstOrDefault(Function(n) n.IsKind(SyntaxKind.NumericLiteralExpression)), + LiteralExpressionSyntax) + + If literalRankSpecifier IsNot Nothing Then + Debug.Assert(literalRankSpecifier.Token.Value IsNot Nothing) + AnalyzeArrayLength(DirectCast(literalRankSpecifier.Token.Value, Integer), arrayCreationExpression, addDiagnostic) + Return + End If + End If + + AnalyzeCollectionInitializerExpression(arrayCreationExpression.Initializer, arrayCreationExpression, addDiagnostic) + End Sub + + Private Shared Sub AnalyzeCollectionInitializerExpression(initializer As CollectionInitializerSyntax, arrayCreationExpression As SyntaxNode, addDiagnostic As Action(Of Diagnostic)) + ' Check length of initializer list for empty or singleton array + If initializer IsNot Nothing Then + AnalyzeArrayLength(initializer.Initializers.Count, arrayCreationExpression, addDiagnostic) + End If + End Sub + End Class + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSymbolDeclaredEventMustBeGeneratedForSourceSymbols.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSymbolDeclaredEventMustBeGeneratedForSourceSymbols.vb new file mode 100644 index 0000000000000..c8cff17347991 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/BasicSymbolDeclaredEventMustBeGeneratedForSourceSymbols.vb @@ -0,0 +1,90 @@ +' 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. + +Imports System.Collections.Immutable +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + Public Class BasicSymbolDeclaredEventAnalyzer + Inherits SymbolDeclaredEventAnalyzer(Of SyntaxKind) + + Private Const SourceModuleTypeFullName As String = "Microsoft.CodeAnalysis.VisualBasic.Symbols.SourceModuleSymbol" + + Protected Overrides Function GetCompilationAnalyzer(compilation As Compilation, symbolType As INamedTypeSymbol) As CompilationAnalyzer + Dim compilationType = compilation.GetOrCreateTypeByMetadataName(GetType(VisualBasicCompilation).FullName) + If compilationType Is Nothing Then + Return Nothing + End If + + Dim sourceModuleType = compilation.GetOrCreateTypeByMetadataName(SourceModuleTypeFullName) + If sourceModuleType Is Nothing Then + Return Nothing + End If + + Return New BasicCompilationAnalyzer(symbolType, compilationType, sourceModuleType) + End Function + + Protected Overrides ReadOnly Property InvocationExpressionSyntaxKind As SyntaxKind + Get + Return SyntaxKind.InvocationExpression + End Get + End Property + + Private NotInheritable Class BasicCompilationAnalyzer + Inherits CompilationAnalyzer + + Private ReadOnly _sourceModuleType As INamedTypeSymbol + + Private Shared ReadOnly s_symbolTypesWithExpectedSymbolDeclaredEvent As ImmutableHashSet(Of String) = + ImmutableHashSet.Create("SourceNamespaceSymbol", "SourceNamedTypeSymbol", "SourceEventSymbol", "SourceFieldSymbol", "SourceMethodSymbol", "SourcePropertySymbol") + Private Const s_atomicSetFlagAndRaiseSymbolDeclaredEventName As String = "AtomicSetFlagAndRaiseSymbolDeclaredEvent" + + Public Sub New(symbolType As INamedTypeSymbol, compilationType As INamedTypeSymbol, sourceModuleSymbol As INamedTypeSymbol) + MyBase.New(symbolType, compilationType) + Me._sourceModuleType = sourceModuleSymbol + End Sub + + Protected Overrides ReadOnly Property SymbolTypesWithExpectedSymbolDeclaredEvent As ImmutableHashSet(Of String) + Get + Return s_symbolTypesWithExpectedSymbolDeclaredEvent + End Get + End Property + + Protected Overrides Function GetFirstArgumentOfInvocation(invocation As SyntaxNode) As SyntaxNode + Dim invocationExpression = DirectCast(invocation, InvocationExpressionSyntax) + If invocationExpression.ArgumentList IsNot Nothing Then + Dim argument = invocationExpression.ArgumentList.Arguments.FirstOrDefault() + If argument IsNot Nothing Then + Return argument.GetExpression + End If + End If + + Return Nothing + End Function + + Friend Overrides Sub AnalyzeMethodInvocation(invocationSymbol As IMethodSymbol, context As SyntaxNodeAnalysisContext) + If invocationSymbol.Name.Equals(s_atomicSetFlagAndRaiseSymbolDeclaredEventName, StringComparison.OrdinalIgnoreCase) AndAlso + _sourceModuleType.Equals(invocationSymbol.ContainingType) Then + + Dim invocationExp = DirectCast(context.Node, InvocationExpressionSyntax) + If invocationExp.ArgumentList IsNot Nothing Then + For Each argument In invocationExp.ArgumentList.Arguments + If AnalyzeSymbolDeclaredEventInvocation(argument.GetExpression, context) Then + Exit For + End If + Next + End If + End If + + MyBase.AnalyzeMethodInvocation(invocationSymbol, context) + End Sub + End Class + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/Roslyn.Diagnostics.VisualBasic.Analyzers.vbproj b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/Roslyn.Diagnostics.VisualBasic.Analyzers.vbproj new file mode 100644 index 0000000000000..54b68b3e08285 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/Roslyn.Diagnostics.VisualBasic.Analyzers.vbproj @@ -0,0 +1,18 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisForRoslynDiagnosticsAnalyzersVersion) + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicApplyTraitToClass.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicApplyTraitToClass.vb new file mode 100644 index 0000000000000..ee4b9f24b2c66 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicApplyTraitToClass.vb @@ -0,0 +1,33 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Analyzer.Utilities +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + <[Shared]> + Public NotInheritable Class VisualBasicApplyTraitToClass + Inherits AbstractApplyTraitToClass(Of AttributeSyntax) + + + + Public Sub New() + End Sub + + Private Protected Overrides ReadOnly Property RefactoringHelpers As IRefactoringHelpers + Get + Return VisualBasicRefactoringHelpers.Instance + End Get + End Property + + Protected Overrides Function GetTypeDeclarationForNode(reportedNode As SyntaxNode) As SyntaxNode + Return reportedNode.FirstAncestorOrSelf(Of TypeBlockSyntax)() + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicCreateTestAccessor.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicCreateTestAccessor.vb new file mode 100644 index 0000000000000..cc819a041726c --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicCreateTestAccessor.vb @@ -0,0 +1,33 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports Analyzer.Utilities +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + <[Shared]> + Public NotInheritable Class VisualBasicCreateTestAccessor + Inherits AbstractCreateTestAccessor(Of TypeStatementSyntax) + + + + Public Sub New() + End Sub + + Private Protected Overrides ReadOnly Property RefactoringHelpers As IRefactoringHelpers + Get + Return VisualBasicRefactoringHelpers.Instance + End Get + End Property + + Protected Overrides Function GetTypeDeclarationForNode(reportedNode As SyntaxNode) As SyntaxNode + Return reportedNode.FirstAncestorOrSelf(Of TypeStatementSyntax)()?.Parent + End Function + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicDoNotCopyValue.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicDoNotCopyValue.vb new file mode 100644 index 0000000000000..14c6100061f3b --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicDoNotCopyValue.vb @@ -0,0 +1,45 @@ +' 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. + +Imports System.Diagnostics.CodeAnalysis +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + Public NotInheritable Class VisualBasicDoNotCopyValue + Inherits AbstractDoNotCopyValue + + Protected Overrides Function CreateWalker(context As OperationBlockAnalysisContext, cache As NonCopyableTypesCache) As NonCopyableWalker + Return New VisualBasicNonCopyableWalker(context, cache) + End Function + + Protected Overrides Function CreateSymbolWalker(context As SymbolAnalysisContext, cache As NonCopyableTypesCache) As NonCopyableSymbolWalker + Return New VisualBasicNonCopyableSymbolWalker(context, cache) + End Function + + Private NotInheritable Class VisualBasicNonCopyableWalker + Inherits NonCopyableWalker + + Public Sub New(context As OperationBlockAnalysisContext, cache As NonCopyableTypesCache) + MyBase.New(context, cache) + End Sub + + Protected Overrides Function CheckForEachGetEnumerator(operation As IForEachLoopOperation, ByRef conversion As IConversionOperation, ByRef instance As IOperation) As Boolean + ' Not supported (yet) + Return False + End Function + End Class + + Private NotInheritable Class VisualBasicNonCopyableSymbolWalker + Inherits NonCopyableSymbolWalker + + Public Sub New(context As SymbolAnalysisContext, cache As NonCopyableTypesCache) + MyBase.New(context, cache) + End Sub + End Class + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicExposeMemberForTesting.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicExposeMemberForTesting.vb new file mode 100644 index 0000000000000..92d50e090abc3 --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicExposeMemberForTesting.vb @@ -0,0 +1,48 @@ +' 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. + +Imports System.Composition +Imports Analyzer.Utilities +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + <[Shared]> + Public NotInheritable Class VisualBasicExposeMemberForTesting + Inherits AbstractExposeMemberForTesting(Of TypeStatementSyntax) + + + + Public Sub New() + End Sub + + Private Protected Overrides ReadOnly Property RefactoringHelpers As IRefactoringHelpers + Get + Return VisualBasicRefactoringHelpers.Instance + End Get + End Property + + Protected Overrides ReadOnly Property HasRefReturns As Boolean + Get + Return False + End Get + End Property + + Protected Overrides Function GetTypeDeclarationForNode(reportedNode As SyntaxNode) As SyntaxNode + Return reportedNode.FirstAncestorOrSelf(Of TypeStatementSyntax)()?.Parent + End Function + + Protected Overrides Function GetByRefType(type As SyntaxNode, refKind As RefKind) As SyntaxNode + Return type + End Function + + Protected Overrides Function GetByRefExpression(expression As SyntaxNode) As SyntaxNode + Return expression + End Function + End Class +End Namespace + diff --git a/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicRunIterations.vb b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicRunIterations.vb new file mode 100644 index 0000000000000..691b46f3204dc --- /dev/null +++ b/src/RoslynAnalyzers/Roslyn.Diagnostics.Analyzers/VisualBasic/VisualBasicRunIterations.vb @@ -0,0 +1,29 @@ +' 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. + +Imports System.Composition +Imports Analyzer.Utilities +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeRefactorings +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Diagnostics.Analyzers + +Namespace Roslyn.Diagnostics.VisualBasic.Analyzers + + <[Shared]> + Public Class VisualBasicRunIterations + Inherits AbstractRunIterations(Of MethodStatementSyntax) + + + + Public Sub New() + End Sub + + Private Protected Overrides ReadOnly Property RefactoringHelpers As IRefactoringHelpers + Get + Return VisualBasicRefactoringHelpers.Instance + End Get + End Property + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Test.Utilities/.editorconfig b/src/RoslynAnalyzers/Test.Utilities/.editorconfig new file mode 100644 index 0000000000000..17082ebd7e1c2 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = none \ No newline at end of file diff --git a/src/RoslynAnalyzers/Test.Utilities/AdditionalMetadataReferences.cs b/src/RoslynAnalyzers/Test.Utilities/AdditionalMetadataReferences.cs new file mode 100644 index 0000000000000..b9df5abd250b7 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/AdditionalMetadataReferences.cs @@ -0,0 +1,109 @@ +// 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; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static class AdditionalMetadataReferences + { + public static ReferenceAssemblies DefaultNetCore { get; } = ReferenceAssemblies.Net.Net90 + .AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.CodeAnalysis", "3.0.0"), + new PackageIdentity("System.Runtime.Serialization.Formatters", "4.3.0"), + new PackageIdentity("System.Configuration.ConfigurationManager", "4.7.0"), + new PackageIdentity("System.Security.Cryptography.Cng", "4.7.0"), + new PackageIdentity("System.Security.Permissions", "4.7.0"), + new PackageIdentity("Microsoft.VisualBasic", "10.3.0"))); + + public static ReferenceAssemblies DefaultNetFramework { get; } = ReferenceAssemblies.Default + .AddAssemblies(ImmutableArray.Create("System.Xml.Data")) + .AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.CodeAnalysis", "3.0.0"))); + + public static ReferenceAssemblies Default { get; } = +#if NETCOREAPP + DefaultNetCore; +#else + DefaultNetFramework; +#endif + + public static ReferenceAssemblies DefaultWithoutRoslynSymbols { get; } = ReferenceAssemblies.Default + .AddAssemblies(ImmutableArray.Create("System.Xml.Data")) + .AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.CodeAnalysis.Workspaces.Common", "3.0.0"))); + + public static ReferenceAssemblies DefaultWithSystemWeb { get; } = ReferenceAssemblies.NetFramework.Net472.Default + .AddAssemblies(ImmutableArray.Create("System.Web", "System.Web.Extensions")); + + public static ReferenceAssemblies DefaultForTaintedDataAnalysis { get; } = ReferenceAssemblies.NetFramework.Net472.Default + .AddAssemblies(ImmutableArray.Create("PresentationFramework", "System.Web", "System.Web.Extensions", "System.Xaml")) + .AddPackages(ImmutableArray.Create( + new PackageIdentity("System.DirectoryServices", "6.0.1"), + new PackageIdentity("AntiXSS", "4.3.0"), + new PackageIdentity("Microsoft.AspNetCore.Mvc", "2.2.0"), + new PackageIdentity("Microsoft.EntityFrameworkCore.Relational", "2.0.3"))); + + public static ReferenceAssemblies DefaultWithSerialization { get; } = ReferenceAssemblies.NetFramework.Net472.Default + .AddAssemblies(ImmutableArray.Create("System.Runtime.Serialization")); + + public static ReferenceAssemblies DefaultWithAzureStorage { get; } = ReferenceAssemblies.Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("WindowsAzure.Storage", "9.0.0"))); + + public static ReferenceAssemblies DefaultWithNewtonsoftJson10 { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("Newtonsoft.Json", "10.0.1"))); + + public static ReferenceAssemblies DefaultWithNewtonsoftJson12 { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("Newtonsoft.Json", "12.0.1"))); + + public static ReferenceAssemblies DefaultWithMELogging { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.Extensions.Logging", "5.0.0"))); + + public static ReferenceAssemblies DefaultWithWilson { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.IdentityModel.Tokens", "6.12.0"))); + + public static ReferenceAssemblies DefaultWithWinForms { get; } = ReferenceAssemblies.NetFramework.Net472.WindowsForms; + + public static ReferenceAssemblies DefaultWithWinHttpHandler { get; } = ReferenceAssemblies.NetStandard.NetStandard20 + .AddPackages(ImmutableArray.Create(new PackageIdentity("System.Net.Http.WinHttpHandler", "4.7.0"))); + + public static ReferenceAssemblies DefaultWithAspNetCoreMvc { get; } = Default + .AddPackages(ImmutableArray.Create( + new PackageIdentity("Microsoft.AspNetCore", "1.1.7"), + new PackageIdentity("Microsoft.AspNetCore.Mvc", "1.1.8"), + new PackageIdentity("Microsoft.AspNetCore.Http", "1.1.2"))); + + public static ReferenceAssemblies DefaultWithNUnit { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("NUnit", "3.12.0"))); + + public static ReferenceAssemblies DefaultWithXUnit { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("xunit", "2.4.1"))); + + public static ReferenceAssemblies DefaultWithMSTest { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("MSTest.TestFramework", "2.1.0"))); + + public static ReferenceAssemblies DefaultWithAsyncInterfaces { get; } = Default + .AddPackages(ImmutableArray.Create(new PackageIdentity("Microsoft.Bcl.AsyncInterfaces", "1.1.0"))); + + public static MetadataReference SystemCollectionsImmutableReference { get; } = MetadataReference.CreateFromFile(typeof(ImmutableHashSet<>).Assembly.Location); + public static MetadataReference SystemComponentModelCompositionReference { get; } = MetadataReference.CreateFromFile(typeof(System.ComponentModel.Composition.ExportAttribute).Assembly.Location); + public static MetadataReference SystemXmlDataReference { get; } = MetadataReference.CreateFromFile(typeof(System.Data.Rule).Assembly.Location); + public static MetadataReference CodeAnalysisReference { get; } = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location); + public static MetadataReference CSharpSymbolsReference { get; } = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location); + public static MetadataReference WorkspacesReference { get; } = MetadataReference.CreateFromFile(typeof(Workspace).Assembly.Location); +#if !NETCOREAPP + public static MetadataReference SystemWebReference { get; } = MetadataReference.CreateFromFile(typeof(System.Web.HttpRequest).Assembly.Location); + public static MetadataReference SystemRuntimeSerialization { get; } = MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.NetDataContractSerializer).Assembly.Location); +#endif + public static MetadataReference TestReferenceAssembly { get; } = MetadataReference.CreateFromFile(typeof(OtherDll.OtherDllStaticMethods).Assembly.Location); +#if !NETCOREAPP + public static MetadataReference SystemXaml { get; } = MetadataReference.CreateFromFile(typeof(System.Xaml.XamlReader).Assembly.Location); + public static MetadataReference PresentationFramework { get; } = MetadataReference.CreateFromFile(typeof(System.Windows.Markup.XamlReader).Assembly.Location); + public static MetadataReference SystemWeb { get; } = MetadataReference.CreateFromFile(typeof(System.Web.HttpRequest).Assembly.Location); + public static MetadataReference SystemWebExtensions { get; } = MetadataReference.CreateFromFile(typeof(System.Web.Script.Serialization.JavaScriptSerializer).Assembly.Location); + public static MetadataReference SystemServiceModel { get; } = MetadataReference.CreateFromFile(typeof(System.ServiceModel.OperationContractAttribute).Assembly.Location); +#endif + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..0870989b0635a --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2+Test.cs @@ -0,0 +1,74 @@ +// 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.Net; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class CSharpCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : CSharpCodeFixTest + { + static Test() + { + // If we have outdated defaults from the host unit test application targeting an older .NET Framework, use more + // reasonable TLS protocol version for outgoing connections. +#pragma warning disable CA5364 // Do Not Use Deprecated Security Protocols +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete + if (ServicePointManager.SecurityProtocol == (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls)) +#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete +#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CA5364 // Do Not Use Deprecated Security Protocols + { +#pragma warning disable CA5386 // Avoid hardcoding SecurityProtocolType value +#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; +#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete +#pragma warning restore CA5386 // Avoid hardcoding SecurityProtocolType value + } + } + + internal static readonly ImmutableDictionary NullableWarnings = GetNullableWarningsFromCompiler(); + + public Test() + { + ReferenceAssemblies = AdditionalMetadataReferences.Default; + } + + private static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = { "/warnaserror:nullable" }; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; + + return nullableWarnings; + } + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.CSharp7_3; + + protected override CompilationOptions CreateCompilationOptions() + { + var compilationOptions = base.CreateCompilationOptions(); + return compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(NullableWarnings)); + } + + protected override ParseOptions CreateParseOptions() + { + return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..cba1fa656ec54 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeFixVerifier`2.cs @@ -0,0 +1,57 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class CSharpCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public static DiagnosticResult Diagnostic() + => CSharpCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync([StringSyntax("C#-test")] string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + public static async Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, [StringSyntax("C#-test")] string fixedSource) + => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static async Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, DiagnosticResult expected, [StringSyntax("C#-test")] string fixedSource) + => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, DiagnosticResult[] expected, [StringSyntax("C#-test")] string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1+Test.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1+Test.cs new file mode 100644 index 0000000000000..95634069b3417 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1+Test.cs @@ -0,0 +1,42 @@ +// 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; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class CSharpCodeRefactoringVerifier + where TRefactoring : CodeRefactoringProvider, new() + { + public class Test : CSharpCodeRefactoringTest + { + public Test() + { + ReferenceAssemblies = AdditionalMetadataReferences.Default; + } + + private static ImmutableDictionary NullableWarnings + => CSharpCodeFixVerifier.Test.NullableWarnings; + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.CSharp7_3; + + protected override CompilationOptions CreateCompilationOptions() + { + var compilationOptions = base.CreateCompilationOptions(); + return compilationOptions.WithSpecificDiagnosticOptions( + compilationOptions.SpecificDiagnosticOptions.SetItems(NullableWarnings)); + } + + protected override ParseOptions CreateParseOptions() + { + return ((CSharpParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1.cs new file mode 100644 index 0000000000000..31eb5573ddd57 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpCodeRefactoringVerifier`1.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class CSharpCodeRefactoringVerifier + where TRefactoring : CodeRefactoringProvider, new() + { + /// + public static async Task VerifyRefactoringAsync([StringSyntax("C#-test")] string source, [StringSyntax("C#-test")] string fixedSource) + => await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + /// + public static async Task VerifyRefactoringAsync([StringSyntax("C#-test")] string source, DiagnosticResult expected, [StringSyntax("C#-test")] string fixedSource) + => await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); + + /// + public static async Task VerifyRefactoringAsync([StringSyntax("C#-test")] string source, DiagnosticResult[] expected, [StringSyntax("C#-test")] string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..98a928870af94 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2+Test.cs @@ -0,0 +1,52 @@ +// 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.Linq; +using System.Net; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Test.Utilities +{ + public static partial class CSharpSecurityCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : CSharpCodeFixVerifier.Test + { + static Test() + { + // If we have outdated defaults from the host unit test application targeting an older .NET Framework, use more + // reasonable TLS protocol version for outgoing connections. +#pragma warning disable CA5364 // Do Not Use Deprecated Security Protocols +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete + if (ServicePointManager.SecurityProtocol == (SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls)) +#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete +#pragma warning restore CS0618 // Type or member is obsolete +#pragma warning restore CA5364 // Do Not Use Deprecated Security Protocols + { +#pragma warning disable CA5386 // Avoid hardcoding SecurityProtocolType value +#pragma warning disable SYSLIB0014 // ServicePointManager is obsolete + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; +#pragma warning restore SYSLIB0014 // ServicePointManager is obsolete +#pragma warning restore CA5386 // Avoid hardcoding SecurityProtocolType value + } + } + + public Test() + { + } + + protected override ParseOptions CreateParseOptions() + { + var parseOptions = base.CreateParseOptions(); + return parseOptions.WithFeatures(parseOptions.Features.Concat( + new[] { new KeyValuePair("flow-analysis", "true") })); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2.cs b/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..ef44324b83d9d --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CSharpSecurityCodeFixVerifier`2.cs @@ -0,0 +1,61 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class CSharpSecurityCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public static DiagnosticResult Diagnostic() + => CSharpCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => CSharpCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => CSharpCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync([StringSyntax("C#-test")] string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + await RunTestAsync(test, expected); + } + + public static Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, [StringSyntax("C#-test")] string fixedSource) + => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, DiagnosticResult expected, [StringSyntax("C#-test")] string fixedSource) + => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync([StringSyntax("C#-test")] string source, DiagnosticResult[] expected, [StringSyntax("C#-test")] string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + await RunTestAsync(test, expected); + } + + public static async Task RunTestAsync(Test test, params DiagnosticResult[] expected) + { + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CodeMetricsTestsBase.cs b/src/RoslynAnalyzers/Test.Utilities/CodeMetricsTestsBase.cs new file mode 100644 index 0000000000000..897879960e628 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CodeMetricsTestsBase.cs @@ -0,0 +1,153 @@ +// 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.Globalization; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Test.Utilities.CodeMetrics +{ + public abstract class CodeMetricsTestBase + { + private static readonly CompilationOptions s_CSharpDefaultOptions = BuildDefaultCSharpOptions(); + private static readonly CompilationOptions s_visualBasicDefaultOptions = new Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + + internal static string DefaultFilePathPrefix = "Test"; + internal static string CSharpDefaultFileExt = "cs"; + internal static string VisualBasicDefaultExt = "vb"; + internal static string CSharpDefaultFilePath = DefaultFilePathPrefix + 0 + "." + CSharpDefaultFileExt; + internal static string VisualBasicDefaultFilePath = DefaultFilePathPrefix + 0 + "." + VisualBasicDefaultExt; + internal static string TestProjectName = "TestProject"; + + protected abstract string GetMetricsDataString(Compilation compilation); + + protected Project CreateProject(string[] sources, string language = LanguageNames.CSharp) + { + string fileNamePrefix = DefaultFilePathPrefix; + string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt; + var options = language == LanguageNames.CSharp ? s_CSharpDefaultOptions : s_visualBasicDefaultOptions; + + var projectId = ProjectId.CreateNewId(debugName: TestProjectName); + + var defaultReferences = ReferenceAssemblies.NetFramework.Net48.Default; + var references = Task.Run(() => defaultReferences.ResolveAsync(language, CancellationToken.None)).GetAwaiter().GetResult(); + +#pragma warning disable CA2000 // Dispose objects before losing scope - Current solution/project takes the dispose ownership of the created AdhocWorkspace + var solution = new AdhocWorkspace() +#pragma warning restore CA2000 // Dispose objects before losing scope + .CurrentSolution + .AddProject(projectId, TestProjectName, TestProjectName, language) + .WithProjectCompilationOptions(projectId, options) + .AddMetadataReferences(projectId, references); + + int count = 0; + foreach (var source in sources) + { + var newFileName = fileNamePrefix + count + "." + fileExt; + var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); + solution = solution.AddDocument(documentId, newFileName, SourceText.From(source)); + count++; + } + + return solution.GetProject(projectId)!; + } + + protected void VerifyCSharp(string source, string expectedMetricsText, bool expectDiagnostics = false) + => Verify(new[] { source }, expectedMetricsText, expectDiagnostics, LanguageNames.CSharp); + + protected void VerifyCSharp(string[] sources, string expectedMetricsText, bool expectDiagnostics = false) + => Verify(sources, expectedMetricsText, expectDiagnostics, LanguageNames.CSharp); + + protected void VerifyBasic(string source, string expectedMetricsText, bool expectDiagnostics = false) + => Verify(new[] { source }, expectedMetricsText, expectDiagnostics, LanguageNames.VisualBasic); + + private void Verify(string[] sources, string expectedMetricsText, bool expectDiagnostics, string language) + { + var project = CreateProject(sources, language); + var compilation = project.GetCompilationAsync(CancellationToken.None).Result!; + var diagnostics = compilation.GetDiagnostics().Where(d => d.Severity is DiagnosticSeverity.Warning or DiagnosticSeverity.Error); + if (expectDiagnostics) + { + Assert.True(diagnostics.Any()); + } + else + { + Assert.Collection(diagnostics, Array.Empty>()); + } + + var actualMetricsText = GetMetricsDataString(compilation).Trim(); + expectedMetricsText = expectedMetricsText.Trim(); + var actualMetricsTextLines = actualMetricsText.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + var expectedMetricsTextLines = expectedMetricsText.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + + var success = true; + if (actualMetricsTextLines.Length != expectedMetricsTextLines.Length) + { + success = false; + } + else + { + for (int i = 0; i < actualMetricsTextLines.Length; i++) + { + var actual = actualMetricsTextLines[i].Trim(); + var expected = expectedMetricsTextLines[i].Trim(); + if (actual != expected) + { + success = false; + break; + } + } + } + + if (!success) + { + // Dump the entire expected and actual lines for easy update to baseline. + Assert.True(false, $"Expected:\r\n{expectedMetricsText}\r\n\r\nActual:\r\n{actualMetricsText}"); + } + } + + private static CompilationOptions BuildDefaultCSharpOptions() + { + // Between the 3.0.0 and 3.5.0 release of Microsoft.CodeAnalysis the + // NullableContextOptions type changed namespaces, making the bound constructor from 3.0.0 + // not resolve in 3.5.0. + // + // This moves the compile-time decision to runtime to work around that limitation. + + foreach (var ctor in typeof(Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions).GetConstructors()) + { + var parameterInfos = ctor.GetParameters(); + + if (parameterInfos.Length < 1 || typeof(OutputKind) != parameterInfos[0].ParameterType) + { + continue; + } + + if (parameterInfos.Length > 1 && !parameterInfos[1].HasDefaultValue) + { + continue; + } + + object[] parameters = new object[parameterInfos.Length]; + parameters.AsSpan().Fill(Type.Missing); + parameters[0] = OutputKind.DynamicallyLinkedLibrary; + + return (Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions)ctor.Invoke( + BindingFlags.OptionalParamBinding | BindingFlags.CreateInstance, + null, + parameters, + CultureInfo.InvariantCulture); + } + + throw new Exception("Could not find a compatible CSharpCompilationOptions constructor via reflection."); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/CompilationUtils.cs b/src/RoslynAnalyzers/Test.Utilities/CompilationUtils.cs new file mode 100644 index 0000000000000..72fad50050e6f --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/CompilationUtils.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.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Xunit; + +namespace Test.Utilities +{ + public static class CompilationUtils + { + public static void ValidateNoCompileErrors(ImmutableArray compilerDiagnostics) + { + var compileErrors = compilerDiagnostics.Where(d => d.Severity == DiagnosticSeverity.Error).ToArray(); + + if (compileErrors.Any()) + { + var builder = new StringBuilder(); + builder.Append($"Test contains compilation error(s):"); + builder.Append(string.Concat(compileErrors.Select(x => "\n" + x.ToString()))); + + string message = builder.ToString(); + Assert.True(false, message); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/Directory.Build.props b/src/RoslynAnalyzers/Test.Utilities/Directory.Build.props new file mode 100644 index 0000000000000..afea15a9e1d65 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/Directory.Build.props @@ -0,0 +1,12 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + true + + + + diff --git a/src/RoslynAnalyzers/Test.Utilities/SharedTestCode.cs b/src/RoslynAnalyzers/Test.Utilities/SharedTestCode.cs new file mode 100644 index 0000000000000..297807039fe07 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/SharedTestCode.cs @@ -0,0 +1,29 @@ +// 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 Test.Utilities +{ + public static class SharedCode + { + public const string WrongSanitizer = @" +using System; +using System.Data.SqlClient; +using System.IO; +using System.Web; + +public partial class WebForm : System.Web.UI.Page +{ + protected void Page_Load(object sender, EventArgs e) + { + string input = Request.Form[""in""]; + StringWriter w = new StringWriter(); + Server.HtmlEncode(input, w); + Response.Write("""" + w.ToString() + """"); + // make sure it is not like any unknown method breaks the taint path, it should warn about sql injection + SqlCommand sqlCommand = new SqlCommand(w.ToString()); + } +} +"; + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/Test.Utilities.csproj b/src/RoslynAnalyzers/Test.Utilities/Test.Utilities.csproj new file mode 100644 index 0000000000000..33cb73c2ec98d --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/Test.Utilities.csproj @@ -0,0 +1,48 @@ + + + + $(NetRoslyn) + true + false + true + $(MicrosoftCodeAnalysisVersionForToolsAndUtilities) + + + TEST_UTILITIES + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Test.Utilities/Traits.cs b/src/RoslynAnalyzers/Test.Utilities/Traits.cs new file mode 100644 index 0000000000000..cd2913f827ba9 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/Traits.cs @@ -0,0 +1,29 @@ +// 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 Test.Utilities +{ + public static class Traits + { + public const string DataflowAnalysis = nameof(DataflowAnalysis); +#pragma warning disable CA1034 // Nested types should not be visible - test class matching the pattern used in Roslyn. +#pragma warning disable CA1724 // Type names should not match namespaces - test class matching the pattern used in Roslyn. + public static class Dataflow +#pragma warning restore CA1724 // Type names should not match namespaces +#pragma warning restore CA1034 // Nested types should not be visible + { + public const string CopyAnalysis = nameof(CopyAnalysis); + public const string NullAnalysis = nameof(NullAnalysis); + public const string PointsToAnalysis = nameof(PointsToAnalysis); + public const string ValueContentAnalysis = nameof(ValueContentAnalysis); + public const string DisposeAnalysis = nameof(DisposeAnalysis); + public const string ParameterValidationAnalysis = nameof(ParameterValidationAnalysis); + public const string PropertySetAnalysis = nameof(PropertySetAnalysis); + + public const string PredicateAnalysis = nameof(PredicateAnalysis); + + public const string TaintedDataAnalysis = nameof(TaintedDataAnalysis); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..fd32e8c6a16a9 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2+Test.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Test.Utilities +{ + public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : VisualBasicCodeFixTest + { + public Test() + { + ReferenceAssemblies = AdditionalMetadataReferences.Default; + } + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.VisualBasic15_5; + + protected override ParseOptions CreateParseOptions() + { + return ((VisualBasicParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..48e2ee4e47028 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeFixVerifier`2.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Test.Utilities +{ + public static partial class VisualBasicCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public static DiagnosticResult Diagnostic() + => VisualBasicCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + public static async Task VerifyCodeFixAsync(string source, string fixedSource) + => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1+Test.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1+Test.cs new file mode 100644 index 0000000000000..ca7d67c34368f --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1+Test.cs @@ -0,0 +1,31 @@ +// 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; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Test.Utilities +{ + public static partial class VisualBasicCodeRefactoringVerifier + where TRefactoring : CodeRefactoringProvider, new() + { + public class Test : VisualBasicCodeRefactoringTest + { + public Test() + { + ReferenceAssemblies = AdditionalMetadataReferences.Default; + } + + public LanguageVersion LanguageVersion { get; set; } = LanguageVersion.VisualBasic15_5; + + protected override ParseOptions CreateParseOptions() + { + return ((VisualBasicParseOptions)base.CreateParseOptions()).WithLanguageVersion(LanguageVersion); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1.cs new file mode 100644 index 0000000000000..684a1b532a5b7 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicCodeRefactoringVerifier`1.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Testing; + +namespace Test.Utilities +{ + public static partial class VisualBasicCodeRefactoringVerifier + where TRefactoring : CodeRefactoringProvider, new() + { + /// + public static async Task VerifyRefactoringAsync(string source, string fixedSource) + => await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource) + => await VerifyRefactoringAsync(source, new[] { expected }, fixedSource); + + /// + public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2+Test.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2+Test.cs new file mode 100644 index 0000000000000..80fbd79cb9fec --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2+Test.cs @@ -0,0 +1,31 @@ +// 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.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Test.Utilities +{ + public static partial class VisualBasicSecurityCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public class Test : VisualBasicCodeFixVerifier.Test + { + public Test() + { + } + + protected override ParseOptions CreateParseOptions() + { + var parseOptions = base.CreateParseOptions(); + return parseOptions.WithFeatures(parseOptions.Features.Concat( + new[] { new KeyValuePair("flow-analysis", "true") })); + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2.cs b/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2.cs new file mode 100644 index 0000000000000..82491d0e23857 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/VisualBasicSecurityCodeFixVerifier`2.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.VisualBasic.Testing; + +namespace Test.Utilities +{ + public static partial class VisualBasicSecurityCodeFixVerifier + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFix : CodeFixProvider, new() + { + public static DiagnosticResult Diagnostic() + => VisualBasicCodeFixVerifier.Diagnostic(); + + public static DiagnosticResult Diagnostic(string diagnosticId) + => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId); + + public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor) + => VisualBasicCodeFixVerifier.Diagnostic(descriptor); + + public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) + { + var test = new Test + { + TestCode = source, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + + public static Task VerifyCodeFixAsync(string source, string fixedSource) + => VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource); + + public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) + => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); + + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + { + var test = new Test + { + TestCode = source, + FixedCode = fixedSource, + }; + + test.ExpectedDiagnostics.AddRange(expected); + await test.RunAsync(); + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyFactAttribute.cs b/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyFactAttribute.cs new file mode 100644 index 0000000000000..f2d6da855d887 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyFactAttribute.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; +using Xunit; + +namespace Test.Utilities +{ + public sealed class WindowsOnlyFactAttribute : FactAttribute + { + public WindowsOnlyFactAttribute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Windows to run"; + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyTheoryAttribute.cs b/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyTheoryAttribute.cs new file mode 100644 index 0000000000000..9295e4d9c78a1 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/WindowsOnlyTheoryAttribute.cs @@ -0,0 +1,20 @@ +// 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.Runtime.InteropServices; +using Xunit; + +namespace Test.Utilities +{ + public sealed class WindowsOnlyTheoryAttribute : TheoryAttribute + { + public WindowsOnlyTheoryAttribute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + this.Skip = "This test requires Windows to run"; + } + } + } +} diff --git a/src/RoslynAnalyzers/Test.Utilities/WorkItemAttribute.cs b/src/RoslynAnalyzers/Test.Utilities/WorkItemAttribute.cs new file mode 100644 index 0000000000000..79603d60fb473 --- /dev/null +++ b/src/RoslynAnalyzers/Test.Utilities/WorkItemAttribute.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Test.Utilities +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true)] + public sealed class WorkItemAttribute : Attribute + { + public WorkItemAttribute(int id, string source) + { + Id = id; + Source = source; + } + + public int Id { get; } + public string Source { get; } + } +} diff --git a/src/RoslynAnalyzers/TestReferenceAssembly/.editorconfig b/src/RoslynAnalyzers/TestReferenceAssembly/.editorconfig new file mode 100644 index 0000000000000..033e4e237a721 --- /dev/null +++ b/src/RoslynAnalyzers/TestReferenceAssembly/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CA5394: Do not use insecure randomness +dotnet_diagnostic.CA5394.severity = none \ No newline at end of file diff --git a/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllClass.cs b/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllClass.cs new file mode 100644 index 0000000000000..b9c5b57bbaf0c --- /dev/null +++ b/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllClass.cs @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +#pragma warning disable CA1801 // Remove unused parameter +#pragma warning disable IDE0060 // Remove unused parameter + +using System; +using System.Linq; +using System.Text; + +namespace OtherDll +{ + /// + /// Aids with testing dataflow analysis _not_ doing interprocedural DFA. + /// + /// + /// Since Roslyn doesn't support cross-binary DFA, and this class is + /// defined in a different binary, using this class from test source code + /// is a way to test handling of non-interprocedural results in dataflow + /// analysis implementations. + /// + public class OtherDllClass + where T : class + { + public OtherDllClass(T? constructedInput) + { + this.ConstructedInput = constructedInput; + } + + public T? ConstructedInput { get; set; } + + public T? Default + { + get => null; + set { } + } + + public string RandomString + { + get + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + return Encoding.ASCII.GetString(bytes); + } + + set { } + } + + public T? ReturnsConstructedInput() + { + return this.ConstructedInput; + } + + public T? ReturnsDefault() + { + return null; + } + + public T? ReturnsInput(T? input) + { + return input; + } + + public T? ReturnsDefault(T? input) + { + return null; + } + + public string ReturnsRandom(string input) + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + return Encoding.ASCII.GetString(bytes); + } + + public void SetsOutputToConstructedInput(out T? output) + { + output = this.ConstructedInput; + } + + public void SetsOutputToDefault(out T? output) + { + output = null; + } + + public void SetsOutputToInput(T? input, out T? output) + { + output = input; + } + + public void SetsOutputToDefault(T? input, out T? output) + { + output = null; + } + + public void SetsOutputToRandom(string input, out string output) + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + output = Encoding.ASCII.GetString(bytes); + } + + public void SetsReferenceToConstructedInput(ref T? output) + { + output = this.ConstructedInput; + } + + public void SetsReferenceToDefault(ref T? output) + { + output = null; + } + + public void SetsReferenceToInput(T? input, ref T? output) + { + output = input; + } + + public void SetsReferenceToDefault(T? input, ref T? output) + { + output = null; + } + + public void SetsReferenceToRandom(string input, ref string output) + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + output = Encoding.ASCII.GetString(bytes); + } + } +} diff --git a/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllStaticMethods.cs b/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllStaticMethods.cs new file mode 100644 index 0000000000000..7cf32a1bfaacf --- /dev/null +++ b/src/RoslynAnalyzers/TestReferenceAssembly/OtherDllStaticMethods.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +#pragma warning disable CA1801 // Remove unused parameter +#pragma warning disable IDE0060 // Remove unused parameter + +using System; +using System.Linq; +using System.Text; + +namespace OtherDll +{ + /// + /// Aids with testing dataflow analysis _not_ doing interprocedural DFA. + /// + /// + /// Since Roslyn doesn't support cross-binary DFA, and this class is + /// defined in a different binary, using this class from test source code + /// is a way to test handling of non-interprocedural results in dataflow + /// analysis implementations. + /// + public static class OtherDllStaticMethods + { + public static T? ReturnsInput(T? input) + where T : class + { + return input; + } + + public static T? ReturnsDefault(T? input) + where T : class + { + return null; + } + + public static string ReturnsRandom(string input) + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + return Encoding.ASCII.GetString(bytes); + } + + public static void SetsOutputToInput(T? input, out T? output) + where T : class + { + output = input; + } + + public static void SetsOutputToDefault(T? input, out T? output) + where T : class + { + output = null; + } + + public static void SetsOutputToRandom(string input, out string output) + { + output = ReturnsRandom(input); + } + + public static void SetsReferenceToInput(T? input, ref T? output) + where T : class + { + output = input; + } + + public static void SetsReferenceToDefault(T? input, ref T? output) + where T : class + { + output = null; + } + + public static void SetsReferenceToRandom(string input, ref string output) + { + Random r = new Random(); + byte[] bytes = new byte[r.Next(20) + 10]; + r.NextBytes(bytes); + bytes = bytes.Where(b => b is >= ((byte)' ') and <= ((byte)'~')).ToArray(); + output = Encoding.ASCII.GetString(bytes); + } + } +} diff --git a/src/RoslynAnalyzers/TestReferenceAssembly/TestReferenceAssembly.csproj b/src/RoslynAnalyzers/TestReferenceAssembly/TestReferenceAssembly.csproj new file mode 100644 index 0000000000000..64c9832db511d --- /dev/null +++ b/src/RoslynAnalyzers/TestReferenceAssembly/TestReferenceAssembly.csproj @@ -0,0 +1,9 @@ + + + + netstandard2.0;net472 + OtherDll + true + true + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/CSharp/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Text.Analyzers/CSharp/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/CSharp/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Text.Analyzers/CSharp/CSharpIdentifiersShouldBeSpelledCorrectly.Fixer.cs b/src/RoslynAnalyzers/Text.Analyzers/CSharp/CSharpIdentifiersShouldBeSpelledCorrectly.Fixer.cs new file mode 100644 index 0000000000000..c01d9be5e2495 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/CSharp/CSharpIdentifiersShouldBeSpelledCorrectly.Fixer.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Text.Analyzers; + +namespace Text.CSharp.Analyzers +{ + /// + /// CA1704: Identifiers should be spelled correctly + /// + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + [method: ImportingConstructor] + [method: Obsolete("This exported object must be obtained through the MEF export provider.", error: true)] + public sealed class CSharpIdentifiersShouldBeSpelledCorrectlyFixer() : IdentifiersShouldBeSpelledCorrectlyFixer + { + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/CSharp/Text.CSharp.Analyzers.csproj b/src/RoslynAnalyzers/Text.Analyzers/CSharp/Text.CSharp.Analyzers.csproj new file mode 100644 index 0000000000000..cd2b8a6c272d4 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/CSharp/Text.CSharp.Analyzers.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForTextAnalyzers) + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Text.Analyzers/Core/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..5925857f73278 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/AnalyzerReleases.Unshipped.md @@ -0,0 +1,9 @@ +; Please do not edit this file manually, it should only be updated through code fix application. + +### New Rules + +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +CA1704 | Naming | Warning | IdentifiersShouldBeSpelledCorrectlyAnalyzer, [Documentation](https://learn.microsoft.com/visualstudio/code-quality/ca1704) +CA1714 | Naming | Disabled | EnumsShouldHavePluralNamesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1714) +CA1717 | Naming | Disabled | EnumsShouldHavePluralNamesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1717) diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/CodeAnalysisDictionary.cs b/src/RoslynAnalyzers/Text.Analyzers/Core/CodeAnalysisDictionary.cs new file mode 100644 index 0000000000000..990c074d6cca7 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/CodeAnalysisDictionary.cs @@ -0,0 +1,110 @@ +// 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.IO; +using System.Linq; +using System.Xml.Linq; + +namespace Text.Analyzers +{ + /// + /// Source for "recognized" misspellings and "unrecognized" spellings obtained by parsing either XML or DIC code + /// analysis dictionaries. + /// + /// + /// + /// + internal sealed class CodeAnalysisDictionary + { + /// + /// A list of misspelled words that the spell checker will now ignore. + /// + /// + /// + /// + /// knokker + /// + /// + /// + private readonly HashSet _recognizedWords; + + /// + /// A list of correctly spelled words that the spell checker will now report. + /// + /// + /// + /// + /// meth + /// + /// + /// + private readonly HashSet _unrecognizedWords; + + /// + /// Initialize a new instance of . + /// + /// Misspelled words that the spell checker will now ignore. + /// Correctly spelled words that the spell checker will now report. + private CodeAnalysisDictionary(IEnumerable recognizedWords, IEnumerable unrecognizedWords) + { + _recognizedWords = new HashSet(recognizedWords, StringComparer.OrdinalIgnoreCase); + _unrecognizedWords = new HashSet(unrecognizedWords, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Creates a new instance of this class with recognized and unrecognized words (if specified) loaded + /// from the specified XML . + /// + /// XML stream of a code analysis dictionary. + /// A new instance of this class with the words loaded. + public static CodeAnalysisDictionary CreateFromXml(StreamReader streamReader) + { + var document = XDocument.Load(streamReader); + // TODO: Include Deprecated/Compound terms as noted here: + // https://learn.microsoft.com/visualstudio/code-quality/how-to-customize-the-code-analysis-dictionary + // Tracked by: + // https://github.com/dotnet/roslyn-analyzers/issues/4693 + return new CodeAnalysisDictionary( + GetSectionWords(document, "Recognized", "Word"), + GetSectionWords(document, "Unrecognized", "Word") + ); + } + + /// + /// Creates a new instance of this class with recognized words loaded from the specified DIC . + /// + /// + /// A DIC file usually has an extension of ".dic". It consists of a list of newline-delimited words. + /// + /// DIC stream of a code analysis dictionary. + /// A new instance of this class with recognized words loaded. + public static CodeAnalysisDictionary CreateFromDic(StreamReader streamReader) + { + var recognizedWords = new List(); + + string word; + while ((word = streamReader.ReadLine()) != null) + { + var trimmedWord = word.Trim(); + if (trimmedWord.Length > 0) + { + recognizedWords.Add(trimmedWord); + } + } + + return new CodeAnalysisDictionary(recognizedWords, Enumerable.Empty()); + } + + private static IEnumerable GetSectionWords(XDocument document, string section, string property) + => document.Descendants(section).SelectMany(section => section.Elements(property)).Select(element => element.Value.Trim()); + + public bool ContainsUnrecognizedWord(string word) + => _unrecognizedWords.Contains(word); + + public bool ContainsRecognizedWord(string word) + => _recognizedWords.Contains(word); + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/Dictionary.dic b/src/RoslynAnalyzers/Text.Analyzers/Core/Dictionary.dic new file mode 100644 index 0000000000000..b349c1239926b --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/Dictionary.dic @@ -0,0 +1,128941 @@ +0 +0th +1 +1st +1th +2 +2nd +2th +3 +3rd +3th +4 +4th +5 +5th +6 +6th +7 +7th +8 +8th +9 +9th +A +As +A's +AA +AA's +AAA +AB +AB's +ABA +ABC +ABCs +ABC's +ABM +ABMs +ABM's +ABS +AC +AC's +ACLU +ACLU's +ACT +ACTH +ACTH's +AD +AD's +ADC +ADD +ADM +ADP +ADP's +AF +AFAIK +AFB +AFC +AFC's +AFDC +AFN +AFT +AI +AIs +AI's +AIDS +AIDS's +AK +AL +AM +AM's +AMA +AMD +AMD's +ANSI +ANSIs +ANZUS +ANZUS's +AOL +AOL's +AP +AP's +APB +APC +API +APO +APR +AR +ARC +ASAP +ASCII +ASCIIs +ASCII's +ASL +ASL's +ASPCA +ATM +ATM's +ATP +ATP's +ATV +AV +AVI +AWACS +AWACS's +AWOL +AWOL's +AWS +AWS's +AZ +AZ's +AZT +AZT's +Aachen +Aachen's +Aaliyah +Aaliyah's +Aaron +Aaron's +Abbas +Abbas's +Abbasid +Abbasid's +Abbott +Abbott's +Abby +Abby's +Abdul +Abdul's +Abe +Abe's +Abel +Abel's +Abelard +Abelard's +Abelson +Abelson's +Aberdeen +Aberdeen's +Abernathy +Abernathy's +Abidjan +Abidjan's +Abigail +Abigail's +Abilene +Abilene's +Abner +Abner's +Aborigine +Aborigines +Aborigine's +Abraham +Abraham's +Abram +Abrams +Abram's +Abrams +Abrams's +Absalom +Absalom's +Abuja +Abuja's +Abyssinia +Abyssinia's +Abyssinian +Abyssinian's +Ac +Ac's +Acadia +Acadia's +Acapulco +Acapulco's +Accenture +Accenture's +Accra +Accra's +Acevedo +Acevedo's +Achaean +Achaean's +Achebe +Achebe's +Achernar +Achernar's +Acheson +Acheson's +Achilles +Achilles's +Aconcagua +Aconcagua's +Acosta +Acosta's +Acropolis +Acrux +Acrux's +Actaeon +Actaeon's +Acton +Acton's +Acts +Acts's +Acuff +Acuff's +Ada +Adas +Ada's +Adam +Adams +Adam's +Adams +Adams's +Adan +Adan's +Adana +Adana's +Adar +Adar's +Addams +Addams's +Adderley +Adderley's +Addie +Addie's +Addison +Addison's +Adela +Adela's +Adelaide +Adelaide's +Adele +Adele's +Adeline +Adeline's +Aden +Aden's +Adenauer +Adenauer's +Adhara +Adhara's +Adidas +Adidas's +Adirondack +Adirondacks +Adirondack's +Adirondacks +Adirondacks's +Adkins +Adkins's +Adler +Adler's +Adm +Admiralty +Adolf +Adolf's +Adolfo +Adolfo's +Adolph +Adolph's +Adonis +Adonises +Adonis's +Adrenalin +Adrenalins +Adrenalin's +Adrian +Adrian's +Adriana +Adriana's +Adriatic +Adriatic's +Adrienne +Adrienne's +Advent +Advents +Advent's +Adventist +Adventists +Adventist's +Advil +Advil's +Aegean +Aegean's +Aelfric +Aelfric's +Aeneas +Aeneas's +Aeneid +Aeneid's +Aeolus +Aeolus's +Aeroflot +Aeroflot's +Aeschylus +Aeschylus's +Aesculapius +Aesculapius's +Aesop +Aesop's +Afghan +Afghans +Afghan's +Afghani +Afghani's +Afghanistan +Afghanistan's +Afr +Africa +Africa's +African +Africans +African's +Afrikaans +Afrikaans's +Afrikaner +Afrikaners +Afrikaner's +Afro +Afros +Afro's +Afrocentric +Afrocentrism +Afrocentrism's +Ag +Ag's +Agamemnon +Agamemnon's +Agana +Agassi +Agassi's +Agassiz +Agassiz's +Agatha +Agatha's +Aggie +Aggie's +Aglaia +Aglaia's +Agnes +Agnes's +Agnew +Agnew's +Agni +Agni's +Agra +Agra's +Agricola +Agricola's +Agrippa +Agrippa's +Agrippina +Agrippina's +Aguascalientes +Aguilar +Aguilar's +Aguinaldo +Aguinaldo's +Aguirre +Aguirre's +Agustin +Agustin's +Ahab +Ahab's +Ahmad +Ahmad's +Ahmadabad +Ahmadabad's +Ahmadinejad +Ahmadinejad's +Ahmed +Ahmed's +Ahriman +Ahriman's +Aida +Aida's +Aiken +Aiken's +Aileen +Aileen's +Aimee +Aimee's +Ainu +Ainu's +Airedale +Airedales +Airedale's +Aires +Aires's +Aisha +Aisha's +Ajax +Ajax's +Akbar +Akbar's +Akhmatova +Akhmatova's +Akihito +Akihito's +Akita +Akita's +Akiva +Akiva's +Akkad +Akkad's +Akron +Akron's +Al +Al's +Ala +Alas +Alabama +Alabama's +Alabaman +Alabamans +Alabaman's +Alabamian +Alabamians +Alabamian's +Aladdin +Aladdin's +Alamo +Alamo's +Alamogordo +Alamogordo's +Alan +Alan's +Alana +Alana's +Alar +Alar's +Alaric +Alaric's +Alaska +Alaska's +Alaskan +Alaskans +Alaskan's +Alba +Alba's +Albania +Albania's +Albanian +Albanians +Albanian's +Albany +Albany's +Albee +Albee's +Alberio +Alberio's +Albert +Albert's +Alberta +Alberta's +Albertan +Alberto +Alberto's +Albigensian +Albigensian's +Albion +Albion's +Albireo +Albireo's +Albuquerque +Albuquerque's +Alcatraz +Alcatraz's +Alcestis +Alcestis's +Alcibiades +Alcibiades's +Alcindor +Alcindor's +Alcmena +Alcmena's +Alcoa +Alcoa's +Alcott +Alcott's +Alcuin +Alcuin's +Alcyone +Alcyone's +Aldan +Aldan's +Aldebaran +Aldebaran's +Alden +Alden's +Alderamin +Alderamin's +Aldo +Aldo's +Aldrin +Aldrin's +Alec +Alec's +Aleichem +Aleichem's +Alejandra +Alejandra's +Alejandro +Alejandro's +Alembert +Alembert's +Aleppo +Aleppo's +Aleut +Aleuts +Aleut's +Aleutian +Aleutians +Aleutian's +Alex +Alex's +Alexander +Alexanders +Alexander's +Alexandra +Alexandra's +Alexandria +Alexandria's +Alexandrian +Alexei +Alexei's +Alexis +Alexis's +Alfonso +Alfonso's +Alfonzo +Alfonzo's +Alford +Alford's +Alfred +Alfred's +Alfreda +Alfreda's +Alfredo +Alfredo's +Algenib +Algenib's +Alger +Alger's +Algeria +Algeria's +Algerian +Algerians +Algerian's +Algieba +Algieba's +Algiers +Algiers's +Algol +Algol's +Algonquian +Algonquians +Algonquian's +Algonquin +Algonquins +Algonquin's +Alhambra +Alhambra's +Alhena +Alhena's +Ali +Ali's +Alice +Alice's +Alicia +Alicia's +Alighieri +Alighieri's +Aline +Aline's +Alioth +Alioth's +Alisa +Alisa's +Alisha +Alisha's +Alison +Alison's +Alissa +Alissa's +Alistair +Alistair's +Alkaid +Alkaid's +Allah +Allah's +Allahabad +Allahabad's +Allan +Allan's +Alleghenies +Alleghenies's +Allegheny +Alleghenies +Allegheny's +Allegra +Allegra's +Allen +Allen's +Allende +Allende's +Allentown +Allentown's +Allhallows +Allhallows's +Allie +Allies +Allie's +Allison +Allison's +Allstate +Allstate's +Allyson +Allyson's +Alma +Alma's +Almach +Almach's +Almaty +Almaty's +Almighty +Almighty's +Almohad +Almohad's +Almoravid +Almoravid's +Alnilam +Alnilam's +Alnitak +Alnitak's +Alonzo +Alonzo's +Alpert +Alpert's +Alphard +Alphard's +Alphecca +Alphecca's +Alpheratz +Alpheratz's +Alphonse +Alphonse's +Alphonso +Alphonso's +Alpine +Alpine's +Alpo +Alpo's +Alps +Alps's +Alsace +Alsace's +Alsatian +Alsatians +Alsatian's +Alsop +Alsop's +Alston +Alston's +Alta +Alta's +Altaba +Altaba's +Altai +Altai's +Altaic +Altaic's +Altair +Altair's +Altamira +Altamira's +Althea +Althea's +Altiplano +Altiplano's +Altman +Altman's +Altoids +Altoids's +Alton +Alton's +Aludra +Aludra's +Alva +Alva's +Alvarado +Alvarado's +Alvarez +Alvarez's +Alvaro +Alvaro's +Alvin +Alvin's +Alyce +Alyce's +Alyson +Alyson's +Alyssa +Alyssa's +Alzheimer +Alzheimer's +Am +Amen +Amer +Am's +Amadeus +Amadeus's +Amado +Amado's +Amalia +Amalia's +Amanda +Amanda's +Amarillo +Amarillo's +Amaru +Amaru's +Amaterasu +Amaterasu's +Amati +Amati's +Amazon +Amazons +Amazon's +Amazonian +Amber +Amber's +Amelia +Amelia's +Amen +Amen's +Amenhotep +Amenhotep's +Amerasian +Amerasian's +America +Americas +America's +American +Americans +American's +Americana +Americana's +Americanism +Americanisms +Americanism's +Americanization +Americanizations +Americanization's +Americanize +Americanizing +Americanized +Americanizes +Amerind +Amerinds +Amerind's +Amerindian +Amerindians +Amerindian's +Ameslan +Ameslan's +Amgen +Amgen's +Amharic +Amharic's +Amherst +Amherst's +Amie +Amie's +Amiga +Amiga's +Amish +Amish's +Amman +Amman's +Amoco +Amoco's +Amos +Amos's +Amparo +Amparo's +Ampere +Ampere's +Amritsar +Amritsar's +Amsterdam +Amsterdam's +Amtrak +Amtrak's +Amundsen +Amundsen's +Amur +Amur's +Amway +Amway's +Amy +Amy's +Ana +Ana's +Anabaptist +Anabaptist's +Anabel +Anabel's +Anacin +Anacin's +Anacreon +Anacreon's +Anaheim +Anaheim's +Analects +Analects's +Ananias +Ananias's +Anasazi +Anasazi's +Anastasia +Anastasia's +Anatole +Anatole's +Anatolia +Anatolia's +Anatolian +Anatolian's +Anaxagoras +Anaxagoras's +Anchorage +Anchorage's +Andalusia +Andalusia's +Andalusian +Andalusian's +Andaman +Andaman's +Andean +Andean's +Andersen +Andersen's +Anderson +Anderson's +Andes +Andes's +Andorra +Andorra's +Andorran +Andorrans +Andorran's +Andre +Andres +Andre's +Andrea +Andrea's +Andrei +Andrei's +Andres +Andres's +Andretti +Andretti's +Andrew +Andrews +Andrew's +Andrews +Andrews's +Andrianampoinimerina +Andrianampoinimerina's +Android +Android's +Andromache +Andromache's +Andromeda +Andromeda's +Andropov +Andropov's +Andy +Andy's +Angara +Angara's +Angel +Angel's +Angela +Angela's +Angeles +Angeles's +Angelia +Angelia's +Angelica +Angelica's +Angelico +Angelico's +Angelina +Angelina's +Angeline +Angeline's +Angelique +Angelique's +Angelita +Angelita's +Angelo +Angelo's +Angelou +Angelou's +Angevin +Angevin's +Angie +Angie's +Angkor +Angkor's +Angle +Angles +Angle's +Anglia +Anglia's +Anglican +Anglicans +Anglican's +Anglicanism +Anglicanisms +Anglicanism's +Anglicism +Anglicisms +Anglicism's +Anglicization +Anglicize +Anglo +Anglo's +Anglophile +Anglophile's +Anglophobe +Angola +Angola's +Angolan +Angolans +Angolan's +Angora +Angoras +Angora's +Angstrom +Angstrom's +Anguilla +Anguilla's +Angus +Angus's +Anhui +Anhui's +Aniakchak +Aniakchak's +Anibal +Anibal's +Anita +Anita's +Ankara +Ankara's +Ann +Ann's +Anna +Anna's +Annabel +Annabel's +Annabelle +Annabelle's +Annam +Annam's +Annapolis +Annapolis's +Annapurna +Annapurna's +Anne +Anne's +Annette +Annette's +Annie +Annie's +Annmarie +Annmarie's +Annunciation +Annunciations +Annunciation's +Anouilh +Anouilh's +Anselm +Anselm's +Anselmo +Anselmo's +Anshan +Anshan's +Antaeus +Antaeus's +Antananarivo +Antananarivo's +Antarctic +Antarctic's +Antarctica +Antarctica's +Antares +Antares's +Anthony +Anthony's +Anthropocene +Antichrist +Antichrists +Antichrist's +Antietam +Antietam's +Antigone +Antigone's +Antigua +Antigua's +Antillean +Antilles +Antilles's +Antioch +Antioch's +Antipas +Antipas's +Antipodes +Antofagasta +Antofagasta's +Antoine +Antoine's +Antoinette +Antoinette's +Anton +Anton's +Antone +Antone's +Antonia +Antonia's +Antoninus +Antoninus's +Antonio +Antonio's +Antonius +Antonius's +Antony +Antony's +Antwan +Antwan's +Antwerp +Antwerp's +Anubis +Anubis's +Anzac +Anzac's +Apache +Apaches +Apache's +Apalachicola +Apalachicola's +Apatosaurus +Apennines +Apennines's +Aphrodite +Aphrodite's +Apia +Apia's +Apocalypse +Apocalypse's +Apocrypha +Apocrypha's +Apollinaire +Apollinaire's +Apollo +Apollos +Apollo's +Apollonian +Apollonian's +Apostle +Apostle's +Appalachia +Appalachia's +Appalachian +Appalachians +Appalachian's +Appalachians +Appalachians's +Appaloosa +Appaloosas +Appaloosa's +Apple +Apple's +Appleseed +Appleseed's +Appleton +Appleton's +Appomattox +Appomattox's +Apr +Apr's +April +Aprils +April's +Apuleius +Apuleius's +Aquafresh +Aquafresh's +Aquarian +Aquarius +Aquariuses +Aquarius's +Aquila +Aquila's +Aquinas +Aquinas's +Aquino +Aquino's +Aquitaine +Aquitaine's +Ar +Ar's +Ara +Ara's +Arab +Arabs +Arab's +Arabia +Arabia's +Arabian +Arabians +Arabian's +Arabic +Arabic's +Arabist +Arabists +Arabist's +Araby +Araby's +Araceli +Araceli's +Arafat +Arafat's +Aragon +Araguaya +Araguaya's +Aral +Aral's +Aramaic +Aramaic's +Aramco +Aramco's +Arapaho +Arapahos +Arapaho's +Arapahoes +Ararat +Ararat's +Araucanian +Araucanian's +Arawak +Arawak's +Arawakan +Arawakan's +Arbitron +Arbitron's +Arcadia +Arcadia's +Arcadian +Arcadian's +Archean +Archean's +Archibald +Archibald's +Archie +Archie's +Archimedes +Archimedes's +Arctic +Arctic's +Arcturus +Arcturus's +Ardabil +Arden +Arden's +Arduino +Arduino's +Arequipa +Arequipa's +Ares +Ares's +Argentina +Argentina's +Argentine +Argentine's +Argentinean +Argentinian +Argentinians +Argentinian's +Argo +Argos +Argo's +Argonaut +Argonauts +Argonaut's +Argonne +Argonne's +Argos +Argos's +Argus +Argus's +Ariadne +Ariadne's +Arianism +Arianism's +Ariel +Ariel's +Aries +Arieses +Aries's +Ariosto +Ariosto's +Aristarchus +Aristarchus's +Aristides +Aristides's +Aristophanes +Aristophanes's +Aristotelian +Aristotelian's +Aristotle +Aristotle's +Arius +Arius's +Ariz +Arizona +Arizona's +Arizonan +Arizonans +Arizonan's +Arizonian +Arizonians +Arizonian's +Arjuna +Arjuna's +Ark +Ark's +Arkansan +Arkansans +Arkansan's +Arkansas +Arkansas's +Arkhangelsk +Arkhangelsk's +Arkwright +Arkwright's +Arlene +Arlene's +Arline +Arline's +Arlington +Arlington's +Armageddon +Armageddons +Armageddon's +Armagnac +Armagnac's +Armand +Armand's +Armando +Armando's +Armani +Armani's +Armenia +Armenia's +Armenian +Armenians +Armenian's +Arminius +Arminius's +Armonk +Armonk's +Armour +Armour's +Armstrong +Armstrong's +Arneb +Arneb's +Arnhem +Arnhem's +Arno +Arno's +Arnold +Arnold's +Arnulfo +Arnulfo's +Aron +Aron's +Arrhenius +Arrhenius's +Arron +Arron's +Art +Art's +Artaxerxes +Artaxerxes's +Artemis +Artemis's +Arthur +Arthur's +Arthurian +Arthurian's +Artie +Artie's +Arturo +Arturo's +Aruba +Aruba's +Aryan +Aryans +Aryan's +As +As's +Asama +Asama's +Ascella +Ascella's +Ascension +Ascension's +Asgard +Asgard's +Ashanti +Ashanti's +Ashcroft +Ashcroft's +Ashe +Ashe's +Ashgabat +Ashikaga +Ashikaga's +Ashkenazim +Ashkenazim's +Ashkhabad +Ashkhabad's +Ashlee +Ashlee's +Ashley +Ashley's +Ashmolean +Ashmolean's +Ashurbanipal +Ashurbanipal's +Asia +Asia's +Asiago +Asian +Asians +Asian's +Asiatic +Asiatics +Asiatic's +Asimov +Asimov's +Asmara +Asmara's +Asoka +Asoka's +Aspell +Aspell's +Aspen +Aspen's +Asperger +Asperger's +Aspidiske +Aspidiske's +Asquith +Asquith's +Assad +Assad's +Assam +Assam's +Assamese +Assamese's +Assembly +Assisi +Assisi's +Assyria +Assyria's +Assyrian +Assyrians +Assyrian's +Astaire +Astaire's +Astana +Astana's +Astarte +Astarte's +Aston +Aston's +Astor +Astor's +Astoria +Astoria's +Astrakhan +Astrakhan's +AstroTurf +AstroTurf's +Asturias +Asturias's +Asuncion +Asuncion's +Aswan +Aswan's +At +Ats +At's +Atacama +Atacama's +Atahualpa +Atahualpa's +Atalanta +Atalanta's +Atari +Atari's +Ataturk +Ataturk's +Athabasca +Athabasca's +Athabaskan +Athabaskans +Athabaskan's +Athanasius +Athena +Athena's +Athene +Athene's +Athenian +Athenians +Athenian's +Athens +Athens's +Atkins +Atkins's +Atkinson +Atkinson's +Atlanta +Atlanta's +Atlantes +Atlantic +Atlantic's +Atlantis +Atlantis's +Atlas +Atlases +Atlas's +Atman +Atman's +Atonement +Atreus +Atreus's +Atria +Atria's +Atropos +Atropos's +Attic +Attic's +Attica +Attica's +Attila +Attila's +Attlee +Attlee's +Attn +Attucks +Attucks's +Atwood +Atwood's +Au +Au's +Aubrey +Aubrey's +Auckland +Auckland's +Auden +Auden's +Audi +Audi's +Audion +Audion's +Audra +Audra's +Audrey +Audrey's +Audubon +Audubon's +Aug +Aug's +Augean +Augean's +Augsburg +Augsburg's +August +Augusts +August's +Augusta +Augusta's +Augustan +Augustan's +Augustine +Augustine's +Augustinian +Augustinians +Augustinian's +Augustus +Augustus's +Aurangzeb +Aurangzeb's +Aurelia +Aurelia's +Aurelio +Aurelio's +Aurelius +Aurelius's +Aureomycin +Aureomycin's +Auriga +Auriga's +Aurora +Aurora's +Auschwitz +Auschwitz's +Aussie +Aussies +Aussie's +Austen +Austen's +Austerlitz +Austerlitz's +Austin +Austins +Austin's +Australasia +Australasia's +Australasian +Australia +Australia's +Australian +Australians +Australian's +Australoid +Australoid's +Australopithecus +Australopithecus's +Austria +Austria's +Austrian +Austrians +Austrian's +Austronesian +Austronesian's +Autumn +Autumn's +Av +Av's +Ava +Ava's +Avalon +Avalon's +Ave +Ave's +Aventine +Aventine's +Avernus +Avernus's +Averroes +Averroes's +Avery +Avery's +Avesta +Avesta's +Avicenna +Avicenna's +Avignon +Avignon's +Avila +Avila's +Avior +Avior's +Avis +Avis's +Avogadro +Avogadro's +Avon +Avon's +Axis +Axum +Axum's +Ayala +Ayala's +Ayers +Ayers's +Aymara +Aymara's +Ayrshire +Ayrshire's +Ayurveda +Ayurveda's +Ayyubid +Ayyubid's +Azana +Azana's +Azania +Azania's +Azazel +Azazel's +Azerbaijan +Azerbaijan's +Azerbaijani +Azerbaijanis +Azerbaijani's +Azores +Azores's +Azov +Azov's +Aztec +Aztecs +Aztec's +Aztecan +Aztecan's +Aztlan +Aztlan's +B +Ben +Best +B's +BA +BA's +BASIC +BASICs +BASIC's +BB +BB's +BBB +BBB's +BBC +BBC's +BBQ +BBS +BBSes +BC +BC's +BFF +BIA +BIOS +BITNET +BLT +BLTs +BLT's +BM +BM's +BMW +BMW's +BO +BP +BP's +BPOE +BR +BS +BS's +BSA +BSD +BSDs +BSD's +BTU +BTW +BYOB +Ba +Ba's +Baal +Baals +Baal's +Baath +Baath's +Baathist +Baathist's +Babbage +Babbage's +Babbitt +Babbitt's +Babel +Babels +Babel's +Babylon +Babylons +Babylon's +Babylonia +Babylonia's +Babylonian +Babylonians +Babylonian's +Bacall +Bacall's +Bacardi +Bacardi's +Bacchanalia +Bacchanalia's +Bacchic +Bacchus +Bacchus's +Bach +Bach's +Backus +Backus's +Bacon +Bacon's +Bactria +Bactria's +Baden +Baden's +Badlands +Badlands's +Baedeker +Baedekers +Baedeker's +Baez +Baez's +Baffin +Baffin's +Baggies +Baggies's +Baghdad +Baghdad's +Baguio +Baguio's +Baha'i +Baha'i's +Baha'ullah +Baha'ullah's +Bahama +Bahamas +Bahama's +Bahamanian +Bahamas +Bahamas's +Bahamian +Bahamians +Bahamian's +Bahia +Bahia's +Bahrain +Bahrain's +Baidu +Baidu's +Baikal +Baikal's +Bailey +Bailey's +Baird +Baird's +Bakelite +Bakelite's +Baker +Baker's +Bakersfield +Bakersfield's +Baku +Baku's +Bakunin +Bakunin's +Balanchine +Balanchine's +Balaton +Balaton's +Balboa +Balboa's +Balder +Balder's +Baldwin +Baldwins +Baldwin's +Balearic +Balearic's +Balfour +Balfour's +Bali +Bali's +Balinese +Balinese's +Balkan +Balkans +Balkan's +Balkans +Balkans's +Balkhash +Balkhash's +Ball +Ball's +Ballard +Ballard's +Balthazar +Balthazar's +Baltic +Baltic's +Baltimore +Baltimore's +Baluchistan +Baluchistan's +Balzac +Balzac's +Bamako +Bamako's +Bambi +Bambi's +Banach +Banach's +Bancroft +Bancroft's +Bandung +Bandung's +Bangalore +Bangalore's +Bangkok +Bangkok's +Bangladesh +Bangladesh's +Bangladeshi +Bangladeshis +Bangladeshi's +Bangor +Bangor's +Bangui +Bangui's +Banjarmasin +Banjarmasin's +Banjul +Banjul's +Banks +Banks's +Banneker +Banneker's +Bannister +Bannister's +Banting +Banting's +Bantu +Bantus +Bantu's +Baotou +Baotou's +Baptist +Baptists +Baptist's +Baptiste +Baptiste's +Barabbas +Barabbas's +Barack +Barack's +Barbadian +Barbadians +Barbadian's +Barbados +Barbados's +Barbara +Barbara's +Barbarella +Barbarella's +Barbarossa +Barbarossa's +Barbary +Barbary's +Barber +Barber's +Barbie +Barbie's +Barbour +Barbour's +Barbra +Barbra's +Barbuda +Barbuda's +Barcelona +Barcelona's +Barclay +Barclays +Barclay's +Barclays +Barclays's +Bardeen +Bardeen's +Barents +Barents's +Barker +Barker's +Barkley +Barkley's +Barlow +Barlow's +Barnabas +Barnabas's +Barnaby +Barnaby's +Barnard +Barnard's +Barnaul +Barnaul's +Barnes +Barnes's +Barnett +Barnett's +Barney +Barney's +Barnum +Barnum's +Baroda +Baroda's +Barquisimeto +Barquisimeto's +Barr +Barr's +Barranquilla +Barranquilla's +Barrera +Barrera's +Barrett +Barrett's +Barrie +Barrie's +Barron +Barron's +Barry +Barry's +Barrymore +Barrymore's +Bart +Bart's +Barth +Barthes +Barth's +Bartholdi +Bartholdi's +Bartholomew +Bartholomew's +Bartlett +Bartlett's +Bartok +Bartok's +Barton +Barton's +Baruch +Baruch's +Baryshnikov +Baryshnikov's +Basel +Basel's +Basho +Basho's +Basie +Basie's +Basil +Basil's +Basque +Basques +Basque's +Basra +Basra's +Bass +Bass's +Basseterre +Basseterre's +Bastille +Bastille's +Basutoland +Basutoland's +Bataan +Bataan's +Bates +Bates's +Bathsheba +Bathsheba's +Batista +Batista's +Batman +Batman's +Battle +Battle's +Batu +Batu's +Baudelaire +Baudelaire's +Baudouin +Baudouin's +Baudrillard +Baudrillard's +Bauer +Bauer's +Bauhaus +Bauhaus's +Baum +Baum's +Bavaria +Bavaria's +Bavarian +Bavarian's +Baxter +Baxter's +Bayamon +Bayer +Bayer's +Bayes +Bayes's +Bayesian +Bayesian's +Bayeux +Bayeux's +Baylor +Baylor's +Bayonne +Bayonne's +Bayreuth +Bayreuth's +Baywatch +Baywatch's +Be +Beth +Be's +Beach +Beach's +Beadle +Beadle's +Bean +Bean's +Beard +Beard's +Beardmore +Beardmore's +Beardsley +Beardsley's +Bearnaise +Bearnaise's +Beasley +Beasley's +Beatlemania +Beatlemania's +Beatles +Beatles's +Beatrice +Beatrice's +Beatrix +Beatrix's +Beatriz +Beatriz's +Beatty +Beatty's +Beau +Beau's +Beaufort +Beaufort's +Beaujolais +Beaujolais's +Beaumarchais +Beaumarchais's +Beaumont +Beaumont's +Beauregard +Beauregard's +Beauvoir +Beauvoir's +Bechtel +Bechtel's +Beck +Becker +Beck's +Becker +Becker's +Becket +Becket's +Beckett +Beckett's +Beckman +Becky +Becky's +Becquerel +Becquerel's +Bede +Bede's +Bedouin +Bedouins +Bedouin's +Beebe +Beebe's +Beecher +Beecher's +Beefaroni +Beefaroni's +Beelzebub +Beelzebub's +Beerbohm +Beerbohm's +Beethoven +Beethoven's +Beeton +Beeton's +Begin +Begin's +Behan +Behan's +Behring +Behring's +Beiderbecke +Beiderbecke's +Beijing +Beijing's +Beirut +Beirut's +Bekesy +Bekesy's +Bela +Bela's +Belarus +Belarus's +Belarusian +Belau +Belau's +Belem +Belem's +Belfast +Belfast's +Belg +Belgian +Belgians +Belgian's +Belgium +Belgium's +Belgrade +Belgrade's +Belinda +Belinda's +Belize +Belize's +Bell +Bell's +Bella +Bella's +Bellamy +Bellamy's +Bellatrix +Bellatrix's +Belleek +Belleek's +Bellini +Bellini's +Bellow +Bellow's +Belmont +Belmont's +Belmopan +Belmopan's +Belorussian +Belorussians +Belorussian's +Belshazzar +Belshazzar's +Beltane +Beltane's +Belushi +Belushi's +Ben +Ben's +Benacerraf +Benacerraf's +Benchley +Benchley's +Bender +Bender's +Bendictus +Bendix +Bendix's +Benedict +Benedict's +Benedictine +Benedictines +Benedictine's +Benelux +Benelux's +Benet +Benet's +Benetton +Benetton's +Bengal +Bengals +Bengal's +Bengali +Bengali's +Benghazi +Benghazi's +Benin +Benin's +Beninese +Beninese's +Benita +Benita's +Benito +Benito's +Benjamin +Benjamin's +Bennett +Bennett's +Bennie +Bennie's +Benny +Benny's +Benson +Benson's +Bentham +Bentham's +Bentley +Bentley's +Benton +Benton's +Benz +Benz's +Benzedrine +Benzedrine's +Beowulf +Beowulf's +Berber +Berbers +Berber's +Berenice +Berenice's +Beretta +Beretta's +Berg +Bergen +Berger +Berg's +Bergen +Bergen's +Berger +Berger's +Bergerac +Bergerac's +Bergman +Bergman's +Bergson +Bergson's +Beria +Beria's +Bering +Bering's +Berkeley +Berkeley's +Berkshire +Berkshires +Berkshire's +Berkshires +Berkshires's +Berle +Berle's +Berlin +Berliner +Berliners +Berlins +Berlin's +Berliner +Berliner's +Berlioz +Berlioz's +Berlitz +Berlitz's +Bermuda +Bermudas +Bermuda's +Bermudan +Bermudans +Bermudan's +Bermudian +Bermudians +Bermudian's +Bern +Bern's +Bernadette +Bernadette's +Bernadine +Bernadine's +Bernanke +Bernanke's +Bernard +Bernard's +Bernardo +Bernardo's +Bernays +Bernays's +Bernbach +Bernbach's +Bernese +Bernhardt +Bernhardt's +Bernice +Bernice's +Bernie +Bernie's +Bernini +Bernini's +Bernoulli +Bernoulli's +Bernstein +Bernstein's +Berra +Berra's +Berry +Berry's +Bert +Bert's +Berta +Berta's +Bertelsmann +Bertelsmann's +Bertha +Bertha's +Bertie +Bertie's +Bertillon +Bertillon's +Bertram +Bertram's +Bertrand +Bertrand's +Beryl +Beryl's +Berzelius +Berzelius's +Bess +Bess's +Bessel +Bessel's +Bessemer +Bessemer's +Bessie +Bessie's +Best +Best's +Betelgeuse +Betelgeuse's +Beth +Beth's +Bethany +Bethany's +Bethe +Bethe's +Bethesda +Bethesda's +Bethlehem +Bethlehem's +Bethune +Bethune's +Betsy +Betsy's +Bette +Bette's +Bettie +Bettie's +Betty +Betty's +Bettye +Bettye's +Beulah +Beulah's +Beveridge +Beverley +Beverley's +Beverly +Beverly's +Beyer +Beyer's +Bharat +Bharat's +Bhopal +Bhopal's +Bhutan +Bhutan's +Bhutanese +Bhutanese's +Bhutto +Bhutto's +Bi +Bi's +Bialystok +Bialystok's +Bianca +Bianca's +Bib +Bible +Bibles +Bible's +Bic +Bic's +Biddle +Biddle's +Biden +Biden's +Bierce +Bierce's +BigQuery +BigQuery's +Bigfoot +Bigfoot's +Biggles +Biggles's +Biko +Biko's +Bilbao +Bilbao's +Bilbo +Bilbo's +Bill +Billings +Bill's +Billie +Billie's +Billings +Billings's +Billy +Billy's +Bimini +Bimini's +Biogen +Biogen's +Bioko +Bioko's +Bird +Bird's +Birdseye +Birdseye's +Birkenstock +Birkenstock's +Birmingham +Birmingham's +Biro +Biro's +Biscay +Biscay's +Biscayne +Biscayne's +Bishkek +Bishkek's +Bishop +Bishop's +Bismarck +Bismarck's +Bismark +Bismark's +Bisquick +Bisquick's +Bissau +Bissau's +BitTorrent +BitTorrent's +Bizet +Bizet's +Bjerknes +Bjerknes's +Bjork +Bjork's +Bk +Bk's +BlackBerry +BlackBerry's +Blackbeard +Blackbeard's +Blackburn +Blackburn's +Blackfeet +Blackfeet's +Blackfoot +Blackfoot's +Blackpool +Blackpool's +Blackshirt +Blackshirt's +Blackstone +Blackstone's +Blackwell +Blackwell's +Blaine +Blaine's +Blair +Blair's +Blake +Blake's +Blanca +Blanca's +Blanchard +Blanchard's +Blanche +Blanche's +Blankenship +Blankenship's +Blantyre +Blantyre's +Blatz +Blatz's +Blavatsky +Blavatsky's +Blenheim +Blenheim's +Blevins +Blevins's +Bligh +Bligh's +Bloch +Bloch's +Blockbuster +Blockbuster's +Bloemfontein +Bloemfontein's +Blondel +Blondel's +Blondie +Blondie's +Bloom +Bloomer +Bloom's +Bloomer +Bloomer's +Bloomfield +Bloomfield's +Bloomingdale +Bloomingdale's +Bloomsbury +Bloomsbury's +Blu +Blucher +Blucher's +Bluebeard +Bluebeard's +Bluetooth +Bluetooth's +Blvd +Blythe +Blythe's +Boadicea +Boas +Boas's +Bob +Bob's +Bobbi +Bobbi's +Bobbie +Bobbie's +Bobbitt +Bobbitt's +Bobby +Bobby's +Boccaccio +Boccaccio's +Bodhidharma +Bodhidharma's +Bodhisattva +Bodhisattva's +Bodleian +Boeing +Boeing's +Boeotia +Boeotia's +Boeotian +Boeotian's +Boer +Boers +Boer's +Boethius +Boethius's +Bogart +Bogart's +Bogota +Bogota's +Bohemia +Bohemia's +Bohemian +Bohemians +Bohemian's +Bohr +Bohr's +Boise +Boise's +Bojangles +Bojangles's +Boleyn +Boleyn's +Bolivar +Bolivar's +Bolivia +Bolivia's +Bolivian +Bolivians +Bolivian's +Bollywood +Bollywood's +Bologna +Bologna's +Bolshevik +Bolsheviks +Bolshevik's +Bolsheviki +Bolshevism +Bolshevism's +Bolshevist +Bolshevist's +Bolshoi +Bolshoi's +Bolton +Bolton's +Boltzmann +Boltzmann's +Bombay +Bombay's +Bonaparte +Bonaparte's +Bonaventure +Bonaventure's +Bond +Bond's +Bonhoeffer +Bonhoeffer's +Boniface +Boniface's +Bonita +Bonita's +Bonn +Bonner +Bonn's +Bonner +Bonner's +Bonneville +Bonneville's +Bonnie +Bonnie's +Bono +Bono's +Booker +Booker's +Boole +Boole's +Boolean +Boolean's +Boone +Boone's +Bootes +Bootes's +Booth +Booth's +Bordeaux +Bordeaux's +Borden +Borden's +Bordon +Bordon's +Boreas +Boreas's +Borg +Borgs +Borg's +Borges +Borges's +Borgia +Borgia's +Borglum +Borglum's +Boris +Boris's +Bork +Bork's +Borlaug +Borlaug's +Born +Born's +Borneo +Borneo's +Borobudur +Borobudur's +Borodin +Borodin's +Boru +Boru's +Bosch +Bosch's +Bose +Bose's +Bosnia +Bosnia's +Bosnian +Bosporus +Bosporus's +Boston +Bostons +Boston's +Bostonian +Bostonian's +Boswell +Boswell's +Botha +Botox +Botswana +Botswana's +Botticelli +Botticelli's +Boulder +Boulder's +Boulez +Boulez's +Bourbaki +Bourbaki's +Bourbon +Bourbons +Bourbon's +Bournemouth +Bournemouth's +Bovary +Bovary's +Bowditch +Bowditch's +Bowell +Bowell's +Bowen +Bowen's +Bowers +Bowers's +Bowery +Bowery's +Bowie +Bowie's +Bowman +Bowman's +Boyd +Boyd's +Boyer +Boyer's +Boyle +Boyle's +Br +Brest +Br's +Brad +Bradly +Brad's +Bradbury +Bradbury's +Braddock +Braddock's +Bradford +Bradford's +Bradley +Bradley's +Bradly +Bradly's +Bradshaw +Bradshaw's +Bradstreet +Bradstreet's +Brady +Brady's +Bragg +Bragg's +Brahe +Brahe's +Brahma +Brahmas +Brahma's +Brahmagupta +Brahmagupta's +Brahman +Brahmans +Brahman's +Brahmani +Brahmanism +Brahmanisms +Brahmanism's +Brahmaputra +Brahmaputra's +Brahms +Brahms's +Braille +Brailles +Braille's +Brain +Brain's +Brampton +Brampton's +Bran +Bran's +Branch +Branch's +Brandeis +Brandeis's +Branden +Branden's +Brandenburg +Brandenburg's +Brandi +Brandi's +Brandie +Brandie's +Brando +Brando's +Brandon +Brandon's +Brandt +Brandt's +Brandy +Brandy's +Brant +Brant's +Braque +Braque's +Brasilia +Brasilia's +Bratislava +Bratislava's +Brattain +Brattain's +Bray +Bray's +Brazil +Brazil's +Brazilian +Brazilians +Brazilian's +Brazos +Brazos's +Brazzaville +Brazzaville's +Breakspear +Breakspear's +Breathalyzer +Brecht +Brecht's +Breckenridge +Breckenridge's +Bremen +Bremen's +Brenda +Brenda's +Brendan +Brendan's +Brennan +Brennan's +Brenner +Brenner's +Brent +Brent's +Brenton +Brenton's +Brest +Brest's +Bret +Bret's +Breton +Breton's +Brett +Brett's +Brewer +Brewer's +Brewster +Brewster's +Brexit +Brezhnev +Brezhnev's +Brian +Brian's +Briana +Briana's +Brianna +Brianna's +Brice +Brice's +Bridalveil +Bridalveil's +Bridgeport +Bridgeport's +Bridger +Bridger's +Bridges +Bridges's +Bridget +Bridget's +Bridgetown +Bridgetown's +Bridgett +Bridgett's +Bridgette +Bridgette's +Bridgman +Bridgman's +Brie +Bries +Brie's +Brigadoon +Brigadoon's +Briggs +Briggs's +Brigham +Brigham's +Bright +Bright's +Brighton +Brighton's +Brigid +Brigid's +Brigitte +Brigitte's +Brillo +Brillo's +Brillouin +Brinkley +Brinkley's +Brisbane +Brisbane's +Bristol +Bristol's +Brit +Brits +Brit's +Britain +Britain's +Britannia +Britannia's +Britannic +Britannic's +Britannica +Britannica's +Briticism +Briticisms +Briticism's +British +Britisher +Britishers +British's +Britisher +Britisher's +Britney +Britney's +Briton +Britons +Briton's +Britt +Britten +Britt's +Brittany +Brittanies +Brittany's +Britten +Britten's +Brittney +Brittney's +Brno +Brno's +Broadway +Broadways +Broadway's +Brobdingnag +Brobdingnag's +Brobdingnagian +Brobdingnagian's +Brock +Brock's +Brokaw +Brokaw's +Bronson +Bronson's +Bronte +Bronte's +Brontosaurus +Bronx +Bronx's +Brooke +Brookes +Brooke's +Brooklyn +Brooklyn's +Brooks +Brooks's +Bros +Brown +Browning +Brown's +Browne +Browne's +Brownian +Brownian's +Brownie +Brownies +Browning +Browning's +Brownshirt +Brownshirt's +Brownsville +Brownsville's +Brubeck +Brubeck's +Bruce +Bruce's +Bruckner +Bruckner's +Bruegel +Brummel +Brummel's +Brunei +Brunei's +Bruneian +Bruneians +Bruneian's +Brunelleschi +Brunelleschi's +Brunhilde +Brunhilde's +Bruno +Bruno's +Brunswick +Brunswick's +Brussels +Brussels's +Brut +Brut's +Brutus +Brutus's +Bryan +Bryan's +Bryant +Bryant's +Bryce +Bryce's +Brynner +Brynner's +Bryon +Bryon's +Brzezinski +Brzezinski's +Btu +Btu's +Buber +Buber's +Buchanan +Buchanan's +Bucharest +Bucharest's +Buchenwald +Buchenwald's +Buchwald +Buchwald's +Buck +Buck's +Buckingham +Buckingham's +Buckley +Buckley's +Buckner +Buckner's +Bud +Bud's +Budapest +Budapest's +Buddha +Buddhas +Buddha's +Buddhism +Buddhisms +Buddhism's +Buddhist +Buddhists +Buddhist's +Buddy +Buddy's +Budweiser +Budweiser's +Buffalo +Buffalo's +Buffy +Buffy's +Buford +Buford's +Bugatti +Bugatti's +Bugzilla +Bugzilla's +Buick +Buick's +Bujumbura +Bujumbura's +Bukhara +Bukhara's +Bukharin +Bukharin's +Bulawayo +Bulawayo's +Bulfinch +Bulfinch's +Bulganin +Bulganin's +Bulgar +Bulgar's +Bulgari +Bulgari's +Bulgaria +Bulgaria's +Bulgarian +Bulgarians +Bulgarian's +Bullock +Bullock's +Bullwinkle +Bullwinkle's +Bultmann +Bultmann's +Bumppo +Bumppo's +Bunche +Bunche's +Bundesbank +Bundesbank's +Bundestag +Bundestag's +Bunin +Bunin's +Bunker +Bunker's +Bunsen +Bunsen's +Bunuel +Bunuel's +Bunyan +Bunyan's +Burbank +Burbank's +Burberry +Burberry's +Burch +Burch's +Burger +Burger's +Burgess +Burgess's +Burgoyne +Burgoyne's +Burgundian +Burgundian's +Burgundy +Burgundies +Burgundy's +Burke +Burke's +Burks +Burks's +Burl +Burl's +Burlington +Burlington's +Burma +Burma's +Burmese +Burmese's +Burnett +Burnett's +Burns +Burns's +Burnside +Burnside's +Burr +Burr's +Burris +Burris's +Burroughs +Burroughs's +Bursa +Bursa's +Burt +Burt's +Burton +Burton's +Burundi +Burundi's +Burundian +Burundians +Burundian's +Busch +Busch's +Bush +Bush's +Bushido +Bushido's +Bushnell +Bushnell's +Butler +Butler's +Butterfingers +Butterfingers's +Buxtehude +Buxtehude's +Byblos +Byblos's +Byers +Byers's +Byrd +Byrd's +Byron +Byron's +Byronic +Byronic's +Byzantine +Byzantines +Byzantine's +Byzantium +Byzantium's +C +Cs +C's +CA +CAD +CAD's +CAI +CAM +CAP +CARE +CATV +CB +CBC +CBC's +CBS +CBS's +CCTV +CCU +CD +CDs +CD's +CDC +CDT +CEO +CEO's +CF +CFC +CFC's +CFO +CGI +CIA +CIA's +CID +CNN +CNN's +CNS +CNS's +CO +CO's +COBOL +COBOLs +COBOL's +COD +COL +COLA +CPA +CPA's +CPI +CPI's +CPO +CPR +CPR's +CPU +CPU's +CRT +CRTs +CRT's +CSS +CSS's +CST +CST's +CT +CT's +CV +CVS +CVS's +CZ +Ca +Ca's +Cabernet +Cabernet's +Cabot +Cabot's +Cabral +Cabral's +Cabrera +Cabrera's +Cabrini +Cabrini's +Cadette +Cadillac +Cadillac's +Cadiz +Cadiz's +Caedmon +Caedmon's +Caerphilly +Caerphilly's +Caesar +Caesars +Caesar's +Cage +Cage's +Cagney +Cagney's +Cahokia +Cahokia's +Caiaphas +Caiaphas's +Cain +Cains +Cain's +Cairo +Cairo's +Caitlin +Caitlin's +Cajun +Cajuns +Cajun's +Cal +Cal's +Calais +Calais's +Calcutta +Calcutta's +Calder +Calder's +Calderon +Calderon's +Caldwell +Caldwell's +Caleb +Caleb's +Caledonia +Caledonia's +Calgary +Calgary's +Calhoun +Calhoun's +Cali +Cali's +Caliban +Caliban's +Calif +California +California's +Californian +Californians +Californian's +Caligula +Caligula's +Callaghan +Callaghan's +Callahan +Callahan's +Callao +Callao's +Callas +Callas's +Callie +Callie's +Calliope +Calliope's +Callisto +Callisto's +Caloocan +Caloocan's +Calvary +Calvary's +Calvert +Calvert's +Calvin +Calvin's +Calvinism +Calvinisms +Calvinism's +Calvinist +Calvinists +Calvinist's +Calvinistic +Camacho +Camacho's +Cambodia +Cambodia's +Cambodian +Cambodians +Cambodian's +Cambrian +Cambrians +Cambrian's +Cambridge +Cambridge's +Camden +Camden's +Camel +Camel's +Camelopardalis +Camelopardalis's +Camelot +Camelots +Camelot's +Camembert +Camemberts +Camembert's +Cameron +Cameron's +Cameroon +Cameroons +Cameroon's +Cameroonian +Cameroonians +Cameroonian's +Camilla +Camilla's +Camille +Camille's +Camoens +Camoens's +Campanella +Campanella's +Campbell +Campbell's +Campinas +Campinas's +Campos +Campos's +Camry +Camry's +Camus +Camus's +Can +Can's +Canaan +Canaan's +Canaanite +Canaanites +Canaanite's +Canad +Canada +Canada's +Canadian +Canadians +Canadian's +Canadianism +Canaletto +Canaletto's +Canaries +Canaries's +Canaveral +Canaveral's +Canberra +Canberra's +Cancer +Cancers +Cancer's +Cancun +Cancun's +Candace +Candace's +Candice +Candice's +Candide +Candide's +Candy +Candy's +Cannes +Cannes's +Cannon +Cannon's +Canon +Canon's +Canopus +Canopus's +Cantabrigian +Cantabrigian's +Canterbury +Canterbury's +Canton +Canton's +Cantonese +Cantonese's +Cantor +Cantor's +Cantrell +Cantrell's +Cantu +Cantu's +Canute +Canute's +Capablanca +Capablanca's +Capek +Capek's +Capella +Capella's +Capet +Capet's +Capetian +Capetian's +Capetown +Capetown's +Caph +Caph's +Capistrano +Capistrano's +Capitol +Capitols +Capitol's +Capitoline +Capitoline's +Capone +Capone's +Capote +Capote's +Capra +Capra's +Capri +Capri's +Capricorn +Capricorns +Capricorn's +Capt +Capuchin +Capuchin's +Capulet +Capulet's +Cara +Cara's +Caracalla +Caracalla's +Caracas +Caracas's +Caravaggio +Caravaggio's +Carboloy +Carboloy's +Carboniferous +Carboniferous's +Carborundum +Carborundum's +Cardenas +Cardenas's +Cardiff +Cardiff's +Cardin +Cardin's +Cardozo +Cardozo's +Carey +Carey's +Carib +Caribs +Carib's +Caribbean +Caribbeans +Caribbean's +Carina +Carina's +Carissa +Carissa's +Carl +Carl's +Carla +Carla's +Carlene +Carlene's +Carlin +Carlin's +Carlo +Carlos +Carlo's +Carlos +Carlos's +Carlsbad +Carlsbad's +Carlson +Carlson's +Carlton +Carlton's +Carly +Carly's +Carlyle +Carlyle's +Carmela +Carmela's +Carmella +Carmella's +Carmelo +Carmelo's +Carmen +Carmen's +Carmichael +Carmichael's +Carmine +Carmine's +Carnap +Carnap's +Carnation +Carnation's +Carnegie +Carnegie's +Carney +Carney's +Carnot +Carnot's +Carol +Carol's +Carole +Carole's +Carolina +Carolina's +Caroline +Caroline's +Carolingian +Carolingian's +Carolinian +Carolinian's +Carolyn +Carolyn's +Carpathian +Carpathians +Carpathian's +Carpathians +Carpathians's +Carpenter +Carpenter's +Carr +Carr's +Carranza +Carranza's +Carrie +Carrier +Carrie's +Carrier +Carrier's +Carrillo +Carrillo's +Carroll +Carroll's +Carson +Carson's +Carter +Carter's +Cartesian +Cartesian's +Carthage +Carthage's +Carthaginian +Carthaginians +Carthaginian's +Cartier +Cartier's +Cartwright +Cartwright's +Caruso +Caruso's +Carver +Carver's +Cary +Cary's +Casablanca +Casablanca's +Casals +Casals's +Casandra +Casandra's +Casanova +Casanovas +Casanova's +Cascades +Cascades's +Case +Case's +Casey +Casey's +Cash +Cash's +Casio +Casio's +Caspar +Caspar's +Caspian +Caspian's +Cassandra +Cassandras +Cassandra's +Cassatt +Cassatt's +Cassidy +Cassidy's +Cassie +Cassie's +Cassiopeia +Cassiopeia's +Cassius +Cassius's +Castaneda +Castaneda's +Castilian +Castillo +Castillo's +Castlereagh +Castlereagh's +Castor +Castor's +Castries +Castries's +Castro +Castro's +Catalan +Catalans +Catalan's +Catalina +Catalina's +Catalonia +Catalonia's +Catawba +Catawba's +Caterpillar +Caterpillar's +Cathay +Cathay's +Cather +Cather's +Catherine +Catherine's +Cathleen +Cathleen's +Catholic +Catholics +Catholic's +Catholicism +Catholicisms +Catholicism's +Cathryn +Cathryn's +Cathy +Cathy's +Catiline +Catiline's +Cato +Cato's +Catskill +Catskills +Catskill's +Catskills +Catskills's +Catt +Catt's +Catullus +Catullus's +Caucasian +Caucasians +Caucasian's +Caucasoid +Caucasus +Caucasus's +Cauchy +Cauchy's +Cavendish +Cavendish's +Cavour +Cavour's +Caxton +Caxton's +Cayenne +Cayenne's +Cayman +Cayman's +Cayuga +Cayugas +Cayuga's +Cayuse +Cb +Cd +Cd's +Ce +Ce's +Ceausescu +Ceausescu's +Cebu +Cebu's +Cebuano +Cebuano's +Cecelia +Cecelia's +Cecil +Cecil's +Cecile +Cecile's +Cecilia +Cecilia's +Cecily +Cecily's +Cedric +Cedric's +Celeste +Celeste's +Celgene +Celgene's +Celia +Celia's +Celina +Celina's +Cellini +Cellini's +Celsius +Celsius's +Celt +Celts +Celt's +Celtic +Celtics +Celtic's +Cenozoic +Cenozoic's +Centaurus +Centaurus's +Centigrade +Central +Cepheid +Cepheid's +Cepheus +Cepheus's +Cerberus +Cerberus's +Cerenkov +Cerenkov's +Ceres +Ceres's +Cerf +Cerf's +Cervantes +Cervantes's +Cesar +Cesar's +Cesarean +Cesarean's +Cessna +Cessna's +Cetus +Cetus's +Ceylon +Ceylon's +Ceylonese +Cezanne +Cezanne's +Cf +Cf's +Ch'in +Ch'in's +Ch +Chen +Chablis +Chablis's +Chad +Chad's +Chadian +Chadians +Chadian's +Chadwick +Chadwick's +Chagall +Chagall's +Chaitanya +Chaitanya's +Chaitin +Chaitin's +Chaldea +Chaldean +Chaldean's +Challenger +Challenger's +Chalmers +Chamberlain +Chamberlain's +Chambers +Chambers's +Champlain +Champlain's +Champollion +Champollion's +Chan +Chan's +Chance +Chance's +Chancellorsville +Chancellorsville's +Chandigarh +Chandigarh's +Chandler +Chandler's +Chandon +Chandon's +Chandra +Chandra's +Chandragupta +Chandragupta's +Chandrasekhar +Chandrasekhar's +Chanel +Chanel's +Chaney +Chaney's +Chang +Chang's +Changchun +Changchun's +Changsha +Changsha's +Chantilly +Chantilly's +Chaplin +Chaplin's +Chaplinesque +Chapman +Chapman's +Chappaquiddick +Chappaquiddick's +Chapultepec +Chapultepec's +Charbray +Charbray's +Chardonnay +Chardonnay's +Charity +Charity's +Charlemagne +Charlemagne's +Charlene +Charlene's +Charles +Charles's +Charleston +Charlestons +Charleston's +Charley +Charley's +Charlie +Charlie's +Charlotte +Charlotte's +Charlottetown +Charlottetown's +Charmaine +Charmaine's +Charmin +Charmin's +Charolais +Charolais's +Charon +Charon's +Chartism +Chartism's +Chartres +Chartres's +Charybdis +Charybdis's +Chase +Chase's +Chasity +Chasity's +Chateaubriand +Chateaubriand's +Chattahoochee +Chattahoochee's +Chattanooga +Chattanooga's +Chatterley +Chatterley's +Chatterton +Chatterton's +Chaucer +Chaucer's +Chauncey +Chauncey's +Chautauqua +Chautauqua's +Chavez +Chavez's +Chayefsky +Chayefsky's +Che +Che's +Chechen +Chechen's +Chechnya +Chechnya's +Cheddar +Cheddar's +Cheer +Cheer's +Cheerios +Cheerios's +Cheetos +Cheetos's +Cheever +Cheever's +Chekhov +Chekhov's +Chekhovian +Chelsea +Chelsea's +Chelyabinsk +Chelyabinsk's +Chen +Chen's +Cheney +Cheney's +Chengdu +Chengdu's +Chennai +Chennai's +Cheops +Cheops's +Cheri +Cheri's +Cherie +Cherie's +Chernenko +Chernenko's +Chernobyl +Chernobyl's +Chernomyrdin +Chernomyrdin's +Cherokee +Cherokees +Cherokee's +Cherry +Cherry's +Cheryl +Cheryl's +Chesapeake +Chesapeake's +Cheshire +Cheshire's +Chester +Chester's +Chesterfield +Chesterfield's +Chesterton +Chesterton's +Chevalier +Chevalier's +Cheviot +Cheviot's +Chevrolet +Chevrolet's +Chevron +Chevron's +Chevy +Chevy's +Cheyenne +Cheyennes +Cheyenne's +Chi +Chi's +Chianti +Chiantis +Chianti's +Chiba +Chiba's +Chibcha +Chibcha's +Chicago +Chicago's +Chicagoan +Chicagoan's +Chicana +Chicana's +Chicano +Chicano's +Chickasaw +Chickasaws +Chickasaw's +Chiclets +Chiclets's +Chihuahua +Chihuahuas +Chihuahua's +Chile +Chile's +Chilean +Chileans +Chilean's +Chimborazo +Chimborazo's +Chimera +Chimeras +Chimera's +Chimu +Chimu's +Chin +Chin's +China +China's +Chinatown +Chinatown's +Chinese +Chinese's +Chinook +Chinooks +Chinook's +Chipewyan +Chipewyan's +Chippendale +Chippendale's +Chippewa +Chippewas +Chippewa's +Chiquita +Chiquita's +Chirico +Chirico's +Chisholm +Chisholm's +Chisinau +Chisinau's +Chittagong +Chittagong's +Chivas +Chivas's +Chloe +Chloe's +Choctaw +Choctaws +Choctaw's +Chomsky +Chomsky's +Chongqing +Chongqing's +Chopin +Chopin's +Chopra +Chopra's +Chou +Chou's +Chretien +Chretien's +Chris +Chris's +Christ +Christs +Christ's +Christa +Christa's +Christchurch +Christchurch's +Christendom +Christendoms +Christendom's +Christensen +Christensen's +Christi +Christi's +Christian +Christians +Christian's +Christianity +Christianities +Christianity's +Christianize +Christie +Christie's +Christina +Christina's +Christine +Christine's +Christlike +Christmas +Christmases +Christmas's +Christmastide +Christmastides +Christmastide's +Christmastime +Christmastimes +Christmastime's +Christoper +Christoper's +Christopher +Christopher's +Chromebook +Chromebooks +Chromebook's +Chronicles +Chrysler +Chrysler's +Chrysostom +Chrysostom's +Chrystal +Chrystal's +Chuck +Chuck's +Chukchi +Chukchi's +Chumash +Chumash's +Chung +Chung's +Church +Church's +Churchill +Churchill's +Churriguera +Churriguera's +Chuvash +Chuvash's +Ci +Ci's +Cicero +Cicero's +Cid +Cid's +Cimabue +Cimabue's +Cincinnati +Cincinnati's +Cinderella +Cinderellas +Cinderella's +Cindy +Cindy's +CinemaScope +CinemaScope's +Cinerama +Cinerama's +Cipro +Cipro's +Circe +Circe's +Cisco +Cisco's +Citibank +Citibank's +Citigroup +Citigroup's +Citroen +Citroen's +Cl +Clive +Cl's +Claiborne +Claiborne's +Clair +Clair's +Claire +Claire's +Clairol +Clairol's +Clancy +Clancy's +Clapeyron +Clapeyron's +Clapton +Clapton's +Clara +Clara's +Clare +Clare's +Clarence +Clarence's +Clarendon +Clarendon's +Clarice +Clarice's +Clarissa +Clarissa's +Clark +Clark's +Clarke +Clarke's +Claude +Claude's +Claudette +Claudette's +Claudia +Claudia's +Claudine +Claudine's +Claudio +Claudio's +Claudius +Claudius's +Claus +Claus's +Clausewitz +Clausewitz's +Clausius +Clausius's +Clay +Clay's +Clayton +Clayton's +Clearasil +Clearasil's +Clem +Clemens +Clem's +Clemenceau +Clemenceau's +Clemens +Clemens's +Clement +Clements +Clement's +Clementine +Clementine's +Clements +Clements's +Clemons +Clemons's +Clemson +Clemson's +Cleo +Cleo's +Cleopatra +Cleopatra's +Cleveland +Cleveland's +Cliburn +Cliburn's +Cliff +Cliff's +Clifford +Clifford's +Clifton +Clifton's +Cline +Cline's +Clint +Clint's +Clinton +Clinton's +Clio +Clio's +Clive +Clive's +Clojure +Clojure's +Clorets +Clorets's +Clorox +Clorox's +Closure +Closure's +Clotho +Clotho's +Clouseau +Clouseau's +Clovis +Clovis's +Clyde +Clyde's +Clydesdale +Clydesdale's +Clytemnestra +Clytemnestra's +Cm +Cm's +Cmdr +Co +Co's +Cobain +Cobain's +Cobb +Cobb's +Cochabamba +Cochabamba's +Cochin +Cochin's +Cochise +Cochise's +Cochran +Cochran's +Cockney +Cockney's +Cocteau +Cocteau's +Cod +Cody +Cody's +Coffey +Coffey's +Cognac +Cognac's +Cohan +Cohan's +Cohen +Cohen's +Coimbatore +Coimbatore's +Cointreau +Cointreau's +Coke +Cokes +Coke's +Col +Col's +Colbert +Colbert's +Colby +Colby's +Cole +Cole's +Coleen +Coleen's +Coleman +Coleman's +Coleridge +Coleridge's +Colette +Colette's +Colfax +Colfax's +Colgate +Colgate's +Colin +Colin's +Colleen +Colleen's +Collier +Collier's +Collin +Collins +Collin's +Collins +Collins's +Colo +Cologne +Cologne's +Colombia +Colombia's +Colombian +Colombians +Colombian's +Colombo +Colombo's +Colon +Colon's +Coloradan +Coloradans +Coloradan's +Colorado +Colorado's +Coloradoan +Colosseum +Colosseum's +Colt +Colt's +Coltrane +Coltrane's +Columbia +Columbia's +Columbine +Columbine's +Columbus +Columbus's +Com +Comanche +Comanches +Comanche's +Combs +Combs's +Comdr +Comintern +Comintern's +Commandment +Commons +Commons's +Commonwealth +Communion +Communions +Communion's +Communism +Communist +Communists +Communist's +Como +Como's +Comoran +Comoros +Comoros's +Compaq +Compaq's +Compton +Compton's +CompuServe +CompuServe's +Comte +Comte's +Conakry +Conakry's +Conan +Conan's +Concepcion +Concepcion's +Concetta +Concetta's +Concord +Concords +Concord's +Concorde +Concorde's +Condillac +Condillac's +Condorcet +Condorcet's +Conestoga +Conestoga's +Confederacy +Confederacy's +Confederate +Confederates +Confederate's +Confucian +Confucians +Confucian's +Confucianism +Confucianisms +Confucianism's +Confucius +Confucius's +Cong +Cong's +Congo +Congo's +Congolese +Congolese's +Congregational +Congregationalist +Congregationalists +Congregationalist's +Congress +Congresses +Congress's +Congressional +Congreve +Congreve's +Conley +Conley's +Conn +Conner +Conn's +Connecticut +Connecticut's +Connemara +Connemara's +Conner +Conner's +Connery +Connery's +Connie +Connie's +Connolly +Connolly's +Connors +Connors's +Conrad +Conrad's +Conrail +Conrail's +Conservative +Constable +Constable's +Constance +Constance's +Constantine +Constantine's +Constantinople +Constantinople's +Constitution +Consuelo +Consuelo's +Continent +Continent's +Continental +Continental's +Contreras +Contreras's +Conway +Conway's +Cook +Cook's +Cooke +Cooke's +Cooley +Cooley's +Coolidge +Coolidge's +Cooper +Cooper's +Cooperstown +Cooperstown's +Coors +Coors's +Copacabana +Copacabana's +Copeland +Copeland's +Copenhagen +Copenhagen's +Copernican +Copernican's +Copernicus +Copernicus's +Copland +Copland's +Copley +Copley's +Copperfield +Copperfield's +Coppertone +Coppertone's +Coppola +Coppola's +Coptic +Coptic's +Cora +Cora's +Cordelia +Cordelia's +Cordilleras +Cordilleras's +Cordoba +Cordoba's +Corey +Corey's +Corfu +Corfu's +Corina +Corina's +Corine +Corine's +Corinne +Corinne's +Corinth +Corinth's +Corinthian +Corinthians +Corinthian's +Corinthians +Corinthians's +Coriolanus +Coriolanus's +Coriolis +Coriolis's +Cork +Corleone +Corleone's +Cormack +Cormack's +Corneille +Corneille's +Cornelia +Cornelia's +Cornelius +Cornelius's +Cornell +Cornell's +Corning +Corning's +Cornish +Cornishes +Cornish's +Cornwall +Cornwall's +Cornwallis +Cornwallis's +Coronado +Coronado's +Corot +Corot's +Corp +Correggio +Correggio's +Corrine +Corrine's +Corsica +Corsica's +Corsican +Corsican's +Cortes +Corteses +Cortes's +Cortland +Cortland's +Corvallis +Corvallis's +Corvette +Corvette's +Corvus +Corvus's +Cory +Cory's +Cosby +Cosby's +CosmosDB +CosmosDB's +Cossack +Cossack's +Costco +Costco's +Costello +Costello's +Costner +Costner's +Cote +Cote's +Cotonou +Cotonou's +Cotopaxi +Cotopaxi's +Cotswold +Cotswold's +Cotton +Cotton's +Coulomb +Coulomb's +Coulter +Coulter's +Couperin +Couperin's +Courbet +Courbet's +Courtney +Courtney's +Cousteau +Cousteau's +Coventry +Coventries +Coventry's +Coward +Coward's +Cowell +Cowell's +Cowley +Cowley's +Cowper +Cowper's +Cox +Cox's +Coy +Coy's +Coyle +Coyle's +Cozumel +Cozumel's +Cpl +Cr +Crest +Cr's +Crabbe +Crabbe's +Craft +Craft's +Craig +Craig's +Cranach +Cranach's +Crane +Crane's +Cranmer +Cranmer's +Crater +Crater's +Crawford +Crawford's +Cray +Cray's +Crayola +Crayola's +Creation +Creation's +Creator +Creator's +Crecy +Crecy's +Cree +Creed +Crees +Cree's +Creek +Creeks +Creek's +Creighton +Creighton's +Creole +Creoles +Creole's +Creon +Creon's +Cressida +Cressida's +Crest +Crest's +Cretaceous +Cretaceous's +Cretan +Cretans +Cretan's +Crete +Crete's +Crichton +Crichton's +Crick +Crick's +Crimea +Crimea's +Crimean +Crimean's +Criollo +Criollo's +Crisco +Crisco's +Cristina +Cristina's +Croat +Croats +Croat's +Croatia +Croatia's +Croatian +Croatians +Croatian's +Croce +Croce's +Crockett +Crockett's +Croesus +Croesus's +Cromwell +Cromwell's +Cromwellian +Cromwellian's +Cronin +Cronin's +Cronkite +Cronkite's +Cronus +Cronus's +Crookes +Crookes's +Crosby +Crosby's +Cross +Cross's +Crow +Crows +Crow's +Crowley +Crowley's +Crucifixion +Crucifixions +Crucifixion's +Cruikshank +Cruikshank's +Cruise +Cruise's +Crusades's +Crusoe +Crusoe's +Crux +Crux's +Cruz +Cruz's +Cryptozoic +Cryptozoic's +Crystal +Crystal's +Csonka +Csonka's +Ct +Ctesiphon +Ctesiphon's +Cthulhu +Cthulhu's +Cu +Cu's +Cuba +Cuba's +Cuban +Cubans +Cuban's +Cuchulain +Cuchulain's +Cuisinart +Cuisinart's +Culbertson +Culbertson's +Cullen +Cullen's +Cumberland +Cumberland's +Cummings +Cummings's +Cunard +Cunard's +Cunningham +Cunningham's +Cupid +Cupid's +Curacao +Curacao's +Curie +Curie's +Curitiba +Curitiba's +Currier +Currier's +Curry +Currier +Curry's +Curt +Curt's +Curtis +Curtis's +Custer +Custer's +Cuvier +Cuvier's +Cuzco +Cuzco's +Cybele +Cybele's +Cyclades +Cyclades's +Cyclopes +Cyclopes's +Cyclops +Cyclops's +Cygnus +Cygnus's +Cymbeline +Cymbeline's +Cynthia +Cynthia's +Cyprian +Cyprian's +Cypriot +Cypriots +Cypriot's +Cyprus +Cyprus's +Cyrano +Cyrano's +Cyril +Cyril's +Cyrillic +Cyrillic's +Cyrus +Cyrus's +Czech +Czech's +Czechia +Czechia's +Czechoslovak +Czechoslovakia +Czechoslovakia's +Czechoslovakian +Czechoslovakians +Czechoslovakian's +Czechs +Czerny +Czerny's +D +D's +DA +DA's +DAR +DAT +DAT's +DBMS +DBMS's +DC +DC's +DD +DD's +DDS +DDS's +DDT +DDTs +DE +DEA +DEC +DECed +DECs +DH +DHS +DI +DJ +DMCA +DMD +DMD's +DMZ +DNA +DNA's +DOA +DOB +DOD +DOE +DOS +DOS's +DOT +DP +DPs +DP's +DPT +DST +DTP +DUI +DVD +DVDs +DVR +DVRs +DVR's +DWI +Dachau +Dachau's +Dacron +Dacrons +Dacron's +Dada +Dada's +Dadaism +Dadaism's +Daedalus +Daedalus's +Daguerre +Daguerre's +Dagwood +Dagwood's +Dahomey +Dahomey's +Daimler +Daimler's +Daisy +Daisy's +Dakar +Dakar's +Dakota +Dakotas +Dakota's +Dakotan +Dakotan's +Dalai +Dale +Dale's +Daley +Daley's +Dali +Dali's +Dalian +Dalian's +Dallas +Dallas's +Dalmatia +Dalmatia's +Dalmatian +Dalmatians +Dalmatian's +Dalton +Dalton's +Damascus +Damascus's +Dame +Damion +Dame's +Damian +Damian's +Damien +Damien's +Damion +Damion's +Damocles +Damocles's +Damon +Damon's +Dan +Dan's +Dana +Dana's +Danae +Danae's +Dane +Danes +Dane's +Danelaw +Danelaw's +Dangerfield +Dangerfield's +Danial +Danial's +Daniel +Daniels +Daniel's +Danielle +Danielle's +Daniels +Daniels's +Danish +Danish's +Dannie +Dannie's +Danny +Danny's +Danone +Danone's +Dante +Dante's +Danton +Danton's +Danube +Danube's +Danubian +Danubian's +Daphne +Daphne's +Darby +Darby's +Darcy +Darcy's +Dardanelles +Dardanelles's +Dare +Dare's +Daren +Daren's +Darfur +Darfur's +Darin +Darin's +Dario +Dario's +Darius +Darius's +Darjeeling +Darjeeling's +Darla +Darla's +Darlene +Darlene's +Darling +Darling's +Darnell +Darnell's +Darrel +Darrel's +Darrell +Darrell's +Darren +Darren's +Darrin +Darrin's +Darrow +Darrow's +Darryl +Darryl's +Darth +Darth's +Dartmoor +Dartmoor's +Dartmouth +Dartmouth's +Darvon +Darvon's +Darwin +Darwin's +Darwinian +Darwinian's +Darwinism +Darwinisms +Darwinism's +Darwinist +Daryl +Daryl's +Datamation +Daugherty +Daugherty's +Daumier +Daumier's +Davao +Davao's +Dave +Dave's +Davenport +Davenport's +David +Davids +David's +Davidson +Davidson's +Davies +Davies's +Davis +Davis's +Davy +Davies +Davy's +Dawes +Dawes's +Dawkins +Dawn +Dawn's +Dawson +Dawson's +Day +Day's +Dayan +Dayton +Dayton's +DeGeneres +DeGeneres's +Deadhead +Deadhead's +Dean +Dean's +Deana +Deana's +Deandre +Deandre's +Deann +Deann's +Deanna +Deanna's +Deanne +Deanne's +Death +Death's +Debbie +Debbie's +Debby +Debby's +Debian +Debian's +Debora +Debora's +Deborah +Deborah's +Debouillet +Debouillet's +Debra +Debra's +Debs +Debs's +Debussy +Debussy's +Dec +Dec's +Decalogue +Decalogue's +Decatur +Decatur's +Decca +Decca's +Deccan +Deccan's +December +Decembers +December's +Decker +Decker's +Dedekind +Dedekind's +Dee +Dee's +Deena +Deena's +Deere +Deere's +Defoe +Defoe's +Degas +Degas's +Deidre +Deidre's +Deimos +Deimos's +Deirdre +Deirdre's +Deity +Dejesus +Dejesus's +Del +Del's +Delacroix +Delacroix's +Delacruz +Delacruz's +Delaney +Delaney's +Delano +Delano's +Delaware +Delawares +Delaware's +Delawarean +Delawareans +Delawarean's +Delbert +Delbert's +Deleon +Deleon's +Delgado +Delgado's +Delhi +Delhi's +Delia +Delia's +Delibes +Delibes's +Delicious +Delicious's +Delilah +Delilah's +Delilahs +Delius +Delius's +Dell +Dell's +Della +Della's +Delmar +Delmar's +Delmarva +Delmarva's +Delmer +Delmer's +Delmonico +Delmonico's +Delores +Delores's +Deloris +Deloris's +Delphi +Delphi's +Delphic +Delphic's +Delphinus +Delphinus's +Delta +Delta's +Dem +Deming +Demavend +Demavend's +Demerol +Demerol's +Demeter +Demeter's +Demetrius +Demetrius's +Deming +Deming's +Democrat +Democrats +Democrat's +Democratic +Democritus +Democritus's +Demosthenes +Demosthenes's +Dempsey +Dempsey's +Dena +Dena's +Denali +Deneb +Deneb's +Denebola +Denebola's +Deng +Deng's +Denis +Denis's +Denise +Denise's +Denmark +Denmark's +Dennis +Dennis's +Denny +Denny's +Denver +Denver's +Deon +Deon's +Depp +Depp's +Derby +Derby's +Derek +Derek's +Derick +Derick's +Dermot +Dermot's +Derrick +Derrick's +Derrida +Derrida's +Descartes +Descartes's +Desdemona +Desdemona's +Desiree +Desiree's +Desmond +Desmond's +Detroit +Detroit's +Deuteronomy +Deuteronomy's +Devanagari +Devanagari's +Devi +Devi's +Devin +Devin's +Devon +Devon's +Devonian +Devonian's +Dewar +Dewar's +Dewayne +Dewayne's +Dewey +Dewey's +Dewitt +Dewitt's +Dexedrine +Dexedrine's +Dexter +Dexter's +Dhaka +Dhaka's +Dhaulagiri +Dhaulagiri's +Di +Dis +Di's +DiCaprio +DiCaprio's +DiMaggio +DiMaggio's +Diaghilev +Diaghilev's +Dial +Dial's +Diana +Diana's +Diane +Diane's +Diann +Diann's +Dianna +Dianna's +Dianne +Dianne's +Dias +Diaspora +Diasporas +Diaspora's +Dick +Dickens +Dick's +Dickens +Dickens's +Dickensian +Dickerson +Dickerson's +Dickinson +Dickinson's +Dickson +Dickson's +Dictaphone +Dictaphones +Dictaphone's +Diderot +Diderot's +Dido +Dido's +Didrikson +Didrikson's +Diefenbaker +Diefenbaker's +Diego +Diego's +Diem +Diem's +Dietrich +Dietrich's +Dijkstra +Dijkstra's +Dijon +Dijon's +Dilbert +Dilberts +Dilbert's +Dillard +Dillard's +Dillinger +Dillinger's +Dillon +Dillon's +Dina +Dina's +Dinah +Dinah's +Dino +Dino's +Diocletian +Diocletian's +Diogenes +Diogenes's +Dion +Dion's +Dionne +Dionne's +Dionysian +Dionysian's +Dionysus +Dionysus's +Diophantine +Diophantine's +Dior +Dior's +Dipper +Dipper's +Dir +Dirac +Dirac's +Dirichlet +Dirichlet's +Dirk +Dirk's +Dis +Dis's +Disney +Disney's +Disneyland +Disneyland's +Disraeli +Disraeli's +Divine +Divine's +Diwali +Diwali's +Dix +Dix's +Dixie +Dixie's +Dixiecrat +Dixiecrat's +Dixieland +Dixielands +Dixieland's +Dixon +Dixon's +Django +Django's +Djibouti +Djibouti's +Dmitri +Dmitri's +Dnepropetrovsk +Dnepropetrovsk's +Dniester +Dniester's +Dobbin +Dobbin's +Doberman +Doberman's +Dobro +Dobro's +Doctor +Doctorow +Doctorow's +Dodge +Dodge's +Dodgson +Dodgson's +Dodoma +Dodoma's +Dodson +Dodson's +Doe +Doe's +Doha +Doha's +Dolby +Dolby's +Dole +Dole's +Dollie +Dollie's +Dolly +Dolly's +Dolores +Dolores's +Domesday +Domesday's +Domingo +Domingo's +Dominguez +Dominguez's +Dominic +Dominic's +Dominica +Dominica's +Dominican +Dominicans +Dominican's +Dominick +Dominick's +Dominion +Dominique +Dominique's +Domitian +Domitian's +Don +Dons +Don's +Dona +Dona's +Donahue +Donahue's +Donald +Donald's +Donaldson +Donaldson's +Donatello +Donatello's +Donetsk +Donetsk's +Donizetti +Donizetti's +Donn +Donner +Donn's +Donna +Donna's +Donne +Donne's +Donnell +Donnell's +Donner +Donner's +Donnie +Donnie's +Donny +Donny's +Donovan +Donovan's +Dooley +Dooley's +Doolittle +Doolittle's +Doonesbury +Doonesbury's +Doppler +Doppler's +Dora +Dora's +Dorcas +Dorcas's +Doreen +Doreen's +Dorian +Dorian's +Doric +Doric's +Doris +Doris's +Doritos +Doritos's +Dorothea +Dorothea's +Dorothy +Dorothy's +Dorset +Dorset's +Dorsey +Dorsey's +Dorthy +Dorthy's +Dortmund +Dortmund's +Dostoevsky +Dostoevsky's +Dot +Dot's +Dotson +Dotson's +Douala +Douala's +Douay +Douay's +Doubleday +Doubleday's +Doug +Doug's +Douglas +Douglas's +Douglass +Douglass's +Douro +Douro's +Dover +Dover's +Dow +Dow's +Downs +Downs's +Downy +Downy's +Doyle +Doyle's +Dr +Draco +Draco's +Draconian +Draconian's +Dracula +Dracula's +Drake +Drake's +Dramamine +Dramamines +Dramamine's +Drambuie +Drambuie's +Drano +Drano's +Dravidian +Dravidian's +Dreiser +Dreiser's +Dresden +Dresden's +Drew +Drew's +Dreyfus +Dreyfus's +Dristan +Dristan's +Dropbox +Dropbox's +Drudge +Drudge's +Drupal +Drupal's +Dryden +Dryden's +Dschubba +Dschubba's +Du +DuPont +DuPont's +Duane +Duane's +Dubai +Dubai's +Dubcek +Dubcek's +Dubhe +Dubhe's +Dublin +Dublin's +Dubrovnik +Dubrovnik's +Duchamp +Duchamp's +Dudley +Dudley's +Duffy +Duffy's +Duisburg +Duisburg's +Duke +Duke's +Dulles +Dulles's +Duluth +Duluth's +Dumas +Dumas's +Dumbledore +Dumbledore's +Dumbo +Dumbo's +Dumpster +Dumpster's +Dunant +Dunant's +Dunbar +Dunbar's +Duncan +Duncan's +Dundee +Dunedin +Dunedin's +Dunkirk +Dunkirk's +Dunlap +Dunlap's +Dunn +Dunn's +Dunne +Dunne's +Duracell +Duracell's +Duran +Duran's +Durant +Durant's +Durante +Durante's +Durban +Durban's +Durer +Durer's +Durex +Durex's +Durham +Durhams +Durham's +Durkheim +Durkheim's +Duroc +Duroc's +Durocher +Durocher's +Duse +Duse's +Dushanbe +Dushanbe's +Dusseldorf +Dusseldorf's +Dustbuster +Dustbuster's +Dustin +Dustin's +Dusty +Dusty's +Dutch +Dutch's +Dutchman +Dutchman's +Dutchmen +Dutchmen's +Dutchwoman +Duvalier +Duvalier's +Dvina +Dvina's +Dvorak +Dvorak's +Dwayne +Dwayne's +Dwight +Dwight's +Dy +Dy's +Dyer +Dyer's +Dylan +Dylan's +DynamoDB +DynamoDB's +Dyson +Dyson's +Dzerzhinsky +Dzerzhinsky's +Dzungaria +Dzungaria's +E +Es +E's +EC +ECG +ECG's +ECMAScript +ECMAScript's +EDP +EDP's +EDT +EEC +EEC's +EEG +EEG's +EEO +EEOC +EFL +EFT +EKG +EKG's +ELF +ELF's +EM +EMT +ENE +ENE's +EOE +EPA +EPA's +ER +ERA +ESE +ESE's +ESL +ESP +ESP's +ESPN +ESPN's +ESR +EST +EST's +ET +ETA +ETD +EU +EULA +EULAs +Eakins +Eakins's +Earhart +Earhart's +Earl +Earl's +Earle +Earle's +Earlene +Earlene's +Earline +Earline's +Earnest +Earnest's +Earnestine +Earnestine's +Earnhardt +Earnhardt's +Earp +Earp's +East +Easter +Easters +Easts +East's +Easter +Easter's +Eastern +Easterner +Eastman +Eastman's +Eastwood +Eastwood's +Eaton +Eaton's +Eben +Eben's +Ebeneezer +Ebeneezer's +Ebert +Ebert's +Ebola +Ebola's +Ebonics +Ebonics's +Ebony +Ebony's +Ebro +Ebro's +Ecclesiastes +Ecclesiastes's +Eco +Eco's +Ecstasy +Ecuador +Ecuador's +Ecuadoran +Ecuadorans +Ecuadoran's +Ecuadorean +Ecuadorian +Ecuadorians +Ecuadorian's +Ed +Eden +Edens +Ed's +Edam +Edams +Edam's +Edda +Edda's +Eddie +Eddie's +Eddington +Eddington's +Eddy +Eddy's +Eden +Eden's +Edgar +Edgar's +Edgardo +Edgardo's +Edinburgh +Edinburgh's +Edison +Edison's +Edith +Edith's +Edmond +Edmond's +Edmonton +Edmonton's +Edmund +Edmund's +Edna +Edna's +Edsel +Edsel's +Eduardo +Eduardo's +Edward +Edwards +Edward's +Edwardian +Edwardian's +Edwardo +Edwardo's +Edwards +Edwards's +Edwin +Edwin's +Edwina +Edwina's +Eeyore +Eeyore's +Effie +Effie's +Efrain +Efrain's +Efren +Efren's +Eggo +Eggo's +Egypt +Egypt's +Egyptian +Egyptians +Egyptian's +Egyptology +Egyptology's +Ehrenberg +Ehrenberg's +Ehrlich +Ehrlich's +Eichmann +Eichmann's +Eiffel +Eiffel's +Eileen +Eileen's +Einstein +Einsteins +Einstein's +Eire +Eire's +Eisenhower +Eisenhower's +Eisenstein +Eisenstein's +Eisner +Eisner's +Elaine +Elaine's +Elam +Elam's +Elanor +Elanor's +Elasticsearch +Elasticsearch's +Elastoplast +Elastoplast's +Elba +Elba's +Elbe +Elbe's +Elbert +Elbert's +Elbrus +Elbrus's +Eldon +Eldon's +Eleanor +Eleanor's +Eleazar +Eleazar's +Electra +Electra's +Elena +Elena's +Elgar +Elgar's +Eli +Eli's +Elias +Elias's +Elijah +Elijah's +Elinor +Elinor's +Eliot +Eliot's +Elisa +Elisa's +Elisabeth +Elisabeth's +Elise +Elise's +Eliseo +Eliseo's +Elisha +Elisha's +Eliza +Eliza's +Elizabeth +Elizabeth's +Elizabethan +Elizabethans +Elizabethan's +Ella +Ella's +Ellen +Ellen's +Ellesmere +Ellesmere's +Ellie +Ellie's +Ellington +Ellington's +Elliot +Elliot's +Elliott +Elliott's +Ellis +Ellis's +Ellison +Ellison's +Elma +Elma's +Elmer +Elmer's +Elmo +Elmo's +Elnath +Elnath's +Elnora +Elnora's +Elohim +Elohim's +Eloise +Eloise's +Eloy +Eloy's +Elroy +Elroy's +Elsa +Elsa's +Elsie +Elsie's +Elsinore +Elsinore's +Eltanin +Eltanin's +Elton +Elton's +Elul +Elul's +Elva +Elva's +Elvia +Elvia's +Elvin +Elvin's +Elvira +Elvira's +Elvis +Elvis's +Elway +Elway's +Elwood +Elwood's +Elysee +Elysee's +Elysian +Elysian's +Elysium +Elysiums +Elysium's +Emacs +Emacs's +Emanuel +Emanuel's +Emerson +Emerson's +Emery +Emery's +Emil +Emil's +Emile +Emile's +Emilia +Emilia's +Emilio +Emilio's +Emily +Emily's +Eminem +Eminem's +Eminence +Emma +Emma's +Emmanuel +Emmanuel's +Emmett +Emmett's +Emmy +Emmy's +Emory +Emory's +Encarta +Encarta's +Endymion +Endymion's +Eng +Eng's +Engels +Engels's +England +England's +English +Englisher +Englishes +English's +Englishman +Englishman's +Englishmen +Englishmen's +Englishwoman +Englishwoman's +Englishwomen +Englishwomen's +Enid +Enid's +Enif +Enif's +Eniwetok +Eniwetok's +Enkidu +Enkidu's +Enoch +Enoch's +Enos +Enos's +Enrico +Enrico's +Enrique +Enrique's +Enron +Enron's +Enterprise +Enterprise's +Eocene +Eocene's +Epcot +Epcot's +Ephesian +Ephesians +Ephesian's +Ephesus +Ephesus's +Ephraim +Ephraim's +Epictetus +Epictetus's +Epicurean +Epicurean's +Epicurus +Epicurus's +Epimethius +Epimethius's +Epiphany +Epiphanies +Epiphany's +Episcopal +Episcopalian +Episcopalians +Episcopalian's +Epistle +Epsom +Epsom's +Epson +Epson's +Epstein +Epstein's +Equuleus +Equuleus's +Er +Er's +Erasmus +Erasmus's +Erato +Erato's +Eratosthenes +Eratosthenes's +Erebus +Erebus's +Erector +Erector's +Erewhon +Erewhon's +Erhard +Erhard's +Eric +Eric's +Erica +Erica's +Erich +Erich's +Erick +Erick's +Ericka +Ericka's +Erickson +Erickson's +Eridanus +Eridanus's +Erie +Erie's +Erik +Erik's +Erika +Erika's +Erin +Erin's +Eris +Erises +Eris's +Eritrea +Eritrea's +Eritrean +Eritreans +Eritrean's +Erlang +Erlang's +Erlenmeyer +Erlenmeyer's +Erma +Erma's +Erna +Erna's +Ernest +Ernest's +Ernestine +Ernestine's +Ernesto +Ernesto's +Ernie +Ernie's +Ernst +Ernst's +Eros +Eroses +Eros's +Errol +Errol's +Erse +Erse's +ErvIn +ErvIn's +Erwin +Erwin's +Esau +Esau's +Escher +Escher's +Escherichia +Escherichia's +Escondido +Eskimo +Eskimos +Eskimo's +Esmeralda +Esmeralda's +Esperanto +Esperanto's +Esperanza +Esperanza's +Espinoza +Espinoza's +Esq +Esq's +Esquire +Esquires +Esquire's +Essen +Essen's +Essene +Essene's +Essequibo +Essequibo's +Essex +Essex's +Essie +Essie's +Establishment +Esteban +Esteban's +Estela +Estela's +Estella +Estella's +Estelle +Estelle's +Ester +Ester's +Esterhazy +Esterhazy's +Estes +Estes's +Esther +Esther's +Estonia +Estonia's +Estonian +Estonians +Estonian's +Estrada +Estrada's +Ethan +Ethan's +Ethel +Ethel's +Ethelred +Ethelred's +Ethernet +Ethernet's +Ethiopia +Ethiopia's +Ethiopian +Ethiopians +Ethiopian's +Etna +Etna's +Eton +Eton's +Etruria +Etruria's +Etruscan +Etruscan's +Etta +Etta's +Eu +Eu's +Eucharist +Eucharists +Eucharist's +Eucharistic +Euclid +Euclid's +Eugene +Eugene's +Eugenia +Eugenia's +Eugenie +Eugenie's +Eugenio +Eugenio's +Eula +Eula's +Euler +Euler's +Eumenides +Eumenides's +Eunice +Eunice's +Euphrates +Euphrates's +Eur +Eurasia +Eurasia's +Eurasian +Eurasians +Eurasian's +Euripides +Euripides's +Eurodollar +Eurodollars +Eurodollar's +Europa +Europa's +Europe +Europe's +European +Europeans +European's +Eurydice +Eurydice's +Eustachian +Eustachian's +Euterpe +Euterpe's +Eva +Eva's +Evan +Evans +Evan's +Evangelical +Evangelina +Evangelina's +Evangeline +Evangeline's +Evangelist +Evangelist's +Evans +Evans's +Evansville +Evansville's +Eve +Eve's +Evelyn +Evelyn's +Evenki +Evenki's +EverReady +EverReady's +Everest +Everest's +Everett +Everett's +Everette +Everette's +Everglades +Everglades's +Evert +Evert's +Evian +Evian's +Evita +Evita's +Ewing +Ewing's +Excalibur +Excalibur's +Excedrin +Excedrin's +Excellency +Excellencies +Excellency's +Exchequer +Exercycle +Exercycle's +Exocet +Exocet's +Exodus +Exodus's +Exxon +Exxon's +Eyck +Eyck's +Eyre +Eyre's +Eysenck +Eysenck's +Ezekiel +Ezekiel's +Ezra +Ezra's +F +Fed +F's +FAA +FAQ +FAQs +FAQ's +FBI +FBI's +FCC +FD +FDA +FDIC +FDIC's +FDR +FDR's +FHA +FHA's +FICA +FICA's +FIFO +FL +FM +FMs +FM's +FNMA +FNMA's +FOFL +FORTRAN +FORTRAN's +FPO +FSF +FSF's +FSLIC +FTC +FUD +FUDs +FWD +FWIW +FY +FYI +Faberge +Faberge's +Fabian +Fabians +Fabian's +Facebook +Facebook's +Faeroe +Faeroe's +Fafnir +Fafnir's +Fagin +Fagin's +Fahd +Fahd's +Fahrenheit +Fahrenheit's +Fairbanks +Fairbanks's +Faisal +Faisal's +Faisalabad +Faisalabad's +Faith +Faith's +Falasha +Falasha's +Falkland +Falklands +Falkland's +Falklands +Falklands's +Fallopian +Fallopian's +Falstaff +Falstaff's +Falwell +Falwell's +Fannie +Fannie's +Fanny +Fanny's +Faraday +Faraday's +Fargo +Fargo's +Farley +Farley's +Farmer +Farmer's +Farragut +Farragut's +Farrakhan +Farrakhan's +Farrell +Farrell's +Farrow +Farrow's +Farsi +Farsi's +Fassbinder +Fassbinder's +Fatah +Fatah's +Fates +Fates's +Father +Fathers +Father's +Fatima +Fatima's +Fatimid +Fatimid's +Faulkner +Faulkner's +Faulknerian +Faulknerian's +Fauntleroy +Fauntleroy's +Faust +Faust's +Faustian +Faustian's +Faustino +Faustino's +Faustus +Faustus's +Fawkes +Fawkes's +Fay +Fay's +Faye +Faye's +Fe +Fe's +Feb +Feb's +February +Februaries +February's +Fed +Feds +Fed's +FedEx +FedEx's +Federal +Federals +Federal's +Federalist +Federalist's +Federico +Federico's +Feds +Feds's +Felecia +Felecia's +Felice +Felice's +Felicia +Felicia's +Felicity +Felicity's +Felipe +Felipe's +Felix +Felix's +Fellini +Fellini's +Fenian +Fenian's +Ferber +Ferber's +Ferdinand +Ferdinand's +Fergus +Fergus's +Ferguson +Ferguson's +Ferlinghetti +Ferlinghetti's +Fermat +Fermat's +Fermi +Fermi's +Fern +Fern's +Fernandez +Fernandez's +Fernando +Fernando's +Ferrari +Ferrari's +Ferraro +Ferraro's +Ferrell +Ferrell's +Ferris +Ferris's +Feynman +Feynman's +Fez +Fez's +Fiat +Fiat's +Fiberglas +Fiberglas's +Fibonacci +Fibonacci's +Fichte +Fichte's +Fidel +Fidel's +Fido +Fido's +Fielding +Fielding's +Fields +Fields's +Figaro +Figaro's +Figueroa +Figueroa's +Fiji +Fiji's +Fijian +Fijians +Fijian's +Filipino +Filipinos +Filipino's +Fillmore +Fillmore's +Filofax +Filofax's +Finch +Finch's +Finland +Finland's +Finlay +Finlay's +Finley +Finley's +Finn +Finns +Finn's +Finnbogadottir +Finnbogadottir's +Finnegan +Finnegan's +Finnish +Finnish's +Fiona +Fiona's +Firebase +Firebase's +Firefox +Firefox's +Firestone +Firestone's +Fischer +Fischer's +Fisher +Fisher's +Fisk +Fisk's +Fitch +Fitch's +Fitzgerald +Fitzgerald's +Fitzpatrick +Fitzpatrick's +Fitzroy +Fitzroy's +Fizeau +Fizeau's +Fla +Flanagan +Flanagan's +Flanders +Flanders's +Flathead +Flatt +Flatt's +Flaubert +Flaubert's +Fleischer +Fleischer's +Fleming +Fleming's +Flemish +Flemish's +Fletcher +Fletcher's +Flint +Flint's +Flintstones +Flintstones's +Flo +Flo's +Flora +Flora's +Florence +Florence's +Florentine +Florentine's +Flores +Flores's +Florida +Florida's +Floridan +Floridan's +Floridian +Floridians +Floridian's +Florine +Florine's +Florsheim +Florsheim's +Flory +Flory's +Flossie +Flossie's +Flowers +Flowers's +Floyd +Floyd's +Flynn +Flynn's +Fm +Fm's +Foch +Foch's +Fokker +Fokker's +Foley +Foley's +Folgers +Folgers's +Folsom +Folsom's +Fomalhaut +Fomalhaut's +Fonda +Fonda's +Foosball +Foosball's +Forbes +Forbes's +Ford +Ford's +Foreman +Foreman's +Forest +Forester +Forest's +Forester +Forester's +Formica +Formicas +Formica's +Formosa +Formosa's +Formosan +Formosan's +Forrest +Forrest's +Forster +Forster's +Fortaleza +Fortaleza's +Fosse +Fosse's +Foster +Foster's +Fotomat +Fotomat's +Foucault +Foucault's +Fourier +Fourier's +Fourneyron +Fourneyron's +Fourth +Fowler +Fowler's +Fox +Foxes +Fox's +Fr +Fred +Fr's +Fragonard +Fragonard's +Fran +Fran's +France +Frances +France's +Frances +Frances's +Francesca +Francesca's +Francine +Francine's +Francis +Francis's +Francisca +Francisca's +Franciscan +Franciscans +Franciscan's +Francisco +Francisco's +Franck +Franck's +Franco +Franco's +Francois +Francois's +Francoise +Francoise's +Francophile +Franglais +Franglais's +Frank +Franks +Frank's +Frankel +Frankel's +Frankenstein +Frankenstein's +Frankfort +Frankfort's +Frankfurt +Frankfurter +Frankfurt's +Frankfurter +Frankfurter's +Frankie +Frankie's +Frankish +Franklin +Franklin's +Franks +Franks's +Franny +Franny's +Franz +Franz's +Fraser +Fraser's +Frau +Frauen +Frau's +Fraulein +Frazier +Frazier's +Fred +Fred's +Freda +Freda's +Freddie +Freddie's +Freddy +Freddy's +Frederic +Frederic's +Frederick +Frederick's +Fredericton +Fredericton's +Fredric +Fredric's +Fredrick +Fredrick's +Freeman +Freeman's +Freemason +Freemasons +Freemason's +Freemasonry +Freemasonries +Freemasonry's +Freetown +Freetown's +Freida +Freida's +Fremont +Fremont's +French +Frenches +French's +Frenchman +Frenchman's +Frenchmen +Frenchmen's +Frenchwoman +Frenchwoman's +Frenchwomen +Frenchwomen's +Freon +Freon's +Fresnel +Fresnel's +Fresno +Fresno's +Freud +Freud's +Freudian +Freudian's +Frey +Frey's +Freya +Freya's +Fri +Fri's +Friday +Fridays +Friday's +Frieda +Frieda's +Friedan +Friedan's +Friedman +Friedman's +Friend +Friends +Friend's +Frigga +Frigga's +Frigidaire +Frigidaire's +Frisbee +Frisbee's +Frisco +Frisco's +Frisian +Frisians +Frisian's +Frito +Frito's +Fritz +Fritz's +Frobisher +Frobisher's +Frodo +Frodo's +Froissart +Froissart's +Fromm +Fromm's +Fronde +Fronde's +Frontenac +Frontenac's +Frost +Frost's +Frostbelt +Frostbelt's +Frunze +Frunze's +Fry +Fry's +Frye +Frye's +Fuchs +Fuchs's +Fuentes +Fuentes's +Fugger +Fugger's +Fuji +Fuji's +Fujian +Fujian's +Fujitsu +Fujitsu's +Fujiwara +Fujiwara's +Fujiyama +Fujiyama's +Fukuoka +Fukuoka's +Fukuyama +Fukuyama's +Fulani +Fulani's +Fulbright +Fulbright's +Fuller +Fuller's +Fullerton +Fullerton's +Fulton +Fulton's +Funafuti +Funafuti's +Fundy +Fundy's +Furies +Furies's +Furman +Furman's +Furtwangler +Furtwangler's +Fushun +Fushun's +Fuzhou +Fuzhou's +Fuzzbuster +Fuzzbuster's +G +Gen +Ger +G's +Gable +GA +GAO +GATT +GATT's +GB +GB's +GCC +GCC's +GDP +GDP's +GE +GE's +GED +GHQ +GHQ's +GHz +GI +GIF +GIGO +GM +GM's +GMAT +GMO +GMT +GMT's +GNP +GNP's +GNU +GNU's +GOP +GOP's +GP +GP's +GPA +GPO +GPS +GPU +GSA +GTE +GTE's +GU +GUI +GUI's +Ga +Ga's +Gable +Gable's +Gabon +Gabon's +Gabonese +Gabonese's +Gaborone +Gaborone's +Gabriel +Gabriel's +Gabriela +Gabriela's +Gabrielle +Gabrielle's +Gacrux +Gacrux's +Gadsden +Gadsden's +Gaea +Gaea's +Gael +Gaels +Gael's +Gaelic +Gaelic's +Gagarin +Gagarin's +Gage +Gage's +Gaia +Gaia's +Gail +Gail's +Gaiman +Gaiman's +Gaines +Gaines's +Gainsborough +Gainsborough's +Galahad +Galahads +Galahad's +Galapagos +Galapagos's +Galatea +Galatea's +Galatia +Galatia's +Galatians +Galatians's +Galaxy +Galbraith +Galbraith's +Gale +Gale's +Galen +Galen's +Galibi +Galibi's +Galilean +Galileans +Galilean's +Galilee +Galilee's +Galileo +Galileo's +Gall +Gall's +Gallagher +Gallagher's +Gallegos +Gallegos's +Gallic +Gallic's +Gallicism +Gallicisms +Gallicism's +Gallo +Gallo's +Galloway +Galloway's +Gallup +Gallup's +Galois +Galois's +Galsworthy +Galsworthy's +Galvani +Galvani's +Galveston +Galveston's +Gama +Gamay +Gamay's +Gambia +Gambia's +Gambian +Gambians +Gambian's +Gamble +Gamble's +Gamow +Gamow's +Gandalf +Gandalf's +Gandhi +Gandhi's +Gandhian +Gandhian's +Ganesha +Ganesha's +Ganges +Ganges's +Gangtok +Gangtok's +Gansu +Gansu's +Gantry +Gantry's +Ganymede +Ganymede's +Gap +Gap's +Garbo +Garbo's +Garcia +Garcia's +Gardner +Gardner's +Gareth +Gareth's +Garfield +Garfield's +Garfunkel +Garfunkel's +Gargantua +Gargantua's +Garibaldi +Garibaldi's +Garland +Garland's +Garner +Garner's +Garrett +Garrett's +Garrick +Garrick's +Garrison +Garrison's +Garry +Garry's +Garth +Garth's +Garvey +Garvey's +Gary +Gary's +Garza +Garza's +Gascony +Gascony's +Gasser +Gasser's +Gastroenterology +Gates +Gates's +Gatling +Gatling's +Gatorade +Gatorade's +Gatsby +Gatsby's +Gatun +Gatun's +Gauguin +Gauguin's +Gaul +Gauls +Gaul's +Gaulish +Gauss +Gauss's +Gaussian +Gaussian's +Gautama +Gautama's +Gautier +Gautier's +Gavin +Gavin's +Gawain +Gawain's +Gay +Gay's +Gayle +Gayle's +Gaza +Gaza's +Gaziantep +Gaziantep's +Gd +Gd's +Gdansk +Gdansk's +Ge +Ge's +Geffen +Geffen's +Gehenna +Gehenna's +Gehrig +Gehrig's +Geiger +Geiger's +Gelbvieh +Gelbvieh's +Geller +Geller's +Gemini +Geminis +Gemini's +Gen +Gen's +Gena +Gena's +Genaro +Genaro's +Gene +Gene's +Genesis +Genesis's +Genet +Genet's +Geneva +Geneva's +Genevieve +Genevieve's +Genghis +Genghis's +Genoa +Genoas +Genoa's +Gentoo +Gentoo's +Gentry +Gentry's +Geo +Geo's +Geoffrey +Geoffrey's +George +Georges +George's +Georgetown +Georgetown's +Georgette +Georgette's +Georgia +Georgia's +Georgian +Georgians +Georgian's +Georgina +Georgina's +Ger +Ger's +Gerald +Gerald's +Geraldine +Geraldine's +Gerard +Gerard's +Gerardo +Gerardo's +Gerber +Gerber's +Gere +Gere's +Geritol +Geritol's +German +Germans +German's +Germanic +Germanic's +Germany +Germany's +Geronimo +Geronimo's +Gerry +Gerry's +Gershwin +Gershwin's +Gertrude +Gertrude's +Gestapo +Gestapos +Gestapo's +Gethsemane +Gethsemane's +Getty +Getty's +Gettysburg +Gettysburg's +Gewurztraminer +Gewurztraminer's +Ghana +Ghana's +Ghanaian +Ghats +Ghats's +Ghazvanid +Ghazvanid's +Ghent +Ghent's +Ghibelline +Ghibelline's +Giacometti +Giacometti's +Giannini +Giannini's +Giauque +Giauque's +Gibbon +Gibbon's +Gibbs +Gibbs's +Gibraltar +Gibraltars +Gibraltar's +Gibson +Gibson's +Gide +Gide's +Gideon +Gideon's +Gielgud +Gielgud's +Gienah +Gienah's +Gil +Gil's +Gila +Gila's +Gilbert +Gilbert's +Gilberto +Gilberto's +Gilchrist +Gilchrist's +Gilda +Gilda's +Gilead +Gilead's +Giles +Giles's +Gilgamesh +Gilgamesh's +Gill +Gill's +Gillespie +Gillespie's +Gillette +Gillette's +Gilliam +Gilliam's +Gillian +Gillian's +Gilligan +Gilligan's +Gilman +Gilmore +Gilmore's +Gina +Gina's +Ginger +Ginger's +Gingrich +Gingrich's +Ginny +Ginny's +Gino +Gino's +Ginsberg +Ginsberg's +Ginsburg +Ginsburg's +Ginsu +Ginsu's +Giorgione +Giorgione's +Giotto +Giotto's +Giovanni +Giovanni's +Giraudoux +Giraudoux's +Giselle +Giselle's +Gish +Gish's +GitHub +GitHub's +Giuliani +Giuliani's +Giuseppe +Giuseppe's +Giza +Giza's +Gk +Gladstone +Gladstones +Gladstone's +Gladys +Gladys's +Glaser +Glaser's +Glasgow +Glasgow's +Glass +Glass's +Glastonbury +Glastonbury's +Glaswegian +Glaswegians +Glaswegian's +Glaxo +Glaxo's +Gleason +Gleason's +Glen +Glen's +Glenda +Glenda's +Glendale +Glenlivet +Glenlivet's +Glenn +Glenn's +Glenna +Glenna's +Gloria +Gloria's +Gloucester +Gloucester's +Glover +Glover's +Gnostic +Gnostic's +Gnosticism +Gnosticism's +GnuPG +Goa +Goa's +Gobi +Gobi's +God +God's +Godard +Godard's +Goddard +Goddard's +Godel +Godel's +Godhead +Godhead's +Godiva +Godiva's +Godot +Godot's +Godspeed +Godspeeds +Godspeed's +Godthaab +Godthaab's +Godunov +Godunov's +Godzilla +Godzilla's +Goebbels +Goebbels's +Goering +Goering's +Goethals +Goethals's +Goethe +Goethe's +Goff +Goff's +Gog +Gog's +Gogol +Gogol's +Goiania +Goiania's +Golan +Golan's +Golconda +Golconda's +Golda +Golda's +Goldberg +Goldberg's +Golden +Golden's +Goldie +Goldie's +Goldilocks +Goldilocks's +Golding +Golding's +Goldman +Goldman's +Goldsmith +Goldsmith's +Goldwater +Goldwater's +Goldwyn +Goldwyn's +Golgi +Golgi's +Golgotha +Golgotha's +Goliath +Goliath's +Gomez +Gomez's +Gomorrah +Gomorrah's +Gompers +Gompers's +Gomulka +Gomulka's +Gondwanaland +Gondwanaland's +Gonzales +Gonzales's +Gonzalez +Gonzalez's +Gonzalo +Gonzalo's +Good +Good's +Goodall +Goodall's +Goode +Goode's +Goodman +Goodman's +Goodrich +Goodrich's +Goodwill +Goodwill's +Goodwin +Goodwin's +Goodyear +Goodyear's +Google +Google's +Goolagong +Goolagong's +Gopher +Gorbachev +Gorbachev's +Gordian +Gordian's +Gordimer +Gordimer's +Gordon +Gordon's +Gore +Gore's +Goren +Goren's +Gorey +Gorey's +Gorgas +Gorgas's +Gorgon +Gorgon's +Gorgonzola +Gorgonzola's +Gorky +Gorky's +Gospel +Gospels +Gospel's +Goteborg +Goteborg's +Goth +Goth's +Gotham +Gotham's +Gothic +Gothics +Gothic's +Goths +Gouda +Goudas +Gouda's +Gould +Gould's +Gounod +Gounod's +Governor +Goya +Goya's +Gr +Grable +Grable +Grable's +Gracchus +Gracchus's +Grace +Grace's +Graceland +Graceland's +Gracie +Gracie's +Graciela +Graciela's +Grady +Grady's +Graffias +Graffias's +Grafton +Grafton's +Graham +Graham's +Grahame +Grahame's +Grail +Grail's +Grammy +Grammy's +Grampians +Grampians's +Granada +Granada's +Grant +Grant's +Grass +Grass's +Graves +Graves's +Gray +Gray's +Grecian +Grecian's +Greece +Greece's +Greek +Greeks +Greek's +Greeley +Greeley's +Green +Greens +Green's +Greene +Greene's +Greenland +Greenland's +Greenlandic +Greenpeace +Greenpeace's +Greensboro +Greensboro's +Greensleeves +Greensleeves's +Greenspan +Greenspan's +Greenwich +Greenwich's +Greer +Greer's +Greg +Greg's +Gregg +Gregg's +Gregorian +Gregorian's +Gregorio +Gregorio's +Gregory +Gregory's +Grenada +Grenada's +Grenadian +Grenadians +Grenadian's +Grenadines +Grenadines's +Grendel +Grendel's +Grenoble +Grenoble's +Gresham +Gresham's +Greta +Greta's +Gretchen +Gretchen's +Gretel +Gretel's +Gretzky +Gretzky's +Grey +Grey's +Grieg +Grieg's +Griffin +Griffin's +Griffith +Griffith's +Grimes +Grimes's +Grimm +Grimm's +Grinch +Grinch's +Gris +Gris's +Gromyko +Gromyko's +Gropius +Gropius's +Gross +Gross's +Grosz +Grosz's +Grotius +Grotius's +Grover +Grover's +Grozny +Grumman +Grumman's +Grundy +Grundy's +Grunewald +Grunewald's +Grus +Grus's +Gruyere +Gruyeres +Gruyere's +Guadalajara +Guadalajara's +Guadalcanal +Guadalcanal's +Guadalquivir +Guadalquivir's +Guadalupe +Guadalupe's +Guadeloupe +Guadeloupe's +Guallatiri +Guallatiri's +Guam +Guam's +Guamanian +Guangdong +Guangdong's +Guangzhou +Guangzhou's +Guantanamo +Guantanamo's +Guarani +Guarani's +Guarnieri +Guarnieri's +Guatemala +Guatemala's +Guatemalan +Guatemalans +Guatemalan's +Guayaquil +Guayaquil's +Gucci +Gucci's +Guelph +Guelph's +Guernsey +Guernseys +Guernsey's +Guerra +Guerra's +Guerrero +Guerrero's +Guevara +Guevara's +Guggenheim +Guggenheim's +Guiana +Guiana's +Guido +Guillermo +Guillermo's +Guinea +Guinea's +Guinean +Guineans +Guinean's +Guinevere +Guinevere's +Guinness +Guinness's +Guiyang +Guiyang's +Guizhou +Guizhou's +Guizot +Guizot's +Gujarat +Gujarat's +Gujarati +Gujarati's +Gujranwala +Gujranwala's +Gullah +Gullah's +Gulliver +Gulliver's +Gumbel +Gumbel's +Gunther +Gunther's +Guofeng +Guofeng's +Gupta +Gupta's +Gurkha +Gurkha's +Gus +Gus's +Gustav +Gustav's +Gustavo +Gustavo's +Gustavus +Gustavus's +Gutenberg +Gutenberg's +Guthrie +Guthrie's +Gutierrez +Gutierrez's +Guy +Guy's +Guyana +Guyana's +Guyanese +Guyanese's +Guzman +Guzman's +Gwalior +Gwalior's +Gwen +Gwen's +Gwendoline +Gwendoline's +Gwendolyn +Gwendolyn's +Gwyn +Gwyn's +Gypsy +Gypsies +Gypsy's +H +H's +HBO +HBO's +HBase +HBase's +HDD +HDMI +HDTV +HF +HF's +HHS +HI +HIV +HIV's +HM +HMO +HMO's +HMS +HOV +HP +HP's +HPV +HQ +HQ's +HR +HRH +HS +HSBC +HSBC's +HST +HT +HTML +HTML's +HTTP +HUD +HUD's +Ha +Ha's +Haas +Haas's +Habakkuk +Habakkuk's +Haber +Haber's +Hadar +Hadar's +Hades +Hades's +Hadoop +Hadoop's +Hadrian +Hadrian's +Hafiz +Hafiz's +Hagar +Hagar's +Haggai +Haggai's +Hagiographa +Hagiographa's +Hague +Hague's +Hahn +Hahn's +Haida +Haidas +Haida's +Haifa +Haifa's +Hainan +Hainan's +Haiphong +Haiphong's +Haiti +Haiti's +Haitian +Haitians +Haitian's +Hakka +Hakka's +Hakluyt +Hakluyt's +Hal +Hals +Hal's +Haldane +Haldane's +Hale +Hale's +Haleakala +Haleakala's +Haley +Haley's +Halifax +Halifax's +Hall +Hall's +Halley +Halley's +Halliburton +Halliburton's +Hallie +Hallie's +Hallmark +Hallmark's +Halloween +Halloweens +Halloween's +Hallstatt +Hallstatt's +Halon +Halon's +Hals +Hals's +Halsey +Halsey's +Ham +Ham's +Haman +Haman's +Hamburg +Hamburgs +Hamburg's +Hamhung +Hamhung's +Hamilcar +Hamilcar's +Hamill +Hamill's +Hamilton +Hamilton's +Hamiltonian +Hamiltonian's +Hamitic +Hamitic's +Hamlet +Hamlet's +Hamlin +Hamlin's +Hammarskjold +Hammarskjold's +Hammerstein +Hammerstein's +Hammett +Hammett's +Hammond +Hammond's +Hammurabi +Hammurabi's +Hampshire +Hampshire's +Hampton +Hampton's +Hamsun +Hamsun's +Han +Hans +Han's +Hancock +Hancock's +Handel +Handel's +Handy +Handy's +Haney +Haney's +Hangul +Hangul's +Hangzhou +Hangzhou's +Hank +Hank's +Hanna +Hanna's +Hannah +Hannah's +Hannibal +Hannibal's +Hanoi +Hanoi's +Hanover +Hanover's +Hanoverian +Hanoverian's +Hans +Hansen +Hans's +Hansel +Hansel's +Hansen +Hansen's +Hanson +Hanson's +Hanuka +Hanukkah +Hanukkah's +Hanukkahs +Hapsburg +Hapsburg's +Harare +Harare's +Harbin +Harbin's +Hardin +Hardin's +Harding +Harding's +Hardy +Hardy's +Hargreaves +Hargreaves's +Harlan +Harlan's +Harlem +Harlem's +Harlequin +Harlequin's +Harley +Harley's +Harlow +Harlow's +Harmon +Harmon's +Harold +Harold's +Harper +Harper's +Harpy +Harpies +Harpy's +Harrell +Harrell's +Harriet +Harriet's +Harriett +Harriett's +Harrington +Harrington's +Harris +Harris's +Harrisburg +Harrisburg's +Harrison +Harrison's +Harrods +Harrods's +Harry +Harry's +Hart +Hart's +Harte +Harte's +Hartford +Hartford's +Hartline +Hartline's +Hartman +Hartman's +Harvard +Harvard's +Harvey +Harvey's +Hasbro +Hasbro's +Hasidim +Hasidim's +Haskell +Haskell's +Hastings +Hastings's +Hatfield +Hatfield's +Hathaway +Hathaway's +Hatsheput +Hatsheput's +Hatteras +Hatteras's +Hattie +Hattie's +Hauptmann +Hauptmann's +Hausa +Hausa's +Hausdorff +Hausdorff's +Havana +Havanas +Havana's +Havarti +Havarti's +Havel +Havel's +Havoline +Havoline's +Haw +Hawaii +Hawaii's +Hawaiian +Hawaiians +Hawaiian's +Hawking +Hawking's +Hawkins +Hawkins's +Hawks +Hawthorne +Hawthorne's +Hay +Hays +Hay's +Hayden +Hayden's +Haydn +Haydn's +Hayek +Hayek's +Hayes +Hayes's +Haynes +Haynes's +Hays +Hays's +Hayward +Hayward's +Haywood +Haywood's +Hayworth +Hayworth's +Hazel +Hazel's +Hazlitt +Hazlitt's +He +He's +Head +Head's +Hearst +Hearst's +Heath +Heather +Heath's +Heather +Heather's +Heaviside +Heaviside's +Heb +Hebe +Hebe's +Hebei +Hebei's +Hebert +Hebert's +Hebraic +Hebraic's +Hebraism +Hebraisms +Hebraism's +Hebrew +Hebrews +Hebrew's +Hebrews +Hebrews's +Hebrides +Hebrides's +Hecate +Hecate's +Hector +Hector's +Hecuba +Hecuba's +Heep +Heep's +Hefner +Hefner's +Hegel +Hegel's +Hegelian +Hegelian's +Hegira +Hegira's +Heidegger +Heidegger's +Heidelberg +Heidelberg's +Heidi +Heidi's +Heifetz +Heifetz's +Heilongjiang +Heilongjiang's +Heimlich +Heimlich's +Heine +Heine's +Heineken +Heineken's +Heinlein +Heinlein's +Heinrich +Heinrich's +Heinz +Heinz's +Heisenberg +Heisenberg's +Heisman +Heisman's +Helen +Helen's +Helena +Helena's +Helene +Helene's +Helga +Helga's +Helicobacter +Helicon +Helicon's +Heliopolis +Heliopolis's +Helios +Helios's +Hellene +Hellenes +Hellene's +Hellenic +Hellenic's +Hellenism +Hellenisms +Hellenism's +Hellenist +Hellenistic +Hellenistic's +Hellenization +Hellenization's +Hellenize +Hellenize's +Heller +Heller's +Hellespont +Hellespont's +Hellman +Hellman's +Helmholtz +Helmholtz's +Heloise +Heloise's +Helsinki +Helsinki's +Helvetian +Helvetius +Helvetius's +Hemingway +Hemingway's +Henan +Henan's +Hench +Hench's +Henderson +Henderson's +Hendrick +Hendricks +Hendrick's +Hendricks +Hendricks's +Hendrix +Hendrix's +Henley +Henley's +Hennessy +Hennessy's +Henri +Henri's +Henrietta +Henrietta's +Henrik +Henrik's +Henry +Henry's +Hensley +Hensley's +Henson +Henson's +Hepburn +Hepburn's +Hephaestus +Hephaestus's +Hepplewhite +Hepplewhite's +Hera +Hera's +Heracles +Heracles's +Heraclitus +Heraclitus's +Herakles +Herakles's +Herbart +Herbart's +Herbert +Herbert's +Herculaneum +Herculaneum's +Herculean +Hercules +Hercules's +Herder +Herder's +Hereford +Herefords +Hereford's +Herero +Herero's +Heriberto +Heriberto's +Herman +Herman's +Hermaphroditus +Hermaphroditus's +Hermes +Hermes's +Herminia +Herminia's +Hermitage +Hermitage's +Hermite +Hermite's +Hermosillo +Hermosillo's +Hernandez +Hernandez's +Herod +Herod's +Herodotus +Herodotus's +Heroku +Heroku's +Herr +Herring +Herr's +Herrera +Herrera's +Herrick +Herrick's +Herring +Herring's +Herschel +Herschel's +Hersey +Hersey's +Hershel +Hershel's +Hershey +Hershey's +Hertz +Hertz's +Hertzsprung +Hertzsprung's +Herzegovina +Herzegovina's +Herzl +Herzl's +Heshvan +Heshvan's +Hesiod +Hesiod's +Hesperus +Hesperus's +Hess +Hess's +Hesse +Hesse's +Hessian +Hessian's +Hester +Hester's +Heston +Heston's +Hettie +Hettie's +Hewitt +Hewitt's +Hewlett +Hewlett's +Heyerdahl +Heyerdahl's +Heywood +Heywood's +Hezbollah +Hezbollah's +Hezekiah +Hezekiah's +Hf +Hf's +Hg +Hg's +Hialeah +Hialeah's +Hiawatha +Hiawatha's +Hibernia +Hibernia's +Hibernian +Hickman +Hickman's +Hickok +Hickok's +Hicks +Hicks's +Hieronymus +Hieronymus's +Higashiosaka +Higgins +Higgins's +Highlander +Highlanders +Highlander's +Highlands +Highness +Highness's +Hilario +Hilario's +Hilary +Hilary's +Hilbert +Hilbert's +Hilda +Hilda's +Hildebrand +Hildebrand's +Hilfiger +Hilfiger's +Hill +Hill's +Hillary +Hillary's +Hillel +Hillel's +Hilton +Hilton's +Himalaya +Himalayas +Himalaya's +Himalayan +Himalayas +Himalayas's +Himmler +Himmler's +Hinayana +Hinayana's +Hindemith +Hindemith's +Hindenburg +Hindenburg's +Hindi +Hindi's +Hindu +Hindus +Hindu's +Hinduism +Hinduisms +Hinduism's +Hindustan +Hindustan's +Hindustani +Hindustanis +Hindustani's +Hines +Hines's +Hinton +Hinton's +Hipparchus +Hipparchus's +Hippocrates +Hippocrates's +Hippocratic +Hippocratic's +Hiram +Hiram's +Hirobumi +Hirobumi's +Hirohito +Hirohito's +Hiroshima +Hiroshima's +Hispanic +Hispanics +Hispanic's +Hispaniola +Hispaniola's +Hiss +Hiss's +Hitachi +Hitachi's +Hitchcock +Hitchcock's +Hitler +Hitlers +Hitler's +Hittite +Hittites +Hittite's +Hmong +Hmong's +Ho +Ho's +Hobart +Hobart's +Hobbes +Hobbes's +Hobbs +Hobbs's +Hockney +Hockney's +Hodge +Hodges +Hodge's +Hodges +Hodges's +Hodgkin +Hodgkin's +Hoff +Hoff's +Hoffa +Hoffa's +Hoffman +Hoffman's +Hofstadter +Hofstadter's +Hogan +Hogan's +Hogarth +Hogarth's +Hogwarts +Hogwarts's +Hohenlohe +Hohenlohe's +Hohenstaufen +Hohenstaufen's +Hohenzollern +Hohenzollern's +Hohhot +Hohhot's +Hohokam +Hohokam's +Hokkaido +Hokkaido's +Hokusai +Hokusai's +Holbein +Holbein's +Holcomb +Holcomb's +Holden +Holden's +Holder +Holder's +Holiday +Holiday's +Holiness +Holland +Hollander +Hollanders +Hollands +Holland's +Hollander +Hollander's +Hollerith +Hollerith's +Holley +Holley's +Hollie +Hollie's +Hollis +Hollis's +Holloway +Holloway's +Holly +Holly's +Hollywood +Hollywood's +Holman +Holman's +Holmes +Holmes's +Holocaust +Holocaust's +Holocene +Holocene's +Holst +Holst's +Holstein +Holsteins +Holstein's +Holt +Holt's +Homer +Homer's +Homeric +Homeric's +Hon +Honda +Honda's +Honduran +Hondurans +Honduran's +Honduras +Honduras's +Honecker +Honecker's +Honeywell +Honeywell's +Hong +Honiara +Honiara's +Honolulu +Honolulu's +Honorable +Honshu +Honshu's +Hood +Hood's +Hooke +Hooker +Hooke's +Hooker +Hooker's +Hooper +Hooper's +Hoosier +Hoosiers +Hoosier's +Hooters +Hooters's +Hoover +Hoovers +Hoover's +Hope +Hope's +Hopewell +Hopewell's +Hopi +Hopis +Hopi's +Hopkins +Hopkins's +Hopper +Hopper's +Horace +Horace's +Horacio +Horacio's +Horatio +Horatio's +Hormel +Hormel's +Hormuz +Hormuz's +Horn +Horn's +Hornblower +Hornblower's +Horne +Horne's +Horowitz +Horowitz's +Horthy +Horthy's +Horton +Horton's +Horus +Horus's +Hosea +Hosea's +Host +Hosts +Host's +Hotpoint +Hotpoint's +Hottentot +Hottentots +Hottentot's +Houdini +Houdini's +House +House's +Housman +Housman's +Houston +Houston's +Houyhnhnm +Houyhnhnm's +Hovhaness +Hovhaness's +Howard +Howard's +Howe +Howe's +Howell +Howells +Howell's +Howells +Howells's +Howrah +Hoyle +Hoyle's +Hrothgar +Hrothgar's +Hts +Huang +Huang's +Hubbard +Hubbard's +Hubble +Hubble's +Hubei +Hubei's +Huber +Huber's +Hubert +Hubert's +Huck +Huck's +Huddersfield +Hudson +Hudson's +Huerta +Huerta's +Huey +Huey's +Huff +Huff's +Huffman +Huffman's +Huggins +Huggins's +Hugh +Hughes +Hugh's +Hughes +Hughes's +Hugo +Hugo's +Huguenot +Huguenots +Huguenot's +Hui +Hui's +Huitzilopotchli +Huitzilopotchli's +Hull +Hull's +Humberto +Humberto's +Humboldt +Humboldt's +Hume +Hume's +Hummel +Hummel's +Hummer +Hummer's +Humphrey +Humphreys +Humphrey's +Humvee +Humvee's +Hun +Huns +Hun's +Hunan +Hunan's +Hung +Hung's +Hungarian +Hungarians +Hungarian's +Hungary +Hungary's +Hunspell +Hunspell's +Hunt +Hunter +Hunt's +Hunter +Hunter's +Huntington +Huntington's +Huntley +Huntley's +Huntsville +Huntsville's +Hurd +Hurd's +Hurley +Hurley's +Huron +Huron's +Hurst +Hurst's +Hus +Hus's +Hussein +Hussein's +Husserl +Husserl's +Hussite +Hussite's +Huston +Huston's +Hutchinson +Hutchinson's +Hutton +Hutton's +Hutu +Hutu's +Huxley +Huxley's +Huygens +Huygens's +Hyades +Hyades's +Hyde +Hyde's +Hyderabad +Hyderabad's +Hydra +Hydra's +Hymen +Hymen's +Hyperion +Hyperion's +Hyundai +Hyundai's +Hz +Hz's +I'd +I'll +I'm +I've +I +I's +IA +IBM +IBM's +ICBM +ICBMs +ICBM's +ICC +ICU +ID +IDs +ID's +IDE +IE +IED +IEEE +IKEA +IKEA's +IL +IMF +IMF's +IMHO +IMNSHO +IMO +IN +ING +ING's +INRI +INS +IOU +IOU's +IP +IPA +IPO +IQ +IQ's +IRA +IRAs +IRA's +IRC +IRS +IRS's +ISBN +ISIS +ISO +ISO's +ISP +ISS +IT +IUD +IV +IVs +IV's +IVF +Ia +Iaccoca +Iaccoca's +Iago +Iago's +Ian +Ian's +Iapetus +Iapetus's +Ibadan +Ibadan's +Iberia +Iberia's +Iberian +Iberian's +Ibiza +Ibiza's +Iblis +Iblis's +Ibo +Ibo's +Ibsen +Ibsen's +Icahn +Icahn's +Icarus +Icarus's +Ice +Iceland +Icelander +Icelanders +Iceland's +Icelander +Icelander's +Icelandic +Icelandic's +Ida +Ida's +Idaho +Idahos +Idaho's +Idahoan +Idahoans +Idahoan's +Idahoes +Ieyasu +Ieyasu's +Ignacio +Ignacio's +Ignatius +Ignatius's +Igor +Igor's +Iguassu +Iguassu's +Ijsselmeer +Ijsselmeer's +Ike +Ike's +Ikhnaton +Ikhnaton's +Ila +Ila's +Ilene +Ilene's +Iliad +Iliads +Iliad's +Ill +Illinois +Illinois's +Illinoisan +Illinoisans +Illinoisan's +Illuminati +Illuminati's +Ilyushin +Ilyushin's +Imelda +Imelda's +Imhotep +Imhotep's +Imodium +Imodium's +Imogene +Imogene's +Imus +Imus's +In +In's +Ina +Ina's +Inc +Inca +Incas +Inca's +Inchon +Inchon's +Incorporated +Ind +Independence +Independence's +India +India's +Indian +Indians +Indian's +Indiana +Indiana's +Indianan +Indianans +Indianan's +Indianapolis +Indianapolis's +Indianian +Indies +Indies's +Indira +Indira's +Indochina +Indochina's +Indochinese +Indochinese's +Indonesia +Indonesia's +Indonesian +Indonesians +Indonesian's +Indore +Indore's +Indra +Indra's +Indus +Indus's +Indy +Indies +Indy's +Ines +Ines's +Inez +Inez's +Inge +Inge's +Inglewood +Ingram +Ingram's +Ingres +Ingres's +Ingrid +Ingrid's +Innocent +Innocent's +Innsbruck +Inonu +Inonu's +Inquisition +Inquisition's +Inst +Instagram +Instagram's +Instamatic +Instamatic's +Intel +Intel's +Intelsat +Intelsat's +Internationale +Internationale's +Internet +Internets +Internet's +Interpol +Interpol's +Inuit +Inuits +Inuit's +Inuktitut +Inuktitut's +Invar +Invar's +Io +Io's +Ionesco +Ionesco's +Ionian +Ionians +Ionian's +Ionic +Ionics +Ionic's +Iowa +Iowas +Iowa's +Iowan +Iowans +Iowan's +Iphigenia +Iphigenia's +Ipswich +Iqaluit +Iqaluit's +Iqbal +Iqbal's +Iquitos +Iquitos's +Ir +Ir's +Ira +Ira's +Iran +Iran's +Iranian +Iranians +Iranian's +Iraq +Iraq's +Iraqi +Iraqis +Iraqi's +Ireland +Ireland's +Irene +Irene's +Iris +Iris's +Irish +Irisher +Irish's +Irishman +Irishman's +Irishmen +Irishmen's +Irishwoman +Irishwoman's +Irishwomen +Irishwomen's +Irkutsk +Irkutsk's +Irma +Irma's +Iroquoian +Iroquoians +Iroquoian's +Iroquois +Iroquois's +Irrawaddy +Irrawaddy's +Irtish +Irtish's +Irvin +Irvin's +Irvine +Irvine's +Irving +Irving's +Irwin +Irwin's +Isaac +Isaac's +Isabel +Isabel's +Isabella +Isabella's +Isabelle +Isabelle's +Isaiah +Isaiah's +Iscariot +Iscariot's +Isfahan +Isfahan's +Isherwood +Isherwood's +Ishim +Ishim's +Ishmael +Ishmael's +Ishtar +Ishtar's +Isiah +Isiah's +Isidro +Isidro's +Isis +Isis's +Islam +Islams +Islam's +Islamabad +Islamabad's +Islamic +Islamic's +Islamism +Islamism's +Islamist +Islamist's +Islamophobia +Islamophobic +Ismael +Ismael's +Ismail +Ismail's +Isolde +Isolde's +Ispell +Ispell's +Israel +Israels +Israel's +Israeli +Israelis +Israeli's +Israelite +Israelite's +Issac +Issac's +Issachar +Issachar's +Istanbul +Istanbul's +Isuzu +Isuzu's +It +Itaipu +Itaipu's +Ital +Italian +Italians +Italian's +Italianate +Italy +Italy's +Itasca +Itasca's +Ithaca +Ithaca's +Ithacan +Ithacan's +Ito +Ito's +Iva +Iva's +Ivan +Ivan's +Ivanhoe +Ivanhoe's +Ives +Ives's +Ivorian +Ivory +Ivory's +Ivy +Ivy's +Iyar +Iyar's +Izaak +Izaak's +Izanagi +Izanagi's +Izanami +Izanami's +Izhevsk +Izhevsk's +Izmir +Izmir's +Izod +Izod's +Izvestia +Izvestia's +J +Jed +J's +JCS +JD +JFK +JFK's +JP +JPEG +JV +Jack +Jack's +Jackie +Jackie's +Jacklyn +Jacklyn's +Jackson +Jackson's +Jacksonian +Jacksonian's +Jacksonville +Jacksonville's +Jacky +Jacky's +Jaclyn +Jaclyn's +Jacob +Jacobs +Jacob's +Jacobean +Jacobean's +Jacobi +Jacobi's +Jacobin +Jacobin's +Jacobite +Jacobite's +Jacobs +Jacobs's +Jacobson +Jacobson's +Jacquard +Jacquard's +Jacqueline +Jacqueline's +Jacquelyn +Jacquelyn's +Jacques +Jacques's +Jacuzzi +Jacuzzi's +Jagger +Jagger's +Jagiellon +Jagiellon's +Jaguar +Jaguar's +Jahangir +Jahangir's +Jaime +Jaime's +Jain +Jain's +Jainism +Jainism's +Jaipur +Jaipur's +Jakarta +Jakarta's +Jake +Jake's +Jamaal +Jamaal's +Jamaica +Jamaica's +Jamaican +Jamaicans +Jamaican's +Jamal +Jamal's +Jamar +Jamar's +Jame +James +Jame's +Jamel +Jamel's +James +James's +Jamestown +Jamestown's +Jami +Jami's +Jamie +Jamie's +Jan +Jan's +Jana +Jana's +Janacek +Janacek's +Jane +Jane's +Janell +Janell's +Janelle +Janelle's +Janet +Janet's +Janette +Janette's +Janice +Janice's +Janie +Janie's +Janine +Janine's +Janis +Janis's +Janissary +Janissary's +Janjaweed +Janjaweed's +Janna +Janna's +Jannie +Jannie's +Jansen +Jansen's +Jansenist +Jansenist's +January +Januaries +January's +Janus +Janus's +Jap +Japs +Jap's +Japan +Japan's +Japanese +Japaneses +Japanese's +Japura +Japura's +Jared +Jared's +Jarlsberg +Jarlsberg's +Jarred +Jarred's +Jarrett +Jarrett's +Jarrod +Jarrod's +Jarvis +Jarvis's +Jasmine +Jasmine's +Jason +Jason's +Jasper +Jasper's +Jataka +Jataka's +Java +Javas +Java's +JavaScript +JavaScript's +Javanese +Javanese's +Javier +Javier's +Jaxartes +Jaxartes's +Jay +Jay's +Jayapura +Jayapura's +Jayawardene +Jayawardene's +Jaycee +Jaycees +Jaycee's +Jaycees +Jaycees's +Jayne +Jayne's +Jayson +Jayson's +Jean +Jean's +Jeanette +Jeanette's +Jeanie +Jeanie's +Jeanine +Jeanine's +Jeanne +Jeanne's +Jeannette +Jeannette's +Jeannie +Jeannie's +Jeannine +Jeannine's +Jed +Jed's +Jedi +Jedi's +Jeep +Jeep's +Jeeves +Jeeves's +Jeff +Jeff's +Jefferey +Jefferey's +Jefferson +Jefferson's +Jeffersonian +Jeffersonian's +Jeffery +Jeffery's +Jeffrey +Jeffrey's +Jeffry +Jeffry's +Jehoshaphat +Jehoshaphat's +Jehovah +Jehovah's +Jekyll +Jekyll's +Jenifer +Jenifer's +Jenkins +Jenkins's +Jenna +Jenna's +Jenner +Jenner's +Jennie +Jennie's +Jennifer +Jennifer's +Jennings +Jennings's +Jenny +Jenny's +Jensen +Jensen's +Jephthah +Jephthah's +Jerald +Jerald's +Jeremiah +Jeremiah's +Jeremiahs +Jeremy +Jeremy's +Jeri +Jeri's +Jericho +Jericho's +Jermaine +Jermaine's +Jeroboam +Jeroboam's +Jerold +Jerold's +Jerome +Jerome's +Jerri +Jerri's +Jerrod +Jerrod's +Jerrold +Jerrold's +Jerry +Jerry's +Jersey +Jerseys +Jersey's +Jerusalem +Jerusalem's +Jess +Jess's +Jesse +Jesse's +Jessica +Jessica's +Jessie +Jessie's +Jesuit +Jesuits +Jesuit's +Jesus +Jesus's +Jetway +Jetway's +Jew +Jews +Jew's +Jewel +Jewel's +Jewell +Jewell's +Jewess +Jewesses +Jewess's +Jewish +Jewishness +Jewish's +Jewry +Jewry's +Jezebel +Jezebels +Jezebel's +Jiangsu +Jiangsu's +Jiangxi +Jiangxi's +Jidda +Jidda's +Jilin +Jilin's +Jill +Jill's +Jillian +Jillian's +Jim +Jim's +Jimenez +Jimenez's +Jimmie +Jimmie's +Jimmy +Jimmy's +Jinan +Jinan's +Jinnah +Jinnah's +Jinny +Jinny's +Jivaro +Jivaro's +Jo +Jo's +Joan +Joan's +Joann +Joann's +Joanna +Joanna's +Joanne +Joanne's +Joaquin +Joaquin's +Job +Jobs +Job's +Jobs +Jobs's +Jocasta +Jocasta's +Jocelyn +Jocelyn's +Jock +Jock's +Jockey +Jockey's +Jodi +Jodi's +Jodie +Jodie's +Jody +Jody's +Joe +Joe's +Joel +Joel's +Joey +Joey's +Jogjakarta +Jogjakarta's +Johann +Johann's +Johanna +Johanna's +Johannes +Johannes's +Johannesburg +Johannesburg's +John +Johns +John's +Johnathan +Johnathan's +Johnathon +Johnathon's +Johnie +Johnie's +Johnnie +Johnnie's +Johnny +Johnny's +Johns +Johns's +Johnson +Johnson's +Johnston +Johnston's +Jolene +Jolene's +Jolson +Jolson's +Jon +Jon's +Jonah +Jonah's +Jonahs +Jonas +Jonas's +Jonathan +Jonathan's +Jonathon +Jonathon's +Jones +Jones's +Joni +Joni's +Jonson +Jonson's +Joplin +Joplin's +Jordan +Jordan's +Jordanian +Jordanians +Jordanian's +Jorge +Jorge's +Jose +Jose's +Josef +Josef's +Josefa +Josefa's +Josefina +Josefina's +Joseph +Joseph's +Josephine +Josephine's +Josephs +Josephson +Josephson's +Josephus +Josephus's +Josh +Josh's +Joshua +Joshua's +Josiah +Josiah's +Josie +Josie's +Josue +Josue's +Joule +Joule's +Jove +Jove's +Jovian +Jovian's +Joy +Joy's +Joyce +Joyce's +Joycean +Joycean's +Joyner +Joyner's +Jpn +Jr +Jr's +Juan +Juan's +Juana +Juana's +Juanita +Juanita's +Juarez +Juarez's +Jubal +Jubal's +Judaeo +Judah +Judah's +Judaic +Judaical +Judaism +Judaisms +Judaism's +Judas +Judases +Judas's +Judd +Judd's +Jude +Jude's +Judea +Judea's +Judges +Judith +Judith's +Judson +Judson's +Judy +Judy's +Juggernaut +Juggernaut's +Jul +Jules +Jules's +Julia +Julia's +Julian +Julian's +Juliana +Juliana's +Julianne +Julianne's +Julie +Julie's +Juliet +Juliet's +Juliette +Juliette's +Julio +Julio's +Julius +Julius's +Julliard +Julliard's +July +Julies +July's +Jun +Jun's +June +Junes +June's +Juneau +Juneau's +Jung +Jung's +Jungfrau +Jungfrau's +Jungian +Jungian's +Junior +Juniors +Junior's +Junker +Junkers +Junker's +Juno +Juno's +Jupiter +Jupiter's +Jurassic +Jurassic's +Jurua +Jurua's +Justice +Justice's +Justin +Justin's +Justine +Justine's +Justinian +Justinian's +Jutland +Jutland's +Juvenal +Juvenal's +K +Ken +King +Kings +Ks +K's +KB +KB's +KC +KFC +KFC's +KGB +KGB's +KIA +KKK +KKK's +KO +KO's +KP +KS +KY +Kaaba +Kaaba's +Kabul +Kabul's +Kafka +Kafka's +Kafkaesque +Kafkaesque's +Kagoshima +Kagoshima's +Kahlua +Kahlua's +Kaifeng +Kaifeng's +Kaiser +Kaisers +Kaiser's +Kaitlin +Kaitlin's +Kalahari +Kalahari's +Kalamazoo +Kalamazoo's +Kalashnikov +Kalashnikov's +Kalb +Kalb's +Kalevala +Kalevala's +Kalgoorlie +Kalgoorlie's +Kali +Kali's +Kalmyk +Kalmyk's +Kama +Kama's +Kamchatka +Kamchatka's +Kamehameha +Kamehameha's +Kampala +Kampala's +Kampuchea +Kampuchea's +Kan +Kans +Kan's +Kanchenjunga +Kanchenjunga's +Kandahar +Kandahar's +Kandinsky +Kandinsky's +Kane +Kane's +Kannada +Kannada's +Kano +Kano's +Kanpur +Kanpur's +Kansan +Kansans +Kansan's +Kansas +Kansas's +Kant +Kant's +Kantian +Kantian's +Kaohsiung +Kaohsiung's +Kaposi +Kaposi's +Kara +Kara's +Karachi +Karachi's +Karaganda +Karaganda's +Karakorum +Karakorum's +Karamazov +Karamazov's +Kareem +Kareem's +Karen +Karen's +Karenina +Karenina's +Kari +Kari's +Karin +Karin's +Karina +Karina's +Karl +Karl's +Karla +Karla's +Karloff +Karloff's +Karo +Karo's +Karol +Karol's +Karroo +Karroo's +Karyn +Karyn's +Kasai +Kasai's +Kasey +Kasey's +Kashmir +Kashmirs +Kashmir's +Kasparov +Kasparov's +Kate +Kate's +Katelyn +Katelyn's +Katharine +Katharine's +Katherine +Katherine's +Katheryn +Katheryn's +Kathiawar +Kathiawar's +Kathie +Kathie's +Kathleen +Kathleen's +Kathmandu +Kathmandu's +Kathrine +Kathrine's +Kathryn +Kathryn's +Kathy +Kathy's +Katie +Katie's +Katina +Katina's +Katmai +Katmai's +Katowice +Katowice's +Katrina +Katrina's +Katy +Katy's +Kauai +Kauai's +Kaufman +Kaufman's +Kaunas +Kaunas's +Kaunda +Kaunda's +Kawabata +Kawabata's +Kawasaki +Kawasaki's +Kay +Kay's +Kaye +Kaye's +Kayla +Kayla's +Kazakh +Kazakh's +Kazakhs +Kazakhstan +Kazakhstan's +Kazan +Kazan's +Kazantzakis +Kazantzakis's +Kb +Kb's +Keaton +Keaton's +Keats +Keats's +Keck +Keck's +Keenan +Keenan's +Keewatin +Keewatin's +Keillor +Keillor's +Keisha +Keisha's +Keith +Keith's +Keller +Keller's +Kelley +Kelley's +Kelli +Kelli's +Kellie +Kellie's +Kellogg +Kellogg's +Kelly +Kelly's +Kelsey +Kelsey's +Kelvin +Kelvin's +Kemerovo +Kemerovo's +Kemp +Kemp's +Kempis +Kempis's +Ken +Ken's +Kendall +Kendall's +Kendra +Kendra's +Kendrick +Kendrick's +Kenmore +Kenmore's +Kennan +Kennan's +Kennedy +Kennedy's +Kenneth +Kenneth's +Kennith +Kennith's +Kenny +Kenny's +Kent +Kent's +Kenton +Kenton's +Kentuckian +Kentuckians +Kentuckian's +Kentucky +Kentucky's +Kenya +Kenya's +Kenyan +Kenyans +Kenyan's +Kenyatta +Kenyatta's +Kenyon +Kenyon's +Keogh +Keogh's +Keokuk +Keokuk's +Kepler +Kepler's +Kerensky +Kerensky's +Keri +Keri's +Kermit +Kermit's +Kern +Kern's +Kerouac +Kerouac's +Kerr +Kerr's +Kerri +Kerri's +Kerry +Kerry's +Kettering +Kettering's +Keven +Keven's +Kevin +Kevin's +Kevlar +Kevlar's +Kevorkian +Kevorkian's +Kewpie +Kewpie's +Key +Key's +Keynes +Keynes's +Keynesian +Keynesian's +Khabarovsk +Khabarovsk's +Khachaturian +Khachaturian's +Khalid +Khalid's +Khan +Khan's +Kharkov +Kharkov's +Khartoum +Khartoum's +Khayyam +Khayyam's +Khazar +Khazar's +Khmer +Khmer's +Khoikhoi +Khoikhoi's +Khoisan +Khoisan's +Khomeini +Khomeini's +Khorana +Khorana's +Khrushchev +Khrushchev's +Khufu +Khufu's +Khulna +Khulna's +Khwarizmi +Khwarizmi's +Khyber +Khyber's +Kickapoo +Kickapoo's +Kidd +Kidd's +Kiel +Kiel's +Kierkegaard +Kierkegaard's +Kieth +Kieth's +Kiev +Kiev's +Kigali +Kigali's +Kikuyu +Kikuyu's +Kilauea +Kilauea's +Kilimanjaro +Kilimanjaro's +Kilroy +Kilroy's +Kim +Kim's +Kimberley +Kimberley's +Kimberly +Kimberly's +King +King's +Kingston +Kingston's +Kingstown +Kingstown's +Kinko's +Kinney +Kinney's +Kinsey +Kinsey's +Kinshasa +Kinshasa's +Kiowa +Kiowas +Kiowa's +Kip +Kip's +Kipling +Kipling's +Kirby +Kirby's +Kirchhoff +Kirchhoff's +Kirchner +Kirchner's +Kirghistan +Kirghistan's +Kirghiz +Kirghiz's +Kirghizia +Kirghizia's +Kiribati +Kiribati's +Kirinyaga +Kirinyaga's +Kirk +Kirk's +Kirkland +Kirkland's +Kirkpatrick +Kirkpatrick's +Kirov +Kirov's +Kirsten +Kirsten's +Kisangani +Kisangani's +Kishinev +Kishinev's +Kislev +Kislev's +Kissinger +Kissinger's +Kit +Kit's +Kitakyushu +Kitakyushu's +Kitchener +Kitchener's +Kitty +Kitty's +Kiwanis +Kiwanis's +Klan +Klan's +Klansman +Klansman's +Klaus +Klaus's +Klee +Klee's +Kleenex +Kleenexes +Kleenex's +Klein +Klein's +Klimt +Klimt's +Kline +Kline's +Klingon +Klingon's +Klondike +Klondikes +Klondike's +Kmart +Kmart's +Knapp +Knapp's +Knesset +Knesset's +Kngwarreye +Kngwarreye's +Knickerbocker +Knickerbocker's +Knievel +Knievel's +Knight +Knight's +Knopf +Knopf's +Knossos +Knossos's +Knowles +Knowles's +Knox +Knox's +Knoxville +Knoxville's +Knudsen +Knudsen's +Knuth +Knuth's +Knuths +Kobe +Kobe's +Koch +Koch's +Kochab +Kochab's +Kodachrome +Kodachrome's +Kodak +Kodak's +Kodaly +Kodaly's +Kodiak +Kodiak's +Koestler +Koestler's +Kohinoor +Kohinoor's +Kohl +Kohl's +Koizumi +Koizumi's +Kojak +Kojak's +Kolyma +Kolyma's +Kommunizma +Kommunizma's +Kong +Kong's +Kongo +Kongo's +Konrad +Konrad's +Koontz +Koontz's +Koppel +Koppel's +Koran +Korans +Koran's +Koranic +Korea +Korea's +Korean +Koreans +Korean's +Kornberg +Kornberg's +Kory +Kory's +Korzybski +Korzybski's +Kosciusko +Kosciusko's +Kossuth +Kossuth's +Kosygin +Kosygin's +Kotlin +Kotlin's +Koufax +Koufax's +Kowloon +Kowloon's +Kr +Kr's +Kraft +Kraft's +Krakatoa +Krakatoa's +Krakow +Krakow's +Kramer +Kramer's +Krasnodar +Krasnodar's +Krasnoyarsk +Krasnoyarsk's +Krebs +Krebs's +Kremlin +Kremlin's +Kremlinologist +Kremlinology +Kresge +Kresge's +Kringle +Kringle's +Kris +Kris's +Krishna +Krishna's +Krishnamurti +Krishnamurti's +Krista +Krista's +Kristen +Kristen's +Kristi +Kristi's +Kristie +Kristie's +Kristin +Kristin's +Kristina +Kristina's +Kristine +Kristine's +Kristopher +Kristopher's +Kristy +Kristy's +Kroc +Kroc's +Kroger +Kroger's +Kronecker +Kronecker's +Kropotkin +Kropotkin's +Kruger +Kruger's +Krugerrand +Krugerrand's +Krupp +Krupp's +Krystal +Krystal's +Kshatriya +Kshatriya's +Kublai +Kublai's +Kubrick +Kubrick's +Kuhn +Kuhn's +Kuibyshev +Kuibyshev's +Kulthumm +Kulthumm's +Kunming +Kunming's +Kuomintang +Kuomintang's +Kurd +Kurd's +Kurdish +Kurdish's +Kurdistan +Kurdistan's +Kurosawa +Kurosawa's +Kurt +Kurt's +Kurtis +Kurtis's +Kusch +Kusch's +Kutuzov +Kutuzov's +Kuwait +Kuwait's +Kuwaiti +Kuwaitis +Kuwaiti's +Kuznets +Kuznets's +Kuznetsk +Kuznetsk's +Kwakiutl +Kwakiutl's +Kwan +Kwan's +Kwangju +Kwangju's +Kwanzaa +Kwanzaas +Kwanzaa's +Ky +Kieth +Ky's +Kyle +Kyle's +Kyoto +Kyoto's +Kyrgyzstan +Kyrgyzstan's +Kyushu +Kyushu's +L'Amour +L'Amour's +L'Enfant +L'Oreal +L'Oreal's +L'Ouverture +L'Ouverture's +L +Len +L's +LA +LAN +LAN's +LBJ +LBJ's +LC +LCD +LCD's +LCM +LDC +LED +LED's +LG +LG's +LGBT +LIFO +LL +LLB +LLB's +LLD +LLD's +LNG +LOGO +LP +LP's +LPG +LPN +LPNs +LPN's +LSAT +LSD +LSD's +LVN +La +Las +La's +Lab +Laban +Laban's +Labrador +Labradors +Labrador's +Labradorean +Lacey +Lacey's +Lachesis +Lachesis's +Lacy +Lacy's +Ladoga +Ladoga's +Ladonna +Ladonna's +Lady +Lady's +Ladyship +Ladyships +Ladyship's +Lafayette +Lafayette's +Lafitte +Lafitte's +Lagos +Lagos's +Lagrange +Lagrange's +Lagrangian +Lagrangian's +Lahore +Lahore's +Laius +Laius's +Lajos +Lajos's +Lakeisha +Lakeisha's +Lakewood +Lakisha +Lakisha's +Lakota +Lakota's +Lakshmi +Lakshmi's +Lamaism +Lamaisms +Lamaism's +Lamar +Lamar's +Lamarck +Lamarck's +Lamaze +Lamaze's +Lamb +Lamb's +Lambert +Lambert's +Lamborghini +Lamborghini's +Lambrusco +Lambrusco's +Lamentations +Lamont +Lamont's +Lana +Lana's +Lanai +Lanai's +Lancashire +Lancashire's +Lancaster +Lancaster's +Lance +Lance's +Lancelot +Lancelot's +Land +Land's +Landon +Landon's +Landry +Landry's +Landsat +Landsat's +Landsteiner +Landsteiner's +Lane +Lane's +Lang +Lang's +Langerhans +Langerhans's +Langland +Langland's +Langley +Langley's +Langmuir +Langmuir's +Lanka +Lanka's +Lankan +Lankan's +Lanny +Lanny's +Lansing +Lansing's +Lanzhou +Lanzhou's +Lao +Laos +Lao's +Laocoon +Laocoon's +Laos +Laos's +Laotian +Laotians +Laotian's +Laplace +Laplace's +Laplacian +Lapland +Laplander +Lapland's +Lapp +Lapps +Lapp's +Lara +Lara's +Laramie +Laramie's +Lardner +Lardner's +Laredo +Laredo's +Larousse +Larousse's +Larry +Larry's +Lars +Larsen +Lars's +Larsen +Larsen's +Larson +Larson's +Lascaux +Lascaux's +Lassa +Lassa's +Lassen +Lassen's +Lassie +Lassie's +Lat +Lat's +Latasha +Latasha's +Lateran +Lateran's +Latham +Latham's +Latin +Latiner +Latins +Latin's +Latina +Latino +Latinos +Latino's +Latisha +Latisha's +Latonya +Latonya's +Latoya +Latoya's +Latrobe +Latrobe's +Latvia +Latvia's +Latvian +Latvians +Latvian's +Laud +Lauder +Laud's +Lauder +Lauder's +Laue +Laue's +Laundromat +Laundromat's +Laura +Laura's +Laurasia +Laurasia's +Laurel +Laurel's +Lauren +Lauren's +Laurence +Laurence's +Laurent +Laurent's +Lauri +Lauri's +Laurie +Laurie's +Laval +Laval's +Lavern +Lavern's +Laverne +Laverne's +Lavoisier +Lavoisier's +Lavonne +Lavonne's +Lawanda +Lawanda's +Lawrence +Lawrence's +Lawson +Lawson's +Layamon +Layamon's +Layla +Layla's +Lazaro +Lazaro's +Lazarus +Lazarus's +Le +Les +Le's +Lea +Lea's +Leach +Leach's +Leadbelly +Leadbelly's +Leah +Leah's +Leakey +Leakey's +Lean +Lean's +Leander +Leander's +Leann +Leann's +Leanna +Leanna's +Leanne +Leanne's +Lear +Lear's +Learjet +Learjet's +Leary +Leary's +Leavenworth +Leavenworth's +Lebanese +Lebanese's +Lebanon +Lebanon's +Lebesgue +Lebesgue's +Leblanc +Leblanc's +Leda +Leda's +Lederberg +Lederberg's +Lee +Lee's +Leeds +Leeds's +Leeuwenhoek +Leeuwenhoek's +Leeward +Leeward's +Left +Legendre +Legendre's +Leger +Leger's +Leghorn +Leghorn's +Lego +Lego's +Legree +Legree's +Lehman +Lehman's +Leibniz +Leibniz's +Leicester +Leicesters +Leicester's +Leiden +Leiden's +Leif +Leif's +Leigh +Leigh's +Leila +Leila's +Leipzig +Leipzig's +Lela +Lela's +Leland +Leland's +Lelia +Lelia's +Lemaitre +Lemaitre's +Lemuel +Lemuel's +Lemuria +Lemuria's +Len +Len's +Lena +Lena's +Lenard +Lenard's +Lenin +Lenin's +Leningrad +Leningrad's +Leninism +Leninism's +Leninist +Leninist's +Lennon +Lennon's +Lenny +Lenny's +Leno +Leno's +Lenoir +Lenoir's +Lenora +Lenora's +Lenore +Lenore's +Lent +Lenten +Lents +Lent's +Lenten +Lenten's +Leo +Leos +Leo's +Leola +Leola's +Leon +Leon's +Leona +Leona's +Leonard +Leonard's +Leonardo +Leonardo's +Leoncavallo +Leoncavallo's +Leonel +Leonel's +Leonid +Leonid's +Leonidas +Leonidas's +Leonor +Leonor's +Leopold +Leopold's +Leopoldo +Leopoldo's +Lepidus +Lepidus's +Lepke +Lepke's +Lepus +Lepus's +Lerner +Lerner's +Leroy +Leroy's +Les +Les's +Lesa +Lesa's +Lesley +Lesley's +Leslie +Leslie's +Lesotho +Lesotho's +Lesseps +Lesseps's +Lessie +Lessie's +Lester +Lester's +Lestrade +Lestrade's +Leta +Leta's +Letha +Letha's +Lethe +Lethe's +Leticia +Leticia's +Letitia +Letitia's +Letterman +Letterman's +Levant +Levant's +Levesque +Levesque's +Levi +Levis +Levi's +Leviathan +Leviathan's +Levine +Levine's +Leviticus +Leviticus's +Levitt +Levitt's +Levy +Levy's +Lew +Lew's +Lewinsky +Lewinsky's +Lewis +Lewis's +Lexington +Lexington's +Lexus +Lexus's +Lhasa +Lhasas +Lhasa's +Lhotse +Lhotse's +Li +Lily +Li's +Liaoning +Liaoning's +Libby +Libby's +Liberace +Liberace's +Liberal +Liberia +Liberia's +Liberian +Liberians +Liberian's +Libra +Libras +Libra's +LibreOffice +LibreOffice's +Libreville +Libreville's +Librium +Librium's +Libya +Libya's +Libyan +Libyans +Libyan's +Lichtenstein +Lichtenstein's +Lidia +Lidia's +Lie +Lie's +Lieberman +Lieberman's +Liebfraumilch +Liebfraumilch's +Liechtenstein +Liechtensteiner +Liechtensteiners +Liechtenstein's +Liechtensteiner +Liechtensteiner's +Liege +Liege's +Lieut +Lila +Lila's +Lilia +Lilia's +Lilian +Lilian's +Liliana +Liliana's +Lilith +Lilith's +Liliuokalani +Liliuokalani's +Lille +Lille's +Lillian +Lillian's +Lillie +Lillie's +Lilliput +Lilliput's +Lilliputian +Lilliputians +Lilliputian's +Lilly +Lilly's +Lilongwe +Lilongwe's +Lily +Lily's +Lima +Lima's +Limbaugh +Limbaugh's +Limbo +Limburger +Limburger's +Limoges +Limoges's +Limousin +Limousin's +Limpopo +Limpopo's +Lin +Lin's +Lina +Lina's +Lincoln +Lincolns +Lincoln's +Lind +Lind's +Linda +Linda's +Lindbergh +Lindbergh's +Lindsay +Lindsay's +Lindsey +Lindsey's +Lindy +Lindy's +Linnaeus +Linnaeus's +Linotype +Linotype's +Linton +Linton's +Linus +Linus's +Linux +Linuxes +Linux's +Linwood +Linwood's +Lionel +Lionel's +Lipizzaner +Lipizzaner's +Lippi +Lippi's +Lippmann +Lippmann's +Lipscomb +Lipscomb's +Lipton +Lipton's +Lisa +Lisa's +Lisbon +Lisbon's +Lissajous +Lissajous's +Lister +Lister's +Listerine +Listerine's +Liston +Liston's +Liszt +Liszt's +Lithuania +Lithuania's +Lithuanian +Lithuanians +Lithuanian's +Little +Little's +Litton +Litton's +Liverpool +Liverpool's +Liverpudlian +Liverpudlians +Liverpudlian's +Livia +Livia's +Livingston +Livingston's +Livingstone +Livingstone's +Livonia +Livonia's +Livy +Livy's +Liz +Liz's +Liza +Liza's +Lizzie +Lizzie's +Lizzy +Lizzy's +Ljubljana +Ljubljana's +Llewellyn +Llewellyn's +Lloyd +Lloyd's +Ln +Loafer +Loafers +Loafer's +Lobachevsky +Lobachevsky's +Lochinvar +Lochinvar's +Locke +Locke's +Lockean +Lockean's +Lockheed +Lockheed's +Lockwood +Lockwood's +Lodge +Lodge's +Lodz +Lodz's +Loewe +Loewe's +Loewi +Loewi's +Loews +Loews's +Logan +Logan's +Lohengrin +Lohengrin's +Loire +Loire's +Lois +Lois's +Loki +Loki's +Lola +Lola's +Lolita +Lolita's +Lollard +Lollard's +Lollobrigida +Lollobrigida's +Lombard +Lombard's +Lombardi +Lombardi's +Lombardy +Lombardy's +Lome +Lome's +Lon +Lon's +London +Londoner +Londoners +London's +Londoner +Londoner's +Long +Long's +Longfellow +Longfellow's +Longstreet +Longstreet's +Longueuil +Lonnie +Lonnie's +Lopez +Lopez's +Lora +Lora's +Loraine +Loraine's +Lord +Lords +Lord's +Lordship +Lordships +Lordship's +Lorelei +Lorelei's +Loren +Loren's +Lorena +Lorena's +Lorene +Lorene's +Lorentz +Lorentz's +Lorentzian +Lorenz +Lorenz's +Lorenzo +Lorenzo's +Loretta +Loretta's +Lori +Lori's +Lorie +Lorie's +Lorna +Lorna's +Lorraine +Lorraine's +Lorre +Lorre's +Lorrie +Lorrie's +Los +Lot +Lot's +Lothario +Lotharios +Lothario's +Lott +Lott's +Lottie +Lottie's +Lou +Lou's +Louella +Louella's +Louie +Louie's +Louis +Louis's +Louisa +Louisa's +Louise +Louise's +Louisiana +Louisiana's +Louisianan +Louisianans +Louisianan's +Louisianian +Louisianians +Louisianian's +Louisville +Louisville's +Lourdes +Lourdes's +Louvre +Louvre's +Love +Love's +Lovecraft +Lovecraft's +Lovelace +Lovelace's +Lowe +Lowe's +Lowell +Lowell's +Lowenbrau +Lowenbrau's +Lowery +Lowery's +Lowlands +Loyang +Loyang's +Loyd +Loyd's +Loyola +Loyola's +Lr +Lt +Ltd +Lu +Lu's +Luanda +Luanda's +Luann +Luann's +Lubavitcher +Lubavitcher's +Lubbock +Lubbock's +Lubumbashi +Lubumbashi's +Lucas +Lucas's +Luce +Luce's +Lucia +Lucia's +Lucian +Lucian's +Luciano +Luciano's +Lucien +Lucien's +Lucifer +Lucifer's +Lucile +Lucile's +Lucille +Lucille's +Lucinda +Lucinda's +Lucio +Lucio's +Lucite +Lucites +Lucite's +Lucius +Lucius's +Lucknow +Lucknow's +Lucretia +Lucretia's +Lucretius +Lucretius's +Lucy +Lucy's +Luddite +Luddites +Luddite's +Ludhiana +Ludhiana's +Ludwig +Ludwig's +Luella +Luella's +Lufthansa +Lufthansa's +Luftwaffe +Luftwaffe's +Luger +Luger's +Lugosi +Lugosi's +Luigi +Luigi's +Luis +Luis's +Luisa +Luisa's +Luke +Luke's +Lula +Lula's +Lully +Lully's +Lulu +Lulu's +Lumiere +Lumiere's +Luna +Luna's +Lupe +Lupe's +Lupercalia +Lupercalia's +Lupus +Lupus's +Luria +Luria's +Lusaka +Lusaka's +Lusitania +Lusitania's +Luther +Luther's +Lutheran +Lutherans +Lutheran's +Lutheranism +Lutheranisms +Lutheranism's +Luvs +Luvs's +Luxembourg +Luxembourger +Luxembourgers +Luxembourg's +Luxembourger +Luxembourger's +Luxembourgian +Luz +Luz's +Luzon +Luzon's +Lvov +Lvov's +LyX +LyX's +Lyallpur +Lycra +Lycra's +Lycurgus +Lycurgus's +Lydia +Lydia's +Lydian +Lydians +Lydian's +Lyell +Lyell's +Lyle +Lyle's +Lyly +Lyly's +Lyman +Lyman's +Lyme +Lyme's +Lynch +Lynch's +Lynda +Lynda's +Lyndon +Lyndon's +Lynette +Lynette's +Lynn +Lynn's +Lynne +Lynne's +Lynnette +Lynnette's +Lyon +Lyons +Lyon's +Lyons +Lyons's +Lyra +Lyra's +Lysenko +Lysenko's +Lysistrata +Lysistrata's +Lysol +Lysol's +M +Ming +Ms +M's +Mable +MA +MA's +MASH +MB +MB's +MBA +MBA's +MC +MCI +MCI's +MD +MD's +MDT +ME +MEGO +MEGOs +MFA +MFA's +MGM +MGM's +MHz +MI +MI's +MIA +MIDI +MIDI's +MIPS +MIRV +MIT +MIT's +MM +MN +MO +MOOC +MP +MP's +MPEG +MRI +MRI's +MS +MS's +MSG +MSG's +MST +MST's +MSW +MT +MT's +MTV +MTV's +MVP +MVP's +MW +Maalox +Maalox's +Mabel +Mabel's +Mable +Mable's +Mac +Mac's +MacArthur +MacArthur's +MacBride +MacBride's +MacDonald +MacDonald's +MacLeish +MacLeish's +Macao +Macao's +Macaulay +Macaulay's +Macbeth +Macbeth's +Maccabees +Maccabeus +Maccabeus's +Mace +Mace's +Macedon +Macedon's +Macedonia +Macedonia's +Macedonian +Macedonians +Macedonian's +Mach +Mach's +Machiavelli +Machiavelli's +Machiavellian +Machiavellian's +Macias +Macias's +Macintosh +Macintosh's +Mack +Mack's +Mackenzie +Mackenzie's +Mackinac +Mackinac's +Mackinaw +Mackinaw's +Macmillan +Macmillan's +Macon +Macon's +Macumba +Macumba's +Macy +Macy's +Madagascan +Madagascans +Madagascan's +Madagascar +Madagascar's +Madam +Madden +Madden's +Maddox +Maddox's +Madeira +Madeiras +Madeira's +Madeleine +Madeleine's +Madeline +Madeline's +Madelyn +Madelyn's +Madge +Madge's +Madison +Madison's +Madonna +Madonnas +Madonna's +Madras +Madras's +Madrid +Madrid's +Madurai +Madurai's +Mae +Mae's +Maeterlinck +Maeterlinck's +Mafia +Mafias +Mafia's +Mafioso +Mafioso's +Magdalena +Magdalena's +Magdalene +Magdalene's +Magellan +Magellan's +Magellanic +Magellanic's +Maggie +Maggie's +Maghreb +Maghreb's +Magi +Maginot +Maginot's +Magnificat +Magnitogorsk +Magnitogorsk's +Magog +Magog's +Magoo +Magoo's +Magritte +Magritte's +Magsaysay +Magsaysay's +Magus +Magyar +Magyars +Magyar's +Mahabharata +Mahabharata's +Maharashtra +Maharashtra's +Mahavira +Mahavira's +Mahayana +Mahayana's +Mahayanist +Mahayanist's +Mahdi +Mahdi's +Mahfouz +Mahfouz's +Mahican +Mahicans +Mahican's +Mahler +Mahler's +Mai +Mai's +Maidenform +Maidenform's +Maigret +Maigret's +Mailer +Mailer's +Maillol +Maillol's +Maiman +Maiman's +Maimonides +Maimonides's +Maine +Mainer +Mainers +Maine's +Mainer +Mainer's +Maisie +Maisie's +Maitreya +Maitreya's +Maj +Majesty +Major +Major's +Majorca +Majorca's +Majuro +Majuro's +Makarios +Makarios's +Maker +Maker's +Malabar +Malabar's +Malabo +Malabo's +Malacca +Malacca's +Malachi +Malachi's +Malagasy +Malagasy's +Malamud +Malamud's +Malaprop +Malaprop's +Malawi +Malawi's +Malawian +Malawians +Malawian's +Malay +Malays +Malay's +Malaya +Malaya's +Malayalam +Malayalam's +Malayan +Malayans +Malayan's +Malaysia +Malaysia's +Malaysian +Malaysians +Malaysian's +Malcolm +Malcolm's +Maldive +Maldives +Maldive's +Maldives +Maldives's +Maldivian +Maldivians +Maldivian's +Maldonado +Maldonado's +Male +Male's +Mali +Mali's +Malian +Malians +Malian's +Malibu +Malibu's +Malinda +Malinda's +Malinowski +Malinowski's +Mallarme +Mallarme's +Mallomars +Mallomars's +Mallory +Mallory's +Malone +Malone's +Malory +Malory's +Malplaquet +Malplaquet's +Malraux +Malraux's +Malta +Malta's +Maltese +Maltese's +Malthus +Malthus's +Malthusian +Malthusians +Malthusian's +Mameluke +Mameluke's +Mamet +Mamet's +Mamie +Mamie's +Mamore +Mamore's +Man +Man's +Managua +Managua's +Manama +Manama's +Manasseh +Manasseh's +Manchester +Manchester's +Manchu +Manchus +Manchu's +Manchuria +Manchuria's +Manchurian +Manchurian's +Mancini +Mancini's +Mancunian +Mancunians +Mancunian's +Mandalay +Mandalay's +Mandarin +Mandarin's +Mandela +Mandela's +Mandelbrot +Mandelbrot's +Mandingo +Mandingo's +Mandrell +Mandrell's +Mandy +Mandy's +Manet +Manet's +Manfred +Manfred's +Manhattan +Manhattans +Manhattan's +Mani +Mani's +Manichean +Manichean's +Manila +Manilas +Manila's +Manitoba +Manitoba's +Manitoulin +Manitoulin's +Manley +Manley's +Mann +Manning +Mann's +Mannheim +Mannheim's +Manning +Manning's +Mansfield +Mansfield's +Manson +Manson's +Mantegna +Mantegna's +Mantle +Mantle's +Manuel +Manuel's +Manuela +Manuela's +Manx +Manx's +Mao +Mao's +Maoism +Maoisms +Maoism's +Maoist +Maoists +Maoist's +Maori +Maoris +Maori's +Mapplethorpe +Mapplethorpe's +Maputo +Maputo's +Mar +Mars +Mar's +Mara +Mara's +Maracaibo +Maracaibo's +Marat +Marat's +Maratha +Maratha's +Marathi +Marathi's +Marathon +Marathon's +Marc +Marc's +Marceau +Marceau's +Marcel +Marcel's +Marcelino +Marcelino's +Marcella +Marcella's +Marcelo +Marcelo's +March +Marches +March's +Marci +Marci's +Marcia +Marcia's +Marciano +Marciano's +Marcie +Marcie's +Marco +Marcos +Marco's +Marconi +Marconi's +Marcos +Marcos's +Marcus +Marcus's +Marcuse +Marcy +Marcy's +Marduk +Marduk's +Margaret +Margaret's +Margarita +Margarita's +Margarito +Margarito's +Marge +Marge's +Margery +Margery's +Margie +Margie's +Margo +Margo's +Margot +Margret +Margret's +Margrethe +Margrethe's +Marguerite +Marguerite's +Mari +Maris +Mari's +Maria +Maria's +MariaDB +MariaDB's +Marian +Marian's +Mariana +Marianas +Mariana's +Marianas +Marianas's +Marianne +Marianne's +Mariano +Mariano's +Maribel +Maribel's +Maricela +Maricela's +Marie +Marie's +Marietta +Marietta's +Marilyn +Marilyn's +Marin +Marin's +Marina +Marina's +Marine +Marines +Marine's +Mario +Mario's +Marion +Marion's +Maris +Maris's +Marisa +Marisa's +Marisol +Marisol's +Marissa +Marissa's +Maritain +Maritain's +Maritza +Maritza's +Mariupol +Marius +Marius's +Marjorie +Marjorie's +Marjory +Marjory's +Mark +Marks +Mark's +Markab +Markab's +Markham +Markham's +Markov +Markov's +Marks +Marks's +Marla +Marla's +Marlboro +Marlboro's +Marlborough +Marlborough's +Marlene +Marlene's +Marley +Marley's +Marlin +Marlin's +Marlon +Marlon's +Marlowe +Marlowe's +Marmara +Marmara's +Marne +Marne's +Maronite +Maronite's +Marple +Marple's +Marquesas +Marquesas's +Marquette +Marquette's +Marquez +Marquez's +Marquis +Marquis's +Marquita +Marquita's +Marrakesh +Marrakesh's +Marriott +Marriott's +Mars +Marses +Mars's +Marsala +Marsala's +Marseillaise +Marseillaises +Marseillaise's +Marseilles +Marseilles's +Marsh +Marsh's +Marsha +Marsha's +Marshall +Marshall's +Marta +Marta's +Martel +Martel's +Martha +Martha's +Martial +Martial's +Martian +Martians +Martian's +Martin +Martin's +Martina +Martina's +Martinez +Martinez's +Martinique +Martinique's +Marty +Marty's +Marva +Marva's +Marvell +Marvell's +Marvin +Marvin's +Marx +Marx's +Marxian +Marxism +Marxisms +Marxism's +Marxist +Marxists +Marxist's +Mary +Mary's +Maryann +Maryann's +Maryanne +Maryanne's +Maryellen +Maryellen's +Maryland +Marylander +Maryland's +Marylander +Marylander's +Marylou +Marylou's +Masada +Masada's +Masai +Masai's +Masaryk +Masaryk's +Mascagni +Mascagni's +Masefield +Masefield's +Maserati +Maserati's +Maseru +Maseru's +Mashhad +Mashhad's +Mason +Masons +Mason's +Masonic +Masonic's +Masonite +Masonite's +Mass +Masses +Mass's +Massachusetts +Massachusetts's +Massasoit +Massasoit's +Massenet +Massenet's +Massey +Massey's +Master +Masters +MasterCard +MasterCard's +Masters +Masters's +Mather +Mather's +Matheson +Matheson's +Mathew +Mathews +Mathew's +Mathews +Mathews's +Mathewson +Mathewson's +Mathias +Mathias's +Mathis +Mathis's +Matilda +Matilda's +Matisse +Matisse's +Matlab +Matlab's +Matt +Matt's +Mattel +Mattel's +Matterhorn +Matterhorn's +Matthew +Matthews +Matthew's +Matthews +Matthews's +Matthias +Matthias's +Mattie +Mattie's +Maud +Maud's +Maude +Maude's +Maugham +Maugham's +Maui +Maui's +Maupassant +Maupassant's +Maura +Maura's +Maureen +Maureen's +Mauriac +Mauriac's +Maurice +Maurice's +Mauricio +Mauricio's +Maurine +Maurine's +Mauritania +Mauritania's +Mauritanian +Mauritanians +Mauritanian's +Mauritian +Mauritians +Mauritian's +Mauritius +Mauritius's +Mauro +Mauro's +Maurois +Maurois's +Mauryan +Mauryan's +Mauser +Mauser's +Mavis +Mavis's +Max +Max's +Maximilian +Maximilian's +Maxine +Maxine's +Maxwell +Maxwell's +May +Mayer +Mays +May's +Maya +Mayas +Maya's +Mayan +Mayans +Mayan's +Mayer +Mayer's +Mayfair +Mayfair's +Mayflower +Mayflower's +Maynard +Maynard's +Mayo +Mayo's +Maypole +Mayra +Mayra's +Mays +Mays's +Maytag +Maytag's +Mazama +Mazama's +Mazarin +Mazarin's +Mazatlan +Mazatlan's +Mazda +Mazda's +Mazola +Mazola's +Mazzini +Mazzini's +Mb +Mb's +Mbabane +Mbabane's +Mbini +Mbini's +McAdam +McAdam's +McBride +McBride's +McCain +McCain's +McCall +McCall's +McCarthy +McCarthy's +McCarthyism +McCarthyism's +McCartney +McCartney's +McCarty +McCarty's +McClain +McClain's +McClellan +McClellan's +McClure +McClure's +McConnell +McConnell's +McCormick +McCormick's +McCoy +McCoy's +McCray +McCray's +McCullough +McCullough's +McDaniel +McDaniel's +McDonald +McDonald's +McDonnell +McDonnell's +McDowell +McDowell's +McEnroe +McEnroe's +McFadden +McFadden's +McFarland +McFarland's +McGee +McGee's +McGovern +McGovern's +McGowan +McGowan's +McGuffey +McGuffey's +McGuire +McGuire's +McIntosh +McIntosh's +McIntyre +McIntyre's +McKay +McKay's +McKee +McKee's +McKenzie +McKenzie's +McKinley +McKinley's +McKinney +McKinney's +McKnight +McKnight's +McLaughlin +McLaughlin's +McLean +McLean's +McLeod +McLeod's +McLuhan +McLuhan's +McMahon +McMahon's +McMillan +McMillan's +McNamara +McNamara's +McNaughton +McNaughton's +McNeil +McNeil's +McPherson +McPherson's +McQueen +McQueen's +McVeigh +McVeigh's +Md +Md's +Me +Mead +Mead's +Meade +Meade's +Meadows +Meadows's +Meagan +Meagan's +Meany +Meany's +Mecca +Meccas +Mecca's +Medan +Medan's +Medea +Medea's +Medellin +Medellin's +Media +Media's +Medicaid +Medicaids +Medicaid's +Medicare +Medicares +Medicare's +Medici +Medici's +Medina +Medina's +Mediterranean +Mediterraneans +Mediterranean's +Medusa +Medusa's +Meg +Meg's +Megan +Megan's +Meghan +Meghan's +Meier +Meier's +Meighen +Meighen's +Meiji +Meiji's +Meir +Meir's +Mejia +Mejia's +Mekong +Mekong's +Mel +Mel's +Melanesia +Melanesia's +Melanesian +Melanesian's +Melanie +Melanie's +Melba +Melba's +Melbourne +Melbourne's +Melchior +Melchior's +Melchizedek +Melchizedek's +Melendez +Melendez's +Melinda +Melinda's +Melisa +Melisa's +Melisande +Melisande's +Melissa +Melissa's +Mellon +Mellon's +Melody +Melody's +Melpomene +Melpomene's +Melton +Melton's +Melva +Melva's +Melville +Melville's +Melvin +Melvin's +Memcached +Memcached's +Memling +Memling's +Memphis +Memphis's +Menander +Menander's +Mencius +Mencius's +Mencken +Mencken's +Mendel +Mendel's +Mendeleev +Mendeleev's +Mendelian +Mendelian's +Mendelssohn +Mendelssohn's +Mendez +Mendez's +Mendocino +Mendocino's +Mendoza +Mendoza's +Menelaus +Menelaus's +Menelik +Menelik's +Menes +Menes's +Mengzi +Menkalinan +Menkalinan's +Menkar +Menkar's +Menkent +Menkent's +Mennen +Mennen's +Mennonite +Mennonites +Mennonite's +Menominee +Menominee's +Menotti +Menotti's +Mensa +Mensa's +Mentholatum +Mentholatum's +Menuhin +Menuhin's +Menzies +Menzies's +Mephisto +Mephistopheles +Mephistopheles's +Merak +Merak's +Mercado +Mercado's +Mercator +Mercator's +Mercedes +Mercedes's +Mercer +Mercer's +Mercia +Mercia's +Merck +Merck's +Mercurochrome +Mercurochrome's +Mercury +Mercuries +Mercury's +Meredith +Meredith's +Merino +Merino's +Merle +Merle's +Merlin +Merlin's +Merlot +Merlot's +Merovingian +Merovingian's +Merriam +Merriam's +Merrick +Merrick's +Merrill +Merrill's +Merrimack +Merrimack's +Merritt +Merritt's +Merthiolate +Merthiolate's +Merton +Merton's +Mervin +Mervin's +Mesa +Mesa's +Mesabi +Mesabi's +Mesmer +Mesmer's +Mesolithic +Mesolithic's +Mesopotamia +Mesopotamia's +Mesopotamian +Mesozoic +Mesozoic's +Messerschmidt +Messerschmidt's +Messiaen +Messiaen's +Messiah +Messiah's +Messiahs +Messianic +Messieurs +Metallica +Metallica's +Metamucil +Metamucil's +Methodism +Methodisms +Methodism's +Methodist +Methodists +Methodist's +Methuselah +Methuselah's +Metternich +Metternich's +Meuse +Meuse's +Mex +Mexicali +Mexicali's +Mexican +Mexicans +Mexican's +Mexico +Mexico's +Meyer +Meyers +Meyer's +Meyerbeer +Meyerbeer's +Meyers +Meyers's +Mfume +Mfume's +Mg +Mg's +Mgr +MiG +MiG's +Mia +Mia's +Miami +Miamis +Miami's +Miaplacidus +Miaplacidus's +Micah +Micah's +Micawber +Micawber's +Mich +Mich's +Michael +Michael's +Michaelmas +Michaelmases +Michaelmas's +Micheal +Micheal's +Michel +Michel's +Michelangelo +Michelangelo's +Michele +Michele's +Michelin +Michelin's +Michelle +Michelle's +Michelob +Michelob's +Michelson +Michelson's +Michigan +Michigan's +Michigander +Michiganders +Michigander's +Michiganite +Mick +Mick's +Mickey +Mickey's +Mickie +Mickie's +Micky +Micky's +Micmac +Micmacs +Micmac's +Micronesia +Micronesia's +Micronesian +Micronesian's +Microsoft +Microsoft's +Midas +Midas's +Middleton +Middleton's +Mideast +Mideastern +Midland +Midlands +Midland's +Midway +Midway's +Midwest +Midwest's +Midwestern +Midwesterner +Midwestern's +Miguel +Miguel's +Mike +Mike's +Mikhail +Mikhail's +Mikoyan +Mikoyan's +Milagros +Milagros's +Milan +Milan's +Milanese +Mildred +Mildred's +Miles +Miles's +Milford +Milford's +Milken +Milken's +Mill +Miller +Mills +Mill's +Millard +Millard's +Millay +Millay's +Miller +Miller's +Millet +Millet's +Millicent +Millicent's +Millie +Millie's +Millikan +Millikan's +Mills +Mills's +Milne +Milne's +Milo +Milo's +Milosevic +Milosevic's +Milquetoast +Milquetoast's +Miltiades +Miltiades's +Milton +Milton's +Miltonic +Miltonic's +Miltown +Miltown's +Milwaukee +Milwaukee's +Mimi +Mimi's +Mimosa +Mimosa's +Min +Min's +Minamoto +Minamoto's +Mindanao +Mindanao's +Mindoro +Mindoro's +Mindy +Mindy's +Minerva +Minerva's +Ming +Ming's +Mingus +Mingus's +Minn +Minneapolis +Minneapolis's +Minnelli +Minnelli's +Minnesota +Minnesota's +Minnesotan +Minnesotans +Minnesotan's +Minnie +Minnie's +Minoan +Minoans +Minoan's +Minolta +Minolta's +Minos +Minos's +Minot +Minot's +Minotaur +Minotaur's +Minsk +Minsk's +Minsky +Minsky's +Mintaka +Mintaka's +Minuit +Minuit's +Minuteman +Minuteman's +Miocene +Miocene's +Mir +Mir's +Mira +Mira's +Mirabeau +Mirabeau's +Mirach +Mirach's +Miranda +Miranda's +Mirfak +Mirfak's +Miriam +Miriam's +Miro +Miro's +Mirzam +Mirzam's +Miskito +Miskito's +Miss +Mississauga +Mississauga's +Mississippi +Mississippi's +Mississippian +Mississippians +Mississippian's +Missouri +Missouri's +Missourian +Missourians +Missourian's +Missy +Missy's +Mistassini +Mistassini's +Mister +Mistress +Misty +Misty's +Mitch +Mitch's +Mitchel +Mitchel's +Mitchell +Mitchell's +Mitford +Mitford's +Mithra +Mithra's +Mithridates +Mithridates's +Mitsubishi +Mitsubishi's +Mitterrand +Mitterrand's +Mitty +Mitty's +Mitzi +Mitzi's +Mixtec +Mixtec's +Mizar +Mizar's +Mk +Mlle +Mme +Mmes +Mn +Mn's +Mnemosyne +Mnemosyne's +Mo +Mo's +Mobil +Mobil's +Mobile +Mobile's +Mobutu +Mobutu's +Modesto +Modesto's +Modigliani +Modigliani's +Moe +Moe's +Moet +Moet's +Mogadishu +Mogadishu's +Mogul +Moguls +Mogul's +Mohacs +Mohacs's +Mohamed +Mohamed's +Mohammad +Mohammad's +Mohammedan +Mohammedans +Mohammedan's +Mohammedanism +Mohammedanisms +Mohammedanism's +Mohave +Mohaves +Mohave's +Mohawk +Mohawks +Mohawk's +Mohegan +Moho +Moho's +Mohorovicic +Mohorovicic's +Moira +Moira's +Moises +Moises's +Moiseyev +Moiseyev's +Mojave +Mojaves +Mojave's +Moldavia +Moldavia's +Moldavian +Moldova +Moldova's +Moldovan +Moliere +Moliere's +Molina +Molina's +Moll +Moll's +Mollie +Mollie's +Molly +Molly's +Molnar +Molnar's +Moloch +Moloch's +Molokai +Molokai's +Molotov +Molotov's +Moluccas +Moluccas's +Mombasa +Mombasa's +Mon +Mons +Mon's +Mona +Mona's +Monacan +Monaco +Monaco's +Mondale +Mondale's +Monday +Mondays +Monday's +Mondrian +Mondrian's +Monegasque +Monegasques +Monegasque's +Monera +Monera's +Monet +Monet's +MongoDB +MongoDB's +Mongol +Mongols +Mongol's +Mongolia +Mongolia's +Mongolian +Mongolians +Mongolian's +Mongolic +Mongolic's +Mongoloid +Monica +Monica's +Monique +Monique's +Monk +Monk's +Monmouth +Monmouth's +Monongahela +Monongahela's +Monroe +Monroe's +Monrovia +Monrovia's +Monsanto +Monsanto's +Monsieur +Monsieur's +Monsignor +Monsignors +Monsignor's +Mont +Mont's +Montague +Montague's +Montaigne +Montaigne's +Montana +Montana's +Montanan +Montanans +Montanan's +Montcalm +Montcalm's +Monte +Monte's +Montenegrin +Montenegrin's +Montenegro +Montenegro's +Monterrey +Monterrey's +Montesquieu +Montesquieu's +Montessori +Montessori's +Monteverdi +Monteverdi's +Montevideo +Montevideo's +Montezuma +Montezuma's +Montgolfier +Montgolfier's +Montgomery +Montgomery's +Monticello +Monticello's +Montoya +Montoya's +Montpelier +Montpelier's +Montrachet +Montrachet's +Montreal +Montreal's +Montserrat +Montserrat's +Monty +Monty's +Moody +Moody's +Moog +Moog's +Moon +Moon's +Mooney +Mooney's +Moor +Moors +Moor's +Moore +Moore's +Moorish +Moorish's +Morales +Morales's +Moran +Moran's +Moravia +Moravia's +Moravian +Moravian's +Mordred +Mordred's +More +More's +Moreno +Moreno's +Morgan +Morgans +Morgan's +Moriarty +Moriarty's +Morin +Morin's +Morison +Morison's +Morita +Morita's +Morley +Morley's +Mormon +Mormons +Mormon's +Mormonism +Mormonisms +Mormonism's +Moro +Moro's +Moroccan +Moroccans +Moroccan's +Morocco +Morocco's +Moroni +Moroni's +Morpheus +Morpheus's +Morphy +Morphy's +Morris +Morris's +Morrison +Morrison's +Morrow +Morrow's +Morse +Morse's +Mort +Mort's +Mortimer +Mortimer's +Morton +Morton's +Mosaic +Mosaic's +Moscow +Moscow's +Moseley +Moseley's +Moselle +Moselle's +Moses +Moses's +Mosley +Mosley's +Moss +Moss's +Mosul +Mosul's +Motorola +Motorola's +Motown +Motown's +Motrin +Motrin's +Mott +Mott's +Moulton +Moulton's +Mount +Mount's +Mountbatten +Mountbatten's +Mountie +Mounties +Mountie's +Moussorgsky +Moussorgsky's +Mouthe +Mouthe's +Mouton +Mouton's +Mowgli +Mowgli's +Mozambican +Mozambicans +Mozambican's +Mozambique +Mozambique's +Mozart +Mozart's +Mozilla +Mozilla's +Mr +Mrs +Mr's +Ms +Mses +Msgr +Mt +Muawiya +Muawiya's +Mubarak +Mubarak's +Mueller +Mueller's +Muenster +Muensters +Muenster's +Mugabe +Mugabe's +Muhammad +Muhammad's +Muhammadan +Muhammadans +Muhammadan's +Muhammadanism +Muhammadanisms +Muhammadanism's +Muir +Muir's +Mujib +Mujib's +Mulder +Mulder's +Mullen +Mullen's +Muller +Muller's +Mulligan +Mulligan's +Mullikan +Mullikan's +Mullins +Mullins's +Mulroney +Mulroney's +Multan +Multan's +Multics +Mumbai +Mumbai's +Mumford +Mumford's +Munch +Munch's +Munchhausen +Munchhausen's +Munich +Munich's +Munoz +Munoz's +Munro +Munro's +Munster +Munster's +Muppet +Muppet's +Murasaki +Murasaki's +Murat +Murat's +Murchison +Murchison's +Murcia +Murdoch +Murdoch's +Muriel +Muriel's +Murillo +Murillo's +Murine +Murine's +Murmansk +Murmansk's +Murphy +Murphy's +Murray +Murray's +Murrow +Murrow's +Murrumbidgee +Murrumbidgee's +Muscat +Muscat's +Muscovite +Muscovite's +Muscovy +Muscovy's +Muse +Muse's +Musharraf +Musharraf's +Musial +Musial's +Muskogee +Muskogee's +Muslim +Muslims +Muslim's +Mussolini +Mussolini's +Mussorgsky +Mussorgsky's +Mutsuhito +Mutsuhito's +Muzak +Muzak's +MySQL +MySQL's +MySpace +MySpace's +Myanmar +Myanmar's +Mycenae +Mycenae's +Mycenaean +Mycenaean's +Myers +Myers's +Mylar +Mylars +Mylar's +Myles +Myles's +Myra +Myra's +Myrdal +Myrdal's +Myrna +Myrna's +Myron +Myron's +Myrtle +Myrtle's +Mysore +Mysore's +Myst +Myst's +N'Djamena +N +Ned +N's +NAACP +NAACP's +NAFTA +NAFTA's +NASA +NASA's +NASCAR +NASCAR's +NASDAQ +NASDAQ's +NATO +NATO's +NB +NBA +NBA's +NBC +NBC's +NBS +NC +NCAA +NCAA's +NCO +ND +NE +NE's +NEH +NF +NFC +NFL +NFL's +NH +NHL +NHL's +NIH +NIMBY +NJ +NLRB +NM +NORAD +NORAD's +NOW +NP +NPR +NPR's +NR +NRA +NRC +NS +NSA +NSA's +NSC +NSF +NSFW +NT +NV +NVIDIA +NVIDIA's +NW +NW's +NWT +NY +NYC +NYSE +NZ +Na +Na's +Nabisco +Nabisco's +Nabokov +Nabokov's +Nader +Nader's +Nadia +Nadia's +Nadine +Nadine's +Nagasaki +Nagasaki's +Nagoya +Nagoya's +Nagpur +Nagpur's +Nagy +Nagy's +Nahuatl +Nahuatls +Nahuatl's +Nahum +Nahum's +Naipaul +Naipaul's +Nair +Nair's +Nairobi +Nairobi's +Naismith +Naismith's +Nam +Nam's +Namath +Namath's +Namibia +Namibia's +Namibian +Namibians +Namibian's +Nan +Nan's +Nanak +Nanak's +Nanchang +Nanchang's +Nancy +Nancy's +Nanette +Nanette's +Nanjing +Nanjing's +Nannie +Nannie's +Nanook +Nanook's +Nansen +Nansen's +Nantes +Nantes's +Nantucket +Nantucket's +Naomi +Naomi's +Naphtali +Naphtali's +Napier +Napier's +Naples +Naples's +Napoleon +Napoleons +Napoleon's +Napoleonic +Napoleonic's +Napster +Napster's +Narcissus +Narcissus's +Narmada +Narmada's +Narnia +Narnia's +Narraganset +Narragansett +Narragansett's +Nash +Nash's +Nashua +Nashua's +Nashville +Nashville's +Nassau +Nassau's +Nasser +Nasser's +Nat +Nat's +Natalia +Natalia's +Natalie +Natalie's +Natasha +Natasha's +Natchez +Natchez's +Nate +Nation +Nate's +Nathan +Nathans +Nathan's +Nathaniel +Nathaniel's +Nathans +Nathans's +Nation +Nation's +Nationwide +Nationwide's +Nativity +Nativity's +Naugahyde +Naugahyde's +Nauru +Nauru's +Nautilus +Nautilus's +Navajo +Navajos +Navajo's +Navajoes +Navarre +Navarre's +Navarro +Navarro's +Navratilova +Navratilova's +Navy +Nazarene +Nazarene's +Nazareth +Nazareth's +Nazca +Nazca's +Nazi +Nazis +Nazi's +Nazism +Nazisms +Nazism's +Nb +Nb's +Nd +Nd's +Ndjamena +Ndjamena's +Ne +Ne's +NeWS +NeWSes +Neal +Neal's +Neanderthal +Neanderthals +Neanderthal's +Neapolitan +Neapolitan's +Neb +Nebr +Nebraska +Nebraska's +Nebraskan +Nebraskans +Nebraskan's +Nebuchadnezzar +Nebuchadnezzar's +Ned +Ned's +Nefertiti +Nefertiti's +Negev +Negev's +Negress +Negresses +Negress's +Negritude +Negro +Negros +Negro's +Negroes +Negroid +Negroids +Negroid's +Negros +Negros's +Nehemiah +Nehemiah's +Nehru +Nehru's +Neil +Neil's +Nelda +Nelda's +Nell +Nell's +Nellie +Nellie's +Nelly +Nelly's +Nelsen +Nelsen's +Nelson +Nelson's +Nembutal +Nembutal's +Nemesis +Nemesis's +Neo +Neo's +Neogene +Neogene's +Neolithic +Nepal +Nepal's +Nepalese +Nepalese's +Nepali +Nepalis +Nepali's +Neptune +Neptune's +Nereid +Nereid's +Nerf +Nerf's +Nero +Nero's +Neruda +Neruda's +Nescafe +Nescafe's +Nesselrode +Nesselrode's +Nestle +Nestle's +Nestor +Nestor's +Nestorius +Nestorius's +Netflix +Netflix's +Netherlander +Netherlanders +Netherlander's +Netherlands +Netherlands's +Netscape +Netscape's +Nettie +Nettie's +Netzahualcoyotl +Netzahualcoyotl's +Nev +Nev's +Neva +Neva's +Nevada +Nevada's +Nevadan +Nevadans +Nevadan's +Nevadian +Nevis +Nevis's +Nevsky +Nevsky's +Newark +Newark's +Newcastle +Newcastle's +Newfoundland +Newfoundlander +Newfoundlands +Newfoundland's +Newman +Newman's +Newport +Newport's +Newsweek +Newsweek's +Newton +Newton's +Newtonian +Newtonian's +Nexis +Nexis's +Ngaliema +Ngaliema's +Nguyen +Nguyen's +Ni +Ni's +Niagara +Niagara's +Niamey +Niamey's +Nibelung +Nibelung's +Nicaea +Nicaea's +Nicaragua +Nicaragua's +Nicaraguan +Nicaraguans +Nicaraguan's +Niccolo +Niccolo's +Nice +Nice's +Nicene +Nicene's +Nichiren +Nichiren's +Nicholas +Nicholas's +Nichole +Nichole's +Nichols +Nichols's +Nicholson +Nicholson's +Nick +Nick's +Nickelodeon +Nickelodeon's +Nicklaus +Nicklaus's +Nickolas +Nickolas's +Nicobar +Nicobar's +Nicodemus +Nicodemus's +Nicola +Nicolas +Nicola's +Nicolas +Nicolas's +Nicole +Nicole's +Nicosia +Nicosia's +Niebuhr +Niebuhr's +Nielsen +Nielsen's +Nietzsche +Nietzsche's +Nieves +Nieves's +Nigel +Nigel's +Niger +Niger's +Nigeria +Nigeria's +Nigerian +Nigerians +Nigerian's +Nigerien +Nigerien's +Nightingale +Nightingale's +Nijinsky +Nijinsky's +Nike +Nike's +Nikita +Nikita's +Nikkei +Nikkei's +Nikki +Nikki's +Nikolai +Nikolai's +Nikon +Nikon's +Nile +Nile's +Nimitz +Nimitz's +Nimrod +Nimrod's +Nina +Nina's +Nineveh +Nineveh's +Nintendo +Nintendo's +Niobe +Niobe's +Nippon +Nippon's +Nipponese +Nipponese's +Nirenberg +Nirenberg's +Nirvana +Nirvana's +Nisan +Nisan's +Nisei +Nisei's +Nissan +Nissan's +Nita +Nita's +Nivea +Nivea's +Nixon +Nixon's +Nkrumah +Nkrumah's +No +Nos +No's +NoDoz +NoDoz's +Noah +Noah's +Nobel +Nobel's +Nobelist +Nobelists +Nobelist's +Noble +Noble's +Noe +Noe's +Noel +Noels +Noel's +Noelle +Noelle's +Noemi +Noemi's +Nokia +Nokia's +Nola +Nola's +Nolan +Nolan's +Nome +Nome's +Nona +Nona's +Nootka +Nootka's +Nora +Nora's +Norbert +Norbert's +Norberto +Norberto's +Nordic +Nordics +Nordic's +Noreen +Noreen's +Norfolk +Norfolk's +Noriega +Noriega's +Norma +Norma's +Norman +Normans +Norman's +Normand +Normand's +Normandy +Normandy's +Norplant +Norplant's +Norris +Norris's +Norse +Norse's +Norseman +Norseman's +Norsemen +Norsemen's +North +North's +Northampton +Northampton's +Northeast +Northeasts +Northeast's +Northerner +Northerner's +Northrop +Northrop's +Northrup +Northrup's +Norths +Northwest +Northwests +Northwest's +Norton +Norton's +Norw +Norway +Norway's +Norwegian +Norwegians +Norwegian's +Norwich +Norwich's +Nosferatu +Nosferatu's +Nostradamus +Nostradamus's +Nottingham +Nottingham's +Nouakchott +Nouakchott's +Noumea +Noumea's +Nov +Nov's +Nova +Nova's +Novartis +Novartis's +November +Novembers +November's +Novgorod +Novgorod's +Novocain +Novocains +Novocain's +Novocaine +Novokuznetsk +Novokuznetsk's +Novosibirsk +Novosibirsk's +Noxzema +Noxzema's +Noyce +Noyce's +Noyes +Noyes's +Np +Np's +Nubia +Nubia's +Nubian +Nubian's +Nukualofa +Nukualofa's +Numbers +Numbers's +Nunavut +Nunavut's +Nunez +Nunez's +Nunki +Nunki's +Nuremberg +Nuremberg's +Nureyev +Nureyev's +NutraSweet +NutraSweet's +NyQuil +NyQuil's +Nyasa +Nyasa's +Nyerere +Nyerere's +O'Brien +O'Brien's +O'Casey +O'Casey's +O'Connell +O'Connell's +O'Connor +O'Connor's +O'Donnell +O'Donnell's +O'Hara +O'Hara's +O'Higgins +O'Higgins's +O'Keeffe +O'Keeffe's +O'Neil +O'Neil's +O'Neill +O'Neill's +O'Rourke +O'Rourke's +O'Toole +O'Toole's +O +Os +O's +OAS +OAS's +OB +OCR +OD +ODs +OD's +OE +OED +OH +OHSA +OHSA's +OJ +OK +OKing +OKed +OKs +OK's +OMB +OMB's +ON +OPEC +OPEC's +OR +OS +OS's +OSHA +OSHA's +OSes +OT +OTB +OTC +OTOH +Oahu +Oahu's +Oakland +Oakland's +Oakley +Oakley's +Oates +Oates's +Oaxaca +Oaxaca's +Ob +Ob's +Obadiah +Obadiah's +Obama +Obama's +Obamacare +Oberlin +Oberlin's +Oberon +Oberon's +Ocaml +Ocaml's +Occam +Occam's +Occident +Occidental +Occidentals +Occidental's +Oceania +Oceania's +Oceanside +Oceanus +Oceanus's +Ochoa +Ochoa's +Oct +Oct's +Octavia +Octavia's +Octavian +Octavian's +Octavio +Octavio's +October +Octobers +October's +Odell +Odell's +Oder +Oder's +Odessa +Odessa's +Odets +Odets's +Odin +Odin's +Odis +Odis's +Odom +Odom's +Odysseus +Odysseus's +Odyssey +Odyssey's +Oedipal +Oedipal's +Oedipus +Oedipus's +Oersted +Oersted's +Ofelia +Ofelia's +Offenbach +Offenbach's +OfficeMax +OfficeMax's +Ogbomosho +Ogbomosho's +Ogden +Ogden's +Ogilvy +Ogilvy's +Oglethorpe +Oglethorpe's +Ohio +Ohio's +Ohioan +Ohioans +Ohioan's +Oise +Oise's +Ojibwa +Ojibwas +Ojibwa's +Okayama +Okeechobee +Okeechobee's +Okefenokee +Okefenokee's +Okhotsk +Okhotsk's +Okinawa +Okinawa's +Okinawan +Okla +Oklahoma +Oklahoma's +Oklahoman +Oklahoman's +Oktoberfest +Oktoberfest's +Ola +Ola's +Olaf +Olaf's +Olajuwon +Olajuwon's +Olav +Olav's +Oldenburg +Oldenburg's +Oldfield +Oldfield's +Oldsmobile +Oldsmobile's +Olduvai +Olduvai's +Olen +Olen's +Olenek +Olenek's +Olga +Olga's +Oligocene +Oligocene's +Olin +Olin's +Olive +Oliver +Olive's +Oliver +Oliver's +Olivetti +Olivetti's +Olivia +Olivia's +Olivier +Olivier's +Ollie +Ollie's +Olmec +Olmec's +Olmsted +Olmsted's +Olsen +Olsen's +Olson +Olson's +Olympia +Olympias +Olympia's +Olympiad +Olympiads +Olympiad's +Olympian +Olympians +Olympian's +Olympic +Olympics +Olympic's +Olympics +Olympics's +Olympus +Olympus's +Omaha +Omahas +Omaha's +Oman +Oman's +Omani +Omanis +Omani's +Omar +Omar's +Omayyad +Omayyad's +Omdurman +Omdurman's +Omnipotent +Omsk +Omsk's +Onassis +Onassis's +Oneal +Oneal's +Onega +Onega's +Onegin +Onegin's +Oneida +Oneidas +Oneida's +Onion +Onion's +Ono +Ono's +Onondaga +Onondagas +Onondaga's +Onsager +Onsager's +Ont +Ontarian +Ontario +Ontario's +Oort +Oort's +Opal +Opal's +Opel +Opel's +OpenOffice +OpenOffice's +Ophelia +Ophelia's +Ophiuchus +Ophiuchus's +Oppenheimer +Oppenheimer's +Opposition +Oprah +Oprah's +Ora +Ora's +Oracle +Oracle's +Oran +Oran's +Orange +Orange's +Oranjestad +Oranjestad's +Orbison +Orbison's +Ordovician +Ordovician's +Ore +Orion +Oreg +Oregon +Oregon's +Oregonian +Oregonians +Oregonian's +Oreo +Oreo's +Orestes +Orestes's +Orient +Orient's +Oriental +Orientals +Oriental's +Orientalism +Orin +Orin's +Orinoco +Orinoco's +Orion +Orion's +Oriya +Oriya's +Orizaba +Orizaba's +Orkney +Orkney's +Orlando +Orlando's +Orleans +Orleans's +Orlon +Orlons +Orlon's +Orly +Orly's +Orpheus +Orpheus's +Orphic +Orphic's +Orr +Orr's +Ortega +Ortega's +Orthodox +Ortiz +Ortiz's +Orval +Orval's +Orville +Orville's +Orwell +Orwell's +Orwellian +Orwellian's +Os +Os's +Osage +Osages +Osage's +Osaka +Osaka's +Osbert +Osbert's +Osborn +Osborn's +Osborne +Osborne's +Oscar +Oscars +Oscar's +Osceola +Osceola's +Osgood +Osgood's +Oshawa +Oshawa's +Oshkosh +Oshkosh's +Osiris +Osiris's +Oslo +Oslo's +Osman +Osman's +Ostrogoth +Ostrogoth's +Ostwald +Ostwald's +Osvaldo +Osvaldo's +Oswald +Oswald's +Othello +Othello's +Otis +Otis's +Ottawa +Ottawas +Ottawa's +Otto +Otto's +Ottoman +Ottoman's +Ouagadougou +Ouagadougou's +Ouija +Ouijas +Ouija's +Ovid +Ovid's +Owen +Owens +Owen's +Owens +Owens's +Oxford +Oxfords +Oxford's +Oxnard +Oxnard's +Oxonian +Oxonian's +Oxus +Oxus's +Oxycontin +Oxycontin's +Oz +Oz's +Ozark +Ozarks +Ozark's +Ozarks +Ozarks's +Ozymandias +Ozymandias's +Ozzie +Ozzie's +P +Pen +P's +PA +PA's +PAC +PAC's +PARC +PARCs +PASCAL +PBS +PBS's +PBX +PC +PCs +PC's +PCB +PCMCIA +PCP +PCP's +PD +PDF +PDQ +PDT +PE +PET +PET's +PFC +PG +PGP +PHP +PHP's +PIN +PJ's +PLO +PLO's +PM +PMing +PMed +PMs +PM's +PMS +PMS's +PO +POW +POW's +PP +PPS +PR +PRC +PRC's +PRO +PS +PS's +PST +PST's +PT +PTA +PTA's +PTO +PVC +PVC's +PW +PX +Pa +Pa's +Paar +Paar's +Pablo +Pablo's +Pablum +Pablum's +Pabst +Pabst's +Pace +Pace's +Pacheco +Pacheco's +Pacific +Pacific's +Pacino +Pacino's +Packard +Packard's +Padang +Paderewski +Paderewski's +Padilla +Padilla's +Paganini +Paganini's +Page +Page's +Paglia +Paglia's +Pahlavi +Pahlavi's +Paige +Paige's +Paine +Paine's +Paiute +Paiutes +Paiute's +Pakistan +Pakistan's +Pakistani +Pakistanis +Pakistani's +Palembang +Palembang's +Paleocene +Paleocene's +Paleogene +Paleogene's +Paleolithic +Paleolithic's +Paleozoic +Paleozoic's +Palermo +Palermo's +Palestine +Palestine's +Palestinian +Palestinians +Palestinian's +Palestrina +Palestrina's +Paley +Paley's +Palikir +Palikir's +Palisades +Palisades's +Palladio +Palladio's +Palmer +Palmer's +Palmerston +Palmerston's +Palmolive +Palmolive's +Palmyra +Palmyra's +Palomar +Palomar's +Pam +Pam's +Pamela +Pamela's +Pamirs +Pamirs's +Pampers +Pampers's +Pan +Pan's +Panama +Panamas +Panama's +Panamanian +Panamanians +Panamanian's +Panasonic +Panasonic's +Pandora +Pandora's +Pangaea +Pangaea's +Pankhurst +Pankhurst's +Panmunjom +Panmunjom's +Pansy +Pansy's +Pantagruel +Pantagruel's +Pantaloon +Pantaloon's +Pantheon +Pantheon's +Panza +Panza's +Paracelsus +Paracelsus's +Paraclete +Paraclete's +Paradise +Paraguay +Paraguay's +Paraguayan +Paraguayans +Paraguayan's +Paralympic +Paralympics +Paramaribo +Paramaribo's +Paramount +Paramount's +Parana +Parana's +Parcheesi +Parcheesi's +Pareto +Pareto's +Paris +Paris's +Parisian +Parisians +Parisian's +Park +Parker +Parks +Park's +Parker +Parker's +Parkinson +Parkinson's +Parkinsonism +Parkman +Parkman's +Parks +Parks's +Parliament +Parliament's +Parmenides +Parmesan +Parmesans +Parmesan's +Parnassus +Parnassuses +Parnassus's +Parnell +Parnell's +Parr +Parr's +Parrish +Parrish's +Parsifal +Parsifal's +Parsons +Parsons's +Parthenon +Parthenon's +Parthia +Parthia's +Pasadena +Pasadena's +Pascal +Pascals +Pascal's +Pasquale +Pasquale's +Passion +Passions +Passion's +Passover +Passovers +Passover's +Pasternak +Pasternak's +Pasteur +Pasteur's +Pat +Pat's +Patagonia +Patagonia's +Patagonian +Patagonian's +Pate +Pate's +Patel +Patel's +Paterson +Paterson's +Patna +Patna's +Patrica +Patrica's +Patrice +Patrice's +Patricia +Patricia's +Patrick +Patrick's +Patsy +Patsy's +Patterson +Patterson's +Patti +Patti's +Patton +Patton's +Patty +Patty's +Paul +Pauling +Paul's +Paula +Paula's +Paulette +Paulette's +Pauli +Pauli's +Pauline +Pauline's +Pauling +Pauling's +Pavarotti +Pavarotti's +Pavlov +Pavlov's +Pavlova +Pavlova's +Pavlovian +Pavlovian's +Pawnee +Pawnees +Pawnee's +PayPal +PayPal's +Payne +Payne's +Pb +Pb's +Pd +Pd's +Peabody +Peabody's +Peace +Peace's +Peale +Peale's +Pearl +Pearl's +Pearlie +Pearlie's +Pearson +Pearson's +Peary +Peary's +Pechora +Pechora's +Peck +Peck's +Peckinpah +Peckinpah's +Pecos +Pecos's +Pedro +Pedro's +Peel +Peel's +Peg +Peg's +Pegasus +Pegasuses +Pegasus's +Peggy +Peggy's +Pei +Pei's +Peiping +Peiping's +Peking +Pekings +Peking's +Pekingese +Pekingeses +Pekingese's +Pele +Pele's +Pelee +Pelee's +Peloponnese +Peloponnese's +Pembroke +Pembroke's +Pen +Pen's +Pena +Pena's +Penderecki +Penderecki's +Penelope +Penelope's +Penn +Penn's +Penna +Penney +Penney's +Pennington +Pennington's +Pennsylvania +Pennsylvania's +Pennsylvanian +Pennsylvanians +Pennsylvanian's +Penny +Penny's +Pennzoil +Pennzoil's +Pensacola +Pensacola's +Pentagon +Pentagon's +Pentateuch +Pentateuch's +Pentax +Pentax's +Pentecost +Pentecosts +Pentecost's +Pentecostal +Pentecostals +Pentecostal's +Pentecostalism +Pentium +Pentiums +Pentium's +Peoria +Peoria's +Pepin +Pepin's +Pepsi +Pepsi's +Pepys +Pepys's +Pequot +Pequot's +Percheron +Percheron's +Percival +Percival's +Percy +Percy's +Perelman +Perelman's +Perez +Perez's +Periclean +Periclean's +Pericles +Pericles's +Perkins +Perkins's +Perl +Perls +Perl's +Perm +Perm's +Permalloy +Permalloy's +Permian +Permian's +Pernod +Pernod's +Peron +Peron's +Perot +Perot's +Perrier +Perrier's +Perry +Perrier +Perry's +Perseid +Perseid's +Persephone +Persephone's +Persepolis +Persepolis's +Perseus +Perseus's +Pershing +Pershing's +Persia +Persia's +Persian +Persians +Persian's +Perth +Perth's +Peru +Peru's +Peruvian +Peruvians +Peruvian's +Peshawar +Peshawar's +Petain +Petain's +Pete +Peter +Peters +Pete's +Peter +Peter's +Peters +Petersen +Peters's +Petersen +Petersen's +Peterson +Peterson's +Petra +Petra's +Petrarch +Petrarch's +Petty +Petty's +Peugeot +Peugeot's +Pfc +Pfizer +Pfizer's +PhD +PhD's +Phaedra +Phaedra's +Phaethon +Phaethon's +Phanerozoic +Phanerozoic's +Pharaoh +Pharaoh's +Pharaohs +Pharisaic +Pharisaical +Pharisee +Pharisees +Pharisee's +Phekda +Phekda's +Phelps +Phelps's +Phidias +Phidias's +Phil +Philly +Phil's +Philadelphia +Philadelphia's +Philby +Philby's +Philemon +Philemon's +Philip +Philips +Philip's +Philippe +Philippe's +Philippians +Philippians's +Philippine +Philippines +Philippine's +Philippines +Philippines's +Philips +Philips's +Philistine +Philistine's +Phillip +Phillips +Phillip's +Phillipa +Phillipa's +Phillips +Phillips's +Philly +Philly's +Phipps +Phipps's +Phobos +Phobos's +Phoebe +Phoebe's +Phoenicia +Phoenicia's +Phoenician +Phoenicians +Phoenician's +Phoenix +Phoenix's +Photostat +Photostats +Photostat's +Photostatted +Photostatting +Phrygia +Phrygia's +Phyllis +Phyllis's +Piaf +Piaf's +Piaget +Piaget's +Pianola +Pianola's +Picasso +Picasso's +Piccadilly +Piccadilly's +Pickering +Pickering's +Pickett +Pickett's +Pickford +Pickford's +Pickwick +Pickwick's +Pict +Pict's +Piedmont +Piedmont's +Pierce +Pierce's +Pierre +Pierre's +Pierrot +Pierrot's +Pike +Pike's +Pilate +Pilates +Pilate's +Pilates +Pilates's +Pilcomayo +Pilcomayo's +Pilgrim +Pilgrims +Pilgrim's +Pillsbury +Pillsbury's +Pinatubo +Pinatubo's +Pincus +Pincus's +Pindar +Pindar's +Pinkerton +Pinkerton's +Pinocchio +Pinocchio's +Pinochet +Pinochet's +Pinter +Pinter's +Pinyin +Pippin +Pippin's +Piraeus +Piraeus's +Pirandello +Pirandello's +Pisa +Pisa's +Pisces +Pisces's +Pisistratus +Pisistratus's +Pissaro +Pissaro's +Pitcairn +Pitcairn's +Pitt +Pitts +Pitt's +Pittman +Pittman's +Pitts +Pitts's +Pittsburgh +Pittsburgh's +Pius +Pius's +Pizarro +Pizarro's +Pkwy +Pl +Planck +Planck's +Plano +Plantagenet +Plantagenet's +Plasticine +Plasticine's +Plataea +Plataea's +Plath +Plath's +Plato +Plato's +Platonic +Platonism +Platonism's +Platonist +Platonist's +Platte +Platte's +Plautus +Plautus's +PlayStation +PlayStation's +Playboy +Playboy's +Playtex +Playtex's +Pleiades +Pleiades's +Pleistocene +Pleistocene's +Plexiglas +Plexiglases +Plexiglas's +Pliny +Pliny's +Pliocene +Pliocenes +Pliocene's +Plutarch +Plutarch's +Pluto +Pluto's +Plymouth +Plymouth's +Pm +Pm's +Po +Po's +Pocahontas +Pocahontas's +Pocono +Poconos +Pocono's +Poconos +Poconos's +Podgorica +Podgorica's +Podhoretz +Podhoretz's +Podunk +Podunk's +Poe +Poe's +Pogo +Pogo's +Poincare +Poincare's +Poiret +Poiret's +Poirot +Poirot's +Poisson +Poisson's +Poitier +Poitier's +Pokemon +Pokemon's +Pol +Polly +Pol's +Poland +Poland's +Polanski +Polanski's +Polaris +Polaris's +Polaroid +Polaroids +Polaroid's +Pole +Poles +Pole's +Polish +Polish's +Politburo +Politburo's +Polk +Polk's +Pollard +Pollard's +Pollock +Pollock's +Pollux +Pollux's +Polly +Polly's +Pollyanna +Pollyanna's +Polo +Polo's +Poltava +Poltava's +Polyhymnia +Polyhymnia's +Polynesia +Polynesia's +Polynesian +Polynesians +Polynesian's +Polyphemus +Polyphemus's +Pomerania +Pomerania's +Pomeranian +Pomeranian's +Pomona +Pomona's +Pompadour +Pompadour's +Pompeian +Pompeii +Pompeii's +Pompey +Pompey's +Ponce +Ponce's +Pontchartrain +Pontchartrain's +Pontiac +Pontiac's +Pontianak +Pontianak's +Pooh +Pooh's +Poole +Poole's +Poona +Poona's +Pope +Pope's +Popeye +Popeye's +Popocatepetl +Popocatepetl's +Popper +Popper's +Poppins +Poppins's +Popsicle +Popsicle's +Porfirio +Porfirio's +Porrima +Porrima's +Porsche +Porsche's +Port +Porter +Port's +Porter +Porter's +Portia +Portia's +Portland +Portland's +Porto +Porto's +Portsmouth +Portsmouth's +Portugal +Portugal's +Portuguese +Portuguese's +Poseidon +Poseidon's +Post +Post's +PostgreSQL +PostgreSQL's +Potemkin +Potemkin's +Potomac +Potomac's +Potsdam +Potsdam's +Pottawatomie +Pottawatomie's +Potter +Potter's +Potts +Potts's +Pound +Pound's +Poussin +Poussin's +Powell +Powell's +PowerPC +PowerPC's +PowerPoint +PowerPoint's +Powers +Powers's +Powhatan +Powhatan's +Poznan +Poznan's +Pr +Pr's +Prada +Prada's +Prado +Prado's +Praetorian +Praetorian's +Prague +Prague's +Praia +Praia's +Prakrit +Prakrit's +Pratchett +Pratchett's +Pratt +Pratt's +Pravda +Pravda's +Praxiteles +Praxiteles's +Preakness +Preakness's +Precambrian +Precambrian's +Preminger +Preminger's +Premyslid +Premyslid's +Prensa +Prensa's +Prentice +Prentice's +Pres +Presbyterian +Presbyterians +Presbyterian's +Presbyterianism +Presbyterianisms +Presbyterianism's +Prescott +Prescott's +Presley +Presley's +Preston +Preston's +Pretoria +Pretoria's +Priam +Priam's +Pribilof +Pribilof's +Price +Price's +Priceline +Priceline's +Priestley +Priestley's +Prince +Prince's +Princeton +Princeton's +Principe +Principe's +Priscilla +Priscilla's +Prius +Prius's +Private +Procrustean +Procrustean's +Procrustes +Procrustes's +Procter +Procter's +Procyon +Procyon's +Prof +Prohibition +Prokofiev +Prokofiev's +Promethean +Promethean's +Prometheus +Prometheus's +Prophets +Proserpina +Proserpina's +Proserpine +Proserpine's +Protagoras +Protagoras's +Proterozoic +Proterozoic's +Protestant +Protestants +Protestant's +Protestantism +Protestantisms +Protestantism's +Proteus +Proteus's +Proudhon +Proudhon's +Proust +Proust's +Provencal +Provencals +Provencal's +Provence +Provence's +Proverbs +Providence +Providences +Providence's +Provo +Provo's +Prozac +Prozacs +Prozac's +Prudence +Prudence's +Prudential +Prudential's +Pruitt +Pruitt's +Prussia +Prussia's +Prussian +Prussians +Prussian's +Prut +Prut's +Pryor +Pryor's +Psalms +Psalms's +Psalter +Psalters +Psalter's +Psyche +Psyche's +Pt +Pt's +Ptah +Ptah's +Ptolemaic +Ptolemaic's +Ptolemy +Ptolemies +Ptolemy's +Pu +Pu's +Puccini +Puccini's +Puck +Puck's +Puckett +Puckett's +Puebla +Puebla's +Pueblo +Pueblo's +Puerto +Puget +Puget's +Pugh +Pugh's +Pulaski +Pulaski's +Pulitzer +Pulitzer's +Pullman +Pullmans +Pullman's +Punch +Punch's +Punic +Punic's +Punjab +Punjab's +Punjabi +Punjabi's +Purana +Purana's +Purcell +Purcell's +Purdue +Purdue's +Purim +Purims +Purim's +Purina +Purina's +Puritan +Puritan's +Puritanism +Puritanisms +Puritanism's +Purus +Purus's +Pusan +Pusan's +Pusey +Pusey's +Pushkin +Pushkin's +Pushtu +Pushtu's +Putin +Putin's +Putnam +Putnam's +Puzo +Puzo's +Pvt +PyTorch +PyTorch's +Pygmalion +Pygmalion's +Pygmy +Pygmies +Pygmy's +Pyle +Pyle's +Pym +Pym's +Pynchon +Pynchon's +Pyongyang +Pyongyang's +Pyotr +Pyotr's +Pyrenees +Pyrenees's +Pyrex +Pyrexes +Pyrex's +Pyrrhic +Pyrrhic's +Pythagoras +Pythagoras's +Pythagorean +Pythagorean's +Pythias +Pythias's +Python +Python's +Q +QA +QB +QC +QED +QM +QWERTY +Qaddafi +Qaddafi's +Qantas +Qantas's +Qatar +Qatar's +Qatari +Qataris +Qatari's +Qingdao +Qingdao's +Qinghai +Qinghai's +Qiqihar +Qiqihar's +Qom +Qom's +Quaalude +Quaalude's +Quaker +Quakers +Quaker's +Quakerism +Quakerisms +Quakerism's +Qualcomm +Qualcomm's +Quaoar +Quaoar's +Quasimodo +Quasimodo's +Quaternary +Quaternary's +Quayle +Quayle's +Que +Quebec +Quebec's +Quebecois +Quebecois's +Quechua +Quechua's +Queen +Queens +Queen's +Queens +Queens's +Queensland +Queensland's +Quentin +Quentin's +Quetzalcoatl +Quetzalcoatl's +Quezon +Quezon's +Quincy +Quincy's +Quinn +Quinn's +Quintilian +Quintilian's +Quinton +Quinton's +Quirinal +Quirinal's +Quisling +Quisling's +Quito +Quito's +Quixote +Quixote's +Quixotism +Quixotism's +Qumran +Qumran's +Quonset +Quonset's +R +R's +RAF +RAF's +RAM +RAMs +RAM's +RBI +RC +RCA +RCA's +RCMP +RD +RDA +RDS +RDS's +REIT +REM +REMs +REM's +RF +RFC +RFCs +RFD +RI +RIF +RIP +RISC +RN +RN's +RNA +RNA's +ROFL +ROM +ROM's +ROTC +ROTC's +RP +RR +RSFSR +RSI +RSV +RSVP +RTFM +RV +RVs +RV's +Ra +Ra's +Rabat +Rabat's +Rabelais +Rabelais's +Rabelaisian +Rabelaisian's +Rabin +Rabin's +Rachael +Rachael's +Rachel +Rachel's +Rachelle +Rachelle's +Rachmaninoff +Rachmaninoff's +Racine +Racine's +Radcliffe +Radcliffe's +Rae +Rae's +Rafael +Rafael's +Raffles +Raffles's +Ragnarok +Ragnarok's +Rainier +Rainier's +Raleigh +Raleigh's +Ralph +Ralph's +Rama +Rama's +Ramada +Ramada's +Ramadan +Ramadans +Ramadan's +Ramakrishna +Ramakrishna's +Ramanujan +Ramanujan's +Ramayana +Ramayana's +Rambo +Rambo's +Ramirez +Ramirez's +Ramiro +Ramiro's +Ramon +Ramon's +Ramona +Ramona's +Ramos +Ramos's +Ramsay +Ramsay's +Ramses +Ramses's +Ramsey +Ramsey's +Rand +Rand's +Randal +Randal's +Randall +Randall's +Randell +Randell's +Randi +Randi's +Randolph +Randolph's +Randy +Randy's +Rangoon +Rangoon's +Rankin +Rankin's +Rankine +Rankine's +Raoul +Raoul's +Raphael +Raphael's +Rappaport +Rappaport's +Rapunzel +Rapunzel's +Raquel +Raquel's +Rasalgethi +Rasalgethi's +Rasalhague +Rasalhague's +Rasmussen +Rasmussen's +Rasputin +Rasputin's +Rasta +Rastaban +Rastaban's +Rastafarian +Rastafarians +Rastafarian's +Rastafarianism +Rather +Rather's +Ratliff +Ratliff's +Raul +Raul's +Ravel +Ravel's +Rawalpindi +Rawalpindi's +Ray +Ray's +RayBan +RayBan's +Rayburn +Rayburn's +Rayleigh +Rayleigh's +Raymond +Raymond's +Raymundo +Raymundo's +Rb +Rb's +Rd +Re +Re's +Reading +Reading's +Reagan +Reagan's +Reaganomics +Reaganomics's +Realtor +Realtor's +Reasoner +Reasoner's +Reba +Reba's +Rebekah +Rebekah's +Recife +Recife's +Reconstruction +Reconstruction's +Redeemer +Redeemer's +Redford +Redford's +Redgrave +Redgrave's +Redis +Redis's +Redmond +Redmond's +Redshift +Redshift's +Reebok +Reebok's +Reed +Reed's +Reese +Reese's +Reeves +Reeves's +Reformation +Reformations +Reformation's +Refugio +Refugio's +Reggie +Reggie's +Regina +Regina's +Reginae +Reginae's +Reginald +Reginald's +Regor +Regor's +Regulus +Regulus's +Rehnquist +Rehnquist's +Reich +Reich's +Reichstag's +Reid +Reid's +Reilly +Reilly's +Reinaldo +Reinaldo's +Reinhardt +Reinhardt's +Reinhold +Reinhold's +Remarque +Remarque's +Rembrandt +Rembrandt's +Remington +Remington's +Remus +Remus's +Rena +Rena's +Renaissance +Renaissances +Renaissance's +Renascence +Renault +Renault's +Rene +Rene's +Renee +Renee's +Reno +Reno's +Renoir +Renoir's +Rep +Representative +Republican +Republicans +Republican's +Republicanism +Requiem +Requiems +Requiem's +Resistance +Restoration +Restoration's +Resurrection +Reuben +Reuben's +Reunion +Reunion's +Reuters +Reuters's +Reuther +Reuther's +Rev +Reva +Reva's +Revelation +Revelations +Revelation's +Revelations +Revelations's +Revere +Revere's +Reverend +Reverend's +Revlon +Revlon's +Rex +Rex's +Reyes +Reyes's +Reykjavik +Reykjavik's +Reyna +Reyna's +Reynaldo +Reynaldo's +Reynolds +Reynolds's +Rf +Rf's +Rh +Rh's +Rhea +Rhea's +Rhee +Rhee's +Rheingau +Rheingau's +Rhenish +Rhenish's +Rhiannon +Rhiannon's +Rhine +Rhine's +Rhineland +Rhineland's +Rhoda +Rhoda's +Rhode +Rhodes +Rhodes +Rhodes's +Rhodesia +Rhodesia's +Rhodesian +Rhonda +Rhonda's +Rhone +Rhone's +Ribbentrop +Ribbentrop's +Ricardo +Ricardo's +Rice +Rice's +Rich +Rich's +Richard +Richards +Richard's +Richards +Richards's +Richardson +Richardson's +Richelieu +Richelieu's +Richie +Richie's +Richmond +Richmond's +Richter +Richter's +Richthofen +Richthofen's +Rick +Rick's +Rickenbacker +Rickenbacker's +Rickey +Rickey's +Rickie +Rickie's +Rickover +Rickover's +Ricky +Ricky's +Rico +Rico's +Riddle +Riddle's +Ride +Ride's +Riefenstahl +Riefenstahl's +Riel +Riel's +Riemann +Riemann's +Riesling +Rieslings +Riesling's +Riga +Riga's +Rigel +Rigel's +Riggs +Riggs's +Right +Rigoberto +Rigoberto's +Rigoletto +Rigoletto's +Riley +Riley's +Rilke +Rilke's +Rimbaud +Rimbaud's +Ringling +Ringling's +Ringo +Ringo's +Rio +Rios +Rio's +Rios +Rios's +Ripley +Ripley's +Risorgimento +Risorgimento's +Rita +Rita's +Ritalin +Ritalin's +Ritz +Ritz's +Rivas +Rivas's +Rivera +Rivera's +Rivers +Rivers's +Riverside +Riviera +Rivieras +Riviera's +Riyadh +Riyadh's +Rizal +Rizal's +Rn +Rn's +Roach +Roach's +Roanoke +Roanoke's +Rob +Rob's +Robbie +Robbie's +Robbin +Robbins +Robbin's +Robbins +Robbins's +Robby +Robby's +Roberson +Roberson's +Robert +Roberts +Robert's +Roberta +Roberta's +Roberto +Roberto's +Roberts +Roberts's +Robertson +Robertson's +Robeson +Robeson's +Robespierre +Robespierre's +Robin +Robin's +Robinson +Robinson's +Robitussin +Robitussin's +Robles +Robles's +Robson +Robson's +Robt +Robt's +Robyn +Robyn's +Rocco +Rocco's +Rocha +Rocha's +Rochambeau +Rochambeau's +Roche +Roche's +Rochelle +Rochelle's +Rochester +Rochester's +Rock +Rock's +Rockefeller +Rockefeller's +Rockford +Rockford's +Rockies +Rockies's +Rockne +Rockne's +Rockwell +Rockwell's +Rocky +Rockies +Rocky's +Rod +Rod's +Roddenberry +Roddenberry's +Roderick +Roderick's +Rodger +Rodgers +Rodger's +Rodgers +Rodgers's +Rodin +Rodin's +Rodney +Rodney's +Rodolfo +Rodolfo's +Rodrick +Rodrick's +Rodrigo +Rodrigo's +Rodriguez +Rodriguez's +Rodriquez +Rodriquez's +Roeg +Roeg's +Roentgen +Rogelio +Rogelio's +Roger +Rogers +Roger's +Rogers +Rogers's +Roget +Roget's +Rojas +Rojas's +Roku +Roku's +Rolaids +Rolaids's +Roland +Roland's +Rolando +Rolando's +Rolex +Rolex's +Rolland +Rolland's +Rollerblade +Rollerblade's +Rollins +Rollins's +Rolodex +Rolodex's +Rolvaag +Rolvaag's +Rom +Roman +Romans +Roman's +Romanesque +Romanesques +Romanesque's +Romania +Romania's +Romanian +Romanians +Romanian's +Romano +Romano's +Romanov +Romanov's +Romans +Romans's +Romansh +Romansh's +Romanticism +Romany +Romanies +Romany's +Rome +Romes +Rome's +Romeo +Romeo's +Romero +Romero's +Rommel +Rommel's +Romney +Romney's +Romulus +Romulus's +Ron +Ron's +Ronald +Ronald's +Ronda +Ronda's +Ronnie +Ronnie's +Ronny +Ronny's +Ronstadt +Ronstadt's +Rontgen +Rooney +Rooney's +Roosevelt +Roosevelt's +Root +Root's +Roquefort +Roqueforts +Roquefort's +Rorschach +Rorschach's +Rory +Rory's +Rosa +Rosa's +Rosales +Rosales's +Rosalie +Rosalie's +Rosalind +Rosalind's +Rosalinda +Rosalinda's +Rosalyn +Rosalyn's +Rosanna +Rosanna's +Rosanne +Rosanne's +Rosario +Rosario's +Roscoe +Roscoe's +Rose +Rose's +Roseann +Roseann's +Roseau +Roseau's +Rosecrans +Rosecrans's +Rosella +Rosella's +Rosemarie +Rosemarie's +Rosemary +Rosemary's +Rosenberg +Rosenberg's +Rosendo +Rosendo's +Rosenzweig +Rosenzweig's +Rosetta +Rosetta's +Rosicrucian +Rosicrucian's +Rosie +Rosie's +Roslyn +Roslyn's +Ross +Ross's +Rossetti +Rossetti's +Rossini +Rossini's +Rostand +Rostand's +Rostov +Rostov's +Rostropovich +Rostropovich's +Roswell +Roswell's +Rotarian +Rotarian's +Roth +Roth's +Rothko +Rothko's +Rothschild +Rothschild's +Rotterdam +Rotterdam's +Rottweiler +Rottweiler's +Rouault +Rouault's +Rourke +Rourke's +Rousseau +Rousseau's +Rove +Rover +Rove's +Rover +Rover's +Rowe +Rowe's +Rowena +Rowena's +Rowland +Rowland's +Rowling +Rowling's +Roxanne +Roxanne's +Roxie +Roxie's +Roxy +Roxy's +Roy +Roy's +Royal +Royal's +Royce +Royce's +Rozelle +Rozelle's +Rte +Ru +Ruth +Ru's +Rubaiyat +Rubaiyat's +Rubbermaid +Rubbermaid's +Ruben +Rubens +Ruben's +Rubens +Rubens's +Rubicon +Rubicons +Rubicon's +Rubik +Rubik's +Rubin +Rubin's +Rubinstein +Rubinstein's +Ruby +Ruby's +Ruchbah +Ruchbah's +Rudolf +Rudolf's +Rudolph +Rudolph's +Rudy +Rudy's +Rudyard +Rudyard's +Rufus +Rufus's +Ruhr +Ruhr's +Ruiz +Ruiz's +Rukeyser +Rukeyser's +Rumpelstiltskin +Rumpelstiltskin's +Rumsfeld +Rumsfeld's +Runnymede +Runnymede's +Runyon +Runyon's +Rupert +Rupert's +Rush +Rush's +Rushdie +Rushdie's +Rushmore +Rushmore's +Ruskin +Ruskin's +Russ +Russ's +Russel +Russel's +Russell +Russell's +Russia +Russia's +Russian +Russians +Russian's +Russo +Russo's +Rustbelt +Rustbelt's +Rusty +Rusty's +Rutan +Rutan's +Rutgers +Rutgers's +Ruth +Ruth's +Rutherford +Rutherford's +Ruthie +Ruthie's +Rutledge +Rutledge's +Rwanda +Rwandas +Rwanda's +Rwandan +Rwandans +Rwandan's +Rwy +Rx +Ry +Ryan +Ryan's +Rydberg +Rydberg's +Ryder +Ryder's +Ryukyu +Ryukyu's +S +Sen +S's +SA +SAC +SALT +SALT's +SAM +SAM's +SAP +SAP's +SARS +SARS's +SASE +SAT +SBA +SC +SC's +SCSI +SCSI's +SD +SDI +SE +SE's +SEATO +SEC +SEC's +SF +SGML +SGML's +SIDS +SIDS's +SJ +SJW +SK +SLR +SO +SOs +SOB +SOB's +SOP +SOP's +SOS +SOS's +SOSes +SPCA +SPF +SQL +SQLite +SQLite's +SRO +SS +SSA +SSE +SSE's +SSS +SST +SSW +SSW's +ST +STD +STOL +SUSE +SUSE's +SUV +SVN +SVN's +SW +SW's +SWAK +SWAT +Saab +Saab's +Saar +Saar's +Saarinen +Saarinen's +Saatchi +Saatchi's +Sabbath +Sabbath's +Sabbaths +Sabik +Sabik's +Sabin +Sabin's +Sabina +Sabina's +Sabine +Sabine's +Sabre +Sabre's +Sabrina +Sabrina's +Sacajawea +Sacajawea's +Sacco +Sacco's +Sachs +Sachs's +Sacramento +Sacramento's +Sadat +Sadat's +Saddam +Saddam's +Sadducee +Sadducee's +Sade +Sade's +Sadie +Sadie's +Sadr +Sadr's +Safavid +Safavid's +Safeway +Safeway's +Sagan +Sagan's +Saginaw +Saginaw's +Sagittarius +Sagittariuses +Sagittarius's +Sahara +Sahara's +Saharan +Saharan's +Sahel +Sahel's +Saigon +Saigon's +Saiph +Saiph's +Sakai +Sakai's +Sakha +Sakha's +Sakhalin +Sakhalin's +Sakharov +Sakharov's +Saki +Saki's +Saks +Saks's +Sal +Sally +Sal's +Saladin +Saladin's +Salado +Salado's +Salamis +Salamis's +Salas +Salas's +Salazar +Salazar's +Salem +Salem's +Salerno +Salerno's +Salesforce +Salesforce's +Salinas +Salinas's +Salinger +Salinger's +Salisbury +Salisbury's +Salish +Salish's +Salk +Salk's +Sallie +Sallie's +Sallust +Sallust's +Sally +Sally's +Salome +Salome's +Salonika +Salonika's +Salton +Salton's +Salvador +Salvador's +Salvadoran +Salvadorans +Salvadoran's +Salvadorean +Salvadoreans +Salvadorean's +Salvadorian +Salvadorians +Salvadorian's +Salvatore +Salvatore's +Salween +Salween's +Salyut +Salyut's +Sam +Sam's +Samantha +Samantha's +Samar +Samar's +Samara +Samara's +Samaritan +Samaritans +Samaritan's +Samarkand +Samarkand's +Sammie +Sammie's +Sammy +Sammy's +Samoa +Samoa's +Samoan +Samoans +Samoan's +Samoset +Samoset's +Samoyed +Samoyed's +Sampson +Sampson's +Samson +Samson's +Samsonite +Samsonite's +Samsung +Samsung's +Samuel +Samuel's +Samuelson +Samuelson's +San'a +San +San's +Sana +Sana's +Sanchez +Sanchez's +Sancho +Sancho's +Sand +Sanders +Sand's +Sandburg +Sandburg's +Sanders +Sanders's +Sandinista +Sandinista's +Sandoval +Sandoval's +Sandra +Sandra's +Sandy +Sandy's +Sanford +Sanford's +Sanforized +Sanforized's +Sang +Sanger +Sang's +Sanger +Sanger's +Sanhedrin +Sanhedrin's +Sanka +Sanka's +Sankara +Sankara's +Sanskrit +Sanskrit's +Santa +Santa's +Santana +Santana's +Santayana +Santayana's +Santeria +Santeria's +Santiago +Santiago's +Santos +Santos's +Sappho +Sappho's +Sapporo +Sapporo's +Sara +Sara's +Saracen +Saracens +Saracen's +Saragossa +Saragossa's +Sarah +Sarah's +Sarajevo +Sarajevo's +Saran +Saran's +Sarasota +Sarasota's +Saratov +Saratov's +Sarawak +Sarawak's +Sardinia +Sardinia's +Sargasso +Sargasso's +Sargent +Sargent's +Sargon +Sargon's +Sarnoff +Sarnoff's +Saroyan +Saroyan's +Sarto +Sarto's +Sartre +Sartre's +Sasha +Sasha's +Sask +Saskatchewan +Saskatchewan's +Saskatoon +Saskatoon's +Sasquatch +Sasquatches +Sasquatch's +Sassanian +Sassanian's +Sassoon +Sassoon's +Sat +Sat's +Satan +Satan's +Satanism +Satanism's +Satanist +Satanist's +Saturday +Saturdays +Saturday's +Saturn +Saturn's +Saturnalia +Saturnalia's +Saudi +Saudis +Saudi's +Saul +Saul's +Saunders +Saunders's +Saundra +Saundra's +Saussure +Saussure's +Sauternes +Savage +Savage's +Savannah +Savannah's +Savior +Savior's +Savonarola +Savonarola's +Savoy +Savoy's +Savoyard +Savoyard's +Sawyer +Sawyer's +Saxon +Saxons +Saxon's +Saxony +Saxony's +Sayers +Sayers's +Sb +Sb's +Sc +Sc's +Scala +Scala's +Scan +Scandinavia +Scandinavia's +Scandinavian +Scandinavians +Scandinavian's +Scaramouch +Scaramouch's +Scarborough +Scarborough's +Scarlatti +Scarlatti's +Scheat +Scheat's +Schedar +Schedar's +Scheherazade +Scheherazade's +Schelling +Schelling's +Schenectady +Schenectady's +Schiaparelli +Schiaparelli's +Schick +Schick's +Schiller +Schiller's +Schindler +Schindler's +Schlesinger +Schlesinger's +Schliemann +Schliemann's +Schlitz +Schlitz's +Schloss +Schloss's +Schmidt +Schmidt's +Schnabel +Schnabel's +Schnauzer +Schnauzer's +Schneider +Schneider's +Schoenberg +Schoenberg's +Schopenhauer +Schopenhauer's +Schrieffer +Schrieffer's +Schrodinger +Schrodinger's +Schroeder +Schroeder's +Schubert +Schubert's +Schultz +Schultz's +Schulz +Schulz's +Schumann +Schumann's +Schumpeter +Schumpeter's +Schuyler +Schuyler's +Schuylkill +Schuylkill's +Schwartz +Schwartz's +Schwarzenegger +Schwarzenegger's +Schwarzkopf +Schwarzkopf's +Schweitzer +Schweitzer's +Schweppes +Schweppes's +Schwinger +Schwinger's +Schwinn +Schwinn's +Scientologist +Scientologists +Scientologist's +Scientology +Scientology's +Scipio +Scipio's +Scopes +Scopes's +Scorpio +Scorpios +Scorpio's +Scorpius +Scorpius's +Scorsese +Scorsese's +Scot +Scots +Scot's +Scotch +Scotches +Scotch's +Scotchman +Scotchman's +Scotchmen +Scotchmen's +Scotchwoman +Scotchwoman's +Scotchwomen +Scotchwomen's +Scotia +Scotia's +Scotland +Scotland's +Scotsman +Scotsman's +Scotsmen +Scotsmen's +Scotswoman +Scotswoman's +Scotswomen +Scotswomen's +Scott +Scott's +Scottie +Scotties +Scottie's +Scottish +Scottish's +Scottsdale +Scottsdale's +Scrabble +Scrabbles +Scrabble's +Scranton +Scranton's +Scriabin +Scriabin's +Scribner +Scribner's +Scripture +Scriptures +Scripture's +Scrooge +Scrooge's +Scruggs +Scruggs's +Scud +Scud's +Sculley +Sculley's +Scylla +Scylla's +Scythia +Scythia's +Scythian +Scythian's +Se +Seth +Se's +Seaborg +Seaborg's +Seagram +Seagram's +Sean +Sean's +Sears +Sears's +Seattle +Seattle's +Sebastian +Sebastian's +Sec +Seconal +Seconal's +Secretariat +Secretariat's +Secretary +Seder +Seders +Seder's +Sedna +Sedna's +Seebeck +Seebeck's +Seeger +Seeger's +Sega +Sega's +Segovia +Segovia's +Segre +Segre's +Segundo +Segundo's +Segway +Segways +Seiko +Seiko's +Seine +Seine's +Seinfeld +Seinfeld's +Sejong +Sejong's +Selassie +Selassie's +Selectric +Selectric's +Selena +Selena's +Seleucid +Seleucid's +Seleucus +Seleucus's +Selim +Selim's +Seljuk +Seljuk's +Selkirk +Selkirk's +Sellers +Sellers's +Selma +Selma's +Selznick +Selznick's +Semarang +Semarang's +Seminole +Seminoles +Seminole's +Semiramis +Semiramis's +Semite +Semites +Semite's +Semitic +Semitics +Semitic's +Semtex +Semtex's +Senate +Senates +Senate's +Sendai +Sendai's +Seneca +Senecas +Seneca's +Senegal +Senegal's +Senegalese +Senegalese's +Senghor +Senghor's +Senior +Senior's +Sennacherib +Sennacherib's +Sennett +Sennett's +Sensurround +Sensurround's +Seoul +Seoul's +Sep +Sephardi +Sephardi's +Sepoy +Sepoy's +Sept +Sept's +September +Septembers +September's +Septuagint +Septuagints +Septuagint's +Sequoya +Sequoya's +Serb +Serbs +Serb's +Serbia +Serbia's +Serbian +Serbians +Serbian's +Serena +Serena's +Serengeti +Serengeti's +Sergei +Sergei's +Sergio +Sergio's +Serpens +Serpens's +Serra +Serra's +Serrano +Serrano's +Set +Set's +Seth +Seth's +Seton +Seton's +Seurat +Seurat's +Seuss +Seuss's +Sevastopol +Sevastopol's +Severn +Severn's +Severus +Severus's +Seville +Seville's +Sevres +Sevres's +Seward +Seward's +Sextans +Sextans's +Sexton +Sexton's +Seychelles +Seychelles's +Seyfert +Seyfert's +Seymour +Seymour's +Sgt +Shaanxi +Shaanxi's +Shackleton +Shackleton's +Shaffer +Shaffer's +Shah +Shah's +Shaka +Shaka's +Shaker +Shakespeare +Shakespeare's +Shakespearean +Shakespearean's +Shana +Shana's +Shandong +Shandong's +Shane +Shane's +Shanghai +Shanghai's +Shankara +Shankara's +Shanna +Shanna's +Shannon +Shannon's +Shantung +Shantung's +Shanxi +Shanxi's +Shapiro +Shapiro's +SharePoint +SharePoint's +Shari'a +Shari'a's +Shari +Shari's +Sharif +Sharif's +Sharlene +Sharlene's +Sharon +Sharon's +Sharp +Sharp's +Sharpe +Sharpe's +Sharron +Sharron's +Shasta +Shasta's +Shaula +Shaula's +Shaun +Shaun's +Shauna +Shauna's +Shavian +Shavian's +Shavuot +Shavuot's +Shaw +Shaw's +Shawn +Shawn's +Shawna +Shawna's +Shawnee +Shawnees +Shawnee's +Shcharansky +Shcharansky's +Shea +Shea's +Sheba +Sheba's +Shebeli +Shebeli's +Sheena +Sheena's +Sheetrock +Sheetrock's +Sheffield +Sheffield's +Sheila +Sheila's +Shelby +Shelby's +Sheldon +Sheldon's +Shelia +Shelia's +Shell +Shell's +Shelley +Shelley's +Shelly +Shelly's +Shelton +Shelton's +Shenandoah +Shenandoah's +Shenyang +Shenyang's +Sheol +Sheol's +Shepard +Shepard's +Shepherd +Shepherd's +Sheppard +Sheppard's +Sheratan +Sheratan's +Sheraton +Sheraton's +Sheree +Sheree's +Sheri +Sheri's +Sheridan +Sheridan's +Sherlock +Sherlock's +Sherman +Sherman's +Sherpa +Sherpa's +Sherri +Sherri's +Sherrie +Sherrie's +Sherry +Sherry's +Sherwood +Sherwood's +Sheryl +Sheryl's +Shetland +Shetlands +Shetland's +Shetlands +Shetlands's +Shevardnadze +Shevardnadze's +Shevat +Shevat's +Shi'ite +Shi'ite's +Shields +Shields's +Shiite +Shiites +Shiite's +Shijiazhuang +Shijiazhuang's +Shikoku +Shikoku's +Shillong +Shillong's +Shiloh +Shiloh's +Shinto +Shintos +Shinto's +Shintoism +Shintoisms +Shintoism's +Shintoist +Shintoists +Shintoist's +Shiraz +Shiraz's +Shirley +Shirley's +Shiva +Shiva's +Shockley +Shockley's +Short +Short's +Shorthorn +Shorthorn's +Shoshone +Shoshones +Shoshone's +Shostakovitch +Shostakovitch's +Shrek +Shrek's +Shreveport +Shreveport's +Shriner +Shriner's +Shropshire +Shropshire's +Shula +Shula's +Shylock +Shylock's +Shylockian +Shylockian's +Si +Si's +Siam +Siam's +Siamese +Siamese's +Sibelius +Sibelius's +Siberia +Siberia's +Siberian +Siberians +Siberian's +Sibyl +Sibyl's +Sichuan +Sichuan's +Sicilian +Sicilians +Sicilian's +Sicily +Sicily's +Sid +Sid's +Siddhartha +Siddhartha's +Sidney +Sidney's +Siegfried +Siegfried's +Siemens +Siemens's +Sierpinski +Sierpinski's +Sierras +Sigismund +Sigismund's +Sigmund +Sigmund's +Sigurd +Sigurd's +Sihanouk +Sihanouk's +Sikh +Sikh's +Sikhism +Sikhs +Sikkim +Sikkim's +Sikkimese +Sikkimese's +Sikorsky +Sikorsky's +Silas +Silas's +Silesia +Silesia's +Silurian +Silurians +Silurian's +Silva +Silva's +Silvia +Silvia's +Simenon +Simenon's +Simmental +Simmental's +Simmons +Simmons's +Simon +Simon's +Simone +Simone's +Simpson +Simpsons +Simpson's +Simpsons +Simpsons's +Sims +Sims's +Sinai +Sinai's +Sinatra +Sinatra's +Sinbad +Sinbad's +Sinclair +Sinclair's +Sindbad +Sindbad's +Sindhi +Sindhi's +Singapore +Singapore's +Singaporean +Singaporeans +Singaporean's +Singer +Singer's +Singh +Singh's +Singleton +Singleton's +Sinhalese +Sinhalese's +Sinkiang +Sinkiang's +Sioux +Sioux's +Sir +Sirs +Sir's +Sirius +Sirius's +Sistine +Sistine's +Sisyphean +Sisyphean's +Sisyphus +Sisyphus's +Siva +Siva's +Sivan +Sivan's +Sjaelland +Sjaelland's +Skinner +Skinner's +Skippy +Skippy's +Skopje +Skopje's +Skye +Skye's +Skylab +Skylab's +Skype +Skype's +Slackware +Slackware's +Slashdot +Slashdot's +Slater +Slater's +Slav +Slavs +Slav's +Slavic +Slavic's +Slavonic +Slavonic's +Slinky +Slinky's +Sloan +Sloan's +Sloane +Sloane's +Slocum +Slocum's +Slovak +Slovaks +Slovak's +Slovakia +Slovakia's +Slovakian +Slovene +Slovenes +Slovene's +Slovenia +Slovenia's +Slovenian +Slovenians +Slovenian's +Slurpee +Slurpee's +Sm +Sm's +Small +Small's +Smetana +Smetana's +Smirnoff +Smirnoff's +Smith +Smith's +Smithson +Smithson's +Smithsonian +Smithsonian's +Smokey +Smokey's +Smolensk +Smolensk's +Smollett +Smollett's +Smuts +Smuts's +Smyrna +Sn +Sn's +Snake +Snake's +Snapple +Snapple's +Snead +Snead's +Snell +Snell's +Snickers +Snickers's +Snider +Snider's +Snoopy +Snoopy's +Snow +Snow's +Snowbelt +Snowbelt's +Snyder +Snyder's +Soave +Soave's +Soc +Socorro +Socorro's +Socrates +Socrates's +Socratic +Socratic's +Soddy +Soddy's +Sodom +Sodom's +Sofia +Sofia's +Soho +Soho's +Sol +Sol's +Solis +Solis's +Solomon +Solomon's +Solon +Solon's +Solzhenitsyn +Solzhenitsyn's +Somali +Somalis +Somali's +Somalia +Somalia's +Somalian +Somalians +Somalian's +Somme +Somme's +Somoza +Somoza's +Son +Son's +Sondheim +Sondheim's +Sondra +Sondra's +Songhai +Songhai's +Songhua +Songhua's +Sonia +Sonia's +Sonja +Sonja's +Sonny +Sonny's +Sonora +Sonora's +Sontag +Sontag's +Sony +Sony's +Sonya +Sonya's +Sophia +Sophia's +Sophie +Sophie's +Sophoclean +Sophoclean's +Sophocles +Sophocles's +Sopwith +Sopwith's +Sorbonne +Sorbonne's +Sosa +Sosa's +Soto +Soto's +Souphanouvong +Souphanouvong's +Sourceforge +Sourceforge's +Sousa +Sousa's +South +South's +Southampton +Southampton's +Southeast +Southeasts +Southeast's +Southerner +Southerners +Southerner's +Southey +Southey's +Souths +Southwest +Southwests +Southwest's +Soviet +Soviet's +Soweto +Soweto's +Soyinka +Soyinka's +Soyuz +Soyuz's +Sp +Spaatz +Spaatz's +Spackle +Spackle's +Spahn +Spahn's +Spain +Spain's +Spam +Spam's +Span +Spanglish +Spaniard +Spaniards +Spaniard's +Spanish +Spanish's +Sparks +Sparks's +Sparta +Sparta's +Spartacus +Spartacus's +Spartan +Spartans +Spartan's +Spears +Spears's +Speer +Speer's +Spence +Spencer +Spence's +Spencer +Spencer's +Spencerian +Spencerian's +Spengler +Spengler's +Spenglerian +Spenglerian's +Spenser +Spenser's +Spenserian +Spenserian's +Sperry +Sperry's +Sphinx +Sphinx's +Spica +Spica's +Spielberg +Spielberg's +Spillane +Spillane's +Spinoza +Spinoza's +Spinx +Spinx's +Spiro +Spiro's +Spirograph +Spirograph's +Spitsbergen +Spitsbergen's +Spitz +Spitz's +Spock +Spock's +Spokane +Spokane's +Springfield +Springfield's +Springsteen +Springsteen's +Sprint +Sprint's +Sprite +Sprite's +Sputnik +Sputnik's +Sq +Squanto +Squanto's +Squibb +Squibb's +Sr +Sr's +Srinagar +Srinagar's +Srivijaya +Srivijaya's +St +Sta +Stacey +Stacey's +Staci +Staci's +Stacie +Stacie's +Stacy +Stacy's +Stael +Stael's +Stafford +Stafford's +StairMaster +StairMaster's +Stalin +Stalin's +Stalingrad +Stalingrad's +Stalinist +Stalinist's +Stallone +Stallone's +Stamford +Stamford's +Stan +Stan's +Standish +Standish's +Stanford +Stanford's +Stanislavsky +Stanislavsky's +Stanley +Stanley's +Stanton +Stanton's +Staples +Staples's +Starbucks +Starbucks's +Stark +Stark's +Starkey +Starkey's +Starr +Starr's +Staten +Staten's +States +Staubach +Staubach's +Ste +Steadicam +Steadicam's +Steele +Steele's +Stefan +Stefan's +Stefanie +Stefanie's +Stein +Steiner +Stein's +Steinbeck +Steinbeck's +Steinem +Steinem's +Steiner +Steiner's +Steinmetz +Steinmetz's +Steinway +Steinway's +Stella +Stella's +Stendhal +Stendhal's +Stengel +Stengel's +Stephan +Stephan's +Stephanie +Stephanie's +Stephen +Stephens +Stephen's +Stephens +Stephens's +Stephenson +Stephenson's +Sterling +Sterling's +Stern +Stern's +Sterne +Sterne's +Sterno +Sterno's +Stetson +Stetson's +Steuben +Steuben's +Steve +Steve's +Steven +Stevens +Steven's +Stevens +Stevens's +Stevenson +Stevenson's +Stevie +Stevie's +Stewart +Stewart's +Stieglitz +Stieglitz's +Stilton +Stiltons +Stilton's +Stimson +Stimson's +Stine +Stine's +Stirling +Stirling's +Stockhausen +Stockhausen's +Stockholm +Stockholm's +Stockton +Stockton's +Stoic +Stoics +Stoic's +Stoicism +Stoicisms +Stoicism's +Stokes +Stokes's +Stolichnaya +Stolichnaya's +Stolypin +Stolypin's +Stone +Stone's +Stonehenge +Stonehenge's +Stoppard +Stoppard's +Stout +Stout's +Stowe +Stowe's +Strabo +Strabo's +Stradivari +Stradivarius +Stradivarius's +Strasbourg +Strasbourg's +Strauss +Strauss's +Stravinsky +Stravinsky's +Streisand +Streisand's +Strickland +Strickland's +Strindberg +Strindberg's +Stromboli +Stromboli's +Strong +Strong's +Stu +Stu's +Stuart +Stuarts +Stuart's +Studebaker +Studebaker's +Stuttgart +Stuttgart's +Stuyvesant +Stuyvesant's +Stygian +Stygian's +Styrofoam +Styrofoams +Styrofoam's +Styron +Styron's +Styx +Styx's +Suarez +Suarez's +Subaru +Subaru's +Sucre +Sucre's +Sucrets +Sucrets's +Sudan +Sudan's +Sudanese +Sudanese's +Sudetenland +Sudetenland's +Sudoku +Sudoku's +Sudra +Sudra's +Sue +Sue's +Suetonius +Suetonius's +Suez +Suez's +Suffolk +Suffolk's +Sufi +Sufi's +Sufism +Sufism's +Suharto +Suharto's +Sui +Sui's +Sukarno +Sukarno's +Sukkot +Sulawesi +Sulawesi's +Suleiman +Suleiman's +Sulla +Sulla's +Sullivan +Sullivan's +Sumatra +Sumatra's +Sumatran +Sumatrans +Sumatran's +Sumeria +Sumeria's +Sumerian +Sumerians +Sumerian's +Summer +Summers +Summer's +Summers +Summers's +Sumner +Sumner's +Sumter +Sumter's +Sun +Suns +Sun's +Sunbeam +Sunbeam's +Sunbelt +Sunbelt's +Sundanese +Sundanese's +Sundas +Sundas's +Sunday +Sundays +Sunday's +Sung +Sung's +Sunkist +Sunkist's +Sunni +Sunnis +Sunni's +Sunnite +Sunnites +Sunnite's +Sunnyvale +Sunnyvale's +Superbowl +Superbowl's +Superfund +Superfund's +Superglue +Superglue's +Superior +Superior's +Superman +Superman's +Supt +Surabaya +Surabaya's +Surat +Surat's +Suriname +Suriname's +Surinamese +Surya +Surya's +Susan +Susan's +Susana +Susana's +Susanna +Susanna's +Susanne +Susanne's +Susie +Susie's +Susquehanna +Susquehanna's +Sussex +Sussex's +Sutherland +Sutherland's +Sutton +Sutton's +Suva +Suva's +Suwanee +Suwanee's +Suzanne +Suzanne's +Suzette +Suzette's +Suzhou +Suzhou's +Suzuki +Suzuki's +Suzy +Suzy's +Svalbard +Svalbard's +Sven +Sven's +Svengali +Svengali's +Sverdlovsk +Swahili +Swahilis +Swahili's +Swammerdam +Swammerdam's +Swanee +Swanee's +Swansea +Swansea's +Swanson +Swanson's +Swazi +Swazis +Swazi's +Swaziland +Swaziland's +Swed +Sweden +Swede +Swedes +Swede's +Sweden +Sweden's +Swedenborg +Swedenborg's +Swedish +Swedish's +Sweeney +Sweeney's +Sweet +Sweet's +Swift +Swift's +Swinburne +Swinburne's +Swiss +Swisses +Swiss's +Swissair +Swissair's +Switz +Switzerland +Switzerland's +Sybil +Sybil's +Sydney +Sydney's +Sykes +Sykes's +Sylvester +Sylvester's +Sylvia +Sylvia's +Sylvie +Sylvie's +Synge +Synge's +Syracuse +Syracuse's +Syria +Syria's +Syriac +Syriac's +Syrian +Syrians +Syrian's +Szilard +Szilard's +Szymborska +Szymborska's +T'ang +T'ang's +T +Ting +Ted +T's +TA +TARP +TB +TB's +TBA +TD +TDD +TEFL +TELNET +TELNETs +TELNETTed +TELNETTing +TESL +TESOL +TGIF +THC +TKO +TKO's +TLC +TLC's +TM +TN +TNT +TNT's +TOEFL +TQM +TV +TVs +TV's +TVA +TWA +TWA's +TWX +TX +Ta +Ta's +Tabasco +Tabascos +Tabasco's +Tabatha +Tabatha's +Tabernacle +Tabernacles +Tabernacle's +Tabitha +Tabitha's +Tabriz +Tabrizes +Tabriz's +Tacitus +Tacitus's +Tacoma +Tacoma's +Tad +Tad's +Tadzhik +Tadzhik's +Taegu +Taegu's +Taejon +Taejon's +Taft +Taft's +Tagalog +Tagalogs +Tagalog's +Tagore +Tagore's +Tagus +Tagus's +Tahiti +Tahiti's +Tahitian +Tahitians +Tahitian's +Tahoe +Tahoe's +Taichung +Taichung's +Tainan +Taine +Taine's +Taipei +Taipei's +Taiping +Taiping's +Taiwan +Taiwan's +Taiwanese +Taiwanese's +Taiyuan +Taiyuan's +Tajikistan +Tajikistan's +Taklamakan +Taklamakan's +Talbot +Talbot's +Taliban +Taliban's +Taliesin +Taliesin's +Tallahassee +Tallahassee's +Tallchief +Tallchief's +Talley +Talley's +Talleyrand +Talleyrand's +Tallinn +Tallinn's +Talmud +Talmuds +Talmud's +Talmudic +Talmudist +Tamara +Tamara's +Tameka +Tameka's +Tamera +Tamera's +Tamerlane +Tamerlane's +Tami +Tami's +Tamika +Tamika's +Tamil +Tamils +Tamil's +Tammany +Tammany's +Tammi +Tammi's +Tammie +Tammie's +Tammuz +Tammuz's +Tammy +Tammy's +Tampa +Tampa's +Tampax +Tampax's +Tamra +Tamra's +Tamworth +Tamworth's +Tancred +Tancred's +Taney +Taney's +Tanganyika +Tanganyika's +Tangier +Tangiers +Tangier's +Tangshan +Tangshan's +Tania +Tania's +Tanisha +Tanisha's +Tanner +Tanner's +Tannhauser +Tannhauser's +Tantalus +Tantalus's +Tanya +Tanya's +Tanzania +Tanzania's +Tanzanian +Tanzanians +Tanzanian's +Tao +Tao's +Taoism +Taoisms +Taoism's +Taoist +Taoists +Taoist's +Tara +Tara's +Tarantino +Tarantino's +Tarawa +Tarawa's +Tarazed +Tarazed's +Tarbell +Tarbell's +Target +Target's +Tarim +Tarim's +Tarkenton +Tarkenton's +Tarkington +Tarkington's +Tartary +Tartary's +Tartuffe +Tartuffe's +Tarzan +Tarzan's +Tasha +Tasha's +Tashkent +Tashkent's +Tasman +Tasman's +Tasmania +Tasmania's +Tasmanian +Tasmanian's +Tass +Tass's +Tatar +Tatars +Tatar's +Tate +Tate's +Tatum +Tatum's +Taurus +Tauruses +Taurus's +Tawney +Tawney's +Taylor +Taylor's +Tb +Tb's +Tbilisi +Tbilisi's +Tc +Tc's +Tchaikovsky +Tchaikovsky's +Te +Te's +TeX +TeXes +Teasdale +Teasdale's +Technicolor +Technicolor's +Tecumseh +Tecumseh's +Ted +Ted's +Teddy +Teddy's +Teflon +Teflons +Teflon's +Tegucigalpa +Tegucigalpa's +Tehran +TelePrompTer +TelePrompter +TelePrompter's +Telemachus +Telemachus's +Telemann +Telemann's +Teletype +Tell +Teller +Tell's +Teller +Teller's +Telugu +Telugu's +Tempe +Templar +Templar's +Tenn +Tenn's +Tennessean +Tennesseans +Tennessean's +Tennessee +Tennessee's +Tennyson +Tennyson's +Tenochtitlan +Tenochtitlan's +TensorFlow +TensorFlow's +Teotihuacan +Teotihuacan's +Terence +Terence's +Teresa +Teresa's +Tereshkova +Tereshkova's +Teri +Teri's +Terkel +Terkel's +Terpsichore +Terpsichore's +Terr +Terr's +Terra +Terra's +Terran +Terran's +Terrance +Terrance's +Terrell +Terrell's +Terrence +Terrence's +Terri +Terri's +Terrie +Terrie's +Terry +Terry's +Tertiary +Tertiary's +Tesla +Tesla's +Tess +Tess's +Tessa +Tessa's +Tessie +Tessie's +Tet +Tet's +Tethys +Tethys's +Tetons +Tetons's +Teuton +Teutons +Teuton's +Teutonic +Teutonic's +Tevet +Tevet's +Tex +Tex's +Texaco +Texaco's +Texan +Texans +Texan's +Texas +Texas's +Th +Th's +Thackeray +Thackeray's +Thad +Thad's +Thaddeus +Thaddeus's +Thai +Thais +Thai's +Thailand +Thailand's +Thales +Thales's +Thalia +Thalia's +Thames +Thames's +Thanh +Thanh's +Thanksgiving +Thanksgivings +Thanksgiving's +Thant +Thant's +Thar +Thar's +Tharp +Tharp's +Thatcher +Thatcher's +Thea +Thea's +Thebes +Thebes's +Theiler +Theiler's +Thelma +Thelma's +Themistocles +Themistocles's +Theocritus +Theocritus's +Theodora +Theodora's +Theodore +Theodore's +Theodoric +Theodoric's +Theodosius +Theodosius's +Theosophy +Theosophy's +Theravada +Theravada's +Theresa +Theresa's +Therese +Therese's +Thermopylae +Thermopylae's +Thermos +Theron +Theron's +Theseus +Theseus's +Thespian +Thespian's +Thespis +Thespis's +Thessalonian +Thessalonians +Thessalonian's +Thessaloniki +Thessaloniki's +Thessaly +Thessaly's +Thieu +Thieu's +Thimbu +Thimbu's +Thimphu +Thomas +Thomas's +Thomism +Thomism's +Thomistic +Thomistic's +Thompson +Thompson's +Thomson +Thomson's +Thor +Thor's +Thorazine +Thorazine's +Thoreau +Thoreau's +Thornton +Thornton's +Thoroughbred +Thoroughbred's +Thorpe +Thorpe's +Thoth +Thoth's +Thrace +Thrace's +Thracian +Thracian's +Thu +Thucydides +Thucydides's +Thule +Thule's +Thunderbird +Thunderbird's +Thur +Thurs +Thurber +Thurber's +Thurman +Thurman's +Thurmond +Thurmond's +Thursday +Thursdays +Thursday's +Thutmose +Thutmose's +Ti +Ti's +Tia +Tia's +Tianjin +Tianjin's +Tiber +Tiber's +Tiberius +Tiberius's +Tibet +Tibet's +Tibetan +Tibetans +Tibetan's +Ticketmaster +Ticketmaster's +Ticonderoga +Ticonderoga's +Tide +Tide's +Tienanmen +Tienanmen's +Tiffany +Tiffany's +Tigris +Tigris's +Tijuana +Tijuana's +Tillich +Tillich's +Tillman +Tillman's +Tilsit +Tilsit's +Tim +Tim's +Timbuktu +Timbuktu's +Timex +Timex's +Timmy +Timmy's +Timon +Timon's +Timor +Timor's +Timothy +Timothy's +Timur +Timur's +Timurid +Timurid's +Tina +Tina's +Ting +Ting's +Tinkerbell +Tinkerbell's +Tinkertoy +Tinkertoy's +Tinseltown +Tinseltown's +Tintoretto +Tintoretto's +Tippecanoe +Tippecanoe's +Tipperary +Tipperary's +Tirane +Tiresias +Tiresias's +Tirol +Tirol's +Tirolean +Tisha +Tisha's +Tishri +Tishri's +Titan +Titans +Titan's +Titania +Titania's +Titanic +Titanic's +Titian +Titian's +Titicaca +Titicaca's +Tito +Tito's +Titus +Titus's +Tl +Tl's +Tlaloc +Tlaloc's +Tlingit +Tlingit's +Tm +Tm's +Tobago +Tobago's +Tobit +Tobit's +Toby +Toby's +Tocantins +Tocantins's +Tocqueville +Tocqueville's +Tod +Tod's +Todd +Todd's +Togo +Togo's +Togolese +Togolese's +Tojo +Tojo's +Tokay +Tokay's +Tokugawa +Tokugawa's +Tokyo +Tokyo's +Tokyoite +Toledo +Toledos +Toledo's +Tolkien +Tolkien's +Tolstoy +Tolstoy's +Toltec +Toltec's +Tolyatti +Tolyatti's +Tom +Tom's +Tomas +Tomas's +Tombaugh +Tombaugh's +Tomlin +Tomlin's +Tommie +Tommie's +Tommy +Tommy's +Tompkins +Tompkins's +Tomsk +Tomsk's +Tonga +Tonga's +Tongan +Tongans +Tongan's +Toni +Toni's +Tonia +Tonia's +Tonto +Tonto's +Tony +Tony's +Tonya +Tonya's +Topeka +Topeka's +Topsy +Topsy's +Torah +Torah's +Torahs +Toronto +Toronto's +Torquemada +Torquemada's +Torrance +Torrance's +Torrens +Torrens's +Torres +Torres's +Torricelli +Torricelli's +Tortola +Tortola's +Tortuga +Tortuga's +Torvalds +Torvalds's +Tory +Tories +Tory's +Tosca +Tosca's +Toscanini +Toscanini's +Toshiba +Toshiba's +Toto +Toto's +Toulouse +Toulouse's +Townes +Townes's +Townsend +Townsend's +Toynbee +Toynbee's +Toyoda +Toyoda's +Toyota +Toyota's +Tracey +Tracey's +Traci +Traci's +Tracie +Tracie's +Tracy +Tracy's +Trafalgar +Trafalgar's +Trailways +Trailways's +Trajan +Trajan's +Tran +Tran's +Transcaucasia +Transcaucasia's +Transvaal +Transvaal's +Transylvania +Transylvania's +Transylvanian +Transylvanian's +Trappist +Trappists +Trappist's +Travis +Travis's +Travolta +Travolta's +Treasury +Treasuries +Treasury's +Treblinka +Treblinka's +Trekkie +Trekkie's +Trent +Trent's +Trenton +Trenton's +Trevelyan +Trevelyan's +Trevino +Trevino's +Trevor +Trevor's +Trey +Trey's +Triangulum +Triangulum's +Triassic +Triassic's +Tricia +Tricia's +Trident +Trident's +Trieste +Trieste's +Trimurti +Trimurti's +Trina +Trina's +Trinidad +Trinidad's +Trinidadian +Trinidadians +Trinidadian's +Trinity +Trinities +Trinity's +Tripitaka +Tripitaka's +Tripoli +Tripoli's +Trippe +Trippe's +Trisha +Trisha's +Tristan +Tristan's +Triton +Triton's +Trobriand +Trobriand's +Troilus +Troilus's +Trojan +Trojans +Trojan's +Trollope +Trollope's +Trondheim +Trondheim's +Tropicana +Tropicana's +Trotsky +Trotsky's +Troy +Troy's +Troyes +Truckee +Truckee's +Trudeau +Trudeau's +Trudy +Trudy's +Truffaut +Truffaut's +Trujillo +Trujillo's +Truman +Truman's +Trumbull +Trumbull's +Trump +Trump's +Truth +Truth's +Tsimshian +Tsimshian's +Tsiolkovsky +Tsiolkovsky's +Tsitsihar +Tsitsihar's +Tsongkhapa +Tsongkhapa's +Tswana +Tswana's +Tu +Tu's +Tuamotu +Tuamotu's +Tuareg +Tuareg's +Tubman +Tubman's +Tucker +Tucker's +Tucson +Tucson's +Tucuman +Tucuman's +Tudor +Tudors +Tudor's +Tue +Tues +Tues +Tues's +Tuesday +Tuesdays +Tuesday's +Tulane +Tulane's +Tull +Tull's +Tulsa +Tulsa's +Tulsidas +Tulsidas's +Tums +Tums's +Tungus +Tungus's +Tunguska +Tunguska's +Tunis +Tunis's +Tunisia +Tunisia's +Tunisian +Tunisians +Tunisian's +Tunney +Tunney's +Tupi +Tupi's +Tupperware +Tupperware's +Tupungato +Tupungato's +Turgenev +Turgenev's +Turin +Turin's +Turing +Turing's +Turk +Turks +Turk's +Turkestan +Turkestan's +Turkey +Turkey's +Turkic +Turkics +Turkic's +Turkish +Turkish's +Turkmenistan +Turkmenistan's +Turner +Turner's +Turpin +Turpin's +Tuscaloosa +Tuscaloosa's +Tuscan +Tuscan's +Tuscany +Tuscany's +Tuscarora +Tuscaroras +Tuscarora's +Tuscon +Tuscon's +Tuskegee +Tuskegee's +Tussaud +Tussaud's +Tut +Tut's +Tutankhamen +Tutankhamen's +Tutsi +Tutsi's +Tutu +Tutu's +Tuvalu +Tuvalu's +Tuvaluan +Twain +Twain's +Tweed +Tweed's +Tweedledee +Tweedledee's +Tweedledum +Tweedledum's +Twila +Twila's +Twinkies +Twinkies's +Twitter +Twitter's +Twizzlers +Twizzlers's +Twp +Ty +Ty's +Tycho +Tycho's +Tylenol +Tylenol's +Tyler +Tyler's +Tyndale +Tyndale's +Tyndall +Tyndall's +Tyre +Tyre's +Tyree +Tyree's +Tyrolean +Tyrone +Tyrone's +Tyson +Tyson's +U +U's +UAR +UAW +UBS +UBS's +UCLA +UCLA's +UFO +UFOs +UFO's +UHF +UHF's +UK +UK's +UL +UN +UN's +UNESCO +UNESCO's +UNICEF +UNICEF's +UNIX +UNIX's +UPC +UPI +UPI's +UPS +UPS's +URL +URLs +US +US's +USA +USA's +USAF +USB +USCG +USDA +USDA's +USIA +USMC +USN +USO +USP +USPS +USS +USSR +USSR's +UT +UT's +UTC +UV +UV's +Ubangi +Ubangi's +Ubuntu +Ubuntu's +Ucayali +Ucayali's +Uccello +Uccello's +Udall +Udall's +Ufa +Ufa's +Uganda +Uganda's +Ugandan +Ugandans +Ugandan's +Uighur +Uighur's +Ujungpandang +Ujungpandang's +Ukraine +Ukraine's +Ukrainian +Ukrainians +Ukrainian's +Ulster +Ulster's +Ultrasuede +Ultrasuede's +Ulyanovsk +Ulyanovsk's +Ulysses +Ulysses's +Umbriel +Umbriel's +Underwood +Underwood's +Ungava +Ungava's +Unicode +Unicode's +Unilever +Unilever's +Union +Unions +Union's +Unionist +Uniroyal +Uniroyal's +Unitarian +Unitarians +Unitarian's +Unitarianism +Unitarianisms +Unitarianism's +Unitas +Unitas's +Unix +Unixes +Unukalhai +Unukalhai's +Upanishads +Upanishads's +Updike +Updike's +Upjohn +Upjohn's +Upton +Upton's +Ur +Ur's +Ural +Urals +Ural's +Urals +Urals's +Urania +Urania's +Uranus +Uranus's +Urban +Urban's +Urdu +Urdu's +Urey +Urey's +Uriah +Uriah's +Uriel +Uriel's +Uris +Uris's +Urquhart +Urquhart's +Ursa +Ursa's +Ursula +Ursula's +Ursuline +Ursuline's +Uruguay +Uruguay's +Uruguayan +Uruguayans +Uruguayan's +Urumqi +Urumqi's +Usenet +Usenets +Usenet's +Ustinov +Ustinov's +Ut +Utah +Utah's +Utahan +Utahans +Utahan's +Ute +Utes +Ute's +Utopia +Utopias +Utopia's +Utopian +Utopians +Utopian's +Utrecht +Utrecht's +Utrillo +Utrillo's +Uzbek +Uzbek's +Uzbekistan +Uzbekistan's +Uzi +Uzis +Uzi's +V +V's +VA +VAT +VAT's +VAX +VAXes +VBA +VBA's +VCR +VCR's +VD +VD's +VDT +VDU +VF +VFW +VFW's +VG +VGA +VHF +VHF's +VHS +VI +VI's +VIP +VIPs +VIP's +VISTA +VJ +VLF +VLF's +VOA +VP +VT +VTOL +Va +Va's +Vader +Vader's +Vaduz +Vaduz's +Val +Val's +Valarie +Valarie's +Valdez +Valdez's +Valencia +Valencias +Valencia's +Valenti +Valenti's +Valentin +Valentin's +Valentine +Valentine's +Valentino +Valentino's +Valenzuela +Valenzuela's +Valeria +Valeria's +Valerian +Valerian's +Valerie +Valerie's +Valery +Valery's +Valhalla +Valhalla's +Valium +Valiums +Valium's +Valkyrie +Valkyries +Valkyrie's +Vallejo +Valletta +Valletta's +Valois +Valois's +Valparaiso +Valparaiso's +Valvoline +Valvoline's +Van +Van's +Vance +Vance's +Vancouver +Vancouver's +Vandal +Vandals +Vandal's +Vanderbilt +Vanderbilt's +Vandyke +Vandyke's +Vanessa +Vanessa's +Vang +Vang's +Vanuatu +Vanuatu's +Vanzetti +Vanzetti's +Varanasi +Varanasi's +Varese +Varese's +Vargas +Vargas's +Vaseline +Vaselines +Vaseline's +Vasquez +Vasquez's +Vassar +Vassar's +Vatican +Vatican's +Vauban +Vauban's +Vaughan +Vaughan's +Vaughn +Vaughn's +Vazquez +Vazquez's +Veblen +Veblen's +Veda +Vedas +Veda's +Vedanta +Vedanta's +Vega +Vegas +Vega's +Vegas +Vegas's +Vegemite +Vegemite's +Vela +Vela's +Velasquez +Velasquez's +Velazquez +Velazquez's +Velcro +Velcros +Velcro's +Velez +Velez's +Velma +Velma's +Velveeta +Velveeta's +Venetian +Venetians +Venetian's +Venezuela +Venezuela's +Venezuelan +Venezuelans +Venezuelan's +Venice +Venice's +Venn +Venn's +Ventolin +Ventolin's +Venus +Venuses +Venus's +Venusian +Venusian's +Vera +Vera's +Veracruz +Veracruz's +Verde +Verde's +Verdi +Verdi's +Verdun +Verdun's +Verizon +Verizon's +Verlaine +Verlaine's +Vermeer +Vermeer's +Vermont +Vermonter +Vermonters +Vermont's +Vermonter +Vermonter's +Vern +Vern's +Verna +Verna's +Verne +Verne's +Vernon +Vernon's +Verona +Verona's +Veronese +Veronese's +Veronica +Veronica's +Versailles +Versailles's +Vesalius +Vesalius's +Vespasian +Vespasian's +Vespucci +Vespucci's +Vesta +Vesta's +Vesuvius +Vesuvius's +Viacom +Viacom's +Viagra +Viagra's +Vic +Vic's +Vicente +Vicente's +Vichy +Vichy's +Vicki +Vicki's +Vickie +Vickie's +Vicksburg +Vicksburg's +Vicky +Vicky's +Victor +Victor's +Victoria +Victoria's +Victorian +Victorians +Victorian's +Victorianism +Victrola +Victrola's +Vidal +Vidal's +Vienna +Vienna's +Viennese +Viennese's +Vientiane +Vientiane's +Vietcong +Vietcong's +Vietminh +Vietminh's +Vietnam +Vietnam's +Vietnamese +Vietnamese's +Vijayanagar +Vijayanagar's +Vijayawada +Vijayawada's +Viking +Vikings +Viking's +Vila +Vila's +Villa +Villa's +Villarreal +Villarreal's +Villon +Villon's +Vilma +Vilma's +Vilnius +Vilnius's +Vilyui +Vilyui's +Vince +Vince's +Vincent +Vincent's +Vindemiatrix +Vindemiatrix's +Vinson +Vinson's +Viola +Viola's +Violet +Violet's +Virgie +Virgie's +Virgil +Virgil's +Virginia +Virginia's +Virginian +Virginians +Virginian's +Virgo +Virgos +Virgo's +Visa +Visa's +Visayans +Visayans's +Vishnu +Vishnu's +Visigoth +Visigoth's +Visigoths +Vistula +Vistula's +Vitim +Vitim's +Vito +Vito's +Vitus +Vitus's +Vivaldi +Vivaldi's +Vivekananda +Vivekananda's +Vivian +Vivian's +Vivienne +Vivienne's +Vlad +Vlad's +Vladimir +Vladimir's +Vladivostok +Vladivostok's +Vlaminck +Vlaminck's +Vlasic +Vlasic's +VoIP +Vogue +Vogue's +Volcker +Volcker's +Voldemort +Voldemort's +Volga +Volga's +Volgograd +Volgograd's +Volkswagen +Volkswagen's +Volstead +Volstead's +Volta +Volta's +Voltaire +Voltaire's +Volvo +Volvo's +Vonda +Vonda's +Vonnegut +Vonnegut's +Voronezh +Voronezh's +Vorster +Vorster's +Voyager +Voyager's +Vt +Vuitton +Vuitton's +Vulcan +Vulcan's +Vulg +Vulgate +Vulgates +Vulgate's +W +Wed +West +W's +WA +WAC +WASP +WASP's +WATS +WATS's +WC +WHO +WHO's +WI +WMD +WNW +WNW's +WP +WSW +WSW's +WTO +WV +WW +WWI +WWII +WWW +WWW's +WY +WYSIWYG +Wabash +Wabash's +Wac +Waco +Waco's +Wade +Wade's +Wagner +Wagner's +Wagnerian +Wagnerian's +Wahhabi +Wahhabi's +Waikiki +Waikiki's +Waite +Waite's +Wake +Wake's +Waksman +Waksman's +Wald +Walden +Wald's +Waldemar +Waldemar's +Walden +Walden's +Waldensian +Waldensian's +Waldheim +Waldheim's +Waldo +Waldo's +Waldorf +Waldorf's +Wales +Wales's +Walesa +Walesa's +Walgreen +Walgreens +Walgreen's +Walgreens +Walgreens's +Walker +Walker's +Walkman +Walkman's +Wall +Waller +Walls +Wall's +Wallace +Wallace's +Wallenstein +Wallenstein's +Waller +Waller's +Wallis +Wallis's +Walloon +Walloon's +Walls +Walls's +Walmart +Walmart's +Walpole +Walpole's +Walpurgisnacht +Walpurgisnacht's +Walsh +Walsh's +Walt +Walter +Walters +Walt's +Walter +Walter's +Walters +Walters's +Walton +Walton's +Wanamaker +Wanamaker's +Wanda +Wanda's +Wang +Wang's +Wankel +Wankel's +Ward +Ward's +Ware +Waring +Ware's +Warhol +Warhol's +Waring +Waring's +Warner +Warner's +Warren +Warren's +Warsaw +Warsaw's +Warwick +Warwick's +Wasatch +Wasatch's +Wash +Wash's +Washington +Washington's +Washingtonian +Washingtonians +Washingtonian's +Wassermann +Wassermann's +Waterbury +Waterbury's +Waterford +Waterford's +Watergate +Watergate's +Waterloo +Waterloos +Waterloo's +Waters +Waters's +Watkins +Watkins's +Watson +Watson's +Watt +Watts +Watt's +Watteau +Watteau's +Watts +Watts's +Watusi +Watusi's +Waugh +Waugh's +Wave +Wayne +Wayne's +Weaver +Weaver's +Web +Weber +Web's +Webb +Webb's +Weber +Weber's +Webern +Webern's +Webster +Websters +Webster's +Wed +Wed's +Weddell +Weddell's +Wedgwood +Wedgwood's +Wednesday +Wednesdays +Wednesday's +Weeks +Weeks's +Wehrmacht +Wehrmacht's +Wei +Wei's +Weierstrass +Weierstrass's +Weill +Weill's +Weinberg +Weinberg's +Weiss +Weiss's +Weissmuller +Weissmuller's +Weizmann +Weizmann's +Weldon +Weldon's +Welland +Welland's +Weller +Weller's +Welles +Welles's +Wellington +Wellingtons +Wellington's +Wells +Wells's +Welsh +Welsh's +Welshman +Welshman's +Welshmen +Welshmen's +Welshwoman +Wendell +Wendell's +Wendi +Wendi's +Wendy +Wendy's +Wesak +Wesak's +Wesley +Wesley's +Wesleyan +Wesleyan's +Wessex +Wessex's +Wesson +Wesson's +West +Wests +West's +Western +Westerner +Westerns +Western's +Westinghouse +Westinghouse's +Westminster +Westminster's +Weston +Weston's +Westphalia +Westphalia's +Weyden +Weyden's +Wezen +Wezen's +Wharton +Wharton's +Wheaties +Wheaties's +Wheatstone +Wheatstone's +Wheeler +Wheeler's +Wheeling +Wheeling's +Whig +Whigs +Whig's +Whipple +Whipple's +Whirlpool +Whirlpool's +Whistler +Whistler's +Whitaker +Whitaker's +White +Whites +White's +Whitefield +Whitefield's +Whitehall +Whitehall's +Whitehead +Whitehead's +Whitehorse +Whitehorse's +Whiteley +Whiteley's +Whitfield +Whitfield's +Whitley +Whitley's +Whitman +Whitman's +Whitney +Whitney's +Whitsunday +Whitsundays +Whitsunday's +Whittier +Whittier's +WiFi +Wicca +Wicca's +Wichita +Wichita's +Wiemar +Wiemar's +Wiesel +Wiesel's +Wiesenthal +Wiesenthal's +Wiggins +Wiggins's +Wigner +Wigner's +Wii +Wii's +Wikileaks +Wikipedia +Wikipedia's +Wilberforce +Wilberforce's +Wilbert +Wilbert's +Wilbur +Wilbur's +Wilburn +Wilburn's +Wilcox +Wilcox's +Wilda +Wilda's +Wilde +Wilder +Wilde's +Wilder +Wilder's +Wiles +Wiles's +Wiley +Wiley's +Wilford +Wilford's +Wilfred +Wilfred's +Wilfredo +Wilfredo's +Wilhelm +Wilhelm's +Wilhelmina +Wilhelmina's +Wilkerson +Wilkerson's +Wilkes +Wilkes's +Wilkins +Wilkins's +Wilkinson +Wilkinson's +Will +Will's +Willa +Willa's +Willamette +Willamette's +Willard +Willard's +Willemstad +Willemstad's +William +Williams +William's +Williams +Williams's +Williamson +Williamson's +Willie +Willie's +Willis +Willis's +Willy +Willy's +Wilma +Wilma's +Wilmer +Wilmer's +Wilmington +Wilmington's +Wilson +Wilson's +Wilsonian +Wilsonian's +Wilton +Wilton's +Wimbledon +Wimbledon's +Wimsey +Wimsey's +Winchell +Winchell's +Winchester +Winchesters +Winchester's +Windbreaker +Windbreaker's +Windex +Windex's +Windhoek +Windhoek's +Windows +Windows's +Windsor +Windsors +Windsor's +Windward +Windward's +Winesap +Winesap's +Winfred +Winfred's +Winfrey +Winfrey's +Winifred +Winifred's +Winkle +Winkle's +Winnebago +Winnebago's +Winnie +Winnie's +Winnipeg +Winnipeg's +Winston +Winston's +Winters +Winters's +Winthrop +Winthrop's +Wis +Wisc +Wisconsin +Wisconsin's +Wisconsinite +Wisconsinites +Wisconsinite's +Wise +Wise's +Witt +Witt's +Wittgenstein +Wittgenstein's +Witwatersrand +Witwatersrand's +Wm +Wm's +Wobegon +Wobegon's +Wodehouse +Wodehouse's +Wolf +Wolf's +Wolfe +Wolfe's +Wolff +Wolff's +Wolfgang +Wolfgang's +Wollongong +Wollongong's +Wollstonecraft +Wollstonecraft's +Wolsey +Wolsey's +Wolverhampton +Wonder +Wonder's +Wonderbra +Wonderbra's +Wong +Wong's +Wood +Woods +Wood's +Woodard +Woodard's +Woodhull +Woodhull's +Woodrow +Woodrow's +Woods +Woods's +Woodstock +Woodstock's +Woodward +Woodward's +Woolf +Woolf's +Woolite +Woolite's +Woolongong +Woolongong's +Woolworth +Woolworth's +Wooster +Wooster's +Wooten +Wooten's +Worcester +Worcesters +Worcester's +Worcestershire +Worcestershire's +WordPress +WordPress's +Wordsworth +Wordsworth's +Workman +Workman's +Worms +Worms's +Wotan +Wotan's +Wovoka +Wovoka's +Wozniak +Wozniak's +Wozzeck +Wozzeck's +Wrangell +Wrangell's +Wren +Wren's +Wright +Wright's +Wrigley +Wrigley's +Wroclaw +Wroclaw's +Wu +Wu's +Wuhan +Wuhan's +Wurlitzer +Wurlitzer's +Wyatt +Wyatt's +Wycherley +Wycherley's +Wycliffe +Wycliffe's +Wyeth +Wyeth's +Wylie +Wylie's +Wynn +Wynn's +Wyo +Wyoming +Wyoming's +Wyomingite +Wyomingites +Wyomingite's +X +X's +XEmacs +XEmacs's +XL +XL's +XML +XS +XXL +Xamarin +Xamarin's +Xanadu +Xanadu's +Xanthippe +Xanthippe's +Xavier +Xavier's +Xe +Xes +Xe's +Xenakis +Xenakis's +Xenia +Xenia's +Xenophon +Xenophon's +Xerox +Xeroxes +Xerox's +Xerxes +Xerxes's +Xhosa +Xhosa's +Xi'an +Xi'an's +Xian +Xians +Xian's +Xiaoping +Xiaoping's +Ximenes +Ximenes's +Xingu +Xingu's +Xinjiang +Xinjiang's +Xiongnu +Xiongnu's +Xizang +Xizang's +Xmas +Xmases +Xmas's +Xochipilli +Xochipilli's +Xuzhou +Xuzhou's +Y +Y's +YMCA +YMCA's +YMHA +YMMV +YT +YWCA +YWCA's +YWHA +Yacc +Yacc's +Yahoo +Yahoo's +Yahtzee +Yahtzee's +Yahweh +Yahweh's +Yakima +Yakima's +Yakut +Yakut's +Yakutsk +Yakutsk's +Yale +Yale's +Yalow +Yalow's +Yalta +Yalta's +Yalu +Yalu's +Yamagata +Yamagata's +Yamaha +Yamaha's +Yamoussoukro +Yamoussoukro's +Yang +Yang's +Yangon +Yangon's +Yangtze +Yangtze's +Yank +Yanks +Yank's +Yankee +Yankees +Yankee's +Yaobang +Yaobang's +Yaounde +Yaounde's +Yaqui +Yaqui's +Yaren +Yaroslavl +Yaroslavl's +Yataro +Yataro's +Yates +Yates's +Yb +Yb's +Yeager +Yeager's +Yeats +Yeats's +Yekaterinburg +Yekaterinburg's +Yellowknife +Yellowknife's +Yellowstone +Yellowstone's +Yeltsin +Yeltsin's +Yemen +Yemen's +Yemeni +Yemenis +Yemeni's +Yemenite +Yenisei +Yenisei's +Yerevan +Yerevan's +Yerkes +Yerkes's +Yesenia +Yesenia's +Yevtushenko +Yevtushenko's +Yggdrasil +Yggdrasil's +Yiddish +Yiddish's +Ymir +Ymir's +Yoda +Yoda's +Yoknapatawpha +Yoknapatawpha's +Yoko +Yoko's +Yokohama +Yokohama's +Yolanda +Yolanda's +Yong +Yong's +Yonkers +Yonkers's +York +York's +Yorkie +Yorkie's +Yorkshire +Yorkshires +Yorkshire's +Yorktown +Yorktown's +Yoruba +Yoruba's +Yosemite +Yosemite's +Yossarian +Yossarian's +YouTube +YouTube's +Young +Young's +Youngstown +Youngstown's +Ypres +Ypres's +Ypsilanti +Ypsilanti's +Yuan +Yuan's +Yucatan +Yucatan's +Yugo +Yugo's +Yugoslav +Yugoslavs +Yugoslav's +Yugoslavia +Yugoslavia's +Yugoslavian +Yugoslavians +Yugoslavian's +Yukon +Yukon's +Yule +Yules +Yule's +Yuletide +Yuletides +Yuletide's +Yuma +Yumas +Yuma's +Yunnan +Yunnan's +Yuri +Yuri's +Yves +Yves's +Yvette +Yvette's +Yvonne +Yvonne's +Z +Zen +Zens +Zest +Zs +Z's +Zachariah +Zachariah's +Zachary +Zachary's +Zachery +Zachery's +Zagreb +Zagreb's +Zaire +Zaire's +Zairian +Zambezi +Zambezi's +Zambia +Zambia's +Zambian +Zambians +Zambian's +Zamboni +Zamboni's +Zamenhof +Zamenhof's +Zamora +Zamora's +Zane +Zane's +Zanuck +Zanuck's +Zanzibar +Zanzibar's +Zapata +Zapata's +Zaporozhye +Zaporozhye's +Zapotec +Zapotec's +Zappa +Zappa's +Zara +Zara's +Zarathustra +Zarathustra's +Zealand +Zealand's +Zebedee +Zebedee's +Zechariah +Zechariah's +Zedekiah +Zedekiah's +Zedong +Zedong's +Zeffirelli +Zeffirelli's +Zeke +Zeke's +Zelig +Zelig's +Zelma +Zelma's +Zen +Zen's +Zenger +Zenger's +Zeno +Zeno's +Zephaniah +Zephaniah's +Zephyrus +Zephyrus's +Zest +Zest's +Zeus +Zeus's +Zhdanov +Zhejiang +Zhejiang's +Zhengzhou +Zhengzhou's +Zhivago +Zhivago's +Zhukov +Zhukov's +Zibo +Zibo's +Ziegfeld +Ziegfeld's +Ziegler +Ziegler's +Ziggy +Ziggy's +Zika +Zimbabwe +Zimbabwe's +Zimbabwean +Zimbabweans +Zimbabwean's +Zimmerman +Zimmerman's +Zinfandel +Zinfandel's +Zion +Zions +Zion's +Zionism +Zionisms +Zionism's +Zionist +Zionists +Zionist's +Ziploc +Ziploc's +Zn +Zn's +Zoe +Zoe's +Zola +Zola's +Zollverein +Zollverein's +Zoloft +Zoloft's +Zomba +Zomba's +Zorn +Zorn's +Zoroaster +Zoroaster's +Zoroastrian +Zoroastrians +Zoroastrian's +Zoroastrianism +Zoroastrianisms +Zoroastrianism's +Zorro +Zorro's +Zosma +Zosma's +Zr +Zr's +Zsigmondy +Zsigmondy's +Zubenelgenubi +Zubenelgenubi's +Zubeneschamali +Zubeneschamali's +Zukor +Zukor's +Zulu +Zulus +Zulu's +Zululand +Zuni +Zuni's +Zurich +Zurich's +Zwingli +Zwingli's +Zworykin +Zworykin's +Zyrtec +Zyrtec's +Zyuganov +Zyuganov's +Zzz +a +as +aah +aardvark +aardvarks +aardvark's +ab +ably +abed +abs +aback +abacus +abacuses +abacus's +abaft +abalone +abalones +abalone's +abandon +abandoning +abandoned +abandons +abandonment +abandonment +abandonment's +abase +abasing +abased +abases +abasement +abasement +abasement's +abash +abashing +abashed +abashes +abashment +abashed +abashedly +unabashedly +unabashed +abashment +abashment's +abate +abating +abated +abates +abatement +abated +unabated +abatement +abatement's +abattoir +abattoirs +abattoir's +abbe +abbes +abbe's +abbess +abbesses +abbess's +abbey +abbeys +abbey's +abbot +abbots +abbot's +abbr +abbrev +abbrevs +abbreviate +abbreviation +abbreviations +abbreviating +abbreviated +abbreviates +abbreviation +abbreviation's +abdicate +abdication +abdications +abdicating +abdicated +abdicates +abdication +abdication's +abdomen +abdomens +abdomen's +abdominal +abduct +abducting +abducted +abducts +abductee +abductees +abductee's +abduction +abductions +abduction's +abductor +abductors +abductor's +abeam +aberrant +aberration +aberrations +aberration's +aberrational +abet +abets +abetted +abetting +abettor +abettors +abettor's +abeyance +abeyance's +abhor +abhors +abhorred +abhorrence +abhorrence's +abhorrent +abhorrently +abhorring +abidance +abidance's +abide +abiding +abides +abiding +abidingly +ability +abilities +ability's +inabilities +disabilities +inability's +disability's +inability +disability +abject +abjectly +abjectness +abjection +abjection's +abjectness +abjectness's +abjuration +abjurations +abjuration's +abjuratory +abjure +abjuring +abjured +abjurer +abjurers +abjures +abjurer +abjurer's +ablate +ablative +ablation +ablations +ablating +ablated +ablates +ablation +ablation's +ablative +ablatives +ablative's +ablaze +able +ablest +unable +abler +abloom +ablution +ablutions +ablution's +abnegate +abnegation +abnegating +abnegated +abnegates +abnegation +abnegation's +abnormal +abnormally +abnormality +abnormalities +abnormality's +aboard +abode +abodes +abode's +abolish +abolishing +abolished +abolishes +abolition +abolition's +abolitionism +abolitionism's +abolitionist +abolitionists +abolitionist's +abominable +abominably +abominate +abomination +abominations +abominating +abominated +abominates +abomination +abomination's +aboriginal +aboriginals +aboriginal's +aborigine +aborigines +aborigine's +aborning +abort +abortive +aborting +aborted +aborts +abortion +abortions +abortion's +abortionist +abortionists +abortionist's +abortive +abortively +abound +abounding +abounded +abounds +about +above +above's +aboveboard +abracadabra +abracadabra's +abrade +abrading +abraded +abrades +abrasion +abrasions +abrasion's +abrasive +abrasively +abrasives +abrasiveness +abrasive's +abrasiveness +abrasiveness's +abreast +abridge +abridging +abridged +abridges +abridgment +abridgments +abridgment's +abroad +abrogate +abrogation +abrogations +abrogating +abrogated +abrogates +abrogation +abrogation's +abrogator +abrogators +abrogator's +abrupt +abruptly +abruptest +abrupter +abruptness +abruptness +abruptness's +abs +abs's +abscess +abscessing +abscessed +abscesses +abscess's +abscissa +abscissas +abscissa's +abscission +abscission's +abscond +absconding +absconded +absconder +absconders +absconds +absconder +absconder's +abseil +abseiling +abseiled +abseils +abseil's +absence +absences +absence's +absent +absently +absenting +absented +absents +absentee +absentees +absentee's +absenteeism +absenteeism's +absentminded +absentmindedly +absentmindedness +absentmindedness +absentmindedness's +absinthe +absinthe's +absolute +absolution +absolutely +absolutest +absolutes +absoluteness +absolute's +absoluteness +absoluteness's +absolution +absolution's +absolutism +absolutism's +absolutist +absolutists +absolutist's +absolve +absolving +absolved +absolves +absorb +absorbing +absorbed +absorbs +reabsorbing +reabsorbed +reabsorbs +reabsorb +absorbance +absorbency +absorbency's +absorbent +absorbents +absorbent's +absorbing +absorbingly +absorption +absorption's +absorptive +abstain +abstaining +abstained +abstainer +abstainers +abstains +abstainer +abstainer's +abstemious +abstemiously +abstemiousness +abstemiousness +abstemiousness's +abstention +abstentions +abstention's +abstinence +abstinence's +abstinent +abstract +abstractly +abstracting +abstracted +abstracts +abstractness +abstract's +abstracted +abstractedly +abstractedness +abstractedness +abstractedness's +abstraction +abstractions +abstraction's +abstractness +abstractnesses +abstractness's +abstruse +abstrusely +abstruseness +abstruseness +abstruseness's +absurd +absurdly +absurdest +absurder +absurdness +absurdist +absurdists +absurdist's +absurdity +absurdities +absurdity's +absurdness +absurdness's +abundance +abundances +abundance's +abundant +abundantly +abuse's +abuse +abusive +abusing +abused +abuses +disabusing +disabused +disabuses +disabuse +abuser +abusers +abuser's +abusive +abusively +abusiveness +abusiveness +abusiveness's +abut +abuts +abutment +abutment +abutments +abutment's +abutted +abutting +abuzz +abysmal +abysmally +abyss +abysses +abyss's +abyssal +ac +acacia +acacias +acacia's +academe +academe's +academia +academia's +academic +academics +academic's +academical +academically +academician +academicians +academician's +academy +academies +academy's +acanthus +acanthuses +acanthus's +accede +acceding +acceded +accedes +accelerate +acceleration +accelerations +accelerating +accelerated +accelerates +acceleration +acceleration's +accelerator +accelerators +accelerator's +accent +accenting +accented +accents +accent's +accented +unaccented +accentual +accentuate +accentuation +accentuating +accentuated +accentuates +accentuation +accentuation's +accept +accepting +accepted +accepts +acceptable +acceptability +acceptability's +acceptableness +acceptableness's +acceptably +unacceptably +acceptance +acceptances +acceptance's +acceptation +acceptations +acceptation's +accepted +unaccepted +access +accessing +accessed +accesses +access's +accessibility +accessibility's +inaccessibility's +inaccessibility +accessible +inaccessible +accessibly +inaccessibly +accession +accessioning +accessioned +accessions +accession's +accessorize +accessorizing +accessorized +accessorizes +accessory +accessories +accessory's +accident +accidents +accident's +accidental +accidentally +accidentals +accidental's +acclaim +acclaiming +acclaimed +acclaims +acclaim's +acclamation +acclamation's +acclimate +acclimation +acclimating +acclimated +acclimates +acclimation +acclimation's +acclimatization +acclimatization's +acclimatize +acclimatizing +acclimatized +acclimatizes +acclivity +acclivities +acclivity's +accolade +accolades +accolade's +accommodate +accommodation +accommodations +accommodating +accommodated +accommodates +accommodating +accommodatingly +accommodation +accommodation's +accompanied +unaccompanied +accompaniment +accompaniments +accompaniment's +accompanist +accompanists +accompanist's +accompany +accompanying +accompanied +accompanies +accomplice +accomplices +accomplice's +accomplish +accomplishing +accomplished +accomplishes +accomplishment +accomplished +unaccomplished +accomplishment +accomplishments +accomplishment's +accord +according +accorded +accords +accord's +accordance +accordance's +accordant +according +accordingly +accordion +accordions +accordion's +accordionist +accordionists +accordionist's +accost +accosting +accosted +accosts +accost's +account +accounting +accounted +accounts +account's +accountable +accountability +accountability's +accountable +unaccountable +accountancy +accountancy's +accountant +accountants +accountant's +accounted +unaccounted +accounting +accounting's +accouter +accoutering +accoutered +accouters +accouterments +accouterments's +accredit +accrediting +accredited +accredits +accreditation +accreditation's +accredited +unaccredited +accretion +accretions +accretion's +accrual +accruals +accrual's +accrue +accruing +accrued +accrues +acct +acculturate +acculturation +acculturating +acculturated +acculturates +acculturation +acculturation's +accumulate +accumulative +accumulation +accumulations +accumulating +accumulated +accumulates +accumulation +accumulation's +accumulator +accumulators +accumulator's +accuracy +accuracy's +inaccuracy's +inaccuracy +accurate +accurately +inaccurately +inaccurate +accurateness +accurateness's +accursed +accursedness +accursedness +accursedness's +accusation +accusations +accusation's +accusative +accusatives +accusative's +accusatory +accuse +accusing +accused +accuser +accusers +accuses +accuser +accuser's +accusing +accusingly +accustom +accustoming +accustomed +accustoms +accustomed +unaccustomed +ace +acing +aced +aces +ace's +acerbate +acerbating +acerbated +acerbates +acerbic +acerbically +acerbity +acerbity's +acetaminophen +acetaminophen's +acetate +acetates +acetate's +acetic +acetone +acetone's +acetonic +acetyl +acetylene +acetylene's +ache +aching +ached +aches +ache's +achene +achenes +achene's +achievable +unachievable +achieve +achieving +achieved +achiever +achievers +achieves +achievable +achievement +achievement +achievements +achievement's +achiever +achiever's +aching +achingly +achoo +achoo's +achromatic +achy +achiest +achier +acid +acidly +acids +acid's +acidic +acidify +acidifying +acidified +acidifies +acidity +acidity's +acidosis +acidosis's +acidulous +acknowledge +acknowledging +acknowledged +acknowledges +acknowledged +unacknowledged +acknowledgment +acknowledgments +acknowledgment's +acme +acmes +acme's +acne +acne's +acolyte +acolytes +acolyte's +aconite +aconites +aconite's +acorn +acorns +acorn's +acoustic +acoustics +acoustical +acoustically +acoustics +acoustics's +acquaint +acquainting +acquainted +acquaints +reacquainting +reacquainted +reacquaints +reacquaint +acquaintance +acquaintances +acquaintance's +acquaintanceship +acquaintanceship's +acquainted +unacquainted +acquiesce +acquiescing +acquiesced +acquiesces +acquiescence +acquiescence's +acquiescent +acquiescently +acquire +acquiring +acquired +acquirer +acquirers +acquires +acquirable +acquirement +acquirement +acquirement's +acquisition +acquisitions +acquisition's +acquisitive +acquisitively +acquisitiveness +acquisitiveness +acquisitiveness's +acquit +acquits +acquittal +acquittals +acquittal's +acquitted +acquitting +acre +acres +acre's +acreage +acreages +acreage's +acrid +acridly +acridest +acrider +acridness +acridity +acridity's +acridness +acridness's +acrimonious +acrimoniously +acrimoniousness +acrimoniousness +acrimoniousness's +acrimony +acrimony's +acrobat +acrobats +acrobat's +acrobatic +acrobatics +acrobatically +acrobatics +acrobatics's +acronym +acronyms +acronym's +acrophobia +acrophobia's +acropolis +acropolises +acropolis's +across +acrostic +acrostics +acrostic's +acrylamide +acrylic +acrylics +acrylic's +act's +act +active +acting +acted +acts +reacting +reacted +reacts +react +acting +acting's +actinium +actinium's +action +actions +action's +reactions +reaction's +reaction +actionable +activate +activation +activating +activated +activates +reactivation +inactivation +deactivation +reactivating +inactivating +deactivating +reactivated +inactivated +deactivated +reactivates +inactivates +deactivates +reactivate +inactivate +deactivate +activation +activation's +reactivation's +inactivation's +deactivation's +reactivation +inactivation +deactivation +activator +activators +activator's +active's +active +actively +inactively +proactively +inactive +proactive +activeness +activeness's +actives +activism +activism's +activist +activists +activist's +activities +activity +activity's +inactivity's +inactivity +actor +actors +actor's +reactors +reactor's +reactor +actress +actresses +actress's +actual +actually +actuality +actualities +actuality's +actualization +actualization's +actualize +actualizing +actualized +actualizes +actuarial +actuary +actuaries +actuary's +actuate +actuation +actuating +actuated +actuates +actuation +actuation's +actuator +actuators +actuator's +acuity +acuity's +acumen +acumen's +acupressure +acupressure's +acupuncture +acupuncture's +acupuncturist +acupuncturists +acupuncturist's +acute +acutely +acutest +acuter +acutes +acuteness +acute's +acuteness +acuteness's +acyclovir +acyclovir's +acyl +ad +ads +ad's +adage +adages +adage's +adagio +adagios +adagio's +adamant +adamantly +adamant's +adapt +adaptive +adapting +adapted +adapter +adapters +adapts +adaptable +adaptability +adaptability's +adaptation +adaptations +adaptation's +adapter +adapter's +adaption +adaptions +add +adding +added +adder +adders +adds +addable +addend +addends +addend's +addenda +addendum +addendum's +adder +adder's +addict +addictive +addicting +addicted +addicts +addict's +addiction +addictions +addiction's +addition +additions +addition's +additional +additionally +additive +additives +additive's +addle +addling +addled +addles +address's +address +addressing +addressed +addresses +readdressing +readdressed +readdresses +readdress +addressable +addressed +unaddressed +addressee +addressees +addressee's +adduce +adducing +adduced +adduces +adenine +adenine's +adenocarcinoma +adenoid +adenoids +adenoid's +adenoidal +adept +adeptly +adepts +adeptness +adept's +adeptness +adeptness's +adequacy +adequacy's +inadequacy's +inadequacy +adequate +adequately +inadequately +inadequate +adequateness +adequateness's +adhere +adhering +adhered +adheres +adherence +adherence's +adherent +adherents +adherent's +adhesion +adhesion's +adhesive +adhesives +adhesiveness +adhesive's +adhesiveness +adhesiveness's +adiabatic +adieu +adieus +adieu's +adios +adipose +adj +adjacency +adjacency's +adjacent +adjacently +adjectival +adjectivally +adjective +adjectives +adjective's +adjoin +adjoining +adjoined +adjoins +adjourn +adjourning +adjourned +adjourns +adjournment +adjournment +adjournments +adjournment's +adjudge +adjudging +adjudged +adjudges +adjudicate +adjudicative +adjudication +adjudications +adjudicating +adjudicated +adjudicates +adjudication +adjudication's +adjudicator +adjudicators +adjudicator's +adjudicatory +adjunct +adjuncts +adjunct's +adjuration +adjurations +adjuration's +adjure +adjuring +adjured +adjures +adjust +adjusting +adjusted +adjusts +adjustment +readjusting +readjusted +readjusts +readjustment +readjust +adjustable +adjuster +adjusters +adjuster's +adjustment +adjustments +adjustment's +readjustments +readjustment's +readjustment +adjutant +adjutants +adjutant's +adman +adman's +admen +admin +admins +administer +administering +administered +administers +administrate +administrative +administration +administrations +administrating +administrated +administrates +administration +administration's +administrative +administratively +administrator +administrators +administrator's +admirably +admiral +admirals +admiral's +admiralty +admiralty's +admiration +admiration's +admire +admiring +admired +admirer +admirers +admires +admirable +admirer +admirer's +admiring +admiringly +admissibility +admissibility's +inadmissibility's +inadmissibility +admissible +inadmissible +admissibly +admission +admission's +readmission's +readmission +admissions +admit +admits +readmits +readmit +admittance +admittance's +admitted +admittedly +admitting +readmitting +admix +admixing +admixed +admixes +admixture +admixtures +admixture's +admonish +admonishing +admonished +admonishes +admonishment +admonishment +admonishments +admonishment's +admonition +admonitions +admonition's +admonitory +ado +ado's +adobe +adobes +adobe's +adolescence +adolescences +adolescence's +adolescent +adolescents +adolescent's +adopt +adoptive +adopting +adopted +adopts +readopting +readopted +readopts +readopt +adoptable +adopter +adopters +adopter's +adoption +adoptions +adoption's +adorableness +adorableness's +adorably +adoration +adoration's +adore +adoring +adored +adorer +adorers +adores +adorable +adorer +adorer's +adoring +adoringly +adorn +adorning +adorned +adorns +adornment +adorned +unadorned +adornment +adornments +adornment's +adrenal +adrenals +adrenal's +adrenalin's +adrenaline +adrenaline's +adrenergic +adrift +adroit +adroitly +adroitness +adroitness +adroitness's +adsorb +adsorbing +adsorbed +adsorbs +adsorbent +adsorbents +adsorbent's +adsorption +adsorptions +adsorption's +adulate +adulation +adulating +adulated +adulates +adulation +adulation's +adulator +adulators +adulator's +adulatory +adult +adults +adult's +adulterant +adulterants +adulterant's +adulterate +adulteration +adulterating +adulterated +adulterates +adulterated +unadulterated +adulteration +adulteration's +adulterer +adulterers +adulterer's +adulteress +adulteresses +adulteress's +adulterous +adultery +adulteries +adultery's +adulthood +adulthood's +adumbrate +adumbration +adumbrating +adumbrated +adumbrates +adumbration +adumbration's +adv +advance +advancing +advanced +advances +advance's +advancement +advancement +advancements +advancement's +advantage +advantaging +advantaged +advantages +advantage's +disadvantaging +disadvantaged +disadvantages +disadvantage's +disadvantage +advantageous +advantageously +disadvantageously +disadvantageous +advent +advents +advent's +adventitious +adventitiously +adventure +adventuring +adventured +adventurer +adventurers +adventures +adventure's +adventurer +adventurer's +adventuresome +adventuress +adventuresses +adventuress's +adventurism +adventurist +adventurists +adventurous +adventurously +adventurousness +adventurousness +adventurousness's +adverb +adverbs +adverb's +adverbial +adverbially +adverbials +adverbial's +adversarial +adversary +adversaries +adversary's +adverse +adversely +adversest +adverser +adverseness +adverseness +adverseness's +adversity +adversities +adversity's +advert +adverting +adverted +adverts +advert's +advertise +advertising +advertised +advertiser +advertisers +advertises +advertisement +advertised +unadvertised +advertisement +advertisements +advertisement's +advertiser +advertiser's +advertising +advertising's +advertorial +advertorials +advertorial's +advice +advice's +advisability +advisability's +inadvisability's +inadvisability +advisable +inadvisable +advisably +advise +advising +advised +adviser +advisers +advises +advisable +advisement +advised +advisedly +unadvisedly +unadvised +advisement +advisement's +adviser +adviser's +advisor +advisors +advisor's +advisory +advisories +advisory's +advocacy +advocacy's +advocate +advocating +advocated +advocates +advocate's +advt +adware +adze +adzes +adze's +aegis +aegis's +aerate +aeration +aerating +aerated +aerates +aeration +aeration's +aerator +aerators +aerator's +aerial +aerially +aerials +aerial's +aerialist +aerialists +aerialist's +aerie +aeries +aerie's +aerobatic +aerobatics +aerobatics +aerobatics's +aerobic +aerobics +aerobically +aerobics +aerobics's +aerodrome +aerodromes +aerodrome's +aerodynamic +aerodynamics +aerodynamically +aerodynamics +aerodynamics's +aerogram +aerograms +aeronautic +aeronautics +aeronautical +aeronautics +aeronautics's +aerosol +aerosols +aerosol's +aerospace +aerospace's +aesthete +aesthetes +aesthete's +aesthetic +aesthetics +aesthetically +aestheticism +aestheticism's +aesthetics +aesthetics's +afar +affability +affability's +affable +affably +affair +affairs +affair's +affect's +affect +affecting +affected +affects +disaffecting +disaffected +disaffects +disaffect +affectation +affectations +affectation's +affected +affectedly +unaffectedly +unaffected +affecting +affectingly +affection +affection's +disaffection's +disaffection +affectionate +affectionately +affections +afferent +affiance +affiancing +affianced +affiances +affidavit +affidavits +affidavit's +affiliate's +affiliate +affiliation +affiliating +affiliated +affiliates +disaffiliation +disaffiliating +disaffiliated +disaffiliates +disaffiliate +affiliated +unaffiliated +affiliation +affiliation's +disaffiliation's +disaffiliation +affiliations +affinity +affinities +affinity's +affirm +affirming +affirmed +affirms +reaffirming +reaffirmed +reaffirms +reaffirm +affirmation +affirmations +affirmation's +reaffirmations +reaffirmation's +reaffirmation +affirmative +affirmatively +affirmatives +affirmative's +affix +affixing +affixed +affixes +affix's +afflatus +afflatus's +afflict +afflicting +afflicted +afflicts +affliction +afflictions +affliction's +affluence +affluence's +affluent +affluently +afford +affording +afforded +affords +affordable +affordability +affordably +afforest +afforesting +afforested +afforests +disafforesting +disafforested +disafforests +disafforest +afforestation +afforestation's +affray +affrays +affray's +affront +affronting +affronted +affronts +affront's +afghan +afghans +afghan's +aficionado +aficionados +aficionado's +afield +afire +aflame +afloat +aflutter +afoot +aforementioned +aforesaid +aforethought +afoul +afraid +unafraid +afresh +aft +after +afters +afterbirth +afterbirth's +afterbirths +afterburner +afterburners +afterburner's +aftercare +aftercare's +aftereffect +aftereffects +aftereffect's +afterglow +afterglows +afterglow's +afterimage +afterimages +afterimage's +afterlife +afterlife's +afterlives +aftermarket +aftermarkets +aftermarket's +aftermath +aftermath's +aftermaths +afternoon +afternoons +afternoon's +aftershave +aftershaves +aftershave's +aftershock +aftershocks +aftershock's +aftertaste +aftertastes +aftertaste's +afterthought +afterthoughts +afterthought's +afterward +afterwards +afterword +afterwords +afterword's +again +against +agape +agape's +agar +agar's +agate +agates +agate's +agave +agave's +age +aging +agings +aged +ages +age's +ageism +ageism's +ageist +ageists +ageist's +ageless +agelessly +agelessness +agelessness +agelessness's +agency +agencies +agency's +agenda +agendas +agenda's +agent +agents +agent's +reagents +reagent's +reagent +ageratum +ageratum's +agglomerate +agglomeration +agglomerations +agglomerating +agglomerated +agglomerates +agglomerate's +agglomeration +agglomeration's +agglutinate +agglutination +agglutinations +agglutinating +agglutinated +agglutinates +agglutination +agglutination's +aggrandize +aggrandizing +aggrandized +aggrandizes +aggrandizement +aggrandizement +aggrandizement's +aggravate +aggravation +aggravations +aggravating +aggravated +aggravates +aggravating +aggravatingly +aggravation +aggravation's +aggregate +aggregation +aggregations +aggregating +aggregated +aggregates +aggregate's +aggregation +aggregation's +aggregator +aggregators +aggregator's +aggression +aggression's +aggressive +aggressively +aggressiveness +aggressiveness +aggressiveness's +aggressor +aggressors +aggressor's +aggrieve +aggrieving +aggrieved +aggrieves +aggro +aghast +agile +agilely +agility +agility's +aging +aging's +agitate +agitation +agitations +agitating +agitated +agitates +agitation +agitation's +agitator +agitators +agitator's +agitprop +agitprop's +agleam +aglitter +aglow +agnostic +agnostics +agnostic's +agnosticism +agnosticism's +ago +agog +agonist +agonists +agonize +agonizing +agonized +agonizes +agonizing +agonizingly +agony +agonies +agony's +agoraphobia +agoraphobia's +agoraphobic +agoraphobics +agoraphobic's +agrarian +agrarians +agrarian's +agrarianism +agrarianism's +agree +agreed +agrees +agreeable +agreement +disagreed +disagrees +disagreeable +disagreement +disagree +agreeableness +agreeableness's +disagreeableness's +disagreeableness +agreeably +disagreeably +agreeing +disagreeing +agreement +agreements +agreement's +disagreements +disagreement's +disagreement +agribusiness +agribusinesses +agribusiness's +agricultural +agriculturally +agriculturalist +agriculturalists +agriculturalist's +agriculture +agriculture's +agriculturist +agriculturists +agriculturist's +agronomic +agronomist +agronomists +agronomist's +agronomy +agronomy's +aground +ague +ague's +ah +aha +ahchoo +ahead +ahem +ahoy +aid +aiding +aided +aids +aid's +aide +aides +aide's +aided +unaided +aigrette +aigrettes +aigrette's +ail +ailing +ailed +ails +ailment +aileron +ailerons +aileron's +ailment +ailments +ailment's +aim +aiming +aimed +aims +aim's +aimless +aimlessly +aimlessness +aimlessness +aimlessness's +ain't +air +airing +airings +aired +airs +air's +airbag +airbags +airbag's +airbase +airbases +airbase's +airbed +airbeds +airborne +airbrush +airbrushing +airbrushed +airbrushes +airbrush's +airbus +airbuses +airbus's +aircraft +aircraft's +aircraftman +aircraftmen +aircrew +aircrews +airdrome +airdromes +airdrop +airdrops +airdrop's +airdropped +airdropping +airfare +airfares +airfare's +airfield +airfields +airfield's +airflow +airflow's +airfoil +airfoils +airfoil's +airfreight +airfreight's +airguns +airhead +airheads +airhead's +airily +airiness +airiness's +airing +airing's +airless +airlessness +airlessness +airlessness's +airletters +airlift +airlifting +airlifted +airlifts +airlift's +airline +airliner +airliners +airlines +airline's +airliner +airliner's +airlock +airlocks +airlock's +airmail +airmailing +airmailed +airmails +airmail's +airman +airman's +airmen +airplane +airplanes +airplane's +airplay +airplay's +airport +airports +airport's +airship +airships +airship's +airshow +airshows +airsick +airsickness +airsickness +airsickness's +airspace +airspace's +airspeed +airstrike +airstrikes +airstrike's +airstrip +airstrips +airstrip's +airtight +airtime +airtime's +airwaves +airwaves's +airway +airways +airway's +airwoman +airwomen +airworthiness +airworthiness's +airworthy +airworthiness +airy +airiest +airier +airiness +aisle +aisles +aisle's +aitch +aitches +aitch's +ajar +aka +akimbo +akin +alabaster +alabaster's +alack +alacrity +alacrity's +alarm +alarming +alarmed +alarms +alarm's +alarming +alarmingly +alarmist +alarmists +alarmist's +alas +alb +albs +alb's +albacore +albacores +albacore's +albatross +albatrosses +albatross's +albeit +albinism +albinism's +albino +albinos +albino's +album +albumen +albums +album's +albumen +albumen's +albumin +albumin's +albuminous +alchemist +alchemists +alchemist's +alchemy +alchemy's +alcohol +alcohols +alcohol's +alcoholic +alcoholics +alcoholic's +alcoholically +alcoholism +alcoholism's +alcove +alcoves +alcove's +alder +alders +alder's +alderman +alderman's +aldermen +alderwoman +alderwoman's +alderwomen +ale +alive +ales +ale's +aleatory +alehouse +alehouses +alehouse's +alembic +alembics +alembic's +alert +alertly +alerting +alerted +alerts +alertness +alert's +alertness +alertness's +alewife +alewife's +alewives +alfalfa +alfalfa's +alfresco +alga +alga's +algae +algal +algebra +algebras +algebra's +algebraic +algebraically +algorithm +algorithms +algorithm's +algorithmic +alias +aliasing +aliased +aliases +alias's +alibi +alibiing +alibied +alibis +alibi's +alien +aliening +aliened +aliens +alien's +alienable +alienable +inalienable +unalienable +alienate +alienation +alienating +alienated +alienates +alienation +alienation's +alienist +alienists +alienist's +alight +alighting +alighted +alights +align +aligning +aligned +aligns +alignment +realigning +realigned +realigns +realignment +realign +aligned +unaligned +aligner +aligners +aligner's +alignment +alignments +alignment's +realignments +realignment's +realignment +alike +unalike +aliment +alimenting +alimented +aliments +aliment's +alimentary +alimony +alimony's +aliveness +aliveness's +aliyah +aliyah's +aliyahs +alkali +alkali's +alkalies +alkaline +alkalinity +alkalinity's +alkalize +alkalizing +alkalized +alkalizes +alkaloid +alkaloids +alkaloid's +alkyd +alkyds +alkyd's +all +all's +allay +allaying +allayed +allays +allegation +allegations +allegation's +allege +alleging +alleged +alleges +alleged +allegedly +allegiance +allegiances +allegiance's +allegoric +allegorical +allegorically +allegorist +allegorists +allegorist's +allegory +allegories +allegory's +allegretto +allegrettos +allegretto's +allegro +allegros +allegro's +allele +alleles +allele's +alleluia +alleluias +alleluia's +allergen +allergens +allergen's +allergenic +allergic +allergically +allergist +allergists +allergist's +allergy +allergies +allergy's +alleviate +alleviation +alleviating +alleviated +alleviates +alleviation +alleviation's +alley +alleys +alley's +alleyway +alleyways +alleyway's +alliance +alliances +alliance's +alligator +alligators +alligator's +alliterate +alliterative +alliteration +alliterations +alliterating +alliterated +alliterates +alliteration +alliteration's +alliterative +alliteratively +allocate +allocation +allocating +allocated +allocates +reallocation +reallocating +reallocated +reallocates +reallocate +allocation +allocation's +reallocation's +reallocation +allocations +allot +allots +allotment +allotment +allotments +allotment's +allotted +allotting +allover +allow +allowing +allowed +allows +disallowing +disallowed +disallows +disallow +allowable +unallowable +allowably +allowance +allowances +allowance's +alloy +alloying +alloyed +alloys +alloy's +alloyed +unalloyed +allspice +allspice's +allude +alluding +alluded +alludes +allure +alluring +allured +allures +allure's +allurement +allurement +allurements +allurement's +alluring +alluringly +allusion +allusions +allusion's +allusive +allusively +allusiveness +allusiveness +allusiveness's +alluvial +alluvial's +alluvium +alluviums +alluvium's +ally +allying +allied +allies +ally's +almanac +almanacs +almanac's +almighty +almond +almonds +almond's +almoner +almoners +almoner's +almost +alms +alms's +almshouse +almshouses +almshouse's +aloe +aloes +aloe's +aloft +aloha +alohas +aloha's +alone +along +alongshore +alongside +aloof +aloofly +aloofness +aloofness +aloofness's +aloud +alp +alps +alp's +alpaca +alpacas +alpaca's +alpha +alphas +alpha's +alphabet +alphabets +alphabet's +alphabetic +alphabetical +alphabetically +alphabetization +alphabetizations +alphabetization's +alphabetize +alphabetizing +alphabetized +alphabetizer +alphabetizers +alphabetizes +alphabetizer +alphabetizer's +alphanumeric +alphanumerical +alphanumerically +alpine +alpines +already +alright +also +alt +alts +altar +altars +altar's +altarpiece +altarpieces +altarpiece's +alter +altering +altered +alters +alterable +alterable +unalterable +alteration +alterations +alteration's +altercation +altercations +altercation's +altered +unaltered +alternate +alternative +alternation +alternations +alternately +alternating +alternated +alternates +alternate's +alternation +alternation's +alternative +alternatively +alternatives +alternative's +alternator +alternators +alternator's +although +altimeter +altimeters +altimeter's +altitude +altitudes +altitude's +alto +altos +alto's +altogether +altruism +altruism's +altruist +altruists +altruist's +altruistic +altruistically +alum +alums +alum's +alumina +alumina's +aluminum +aluminum's +alumna +alumna's +alumnae +alumni +alumnus +alumnus's +alveolar +alveolars +always +am +amen +amalgam +amalgams +amalgam's +amalgamate +amalgamation +amalgamations +amalgamating +amalgamated +amalgamates +amalgamation +amalgamation's +amanuenses +amanuensis +amanuensis's +amaranth +amaranth's +amaranths +amaretto +amaretto's +amaryllis +amaryllises +amaryllis's +amass +amassing +amassed +amasses +amateur +amateurs +amateur's +amateurish +amateurishly +amateurishness +amateurishness +amateurishness's +amateurism +amateurism's +amatory +amaze +amazing +amazed +amazes +amaze's +amazement +amazement +amazement's +amazing +amazingly +amazon +amazons +amazon's +amazonian +ambassador +ambassadors +ambassador's +ambassadorial +ambassadorship +ambassadorships +ambassadorship's +ambassadress +ambassadresses +ambassadress's +amber +amber's +ambergris +ambergris's +ambiance +ambiances +ambiance's +ambidexterity +ambidexterity's +ambidextrous +ambidextrously +ambient +ambiguity +ambiguities +ambiguity's +ambiguous +ambiguously +unambiguously +unambiguous +ambit +ambition +ambitions +ambition's +ambitious +ambitiously +ambitiousness +ambitiousness +ambitiousness's +ambivalence +ambivalence's +ambivalent +ambivalently +amble +ambling +ambled +ambler +amblers +ambles +amble's +ambler +ambler's +ambrosia +ambrosia's +ambrosial +ambulance +ambulances +ambulance's +ambulanceman +ambulancemen +ambulancewoman +ambulancewomen +ambulant +ambulate +ambulation +ambulations +ambulating +ambulated +ambulates +ambulation +ambulation's +ambulatory +ambulatories +ambulatory's +ambuscade +ambuscading +ambuscaded +ambuscades +ambuscade's +ambush +ambushing +ambushed +ambushes +ambush's +ameliorate +ameliorative +amelioration +ameliorating +ameliorated +ameliorates +amelioration +amelioration's +amen +amenable +amenability +amenability's +amenably +amend +amending +amended +amends +amendable +amendment +amendment +amendments +amendment's +amenity +amenities +amenity's +amerce +amercing +amerced +amerces +amercement +amercement +amercements +amercement's +americium +americium's +amethyst +amethysts +amethyst's +amiability +amiability's +amiable +amiably +amicability +amicability's +amicable +amicably +amid +amide +amides +amide's +amidship +amidships +amigo +amigos +amigo's +amine +amines +amino +amiss +amity +amity's +ammeter +ammeters +ammeter's +ammo +ammo's +ammonia +ammonia's +ammonium +ammunition +ammunition's +amnesia +amnesia's +amnesiac +amnesiacs +amnesiac's +amnesic +amnesics +amnesic's +amnesty +amnestying +amnestied +amnesties +amnesty's +amniocenteses +amniocentesis +amniocentesis's +amnion +amnions +amnion's +amniotic +amoeba +amoebas +amoeba's +amoebae +amoebic +amok +among +amontillado +amontillados +amontillado's +amoral +amorally +amorality +amorality's +amorous +amorously +amorousness +amorousness +amorousness's +amorphous +amorphously +amorphousness +amorphousness +amorphousness's +amortization +amortizations +amortization's +amortize +amortizing +amortized +amortizes +amortizable +amount +amounting +amounted +amounts +amount's +amour +amours +amour's +amoxicillin +amp +amply +amps +amp's +amperage +amperage's +ampere +amperes +ampere's +ampersand +ampersands +ampersand's +amphetamine +amphetamines +amphetamine's +amphibian +amphibians +amphibian's +amphibious +amphibiously +amphitheater +amphitheaters +amphitheater's +amphora +amphora's +amphorae +ampicillin +ample +amplest +ampler +amplification +amplification's +amplifier +amplifier's +amplify +amplification +amplifications +amplifying +amplified +amplifier +amplifiers +amplifies +amplitude +amplitudes +amplitude's +ampule +ampules +ampule's +amputate +amputation +amputations +amputating +amputated +amputates +amputation +amputation's +amputee +amputees +amputee's +amt +amulet +amulets +amulet's +amuse +amusing +amused +amuses +amusement +amusement +amusements +amusement's +amusing +amusingly +amygdala +amylase +amylase's +amyloid +an +ans +deans +dean +anabolism +anabolism's +anachronism +anachronisms +anachronism's +anachronistic +anachronistically +anaconda +anacondas +anaconda's +anaerobe +anaerobes +anaerobe's +anaerobic +anaerobically +anagram +anagrams +anagram's +anal +anally +analgesia +analgesia's +analgesic +analgesics +analgesic's +analog +analogs +analog's +analogical +analogically +analogize +analogizing +analogized +analogizes +analogous +analogously +analogousness +analogousness +analogousness's +analogue +analogues +analogue's +analogy +analogies +analogy's +analysand +analysands +analysand's +analyses +reanalyses +analysis +analysis's +reanalysis's +reanalysis +analyst +analysts +analyst's +analytic +analytical +analytically +analyzable +analyze +analyzing +analyzed +analyzes +reanalyzing +reanalyzed +reanalyzes +reanalyze +analyzer +analyzers +analyzer's +anapest +anapests +anapest's +anapestic +anapestics +anapestic's +anarchic +anarchically +anarchism +anarchism's +anarchist +anarchists +anarchist's +anarchistic +anarchy +anarchy's +anathema +anathemas +anathema's +anathematize +anathematizing +anathematized +anathematizes +anatomic +anatomical +anatomically +anatomist +anatomists +anatomist's +anatomize +anatomizing +anatomized +anatomizes +anatomy +anatomies +anatomy's +ancestor +ancestors +ancestor's +ancestral +ancestrally +ancestress +ancestresses +ancestress's +ancestry +ancestries +ancestry's +anchor +anchoring +anchored +anchors +anchor's +anchorage +anchorages +anchorage's +anchorite +anchorites +anchorite's +anchorman +anchorman's +anchormen +anchorpeople +anchorperson +anchorpersons +anchorperson's +anchorwoman +anchorwoman's +anchorwomen +anchovy +anchovies +anchovy's +ancient +anciently +ancientest +ancienter +ancients +ancientness +ancient's +ancientness +ancientness's +ancillary +ancillaries +ancillary's +and +andante +andantes +andante's +andiron +andirons +andiron's +androgen +androgen's +androgenic +androgynous +androgyny +androgyny's +android +androids +android's +anecdotal +anecdotally +anecdote +anecdotes +anecdote's +anemia +anemia's +anemic +anemically +anemometer +anemometers +anemometer's +anemone +anemones +anemone's +anent +anesthesia +anesthesia's +anesthesiologist +anesthesiologists +anesthesiologist's +anesthesiology +anesthesiology's +anesthetic +anesthetics +anesthetic's +anesthetist +anesthetists +anesthetist's +anesthetization +anesthetizations +anesthetization's +anesthetize +anesthetizing +anesthetized +anesthetizes +aneurysm +aneurysms +aneurysm's +anew +angel +angels +angel's +angelfish +angelfishes +angelfish's +angelic +angelica +angelica's +angelical +angelically +anger +angering +angered +angers +anger's +angina +angina's +angioplasty +angioplasties +angioplasty's +angiosperm +angiosperms +angiosperm's +angle +angling +angled +angler +anglers +angles +angle's +angler +angler's +angleworm +angleworms +angleworm's +anglicism +anglicisms +anglicize +anglicizing +anglicized +anglicizes +angling +angling's +anglophile +anglophiles +anglophone +anglophones +angora +angoras +angora's +angostura +angrily +angry +angriest +angrier +angst +angst's +angstrom +angstroms +angstrom's +anguish +anguishing +anguished +anguishes +anguish's +angular +angularity +angularities +angularity's +angulation +anhydrous +aniline +aniline's +anilingus +animadversion +animadversions +animadversion's +animadvert +animadverting +animadverted +animadverts +animal +animals +animal's +animalcule +animalcules +animalcule's +animate +animation +animating +animated +animates +reanimation +reanimating +reanimated +reanimates +reanimate +animated +animatedly +animation +animation's +reanimation's +reanimation +animations +animator +animators +animator's +anime +anime's +animism +animism's +animist +animists +animist's +animistic +animosity +animosities +animosity's +animus +animus's +anion +anions +anion's +anionic +anise +anise's +aniseed +aniseed's +anisette +anisette's +ankh +ankh's +ankhs +ankle +ankles +ankle's +anklebone +anklebones +anklebone's +anklet +anklets +anklet's +annalist +annalists +annalist's +annals +annals's +anneal +annealing +annealed +anneals +annelid +annelids +annelid's +annex +annexing +annexed +annexes +annex's +annexation +annexations +annexation's +annihilate +annihilation +annihilating +annihilated +annihilates +annihilation +annihilation's +annihilator +annihilators +annihilator's +anniversary +anniversaries +anniversary's +annotate +annotative +annotation +annotations +annotating +annotated +annotates +annotation +annotation's +annotator +annotators +annotator's +announce +announcing +announced +announcer +announcers +announces +announcement +announced +unannounced +announcement +announcements +announcement's +announcer +announcer's +annoy +annoying +annoyed +annoys +annoyance +annoyances +annoyance's +annoying +annoyingly +annual +annually +annuals +annual's +annualized +annuitant +annuitants +annuitant's +annuity +annuities +annuity's +annul +annuls +annulment +annular +annulled +annulling +annulment +annulments +annulment's +annulus +annunciation +annunciations +annunciation's +anode +anodes +anode's +anodize +anodizing +anodized +anodizes +anodyne +anodynes +anodyne's +anoint +anointing +anointed +anoints +anointment +anointment +anointment's +anomalous +anomalously +anomaly +anomalies +anomaly's +anon +anons +anonymity +anonymity's +anonymous +anonymously +anopheles +anopheles's +anorak +anoraks +anorak's +anorectic +anorectics +anorectic's +anorexia +anorexia's +anorexic +anorexics +anorexic's +another +answer +answering +answered +answers +answer's +answerable +answerable +unanswerable +answered +unanswered +answerphone +answerphones +ant +anted +ants +ant's +antacid +antacids +antacid's +antagonism +antagonisms +antagonism's +antagonist +antagonists +antagonist's +antagonistic +antagonistically +antagonize +antagonizing +antagonized +antagonizes +antarctic +ante +antes +ante's +anteater +anteaters +anteater's +antebellum +antecedence +antecedence's +antecedent +antecedents +antecedent's +antechamber +antechambers +antechamber's +antedate +antedating +antedated +antedates +antediluvian +anteing +antelope +antelopes +antelope's +antenatal +antenna +antennas +antenna's +antennae +anterior +anteroom +anterooms +anteroom's +anthem +anthems +anthem's +anther +anthers +anther's +anthill +anthills +anthill's +anthologist +anthologists +anthologist's +anthologize +anthologizing +anthologized +anthologizes +anthology +anthologies +anthology's +anthracite +anthracite's +anthrax +anthrax's +anthropocentric +anthropoid +anthropoids +anthropoid's +anthropological +anthropologically +anthropologist +anthropologists +anthropologist's +anthropology +anthropology's +anthropomorphic +anthropomorphically +anthropomorphism +anthropomorphism's +anthropomorphize +anthropomorphous +anti +antis +anti's +antiabortion +antiabortionist +antiabortionists +antiabortionist's +antiaircraft +antibacterial +antibacterials +antibacterial's +antibiotic +antibiotics +antibiotic's +antibody +antibodies +antibody's +antic +antics +antic's +anticancer +anticipate +anticipation +anticipations +anticipating +anticipated +anticipates +anticipated +unanticipated +anticipation +anticipation's +anticipatory +anticked +anticking +anticlerical +anticlimactic +anticlimactically +anticlimax +anticlimaxes +anticlimax's +anticline +anticlines +anticline's +anticlockwise +anticoagulant +anticoagulants +anticoagulant's +anticommunism +anticommunism's +anticommunist +anticommunists +anticommunist's +anticyclone +anticyclones +anticyclone's +anticyclonic +antidemocratic +antidepressant +antidepressants +antidepressant's +antidote +antidotes +antidote's +antifascist +antifascists +antifascist's +antiferromagnetic +antifreeze +antifreeze's +antigen +antigens +antigen's +antigenic +antigenicity +antigenicity's +antihero +antihero's +antiheroes +antihistamine +antihistamines +antihistamine's +antiknock +antiknock's +antilabor +antilogarithm +antilogarithms +antilogarithm's +antimacassar +antimacassars +antimacassar's +antimalarial +antimatter +antimatter's +antimicrobial +antimissile +antimony +antimony's +antineutrino +antineutrinos +antineutrino's +antineutron +antineutrons +antineutron's +antinuclear +antioxidant +antioxidants +antioxidant's +antiparticle +antiparticles +antiparticle's +antipasti +antipasto +antipastos +antipasto's +antipathetic +antipathy +antipathies +antipathy's +antipersonnel +antiperspirant +antiperspirants +antiperspirant's +antiphon +antiphons +antiphon's +antiphonal +antiphonally +antiphonals +antiphonal's +antipodal +antipodals +antipodean +antipodeans +antipodean's +antipodes +antipodes's +antipollution +antipoverty +antiproton +antiprotons +antiproton's +antiquarian +antiquarians +antiquarian's +antiquarianism +antiquarianism's +antiquary +antiquaries +antiquary's +antiquate +antiquating +antiquated +antiquates +antique +antiquing +antiqued +antiques +antique's +antiquity +antiquities +antiquity's +antirrhinum +antirrhinums +antiscience +antisemitic +antisemitism +antisemitism's +antisepsis +antisepsis's +antiseptic +antiseptics +antiseptic's +antiseptically +antiserum +antiserums +antiserum's +antislavery +antisocial +antisocially +antispasmodic +antispasmodics +antispasmodic's +antisubmarine +antitank +antitheses +antithesis +antithesis's +antithetic +antithetical +antithetically +antitoxin +antitoxins +antitoxin's +antitrust +antivenin +antivenins +antivenin's +antivenom +antiviral +antivirals +antiviral's +antivirus +antivivisectionist +antivivisectionists +antivivisectionist's +antiwar +antler +antlered +antlers +antler's +antonym +antonyms +antonym's +antonymous +antrum +antsy +antsiest +antsier +anus +anuses +anus's +anvil +anvils +anvil's +anxiety +anxieties +anxiety's +anxious +anxiously +anxiousness +anxiousness +anxiousness's +any +anybody +anybodies +anybody's +anyhow +anymore +anyone +anyone's +anyplace +anything +anythings +anything's +anytime +anyway +anyways +anywhere +anywise +aorta +aortas +aorta's +aortic +apace +apart +apartheid +apartheid's +apartment +apartments +apartment's +apathetic +apathetically +apathy +apathy's +apatite +apatite's +ape +aping +aped +apes +ape's +apelike +aperitif +aperitifs +aperitif's +aperture +apertures +aperture's +apex +apexes +apex's +aphasia +aphasia's +aphasic +aphasics +aphasic's +aphelia +aphelion +aphelions +aphelion's +aphid +aphids +aphid's +aphorism +aphorisms +aphorism's +aphoristic +aphoristically +aphrodisiac +aphrodisiacs +aphrodisiac's +apiarist +apiarists +apiarist's +apiary +apiaries +apiary's +apical +apically +apiece +apish +apishly +aplenty +aplomb +aplomb's +apocalypse +apocalypses +apocalypse's +apocalyptic +apocrypha +apocrypha's +apocryphal +apocryphally +apogee +apogees +apogee's +apolitical +apolitically +apologetic +unapologetic +apologetically +apologia +apologias +apologia's +apologist +apologists +apologist's +apologize +apologizing +apologized +apologizes +apology +apologies +apology's +apoplectic +apoplexy +apoplexies +apoplexy's +apoptosis +apoptotic +apostasy +apostasies +apostasy's +apostate +apostates +apostate's +apostatize +apostatizing +apostatized +apostatizes +apostle +apostles +apostle's +apostleship +apostleship's +apostolic +apostrophe +apostrophes +apostrophe's +apothecary +apothecaries +apothecary's +apothegm +apothegms +apothegm's +apotheoses +apotheosis +apotheosis's +app +apps +app's +appall +appalling +appalled +appalls +appalling +appallingly +appaloosa +appaloosas +appaloosa's +apparatchik +apparatchiks +apparatus +apparatuses +apparatus's +apparel +appareling +appareled +apparels +apparel's +apparent +apparently +apparition +apparitions +apparition's +appeal +appealing +appealed +appeals +appeal's +appealing +appealingly +unappealingly +unappealing +appear +appearing +appeared +appears +reappearing +disappearing +reappeared +disappeared +reappears +disappears +reappear +disappear +appearance +appearances +appearance's +reappearances +disappearances +reappearance's +disappearance's +reappearance +disappearance +appease +appeasing +appeased +appeaser +appeasers +appeases +appeasement +appeasement +appeasements +appeasement's +appeaser +appeaser's +appellant +appellants +appellant's +appellate +appellation +appellations +appellation +appellation's +append +appending +appended +appends +appendage +appendages +appendage's +appendectomy +appendectomies +appendectomy's +appendices +appendicitis +appendicitis's +appendix +appendixes +appendix's +appertain +appertaining +appertained +appertains +appetite +appetites +appetite's +appetizer +appetizers +appetizer's +appetizing +appetizingly +applaud +applauding +applauded +applauder +applauders +applauds +applauder +applauder's +applause +applause's +apple +apples +apple's +applejack +applejack's +applesauce +applesauce's +applet +applets +applet's +appliance +appliances +appliance's +applicability +applicability's +applicable +inapplicable +applicably +applicant +applicants +applicant's +application +application's +reapplication's +reapplication +applicator +applicators +applicator's +applier +appliers +applier's +applique +appliqued +appliques +applique's +appliqueing +apply +application +applications +applying +applied +applies +reapplication +reapplications +reapplying +reapplied +reapplies +reapply +appoint +appointive +appointing +appointed +appoints +appointment +reappointing +disappointing +reappointed +disappointed +reappoints +disappoints +reappointment +disappointment +reappoint +disappoint +appointee +appointees +appointee's +appointment's +reappointment's +appointment +appointments +appointment's +disappointments +disappointment's +disappointment +apportion +apportioning +apportioned +apportions +apportionment +reapportioning +reapportioned +reapportions +reapportionment +reapportion +apportionment +apportionment's +reapportionment's +reapportionment +appose +apposing +apposed +apposes +apposite +appositive +apposition +appositely +appositeness +appositeness +appositeness's +apposition +apposition's +appositive +appositives +appositive's +appraisal +appraisals +appraisal's +reappraisals +reappraisal's +reappraisal +appraise +appraising +appraised +appraises +reappraising +reappraised +reappraises +reappraise +appraiser +appraisers +appraiser's +appreciable +inappreciable +appreciably +inappreciably +appreciate +appreciative +appreciation +appreciations +appreciating +appreciated +appreciates +appreciated +unappreciated +appreciation +appreciation's +appreciative +appreciatively +appreciator +appreciators +appreciator's +appreciatory +apprehend +apprehending +apprehended +apprehends +apprehension +apprehensions +apprehension's +apprehensive +apprehensively +apprehensiveness +apprehensiveness +apprehensiveness's +apprentice +apprenticing +apprenticed +apprentices +apprentice's +apprenticeship +apprenticeships +apprenticeship's +apprise +apprising +apprised +apprises +approach +approaching +approached +approaches +approach's +approachable +approachable +inapproachable +unapproachable +approbation +approbation's +disapprobation's +disapprobation +approbations +appropriate +appropriation +appropriations +appropriately +appropriating +appropriated +appropriates +appropriateness +appropriated +unappropriated +appropriateness +appropriateness's +inappropriateness's +inappropriateness +appropriation +appropriation's +appropriator +appropriators +appropriator's +approval +approval's +disapproval's +disapproval +approvals +approve +approving +approved +approves +disapproving +disapproved +disapproves +disapprove +approved +unapproved +approving +approvingly +disapprovingly +disapproving +approx +approximate +approximation +approximations +approximately +approximating +approximated +approximates +approximation +approximation's +appurtenance +appurtenances +appurtenance's +appurtenant +apricot +apricots +apricot's +apron +aprons +apron's +apropos +apse +apses +apse's +apt +aptly +aptest +aptness +inaptly +inaptness +inapt +apter +aptitude +aptitudes +aptitude's +aptness +aptness's +inaptness's +inaptness +aqua +aquas +aqua's +aquaculture +aquaculture's +aqualung +aqualungs +aqualung's +aquamarine +aquamarines +aquamarine's +aquanaut +aquanauts +aquanaut's +aquaplane +aquaplaning +aquaplaned +aquaplanes +aquaplane's +aquarium +aquariums +aquarium's +aquatic +aquatics +aquatic's +aquatically +aquatics +aquatics's +aquatint +aquatints +aquavit +aquavit's +aqueduct +aqueducts +aqueduct's +aqueous +aquifer +aquifers +aquifer's +aquiline +arabesque +arabesques +arabesque's +arability +arability's +arachnid +arachnids +arachnid's +arachnophobia +arbiter +arbiters +arbiter's +arbitrage +arbitraging +arbitraged +arbitrager +arbitragers +arbitrages +arbitrage's +arbitrager +arbitrager's +arbitrageur +arbitrageurs +arbitrageur's +arbitrament +arbitraments +arbitrament's +arbitrarily +arbitrariness +arbitrariness's +arbitrary +arbitrariness +arbitrate +arbitration +arbitrating +arbitrated +arbitrates +arbitration +arbitration's +arbitrator +arbitrators +arbitrator's +arbor +arbors +arbor's +arboreal +arboretum +arboretums +arboretum's +arborvitae +arborvitaes +arborvitae's +arbutus +arbutuses +arbutus's +arc +arcing +arced +arcs +arc's +arcade +arcades +arcade's +arcane +arch +archive +archly +arching +arched +archest +archer +archers +arches +archness +arch's +archaeological +archaeologically +archaeologist +archaeologists +archaeologist's +archaeology +archaeology's +archaic +archaically +archaism +archaisms +archaism's +archaist +archaists +archaist's +archangel +archangels +archangel's +archbishop +archbishops +archbishop's +archbishopric +archbishoprics +archbishopric's +archdeacon +archdeacons +archdeacon's +archdiocesan +archdiocese +archdioceses +archdiocese's +archduchess +archduchesses +archduchess's +archduke +archdukes +archduke's +archenemy +archenemies +archenemy's +archer +archer's +archery +archery's +archetypal +archetype +archetypes +archetype's +archfiend +archfiends +archfiend's +archiepiscopal +archipelago +archipelagos +archipelago's +architect +architects +architect's +architectonic +architectonics +architectonics +architectonics's +architectural +architecturally +architecture +architectures +architecture's +architrave +architraves +architrave's +archival +archive +archiving +archived +archives +archive's +archivist +archivists +archivist's +archness +archness's +archway +archways +archway's +arctic +arctics +arctic's +ardent +ardently +ardor +ardors +ardor's +arduous +arduously +arduousness +arduousness +arduousness's +are +ares +are's +arable +area +areas +area's +areal +aren't +arena +arenas +arena's +argent +argent's +arginine +argon +argon's +argosy +argosies +argosy's +argot +argots +argot's +arguable +inarguable +unarguable +arguably +unarguably +argue +arguing +argued +arguer +arguers +argues +arguer +arguer's +argument +arguments +argument's +argumentation +argumentation's +argumentative +argumentatively +argumentativeness +argumentativeness +argumentativeness's +argyle +argyles +argyle's +aria +arias +aria's +arid +aridly +aridity +aridity's +aright +arise +arising +arises +arisen +aristocracy +aristocracies +aristocracy's +aristocrat +aristocrats +aristocrat's +aristocratic +aristocratically +arithmetic +arithmetic's +arithmetical +arithmetically +arithmetician +arithmeticians +arithmetician's +ark +arks +ark's +arm's +arm +arming +armed +arms +rearming +disarming +rearmed +disarmed +rearms +disarms +rearm +disarm +armada +armadas +armada's +armadillo +armadillos +armadillo's +armament +armament's +rearmament's +disarmament's +rearmament +disarmament +armaments +armature +armatures +armature's +armband +armbands +armband's +armchair +armchairs +armchair's +armed +unarmed +armful +armfuls +armful's +armhole +armholes +armhole's +armistice +armistices +armistice's +armlet +armlets +armlet's +armload +armloads +armor +armoring +armored +armorer +armorers +armors +armor's +armored +unarmored +armorer +armorer's +armorial +armory +armories +armory's +armpit +armpits +armpit's +armrest +armrests +armrest's +army +armies +army's +aroma +aromas +aroma's +aromatherapist +aromatherapists +aromatherapist's +aromatherapy +aromatherapy's +aromatic +aromatics +aromatic's +aromatically +arose +around +arousal +arousal's +arouse +arousing +aroused +arouses +arpeggio +arpeggios +arpeggio's +arr +arraign +arraigning +arraigned +arraigns +arraignment +arraignment +arraignments +arraignment's +arrange +arranging +arranged +arranges +arrangement +rearranging +disarranging +rearranged +disarranged +rearranges +disarranges +rearrangement +disarrangement +rearrange +disarrange +arrangement's +disarrangement's +arrangement +arrangements +arrangement's +rearrangements +rearrangement's +rearrangement +arranger +arrangers +arranger's +arrant +arras +arrases +arras's +array +arraying +arrayed +arrays +array's +disarraying +disarrayed +disarrays +disarray's +disarray +arrears +arrears's +arrest +arresting +arrested +arrests +arrest's +rearresting +rearrested +rearrests +rearrest's +rearrest +arrhythmia +arrhythmia's +arrhythmic +arrhythmical +arrival +arrivals +arrival's +arrive +arriving +arrived +arrives +arrogance +arrogance's +arrogant +arrogantly +arrogate +arrogation +arrogating +arrogated +arrogates +arrogation +arrogation's +arrow +arrows +arrow's +arrowhead +arrowheads +arrowhead's +arrowroot +arrowroot's +arroyo +arroyos +arroyo's +arsed +arsenal +arsenals +arsenal's +arsenic +arsenic's +arsing +arson +arson's +arsonist +arsonists +arsonist's +art +arts +art's +arterial +arteriole +arterioles +arteriole's +arteriosclerosis +arteriosclerosis's +artery +arteries +artery's +artful +artfully +artfulness +artfulness +artfulness's +arthritic +arthritics +arthritic's +arthritis +arthritis's +arthropod +arthropods +arthropod's +arthroscope +arthroscopes +arthroscope's +arthroscopic +arthroscopy +artichoke +artichokes +artichoke's +article +articled +articles +article's +articulacy +inarticulacy +articular +articulate +articulation +articulations +articulately +articulating +articulated +articulates +articulateness +articulateness +articulateness's +inarticulateness's +inarticulateness +articulation +articulation's +artifact +artifacts +artifact's +artifice +artificer +artificers +artifices +artifice's +artificer +artificer's +artificial +artificially +artificiality +artificiality's +artillery +artillery's +artilleryman +artilleryman's +artillerymen +artiness +artiness's +artisan +artisans +artisan's +artist +artists +artist's +artiste +artistes +artiste's +artistic +inartistic +artistically +artistry +artistry's +artless +artlessly +artlessness +artlessness +artlessness's +artsy +artsiest +artsier +artwork +artworks +artwork's +arty +artiest +artier +artiness +arugula +arum +arums +arum's +asap +asbestos +asbestos's +ascend +ascending +ascended +ascends +reascending +reascended +reascends +reascend +ascendance +ascendance's +ascendancy +ascendancy's +ascendant +ascendants +ascendant's +ascension +ascensions +ascension's +ascent +ascents +ascent's +ascertain +ascertaining +ascertained +ascertains +ascertainable +ascertainment +ascertainment +ascertainment's +ascetic +ascetics +ascetic's +ascetically +asceticism +asceticism's +ascot +ascots +ascot's +ascribe +ascribing +ascribed +ascribes +ascribable +ascription +ascription's +aseptic +aseptically +asexual +asexually +asexuality +asexuality's +ash +ashen +ashing +ashed +ashes +ash's +ashamed +ashamedly +unashamedly +unashamed +ashcan +ashcans +ashcan's +ashlar +ashlars +ashlar's +ashore +ashram +ashrams +ashram's +ashtray +ashtrays +ashtray's +ashy +ashiest +ashier +aside +asides +aside's +asinine +asininely +asininity +asininities +asininity's +ask +asking +asked +asks +askance +asked +unasked +askew +aslant +asleep +asocial +asp +aspen +aspens +asps +asp's +asparagus +asparagus's +aspartame +aspartame's +aspect +aspects +aspect's +aspen +aspen's +asperity +asperities +asperity's +aspersion +aspersions +aspersion's +asphalt +asphalting +asphalted +asphalts +asphalt's +asphodel +asphodels +asphodel's +asphyxia +asphyxia's +asphyxiate +asphyxiation +asphyxiations +asphyxiating +asphyxiated +asphyxiates +asphyxiation +asphyxiation's +aspic +aspics +aspic's +aspidistra +aspidistras +aspidistra's +aspirant +aspirants +aspirant's +aspirate +aspiration +aspirations +aspirating +aspirated +aspirates +aspirate's +aspiration +aspiration's +aspirator +aspirators +aspirator's +aspire +aspiring +aspired +aspires +aspirin +aspirins +aspirin's +ass +asses +ass's +assail +assailing +assailed +assails +assailable +assailable +unassailable +assailant +assailants +assailant's +assassin +assassins +assassin's +assassinate +assassination +assassinations +assassinating +assassinated +assassinates +assassination +assassination's +assault +assaulting +assaulted +assaulter +assaults +assault's +assay +assaying +assayed +assayer +assayers +assays +assay's +assayer +assayer's +assemblage +assemblages +assemblage's +assemble +assembling +assembled +assembles +reassembling +disassembling +reassembled +disassembled +reassembles +disassembles +reassemble +disassemble +assembler +assemblers +assembler's +assemblies +assembly +assembly's +reassembly's +reassembly +assemblyman +assemblyman's +assemblymen +assemblywoman +assemblywoman's +assemblywomen +assent +assenting +assented +assents +assent's +assert +assertive +asserting +asserted +asserts +reasserting +reasserted +reasserts +reassert +assertion +assertion's +reassertion's +reassertion +assertions +assertive +assertively +assertiveness +assertiveness +assertiveness's +assess +assessing +assessed +assesses +assessment +reassessing +reassessed +reassesses +reassessment +reassess +assessment +assessments +assessment's +reassessments +reassessment's +reassessment +assessor +assessors +assessor's +asset +assets +asset's +asseverate +asseveration +asseverating +asseverated +asseverates +asseveration +asseveration's +asshole +assholes +asshole's +assiduity +assiduity's +assiduous +assiduously +assiduousness +assiduousness +assiduousness's +assign's +assign +assigning +assigned +assigns +assignment +reassigning +reassigned +reassigns +reassignment +reassign +assignable +assignation +assignations +assignation's +assigned +unassigned +assignee +assignee's +assigner +assigners +assigner's +assignment +assignments +assignment's +reassignments +reassignment's +reassignment +assignor +assignors +assignor's +assimilate +assimilation +assimilating +assimilated +assimilates +assimilation +assimilation's +assist +assistive +assisting +assisted +assists +assist's +assistance +assistance's +assistant +assistants +assistant's +assisted +unassisted +assize +assizes +assize's +assn +assoc +associate's +associate +associative +association +associating +associated +associates +disassociation +disassociating +disassociated +disassociates +disassociate +association +association's +disassociation's +disassociation +associations +associativity +assonance +assonance's +assonant +assonants +assonant's +assort +assorting +assorted +assorts +assortment +assortative +assortment +assortments +assortment's +asst +assuage +assuaging +assuaged +assuages +assume +assuming +assumed +assumes +assumable +assumption +assumptions +assumption's +assumptive +assurance +assurances +assurance's +reassurances +reassurance's +reassurance +assure +assuring +assured +assures +reassuring +reassured +reassures +reassure +assured +assuredly +assureds +assured's +astatine +astatine's +aster +asters +aster's +disasters +disaster's +disaster +asterisk +asterisking +asterisked +asterisks +asterisk's +astern +asteroid +asteroids +asteroid's +asthma +asthma's +asthmatic +asthmatics +asthmatic's +asthmatically +astigmatic +astigmatism +astigmatisms +astigmatism's +astir +astonish +astonishing +astonished +astonishes +astonishment +astonishing +astonishingly +astonishment +astonishment's +astound +astounding +astounded +astounds +astounding +astoundingly +astraddle +astrakhan +astrakhan's +astral +astray +astride +astringency +astringency's +astringent +astringently +astringents +astringent's +astrolabe +astrolabes +astrolabe's +astrologer +astrologers +astrologer's +astrological +astrologically +astrologist +astrologists +astrologist's +astrology +astrology's +astronaut +astronauts +astronaut's +astronautic +astronautics +astronautical +astronautics +astronautics's +astronomer +astronomers +astronomer's +astronomic +astronomical +astronomically +astronomy +astronomy's +astrophysical +astrophysicist +astrophysicists +astrophysicist's +astrophysics +astrophysics's +astute +astutely +astutest +astuter +astuteness +astuteness +astuteness's +asunder +asylum +asylums +asylum's +asymmetric +asymmetrical +asymmetrically +asymmetry +asymmetries +asymmetry's +asymptomatic +asymptotic +asymptotically +asynchronous +asynchronously +at +atavism +atavism's +atavist +atavists +atavist's +atavistic +ataxia +ataxia's +ataxic +ataxics +ataxic's +ate +atelier +ateliers +atelier's +atheism +atheism's +atheist +atheists +atheist's +atheistic +atherosclerosis +atherosclerosis's +atherosclerotic +athirst +athlete +athletes +athlete's +athletic +athletics +athletically +athleticism +athletics +athletics's +athwart +atilt +atishoo +atlas +atlases +atlas's +atmosphere +atmospheres +atmosphere's +atmospheric +atmospherics +atmospherically +atmospherics +atmospherics's +atoll +atolls +atoll's +atom +atoms +atom's +atomic +atomically +atomize +atomizing +atomized +atomizer +atomizers +atomizes +atomizer +atomizer's +atonal +atonally +atonality +atonality's +atone +atoning +atoned +atones +atonement +atonement +atonement's +atop +atria +atrial +atrioventricular +atrium +atrium's +atrocious +atrociously +atrociousness +atrociousness +atrociousness's +atrocity +atrocities +atrocity's +atrophy +atrophying +atrophied +atrophies +atrophy's +atropine +atropine's +attach +attaching +attached +attaches +attachment +reattaching +reattached +reattaches +reattachment +reattach +attache +attache's +attachable +attached +unattached +attachment +attachment's +reattachment's +reattachment +attachments +attack +attacking +attacked +attacker +attackers +attacks +attack's +attacker +attacker's +attain +attaining +attained +attains +reattaining +reattained +reattains +reattain +attainability +attainability's +attainable +unattainable +attainder +attainder's +attainment +attainments +attainment's +attar +attar's +attempt's +attempt +attempting +attempted +attempts +reattempting +reattempted +reattempts +reattempt +attend +attending +attended +attender +attenders +attends +attendance +attendances +attendance's +attendant +attendants +attendant's +attended +unattended +attendee +attendees +attendee's +attention +attention's +inattention's +inattention +attentions +attentive +attentively +attentiveness +inattentively +inattentiveness +inattentive +attentiveness +attentiveness's +inattentiveness's +inattentiveness +attenuate +attenuation +attenuating +attenuated +attenuates +attenuation +attenuation's +attest +attesting +attested +attests +attestation +attestations +attestation's +attested +unattested +attic +attics +attic's +attire +attiring +attired +attires +attire's +attitude +attitudes +attitude's +attitudinal +attitudinize +attitudinizing +attitudinized +attitudinizes +attn +attorney +attorneys +attorney's +attract +attractive +attracting +attracted +attracts +attractable +attractant +attractants +attractant's +attraction +attractions +attraction's +attractive +attractively +unattractively +unattractive +attractiveness +attractiveness's +attribute +attributive +attribution +attributions +attributing +attributed +attributes +attribute's +attributable +attributed +unattributed +attribution +attribution's +attributive +attributively +attributives +attributive's +attrition +attrition's +attune +attuning +attuned +attunes +atty +atwitter +atypical +atypically +aubergine +aubergines +auburn +auburn's +auction +auctioning +auctioned +auctions +auction's +auctioneer +auctioneers +auctioneer's +audacious +audaciously +audaciousness +audaciousness +audaciousness's +audacity +audacity's +audibility +audibility's +inaudibility's +inaudibility +audible +audibles +audible's +audibly +inaudibly +audience +audiences +audience's +audio +audios +audio's +audiological +audiologist +audiologists +audiologist's +audiology +audiology's +audiometer +audiometers +audiometer's +audiophile +audiophiles +audiophile's +audiotape +audiotapes +audiotape's +audiovisual +audiovisuals +audiovisuals +audiovisuals's +audit +auditing +audited +audits +audit's +audition +auditioning +auditioned +auditions +audition's +auditor +auditors +auditor's +auditorium +auditoriums +auditorium's +auditory +auger +augers +auger's +aught +aughts +aught's +augment +augmenting +augmented +augmenter +augmenters +augments +augmentation +augmentations +augmentation's +augmentative +augmenter +augmenter's +augur +auguring +augured +augurs +augur's +augury +auguries +augury's +august +augustly +augustest +auguster +augustness +augustness +augustness's +auk +auks +auk's +aunt +aunts +aunt's +auntie +aunties +auntie's +aura +auras +aura's +aural +aurally +aureole +aureoles +aureole's +aureus +auricle +auricles +auricle's +auricular +aurora +auroras +aurora's +auscultate +auscultation +auscultations +auscultating +auscultated +auscultates +auscultation +auscultation's +auspice +auspices +auspice's +auspicious +auspiciously +inauspiciously +inauspicious +auspiciousness +auspiciousness's +austere +austerely +austerest +austerer +austerity +austerities +austerity's +austral +authentic +inauthentic +unauthentic +authentically +authenticate +authentication +authentications +authenticating +authenticated +authenticates +authenticated +unauthenticated +authentication +authentication's +authenticity +authenticity's +author +authoring +authored +authors +author's +authoress +authoresses +authoress's +authorial +authoritarian +authoritarians +authoritarian's +authoritarianism +authoritarianism's +authoritative +authoritatively +authoritativeness +authoritativeness +authoritativeness's +authority +authorities +authority's +authorization +authorizations +authorization's +authorize +authorizing +authorized +authorizes +reauthorizing +reauthorized +reauthorizes +reauthorize +authorized +unauthorized +authorship +authorship's +autism +autism's +autistic +auto +autos +auto's +autobahn +autobahns +autobahn's +autobiographer +autobiographers +autobiographer's +autobiographic +autobiographical +autobiographically +autobiography +autobiographies +autobiography's +autoclave +autoclaves +autoclave's +autocracy +autocracies +autocracy's +autocrat +autocrats +autocrat's +autocratic +autocratically +autocross +autodidact +autodidacts +autodidact's +autograph +autographing +autographed +autograph's +autographs +autoimmune +autoimmunity +autoimmunity's +automaker +automakers +automaker's +automate +automation +automating +automated +automates +automatic +automatics +automatic's +automatically +automation +automation's +automatism +automatism's +automatize +automatizing +automatized +automatizes +automaton +automatons +automaton's +automobile +automobiling +automobiled +automobiles +automobile's +automotive +autonomic +autonomous +autonomously +autonomy +autonomy's +autopilot +autopilots +autopilot's +autopsy +autopsying +autopsied +autopsies +autopsy's +autosuggestion +autoworker +autoworkers +autoworker's +autumn +autumns +autumn's +autumnal +aux +auxiliary +auxiliaries +auxiliary's +auxin +auxin's +av +aver +avers +avail +availing +availed +avails +avail's +available +availability +availability's +unavailability's +unavailability +available +unavailable +avalanche +avalanches +avalanche's +avarice +avarice's +avaricious +avariciously +avast +avatar +avatars +avatar's +avaunt +avdp +ave +avenge +avenging +avenged +avenger +avengers +avenges +avenger +avenger's +avenue +avenues +avenue's +average +averagely +averaging +averaged +averages +average's +averred +averring +averse +aversion +aversions +aversion +aversion's +avert +averting +averted +averts +avg +avian +aviary +aviaries +aviary's +aviation +aviation's +aviator +aviators +aviator's +aviatrices +aviatrix +aviatrixes +aviatrix's +avid +avidly +avidity +avidity's +avionic +avionics +avionics +avionics's +avitaminosis +avitaminosis's +avocado +avocados +avocado's +avocation +avocations +avocation's +avocational +avoid +avoiding +avoided +avoids +avoidable +avoidable +unavoidable +avoidably +unavoidably +avoidance +avoidance's +avoidant +avoirdupois +avoirdupois's +avouch +avouching +avouched +avouches +avow +avowing +avowed +avows +disavowing +disavowed +disavows +disavow +avowal +avowals +avowal's +disavowals +disavowal's +disavowal +avowed +avowedly +avuncular +avuncularly +aw +await +awaiting +awaited +awaits +awake +awaking +awakes +awaken +awakening +awakened +awakens +reawakening +reawakened +reawakens +reawaken +awakening +awakenings +awakening's +award +awarding +awarded +awards +award's +awardee +awardees +aware +awareness +unawareness +unaware +awareness +awareness's +unawareness's +unawareness +awash +away +awe +awing +awed +awes +awe's +aweigh +awesome +awesomely +awesomeness +awesomeness +awesomeness's +awestruck +awful +awfully +awfulness +awfuller +awfullest +awfulness +awfulness's +awhile +awkward +awkwardly +awkwardest +awkwarder +awkwardness +awkwardness +awkwardness's +awl +awls +awl's +awn +awning +awnings +awns +awn's +awning +awning's +awoke +awoken +awry +ax +axing +axed +axes +ax's +axial +axially +axiom +axioms +axiom's +axiomatic +axiomatically +axis +axis's +axle +axles +axle's +axletree +axletrees +axletree's +axolotl +axolotls +axolotl's +axon +axons +axon's +ayah +ayah's +ayahs +ayatollah +ayatollah's +ayatollahs +aye +ayes +aye's +azalea +azaleas +azalea's +azimuth +azimuth's +azimuths +azure +azures +azure's +b +bed +best +probed +prob +baa +baaing +baaed +baas +baa's +babble +babbling +babbled +babbler +babblers +babbles +babble's +babbler +babbler's +babe +babes +babe's +babel +babels +babel's +baboon +baboons +baboon's +babushka +babushkas +babushka's +baby +babying +babied +babiest +babier +babies +baby's +babyhood +babyhood's +babyish +babysat +babysit +babysits +babysitter +babysitters +babysitter's +babysitting +babysitting's +baccalaureate +baccalaureates +baccalaureate's +baccarat +baccarat's +bacchanal +bacchanals +bacchanal's +bacchanalia +bacchanalia's +bacchanalian +bacchanalians +bacchanalian's +baccy +bachelor +bachelors +bachelor's +bachelorhood +bachelorhood's +bacillary +bacilli +bacillus +bacillus's +back +backing +backings +backed +backer +backers +backs +back's +backache +backaches +backache's +backbench +backbenches +backbit +backbite +backbiting +backbiter +backbiters +backbites +backbiter +backbiter's +backbitten +backboard +backboards +backboard's +backbone +backbones +backbone's +backbreaking +backchat +backcloth +backcloths +backcomb +backcombing +backcombed +backcombs +backdate +backdating +backdated +backdates +backdoor +backdrop +backdrops +backdrop's +backer +backer's +backfield +backfields +backfield's +backfire +backfiring +backfired +backfires +backfire's +backgammon +backgammon's +background +backgrounder +backgrounders +backgrounds +background's +backgrounder +backgrounder's +backhand +backhanding +backhanded +backhander +backhanders +backhands +backhand's +backhanded +backhandedly +backhander +backhander's +backhoe +backhoes +backhoe's +backing +backing's +backlash +backlashes +backlash's +backless +backlog +backlogs +backlog's +backlogged +backlogging +backpack +backpacking +backpacked +backpacker +backpackers +backpacks +backpack's +backpacker +backpacker's +backpacking +backpacking's +backpedal +backpedaling +backpedaled +backpedals +backrest +backrests +backrest's +backroom +backrooms +backscratching +backscratching's +backseat +backseats +backseat's +backside +backsides +backside's +backslapper +backslappers +backslapper's +backslapping +backslapping's +backslash +backslashes +backslash's +backslid +backslide +backsliding +backslider +backsliders +backslides +backslider +backslider's +backspace +backspacing +backspaced +backspaces +backspace's +backspin +backspin's +backstabber +backstabbers +backstabber's +backstabbing +backstage +backstage's +backstair +backstairs +backstop +backstops +backstop's +backstopped +backstopping +backstory +backstories +backstreet +backstreets +backstretch +backstretches +backstretch's +backstroke +backstroking +backstroked +backstrokes +backstroke's +backtalk +backtalk's +backtrack +backtracking +backtracked +backtracks +backup +backups +backup's +backward +backwardly +backwards +backwardness +backwardness +backwardness's +backwash +backwash's +backwater +backwaters +backwater's +backwoods +backwoods's +backwoodsman +backwoodsman's +backwoodsmen +backyard +backyards +backyard's +bacon +bacon's +bacteria +bacteria's +bacterial +bactericidal +bactericide +bactericides +bactericide's +bacteriologic +bacteriological +bacteriologist +bacteriologists +bacteriologist's +bacteriology +bacteriology's +bacterium +bacterium's +bad +badly +badness +bad's +badder +baddest +baddie +baddies +baddie's +bade +badge +badger +badgers +badges +badge's +badger +badgering +badgered +badger's +badinage +badinage's +badlands +badlands's +badman +badman's +badmen +badminton +badminton's +badmouth +badmouthing +badmouthed +badmouths +badness +badness's +baffle +baffling +baffled +baffler +bafflers +baffles +baffle's +bafflement +bafflement +bafflement's +baffler +baffler's +bag +bags +bag's +bagatelle +bagatelles +bagatelle's +bagel +bagels +bagel's +bagful +bagfuls +bagful's +baggage +baggage's +bagged +baggie +baggie's +baggily +bagginess +bagginess's +bagging +baggy +baggiest +baggier +baggies +bagginess +bagpipe +bagpiper +bagpipers +bagpipes +bagpipe's +bagpiper +bagpiper's +baguette +baguettes +baguette's +bah +baht +bahts +baht's +bail +bailing +bailed +bails +bail's +bailable +bailey +baileys +bailiff +bailiffs +bailiwick +bailiwicks +bailiwick's +bailout +bailouts +bailout's +bailsman +bailsman's +bailsmen +bairn +bairns +bairn's +bait +baiting +baited +baits +bait's +baize +baize's +bake +baking +baked +baker +bakers +bakes +bake's +baked +unbaked +baker +baker's +bakery +bakeries +bakery's +bakeshop +bakeshops +bakeshop's +baklava +baklava's +baksheesh +baksheesh's +balaclava +balaclavas +balaclava's +balalaika +balalaikas +balalaika's +balance's +balance +balancing +balanced +balances +unbalancing +unbalanced +unbalances +unbalance +balboa +balboas +balboa's +balcony +balconies +balcony's +bald +baldly +balding +balded +baldest +balder +balds +baldness +balderdash +balderdash's +baldfaced +baldness +baldness's +baldric +baldrics +baldric's +baldy +baldies +bale +baling +baled +baler +balers +bales +bale's +baleen +baleen's +baleful +balefully +balefulness +balefulness +balefulness's +baler +baler's +balk +balking +balked +balks +balk's +balky +balkiest +balkier +ball +balling +balled +balls +ball's +ballad +ballads +ballad's +balladeer +balladeers +balladeer's +balladry +balladry's +ballast +ballasting +ballasted +ballasts +ballast's +ballcock +ballcocks +ballcock's +ballerina +ballerinas +ballerina's +ballet +ballets +ballet's +balletic +ballgame +ballgames +ballgame's +ballgirl +ballgirls +ballgown +ballgowns +ballistic +ballistics +ballistics +ballistics's +balloon +ballooning +ballooned +balloons +balloon's +balloonist +balloonists +balloonist's +ballot +balloting +balloted +ballots +ballot's +ballpark +ballparks +ballpark's +ballplayer +ballplayers +ballplayer's +ballpoint +ballpoints +ballpoint's +ballroom +ballrooms +ballroom's +balls +ballsing +ballsed +ballses +ballsy +ballsiest +ballsier +bally +ballyhoo +ballyhooing +ballyhooed +ballyhoos +ballyhoo's +balm +balms +balm's +balminess +balminess's +balmy +balmiest +balmier +balminess +baloney +baloney's +balsa +balsas +balsa's +balsam +balsams +balsam's +balsamic +baluster +balusters +baluster's +balustrade +balustrades +balustrade's +bamboo +bamboos +bamboo's +bamboozle +bamboozling +bamboozled +bamboozles +ban +bans +ban's +banal +banally +banality +banalities +banality's +banana +bananas +banana's +band's +band +banding +banded +bands +disbanding +disbanded +disbands +disband +bandage +bandaging +bandaged +bandages +bandage's +bandanna +bandannas +bandanna's +bandbox +bandboxes +bandbox's +bandeau +bandeau's +bandeaux +bandit +bandits +bandit's +banditry +banditry's +bandleader +bandleaders +bandmaster +bandmasters +bandmaster's +bandoleer +bandoleers +bandoleer's +bandsman +bandsman's +bandsmen +bandstand +bandstands +bandstand's +bandwagon +bandwagons +bandwagon's +bandwidth +bandwidths +bandy +bandying +bandied +bandiest +bandier +bandies +bane +banes +bane's +baneful +bang +banging +banged +banger +bangs +bang's +bangle +bangles +bangle's +bani +banish +banishing +banished +banishes +banishment +banishment +banishment's +banister +banisters +banister's +banjo +banjos +banjo's +banjoist +banjoists +banjoist's +bank +banking +banked +banker +bankers +banks +bank's +bankable +bankbook +bankbooks +bankbook's +bankcard +bankcards +bankcard's +banker +banker's +banking +banking's +banknote +banknotes +banknote's +bankroll +bankrolling +bankrolled +bankrolls +bankroll's +bankrupt +bankrupting +bankrupted +bankrupts +bankrupt's +bankruptcy +bankruptcies +bankruptcy's +banned +banner +banners +banner's +banning +bannock +bannocks +bannock's +banns +banns's +banquet +banqueting +banqueted +banqueter +banqueters +banquets +banquet's +banqueter +banqueter's +banquette +banquettes +banquette's +banshee +banshees +banshee's +bantam +bantams +bantam's +bantamweight +bantamweights +bantamweight's +banter +bantering +bantered +banters +banter's +bantering +banteringly +banyan +banyans +banyan's +banzai +banzais +banzai's +baobab +baobabs +baobab's +bap +baps +baptism +baptisms +baptism's +baptismal +baptist +baptists +baptistery +baptisteries +baptistery's +baptize +baptizing +baptized +baptizer +baptizers +baptizes +baptized +unbaptized +baptizer +baptizer's +bar's +bar +barest +bars +unbars +debars +disbars +unbar +debar +disbar +barb +barbing +barbed +barber +barbers +barbs +barb's +barbacoa +barbarian +barbarians +barbarian's +barbarianism +barbarianisms +barbarianism's +barbaric +barbarically +barbarism +barbarisms +barbarism's +barbarity +barbarities +barbarity's +barbarize +barbarizing +barbarized +barbarizes +barbarous +barbarously +barbecue +barbecuing +barbecued +barbecues +barbecue's +barbel +barbels +barbel's +barbell +barbells +barbell's +barber +barbering +barbered +barber's +barberry +barberries +barberry's +barbershop +barbershops +barbershop's +barbie +barbies +barbiturate +barbiturates +barbiturate's +barbwire +barbwire's +barcarole +barcaroles +barcarole's +bard +bards +bard's +bardic +bare +barely +baring +bared +barer +bares +bareness +bareback +barebacked +barefaced +barefacedly +barefoot +barefooted +barehanded +bareheaded +barelegged +bareness +bareness's +barf +barfly +barfing +barfed +barfs +barf's +barfly +barflies +barfly's +bargain +bargaining +bargained +bargainer +bargainers +bargains +bargain's +bargainer +bargainer's +barge +barging +barged +barges +barge's +bargeman +bargeman's +bargemen +barhop +barhops +barhopped +barhopping +barista +baristas +barista's +baritone +baritones +baritone's +barium +barium's +bark's +bark +barking +barked +barks +debarking +debarked +debarks +debark +barkeep +barkeeper +barkeepers +barkeeps +barkeep's +barkeeper +barkeeper's +barker +barkers +barker's +barley +barley's +barmaid +barmaids +barmaid's +barman +barman's +barmen +barmy +barmiest +barmier +barn +barns +barn's +barnacle +barnacled +barnacles +barnacle's +barney +barneys +barnstorm +barnstorming +barnstormed +barnstormer +barnstormers +barnstorms +barnstormer +barnstormer's +barnyard +barnyards +barnyard's +barometer +barometers +barometer's +barometric +barometrically +baron +barons +baron's +baronage +baronages +baronage's +baroness +baronesses +baroness's +baronet +baronets +baronet's +baronetcy +baronetcies +baronetcy's +baronial +barony +baronies +barony's +baroque +baroque's +barque +barques +barque's +barrack +barracking +barracked +barracks +barrack's +barracuda +barracudas +barracuda's +barrage +barraging +barraged +barrages +barrage's +barre +barring +barrings +barred +barres +barre's +barred +unbarred +debarred +disbarred +barrel +barreling +barreled +barrels +barrel's +barren +barrenest +barrener +barrens +barrenness +barren's +barrenness +barrenness's +barrette +barrettes +barrette's +barricade +barricading +barricaded +barricades +barricade's +barrier +barriers +barrier's +barring +unbarring +debarring +disbarring +barrio +barrios +barrio's +barrister +barristers +barrister's +barroom +barrooms +barroom's +barrow +barrows +barrow's +bartender +bartenders +bartender's +barter +bartering +bartered +barterer +barterers +barters +barter's +barterer +barterer's +baryon +baryons +baryon's +basal +basally +basalt +basalt's +basaltic +base's +base +basing +based +basest +bases +basement +debasing +debased +debases +debasement +debase +baseball +baseballs +baseball's +baseboard +baseboards +baseboard's +baseless +baseline +baselines +baseline's +basely +baseman +baseman's +basemen +basement +basements +basement's +debasements +debasement's +debasement +baseness +baseness's +baser +bash +bashing +bashed +bashes +bash's +bashful +bashfully +bashfulness +bashfulness +bashfulness's +bashing +bashing's +basic +basics +basic's +basically +basil +basil's +basilica +basilicas +basilica's +basilisk +basilisks +basilisk's +basin +basins +basin's +basinful +basinfuls +basinful's +basis +basis's +bask +basking +basked +basks +basket +baskets +basket's +basketball +basketballs +basketball's +basketry +basketry's +basketwork +basketwork's +basque +basques +bass +basses +bass's +basset +bassets +basset's +bassinet +bassinets +bassinet's +bassist +bassists +bassist's +basso +bassos +basso's +bassoon +bassoons +bassoon's +bassoonist +bassoonists +bassoonist's +basswood +basswoods +basswood's +bast +bast's +bastard +bastards +bastard's +bastardization +bastardizations +bastardization's +bastardize +bastardizing +bastardized +bastardizes +bastardy +bastardy's +baste +bastion +bastions +basting +basted +baster +basters +bastes +baster +baster's +bastion +bastion's +bat +bats +bat's +batch +batching +batched +batches +batch's +bate +bating +bated +bates +rebating +debating +probating +rebated +debated +probated +rebates +debates +probates +rebate +debate +probate +bath +bathing +bathed +bather +bathers +bathes +bath's +bathe +bathe's +bather +bather's +bathetic +bathhouse +bathhouses +bathhouse's +bathing +bathing's +bathmat +bathmats +bathmat's +bathos +bathos's +bathrobe +bathrobes +bathrobe's +bathroom +bathrooms +bathroom's +baths +bathtub +bathtubs +bathtub's +bathwater +bathyscaphe +bathyscaphes +bathyscaphe's +bathysphere +bathyspheres +bathysphere's +batik +batiks +batik's +batiste +batiste's +batman +batman's +batmen +baton +batons +baton's +batsman +batsman's +batsmen +battalion +battalions +battalion's +batted +batten +battening +battened +battens +batten's +batter +battering +batterings +battered +batterer +batterers +batters +batter's +batterer +batterer's +battery +batteries +battery's +batting +batting's +battle +battling +battled +battler +battlers +battles +battle's +battlement +battleaxe +battleaxes +battleaxe's +battledore +battledores +battledore's +battledress +battlefield +battlefields +battlefield's +battlefront +battlefronts +battlefront's +battleground +battlegrounds +battleground's +battlement +battlements +battlement's +battler +battler's +battleship +battleships +battleship's +batty +battiest +battier +bauble +baubles +bauble's +baud +bauds +baud's +bauxite +bauxite's +bawd +bawds +bawd's +bawdily +bawdiness +bawdiness's +bawdy +bawdiest +bawdier +bawdiness +bawl +bawling +bawled +bawls +bawl's +bay +baying +bayed +bays +bay's +bayberry +bayberries +bayberry's +bayonet +bayoneting +bayoneted +bayonets +bayonet's +bayou +bayous +bayou's +bazaar +bazaars +bazaar's +bazillion +bazillions +bazooka +bazookas +bazooka's +bbl +bdrm +be +beach +beaching +beached +beaches +beach's +beachcomber +beachcombers +beachcomber's +beachfront +beachhead +beachheads +beachhead's +beachwear +beachwear's +beacon +beacons +beacon's +bead +beading +beaded +beads +bead's +beading +beading's +beadle +beadles +beadle's +beady +beadiest +beadier +beagle +beagles +beagle's +beak +beaked +beaker +beakers +beaks +beak's +beaker +beaker's +beam +beaming +beamed +beams +beam's +bean +beaning +beaned +beans +bean's +beanbag +beanbags +beanbag's +beanfeast +beanfeasts +beanie +beanies +beanie's +beanpole +beanpoles +beanpole's +beansprout +beansprouts +beanstalk +beanstalks +beanstalk's +bear +bearing +bearings +bearer +bearers +bears +bear's +bearable +bearable +unbearable +bearably +unbearably +beard +bearding +bearded +beards +beard's +beardless +bearer +bearer's +bearing +bearing's +bearish +bearishly +bearishness +bearishness +bearishness's +bearlike +bearskin +bearskins +bearskin's +beast +beasts +beast's +beastliness +beastliness's +beastly +beastliest +beastlier +beastliness +beastly's +beat +beaten +beating +beatings +beater +beaters +beats +beat's +beatable +beatable +unbeatable +beaten +unbeaten +beater +beater's +beatific +beatifically +beatification +beatification's +beatify +beatification +beatifications +beatifying +beatified +beatifies +beating +beating's +beatitude +beatitudes +beatitude's +beatnik +beatniks +beatnik's +beau +beaus +beau's +beaut +beauts +beaut's +beauteous +beauteously +beautician +beauticians +beautician's +beautification +beautification's +beautifier +beautifier's +beautiful +beautifully +beautify +beautification +beautifying +beautified +beautifier +beautifiers +beautifies +beauty +beauties +beauty's +beaver +beavering +beavered +beavers +beaver's +bebop +bebops +bebop's +becalm +becalming +becalmed +becalms +became +because +beck +becks +beck's +beckon +beckoning +beckoned +beckons +becloud +beclouding +beclouded +beclouds +become +becomes +becoming +becomingly +unbecomingly +unbecoming +becquerel +becquerels +bed +beds +bed's +bedaub +bedaubing +bedaubed +bedaubs +bedazzle +bedazzling +bedazzled +bedazzles +bedazzlement +bedazzlement +bedazzlement's +bedbug +bedbugs +bedbug's +bedchamber +bedchambers +bedclothes +bedclothes's +bedded +bedder +bedding +bedding's +bedeck +bedecking +bedecked +bedecks +bedevil +bedeviling +bedeviled +bedevils +bedevilment +bedevilment +bedevilment's +bedfellow +bedfellows +bedfellow's +bedhead +bedheads +bedim +bedims +bedimmed +bedimming +bedizen +bedizening +bedizened +bedizens +bedlam +bedlams +bedlam's +bedpan +bedpans +bedpan's +bedpost +bedposts +bedpost's +bedraggle +bedraggling +bedraggled +bedraggles +bedridden +bedrock +bedrocks +bedrock's +bedroll +bedrolls +bedroll's +bedroom +bedrooms +bedroom's +bedside +bedsides +bedside's +bedsit +bedsits +bedsitter +bedsitters +bedsore +bedsores +bedsore's +bedspread +bedspreads +bedspread's +bedstead +bedsteads +bedstead's +bedtime +bedtimes +bedtime's +bee +being +beings +beer +beers +bees +bee's +beebread +beebread's +beech +beeches +beech's +beechnut +beechnuts +beechnut's +beef +beefing +beefed +beefs +beef's +beefburger +beefburgers +beefburger's +beefcake +beefcakes +beefcake's +beefiness +beefiness's +beefsteak +beefsteaks +beefsteak's +beefy +beefiest +beefier +beefiness +beehive +beehives +beehive's +beekeeper +beekeepers +beekeeper's +beekeeping +beekeeping's +beeline +beelines +beeline's +been +beep +beeping +beeped +beeper +beepers +beeps +beep's +beeper +beeper's +beer +beer's +beery +beeriest +beerier +beeswax +beeswax's +beet +beets +beet's +beetle +beetling +beetled +beetles +beetle's +beetroot +beetroots +beeves +befall +befallen +befalling +befalls +befell +befit +befits +befitted +befitting +befittingly +befog +befogs +befogged +befogging +before +beforehand +befoul +befouling +befouled +befouls +befriend +befriending +befriended +befriends +befuddle +befuddling +befuddled +befuddles +befuddlement +befuddlement +befuddlement's +beg +begs +began +begat +beget +begets +begetter +begetters +begetting +beggar +beggarly +beggaring +beggared +beggars +beggar's +beggary +beggary's +begged +begging +begin +begins +beginner +beginners +beginner's +beginning +beginnings +beginning's +begone +begonia +begonias +begonia's +begot +begotten +begrime +begriming +begrimed +begrimes +begrudge +begrudging +begrudged +begrudges +begrudging +begrudgingly +beguile +beguiling +beguiled +beguiler +beguilers +beguiles +beguilement +beguilement +beguilement's +beguiler +beguiler's +beguiling +beguilingly +beguine +beguines +beguine's +begum +begums +begum's +begun +behalf +behalf's +behalves +behave +behaving +behaved +behaves +behavior +behaviors +behavior's +behavioral +behaviorally +behaviorism +behaviorism's +behaviorist +behaviorists +behaviorist's +behead +beheading +beheaded +beheads +beheld +behemoth +behemoth's +behemoths +behest +behests +behest's +behind +behinds +behind's +behindhand +behold +beholden +beholding +beholder +beholders +beholds +beholder +beholder's +behoove +behooving +behooved +behooves +beige +beige's +being +being's +bejewel +bejeweling +bejeweled +bejewels +belabor +belaboring +belabored +belabors +belated +belatedly +belay +belaying +belayed +belays +belch +belching +belched +belches +belch's +beleaguer +beleaguering +beleaguered +beleaguers +belfry +belfries +belfry's +belie +belied +belies +belief +belief's +unbelief's +disbelief's +unbelief +disbelief +beliefs +believable +unbelievable +believably +unbelievably +believe +believing +believed +believer +believers +believes +disbelieving +disbelieved +disbeliever +disbelievers +disbelieves +disbelieve +believer +believers +believer's +unbelievers +disbelievers +unbeliever's +disbeliever's +unbeliever +disbeliever +believing +unbelieving +belittle +belittling +belittled +belittles +belittlement +belittlement +belittlement's +bell +belling +belled +bells +bell's +belladonna +belladonna's +bellboy +bellboys +bellboy's +belle +belles +belle's +belled +rebelled +belletrist +belletrists +belletrist's +belletristic +bellhop +bellhops +bellhop's +bellicose +bellicosity +bellicosity's +belligerence +belligerence's +belligerency +belligerency's +belligerent +belligerently +belligerents +belligerent's +belling +rebelling +bellman +bellman's +bellmen +bellow +bellowing +bellowed +bellows +bellow's +bellwether +bellwethers +bellwether's +belly +bellying +bellied +bellies +belly's +bellyache +bellyaching +bellyached +bellyaches +bellyache's +bellybutton +bellybuttons +bellybutton's +bellyful +bellyfuls +bellyful's +belong +belonging +belongings +belonged +belongs +belonging +belonging's +beloved +beloveds +beloved's +below +belt +belting +belted +belts +belt's +beltway +beltways +beltway's +beluga +belugas +beluga's +belying +bemire +bemiring +bemired +bemires +bemoan +bemoaning +bemoaned +bemoans +bemuse +bemusing +bemused +bemuses +bemusement +bemused +bemusedly +bemusement +bemusement's +bench +benching +benched +benches +bench's +benchmark +benchmarks +benchmark's +bend +bending +bender +benders +bends +bend's +bendable +bender +bender's +bendy +bendiest +bendier +beneath +benedictine +benediction +benedictions +benediction's +benedictory +benefaction +benefactions +benefaction's +benefactor +benefactors +benefactor's +benefactress +benefactresses +benefactress's +benefice +benefices +benefice's +beneficence +beneficence's +beneficent +beneficently +beneficial +beneficially +beneficiary +beneficiaries +beneficiary's +benefit +benefiting +benefited +benefits +benefit's +benevolence +benevolences +benevolence's +benevolent +benevolently +benighted +benightedly +benign +benignly +benignant +benignity +benignity's +bent +bents +bent's +bentwood +bentwood's +benumb +benumbing +benumbed +benumbs +benzene +benzene's +benzine +benzine's +benzyl +bequeath +bequeathing +bequeathed +bequeaths +bequest +bequests +bequest's +berate +berating +berated +berates +bereave +bereaving +bereaved +bereaves +bereavement +bereavement +bereavements +bereavement's +bereft +beret +berets +beret's +berg +bergs +berg's +beriberi +beriberi's +berk +berks +berkelium +berkelium's +berm +berms +berm's +berry +berrying +berried +berries +berry's +berrylike +berserk +berth +berthing +berthed +berth's +berths +beryl +beryls +beryl's +beryllium +beryllium's +beseech +beseeching +beseecher +beseechers +beseeches +beseecher +beseecher's +beseeching +beseechingly +beseem +beseeming +beseemed +beseems +beset +besets +besetting +beside +besides +besiege +besieging +besieged +besieger +besiegers +besieges +besieger +besieger's +besmear +besmearing +besmeared +besmears +besmirch +besmirching +besmirched +besmirches +besom +besoms +besom's +besot +besots +besotted +besotting +besought +bespangle +bespangling +bespangled +bespangles +bespatter +bespattering +bespattered +bespatters +bespeak +bespeaking +bespeaks +bespectacled +bespoke +bespoken +best +besting +bested +bests +best's +bestial +bestially +bestiality +bestiality's +bestiary +bestiaries +bestiary's +bestir +bestirs +bestirred +bestirring +bestow +bestowing +bestowed +bestows +bestowal +bestowals +bestowal's +bestrew +bestrewing +bestrewed +bestrews +bestrewn +bestridden +bestride +bestriding +bestrides +bestrode +bestseller +bestsellers +bestseller's +bestselling +bet +bets +bet's +beta +betas +beta's +betake +betaking +betakes +betaken +betcha +betel +betel's +bethink +bethinking +bethinks +bethought +betide +betiding +betided +betides +betimes +betoken +betokening +betokened +betokens +betook +betray +betraying +betrayed +betrayer +betrayers +betrays +betrayal +betrayals +betrayal's +betrayer +betrayer's +betroth +betrothing +betrothed +betrothal +betrothals +betrothal's +betrothed +betrothed's +betroths +better +bettering +bettered +betters +better's +betterment +betterment +betterment's +betting +bettor +bettors +bettor's +between +betwixt +bevel +beveling +beveled +bevels +bevel's +beverage +beverages +beverage's +bevvy +bevvies +bevy +bevies +bevy's +bewail +bewailing +bewailed +bewails +beware +bewaring +bewared +bewares +bewhiskered +bewigged +bewilder +bewildering +bewildered +bewilders +bewilderment +bewildering +bewilderingly +bewilderment +bewilderment's +bewitch +bewitching +bewitched +bewitches +bewitchment +bewitching +bewitchingly +bewitchment +bewitchment's +bey +beys +bey's +beyond +bezel +bezels +bezel's +bf +bhaji +bi +bier +biers +bis +bi's +biannual +biannually +bias +biasing +biased +biases +bias's +biased +unbiased +biathlon +biathlons +biathlon's +bib +bibs +bib's +bible +bibles +bible's +biblical +bibliographer +bibliographers +bibliographer's +bibliographic +bibliographical +bibliographically +bibliography +bibliographies +bibliography's +bibliophile +bibliophiles +bibliophile's +bibulous +bicameral +bicameralism +bicameralism's +bicarb +bicarbs +bicarb's +bicarbonate +bicarbonates +bicarbonate's +bicentenary +bicentenaries +bicentenary's +bicentennial +bicentennials +bicentennial's +bicep +biceps +bicep's +biceps +biceps's +bicker +bickering +bickered +bickerer +bickerers +bickers +bicker's +bickerer +bickerer's +biconcave +biconvex +bicuspid +bicuspids +bicuspid's +bicycle +bicycling +bicycled +bicycler +bicyclers +bicycles +bicycle's +bicycler +bicycler's +bicyclist +bicyclists +bicyclist's +bid +biding +bids +bid's +biddable +bidden +unbidden +bidder +bidders +bidder's +bidding +bidding's +biddy +biddies +biddy's +bide +bides +bidet +bidets +bidet's +bidirectional +bidirectionally +biennial +biennially +biennials +biennial's +biennium +bienniums +biennium's +bier +bier's +biff +biffing +biffed +biffs +bifocal +bifocals +bifocals +bifocals's +bifurcate +bifurcation +bifurcations +bifurcating +bifurcated +bifurcates +bifurcation +bifurcation's +big +bigness +bigamist +bigamists +bigamist's +bigamous +bigamy +bigamy's +bigger +biggest +biggie +biggies +biggie's +biggish +bighead +bigheads +bighead's +bighearted +bigheartedness +bigheartedness +bigheartedness's +bighorn +bighorns +bighorn's +bight +bights +bight's +bigmouth +bigmouth's +bigmouths +bigness +bigness's +bigot +bigoted +bigots +bigot's +bigotry +bigotries +bigotry's +bigwig +bigwigs +bigwig's +bijou +bijou's +bijoux +bike +biking +biked +biker +bikers +bikes +bike's +biker +biker's +bikini +bikinis +bikini's +bilabial +bilabials +bilabial's +bilateral +bilaterally +bilberry +bilberries +bile +bile's +bilge +bilges +bilge's +bilingual +bilingually +bilinguals +bilingual's +bilingualism +bilingualism's +bilious +biliousness +biliousness +biliousness's +bilirubin +bilk +bilking +bilked +bilker +bilkers +bilks +bilker +bilker's +bill +billing +billings +billed +bills +bill's +billable +billboard +billboards +billboard's +billet +billeting +billeted +billets +billet's +billfold +billfolds +billfold's +billhook +billhooks +billiard +billiards +billiards +billiards's +billing +billing's +billingsgate +billingsgate's +billion +billionth +billions +billion's +billionaire +billionaires +billionaire's +billionth +billionth's +billionths +billow +billowing +billowed +billows +billow's +billowy +billy +billies +billy's +billycan +billycans +bimbo +bimbos +bimbo's +bimetallic +bimetallics +bimetallic's +bimetallism +bimetallism's +bimodal +bimonthly +bimonthlies +bimonthly's +bin +bins +bin's +binary +binaries +binary's +binaural +bind's +bind +binding +binds +rebinding +unbinding +rebinds +unbinds +rebind +unbind +binder +binders +binder's +bindery +binderies +bindery's +binding +bindings +binding's +bindweed +bindweed's +binge +binged +binges +binge's +bingo +bingo's +binman +binmen +binnacle +binnacles +binnacle's +binned +binning +binocular +binoculars +binocular's +binomial +binomials +binomial's +bio +bios +bio's +biochemical +biochemically +biochemicals +biochemical's +biochemist +biochemists +biochemist's +biochemistry +biochemistry's +biodegradability +biodegradability's +biodegrade +biodegrading +biodegraded +biodegrades +biodegradable +biodiversity +biodiversity's +bioethics +bioethics's +biofeedback +biofeedback's +biog +biographer +biographers +biographer's +biographic +biographical +biographically +biography +biographies +biography's +biol +biologic +biological +biologically +biologist +biologists +biologist's +biology +biology's +biomass +biomass's +biomedical +bionic +bionics +bionically +bionics +bionics's +biophysical +biophysicist +biophysicists +biophysicist's +biophysics +biophysics's +biopic +biopics +biopic's +biopsy +biopsying +biopsied +biopsies +biopsy's +bioreactor +bioreactors +biorhythm +biorhythms +biorhythm's +biosensor +biosensors +biosphere +biospheres +biosphere's +biosynthesis +biotech +biotechnological +biotechnology +biotechnology's +biotin +biotin's +bipartisan +bipartisanship +bipartisanship's +bipartite +biped +bipeds +biped's +bipedal +biplane +biplanes +biplane's +bipolar +bipolarity +bipolarity's +biracial +birch +birching +birched +birches +birch's +bird +birding +birded +birder +birders +birds +bird's +birdbath +birdbath's +birdbaths +birdbrain +birdbrained +birdbrains +birdbrain's +birdcage +birdcages +birder +birder's +birdhouse +birdhouses +birdhouse's +birdie +birdied +birdies +birdie's +birdieing +birdlike +birdlime +birdlime's +birdseed +birdseed's +birdsong +birdwatcher +birdwatchers +birdwatcher's +birdying +biretta +birettas +biretta's +birth +birthing +birthed +birther +birthers +birth's +birthday +birthdays +birthday's +birther +birther's +birthmark +birthmarks +birthmark's +birthplace +birthplaces +birthplace's +birthrate +birthrates +birthrate's +birthright +birthrights +birthright's +births +rebirths +birthstone +birthstones +birthstone's +biscuit +biscuits +biscuit's +bisect +bisecting +bisected +bisects +bisection +bisections +bisection's +bisector +bisectors +bisector's +bisexual +bisexually +bisexuals +bisexual's +bisexuality +bisexuality's +bishop +bishops +bishop's +bishopric +bishoprics +bishopric's +bismuth +bismuth's +bison +bison's +bisque +bisque's +bistro +bistros +bistro's +bit +biting +bits +bit's +debiting +debits +debit's +debit +bitch +bitching +bitched +bitches +bitch's +bitchily +bitchiness +bitchiness's +bitchy +bitchiest +bitchier +bitchiness +bitcoin +bitcoins +bitcoin's +bite +biter +biters +bites +bite's +biter +biter's +biting +bitingly +bitmap +bitmaps +bitten +bitter +bitterly +bitterest +bitterer +bitters +bitterness +bitter's +bittern +bitterns +bittern's +bitterness +bitterness's +bitters +bitters's +bittersweet +bittersweets +bittersweet's +bitty +bittiest +bittier +bitumen +bitumen's +bituminous +bivalent +bivalve +bivalves +bivalve's +bivouac +bivouacs +bivouac's +bivouacked +bivouacking +biweekly +biweeklies +biweekly's +biyearly +biz +biz's +bizarre +bizarrely +bk +bl +bling +bled +blab +blabs +blab's +blabbed +blabber +blabbering +blabbered +blabbers +blabbermouth +blabbermouth's +blabbermouths +blabbing +black +blacken +blackens +blackly +blacking +blacked +blackest +blacker +blacks +blackness +black's +blackamoor +blackamoors +blackamoor's +blackball +blackballing +blackballed +blackballs +blackball's +blackberry +blackberrying +blackberries +blackberry's +blackbird +blackbirds +blackbird's +blackboard +blackboards +blackboard's +blackcurrant +blackcurrants +blacken +blackening +blackened +blackface +blackguard +blackguards +blackguard's +blackhead +blackheads +blackhead's +blacking +blacking's +blackish +blackjack +blackjacking +blackjacked +blackjacks +blackjack's +blackleg +blacklegs +blacklist +blacklisting +blacklisted +blacklists +blacklist's +blackmail +blackmailing +blackmailed +blackmailer +blackmailers +blackmails +blackmail's +blackmailer +blackmailer's +blackness +blackness's +blackout +blackouts +blackout's +blacksmith +blacksmith's +blacksmiths +blacksnake +blacksnakes +blacksnake's +blackthorn +blackthorns +blackthorn's +blacktop +blacktops +blacktop's +blacktopped +blacktopping +bladder +bladders +bladder's +blade +bladed +blades +blade's +blag +blags +blagged +blagging +blah +blah's +blahs +blahs's +blame +blaming +blamed +blamer +blames +blame's +blamable +blameless +blamelessly +blamelessness +blamelessness +blamelessness's +blameworthiness +blameworthiness's +blameworthy +blameworthiness +blammo +blanch +blanching +blanched +blanches +blancmange +blancmanges +blancmange's +bland +blandly +blandest +blander +blandness +blandish +blandishing +blandished +blandishes +blandishment +blandishment +blandishments +blandishment's +blandness +blandness's +blank +blankly +blanking +blanked +blankest +blanker +blanks +blankness +blank's +blanket +blanketing +blanketed +blankets +blanket's +blankness +blankness's +blare +blaring +blared +blares +blare's +blarney +blarneying +blarneyed +blarneys +blarney's +blase +blaspheme +blaspheming +blasphemed +blasphemer +blasphemers +blasphemes +blasphemer +blasphemer's +blasphemous +blasphemously +blasphemy +blasphemies +blasphemy's +blast +blasting +blasted +blaster +blasters +blasts +blast's +blaster +blaster's +blastoff +blastoffs +blastoff's +blat +blats +blatancy +blatancies +blatancy's +blatant +blatantly +blather +blathering +blathered +blathers +blather's +blaze +blazing +blazed +blazer +blazers +blazes +blaze's +blazer +blazer's +blazon +blazoning +blazoned +blazons +blazon's +bldg +bleach +bleaching +bleached +bleacher +bleachers +bleaches +bleach's +bleached +unbleached +bleacher +bleacher's +bleak +bleakly +bleakest +bleaker +bleakness +bleakness +bleakness's +blear +blearily +bleariness +bleariness's +bleary +bleariest +blearier +bleariness +bleat +bleating +bleated +bleats +bleat's +bleed +bleeding +bleeder +bleeders +bleeds +bleeder +bleeder's +bleeding +bleeding's +bleep +bleeping +bleeped +bleeper +bleepers +bleeps +bleep's +bleeper +bleeper's +blemish +blemishing +blemished +blemishes +blemish's +blemished +unblemished +blench +blenching +blenched +blenches +blend +blending +blended +blender +blenders +blends +blend's +blender +blender's +bless +blessing +blessings +blessed +blesses +blessed +blessedly +blessedness +blessedness +blessedness's +blessing +blessing's +bletch +blew +blight +blighting +blighted +blighter +blighters +blights +blight's +blimey +blimp +blimps +blimp's +blimpish +blind +blindly +blinding +blinded +blindest +blinder +blinders +blinds +blindness +blind's +blinder +blinder's +blindfold +blindfolding +blindfolded +blindfolds +blindfold's +blinding +blindingly +blindness +blindness's +blindside +blindsiding +blindsided +blindsides +blini +blinis +blini's +blink +blinking +blinked +blinker +blinkers +blinks +blink's +blinker +blinkering +blinkered +blinker's +blintz +blintzes +blintz's +blintze +blintze's +blip +blips +blip's +bliss +bliss's +blissful +blissfully +blissfulness +blissfulness +blissfulness's +blister +blistering +blistered +blisters +blister's +blistering +blisteringly +blistery +blithe +blithely +blithest +blither +blitheness +blitheness +blitheness's +blither +blithering +blithesome +blitz +blitzing +blitzed +blitzes +blitz's +blitzkrieg +blitzkriegs +blitzkrieg's +blivet +blivets +blizzard +blizzards +blizzard's +bloat +bloating +bloated +bloater +bloaters +bloats +bloatware +blob +blobs +blob's +blobbed +blobbing +bloc +blocs +bloc's +block's +block +blocking +blocked +blocks +unblocking +unblocked +unblocks +unblock +blockade +blockading +blockaded +blockader +blockaders +blockades +blockade's +blockader +blockader's +blockage +blockages +blockage's +blockbuster +blockbusters +blockbuster's +blockbusting +blockbusting's +blocker +blockers +blocker's +blockhead +blockheads +blockhead's +blockhouse +blockhouses +blockhouse's +blog +blogs +blog's +blogged +blogger +bloggers +blogger's +blogging +bloke +blokes +bloke's +blokish +blond +blondest +blonder +blonds +blondness +blond's +blonde +blondes +blonde's +blondish +blondness +blondness's +blood +blooding +blooded +bloods +blood's +bloodbath +bloodbath's +bloodbaths +bloodcurdling +bloodhound +bloodhounds +bloodhound's +bloodily +bloodiness +bloodiness's +bloodless +bloodlessly +bloodlessness +bloodlessness +bloodlessness's +bloodletting +bloodletting's +bloodline +bloodlines +bloodline's +bloodmobile +bloodmobiles +bloodmobile's +bloodshed +bloodshed's +bloodshot +bloodstain +bloodstained +bloodstains +bloodstain's +bloodstock +bloodstock's +bloodstream +bloodstreams +bloodstream's +bloodsucker +bloodsuckers +bloodsucker's +bloodsucking +bloodthirstily +bloodthirstiness +bloodthirstiness's +bloodthirsty +bloodthirstiest +bloodthirstier +bloodthirstiness +bloody +bloodying +bloodied +bloodiest +bloodier +bloodies +bloodiness +bloom +blooming +bloomed +bloomer +bloomers +blooms +bloom's +bloomer +bloomer's +bloop +blooping +blooped +blooper +bloopers +bloops +bloop's +blooper +blooper's +blossom +blossoming +blossomed +blossoms +blossom's +blossomy +blot +blots +blot's +blotch +blotching +blotched +blotches +blotch's +blotchy +blotchiest +blotchier +blotted +blotter +blotters +blotter's +blotting +blotto +blouse +blousing +bloused +blouses +blouse's +blow +blowing +blower +blowers +blows +blow's +blower +blower's +blowfly +blowflies +blowfly's +blowgun +blowguns +blowgun's +blowhard +blowhards +blowhard's +blowhole +blowholes +blowjob +blowjobs +blowjob's +blowlamp +blowlamps +blown +blowout +blowouts +blowout's +blowpipe +blowpipes +blowpipe's +blowtorch +blowtorches +blowtorch's +blowup +blowups +blowup's +blowy +blowiest +blowier +blowzy +blowziest +blowzier +blubber +blubbering +blubbered +blubbers +blubber's +blubbery +bludgeon +bludgeoning +bludgeoned +bludgeons +bludgeon's +blue +bluing +blued +bluest +bluer +blues +blueness +blue's +bluebell +bluebells +bluebell's +blueberry +blueberries +blueberry's +bluebird +bluebirds +bluebird's +bluebonnet +bluebonnets +bluebonnet's +bluebottle +bluebottles +bluebottle's +bluefish +bluefishes +bluefish's +bluegill +bluegills +bluegill's +bluegrass +bluegrass's +blueish +bluejacket +bluejackets +bluejacket's +bluejeans +bluejeans's +blueness +blueness's +bluenose +bluenoses +bluenose's +bluepoint +bluepoints +bluepoint's +blueprint +blueprinting +blueprinted +blueprints +blueprint's +bluestocking +bluestockings +bluestocking's +bluesy +bluesiest +bluesier +bluet +bluets +bluet's +bluff +bluffly +bluffing +bluffed +bluffest +bluffer +bluffers +bluffs +bluffness +bluff's +bluffer +bluffer's +bluffness +bluffness's +bluing +bluing's +bluish +blunder +blundering +blundered +blunderer +blunderers +blunders +blunder's +blunderbuss +blunderbusses +blunderbuss's +blunderer +blunderer's +blunt +bluntly +blunting +blunted +bluntest +blunter +blunts +bluntness +bluntness +bluntness's +blur +blurs +blur's +blurb +blurbs +blurb's +blurred +blurriness +blurriness's +blurring +blurry +blurriest +blurrier +blurriness +blurt +blurting +blurted +blurts +blush +blushing +blushed +blusher +blushers +blushes +blush's +blusher +blusher's +bluster +blustering +blustered +blusterer +blusterers +blusters +bluster's +blusterer +blusterer's +blusterous +blustery +blvd +boa +boas +boa's +boar +boars +boar's +board +boarding +boarded +boarder +boarders +boards +board's +boarder +boarder's +boarding +boarding's +boardinghouse +boardinghouses +boardinghouse's +boardroom +boardrooms +boardroom's +boardwalk +boardwalks +boardwalk's +boast +boasting +boasted +boaster +boasters +boasts +boast's +boaster +boaster's +boastful +boastfully +boastfulness +boastfulness +boastfulness's +boat +boating +boated +boater +boaters +boats +boat's +boater +boater's +boathouse +boathouses +boathouse's +boating +boating's +boatload +boatloads +boatman +boatman's +boatmen +boatswain +boatswains +boatswain's +boatyard +boatyards +bob +bobs +bob's +bobbed +bobbin +bobbins +bobbin's +bobbing +bobble +bobbling +bobbled +bobbles +bobble's +bobby +bobbies +bobby's +bobbysoxer +bobbysoxers +bobbysoxer's +bobcat +bobcats +bobcat's +bobolink +bobolinks +bobolink's +bobsled +bobsleds +bobsled's +bobsledded +bobsledder +bobsledders +bobsledder's +bobsledding +bobsleigh +bobsleigh's +bobsleighs +bobtail +bobtails +bobtail's +bobwhite +bobwhites +bobwhite's +boccie +boccie's +bock +bock's +bod +boding +boded +bods +bod's +bodacious +bode +bodes +bodega +bodegas +bodega's +bodge +bodging +bodged +bodges +bodice +bodices +bodice's +bodily +bodkin +bodkins +bodkin's +body +bodied +bodies +body's +bodybuilder +bodybuilders +bodybuilder's +bodybuilding +bodybuilding's +bodyguard +bodyguards +bodyguard's +bodysuit +bodysuits +bodysuit's +bodywork +bodywork's +boffin +boffins +boffo +bog +bogs +bog's +boga +bogey +bogeying +bogeyed +bogeys +bogey's +bogeyman +bogeyman's +bogeymen +bogged +bogging +boggle +boggling +boggled +boggles +boggy +boggiest +boggier +bogie +bogies +bogie's +bogon +bogosity +bogus +bogyman +bogyman's +bogymen +bohemian +bohemians +bohemian's +bohemianism +bohemianism's +boil +boiling +boilings +boiled +boiler +boilers +boils +boil's +boiler +boiler's +boilermaker +boilermakers +boilermaker's +boilerplate +boilerplate's +boink +boinking +boinked +boinks +boisterous +boisterously +boisterousness +boisterousness +boisterousness's +bola +bolas +bola's +bold +boldly +boldest +bolder +boldness +boldface +boldfaced +boldface's +boldness +boldness's +bole +boles +bole's +bolero +boleros +bolero's +bolivar +bolivars +bolivar's +bolivares +boll +bolls +boll's +bollard +bollards +bollix +bollixing +bollixed +bollixes +bollix's +bollocking +bollockings +bollocks +bologna +bologna's +bolshie +bolster +bolstering +bolstered +bolsters +bolster's +bolt's +bolt +bolting +bolted +bolts +unbolting +unbolted +unbolts +unbolt +bolthole +boltholes +bolus +boluses +bolus's +bomb +bombing +bombings +bombed +bomber +bombers +bombs +bomb's +bombard +bombarding +bombarded +bombards +bombardment +bombardier +bombardiers +bombardier's +bombardment +bombardments +bombardment's +bombast +bombast's +bombastic +bombastically +bomber +bomber's +bombproof +bombshell +bombshells +bombshell's +bombsite +bombsites +bonanza +bonanzas +bonanza's +bonbon +bonbons +bonbon's +bonce +bonces +bond +bonding +bonded +bonds +bond's +bondage +bondage's +bondholder +bondholders +bondholder's +bonding +bonding's +bondman +bondman's +bondmen +bondsman +bondsman's +bondsmen +bondwoman +bondwoman's +bondwomen +bone +boning +boned +boner +boners +bones +bone's +bonehead +boneheaded +boneheads +bonehead's +boneless +boner +boner's +boneshaker +boneshakers +boneyard +bonfire +bonfires +bonfire's +bong +bonging +bonged +bongs +bong's +bongo +bongos +bongo's +bonhomie +bonhomie's +boniness +boniness's +bonito +bonitos +bonito's +bonk +bonking +bonked +bonkers +bonks +bonnet +bonnets +bonnet's +bonny +bonniest +bonnier +bonobo +bonobos +bonobo's +bonsai +bonsai's +bonus +bonuses +bonus's +bony +boniest +bonier +boniness +boo +booth +booing +booed +boos +boo's +boob +boobing +boobed +boobs +boob's +booby +boobies +booby's +boodle +boodles +boodle's +booger +boogers +boogeyman +boogeyman's +boogeymen +boogie +boogied +boogies +boogie's +boogieing +boogieman +boogieman's +boohoo +boohooing +boohooed +boohoos +boohoo's +book +booking +bookings +booked +books +book's +bookable +bookbinder +bookbinders +bookbinder's +bookbindery +bookbinderies +bookbindery's +bookbinding +bookbinding's +bookcase +bookcases +bookcase's +bookend +bookends +bookend's +bookie +bookies +bookie's +booking +booking's +bookish +bookkeeper +bookkeepers +bookkeeper's +bookkeeping +bookkeeping's +booklet +booklets +booklet's +bookmaker +bookmakers +bookmaker's +bookmaking +bookmaking's +bookmark +bookmarking +bookmarked +bookmarks +bookmark's +bookmobile +bookmobiles +bookmobile's +bookplate +bookplates +bookplate's +bookseller +booksellers +bookseller's +bookshelf +bookshelf's +bookshelves +bookshop +bookshops +bookshop's +bookstall +bookstalls +bookstore +bookstores +bookstore's +bookworm +bookworms +bookworm's +boom +booming +boomed +boomer +boomers +booms +boom's +boombox +boomboxes +boombox's +boomerang +boomeranging +boomeranged +boomerangs +boomerang's +boon +boons +boon's +boondocks +boondocks's +boondoggle +boondoggling +boondoggled +boondoggler +boondogglers +boondoggles +boondoggle's +boondoggler +boondoggler's +boonies +boonies's +boor +boors +boor's +boorish +boorishly +boorishness +boorishness +boorishnesses +boorishness's +boost +boosting +boosted +booster +boosters +boosts +boost's +booster +booster's +boot's +boot +booting +booted +boots +rebooting +rebooted +reboots +reboot +bootblack +bootblacks +bootblack's +bootee +bootees +bootee's +booth +booth's +booths +bootlace +bootlaces +bootleg +bootlegs +bootleg's +bootlegged +bootlegger +bootleggers +bootlegger's +bootlegging +bootlegging's +bootless +bootstrap +bootstraps +bootstrap's +bootstrapped +bootstrapping +booty +booties +booty's +booze +boozing +boozed +boozer +boozers +boozes +booze's +boozer +boozer's +boozy +booziest +boozier +bop +bops +bop's +bopped +bopping +borax +borax's +bordello +bordellos +bordello's +border +bordering +bordered +borders +border's +borderland +borderlands +borderland's +borderline +borderlines +borderline's +bore +boring +bored +borer +borers +bores +bore's +boredom +boredom's +borehole +boreholes +borer +borer's +boring +boringly +born +reborn +inborn +unborn +borne +boron +boron's +borough +borough's +boroughs +borrow +borrowing +borrowings +borrowed +borrower +borrowers +borrows +borrower +borrower's +borrowing +borrowing's +borscht +borscht's +borstal +borstals +borzoi +borzois +borzoi's +bosh +bosh's +bosom's +bosom +bosoms +unbosoms +unbosom +bosomy +boss +bossing +bossed +bosses +boss's +bossily +bossiness +bossiness's +bossism +bossism's +bossy +bossiest +bossier +bossiness +bot +bots +botanic +botanical +botanically +botanist +botanists +botanist's +botany +botany's +botch +botching +botched +botcher +botchers +botches +botch's +botcher +botcher's +both +bother +bothering +bothered +bothers +bother's +botheration +bothered +unbothered +bothersome +botnet +botnets +botnet's +bottle +bottling +bottled +bottler +bottlers +bottles +bottle's +bottleneck +bottlenecks +bottleneck's +bottler +bottler's +bottom +bottoming +bottomed +bottoms +bottom's +bottomless +botulinum +botulism +botulism's +boudoir +boudoirs +boudoir's +bouffant +bouffants +bouffant's +bougainvillea +bougainvilleas +bougainvillea's +bough +bough's +boughs +bought +bouillabaisse +bouillabaisses +bouillabaisse's +bouillon +bouillons +bouillon's +boulder +boulders +boulder's +boules +boulevard +boulevards +boulevard's +bounce +bouncing +bounced +bouncer +bouncers +bounces +bounce's +bouncer +bouncer's +bouncily +bounciness +bounciness's +bouncy +bounciest +bouncier +bounciness +bound +bounding +bounded +bounds +bound's +rebounding +rebounded +rebounds +rebound's +rebound +boundary +boundaries +boundary's +bounden +bounder +bounders +bounder's +boundless +boundlessly +boundlessness +boundlessness +boundlessness's +bounteous +bounteously +bounteousness +bounteousness +bounteousness's +bountiful +bountifully +bountifulness +bountifulness +bountifulness's +bounty +bounties +bounty's +bouquet +bouquets +bouquet's +bourbon +bourbons +bourbon's +bourgeois +bourgeois's +bourgeoisie +bourgeoisie's +boustrophedon +bout +bouts +bout's +boutique +boutiques +boutique's +boutonniere +boutonnieres +boutonniere's +bouzouki +bouzoukis +bouzouki's +bovine +bovines +bovine's +bovver +bow +bowing +bowed +bower +bowers +bows +bow's +bowdlerization +bowdlerizations +bowdlerization's +bowdlerize +bowdlerizing +bowdlerized +bowdlerizes +bowed +unbowed +bowel +bowels +bowel's +bower +bower's +bowl +bowling +bowled +bowler +bowlers +bowls +bowl's +bowleg +bowlegs +bowleg's +bowlegged +bowler +bowler's +bowlful +bowlfuls +bowlful's +bowline +bowlines +bowline's +bowling +bowling's +bowman +bowman's +bowmen +bowsprit +bowsprits +bowsprit's +bowstring +bowstrings +bowstring's +bowwow +bowwows +bowwow's +box +boxen +boxing +boxed +boxer +boxers +boxes +box's +boxcar +boxcars +boxcar's +boxer +boxer's +boxing +boxing's +boxlike +boxroom +boxrooms +boxwood +boxwood's +boxy +boxiest +boxier +boy +boys +boy's +boycott +boycotting +boycotted +boycotts +boycott's +boyfriend +boyfriends +boyfriend's +boyhood +boyhoods +boyhood's +boyish +boyishly +boyishness +boyishness +boyishness's +boysenberry +boysenberries +boysenberry's +bozo +bozos +bozo's +bpm +bps +bra +bras +bra's +brace +bracing +braced +bracer +bracers +braces +brace's +bracelet +bracelets +bracelet's +bracer +bracer's +bracero +braceros +bracero's +bracken +bracken's +bracket +bracketing +bracketed +brackets +bracket's +brackish +brackishness +brackishness +brackishness's +bract +bracts +bract's +brad +brads +brad's +bradawl +bradawls +bradycardia +brae +braes +brae's +brag +brags +brag's +braggadocio +braggadocios +braggadocio's +braggart +braggarts +braggart's +bragged +bragger +braggers +bragger's +bragging +braid +braiding +braided +braids +braid's +braiding +braiding's +braille +braille's +brain +braining +brained +brains +brain's +brainchild +brainchild's +brainchildren +brainchildren's +braininess +braininess's +brainless +brainlessly +brainpower +brainstorm +brainstorming +brainstormed +brainstorms +brainstorm's +brainstorming +brainstorming's +brainteaser +brainteasers +brainteaser's +brainwash +brainwashing +brainwashed +brainwashes +brainwashing +brainwashing's +brainwave +brainwaves +brainy +brainiest +brainier +braininess +braise +braising +braised +braises +brake +braking +braked +brakes +brake's +brakeman +brakeman's +brakemen +bramble +brambles +bramble's +brambly +bran +bran's +branch +branching +branched +branches +branch's +branchlike +brand +branding +branded +brander +branders +brands +brand's +branded +unbranded +brander +brander's +brandish +brandishing +brandished +brandishes +brandy +brandying +brandied +brandies +brandy's +brash +brashly +brashest +brasher +brashness +brashness +brashness's +brass +brasses +brass's +brasserie +brasseries +brasserie's +brassiere +brassieres +brassiere's +brassily +brassiness +brassiness's +brassy +brassiest +brassier +brassiness +brat +brats +brat's +bratty +brattiest +brattier +bratwurst +bratwursts +bratwurst's +bravado +bravado's +brave +bravely +braving +braved +bravest +braver +braves +braveness +brave's +braveness +braveness's +bravery +bravery's +bravo +bravos +bravo's +bravura +bravuras +bravura's +brawl +brawling +brawled +brawler +brawlers +brawls +brawl's +brawler +brawler's +brawn +brawn's +brawniness +brawniness's +brawny +brawniest +brawnier +brawniness +bray +braying +brayed +brays +bray's +braze +brazing +brazed +brazer +brazers +brazes +brazen +brazenly +brazening +brazened +brazens +brazenness +brazenness +brazenness's +brazer +brazer's +brazier +braziers +brazier's +breach +breaching +breached +breaches +breach's +bread +breadth +breading +breaded +breads +bread's +breadbasket +breadbaskets +breadbasket's +breadboard +breadboards +breadboard's +breadbox +breadboxes +breadbox's +breadcrumb +breadcrumbs +breadcrumb's +breadfruit +breadfruits +breadfruit's +breadline +breadlines +breadline's +breadth +breadth's +breadths +breadwinner +breadwinners +breadwinner's +break +breaking +breaker +breakers +breaks +break's +breakable +breakable +breakables +breakable's +breakage +breakages +breakage's +breakaway +breakaways +breakaway's +breakdown +breakdowns +breakdown's +breaker +breaker's +breakfast +breakfasting +breakfasted +breakfasts +breakfast's +breakfront +breakfronts +breakfront's +breakneck +breakout +breakouts +breakout's +breakpoints +breakthrough +breakthrough's +breakthroughs +breakup +breakups +breakup's +breakwater +breakwaters +breakwater's +bream +breams +bream's +breast +breasting +breasted +breasts +breast's +breastbone +breastbones +breastbone's +breastfed +breastfeed +breastfeeding +breastfeeds +breastplate +breastplates +breastplate's +breaststroke +breaststrokes +breaststroke's +breastwork +breastworks +breastwork's +breath +breathing +breathed +breather +breathers +breathes +breath's +breathable +breathalyze +breathalyzing +breathalyzed +breathalyzer +breathalyzers +breathalyzes +breathe +breather +breather's +breathing +breathing's +breathless +breathlessly +breathlessness +breathlessness +breathlessness's +breaths +breathtaking +breathtakingly +breathy +breathiest +breathier +bred +inbred +breech +breeches +breech's +breed +breeding +breeder +breeders +breeds +breed's +breeder +breeder's +breeding +breeding's +inbreeding's +inbreeding +breeze +breezing +breezed +breezes +breeze's +breezeway +breezeways +breezeway's +breezily +breeziness +breeziness's +breezy +breeziest +breezier +breeziness +brethren +breve +breves +breve's +brevet +brevets +brevet's +brevetted +brevetting +breviary +breviaries +breviary's +brevity +brevity's +brew +brewing +brewed +brewer +brewers +brews +brew's +brewer +brewer's +brewery +breweries +brewery's +brewpub +brewpubs +brewpub's +bribe +bribing +bribed +briber +bribers +bribes +bribe's +briber +briber's +bribery +bribery's +brick +bricking +bricked +bricks +brick's +brickbat +brickbats +brickbat's +brickie +brickies +bricklayer +bricklayers +bricklayer's +bricklaying +bricklaying's +brickwork +brickwork's +brickyard +brickyards +bridal +bridals +bridal's +bride +brides +bride's +bridegroom +bridegrooms +bridegroom's +bridesmaid +bridesmaids +bridesmaid's +bridge +bridging +bridged +bridges +bridge's +bridgeable +unbridgeable +bridgehead +bridgeheads +bridgehead's +bridgework +bridgework's +bridle +bridling +bridled +bridles +bridle's +bridled +unbridled +bridleway +bridleways +brie +brier +briers +brie's +brief's +brief +briefing +briefings +briefed +briefest +briefs +debriefing +debriefings +debriefed +debriefs +debrief +briefcase +briefcases +briefcase's +briefer +briefing +briefing's +debriefing's +debriefing +briefly +briefness +briefness's +brier +brier's +brig +brigs +brig's +brigade +brigades +brigade's +brigadier +brigadiers +brigadier's +brigand +brigands +brigand's +brigandage +brigandage's +brigantine +brigantines +brigantine's +bright +brighten +brightens +brightly +brightest +brighter +brights +brightness +brighten +brightening +brightened +brightener +brighteners +brightener +brightener's +brightness +brightness's +brights +brights's +brill +brilliance +brilliance's +brilliancy +brilliancy's +brilliant +brilliantly +brilliants +brilliant's +brilliantine +brilliantine's +brim +brims +brim's +brimful +brimless +brimmed +brimming +brimstone +brimstone's +brindle +brindled +brindle's +brine +brine's +bring +bringing +bringer +bringers +brings +bringer +bringer's +brininess +brininess's +brink +brinks +brink's +brinkmanship +brinkmanship's +briny +briniest +brinier +brininess +brioche +brioches +brioche's +briquette +briquettes +briquette's +brisk +briskly +brisking +brisked +briskest +brisker +brisks +briskness +brisket +briskets +brisket's +briskness +briskness's +bristle +bristling +bristled +bristles +bristle's +bristly +bristliest +bristlier +britches +britches's +brittle +brittlest +brittler +brittleness +brittle's +brittleness +brittleness's +bro +broth +bros +bro's +broach +broaching +broached +broaches +broach's +broad +broaden +broadens +broadly +broadest +broader +broads +broadness +broad's +broadband +broadband's +broadcast +broadcasting +broadcasts +broadcast's +rebroadcasting +rebroadcasts +rebroadcast's +rebroadcast +broadcaster +broadcasters +broadcaster's +broadcasting +broadcasting's +broadcloth +broadcloth's +broaden +broadening +broadened +broadloom +broadloom's +broadminded +broadness +broadness's +broadsheet +broadsheets +broadsheet's +broadside +broadsiding +broadsided +broadsides +broadside's +broadsword +broadswords +broadsword's +brocade +brocading +brocaded +brocades +brocade's +broccoli +broccoli's +brochette +brochettes +brochette's +brochure +brochures +brochure's +brogan +brogans +brogan's +brogue +brogues +brogue's +broil +broiling +broiled +broiler +broilers +broils +broil's +broiler +broiler's +broke +broken +brokenly +brokenness +brokenhearted +brokenheartedly +brokenness +brokenness's +broker +brokering +brokered +brokers +broker's +brokerage +brokerages +brokerage's +brolly +brollies +bromide +bromides +bromide's +bromidic +bromine +bromine's +bronc +broncs +bronc's +bronchi +bronchial +bronchitic +bronchitis +bronchitis's +bronchus +bronchus's +bronco +broncos +bronco's +broncobuster +broncobusters +broncobuster's +brontosaur +brontosaurs +brontosaur's +brontosaurus +brontosauruses +brontosaurus's +bronze +bronzing +bronzed +bronzes +bronze's +brooch +brooches +brooch's +brood +brooding +brooded +brooder +brooders +broods +brood's +brooder +brooder's +broodily +brooding +broodingly +brooding's +broodmare +broodmares +broodmare's +broody +broodiest +broodier +broodiness +broody's +brook +brooking +brooked +brooks +brook's +brooklet +brooklets +brooklet's +broom +brooms +broom's +broomstick +broomsticks +broomstick's +broth +brother +brothers +broth's +brothel +brothels +brothel's +brother +brotherly +brother's +brotherhood +brotherhoods +brotherhood's +brotherliness +brotherliness's +broths +brougham +broughams +brougham's +brought +brouhaha +brouhahas +brouhaha's +brow +brows +brow's +browbeat +browbeaten +browbeating +browbeats +brown +browning +browned +brownest +browner +browns +brownness +brown's +brownfield +brownie +brownies +brownie's +brownish +brownness +brownness's +brownout +brownouts +brownout's +brownstone +brownstones +brownstone's +browse +browsing +browsed +browser +browsers +browses +browse's +browser +browser's +brr +bruin +bruins +bruin's +bruise +bruising +bruised +bruiser +bruisers +bruises +bruise's +bruiser +bruiser's +bruising +bruising's +bruit +bruiting +bruited +bruits +brunch +brunching +brunched +brunches +brunch's +brunet +brunets +brunet's +brunette +brunettes +brunette's +brunt +brunt's +brush +brushing +brushed +brushes +brush's +brushoff +brushoffs +brushoff's +brushstroke +brushstrokes +brushwood +brushwood's +brushwork +brushwork's +brusque +brusquely +brusquest +brusquer +brusqueness +brusqueness +brusqueness's +brutal +brutally +brutality +brutalities +brutality's +brutalization +brutalization's +brutalize +brutalizing +brutalized +brutalizes +brute +brutes +brute's +brutish +brutishly +brutishness +brutishness +brutishness's +bu +bub +bubs +bub's +bubble +bubbling +bubbled +bubbles +bubble's +bubblegum +bubblegum's +bubbly +bubbliest +bubblier +bubbly's +bubo +bubo's +buboes +buccaneer +buccaneering +buccaneered +buccaneers +buccaneer's +buck +bucking +bucked +bucks +buck's +buckaroo +buckaroos +buckaroo's +buckboard +buckboards +buckboard's +bucket +bucketing +bucketed +buckets +bucket's +bucketful +bucketfuls +bucketful's +buckeye +buckeyes +buckeye's +buckle's +buckle +buckling +buckled +buckles +unbuckling +unbuckled +unbuckles +unbuckle +buckler +bucklers +buckler's +buckram +buckram's +bucksaw +bucksaws +bucksaw's +buckshot +buckshot's +buckskin +buckskins +buckskin's +buckteeth +bucktooth +bucktoothed +bucktooth's +buckwheat +buckwheat's +buckyball +buckyballs +buckyball's +bucolic +bucolics +bucolic's +bucolically +bud +buds +bud's +budded +budding +buddings +buddy +buddies +buddy's +budge +budging +budged +budges +budgerigar +budgerigars +budgerigar's +budget +budgeting +budgeted +budgets +budget's +budgetary +budgie +budgies +budgie's +buff +buffing +buffed +buffs +buff's +rebuffing +rebuffed +rebuffs +rebuff's +rebuff +buffalo +buffaloing +buffaloed +buffalo's +buffaloes +buffer +buffering +buffered +buffers +buffer's +buffet +buffeting +buffetings +buffeted +buffets +buffet's +buffoon +buffoons +buffoon's +buffoonery +buffoonery's +buffoonish +bug's +bug +bugs +debugs +debug +bugaboo +bugaboos +bugaboo's +bugbear +bugbears +bugbear's +bugged +debugged +bugger +buggering +buggered +buggers +bugger's +buggery +bugging +debugging +buggy +buggiest +buggier +buggies +buggy's +bugle +bugling +bugled +bugler +buglers +bugles +bugle's +bugler +bugler's +build +building +buildings +builder +builders +builds +build's +builder +builder's +building +building's +buildup +buildups +buildup's +built +rebuilt +inbuilt +builtin +bulb +bulbs +bulb's +bulbous +bulge +bulging +bulged +bulges +bulge's +bulgy +bulgiest +bulgier +bulimarexia +bulimarexia's +bulimia +bulimia's +bulimic +bulimics +bulimic's +bulk +bulking +bulked +bulks +bulk's +bulkhead +bulkheads +bulkhead's +bulkiness +bulkiness's +bulky +bulkiest +bulkier +bulkiness +bull +bulling +bulled +bulls +bull's +bulldog +bulldogs +bulldog's +bulldogged +bulldogging +bulldoze +bulldozing +bulldozed +bulldozer +bulldozers +bulldozes +bulldozer +bulldozer's +bullet +bulleted +bullets +bullet's +bulletin +bulletining +bulletined +bulletins +bulletin's +bulletproof +bulletproofing +bulletproofed +bulletproofs +bullfight +bullfighting +bullfighter +bullfighters +bullfights +bullfight's +bullfighter +bullfighter's +bullfighting +bullfighting's +bullfinch +bullfinches +bullfinch's +bullfrog +bullfrogs +bullfrog's +bullhead +bullheaded +bullheads +bullhead's +bullheaded +bullheadedly +bullheadedness +bullheadedness +bullheadedness's +bullhorn +bullhorns +bullhorn's +bullion +bullion's +bullish +bullishly +bullishness +bullishness +bullishness's +bullock +bullocks +bullock's +bullpen +bullpens +bullpen's +bullring +bullrings +bullring's +bullseye +bullshit +bullshits +bullshit's +bullshitted +bullshitter +bullshitters +bullshitter's +bullshitting +bullwhip +bullwhips +bully +bullying +bullied +bullies +bully's +bulrush +bulrushes +bulrush's +bulwark +bulwarks +bulwark's +bum +bums +bum's +bumbag +bumbags +bumble +bumbling +bumbled +bumbler +bumblers +bumbles +bumblebee +bumblebees +bumblebee's +bumbler +bumbler's +bumf +bummed +bummer +bummers +bummer's +bummest +bumming +bump +bumping +bumped +bumper +bumpers +bumps +bump's +bumper +bumper's +bumph +bumpiness +bumpiness's +bumpkin +bumpkins +bumpkin's +bumptious +bumptiously +bumptiousness +bumptiousness +bumptiousness's +bumpy +bumpiest +bumpier +bumpiness +bun +buns +bun's +bunch +bunching +bunched +bunches +bunch's +bunchy +bunchiest +bunchier +bunco +buncoing +buncoed +buncos +bunco's +bundle +bundling +bundled +bundles +bundle's +bung +bunging +bunged +bungs +bung's +bungalow +bungalows +bungalow's +bungee +bungees +bungee's +bunghole +bungholes +bunghole's +bungle +bungling +bungled +bungler +bunglers +bungles +bungle's +bungler +bungler's +bunion +bunions +bunion's +bunk's +bunk +bunking +bunked +bunks +debunking +debunked +debunks +debunk +bunker +bunkers +bunker's +bunkhouse +bunkhouses +bunkhouse's +bunkum +bunkum's +bunny +bunnies +bunny's +bunt +bunting +buntings +bunted +bunts +bunt's +bunting +bunting's +buoy +buoying +buoyed +buoys +buoy's +buoyancy +buoyancy's +buoyant +buoyantly +bur +burly +burs +bur's +burble +burbling +burbled +burbles +burble's +burbs +burbs's +burden's +burden +burdening +burdened +burdens +unburdening +unburdened +unburdens +unburden +burdensome +burdock +burdock's +bureau +bureaus +bureau's +bureaucracy +bureaucracies +bureaucracy's +bureaucrat +bureaucrats +bureaucrat's +bureaucratic +bureaucratically +bureaucratization +bureaucratization's +bureaucratize +bureaucratizing +bureaucratized +bureaucratizes +burg +burger +burgers +burgs +burg's +burgeon +burgeoning +burgeoned +burgeons +burger +burger's +burgh +burgher +burghers +burgh's +burgher +burgher's +burghs +burglar +burglars +burglar's +burglarize +burglarizing +burglarized +burglarizes +burglarproof +burglary +burglaries +burglary's +burgle +burgling +burgled +burgles +burgomaster +burgomasters +burgomaster's +burgundy +burgundies +burgundy's +burial +burials +burial's +reburials +reburial's +reburial +burka +burkas +burka's +burl +burled +burls +burl's +burlap +burlap's +burlesque +burlesquing +burlesqued +burlesques +burlesque's +burliness +burliness's +burly +burliest +burlier +burliness +burn +burning +burned +burner +burners +burns +burn's +burnable +burnable +burnables +burnable's +burner +burner's +burnish +burnishing +burnished +burnisher +burnishers +burnishes +burnish's +burnisher +burnisher's +burnoose +burnooses +burnoose's +burnout +burnouts +burnout's +burnt +burp +burping +burped +burps +burp's +burr +burring +burred +burrs +burr's +burrito +burritos +burrito's +burro +burros +burro's +burrow +burrowing +burrowed +burrower +burrowers +burrows +burrow's +burrower +burrower's +bursa +bursa's +bursae +bursar +bursars +bursar's +bursary +bursaries +bursary's +bursitis +bursitis's +burst +bursting +bursts +burst's +bury +burying +buried +buries +reburying +reburied +reburies +rebury +bus +buses +bus's +rebuses +rebus's +rebus +busboy +busboys +busboy's +busby +busbies +busby's +bused +busgirl +busgirls +busgirl's +bush +bushing +bushings +bushed +bushes +bush's +bushel +busheling +busheled +bushels +bushel's +bushiness +bushiness's +bushing +bushing's +bushman +bushman's +bushmaster +bushmasters +bushmaster's +bushmen +bushwhack +bushwhacking +bushwhacked +bushwhacker +bushwhackers +bushwhacks +bushwhacker +bushwhacker's +bushy +bushiest +bushier +bushiness +busily +business +businesses +business's +businesslike +businessman +businessman's +businessmen +businessperson +businesspersons +businessperson's +businesswoman +businesswoman's +businesswomen +busing +busing's +busk +busking +busked +busker +buskers +busks +buskin +buskins +buskin's +busload +busloads +buss +buss's +bust +busting +busted +buster +busters +busts +bust's +buster +buster's +bustle +bustling +bustled +bustles +bustle's +busty +bustiest +bustier +bustiers +busy +busying +busied +busiest +busier +busies +business +busybody +busybodies +busybody's +busyness +busyness's +busywork +busywork's +but +buts +rebuts +debuts +rebut +debut +butane +butane's +butch +butcher +butchers +butches +butch's +butcher +butchering +butchered +butcher's +butchery +butcheries +butchery's +butler +butlers +butler's +butt +butting +butted +butter +butters +butts +butt's +butte +buttes +butte's +butted +rebutted +butter +buttering +buttered +butter's +butterball +butterballs +butterball's +buttercream +buttercup +buttercups +buttercup's +butterfat +butterfat's +butterfingered +butterfingers +butterfingers's +butterfly +butterflying +butterflied +butterflies +butterfly's +buttermilk +buttermilk's +butternut +butternuts +butternut's +butterscotch +butterscotch's +buttery +butteriest +butterier +butteries +buttery's +butting +rebutting +buttock +buttocks +buttock's +button's +button +buttoning +buttoned +buttons +unbuttoning +unbuttoned +unbuttons +unbutton +buttonhole +buttonholing +buttonholed +buttonholes +buttonhole's +buttonwood +buttonwoods +buttonwood's +buttress +buttressing +buttressed +buttresses +buttress's +butty +butties +buxom +buy +buying +buyer +buyers +buys +buy's +buyback +buybacks +buyback's +buyer +buyer's +buyout +buyouts +buyout's +buzz +buzzing +buzzed +buzzer +buzzers +buzzes +buzz's +buzzard +buzzards +buzzard's +buzzer +buzzer's +buzzkill +buzzkills +buzzkill's +buzzword +buzzwords +buzzword's +bx +bxs +by +by's +bye +byes +bye's +bygone +bygones +bygone's +bylaw +bylaws +bylaw's +byline +bylines +byline's +bypass +bypassing +bypassed +bypasses +bypass's +bypath +bypath's +bypaths +byplay +byplay's +byproduct +byproducts +byproduct's +byre +byres +byroad +byroads +byroad's +bystander +bystanders +bystander's +byte +bytes +byte's +byway +byways +byway's +byword +bywords +byword's +byzantine +c +cs +incs +discs +inc +disc +ca +cab +caber +cabers +cabs +cab's +cabal +cabals +cabal's +cabala's +caballero +caballeros +caballero's +cabana +cabanas +cabana's +cabaret +cabarets +cabaret's +cabbage +cabbages +cabbage's +cabbed +cabbing +cabby +cabbies +cabby's +cabdriver +cabdrivers +cabdriver's +cabin +cabins +cabin's +cabinet +cabinets +cabinet's +cabinetmaker +cabinetmakers +cabinetmaker's +cabinetmaking +cabinetmaking's +cabinetry +cabinetry's +cabinetwork +cabinetwork's +cable +cabling +cabled +cables +cable's +cablecast +cablecasting +cablecasts +cablecast's +cablegram +cablegrams +cablegram's +cabochon +cabochons +cabochon's +caboodle +caboodle's +caboose +cabooses +caboose's +cabriolet +cabriolets +cabriolet's +cabstand +cabstands +cabstand's +cacao +cacaos +cacao's +cache +caching +cached +caches +cache's +cachepot +cachepots +cachepot's +cachet +cachets +cachet's +cackle +cackling +cackled +cackler +cacklers +cackles +cackle's +cackler +cackler's +cacophonous +cacophony +cacophonies +cacophony's +cacti +cactus +cactus's +cad +cads +cad's +cadaver +cadavers +cadaver's +cadaverous +caddie +caddied +caddies +caddie's +caddish +caddishly +caddishness +caddishness +caddishness's +caddying +cadence +cadenced +cadences +cadence's +cadenza +cadenzas +cadenza's +cadet +cadets +cadet's +cadge +cadging +cadged +cadger +cadgers +cadges +cadger +cadger's +cadmium +cadmium's +cadre +cadres +cadre's +caducei +caduceus +caduceus's +caesura +caesuras +caesura's +cafe +cafes +cafe's +cafeteria +cafeterias +cafeteria's +cafetiere +cafetieres +caff +caffs +decaffs +decaff +caffeinated +caffeine +caffeine's +caftan +caftans +caftan's +cage +caging +caged +cages +cage's +cagey +cagier +cagiest +cagily +caginess +caginess's +cagoule +cagoules +cahoot +cahoots +cahoot's +caiman +caimans +caiman's +cairn +cairns +cairn's +caisson +caissons +caisson's +caitiff +caitiffs +caitiff's +cajole +cajoling +cajoled +cajoler +cajolers +cajoles +cajolement +cajolement +cajolement's +cajoler +cajoler's +cajolery +cajolery's +cake +caking +caked +cakes +cake's +cakewalk +cakewalks +cakewalk's +cal +calabash +calabashes +calabash's +calaboose +calabooses +calaboose's +calamari +calamaris +calamari's +calamine +calamine's +calamitous +calamitously +calamity +calamities +calamity's +calcareous +calciferous +calcification +calcification's +calcify +calcification +calcifying +calcified +calcifies +calcimine +calcimining +calcimined +calcimines +calcimine's +calcine +calcining +calcined +calcines +calcite +calcite's +calcium +calcium's +calculable +incalculable +calculate +calculative +calculation +calculations +calculating +calculated +calculates +recalculation +recalculations +recalculating +recalculated +recalculates +recalculate +calculated +calculatedly +calculating +calculatingly +calculation +calculation's +recalculation's +recalculation +calculator +calculators +calculator's +calculi +calculus +calculus's +caldera +calderas +caldera's +calendar +calendaring +calendared +calendars +calendar's +calender's +calf +calf's +calfskin +calfskin's +caliber +calibers +caliber's +calibrate +calibration +calibrations +calibrating +calibrated +calibrates +calibration +calibration's +calibrator +calibrators +calibrator's +calico +calico's +calicoes +californium +californium's +caliper +calipering +calipered +calipers +caliper's +caliph +caliph's +caliphate +caliphates +caliphate's +caliphs +calisthenic +calisthenics +calisthenics +calisthenics's +calk +calking +calked +calks +calk's +call +calling +called +calls +call's +recalling +recalled +recalls +recall's +recall +calla +callas +calla's +callable +callback +callbacks +callback's +called +uncalled +caller +callers +caller's +calligrapher +calligraphers +calligrapher's +calligraphic +calligraphist +calligraphists +calligraphist's +calligraphy +calligraphy's +calling +callings +calling's +calliope +calliopes +calliope's +callosity +callosities +callosity's +callous +callously +callousing +calloused +callouses +callousness +callousness +callousness's +callow +callowest +callower +callowness +callowness +callowness's +callus +callusing +callused +calluses +callus's +calm +calmly +calming +calmed +calmest +calmer +calms +calmness +calm's +calmness +calmness's +caloric +calorie +calories +calorie's +calorific +calumet +calumets +calumet's +calumniate +calumniation +calumniating +calumniated +calumniates +calumniation +calumniation's +calumniator +calumniators +calumniator's +calumnious +calumny +calumnies +calumny's +calve +calving +calved +calves +calypso +calypsos +calypso's +calyx +calyxes +calyx's +cam +cams +cam's +camaraderie +camaraderie's +camber +cambering +cambered +cambers +camber's +cambial +cambium +cambiums +cambium's +cambric +cambric's +camcorder +camcorders +camcorder's +came +camel +camels +camel's +camelhair +camellia +camellias +camellia's +cameo +cameos +cameo's +camera +cameras +camera's +cameraman +cameraman's +cameramen +camerapeople +cameraperson +camerawoman +camerawoman's +camerawomen +camerawork +camiknickers +camisole +camisoles +camisole's +camouflage +camouflaging +camouflaged +camouflager +camouflagers +camouflages +camouflage's +camouflager +camouflager's +camp's +camp +camping +camped +camps +decamping +decamped +decamps +decamp +campaign +campaigning +campaigned +campaigner +campaigners +campaigns +campaign's +campaigner +campaigner's +campanile +campaniles +campanile's +campanologist +campanologists +campanologist's +campanology +campanology's +camper +campers +camper's +campfire +campfires +campfire's +campground +campgrounds +campground's +camphor +camphor's +camping +camping's +campsite +campsites +campsite's +campus +campuses +campus's +campy +campiest +campier +camshaft +camshafts +camshaft's +can't +can +caning +caned +caner +caners +cans +can's +canal +canals +canal's +canalization +canalization's +canalize +canalizing +canalized +canalizes +canape +canapes +canape's +canard +canards +canard's +canary +canaries +canary's +canasta +canasta's +cancan +cancans +cancan's +cancel +canceling +canceled +canceler +cancelers +cancels +canceler +canceler's +cancellation +cancellations +cancellation's +cancelous +cancer +cancers +cancer's +cancerous +candelabra +candelabras +candelabra's +candelabrum +candelabrum's +candid +candidly +candidness +candida +candidacy +candidacies +candidacy's +candidate +candidates +candidate's +candidature +candidatures +candidature's +candidness +candidness's +candle +candling +candled +candler +candlers +candles +candle's +candlelight +candlelight's +candlelit +candlepower +candlepower's +candler +candler's +candlestick +candlesticks +candlestick's +candlewick +candlewicks +candlewick's +candor +candor's +candy +candying +candied +candies +candy's +candyfloss +cane +canes +cane's +canebrake +canebrakes +canebrake's +caner +caner's +canine +canines +canine's +canister +canisters +canister's +canker +cankering +cankered +cankers +canker's +cankerous +cannabis +cannabises +cannabis's +canned +cannelloni +cannelloni's +cannery +canneries +cannery's +cannibal +cannibals +cannibal's +cannibalism +cannibalism's +cannibalistic +cannibalization +cannibalization's +cannibalize +cannibalizing +cannibalized +cannibalizes +cannily +uncannily +canniness +canniness's +canning +cannon +cannoning +cannoned +cannons +cannon's +cannonade +cannonading +cannonaded +cannonades +cannonade's +cannonball +cannonballs +cannonball's +cannot +canny +canniest +cannier +uncannier +uncanny +canoe +canoed +canoes +canoe's +canoeing +canoeist +canoeists +canoeist's +canola +canola's +canon +canons +canon's +canonical +canonically +canonization +canonizations +canonization's +canonize +canonizing +canonized +canonizes +canoodle +canoodling +canoodled +canoodles +canopy +canopying +canopied +canopies +canopy's +canst +cant's +cant +canting +canted +canter +canters +cants +decanting +decanted +decanter +decanters +decants +decant +cantabile +cantaloupe +cantaloupes +cantaloupe's +cantankerous +cantankerously +cantankerousness +cantankerousness +cantankerousness's +cantata +cantatas +cantata's +canteen +canteens +canteen's +canter +canter's +decanter's +decanter +cantered +cantering +canticle +canticles +canticle's +cantilever +cantilevering +cantilevered +cantilevers +cantilever's +canto +cantos +canto's +canton +cantons +canton's +cantonment +cantonal +cantonment +cantonments +cantonment's +cantor +cantors +cantor's +canvas +canvasing +canvased +canvases +canvas's +canvasback +canvasbacks +canvasback's +canvass +canvassing +canvassed +canvasser +canvassers +canvasses +canvass's +canvasser +canvasser's +canyon +canyoning +canyons +canyon's +cap +caped +caper +capers +caps +cap's +capable +capabilities +capability +capability's +incapability's +incapability +capable +incapable +capably +incapably +capacious +capaciously +capaciousness +capaciousness +capaciousness's +capacitance +capacitance's +capacities +capacitor +capacitors +capacitor's +capacity +capacity's +incapacity's +incapacity +caparison +caparisoning +caparisoned +caparisons +caparison's +cape +capes +cape's +caper +capering +capered +caper's +capeskin +capeskin's +capillarity +capillarity's +capillary +capillaries +capillary's +capital +capitally +capitals +capital's +capitalism +capitalism's +capitalist +capitalists +capitalist's +capitalistic +capitalistically +capitalization +capitalization's +capitalize +capitalizing +capitalized +capitalizes +recapitalizing +recapitalized +recapitalizes +recapitalize +capitation +capitations +capitation's +decapitations +decapitation's +decapitation +capitol +capitols +capitol's +capitulate +capitulation +capitulations +capitulating +capitulated +capitulates +recapitulation +recapitulations +recapitulating +recapitulated +recapitulates +recapitulate +capitulation +capitulation's +recapitulation's +recapitulation +caplet +caplets +caplet's +capo +capos +capo's +capon +capons +capon's +capped +recapped +uncapped +capping +recapping +uncapping +cappuccino +cappuccinos +cappuccino's +caprice +caprices +caprice's +capricious +capriciously +capriciousness +capriciousness +capriciousness's +capsicum +capsicums +capsicum's +capsize +capsizing +capsized +capsizes +capstan +capstans +capstan's +capstone +capstones +capstone's +capsular +capsule +capsuling +capsuled +capsules +capsule's +capsulize +capsulizing +capsulized +capsulizes +capt +captain +captaining +captained +captains +captain's +captaincy +captaincies +captaincy's +caption +captioning +captioned +captions +caption's +captious +captiously +captiousness +captiousness +captiousness's +captivate +captivation +captivating +captivated +captivates +captivation +captivation's +captivator +captivators +captivator's +captive +captives +captive's +captivity +captivities +captivity's +captor +captors +captor's +capture +capturing +captured +captures +capture's +recapturing +recaptured +recaptures +recapture's +recapture +car +caring +cared +carer +carers +cars +car's +carafe +carafes +carafe's +caramel +caramels +caramel's +caramelize +caramelizing +caramelized +caramelizes +carapace +carapaces +carapace's +carat +carats +carat's +caravan +caravans +caravan's +caravansary +caravansaries +caravansary's +caravel +caravels +caravel's +caraway +caraways +caraway's +carbide +carbides +carbide's +carbine +carbines +carbine's +carbohydrate +carbohydrates +carbohydrate's +carbolic +carbon +carbons +carbon's +carbonaceous +carbonate +carbonation +carbonating +carbonated +carbonates +carbonate's +carbonation +carbonation's +carboniferous +carbonize +carbonizing +carbonized +carbonizes +carborundum +carborundum's +carboy +carboys +carboy's +carbs +carbuncle +carbuncles +carbuncle's +carbuncular +carburetor +carburetors +carburetor's +carcass +carcasses +carcass's +carcinogen +carcinogens +carcinogen's +carcinogenic +carcinogenics +carcinogenic's +carcinogenicity +carcinogenicity's +carcinoma +carcinomas +carcinoma's +card +carding +carded +cards +card's +discarding +discarded +discards +discard's +discard +cardamom +cardamoms +cardamom's +cardamon +cardamons +cardboard +cardboard's +carder +carders +carder's +cardholder +cardholders +cardiac +cardie +cardies +cardigan +cardigans +cardigan's +cardinal +cardinally +cardinals +cardinal's +cardio +cardiogram +cardiograms +cardiogram's +cardiograph +cardiograph's +cardiographs +cardiologist +cardiologists +cardiologist's +cardiology +cardiology's +cardiomyopathy +cardiopulmonary +cardiovascular +cardsharp +cardsharper +cardsharpers +cardsharps +cardsharp's +cardsharper +cardsharper's +care +cares +care's +careen +careening +careened +careens +career +careering +careered +careers +career's +careerism +careerist +careerists +careerist's +carefree +careful +carefully +carefulness +carefuller +carefullest +carefulness +carefulness's +caregiver +caregivers +caregiver's +careless +carelessly +carelessness +carelessness +carelessness's +carer +carer's +caress +caressing +caressed +caresses +caress's +caret +carets +caret's +caretaker +caretakers +caretaker's +careworn +carfare +carfare's +cargo +cargo's +cargoes +carhop +carhops +carhop's +caribou +caribous +caribou's +caricature +caricaturing +caricatured +caricatures +caricature's +caricaturist +caricaturists +caricaturist's +caries +caries's +carillon +carillons +carillon's +caring +caring's +carious +carjack +carjacking +carjackings +carjacked +carjacker +carjackers +carjacks +carjacker +carjacker's +carjacking +carjacking's +carload +carloads +carload's +carmaker +carmakers +carmine +carmines +carmine's +carnage +carnage's +carnal +carnally +carnality +carnality's +carnation +carnations +carnation's +incarnations +incarnation's +incarnation +carnelian +carnelians +carnelian's +carnival +carnivals +carnival's +carnivora +carnivore +carnivores +carnivore's +carnivorous +carnivorously +carnivorousness +carnivorousness +carnivorousness's +carny +carnies +carny's +carob +carobs +carob's +carol +caroling +caroled +caroler +carolers +carols +carol's +caroler +caroler's +carom +caroming +caromed +caroms +carom's +carotene +carotene's +carotid +carotids +carotid's +carousal +carousals +carousal's +carouse +carousing +caroused +carouser +carousers +carouses +carouse's +carousel +carousels +carousel's +carouser +carouser's +carp +carping +carped +carper +carpers +carps +carp's +carpal +carpals +carpal's +carpel +carpels +carpel's +carpenter +carpentering +carpentered +carpenters +carpenter's +carpentry +carpentry's +carper +carper's +carpet +carpeting +carpeted +carpets +carpet's +carpetbag +carpetbags +carpetbag's +carpetbagged +carpetbagger +carpetbaggers +carpetbagger's +carpetbagging +carpeting +carpeting's +carpi +carpool +carpooling +carpooled +carpools +carpool's +carport +carports +carport's +carpus +carpus's +carrel +carrels +carrel's +carriage +carriages +carriage's +carriageway +carriageways +carrier +carrier's +carrion +carrion's +carrot +carrots +carrot's +carroty +carry +carrying +carried +carrier +carriers +carries +carry's +carryall +carryalls +carryall's +carrycot +carrycots +carryout +carryover +carryovers +carryover's +carsick +carsickness +carsickness +carsickness's +cart +carting +carted +carter +carters +carts +cart's +cartage +cartage's +cartel +cartels +cartel's +carter +carter's +carthorse +carthorses +carthorse's +cartilage +cartilages +cartilage's +cartilaginous +cartload +cartloads +cartload's +cartographer +cartographers +cartographer's +cartographic +cartography +cartography's +carton +cartons +carton's +cartoon +cartooning +cartooned +cartoons +cartoon's +cartoonist +cartoonists +cartoonist's +cartridge +cartridges +cartridge's +cartwheel +cartwheeling +cartwheeled +cartwheels +cartwheel's +carve +carving +carvings +carved +carver +carvers +carves +carver +carver's +carvery +carveries +carving +carving's +caryatid +caryatids +caryatid's +casaba +casabas +casaba's +cascade +cascading +cascaded +cascades +cascade's +cascara +cascaras +cascara's +case +casing +casings +cased +cases +case's +casement +casebook +casebooks +cased +uncased +caseharden +casehardening +casehardened +casehardens +casein +casein's +caseload +caseloads +caseload's +casement +casements +casement's +casework +caseworker +caseworkers +casework's +caseworker +caseworker's +cash +cashing +cashed +cashes +cash's +cashback +cashback's +cashbook +cashbooks +cashbook's +cashew +cashews +cashew's +cashier +cashiering +cashiered +cashiers +cashier's +cashless +cashmere +cashmere's +casing +casing's +casino +casinos +casino's +cask +casks +cask's +casket +caskets +casket's +cassava +cassavas +cassava's +casserole +casseroling +casseroled +casseroles +casserole's +cassette +cassettes +cassette's +cassia +cassias +cassia's +cassock +cassocks +cassock's +cassowary +cassowaries +cassowary's +cast +casting +casts +cast's +recasting +recasts +recast's +recast +castanet +castanets +castanet's +castaway +castaways +castaway's +caste +castings +caster +casters +castes +caste's +castellated +caster +caster's +castigate +castigation +castigating +castigated +castigates +castigation +castigation's +castigator +castigators +castigator's +casting +casting's +recasting's +recasting +castle +castling +castled +castles +castle's +castoff +castoffs +castoff's +castor +castors +castor's +castrate +castration +castrations +castrating +castrated +castrates +castration +castration's +casual +casually +casuals +casualness +casual's +casualness +casualness's +casualty +casualties +casualty's +casuist +casuists +casuist's +casuistic +casuistry +casuistry's +cat +cats +cat's +cataclysm +cataclysms +cataclysm's +cataclysmal +cataclysmic +catacomb +catacombs +catacomb's +catafalque +catafalques +catafalque's +catalepsy +catalepsy's +cataleptic +cataleptics +cataleptic's +catalog +cataloging +cataloged +cataloger +catalogers +catalogs +catalog's +cataloger +cataloger's +catalpa +catalpas +catalpa's +catalyses +catalysis +catalysis's +catalyst +catalysts +catalyst's +catalytic +catalytic's +catalyze +catalyzing +catalyzed +catalyzes +catamaran +catamarans +catamaran's +catapult +catapulting +catapulted +catapults +catapult's +cataract +cataracts +cataract's +catarrh +catarrh's +catastrophe +catastrophes +catastrophe's +catastrophic +catastrophically +catatonia +catatonia's +catatonic +catatonics +catatonic's +catbird +catbirds +catbird's +catboat +catboats +catboat's +catcall +catcalling +catcalled +catcalls +catcall's +catch +catching +catchings +catcher +catchers +catches +catch's +catchment +catchall +catchalls +catchall's +catcher +catcher's +catchment +catchments +catchment's +catchpenny +catchphrase +catchphrases +catchphrase's +catchword +catchwords +catchword's +catchy +catchiest +catchier +catechism +catechisms +catechism's +catechist +catechists +catechist's +catechize +catechizing +catechized +catechizes +categorical +categorically +categorization +categorizations +categorization's +categorize +categorizing +categorized +categorizes +category +categories +category's +cater +catering +caterings +catered +caterer +caterers +caters +catercorner +caterer +caterer's +caterpillar +caterpillars +caterpillar's +caterwaul +caterwauling +caterwauled +caterwauls +caterwaul's +catfish +catfishes +catfish's +catgut +catgut's +catharses +catharsis +catharsis's +cathartic +cathartics +cathartic's +cathedral +cathedrals +cathedral's +catheter +catheters +catheter's +catheterize +catheterizing +catheterized +catheterizes +cathode +cathodes +cathode's +cathodic +catholic +catholicity +catholicity's +cation +cations +cation's +catkin +catkins +catkin's +catlike +catnap +catnaps +catnap's +catnapped +catnapping +catnip +catnip's +catsuit +catsuits +cattail +cattails +cattail's +catted +cattery +catteries +cattily +cattiness +cattiness's +catting +cattle +cattle's +cattleman +cattleman's +cattlemen +catty +cattiest +cattier +cattiness +catwalk +catwalks +catwalk's +caucus +caucusing +caucused +caucuses +caucus's +caudal +caudally +caught +uncaught +cauldron +cauldrons +cauldron's +cauliflower +cauliflowers +cauliflower's +caulk +caulking +caulked +caulker +caulkers +caulks +caulk's +caulker +caulker's +causal +causally +causality +causalities +causality's +causation +causation's +causative +cause +causing +caused +causer +causers +causes +cause's +causeless +causer +causer's +causerie +causeries +causerie's +causeway +causeways +causeway's +caustic +caustics +caustic's +caustically +causticity +causticity's +cauterization +cauterization's +cauterize +cauterizing +cauterized +cauterizes +caution +cautioning +cautioned +cautions +caution's +cautionary +cautious +cautiously +incautiously +incautious +cautiousness +cautiousness's +cavalcade +cavalcades +cavalcade's +cavalier +cavalierly +cavaliers +cavalier's +cavalry +cavalries +cavalry's +cavalryman +cavalryman's +cavalrymen +cave +caving +caved +caver +cavers +caves +cave's +caveat +caveats +caveat's +caveman +caveman's +cavemen +cavern +caverns +cavern's +cavernous +cavernously +caviar +caviar's +cavil +caviling +cavilings +caviled +caviler +cavilers +cavils +cavil's +caviler +caviler's +caving +caving's +cavitation +cavity +cavities +cavity's +concavities +concavity's +concavity +cavort +cavorting +cavorted +cavorts +caw +cawing +cawed +caws +caw's +cay +cays +cay's +decays +decay's +decay +cayenne +cayenne's +cayuse +cayuses +cayuse's +cc +cease +ceasing +ceased +ceases +cease's +deceasing +deceased +deceases +decease's +decease +ceasefire +ceasefires +ceasefire's +ceaseless +ceaselessly +ceaselessness +ceaselessness +ceaselessness's +ceca +cecal +cecum +cecum's +cedar +cedars +cedar's +cede +ceding +ceded +cedes +receding +conceding +receded +conceded +recedes +concedes +recede +concede +ceder +ceders +ceder's +cedilla +cedillas +cedilla's +ceilidh +ceilidhs +ceiling +ceilings +ceiling's +celandine +celandine's +celeb +celebs +celebrant +celebrants +celebrant's +celebrate +celebration +celebrations +celebrating +celebrated +celebrates +celebration +celebration's +celebrator +celebrators +celebrator's +celebratory +celebrity +celebrities +celebrity's +celeriac +celerity +celerity's +celery +celery's +celesta +celestas +celesta's +celestial +celestially +celibacy +celibacy's +celibate +celibates +celibate's +cell +celled +cells +cell's +cellar +cellars +cellar's +cellist +cellists +cellist's +cellmate +cellmates +cellmate's +cello +cellos +cello's +cellophane +cellophane's +cellphone +cellphones +cellphone's +cellular +cellulars +cellular's +cellulite +cellulite's +cellulitis +celluloid +celluloid's +cellulose +cellulose's +cement +cementing +cemented +cementer +cementers +cements +cement's +cementer +cementer's +cementum +cementum's +cemetery +cemeteries +cemetery's +cenobite +cenobites +cenobite's +cenobitic +cenotaph +cenotaph's +cenotaphs +censer +censers +censer's +censor +censoring +censored +censors +censor's +censored +uncensored +censorial +censorious +censoriously +censoriousness +censoriousness +censoriousness's +censorship +censorship's +censure +censuring +censured +censurer +censurers +censures +censure's +censurable +censurer +censurer's +census +censusing +censused +censuses +census's +cent +center +centers +cents +cent's +centaur +centaurs +centaur's +centavo +centavos +centavo's +centenarian +centenarians +centenarian's +centenary +centenaries +centenary's +centennial +centennially +centennials +centennial's +center +centering +centered +center's +centerboard +centerboards +centerboard's +centerfold +centerfolds +centerfold's +centerpiece +centerpieces +centerpiece's +centigrade +centigram +centigrams +centigram's +centiliter +centiliters +centiliter's +centime +centimes +centime's +centimeter +centimeters +centimeter's +centipede +centipedes +centipede's +central +centrally +centrals +central's +centralism +centralist +centrality +centrality's +centralization +centralization's +decentralization's +decentralization +centralize +centralizing +centralized +centralizes +decentralizing +decentralized +decentralizes +decentralize +centralizer +centralizers +centralizer's +centrifugal +centrifugally +centrifuge +centrifuging +centrifuged +centrifuges +centrifuge's +centripetal +centripetally +centrism +centrism's +centrist +centrists +centrist's +centurion +centurions +centurion's +century +centuries +century's +cephalic +ceramic +ceramics +ceramic's +ceramicist +ceramicists +ceramicist's +ceramics +ceramics's +ceramist +ceramists +ceramist's +cereal +cereals +cereal's +cerebellar +cerebellum +cerebellums +cerebellum's +cerebra +cerebral +cerebrate +cerebration +cerebrating +cerebrated +cerebrates +cerebration +cerebration's +cerebrovascular +cerebrum +cerebrums +cerebrum's +cerement +cerements +cerement's +ceremonial +ceremonially +ceremonials +ceremonial's +ceremonious +ceremoniously +unceremoniously +unceremonious +ceremoniousness +ceremoniousness's +ceremony +ceremonies +ceremony's +cerise +cerise's +cerium +cerium's +cermet +cermet's +cert +certs +certain +certainly +uncertainly +uncertain +certainty +certainties +certainty's +uncertainties +uncertainty's +uncertainty +certifiable +certifiably +certificate +certification +certifications +certificating +certificated +certificates +certificate's +certification +certification's +certify +certifying +certified +certifies +certitude +certitude's +incertitude's +incertitude +certitudes +cerulean +cerulean's +cervical +cervices +cervix +cervix's +cesarean +cesareans +cesarean's +cesium +cesium's +cessation +cessations +cessation's +cession +cessions +cession's +recessions +concessions +processions +recession's +concession's +procession's +recession +concession +procession +cesspit +cesspits +cesspool +cesspools +cesspool's +cetacean +cetaceans +cetacean's +ceteris +cf +cg +ch +chive +chest +inch +conch +chad +chads +chafe +chafing +chafed +chafes +chaff +chaffing +chaffed +chaffs +chaff's +chaffinch +chaffinches +chaffinch's +chagrin +chagrining +chagrined +chagrins +chagrin's +chain's +chain +chaining +chained +chains +unchaining +unchained +unchains +unchain +chainsaw +chainsawing +chainsawed +chainsaws +chainsaw's +chair +chairing +chaired +chairs +chair's +chairlift +chairlifts +chairlift's +chairman +chairman's +chairmanship +chairmanships +chairmanship's +chairmen +chairperson +chairpersons +chairperson's +chairwoman +chairwoman's +chairwomen +chaise +chaises +chaise's +chalcedony +chalcedony's +chalet +chalets +chalet's +chalice +chalices +chalice's +chalk +chalking +chalked +chalks +chalk's +chalkboard +chalkboards +chalkboard's +chalkiness +chalkiness's +chalky +chalkiest +chalkier +chalkiness +challenge +challenging +challenged +challenger +challengers +challenges +challenge's +challenged +unchallenged +challenger +challenger's +challis +challis's +chamber +chambered +chambers +chamber's +chamberlain +chamberlains +chamberlain's +chambermaid +chambermaids +chambermaid's +chambray +chambray's +chameleon +chameleons +chameleon's +chamois +chamois's +chamomile +chamomiles +chamomile's +champ +champing +champed +champers +champs +champ's +champagne +champagnes +champagne's +champion +championing +championed +champions +champion's +championship +championships +championship's +chance +chancing +chanced +chances +chance's +chancel +chancels +chancel's +chancellery +chancelleries +chancellery's +chancellor +chancellors +chancellor's +chancellorship +chancellorship's +chancery +chanceries +chancery's +chanciness +chanciness's +chancre +chancres +chancre's +chancy +chanciest +chancier +chanciness +chandelier +chandeliers +chandelier's +chandler +chandlers +chandler's +change +changing +changed +changer +changers +changes +change's +changeability +changeability's +changeable +changeableness +changeableness +changeableness's +changeably +changed +unchanged +changeless +changelessly +changeling +changelings +changeling's +changeover +changeovers +changeover's +changer +changer's +changing +unchanging +channel +channeling +channeled +channels +channel's +channelization +channelization's +channelize +channelizing +channelized +channelizes +chanson +chansons +chanson's +chant +chanting +chanted +chanter +chanters +chants +chant's +chanter +chanter's +chanteuse +chanteuses +chanteuse's +chantey +chanteys +chantey's +chanticleer +chanticleers +chanticleer's +chaos +chaos's +chaotic +chaotically +chap +chaps +chap's +chaparral +chaparrals +chaparral's +chapati +chapatis +chapatti +chapattis +chapbook +chapbooks +chapbook's +chapeau +chapeaus +chapeau's +chapel +chapels +chapel's +chaperon +chaperoning +chaperoned +chaperons +chaperon's +chaperonage +chaperonage's +chaperoned +unchaperoned +chaplain +chaplains +chaplain's +chaplaincy +chaplaincies +chaplaincy's +chaplet +chaplets +chaplet's +chapped +chapping +chappy +chappies +chapter +chapters +chapter's +char +chars +char's +charabanc +charabancs +charabanc's +character +characters +character's +characterful +characteristic +characteristics +characteristic's +characteristically +uncharacteristically +characterization +characterizations +characterization's +characterize +characterizing +characterized +characterizes +characterless +charade +charades +charade's +charbroil +charbroiling +charbroiled +charbroils +charcoal +charcoals +charcoal's +chard +chard's +chardonnay +chardonnays +chardonnay's +charge +charging +charged +charges +charge's +recharging +discharging +recharged +discharged +recharges +discharges +recharge's +discharge's +recharge +discharge +chargeable +rechargeable +charged +uncharged +charger +chargers +charger's +charily +chariness +chariness's +chariot +chariots +chariot's +charioteer +charioteers +charioteer's +charisma +charisma's +charismatic +charismatics +charismatic's +charitable +charitableness +charitableness +charitableness's +charitably +uncharitably +charity +charities +charity's +charlady +charladies +charlatan +charlatans +charlatan's +charlatanism +charlatanism's +charlatanry +charlatanry's +charlie +charlies +charm +charming +charmed +charmer +charmers +charms +charm's +charmer +charmer's +charming +charmingly +charmless +charred +charring +chart +charting +charted +charts +chart's +charted +uncharted +charter's +charter +chartering +chartered +charters +rechartering +rechartered +recharters +recharter +charterer +charterers +charterer's +chartreuse +chartreuse's +charwoman +charwoman's +charwomen +chary +chariest +charier +chariness +chase +chasing +chased +chaser +chasers +chases +chase's +chaser +chaser's +chasm +chasms +chasm's +chassis +chassis's +chaste +chastely +chastest +chaster +chasteness +chasten +chastening +chastened +chastens +chasteness +chasteness's +chastise +chastising +chastised +chastiser +chastisers +chastises +chastisement +chastisement +chastisements +chastisement's +chastiser +chastiser's +chastity +chastity's +chasuble +chasubles +chasuble's +chat +chats +chat's +chateau +chateaus +chateau's +chateaux +chatelaine +chatelaines +chatelaine's +chatline +chatlines +chatroom +chatroom's +chatted +chattel +chattels +chattel's +chatter +chattering +chattered +chatterer +chatterers +chatters +chatter's +chatterbox +chatterboxes +chatterbox's +chatterer +chatterer's +chattily +chattiness +chattiness's +chatting +chatty +chattiest +chattier +chattiness +chauffeur +chauffeuring +chauffeured +chauffeurs +chauffeur's +chauvinism +chauvinism's +chauvinist +chauvinists +chauvinist's +chauvinistic +chauvinistically +cheap +cheapen +cheapens +cheaply +cheapest +cheaper +cheapness +cheapen +cheapening +cheapened +cheapness +cheapness's +cheapo +cheapskate +cheapskates +cheapskate's +cheat +cheating +cheated +cheater +cheaters +cheats +cheat's +cheater +cheater's +check +checking +checked +checks +check's +rechecking +rechecked +rechecks +recheck's +recheck +checkbook +checkbooks +checkbook's +checkbox +checked +unchecked +checker +checkering +checkered +checkers +checker's +checkerboard +checkerboards +checkerboard's +checkers +checkers's +checklist +checklists +checklist's +checkmate +checkmating +checkmated +checkmates +checkmate's +checkoff +checkoffs +checkoff's +checkout +checkouts +checkout's +checkpoint +checkpoints +checkpoint's +checkroom +checkrooms +checkroom's +checksum +checkup +checkups +checkup's +cheddar +cheddar's +cheek +cheeking +cheeked +cheeks +cheek's +cheekbone +cheekbones +cheekbone's +cheekily +cheekiness +cheekiness's +cheeky +cheekiest +cheekier +cheekiness +cheep +cheeping +cheeped +cheeps +cheep's +cheer +cheering +cheered +cheerer +cheerers +cheers +cheer's +cheerer +cheerer's +cheerful +cheerfully +cheerfulness +cheerfuller +cheerfullest +cheerfulness +cheerfulness's +cheerily +cheeriness +cheeriness's +cheerio +cheerios +cheerio's +cheerleader +cheerleaders +cheerleader's +cheerless +cheerlessly +cheerlessness +cheerlessness +cheerlessness's +cheery +cheeriest +cheerier +cheeriness +cheese +cheesing +cheesed +cheeses +cheese's +cheeseboard +cheeseboards +cheeseburger +cheeseburgers +cheeseburger's +cheesecake +cheesecakes +cheesecake's +cheesecloth +cheesecloth's +cheeseparing +cheeseparing's +cheesiness +cheesiness's +cheesy +cheesiest +cheesier +cheesiness +cheetah +cheetah's +cheetahs +chef +chefs +chef's +chem +chemical +chemically +chemicals +chemical's +chemise +chemises +chemise's +chemist +chemists +chemist's +chemistry +chemistry's +chemo +chemo's +chemotherapeutic +chemotherapy +chemotherapy's +chemurgy +chemurgy's +chenille +chenille's +cherish +cherishing +cherished +cherishes +cheroot +cheroots +cheroot's +cherry +cherries +cherry's +chert +chert's +cherub +cherubs +cherub's +cherubic +cherubim +chervil +chervil's +chess +chess's +chessboard +chessboards +chessboard's +chessman +chessman's +chessmen +chest +chested +chests +chest's +chesterfield +chesterfields +chesterfield's +chestful +chestfuls +chestful's +chestnut +chestnuts +chestnut's +chesty +chestiest +chestier +chevalier +chevaliers +chevalier's +cheviot +cheviot's +chevron +chevrons +chevron's +chew +chewing +chewed +chewer +chewers +chews +chew's +chewer +chewer's +chewiness +chewiness's +chewy +chewiest +chewier +chewiness +chg +chge +chi +chis +chi's +chiaroscuro +chiaroscuro's +chic +chicest +chicer +chicness +chic's +chicane +chicanes +chicane's +chicanery +chicaneries +chicanery's +chichi +chichis +chichi's +chick +chicken +chickens +chicks +chick's +chickadee +chickadees +chickadee's +chicken +chickening +chickened +chicken's +chickenfeed +chickenfeed's +chickenhearted +chickenpox +chickenpox's +chickenshit +chickenshits +chickpea +chickpeas +chickpea's +chickweed +chickweed's +chicle +chicle's +chicness +chicness's +chicory +chicories +chicory's +chide +chiding +chided +chides +chiding +chidingly +chief +chiefly +chiefest +chiefer +chiefs +chief's +chiefdom +chiefdom's +chieftain +chieftains +chieftain's +chieftainship +chieftainships +chieftainship's +chiffon +chiffon's +chiffonier +chiffoniers +chiffonier's +chigger +chiggers +chigger's +chignon +chignons +chignon's +chihuahua +chihuahuas +chihuahua's +chilblain +chilblains +chilblain's +child +child's +childbearing +childbearing's +childbirth +childbirth's +childbirths +childcare +childcare's +childhood +childhoods +childhood's +childish +childishly +childishness +childishness +childishness's +childless +childlessness +childlessness +childlessness's +childlike +childminder +childminders +childminding +childproof +childproofing +childproofed +childproofs +children +children's +chili +chili's +chilies +chill +chilling +chillings +chilled +chillest +chiller +chillers +chills +chillness +chill's +chiller +chiller's +chilliness +chilliness's +chilling +chillingly +chillness +chillness's +chilly +chilliest +chillier +chilliness +chime +chiming +chimed +chimer +chimers +chimes +chime's +chimer +chimer's +chimera +chimeras +chimera's +chimeric +chimerical +chimney +chimneys +chimney's +chimp +chimps +chimp's +chimpanzee +chimpanzees +chimpanzee's +chin +chins +chin's +china +china's +chinaware +chinaware's +chinchilla +chinchillas +chinchilla's +chine +chines +chine's +chink +chinking +chinked +chinks +chink's +chinless +chinned +chinning +chino +chinos +chino's +chinstrap +chinstraps +chinstrap's +chintz +chintz's +chintzy +chintziest +chintzier +chinwag +chinwags +chip +chips +chip's +chipboard +chipmunk +chipmunks +chipmunk's +chipolata +chipolatas +chipped +chipper +chippers +chipper's +chippie +chipping +chippings +chippy +chippies +chirography +chirography's +chiropodist +chiropodists +chiropodist's +chiropody +chiropody's +chiropractic +chiropractics +chiropractic's +chiropractor +chiropractors +chiropractor's +chirp +chirping +chirped +chirps +chirp's +chirpily +chirpy +chirpiest +chirpier +chirpiness +chirrup +chirruping +chirruped +chirrups +chirrup's +chisel +chiseling +chiseled +chiseler +chiselers +chisels +chisel's +chiseler +chiseler's +chit +chits +chit's +chitchat +chitchats +chitchat's +chitchatted +chitchatting +chitin +chitin's +chitinous +chitosan +chitterlings +chitterlings's +chivalrous +chivalrously +chivalrousness +chivalrousness +chivalrousness's +chivalry +chivalry's +chive +chives +chive's +chivy +chivying +chivied +chivies +chlamydia +chlamydias +chlamydia's +chlamydiae +chloral +chloral's +chlordane +chlordane's +chloride +chlorides +chloride's +chlorinate +chlorination +chlorinating +chlorinated +chlorinates +chlorination +chlorination's +chlorine +chlorine's +chlorofluorocarbon +chlorofluorocarbons +chlorofluorocarbon's +chloroform +chloroforming +chloroformed +chloroforms +chloroform's +chlorophyll +chlorophyll's +chloroplast +chloroplasts +chloroplast's +chm +choc +chocs +chock +chocking +chocked +chocks +chock's +chockablock +chocoholic +chocoholics +chocoholic's +chocolate +chocolates +chocolate's +chocolaty +choice +choicest +choicer +choices +choice's +choir +choirs +choir's +choirboy +choirboys +choirboy's +choirmaster +choirmasters +choirmaster's +choke +choking +choked +choker +chokers +chokes +choke's +chokecherry +chokecherries +chokecherry's +choker +choker's +cholecystectomy +cholecystitis +choler +choler's +cholera +cholera's +choleric +cholesterol +cholesterol's +chomp +chomping +chomped +chomper +chompers +chomps +chomp's +choose +choosing +chooser +choosers +chooses +chooser +chooser's +choosiness +choosiness's +choosy +choosiest +choosier +choosiness +chop +chops +chop's +chophouse +chophouses +chophouse's +chopped +chopper +choppering +choppered +choppers +chopper's +choppily +choppiness +choppiness's +chopping +choppy +choppiest +choppier +choppiness +chopstick +chopsticks +chopstick's +choral +chorally +chorals +choral's +chorale +chorales +chorale's +chord +chords +chord's +chordal +chordate +chordates +chordate's +chore +chores +chore's +chorea +chorea's +choreograph +choreographing +choreographed +choreographer +choreographers +choreographer +choreographer's +choreographic +choreographically +choreographs +choreography +choreography's +chorister +choristers +chorister's +choroid +choroids +choroid's +chortle +chortling +chortled +chortler +chortlers +chortles +chortle's +chortler +chortler's +chorus +chorusing +chorused +choruses +chorus's +chose +chosen +chow +chowing +chowed +chows +chow's +chowder +chowders +chowder's +chrism +chrism's +christen +christening +christened +christens +rechristening +rechristened +rechristens +rechristen +christening +christenings +christening's +christian +unchristian +christology +chromatic +chromatically +chromatin +chromatin's +chromatography +chrome +chroming +chromed +chromes +chrome's +chromium +chromium's +chromosomal +chromosome +chromosomes +chromosome's +chronic +chronically +chronicle +chronicling +chronicled +chronicler +chroniclers +chronicles +chronicle's +chronicler +chronicler's +chronograph +chronograph's +chronographs +chronological +chronologically +chronologist +chronologists +chronologist's +chronology +chronologies +chronology's +chronometer +chronometers +chronometer's +chrysalis +chrysalises +chrysalis's +chrysanthemum +chrysanthemums +chrysanthemum's +chub +chubs +chub's +chubbiness +chubbiness's +chubby +chubbiest +chubbier +chubbiness +chuck +chucking +chucked +chucks +chuck's +chuckhole +chuckholes +chuckhole's +chuckle +chuckling +chuckled +chuckles +chuckle's +chuffed +chug +chugs +chug's +chugged +chugging +chukka +chukkas +chukka's +chum +chums +chum's +chummed +chummily +chumminess +chumminess's +chumming +chummy +chummiest +chummier +chumminess +chump +chumps +chump's +chunder +chundering +chundered +chunders +chunk +chunking +chunked +chunks +chunk's +chunkiness +chunkiness's +chunky +chunkiest +chunkier +chunkiness +chunter +chuntering +chuntered +chunters +church +churches +church's +churchgoer +churchgoers +churchgoer's +churchgoing +churchgoing's +churchman +churchman's +churchmen +churchwarden +churchwardens +churchwarden's +churchwoman +churchwomen +churchyard +churchyards +churchyard's +churl +churls +churl's +churlish +churlishly +churlishness +churlishness +churlishness's +churn +churning +churned +churner +churners +churns +churn's +churner +churner's +chute +chutes +chute's +chutney +chutneys +chutney's +chutzpah +chutzpah's +chyme +chyme's +ciabatta +ciabattas +ciabatta's +ciao +ciaos +cicada +cicadas +cicada's +cicatrices +cicatrix +cicatrix's +cicerone +cicerones +cicerone's +ciceroni +cider's +cider +ciders +cigar +cigars +cigar's +cigarette +cigarettes +cigarette's +cigarillo +cigarillos +cigarillo's +cilantro +cilantro's +cilia +cilium +cilium's +cinch +cinching +cinched +cinches +cinch's +cinchona +cinchonas +cinchona's +cincture +cinctures +cincture's +cinder +cindering +cindered +cinders +cinder's +cine +cinema +cinemas +cinema's +cinematic +cinematographer +cinematographers +cinematographer's +cinematographic +cinematography +cinematography's +cinnabar +cinnabar's +cinnamon +cinnamon's +cipher's +cipher +ciphering +ciphered +ciphers +deciphering +deciphered +deciphers +decipher +cir +circa +circadian +circle +circling +circled +circles +circle's +circlet +circlets +circlet's +circuit +circuiting +circuited +circuits +circuit's +circuital +circuitous +circuitously +circuitousness +circuitousness +circuitousness's +circuitry +circuitry's +circuity +circuity's +circular +circularly +circulars +circular's +circularity +circularity's +circularize +circularizing +circularized +circularizes +circulate +circulating +circulated +circulates +recirculating +recirculated +recirculates +recirculate +circulation +circulations +circulation's +circulatory +circumcise +circumcision +circumcisions +circumcising +circumcised +circumcises +circumcised +uncircumcised +circumcision +circumcision's +circumference +circumferences +circumference's +circumferential +circumflex +circumflexes +circumflex's +circumlocution +circumlocutions +circumlocution's +circumlocutory +circumnavigate +circumnavigation +circumnavigations +circumnavigating +circumnavigated +circumnavigates +circumnavigation +circumnavigation's +circumpolar +circumscribe +circumscribing +circumscribed +circumscribes +circumscription +circumscriptions +circumscription's +circumspect +circumspectly +circumspection +circumspection's +circumstance +circumstancing +circumstanced +circumstances +circumstance's +circumstantial +circumstantially +circumvent +circumventing +circumvented +circumvents +circumvention +circumvention's +circus +circuses +circus's +cirque +cirques +cirque's +cirrhosis +cirrhosis's +cirrhotic +cirrhotics +cirrhotic's +cirri +cirrus +cirrus's +cistern +cisterns +cistern's +cit +citadel +citadels +citadel's +citation +citations +citation's +recitations +recitation's +recitation +cite's +cite +citing +cited +cites +reciting +inciting +recited +incited +recites +incites +recite +incite +citified +citizen +citizens +citizen's +citizenry +citizenry's +citizenship +citizenship's +citric +citron +citrons +citron's +citronella +citronella's +citrus +citruses +citrus's +city +cities +city's +citywide +civet +civets +civet's +civic +civics +civically +civics +civics's +civil +civilly +uncivilly +uncivil +civilian +civilians +civilian's +civility +civilities +civility's +incivilities +incivility's +incivility +civilization +civilizations +civilization's +civilize +civilizing +civilized +civilizes +civilized +uncivilized +civvies +civvies's +ck +cl +clack +clacking +clacked +clacks +clack's +clad +unclad +cladding +cladding's +claim's +claim +claiming +claimed +claims +reclaiming +declaiming +disclaiming +proclaiming +reclaimed +declaimed +disclaimed +proclaimed +reclaims +declaims +disclaims +proclaims +reclaim +declaim +disclaim +proclaim +claimable +reclaimable +claimant +claimants +claimant's +claimed +unclaimed +claimer +claimers +claimer's +declaimers +disclaimers +declaimer's +disclaimer's +declaimer +disclaimer +clairvoyance +clairvoyance's +clairvoyant +clairvoyants +clairvoyant's +clam +clams +clam's +clambake +clambakes +clambake's +clamber +clambering +clambered +clamberer +clamberers +clambers +clamber's +clamberer +clamberer's +clammed +clammily +clamminess +clamminess's +clamming +clammy +clammiest +clammier +clamminess +clamor +clamoring +clamored +clamors +clamor's +clamorous +clamp +clamping +clamped +clamps +clamp's +clampdown +clampdowns +clampdown's +clan +clans +clan's +clandestine +clandestinely +clang +clanging +clanged +clanger +clangers +clangs +clang's +clangor +clangor's +clangorous +clangorously +clank +clanking +clanked +clanks +clank's +clannish +clannishness +clannishness +clannishness's +clansman +clansman's +clansmen +clanswoman +clanswomen +clap +claps +clap's +clapboard +clapboarding +clapboarded +clapboards +clapboard's +clapped +clapper +clappers +clapper's +clapperboard +clapperboards +clapping +clapping's +claptrap +claptrap's +claque +claques +claque's +claret +clarets +claret's +clarification +clarification's +clarify +clarification +clarifications +clarifying +clarified +clarifies +clarinet +clarinets +clarinet's +clarinetist +clarinetists +clarinetist's +clarion +clarioning +clarioned +clarions +clarion's +clarity +clarity's +clash +clashing +clashed +clashes +clash's +clasp's +clasp +clasping +clasped +clasps +unclasping +unclasped +unclasps +unclasp +class +classing +classed +classes +class's +classic +classics +classic's +classical +classically +classical's +classicism +classicism's +classicist +classicists +classicist's +classifiable +classification +classification's +reclassification's +declassification's +reclassification +declassification +classifications +classified's +classified +unclassified +classifieds +classifier +classifiers +classifier's +classify +classification +classifying +classified +classifies +reclassification +declassification +reclassifying +declassifying +reclassified +declassified +reclassifies +declassifies +reclassify +declassify +classiness +classiness's +classless +classlessness +classmate +classmates +classmate's +classroom +classrooms +classroom's +classwork +classwork's +classy +classiest +classier +classiness +clatter +clattering +clattered +clatters +clatter's +clausal +clause +clauses +clause's +claustrophobia +claustrophobia's +claustrophobic +clavichord +clavichords +clavichord's +clavicle +clavicles +clavicle's +clavier +claviers +clavier's +claw's +claw +clawing +clawed +claws +declawing +declawed +declaws +declaw +clay +clay's +clayey +clayier +clayiest +clean +cleanly +cleaning +cleanings +cleaned +cleanest +cleaner +cleaners +cleans +cleanness +cleanable +cleaner +cleaner's +cleaning +cleaning's +cleanliness +cleanliness's +uncleanliness's +uncleanliness +cleanly +cleanliest +cleanlier +cleanliness +uncleanlier +uncleanliness +uncleanly +cleanness +cleanness's +uncleanness's +uncleanness +cleanse +cleansing +cleansed +cleanser +cleansers +cleanses +cleanser +cleanser's +cleanup +cleanups +cleanup's +clear +clearly +clearing +clearings +cleared +clearest +clearer +clears +clearness +clear's +clearance +clearances +clearance's +clearheaded +clearing +clearing's +clearinghouse +clearinghouses +clearinghouse's +clearness +clearness's +clearway +clearways +cleat +cleats +cleat's +cleavage +cleavages +cleavage's +cleave +cleaving +cleaved +cleaver +cleavers +cleaves +cleaver +cleaver's +clef +clefs +clef's +cleft +clefts +cleft's +clematis +clematises +clematis's +clemency +clemency's +inclemency's +inclemency +clement +clemently +clementine +clementines +clench +clenching +clenched +clenches +clench's +clerestory +clerestories +clerestory's +clergy +clergies +clergy's +clergyman +clergyman's +clergymen +clergywoman +clergywoman's +clergywomen +cleric +clerics +cleric's +clerical +clerically +clericalism +clericalism's +clerk +clerking +clerked +clerks +clerk's +clerkship +clerkship's +clever +cleverly +cleverest +cleverer +cleverness +cleverness +cleverness's +clevis +clevises +clevis's +clew +clewing +clewed +clews +clew's +cliche +cliched +cliches +cliche's +click +clicking +clicked +clicker +clickers +clicks +click's +clickable +clicker +clicker's +client +clients +client's +clientele +clienteles +clientele's +cliff +cliffs +cliff's +cliffhanger +cliffhangers +cliffhanger's +cliffhanging +clifftop +clifftops +clii +climacteric +climacteric's +climactic +climate +climates +climate's +climatic +climatically +climatologist +climatologists +climatologist's +climatology +climatology's +climax +climaxing +climaxed +climaxes +climax's +climb +climbing +climbed +climber +climbers +climbs +climb's +climbable +climber +climber's +climbing +climbing's +clime +climes +clime's +clinch +clinching +clinched +clincher +clinchers +clinches +clinch's +clincher +clincher's +cling +clinging +clinger +clingers +clings +cling's +clinger +clinger's +clingfilm +clingy +clingiest +clingier +clinic +clinics +clinic's +clinical +clinically +clinician +clinicians +clinician's +clink +clinking +clinked +clinker +clinkers +clinks +clink's +clinker +clinker's +cliometric +cliometrics +cliometrician +cliometricians +cliometrician's +cliometrics +cliometrics's +clip +clips +clip's +clipboard +clipboards +clipboard's +clipped +clipper +clippers +clipper's +clipping +clippings +clipping's +clique +cliques +clique's +cliquey +cliquish +cliquishly +cliquishness +cliquishness +cliquishness's +clit +clits +clit's +clitoral +clitorides +clitoris +clitorises +clitoris's +clix +cloaca +cloaca's +cloacae +cloak's +cloak +cloaking +cloaked +cloaks +uncloaking +uncloaked +uncloaks +uncloak +cloakroom +cloakrooms +cloakroom's +clobber +clobbering +clobbered +clobbers +clobber's +cloche +cloches +cloche's +clock +clocking +clocked +clocks +clock's +clockwise +clockwork +clockworks +clockwork's +clod +clods +clod's +cloddish +clodhopper +clodhoppers +clodhopper's +clog's +clog +clogs +unclogs +unclog +clogged +unclogged +clogging +unclogging +cloisonne +cloisonne's +cloister +cloistering +cloistered +cloisters +cloister's +cloistral +clomp +clomping +clomped +clomps +clonal +clone +cloning +cloned +clones +clone's +clonk +clonking +clonked +clonks +clonk's +clop +clops +clop's +clopped +clopping +close +closely +closing +closings +closed +closest +closer +closes +closeness +close's +closefisted +closemouthed +closeness +closeness's +closeout +closeouts +closeout's +closet +closeting +closeted +closets +closet's +closeup +closeups +closeup's +closing +closing's +closure +closures +closure's +disclosures +disclosure's +disclosure +clot +clots +clot's +cloth +cloth's +clothe +clothing +clothed +clothes +unclothing +unclothed +unclothes +unclothe +clotheshorse +clotheshorses +clotheshorse's +clothesline +clotheslines +clothesline's +clothespin +clothespins +clothespin's +clothier +clothiers +clothier's +clothing +clothing's +cloths +clotted +clotting +cloture +clotures +cloture's +cloud +clouding +clouded +clouds +cloud's +cloudburst +cloudbursts +cloudburst's +clouded +unclouded +cloudiness +cloudiness's +cloudless +cloudy +cloudiest +cloudier +cloudiness +clout +clouting +clouted +clouts +clout's +clove +clover +clovers +cloves +clove's +cloven +clover +clover's +cloverleaf +cloverleafs +cloverleaf's +cloverleaves +clown +clowning +clowned +clowns +clown's +clownish +clownishly +clownishness +clownishness +clownishness's +cloy +cloying +cloyed +cloys +cloying +cloyingly +club +clubs +club's +clubbable +clubbed +clubber +clubbers +clubbing +clubfeet +clubfoot +clubfooted +clubfoot's +clubhouse +clubhouses +clubhouse's +clubland +cluck +clucking +clucked +clucks +cluck's +clue +cluing +clued +clues +clue's +clueless +clump +clumping +clumped +clumps +clump's +clumpy +clumpiest +clumpier +clumsily +clumsiness +clumsiness's +clumsy +clumsiest +clumsier +clumsiness +clung +clunk +clunking +clunked +clunker +clunkers +clunks +clunk's +clunker +clunker's +clunky +clunkiest +clunkier +cluster +clustering +clustered +clusters +cluster's +clutch +clutching +clutched +clutches +clutch's +clutter's +clutter +cluttering +cluttered +clutters +uncluttering +uncluttered +unclutters +unclutter +clvi +clvii +clxi +clxii +clxiv +clxix +clxvi +clxvii +cm +cnidarian +cnidarians +cnidarian's +co +coed +cos +discoed +discos +disco +coach +coaching +coached +coaches +coach's +coachload +coachloads +coachman +coachman's +coachmen +coachwork +coadjutor +coadjutors +coadjutor's +coagulant +coagulants +coagulant's +coagulate +coagulation +coagulating +coagulated +coagulates +coagulation +coagulation's +coagulator +coagulators +coagulator's +coal +coaling +coaled +coals +coal's +coalesce +coalescing +coalesced +coalesces +coalescence +coalescence's +coalescent +coalface +coalfaces +coalface's +coalfield +coalfields +coalition +coalitions +coalition's +coalitionist +coalitionists +coalitionist's +coalmine +coalmines +coarse +coarsely +coarsest +coarser +coarseness +coarsen +coarsening +coarsened +coarsens +coarseness +coarseness's +coast +coasting +coasted +coaster +coasters +coasts +coast's +coastal +coaster +coaster's +coastguard +coastguards +coastline +coastlines +coastline's +coat +coating +coatings +coated +coats +coat's +coating +coating's +coatroom +coatrooms +coattail +coattails +coattail's +coauthor +coauthoring +coauthored +coauthors +coauthor's +coax +coaxing +coaxed +coaxer +coaxers +coaxes +coaxer +coaxer's +coaxial +coaxing +coaxingly +cob +cobs +cob's +cobalt +cobalt's +cobber +cobbers +cobble +cobbling +cobbled +cobbler +cobblers +cobbles +cobble's +cobbler +cobbler's +cobblestone +cobblestones +cobblestone's +cobnut +cobnuts +cobra +cobras +cobra's +cobweb +cobwebs +cobweb's +cobwebbed +cobwebby +cobwebbiest +cobwebbier +coca +coca's +cocaine +cocaine's +cocci +coccis +coccus +coccus's +coccyges +coccyx +coccyx's +cochineal +cochineal's +cochlea +cochleas +cochlea's +cochleae +cochlear +cock +cocking +cocked +cocks +cock's +cockade +cockades +cockade's +cockamamie +cockatiel +cockatiels +cockatiel's +cockatoo +cockatoos +cockatoo's +cockatrice +cockatrices +cockatrice's +cockchafer +cockchafers +cockcrow +cockcrows +cockcrow's +cockerel +cockerels +cockerel's +cockeyed +cockfight +cockfighting +cockfights +cockfight's +cockfighting +cockfighting's +cockily +cockiness +cockiness's +cockle +cockles +cockle's +cockleshell +cockleshells +cockleshell's +cockney +cockneys +cockney's +cockpit +cockpits +cockpit's +cockroach +cockroaches +cockroach's +cockscomb +cockscombs +cockscomb's +cocksucker +cocksuckers +cocksucker's +cocksure +cocktail +cocktails +cocktail's +cocky +cockiest +cockier +cockiness +coco +cocos +coco's +cocoa +cocoas +cocoa's +coconut +coconuts +coconut's +cocoon +cocooning +cocooned +cocoons +cocoon's +cod +cods +cod's +coda +codas +coda's +codded +codding +coddle +coddling +coddled +coddles +code's +code +coding +coded +coder +coders +codes +decoding +decoded +decoder +decoders +decodes +decode +codeine +codeine's +codependency +codependency's +codependent +codependents +codependent's +coder +coder's +decoder's +decoder +codex +codex's +codfish +codfishes +codfish's +codger +codgers +codger's +codices +codicil +codicils +codicil's +codification +codification's +codifier +codifier's +codify +codification +codifications +codifying +codified +codifier +codifiers +codifies +codon +codons +codpiece +codpieces +codpiece's +codswallop +coed +coeds +coed's +coeducation +coeducation's +coeducational +coefficient +coefficients +coefficient's +coelenterate +coelenterates +coelenterate's +coenzyme +coequal +coequally +coequals +coequal's +coerce +coercive +coercion +coercing +coerced +coercer +coercers +coerces +coercer +coercer's +coercion +coercion's +coeval +coevally +coevals +coeval's +coexist +coexisting +coexisted +coexists +coexistence +coexistence's +coexistent +coextensive +coffee +coffees +coffee's +coffeecake +coffeecakes +coffeecake's +coffeehouse +coffeehouses +coffeehouse's +coffeemaker +coffeemakers +coffeemaker's +coffeepot +coffeepots +coffeepot's +coffer +coffers +coffer's +cofferdam +cofferdams +cofferdam's +coffin +coffining +coffined +coffins +coffin's +cog +cogs +cog's +cogency +cogency's +cogent +cogently +cogitate +cogitative +cogitation +cogitations +cogitating +cogitated +cogitates +cogitation +cogitation's +cogitator +cogitators +cogitator's +cognac +cognacs +cognac's +cognate +cognates +cognate's +cognition +cognition's +recognition's +recognition +cognitional +cognitive +cognitively +cognizable +cognizance +cognizance's +recognizance's +recognizance +cognizant +cognomen +cognomens +cognomen's +cognoscente +cognoscente's +cognoscenti +cogwheel +cogwheels +cogwheel's +cohabit +cohabiting +cohabited +cohabits +cohabitant +cohabitants +cohabitant's +cohabitation +cohabitation's +coheir +coheirs +coheir's +cohere +cohering +cohered +coheres +coherence +coherence's +incoherence's +incoherence +coherency +coherency's +coherent +coherently +incoherently +incoherent +cohesion +cohesion's +cohesive +cohesively +cohesiveness +cohesiveness +cohesiveness's +coho +cohos +coho's +cohort +cohorts +cohort's +coif +coifs +coif's +coiffed +coiffing +coiffure +coiffuring +coiffured +coiffures +coiffure's +coil's +recoil's +coil +coiling +coiled +coils +recoiling +uncoiling +recoiled +uncoiled +recoils +uncoils +recoil +uncoil +coin +coining +coined +coiner +coiners +coins +coin's +coinage +coinages +coinage's +coincide +coinciding +coincided +coincides +coincidence +coincidences +coincidence's +coincident +coincidental +coincidentally +coiner +coiner's +coinsurance +coinsurance's +coir +coital +coitus +coitus's +coke +coking +coked +cokes +coke's +col +cols +cola +colas +cola's +colander +colanders +colander's +cold +coldly +coldest +colder +colds +coldness +cold's +coldblooded +coldness +coldness's +coleslaw +coleslaw's +coleus +coleuses +coleus's +coley +coleys +colic +colic's +colicky +coliseum +coliseums +coliseum's +colitis +colitis's +coll +collaborate +collaborative +collaboration +collaborations +collaborating +collaborated +collaborates +collaboration +collaboration's +collaborationist +collaborative +collaboratively +collaborator +collaborators +collaborator's +collage +collages +collage's +collagen +collapse +collapsing +collapsed +collapses +collapse's +collapsible +collar +collaring +collared +collars +collar's +collarbone +collarbones +collarbone's +collard +collards +collard's +collarless +collate +collation +collations +collating +collated +collates +collateral +collaterally +collateral's +collateralize +collation +collation's +collator +collators +collator's +colleague +colleagues +colleague's +collect's +collect +collective +collecting +collected +collects +recollecting +recollected +recollects +recollect +collected +uncollected +collectedly +collectible +collectibles +collectible's +collection +collections +collection's +recollections +recollection's +recollection +collective +collectively +collectives +collective's +collectivism +collectivism's +collectivist +collectivists +collectivist's +collectivization +collectivization's +collectivize +collectivizing +collectivized +collectivizes +collector +collectors +collector's +colleen +colleens +colleen's +college +colleges +college's +collegiality +collegiality's +collegian +collegians +collegian's +collegiate +collide +colliding +collided +collider +colliders +collides +collie +collier +colliers +collies +collie's +collier +collier's +colliery +collieries +colliery's +collision +collisions +collision's +collocate +collocation +collocations +collocating +collocated +collocates +collocate's +collocation +collocation's +colloid +colloids +colloid's +colloidal +colloq +colloquial +colloquially +colloquialism +colloquialisms +colloquialism's +colloquies +colloquium +colloquiums +colloquium's +colloquy +colloquy's +collude +colluding +colluded +colludes +collusion +collusion's +collusive +cologne +colognes +cologne's +colon +colons +colon's +colonel +colonels +colonel's +colonelcy +colonelcy's +colones +colonial +colonially +colonials +colonial's +colonialism +colonialism's +colonialist +colonialists +colonialist's +colonist +colonists +colonist's +colonization +colonization's +recolonization's +decolonization's +recolonization +decolonization +colonize +colonizing +colonized +colonizes +recolonizing +decolonizing +recolonized +decolonized +recolonizes +decolonizes +recolonize +decolonize +colonizer +colonizers +colonizer's +colonnade +colonnaded +colonnades +colonnade's +colonoscopy +colonoscopies +colonoscopy's +colony +colonies +colony's +colophon +colophons +colophon's +color's +color +coloring +colored +colors +recoloring +discoloring +recolored +discolored +recolors +discolors +recolor +discolor +colorant +colorants +colorant's +coloration +coloration's +discoloration's +discoloration +coloratura +coloraturas +coloratura's +colorblind +colorblindness +colorblindness +colorblindness's +colored's +colored +uncolored +coloreds +colorfast +colorfastness +colorfastness +colorfastness's +colorful +colorfully +colorfulness +colorfulness +colorfulness's +coloring's +colorist +colorists +colorization +colorization's +colorize +colorizing +colorized +colorizes +colorless +colorlessly +colorlessness +colorlessness +colorlessness's +colorway +colorways +colossal +colossally +colossi +colossus +colossus's +colostomy +colostomies +colostomy's +colostrum +colostrum's +colt +colts +colt's +coltish +columbine +columbines +columbine's +column +columned +columns +column's +columnar +columnist +columnists +columnist's +com +comings +comment +coma +comas +coma's +comaker +comakers +comaker's +comatose +comb +combing +combings +combed +comber +combers +combs +comb's +combat +combative +combating +combated +combats +combat's +combatant +combatants +combatant's +combativeness +combativeness's +combed +uncombed +comber +comber's +combination +combinations +combination's +combine's +combine +combining +combined +combines +recombining +recombined +recombines +recombine +combined +uncombined +combiner +combiners +combiner's +combings +combings's +combo +combos +combo's +combust +combustive +combusting +combusted +combusts +combustibility +combustibility's +combustible +combustibles +combustible's +combustion +combustion's +come +coming +comer +comers +comes +come's +incoming +incomer +incomers +incomes +income's +income +comeback +comebacks +comeback's +comedian +comedians +comedian's +comedic +comedienne +comediennes +comedienne's +comedown +comedowns +comedown's +comedy +comedies +comedy's +comeliness +comeliness's +comely +comeliest +comelier +comeliness +comer's +comestible +comestibles +comestible's +comet +comets +comet's +comeuppance +comeuppances +comeuppance's +comfit's +comfit +comfits +discomfits +discomfit +comfort +comforting +comforted +comforts +comfort's +discomforting +discomforted +discomforts +discomfort's +discomfort +comfortable +comfortableness +comfortableness +comfortableness's +comfortably +uncomfortably +comforter +comforters +comforter's +comforting +comfortingly +comfortless +comfy +comfiest +comfier +comic +comics +comic's +comical +comically +comicality +comicality's +coming +coming's +comity +comity's +comm +comma +commas +comma's +command +commanding +commanded +commander +commanders +commands +command's +commandment +commandant +commandants +commandant's +commandeer +commandeering +commandeered +commandeers +commander +commander's +commandment +commandments +commandment's +commando +commandos +commando's +commemorate +commemorative +commemoration +commemorations +commemorating +commemorated +commemorates +commemoration +commemoration's +commemorator +commemorators +commemorator's +commence +commencing +commenced +commences +commencement +recommencing +recommenced +recommences +recommencement +recommence +commencement +commencement's +recommencement's +recommencement +commencements +commend +commending +commended +commends +commendable +recommending +recommended +recommends +recommendable +recommend +commendably +commendation +commendations +commendation's +recommendations +recommendation's +recommendation +commendatory +commensurable +commensurate +commensurately +incommensurately +incommensurate +comment +commenting +commented +comments +comment's +commentary +commentaries +commentary's +commentate +commentating +commentated +commentates +commentator +commentators +commentator's +commerce +commerce's +commercial +commercially +commercials +commercial's +commercialism +commercialism's +commercialization +commercialization's +commercialize +commercializing +commercialized +commercializes +commie +commies +commie's +commingle +commingling +commingled +commingles +commiserate +commiserative +commiseration +commiserations +commiserating +commiserated +commiserates +commiseration +commiseration's +commissar +commissars +commissar's +commissariat +commissariats +commissariat's +commissary +commissaries +commissary's +commission's +commission +commissioning +commissioned +commissions +recommissioning +decommissioning +recommissioned +decommissioned +recommissions +decommissions +recommission +decommission +commissionaire +commissionaires +commissioner +commissioners +commissioner's +commit +commits +recommits +recommit +commitment +commitments +commitment's +committal +committals +committal's +committed +recommitted +uncommitted +committee +committees +committee's +committeeman +committeeman's +committeemen +committeewoman +committeewoman's +committeewomen +committer +committers +committing +recommitting +commode's +commode +commodes +incommodes +discommodes +incommode +discommode +commodification +commodious +commodiously +commodity +commodities +commodity's +commodore +commodores +commodore's +common's +common +commonly +commonest +commoner +commonness +uncommonly +uncommoner +uncommonness +uncommon +commonality +commonalities +commonalty +commonalty's +commoner +commoners +commoner's +commonness +commonness's +uncommonness's +uncommonness +commonplace +commonplaces +commonplace's +commons +commonsense +commonweal +commonwealth +commonweal's +commonwealth +commonwealth's +commonwealths +commotion +commotions +commotion's +communal +communally +commune +communion +communions +communing +communed +communes +commune's +communicability +communicability's +communicable +incommunicable +communicably +communicant +communicants +communicant's +communicate +communicative +communication +communications +communicating +communicated +communicates +communication +communication's +communicative +uncommunicative +communicator +communicators +communicator's +communion +communion's +communique +communiques +communique's +communism +communism's +communist +communists +communist's +communistic +community +communities +community's +commutation +commutations +commutation's +commutative +commutativity +commutator +commutators +commutator's +commute +commuting +commuted +commuter +commuters +commutes +commute's +commutable +commuter +commuter's +comorbidity +comp +comply +comping +comped +comps +comp's +compact +compactly +compacting +compacted +compactest +compacter +compacts +compactness +compact's +compaction +compactness +compactness's +compactor +compactors +compactor's +companion +companions +companion's +companionable +companionably +companionship +companionship's +companionway +companionways +companionway's +company +companies +company's +comparability +comparability's +comparable +incomparable +comparably +incomparably +comparative +comparatively +comparatives +comparative's +compare +comparing +compared +compares +compare's +comparable +comparison +comparisons +comparison's +compartment +compartments +compartment's +compartmental +compartmentalization +compartmentalization's +compartmentalize +compartmentalizing +compartmentalized +compartmentalizes +compass +compassing +compassed +compasses +compass's +compassion +compassion's +compassionate +compassionately +compatibility +compatibility's +incompatibility's +incompatibility +compatible +compatibles +compatible's +incompatibles +incompatible's +incompatible +compatibly +incompatibly +compatriot +compatriots +compatriot's +compeer +compeers +compeer's +compel +compels +compelled +compelling +compellingly +compendious +compendium +compendiums +compendium's +compensate +compensation +compensations +compensating +compensated +compensates +compensated +uncompensated +compensation +compensation's +compensatory +compere +compering +compered +comperes +compete +competing +competed +competes +competence +competence's +incompetence's +incompetence +competences +competencies +competency +competency's +incompetency's +incompetency +competent +competently +incompetently +incompetent +competition +competitions +competition's +competitive +competitively +competitiveness +competitiveness +competitiveness's +competitor +competitors +competitor's +compilation +compilations +compilation's +compile +compiling +compiled +compiler +compilers +compiles +compiler +compiler's +complacence +complacence's +complacency +complacency's +complacent +complacently +complain +complaining +complained +complainer +complainers +complains +complainant +complainants +complainant's +complainer +complainer's +complaint +complaints +complaint's +complaisance +complaisance's +complaisant +complaisantly +complected +complement +complementing +complemented +complements +complement's +complementary +complete +completion +completions +completely +completing +completed +completest +completer +completes +completeness +completed +uncompleted +completeness +completeness's +incompleteness's +incompleteness +completion +completion's +complex +complexly +complexes +complex's +complexion +complexioned +complexions +complexion's +complexional +complexity +complexities +complexity's +compliance +compliance's +compliant +compliantly +complicate +complicating +complicated +complicates +complicated +complicatedly +complication +complication's +complicit +complicity +complicity's +compliment +complimenting +complimented +compliments +compliment's +complimentary +uncomplimentary +comply +complication +complications +complying +complied +complies +compo +compos +component +components +component's +comport +comporting +comported +comports +comportment +comportment +comportment's +compose +composing +composed +composes +recomposing +decomposing +discomposing +recomposed +decomposed +discomposed +recomposes +decomposes +discomposes +recompose +decompose +discompose +composedly +composer +composers +composer's +composite +composition +compositions +compositely +compositing +composited +composites +composite's +composition +composition's +decomposition's +decomposition +compositor +compositors +compositor's +compost +composting +composted +composts +compost's +composure +composure's +discomposure's +discomposure +compote +compotes +compote's +compound +compounding +compounded +compounds +compound's +compoundable +compounded +uncompounded +comprehend +comprehending +comprehended +comprehends +comprehensibility +comprehensibility's +incomprehensibility's +incomprehensibility +comprehensible +incomprehensible +comprehensibly +incomprehensibly +comprehension +comprehension's +incomprehension's +incomprehension +comprehensions +comprehensive +comprehensively +comprehensives +comprehensiveness +comprehensive's +comprehensiveness +comprehensiveness's +compress's +compress +compressing +compressed +compresses +decompressing +decompressed +decompresses +decompress +compressed +uncompressed +compressible +compression +compression's +decompression's +decompression +compressor +compressors +compressor's +comprise +comprising +comprised +comprises +compromise +compromising +compromised +compromises +compromise's +comptroller +comptrollers +comptroller's +compulsion +compulsions +compulsion's +compulsive +compulsively +compulsiveness +compulsiveness +compulsiveness's +compulsorily +compulsory +compulsories +compulsory's +compunction +compunctions +compunction's +computation +computations +computation's +computational +computationally +compute +computing +computed +computes +recomputing +recomputed +recomputes +recompute +computer +computers +computer's +computerate +computerization +computerization's +computerize +computerizing +computerized +computerizes +computing +computing's +comrade +comradely +comrades +comrade's +comradeship +comradeship's +con +coning +cons +con's +concatenate +concatenation +concatenations +concatenating +concatenated +concatenates +concatenation +concatenation's +concave +concavely +concaveness +concaveness +concaveness's +conceal +concealing +concealed +concealer +concealers +conceals +concealable +concealment +concealed +unconcealed +concealer +concealer's +concealment +concealment's +conceit +conceited +conceits +conceit's +conceited +conceitedly +conceitedness +conceitedness +conceitedness's +conceivable +inconceivable +conceivably +inconceivably +conceive +conceiving +conceived +conceives +conceivable +concentrate +concentration +concentrations +concentrating +concentrated +concentrates +concentrate's +concentration +concentration's +concentric +concentrically +concept +concepts +concept's +conception +conceptions +conception's +conceptional +conceptual +conceptually +conceptualization +conceptualizations +conceptualization's +conceptualize +conceptualizing +conceptualized +conceptualizes +concern +concerned +concern's +unconcerned +unconcern's +unconcern +concerned +concernedly +unconcernedly +unconcerned +concerning +concerns +concert's +concert +concerting +concerted +concerts +disconcerting +disconcerted +disconcerts +disconcert +concerted +concertedly +concertgoer +concertgoers +concertina +concertinaing +concertinaed +concertinas +concertina's +concertize +concertizing +concertized +concertizes +concertmaster +concertmasters +concertmaster's +concerto +concertos +concerto's +concessionaire +concessionaires +concessionaire's +concessional +concessionary +conch +conch's +conchie +conchies +conchs +concierge +concierges +concierge's +conciliate +conciliation +conciliating +conciliated +conciliates +conciliation +conciliation's +reconciliation's +reconciliation +conciliator +conciliators +conciliator's +conciliatory +concise +concision +concisely +concisest +conciser +conciseness +conciseness +conciseness's +concision +concision's +conclave +conclaves +conclave's +conclude +concluding +concluded +concludes +conclusion +conclusions +conclusion's +conclusive +conclusively +conclusiveness +inconclusively +inconclusiveness +inconclusive +conclusiveness +conclusiveness's +inconclusiveness's +inconclusiveness +concoct +concocting +concocted +concocts +concoction +concoctions +concoction's +concomitant +concomitantly +concomitants +concomitant's +concord +concord's +concordance +concordances +concordance's +concordant +concordat +concordats +concordat's +concourse +concourses +concourse's +concrete +concretion +concretions +concretely +concreting +concreted +concretes +concreteness +concrete's +concreteness +concreteness's +concretion +concretion's +concubinage +concubinage's +concubine +concubines +concubine's +concupiscence +concupiscence's +concupiscent +concur +concurs +concurred +concurrence +concurrences +concurrence's +concurrency +concurring +concuss +concussive +concussion +concussions +concussion's +condemn +condemning +condemned +condemner +condemners +condemns +condemnation +condemnations +condemnation's +condemnatory +condemner +condemner's +condensate +condensation +condensations +condensates +condensate's +condensation +condensation's +condense +condensing +condensed +condenser +condensers +condenses +condenser +condenser's +condescending +condescendingly +condescension +condescension's +condign +condiment +condiments +condiment's +condition's +condition +conditioning +conditioned +conditions +reconditioning +reconditioned +reconditions +recondition +conditional +conditionally +conditionals +conditional's +conditionality +conditioned +unconditioned +conditioner +conditioners +conditioner's +conditioning +conditioning's +condo +condos +condo's +condolence +condolences +condolence's +condom +condoms +condom's +condominium +condominiums +condominium's +condone +condoning +condoned +condones +condor +condors +condor's +conduce +conducive +conducing +conduced +conduces +conduct +conductive +conducting +conducted +conduct's +conductance +conductance's +conductibility +conductibility's +conductible +conduction +conduction's +conductivity +conductivity's +conductor +conductors +conductor's +conductress +conductresses +conductress's +conduit +conduits +conduit's +cone +cone's +coneys +confab +confabs +confab's +confabbed +confabbing +confabulate +confabulation +confabulations +confabulating +confabulated +confabulates +confabulation +confabulation's +confection +confectioner +confectioners +confections +confection's +confectioner +confectioner's +confectionery +confectioneries +confectionery's +confederacy +confederacies +confederacy's +confederate +confederate's +confer +confers +conferee +conferees +conferee's +conference +conferencing +conferences +conference's +conferrable +conferral +conferral's +conferred +conferrer +conferrers +conferrer's +conferring +confessed +confessedly +confession +confessions +confession's +confessional +confessionals +confessional's +confessor +confessors +confessor's +confetti +confetti's +confidant +confidants +confidant's +confidante +confidantes +confidante's +confide +confiding +confided +confider +confiders +confides +confidence +confidences +confidence's +confident +confidently +confidential +confidentially +confidentiality +confidentiality's +confider +confider's +confiding +confidingly +configuration +configurations +configure +configurable +confined +unconfined +confinement +confinements +confinement's +confirm +confirming +confirmed +confirms +reconfirming +reconfirmed +reconfirms +reconfirm +confirmation +confirmations +confirmation's +reconfirmations +reconfirmation's +reconfirmation +confirmatory +confirmed +unconfirmed +confiscate +confiscation +confiscations +confiscating +confiscated +confiscates +confiscation +confiscation's +confiscator +confiscators +confiscator's +confiscatory +conflagration +conflagrations +conflagration's +conflate +conflation +conflations +conflating +conflated +conflates +conflation +conflation's +conflict +conflicting +conflicted +conflicts +conflict's +confluence +confluences +confluence's +confluent +conform +conformers +conformable +conformable +unconformable +conformal +conformance +conformance's +conformism +conformism's +conformist +conformists +conformist's +conformity +conformity's +confrere +confreres +confrere's +confrontation +confrontations +confrontation's +confrontational +confuse +confuser +confusers +confused +confusedly +confusing +confusingly +confutation +confutation's +confute +confuting +confuted +confutes +conga +congaing +congaed +congas +conga's +congeal +congealing +congealed +congeals +congealment +congealment +congealment's +conger +congers +conger's +congeries +congeries's +congest +congestive +congesting +congested +congests +congestion +congestion's +conglomerate +conglomeration +conglomerations +conglomerating +conglomerated +conglomerates +conglomerate's +conglomeration +conglomeration's +congrats +congrats's +congratulate +congratulation +congratulations +congratulating +congratulated +congratulates +congratulation +congratulation's +congratulatory +congregant +congregants +congregant's +congregate +congregation +congregations +congregating +congregated +congregates +congregation +congregation's +congregational +congregationalism +congregationalism's +congregationalist +congregationalists +congregationalist's +congress +congresses +congress's +congressional +congressman +congressman's +congressmen +congresspeople +congressperson +congresspersons +congressperson's +congresswoman +congresswoman's +congresswomen +congruence +congruence's +congruent +congruently +congruity +congruities +congruity's +incongruities +incongruity's +incongruity +congruous +conic +conics +conic's +conical +conically +conifer +conifers +conifer's +coniferous +conjectural +conjecture +conjecturing +conjectured +conjectures +conjecture's +conjoint +conjugal +conjugally +conjugate +conjugation +conjugations +conjugating +conjugated +conjugates +conjugation +conjugation's +conjunct +conjunctive +conjuncts +conjunct's +conjunctiva +conjunctivas +conjunctiva's +conjunctive +conjunctives +conjunctive's +conjunctivitis +conjunctivitis's +conjuration +conjurations +conjuration's +conjure +conjuring +conjured +conjurer +conjurers +conjures +conjurer +conjurer's +conk +conked +conker +conkers +conk's +conman +connect +connective +connecting +connected +connects +reconnecting +disconnecting +reconnected +disconnected +reconnects +disconnects +reconnect +disconnect +connectable +connected +unconnected +connection +connections +connection's +disconnections +disconnection's +disconnection +connective +connectives +connective's +connectivity +connectivity's +connector +connectors +connector's +conned +conning +conniption +conniptions +conniption's +connivance +connivance's +connive +conniving +connived +conniver +connivers +connives +conniver +conniver's +connoisseur +connoisseurs +connoisseur's +connotative +connubial +conquer +conquering +conquered +conquers +reconquering +reconquered +reconquers +reconquer +conquerable +unconquerable +conquered +unconquered +conqueror +conquerors +conqueror's +conquest +conquest's +reconquest's +reconquest +conquistador +conquistadors +conquistador's +cons +consing +consed +conses +consanguineous +consanguinity +consanguinity's +conscienceless +conscientious +conscientiously +conscientiousness +conscientiousness +conscientiousness's +conscious +consciously +consciousness +unconsciously +unconsciousness +unconscious +consciousness +consciousness's +unconsciousness's +unconsciousness +consciousnesses +conscription +conscription's +consecrate +consecration +consecrating +consecrated +consecrates +reconsecration +reconsecrating +reconsecrated +reconsecrates +reconsecrate +consecrated +unconsecrated +consecration +consecration's +reconsecration's +reconsecration +consecrations +consecutive +consecutively +consensual +consensus +consensuses +consensus's +consent +consenting +consented +consents +consent's +consequence +consequences +consequence's +consequent +consequently +consequential +consequentially +inconsequentially +inconsequential +conservancy +conservancies +conservancy's +conservation +conservation's +conservationism +conservationism's +conservationist +conservationists +conservationist's +conservatism +conservatism's +conservative +conservatively +conservatives +conservative's +conservatoire +conservatoires +conservator +conservators +conservator's +conservatory +conservatories +conservatory's +consider +considering +considered +considers +reconsidering +reconsidered +reconsiders +reconsider +considerable +inconsiderable +considerably +considerate +consideration +considerately +considerateness +inconsideration +inconsiderately +inconsiderateness +inconsiderate +considerateness +considerateness's +inconsiderateness's +inconsiderateness +consideration +consideration's +reconsideration's +inconsideration's +reconsideration +inconsideration +considerations +considered +unconsidered +consign +consigning +consigned +consigns +reconsigning +reconsigned +reconsigns +reconsign +consignee +consignees +consignee's +consignment +consignments +consignment's +consist +consisting +consisted +consists +consistence +consistences +consistence's +consistency +consistencies +consistency's +inconsistencies +inconsistency's +inconsistency +consistent +consistently +inconsistently +inconsistent +consistory +consistories +consistory's +consolable +inconsolable +consolation +consolations +consolation's +consolatory +consolidate +consolidation +consolidations +consolidating +consolidated +consolidates +consolidated +unconsolidated +consolidation +consolidation's +consolidator +consolidators +consolidator's +consoling +consolingly +consomme +consomme's +consonance +consonances +consonance's +consonant +consonantly +consonants +consonant's +consortia +consortium +consortium's +conspectus +conspectuses +conspectus's +conspicuous +conspicuously +conspicuousness +inconspicuously +inconspicuousness +inconspicuous +conspicuousness +conspicuousness's +inconspicuousness's +inconspicuousness +conspiracy +conspiracies +conspiracy's +conspirator +conspirators +conspirator's +conspiratorial +conspiratorially +conspire +conspiring +conspired +constable +constables +constable's +constabulary +constabularies +constabulary's +constancy +constancy's +inconstancy's +inconstancy +constant +constantly +constants +constant's +constellation +constellations +constellation's +consternation +consternation's +constipate +constipation +constipating +constipated +constipates +constipation +constipation's +constituency +constituencies +constituency's +constituent +constituents +constituent's +constitute +constitutive +constitution +constituting +constituted +constitutes +reconstitution +reconstituting +reconstituted +reconstitutes +reconstitute +constitution +constitution's +reconstitution's +reconstitution +constitutional +constitutionally +constitutionals +constitutional's +constitutionalism +constitutionality +constitutionality's +unconstitutionality's +unconstitutionality +constitutions +constrained +unconstrained +constraint +constraints +constraint's +constrict +constrictive +constricting +constricted +constricts +constriction +constrictions +constriction's +constrictor +constrictors +constrictor's +construable +construct's +construct +constructive +constructing +constructed +constructs +reconstructing +deconstructing +reconstructed +deconstructed +reconstructs +deconstructs +reconstruct +deconstruct +construction +constructions +construction's +reconstructions +deconstructions +reconstruction's +deconstruction's +reconstruction +deconstruction +constructional +constructionist's +constructionist +constructionists +deconstructionists +deconstructionist +constructive +constructively +constructiveness +constructiveness +constructiveness's +constructor +constructors +constructor's +construe +construing +construed +construes +consul +consuls +consul's +proconsuls +proconsul's +proconsul +consular +proconsular +consulate +consulates +consulate's +consulship +consulship's +consult +consulting +consulted +consults +consultancy +consultancies +consultancy's +consultant +consultants +consultant's +consultation +consultations +consultation's +consultative +consumable +consumables +consumable's +consume +consuming +consumed +consumer +consumers +consumes +consumable +consumed +unconsumed +consumer +consumer's +consumerism +consumerism's +consumerist +consumerists +consumerist's +consummate +consummation +consummations +consummately +consummating +consummated +consummates +consummated +unconsummated +consumption +consumption's +consumptive +consumptives +consumptive's +cont +contact +contacting +contacted +contacts +recontacting +recontacted +recontacts +recontact +contactable +contagion +contagions +contagion's +contagious +contagiously +contagiousness +contagiousness +contagiousness's +contain +containing +contained +container +containers +contains +containable +containment +container +container's +containerization +containerization's +containerize +containerizing +containerized +containerizes +containment +containment's +contaminant +contaminants +contaminant's +contaminate +contaminating +contaminated +contaminates +recontaminating +decontaminating +recontaminated +decontaminated +recontaminates +decontaminates +recontaminate +decontaminate +contaminated +uncontaminated +contamination +contamination's +decontamination's +decontamination +contaminator +contaminators +contaminator's +contd +contemn +contemning +contemned +contemns +contemplate +contemplative +contemplation +contemplating +contemplated +contemplates +contemplation +contemplation's +contemplative +contemplatively +contemplatives +contemplative's +contemporaneity +contemporaneity's +contemporaneous +contemporaneously +contempt +contempt's +contemptible +contemptibly +contemptuous +contemptuously +contemptuousness +contemptuousness +contemptuousness's +contender +contenders +contender's +content +contenting +contented +contents +content's +contentment +discontenting +discontented +discontents +discontent's +discontentment +discontent +contented +contentedly +discontentedly +discontented +contentedness +contentedness's +contention +contentions +contention's +contentious +contentiously +contentiousness +contentiousness +contentiousness's +contently +contentment +contentment's +discontentment's +discontentment +conterminous +conterminously +contestable +incontestable +contestant +contestants +contestant's +contested +uncontested +contextualization +contextualize +contextualizing +contextualized +contextualizes +contiguity +contiguity's +contiguous +contiguously +continence +continence's +incontinence's +incontinence +continent +continents +continent's +continental +continentals +continental's +contingency +contingencies +contingency's +contingent +contingently +contingents +contingent's +continua +continual +continually +continuance +continuances +continuance's +discontinuances +discontinuance's +discontinuance +continuation +continuations +continuation's +discontinuations +discontinuation's +discontinuation +continue +continuing +continued +continues +discontinuing +discontinued +discontinues +discontinue +continuity +continuities +continuity's +discontinuities +discontinuity's +discontinuity +continuous +continuously +discontinuously +discontinuous +continuum +continuum's +contort +contorting +contorted +contortion +contortions +contortion's +contortionist +contortionists +contortionist's +contra +contraband +contraband's +contrabassoon +contrabassoons +contraception +contraception's +contraceptive +contraceptives +contraceptive's +contract +contracting +contracted +contract's +contractible +contractile +contractility +contraction +contractions +contractual +contractually +contradict +contradicting +contradicted +contradicts +contradiction +contradictions +contradiction's +contradictory +contradistinction +contradistinctions +contradistinction's +contraflow +contraflows +contrail +contrails +contrail's +contraindicate +contraindication +contraindications +contraindicating +contraindicated +contraindicates +contraindication +contraindication's +contralto +contraltos +contralto's +contraption +contraptions +contraption's +contrapuntal +contrapuntally +contrarian +contrarians +contrarian's +contrarianism +contrariety +contrariety's +contrarily +contrariness +contrariness's +contrariwise +contrary +contraries +contrariness +contrary's +contrast +contrasting +contrasted +contrasts +contrast's +contravene +contravening +contravened +contravenes +contravention +contraventions +contravention's +contretemps +contretemps's +contribute +contribution +contributions +contributing +contributed +contribution +contribution's +contributor +contributors +contributor's +contributory +contrition +contrition's +contrivance +contrivances +contrivance's +contrive +contriving +contrived +contriver +contrivers +contrives +contriver +contriver's +control's +control +controls +decontrols +decontrol +controllable +uncontrollable +controlled +uncontrolled +decontrolled +controller +controllers +controller's +controlling +decontrolling +controversial +controversially +controversy +controversies +controversy's +controvert +controverting +controverted +controverts +controvertible +incontrovertible +contumacious +contumaciously +contumacy +contumacy's +contumelious +contumely +contumelies +contumely's +contuse +contusion +contusions +contusing +contused +contuses +contusion +contusion's +conundrum +conundrums +conundrum's +conurbation +conurbations +conurbation's +convalesce +convalescing +convalesced +convalesces +convalescence +convalescences +convalescence's +convalescent +convalescents +convalescent's +convection +convection's +convectional +convective +convector +convectors +convene +convening +convened +convenes +reconvening +reconvened +reconvenes +reconvene +convener +conveners +convener's +convenience +conveniences +convenience's +inconveniences +inconvenience's +inconvenience +convenient +conveniently +inconveniently +inconvenient +convent +convents +convent's +conventicle +conventicles +conventicle's +convention +conventions +convention's +conventional +conventionally +unconventionally +unconventional +conventionality +conventionality's +unconventionality's +unconventionality +conventionalize +conventionalizing +conventionalized +conventionalizes +conventioneer +conventioneers +convergence +convergences +convergence's +convergent +conversant +conversation +conversations +conversation's +conversational +conversationally +conversationalist +conversationalists +conversationalist's +converse +conversely +convert's +convert +converting +converted +converts +reconverting +reconverted +reconverts +reconvert +converted +unconverted +converter +converters +converter's +convertibility +convertibility's +convertible +convertibles +convertible's +convex +convexly +convexity +convexity's +convey +conveying +conveyed +conveys +conveyable +conveyance +conveyancing +conveyances +conveyance's +conveyor +conveyors +conveyor's +convict +convicting +convicted +convicts +convict's +conviction +convictions +conviction's +convince +convincing +convinced +convinces +convinced +unconvinced +convincing +convincingly +unconvincingly +unconvincing +convivial +convivially +conviviality +conviviality's +convoke +convoking +convoked +convokes +convoluted +convolution +convolutions +convolution's +convoy +convoying +convoyed +convoys +convoy's +convulse +convulsive +convulsion +convulsions +convulsing +convulsed +convulses +convulsion +convulsion's +convulsive +convulsively +cony +cony's +coo +cooing +cooed +coos +coo's +cook's +cook +cooking +cooked +cooks +recooking +recooked +recooks +recook +cookbook +cookbooks +cookbook's +cooked +uncooked +cooker +cookers +cooker's +cookery +cookeries +cookery's +cookhouse +cookhouses +cookie +cookies +cookie's +cooking +cooking's +cookout +cookouts +cookout's +cookware +cookwares +cookware's +cool +coolly +cooling +cooled +coolest +cooler +coolers +cools +coolness +cool's +coolant +coolants +coolant's +cooler +cooler's +coolie +coolies +coolie's +coolness +coolness's +coon +coons +coon's +coonskin +coonskins +coonskin's +coop +cooping +cooped +cooper +coopers +coops +coop's +cooper +coopering +coopered +cooper's +cooperage +cooperage's +cooperate +cooperative +cooperation +cooperating +cooperated +cooperates +cooperation +cooperation's +cooperative +cooperatively +cooperatives +cooperativeness +cooperative's +cooperativeness +cooperativeness's +cooperator +cooperators +cooperator's +coordinate +coordination +coordinately +coordinating +coordinated +coordinates +coordinate's +coordinated +uncoordinated +coordination +coordination's +coordinator +coordinators +coordinator's +coot +coots +coot's +cootie +cooties +cootie's +cop +coping +copings +coped +cops +cop's +copacetic +copay +copay's +cope +copes +cope's +copier +copiers +copier's +copilot +copilots +copilot's +coping +coping's +copious +copiously +copiousness +copiousness +copiousness's +copped +copper +coppers +copper's +copperhead +copperheads +copperhead's +copperplate +copperplate's +coppery +copping +copra +copra's +copse +copses +copse's +copter +copters +copter's +copula +copulas +copula's +copulate +copulative +copulation +copulating +copulated +copulates +copulation +copulation's +copulative +copulatives +copulative's +copy's +copy +copying +copied +copies +recopying +recopied +recopies +recopy +copybook +copybooks +copybook's +copycat +copycats +copycat's +copycatted +copycatting +copyist +copyists +copyist's +copyleft +copyright +copyrighting +copyrighted +copyrights +copyright's +copywriter +copywriters +copywriter's +coquetry +coquetries +coquetry's +coquette +coquetting +coquetted +coquettes +coquette's +coquettish +coquettishly +cor +coracle +coracles +coracle's +coral +corals +coral's +corbel +corbels +corbel's +cord +cording +corded +cords +cord's +recording +discording +recorded +discorded +records +discords +record's +discord's +record +discord +cordage +cordage's +cordial +cordially +cordials +cordial's +cordiality +cordiality's +cordillera +cordilleras +cordillera's +cordite +cordite's +cordless +cordon +cordoning +cordoned +cordons +cordon's +cordovan +cordovan's +corduroy +corduroys +corduroy's +corduroys +corduroys's +core +coring +cored +corer +corers +cores +core's +coreligionist +coreligionists +corer +corer's +corespondent +corespondents +corespondent's +corgi +corgis +corgi's +coriander +coriander's +cork's +cork +corking +corked +corks +uncorking +uncorked +uncorks +uncork +corkage +corker +corkers +corker's +corkscrew +corkscrewing +corkscrewed +corkscrews +corkscrew's +corm +corms +corm's +cormorant +cormorants +cormorant's +corn +corning +corned +corner +corners +corns +corn's +cornball +cornballs +cornball's +cornbread +cornbread's +corncob +corncobs +corncob's +corncrake +corncrakes +cornea +corneas +cornea's +corneal +corner +cornering +cornered +corner's +cornerstone +cornerstones +cornerstone's +cornet +cornets +cornet's +cornfield +cornfields +cornflakes +cornflakes's +cornflour +cornflower +cornflowers +cornflower's +cornice +cornices +cornice's +cornily +corniness +corniness's +cornmeal +cornmeal's +cornrow +cornrowing +cornrowed +cornrows +cornrow's +cornstalk +cornstalks +cornstalk's +cornstarch +cornstarch's +cornucopia +cornucopias +cornucopia's +corny +corniest +cornier +corniness +corolla +corollas +corolla's +corollary +corollaries +corollary's +corona +coronas +corona's +coronal +coronals +coronal's +coronary +coronaries +coronary's +coronation +coronations +coronation's +coroner +coroners +coroner's +coronet +coronets +coronet's +corp +corpora +corporal +corporals +corporal's +corporate +corporation +corporations +corporately +corporation +corporation's +incorporation's +incorporation +corporatism +corporeal +corporeally +corporeality +corporeality's +corps +corpses +corps's +corpse +corpse's +corpsman +corpsman's +corpsmen +corpulence +corpulence's +corpulent +corpus +corpus's +corpuscle +corpuscles +corpuscle's +corpuscular +corr +corral +corrals +corral's +corralled +corralling +correct +corrective +correctly +correcting +corrected +correctest +correcter +corrects +correctness +correctable +corrected +uncorrected +correction +corrections +correction's +correctional +corrective +correctives +corrective's +correctness +correctness's +incorrectness's +incorrectness +corrector +correlate +correlative +correlation +correlations +correlating +correlated +correlates +correlate's +correlated +uncorrelated +correlation +correlation's +correlational +correlative +correlatives +correlative's +correspond +corresponding +corresponded +corresponds +correspondence +correspondences +correspondence's +correspondent +correspondents +correspondent's +corresponding +correspondingly +corridor +corridors +corridor's +corrie +corries +corroborate +corroborative +corroboration +corroborations +corroborating +corroborated +corroborates +corroborated +uncorroborated +corroboration +corroboration's +corroborator +corroborators +corroborator's +corroboratory +corrode +corroding +corroded +corrodes +corrosion +corrosion's +corrosive +corrosively +corrosives +corrosive's +corrugate +corrugation +corrugations +corrugating +corrugated +corrugates +corrugation +corrugation's +corrupt +corruptly +corrupting +corrupted +corruptest +corrupter +corrupts +corruptness +corruptibility +corruptibility's +incorruptibility's +incorruptibility +corruptible +incorruptible +corruption +corruptions +corruption's +corruptness +corruptness's +corsage +corsages +corsage's +corsair +corsairs +corsair's +corset +corseting +corseted +corsets +corset's +cortege +corteges +cortege's +cortex +cortex's +cortical +cortices +cortisol +cortisone +cortisone's +corundum +corundum's +coruscate +coruscation +coruscating +coruscated +coruscates +coruscation +coruscation's +corvette +corvettes +corvette's +cos +cos's +cosh +coshing +coshed +coshes +cosign +cosigning +cosigned +cosigner +cosigners +cosigns +cosignatory +cosignatories +cosignatory's +cosigner +cosigner's +cosine +cosines +cosine's +cosmetic +cosmetics +cosmetic's +cosmetically +cosmetician +cosmeticians +cosmetician's +cosmetologist +cosmetologists +cosmetologist's +cosmetology +cosmetology's +cosmic +cosmically +cosmogonist +cosmogonists +cosmogonist's +cosmogony +cosmogonies +cosmogony's +cosmological +cosmologist +cosmologists +cosmologist's +cosmology +cosmologies +cosmology's +cosmonaut +cosmonauts +cosmonaut's +cosmopolitan +cosmopolitans +cosmopolitan's +cosmopolitanism +cosmopolitanism's +cosmos +cosmoses +cosmos's +cosplay +cosponsor +cosponsoring +cosponsored +cosponsors +cosponsor's +cosset +cosseting +cosseted +cossets +cossetted +cossetting +cost +costly +costing +costings +costed +costs +cost's +costar +costars +costar's +costarred +costarring +costliness +costliness's +costly +costliest +costlier +costliness +costume +costuming +costumed +costumer +costumers +costumes +costume's +costumer +costumer's +costumier +costumiers +cot +cots +cot's +cotangent +cotangents +cotangent's +cote +cotes +cote's +coterie +coteries +coterie's +coterminous +cotillion +cotillions +cotillion's +cottage +cottaging +cottager +cottagers +cottages +cottage's +cottager +cottager's +cottar +cottars +cottar's +cotter +cotters +cotter's +cotton +cottoning +cottoned +cottons +cotton's +cottonmouth +cottonmouth's +cottonmouths +cottonseed +cottonseeds +cottonseed's +cottontail +cottontails +cottontail's +cottonwood +cottonwoods +cottonwood's +cottony +cotyledon +cotyledons +cotyledon's +couch +couching +couched +couches +couch's +couchette +couchettes +cougar +cougars +cougar's +cough +coughing +coughed +cough's +coughs +could +could've +couldn't +coulee +coulees +coulee's +coulis +coulomb +coulombs +coulomb's +council +councils +council's +councilman +councilman's +councilmen +councilor +councilors +councilor's +councilperson +councilpersons +councilperson's +councilwoman +councilwoman's +councilwomen +counsel +counseling +counselings +counseled +counsels +counsel's +counselor +counselors +counselor's +count +counting +counted +counts +count's +recounting +discounting +recounted +discounted +recounts +discounts +recount's +discount's +recount +discount +countable +uncountable +countably +countdown +countdowns +countdown's +counted +uncounted +countenance's +countenance +countenancing +countenanced +countenances +discountenancing +discountenanced +discountenances +discountenance +counter +counters +counter's +discounters +discounter's +discounter +counteract +counteractive +counteracting +counteracted +counteracts +counteraction +counteractions +counteraction's +counterargument +counterarguments +counterattack +counterattacking +counterattacked +counterattacks +counterattack's +counterbalance +counterbalancing +counterbalanced +counterbalances +counterbalance's +counterblast +counterblasts +counterclaim +counterclaiming +counterclaimed +counterclaims +counterclaim's +counterclockwise +counterculture +countercultures +counterculture's +countered +counterespionage +counterespionage's +counterexample +counterexamples +counterfactual +counterfeit +counterfeiting +counterfeited +counterfeiter +counterfeiters +counterfeits +counterfeit's +counterfeiter +counterfeiter's +counterfoil +counterfoils +counterfoil's +countering +counterinsurgency +counterinsurgencies +counterinsurgency's +counterintelligence +counterintelligence's +counterman +counterman's +countermand +countermanding +countermanded +countermands +countermand's +countermeasure +countermeasures +countermeasure's +countermelody +countermelodies +countermen +countermove +countermoves +counteroffensive +counteroffensives +counteroffensive's +counteroffer +counteroffers +counteroffer's +counterpane +counterpanes +counterpane's +counterpart +counterparts +counterpart's +counterpetition +counterpoint +counterpointing +counterpointed +counterpoints +counterpoint's +counterpoise +counterpoising +counterpoised +counterpoises +counterpoise's +counterproductive +counterrevolution +counterrevolutions +counterrevolution's +counterrevolutionary +counterrevolutionaries +counterrevolutionary's +countersign +countersigning +countersigned +countersigns +countersign's +countersignature +countersignatures +countersignature's +countersink +countersinking +countersinks +countersink's +counterspy +counterspies +counterspy's +counterstroke +counterstrokes +counterstroke's +countersunk +countertenor +countertenors +countertenor's +countervail +countervailing +countervailed +countervails +counterweight +counterweights +counterweight's +countess +countesses +countess's +countless +countrified +country +countries +country's +countryman +countryman's +countrymen +countryside +countrysides +countryside's +countrywide +countrywoman +countrywoman's +countrywomen +county +counties +county's +countywide +coup's +coup +coups +recoups +recoup +coupe +coupes +coupe's +couple's +couple +coupling +coupled +couples +uncoupling +decoupling +uncoupled +decoupled +uncouples +decouples +uncouple +decouple +couplet +couplets +couplet's +coupling +couplings +coupling's +coupon +coupons +coupon's +courage +courage's +courageous +courageously +courageousness +courageousness +courageousness's +courgette +courgettes +courier +couriering +couriered +couriers +courier's +course +coursing +coursed +courses +course's +discoursing +discoursed +discourses +discourse's +discourse +coursebook +coursebooks +courser +coursers +courser's +coursework +court +courtly +courting +courted +courts +court's +courteous +courteously +discourteously +discourteous +courteousness +courteousness's +courtesan +courtesans +courtesan's +courtesy +courtesies +courtesy's +discourtesies +discourtesy's +discourtesy +courthouse +courthouses +courthouse's +courtier +courtiers +courtier's +courtliness +courtliness's +courtly +courtliest +courtlier +courtliness +courtroom +courtrooms +courtroom's +courtship +courtships +courtship's +courtyard +courtyards +courtyard's +couscous +couscous's +cousin +cousins +cousin's +couture +couture's +couturier +couturiers +couturier's +covalent +covariance +covariant +cove +coves +cove's +coven +covens +coven's +covenant +covenanting +covenanted +covenants +covenant's +cover's +cover +covering +covered +covers +recovering +uncovering +discovering +recovered +uncovered +discovered +recovers +uncovers +discovers +recover +uncover +discover +coverage +coverage's +coverall +coveralls +coverall's +covering's +coverings +coverlet +coverlets +coverlet's +covert +covertly +coverts +covertness +covert's +covertness +covertness's +covet +coveting +coveted +covets +covetous +covetously +covetousness +covetousness +covetousness's +covey +coveys +covey's +cow +cowing +cowed +cower +cowers +cows +cow's +coward +cowardly +cowards +coward's +cowardice +cowardice's +cowardliness +cowardliness's +cowbell +cowbells +cowbell's +cowbird +cowbirds +cowbird's +cowboy +cowboys +cowboy's +cowcatcher +cowcatchers +cowcatcher's +cower +cowering +cowered +cowgirl +cowgirls +cowgirl's +cowhand +cowhands +cowhand's +cowherd +cowherds +cowherd's +cowhide +cowhides +cowhide's +cowl +cowling +cowlings +cowls +cowl's +cowlick +cowlicks +cowlick's +cowling +cowling's +cowman +cowman's +cowmen +coworker +coworkers +coworker's +cowpat +cowpats +cowpoke +cowpokes +cowpoke's +cowpox +cowpox's +cowpuncher +cowpunchers +cowpuncher's +cowrie +cowries +cowrie's +cowshed +cowsheds +cowslip +cowslips +cowslip's +cox +coxing +coxed +coxes +coxcomb +coxcombs +coxcomb's +coxswain +coxswains +coxswain's +coy +coyly +coyest +coyer +coyness +coyness +coyness's +coyote +coyotes +coyote's +coypu +coypus +coypu's +cozen +cozening +cozened +cozens +cozenage +cozenage's +cozily +coziness +coziness's +cozy +coziest +cozier +cozies +coziness +cozy's +cpd +cpl +cps +crab +crabs +crab's +crabbed +crabber +crabbers +crabber's +crabbily +crabbiness +crabbiness's +crabbing +crabby +crabbiest +crabbier +crabbiness +crabgrass +crabgrass's +crablike +crabwise +crack +crackly +cracking +crackings +cracked +cracker +crackers +cracks +crack's +crackdown +crackdowns +crackdown's +cracker +cracker's +crackerjack +crackerjacks +crackerjack's +crackhead +crackheads +crackhead's +crackle +crackling +cracklings +crackled +crackles +crackle's +crackling +crackling's +crackpot +crackpots +crackpot's +crackup +crackups +crackup's +cradle +cradling +cradled +cradles +cradle's +craft +crafting +crafted +crafts +craft's +craftily +craftiness +craftiness's +craftsman +craftsman's +craftsmanship +craftsmanship's +craftsmen +craftspeople +craftswoman +craftswoman's +craftswomen +crafty +craftiest +craftier +craftiness +crag +crags +crag's +cragginess +cragginess's +craggy +craggiest +craggier +cragginess +cram +crams +crammed +crammer +crammers +cramming +cramp +cramping +cramped +cramps +cramp's +cramping +cramping's +crampon +crampons +crampon's +cranberry +cranberries +cranberry's +crane +craning +craned +cranes +crane's +cranial +cranium +craniums +cranium's +crank +cranking +cranked +cranks +crank's +crankcase +crankcases +crankcase's +crankily +crankiness +crankiness's +crankshaft +crankshafts +crankshaft's +cranky +crankiest +crankier +crankiness +cranny +crannied +crannies +cranny's +crap +craps +crap's +crape +crapes +crape's +crapped +crapper +crappers +crappie +crappiest +crappier +crappies +crappie's +crapping +crappy +craps +craps's +crapshooter +crapshooters +crapshooter's +crash +crashing +crashed +crashes +crash's +crass +crassly +crassest +crasser +crassness +crassness +crassness's +crate +crating +crated +crater +craters +crates +crate's +crater +cratering +cratered +crater's +cravat +cravats +cravat's +crave +craving +cravings +craved +craves +craven +cravenly +cravens +cravenness +craven's +cravenness +cravenness's +craving +craving's +craw +craws +craw's +crawdad +crawdads +crawdad's +crawl +crawling +crawled +crawler +crawlers +crawls +crawl's +crawler +crawler's +crawlspace +crawlspaces +crawlspace's +crawly +crawliest +crawlier +crawlies +crawly's +cray +crays +crayfish +crayfishes +crayfish's +crayola +crayolas +crayon +crayoning +crayoned +crayons +crayon's +craze +crazing +crazed +crazes +craze's +crazily +craziness +craziness's +crazy +craziest +crazier +crazies +craziness +crazy's +creak +creaking +creaked +creaks +creak's +creakily +creakiness +creakiness's +creaky +creakiest +creakier +creakiness +cream +creaming +creamed +creamer +creamers +creams +cream's +creamer +creamer's +creamery +creameries +creamery's +creamily +creaminess +creaminess's +creamy +creamiest +creamier +creaminess +crease +creasing +creased +creases +crease's +increasing +decreasing +increased +decreased +increases +decreases +increase's +decrease's +increase +decrease +create +creative +creation +creating +created +creates +recreation +procreation +recreating +procreating +recreated +procreated +recreates +procreates +recreate +procreate +creation's +procreation's +creation +creations +creation's +recreations +recreation's +recreation +creationism +creationisms +creationism's +creationist +creationists +creationist's +creative +creatively +creatives +creativeness +creative's +creativeness +creativeness's +creativity +creativity's +creator +creators +creator's +creature +creatures +creature's +creche +creches +creche's +cred +credence +credence's +credential +credentialing +credentialed +credentials +credential's +credenza +credenzas +credenza's +credibility +credibility's +incredibility's +incredibility +credible +incredible +credibly +incredibly +credit +crediting +credited +credits +credit's +creditable +discrediting +discredited +discredits +discredit's +discreditable +discredit +creditably +discreditably +creditor +creditors +creditor's +creditworthy +creditworthiness +credo +credos +credo's +credulity +credulity's +incredulity's +incredulity +credulous +credulously +incredulously +incredulous +credulousness +credulousness's +creed +creeds +creed's +creek +creeks +creek's +creel +creels +creel's +creep +creeping +creeper +creepers +creeps +creep's +creeper +creeper's +creepily +creepiness +creepiness's +creepy +creepiest +creepier +creepiness +cremains +cremains's +cremate +cremation +cremations +cremating +cremated +cremates +cremation +cremation's +crematoria +crematorium +crematoriums +crematorium's +crematory +crematories +crematory's +creme +cremes +creme's +crenelate +crenelation +crenelations +crenelating +crenelated +crenelates +crenelation +crenelation's +creole +creoles +creole's +creosote +creosoting +creosoted +creosotes +creosote's +crepe +crepes +crepe's +crept +crepuscular +crescendo +crescendos +crescendo's +decrescendos +decrescendo's +decrescendo +crescent +crescents +crescent's +cress +cress's +crest +cresting +crested +crests +crest's +crestfallen +crestless +cretaceous +cretin +cretins +cretin's +cretinism +cretinism's +cretinous +cretonne +cretonne's +crevasse +crevasses +crevasse's +crevice +crevices +crevice's +crew +crewing +crewed +crews +crew's +crewel +crewel's +crewelwork +crewelwork's +crewman +crewman's +crewmen +crib +cribs +crib's +cribbage +cribbage's +cribbed +cribber +cribbers +cribber's +cribbing +crick +cricking +cricked +cricks +crick's +cricket +cricketing +cricketer +cricketers +crickets +cricket's +cricketer +cricketer's +crier +crier's +crikey +crime +crimes +crime's +criminal +criminally +criminals +criminal's +criminality +criminality's +criminalize +criminalizing +criminalized +criminalizes +decriminalizing +decriminalized +decriminalizes +decriminalize +criminologist +criminologists +criminologist's +criminology +criminology's +crimp +crimping +crimped +crimps +crimp's +crimson +crimsoning +crimsoned +crimsons +crimson's +cringe +cringing +cringed +cringes +cringe's +crinkle +crinkling +crinkled +crinkles +crinkle's +crinkly +crinkliest +crinklier +crinoline +crinolines +crinoline's +cripes +cripple +crippling +crippled +crippler +cripplers +cripples +cripple's +crippler +crippler's +crippleware +crippling +cripplingly +crises +crisis +crisis's +crisp +crisply +crisping +crisped +crispest +crisper +crisps +crispness +crisp's +crispbread +crispbreads +crispiness +crispiness's +crispness +crispness's +crispy +crispiest +crispier +crispiness +crisscross +crisscrossing +crisscrossed +crisscrosses +crisscross's +criteria +criterion +criterion's +critic +critics +critic's +critical +critically +uncritically +uncritical +criticality +criticism +criticisms +criticism's +criticize +criticizing +criticized +criticizer +criticizers +criticizes +criticizer +criticizer's +critique +critiquing +critiqued +critiques +critique's +critter +critters +critter's +croak +croaking +croaked +croaks +croak's +croaky +croakiest +croakier +crochet +crocheting +crocheted +crocheter +crocheters +crochets +crochet's +crocheter +crocheter's +crocheting +crocheting's +crock +crocked +crocks +crock's +crockery +crockery's +crocodile +crocodiles +crocodile's +crocus +crocuses +crocus's +croft +crofting +crofter +crofters +crofts +croissant +croissants +croissant's +crone +crones +crone's +crony +cronies +crony's +cronyism +cronyism's +crook +crooking +crooked +crooks +crook's +crooked +crookedly +crookedest +crookeder +crookedness +crookedness +crookedness's +crookneck +crooknecks +crookneck's +croon +crooning +crooned +crooner +crooners +croons +croon's +crooner +crooner's +crop +crops +crop's +cropland +croplands +cropland's +cropped +cropper +croppers +cropper's +cropping +croquet +croquet's +croquette +croquettes +croquette's +crosier +crosiers +crosier's +cross's +cross +crossing +crossed +crossest +crosses +recrossing +uncrossing +recrossed +uncrossed +recrosses +uncrosses +recross +uncross +crossbar +crossbars +crossbar's +crossbeam +crossbeams +crossbeam's +crossbones +crossbones's +crossbow +crossbows +crossbow's +crossbowman +crossbowman's +crossbowmen +crossbred +crossbreed +crossbreeding +crossbreeds +crossbreed's +crosscheck +crosschecking +crosschecked +crosschecks +crosscheck's +crosscurrent +crosscurrents +crosscurrent's +crosscut +crosscuts +crosscut's +crosscutting +crosser +crossfire +crossfires +crossfire's +crosshatch +crosshatching +crosshatched +crosshatches +crossing +crossings +crossing's +crossly +crossness +crossness's +crossover +crossovers +crossover's +crosspatch +crosspatches +crosspatch's +crosspiece +crosspieces +crosspiece's +crossroad +crossroads +crossroad's +crossroads +crossroads's +crosstown +crosswalk +crosswalks +crosswalk's +crosswind +crosswinds +crosswind's +crosswise +crossword +crosswords +crossword's +crotch +crotches +crotch's +crotchet +crotchets +crotchet's +crotchety +crouch +crouching +crouched +crouches +crouch's +croup +croup's +croupier +croupier's +croupy +croupiest +croupier +croupiers +crouton +croutons +crouton's +crow +crowing +crowed +crows +crow's +crowbar +crowbars +crowbar's +crowd +crowding +crowded +crowds +crowd's +crowded +uncrowded +crowdfund +crowdfunding +crowdfunded +crowdfunds +crowfeet +crowfoot +crowfoots +crowfoot's +crown +crowning +crowned +crowns +crown's +crowned +uncrowned +crucial +crucially +crucible +crucibles +crucible's +crucifix +crucifixes +crucifix's +crucifixion +crucifixions +crucifixion's +cruciform +cruciforms +cruciform's +crucify +crucifying +crucified +crucifies +crud +crud's +cruddy +cruddiest +cruddier +crude +crudely +crudest +cruder +crudeness +crude's +crudeness +crudeness's +crudites +crudites's +crudity +crudities +crudity's +cruel +cruelly +cruelest +crueler +cruelness +cruelness +cruelness's +cruelty +cruelties +cruelty's +cruet +cruets +cruet's +cruft +crufted +crufts +crufty +cruise +cruising +cruised +cruiser +cruisers +cruises +cruise's +cruiser +cruiser's +cruller +crullers +cruller's +crumb +crumbly +crumbing +crumbed +crumbs +crumb's +crumble +crumbling +crumbled +crumbles +crumble's +crumbliness +crumbliness's +crumbly +crumbliest +crumblier +crumbliness +crumby +crumbiest +crumbier +crumminess +crumminess's +crummy +crummiest +crummier +crumminess +crumpet +crumpets +crumpet's +crumple +crumpling +crumpled +crumples +crumple's +crunch +crunching +crunched +cruncher +crunches +crunch's +crunchiness +crunchiness's +crunchy +crunchiest +crunchier +crunchiness +crupper +cruppers +crupper's +crusade +crusading +crusaded +crusader +crusaders +crusades +crusade's +crusader +crusader's +cruse +cruses +cruse's +crush +crushing +crushed +crusher +crushers +crushes +crush's +crusher +crusher's +crushing +crushingly +crust +crusting +crusted +crusts +crust's +crustacean +crustaceans +crustacean's +crustal +crustily +crustiness +crustiness's +crusty +crustiest +crustier +crustiness +crutch +crutches +crutch's +crux +cruxes +crux's +cry +crying +cryings +cried +crier +criers +cries +cry's +crybaby +crybabies +crybaby's +cryogenic +cryogenics +cryogenics +cryogenics's +cryonics +cryosurgery +cryosurgery's +crypt +crypts +crypt's +cryptic +cryptically +cryptogram +cryptograms +cryptogram's +cryptographer +cryptographers +cryptographer's +cryptography +cryptography's +crystal +crystals +crystal's +crystalline +crystallization +crystallization's +crystallize +crystallizing +crystallized +crystallizes +recrystallizing +recrystallized +recrystallizes +recrystallize +crystallographic +crystallography +ct +ctn +ctr +cu +cub +cubing +cubed +cuber +cubers +cubs +cub's +cubbyhole +cubbyholes +cubbyhole's +cube +cubes +cube's +cuber +cuber's +cubic +cubical +cubicle +cubicles +cubicle's +cubism +cubism's +cubist +cubists +cubist's +cubit +cubits +cubit's +cuboid +cuboids +cuckold +cuckolding +cuckolded +cuckolds +cuckold's +cuckoldry +cuckoldry's +cuckoo +cuckoos +cuckoo's +cucumber +cucumbers +cucumber's +cud +cuds +cud's +cuddle +cuddling +cuddled +cuddles +cuddle's +cuddly +cuddliest +cuddlier +cudgel +cudgeling +cudgelings +cudgeled +cudgels +cudgel's +cue +cuing +cued +cues +cue's +cuff +cuffing +cuffed +cuffs +cuff's +cuisine +cuisines +cuisine's +culinary +cull +culling +culled +culls +cull's +culminate +culmination +culminations +culminating +culminated +culminates +culmination +culmination's +culotte +culottes +culotte's +culpability +culpability's +culpable +inculpable +culpably +culprit +culprits +culprit's +cult +cults +cult's +cultism +cultism's +cultist +cultists +cultist's +cultivable +cultivar +cultivars +cultivar's +cultivate +cultivation +cultivating +cultivated +cultivates +cultivatable +cultivated +uncultivated +cultivation +cultivation's +cultivator +cultivators +cultivator's +cultural +culturally +culture +culturing +cultured +cultures +culture's +cultured +uncultured +culvert +culverts +culvert's +cum +cums +cum's +cumber +cumbering +cumbered +cumbers +cumbersome +cumbersomeness +cumbersomeness +cumbersomeness's +cumbrous +cumin +cumin's +cummerbund +cummerbunds +cummerbund's +cumming +cumulative +cumulatively +cumuli +cumulonimbi +cumulonimbus +cumulonimbus's +cumulus +cumulus's +cuneiform +cuneiform's +cunnilingus +cunnilingus's +cunning +cunningly +cunningest +cunninger +cunning's +cunt +cunts +cunt's +cup +cups +cup's +cupboard +cupboards +cupboard's +cupcake +cupcakes +cupcake's +cupful +cupfuls +cupful's +cupid +cupids +cupid's +cupidity +cupidity's +cupola +cupolaed +cupolas +cupola's +cuppa +cuppas +cupped +cupping +cupric +cur +curly +curs +cur's +curability +curability's +curacao +curacy +curacies +curacy's +curare +curare's +curate +curative +curating +curated +curates +curate's +curative +curatives +curative's +curator +curators +curator's +procurators +procurator's +procurator +curatorial +curb +curbing +curbed +curbs +curb's +curbing +curbing's +curbside +curbstone +curbstones +curbstone's +curd +curds +curd's +curdle +curdling +curdled +curdles +cure's +cure +curing +cured +curer +curers +cures +curable +procuring +procured +procurer +procurers +procures +procurable +procure +cured +uncured +curer +curer's +procurer's +procurer +curettage +curettage's +curfew +curfews +curfew's +curia +curia's +curiae +curie +curies +curie's +curio +curios +curio's +curiosity +curiosities +curiosity's +curious +curiously +curiousness +curiousness +curiousness's +curium +curium's +curl's +curl +curling +curled +curls +uncurling +uncurled +uncurls +uncurl +curler +curlers +curler's +curlew +curlews +curlew's +curlicue +curlicuing +curlicued +curlicues +curlicue's +curliness +curliness's +curling +curling's +curly +curliest +curlier +curliness +curmudgeon +curmudgeonly +curmudgeons +curmudgeon's +currant +currants +currant's +currency +currencies +currency's +current's +current +currently +recurrently +concurrently +recurrent +concurrent +currents +curricula +curricular +curriculum +curriculum's +curry +currying +curried +curries +curry's +currycomb +currycombing +currycombed +currycombs +currycomb's +curse +cursive +cursing +cursed +curses +curse's +cursed +cursedly +cursive's +cursive +cursively +recursively +discursively +recursive +discursive +cursor +cursors +cursor's +cursorily +cursoriness +cursoriness's +cursory +cursoriness +curt +curtly +curtest +curter +curtness +curtail +curtailing +curtailed +curtails +curtailment +curtailment +curtailments +curtailment's +curtain +curtaining +curtained +curtains +curtain's +curtness +curtness's +curtsy +curtsying +curtsied +curtsies +curtsy's +curvaceous +curvaceousness +curvaceousness +curvaceousness's +curvature +curvatures +curvature's +curve +curving +curved +curves +curve's +curvy +curviest +curvier +cushion +cushioning +cushioned +cushions +cushion's +cushy +cushiest +cushier +cusp +cusps +cusp's +cuspid +cuspids +cuspid's +cuspidor +cuspidors +cuspidor's +cuss's +cuss +cussing +cussed +cusses +discussing +concussing +discussed +concussed +discusses +concusses +discuss +concuss +cussed +cussedly +cussedness +custard +custards +custard's +custodial +custodian +custodians +custodian's +custodianship +custodianship's +custody +custody's +custom +customer +customers +customs +custom's +customarily +customary +uncustomary +customer +customer's +customhouse +customhouses +customhouse's +customization +customization's +customize +customizing +customized +customizes +cut +cutest +cuter +cuts +cut's +cutaneous +cutaway +cutaways +cutaway's +cutback +cutbacks +cutback's +cute +cutely +cuteness +cuteness +cuteness's +cutesy +cutesiest +cutesier +cutey +cuteys +cuticle +cuticles +cuticle's +cutie +cuties +cutie's +cutlass +cutlasses +cutlass's +cutler +cutlers +cutler's +cutlery +cutlery's +cutlet +cutlets +cutlet's +cutoff +cutoffs +cutoff's +cutout +cutouts +cutout's +cutter +cutters +cutter's +cutthroat +cutthroats +cutthroat's +cutting +cuttingly +cuttings +cutting's +cuttlefish +cuttlefishes +cuttlefish's +cutup +cutups +cutup's +cutworm +cutworms +cutworm's +cw +cwt +cyan +cyan's +cyanide +cyanide's +cyberbully +cyberbullies +cyberbully's +cybercafe +cybercafes +cybernetic +cybernetics +cybernetics +cybernetics's +cyberpunk +cyberpunks +cyberpunk's +cybersex +cyberspace +cyberspaces +cyberspace's +cyborg +cyborgs +cyborg's +cyclamen +cyclamens +cyclamen's +cycle +cycling +cycled +cycles +cycle's +recycling +recycled +recycles +recycle's +recycle +cyclic +cyclical +cyclically +cyclist +cyclists +cyclist's +cyclometer +cyclometers +cyclometer's +cyclone +cyclones +cyclone's +cyclonic +cyclopedia +cyclopedias +cyclopedia's +cyclopes +cyclops +cyclops's +cyclotron +cyclotrons +cyclotron's +cygnet +cygnets +cygnet's +cylinder +cylinders +cylinder's +cylindrical +cymbal +cymbals +cymbal's +cymbalist +cymbalists +cymbalist's +cynic +cynics +cynic's +cynical +cynically +cynicism +cynicism's +cynosure +cynosures +cynosure's +cypress +cypresses +cypress's +cyst +cysts +cyst's +cystic +cystitis +cytokines +cytologist +cytologists +cytologist's +cytology +cytology's +cytoplasm +cytoplasm's +cytoplasmic +cytosine +cytosine's +czar +czars +czar's +czarina +czarinas +czarina's +czarism +czarist +czarists +czarist's +d'Arezzo +d'Arezzo's +d'Estaing +d'Estaing's +d +den +dens +ding +dings +dB +dab +dabs +dab's +dabbed +dabber +dabbers +dabber's +dabbing +dabble +dabbling +dabbled +dabbler +dabblers +dabbles +dabbler +dabbler's +dace +daces +dace's +dacha +dachas +dacha's +dachshund +dachshunds +dachshund's +dactyl +dactyls +dactyl's +dactylic +dactylics +dactylic's +dad +dads +dad's +dadaism +dadaism's +dadaist +dadaists +dadaist's +daddy +daddies +daddy's +dado +dado's +dadoes +daemon +daemons +daemon's +daemonic +daffiness +daffiness's +daffodil +daffodils +daffodil's +daffy +daffiest +daffier +daffiness +daft +daftly +daftest +dafter +daftness +daftness +daftness's +dag +dags +dagger +daggers +dagger's +dago +dagos +dagoes +daguerreotype +daguerreotyping +daguerreotyped +daguerreotypes +daguerreotype's +dahlia +dahlias +dahlia's +dailiness +dailiness's +daily +dailies +dailiness +daily's +daintily +daintiness +daintiness's +dainty +daintiest +daintier +dainties +daintiness +dainty's +daiquiri +daiquiris +daiquiri's +dairy +dairying +dairies +dairy's +dairying +dairying's +dairymaid +dairymaids +dairymaid's +dairyman +dairyman's +dairymen +dairywoman +dairywoman's +dairywomen +dais +daises +dais's +daisy +daisies +daisy's +dale +dales +dale's +dalliance +dalliances +dalliance's +dallier +dallier's +dally +dallying +dallied +dallier +dalliers +dallies +dalmatian +dalmatians +dalmatian's +dam +dams +dam's +damage +damaging +damaged +damages +damage's +damageable +damaged +undamaged +damages +damages's +damask +damasking +damasked +damasks +damask's +dame +dames +dame's +dammed +damming +dammit +damn +damning +damned +damns +damn's +damnable +damnably +damnation +damnation's +damned +damnedest +damp +dampen +dampens +damply +damping +damped +dampest +damper +dampers +damps +dampness +damp's +dampen +dampening +dampened +dampener +dampeners +dampener +dampener's +damper +damper's +dampness +dampness's +damsel +damsels +damsel's +damselfly +damselflies +damselfly's +damson +damsons +damson's +dance +dancing +danced +dancer +dancers +dances +dance's +dancer +dancer's +dancing +dancing's +dandelion +dandelions +dandelion's +dander +dander's +dandify +dandifying +dandified +dandifies +dandle +dandling +dandled +dandles +dandruff +dandruff's +dandy +dandiest +dandier +dandies +dandy's +dang +danging +danged +danger +dangers +dangs +danger +danger's +dangerous +dangerously +dangle +dangling +dangled +dangler +danglers +dangles +dangler +dangler's +danish +danishes +danish's +dank +dankly +dankest +danker +dankness +dankness +dankness's +danseuse +danseuses +danseuse's +dapper +dapperest +dapperer +dapple +dappling +dappled +dapples +dapple's +dare +daring +dared +darer +darers +dares +dare's +daredevil +daredevils +daredevil's +daredevilry +daredevilry's +darer +darer's +daresay +daring +daringly +daring's +dark +darken +darkens +darkly +darkest +darker +darkness +dark's +darken +darkening +darkened +darkener +darkeners +darkener +darkener's +darkie +darkies +darkness +darkness's +darkroom +darkrooms +darkroom's +darling +darlings +darling's +darn +darning +darned +darner +darners +darns +darn's +darned +darnedest +darneder +darner +darner's +dart +darting +darted +darter +darters +darts +dart's +dartboard +dartboards +dartboard's +darter +darter's +dash +dashing +dashed +dasher +dashers +dashes +dash's +dashboard +dashboards +dashboard's +dasher +dasher's +dashiki +dashikis +dashiki's +dashing +dashingly +dastard +dastardly +dastards +dastard's +dastardliness +dastardliness's +data +database +databases +database's +datatype +date +dative +dating +dated +dater +daters +dates +date's +datebook +datebooks +dated +undated +dateless +dateline +datelining +datelined +datelines +dateline's +dater +dater's +dative +datives +dative's +datum +datum's +daub +daubing +daubed +dauber +daubers +daubs +daub's +dauber +dauber's +daughter +daughterly +daughters +daughter's +daunt +daunting +daunted +daunts +daunting +dauntingly +dauntless +dauntlessly +dauntlessness +dauntlessness +dauntlessness's +dauphin +dauphins +dauphin's +davenport +davenports +davenport's +davit +davits +davit's +dawdle +dawdling +dawdled +dawdler +dawdlers +dawdles +dawdler +dawdler's +dawn +dawning +dawned +dawns +dawn's +day +days +day's +daybed +daybeds +daybed's +daybreak +daybreak's +daycare +daycare's +daydream +daydreaming +daydreamed +daydreamer +daydreamers +daydreams +daydream's +daydreamer +daydreamer's +daylight +daylights +daylight's +daylights +daylights's +daylong +daytime +daytime's +daze +dazing +dazed +dazes +daze's +dazed +dazedly +dazzle +dazzling +dazzled +dazzler +dazzlers +dazzles +dazzle's +dazzler +dazzler's +dazzling +dazzlingly +db +dbl +dc +dd +dding +dded +dds +dded +prodded +dding +prodding +deacon +deacons +deacon's +deaconess +deaconesses +deaconess's +dead +deaden +deadens +deadly +deadest +deader +dead's +deadbeat +deadbeats +deadbeat's +deadbolt +deadbolts +deadbolt's +deaden +deadening +deadened +deadhead +deadheading +deadheaded +deadheads +deadline +deadlines +deadline's +deadliness +deadliness's +deadlock +deadlocking +deadlocked +deadlocks +deadlock's +deadly +deadliest +deadlier +deadliness +deadpan +deadpans +deadpan's +deadpanned +deadpanning +deadwood +deadwood's +deaf +deafen +deafens +deafest +deafer +deafness +deafen +deafening +deafened +deafening +deafeningly +deafness +deafness's +deal +dealing +dealings +dealer +dealers +deals +deal's +dealer +dealer's +dealership +dealerships +dealership's +dealing +dealing's +dealt +dean +dean's +deanery +deaneries +deanery's +deanship +deanship's +dear +dearth +dearly +dearest +dearer +dears +dearness +dear's +dearest +dearests +dearness +dearness's +dearth +dearth's +dearths +deary +dearies +deary's +death +deathly +death's +deathbed +deathbeds +deathbed's +deathblow +deathblows +deathblow's +deathless +deathlessly +deathlike +deaths +deathtrap +deathtraps +deathtrap's +deathwatch +deathwatches +deathwatch's +deaves +deb +debs +deb's +debacle +debacles +debacle's +debarkation +debarkation's +debarment +debarment's +debate +debater +debaters +debate's +debatable +debater +debater's +debating +debating's +debauch +debauching +debauched +debauches +debauch's +debauchee +debauchees +debauchee's +debauchery +debaucheries +debauchery's +debenture +debentures +debenture's +debilitate +debilitation +debilitating +debilitated +debilitates +debilitation +debilitation's +debility +debilities +debility's +debit +debited +debonair +debonairly +debonairness +debonairness +debonairness's +debouch +debouching +debouched +debouches +debridement +debris +debris's +debt +debts +debt's +debtor +debtors +debtor's +debugger +debuggers +debut +debuting +debuted +debut's +debutante +debutantes +debutante's +decade +decades +decade's +decadence +decadence's +decadency +decadency's +decadent +decadently +decadents +decadent's +decaf +decafs +decaf's +decaffeinate +decaffeinating +decaffeinated +decaffeinates +decagon +decagons +decagon's +decal +decals +decal's +decampment +decampment's +decapitate +decapitation +decapitations +decapitating +decapitated +decapitates +decapitator +decapitators +decapitator's +decathlete +decathletes +decathlon +decathlons +decathlon's +decay +decaying +decayed +deceased +deceased's +decedent +decedents +decedent's +deceit +deceits +deceit's +deceitful +deceitfully +deceitfulness +deceitfulness +deceitfulness's +deceive +deceiving +deceived +deceives +undeceiving +undeceived +undeceives +undeceive +deceiver +deceivers +deceiver's +deceiving +deceivingly +decelerate +deceleration +decelerating +decelerated +decelerates +deceleration +deceleration's +decelerator +decelerators +decelerator's +decency +decencies +decency's +indecencies +indecency's +indecency +decennial +decennials +decennial's +decent +decently +indecently +indecent +deception +deceptions +deception's +deceptive +deceptively +deceptiveness +deceptiveness +deceptiveness's +decibel +decibels +decibel's +decidable +undecidable +decide +deciding +decided +decider +deciders +decides +decidable +decided +decidedly +deciduous +deciliter +deciliters +deciliter's +decimal +decimals +decimal's +decimalization +decimate +decimation +decimating +decimated +decimates +decimation +decimation's +decimeter +decimeters +decimeter's +decipherable +indecipherable +undecipherable +decision +decision's +indecision's +indecision +decisions +decisive +decisively +decisiveness +indecisively +indecisiveness +indecisive +decisiveness +decisiveness's +indecisiveness's +indecisiveness +deck +decking +decked +decks +deck's +deckchair +deckchairs +deckhand +deckhands +deckhand's +deckle +deckles +declamation +declamations +declamation's +declamatory +declaration +declarations +declaration's +declarative +declaratory +declare +declaring +declared +declarer +declarers +declares +declarable +declared +undeclared +declarer +declarer's +declension +declensions +declension's +declination +declination's +decline +declining +declined +decliner +decliners +declines +decline's +decliner +decliner's +declivity +declivities +declivity's +decoherence +decolletage +decolletages +decolletage's +decollete +decongestant +decongestants +decongestant's +deconstructionism +decor +decors +decor's +decorate +decorative +decoration +decorating +decorated +decorates +redecoration +redecorating +redecorated +redecorates +redecorate +decorating +decorating's +decoration +decoration's +redecoration's +redecoration +decorations +decorative +decoratively +decorator +decorators +decorator's +decorous +decorously +indecorously +indecorous +decorousness +decorousness's +decorum +decorum's +decoupage +decoupaging +decoupaged +decoupages +decoupage's +decoy +decoying +decoyed +decoys +decoy's +decreasing +decreasingly +decree +decreed +decrees +decree's +decreeing +decrement +decremented +decrements +decrepit +decrepitude +decrepitude's +decriminalization +decriminalization's +decry +decrying +decried +decries +decryption +dedicate +dedicating +dedicated +dedicates +rededicating +rededicated +rededicates +rededicate +dedication +dedications +dedication's +dedicator +dedicators +dedicator's +dedicatory +deduce +deducing +deduced +deduces +deducible +deduct +deductive +deducting +deducted +deductible +deductibles +deductible's +deduction +deductions +deduction's +deductive +deductively +deed +deeding +deeded +deejay +deejays +deejay's +deem +deeming +deemed +deems +redeeming +redeemed +redeems +redeem +deep +deepen +deepens +deeply +deepest +deeper +deeps +deepness +deep's +deepen +deepening +deepened +deepness +deepness's +deer +deer's +deerskin +deerskin's +deerstalker +deerstalkers +def +defers +defacement +defacement's +defacer +defacers +defacer's +defalcate +defalcation +defalcations +defalcating +defalcated +defalcates +defalcation +defalcation's +defamation +defamation's +defamatory +defame +defaming +defamed +defamer +defamers +defames +defamer +defamer's +defaulter +defaulters +defaulter's +defeat +defeating +defeated +defeater +defeaters +defeats +defeat's +defeated +undefeated +defeater +defeater's +defeatism +defeatism's +defeatist +defeatists +defeatist's +defecate +defecation +defecating +defecated +defecates +defecation +defecation's +defect +defective +defecting +defected +defects +defect's +defection +defections +defection's +defective +defectively +defectives +defectiveness +defective's +defectiveness +defectiveness's +defector +defectors +defector's +defendant +defendants +defendant's +defended +undefended +defenestration +defenestrations +defense +defensive +defensing +defensed +defenses +defense's +defenseless +defenselessly +defenselessness +defenselessness +defenselessness's +defensible +indefensible +defensibly +indefensibly +defensive +defensively +defensiveness +defensive's +defensiveness +defensiveness's +deference +deference's +deferential +deferentially +deferral +deferrals +deferral's +deferred +deferring +deffer +deffest +defiant +defiantly +defibrillation +defibrillator +defibrillators +deficiency +deficiencies +deficiency's +deficient +deficit +deficits +deficit's +defilement +defilement's +definable +indefinable +undefinable +define +defining +defined +defines +redefining +redefined +redefines +redefine +defined +undefined +definer +definers +definer's +definite +definitive +definitely +definiteness +indefinitely +indefiniteness +indefinite +definiteness +definiteness's +indefiniteness's +indefiniteness +definition +definition's +redefinition's +redefinition +definitions +definitive +definitively +deflate +deflation +deflating +deflated +deflates +deflation +deflation's +deflationary +deflect +deflective +deflecting +deflected +deflects +deflection +deflections +deflection's +deflector +deflectors +deflector's +defogger +defoggers +defogger's +defoliant +defoliants +defoliant's +defoliate +defoliation +defoliating +defoliated +defoliates +defoliation +defoliation's +defoliator +defoliators +defoliator's +deformity +deformities +deformity's +defraud +defrauding +defrauded +defrauder +defrauders +defrauds +defrauder +defrauder's +defrayal +defrayal's +defrock +defrocking +defrocked +defroster +defrosters +defroster's +deft +deftly +deftest +defter +deftness +deftness +deftness's +defunct +defy +defying +defied +defies +deg +degeneracy +degeneracy's +degenerate +degenerative +degenerate's +degrade +degradable +degree +degrees +degree's +dehydrator +dehydrators +dehydrator's +dehydrogenase +deicer +deicers +deicer's +deification +deification's +deify +deification +deifying +deified +deifies +deign +deigning +deigned +deigns +deist +deists +deist's +deistic +deity +deities +deity's +deject +dejecting +dejected +dejects +dejected +dejectedly +dejection +dejection's +delay +delayed +delayer +delayers +delectable +delectably +delectation +delectation's +delegate +delegating +delegated +delete +deletion +deletions +deleting +deleted +deletes +deleterious +deletion +deletion's +delft +delft's +delftware +delftware's +deli +delis +deli's +deliberate +deliberative +deliberations +deliberately +deliberateness +deliberateness +deliberateness's +delicacy +delicacies +delicacy's +indelicacies +indelicacy's +indelicacy +delicate +delicately +indelicately +indelicate +delicateness +delicateness's +delicatessen +delicatessens +delicatessen's +delicious +deliciously +deliciousness +deliciousness +deliciousness's +delighted +delightedly +delightful +delightfully +deliminator +delineate +delineation +delineations +delineating +delineated +delineates +delineation +delineation's +delinquency +delinquencies +delinquency's +delinquent +delinquently +delinquents +delinquent's +deliquesce +deliquescing +deliquesced +deliquesces +deliquescent +delirious +deliriously +deliriousness +deliriousness +deliriousness's +delirium +deliriums +delirium's +deliver +delivering +delivered +delivers +redelivering +redelivered +redelivers +redeliver +deliverable +deliverance +deliverance's +delivered +undelivered +deliverer +deliverers +deliverer's +dell +dells +dell's +delphinium +delphiniums +delphinium's +delta +deltas +delta's +delude +deluding +deluded +deludes +deluge +deluging +deluged +deluges +deluge's +delusion +delusions +delusion's +delusional +delusive +delusively +deluxe +delve +delving +delved +delver +delvers +delves +delver +delver's +demagogic +demagogically +demagogue +demagogues +demagogue's +demagoguery +demagoguery's +demagogy +demagogy's +demand +demanding +demanded +demands +demand's +demanding +undemanding +demarcate +demarcation +demarcations +demarcating +demarcated +demarcates +demarcation +demarcation's +demean +demeaning +demeaned +demeans +demeanor +demeanor's +demented +dementedly +dementia +dementia's +demesne +demesnes +demesne's +demigod +demigods +demigod's +demigoddess +demigoddesses +demigoddess's +demijohn +demijohns +demijohn's +demimondaine +demimondaines +demimondaine's +demimonde +demimonde's +demise +demising +demised +demises +demise's +demitasse +demitasses +demitasse's +demo +demoing +demoed +demo's +democracy +democracies +democracy's +democrat +democrats +democrat's +democratic +undemocratic +democratically +democratization +democratization's +democratize +democratizing +democratized +democratizes +demode +demographer +demographers +demographer's +demographic +demographics +demographic's +demographically +demographics +demographics's +demography +demography's +demolish +demolishing +demolished +demolishes +demolition +demolitions +demolition's +demon +demons +demon's +demonetization +demonetization's +demoniac +demoniacal +demoniacally +demonic +demonically +demonize +demonizing +demonized +demonizes +demonology +demonologies +demonology's +demonstrability +demonstrable +indemonstrable +demonstrably +demonstrate +demonstrative +demonstration +demonstrations +demonstrating +demonstrated +demonstrates +demonstration +demonstration's +demonstrative +demonstratively +demonstratives +demonstrativeness +demonstrative's +demonstrativeness +demonstrativeness's +demonstrator +demonstrators +demonstrator's +demote +demoting +demoted +demotic +demount +demulcent +demulcents +demulcent's +demur +demurest +demurer +demurs +demur's +demure +demurely +demureness +demureness +demureness's +demurral +demurrals +demurral's +demurred +demurrer +demurrers +demurrer's +demurring +den +den's +denationalization +denaturation +denature +denaturing +denatured +dendrite +dendrites +dendrite's +dengue +dengue's +deniability +deniable +undeniable +denial +denials +denial's +denier +denier's +denigrate +denigration +denigrating +denigrated +denigrates +denigration +denigration's +denim +denims +denim's +denitrification +denizen +denizens +denizen's +denominational +denotative +denouement +denouements +denouement's +denounce +denouncing +denounced +denounces +denouncement +denouncement +denouncements +denouncement's +dense +densely +densest +denser +denseness +denseness +denseness's +density +densities +density's +dent +denting +dented +dents +dent's +indenting +indented +indents +indent's +indent +dental +dentally +dentifrice +dentifrices +dentifrice's +dentin +dentin's +dentist +dentists +dentist's +dentistry +dentistry's +dentition +dentition's +denture +dentures +denture's +indentures +indenture's +indenture +denuclearize +denuclearizing +denuclearized +denuclearizes +denudation +denudation's +denude +denuding +denuded +denudes +denunciation +denunciations +denunciation's +deny +denying +denied +denier +deniers +denies +deodorant +deodorants +deodorant's +deodorization +deodorization's +deodorize +deodorizing +deodorized +deodorizer +deodorizers +deodorizes +deodorizer +deodorizer's +departed +departed's +department +departments +department's +departmental +departmentally +departmentalization +departmentalization's +departmentalize +departmentalizing +departmentalized +departmentalizes +departure +departures +departure's +dependability +dependability's +dependable +undependable +dependably +dependence +dependence's +independence's +independence +dependency +dependencies +dependency's +dependent +dependently +dependents +dependent's +independently +independents +independent's +independent +depict +depicting +depicted +depicts +depiction +depictions +depiction's +depilatory +depilatories +depilatory's +deplete +depletion +depleting +depleted +depletes +depletion +depletion's +deplorably +deplore +deploring +deplored +deplores +deplorable +deploy +deploying +deployed +deploys +deployment +redeploying +redeployed +redeploys +redeployment +redeploy +deployment +deployment's +redeployment's +redeployment +deployments +deponent +deponents +deponent's +deportation +deportations +deportation's +deportee +deportees +deportee's +deportment +deportment's +deposit +depositing +deposited +deposits +deposit's +redepositing +redeposited +redeposits +redeposit's +redeposit +depositor +depositors +depositor's +depository +depositories +depository's +deprave +depraving +depraved +depraves +depravity +depravities +depravity's +deprecate +deprecation +deprecating +deprecated +deprecates +deprecating +deprecatingly +deprecation +deprecation's +deprecatory +depreciate +depreciation +depreciating +depreciated +depreciates +depreciation +depreciation's +depredation +depredations +depredation's +depressant +depressants +depressant's +depressing +depressingly +depression +depressions +depression's +depressive +depressives +depressive's +depressor +depressors +depressor's +depressurization +deprive +depriving +deprived +deprives +deprogramming +depth +depth's +depths +deputation +deputations +deputation's +depute +deputing +deputed +deputes +deputize +deputizing +deputized +deputizes +deputy +deputies +deputy's +derailleur +derailleurs +derailleur's +derailment +derailments +derailment's +derangement +derangement's +derby +derbies +derby's +derelict +derelicts +derelict's +dereliction +dereliction's +deride +deriding +derided +derides +derision +derision's +derisive +derisively +derisiveness +derisiveness +derisiveness's +derisory +derivation +derivations +derivation's +derivative +derivatives +derivative's +derive +derivable +dermal +dermatitis +dermatitis's +dermatological +dermatologist +dermatologists +dermatologist's +dermatology +dermatology's +dermis +dermis's +derogate +derogation +derogating +derogated +derogates +derogation +derogation's +derogatorily +derogatory +derrick +derricks +derrick's +derriere +derrieres +derriere's +derringer +derringers +derringer's +derv +dervish +dervishes +dervish's +desalinate +desalination +desalinating +desalinated +desalinates +desalination +desalination's +desalinization +desalinization's +desalinize +desalinizing +desalinized +desalinizes +descant +descant's +descend +descending +descended +descends +condescending +condescended +condescends +condescend +descendant +descendants +descendant's +descender +describable +indescribable +describe +describing +described +describer +describers +describable +describer +describer's +description +descriptions +description's +descriptive +descriptively +descriptiveness +descriptiveness +descriptiveness's +descriptor +descriptors +descry +descrying +descried +descries +desecrate +desecration +desecrating +desecrated +desecrates +desecration +desecration's +deselection +desert +deserting +deserted +deserter +deserters +deserts +desert's +deserter +deserter's +desertification +desertion +desertions +desertion's +deserved +deservedly +undeservedly +undeserved +deserving +undeserving +desiccant +desiccants +desiccant's +desiccate +desiccation +desiccating +desiccated +desiccates +desiccation +desiccation's +desiccator +desiccators +desiccator's +desiderata +desideratum +desideratum's +design +designing +designed +designs +redesigning +redesigned +redesigns +redesign +designate +designation +designations +designating +designated +designates +designation +designation's +desirability +desirability's +undesirability's +undesirability +desirableness +desirableness's +desirably +undesirably +desire +desirable +desired +undesired +desirous +desist +desisting +desisted +desists +desk +desks +desk's +deskill +deskilling +desktop +desktops +desktop's +desolate +desolation +desolately +desolating +desolated +desolates +desolateness +desolateness +desolateness's +desolation +desolation's +despair +despairing +despaired +despairs +despair's +despairing +despairingly +desperado +desperado's +desperadoes +desperate +desperation +desperately +desperateness +desperateness +desperateness's +desperation +desperation's +despicable +despicably +despise +despising +despised +despises +despite +despoilment +despoilment's +despondence +despondence's +despondency +despondency's +despondent +despondently +despotic +despotically +despotism +despotism's +dessert +desserts +dessert's +dessertspoon +dessertspoons +dessertspoonful +dessertspoonfuls +destination +destinations +destination's +destine +destining +destined +destines +destiny +destinies +destiny's +destitute +destitution +destitution +destitution's +destroy +destroying +destroyed +destroyer +destroyers +destroys +destroyer +destroyer's +destruct +destructive +destructing +destructed +destructs +destruct's +destructibility +destructibility's +indestructibility's +indestructibility +destructible +indestructible +destruction +destruction's +destructive +destructively +destructiveness +destructiveness +destructiveness's +desuetude +desuetude's +desultorily +desultory +detach +detaching +detached +detaches +detachable +detachment +detachment +detachments +detachment's +detain +detaining +detained +detains +detainment +detainee +detainees +detainee's +detainment +detainment's +detect +detective +detecting +detected +detects +detectable +detectable +undetectable +detected +undetected +detection +detection's +detective +detectives +detective's +detector +detectors +detector's +detente +detention +detentions +detentes +detente's +detention +detention's +deter +deters +determent +detergent +detergents +detergent's +deteriorate +deterioration +deteriorating +deteriorated +deteriorates +deterioration +deterioration's +determent +determent's +determinable +indeterminable +determinant +determinants +determinant's +determinate +determine +determining +determined +determines +redetermining +redetermined +redetermines +redetermine +determined +undetermined +determinedly +determiner +determiners +determiner's +determinism +determinism's +deterministic +deterred +undeterred +deterrence +deterrence's +deterrent +deterrents +deterrent's +deterring +detestably +detestation +detestation's +dethrone +dethroning +dethroned +dethrones +dethronement +dethronement +dethronement's +detonate +detonation +detonations +detonating +detonated +detonates +detonation +detonation's +detonator +detonators +detonator's +detox +detoxing +detoxed +detoxes +detox's +detoxification +detoxification's +detoxify +detoxification +detoxifying +detoxified +detoxifies +detract +detracting +detracted +detriment +detriments +detriment's +detrimental +detrimentally +detritus +detritus's +deuce +deuces +deuce's +deuterium +deuterium's +devastate +devastation +devastating +devastated +devastates +devastating +devastatingly +devastation +devastation's +devastator +devastators +devastator's +develop +developing +developed +develops +development +redeveloping +redeveloped +redevelops +redevelopment +redevelop +developed +undeveloped +developer +developers +developer's +development +developments +development's +redevelopments +redevelopment's +redevelopment +developmental +developmentally +deviance +deviance's +deviancy +deviancy's +deviant +deviants +deviant's +deviate +deviation +deviations +deviating +deviated +deviates +deviate's +deviating +undeviating +deviation +deviation's +devil +deviling +deviled +devils +devil's +devilment +devilish +devilishly +devilishness +devilishness +devilishness's +devilment +devilment's +devilry +devilries +devilry's +deviltry +deviltries +deviltry's +devious +deviously +deviousness +deviousness +deviousness's +devoid +devolution +devolution's +devolve +devolving +devolved +devolves +devoted +devotedly +devotee +devotees +devotee's +devotion +devotions +devotion's +devotional +devotionals +devotional's +devour +devouring +devoured +devours +devout +devoutly +devoutest +devouter +devoutness +devoutness +devoutness's +dew +dew's +dewberry +dewberries +dewberry's +dewclaw +dewclaws +dewclaw's +dewdrop +dewdrops +dewdrop's +dewiness +dewiness's +dewlap +dewlaps +dewlap's +dewy +dewiest +dewier +dewiness +dexterity +dexterity's +dexterous +dexterously +dexterousness +dexterousness +dexterousness's +dextrose +dextrose's +dharma +dhoti +dhotis +dhoti's +dhow +dhows +dhow's +diabetes +diabetes's +diabetic +diabetics +diabetic's +diabolic +diabolical +diabolically +diacritic +diacritics +diacritic's +diacritical +diadem +diadems +diadem's +diaereses +diaeresis +diaeresis's +diagnose +diagnosing +diagnosed +diagnoses +diagnosis +diagnosis's +diagnostic +diagnostics +diagnostically +diagnostician +diagnosticians +diagnostician's +diagnostics +diagnostics's +diagonal +diagonally +diagonals +diagonal's +diagram +diagrams +diagram's +diagrammatic +diagrammatically +diagrammed +diagramming +dial +dialing +dialed +dials +dial's +redialing +redialed +redials +redial's +redial +dialect +dialects +dialect's +dialectal +dialectic +dialectics +dialectic's +dialectical +dialectics +dialectics's +dialing +dialings +dialog +dialogue +dialogues +dialogue's +dialyses +dialysis +dialysis's +dialyzes +diam +diamagnetic +diamagnetism +diamante +diameter +diameters +diameter's +diametric +diametrical +diametrically +diamond +diamonds +diamond's +diamondback +diamondbacks +diamondback's +diapason +diapasons +diapason's +diaper +diapering +diapered +diapers +diaper's +diaphanous +diaphragm +diaphragms +diaphragm's +diaphragmatic +diarist +diarists +diarist's +diarrhea +diarrhea's +diary +diaries +diary's +diaspora +diasporas +diaspora's +diastase +diastase's +diastole +diastole's +diastolic +diathermy +diathermy's +diatom +diatoms +diatom's +diatomic +diatonic +diatribe +diatribes +diatribe's +dibble +dibbling +dibbled +dibbles +dibble's +dibs +dibs's +dice +dicing +diced +dices +dices +indices +dicey +dichotomous +dichotomy +dichotomies +dichotomy's +dicier +diciest +dick +dickens +dicker +dickers +dicks +dick's +dicker +dickering +dickered +dickey +dickeys +dickey's +dickhead +dickheads +dickybird +dickybirds +dicotyledon +dicotyledons +dicotyledon's +dicotyledonous +dict +dicta +dictate +dictation +dictations +dictating +dictated +dictates +dictate's +dictation +dictation's +dictator +dictators +dictator's +dictatorial +dictatorially +dictatorship +dictatorships +dictatorship's +diction +diction's +dictionary +dictionaries +dictionary's +dictum +dictum's +did +redid +undid +didactic +didactically +diddle +diddling +diddled +diddler +diddlers +diddles +diddler +diddler's +diddly +diddlysquat +diddums +didgeridoo +didgeridoos +didn't +dido +dido's +didoes +didst +die +died +dies +die's +dielectric +dielectrics +dielectric's +diereses +dieresis +dieresis's +diesel +dieseling +dieseled +diesels +diesel's +diet +dieting +dieted +dieter +dieters +diets +diet's +dietary +dietaries +dietary's +dieter +dieter's +dietetic +dietetics +dietetics +dietetics's +dietitian +dietitians +dietitian's +diff +diffing +diffed +differ +differs +diffs +differ +differing +differed +difference +difference's +indifference's +indifference +differences +different +differently +indifferently +indifferent +differentiable +differential +differentials +differential's +differentiate +differentiation +differentiating +differentiated +differentiates +differentiated +undifferentiated +differentiation +differentiation's +difficult +difficultly +difficulty +difficulties +difficulty's +diffidence +diffidence's +diffident +diffidently +diffract +diffracting +diffracted +diffracts +diffraction +diffraction's +diffuse +diffusive +diffusion +diffusely +diffusing +diffused +diffuses +diffuseness +diffuseness +diffuseness's +diffusion +diffusion's +diffusivity +dig +digs +dig's +digerati +digerati's +digest +digestive +digesting +digested +digests +digest's +digested +undigested +digestibility +digestibility's +digestible +indigestible +digestion +digestion's +indigestion's +indigestion +digestions +digestive +digestives +digger +diggers +digger's +digging +diggings +diggings +diggings's +digicam +digicams +digit +digits +digit's +digital +digitally +digitalis +digitalis's +digitization +digitize +digitizing +digitized +digitizes +dignified +undignified +dignify +dignifying +dignified +dignifies +dignitary +dignitaries +dignitary's +dignity +dignities +dignity's +indignities +indignity's +indignity +digraph +digraph's +digraphs +digress +digressive +digressing +digressed +digresses +digression +digressions +digression's +dike +diking +diked +dikes +dike's +diktat +diktats +dilapidated +dilapidation +dilapidation's +dilatation +dilatation's +dilate +dilation +dilating +dilated +dilates +dilation +dilation's +dilator +dilators +dilator's +dilatory +dildo +dildos +dilemma +dilemmas +dilemma's +dilettante +dilettantes +dilettante's +dilettantish +dilettantism +dilettantism's +diligence +diligence's +diligent +diligently +dill +dills +dill's +dilly +dillies +dilly's +dillydally +dillydallying +dillydallied +dillydallies +diluent +dilute +dilution +dilutions +diluting +diluted +dilutes +diluted +undiluted +dilution +dilution's +dim +dimly +dimer +dims +dimness +dime +dimes +dime's +dimension +dimensions +dimension's +dimensional +dimensionless +diminish +diminishing +diminished +diminishes +diminished +undiminished +diminuendo +diminuendos +diminuendo's +diminution +diminutions +diminution's +diminutive +diminutives +diminutive's +dimity +dimity's +dimmed +undimmed +dimmer +dimmers +dimmer's +dimmest +dimming +dimness +dimness's +dimple +dimpling +dimpled +dimples +dimple's +dimply +dimwit +dimwits +dimwit's +dimwitted +din +dining +dined +diner +diners +dins +din's +dinar +dinars +dinar's +dine +dines +diner +diner's +dinette +dinettes +dinette's +ding +dinging +dinged +ding's +dingbat +dingbats +dingbat's +dinghy +dinghies +dinghy's +dingily +dinginess +dinginess's +dingle +dingles +dingle's +dingo +dingo's +dingoes +dingus +dinguses +dingus's +dingy +dingiest +dingier +dinginess +dink +dinker +dinky +dinkiest +dinkier +dinkies +dinky's +dinned +dinner +dinnering +dinnered +dinners +dinner's +dinnertime +dinnertime's +dinnerware +dinnerware's +dinning +dinosaur +dinosaurs +dinosaur's +dint +dint's +diocesan +diocesans +diocesan's +diocese +dioceses +diocese's +diode +diodes +diode's +diorama +dioramas +diorama's +dioxide +dioxides +dioxide's +dioxin +dioxins +dioxin's +dip +dips +dip's +diphtheria +diphtheria's +diphthong +diphthongs +diphthong's +diploid +diploids +diploid's +diploma +diplomas +diploma's +diplomacy +diplomacy's +diplomat +diplomats +diplomat's +diplomata +diplomatic +undiplomatic +diplomatically +diplomatist +diplomatists +diplomatist's +diplopia +dipole +dipoles +dipole's +dipped +dipper +dippers +dipper's +dipping +dippy +dippiest +dippier +dipso +dipsos +dipsomania +dipsomania's +dipsomaniac +dipsomaniacs +dipsomaniac's +dipstick +dipsticks +dipstick's +dipterous +diptych +diptych's +diptychs +dire +direly +direst +direr +direct +directive +directing +directed +directest +directs +redirecting +redirected +redirects +redirect +directer +direction +direction's +indirection's +indirection +directional +directionless +directions +directive +directives +directive's +directly +directness +directness's +indirectness's +indirectness +director +directors +director's +directorate +directorates +directorate's +directorial +directorship +directorships +directorship's +directory +directories +directory's +direful +dirge +dirges +dirge's +dirigible +dirigibles +dirigible's +dirk +dirks +dirk's +dirndl +dirndls +dirndl's +dirt +dirt's +dirtball +dirtballs +dirtily +dirtiness +dirtiness's +dirty +dirtying +dirtied +dirtiest +dirtier +dirties +dirtiness +dis +dis's +disable +disabling +disabled +disables +disablement +disablement +disablement's +disambiguate +disambiguation +disappointing +disappointingly +disarming +disarmingly +disassembly +disastrous +disastrously +disbandment +disbandment's +disbarment +disbarment's +disbelieving +disbelievingly +disbursal +disbursal's +disburse +disbursing +disbursed +disburses +disbursement +disbursement +disbursements +disbursement's +disc +disc's +discern +discerning +discerned +discerns +discernment +discernible +indiscernible +discernibly +discerning +discerningly +discernment +discernment's +discharged +undischarged +disciple +disciples +disciple's +discipleship +discipleship's +disciplinarian +disciplinarians +disciplinarian's +disciplinary +discipline +disciplining +disciplined +disciplines +discipline's +disciplined +undisciplined +disclose +disclosing +disclosed +discloses +disclosed +undisclosed +disco +discoing +disco's +discography +discographies +discography's +discoloration +discolorations +discombobulate +discombobulation +discombobulating +discombobulated +discombobulates +discombobulation +discombobulation's +discomfit +discomfiting +discomfited +discomfiture +discomfiture's +discommode +discommoding +discommoded +disconcerting +disconcertingly +disconnected +disconnectedly +disconnectedness +disconnectedness +disconnectedness's +disconsolate +disconsolately +discordance +discordance's +discordant +discordantly +discotheque +discotheques +discotheque's +discourage +discouraging +discouraged +discourages +discouragement +discouragement +discouragements +discouragement's +discouraging +discouragingly +discover +discovering +discovered +discovers +rediscovering +rediscovered +rediscovers +rediscover +discovered +undiscovered +discoverer +discoverers +discoverer's +discovery +discoveries +discovery's +rediscoveries +rediscovery's +rediscovery +discreet +discreetly +discreetest +discreeter +discreetness +discreetness +discreetness's +discrepancy +discrepancies +discrepancy's +discrepant +discrete +discretion +discretely +discreteness +discreteness +discreteness's +discretion +discretion's +indiscretion's +indiscretion +discretionary +discriminant +discriminate +discrimination +discriminating +discriminated +discriminates +discriminating +undiscriminating +discrimination +discrimination's +discriminator +discriminators +discriminator's +discriminatory +discursiveness +discursiveness's +discus +discuses +discus's +discussant +discussants +discussant's +discussion +discussions +discussion's +disdain +disdaining +disdained +disdains +disdain's +disdainful +disdainfully +disembowel +disemboweling +disemboweled +disembowels +disembowelment +disembowelment +disembowelment's +disfigurement +disfigurements +disfigurement's +disfranchisement +disfranchisement's +disgorgement +disgorgement's +disgruntle +disgruntling +disgruntled +disgruntles +disgruntlement +disgruntlement +disgruntlement's +disguise +disguising +disguised +disguised +undisguised +disgusted +disgustedly +disgusting +disgustingly +dish +dishing +dished +dishes +dish's +dishabille +dishabille's +disharmonious +dishcloth +dishcloth's +dishcloths +disheartening +dishearteningly +dishevel +disheveling +disheveled +dishevels +dishevelment +dishevelment +dishevelment's +dishpan +dishpans +dishpan's +dishrag +dishrags +dishrag's +dishtowel +dishtowels +dishtowel's +dishware +dishware's +dishwasher +dishwashers +dishwasher's +dishwater +dishwater's +dishy +disillusion +disillusioning +disillusioned +disillusionment +disillusionment +disillusionment's +disinfectant +disinfectants +disinfectant's +disinfection +disinfection's +disinterested +disinterestedly +disinterestedness +disinterestedness +disinterestedness's +disjointed +disjointedly +disjointedness +disjointedness +disjointedness's +disjunctive +disjuncture +disk +disks +disk's +diskette +diskettes +diskette's +dislodge +dislodging +dislodged +dislodges +dismal +dismally +dismantlement +dismantlement's +dismay +dismaying +dismayed +dismays +dismay's +dismayed +undismayed +dismember +dismembering +dismembered +dismemberment +dismemberment +dismemberment's +dismissive +dismissively +disorder +disorderly +disorganization +disorganization's +disparage +disparaging +disparaged +disparages +disparagement +disparagement +disparagement's +disparaging +disparagingly +disparate +disparately +dispatcher +dispatchers +dispatcher's +dispel +dispels +dispelled +dispelling +dispensary +dispensaries +dispensary's +dispensation +dispensations +dispensation's +dispense +dispensing +dispensed +dispenser +dispensers +dispenses +dispensable +dispenser +dispenser's +dispersal +dispersal's +disperse +dispersion +dispersing +dispersed +disperses +dispersion +dispersion's +dispirit +dispiriting +dispirited +dispirits +displeasure +displeasure's +disposable +disposables +disposable's +disposal +disposals +disposal's +disposed +indisposed +disposition +dispositions +disposition's +indispositions +indisposition's +indisposition +dispossession +dispossession's +disproof +disproofs +disproof's +disproportional +disprove +disprovable +disputable +indisputable +disputably +indisputably +disputant +disputants +disputant's +disputation +disputations +disputation's +disputatious +disputatiously +dispute +disputing +disputed +disputer +disputers +disputes +dispute's +disputable +disputed +undisputed +disputer +disputer's +disquiet +disquieting +disquieted +disquiets +disquiet's +disquisition +disquisitions +disquisition's +disregardful +disrepair +disrepair's +disrepute +disrepute's +disreputable +disrupt +disruptive +disrupting +disrupted +disrupts +disruption +disruptions +disruption's +disruptive +disruptively +dissect +dissecting +dissected +dissects +dissed +dissemblance +dissemblance's +dissemble +dissembling +dissembled +dissembler +dissemblers +dissembles +dissembler +dissembler's +disseminate +dissemination +disseminating +disseminated +disseminates +dissemination +dissemination's +dissension +dissensions +dissension's +dissent +dissenting +dissented +dissenter +dissenters +dissents +dissent's +dissenter +dissenter's +dissertation +dissertations +dissertation's +dissidence +dissidence's +dissident +dissidents +dissident's +dissimilar +dissimilitude +dissimilitudes +dissing +dissipate +dissipation +dissipating +dissipated +dissipates +dissipation +dissipation's +dissociate +dissociative +dissociation +dissociating +dissociated +dissociates +dissociation +dissociation's +dissoluble +indissoluble +dissolute +dissolution +dissolutely +dissoluteness +dissoluteness +dissoluteness's +dissolve +dissolving +dissolved +dissolves +redissolving +redissolved +redissolves +redissolve +dissolved +undissolved +dissonance +dissonances +dissonance's +dissonant +dissuade +dissuading +dissuaded +dissuades +dissuasive +dist +distaff +distaffs +distaff's +distal +distally +distance +distancing +distanced +distances +distance's +distant +distantly +distaste +distastes +distaste's +distemper +distemper's +distention +distentions +distention's +distillate +distillation +distillations +distillates +distillate's +distillation +distillation's +distillery +distilleries +distillery's +distinct +distinctive +distinctly +distinctest +distinctness +indistinctly +indistinctness +indistinct +distincter +distinction +distinctions +distinction's +distinctive +distinctively +distinctiveness +distinctiveness +distinctiveness's +distinctness +distinctness's +indistinctness's +indistinctness +distinguish +distinguishing +distinguished +distinguishes +distinguishable +distinguishable +indistinguishable +distinguished +undistinguished +distort +distorting +distorted +distorter +distortion +distortions +distortion's +distract +distracting +distracted +distracted +distractedly +distraction +distractions +distrait +distraught +distress +distressing +distressed +distressful +distressing +distressingly +distribute +distributive +distribution +distributing +distributed +distributes +redistribution +redistributing +redistributed +redistributes +redistribute +distributed +undistributed +distribution +distribution's +redistribution's +redistribution +distributional +distributions +distributive +distributively +distributor's +distributor +distributors +redistributors +redistributor +distributorship +distributorships +district's +district +districts +redistricts +redistrict +disturb +disturbing +disturbed +disturber +disturbers +disturbs +disturbance +disturbances +disturbance's +disturbed +undisturbed +disturber +disturber's +disturbing +disturbingly +disunion +disunion's +disyllabic +ditch +ditching +ditched +ditches +ditch's +dither +dithering +dithered +ditherer +ditherers +dithers +dither's +ditherer +ditherer's +ditransitive +ditsy +ditto +dittoing +dittoed +dittos +ditto's +ditty +ditties +ditty's +ditz +ditzes +ditz's +diuretic +diuretics +diuretic's +diurnal +diurnally +div +diva +divas +diva's +divalent +divan +divans +divan's +dive +diving +dived +divest +diver +divers +dives +dive's +diver +diver's +diverge +diverging +diverged +diverges +divergence +divergences +divergence's +divergent +diverse +diversion +diversions +diversely +diverseness +diverseness +diverseness's +diversification +diversification's +diversify +diversification +diversifying +diversified +diversifies +diversion +diversion's +diversionary +diversity +diversities +diversity's +divert +diverting +diverted +diverts +diverticulitis +diverticulitis's +divest +divesting +divested +divests +divestment +divestiture +divestitures +divestiture's +divestment +divestment's +divide +dividing +divided +divider +dividers +divides +divide's +dividable +divided +undivided +dividend +dividends +dividend's +divider +divider's +divination +divination's +divine +divinely +divining +divined +divinest +diviner +diviners +divines +divine's +diviner +diviner's +diving +diving's +divinity +divinities +divinity's +divisibility +divisibility's +indivisibility's +indivisibility +divisible +indivisible +division +divisions +division's +divisional +divisive +divisively +divisiveness +divisiveness +divisiveness's +divisor +divisors +divisor's +divorce +divorcing +divorced +divorces +divorce's +divorcement +divorcee +divorcees +divorcee's +divorcement +divorcements +divorcement's +divot +divots +divot's +divulge +divulging +divulged +divulges +divvy +divvying +divvied +divvies +divvy's +dixieland +dixieland's +dizzily +dizziness +dizziness's +dizzy +dizzying +dizzied +dizziest +dizzier +dizzies +dizziness +djellaba +djellabas +djellaba's +do +doth +doing +doings +doer +doers +dos +do's +doable +dob +dobs +dobbed +dobbin +dobbins +dobbin's +dobbing +doberman +dobermans +doberman's +dobro +doc +docs +doc's +docent +docents +docent's +docile +docilely +docility +docility's +dock +docking +docked +docker +dockers +docks +dock's +docket +docketing +docketed +dockets +docket's +dockland +docklands +dockside +dockworker +dockworkers +dockworker's +dockyard +dockyards +dockyard's +doctor +doctoring +doctored +doctors +doctor's +doctoral +doctorate +doctorates +doctorate's +doctrinaire +doctrinaires +doctrinaire's +doctrinal +doctrine +doctrines +doctrine's +docudrama +docudramas +docudrama's +document +documenting +documented +documents +document's +documentary +documentaries +documentary's +documentation +documentations +documentation's +documented +undocumented +dodder +doddering +doddered +dodders +dodder's +doddery +doddle +dodge +dodging +dodged +dodger +dodgers +dodges +dodge's +dodgem +dodgems +dodger +dodger's +dodgy +dodgiest +dodgier +dodo +dodos +dodo's +doe +does +doe's +doer +doer's +does +redoes +undoes +doeskin +doeskins +doeskin's +doesn't +doff +doffing +doffed +doffs +dog +dogs +dog's +dogcart +dogcarts +dogcart's +dogcatcher +dogcatchers +dogcatcher's +doge +doges +doge's +dogeared +dogfight +dogfights +dogfight's +dogfish +dogfishes +dogfish's +dogged +doggedly +doggedness +doggedness +doggedness's +doggerel +doggerel's +dogging +doggone +doggoning +doggonest +doggoner +doggones +doggy +doggiest +doggier +doggies +doggy's +doghouse +doghouses +doghouse's +dogie +dogies +dogie's +dogleg +doglegs +dogleg's +doglegged +doglegging +doglike +dogma +dogmas +dogma's +dogmatic +dogmatically +dogmatism +dogmatism's +dogmatist +dogmatists +dogmatist's +dognapper +dogsbody +dogsbodies +dogsled +dogsleds +dogtrot +dogtrots +dogtrot's +dogtrotted +dogtrotting +dogwood +dogwoods +dogwood's +doily +doilies +doily's +doing +doings +doing's +undoings +undoing's +undoing +doldrums +doldrums's +dole's +dole +doling +doled +doles +condoling +condoled +condoles +condole +doleful +dolefully +dolefulness +dolefulness +dolefulness's +doll +dolling +dolled +dolls +doll's +dollar +dollars +dollar's +dollhouse +dollhouses +dollhouse's +dollop +dolloping +dolloped +dollops +dollop's +dolly +dollies +dolly's +dolmen +dolmens +dolmen's +dolomite +dolomite's +dolor +dolor's +dolorous +dolorously +dolphin +dolphins +dolphin's +dolt +dolts +dolt's +doltish +doltishly +doltishness +doltishness +doltishness's +domain +domains +domain's +dome +doming +domed +domes +dome's +domestic +domestics +domestic's +domestically +domesticate +domestication +domesticating +domesticated +domesticates +domesticated +undomesticated +domestication +domestication's +domesticity +domesticity's +domicile +domiciling +domiciled +domiciles +domicile's +domiciliary +dominance +dominance's +dominant +dominantly +dominants +dominant's +dominate +domination +dominating +dominated +dominates +domination +domination's +dominatrices +dominatrix +dominatrix's +domineer +domineering +domineered +domineers +domineering +domineeringly +dominion +dominions +dominion's +domino +domino's +dominoes +don't +don +dons +don's +dona +donas +dona's +donate +donation +donations +donating +donated +donates +donation +donation's +done +redone +undone +condone +dong +donging +donged +dongs +dong's +dongle +dongles +dongle's +donkey +donkeys +donkey's +donned +donning +donnish +donnybrook +donnybrooks +donnybrook's +donor +donors +donor's +donuts +doodad +doodads +doodad's +doodah +doodahs +doodle +doodling +doodled +doodler +doodlers +doodles +doodle's +doodlebug +doodlebugs +doodlebug's +doodler +doodler's +doohickey +doohickeys +doohickey's +doolally +doom +dooming +doomed +dooms +doom's +doomsayer +doomsayers +doomsayer's +doomsday +doomsday's +doomster +doomsters +door's +door +doors +indoors +indoor +doorbell +doorbells +doorbell's +doorjamb +doorjambs +doorkeeper +doorkeepers +doorkeeper's +doorknob +doorknobs +doorknob's +doorknocker +doorknockers +doorman +doorman's +doormat +doormats +doormat's +doormen +doorplate +doorplates +doorplate's +doorpost +doorposts +doorstep +doorsteps +doorstep's +doorstepped +doorstepping +doorstop +doorstops +doorstop's +doorway +doorways +doorway's +dooryard +dooryards +dooryard's +dopa +dopa's +dopamine +dope +doping +doped +doper +dopers +dopes +dope's +doper +doper's +dopey +dopier +dopiest +dopiness +dopiness's +doping +doping's +doppelganger +doppelgangers +dork +dorks +dork's +dorky +dorkiest +dorkier +dorm +dormer +dormers +dorms +dorm's +dormancy +dormancy's +dormant +dormer +dormer's +dormice +dormitory +dormitories +dormitory's +dormouse +dormouse's +dorsal +dorsally +dory +dories +dory's +dosage +dosages +dosage's +dose +dosing +dosed +doses +dose's +dosh +dosimeter +dosimeters +dosimeter's +doss +dossing +dossed +dosser +dossers +dosses +dosshouse +dosshouses +dossier +dossiers +dossier's +dost +dot +doting +doted +doter +doters +dots +dot's +dotage +dotage's +dotard +dotards +dotard's +dotcom +dotcoms +dotcom's +dote +dotes +doter +doter's +doting +dotingly +dotted +dotting +dotty +dottiest +dottier +double's +double +doubling +doubled +doubles +redoubling +redoubled +redoubles +redouble +doubleheader +doubleheaders +doubleheader's +doublespeak +doublespeak's +doublet +doublets +doublet's +doubloon +doubloons +doubloon's +doubly +doubt +doubting +doubted +doubter +doubters +doubts +doubt's +doubter +doubter's +doubtful +doubtfully +doubtfulness +doubtfulness +doubtfulness's +doubting +doubtingly +doubtless +doubtlessly +douche +douching +douched +douches +douche's +dough +dough's +doughnut +doughnuts +doughnut's +doughty +doughtiest +doughtier +doughy +doughiest +doughier +dour +dourly +dourest +dourer +dourness +dourness +dourness's +douse +dousing +doused +douses +dove +doves +dove's +dovecot +dovecots +dovecote +dovecotes +dovecote's +dovetail +dovetailing +dovetailed +dovetails +dovetail's +dovish +dowager +dowagers +dowager's +dowdily +dowdiness +dowdiness's +dowdy +dowdiest +dowdier +dowdies +dowdiness +dowel +doweling +doweled +dowels +dowel's +dower +dowering +dowered +dowers +dower's +down +downing +downed +downer +downers +downs +down's +downbeat +downbeats +downbeat's +downcast +downdraft +downdrafts +downdraft's +downer +downer's +downfall +downfallen +downfalls +downfall's +downfield +downgrade +downgrading +downgraded +downgrades +downgrade's +downhearted +downheartedly +downheartedness +downheartedness +downheartedness's +downhill +downhills +downhill's +download +downloading +downloaded +downloads +download's +downloadable +downmarket +downplay +downplaying +downplayed +downplays +downpour +downpours +downpour's +downrange +downright +downriver +downscale +downshift +downshifting +downshifted +downshifts +downside +downsides +downside's +downsize +downsizing +downsized +downsizes +downsizing +downsizing's +downspout +downspouts +downspout's +downstage +downstairs +downstairs's +downstate +downstate's +downstream +downswing +downswings +downswing's +downtempo +downtime +downtime's +downtown +downtown's +downtrend +downtrends +downtrend's +downtrodden +downturn +downturns +downturn's +downward +downwards +downwind +downy +downiest +downier +dowry +dowries +dowry's +dowse +dowsing +dowsed +dowser +dowsers +dowses +dowser +dowser's +doxology +doxologies +doxology's +doyen +doyens +doyen's +doyenne +doyennes +doyenne's +doz +dozen +dozens +dozing +dozed +dozes +doze +doze's +dozen +dozenth +dozen's +dozily +dozy +doziest +dozier +doziness +dpi +dpt +drab +drably +drabs +drabness +drab's +drabber +drabbest +drabness +drabness's +drachma +drachmas +drachma's +draconian +draft's +draft +drafting +drafted +drafts +redrafting +redrafted +redrafts +redraft +draftee +draftees +draftee's +drafter +drafters +drafter's +draftily +draftiness +draftiness's +drafting +drafting's +draftsman +draftsman's +draftsmanship +draftsmanship's +draftsmen +draftswoman +draftswoman's +draftswomen +drafty +draftiest +draftier +draftiness +drag +drags +drag's +dragged +dragging +draggy +draggiest +draggier +dragnet +dragnets +dragnet's +dragon +dragons +dragon's +dragonfly +dragonflies +dragonfly's +dragoon +dragooning +dragooned +dragoons +dragoon's +dragster +dragsters +drain +draining +drained +drainer +drainers +drains +drain's +drainage +drainage's +drainboard +drainboards +drainboard's +drainer +drainer's +drainpipe +drainpipes +drainpipe's +drake +drakes +drake's +dram +drams +dram's +drama +dramas +drama's +dramatic +dramatics +dramatically +dramatics +dramatics's +dramatist +dramatists +dramatist's +dramatization +dramatizations +dramatization's +dramatize +dramatizing +dramatized +dramatizes +drank +drape +draping +draped +draper +drapers +drapes +drape's +draper +draper's +drapery +draperies +drapery's +drastic +drastically +drat +dratted +draughtboard +draughtboards +draw +drawing +drawings +drawer +drawers +draws +draw's +drawback +drawbacks +drawback's +drawbridge +drawbridges +drawbridge's +drawer +drawer's +drawing +drawing's +drawl +drawling +drawled +drawls +drawl's +drawn +redrawn +drawstring +drawstrings +drawstring's +dray +drays +dray's +dread +dreading +dreaded +dreads +dread's +dreadful +dreadfully +dreadfulness +dreadfulness +dreadfulness's +dreadlocks +dreadlocks's +dreadnought +dreadnoughts +dreadnought's +dream +dreaming +dreamed +dreamer +dreamers +dreams +dream's +dreamboat +dreamboats +dreamboat's +dreamed +undreamed +dreamer +dreamer's +dreamily +dreaminess +dreaminess's +dreamland +dreamland's +dreamless +dreamlike +dreamworld +dreamworlds +dreamworld's +dreamy +dreamiest +dreamier +dreaminess +drear +drearily +dreariness +dreariness's +dreary +dreariest +drearier +dreariness +dredge +dredging +dredged +dredger +dredgers +dredges +dredge's +dredger +dredger's +dregs +dregs's +drench +drenching +drenched +drenches +dress +dressing +dressed +dresses +dress's +redressing +undressing +redressed +undressed +redresses +undresses +redress's +undress's +redress +undress +dressage +dressage's +dresser +dressers +dresser's +dressiness +dressiness's +dressing +dressings +dressing's +dressmaker +dressmakers +dressmaker's +dressmaking +dressmaking's +dressy +dressiest +dressier +dressiness +drew +redrew +dribble +dribbling +dribbled +dribbler +dribblers +dribbles +dribble's +dribbler +dribbler's +driblet +driblets +driblet's +drier +drier's +drift +drifting +drifted +drifter +drifters +drifts +drift's +drifter +drifter's +driftnet +driftnets +driftwood +driftwood's +drill +drilling +drilled +driller +drillers +drills +drill's +driller +driller's +drillmaster +drillmasters +drillmaster's +drink +drinking +drinkings +drinker +drinkers +drinks +drink's +drinkable +drinkable +undrinkable +drinker +drinker's +drip +drips +drip's +dripped +dripping +drippings +dripping's +drippy +drippiest +drippier +drive +driving +drivings +driver +drivers +drives +drive's +drivel +driveling +driveled +driveler +drivelers +drivels +drivel's +driveler +driveler's +driven +driver +driver's +driveshaft +driveshafts +driveshaft's +driveway +driveways +driveway's +drizzle +drizzling +drizzled +drizzles +drizzle's +drizzly +drogue +drogues +drogue's +droid +droids +droll +drollest +droller +drollness +drollery +drolleries +drollery's +drollness +drollness's +drolly +dromedary +dromedaries +dromedary's +drone +droning +droned +drones +drone's +drool +drooling +drooled +drools +drool's +droop +drooping +drooped +droops +droop's +droopiness +droopiness's +droopy +droopiest +droopier +droopiness +drop +drops +drop's +dropkick +dropkicks +dropkick's +droplet +droplets +droplet's +dropout +dropouts +dropout's +dropped +dropper +droppers +dropper's +dropping +droppings +droppings +droppings's +dropsical +dropsy +dropsy's +dross +dross's +drought +droughts +drought's +drove +drover +drovers +droves +drove's +drover +drover's +drown +drowning +drownings +drowned +drowns +drowning +drowning's +drowse +drowsing +drowsed +drowses +drowse's +drowsily +drowsiness +drowsiness's +drowsy +drowsiest +drowsier +drowsiness +drub +drubs +drubbed +drubber +drubbers +drubber's +drubbing +drubbings +drubbing's +drudge +drudging +drudged +drudges +drudge's +drudgery +drudgery's +drug +drugs +drug's +drugged +druggie +druggies +druggie's +drugging +druggist +druggists +druggist's +druggy +drugstore +drugstores +drugstore's +druid +druids +druid's +druidism +druidism's +drum +drums +drum's +drumbeat +drumbeats +drumbeat's +drumlin +drumlins +drumlin's +drummed +drummer +drummers +drummer's +drumming +drumstick +drumsticks +drumstick's +drunk +drunken +drunkest +drunker +drunks +drunk's +drunkard +drunkards +drunkard's +drunken +drunkenly +drunkenness +drunkenness +drunkenness's +drupe +drupes +drupe's +druthers +druthers's +dry +dryly +drying +dried +driest +drier +driers +dries +dry's +dryad +dryads +dryad's +dryer +dryers +dryer's +dryness +dryness's +drys +drywall +drywall's +dual +dualism +dualism's +duality +duality's +dub +dubs +dub's +dubbed +dubber +dubbers +dubber's +dubbin +dubbin's +dubbing +dubiety +dubiety's +dubious +dubiously +dubiousness +dubiousness +dubiousness's +ducal +ducat +ducats +ducat's +duchess +duchesses +duchess's +duchy +duchies +duchy's +duck +ducking +ducked +ducks +duck's +duckbill +duckbills +duckbill's +duckboards +duckling +ducklings +duckling's +duckpins +duckpins's +duckweed +duckweed's +ducky +duckiest +duckier +duckies +ducky's +duct's +product's +duct +ducts +inducts +deducts +conducts +products +induct +deduct +conduct +product +ductile +ductility +ductility's +ducting +ductless +dud +duding +duded +duds +dud's +dude +dudes +dude's +dudgeon +dudgeon's +due +dues +due's +duel +dueling +duelings +dueled +dueler +duelers +duels +duel's +dueler +dueler's +duelist +duelists +duelist's +duenna +duennas +duenna's +duet +duets +duet's +duff +duffing +duffed +duffer +duffers +duffs +duff's +duffer +duffer's +dug +dugout +dugouts +dugout's +duh +duke +dukes +duke's +dukedom +dukedoms +dukedom's +dulcet +dulcimer +dulcimers +dulcimer's +dull +dulling +dulled +dullest +duller +dulls +dullness +dullard +dullards +dullard's +dullness +dullness's +dully +duly +unduly +dumb +dumbly +dumbest +dumber +dumbness +dumbbell +dumbbells +dumbbell's +dumbfound +dumbfounding +dumbfounded +dumbfounds +dumbness +dumbness's +dumbo +dumbos +dumbstruck +dumbwaiter +dumbwaiters +dumbwaiter's +dumdum +dumdums +dumdum's +dummy +dummies +dummy's +dump +dumping +dumped +dumper +dumpers +dumps +dump's +dumpiness +dumpiness's +dumpling +dumplings +dumpling's +dumpsite +dumpsites +dumpster +dumpsters +dumpster's +dumpy +dumpiest +dumpier +dumpiness +dun +duns +dun's +dunce +dunces +dunce's +dunderhead +dunderheads +dunderhead's +dune +dunes +dune's +dung +dunging +dunged +dungs +dung's +dungaree +dungarees +dungaree's +dungeon +dungeons +dungeon's +dunghill +dunghills +dunghill's +dunk +dunking +dunked +dunks +dunk's +dunned +dunner +dunnest +dunning +dunno +duo +duos +duo's +duodecimal +duodena +duodenal +duodenum +duodenum's +duopoly +duopolies +dupe +duping +duped +duper +dupers +dupes +dupe's +duper +duper's +duple +duplex +duplexes +duplex's +duplicate's +duplicate +duplication +duplicating +duplicated +duplicates +reduplication +reduplicating +reduplicated +reduplicates +reduplicate +duplication +duplication's +reduplication's +reduplication +duplicator +duplicators +duplicator's +duplicitous +duplicity +duplicity's +durability +durability's +durable +durably +durance +durance's +duration +duration's +duress +duress's +during +durst +durum +durum's +dusk +dusk's +duskiness +duskiness's +dusky +duskiest +duskier +duskiness +dust +dusting +dusted +duster +dusters +dusts +dust's +dustbin +dustbins +dustbin's +dustcart +dustcarts +duster +duster's +dustiness +dustiness's +dustless +dustman +dustmen +dustpan +dustpans +dustpan's +dustsheet +dustsheets +dusty +dustiest +dustier +dustiness +dutch +duteous +duteously +dutiable +dutiful +dutifully +dutifulness +dutifulness +dutifulness's +duty +duties +duty's +duvet +duvets +duvet's +dwarf +dwarfing +dwarfed +dwarfs +dwarf's +dwarfish +dwarfism +dwarfism's +dweeb +dweebs +dweeb's +dwell +dwelling +dwellings +dweller +dwellers +dwells +dweller +dweller's +dwelling +dwelling's +dwelt +indwelt +dwindle +dwindling +dwindled +dwindles +dyadic +dybbuk +dybbuks +dybbuk's +dybbukim +dye +dying +dyed +dyer +dyers +dyes +dye's +dyeing +redyeing +dyer +dyer's +dyestuff +dyestuff's +dying +dying's +dyke +dykes +dyke's +dynamic +dynamics +dynamic's +dynamical +dynamically +dynamics +dynamics's +dynamism +dynamism's +dynamite +dynamiting +dynamited +dynamiter +dynamiters +dynamites +dynamite's +dynamiter +dynamiter's +dynamo +dynamos +dynamo's +dynastic +dynasty +dynasties +dynasty's +dysentery +dysentery's +dysfunction +dysfunctions +dysfunction's +dysfunctional +dyslectic +dyslectics +dyslectic's +dyslexia +dyslexia's +dyslexic +dyslexics +dyslexic's +dyspepsia +dyspepsia's +dyspeptic +dyspeptics +dyspeptic's +dysphagia +dysphoria +dysphoric +dysprosium +dysprosium's +dystonia +dystopi +dystopia +dystopian +dz +e'en +e'er +e +ed +est +es +coned +cones +cone +eBay +eBay's +eMusic +eMusic's +ea +each +eager +eagerly +eagerest +eagerer +eagerness +eagerness +eagerness's +eagle +eagles +eagle's +eaglet +eaglets +eaglet's +ear +early +eared +ears +ear's +earache +earaches +earache's +earbud +earbuds +earbud's +eardrum +eardrums +eardrum's +earful +earfuls +earful's +earl +earls +earl's +earldom +earldoms +earldom's +earliness +earliness's +earlobe +earlobes +earlobe's +early +earliest +earlier +earliness +earmark +earmarking +earmarked +earmarks +earmark's +earmuff +earmuffs +earmuff's +earn +earning +earnings +earned +earnest +earner +earners +earns +earned +unearned +earner +earner's +earnest +earnestly +earnests +earnestness +earnest's +earnestness +earnestness's +earnings +earnings's +earphone +earphones +earphone's +earpiece +earpieces +earplug +earplugs +earplug's +earring +earrings +earring's +earshot +earshot's +earsplitting +earth's +earth +earthly +earthing +earthed +unearthly +unearthing +unearthed +unearth +earthbound +earthen +earthenware +earthenware's +earthiness +earthiness's +earthling +earthlings +earthling's +earthly +earthliest +earthlier +earthquake +earthquakes +earthquake's +earths +unearths +earthshaking +earthward +earthwards +earthwork +earthworks +earthwork's +earthworm +earthworms +earthworm's +earthy +earthiest +earthier +earthiness +earwax +earwax's +earwig +earwigs +earwig's +ease +eased +eases +ease's +diseased +diseases +disease's +disease +easel +easels +easel's +easement +easements +easement's +easily +uneasily +easiness +easiness's +uneasiness's +uneasiness +easing +east +east's +eastbound +easterly +easterlies +easterly's +eastern +easterner +easterners +easterner +easterner's +easternmost +eastward +eastwards +easy +easiest +easier +easiness +uneasier +uneasiness +uneasy +easygoing +eat +eaten +eating +eater +eaters +eats +eatable +eatable +eatables +eatable's +eaten +uneaten +eater +eater's +eatery +eateries +eatery's +eave +eaves +eave's +eavesdrop +eavesdrops +eavesdropped +eavesdropper +eavesdroppers +eavesdropper's +eavesdropping +ebb +ebbing +ebbed +ebbs +ebb's +ebony +ebonies +ebony's +ebullience +ebullience's +ebullient +ebulliently +ebullition +ebullition's +eccentric +eccentrics +eccentric's +eccentrically +eccentricity +eccentricities +eccentricity's +eccl +ecclesial +ecclesiastic +ecclesiastics +ecclesiastic's +ecclesiastical +ecclesiastically +echelon +echelons +echelon's +echidna +echinoderm +echinoderms +echinoderm's +echo's +echo +echoing +echoed +reechoing +reechoed +reecho +echoes +reechoes +echoic +echolocation +echolocation's +echos +eclair +eclairs +eclair's +eclat +eclat's +eclectic +eclectics +eclectic's +eclectically +eclecticism +eclecticism's +eclipse +eclipsing +eclipsed +eclipses +eclipse's +ecliptic +ecliptic's +eclogue +eclogues +eclogue's +ecocide +ecocide's +ecol +ecologic +ecological +ecologically +ecologist +ecologists +ecologist's +ecology +ecology's +econ +econometric +econometrics +economic +economics +economical +economically +uneconomically +uneconomical +economics +economics's +economist +economists +economist's +economize +economizing +economized +economizer +economizers +economizes +economizer +economizer's +economy +economies +economy's +ecosystem +ecosystems +ecosystem's +ecotourism +ecotourism's +ecotourist +ecotourists +ecotourist's +ecru +ecru's +ecstasy +ecstasies +ecstasy's +ecstatic +ecstatically +ecu +ecus +ecumenical +ecumenically +ecumenicism +ecumenicism's +ecumenism +ecumenism's +eczema +eczema's +ed +eds +ed's +reeds +deeds +reed's +deed's +reed +deed +edamame +eddy +eddying +eddied +eddies +eddy's +edelweiss +edelweiss's +edema +edemas +edema's +edge +edging +edgings +edged +edger +edgers +edges +edge's +edger +edger's +edgewise +edgily +edginess +edginess's +edging +edging's +edgy +edgiest +edgier +edginess +edibility +edibility's +edible +edibles +edibleness +edible's +edibleness +edibleness's +edict +edicts +edict's +edification +edification's +edifice +edifices +edifice's +edifier +edifier's +edify +edification +edifying +edified +edifier +edifiers +edifies +edifying +unedifying +edit's +edit +editing +edited +edits +reediting +reedited +reedits +reedit +editable +edited +unedited +edition +editions +edition's +editor +editors +editor's +editorial +editorially +editorials +editorial's +editorialize +editorializing +editorialized +editorializes +editorship +editorship's +educ +educability +educability's +educable +ineducable +educate +educative +education +educating +educated +educates +reeducation +reeducating +reeducated +reeducates +reeducate +educated +uneducated +education +education's +reeducation's +reeducation +educational +educationally +educationalist +educationalists +educationist +educationists +educations +educator +educators +educator's +educe +educing +educed +educes +educable +edutainment +edutainment's +eek +eel +eels +eel's +eerie +eeriest +eerier +eerily +eeriness +eeriness's +eff +effing +effed +effs +efface +effacing +effaced +effaces +effacement +effacement +effacement's +effect +effective +effecting +effected +effects +effect's +effective +effectively +effectiveness +ineffectively +ineffectiveness +ineffective +effectiveness +effectiveness's +ineffectiveness's +ineffectiveness +effectual +effectually +ineffectually +ineffectual +effectuate +effectuating +effectuated +effectuates +effeminacy +effeminacy's +effeminate +effeminately +effendi +effendis +effendi's +efferent +effervesce +effervescing +effervesced +effervesces +effervescence +effervescence's +effervescent +effervescently +effete +effetely +effeteness +effeteness +effeteness's +efficacious +efficaciously +efficacy +efficacy's +inefficacy's +inefficacy +efficiency +efficiencies +efficiency's +inefficiencies +inefficiency's +inefficiency +efficient +efficiently +inefficiently +inefficient +effigy +effigies +effigy's +efflorescence +efflorescence's +efflorescent +effluence +effluence's +effluent +effluents +effluent's +effluvia +effluvium +effluvium's +efflux +effort +efforts +effort's +effortful +effortless +effortlessly +effortlessness +effortlessness +effortlessness's +effrontery +effrontery's +effulgence +effulgence's +effulgent +effuse +effusive +effusion +effusions +effusing +effused +effuses +effusion +effusion's +effusive +effusively +effusiveness +effusiveness +effusiveness's +egad +egalitarian +egalitarians +egalitarian's +egalitarianism +egalitarianism's +egg +egging +egged +eggs +egg's +eggbeater +eggbeaters +eggbeater's +eggcup +eggcups +eggcup's +egghead +eggheads +egghead's +eggnog +eggnog's +eggplant +eggplants +eggplant's +eggshell +eggshells +eggshell's +eglantine +eglantines +eglantine's +ego +egos +ego's +egocentric +egocentrics +egocentric's +egocentrically +egocentricity +egocentricity's +egoism +egoism's +egoist +egoists +egoist's +egoistic +egoistical +egoistically +egomania +egomania's +egomaniac +egomaniacs +egomaniac's +egotism +egotism's +egotist +egotists +egotist's +egotistic +egotistical +egotistically +egregious +egregiously +egregiousness +egregiousness +egregiousness's +egress +egresses +egress's +egret +egrets +egret's +eh +eider +eiders +eider's +eiderdown +eiderdowns +eiderdown's +eigenvalue +eigenvalues +eigenvector +eigenvectors +eight +eights +eight's +eighteen +eighteenth +eighteens +eighteen's +eighteenth +eighteenth's +eighteenths +eighth +eighth's +eighths +eightieth +eightieth's +eightieths +eighty +eightieth +eighties +eighty's +einsteinium +einsteinium's +eisteddfod +eisteddfods +either +ejaculate +ejaculation +ejaculations +ejaculating +ejaculated +ejaculates +ejaculation +ejaculation's +ejaculatory +eject +ejecting +ejected +ejects +ejection +ejections +ejection's +ejector +ejectors +ejector's +eke +eking +eked +ekes +elaborate +elaboration +elaborations +elaborately +elaborating +elaborated +elaborates +elaborateness +elaborateness +elaborateness's +elaboration +elaboration's +elan +elan's +eland +elands +eland's +elapse +elapsing +elapsed +elapses +elastic +elastics +elastic's +elastically +elasticated +elasticity +elasticity's +elasticize +elasticizing +elasticized +elasticizes +elate +elation +elating +elated +elates +elated +elatedly +elation +elation's +elbow +elbowing +elbowed +elbows +elbow's +elbowroom +elbowroom's +elder +elderly +elders +elder's +elderberry +elderberries +elderberry's +eldercare +eldercare's +eldest +eldritch +elect's +elect +elective +electing +elected +elects +reelecting +reelected +reelects +reelect +electable +election +elections +election's +reelections +reelection's +reelection +electioneer +electioneering +electioneered +electioneers +elective +electives +elective's +elector +electors +elector's +electoral +electorally +electorate +electorates +electorate's +electric +electrics +electrical +electrically +electrician +electricians +electrician's +electricity +electricity's +electrification +electrification's +electrifier +electrifier's +electrify +electrification +electrifying +electrified +electrifier +electrifiers +electrifies +electrocardiogram +electrocardiograms +electrocardiogram's +electrocardiograph +electrocardiograph's +electrocardiographs +electrocardiography +electrocardiography's +electrocute +electrocution +electrocutions +electrocuting +electrocuted +electrocutes +electrocution +electrocution's +electrode +electrodes +electrode's +electrodynamics +electroencephalogram +electroencephalograms +electroencephalogram's +electroencephalograph +electroencephalograph's +electroencephalographic +electroencephalographs +electroencephalography +electroencephalography's +electrologist +electrologists +electrologist's +electrolysis +electrolysis's +electrolyte +electrolytes +electrolyte's +electrolytic +electromagnet +electromagnets +electromagnet's +electromagnetic +electromagnetically +electromagnetism +electromagnetism's +electromotive +electron +electrons +electron's +electronic +electronics +electronica +electronica's +electronically +electronics +electronics's +electroplate +electroplating +electroplated +electroplates +electroscope +electroscopes +electroscope's +electroscopic +electroshock +electroshock's +electrostatic +electrostatics +electrostatics +electrostatics's +electrotype +electrotypes +electrotype's +electroweak +eleemosynary +elegance +elegance's +inelegance's +inelegance +elegant +elegantly +inelegantly +inelegant +elegiac +elegiacs +elegiac's +elegiacal +elegy +elegies +elegy's +elem +element +elements +element's +elemental +elementally +elementary +elephant +elephants +elephant's +elephantiasis +elephantiasis's +elephantine +elev +elevate +elevation +elevations +elevating +elevated +elevates +elevation +elevation's +elevator +elevators +elevator's +eleven +eleventh +elevens +eleven's +elevens +elevenses +eleventh +eleventh's +elevenths +elf +elf's +elfin +elfish +elicit +eliciting +elicited +elicits +elicitation +elicitation's +elide +eliding +elided +elides +eligibility +eligibility's +ineligibility's +ineligibility +eligible +eliminate +elimination +eliminations +eliminating +eliminated +eliminates +elimination +elimination's +eliminator +eliminators +elision +elisions +elision's +elite +elites +elite's +elitism +elitism's +elitist +elitists +elitist's +elixir +elixirs +elixir's +elk +elks +elk's +ell +ells +ell's +ellipse +ellipses +ellipse's +ellipsis +ellipsis's +ellipsoid +ellipsoids +ellipsoid's +ellipsoidal +elliptic +elliptical +elliptically +elm +elms +elm's +elocution +elocution's +elocutionary +elocutionist +elocutionists +elocutionist's +elodea +elodeas +elodea's +elongate +elongation +elongations +elongating +elongated +elongates +elongation +elongation's +elope +eloping +eloped +elopes +elopement +elopement +elopements +elopement's +eloquence +eloquence's +eloquent +eloquently +else +elsewhere +elucidate +elucidation +elucidations +elucidating +elucidated +elucidates +elucidation +elucidation's +elude +eluding +eluded +eludes +elusive +elusively +elusiveness +elusiveness +elusiveness's +elver +elvers +elver's +elves +elvish +em's +em +ems +emaciate +emaciation +emaciating +emaciated +emaciates +emaciation +emaciation's +email +emailing +emailed +emails +email's +emanate +emanation +emanations +emanating +emanated +emanates +emanation +emanation's +emancipate +emancipation +emancipating +emancipated +emancipates +emancipation +emancipation's +emancipator +emancipators +emancipator's +emasculate +emasculation +emasculating +emasculated +emasculates +emasculation +emasculation's +embalm +embalming +embalmed +embalmer +embalmers +embalms +embalmer +embalmer's +embank +embanking +embanked +embanks +embankment +embankment +embankments +embankment's +embargo +embargoing +embargoed +embargo's +embargoes +embark +embarking +embarked +embarks +reembarking +disembarking +reembarked +disembarked +reembarks +disembarks +reembark +disembark +embarkation +embarkation's +disembarkation's +disembarkation +embarkations +embarrass +embarrassing +embarrassed +embarrasses +embarrassment +embarrassed +unembarrassed +embarrassing +embarrassingly +embarrassment +embarrassments +embarrassment's +embassy +embassies +embassy's +embattled +embed +embeds +embedded +embedding +embellish +embellishing +embellished +embellishes +embellishment +embellishment +embellishments +embellishment's +ember +embers +ember's +embezzle +embezzling +embezzled +embezzler +embezzlers +embezzles +embezzlement +embezzlement +embezzlement's +embezzler +embezzler's +embitter +embittering +embittered +embitters +embitterment +embitterment +embitterment's +emblazon +emblazoning +emblazoned +emblazons +emblazonment +emblazonment +emblazonment's +emblem +emblems +emblem's +emblematic +emblematically +embodiment +embodiment's +disembodiment's +disembodiment +embody +embodying +embodied +embodies +reembodying +disembodying +reembodied +disembodied +reembodies +disembodies +reembody +disembody +embolden +emboldening +emboldened +emboldens +embolism +embolisms +embolism's +embolization +emboss +embossing +embossed +embosser +embossers +embosses +embosser +embosser's +embouchure +embouchure's +embower +embowering +embowered +embowers +embrace +embracing +embraced +embraces +embrace's +embraceable +embrasure +embrasures +embrasure's +embrocation +embrocations +embrocation's +embroider +embroidering +embroidered +embroiderer +embroiderers +embroiders +embroiderer +embroiderer's +embroidery +embroideries +embroidery's +embroil +embroiling +embroiled +embroils +embroilment +embroilment +embroilment's +embryo +embryos +embryo's +embryological +embryologist +embryologists +embryologist's +embryology +embryology's +embryonic +emcee +emceed +emcees +emcee's +emceeing +emend +emending +emended +emends +emendation +emendations +emendation's +emerald +emeralds +emerald's +emerge +emerging +emerged +emerges +reemerging +reemerged +reemerges +reemerge +emergence +emergence's +reemergence's +reemergence +emergency +emergencies +emergency's +emergent +emerita +emeritus +emery +emery's +emetic +emetics +emetic's +emf +emfs +emigrant +emigrants +emigrant's +emigrate +emigration +emigrations +emigrating +emigrated +emigrates +emigration +emigration's +emigre +emigres +emigre's +eminence +eminences +eminence's +eminent +eminently +emir +emirs +emir's +emirate +emirates +emirate's +emissary +emissaries +emissary's +emission +emissions +emission's +emit +emits +emitted +emitter +emitters +emitter's +emitting +emo +emos +emo's +emoji +emojis +emoji's +emollient +emollients +emollient's +emolument +emoluments +emolument's +emote +emotive +emotion +emotions +emoting +emoted +emotes +emoticon +emoticons +emoticon's +emotion +emotion's +emotional +emotionally +unemotionally +unemotional +emotionalism +emotionalism's +emotionalize +emotionalizing +emotionalized +emotionalizes +emotionless +emotive +emotively +empathetic +empathically +empathize +empathizing +empathized +empathizes +empathy +empathy's +emperor +emperors +emperor's +emphases +emphasis +emphasis's +emphasize +emphasizing +emphasized +emphasizes +reemphasizing +reemphasized +reemphasizes +reemphasize +emphatic +unemphatic +emphatically +emphysema +emphysema's +empire +empires +empire's +empiric +empirical +empirically +empiricism +empiricism's +empiricist +empiricists +empiricist's +emplacement +emplacements +emplacement's +employ's +employ +employing +employed +employs +employment +reemploying +reemployed +reemploys +reemployment +reemploy +employable +unemployable +employee +employees +employee's +employer +employers +employer's +employment +employment's +reemployment's +unemployment's +reemployment +unemployment +employments +emporium +emporiums +emporium's +empower +empowering +empowered +empowers +empowerment +empowerment +empowerment's +empress +empresses +empress's +emptily +emptiness +emptiness's +empty +emptying +emptied +emptiest +emptier +empties +emptiness +empty's +empyrean +empyrean's +emu +emus +emu's +emulate +emulative +emulation +emulations +emulating +emulated +emulates +emulation +emulation's +emulator +emulators +emulator's +emulsification +emulsification's +emulsifier +emulsifier's +emulsify +emulsification +emulsifying +emulsified +emulsifier +emulsifiers +emulsifies +emulsion +emulsions +emulsion's +en +ens +en's +enable +enabling +enabled +enabler +enablers +enables +enabler +enabler's +enact +enacting +enacted +enacts +enactment +reenacting +reenacted +reenacts +reenactment +reenact +enactment +enactments +enactment's +reenactments +reenactment's +reenactment +enamel +enameling +enamelings +enameled +enameler +enamelers +enamels +enamel's +enameler +enameler's +enamelware +enamelware's +enamor +enamoring +enamored +enamors +enc +encamp +encamping +encamped +encamps +encampment +encampment +encampments +encampment's +encapsulate +encapsulation +encapsulations +encapsulating +encapsulated +encapsulates +encapsulation +encapsulation's +encase +encasing +encased +encases +encasement +encasement +encasement's +encephalitic +encephalitis +encephalitis's +enchain +enchaining +enchained +enchains +enchant +enchanting +enchanted +enchants +enchantment +disenchanting +disenchanted +disenchants +disenchantment +disenchant +enchanter +enchanters +enchanter's +enchanting +enchantingly +enchantment +enchantment's +disenchantment's +disenchantment +enchantments +enchantress +enchantresses +enchantress's +enchilada +enchiladas +enchilada's +encipher +enciphering +enciphered +enciphers +encircle +encircling +encircled +encircles +encirclement +encirclement +encirclement's +encl +enclave +enclaves +enclave's +enclose +enclosing +enclosed +encloses +enclosed +unenclosed +enclosure +enclosures +enclosure's +encode +encoding +encoded +encoder +encoders +encodes +encoder +encoder's +encomium +encomiums +encomium's +encompass +encompassing +encompassed +encompasses +encore +encoring +encored +encores +encore's +encounter +encountering +encountered +encounters +encounter's +encourage +encouraging +encouraged +encourages +encouragement +encouragement +encouragements +encouragement's +encouraging +encouragingly +encroach +encroaching +encroached +encroaches +encroachment +encroachment +encroachments +encroachment's +encrust +encrusting +encrusted +encrusts +encrustation +encrustations +encrustation's +encrypt +encrypting +encrypted +encrypts +encryption +encumber +encumbering +encumbered +encumbers +disencumbering +disencumbered +disencumbers +disencumber +encumbered +unencumbered +encumbrance +encumbrances +encumbrance's +ency +encyclical +encyclicals +encyclical's +encyclopedia +encyclopedias +encyclopedia's +encyclopedic +encyst +encysting +encysted +encysts +encystment +encystment +encystment's +end +endive +ending +endings +ended +ends +end's +endanger +endangering +endangered +endangers +endangerment +endangerment +endangerment's +endear +endearing +endeared +endears +endearment +endearing +endearingly +endearment +endearments +endearment's +endeavor +endeavoring +endeavored +endeavors +endeavor's +endemic +endemics +endemic's +endemically +endgame +endgames +ending +ending's +endive +endives +endive's +endless +endlessly +endlessness +endlessness +endlessness's +endmost +endocarditis +endocrine +endocrines +endocrine's +endocrinologist +endocrinologists +endocrinologist's +endocrinology +endocrinology's +endogenous +endogenously +endometrial +endometriosis +endometrium +endorphin +endorphins +endorphin's +endorse +endorsing +endorsed +endorser +endorsers +endorses +endorsement +endorsement +endorsements +endorsement's +endorser +endorser's +endoscope +endoscopes +endoscope's +endoscopic +endoscopy +endoscopy's +endothelial +endothermic +endotracheal +endow +endowing +endowed +endows +endowment +endowment +endowments +endowment's +endpoint +endpoints +endpoint's +endue +enduing +endued +endues +endurable +unendurable +endurance +endurance's +endure +enduring +endured +endures +endurable +endways +enema +enemas +enema's +enemy +enemies +enemy's +energetic +energetically +energize +energizing +energized +energizer +energizers +energizes +energizer +energizer's +energy +energies +energy's +enervate +enervation +enervating +enervated +enervates +enervation +enervation's +enfeeble +enfeebling +enfeebled +enfeebles +enfeeblement +enfeeblement +enfeeblement's +enfilade +enfilading +enfiladed +enfilades +enfilade's +enfold +enfolding +enfolded +enfolds +enforce +enforcing +enforced +enforcer +enforcers +enforces +enforcement +enforceable +unenforceable +enforced +unenforced +enforcement +enforcement's +enforcer +enforcer's +enfranchise +enfranchising +enfranchised +enfranchises +enfranchisement +disenfranchising +disenfranchised +disenfranchises +disenfranchisement +disenfranchise +enfranchisement +enfranchisement's +disenfranchisement's +disenfranchisement +engage +engaging +engaged +engages +reengaging +disengaging +reengaged +disengaged +reengages +disengages +reengage +disengage +engagement +engagements +engagement's +disengagements +disengagement's +disengagement +engagingly +engender +engendering +engendered +engenders +engine +engines +engine's +engineer +engineering +engineered +engineers +engineer's +engineering +engineering's +engorge +engorging +engorged +engorges +engorgement +engorgement +engorgement's +engram +engrams +engram's +engrave +engraving +engravings +engraved +engraver +engravers +engraves +engraver +engraver's +engraving +engraving's +engross +engrossing +engrossed +engrosses +engrossment +engrossment +engrossment's +engulf +engulfing +engulfed +engulfs +engulfment +engulfment +engulfment's +enhance +enhancing +enhanced +enhancer +enhancers +enhances +enhancement +enhancement +enhancements +enhancement's +enigma +enigmas +enigma's +enigmatic +enigmatically +enjambment +enjambments +enjambment's +enjoin +enjoining +enjoined +enjoins +enjoy +enjoying +enjoyed +enjoys +enjoyable +enjoyment +enjoyably +enjoyment +enjoyments +enjoyment's +enlarge +enlarging +enlarged +enlarger +enlargers +enlarges +enlargement +enlargeable +enlargement +enlargements +enlargement's +enlarger +enlarger's +enlighten +enlightening +enlightened +enlightens +enlightenment +enlightened +unenlightened +enlightenment +enlightenment's +enlist +enlisting +enlisted +enlists +enlistment +reenlisting +reenlisted +reenlists +reenlistment +reenlist +enlistee +enlistees +enlistee's +enlistment +enlistment's +reenlistment's +reenlistment +enlistments +enliven +enlivening +enlivened +enlivens +enlivenment +enlivenment +enlivenment's +enmesh +enmeshing +enmeshed +enmeshes +enmeshment +enmeshment +enmeshment's +enmity +enmities +enmity's +ennoble +ennobling +ennobled +ennobles +ennoblement +ennoblement +ennoblement's +ennui +ennui's +enormity +enormities +enormity's +enormous +enormously +enormousness +enormousness +enormousness's +enough +enough's +enplane +enplaning +enplaned +enplanes +enquirer +enquirers +enquiringly +enrage +enraging +enraged +enrages +enrapture +enrapturing +enraptured +enraptures +enrich +enriching +enriched +enriches +enrichment +enrichment +enrichment's +enroll +enrolling +enrolled +enrolls +enrollment +enrollment +enrollments +enrollment's +ensconce +ensconcing +ensconced +ensconces +ensemble +ensembles +ensemble's +enshrine +enshrining +enshrined +enshrines +enshrinement +enshrinement +enshrinement's +enshroud +enshrouding +enshrouded +enshrouds +ensign +ensigns +ensign's +ensilage +ensilage's +enslave +enslaving +enslaved +enslaves +enslavement +enslavement +enslavement's +ensnare +ensnaring +ensnared +ensnares +ensnarement +ensnarement +ensnarement's +ensue +ensuing +ensued +ensues +ensure +ensuring +ensured +ensurer +ensurers +ensures +ensurer +ensurer's +entail +entailing +entailed +entails +entailment +entailment +entailment's +entangle +entangling +entangled +entangles +entanglement +disentangling +disentangled +disentangles +disentanglement +disentangle +entanglement +entanglement's +disentanglement's +disentanglement +entanglements +entente +ententes +entente's +enter +entering +entered +enters +reentering +reentered +reenters +reenter +enteral +enteric +enteritis +enteritis's +enterprise +enterprising +enterprises +enterprise's +enterprising +enterprisingly +entertain +entertaining +entertained +entertainer +entertainers +entertains +entertainment +entertainer +entertainer's +entertaining +entertainingly +entertaining's +entertainment +entertainments +entertainment's +enthrall +enthralling +enthralled +enthralls +enthrallment +enthrallment +enthrallment's +enthrone +enthroning +enthroned +enthrones +enthronement +enthronement +enthronements +enthronement's +enthuse +enthusing +enthused +enthuses +enthusiasm +enthusiasms +enthusiasm's +enthusiast +enthusiasts +enthusiast's +enthusiastic +unenthusiastic +enthusiastically +entice +enticing +enticed +entices +enticement +enticement +enticements +enticement's +enticing +enticingly +entire +entirely +entirety +entirety's +entitle +entitling +entitled +entitles +entitlement +entitlement +entitlements +entitlement's +entity +entities +entity's +entomb +entombing +entombed +entombs +entombment +entombment +entombment's +entomological +entomologist +entomologists +entomologist's +entomology +entomology's +entourage +entourages +entourage's +entr'acte +entrails +entrails's +entrained +entrance +entrancing +entranced +entrances +entrance's +entrancement +entrancement +entrancement's +entrancing +entrancingly +entrant +entrants +entrant's +entrap +entraps +entrapment +entrapment +entrapment's +entrapped +entrapping +entreat +entreating +entreated +entreats +entreating +entreatingly +entreaty +entreaties +entreaty's +entree +entrees +entree's +entrench +entrenching +entrenched +entrenches +entrenchment +entrenchment +entrenchments +entrenchment's +entrepreneur +entrepreneurs +entrepreneur's +entrepreneurial +entrepreneurship +entropy +entropy's +entrust +entrusting +entrusted +entrusts +entry +entries +entry's +reentries +reentry's +reentry +entryphone +entryphones +entryway +entryways +entryway's +entwine +entwining +entwined +entwines +enumerable +enumerate +enumeration +enumerations +enumerating +enumerated +enumerates +enumeration +enumeration's +enumerator +enumerators +enumerator's +enunciate +enunciation +enunciating +enunciated +enunciates +enunciation +enunciation's +enuresis +enuresis's +envelop +enveloping +enveloped +enveloper +envelopers +envelops +envelopment +envelope +envelopes +envelope's +enveloper +enveloper's +envelopment +envelopment's +envenom +envenoming +envenomed +envenoms +enviable +unenviable +enviably +envious +enviously +enviousness +enviousness +enviousness's +environment +environments +environment's +environmental +environmentally +environmentalism +environmentalism's +environmentalist +environmentalists +environmentalist's +environs +environs's +envisage +envisaging +envisaged +envisages +envision +envisioning +envisioned +envisions +envoy +envoys +envoy's +envy +envying +envied +envies +envy's +envying +envyingly +enzymatic +enzyme +enzymes +enzyme's +eolian +eon +eons +eon's +eosinophil +eosinophils +eosinophilic +epaulet +epaulets +epaulet's +epee +epees +epee's +ephedrine +ephedrine's +ephemera +ephemera's +ephemeral +ephemerally +epic +epics +epic's +epicenter +epicenters +epicenter's +epicure +epicures +epicure's +epicurean +epicureans +epicurean's +epidemic +epidemics +epidemic's +epidemically +epidemiological +epidemiologist +epidemiologists +epidemiologist's +epidemiology +epidemiology's +epidermal +epidermic +epidermis +epidermises +epidermis's +epidural +epidurals +epiglottis +epiglottises +epiglottis's +epigram +epigrams +epigram's +epigrammatic +epigraph +epigraph's +epigraphs +epigraphy +epigraphy's +epilepsy +epilepsy's +epileptic +epileptics +epileptic's +epilogue +epilogues +epilogue's +epinephrine +epinephrine's +epiphany +epiphanies +epiphany's +episcopacy +episcopacy's +episcopal +episcopate +episcopate's +episode +episodes +episode's +episodic +episodically +epistemic +epistemological +epistemology +epistle +epistles +epistle's +epistolary +epitaph +epitaph's +epitaphs +epithelial +epithelium +epithelium's +epithet +epithets +epithet's +epitome +epitomes +epitome's +epitomize +epitomizing +epitomized +epitomizes +epoch +epoch's +epochal +epochs +eponymous +epoxy +epoxying +epoxied +epoxies +epoxy's +epsilon +epsilons +epsilon's +equability +equability's +equable +equably +equal +equally +equaling +equaled +equals +equal's +equality +equality's +inequality's +inequality +equalization +equalization's +equalize +equalizing +equalized +equalizer +equalizers +equalizes +equalizer +equalizer's +equanimity +equanimity's +equate +equation +equations +equating +equated +equates +equatable +equation +equation's +equator +equators +equator's +equatorial +equerry +equerries +equerry's +equestrian +equestrians +equestrian's +equestrianism +equestrianism's +equestrienne +equestriennes +equestrienne's +equidistant +equidistantly +equilateral +equilaterals +equilateral's +equilibrium +equilibrium's +disequilibrium's +disequilibrium +equine +equines +equine's +equinoctial +equinox +equinoxes +equinox's +equip +equips +reequips +reequip +equipage +equipages +equipage's +equipment +equipment's +equipoise +equipoise's +equipped +reequipped +unequipped +equipping +reequipping +equitable +inequitable +equitably +inequitably +equitation +equitation's +equity +equities +equity's +inequities +inequity's +inequity +equiv +equivalence +equivalences +equivalence's +equivalency +equivalencies +equivalency's +equivalent +equivalently +equivalents +equivalent's +equivocal +equivocally +unequivocally +unequivocal +equivocalness +equivocalness's +equivocate +equivocation +equivocations +equivocating +equivocated +equivocates +equivocation +equivocation's +equivocator +equivocators +equivocator's +er +deer +era +eras +era's +eradicable +ineradicable +eradicate +eradication +eradicating +eradicated +eradicates +eradication +eradication's +eradicator +eradicators +eradicator's +erase +erasing +erased +eraser +erasers +erases +erasable +eraser +eraser's +erasure +erasures +erasure's +erbium +erbium's +ere +erect +erectly +erecting +erected +erects +erectness +erectile +erection +erections +erection's +erectness +erectness's +erector +erectors +erector's +erelong +eremite +eremites +eremite's +erg +ergs +erg's +ergo +ergonomic +ergonomics +ergonomically +ergonomics +ergonomics's +ergosterol +ergosterol's +ergot +ergot's +ermine +ermines +ermine's +erode +eroding +eroded +erodes +erodible +erogenous +erosion +erosion's +erosive +erotic +erotics +erotica +erotica's +erotically +eroticism +eroticism's +err +erring +erred +errs +errand +errands +errand's +errant +inerrant +errata +erratas +errata's +erratic +erratically +erratum +erratum's +erroneous +erroneously +error +errors +error's +ersatz +ersatzes +ersatz's +erst +erstwhile +eruct +eructing +eructed +eructs +eructation +eructations +eructation's +erudite +erudition +eruditely +erudition +erudition's +erupt +eruptive +erupting +erupted +erupts +eruption +eruptions +eruption's +erysipelas +erysipelas's +erythrocyte +erythrocytes +erythrocyte's +erythromycin +escalate +escalation +escalating +escalated +escalates +deescalation +deescalating +deescalated +deescalates +deescalate +escalation +escalation's +deescalation's +deescalation +escalations +escalator +escalators +escalator's +escallop +escalloping +escalloped +escallops +escallop's +escalope +escalopes +escapade +escapades +escapade's +escape +escaping +escaped +escapes +escape's +escapement +escapee +escapees +escapee's +escapement +escapements +escapement's +escapism +escapism's +escapist +escapists +escapist's +escapologist +escapologists +escapology +escargot +escargots +escargot's +escarole +escaroles +escarole's +escarpment +escarpments +escarpment's +eschatological +eschatology +eschew +eschewing +eschewed +eschews +escort +escorting +escorted +escorts +escort's +escritoire +escritoires +escritoire's +escrow +escrows +escrow's +escudo +escudos +escudo's +escutcheon +escutcheons +escutcheon's +esophageal +esophagi +esophagus +esophagus's +esoteric +esoterically +esp +espadrille +espadrilles +espadrille's +espalier +espaliering +espaliered +espaliers +espalier's +especial +especially +espionage +espionage's +esplanade +esplanades +esplanade's +espousal +espousal's +espouse +espousing +espoused +espouses +espresso +espressos +espresso's +esprit +esprit's +espy +espying +espied +espies +esquire +esquires +esquire's +essay +essaying +essayed +essayer +essayers +essays +essay's +essayer +essayer's +essayist +essayists +essayist's +essence +essences +essence's +essential +essentials +essential's +inessentials +inessential's +inessential +essentially +establish +establishing +established +establishes +establishment +reestablishing +disestablishing +reestablished +disestablished +reestablishes +disestablishes +reestablishment +disestablishment +reestablish +disestablish +establishment +establishment's +reestablishment's +disestablishment's +reestablishment +disestablishment +establishments +estate +estates +estate's +esteem +esteeming +esteemed +esteems +esteem's +disesteeming +disesteemed +disesteems +disesteem's +disesteem +ester +esters +ester's +estimable +inestimable +estimate +estimation +estimations +estimating +estimated +estimates +estimate's +estimation +estimation's +estimator +estimators +estimator's +estoppel +estrange +estranging +estranged +estranges +estrangement +estrangement +estrangements +estrangement's +estrogen +estrogen's +estrous +estrus +estruses +estrus's +estuary +estuaries +estuary's +eta +etas +eta's +etc +etch +etching +etchings +etched +etcher +etchers +etches +etcher +etcher's +etching +etching's +eternal +eternally +eternalness +eternalness +eternalness's +eternity +eternities +eternity's +ethane +ethane's +ethanol +ethanol's +ether +ether's +ethereal +ethereally +ethic +ethics +ethic's +ethical +ethically +unethically +unethical +ethics +ethics's +ethmoid +ethnic +ethnics +ethnic's +ethnically +ethnicity +ethnicity's +ethnocentric +ethnocentrism +ethnocentrism's +ethnographer +ethnographers +ethnographic +ethnographically +ethnography +ethnological +ethnologically +ethnologist +ethnologists +ethnologist's +ethnology +ethnology's +ethological +ethologist +ethologists +ethologist's +ethology +ethology's +ethos +ethos's +ethyl +ethyl's +ethylene +ethylene's +etiolated +etiologic +etiological +etiology +etiologies +etiology's +etiquette +etiquette's +etude +etudes +etude's +etymological +etymologically +etymologist +etymologists +etymologist's +etymology +etymologies +etymology's +eucalypti +eucalyptus +eucalyptuses +eucalyptus's +euchre +euchring +euchred +euchres +euchre's +euclidean +eugenic +eugenics +eugenically +eugenicist +eugenicists +eugenicist's +eugenics +eugenics's +eukaryotes +eulogist +eulogists +eulogist's +eulogistic +eulogize +eulogizing +eulogized +eulogizer +eulogizers +eulogizes +eulogizer +eulogizer's +eulogy +eulogies +eulogy's +eunuch +eunuch's +eunuchs +euphemism +euphemisms +euphemism's +euphemistic +euphemistically +euphonious +euphoniously +euphony +euphony's +euphoria +euphoria's +euphoric +euphorically +eureka +euro +euros +euro's +europium +europium's +eutectic +euthanasia +euthanasia's +euthanize +euthanizing +euthanized +euthanizes +euthenics +euthenics's +eutrophication +evacuate +evacuation +evacuations +evacuating +evacuated +evacuates +evacuation +evacuation's +evacuee +evacuees +evacuee's +evade +evading +evaded +evader +evaders +evades +evader +evader's +evaluate +evaluative +evaluation +evaluations +evaluating +evaluated +evaluates +reevaluation +reevaluations +reevaluating +reevaluated +reevaluates +reevaluate +evaluation +evaluation's +reevaluation's +reevaluation +evaluator +evaluators +evanescence +evanescence's +evanescent +evangelic +evangelical +evangelically +evangelicals +evangelical's +evangelicalism +evangelicalism's +evangelism +evangelism's +evangelist +evangelists +evangelist's +evangelistic +evangelize +evangelizing +evangelized +evangelizes +evaporate +evaporation +evaporating +evaporated +evaporates +evaporation +evaporation's +evaporator +evaporators +evaporator's +evasion +evasions +evasion's +evasive +evasively +evasiveness +evasiveness +evasiveness's +eve +eves +eve's +reeves +reeve's +reeve +even +evenly +evening +evenings +evened +evenest +evener +evens +evenness +even's +evenhanded +evenhandedly +evening +evening's +evenness +evenness's +unevenness's +unevenness +evensong +evensong's +event +events +event's +eventful +eventfully +uneventfully +uneventful +eventfulness +eventfulness's +eventide +eventide's +eventual +eventually +eventuality +eventualities +eventuality's +eventuate +eventuating +eventuated +eventuates +ever +everglade +everglades +everglade's +evergreen +evergreens +evergreen's +everlasting +everlastingly +everlastings +everlasting's +evermore +every +everybody +everybody's +everyday +everyone +everyone's +everyplace +everything +everything's +everywhere +evict +evicting +evicted +evicts +eviction +evictions +eviction's +evidence +evidencing +evidenced +evidences +evidence's +evident +evidently +evil +evilly +evilest +eviler +evils +evilness +evil's +evildoer +evildoers +evildoer's +evildoing +evildoing's +eviller +evillest +evilness +evilness's +evince +evincing +evinced +evinces +eviscerate +evisceration +eviscerating +eviscerated +eviscerates +evisceration +evisceration's +evocation +evocations +evocation's +evocative +evocatively +evoke +evoking +evoked +evokes +evolution +evolution's +evolutionary +evolutionist +evolutionists +evolutionist's +evolve +evolving +evolved +evolves +ewe +ewer +ewers +ewes +ewe's +ewer +ewer's +ex +exes +ex's +exabyte +exabytes +exabyte's +exacerbate +exacerbation +exacerbating +exacerbated +exacerbates +exacerbation +exacerbation's +exact +exactly +exacting +exacted +exactest +exacter +exacts +exactness +exacting +exactingly +exaction +exaction's +exactitude +exactitude's +exactness +exactness's +inexactness's +inexactness +exaggerate +exaggeration +exaggerations +exaggerating +exaggerated +exaggerates +exaggerated +exaggeratedly +exaggeration +exaggeration's +exaggerator +exaggerators +exaggerator's +exalt +exalting +exalted +exalts +exaltation +exaltation's +exam +exams +exam's +examination +examinations +examination's +reexaminations +reexamination's +reexamination +examine +examining +examined +examines +reexamining +reexamined +reexamines +reexamine +examiner +examiners +examiner's +example +exampling +exampled +examples +example's +exampled +unexampled +exasperate +exasperation +exasperating +exasperated +exasperates +exasperated +exasperatedly +exasperating +exasperatingly +exasperation +exasperation's +excavate +excavation +excavations +excavating +excavated +excavates +excavation +excavation's +excavator +excavators +excavator's +exceed +exceeding +exceeded +exceeds +exceeding +exceedingly +excel +excels +excelled +excellence +excellence's +excellency +excellencies +excellency's +excellent +excellently +excelling +excelsior +excelsior's +except +excepting +excepted +excepts +exception +exceptions +exception's +exceptionable +exceptionable +unexceptionable +exceptional +exceptionally +unexceptionally +unexceptional +exceptionalism +excerpt +excerpting +excerpted +excerpts +excerpt's +excess +excessive +excesses +excess's +excessive +excessively +exchange +exchanging +exchanged +exchanges +exchange's +exchangeable +exchequer +exchequers +exchequer's +excise +excision +excisions +excising +excised +excises +excise's +excision +excision's +excitability +excitability's +excitably +excitation +excitation's +excite +exciting +excited +exciter +exciters +excites +excitable +excitement +excited +excitedly +excitement +excitements +excitement's +exciter +exciter's +exciting +excitingly +exciton +excl +exclaim +exclaiming +exclaimed +exclaims +exclamation +exclamations +exclamation's +exclamatory +exclude +excluding +excluded +excludes +exclusion +exclusions +exclusion's +exclusionary +exclusive +exclusively +exclusives +exclusiveness +exclusive's +exclusiveness +exclusiveness's +exclusivity +exclusivity's +excommunicate +excommunication +excommunications +excommunicating +excommunicated +excommunicates +excommunication +excommunication's +excoriate +excoriation +excoriations +excoriating +excoriated +excoriates +excoriation +excoriation's +excrement +excrement's +excremental +excrescence +excrescences +excrescence's +excrescent +excreta +excreta's +excrete +excretion +excretions +excreting +excreted +excretes +excretion +excretion's +excretory +excruciating +excruciatingly +exculpate +exculpation +exculpating +exculpated +exculpates +exculpation +exculpation's +exculpatory +excursion +excursions +excursion's +excursionist +excursionists +excursionist's +excursive +excursively +excursiveness +excursiveness +excursiveness's +excusable +inexcusable +excusably +inexcusably +excuse +excusing +excused +excuses +excuse's +excusable +excused +unexcused +exec +execs +exec's +execrable +execrably +execrate +execration +execrating +execrated +execrates +execration +execration's +execute +executive +execution +executions +executing +executed +executes +executable +execution +executioner +executioners +execution's +executioner +executioner's +executive +executives +executive's +executor +executors +executor's +executrices +executrix +executrix's +exegeses +exegesis +exegesis's +exegetic +exegetical +exemplar +exemplars +exemplar's +exemplary +exemplification +exemplification's +exemplify +exemplification +exemplifications +exemplifying +exemplified +exemplifies +exempt +exempting +exempted +exempts +exemption +exemptions +exemption's +exercise +exercising +exercised +exerciser +exercisers +exercises +exercise's +exerciser +exerciser's +exert +exerting +exerted +exerts +exertion +exertions +exertion's +exeunt +exfoliate +exfoliation +exfoliating +exfoliated +exfoliates +exhalation +exhalations +exhalation's +exhale +exhaling +exhaled +exhales +exhaust +exhaustive +exhausting +exhausted +exhausts +exhaust's +exhaustible +inexhaustible +exhaustion +exhaustion's +exhaustive +exhaustively +exhaustiveness +exhaustiveness +exhaustiveness's +exhibit +exhibiting +exhibited +exhibits +exhibit's +exhibition +exhibitions +exhibition's +exhibitionism +exhibitionism's +exhibitionist +exhibitionists +exhibitionist's +exhibitor +exhibitors +exhibitor's +exhilarate +exhilaration +exhilarating +exhilarated +exhilarates +exhilaration +exhilaration's +exhort +exhorting +exhorted +exhorts +exhortation +exhortations +exhortation's +exhumation +exhumations +exhumation's +exhume +exhuming +exhumed +exhumes +exigence +exigences +exigence's +exigency +exigencies +exigency's +exigent +exiguity +exiguity's +exiguous +exile +exiling +exiled +exiles +exile's +exilic +exist +existing +existed +exists +existence +existences +existence's +existent +existential +existentially +existentialism +existentialism's +existentialist +existentialists +existentialist's +exit +exiting +exited +exits +exit's +exobiology +exobiology's +exodus +exoduses +exodus's +exogenous +exon +exons +exon's +exonerate +exoneration +exonerating +exonerated +exonerates +exoneration +exoneration's +exoplanet +exoplanets +exoplanet's +exorbitance +exorbitance's +exorbitant +exorbitantly +exorcise +exorcising +exorcised +exorcises +exorcism +exorcisms +exorcism's +exorcist +exorcists +exorcist's +exoskeleton +exoskeletons +exoskeleton's +exosphere +exospheres +exosphere's +exothermic +exotic +exotics +exotic's +exotica +exotically +exoticism +exoticism's +exp +expand +expanding +expanded +expands +expandable +expanse +expansive +expansion +expansions +expanses +expanse's +expansible +expansion +expansion's +expansionary +expansionism +expansionism's +expansionist +expansionists +expansionist's +expansive +expansively +expansiveness +expansiveness +expansiveness's +expat +expats +expatiate +expatiation +expatiating +expatiated +expatiates +expatiation +expatiation's +expatriate +expatriation +expatriating +expatriated +expatriates +expatriate's +expatriation +expatriation's +expect +expecting +expected +expects +expectancy +expectancy's +expectant +expectantly +expectation +expectations +expectation's +expectorant +expectorants +expectorant's +expectorate +expectoration +expectorating +expectorated +expectorates +expectoration +expectoration's +expedience +expedience's +inexpedience's +inexpedience +expediences +expediencies +expediency +expediency's +inexpediency's +inexpediency +expedient +expediently +expedients +expedient's +expedite +expedition +expeditions +expediting +expedited +expediter +expediters +expedites +expediter +expediter's +expedition +expedition's +expeditionary +expeditious +expeditiously +expeditiousness +expeditiousness +expeditiousness's +expel +expels +expelled +expelling +expend +expending +expended +expends +expendable +expendable +expendables +expendable's +expenditure +expenditures +expenditure's +expense +expenses +expense's +expensive +expensively +expensiveness +inexpensively +inexpensiveness +inexpensive +expensiveness +expensiveness's +inexpensiveness's +inexpensiveness +experience +experienced +experience's +inexperienced +inexperience's +inexperience +experiences +experiencing +experiential +experiment +experimenting +experimented +experimenter +experimenters +experiments +experiment's +experimental +experimentally +experimentation +experimentation's +experimenter +experimenter's +expert +expertly +experts +expertness +expert's +expertise +expertise's +expertness +expertness's +expiate +expiation +expiating +expiated +expiates +expiation +expiation's +expiatory +expiration +expiration's +expire +expiring +expired +expires +expired +unexpired +expiry +expiry's +explain +explaining +explained +explains +reexplaining +reexplained +reexplains +reexplain +explainable +explained +unexplained +explanation +explanations +explanation's +explanatory +expletive +expletives +expletive's +explicable +inexplicable +explicate +explication +explications +explicating +explicated +explicates +explication +explication's +explicit +explicitly +explicitness +explicitness +explicitness's +explode +exploding +exploded +explodes +exploit +exploiting +exploited +exploiter +exploiters +exploits +exploit's +exploitable +exploitation +exploitation's +exploitative +exploited +unexploited +exploiter +exploiter's +exploration +explorations +exploration's +exploratory +explore +exploring +explored +explorer +explorers +explores +explored +unexplored +explorer +explorer's +explosion +explosions +explosion's +explosive +explosively +explosives +explosiveness +explosive's +explosiveness +explosiveness's +expo +expos +expo's +exponent +exponents +exponent's +exponential +exponentially +exponentiation +export +exporting +exported +exporter +exporters +exports +export's +exportable +exportation +exportation's +exporter +exporter's +expose +exposing +exposed +exposes +expose's +exposed +unexposed +exposition +expositions +exposition's +expositor +expositors +expositor's +expository +expostulate +expostulation +expostulations +expostulating +expostulated +expostulates +expostulation +expostulation's +exposure +exposures +exposure's +expound +expounding +expounded +expounder +expounders +expounds +expounder +expounder's +express +expressive +expressly +expressing +expressed +expresses +express's +expressed +unexpressed +expressible +inexpressible +expression +expressions +expression's +expressionism +expressionism's +expressionist +expressionists +expressionist's +expressionistic +expressionless +expressionlessly +expressive +expressively +expressiveness +expressiveness +expressiveness's +expressway +expressways +expressway's +expropriate +expropriation +expropriations +expropriating +expropriated +expropriates +expropriation +expropriation's +expropriator +expropriators +expropriator's +expulsion +expulsions +expulsion's +expunge +expunging +expunged +expunges +expurgate +expurgation +expurgations +expurgating +expurgated +expurgates +expurgated +unexpurgated +expurgation +expurgation's +exquisite +exquisitely +exquisiteness +exquisiteness +exquisiteness's +ext +extant +extemporaneous +extemporaneously +extemporaneousness +extemporaneousness +extemporaneousness's +extempore +extemporization +extemporization's +extemporize +extemporizing +extemporized +extemporizes +extend +extending +extended +extender +extenders +extends +extendable +extender +extender's +extensible +extension +extensions +extension's +extensional +extensive +extensively +extensiveness +extensiveness +extensiveness's +extent +extents +extent's +extenuate +extenuation +extenuating +extenuated +extenuates +extenuation +extenuation's +exterior +exteriors +exterior's +exterminate +extermination +exterminations +exterminating +exterminated +exterminates +extermination +extermination's +exterminator +exterminators +exterminator's +external +externally +externals +external's +externalization +externalizations +externalization's +externalize +externalizing +externalized +externalizes +extinct +extincting +extincted +extincts +extinction +extinctions +extinction's +extinguish +extinguishing +extinguished +extinguisher +extinguishers +extinguishes +extinguishable +extinguishable +inextinguishable +extinguisher +extinguisher's +extirpate +extirpation +extirpating +extirpated +extirpates +extirpation +extirpation's +extol +extols +extolled +extolling +extort +extorting +extorted +extorts +extortion +extortioner +extortioners +extortion's +extortionate +extortionately +extortioner +extortioner's +extortionist +extortionists +extortionist's +extra +extras +extra's +extracellular +extract +extractive +extracting +extracted +extracts +extract's +extraction +extractions +extraction's +extractor +extractors +extractor's +extracurricular +extradite +extradition +extraditions +extraditing +extradited +extradites +extraditable +extradition +extradition's +extrajudicial +extralegal +extramarital +extramural +extraneous +extraneously +extraordinaire +extraordinarily +extraordinary +extrapolate +extrapolation +extrapolations +extrapolating +extrapolated +extrapolates +extrapolation +extrapolation's +extrasensory +extraterrestrial +extraterrestrials +extraterrestrial's +extraterritorial +extraterritoriality +extraterritoriality's +extravagance +extravagances +extravagance's +extravagant +extravagantly +extravaganza +extravaganzas +extravaganza's +extravehicular +extreme +extremely +extremest +extremer +extremes +extremeness +extreme's +extremeness +extremeness's +extremism +extremism's +extremist +extremists +extremist's +extremity +extremities +extremity's +extricable +inextricable +extricate +extrication +extricating +extricated +extricates +extrication +extrication's +extrinsic +extrinsically +extroversion +extroversion's +extrovert +extroverted +extroverts +extrovert's +extrude +extruding +extruded +extrudes +extrusion +extrusions +extrusion's +extrusive +exuberance +exuberance's +exuberant +exuberantly +exudation +exudation's +exude +exuding +exuded +exudes +exult +exulting +exulted +exults +exultant +exultantly +exultation +exultation's +exurb +exurbs +exurb's +exurban +exurbanite +exurbanites +exurbanite's +exurbia +exurbia's +eye +eyed +eyes +eye's +eyeball +eyeballing +eyeballed +eyeballs +eyeball's +eyebrow +eyebrows +eyebrow's +eyedropper +eyedroppers +eyedropper's +eyeful +eyefuls +eyeful's +eyeglass +eyeglasses +eyeglass's +eyeing +eyelash +eyelashes +eyelash's +eyeless +eyelet +eyelets +eyelet's +eyelid +eyelids +eyelid's +eyeliner +eyeliners +eyeliner's +eyeopener +eyeopeners +eyeopener's +eyeopening +eyepiece +eyepieces +eyepiece's +eyesight +eyesight's +eyesore +eyesores +eyesore's +eyestrain +eyestrain's +eyeteeth +eyetooth +eyetooth's +eyewash +eyewash's +eyewitness +eyewitnesses +eyewitness's +f +five +fest +fer +refer +infer +defer +ref +inf +def +fMRI +fa +fa's +fab +fable +fabled +fables +fable's +fabric +fabrics +fabric's +fabricate +fabrication +fabrications +fabricating +fabricated +fabricates +fabrication +fabrication's +fabricator +fabricators +fabricator's +fabulous +fabulously +facade +facades +facade's +face's +face +facing +faced +faces +refacing +defacing +refaced +defaced +refaces +defaces +reface +deface +facecloth +facecloth's +facecloths +faceless +facepalm +facepalming +facepalmed +facepalms +facet +faceting +faceted +facets +facet's +facetious +facetiously +facetiousness +facetiousness +facetiousness's +facial +facially +facials +facial's +facile +facilely +facilitate +facilitation +facilitating +facilitated +facilitates +facilitation +facilitation's +facilitator +facilitators +facilitator's +facility +facilities +facility's +facing +facings +facing's +facsimile +facsimiled +facsimiles +facsimile's +facsimileing +fact +facts +fact's +faction +factions +faction's +factional +factionalism +factionalism's +factious +factitious +factoid +factoids +factoid's +factor's +factor +factoring +factored +factors +refactoring +refactored +refactors +refactor +factorial +factorials +factorial's +factorization +factorize +factorizing +factorized +factorizes +factory +factories +factory's +factotum +factotums +factotum's +factual +factually +faculty +faculties +faculty's +fad +fading +faded +fads +fad's +faddish +faddishness +faddist +faddists +faddist's +faddy +faddiness +fade +fades +fade's +fading +unfading +faerie +faeries +faerie's +faff +faffing +faffed +faffs +fag +fags +fag's +fagged +fagging +faggot +faggots +faggot's +fagot +fagoting +fagots +fagot's +faience +faience's +fail +failing +failings +failed +fails +fail's +failing +failing's +faille +faille's +failure +failures +failure's +fain +fainest +fainer +faint +faintly +fainting +fainted +faintest +fainter +faints +faintness +faint's +fainthearted +faintness +faintness's +fair +fairly +fairing +fairings +fairest +fairer +fairs +fairness +fair's +fairground +fairgrounds +fairground's +fairing +fairing's +fairness +fairness's +unfairness's +unfairness +fairway +fairways +fairway's +fairy +fairies +fairy's +fairyland +fairylands +fairyland's +faith +faith's +faithful's +faithful +faithfully +faithfulness +unfaithfully +unfaithfulness +unfaithful +faithfulness +faithfulness's +unfaithfulness's +unfaithfulness +faithfuls +faithless +faithlessly +faithlessness +faithlessness +faithlessness's +faiths +fajita +fajitas +fajita's +fajitas +fajitas's +fake +faking +faked +faker +fakers +fakes +fake's +faker +faker's +fakir +fakirs +fakir's +falcon +falconer +falconers +falcons +falcon's +falconer +falconer's +falconry +falconry's +fall +fallen +falling +falls +fall's +fallacious +fallaciously +fallacy +fallacies +fallacy's +fallback +fallibility +fallibility's +infallibility's +infallibility +fallible +fallibleness +fallibleness +fallibleness's +fallibly +infallibly +falloff +falloffs +falloff's +fallout +fallout's +fallow +fallowing +fallowed +fallows +fallow's +false +falsely +falsest +falser +falseness +falsehood +falsehoods +falsehood's +falseness +falseness's +falsetto +falsettos +falsetto's +falsie +falsies +falsie's +falsifiable +falsification +falsification's +falsifier +falsifier's +falsify +falsification +falsifications +falsifying +falsified +falsifier +falsifiers +falsifies +falsity +falsities +falsity's +falter +faltering +falterings +faltered +falters +falter's +faltering +falteringly +fame's +fame +famed +familial +familiar +familiarly +familiars +familiar's +familiarity +familiarity's +unfamiliarity's +unfamiliarity +familiarization +familiarization's +familiarize +familiarizing +familiarized +familiarizes +family +families +family's +famine +famines +famine's +famish +famishing +famished +famishes +famous +famously +infamously +infamous +fan +fans +fan's +fanatic +fanatics +fanatic's +fanatical +fanatically +fanaticism +fanaticism's +fanboy +fanboys +fanboy's +fanciable +fancier +fancier's +fanciful +fancifully +fancifulness +fancifulness +fancifulness's +fancily +fanciness +fanciness's +fancy +fancying +fancied +fanciest +fancier +fanciers +fancies +fanciness +fancy's +fancywork +fancywork's +fandango +fandangos +fandango's +fandom +fanfare +fanfares +fanfare's +fang +fanged +fangs +fang's +fanlight +fanlights +fanlight's +fanned +fanning +fanny +fannies +fanny's +fantail +fantails +fantail's +fantasia +fantasias +fantasia's +fantasist +fantasists +fantasize +fantasizing +fantasized +fantasizes +fantastic +fantastical +fantastically +fantasy +fantasying +fantasied +fantasies +fantasy's +fanzine +fanzines +fanzine's +far +farad +farads +farad's +faradize +faradizing +faradized +faraway +farce +farces +farce's +farcical +farcically +fare +faring +fared +fares +fare's +farewell +farewells +farewell's +farina +farina's +farinaceous +farm +farming +farmings +farmed +farmer +farmers +farms +farm's +farmer +farmer's +farmhand +farmhands +farmhand's +farmhouse +farmhouses +farmhouse's +farming +farming's +farmland +farmlands +farmland's +farmstead +farmsteads +farmstead's +farmyard +farmyards +farmyard's +faro +faro's +farrago +farrago's +farragoes +farrier +farriers +farrier's +farrow +farrowing +farrowed +farrows +farrow's +farseeing +farsighted +farsightedness +farsightedness +farsightedness's +fart +farting +farted +farts +fart's +farther +farthermost +farthest +farthing +farthings +farthing's +fascia +fascias +fascia's +fascicle +fascicles +fascicle's +fascinate +fascination +fascinations +fascinating +fascinated +fascinates +fascinating +fascinatingly +fascination +fascination's +fascism +fascism's +fascist +fascists +fascist's +fascistic +fashion +fashioning +fashioned +fashioner +fashioners +fashions +fashion's +fashionable +fashionable +unfashionable +fashionably +unfashionably +fashioner +fashioner's +fashionista +fashionistas +fashionista's +fast +fasting +fasted +fastest +faster +fasts +fastness +fast's +fastback +fastbacks +fastback's +fastball +fastballs +fastball's +fasten +fastening +fastened +fastens +refastening +unfastening +refastened +unfastened +refastens +unfastens +refasten +unfasten +fastener +fasteners +fastener's +fastening +fastenings +fastening's +fastidious +fastidiously +fastidiousness +fastidiousness +fastidiousness's +fastness +fastnesses +fastness's +fat +fating +fated +fats +fatness +fat's +fatal +fatally +fatalism +fatalism's +fatalist +fatalists +fatalist's +fatalistic +fatalistically +fatality +fatalities +fatality's +fatback +fatback's +fate +fates +fate's +fateful +fatefully +fatefulness +fatefulness +fatefulness's +fathead +fatheaded +fatheads +fathead's +father +fatherly +fathering +fathered +fathers +father's +fatherhood +fatherhood's +fatherland +fatherlands +fatherland's +fatherless +fathom +fathoming +fathomed +fathoms +fathom's +fathomable +fathomable +unfathomable +fathomless +fatigue +fatiguing +fatigued +fatigues +fatigue's +fatigues +fatigues's +fatness +fatness's +fatso +fatsos +fatten +fattening +fattened +fattens +fatter +fattest +fattiness +fattiness's +fatty +fattiest +fattier +fatties +fattiness +fatty's +fatuity +fatuity's +fatuous +fatuously +fatuousness +fatuousness +fatuousness's +fatwa +fatwas +fatwa's +faucet +faucets +faucet's +fault +faulting +faulted +faults +fault's +defaulting +defaulted +defaults +default's +default +faultfinder +faultfinders +faultfinder's +faultfinding +faultfinding's +faultily +faultiness +faultiness's +faultless +faultlessly +faultlessness +faultlessness +faultlessness's +faulty +faultiest +faultier +faultiness +faun +fauns +faun's +fauna +faunas +fauna's +fauvism +fauvism's +fauvist +fauvists +fauvist's +faux +fave +faves +favor +favoring +favored +favors +favor's +disfavoring +disfavored +disfavors +disfavor's +disfavor +favorable +unfavorable +favorably +unfavorably +favorite +favorites +favorite's +favoritism +favoritism's +fawn +fawning +fawned +fawner +fawners +fawns +fawn's +fawner +fawner's +fax +faxing +faxed +faxes +fax's +fay +fayest +fayer +fays +fay's +faze +fazing +fazed +fazes +fazed +unfazed +fealty +fealty's +fear +fearing +feared +fears +fear's +fearful +fearfully +fearfulness +fearfulness +fearfulness's +fearless +fearlessly +fearlessness +fearlessness +fearlessness's +fearsome +feasibility +feasibility's +feasible +infeasible +unfeasible +feasibly +feast +feasting +feasted +feaster +feasters +feasts +feast's +feaster +feaster's +feat +feats +feat's +feather +feathering +feathered +feathers +feather's +featherbedding +featherbedding's +featherbrained +featherless +featherweight +featherweights +featherweight's +feathery +featheriest +featherier +feature +featuring +featured +features +feature's +featureless +febrile +fecal +feces +feces's +feckless +fecklessly +fecklessness +fecund +fecundate +fecundation +fecundating +fecundated +fecundates +fecundation +fecundation's +fecundity +fecundity's +fed +feds +fed's +federal +federally +federals +federal's +federalism +federalism's +federalist +federalists +federalist's +federalization +federalization's +federalize +federalizing +federalized +federalizes +federate +federation +federations +federating +federated +federates +confederation +confederations +confederating +confederated +confederates +confederate +federation +federation's +confederation's +confederation +fedora +fedoras +fedora's +fee +fees +fee's +feeble +feeblest +feebler +feebleness +feebleness +feebleness's +feebly +feed +feeding +feedings +feeder +feeders +feeds +feed's +feedback +feedback's +feedbag +feedbags +feedbag's +feeder +feeder's +feeding +feeding's +feedlot +feedlots +feedlot's +feel +feeling +feelings +feeler +feelers +feels +feel's +feeler +feeler's +feelgood +feeling +feelingly +feeling's +feet +feign +feigning +feigned +feigns +feigned +unfeigned +feint +feinting +feinted +feints +feint's +feisty +feistiest +feistier +feldspar +feldspar's +felicitate +felicitation +felicitations +felicitating +felicitated +felicitates +felicitation +felicitation's +felicitous +felicitously +felicity +felicities +felicity's +infelicities +infelicity's +infelicity +feline +felines +feline's +fell +felling +felled +fellest +feller +fellers +fells +fell's +fella +fellas +fellatio +fellatio's +fellow +fellows +fellow's +fellowman +fellowman's +fellowmen +fellowship +fellowships +fellowship's +felon +felons +felon's +felonious +felony +felonies +felony's +felt +felting +felted +felts +felt's +fem +female +females +femaleness +female's +femaleness +femaleness's +feminine +femininely +feminines +feminine's +femininity +femininity's +feminism +feminism's +feminist +feminists +feminist's +feminize +feminizing +feminized +feminizes +femoral +femur +femurs +femur's +fen +fens +fen's +fence +fencing +fenced +fencer +fencers +fences +fence's +fencer +fencer's +fencing +fencing's +fend +fending +fended +fender +fenders +fends +defending +defended +defender +defenders +defends +defend +fender +fender's +defender's +defender +fenestration +fenestration's +fennel +fennel's +feral +ferment +ferments +ferment's +deferments +conferments +deferment's +conferment's +deferment +conferment +fermentation +fermentation's +fermented +fermenting +fermium +fermium's +fern +ferns +fern's +ferny +ferniest +fernier +ferocious +ferociously +ferociousness +ferociousness +ferociousness's +ferocity +ferocity's +ferret +ferreting +ferreted +ferrets +ferret's +ferric +ferromagnetic +ferromagnetism +ferrous +ferrule +ferrules +ferrule's +ferry +ferrying +ferried +ferries +ferry's +ferryboat +ferryboats +ferryboat's +ferryman +ferryman's +ferrymen +fertile +infertile +fertility +fertility's +infertility's +infertility +fertilization +fertilization's +fertilize +fertilizing +fertilized +fertilizer +fertilizers +fertilizes +fertilized +unfertilized +fertilizer +fertilizer's +ferule +ferules +ferule's +fervency +fervency's +fervent +fervently +fervid +fervidly +fervor +fervor's +fess +fessing +fessed +fesses +confessing +professing +confessed +professed +confesses +professes +confess +profess +fest +festive +fester +festers +fests +fest's +festal +fester +festering +festered +fester's +festival +festivals +festival's +festive +festively +festiveness +festiveness +festiveness's +festivity +festivities +festivity's +festoon +festooning +festooned +festoons +festoon's +feta +feta's +fetal +fetch +fetching +fetched +fetcher +fetchers +fetches +fetcher +fetcher's +fetching +fetchingly +fete +feting +feted +fetes +fete's +fetid +fetidness +fetidness +fetidness's +fetish +fetishes +fetish's +fetishism +fetishism's +fetishist +fetishists +fetishist's +fetishistic +fetlock +fetlocks +fetlock's +fetter's +fetter +fettering +fettered +fetters +unfettering +unfettered +unfetters +unfetter +fettle +fettle's +fettuccine +fettuccine's +fetus +fetuses +fetus's +feud +feuding +feuded +feuds +feud's +feudal +feudalism +feudalism's +feudalistic +fever +fevered +fevers +fever's +feverish +feverishly +feverishness +feverishness +feverishness's +few +fewest +fewer +fewness +few's +fewness +fewness's +fey +fez +fez's +fezzes +ff +fiance +fiance's +defiance's +defiance +fiancee +fiancees +fiancee's +fiances +fiasco +fiasco's +fiascoes +fiat +fiats +fiat's +fib +fiber +fibers +fibs +fib's +fibbed +fibber +fibbers +fibber's +fibbing +fiber +fiber's +fiberboard +fiberboard's +fiberfill +fiberfill's +fiberglass +fiberglass's +fibril +fibrils +fibril's +fibrillate +fibrillation +fibrillating +fibrillated +fibrillates +fibrillation +fibrillation's +fibrin +fibrin's +fibroid +fibrosis +fibrosis's +fibrous +fibula +fibula's +fibulae +fibular +fiche +fiches +fiche's +fichu +fichus +fichu's +fickle +ficklest +fickler +fickleness +fickleness +fickleness's +fiction +fictions +fiction's +fictional +fictionally +fictionalization +fictionalizations +fictionalization's +fictionalize +fictionalizing +fictionalized +fictionalizes +fictitious +fictitiously +fictive +ficus +ficus's +fiddle +fiddling +fiddled +fiddler +fiddlers +fiddles +fiddle's +fiddler +fiddler's +fiddlesticks +fiddly +fiddliest +fiddlier +fidelity +fidelity's +infidelity's +infidelity +fidget +fidgeting +fidgeted +fidgets +fidget's +fidgety +fiduciary +fiduciaries +fiduciary's +fie +fief +fiefs +fief's +fiefdom +fiefdoms +fiefdom's +field +fielder +fielders +fields +field's +infielder +infielders +infields +infield's +infield +fielded +fielder +fielder's +infielder's +infielder +fielding +fieldsman +fieldsmen +fieldwork +fieldworker +fieldworkers +fieldwork's +fieldworker +fieldworker's +fiend +fiends +fiend's +fiendish +fiendishly +fierce +fiercely +fiercest +fiercer +fierceness +fierceness +fierceness's +fieriness +fieriness's +fiery +fieriest +fierier +fieriness +fiesta +fiestas +fiesta's +fife +fifer +fifers +fifes +fife's +fifer +fifer's +fifteen +fifteenth +fifteens +fifteen's +fifteenth +fifteenth's +fifteenths +fifth +fifthly +fifth's +fifths +fiftieth +fiftieth's +fiftieths +fifty +fiftieth +fifties +fifty's +fig +figs +fig's +figment +fight +fighting +fighter +fighters +fights +fight's +fightback +fighter +fighters +fighter's +infighters +infighter's +infighter +fighting +fighting's +infighting's +infighting +figment +figments +figment's +figuration +figuration's +configuration's +configuration +figurative +figuratively +figure's +figure +figuring +figured +figures +disfiguring +configuring +disfigured +configured +disfigures +configures +disfigure +configure +figurehead +figureheads +figurehead's +figurine +figurines +figurine's +filament +filaments +filament's +filamentous +filbert +filberts +filbert's +filch +filching +filched +filches +file's +defile's +profile's +file +filing +filed +files +refiling +defiling +profiling +refiled +defiled +profiled +refiles +defiles +profiles +refile +defile +profile +filename +filenames +filer +filers +filer's +defilers +defiler's +defiler +filet +filial +filibuster +filibustering +filibustered +filibusterer +filibusterers +filibusters +filibuster's +filibusterer +filibusterer's +filigree +filigreed +filigrees +filigree's +filigreeing +filing's +filings +fill's +fill +filling +filled +fills +refilling +infilling +refilled +infilled +refills +infills +refill +infill +filled +unfilled +filler +fillers +filler's +fillet +filleting +filleted +fillets +fillet's +filling +fillings +filling's +fillip +filliping +filliped +fillips +fillip's +filly +fillies +filly's +film +filming +filmed +films +film's +filminess +filminess's +filmmaker +filmmakers +filmmaker's +filmstrip +filmstrips +filmstrip's +filmy +filmiest +filmier +filminess +filo +filter +filtering +filtered +filterer +filterers +filters +filter's +filterable +filtered +unfiltered +filterer +filterer's +filth +filth's +filthily +filthiness +filthiness's +filthy +filthiest +filthier +filthiness +filtrate's +filtrate +filtration +filtrating +filtrated +filtrates +infiltration +infiltrating +infiltrated +infiltrates +infiltrate +filtration +filtration's +infiltration's +infiltration +fin +finer +fins +fin's +finagle +finagling +finagled +finagler +finaglers +finagles +finagler +finagler's +final +finally +finals +final's +finale +finales +finale's +finalist +finalists +finalist's +finality +finality's +finalization +finalization's +finalize +finalizing +finalized +finalizes +finance's +finance +financing +financed +finances +refinancing +refinanced +refinances +refinance +financial +financially +financier +financiers +financier's +financing +financing's +finch +finches +finch's +find +finding +findings +finder +finders +finds +find's +finder +finder's +finding +finding's +findings +findings's +fine's +confine's +fine +fining +fined +finest +fines +refining +defining +confining +refined +defined +confined +refines +defines +confines +refine +define +confine +finely +fineness +fineness's +finery +finery's +refinery's +refinery +finespun +finesse +finessing +finessed +finesses +finesse's +finger +fingering +fingerings +fingered +fingers +finger's +fingerboard +fingerboards +fingerboard's +fingering +fingering's +fingerling +fingerlings +fingerling's +fingermark +fingermarks +fingernail +fingernails +fingernail's +fingerprint +fingerprinting +fingerprinted +fingerprints +fingerprint's +fingertip +fingertips +fingertip's +finial +finials +finial's +finical +finickiness +finickiness's +finicky +finickiest +finickier +finickiness +finis +finises +finis's +finish's +finish +finishing +finished +finishes +refinishing +refinished +refinishes +refinish +finished +unfinished +finisher +finishers +finisher's +finite +finitely +infinitely +infinite +fink +finking +finked +finks +fink's +finned +finny +fir +firth +firing +firings +fired +firer +firers +firs +fir's +fire +fires +fire's +firearm +firearms +firearm's +fireball +fireballs +fireball's +firebomb +firebombing +firebombings +firebombed +firebombs +firebomb's +firebox +fireboxes +firebox's +firebrand +firebrands +firebrand's +firebreak +firebreaks +firebreak's +firebrick +firebricks +firebrick's +firebug +firebugs +firebug's +firecracker +firecrackers +firecracker's +firedamp +firedamp's +firefight +firefighting +firefighter +firefighters +firefights +firefight's +firefighter +firefighter's +firefighting +firefighting's +firefly +fireflies +firefly's +fireguard +fireguards +firehouse +firehouses +firehouse's +firelight +firelighter +firelighters +firelight's +fireman +fireman's +firemen +fireplace +fireplaces +fireplace's +fireplug +fireplugs +fireplug's +firepower +firepower's +fireproof +fireproofing +fireproofed +fireproofs +firer +firer's +firescreen +firescreens +fireside +firesides +fireside's +firestorm +firestorms +firestorm's +firetrap +firetraps +firetrap's +firetruck +firetrucks +firetruck's +firewall +firewalls +firewall's +firewater +firewater's +firewood +firewood's +firework +fireworks +firework's +firm +firmly +firming +firmed +firmest +firmer +firms +firmness +firm's +firmament +firmaments +firmament's +firmness +firmness's +firmware +firmware's +first +firstly +firsts +first's +firstborn +firstborns +firstborn's +firsthand +firth +firth's +firths +fiscal +fiscally +fiscals +fiscal's +fish +fishing +fished +fisher +fishers +fishes +fish's +fishbowl +fishbowls +fishbowl's +fishcake +fishcakes +fishcake's +fisher +fisher's +fisherman +fisherman's +fishermen +fishery +fisheries +fishery's +fishhook +fishhooks +fishhook's +fishily +fishiness +fishiness's +fishing +fishing's +fishmonger +fishmongers +fishmonger's +fishnet +fishnets +fishnet's +fishpond +fishponds +fishpond's +fishtail +fishtailing +fishtailed +fishtails +fishwife +fishwife's +fishwives +fishy +fishiest +fishier +fishiness +fissile +fission +fission's +fissionable +fissure +fissures +fissure's +fist +fists +fist's +fistfight +fistfights +fistfight's +fistful +fistfuls +fistful's +fisticuffs +fisticuffs's +fistula +fistulas +fistula's +fistulous +fistulous's +fit +fits +fit's +refits +profits +refit's +profit's +refit +profit +fitful +fitfully +fitfulness +fitfulness +fitfulness's +fitly +fitment +fitments +fitness +fitness's +unfitness's +unfitness +fitted +refitted +unfitted +fitter +fitters +fitter's +fittest +fitting +fittingly +fittings +fitting's +five +fiver +fivers +fives +five's +fix +fixing +fixings +fixed +fixer +fixers +fixes +fix's +fixable +fixate +fixative +fixation +fixations +fixating +fixated +fixates +fixation +fixation's +fixative +fixatives +fixative's +fixed +fixedly +fixer +fixer's +fixings +fixings's +fixity +fixity's +fixture +fixtures +fixture's +fizz +fizzing +fizzed +fizzes +fizz's +fizzle +fizzling +fizzled +fizzles +fizzle's +fizzy +fizziest +fizzier +fjord +fjords +fjord's +fl +fling +flings +fled +flab +flab's +flabbergast +flabbergasting +flabbergasted +flabbergasts +flabbily +flabbiness +flabbiness's +flabby +flabbiest +flabbier +flabbiness +flaccid +flaccidly +flaccidity +flaccidity's +flack +flacks +flack's +flag +flags +flag's +flagella +flagellant +flagellants +flagellate +flagellation +flagellating +flagellated +flagellates +flagellation +flagellation's +flagellum +flagellum's +flagged +flagging +unflagging +flagman +flagman's +flagmen +flagon +flagons +flagon's +flagpole +flagpoles +flagpole's +flagrance +flagrance's +flagrancy +flagrancy's +flagrant +flagrantly +flagship +flagships +flagship's +flagstaff +flagstaffs +flagstaff's +flagstone +flagstones +flagstone's +flail +flailing +flailed +flails +flail's +flair +flairs +flair's +flak +flak's +flake +flaking +flaked +flakes +flake's +flakiness +flakiness's +flaky +flakiest +flakier +flakiness +flamage +flambe +flambes +flambe's +flambeed +flambeing +flamboyance +flamboyance's +flamboyancy +flamboyancy's +flamboyant +flamboyantly +flame +flaming +flamings +flamed +flamer +flamers +flames +flame's +flamenco +flamencos +flamenco's +flameproof +flameproofing +flameproofed +flameproofs +flamethrower +flamethrowers +flamethrower's +flamingo +flamingos +flamingo's +flammability +flammability's +inflammability's +inflammability +flammable +flammables +flammable's +flan +flans +flan's +flange +flanges +flange's +flank +flanking +flanked +flanker +flankers +flanks +flank's +flanker +flanker's +flannel +flanneling +flanneled +flannels +flannel's +flannelette +flannelette's +flap +flaps +flap's +flapjack +flapjacks +flapjack's +flapped +flapper +flappers +flapper's +flapping +flare +flaring +flared +flares +flare's +flareup +flareups +flareup's +flash +flashing +flashed +flashest +flasher +flashers +flashes +flash's +flashback +flashbacks +flashback's +flashbulb +flashbulbs +flashbulb's +flashcard +flashcards +flashcard's +flashcube +flashcubes +flashcube's +flasher +flasher's +flashgun +flashguns +flashgun's +flashily +flashiness +flashiness's +flashing +flashing's +flashlight +flashlights +flashlight's +flashy +flashiest +flashier +flashiness +flask +flasks +flask's +flat +flatly +flats +flatness +flat's +flatbed +flatbeds +flatbed's +flatboat +flatboats +flatboat's +flatbread +flatcar +flatcars +flatcar's +flatfeet +flatfish +flatfishes +flatfish's +flatfoot +flatfooted +flatfoots +flatfoot's +flatiron +flatirons +flatiron's +flatland +flatland's +flatlet +flatlets +flatmate +flatmates +flatness +flatness's +flatted +flatten +flattening +flattened +flattens +flatter +flattering +flattered +flatterer +flatterers +flatters +flatterer +flatterer's +flattering +flatteringly +flattery +flattery's +flattest +flatting +flattish +flattop +flattops +flattop's +flatulence +flatulence's +flatulent +flatus +flatus's +flatware +flatware's +flatworm +flatworms +flatworm's +flaunt +flaunting +flaunted +flaunts +flaunt's +flaunting +flauntingly +flavor +flavoring +flavorings +flavored +flavors +flavor's +flavored +unflavored +flavorful +flavoring +flavoring's +flavorless +flavorsome +flaw +flawing +flawed +flaws +flaw's +flawless +flawlessly +flawlessness +flawlessness +flawlessness's +flax +flaxen +flax's +flay +flaying +flayed +flays +flea +fleas +flea's +fleabag +fleabags +fleabag's +fleabite +fleabites +fleapit +fleapits +fleck +flecking +flecked +flecks +fleck's +fledged +unfledged +fledgling +fledglings +fledgling's +flee +flees +fleece +fleecing +fleeced +fleecer +fleecers +fleeces +fleece's +fleecer +fleecer's +fleeciness +fleeciness's +fleecy +fleeciest +fleecier +fleeciness +fleeing +fleet +fleetly +fleeting +fleeted +fleetest +fleeter +fleets +fleetness +fleet's +fleetingly +fleetingly's +fleetingness +fleetingness's +fleetness +fleetness's +flesh +fleshly +fleshing +fleshed +fleshes +flesh's +fleshly +fleshliest +fleshlier +fleshpot +fleshpots +fleshpot's +fleshy +fleshiest +fleshier +flew +flex +flexes +flex's +reflexes +reflex's +reflex +flexed +flexibility +flexibility's +inflexibility's +inflexibility +flexible +inflexible +flexibly +inflexibly +flexing +flexion +flextime +flextime's +flibbertigibbet +flibbertigibbets +flibbertigibbet's +flick +flicking +flicked +flicker +flickers +flicks +flick's +flicker +flickering +flickered +flicker's +flier +flier's +flight +flights +flight's +flightiness +flightiness's +flightless +flighty +flightiest +flightier +flightiness +flimflam +flimflams +flimflam's +flimflammed +flimflamming +flimsily +flimsiness +flimsiness's +flimsy +flimsiest +flimsier +flimsiness +flinch +flinching +flinched +flinches +flinch's +fling +flinging +fling's +flint +flints +flint's +flintlock +flintlocks +flintlock's +flinty +flintiest +flintier +flip +flips +flip's +flippancy +flippancy's +flippant +flippantly +flipped +flipper +flippers +flipper's +flippest +flipping +flippy +flippies +flirt +flirting +flirted +flirts +flirt's +flirtation +flirtations +flirtation's +flirtatious +flirtatiously +flirtatiousness +flirtatiousness +flirtatiousness's +flirty +flit +flits +flit's +flitted +flitting +float +floating +floated +floater +floaters +floats +float's +floater +floater's +flock +flocking +flocked +flocks +flock's +flocking +flocking's +floe +floes +floe's +flog +flogs +flogged +flogger +floggers +flogger's +flogging +floggings +flogging's +flood +flooding +flooded +flooder +floods +flood's +floodgate +floodgates +floodgate's +floodlight +floodlighting +floodlighted +floodlights +floodlight's +floodlit +floodplain +floodplains +floodplain's +floodwater +floodwaters +floodwater's +floor +flooring +floored +floors +floor's +floorboard +floorboards +floorboard's +flooring +flooring's +floorwalker +floorwalkers +floorwalker's +floozy +floozies +floozy's +flop +flops +flop's +flophouse +flophouses +flophouse's +flopped +floppily +floppiness +floppiness's +flopping +floppy +floppiest +floppier +floppies +floppiness +floppy's +flora +floras +flora's +floral +florescence +florescence's +inflorescence's +inflorescence +florescent +inflorescent +floret +florets +floret's +florid +floridly +floridness +floridness +floridness's +florin +florins +florin's +florist +florists +florist's +floss +flossing +flossed +flosses +floss's +flossy +flossiest +flossier +flotation +flotations +flotation's +flotilla +flotillas +flotilla's +flotsam +flotsam's +flounce +flouncing +flounced +flounces +flounce's +flouncy +flounder +floundering +floundered +flounders +flounder's +flour +flouring +floured +flours +flour's +flourish +flourishing +flourished +flourishes +flourish's +floury +flout +flouting +flouted +flouter +flouters +flouts +flout's +flouter +flouter's +flow +flowing +flowed +flows +flow's +flowchart +flowcharts +flowchart's +flower's +flower +flowering +flowered +flowers +deflowering +deflowered +deflowers +deflower +flowerbed +flowerbeds +flowerbed's +floweriness +floweriness's +flowering +flowerings +flowerless +flowerpot +flowerpots +flowerpot's +flowery +floweriest +flowerier +floweriness +flown +flt +flu +flu's +flub +flubs +flub's +flubbed +flubbing +fluctuate +fluctuation +fluctuations +fluctuating +fluctuated +fluctuates +fluctuation +fluctuation's +flue +flues +flue's +fluency +fluency's +fluent +fluently +fluff +fluffing +fluffed +fluffs +fluff's +fluffiness +fluffiness's +fluffy +fluffiest +fluffier +fluffiness +fluid +fluidly +fluids +fluid's +fluidity +fluidity's +fluke +flukes +fluke's +fluky +flukiest +flukier +flume +flumes +flume's +flummox +flummoxing +flummoxed +flummoxes +flung +flunk +flunking +flunked +flunks +flunk's +flunky +flunkies +flunky's +fluoresce +fluorescing +fluoresced +fluoresces +fluorescence +fluorescence's +fluorescent +fluoridate +fluoridation +fluoridating +fluoridated +fluoridates +fluoridation +fluoridation's +fluoride +fluorides +fluoride's +fluorine +fluorine's +fluorite +fluorite's +fluorocarbon +fluorocarbons +fluorocarbon's +fluoroscope +fluoroscopes +fluoroscope's +fluoroscopic +flurry +flurrying +flurried +flurries +flurry's +flush +flushing +flushed +flushest +flusher +flushes +flush's +fluster +flustering +flustered +flusters +fluster's +flute +fluting +fluted +flutes +flute's +fluting +fluting's +flutist +flutists +flutist's +flutter +fluttering +fluttered +flutters +flutter's +fluttery +fluvial +flux +fluxes +flux's +influxes +influx's +influx +fluxed +fluxing +fly +flying +flied +fliest +flier +fliers +flies +fly's +flyable +flyaway +flyblown +flyby +flyby's +flybys +flycatcher +flycatchers +flycatcher's +flying +flying's +flyleaf +flyleaf's +flyleaves +flyover +flyovers +flyover's +flypaper +flypapers +flypaper's +flypast +flypasts +flysheet +flysheets +flyspeck +flyspecking +flyspecked +flyspecks +flyspeck's +flyswatter +flyswatters +flyswatter's +flytrap +flytraps +flyway +flyways +flyway's +flyweight +flyweights +flyweight's +flywheel +flywheels +flywheel's +foal +foaling +foaled +foals +foal's +foam +foaming +foamed +foams +foam's +foaminess +foaminess's +foamy +foamiest +foamier +foaminess +fob +fobs +fob's +fobbed +fobbing +focal +focally +focus's +focus +focusing +focused +focuses +refocusing +refocused +refocuses +refocus +focused +unfocused +fodder +fodders +fodder's +foe +foes +foe's +fog's +fog +fogs +defogs +defog +fogbound +fogged +defogged +foggily +fogginess +fogginess's +fogging +defogging +foggy +foggiest +foggier +fogginess +foghorn +foghorns +foghorn's +fogy +fogies +fogy's +fogyish +foible +foibles +foible's +foil +foiling +foiled +foils +foil's +foist +foisting +foisted +foists +fol +fold's +fold +folding +folded +folds +refolding +unfolding +refolded +unfolded +refolds +unfolds +refold +unfold +foldaway +folder +folders +folder's +foldout +foldouts +foldout's +foliage +foliage's +folic +folio +folios +folio's +folk +folks +folk's +folklore +folklore's +folkloric +folklorist +folklorists +folklorist's +folksiness +folksiness's +folksinger +folksingers +folksinger's +folksinging +folksinging's +folksy +folksiest +folksier +folksiness +folktale +folktales +folktale's +folkway +folkways +folkway's +foll +follicle +follicles +follicle's +follow +following +followings +followed +follower +followers +follows +follower +follower's +following +following's +followup +followups +folly +follies +folly's +foment +fomenting +fomented +foments +fomentation +fomentation's +fond +fondly +fondest +fonder +fondness +fondant +fondants +fondant's +fondle +fondling +fondled +fondles +fondness +fondness's +fondue +fondues +fondue's +font +fonts +font's +fontanel +fontanels +fontanel's +foo +foobar +food +foods +food's +foodie +foodies +foodie's +foodstuff +foodstuffs +foodstuff's +fool +fooling +fooled +fools +fool's +foolery +fooleries +foolery's +foolhardily +foolhardiness +foolhardiness's +foolhardy +foolhardiest +foolhardier +foolhardiness +foolish +foolishly +foolishness +foolishness +foolishness's +foolproof +foolscap +foolscap's +foot +footing +footings +footed +footer +footers +foots +foot's +footage +footage's +football +footballing +footballer +footballers +footballs +football's +footballer +footballer's +footbridge +footbridges +footbridge's +footfall +footfalls +footfall's +foothill +foothills +foothill's +foothold +footholds +foothold's +footie +footing +footing's +footless +footlights +footlights's +footling +footlings +footling's +footlocker +footlockers +footlocker's +footloose +footman +footman's +footmen +footnote +footnoting +footnoted +footnotes +footnote's +footpath +footpath's +footpaths +footplate +footplates +footprint +footprints +footprint's +footrace +footraces +footrace's +footrest +footrests +footrest's +footsie +footsies +footsie's +footslogging +footsore +footstep +footsteps +footstep's +footstool +footstools +footstool's +footwear +footwear's +footwork +footwork's +footy +fop +fops +fop's +foppery +foppery's +foppish +foppishness +foppishness +foppishness's +for +forth +fora +forage +foraging +foraged +forager +foragers +forages +forage's +forager +forager's +foray +foraying +forayed +forays +foray's +forbade +forbear +forbearing +forbears +forbear's +forbearance +forbearance's +forbid +forbids +forbidden +forbidding +forbiddingly +forbiddings +forbore +forborne +force +forcing +forced +forces +force's +forced +unforced +forceful +forcefully +forcefulness +forcefulness +forcefulness's +forceps +forceps's +forcible +forcibly +ford +fording +forded +fords +ford's +fordable +fore +fores +fore's +forearm +forearming +forearmed +forearms +forearm's +forebear +forebears +forebear's +forebode +foreboding +forebodings +foreboded +forebodes +foreboding +foreboding's +forecast +forecasting +forecaster +forecasters +forecasts +forecast's +forecaster +forecaster's +forecastle +forecastles +forecastle's +foreclose +foreclosing +foreclosed +forecloses +foreclosure +foreclosures +foreclosure's +forecourt +forecourts +forecourt's +foredoom +foredooming +foredoomed +foredooms +forefather +forefathers +forefather's +forefeet +forefinger +forefingers +forefinger's +forefoot +forefoot's +forefront +forefronts +forefront's +forego +foregoing +foregoes +foregone +foreground +foregrounding +foregrounded +foregrounds +foreground's +forehand +forehands +forehand's +forehead +foreheads +forehead's +foreign +foreigner +foreigners +foreignness +foreigner +foreigner's +foreignness +foreignness's +foreknew +foreknow +foreknowing +foreknows +foreknowledge +foreknowledge's +foreknown +foreleg +forelegs +foreleg's +forelimb +forelimbs +forelimb's +forelock +forelocks +forelock's +foreman +foreman's +foremast +foremasts +foremast's +foremen +foremost +forename +forenamed +forenames +forename's +forenoon +forenoons +forenoon's +forensic +forensics +forensic's +forensically +forensics +forensics's +foreordain +foreordaining +foreordained +foreordains +forepart +foreparts +forepart's +foreperson +forepersons +foreperson's +foreplay +foreplay's +forequarter +forequarters +forequarter's +forerunner +forerunners +forerunner's +foresail +foresails +foresail's +foresaw +foresee +foreseer +foreseers +foresees +foreseeable +foreseeable +unforeseeable +foreseeing +foreseen +unforeseen +foreseer +foreseer's +foreshadow +foreshadowing +foreshadowed +foreshadows +foreshore +foreshores +foreshorten +foreshortening +foreshortened +foreshortens +foresight +foresighted +foresight's +foresightedness +foresightedness's +foreskin +foreskins +foreskin's +forest's +forest +foresting +forested +forests +reforesting +deforesting +reforested +deforested +reforests +deforests +reforest +deforest +forestall +forestalling +forestalled +forestalls +forestation +forestation's +reforestation's +deforestation's +reforestation +deforestation +forester +foresters +forester's +forestland +forestland's +forestry +forestry's +foretaste +foretasting +foretasted +foretastes +foretaste's +foretell +foretelling +foretells +forethought +forethought's +foretold +forever +forever's +forevermore +forewarn +forewarning +forewarned +forewarns +forewent +forewoman +forewoman's +forewomen +foreword +forewords +foreword's +forfeit +forfeiting +forfeited +forfeits +forfeit's +forfeiture +forfeitures +forfeiture's +forgather +forgathering +forgathered +forgathers +forgave +forge +forgive +forging +forgings +forged +forger +forgers +forges +forge's +forger +forger's +forgery +forgeries +forgery's +forget +forgets +forgetful +forgetfully +forgetfulness +forgetfulness +forgetfulness's +forgettable +unforgettable +forgetting +forging +forging's +forgivable +unforgivable +forgive +forgiving +forgiver +forgivers +forgives +forgiveness +forgivable +forgiven +forgiveness +forgiveness's +forgiver +forgiver's +forgiving +unforgiving +forgo +forgoing +forgoer +forgoers +forgoer +forgoer's +forgoes +forgone +forgot +forgotten +unforgotten +fork +forking +forked +forks +fork's +forkful +forkfuls +forkful's +forklift +forklifts +forklift's +forlorn +forlornly +form's +form +forming +formed +forms +reforming +informing +deforming +conforming +reformed +informed +deformed +conformed +reforms +informs +deforms +conforms +reform +inform +deform +conform +formal +formally +formals +formal's +formaldehyde +formaldehyde's +formalin +formalism +formalism's +formalist +formalists +formalist's +formalities +formality +formality's +informality's +informality +formalization +formalization's +formalize +formalizing +formalized +formalizes +format +formative +formats +format's +formation +formations +formation's +reformations +deformations +conformations +reformation's +deformation's +conformation's +reformation +deformation +conformation +formatted +reformatted +formatting +formatting's +formed +unformed +former +former's +reformer's +informer's +conformer's +reformer +informer +conformer +formerly +formfitting +formic +formidable +formidably +formless +formlessly +formlessness +formlessness +formlessness's +formula +formulas +formula's +formulae +formulaic +formulate +formulation +formulations +formulating +formulated +formulates +reformulation +reformulations +reformulating +reformulated +reformulates +reformulate +formulated +unformulated +formulation +formulation's +reformulation's +reformulation +formulator +formulators +formulator's +fornicate +fornication +fornicating +fornicated +fornicates +fornication +fornication's +fornicator +fornicators +fornicator's +forsake +forsaking +forsakes +forsaken +forsook +forsooth +forswear +forswearing +forswears +forswore +forsworn +forsythia +forsythias +forsythia's +fort +forts +fort's +forte +fortes +forte's +forthcoming +forthcoming's +forthright +forthrightly +forthrightness +forthrightness +forthrightness's +forthwith +fortieth +fortieth's +fortieths +fortification +fortification's +fortified +unfortified +fortifier +fortifier's +fortify +fortification +fortifications +fortifying +fortified +fortifier +fortifiers +fortifies +fortissimo +fortitude +fortitude's +fortnight +fortnightly +fortnights +fortnight's +fortress +fortresses +fortress's +fortuitous +fortuitously +fortuitousness +fortuitousness +fortuitousness's +fortuity +fortuity's +fortunate +fortunately +unfortunately +unfortunate +fortune +fortunes +fortune's +fortuneteller +fortunetellers +fortuneteller's +fortunetelling +fortunetelling's +forty +fortieth +forties +forty's +forum +forums +forum's +forward +forwardly +forwarding +forwarded +forwardest +forwarder +forwarders +forwards +forwardness +forward's +forwarder +forwarder's +forwardness +forwardness's +forwent +fossa +fossil +fossils +fossil's +fossilization +fossilization's +fossilize +fossilizing +fossilized +fossilizes +foster +fostering +fostered +fosters +fought +foul +foully +fouling +fouled +foulest +fouler +fouls +foulness +foul's +foulard +foulard's +foulmouthed +foulness +foulness's +found +founding +founded +founds +confounding +confounded +confounds +confound +foundation +foundations +foundation's +foundational +founded +unfounded +founder +foundering +foundered +founders +founder's +foundling +foundlings +foundling's +foundry +foundries +foundry's +fount +founts +fount's +fountain +fountains +fountain's +fountainhead +fountainheads +fountainhead's +four +fourth +fours +four's +fourfold +fourposter +fourposters +fourposter's +fourscore +fourscore's +foursome +foursomes +foursome's +foursquare +fourteen +fourteenth +fourteens +fourteen's +fourteenth +fourteenth's +fourteenths +fourth +fourthly +fourth's +fourths +fowl +fowling +fowled +fowls +fowl's +fox +foxing +foxed +foxes +fox's +foxfire +foxfire's +foxglove +foxgloves +foxglove's +foxhole +foxholes +foxhole's +foxhound +foxhounds +foxhound's +foxhunt +foxhunting +foxhunts +foxily +foxiness +foxiness's +foxtrot +foxtrots +foxtrot's +foxtrotted +foxtrotting +foxy +foxiest +foxier +foxiness +foyer +foyers +foyer's +fps +fr +fracas +fracases +fracas's +frack +fracking +fracked +fracks +fractal +fractals +fractal's +fraction +fractions +fraction's +infractions +infraction's +infraction +fractional +fractionally +fractious +fractiously +fractiousness +fractiousness +fractiousness's +fracture +fracturing +fractured +fractures +fracture's +frag +frags +fragile +fragilest +fragiler +fragility +fragility's +fragment +fragmenting +fragmented +fragments +fragment's +fragmentary +fragmentary's +fragmentation +fragmentation's +fragrance +fragrances +fragrance's +fragrant +fragrantly +frail +frailly +frailest +frailer +frailness +frailness +frailness's +frailty +frailties +frailty's +frame +framing +framed +framer +framers +frames +frame's +framed +unframed +framer +framer's +framework +frameworks +framework's +franc +francs +franc's +franchise's +franchise +franchising +franchised +franchises +disfranchising +disfranchised +disfranchises +disfranchise +franchisee +franchisees +franchisee's +franchiser +franchisers +franchiser's +francium +francium's +francophone +frangibility +frangibility's +frangible +frank +frankly +franking +franked +frankest +franker +franks +frankness +frank's +frankfurter +frankfurters +frankfurter's +frankincense +frankincense's +frankness +frankness's +frantic +frantically +frappe +frappes +frappe's +frat +frats +frat's +fraternal +fraternally +fraternity +fraternities +fraternity's +confraternities +confraternity's +confraternity +fraternization +fraternization's +fraternize +fraternizing +fraternized +fraternizer +fraternizers +fraternizes +fraternizer +fraternizer's +fratricidal +fratricide +fratricides +fratricide's +fraud's +fraud +frauds +fraudster +fraudsters +fraudulence +fraudulence's +fraudulent +fraudulently +fraught +fray's +fray +fraying +frayed +frays +defraying +defrayed +defrays +defray +frazzle +frazzling +frazzled +frazzles +frazzle's +freak +freaking +freaked +freaks +freak's +freakish +freakishly +freakishness +freakishness +freakishness's +freaky +freakiest +freakier +freckle +freckling +freckled +freckles +freckle's +freckly +free +freely +freed +freest +freer +frees +freebase +freebasing +freebased +freebases +freebase's +freebie +freebies +freebie's +freebooter +freebooters +freebooter's +freeborn +freedman +freedman's +freedmen +freedom +freedoms +freedom's +freehand +freehold +freeholder +freeholders +freeholds +freehold's +freeholder +freeholder's +freeing +freelance +freelancing +freelanced +freelancer +freelancers +freelances +freelance's +freelancer +freelancer's +freeload +freeloading +freeloaded +freeloader +freeloaders +freeloads +freeloader +freeloader's +freeman +freeman's +freemasonry +freemen +freephone +freesia +freesias +freestanding +freestone +freestones +freestone's +freestyle +freestyles +freestyle's +freethinker +freethinkers +freethinker's +freethinking +freethinking's +freeware +freeware's +freeway +freeways +freeway's +freewheel +freewheeling +freewheeled +freewheels +freewill +freezable +freeze's +freeze +freezing +freezes +refreezing +unfreezing +refreezes +unfreezes +refreeze +unfreeze +freezer +freezers +freezer's +freezing's +freight +freighting +freighted +freighter +freighters +freights +freight's +freighter +freighter's +french +frenemy +frenemies +frenetic +frenetically +frenzied +frenziedly +frenzy +frenzied +frenzies +frenzy's +freq +frequencies +frequency +frequency's +infrequency's +infrequency +frequent +frequently +frequenting +frequented +frequentest +frequenter +frequenters +frequents +frequented +unfrequented +frequenter +frequenter's +fresco +fresco's +frescoes +fresh +freshen +freshens +freshly +freshest +fresher +freshers +freshness +freshen +freshening +freshened +freshener +fresheners +freshener +freshener's +freshet +freshets +freshet's +freshman +freshman's +freshmen +freshness +freshness's +freshwater +freshwater's +fret +frets +fret's +fretful +fretfully +fretfulness +fretfulness +fretfulness's +fretsaw +fretsaws +fretsaw's +fretted +fretting +fretwork +fretwork's +friable +friar +friars +friar's +friary +friaries +friary's +fricassee +fricasseed +fricassees +fricassee's +fricasseeing +fricative +fricatives +fricative's +friction +frictions +friction's +frictional +fridge +fridges +fridge's +friedcake +friedcakes +friedcake's +friend's +friend +friendly +friending +friended +friends +unfriendly +unfriending +unfriended +unfriends +unfriend +friendless +friendlies +friendliness +friendliness's +unfriendliness's +unfriendliness +friendly's +friendly +friendliest +friendlier +friendliness +unfriendlier +unfriendliness +unfriendly +friendship +friendships +friendship's +frieze +friezes +frieze's +frig +frigs +frigate +frigates +frigate's +frigged +frigging +fright +frighten +frightens +frighting +frighted +frights +fright's +frighten +frightening +frightened +frightening +frighteningly +frightful +frightfully +frightfulness +frightfulness +frightfulness's +frigid +frigidly +frigidness +frigidity +frigidity's +frigidness +frigidness's +frill +frilled +frills +frill's +frilly +frilliest +frillier +fringe's +fringe +fringing +fringed +fringes +infringing +infringed +infringes +infringe +frippery +fripperies +frippery's +frisk +frisking +frisked +frisks +friskily +friskiness +friskiness's +frisky +friskiest +friskier +friskiness +frisson +frissons +fritter +frittering +frittered +fritters +fritter's +fritz +fritz's +frivolity +frivolities +frivolity's +frivolous +frivolously +frivolousness +frivolousness +frivolousness's +frizz +frizzly +frizzing +frizzed +frizzes +frizz's +frizzle +frizzling +frizzled +frizzles +frizzle's +frizzy +frizziest +frizzier +fro +frock's +frock +frocks +unfrocks +defrocks +unfrock +defrock +frog +frogs +frog's +frogging +froggings +frogman +frogman's +frogmarch +frogmarching +frogmarched +frogmarches +frogmen +frogspawn +frolic +frolics +frolic's +frolicked +frolicker +frolickers +frolicker's +frolicking +frolicsome +from +frond +fronds +frond's +front's +front +fronting +fronted +fronts +confronting +confronted +confronts +confront +frontage +frontages +frontage's +frontal +frontally +frontbench +frontbencher +frontbenchers +frontbenches +frontier +frontiers +frontier's +frontiersman +frontiersman's +frontiersmen +frontierswoman +frontierswomen +frontispiece +frontispieces +frontispiece's +frontward +frontwards +frosh +frosh's +frost's +frost +frosting +frosted +frosts +defrosting +defrosted +defrosts +defrost +frostbit +frostbite +frostbiting +frostbites +frostbite's +frostbitten +frostily +frostiness +frostiness's +frosting +frostings +frosting's +frosty +frostiest +frostier +frostiness +froth +frothing +frothed +froth's +frothiness +frothiness's +froths +frothy +frothiest +frothier +frothiness +froufrou +froufrou's +froward +frowardness +frowardness +frowardness's +frown +frowning +frowned +frowns +frown's +frowzily +frowziness +frowziness's +frowzy +frowziest +frowzier +frowziness +froze +refroze +unfroze +frozen +refrozen +unfrozen +fructify +fructifying +fructified +fructifies +fructose +fructose's +frugal +frugally +frugality +frugality's +fruit +fruiting +fruited +fruits +fruit's +fruitcake +fruitcakes +fruitcake's +fruiterer +fruiterers +fruitful +fruitfully +fruitfulness +fruitfulness +fruitfulness's +fruitiness +fruitiness's +fruition +fruition's +fruitless +fruitlessly +fruitlessness +fruitlessness +fruitlessness's +fruity +fruitiest +fruitier +fruitiness +frump +frumps +frump's +frumpish +frumpy +frumpiest +frumpier +frustrate +frustration +frustrations +frustrating +frustrated +frustrates +frustrating +frustratingly +frustration +frustration's +frustum +frustums +frustum's +fry +frying +fried +fries +fry's +fryer +fryers +fryer's +ft +ftp +ftping +ftpers +ftps +fuchsia +fuchsias +fuchsia's +fuck +fucking +fucked +fucker +fuckers +fucks +fuck's +fucker +fucker's +fuckhead +fuckheads +fuddle +fuddling +fuddled +fuddles +fuddle's +fudge +fudging +fudged +fudges +fudge's +fuehrer +fuehrers +fuehrer's +fuel's +fuel +fueling +fueled +fuels +refueling +refueled +refuels +refuel +fug +fugal +fuggy +fugitive +fugitives +fugitive's +fugue +fugues +fugue's +fuhrer +fuhrers +fuhrer's +fulcrum +fulcrums +fulcrum's +fulfill +fulfilling +fulfilled +fulfills +fulfillment +fulfilled +unfulfilled +fulfilling +unfulfilling +fulfillment +fulfillment's +full +fulling +fulled +fullest +fuller +fullers +fulls +fullness +full's +fullback +fullbacks +fullback's +fuller +fuller's +fullness +fullness's +fully +fulminate +fulmination +fulminations +fulminating +fulminated +fulminates +fulmination +fulmination's +fulsome +fulsomely +fulsomeness +fulsomeness +fulsomeness's +fum +fums +fumble +fumbling +fumbled +fumbler +fumblers +fumbles +fumble's +fumbler +fumbler's +fumbling +fumblingly +fume +fuming +fumed +fumes +fume's +fumigant +fumigants +fumigant's +fumigate +fumigation +fumigating +fumigated +fumigates +fumigation +fumigation's +fumigator +fumigators +fumigator's +fumy +fumiest +fumier +fun +fun's +function +functioning +functioned +functions +function's +functional +functionally +functionalism +functionalist +functionalists +functionality +functionalities +functionary +functionaries +functionary's +functor +fund +funding +funded +funds +fund's +refunding +refunded +refunds +refund's +refund +fundamental +fundamentally +fundamentals +fundamental's +fundamentalism +fundamentalism's +fundamentalist +fundamentalists +fundamentalist's +funded +unfunded +funding +funding's +fundraiser +fundraisers +fundraiser's +fundraising +funeral +funerals +funeral's +funerary +funereal +funereally +funfair +funfairs +fungal +fungi +fungible +fungibles +fungible's +fungicidal +fungicide +fungicides +fungicide's +fungoid +fungous +fungus +fungus's +funicular +funiculars +funicular's +funk +funking +funked +funks +funk's +funkiness +funkiness's +funky +funkiest +funkier +funkiness +funnel +funneling +funneled +funnels +funnel's +funner +funnest +funnily +funniness +funniness's +funny +funniest +funnier +funnies +funniness +funny's +funnyman +funnyman's +funnymen +fur +furs +fur's +furbelow +furbelow's +furbish +furbishing +furbished +furbishes +refurbishing +refurbished +refurbishes +refurbish +furious +furiously +furl's +furl +furling +furled +furls +unfurling +unfurled +unfurls +unfurl +furlong +furlongs +furlong's +furlough +furloughing +furloughed +furlough's +furloughs +furn +furnace +furnaces +furnace's +furnish +furnishing +furnished +furnishes +refurnishing +refurnished +refurnishes +refurnish +furnished +unfurnished +furnishings +furnishings's +furniture +furniture's +furor +furors +furor's +furred +furrier +furrier's +furriness +furriness's +furring +furring's +furrow +furrowing +furrowed +furrows +furrow's +furry +furriest +furrier +furriers +furriness +further +furthering +furthered +furthers +furtherance +furtherance's +furthermore +furthermost +furthest +furtive +furtively +furtiveness +furtiveness +furtiveness's +fury +furies +fury's +furze +furze's +fuse's +refuse's +fuse +fusing +fused +fuses +refusing +infusing +defusing +confusing +refused +infused +defused +confused +refuses +infuses +defuses +confuses +refuse +infuse +defuse +confuse +fusee +fusees +fusee's +fuselage +fuselages +fuselage's +fusibility +fusibility's +fusible +fusilier +fusiliers +fusilier's +fusillade +fusillades +fusillade's +fusion +fusions +fusion's +infusions +confusions +profusions +infusion's +confusion's +profusion's +infusion +confusion +profusion +fuss +fussing +fussed +fusses +fuss's +fussbudget +fussbudgets +fussbudget's +fussily +fussiness +fussiness's +fusspot +fusspots +fusspot's +fussy +fussiest +fussier +fussiness +fustian +fustian's +fustiness +fustiness's +fusty +fustiest +fustier +fustiness +fut +futile +futilely +futility +futility's +futon +futons +futon's +future +futures +future's +futurism +futurism's +futurist +futurists +futurist's +futuristic +futurity +futurities +futurity's +futurologist +futurologists +futurologist's +futurology +futurology's +futz +futzing +futzed +futzes +fuzz +fuzzing +fuzzed +fuzzes +fuzz's +fuzzball +fuzzballs +fuzzily +fuzziness +fuzziness's +fuzzy +fuzziest +fuzzier +fuzziness +fwd +fwy +g +give +gen +gens +gs +gable +gab +gabs +gab's +gabardine +gabardines +gabardine's +gabbed +gabbiness +gabbiness's +gabbing +gabble +gabbling +gabbled +gabbles +gabble's +gabby +gabbiest +gabbier +gabbiness +gaberdine +gaberdines +gaberdine's +gabfest +gabfests +gabfest's +gable +gabled +gables +gable's +gad +gads +gadabout +gadabouts +gadabout's +gadded +gadder +gadders +gadder's +gadding +gadfly +gadflies +gadfly's +gadget +gadgets +gadget's +gadgetry +gadgetry's +gadolinium +gadolinium's +gaff +gaffing +gaffed +gaffer +gaffers +gaffs +gaff's +gaffe +gaffes +gaffe's +gaffer +gaffer's +gag +gags +gag's +gaga +gagged +gagging +gaggle +gaggles +gaggle's +gaiety +gaiety's +gaily +gain's +gain +gaining +gained +gains +regaining +regained +regains +regain +gainer +gainers +gainer's +gainful +gainfully +gainsaid +gainsay +gainsaying +gainsayer +gainsayers +gainsays +gainsayer +gainsayer's +gait +gaiter +gaiters +gaits +gait's +gaiter +gaiter's +gal +gals +gal's +gala +galas +gala's +galactic +galaxy +galaxies +galaxy's +gale's +gale +gales +regales +regale +galena +galena's +gall +galling +galled +galls +gall's +gallant +gallantly +gallants +gallant's +gallantry +gallantry's +gallbladder +gallbladders +gallbladder's +galleon +galleons +galleon's +galleria +gallerias +galleria's +gallery +galleries +gallery's +galley +galleys +galley's +gallimaufry +gallimaufries +gallimaufry's +gallium +gallium's +gallivant +gallivanting +gallivanted +gallivants +gallon +gallons +gallon's +gallop +galloping +galloped +gallops +gallop's +gallows +gallows's +gallstone +gallstones +gallstone's +galoot +galoots +galoot's +galore +galosh +galoshes +galosh's +galumph +galumphing +galumphed +galumphs +galvanic +galvanism +galvanism's +galvanization +galvanization's +galvanize +galvanizing +galvanized +galvanizes +galvanometer +galvanometers +galvanometer's +gambit +gambits +gambit's +gamble +gambling +gambled +gambler +gamblers +gambles +gamble's +gambler +gambler's +gambling +gambling's +gambol +gamboling +gamboled +gambols +gambol's +game +gamely +gaming +gamed +gamest +gamer +games +gameness +game's +gamecock +gamecocks +gamecock's +gamekeeper +gamekeepers +gamekeeper's +gameness +gameness's +gamesmanship +gamesmanship's +gamester +gamesters +gamester's +gamete +gametes +gamete's +gametic +gamin +gamins +gamin's +gamine +gamines +gamine's +gaminess +gaminess's +gaming +gaming's +gamma +gammas +gamma's +gammon +gammon's +gammy +gamut +gamuts +gamut's +gamy +gamiest +gamier +gaminess +gander +ganders +gander's +gang +ganging +ganged +gangs +gang's +gangbusters +gangbusters's +gangland +gangland's +ganglia +gangling +ganglion +ganglion's +ganglionic +gangplank +gangplanks +gangplank's +gangrene +gangrening +gangrened +gangrenes +gangrene's +gangrenous +gangsta +gangstas +gangster +gangsters +gangster's +gangway +gangways +gangway's +ganja +gannet +gannets +gannet's +gantlet +gantlets +gantlet's +gantry +gantries +gantry's +gap +gaping +gaped +gaps +gap's +gape +gapes +gape's +gar +gars +gar's +garment +garage +garaging +garaged +garages +garage's +garb +garbing +garbed +garbs +garb's +garbage +garbage's +garbageman +garbanzo +garbanzos +garbanzo's +garble +garbling +garbled +garbles +garcon +garcons +garcon's +garden +gardening +gardened +gardener +gardeners +gardens +garden's +gardener +gardener's +gardenia +gardenias +gardenia's +gardening +gardening's +garfish +garfishes +garfish's +gargantuan +gargle +gargling +gargled +gargles +gargle's +gargoyle +gargoyles +gargoyle's +garish +garishly +garishness +garishness +garishness's +garland +garlanding +garlanded +garlands +garland's +garlic +garlic's +garlicky +garment +garments +garment's +garner +garnering +garnered +garners +garnet +garnets +garnet's +garnish +garnishing +garnished +garnishes +garnish's +garnishment +garnishee +garnisheed +garnishees +garnishee's +garnisheeing +garnishment +garnishments +garnishment's +garret +garrets +garret's +garrison +garrisoning +garrisoned +garrisons +garrison's +garrote +garroting +garroted +garroter +garroters +garrotes +garrote's +garroter +garroter's +garrulity +garrulity's +garrulous +garrulously +garrulousness +garrulousness +garrulousness's +garter +garters +garter's +gas's +gas +gases +degases +degas +gasbag +gasbags +gasbag's +gaseous +gash +gashing +gashed +gashes +gash's +gasholder +gasholders +gasket +gaskets +gasket's +gaslight +gaslights +gaslight's +gasman +gasmen +gasohol +gasohol's +gasoline +gasoline's +gasometer +gasometers +gasp +gasping +gasped +gasps +gasp's +gassed +degassed +gasses +gassing +degassing +gassy +gassiest +gassier +gastric +gastritis +gastritis's +gastroenteritis +gastroenteritis's +gastrointestinal +gastronome +gastronomes +gastronomic +gastronomical +gastronomically +gastronomy +gastronomy's +gastropod +gastropods +gastropod's +gasworks +gasworks's +gate +gating +gated +gates +gate's +gateau +gateaux +gatecrash +gatecrashing +gatecrashed +gatecrasher +gatecrashers +gatecrashes +gatecrasher +gatecrasher's +gatehouse +gatehouses +gatehouse's +gatekeeper +gatekeepers +gatekeeper's +gatepost +gateposts +gatepost's +gateway +gateways +gateway's +gather +gathering +gatherings +gathered +gatherer +gatherers +gathers +gather's +gatherer +gatherer's +gathering +gathering's +gator +gators +gator's +gauche +gauchely +gauchest +gaucher +gaucheness +gaucheness +gaucheness's +gaucherie +gaucherie's +gaucho +gauchos +gaucho's +gaudily +gaudiness +gaudiness's +gaudy +gaudiest +gaudier +gaudiness +gauge +gauging +gauged +gauges +gauge's +gaunt +gauntest +gaunter +gauntness +gauntlet +gauntlets +gauntlet's +gauntness +gauntness's +gauze +gauze's +gauziness +gauziness's +gauzy +gauziest +gauzier +gauziness +gave +gavel +gavels +gavel's +gavotte +gavottes +gavotte's +gawd +gawk +gawking +gawked +gawks +gawkily +gawkiness +gawkiness's +gawky +gawkiest +gawkier +gawkiness +gawp +gawping +gawped +gawps +gay +gayest +gayer +gays +gayness +gay's +gayness +gayness's +gaze +gazing +gazed +gazer +gazers +gazes +gaze's +gazebo +gazebos +gazebo's +gazelle +gazelles +gazelle's +gazer +gazer's +gazette +gazetting +gazetted +gazettes +gazette's +gazetteer +gazetteers +gazetteer's +gazillion +gazillions +gazpacho +gazpacho's +gazump +gazumping +gazumped +gazumps +gear +gearing +geared +gears +gear's +gearbox +gearboxes +gearbox's +gearing +gearing's +gearshift +gearshifts +gearshift's +gearwheel +gearwheels +gearwheel's +gecko +geckos +gecko's +geddit +gee +geed +gees +geeing +geek +geeks +geek's +geeky +geekiest +geekier +geese +geezer +geezers +geezer's +geisha +geisha's +gel +gels +gel's +gelatin +gelatin's +gelatinous +gelcap +gelcap's +geld +gelding +geldings +gelded +gelds +gelding +gelding's +gelid +gelignite +gelignite's +gelled +gelling +gem +gems +gem's +gemological +gemologist +gemologists +gemologist's +gemology +gemology's +gemstone +gemstones +gemstone's +gendarme +gendarmes +gendarme's +gender +gendered +genders +gender's +gene +genes +gene's +genealogical +genealogically +genealogist +genealogists +genealogist's +genealogy +genealogies +genealogy's +genera +general +generally +generals +general's +generalissimo +generalissimos +generalissimo's +generalist +generalists +generalist's +generality +generalities +generality's +generalization +generalizations +generalization's +generalize +generalizing +generalized +generalizes +generalship +generalship's +generate +generative +generation +generating +generated +generates +regeneration +degeneration +regenerating +degenerating +regenerated +degenerated +regenerates +degenerates +regenerate +degenerate +generation +generation's +regeneration's +degeneration's +regeneration +degeneration +generational +generations +generator +generators +generator's +generic +generics +generic's +generically +generosity +generosities +generosity's +generous +generously +generousness +generousness +generousness's +genes +geneses +genesis +genesis's +genetic +genetics +genetically +geneticist +geneticists +geneticist's +genetics +genetics's +genial +genially +congenially +congenial +geniality +geniality's +congeniality's +congeniality +geniculate +genie +genies +genie's +genii +genital +genitally +congenitally +congenital +genitalia +genitalia's +genitals +genitals's +genitive +genitives +genitive's +genitourinary +genius +geniuses +genius's +genned +genning +genocidal +genocide +genocides +genocide's +genome +genomes +genome's +genomics +genre +genres +genre's +gent +gents +gent's +regents +regent's +regent +genteel +genteelly +genteelness +genteelness +genteelness's +gentian +gentians +gentian's +gentile +gentiles +gentile's +gentility +gentility's +gentle +gentling +gentled +gentlest +gentler +gentles +gentleness +gentlefolk +gentlefolks +gentlefolk's +gentlefolks +gentlefolks's +gentleman +gentlemanly +gentleman's +gentlemanly +ungentlemanly +gentlemen +gentleness +gentleness's +gentlewoman +gentlewoman's +gentlewomen +gently +gentrification +gentrification's +gentrify +gentrification +gentrifying +gentrified +gentrifies +gentry +gentries +gentry's +genuflect +genuflecting +genuflected +genuflects +genuflection +genuflections +genuflection's +genuine +genuinely +genuineness +genuineness +genuineness's +genus +genus's +geocache +geocaching +geocached +geocaches +geocentric +geocentrically +geochemistry +geochemistry's +geode +geodes +geode's +geodesic +geodesics +geodesic's +geodesy +geodesy's +geodetic +geoengineering +geog +geographer +geographers +geographer's +geographic +geographical +geographically +geography +geographies +geography's +geologic +geological +geologically +geologist +geologists +geologist's +geology +geologies +geology's +geom +geomagnetic +geomagnetism +geomagnetism's +geometer +geometric +geometrical +geometrically +geometry +geometries +geometry's +geophysical +geophysicist +geophysicists +geophysicist's +geophysics +geophysics's +geopolitical +geopolitics +geopolitics's +geostationary +geosynchronous +geosyncline +geosynclines +geosyncline's +geothermal +geothermic +geranium +geraniums +geranium's +gerbil +gerbils +gerbil's +geriatric +geriatrics +geriatrician +geriatricians +geriatrics +geriatrics's +germ +germs +germ's +germane +germanium +germanium's +germicidal +germicide +germicides +germicide's +germinal +germinal's +germinate +germination +germinating +germinated +germinates +germination +germination's +gerontological +gerontologist +gerontologists +gerontologist's +gerontology +gerontology's +gerrymander +gerrymandering +gerrymandered +gerrymanders +gerrymander's +gerrymandering +gerrymandering's +gerund +gerunds +gerund's +gestalt +gestalts +gestapo +gestapos +gestapo's +gestate +gestation +gestating +gestated +gestates +gestation +gestation's +gestational +gesticulate +gesticulation +gesticulations +gesticulating +gesticulated +gesticulates +gesticulation +gesticulation's +gestural +gesture +gesturing +gestured +gestures +gesture's +gesundheit +get +gets +getaway +getaways +getaway's +getting +getup +getup's +gewgaw +gewgaws +gewgaw's +geyser +geysers +geyser's +ghastliness +ghastliness's +ghastly +ghastliest +ghastlier +ghastliness +ghat +ghats +ghat's +ghee +gherkin +gherkins +gherkin's +ghetto +ghettos +ghetto's +ghettoize +ghettoizing +ghettoized +ghettoizes +ghost +ghostly +ghosting +ghosted +ghosts +ghost's +ghostliness +ghostliness's +ghostly +ghostliest +ghostlier +ghostliness +ghostwrite +ghostwriting +ghostwriter +ghostwriters +ghostwrites +ghostwriter +ghostwriter's +ghostwritten +ghostwrote +ghoul +ghouls +ghoul's +ghoulish +ghoulishly +ghoulishness +ghoulishness +ghoulishness's +giant +giants +giant's +giantess +giantesses +giantess's +gibber +gibbering +gibbered +gibbers +gibberish +gibberish's +gibbet +gibbeting +gibbeted +gibbets +gibbet's +gibbon +gibbons +gibbon's +gibbous +gibe +gibing +gibed +gibes +gibe's +giblet +giblets +giblet's +giddily +giddiness +giddiness's +giddy +giddiest +giddier +giddiness +gift +gifting +gifted +gifts +gift's +gig +gigs +gig's +gigabit +gigabits +gigabit's +gigabyte +gigabytes +gigabyte's +gigahertz +gigahertz's +gigantic +gigantically +gigapixel +gigapixels +gigapixel's +gigawatt +gigawatts +gigawatt's +gigged +gigging +giggle +giggling +giggled +giggler +gigglers +giggles +giggle's +giggler +giggler's +giggly +giggliest +gigglier +gigolo +gigolos +gigolo's +gild +gilding +gilded +gilder +gilders +gilds +gild's +gilder +gilder's +gilding +gilding's +gill +gills +gill's +gillie +gillies +gillion +gillions +gilt +gilts +gilt's +gimbals +gimbals's +gimcrack +gimcracks +gimcrack's +gimcrackery +gimcrackery's +gimlet +gimleting +gimleted +gimlets +gimlet's +gimme +gimmes +gimme's +gimmick +gimmicks +gimmick's +gimmickry +gimmickry's +gimmicky +gimp +gimping +gimped +gimps +gimp's +gimpy +gin +gins +gin's +ginger +gingerly +gingering +gingered +gingers +ginger's +gingerbread +gingerbread's +gingersnap +gingersnaps +gingersnap's +gingery +gingham +gingham's +gingivitis +gingivitis's +ginkgo +ginkgo's +ginkgoes +ginned +ginning +ginormous +ginseng +ginseng's +giraffe +giraffes +giraffe's +gird +girding +girded +girder +girders +girds +girder +girder's +girdle +girdling +girdled +girdles +girdle's +girl +girls +girl's +girlfriend +girlfriends +girlfriend's +girlhood +girlhoods +girlhood's +girlish +girlishly +girlishness +girlishness +girlishness's +girly +giro +giros +girt +girting +girted +girts +girt's +girth +girth's +girths +gist +gist's +git +gits +gite +gites +give +giving +givings +giver +givers +gives +giveaway +giveaways +giveaway's +giveback +givebacks +giveback's +given +givens +given's +giver +giver's +gizmo +gizmos +gizmo's +gizzard +gizzards +gizzard's +glace +glaces +glaceed +glaceing +glacial +glacially +glaciate +glaciation +glaciations +glaciating +glaciated +glaciates +glaciation +glaciation's +glacier +glaciers +glacier's +glad +gladly +glads +gladness +glad's +gladden +gladdening +gladdened +gladdens +gladder +gladdest +glade +glades +glade's +gladiator +gladiators +gladiator's +gladiatorial +gladiola +gladiolas +gladiola's +gladioli +gladiolus +gladiolus's +gladness +gladness's +gladsome +glam +glamorization +glamorization's +glamorize +glamorizing +glamorized +glamorizes +glamorous +glamorously +glamour +glamouring +glamoured +glamours +glamour's +glance +glancing +glanced +glances +glance's +gland +glands +gland's +glandes +glandular +glans +glans's +glare +glaring +glared +glares +glare's +glaring +glaringly +glasnost +glasnost's +glass +glassing +glassed +glasses +glass's +glassblower +glassblowers +glassblower's +glassblowing +glassblowing's +glassful +glassfuls +glassful's +glasshouse +glasshouses +glassily +glassiness +glassiness's +glassware +glassware's +glassy +glassiest +glassier +glassiness +glaucoma +glaucoma's +glaze +glazing +glazed +glazes +glaze's +glazier +glaziers +glazier's +glazing +glazing's +gleam +gleaming +gleamings +gleamed +gleams +gleam's +glean +gleaning +gleanings +gleaned +gleaner +gleaners +gleans +gleaner +gleaner's +gleanings +gleanings's +glee +glee's +gleeful +gleefully +gleefulness +gleefulness +gleefulness's +glen +glens +glen's +glenohumeral +glenoid +glib +glibly +glibness +glibber +glibbest +glibness +glibness's +glide +gliding +glided +glider +gliders +glides +glide's +glider +glider's +gliding +gliding's +glimmer +glimmering +glimmerings +glimmered +glimmers +glimmer's +glimmering +glimmering's +glimpse +glimpsing +glimpsed +glimpses +glimpse's +glint +glinting +glinted +glints +glint's +glissandi +glissando +glissando's +glisten +glistening +glistened +glistens +glisten's +glister +glistering +glistered +glisters +glitch +glitching +glitched +glitches +glitch's +glitter +glittering +glittered +glitters +glitter's +glitterati +glittery +glitz +glitz's +glitzy +glitziest +glitzier +gloaming +gloamings +gloaming's +gloat +gloating +gloated +gloats +gloat's +gloating +gloatingly +glob +globing +globed +globs +glob's +global +globally +globalism +globalism's +globalist +globalists +globalist's +globalization +globalization's +globalize +globalizing +globalized +globalizes +globe +globes +globe's +globetrotter +globetrotters +globetrotter's +globetrotting +globular +globule +globules +globule's +globulin +globulin's +glockenspiel +glockenspiels +glockenspiel's +gloom +gloom's +gloomily +gloominess +gloominess's +gloomy +gloomiest +gloomier +gloominess +glop +glop's +gloppy +glorification +glorification's +glorify +glorification +glorifying +glorified +glorifies +glorious +gloriously +ingloriously +inglorious +glory +glorying +gloried +glories +glory's +gloss +glossing +glossed +glosses +gloss's +glossary +glossaries +glossary's +glossily +glossiness +glossiness's +glossolalia +glossolalia's +glossy +glossiest +glossier +glossies +glossiness +glossy's +glottal +glottis +glottises +glottis's +glove +gloving +gloved +gloves +glove's +glow +glowing +glowed +glower +glowers +glows +glow's +glower +glowering +glowered +glower's +glowing +glowingly +glowworm +glowworms +glowworm's +glucagon +glucose +glucose's +glue +gluing +glued +glues +glue's +glued +unglued +gluey +gluier +gluiest +glum +glumly +glumness +glummer +glummest +glumness +glumness's +gluon +gluons +glut +gluten +gluts +glut's +gluten +gluten's +glutenous +glutinous +glutinously +glutted +glutting +glutton +gluttons +glutton's +gluttonous +gluttonously +gluttony +gluttony's +glycerin +glycerin's +glycerol +glycerol's +glycogen +glycogen's +glycol +glyph +gm +gnarl +gnarling +gnarled +gnarls +gnarl's +gnarly +gnarliest +gnarlier +gnash +gnashing +gnashed +gnashes +gnash's +gnat +gnats +gnat's +gnaw +gnawing +gnawed +gnaws +gneiss +gneiss's +gnocchi +gnome +gnomes +gnome's +gnomic +gnomish +gnu +gnus +gnu's +go +goth +going +goings +goer +goers +go's +goad +goading +goaded +goads +goad's +goal +goals +goal's +goalie +goalies +goalie's +goalkeeper +goalkeepers +goalkeeper's +goalkeeping +goalkeeping's +goalless +goalmouth +goalmouths +goalpost +goalposts +goalpost's +goalscorer +goalscorers +goaltender +goaltenders +goaltender's +goat +goats +goat's +goatee +goatees +goatee's +goatherd +goatherds +goatherd's +goatskin +goatskins +goatskin's +gob +gobs +gob's +gobbed +gobbet +gobbets +gobbet's +gobbing +gobble +gobbling +gobbled +gobbler +gobblers +gobbles +gobble's +gobbledygook +gobbledygook's +gobbler +gobbler's +goblet +goblets +goblet's +goblin +goblins +goblin's +gobsmacked +gobstopper +gobstoppers +god +gods +god's +godawful +godchild +godchild's +godchildren +godchildren's +goddammit +goddamn +goddamned +goddaughter +goddaughters +goddaughter's +goddess +goddesses +goddess's +godfather +godfathers +godfather's +godforsaken +godhead +godhead's +godhood +godhood's +godless +godlessly +godlessness +godlessness +godlessness's +godlike +godliness +godliness's +ungodliness's +ungodliness +godly +godliest +godlier +godliness +ungodlier +ungodliness +ungodly +godmother +godmothers +godmother's +godparent +godparents +godparent's +godsend +godsends +godsend's +godson +godsons +godson's +godspeed +goer +goer's +goes +gofer +gofers +gofer's +goggle +goggling +goggled +goggles +goggle's +goggles +goggles's +going +going's +goiter +goiters +goiter's +gold +golden +golds +gold's +goldbrick +goldbricking +goldbricked +goldbricker +goldbrickers +goldbricks +goldbrick's +goldbricker +goldbricker's +golden +goldenest +goldener +goldenrod +goldenrod's +goldfield +goldfields +goldfinch +goldfinches +goldfinch's +goldfish +goldfishes +goldfish's +goldmine +goldmines +goldmine's +goldsmith +goldsmith's +goldsmiths +golf +golfing +golfed +golfer +golfers +golfs +golf's +golfer +golfer's +golliwog +golliwogs +golly +gollies +golly's +gonad +gonads +gonad's +gonadal +gondola +gondolas +gondola's +gondolier +gondoliers +gondolier's +gone +goner +goners +goner +goner's +gong +gonging +gonged +gongs +gong's +gonk +gonks +gonna +gonorrhea +gonorrhea's +gonorrheal +gonzo +goo +goo's +goober +goobers +goober's +good +goodly +goods +goodness +good's +goodbye +goodbyes +goodbye's +goodhearted +goodish +goodly +goodliest +goodlier +goodness +goodness's +goodnight +goods +goods's +goodwill +goodwill's +goody +goodies +goody's +gooey +goof +goofing +goofed +goofs +goof's +goofball +goofballs +goofball's +goofiness +goofiness's +goofy +goofiest +goofier +goofiness +google +googling +googled +googles +google's +googly +googlies +gooier +gooiest +gook +gooks +gook's +goon +goons +goon's +goop +goop's +goose +goosing +goosed +gooses +goose's +gooseberry +gooseberries +gooseberry's +goosebumps +goosebumps's +goosestep +goosesteps +goosestepped +goosestepping +gopher +gophers +gopher's +gore +goring +gored +gores +gore's +gorge's +gorge +gorging +gorged +gorges +disgorging +disgorged +disgorges +disgorge +gorgeous +gorgeously +gorgeousness +gorgeousness +gorgeousness's +gorgon +gorgons +gorgon's +gorilla +gorillas +gorilla's +gorily +goriness +goriness's +gormandize +gormandizing +gormandized +gormandizer +gormandizers +gormandizes +gormandizer +gormandizer's +gormless +gorp +gorps +gorp's +gorse +gorse's +gory +goriest +gorier +goriness +gosh +goshawk +goshawks +goshawk's +gosling +goslings +gosling's +gospel +gospels +gospel's +gossamer +gossamer's +gossip +gossiping +gossiped +gossiper +gossipers +gossips +gossip's +gossiper +gossiper's +gossipy +got +gotcha +gotchas +goths +gotta +gotten +gouache +gouaches +gouge +gouging +gouged +gouger +gougers +gouges +gouge's +gouger +gouger's +goulash +goulashes +goulash's +gourd +gourds +gourd's +gourde +gourdes +gourde's +gourmand +gourmands +gourmand's +gourmet +gourmets +gourmet's +gout +gout's +gouty +goutiest +goutier +gov +govern +governing +governed +governs +governable +government +governable +ungovernable +governance +governance's +governed +ungoverned +governess +governesses +governess's +government +governments +government's +governmental +governor +governors +governor's +governorship +governorship's +govt +gown +gowning +gowned +gowns +gown's +gr +grab +grabs +grab's +grabbed +grabber +grabbers +grabber's +grabbing +grabby +grabbiest +grabbier +grace +gracing +graced +graces +grace's +disgracing +disgraced +disgraces +disgrace's +disgrace +graceful +gracefully +gracefulness +disgracefully +disgracefulness +disgraceful +gracefulness +gracefulness's +disgracefulness's +disgracefulness +graceless +gracelessly +gracelessness +gracelessness +gracelessness's +gracious +graciously +ungraciously +ungracious +graciousness +graciousness's +grackle +grackles +grackle's +grad +grader +graders +grads +grad's +gradable +gradate +gradation +gradations +gradating +gradated +gradates +gradation +gradation's +degradation's +degradation +grade's +grade +grading +graded +grades +regrading +degrading +regraded +degraded +regrades +degrades +regrade +degrade +graded +ungraded +grader +grader's +gradient +gradients +gradient's +gradual +gradually +gradualness +gradualism +gradualism's +gradualness +gradualness's +graduate +graduation +graduations +graduating +graduated +graduates +graduate's +graduation +graduation's +graffiti +graffito +graffito's +graft +grafting +grafted +grafter +grafters +grafts +graft's +grafter +grafter's +graham +grahams +grail +grain +grained +grains +grain's +ingrained +ingrains +ingrain's +ingrain +graininess +graininess's +grainy +grainiest +grainier +graininess +gram +grams +gram's +programs +program's +program +grammar +grammars +grammar's +grammarian +grammarians +grammarian's +grammatical +grammatically +ungrammatically +ungrammatical +gramophone +gramophones +gramophone's +grampus +grampuses +grampus's +gran +grans +granary +granaries +granary's +grand +grandly +grandest +grander +grands +grandness +grand's +grandam +grandams +grandam's +grandaunt +grandaunts +grandaunt's +grandchild +grandchild's +grandchildren +grandchildren's +granddad +granddads +granddad's +granddaddy +granddaddies +granddaddy's +granddaughter +granddaughters +granddaughter's +grandee +grandees +grandee's +grandeur +grandeur's +grandfather +grandfatherly +grandfathering +grandfathered +grandfathers +grandfather's +grandiloquence +grandiloquence's +grandiloquent +grandiose +grandiosely +grandiosity +grandiosity's +grandma +grandmas +grandma's +grandmother +grandmotherly +grandmothers +grandmother's +grandnephew +grandnephews +grandnephew's +grandness +grandness's +grandniece +grandnieces +grandniece's +grandpa +grandpas +grandpa's +grandparent +grandparents +grandparent's +grandson +grandsons +grandson's +grandstand +grandstanding +grandstanded +grandstands +grandstand's +granduncle +granduncles +granduncle's +grange +granges +grange's +granite +granite's +granitic +granny +grannies +granny's +granola +granola's +grant +granting +granted +granter +granters +grants +grant's +grantee +grantees +grantee's +granter +granter's +grantsmanship +grantsmanship's +granular +granularity +granularity's +granulate +granulation +granulating +granulated +granulates +granulation +granulation's +granule +granules +granule's +grape +grapes +grape's +grapefruit +grapefruits +grapefruit's +grapeshot +grapeshot's +grapevine +grapevines +grapevine's +graph +graphing +graphed +graph's +graphic +graphics +graphic's +graphical +graphically +graphite +graphite's +graphologist +graphologists +graphologist's +graphology +graphology's +graphs +grapnel +grapnels +grapnel's +grapple +grappling +grappled +grapples +grapple's +grasp +grasping +grasped +grasps +grasp's +graspable +grass +grassing +grassed +grasses +grass's +grasshopper +grasshoppers +grasshopper's +grassland +grasslands +grassland's +grassroots +grassy +grassiest +grassier +grate +grating +gratings +grated +grater +graters +grates +grate's +grateful +gratefully +gratefulness +ungratefully +ungratefulness +ungrateful +gratefulness +gratefulness's +ungratefulness's +ungratefulness +grater +grater's +gratification +gratification's +gratify +gratification +gratifications +gratifying +gratified +gratifies +gratifying +gratifyingly +gratin +gratins +grating +gratingly +grating's +gratis +gratitude +gratitude's +ingratitude's +ingratitude +gratuitous +gratuitously +gratuitousness +gratuitousness +gratuitousness's +gratuity +gratuities +gratuity's +gravamen +gravamens +gravamen's +grave +gravely +graving +graved +gravest +graver +graves +graveness +grave's +gravedigger +gravediggers +gravedigger's +gravel +gravelly +graveling +graveled +gravels +gravel's +graven +graveness +graveness's +graveside +gravesides +graveside's +gravestone +gravestones +gravestone's +graveyard +graveyards +graveyard's +gravid +gravimeter +gravimeters +gravimeter's +gravitas +gravitate +gravitation +gravitating +gravitated +gravitates +gravitation +gravitation's +gravitational +gravity +gravity's +gravy +gravies +gravy's +gray +graying +grayed +grayest +grayer +grays +grayness +gray's +graybeard +graybeards +graybeard's +grayish +grayness +grayness's +graze +grazing +grazed +grazer +grazers +grazes +graze's +grazer +grazer's +grease +greasing +greased +greaser +greasers +greases +grease's +greasepaint +greasepaint's +greasily +greasiness +greasiness's +greasy +greasiest +greasier +greasiness +great +greatly +greatest +greater +greats +greatness +great's +greatcoat +greatcoats +greatcoat's +greathearted +greatness +greatness's +grebe +grebes +grebe's +greed +greed's +greedily +greediness +greediness's +greedy +greediest +greedier +greediness +green +greenly +greening +greened +greenest +greener +greens +greenness +green's +greenback +greenbacks +greenback's +greenbelt +greenbelts +greenbelt's +greenery +greenery's +greenfield +greenfly +greenflies +greengage +greengages +greengage's +greengrocer +greengrocers +greengrocer's +greenhorn +greenhorns +greenhorn's +greenhouse +greenhouses +greenhouse's +greenish +greenmail +greenmail's +greenness +greenness's +greenroom +greenrooms +greenroom's +greensward +greensward's +greenwood +greenwood's +greet +greeting +greetings +greeted +greeter +greeters +greets +greeter +greeter's +greeting +greeting's +gregarious +gregariously +gregariousness +gregariousness +gregariousness's +gremlin +gremlins +gremlin's +grenade +grenades +grenade's +grenadier +grenadiers +grenadier's +grenadine +grenadine's +grep +greps +grepped +grepping +grew +regrew +greyhound +greyhounds +greyhound's +gribble +gribbles +grid +grids +grid's +griddle +griddles +griddle's +griddlecake +griddlecakes +griddlecake's +gridiron +gridirons +gridiron's +gridlock +gridlocked +gridlocks +gridlock's +grief +griefs +grief's +grievance +grievances +grievance's +grieve +grieving +grieved +griever +grievers +grieves +griever +griever's +grievous +grievously +grievousness +grievousness +grievousness's +griffin +griffins +griffin's +griffon +griffons +griffon's +grill +grilling +grillings +grilled +grills +grill's +grille +grilles +grille's +grim +grimly +griming +grimed +grimness +grimace +grimacing +grimaced +grimaces +grimace's +grime +grimes +grime's +griminess +griminess's +grimmer +grimmest +grimness +grimness's +grimy +grimiest +grimier +griminess +grin +grins +grin's +grind +grinding +grindings +grinder +grinders +grinds +grind's +grinder +grinder's +grindstone +grindstones +grindstone's +gringo +gringos +gringo's +grinned +grinning +grip +griping +griped +griper +gripers +grips +grip's +gripe +gripes +gripe's +griper +griper's +grippe +gripping +gripped +gripper +grippers +grippe's +gripper +gripper's +grisliness +grisliness's +grisly +grisliest +grislier +grisliness +grist +gristly +grist's +gristle +gristle's +gristmill +gristmills +gristmill's +grit +grits +grit's +grits +grits's +gritted +gritter +gritters +gritter's +grittiness +grittiness's +gritting +gritty +grittiest +grittier +grittiness +grizzle +grizzling +grizzled +grizzles +grizzly +grizzliest +grizzlier +grizzlies +grizzly's +groan +groaning +groaned +groans +groan's +groat +groats +groat's +grocer +grocers +grocer's +grocery +groceries +grocery's +grog +grog's +groggily +grogginess +grogginess's +groggy +groggiest +groggier +grogginess +groin +groins +groin's +grok +groks +grokked +grokking +grommet +grommets +grommet's +groom +grooming +groomed +groomer +groomers +grooms +groom's +groomer +groomer's +grooming +grooming's +groomsman +groomsman's +groomsmen +groove +grooving +grooved +grooves +groove's +groovy +grooviest +groovier +grope +groping +groped +groper +gropers +gropes +grope's +groper +groper's +grosbeak +grosbeaks +grosbeak's +grosgrain +grosgrain's +gross +grossly +grossing +grossed +grossest +grosser +grosses +grossness +gross's +grossness +grossness's +grotesque +grotesquely +grotesques +grotesqueness +grotesque's +grotesqueness +grotesqueness's +grotto +grotto's +grottoes +grotty +grottiest +grottier +grouch +grouching +grouched +grouches +grouch's +grouchily +grouchiness +grouchiness's +grouchy +grouchiest +grouchier +grouchiness +ground +grounding +groundings +grounded +grounder +grounders +grounds +ground's +groundbreaking +groundbreakings +groundbreaking's +groundcloth +groundcloths +grounder +grounder's +groundhog +groundhogs +groundhog's +grounding +grounding's +groundless +groundlessly +groundnut +groundnuts +groundnut's +groundsheet +groundsheets +groundskeeper +groundskeepers +groundsman +groundsmen +groundswell +groundswells +groundswell's +groundwater +groundwater's +groundwork +groundwork's +group +grouping +groupings +grouped +grouper +groupers +groups +group's +grouper +grouper's +groupie +groupies +groupie's +grouping +grouping's +groupware +groupware's +grouse +grousing +groused +grouser +grousers +grouses +grouse's +grouser +grouser's +grout +grouting +grouted +grouts +grout's +grove +groves +grove's +grovel +groveling +groveled +groveler +grovelers +grovels +groveler +groveler's +grovelled +grovelling +grow +growth +growing +grows +regrowing +regrows +regrow +grower +growers +grower's +growing +ingrowing +growl +growling +growled +growler +growlers +growls +growl's +growler +growler's +grown +regrown +ingrown +grownup +grownups +grownup's +growth +growth's +regrowth's +regrowth +growths +grub +grubs +grub's +grubbed +grubber +grubbers +grubber's +grubbily +grubbiness +grubbiness's +grubbing +grubby +grubbiest +grubbier +grubbiness +grubstake +grubstake's +grudge +grudging +grudged +grudges +grudge's +grudging +grudgingly +grue +grues +gruel +grueling +gruelings +gruel's +grueling +gruelingly +gruesome +gruesomely +gruesomest +gruesomer +gruesomeness +gruesomeness +gruesomeness's +gruff +gruffly +gruffest +gruffer +gruffness +gruffness +gruffness's +grumble +grumbling +grumblings +grumbled +grumbler +grumblers +grumbles +grumble's +grumbler +grumbler's +grump +grumps +grump's +grumpily +grumpiness +grumpiness's +grumpy +grumpiest +grumpier +grumpiness +grunge +grunges +grunge's +grungy +grungiest +grungier +grunion +grunions +grunion's +grunt +grunting +grunted +grunts +grunt's +gt +guacamole +guacamole's +guanine +guanine's +guano +guano's +guarani +guaranis +guarani's +guarantee +guaranteed +guarantees +guarantee's +guaranteeing +guarantor +guarantors +guarantor's +guaranty +guarantying +guarantied +guaranties +guaranty's +guard +guarding +guarded +guarder +guarders +guards +guard's +guarded +guardedly +guarder +guarder's +guardhouse +guardhouses +guardhouse's +guardian +guardians +guardian's +guardianship +guardianship's +guardrail +guardrails +guardrail's +guardroom +guardrooms +guardroom's +guardsman +guardsman's +guardsmen +guava +guavas +guava's +gubernatorial +guerrilla +guerrillas +guerrilla's +guess +guessing +guessed +guesser +guessers +guesses +guess's +guessable +guesser +guesser's +guesstimate +guesstimating +guesstimated +guesstimates +guesstimate's +guesswork +guesswork's +guest +guesting +guested +guests +guest's +guestbook +guestbooks +guestbook's +guesthouse +guesthouses +guestroom +guestrooms +guff +guff's +guffaw +guffawing +guffawed +guffaws +guffaw's +guidance +guidance's +guide +guiding +guided +guider +guiders +guides +guide's +guidebook +guidebooks +guidebook's +guided +unguided +guideline +guidelines +guideline's +guidepost +guideposts +guidepost's +guider +guider's +guild +guilder +guilders +guilds +guild's +guilder +guilder's +guildhall +guildhalls +guildhall's +guile +guile's +guileful +guileless +guilelessly +guilelessness +guilelessness +guilelessness's +guillemot +guillemots +guillotine +guillotining +guillotined +guillotines +guillotine's +guilt +guilt's +guiltily +guiltiness +guiltiness's +guiltless +guilty +guiltiest +guiltier +guiltiness +guinea +guineas +guinea's +guise +guises +guise's +disguises +disguise's +disguise +guitar +guitars +guitar's +guitarist +guitarists +guitarist's +gulag +gulags +gulag's +gulch +gulches +gulch's +gulden +guldens +gulden's +gulf +gulfs +gulf's +gull +gulling +gulled +gulls +gull's +gullet +gullets +gullet's +gullibility +gullibility's +gullible +gully +gullies +gully's +gulp +gulping +gulped +gulper +gulpers +gulps +gulp's +gulper +gulper's +gum +gums +gum's +gumball +gumballs +gumbo +gumbos +gumbo's +gumboil +gumboils +gumboil's +gumboot +gumboots +gumdrop +gumdrops +gumdrop's +gummed +gumming +gummy +gummiest +gummier +gumption +gumption's +gumshoe +gumshoed +gumshoes +gumshoe's +gumshoeing +gun +guns +gun's +gunboat +gunboats +gunboat's +gunfight +gunfighter +gunfighters +gunfights +gunfight's +gunfighter +gunfighter's +gunfire +gunfire's +gunge +gungy +gunk +gunk's +gunky +gunman +gunman's +gunmen +gunmetal +gunmetal's +gunned +gunnel +gunnels +gunnel's +gunner +gunners +gunner's +gunnery +gunnery's +gunning +gunny +gunny's +gunnysack +gunnysacks +gunnysack's +gunpoint +gunpoint's +gunpowder +gunpowder's +gunrunner +gunrunners +gunrunner's +gunrunning +gunrunning's +gunship +gunships +gunship's +gunshot +gunshots +gunshot's +gunslinger +gunslingers +gunslinger's +gunsmith +gunsmith's +gunsmiths +gunwale +gunwales +gunwale's +guppy +guppies +guppy's +gurgle +gurgling +gurgled +gurgles +gurgle's +gurney +gurneys +gurney's +guru +gurus +guru's +gush +gushing +gushed +gusher +gushers +gushes +gush's +gusher +gusher's +gushing +gushingly +gushy +gushiest +gushier +gusset +gusseting +gusseted +gussets +gusset's +gussy +gussying +gussied +gussies +gust +gusting +gusted +gusts +gust's +disgusting +disgusted +disgusts +disgust's +disgust +gustatory +gustily +gusto +gusto's +gusty +gustiest +gustier +gut +guts +gut's +gutless +gutlessness +gutlessness +gutlessness's +gutsy +gutsiest +gutsier +gutted +gutter +guttering +guttered +gutters +gutter's +guttersnipe +guttersnipes +guttersnipe's +gutting +guttural +gutturals +guttural's +gutty +guttiest +guttier +guv +guvs +guvnor +guvnors +guy +guying +guyed +guys +guy's +guzzle +guzzling +guzzled +guzzler +guzzlers +guzzles +guzzler +guzzler's +gym +gyms +gym's +gymkhana +gymkhanas +gymkhana's +gymnasium +gymnasiums +gymnasium's +gymnast +gymnasts +gymnast's +gymnastic +gymnastics +gymnastically +gymnastics +gymnastics's +gymnosperm +gymnosperms +gymnosperm's +gymslip +gymslips +gynecologic +gynecological +gynecologist +gynecologists +gynecologist's +gynecology +gynecology's +gyp +gyps +gyp's +gypped +gypper +gyppers +gypper's +gypping +gypster +gypsters +gypster's +gypsum +gypsum's +gypsy +gypsies +gypsy's +gyrate +gyration +gyrations +gyrating +gyrated +gyrates +gyration +gyration's +gyrator +gyrators +gyrator's +gyrfalcon +gyrfalcons +gyrfalcon's +gyro +gyros +gyro's +gyroscope +gyroscopes +gyroscope's +gyroscopic +gyve +gyving +gyved +gyves +gyve's +h'm +h +hive +hen +hens +hing +hings +her +hers +hes +ha +hath +has +haberdasher +haberdashers +haberdasher's +haberdashery +haberdasheries +haberdashery's +habiliment +habiliments +habiliment's +habit's +habit +habits +habitable +inhabits +inhabitable +inhabit +habitability +habitability's +habitat +habitats +habitat's +habitation +habitations +habitation's +habitual +habitually +habitualness +habitualness +habitualness's +habituate +habituation +habituating +habituated +habituates +habituation +habituation's +habitue +habitues +habitue's +hacienda +haciendas +hacienda's +hack +hacking +hacked +hacker +hackers +hacks +hack's +hacker +hacker's +hacking +hacking's +hackish +hackle +hackles +hackle's +hackney +hackneying +hackneyed +hackneys +hackney's +hacksaw +hacksaws +hacksaw's +hacktivist +hacktivists +hacktivist's +hackwork +hackwork's +had +haddock +haddocks +haddock's +hadith +hadn't +hadst +hafnium +hafnium's +haft +hafts +haft's +hag +hags +hag's +haggard +haggardly +haggardness +haggardness +haggardness's +haggis +haggises +haggis's +haggish +haggle +haggling +haggled +haggler +hagglers +haggles +haggle's +haggler +haggler's +hagiographer +hagiographers +hagiographer's +hagiography +hagiographies +hagiography's +hahnium +hahnium's +haiku +haiku's +hail +hailing +hailed +hails +hail's +hailstone +hailstones +hailstone's +hailstorm +hailstorms +hailstorm's +hair +haired +hairs +hair's +hairball +hairballs +hairball's +hairband +hairbands +hairbreadth +hairbreadth's +hairbreadths +hairbrush +hairbrushes +hairbrush's +haircloth +haircloth's +haircut +haircuts +haircut's +hairdo +hairdos +hairdo's +hairdresser +hairdressers +hairdresser's +hairdressing +hairdressing's +hairdryer +hairdryers +hairdryer's +hairgrip +hairgrips +hairiness +hairiness's +hairless +hairlike +hairline +hairlines +hairline's +hairnet +hairnets +hairnet's +hairpiece +hairpieces +hairpiece's +hairpin +hairpins +hairpin's +hairsbreadth +hairsbreadth's +hairsbreadths +hairsplitter +hairsplitters +hairsplitter's +hairsplitting +hairsplitting's +hairspray +hairsprays +hairspring +hairsprings +hairspring's +hairstyle +hairstyles +hairstyle's +hairstylist +hairstylists +hairstylist's +hairy +hairiest +hairier +hairiness +haj +hajj +hajj's +hajjes +hajji +hajjis +hajji's +hake +hakes +hake's +halal +halal's +halberd +halberds +halberd's +halcyon +hale +haling +haled +halest +haler +hales +inhaling +inhaled +inhaler +inhales +inhale +half +half's +halfback +halfbacks +halfback's +halfhearted +halfheartedly +halfheartedness +halfheartedness +halfheartedness's +halfpence +halfpenny +halfpennies +halfpenny's +halftime +halftimes +halftime's +halftone +halftones +halftone's +halfway +halfwit +halfwits +halfwit's +halibut +halibuts +halibut's +halite +halite's +halitosis +halitosis's +hall +halls +hall's +hallelujah +hallelujah's +hallelujahs +hallmark +hallmarking +hallmarked +hallmarks +hallmark's +halloo +hallooing +halloos +halloo's +hallow +hallowing +hallowed +hallows +hallowed +unhallowed +hallucinate +hallucination +hallucinations +hallucinating +hallucinated +hallucinates +hallucination +hallucination's +hallucinatory +hallucinogen +hallucinogens +hallucinogen's +hallucinogenic +hallucinogenics +hallucinogenic's +hallway +hallways +hallway's +halo +haloing +haloed +halos +halo's +halogen +halogens +halogen's +halon +halt +halting +halted +halter +halters +halts +halt's +halter +haltering +haltered +halter's +halterneck +halternecks +halting +haltingly +halve +halving +halved +halves +halyard +halyards +halyard's +ham +hams +ham's +hamburg +hamburger +hamburgers +hamburgs +hamburg's +hamburger +hamburger's +hamlet +hamlets +hamlet's +hammed +hammer +hammering +hammerings +hammered +hammerer +hammerers +hammers +hammer's +hammerer +hammerer's +hammerhead +hammerheads +hammerhead's +hammerlock +hammerlocks +hammerlock's +hammertoe +hammertoes +hammertoe's +hamming +hammock +hammocks +hammock's +hammy +hammiest +hammier +hamper +hampering +hampered +hampers +hamper's +hampered +unhampered +hamster +hamsters +hamster's +hamstring +hamstringing +hamstrings +hamstring's +hamstrung +hand's +hand +handing +handed +hands +unhanding +unhanded +unhands +unhand +handbag +handbags +handbag's +handball +handballs +handball's +handbarrow +handbarrows +handbarrow's +handbill +handbills +handbill's +handbook +handbooks +handbook's +handbrake +handbrakes +handcar +handcars +handcar's +handcart +handcarts +handcart's +handclasp +handclasps +handclasp's +handcraft +handcrafting +handcrafted +handcrafts +handcraft's +handcuff +handcuffing +handcuffed +handcuffs +handcuff's +handed +handedness +handful +handfuls +handful's +handgun +handguns +handgun's +handheld +handhelds +handheld's +handhold +handholds +handhold's +handicap +handicaps +handicap's +handicapped +handicapper +handicappers +handicapper's +handicapping +handicraft +handicrafts +handicraft's +handily +handiness +handiness's +handiwork +handiwork's +handkerchief +handkerchiefs +handkerchief's +handle +handling +handled +handler +handlers +handles +handle's +handlebar +handlebars +handlebar's +handler +handler's +handmade +handmaid +handmaiden +handmaidens +handmaids +handmaid's +handmaiden +handmaiden's +handout +handouts +handout's +handover +handovers +handpick +handpicking +handpicked +handpicks +handrail +handrails +handrail's +handsaw +handsaws +handsaw's +handset +handsets +handset's +handshake +handshaking +handshakings +handshakes +handshake's +handsome +handsomely +handsomest +handsomer +handsomeness +handsomeness +handsomeness's +handspring +handsprings +handspring's +handstand +handstands +handstand's +handwork +handwork's +handwoven +handwriting +handwriting's +handwritten +handy +handiest +handier +unhandier +unhandy +handyman +handyman's +handymen +hang +hanging +hangings +hanged +hanger +hangers +hangs +hang's +hangar +hangars +hangar's +hangdog +hanger +hanger's +hanging +hanging's +hangman +hangman's +hangmen +hangnail +hangnails +hangnail's +hangout +hangouts +hangout's +hangover +hangovers +hangover's +hangup +hangups +hangup's +hank +hanker +hankers +hanks +hank's +hanker +hankering +hankerings +hankered +hankering +hankering's +hankie +hankies +hankie's +hansom +hansoms +hansom's +hap +haply +hap's +haphazard +haphazardly +haphazardness +haphazardness +haphazardness's +hapless +haplessly +haplessness +haplessness +haplessness's +haploid +haploids +haploid's +happen +happening +happenings +happened +happens +happening +happening's +happenstance +happenstances +happenstance's +happily +unhappily +happiness +happiness's +unhappiness's +unhappiness +happy +happiest +happier +happiness +unhappier +unhappiness +unhappy +haptic +harangue +haranguing +harangued +harangues +harangue's +harass +harassing +harassed +harasser +harassers +harasses +harassment +harasser +harasser's +harassment +harassment's +harbinger +harbingers +harbinger's +harbor +harboring +harbored +harbors +harbor's +harbormaster +harbormasters +hard +harden +hardens +hardly +hardest +harder +hardness +hardback +hardbacks +hardback's +hardball +hardball's +hardboard +hardboard's +hardbound +hardcore +hardcover +hardcovers +hardcover's +harden +hardening +hardened +hardener +hardeners +hardened +unhardened +hardener +hardener's +hardhat +hardhats +hardhat's +hardheaded +hardheadedly +hardheadedness +hardheadedness +hardheadedness's +hardhearted +hardheartedly +hardheartedness +hardheartedness +hardheartedness's +hardihood +hardihood's +hardily +hardiness +hardiness's +hardliner +hardliners +hardliner's +hardness +hardness's +hardscrabble +hardship +hardships +hardship's +hardstand +hardstands +hardstand's +hardtack +hardtack's +hardtop +hardtops +hardtop's +hardware +hardware's +hardwired +hardwood +hardwoods +hardwood's +hardworking +hardy +hardiest +hardier +hardiness +hare +haring +hared +hares +hare's +harebell +harebells +harebell's +harebrained +harelip +harelips +harelip's +harelipped +harem +harems +harem's +haricot +haricots +hark +harking +harked +harks +harlequin +harlequins +harlequin's +harlot +harlots +harlot's +harlotry +harlotry's +harm +harming +harmed +harms +harm's +harmed +unharmed +harmful +harmfully +harmfulness +harmfulness +harmfulness's +harmless +harmlessly +harmlessness +harmlessness +harmlessness's +harmonic +harmonics +harmonic's +harmonica +harmonicas +harmonica's +harmonically +harmonies +harmonious +harmoniously +harmoniousness +harmoniousness +harmoniousness's +harmonium +harmoniums +harmonium's +harmonization +harmonization's +harmonize +harmonizing +harmonized +harmonizer +harmonizers +harmonizes +harmonizer +harmonizer's +harmony +harmony's +disharmony's +disharmony +harness's +harness +harnessing +harnessed +harnesses +unharnessing +unharnessed +unharnesses +unharness +harp +harping +harped +harps +harp's +harpist +harpists +harpist's +harpoon +harpooning +harpooned +harpooner +harpooners +harpoons +harpoon's +harpooner +harpooner's +harpsichord +harpsichords +harpsichord's +harpsichordist +harpsichordists +harpsichordist's +harpy +harpies +harpy's +harridan +harridans +harridan's +harrier +harrier's +harrow +harrowing +harrowed +harrows +harrow's +harrumph +harrumphing +harrumphed +harrumphs +harry +harrying +harried +harrier +harriers +harries +harsh +harshly +harshest +harsher +harshness +harshness +harshness's +hart +harts +hart's +harvest +harvesting +harvested +harvester +harvesters +harvests +harvest's +harvested +unharvested +harvester +harvester's +hash +hashing +hashed +hashes +hash's +rehashing +rehashed +rehashes +rehash's +rehash +hashish +hashish's +hashtag +hashtags +hashtag's +hasn't +hasp +hasps +hasp's +hassle +hassling +hassled +hassles +hassle's +hassock +hassocks +hassock's +hast +hasten +hastens +hasting +hasted +haste +hastes +haste's +hasten +hastening +hastened +hastily +hastiness +hastiness's +hasty +hastiest +hastier +hastiness +hat +hating +hated +hater +haters +hats +hat's +hatband +hatbands +hatbox +hatboxes +hatbox's +hatch +hatching +hatched +hatches +hatch's +hatchback +hatchbacks +hatchback's +hatcheck +hatchecks +hatcheck's +hatched +unhatched +hatchery +hatcheries +hatchery's +hatchet +hatchets +hatchet's +hatching +hatching's +hatchway +hatchways +hatchway's +hate +hates +hate's +hateful +hatefully +hatefulness +hatefulness +hatefulness's +hatemonger +hatemongers +hatemonger's +hater +hater's +hatpin +hatpins +hatred +hatreds +hatred's +hatstand +hatstands +hatted +hatter +hatters +hatter's +hatting +hauberk +hauberks +hauberk's +haughtily +haughtiness +haughtiness's +haughty +haughtiest +haughtier +haughtiness +haul +hauling +hauled +hauler +haulers +hauls +haul's +haulage +haulage's +hauler +hauler's +haulier +hauliers +haunch +haunches +haunch's +haunt +haunting +haunted +haunter +haunters +haunts +haunt's +haunter +haunter's +haunting +hauntingly +hauteur +hauteur's +have +having +haves +have's +haven't +haven +havens +haven's +haversack +haversacks +haversack's +havoc +havoc's +haw +hawing +hawed +haws +haw's +hawk +hawking +hawked +hawker +hawkers +hawks +hawk's +hawker +hawker's +hawkish +hawkishness +hawkishness +hawkishness's +hawser +hawsers +hawser's +hawthorn +hawthorns +hawthorn's +hay +haying +hayed +hays +hay's +haycock +haycocks +haycock's +hayloft +haylofts +hayloft's +haymaker +haymakers +haymaking +haymow +haymows +haymow's +hayrick +hayricks +hayrick's +hayride +hayrides +hayride's +hayseed +hayseeds +hayseed's +haystack +haystacks +haystack's +haywire +hazard +hazarding +hazarded +hazards +hazard's +hazardous +hazardously +haze +hazing +hazings +hazed +hazer +hazers +hazes +haze's +hazel +hazels +hazel's +hazelnut +hazelnuts +hazelnut's +hazer +hazer's +hazily +haziness +haziness's +hazing +hazing's +hazmat +hazy +haziest +hazier +haziness +hdqrs +he'd +he'll +he +he's +head +heading +headings +headed +header +headers +heads +head's +headache +headaches +headache's +headband +headbands +headband's +headbanger +headbangers +headbanging +headboard +headboards +headboard's +headbutt +headbutting +headbutted +headbutts +headcase +headcases +headcheese +headcount +headcounts +headdress +headdresses +headdress's +header +header's +headfirst +headgear +headgear's +headhunt +headhunting +headhunted +headhunter +headhunters +headhunts +headhunter +headhunter's +headhunting +headhunting's +headily +headiness +headiness's +heading +heading's +headlamp +headlamps +headlamp's +headland +headlands +headland's +headless +headlight +headlights +headlight's +headline +headlining +headlined +headliner +headliners +headlines +headline's +headliner +headliner's +headlock +headlocks +headlock's +headlong +headman +headman's +headmaster +headmasters +headmaster's +headmen +headmistress +headmistresses +headmistress's +headphone +headphones +headphone's +headpiece +headpieces +headpiece's +headpin +headpins +headpin's +headquarter +headquartering +headquartered +headquarters +headquarters +headquarters's +headrest +headrests +headrest's +headroom +headroom's +headscarf +headscarves +headset +headsets +headset's +headship +headships +headship's +headshrinker +headshrinkers +headshrinker's +headsman +headsman's +headsmen +headstall +headstalls +headstall's +headstand +headstands +headstand's +headstone +headstones +headstone's +headstrong +headteacher +headteachers +headwaiter +headwaiters +headwaiter's +headwaters +headwaters's +headway +headway's +headwind +headwinds +headwind's +headword +headwords +headword's +heady +headiest +headier +headiness +heal +health +healing +healed +healer +healers +heals +healed +unhealed +healer +healer's +health +health's +healthcare +healthful +healthfully +healthfulness +healthfulness +healthfulness's +healthily +unhealthily +healthiness +healthiness's +unhealthiness's +unhealthiness +healthy +healthiest +healthier +healthiness +unhealthier +unhealthiness +unhealthy +heap +heaping +heaped +heaps +heap's +hear +hearth +hearing +hearings +hears +rehearing +rehearings +rehears +rehear +heard +reheard +unheard +hearer +hearers +hearer's +hearing +hearing's +rehearing's +rehearing +hearken +hearkening +hearkened +hearkens +hearsay +hearsay's +hearse's +hearse +hearses +rehearses +rehearse +heart +hearts +heart's +heartache +heartaches +heartache's +heartbeat +heartbeats +heartbeat's +heartbreak +heartbreaking +heartbreaks +heartbreak's +heartbroken +heartburn +heartburn's +hearten +heartening +heartened +heartens +disheartening +disheartened +disheartens +dishearten +heartfelt +hearth +hearth's +hearthrug +hearthrugs +hearths +hearthstone +hearthstones +hearthstone's +heartily +heartiness +heartiness's +heartland +heartlands +heartland's +heartless +heartlessly +heartlessness +heartlessness +heartlessness's +heartrending +heartrendingly +heartsick +heartsickness +heartsickness +heartsickness's +heartstrings +heartstrings's +heartthrob +heartthrobs +heartthrob's +heartwarming +heartwood +heartwood's +hearty +heartiest +heartier +hearties +heartiness +hearty's +heat's +heat +heating +heated +heats +reheating +reheated +reheats +reheat +heated +unheated +heatedly +heater +heaters +heater's +heath +heathen +heathens +heather +heath's +heathen +heathen's +heathendom +heathendom's +heathenish +heathenism +heathenism's +heather +heather's +heaths +heating +heating's +heatproof +heatstroke +heatstroke's +heatwave +heatwaves +heave +heaving +heaved +heaver +heavers +heaves +heave's +heaven +heavenly +heavens +heaven's +heavenly +heavenliest +heavenlier +heavens +heavens's +heavenward +heavenwards +heaver +heaver's +heavily +heaviness +heaviness's +heavy +heaviest +heavier +heavies +heaviness +heavy's +heavyhearted +heavyset +heavyweight +heavyweights +heavyweight's +heck +heck's +heckle +heckling +heckled +heckler +hecklers +heckles +heckle's +heckler +heckler's +heckling +heckling's +hectare +hectares +hectare's +hectic +hectically +hectogram +hectograms +hectogram's +hectometer +hectometers +hectometer's +hector +hectoring +hectored +hectors +hector's +hedge +hedging +hedged +hedger +hedgers +hedges +hedge's +hedgehog +hedgehogs +hedgehog's +hedgehop +hedgehops +hedgehopped +hedgehopping +hedger +hedger's +hedgerow +hedgerows +hedgerow's +hedonism +hedonism's +hedonist +hedonists +hedonist's +hedonistic +heed +heeding +heeded +heeds +heed's +heeded +unheeded +heedful +heedfully +heedless +heedlessly +heedlessness +heedlessness +heedlessness's +heehaw +heehawing +heehawed +heehaws +heehaw's +heel +heeling +heeled +heels +heel's +heelless +heft +hefting +hefted +hefts +heft's +heftily +heftiness +heftiness's +hefty +heftiest +heftier +heftiness +hegemonic +hegemony +hegemony's +hegira +hegiras +hegira's +heifer +heifers +heifer's +height +heighten +heightens +heights +height's +heighten +heightening +heightened +heinous +heinously +heinousness +heinousness +heinousness's +heir +heirs +heir's +heiress +heiresses +heiress's +heirloom +heirlooms +heirloom's +heist +heisting +heisted +heists +heist's +held +helical +helices +helicopter +helicoptering +helicoptered +helicopters +helicopter's +heliocentric +heliotrope +heliotropes +heliotrope's +helipad +helipads +heliport +heliports +heliport's +helium +helium's +helix +helix's +hell +hell's +hellbent +hellcat +hellcats +hellcat's +hellebore +hellebore's +hellfire +hellhole +hellholes +hellhole's +hellion +hellions +hellion's +hellish +hellishly +hellishness +hellishness +hellishness's +hello +hellos +hello's +helluva +helm +helms +helm's +helmet +helmeted +helmets +helmet's +helmsman +helmsman's +helmsmen +helot +helots +helot's +help +helping +helpings +helped +helper +helpers +helps +help's +helper +helper's +helpful +helpfully +unhelpfully +unhelpful +helpfulness +helpfulness's +helping +helping's +helpless +helplessly +helplessness +helplessness +helplessness's +helpline +helplines +helpline's +helpmate +helpmates +helpmate's +helve +helves +helve's +hem +hems +hem's +hematite +hematite's +hematologic +hematological +hematologist +hematologists +hematologist's +hematology +hematology's +heme +heme's +hemiplegia +hemisphere +hemispheres +hemisphere's +hemispheric +hemispherical +hemline +hemlines +hemline's +hemlock +hemlocks +hemlock's +hemmed +hemmer +hemmers +hemmer's +hemming +hemoglobin +hemoglobin's +hemophilia +hemophilia's +hemophiliac +hemophiliacs +hemophiliac's +hemorrhage +hemorrhaging +hemorrhaged +hemorrhages +hemorrhage's +hemorrhagic +hemorrhoid +hemorrhoids +hemorrhoid's +hemostat +hemostats +hemostat's +hemp +hempen +hemp's +hemstitch +hemstitching +hemstitched +hemstitches +hemstitch's +hen +hen's +hence +henceforth +henceforward +henchman +henchman's +henchmen +henna +hennaing +hennaed +hennas +henna's +henpeck +henpecking +henpecked +henpecks +hep +heparin +heparin's +hepatic +hepatitis +hepatitis's +hepatocyte +hepatocytes +hepper +heppest +heptagon +heptagons +heptagon's +heptagonal +heptathlon +heptathlons +heptathlon's +herald +heralding +heralded +heralds +herald's +heralded +unheralded +heraldic +heraldry +heraldry's +herb +herbs +herb's +herbaceous +herbage +herbage's +herbal +herbals +herbalist +herbalists +herbalist's +herbicidal +herbicide +herbicides +herbicide's +herbivore +herbivores +herbivore's +herbivorous +herculean +herd +herding +herded +herder +herders +herds +herd's +herder +herder's +herdsman +herdsman's +herdsmen +here +here's +hereabout +hereabouts +hereafter +hereafters +hereafter's +hereby +hereditary +heredity +heredity's +herein +hereinafter +hereof +hereon +heresy +heresies +heresy's +heretic +heretics +heretic's +heretical +hereto +heretofore +hereunder +hereunto +hereupon +herewith +heritable +inheritable +heritage +heritages +heritage's +hermaphrodite +hermaphrodites +hermaphrodite's +hermaphroditic +hermetic +hermetical +hermetically +hermit +hermits +hermit's +hermitage +hermitages +hermitage's +hermitian +hernia +hernias +hernia's +hernial +herniate +herniation +herniating +herniated +herniates +herniation +herniation's +hero +hero's +heroes +heroic +heroics +heroically +heroics +heroics's +heroin +heroins +heroin's +heroine +heroines +heroine's +heroism +heroism's +heron +herons +heron's +herpes +herpes's +herpetologist +herpetologists +herpetologist's +herpetology +herpetology's +herring +herrings +herring's +herringbone +herringbone's +herself +hertz +hertz's +hesitance +hesitance's +hesitancy +hesitancy's +hesitant +hesitantly +hesitate +hesitation +hesitations +hesitating +hesitated +hesitates +hesitating +hesitatingly +unhesitatingly +unhesitating +hesitation +hesitation's +hessian +hetero +heteros +hetero's +heterodox +heterodoxy +heterodoxy's +heterogeneity +heterogeneity's +heterogeneous +heterogeneously +heterosexual +heterosexually +heterosexuals +heterosexual's +heterosexuality +heterosexuality's +heuristic +heuristics +heuristic's +heuristically +heuristics +heuristics's +hew +hewing +hewed +hewer +hewers +hews +hewer +hewer's +hex +hexing +hexed +hexes +hex's +hexadecimal +hexadecimals +hexagon +hexagons +hexagon's +hexagonal +hexagram +hexagrams +hexagram's +hexameter +hexameters +hexameter's +hey +heyday +heydays +heyday's +hf +hgt +hgwy +hi +hied +his +hiatus +hiatuses +hiatus's +hibachi +hibachis +hibachi's +hibernate +hibernation +hibernating +hibernated +hibernates +hibernation +hibernation's +hibernator +hibernators +hibernator's +hibiscus +hibiscuses +hibiscus's +hiccough +hiccoughing +hiccoughed +hiccoughs +hiccup +hiccuping +hiccuped +hiccups +hiccup's +hick +hicks +hick's +hickey +hickeys +hickey's +hickory +hickories +hickory's +hid +hidden +hide +hiding +hidings +hided +hider +hiders +hides +hide's +hideaway +hideaways +hideaway's +hidebound +hideous +hideously +hideousness +hideousness +hideousness's +hideout +hideouts +hideout's +hider +hider's +hiding +hiding's +hie +hies +hieing +hierarchic +hierarchical +hierarchically +hierarchy +hierarchies +hierarchy's +hieroglyph +hieroglyph's +hieroglyphic +hieroglyphics +hieroglyphic's +hieroglyphs +high +highly +highest +higher +highers +highness +high's +highball +highballs +highball's +highborn +highboy +highboys +highboy's +highbrow +highbrows +highbrow's +highchair +highchairs +highchair's +highfalutin +highhanded +highhandedly +highhandedness +highhandedness +highhandedness's +highland +highlander +highlanders +highlands +highland's +highlander +highlander's +highlight +highlighting +highlighted +highlighter +highlighters +highlights +highlight's +highlighter +highlighter's +highness +highness's +highroad +highroads +highroad's +highs +hightail +hightailing +hightailed +hightails +highway +highways +highway's +highwayman +highwayman's +highwaymen +hijab +hijabs +hijab's +hijack +hijacking +hijackings +hijacked +hijacker +hijackers +hijacks +hijack's +hijacker +hijacker's +hijacking +hijacking's +hike +hiking +hiked +hiker +hikers +hikes +hike's +hiker +hiker's +hiking +hiking's +hilarious +hilariously +hilariousness +hilariousness +hilariousness's +hilarity +hilarity's +hill +hills +hill's +hillbilly +hillbillies +hillbilly's +hilliness +hilliness's +hillock +hillocks +hillock's +hillside +hillsides +hillside's +hilltop +hilltops +hilltop's +hilly +hilliest +hillier +hilliness +hilt +hilts +hilt's +him +hims +himself +hind +hinder +hinders +hinds +hind's +hinder +hindering +hindered +hindered +unhindered +hindmost +hindquarter +hindquarters +hindquarter's +hindrance +hindrances +hindrance's +hindsight +hindsight's +hinge's +hinge +hinging +hinged +hinges +unhinging +unhinged +unhinges +unhinge +hint +hinting +hinted +hinter +hinters +hints +hint's +hinter +hinter's +hinterland +hinterlands +hinterland's +hip +hips +hipness +hip's +hipbath +hipbaths +hipbone +hipbones +hipbone's +hiphuggers +hipness +hipness's +hipped +hipper +hippest +hippie +hippies +hippie's +hipping +hippo +hippos +hippo's +hippocampus +hippodrome +hippodromes +hippodrome's +hippopotamus +hippopotamuses +hippopotamus's +hippy +hipster +hipsters +hipster's +hiragana +hire's +hire +hiring +hired +hires +rehiring +rehired +rehires +rehire +hireling +hirelings +hireling's +hirsute +hirsuteness +hirsuteness +hirsuteness's +hiss +hissing +hissed +hisses +hiss's +hist +histamine +histamines +histamine's +histogram +histograms +histogram's +histologist +histologists +histologist's +histology +histology's +histopathology +historian +historians +historian's +historic +historical +historically +historicity +historicity's +historiographer +historiographers +historiographer's +historiography +historiography's +history +histories +history's +histrionic +histrionics +histrionically +histrionics +histrionics's +hit +hits +hit's +hitch's +hitch +hitching +hitched +hitches +unhitching +unhitched +unhitches +unhitch +hitcher +hitchers +hitcher's +hitchhike +hitchhiking +hitchhiked +hitchhiker +hitchhikers +hitchhikes +hitchhike's +hitchhiker +hitchhiker's +hither +hitherto +hitter +hitters +hitter's +hitting +hive +hiving +hived +hives +hive's +hiya +hmm +ho +holy +hoed +hoer +hoers +hos +ho's +hoagie +hoagies +hoagie's +hoard +hoarding +hoardings +hoarded +hoarder +hoarders +hoards +hoard's +hoarder +hoarder's +hoarding +hoarding's +hoarfrost +hoarfrost's +hoariness +hoariness's +hoarse +hoarsely +hoarsest +hoarser +hoarseness +hoarseness +hoarseness's +hoary +hoariest +hoarier +hoariness +hoax +hoaxing +hoaxed +hoaxer +hoaxers +hoaxes +hoax's +hoaxer +hoaxer's +hob +hobs +hob's +hobbit +hobbits +hobble +hobbling +hobbled +hobbler +hobblers +hobbles +hobble's +hobbler +hobbler's +hobby +hobbies +hobby's +hobbyhorse +hobbyhorses +hobbyhorse's +hobbyist +hobbyists +hobbyist's +hobgoblin +hobgoblins +hobgoblin's +hobnail +hobnailing +hobnailed +hobnails +hobnail's +hobnob +hobnobs +hobnobbed +hobnobbing +hobo +hobos +hobo's +hock +hocking +hocked +hocks +hock's +hockey +hockey's +hockshop +hockshops +hockshop's +hod +hods +hod's +hodgepodge +hodgepodges +hodgepodge's +hoe +hoes +hoe's +hoecake +hoecakes +hoecake's +hoedown +hoedowns +hoedown's +hoeing +hoer +hoer's +hog +hogs +hog's +hogan +hogans +hogan's +hogback +hogbacks +hogback's +hogged +hogging +hoggish +hoggishly +hogshead +hogsheads +hogshead's +hogtie +hogtied +hogties +hogtying +hogwash +hogwash's +hoick +hoicking +hoicked +hoicks +hoist +hoisting +hoisted +hoists +hoist's +hoke +hoking +hoked +hokes +hokey +hokier +hokiest +hokum +hokum's +hold +holding +holdings +holder +holders +holds +hold's +holdall +holdalls +holder +holder's +holding +holding's +holdout +holdouts +holdout's +holdover +holdovers +holdover's +holdup +holdups +holdup's +hole +holing +holed +holes +hole's +holey +holiday +holidaying +holidayed +holidays +holiday's +holidaymaker +holidaymakers +holiness +holiness's +unholiness's +unholiness +holism +holistic +holistically +holler +hollering +hollered +hollers +holler's +hollow +hollowly +hollowing +hollowed +hollowest +hollower +hollows +hollowness +hollow's +hollowness +hollowness's +holly +hollies +holly's +hollyhock +hollyhocks +hollyhock's +holmium +holmium's +holocaust +holocausts +holocaust's +hologram +holograms +hologram's +holograph +holograph's +holographic +holographs +holography +holography's +hols +holster +holstering +holstered +holsters +holster's +holy +holiest +holier +holiness +unholier +unholiness +unholy +homage +homages +homage's +hombre +hombres +hombre's +homburg +homburgs +homburg's +home +homely +homing +homed +homer +homers +homes +home's +homebody +homebodies +homebody's +homeboy +homeboys +homeboy's +homecoming +homecomings +homecoming's +homegrown +homeland +homelands +homeland's +homeless +homelessness +homeless's +homelessness +homelessness's +homelike +homeliness +homeliness's +homely +homeliest +homelier +homeliness +homemade +homemaker +homemakers +homemaker's +homemaking +homemaking's +homeopath +homeopath's +homeopathic +homeopaths +homeopathy +homeopathy's +homeostasis +homeostasis's +homeostatic +homeowner +homeowners +homeowner's +homepage +homepages +homepage's +homer +homering +homered +homer's +homeroom +homerooms +homeroom's +homeschooling +homeschooling's +homesick +homesickness +homesickness +homesickness's +homespun +homespun's +homestead +homesteading +homesteaded +homesteader +homesteaders +homesteads +homestead's +homesteader +homesteader's +homestretch +homestretches +homestretch's +hometown +hometowns +hometown's +homeward +homewards +homework +homeworking +homeworker +homeworkers +homework's +homewrecker +homewreckers +homewrecker's +homey +homeys +homeyness +homey's +homeyness +homeyness's +homicidal +homicide +homicides +homicide's +homier +homiest +homiletic +homily +homilies +homily's +hominid +hominids +hominid's +hominoid +hominoids +hominy +hominy's +homo +homos +homo's +homoerotic +homogeneity +homogeneity's +homogeneous +homogeneously +homogenization +homogenization's +homogenize +homogenizing +homogenized +homogenizes +homograph +homograph's +homographs +homologous +homology +homonym +homonyms +homonym's +homophobia +homophobia's +homophobic +homophone +homophones +homophone's +homosexual +homosexuals +homosexual's +homosexuality +homosexuality's +hon +honing +honed +honest +honer +honers +hons +hon's +honcho +honchos +honcho's +hone +hones +hone's +honer +honer's +honest +honestly +honestest +dishonestly +dishonest +honester +honesty +honesty's +dishonesty's +dishonesty +honey +honeying +honeyed +honeys +honey's +honeybee +honeybees +honeybee's +honeycomb +honeycombing +honeycombed +honeycombs +honeycomb's +honeydew +honeydews +honeydew's +honeylocust +honeylocust's +honeymoon +honeymooning +honeymooned +honeymooner +honeymooners +honeymoons +honeymoon's +honeymooner +honeymooner's +honeypot +honeypots +honeysuckle +honeysuckles +honeysuckle's +honk +honking +honked +honker +honkers +honks +honk's +honker +honker's +honky +honkies +honky's +honor +honoring +honored +honors +honor's +honorable +dishonoring +dishonored +dishonors +dishonor's +dishonorable +dishonor +honorableness +honorableness's +honorably +dishonorably +honorarily +honorarium +honorariums +honorarium's +honorary +honoree +honorees +honoree's +honorer +honorers +honorer's +honorific +honorifics +honorific's +hooch +hooch's +hood +hooding +hooded +hoods +hood's +hoodie +hoodies +hoodie's +hoodlum +hoodlums +hoodlum's +hoodoo +hoodooing +hoodooed +hoodoos +hoodoo's +hoodwink +hoodwinking +hoodwinked +hoodwinks +hooey +hooey's +hoof +hoofing +hoofed +hoofer +hoofers +hoofs +hoof's +hook's +hook +hooking +hooked +hooks +unhooking +unhooked +unhooks +unhook +hookah +hookah's +hookahs +hooker +hookers +hooker's +hookup +hookups +hookup's +hookworm +hookworms +hookworm's +hooky +hooky's +hooligan +hooligans +hooligan's +hooliganism +hooliganism's +hoop +hooping +hooped +hoops +hoop's +hoopla +hoopla's +hooray +hoosegow +hoosegows +hoosegow's +hoot +hooting +hooted +hooter +hooters +hoots +hoot's +hootenanny +hootenannies +hootenanny's +hooter +hooter's +hoover +hoovering +hoovered +hoovers +hooves +hop +hoping +hoped +hops +hop's +hope +hopes +hope's +hopeful +hopefully +hopefuls +hopefulness +hopeful's +hopefulness +hopefulness's +hopeless +hopelessly +hopelessness +hopelessness +hopelessness's +hopped +hopper +hoppers +hopper's +hopping +hopscotch +hopscotching +hopscotched +hopscotches +hopscotch's +hora +horas +hora's +horde +hording +horded +hordes +horde's +horehound +horehounds +horehound's +horizon +horizons +horizon's +horizontal +horizontally +horizontals +horizontal's +hormonal +hormone +hormones +hormone's +horn +horned +horns +horn's +hornbeam +hornblende +hornblende's +hornet +hornets +hornet's +hornless +hornlike +hornpipe +hornpipes +hornpipe's +horny +horniest +hornier +horologic +horological +horologist +horologists +horologist's +horology +horology's +horoscope +horoscopes +horoscope's +horrendous +horrendously +horrible +horribleness +horribleness +horribleness's +horribly +horrid +horridly +horrific +horrifically +horrify +horrifying +horrified +horrifies +horrifying +horrifyingly +horror +horrors +horror's +horse's +horse +horsing +horsed +horses +unhorsing +unhorsed +unhorses +unhorse +horseback +horseback's +horsebox +horseboxes +horseflesh +horseflesh's +horsefly +horseflies +horsefly's +horsehair +horsehair's +horsehide +horsehide's +horselaugh +horselaugh's +horselaughs +horseless +horseman +horseman's +horsemanship +horsemanship's +horsemen +horseplay +horseplay's +horsepower +horsepower's +horseradish +horseradishes +horseradish's +horseshit +horseshoe +horseshoed +horseshoes +horseshoe's +horseshoeing +horsetail +horsetails +horsetail's +horsetrading +horsewhip +horsewhips +horsewhip's +horsewhipped +horsewhipping +horsewoman +horsewoman's +horsewomen +horsey +horsier +horsiest +hortatory +horticultural +horticulturalist +horticulturalists +horticulture +horticulture's +horticulturist +horticulturists +horticulturist's +hosanna +hosannas +hosanna's +hose +hosing +hosed +hoses +hose's +hosepipe +hosepipes +hosier +hosiers +hosier's +hosiery +hosiery's +hosp +hospholipase +hospice +hospices +hospice's +hospitable +inhospitable +hospitably +inhospitably +hospital +hospitals +hospital's +hospitality +hospitality's +hospitalization +hospitalizations +hospitalization's +hospitalize +hospitalizing +hospitalized +hospitalizes +host +hosting +hosted +hosts +host's +hostage +hostages +hostage's +hostel +hosteling +hosteled +hosteler +hostelers +hostels +hostel's +hosteler +hosteler's +hostelry +hostelries +hostelry's +hostess +hostessing +hostessed +hostesses +hostess's +hostile +hostilely +hostiles +hostile's +hostilities +hostilities's +hostility +hostilities +hostility's +hostler +hostlers +hostler's +hot +hotly +hots +hotness +hotbed +hotbeds +hotbed's +hotblooded +hotbox +hotboxes +hotbox's +hotcake +hotcakes +hotcake's +hotel +hotels +hotel's +hotelier +hoteliers +hotelier's +hotfoot +hotfooting +hotfooted +hotfoots +hotfoot's +hothead +hotheaded +hotheads +hothead's +hotheaded +hotheadedly +hotheadedness +hotheadedness +hotheadedness's +hothouse +hothouses +hothouse's +hotkey +hotkeys +hotlink +hotlinks +hotness +hotness's +hotplate +hotplates +hotplate's +hotpot +hotpots +hots +hots's +hotshot +hotshots +hotshot's +hotted +hotter +hottest +hottie +hotties +hotting +hound +hounding +hounded +hounds +hound's +hour +hourly +hours +hour's +hourglass +hourglasses +hourglass's +houri +houris +houri's +house's +house +housing +housed +houses +rehousing +rehoused +rehouses +rehouse +houseboat +houseboats +houseboat's +housebound +houseboy +houseboys +houseboy's +housebreak +housebreaking +housebreaker +housebreakers +housebreaks +housebreaker +housebreaker's +housebreaking +housebreaking's +housebroke +housebroken +houseclean +housecleaning +housecleaned +housecleans +housecleaning +housecleaning's +housecoat +housecoats +housecoat's +housefly +houseflies +housefly's +houseful +housefuls +houseful's +household +householder +householders +households +household's +householder +householder's +househusband +househusbands +househusband's +housekeeper +housekeepers +housekeeper's +housekeeping +housekeeping's +houselights +houselights's +housemaid +housemaids +housemaid's +houseman +houseman's +housemaster +housemasters +housemate +housemates +housemen +housemistress +housemistresses +housemother +housemothers +housemother's +houseparent +houseparents +houseparent's +houseplant +houseplants +houseplant's +houseproud +houseroom +housetop +housetops +housetop's +housewares +housewares's +housewarming +housewarmings +housewarming's +housewife +housewifely +housewife's +housewives +housework +housework's +housing +housings +housing's +hove +hovel +hovels +hovel's +hover +hovering +hovered +hovers +hovercraft +hovercraft's +how'd +how're +how +hows +how's +howbeit +howdah +howdah's +howdahs +howdy +however +howitzer +howitzers +howitzer's +howl +howling +howled +howler +howlers +howls +howl's +howler +howler's +howsoever +hoyden +hoydens +hoyden's +hoydenish +hp +hr +hrs +ht +huarache +huaraches +huarache's +hub +hubs +hub's +hubbub +hubbubs +hubbub's +hubby +hubbies +hubby's +hubcap +hubcaps +hubcap's +hubris +hubris's +huckleberry +huckleberries +huckleberry's +huckster +huckstering +huckstered +hucksters +huckster's +hucksterism +hucksterism's +huddle +huddling +huddled +huddles +huddle's +hue +hued +hues +hue's +huff +huffing +huffed +huffs +huff's +huffily +huffiness +huffiness's +huffy +huffiest +huffier +huffiness +hug +hugest +huger +hugs +hug's +huge +hugely +hugeness +hugeness +hugeness's +hugged +hugging +huh +hula +hulas +hula's +hulk +hulking +hulks +hulk's +hull +hulling +hulled +huller +hullers +hulls +hull's +hullabaloo +hullabaloos +hullabaloo's +huller +huller's +hum +hums +hum's +human +humanly +humanest +humaner +humans +humanness +human's +humane +humanely +humaneness +humaneness +humaneness's +humanism +humanism's +humanist +humanists +humanist's +humanistic +humanitarian +humanitarians +humanitarian's +humanitarianism +humanitarianism's +humanities +humanities's +humanity +humanities +humanity's +inhumanities +inhumanity's +inhumanity +humanization +humanization's +dehumanization's +dehumanization +humanize +humanizing +humanized +humanizes +dehumanizing +dehumanized +dehumanizes +dehumanize +humanizer +humanizers +humanizer's +humankind +humankind's +humanness +humanness's +humanoid +humanoids +humanoid's +humble +humbling +humblings +humbled +humblest +humbler +humblers +humbles +humbleness +humbleness +humbleness's +humbler +humbler's +humbly +humbug +humbugs +humbug's +humbugged +humbugging +humdinger +humdingers +humdinger's +humdrum +humdrum's +humeral +humeri +humerus +humerus's +humid +humidly +humidification +humidification's +humidifier +humidifier's +dehumidifier's +dehumidifier +humidify +humidifying +humidified +humidifier +humidifiers +humidifies +dehumidifying +dehumidified +dehumidifier +dehumidifiers +dehumidifies +dehumidify +humidity +humidity's +humidor +humidors +humidor's +humiliate +humiliation +humiliations +humiliating +humiliated +humiliates +humiliating +humiliatingly +humiliation +humiliation's +humility +humility's +hummed +hummer +hummers +hummer's +humming +hummingbird +hummingbirds +hummingbird's +hummock +hummocks +hummock's +hummocky +hummus +hummus's +humongous +humor +humoring +humored +humors +humor's +humoresque +humorist +humorists +humorist's +humorless +humorlessly +humorlessness +humorlessness +humorlessness's +humorous +humorously +humorousness +humorousness +humorousness's +hump +humping +humped +humps +hump's +humpback +humpbacked +humpbacks +humpback's +humph +humphing +humphed +humphs +humus +humus's +hunch +hunching +hunched +hunches +hunch's +hunchback +hunchbacked +hunchbacks +hunchback's +hundred +hundredth +hundreds +hundred's +hundredfold +hundredth +hundredth's +hundredths +hundredweight +hundredweights +hundredweight's +hung +hunger +hungering +hungered +hungers +hunger's +hungover +hungrily +hungriness +hungriness's +hungry +hungriest +hungrier +hungriness +hunk +hunker +hunkers +hunks +hunk's +hunker +hunkering +hunkered +hunky +hunkiest +hunkier +hunt +hunting +hunted +hunter +hunters +hunts +hunt's +hunter +hunter's +hunting +hunting's +huntress +huntresses +huntress's +huntsman +huntsman's +huntsmen +hurdle +hurdling +hurdled +hurdler +hurdlers +hurdles +hurdle's +hurdler +hurdler's +hurdling +hurdling's +hurl +hurling +hurled +hurler +hurlers +hurls +hurl's +hurler +hurler's +hurling +hurling's +hurrah +hurrahing +hurrahed +hurrah's +hurrahs +hurricane +hurricanes +hurricane's +hurried +hurriedly +unhurriedly +unhurried +hurry +hurrying +hurried +hurries +hurry's +hurt +hurting +hurts +hurt's +hurtful +hurtfully +hurtfulness +hurtfulness +hurtfulness's +hurtle +hurtling +hurtled +hurtles +husband +husbanding +husbanded +husbands +husband's +husbandman +husbandman's +husbandmen +husbandry +husbandry's +hush +hushing +hushed +hushes +hush's +husk +husking +husked +husker +huskers +husks +husk's +husker +husker's +huskily +huskiness +huskiness's +husky +huskiest +huskier +huskies +huskiness +husky's +hussar +hussars +hussar's +hussy +hussies +hussy's +hustings +hustings's +hustle +hustling +hustled +hustler +hustlers +hustles +hustle's +hustler +hustler's +hut +huts +hut's +hutch +hutches +hutch's +huzzah +huzzahing +huzzahed +huzzah's +huzzahs +hwy +hyacinth +hyacinth's +hyacinths +hybrid +hybrids +hybrid's +hybridism +hybridism's +hybridization +hybridization's +hybridize +hybridizing +hybridized +hybridizes +hydra +hydras +hydra's +hydrangea +hydrangeas +hydrangea's +hydrant +hydrants +hydrant's +hydrate's +hydrate +hydration +hydrating +hydrated +hydrates +dehydration +dehydrating +dehydrated +dehydrates +dehydrate +hydration +hydration's +dehydration's +dehydration +hydraulic +hydraulics +hydraulically +hydraulics +hydraulics's +hydro +hydro's +hydrocarbon +hydrocarbons +hydrocarbon's +hydrocephalus +hydrocephalus's +hydrodynamic +hydrodynamics +hydrodynamics +hydrodynamics's +hydroelectric +hydroelectrically +hydroelectricity +hydroelectricity's +hydrofoil +hydrofoils +hydrofoil's +hydrogen +hydrogen's +hydrogenate +hydrogenating +hydrogenated +hydrogenates +dehydrogenating +dehydrogenated +dehydrogenates +dehydrogenate +hydrogenation +hydrogenation's +hydrogenous +hydrologist +hydrologists +hydrologist's +hydrology +hydrology's +hydrolyses +hydrolysis +hydrolysis's +hydrolyze +hydrolyzing +hydrolyzed +hydrolyzes +hydrometer +hydrometers +hydrometer's +hydrometry +hydrometry's +hydrophilic +hydrophobia +hydrophobia's +hydrophobic +hydrophone +hydrophones +hydrophone's +hydroplane +hydroplaning +hydroplaned +hydroplanes +hydroplane's +hydroponic +hydroponics +hydroponically +hydroponics +hydroponics's +hydrosphere +hydrosphere's +hydrotherapy +hydrotherapy's +hydrous +hydroxide +hydroxides +hydroxide's +hyena +hyenas +hyena's +hygiene +hygiene's +hygienic +unhygienic +hygienically +hygienist +hygienists +hygienist's +hygrometer +hygrometers +hygrometer's +hying +hymen +hymens +hymen's +hymeneal +hymn +hymning +hymned +hymns +hymn's +hymnal +hymnals +hymnal's +hymnbook +hymnbooks +hymnbook's +hype +hyping +hyped +hyper +hypes +hype's +hyperactive +hyperactivity +hyperactivity's +hyperbola +hyperbolas +hyperbola's +hyperbole +hyperbole's +hyperbolic +hypercritical +hypercritically +hypercube +hyperglycemia +hyperglycemia's +hyperinflation +hyperlink +hyperlinking +hyperlinked +hyperlinks +hyperlink's +hypermarket +hypermarkets +hypermedia +hypermedia's +hyperparathyroidism +hyperplane +hypersensitive +hypersensitiveness +hypersensitiveness +hypersensitiveness's +hypersensitivity +hypersensitivities +hypersensitivity's +hyperspace +hyperspaces +hypertension +hypertension's +hypertensive +hypertensives +hypertensive's +hypertext +hypertext's +hyperthyroid +hyperthyroid's +hyperthyroidism +hyperthyroidism's +hypertrophy +hypertrophying +hypertrophied +hypertrophies +hypertrophy's +hyperventilate +hyperventilation +hyperventilating +hyperventilated +hyperventilates +hyperventilation +hyperventilation's +hypervisor +hypervisors +hypervisor's +hyphen +hyphening +hyphened +hyphens +hyphen's +hyphenate +hyphenation +hyphenations +hyphenating +hyphenated +hyphenates +hyphenate's +hyphenation +hyphenation's +hypnoses +hypnosis +hypnosis's +hypnotherapist +hypnotherapists +hypnotherapy +hypnotherapy's +hypnotic +hypnotics +hypnotic's +hypnotically +hypnotism +hypnotism's +hypnotist +hypnotists +hypnotist's +hypnotize +hypnotizing +hypnotized +hypnotizes +hypo +hypos +hypo's +hypoallergenic +hypochondria +hypochondria's +hypochondriac +hypochondriacs +hypochondriac's +hypocrisy +hypocrisies +hypocrisy's +hypocrite +hypocrites +hypocrite's +hypocritical +hypocritically +hypodermic +hypodermics +hypodermic's +hypoglycemia +hypoglycemia's +hypoglycemic +hypoglycemics +hypoglycemic's +hypotenuse +hypotenuses +hypotenuse's +hypothalami +hypothalamus +hypothalamus's +hypothermia +hypothermia's +hypotheses +hypothesis +hypothesis's +hypothesize +hypothesizing +hypothesized +hypothesizes +hypothetical +hypothetically +hypothyroid +hypothyroid's +hypothyroidism +hypothyroidism's +hyssop +hyssop's +hysterectomy +hysterectomies +hysterectomy's +hysteresis +hysteria +hysteria's +hysteric +hysterics +hysteric's +hysterical +hysterically +hysterics +hysterics's +i +is +unis +uni +iOS +iOS's +iPad +iPad's +iPhone +iPhone's +iPod +iPod's +iTunes +iTunes's +iamb +iambs +iamb's +iambi +iambic +iambics +iambic's +iambus +iambuses +iambus's +ibex +ibexes +ibex's +ibid +ibidem +ibis +ibises +ibis's +ibuprofen +ibuprofen's +ice's +ice +icing +iced +ices +deicing +deiced +deices +deice +iceberg +icebergs +iceberg's +iceboat +iceboats +iceboat's +icebound +icebox +iceboxes +icebox's +icebreaker +icebreakers +icebreaker's +icecap +icecaps +icecap's +iceman +iceman's +icemen +ichthyologist +ichthyologists +ichthyologist's +ichthyology +ichthyology's +icicle +icicles +icicle's +icily +iciness +iciness's +icing +icings +icing's +icky +ickiest +ickier +icon +icons +icon's +iconic +iconoclasm +iconoclasm's +iconoclast +iconoclasts +iconoclast's +iconoclastic +iconography +iconography's +ictus +ictus's +icy +iciest +icier +iciness +id +idly +ids +id's +idea +ideas +idea's +ideal +ideally +ideals +ideal's +idealism +idealism's +idealist +idealists +idealist's +idealistic +idealistically +idealization +idealizations +idealization's +idealize +idealizing +idealized +idealizes +idem +idempotent +identical +identically +identifiable +unidentifiable +identification +identification's +identified +unidentified +identify +identification +identifications +identifying +identified +identifier +identifiers +identifies +identikit +identikits +identity +identities +identity's +ideogram +ideograms +ideogram's +ideograph +ideograph's +ideographs +ideological +ideologically +ideologist +ideologists +ideologist's +ideologue +ideologues +ideologue's +ideology +ideologies +ideology's +ides +ides's +idiocy +idiocies +idiocy's +idiom +idioms +idiom's +idiomatic +unidiomatic +idiomatically +idiopathic +idiosyncrasy +idiosyncrasies +idiosyncrasy's +idiosyncratic +idiosyncratically +idiot +idiots +idiot's +idiotic +idiotically +idle +idling +idled +idlest +idler +idlers +idles +idleness +idle's +idleness +idleness's +idler +idler's +idol +idols +idol's +idolater +idolaters +idolater's +idolatress +idolatresses +idolatress's +idolatrous +idolatry +idolatry's +idolization +idolization's +idolize +idolizing +idolized +idolizes +idyll +idylls +idyll's +idyllic +idyllically +if +ifs +if's +iffiness +iffiness's +iffy +iffiest +iffier +iffiness +igloo +igloos +igloo's +igneous +ignitable +ignite +igniting +ignited +ignites +reigniting +reignited +reignites +reignite +ignition +ignitions +ignition's +ignoble +ignobly +ignominious +ignominiously +ignominy +ignominies +ignominy's +ignoramus +ignoramuses +ignoramus's +ignorance +ignorance's +ignorant +ignorantly +ignore +ignoring +ignored +ignores +iguana +iguanas +iguana's +ii +iii +ilea +ileitis +ileitis's +ileum +ileum's +ilia +ilium +ilium's +ilk +ilks +ilk's +ill +ills +illness +ill's +illegal +illegally +illegals +illegal's +illegality +illegalities +illegality's +illegibility +illegibility's +illegible +illegibly +illegitimacy +illegitimacy's +illegitimate +illegitimately +illiberal +illiberally +illiberality +illiberality's +illicit +illicitly +illicitness +illicitness +illicitness's +illimitable +illiteracy +illiteracy's +illiterate +illiterately +illiterates +illiterate's +illness +illnesses +illness's +illogical +illogically +illogicality +illogicality's +illuminate +illumination +illuminations +illuminating +illuminated +illuminates +illuminating +illuminatingly +illumination +illumination's +illumine +illumining +illumined +illumines +illuminable +illus +illusive +illusion +illusions +illusion's +disillusions +disillusion's +disillusion +illusionist +illusionists +illusionist's +illusory +illustrate +illustrative +illustration +illustrations +illustrating +illustrated +illustrates +illustration +illustration's +illustrative +illustratively +illustrator +illustrators +illustrator's +illustrious +illustriously +illustriousness +illustriousness +illustriousness's +image +imaging +imaged +images +image's +imagery +imagery's +imaginable +unimaginable +imaginably +unimaginably +imaginal +imaginary +imagination +imaginations +imagination's +imaginative +imaginatively +unimaginatively +unimaginative +imagine +imagining +imaginings +imagined +imagines +imaginable +imago +imago's +imagoes +imam +imams +imam's +imbalance +imbalanced +imbalances +imbalance's +imbecile +imbeciles +imbecile's +imbecilic +imbecility +imbecilities +imbecility's +imbibe +imbibing +imbibed +imbiber +imbibers +imbibes +imbiber +imbiber's +imbrication +imbrication's +imbroglio +imbroglios +imbroglio's +imbue +imbuing +imbued +imbues +imitable +inimitable +imitate +imitative +imitation +imitations +imitating +imitated +imitates +imitation +imitation's +imitative +imitatively +imitativeness +imitativeness +imitativeness's +imitator +imitators +imitator's +immaculate +immaculately +immaculateness +immaculateness +immaculateness's +immanence +immanence's +immanency +immanency's +immanent +immanently +immaterial +immaterially +immaterialness +immateriality +immateriality's +immaterialness +immaterialness's +immature +immaturely +immaturity +immaturity's +immeasurable +immeasurably +immediacies +immediacies's +immediacy +immediacies +immediacy's +immediate +immediately +immediateness +immediateness +immediateness's +immemorial +immemorially +immense +immensely +immensity +immensities +immensity's +immerse +immersive +immersion +immersions +immersing +immersed +immerses +immersible +immersion +immersion's +immigrant +immigrants +immigrant's +immigrate +immigration +immigrating +immigrated +immigrates +immigration +immigration's +imminence +imminence's +imminent +imminently +immobile +immobility +immobility's +immobilization +immobilization's +immobilize +immobilizing +immobilized +immobilizer +immobilizers +immobilizes +immoderate +immoderately +immodest +immodestly +immodesty +immodesty's +immolate +immolation +immolating +immolated +immolates +immolation +immolation's +immoral +immorally +immorality +immoralities +immorality's +immortal +immortally +immortals +immortal's +immortality +immortality's +immortalize +immortalizing +immortalized +immortalizes +immovability +immovability's +immovable +immovably +immune +immunity +immunity's +immunization +immunizations +immunization's +immunize +immunizing +immunized +immunizes +immunodeficiency +immunodeficiency's +immunodeficient +immunoglobulin +immunoglobulins +immunologic +immunological +immunologist +immunologists +immunologist's +immunology +immunology's +immure +immuring +immured +immures +immutability +immutability's +immutable +immutably +imp +imper +imps +imp's +impact +impacting +impacted +impacts +impact's +impair +impairing +impaired +impairs +impairment +impaired +unimpaired +impairment +impairments +impairment's +impala +impalas +impala's +impale +impaling +impaled +impales +impalement +impalement +impalement's +impalpable +impalpably +impanel +impaneling +impaneled +impanels +impart +imparting +imparted +imparts +impartial +impartially +impartiality +impartiality's +impassably +impasse +impassive +impasses +impasse's +impassable +impassibility +impassibility's +impassible +impassibly +impassioned +impassive +impassively +impassiveness +impassiveness +impassiveness's +impassivity +impassivity's +impasto +impasto's +impatience +impatiences +impatience's +impatiens +impatiens's +impatient +impatiently +impeach +impeaching +impeached +impeacher +impeachers +impeaches +impeachable +impeachment +impeachable +unimpeachable +impeacher +impeacher's +impeachment +impeachments +impeachment's +impeccability +impeccability's +impeccable +impeccably +impecunious +impecuniously +impecuniousness +impecuniousness +impecuniousness's +impedance +impedance's +impede +impeding +impeded +impedes +impeded +unimpeded +impediment +impediments +impediment's +impedimenta +impedimenta's +impel +impels +impelled +impeller +impellers +impeller's +impelling +impend +impending +impended +impends +impenetrability +impenetrability's +impenetrable +impenetrably +impenitence +impenitence's +impenitent +impenitently +imperative +imperatively +imperatives +imperative's +imperceptibility +imperceptibility's +imperceptible +imperceptibly +imperceptive +imperf +imperfect +imperfectly +imperfects +imperfectness +imperfect's +imperfection +imperfections +imperfection's +imperfectness +imperfectness's +imperial +imperially +imperials +imperial's +imperialism +imperialism's +imperialist +imperialists +imperialist's +imperialistic +imperialistically +imperil +imperiling +imperiled +imperils +imperilment +imperilment +imperilment's +imperious +imperiously +imperiousness +imperiousness +imperiousness's +imperishable +imperishably +impermanence +impermanence's +impermanent +impermanently +impermeability +impermeability's +impermeable +impermeably +impermissible +impersonal +impersonally +impersonate +impersonation +impersonations +impersonating +impersonated +impersonates +impersonation +impersonation's +impersonator +impersonators +impersonator's +impertinence +impertinences +impertinence's +impertinent +impertinently +imperturbability +imperturbability's +imperturbable +imperturbably +impervious +imperviously +impetigo +impetigo's +impetuosity +impetuosity's +impetuous +impetuously +impetuousness +impetuousness +impetuousness's +impetus +impetuses +impetus's +impiety +impieties +impiety's +impinge +impinging +impinged +impinges +impingement +impingement +impingement's +impious +impiously +impiousness +impiousness +impiousness's +impish +impishly +impishness +impishness +impishness's +implacability +implacability's +implacable +implacably +implant +implanting +implanted +implants +implant's +implantable +implantation +implantation's +implausibility +implausibilities +implausibility's +implausible +implausibly +implement +implementing +implemented +implementer +implements +implement's +implementable +implementable +unimplementable +implementation +implementations +implementation's +implemented +unimplemented +implicate +implicating +implicated +implicates +implication +implication's +implicit +implicitly +implicitness +implicitness +implicitness's +implode +imploding +imploded +implodes +implore +imploring +implored +implores +imploring +imploringly +implosion +implosions +implosion's +implosive +imply +implication +implications +implying +implied +implies +impolite +impolitely +impoliteness +impoliteness +impolitenesses +impoliteness's +impolitic +imponderable +imponderables +imponderable's +import +importing +imported +importer +importers +imports +import's +importable +importance +importance's +important +importantly +importation +importations +importation's +importer +importer's +importunate +importunately +importune +importuning +importuned +importunes +importunity +importunity's +impose +imposing +imposed +imposes +reimposing +reimposed +reimposes +reimpose +imposer +imposers +imposer's +imposing +unimposing +imposingly +imposition +impositions +imposition's +impossibility +impossibilities +impossibility's +impossible +impossibles +impossibly +impost +imposts +impost's +impostor +impostors +impostor's +imposture +impostures +imposture's +impotence +impotence's +impotency +impotency's +impotent +impotently +impound +impounding +impounded +impounds +impoverish +impoverishing +impoverished +impoverishes +impoverishment +impoverishment +impoverishment's +impracticability +impracticable +impracticably +impractical +impractically +impracticality +impracticality's +imprecate +imprecation +imprecations +imprecating +imprecated +imprecates +imprecation +imprecation's +imprecise +imprecision +imprecisely +impreciseness +impreciseness +impreciseness's +imprecision +imprecision's +impregnability +impregnability's +impregnable +impregnably +impregnate +impregnation +impregnating +impregnated +impregnates +impregnation +impregnation's +impresario +impresarios +impresario's +impress +impressive +impressing +impressed +impresses +impress's +impressed +unimpressed +impressibility +impressibility's +impressible +impression +impressions +impression's +impressionable +impressionability +impressionability's +impressionism +impressionism's +impressionist +impressionists +impressionist's +impressionistic +impressive +impressively +impressiveness +impressiveness +impressiveness's +imprimatur +imprimaturs +imprimatur's +imprint +imprinting +imprinted +imprinter +imprinters +imprints +imprint's +imprinter +imprinter's +imprison +imprisoning +imprisoned +imprisons +imprisonment +imprisonment +imprisonments +imprisonment's +improbability +improbabilities +improbability's +improbable +improbably +impromptu +impromptus +impromptu's +improper +improperly +impropriety +improprieties +impropriety's +improve +improving +improved +improves +improvable +improvement +improved +unimproved +improvement +improvements +improvement's +improvidence +improvidence's +improvident +improvidently +improvisation +improvisations +improvisation's +improvisational +improvise +improvising +improvised +improviser +improvisers +improvises +improviser +improviser's +imprudence +imprudence's +imprudent +imprudently +impudence +impudence's +impudent +impudently +impugn +impugning +impugned +impugner +impugners +impugns +impugner +impugner's +impulse +impulsive +impulsion +impulsing +impulsed +impulses +impulse's +impulsion +impulsion's +impulsive +impulsively +impulsiveness +impulsiveness +impulsiveness's +impulsivity +impunity +impunity's +impure +impurely +impurest +impurer +impurity +impurities +impurity's +imputation +imputations +imputation's +impute +imputing +imputed +imputes +imputable +in +ins +in's +reins +rein's +rein +inaccuracy +inaccuracies +inaction +inaction's +inadequacy +inadequacies +inadvertence +inadvertence's +inadvertent +inadvertently +inalienability +inalienability's +inalienably +inamorata +inamoratas +inamorata's +inane +inanely +inanest +inaner +inanimate +inanimately +inanimateness +inanimateness +inanimateness's +inanity +inanities +inanity's +inappropriate +inappropriately +inarticulate +inarticulately +inasmuch +inaudible +inaugural +inaugurals +inaugural's +inaugurate +inauguration +inaugurations +inaugurating +inaugurated +inaugurates +inauguration +inauguration's +inboard +inboards +inboard's +inbound +inbox +inboxes +inbox's +inbreed +inbreeds +inc +incing +inced +incest +incalculably +incandescence +incandescence's +incandescent +incandescently +incantation +incantations +incantation's +incapacitate +incapacitation +incapacitating +incapacitated +incapacitates +incarcerate +incarceration +incarcerations +incarcerating +incarcerated +incarcerates +incarceration +incarceration's +incarnadine +incarnadining +incarnadined +incarnadines +incarnate +incarnation +incarnations +incarnating +incarnated +incarnates +reincarnation +reincarnations +reincarnating +reincarnated +reincarnates +reincarnate +incarnation +incarnation's +reincarnation's +reincarnation +incendiary +incendiaries +incendiary's +incense +incensing +incensed +incenses +incense's +incentive's +incentive +incentives +disincentives +disincentive +inception +inceptions +inception's +incessant +incessantly +incest +incest's +incestuous +incestuously +incestuousness +incestuousness +incestuousness's +inch +inching +inched +inches +inch's +inchoate +inchworm +inchworms +inchworm's +incidence +incidences +incidence's +incident +incidents +incident's +incidental +incidentally +incidentals +incidental's +incinerate +incineration +incinerating +incinerated +incinerates +incineration +incineration's +incinerator +incinerators +incinerator's +incipience +incipience's +incipient +incipiently +incise +incisive +incision +incisions +incising +incised +incises +incision +incision's +incisive +incisively +incisiveness +incisiveness +incisiveness's +incisor +incisors +incisor's +incitement +incitements +incitement's +inciter +inciters +inciter's +incl +inclement +inclination +inclination's +disinclination's +disinclination +inclinations +incline's +incline +inclining +inclined +inclines +disinclining +disinclined +disinclines +disincline +include +including +included +includes +inclusion +inclusions +inclusion's +inclusive +inclusively +inclusiveness +inclusiveness +inclusiveness's +incognito +incognitos +incognito's +incombustible +incommode +incommoding +incommoded +incommodious +incommunicado +incompatibility +incompatibilities +incompetent +incompetents +incompetent's +incomplete +incompletely +inconceivability +inconceivability's +incongruous +incongruously +incongruousness +incongruousness +incongruousness's +inconsolably +inconstant +inconstantly +incontestability +incontestability's +incontestably +incontinent +incontrovertibly +inconvenience +inconveniencing +inconvenienced +incorporate +incorporation +incorporating +incorporated +incorporates +reincorporation +reincorporating +reincorporated +reincorporates +reincorporate +incorporated +unincorporated +incorporation +incorporation's +reincorporation's +reincorporation +incorporeal +incorrect +incorrectly +incorrigibility +incorrigibility's +incorrigible +incorrigibly +incorruptibly +increasing +increasingly +increment +incremented +increments +increment's +incremental +incrementally +incrementalism +incrementalist +incrementalists +incrementalist's +incriminate +incrimination +incriminating +incriminated +incriminates +incrimination +incrimination's +incriminatory +incrustation +incrustations +incrustation's +incubate +incubation +incubating +incubated +incubates +incubation +incubation's +incubator +incubators +incubator's +incubus +incubuses +incubus's +inculcate +inculcation +inculcating +inculcated +inculcates +inculcation +inculcation's +inculpate +inculpating +inculpated +inculpates +incumbency +incumbencies +incumbency's +incumbent +incumbents +incumbent's +incunabula +incunabulum +incunabulum's +incur +incurs +incurable +incurable +incurables +incurable's +incurably +incurious +incurred +incurring +incursion +incursions +incursion's +ind +indebted +indebtedness +indebtedness +indebtedness's +indeed +indefatigable +indefatigably +indefeasible +indefeasibly +indefinably +indelible +indelibly +indemnification +indemnification's +indemnify +indemnification +indemnifications +indemnifying +indemnified +indemnifies +indemnity +indemnities +indemnity's +indentation +indentations +indentation's +indention +indention's +indenture +indenturing +indentured +indescribably +indestructibly +indeterminably +indeterminacy +indeterminacy's +indeterminate +indeterminately +index +indexing +indexed +indexer +indexers +indexes +index's +indexation +indexations +indexation's +indexer +indexer's +indicate +indicative +indication +indications +indicating +indicated +indicates +indication +indication's +indicative +indicatively +indicatives +indicative's +indicator +indicators +indicator's +indict +indicting +indicted +indicts +indictable +indictment +indictment +indictments +indictment's +indie +indies +indigence +indigence's +indigenous +indigent +indigently +indigents +indigent's +indignant +indignantly +indignation +indignation's +indigo +indigo's +indirect +indirectly +indiscipline +indiscreet +indiscreetly +indiscretion +indiscretions +indiscriminate +indiscriminately +indispensability +indispensability's +indispensable +indispensables +indispensable's +indispensably +indissolubility +indissolubly +indistinguishably +indite +inditing +indited +indites +indium +indium's +individual +individually +individuals +individual's +individualism +individualism's +individualist +individualists +individualist's +individualistic +individualistically +individuality +individuality's +individualization +individualization's +individualize +individualizing +individualized +individualizes +individuate +individuation +individuating +individuated +individuates +individuation +individuation's +indivisibly +indoctrinate +indoctrination +indoctrinating +indoctrinated +indoctrinates +indoctrination +indoctrination's +indolence +indolence's +indolent +indolently +indomitable +indomitably +indubitable +indubitably +induce +inducing +induced +inducer +inducers +induces +inducement +inducement +inducements +inducement's +inducer +inducer's +induct +inductive +inducting +inducted +inductance +inductance's +inductee +inductees +inductee's +induction +inductions +induction's +inductive +inductively +indulge +indulging +indulged +indulges +indulgence +indulgences +indulgence's +indulgent +indulgently +industrial +industrially +industrialism +industrialism's +industrialist +industrialists +industrialist's +industrialization +industrialization's +industrialize +industrializing +industrialized +industrializes +industrious +industriously +industriousness +industriousness +industriousness's +industry +industries +industry's +indwell +indwelling +indwells +inebriate +inebriation +inebriating +inebriated +inebriates +inebriate's +inebriation +inebriation's +inedible +ineffability +ineffability's +ineffable +ineffably +inelastic +ineligible +ineligibles +ineligible's +ineligibly +ineluctable +ineluctably +inept +ineptly +ineptness +ineptitude +ineptitude's +ineptness +ineptness's +inequality +inequalities +inert +inertly +inertness +inertia +inertia's +inertial +inertness +inertness's +inescapable +inescapably +inestimably +inevitability +inevitability's +inevitable +inevitable's +inevitably +inexact +inexactly +inexhaustibly +inexorability +inexorable +inexorably +inexpedient +inexpert +inexpertly +inexpiable +inexplicably +inexpressibly +inexpressive +inextricably +inf +infest +infers +infallible +infamy +infamies +infamy's +infancy +infancy's +infant +infants +infant's +infanticide +infanticides +infanticide's +infantile +infantry +infantries +infantry's +infantryman +infantryman's +infantrymen +infarct +infarcts +infarct's +infarction +infarction's +infatuate +infatuation +infatuations +infatuating +infatuated +infatuates +infatuation +infatuation's +infect +infecting +infected +infects +reinfecting +disinfecting +reinfected +disinfected +reinfects +disinfects +reinfect +disinfect +infected +uninfected +infection +infections +infection's +reinfections +reinfection's +reinfection +infectious +infectiously +infectiousness +infectiousness +infectiousness's +infelicitous +inference +inferences +inference's +inferential +inferior +inferiors +inferior's +inferiority +inferiority's +infernal +infernally +inferno +infernos +inferno's +inferred +inferring +infest +infesting +infested +infests +infestation +infestations +infestation's +infidel +infidels +infidel's +infidelity +infidelities +infiltrator +infiltrators +infiltrator's +infinite +infinitive +infinite's +infinitesimal +infinitesimally +infinitesimals +infinitesimal's +infinitival +infinitive +infinitives +infinitive's +infinitude +infinitude's +infinity +infinities +infinity's +infirm +infirmary +infirmaries +infirmary's +infirmity +infirmities +infirmity's +infix +inflame +inflaming +inflamed +inflames +inflammable +inflammation +inflammations +inflammation's +inflammatory +inflatable +inflatables +inflatable's +inflate +inflating +inflated +inflates +reinflating +reinflated +reinflates +reinflate +inflation +inflation's +disinflation's +disinflation +inflationary +inflect +inflecting +inflected +inflects +inflection +inflections +inflection's +inflectional +inflict +inflictive +inflicting +inflicted +inflicts +infliction +infliction's +inflow +inflows +inflow's +influence +influencing +influenced +influences +influence's +influenced +uninfluenced +influential +influentially +influenza +influenza's +info +info's +infomercial +infomercials +infomercial's +inform +informers +informal +informally +informant +informants +informant's +informatics +information +information's +disinformation's +disinformation +informational +informative +informatively +informativeness +informativeness +informativeness's +informed +uninformed +infotainment +infotainment's +infra +infrared +infrared's +infrasonic +infrastructural +infrastructure +infrastructures +infrastructure's +infrequence +infrequence's +infrequent +infrequently +infringement +infringements +infringement's +infuriate +infuriating +infuriated +infuriates +infuriating +infuriatingly +infuser +infusers +infuser's +ingenious +ingeniously +ingeniousness +ingeniousness +ingeniousness's +ingenue +ingenues +ingenue's +ingenuity +ingenuity's +ingenuous +ingenuously +disingenuously +disingenuous +ingenuousness +ingenuousness's +ingest +ingesting +ingested +ingests +ingestion +ingestion's +inglenook +inglenooks +inglenook's +ingot +ingots +ingot's +ingrain +ingraining +ingrate +ingrates +ingrate's +ingratiate +ingratiation +ingratiating +ingratiated +ingratiates +ingratiating +ingratiatingly +ingratiation +ingratiation's +ingredient +ingredients +ingredient's +ingress +ingresses +ingress's +inguinal +inhabit +inhabiting +inhabited +inhabitable +uninhabitable +inhabitant +inhabitants +inhabitant's +inhabited +uninhabited +inhalant +inhalants +inhalant's +inhalation +inhalations +inhalation's +inhalator +inhalators +inhalator's +inhaler +inhalers +inhaler's +inharmonious +inhere +inhering +inhered +inheres +inherent +inherently +inherit +inheriting +inherited +inherits +disinheriting +disinherited +disinherits +disinherit +inheritance +inheritance's +disinheritance's +disinheritance +inheritances +inheritor +inheritors +inheritor's +inhibit +inhibiting +inhibited +inhibits +inhibition +inhibitions +inhibition's +inhibitor +inhibitors +inhibitor's +inhibitory +inhuman +inhumanly +inhumane +inhumanely +inimical +inimically +inimitably +iniquitous +iniquitously +iniquity +iniquities +iniquity's +initial +initially +initialing +initialed +initials +initial's +initialism +initialization +initialize +initializing +initialized +initializes +initialized +reinitialized +uninitialized +initiate +initiative +initiation +initiations +initiating +initiated +initiates +initiate's +initiated +uninitiated +initiation +initiation's +initiative +initiatives +initiative's +initiator +initiators +initiator's +initiatory +inject +injecting +injected +injects +injection +injections +injection's +injector +injectors +injector's +injunctive +injure +injuring +injured +injurer +injurers +injures +injured +uninjured +injurer +injurer's +injurious +ink +inked +ink's +inkblot +inkblots +inkblot's +inkiness +inkiness's +inkling +inklings +inkling's +inkstand +inkstands +inkstand's +inkwell +inkwells +inkwell's +inky +inkiest +inkier +inkiness +inland +inland's +inline +inmate +inmates +inmate's +inmost +inn +inning +innings +inner +inns +inn's +innards +innards's +innate +innately +innateness +innateness +innateness's +innermost +innersole +innersoles +innersole's +innerspring +innervate +innervation +innervating +innervated +innervates +innervation +innervation's +inning +inning's +innit +innkeeper +innkeepers +innkeeper's +innocence +innocence's +innocent +innocently +innocents +innocent's +innocuous +innocuously +innocuousness +innocuousness +innocuousness's +innovate +innovative +innovation +innovations +innovating +innovated +innovates +innovation +innovation's +innovator +innovators +innovator's +innovatory +innuendo +innuendos +innuendo's +innumerably +innumerate +inoculate +inoculating +inoculated +inoculates +reinoculating +reinoculated +reinoculates +reinoculate +inoculation +inoculations +inoculation's +inoperative +inordinate +inordinately +inorganic +inositol +inquire +inquiring +inquired +inquirer +inquirers +inquirer +inquirer's +inquiring +inquiringly +inquiry +inquiries +inquiry's +inquisition +inquisitions +inquisition's +inquisitional +inquisitive +inquisitively +inquisitiveness +inquisitiveness +inquisitiveness's +inquisitor +inquisitors +inquisitor's +inquisitorial +inrush +inrushes +inrush's +insane +insanest +insatiability +insatiability's +insatiably +inscribe +inscribing +inscribed +inscriber +inscribers +inscriber +inscriber's +inscription +inscriptions +inscription's +inscrutability +inscrutability's +inscrutable +inscrutableness +inscrutableness +inscrutableness's +inscrutably +inseam +inseams +inseam's +insecticidal +insecticide +insecticides +insecticide's +insectivore +insectivores +insectivore's +insectivorous +insecure +insecurely +inseminate +insemination +inseminating +inseminated +inseminates +insemination +insemination's +insensate +insensible +insensitive +insensitively +inseparable +inseparables +inseparable's +insert's +insert +inserting +inserted +inserts +reinserting +reinserted +reinserts +reinsert +insertion +insertion's +reinsertion's +reinsertion +insertions +insetting +inshore +inside +insider +insiders +insides +inside's +insider +insider's +insidious +insidiously +insidiousness +insidiousness +insidiousness's +insight +insights +insight's +insightful +insignia +insignia's +insinuate +insinuative +insinuation +insinuations +insinuating +insinuated +insinuates +insinuation +insinuation's +insinuator +insinuators +insinuator's +insipid +insipidly +insipidness +insipidity +insipidity's +insist +insisting +insisted +insists +insistence +insistence's +insistent +insistently +insisting +insistingly +insofar +insole +insoles +insole's +insolence +insolence's +insolent +insolently +insoluble +insolubly +insolvency +insolvencies +insomnia +insomnia's +insomniac +insomniacs +insomniac's +insomuch +insouciance +insouciance's +insouciant +inspect +inspecting +inspected +inspects +reinspecting +reinspected +reinspects +reinspect +inspection +inspections +inspection's +inspector +inspectors +inspector's +inspectorate +inspectorates +inspectorate's +inspiration +inspirations +inspiration's +inspirational +inspiratory +inspired +uninspired +inspiring +uninspiring +inst +instability +instabilities +installation +installations +installation's +installer +installers +installer's +uninstallers +uninstaller's +uninstaller +installment +installments +installment's +instance +instancing +instanced +instant +instantly +instanter +instants +instant's +instantaneous +instantaneously +instantiate +instantiating +instantiated +instantiates +instar +instate +instating +instated +instates +reinstating +reinstated +reinstates +reinstate +instead +instigate +instigation +instigating +instigated +instigates +instigation +instigation's +instigator +instigators +instigator's +instillation +instillation's +instinct +instinctive +instincts +instinct's +instinctive +instinctively +instinctual +institute +institution +institutions +instituting +instituted +instituter +instituters +institutes +institute's +instituter +instituter's +institution +institution's +institutional +institutionally +institutionalization +institutionalization's +institutionalize +institutionalizing +institutionalized +institutionalizes +instr +instruct +instructive +instructing +instructed +instructs +instructed +uninstructed +instruction +instructions +instruction's +instructional +instructive +instructively +instructor +instructors +instructor's +instrument +instrumenting +instrumented +instruments +instrument's +instrumental +instrumentally +instrumentals +instrumental's +instrumentalist +instrumentalists +instrumentalist's +instrumentality +instrumentality's +instrumentation +instrumentation's +insubordinate +insufferable +insufferably +insula +insular +insularity +insularity's +insulate +insulation +insulating +insulated +insulates +insulation +insulation's +insulator +insulators +insulator's +insulin +insulin's +insult +insulting +insulted +insults +insult's +insulting +insultingly +insuperable +insuperably +insurance +insurances +insurance's +insure +insuring +insured +insurer +insurers +insures +insurable +insured +insureds +insured's +insurer +insurer's +insurgence +insurgences +insurgence's +insurgency +insurgencies +insurgency's +insurgent +insurgents +insurgent's +insurmountably +insurrection +insurrections +insurrection's +insurrectionist +insurrectionists +insurrectionist's +int +intact +intaglio +intaglios +intaglio's +integer +integers +integer's +integral +integrally +integrals +integral's +integrate +integrative +integration +integrating +integrated +integrates +reintegration +disintegration +reintegrating +disintegrating +reintegrated +disintegrated +reintegrates +disintegrates +reintegrate +disintegrate +integration +integration's +reintegration's +disintegration's +reintegration +disintegration +integrator +integrity +integrity's +integument +integuments +integument's +intellect +intellects +intellect's +intellectual +intellectually +intellectuals +intellectual's +intellectualism +intellectualism's +intellectualize +intellectualizing +intellectualized +intellectualizes +intelligence +intelligence's +intelligent +intelligently +intelligentsia +intelligentsia's +intelligibility +intelligibility's +intelligible +unintelligible +intelligibly +unintelligibly +intended +intendeds +intended's +intense +intensive +intensely +intensest +intenser +intensification +intensification's +intensifier +intensifier's +intensify +intensification +intensifying +intensified +intensifier +intensifiers +intensifies +intensity +intensities +intensive +intensively +intensives +intensiveness +intensive's +intensiveness +intensiveness's +intent +intently +intents +intentness +intent's +intention +intentions +intention's +intentional +intentionally +unintentionally +unintentional +intentness +intentness's +inter +inters +interment +disinters +disinterment +disinter +interact +interactive +interacting +interacted +interacts +interaction +interactions +interaction's +interactive +interactively +interactivity +interbred +interbreed +interbreeding +interbreeds +intercede +interceding +interceded +intercedes +intercept +intercepting +intercepted +intercepts +intercept's +interception +interceptions +interception's +interceptor +interceptors +interceptor's +intercession +intercessions +intercession's +intercessor +intercessors +intercessor's +intercessory +interchange +interchanging +interchanged +interchanges +interchange's +interchangeability +interchangeable +interchangeably +intercity +intercollegiate +intercom +intercoms +intercom's +intercommunicate +intercommunication +intercommunicating +intercommunicated +intercommunicates +intercommunication +intercommunication's +interconnect +interconnecting +interconnected +interconnects +interconnection +interconnections +interconnection's +intercontinental +intercourse +intercourse's +intercultural +interdenominational +interdepartmental +interdependence +interdependence's +interdependent +interdependently +interdict +interdicting +interdicted +interdicts +interdict's +interdiction +interdiction's +interdisciplinary +interest +interested +interests +interest's +disinterested +disinterests +disinterest's +disinterest +interested +uninterested +interesting +interestingly +interface +interfacing +interfaced +interfaces +interface's +interfaith +interfere +interfering +interfered +interferes +interference +interference's +interferon +interferon's +interfile +interfiling +interfiled +interfiles +intergalactic +intergovernmental +interim +interim's +interior +interiors +interior's +interj +interject +interjecting +interjected +interjects +interjection +interjections +interjection's +interlace +interlacing +interlaced +interlaces +interlard +interlarding +interlarded +interlards +interleave +interleaving +interleaved +interleaves +interleukin +interleukin's +interline +interlining +interlinings +interlined +interlines +interlinear +interlining +interlining's +interlink +interlinking +interlinked +interlinks +interlock +interlocking +interlocked +interlocks +interlock's +interlocutor +interlocutors +interlocutor's +interlocutory +interlope +interloping +interloped +interloper +interlopers +interlopes +interloper +interloper's +interlude +interluding +interluded +interludes +interlude's +intermarriage +intermarriages +intermarriage's +intermarry +intermarrying +intermarried +intermarries +intermediary +intermediaries +intermediary's +intermediate +intermediately +intermediates +intermediate's +interment +interment's +disinterment's +disinterment +interments +intermezzi +intermezzo +intermezzos +intermezzo's +interminably +intermingle +intermingling +intermingled +intermingles +intermission +intermissions +intermission's +intermittence +intermittency +intermittent +intermittently +intermix +intermixing +intermixed +intermixes +intern +interning +interned +internment +internal +internally +internals +internalization +internalization's +internalize +internalizing +internalized +internalizes +international +internationally +internationals +international's +internationalism +internationalism's +internationalist +internationalists +internationalist's +internationalization +internationalize +internationalizing +internationalized +internationalizes +internecine +internee +internees +internee's +internet +internist +internists +internist's +internment +internment's +internship +internships +internship's +interoffice +interoperability +interpenetrate +interpenetration +interpenetrating +interpenetrated +interpenetrates +interpersonal +interplanetary +interplay +interplay's +interpolate +interpolation +interpolations +interpolating +interpolated +interpolates +interpolation +interpolation's +interpose +interposing +interposed +interposes +interposition +interposition's +interpret +interpretive +interpreting +interpreted +interprets +reinterpreting +reinterpreted +reinterprets +reinterpret +interpretation +interpretations +interpretation's +reinterpretations +reinterpretation's +reinterpretation +interpretative +interpreted +uninterpreted +interpreter +interpreters +interpreter's +interracial +interred +disinterred +interregnum +interregnums +interregnum's +interrelate +interrelation +interrelations +interrelating +interrelated +interrelates +interrelation +interrelation's +interrelationship +interrelationships +interrelationship's +interring +disinterring +interrogate +interrogative +interrogation +interrogations +interrogating +interrogated +interrogates +interrogation +interrogation's +interrogative +interrogatively +interrogatives +interrogative's +interrogator +interrogators +interrogator's +interrogatory +interrogatories +interrogatory's +interrupt +interrupting +interrupted +interrupter +interrupters +interrupts +interrupt's +interrupter +interrupter's +interruption +interruptions +interruption's +interscholastic +intersect +intersecting +intersected +intersects +intersection +intersections +intersection's +intersession +intersessions +intersession's +intersex +intersperse +interspersion +interspersing +interspersed +intersperses +interspersion +interspersion's +interstate +interstates +interstate's +interstellar +interstice +interstices +interstice's +interstitial +intertwine +intertwining +intertwined +intertwines +interurban +interval +intervals +interval's +intervene +intervening +intervened +intervenes +intervention +interventions +intervention's +interventionism +interventionism's +interventionist +interventionists +interventionist's +interview +interviewing +interviewed +interviewer +interviewers +interviews +interview's +interviewee +interviewees +interviewee's +interviewer +interviewer's +intervocalic +interwar +interweave +interweaving +interweaves +interwove +interwoven +intestacy +intestacy's +intestate +intestinal +intestine +intestines +intestine's +intifada +intimacy +intimacies +intimacy's +intimate +intimation +intimations +intimately +intimating +intimated +intimates +intimate's +intimation +intimation's +intimidate +intimidation +intimidating +intimidated +intimidates +intimidating +intimidatingly +intimidation +intimidation's +intonation +intonations +intonation's +intoxicant +intoxicants +intoxicant's +intoxicate +intoxication +intoxicating +intoxicated +intoxicates +intoxication +intoxication's +intracranial +intramural +intramuscular +intranet +intranets +intranet's +intransigence +intransigence's +intransigent +intransigently +intransigents +intransigent's +intrastate +intrauterine +intravenous +intravenously +intravenouses +intravenous's +intrepid +intrepidly +intrepidity +intrepidity's +intricacy +intricacies +intricacy's +intricate +intricately +intrigue +intriguing +intrigued +intriguer +intriguers +intrigues +intrigue's +intriguer +intriguer's +intriguing +intriguingly +intrinsic +intrinsically +intro +intros +intro's +introduce +introducing +introduced +introduces +reintroducing +reintroduced +reintroduces +reintroduce +introduction +introduction's +reintroduction's +reintroduction +introductions +introductory +introit +introits +introit's +introspect +introspective +introspecting +introspected +introspects +introspection +introspection's +introspective +introspectively +introversion +introversion's +introvert +introverted +introverts +introvert's +intrude +intruding +intruded +intruder +intruders +intrudes +intruder +intruder's +intrusion +intrusions +intrusion's +intrusive +intrusively +intrusiveness +intrusiveness +intrusiveness's +intuit +intuitive +intuiting +intuited +intuits +intuition +intuitions +intuitive +intuitively +intuitiveness +intuitiveness +intuitiveness's +inundate +inundation +inundations +inundating +inundated +inundates +inundation +inundation's +inure +inuring +inured +inures +invade +invading +invaded +invader +invaders +invades +invader +invader's +invalid +invalidly +invaliding +invalided +invalids +invalid's +invalidism +invalidism's +invaluable +invaluably +invariant +invasion +invasions +invasion's +invasive +invective +invective's +inveigh +inveighing +inveighed +inveighs +inveigle +inveigling +inveigled +inveigler +inveiglers +inveigles +inveigler +inveigler's +invent +inventive +inventing +invented +invents +reinventing +reinvented +reinvents +reinvent +invention +inventions +invention's +reinventions +reinvention's +reinvention +inventive +inventively +inventiveness +inventiveness +inventiveness's +inventor +inventors +inventor's +inventory +inventorying +inventoried +inventories +inventory's +inverse +inversely +inverses +inverse's +invert +inverting +inverted +inverter +inverters +inverts +invert's +inverter +inverter's +invest +investing +invested +invests +investment +reinvesting +reinvested +reinvests +reinvestment +reinvest +investigate +investigative +investigation +investigations +investigating +investigated +investigates +investigation +investigation's +investigator +investigators +investigator's +investigatory +investiture +investitures +investiture's +investment +investment's +reinvestment's +disinvestment's +reinvestment +disinvestment +investor +investors +investor's +inveteracy +inveteracy's +inveterate +invidious +invidiously +invidiousness +invidiousness +invidiousness's +invigilate +invigilation +invigilating +invigilated +invigilates +invigilator +invigilators +invigorate +invigorating +invigorated +invigorates +reinvigorating +reinvigorated +reinvigorates +reinvigorate +invigorating +invigoratingly +invigoration +invigoration's +invincibility +invincibility's +invincibly +inviolability +inviolability's +inviolably +inviolate +invitation +invitations +invitation's +invitational +invitationals +invitational's +invite +inviting +invited +invites +invite's +invited +uninvited +invitee +invitees +invitee's +inviting +invitingly +invoke +invoking +invoked +invokes +involuntariness +involuntariness's +involuntary +involuntariness +involution +involution's +involve +involving +involved +involves +involvement +involved +uninvolved +involvement +involvements +involvement's +inward +inwardly +inwards +ioctl +iodide +iodides +iodide's +iodine +iodine's +iodize +iodizing +iodized +iodizes +ion +ions +ion's +unions +union's +union +ionic +ionization +ionization's +unionization's +unionization +ionize +ionizing +ionized +ionizes +unionizing +unionized +unionizes +unionize +ionizer +ionizers +ionizer's +ionosphere +ionospheres +ionosphere's +ionospheric +iota +iotas +iota's +ipecac +ipecacs +ipecac's +irascibility +irascibility's +irascible +irascibly +irate +irately +irateness +irateness +irateness's +ire +ire's +ireful +irenic +irides +iridescence +iridescence's +iridescent +iridescently +iridium +iridium's +iris +irises +iris's +irk +irking +irked +irks +irksome +irksomely +irksomeness +irksomeness +irksomeness's +iron +ironing +ironed +irons +iron's +ironclad +ironclads +ironclad's +ironic +ironical +ironically +ironing +ironing's +ironmonger +ironmongers +ironmongery +ironstone +ironstone's +ironware +ironware's +ironwood +ironwoods +ironwood's +ironwork +ironwork's +irony +ironies +irony's +irradiate +irradiation +irradiating +irradiated +irradiates +irradiation +irradiation's +irrational +irrationally +irrationals +irrational's +irrationality +irrationality's +irreclaimable +irreconcilability +irreconcilability's +irreconcilable +irreconcilably +irrecoverable +irrecoverably +irredeemable +irredeemably +irreducible +irreducibly +irrefutable +irrefutably +irregardless +irregular +irregularly +irregulars +irregular's +irregularity +irregularities +irregularity's +irrelevance +irrelevances +irrelevance's +irrelevancy +irrelevancies +irrelevancy's +irrelevant +irrelevantly +irreligion +irreligious +irremediable +irremediably +irremovable +irreparable +irreparably +irreplaceable +irrepressible +irrepressibly +irreproachable +irreproachably +irresistible +irresistibly +irresolute +irresolution +irresolutely +irresoluteness +irresoluteness +irresoluteness's +irresolution +irresolution's +irrespective +irresponsibility +irresponsibility's +irresponsible +irresponsibly +irretrievable +irretrievably +irreverence +irreverence's +irreverent +irreverently +irreversible +irreversibly +irrevocable +irrevocably +irrigable +irrigate +irrigation +irrigating +irrigated +irrigates +irrigation +irrigation's +irritability +irritability's +irritable +irritably +irritant +irritants +irritant's +irritate +irritation +irritations +irritating +irritated +irritates +irritating +irritatingly +irritation +irritation's +irrupt +irruptive +irrupting +irrupted +irrupts +irruption +irruptions +irruption's +ischemia +ischemic +isinglass +isinglass's +isl +island +islander +islanders +islands +island's +islander +islander's +isle +isles +isle's +islet +islets +islet's +ism +ism's +deism's +deism +isms +isn't +isobar +isobars +isobar's +isobaric +isolate +isolation +isolating +isolated +isolates +isolate's +isolation +isolation's +isolationism +isolationism's +isolationist +isolationists +isolationist's +isomer +isomers +isomer's +isomeric +isomerism +isomerism's +isometric +isometrics +isometrically +isometrics +isometrics's +isomorphic +isomorphism +isosceles +isotherm +isotherms +isotherm's +isotope +isotopes +isotope's +isotopic +isotropic +issuance +issuance's +issue +issuing +issued +issues +issue's +reissuing +reissued +reissues +reissue's +reissue +issuer +issuers +issuer's +isthmian +isthmus +isthmuses +isthmus's +it'd +it'll +it +its +it's +units +unit's +unit +ital +italic +italics +italic's +italicization +italicization's +italicize +italicizing +italicized +italicizes +italics +italics's +itch +itching +itched +itches +itch's +itchiness +itchiness's +itchy +itchiest +itchier +itchiness +item +items +item's +itemization +itemization's +itemize +itemizing +itemized +itemizes +iterate +iterative +iteration +iterations +iterating +iterated +iterates +reiteration +reiterations +reiterating +reiterated +reiterates +reiterate +iteration +iteration's +reiteration's +reiteration +iterator +iterators +itinerant +itinerants +itinerant's +itinerary +itineraries +itinerary's +itself +iv +univ +ivory +ivories +ivory's +ivy +ivied +ivies +ivy's +ix +j +conj +jab +jabs +jab's +jabbed +jabber +jabbering +jabbered +jabberer +jabberers +jabbers +jabber's +jabberer +jabberer's +jabbing +jabot +jabots +jabot's +jacaranda +jacarandas +jacaranda's +jack +jacking +jacked +jacks +jack's +jackal +jackals +jackal's +jackass +jackasses +jackass's +jackboot +jackbooted +jackboots +jackboot's +jackdaw +jackdaws +jackdaw's +jacket +jacketed +jackets +jacket's +jackhammer +jackhammers +jackhammer's +jackknife +jackknifing +jackknifed +jackknifes +jackknife's +jackknives +jackpot +jackpots +jackpot's +jackrabbit +jackrabbits +jackrabbit's +jackstraw +jackstraws +jackstraw's +jacquard +jacquard's +jade +jading +jaded +jades +jade's +jaded +jadedly +jadedness +jadedness +jadedness's +jadeite +jadeite's +jag +jags +jag's +jagged +jaggedly +jaggedest +jaggeder +jaggedness +jaggedness +jaggedness's +jaggies +jaguar +jaguars +jaguar's +jail +jailing +jailed +jailer +jailers +jails +jail's +jailbird +jailbirds +jailbird's +jailbreak +jailbreaks +jailbreak's +jailer +jailer's +jailhouse +jailhouses +jalapeno +jalapenos +jalapeno's +jalopy +jalopies +jalopy's +jalousie +jalousies +jalousie's +jam +jams +jam's +jamb +jambs +jamb's +jambalaya +jambalaya's +jamboree +jamborees +jamboree's +jammed +jamming +jammy +jammiest +jammier +jangle +jangling +jangled +jangler +janglers +jangles +jangle's +jangler +jangler's +janitor +janitors +janitor's +janitorial +japan +japans +japan's +japanned +japanning +jape +japing +japed +japes +jape's +jar +jars +jar's +jardiniere +jardinieres +jardiniere's +jarful +jarfuls +jarful's +jargon +jargon's +jarred +jarring +jarringly +jasmine +jasmines +jasmine's +jasper +jasper's +jato +jatos +jato's +jaundice +jaundicing +jaundiced +jaundices +jaundice's +jaunt +jaunting +jaunted +jaunts +jaunt's +jauntily +jauntiness +jauntiness's +jaunty +jauntiest +jauntier +jauntiness +java +java's +javelin +javelins +javelin's +jaw +jawing +jawed +jaws +jaw's +jawbone +jawboning +jawboned +jawbones +jawbone's +jawbreaker +jawbreakers +jawbreaker's +jawline +jawlines +jay +jays +jay's +jaybird +jaybirds +jaybird's +jaywalk +jaywalking +jaywalked +jaywalker +jaywalkers +jaywalks +jaywalker +jaywalker's +jaywalking +jaywalking's +jazz +jazzing +jazzed +jazzes +jazz's +jazzy +jazziest +jazzier +jct +jealous +jealously +jealousy +jealousies +jealousy's +jean +jeans +jean's +jeans +jeans's +jeep +jeeps +jeep's +jeer +jeering +jeered +jeers +jeer's +jeering +jeeringly +jeering's +jeez +jejuna +jejune +jejunum +jejunum's +jell +jelling +jelled +jells +jello +jellos +jelly +jellying +jellied +jellies +jelly's +jellybean +jellybeans +jellybean's +jellyfish +jellyfishes +jellyfish's +jellylike +jellyroll +jellyrolls +jellyroll's +jemmy +jemmying +jemmied +jemmies +jennet +jennets +jennet's +jenny +jennies +jenny's +jeopardize +jeopardizing +jeopardized +jeopardizes +jeopardy +jeopardy's +jeremiad +jeremiads +jeremiad's +jerk +jerking +jerked +jerks +jerk's +jerkily +jerkin +jerkins +jerkin's +jerkiness +jerkiness's +jerkwater +jerky +jerkiest +jerkier +jerkiness +jerky's +jeroboam +jeroboams +jerrybuilt +jerrycan +jerrycans +jersey +jerseys +jersey's +jest +jesting +jested +jester +jesters +jests +jest's +jester +jester's +jesting +jestingly +jet +jets +jet's +jetliner +jetliners +jetliner's +jetport +jetports +jetport's +jetsam +jetsam's +jetted +jetting +jettison +jettisoning +jettisoned +jettisons +jettison's +jetty +jetties +jetty's +jew +jewel +jeweling +jeweled +jeweler +jewelers +jewels +jewel's +jeweler +jeweler's +jewelry +jewelries +jewelry's +jg +jib +jibing +jibed +jibs +jib's +jibbed +jibbing +jibe +jibes +jibe's +jiff +jiffs +jiff's +jiffy +jiffies +jiffy's +jig's +jig +jigs +rejigs +rejig +jigged +rejigged +jigger's +jigger +jiggering +jiggered +jiggers +rejiggering +rejiggered +rejiggers +rejigger +jigging +rejigging +jiggle +jiggling +jiggled +jiggles +jiggle's +jiggly +jigsaw +jigsawing +jigsawed +jigsaws +jigsaw's +jihad +jihads +jihad's +jihadist +jihadists +jihadist's +jilt +jilting +jilted +jilts +jilt's +jimmy +jimmying +jimmied +jimmies +jimmy's +jimsonweed +jimsonweed's +jingle +jingling +jingled +jingles +jingle's +jingly +jingoism +jingoism's +jingoist +jingoists +jingoist's +jingoistic +jink +jinking +jinked +jinks +jinn +jinni +jinni's +jinrikisha +jinrikishas +jinrikisha's +jinx +jinxing +jinxed +jinxes +jinx's +jitney +jitneys +jitney's +jitterbug +jitterbugs +jitterbug's +jitterbugged +jitterbugger +jitterbugger's +jitterbugging +jitters +jitters's +jittery +jitteriest +jitterier +jive +jiving +jived +jives +jive's +job +jobs +job's +jobbed +jobber +jobbers +jobber's +jobbing +jobholder +jobholders +jobholder's +jobless +joblessness +joblessness +joblessness's +jobshare +jobshares +jobsworth +jobsworths +jock +jocks +jock's +jockey +jockeying +jockeyed +jockeys +jockey's +jockstrap +jockstraps +jockstrap's +jocose +jocosely +jocoseness +jocoseness +jocoseness's +jocosity +jocosity's +jocular +jocularly +jocularity +jocularity's +jocund +jocundly +jocundity +jocundity's +jodhpurs +jodhpurs's +joey +joeys +jog +jogs +jog's +jogged +jogger +joggers +jogger's +jogging +jogging's +joggle +joggling +joggled +joggles +joggle's +john +johns +john's +johnny +johnnies +johnny's +johnnycake +johnnycakes +johnnycake's +join's +join +joining +joined +joins +rejoining +conjoining +rejoined +conjoined +rejoins +conjoins +rejoin +conjoin +joiner +joiners +joiner's +conjoiners +conjoiner's +conjoiner +joinery +joinery's +joint's +joint +jointing +jointed +joints +disjointing +disjointed +disjoints +disjoint +jointly +conjointly +joist +joists +joist's +jojoba +joke +joking +joked +joker +jokers +jokes +joke's +joker +joker's +jokey +jokier +jokiest +joking +jokingly +jollification +jollifications +jollification's +jollily +jolliness +jolliness's +jollity +jollity's +jolly +jollying +jollied +jolliest +jollier +jollies +jolliness +jolly's +jolt +jolting +jolted +jolter +jolters +jolts +jolt's +jolter +jolter's +jonquil +jonquils +jonquil's +josh +joshing +joshed +josher +joshers +joshes +josh's +josher +josher's +jostle +jostling +jostled +jostles +jostle's +jot +jots +jot's +jotted +jotter +jotters +jotter's +jotting +jottings +jotting's +joule +joules +joule's +jounce +jouncing +jounced +jounces +jounce's +jouncy +journal +journals +journal's +journalese +journalese's +journalism +journalism's +journalist +journalists +journalist's +journalistic +journey +journeying +journeyed +journeyer +journeyers +journeys +journey's +journeyer +journeyer's +journeyman +journeyman's +journeymen +journo +journos +joust +jousting +jousted +jouster +jousters +jousts +joust's +jouster +jouster's +jousting +jousting's +jovial +jovially +joviality +joviality's +jowl +jowls +jowl's +jowly +jowliest +jowlier +joy +joying +joyed +joys +joy's +joyful +joyfully +joyfulness +joyfuller +joyfullest +joyfulness +joyfulness's +joyless +joylessly +joylessness +joylessness +joylessness's +joyous +joyously +joyousness +joyousness +joyousness's +joyridden +joyride +joyriding +joyrider +joyriders +joyrides +joyride's +joyrider +joyrider's +joyriding +joyriding's +joyrode +joystick +joysticks +joystick's +jr +jubilant +jubilantly +jubilation +jubilation's +jubilee +jubilees +jubilee's +judder +juddering +juddered +judders +judge's +judge +judging +judged +judges +rejudging +rejudged +rejudges +rejudge +judgeship +judgeship's +judgment +judgments +judgment's +judgmental +judgmentally +judicatory +judicatories +judicatory's +judicature +judicature's +judicial +judicially +judiciary +judiciaries +judiciary's +judicious +judiciously +judiciousness +injudiciously +injudiciousness +injudicious +judiciousness +judiciousness's +injudiciousness's +injudiciousness +judo +judo's +jug +jugs +jug's +jugful +jugfuls +jugful's +jugged +juggernaut +juggernauts +juggernaut's +jugging +juggle +juggling +juggled +juggler +jugglers +juggles +juggle's +juggler +juggler's +jugglery +jugglery's +jugular +jugulars +jugular's +juice +juicing +juiced +juicer +juicers +juices +juice's +juicer +juicer's +juicily +juiciness +juiciness's +juicy +juiciest +juicier +juiciness +jujitsu +jujitsu's +jujube +jujubes +jujube's +jukebox +jukeboxes +jukebox's +julep +juleps +julep's +julienne +jumble +jumbling +jumbled +jumbles +jumble's +jumbo +jumbos +jumbo's +jump +jumping +jumped +jumper +jumpers +jumps +jump's +jumper +jumper's +jumpily +jumpiness +jumpiness's +jumpsuit +jumpsuits +jumpsuit's +jumpy +jumpiest +jumpier +jumpiness +jun +junco +juncos +junco's +junction +junctions +junction's +injunctions +conjunctions +injunction's +conjunction's +injunction +conjunction +juncture +junctures +juncture's +conjunctures +conjuncture's +conjuncture +jungle +jungles +jungle's +junior +juniors +junior's +juniper +junipers +juniper's +junk +junking +junked +junker +junkers +junks +junk's +junker +junker's +junket +junketing +junketed +junkets +junket's +junketeer +junketeers +junketeer's +junkie +junkiest +junkier +junkies +junkie's +junkyard +junkyards +junkyard's +junta +juntas +junta's +juridic +juridical +juridically +jurisdiction +jurisdictions +jurisdiction's +jurisdictional +jurisprudence +jurisprudence's +jurist +jurists +jurist's +juristic +juror +jurors +juror's +jury +juries +jury's +injuries +injury's +injury +juryman +juryman's +jurymen +jurywoman +jurywoman's +jurywomen +just +justly +justest +juster +justness +justice +justices +justice's +injustices +injustice's +injustice +justifiable +unjustifiable +justifiably +unjustifiably +justification +justification's +justified +unjustified +justify +justification +justifications +justifying +justified +justifies +justness +justness's +jut +juts +jut's +jute +jute's +jutted +jutting +juvenile +juveniles +juvenile's +juxtapose +juxtaposing +juxtaposed +juxtaposes +juxtaposition +juxtapositions +juxtaposition's +k +king +ks +inking +conking +inks +conks +ink +conk +kHz +kW +kWh +kabbalah +kaboom +kabuki +kabuki's +kaddish +kaddishes +kaddish's +kaffeeklatch +kaffeeklatches +kaffeeklatch's +kaffeeklatsch +kaffeeklatsches +kaffeeklatsch's +kahuna +kahunas +kaiser +kaisers +kaiser's +kale +kale's +kaleidoscope +kaleidoscopes +kaleidoscope's +kaleidoscopic +kaleidoscopically +kamikaze +kamikazes +kamikaze's +kana +kangaroo +kangaroos +kangaroo's +kanji +kaolin +kaolin's +kapok +kapok's +kappa +kappas +kappa's +kaput +karakul +karakul's +karaoke +karaokes +karaoke's +karat +karats +karat's +karate +karate's +karma +karma's +karmic +kart +karts +kart's +katakana +katydid +katydids +katydid's +kayak +kayaking +kayaked +kayaks +kayak's +kayaking +kayaking's +kayo +kayoing +kayoed +kayos +kayo's +kazoo +kazoos +kazoo's +kc +kebab +kebabs +kebab's +kedgeree +keel +keeling +keeled +keels +keel's +keelhaul +keelhauling +keelhauled +keelhauls +keen +keenly +keening +keened +keenest +keener +keens +keenness +keen's +keenness +keenness's +keep +keeping +keeper +keepers +keeps +keep's +keeper +keeper's +keeping +keeping's +keepsake +keepsakes +keepsake's +keg +kegs +keg's +kelp +kelp's +kelvin +kelvins +kelvin's +ken +kens +ken's +kenned +kennel +kenneling +kenneled +kennels +kennel's +kenning +keno +keno's +kepi +kepis +kepi's +kept +keratin +keratin's +keratitis +kerbside +kerchief +kerchiefs +kerchief's +kerfuffle +kerfuffles +kernel +kernels +kernel's +kerosene +kerosene's +kestrel +kestrels +kestrel's +ketch +ketches +ketch's +ketchup +ketchup's +ketone +ketones +kettle +kettles +kettle's +kettledrum +kettledrums +kettledrum's +key +keying +keyed +keys +key's +keybinding +keybindings +keyboard +keyboarding +keyboarded +keyboarder +keyboarders +keyboards +keyboard's +keyboarder +keyboarder's +keyboardist +keyboardists +keyboardist's +keyhole +keyholes +keyhole's +keynote +keynoting +keynoted +keynoter +keynoters +keynotes +keynote's +keynoter +keynoter's +keypad +keypads +keypad's +keypunch +keypunching +keypunched +keypuncher +keypunchers +keypunches +keypunch's +keypuncher +keypuncher's +keystone +keystones +keystone's +keystroke +keystrokes +keystroke's +keyword +keywords +keyword's +kg +khaki +khakis +khaki's +khan +khans +khan's +kibble +kibbling +kibbled +kibbles +kibble's +kibbutz +kibbutzes +kibbutz's +kibbutzim +kibitz +kibitzing +kibitzed +kibitzer +kibitzers +kibitzes +kibitzer +kibitzer's +kibosh +kibosh's +kick +kicking +kicked +kicker +kickers +kicks +kick's +kickback +kickbacks +kickback's +kickball +kickball's +kickboxing +kicker +kicker's +kickoff +kickoffs +kickoff's +kickstand +kickstands +kickstand's +kicky +kickiest +kickier +kid +kids +kid's +kidded +kidder +kidders +kidder's +kiddie +kiddies +kiddie's +kidding +kiddish +kiddo +kiddos +kiddo's +kidnap +kidnaps +kidnapped +kidnapper +kidnappers +kidnapper's +kidnapping +kidnappings +kidnapping's +kidney +kidneys +kidney's +kidskin +kidskin's +kielbasa +kielbasas +kielbasa's +kielbasi +kike +kikes +kill +killing +killings +killed +killer +killers +kills +kill's +killdeer +killdeers +killdeer's +killer +killer's +killing +killing's +killjoy +killjoys +killjoy's +kiln +kilning +kilned +kilns +kiln's +kilo +kilos +kilo's +kilobyte +kilobytes +kilobyte's +kilocycle +kilocycles +kilocycle's +kilogram +kilograms +kilogram's +kilohertz +kilohertz's +kiloliter +kiloliters +kiloliter's +kilometer +kilometers +kilometer's +kiloton +kilotons +kiloton's +kilowatt +kilowatts +kilowatt's +kilt +kilted +kilter +kilts +kilt's +kilter +kilter's +kimono +kimonos +kimono's +kin +kin's +kinase +kind's +kind +kindly +kindest +kinder +kindness +unkindly +unkinder +unkindness +unkind +kinda +kindergarten +kindergartens +kindergarten's +kindergartner +kindergartners +kindergartner's +kindhearted +kindheartedly +kindheartedness +kindheartedness +kindheartedness's +kindle +kindling +kindled +kindles +rekindling +rekindled +rekindles +rekindle +kindliness +kindliness's +kindling +kindling's +kindly +kindliest +kindlier +unkindlier +unkindly +kindness +kindness's +unkindness's +unkindness +kindnesses +kindred +kindred's +kinds +kine +kines +kinematic +kinematics +kinematics +kinematics's +kinetic +kinetics +kinetically +kinetics +kinetics's +kinfolk +kinfolks +kinfolk's +kinfolks +kinfolks's +king +kingly +kings +king's +kingdom +kingdoms +kingdom's +kingfisher +kingfishers +kingfisher's +kingly +kingliest +kinglier +kingmaker +kingmakers +kingpin +kingpins +kingpin's +kingship +kingship's +kink +kinking +kinked +kinks +kink's +kinkily +kinkiness +kinkiness's +kinky +kinkiest +kinkier +kinkiness +kinsfolk +kinsfolk's +kinship +kinship's +kinsman +kinsman's +kinsmen +kinswoman +kinswoman's +kinswomen +kiosk +kiosks +kiosk's +kip +kips +kip's +kipped +kipper +kippering +kippered +kippers +kipper's +kipping +kirsch +kirsches +kirsch's +kismet +kismet's +kiss +kissing +kissed +kisser +kissers +kisses +kiss's +kissable +kisser +kisser's +kissoff +kissoffs +kissoff's +kissogram +kissograms +kit +kiting +kited +kits +kit's +kitchen +kitchens +kitchen's +kitchenette +kitchenettes +kitchenette's +kitchenware +kitchenware's +kite +kites +kite's +kith +kith's +kitsch +kitsch's +kitschy +kitted +kitten +kittens +kitten's +kittenish +kitting +kitty +kitties +kitty's +kiwi +kiwis +kiwi's +kiwifruit +kiwifruits +kiwifruit's +kl +klaxon +klaxons +kleptocracy +kleptomania +kleptomania's +kleptomaniac +kleptomaniacs +kleptomaniac's +kludge +kludging +kludged +kludges +kluge +kluged +kluges +klutz +klutzes +klutz's +klutziness +klutziness's +klutzy +klutziest +klutzier +klutziness +km +kn +knack +knacker +knackers +knacks +knack's +knacker +knackering +knackered +knapsack +knapsacks +knapsack's +knave +knaves +knave's +knavery +knavery's +knavish +knavishly +knead +kneading +kneaded +kneader +kneaders +kneads +kneader +kneader's +knee +kneed +knees +knee's +kneecap +kneecaps +kneecap's +kneecapped +kneecapping +kneeing +kneel +kneeling +kneels +knell +knelling +knelled +knells +knell's +knelt +knew +knicker +knickers +knickerbockers +knickerbockers's +knickers +knickers's +knickknack +knickknacks +knickknack's +knife +knifing +knifed +knifes +knife's +knight +knightly +knighting +knighted +knights +knight's +knighthood +knighthoods +knighthood's +knightliness +knightliness's +knish +knishes +knish's +knit +knits +knit's +knitted +knitter +knitters +knitter's +knitting +knitting's +knitwear +knitwear's +knives +knob +knobs +knob's +knobbly +knobby +knobbiest +knobbier +knock +knocking +knocked +knocker +knockers +knocks +knock's +knockabout +knockdown +knockdowns +knockdown's +knocker +knocker's +knockoff +knockoffs +knockoff's +knockout +knockouts +knockout's +knockwurst +knockwursts +knockwurst's +knoll +knolls +knoll's +knot +knots +knot's +knothole +knotholes +knothole's +knotted +knotting +knotty +knottiest +knottier +know +knows +knowable +knowing +knowingly +knowings +unknowingly +unknowings +unknowing +knowledge +knowledge's +knowledgeable +knowledgeably +known +knuckle +knuckling +knuckled +knuckles +knuckle's +knuckleduster +knuckledusters +knucklehead +knuckleheads +knucklehead's +knurl +knurling +knurled +knurls +knurl's +koala +koalas +koala's +koan +koans +kohl +kohlrabi +kohlrabi's +kohlrabies +kola +kolas +kola's +kook +kooks +kook's +kookaburra +kookaburras +kookaburra's +kookiness +kookiness's +kooky +kookiest +kookier +kookiness +kopeck +kopecks +kopeck's +korma +kosher +koshering +koshered +koshers +kowtow +kowtowing +kowtowed +kowtows +kowtow's +kph +kraal +kraals +kraal's +kraut +krauts +kraut's +krill +krill's +krona +krona's +krone +kroner +krone's +kronor +kronur +krypton +krypton's +kt +kuchen +kuchens +kuchen's +kudos +kudos's +kudzu +kudzus +kudzu's +kumquat +kumquats +kumquat's +kvetch +kvetching +kvetched +kvetcher +kvetchers +kvetches +kvetch's +kvetcher +kvetcher's +kw +l +lens +ling +lings +led +lest +ls +la +la's +lab +labs +lab's +label's +label +labeling +labeled +labels +relabeling +relabeled +relabels +relabel +labeled +unlabeled +labia +labial +labials +labial's +labile +labium +labium's +labor +laboring +labored +laborer +laborers +labors +labor's +laboratory +laboratories +laboratory's +laborer +laborer's +laborious +laboriously +laboriousness +laboriousness +laboriousness's +laborsaving +laburnum +laburnums +laburnum's +labyrinth +labyrinth's +labyrinthine +labyrinths +lac +lac's +lace's +lace +lacing +laced +laces +unlacing +unlaced +unlaces +unlace +lacerate +laceration +lacerations +lacerating +lacerated +lacerates +laceration +laceration's +lacewing +lacewings +lacewing's +lacework +lacework's +lachrymal +lachrymose +lack +lacking +lacked +lacks +lack's +lackadaisical +lackadaisically +lackey +lackeys +lackey's +lackluster +laconic +laconically +lacquer +lacquering +lacquered +lacquers +lacquer's +lacrosse +lacrosse's +lactate +lactation +lactating +lactated +lactates +lactation +lactation's +lacteal +lactic +lactose +lactose's +lacuna +lacuna's +lacunae +lacy +laciest +lacier +lad +laden +lading +ladings +laded +lads +lad's +ladder +laddering +laddered +ladders +ladder's +laddie +laddies +laddie's +laddish +laddishness +lade +lades +laden +unladen +lading +lading's +ladle +ladling +ladled +ladles +ladle's +lady +ladies +lady's +ladybird +ladybirds +ladybird's +ladybug +ladybugs +ladybug's +ladyfinger +ladyfingers +ladyfinger's +ladylike +unladylike +ladylove +ladyloves +ladylove's +ladyship +ladyships +ladyship's +laetrile +laetrile's +lag +lager +lagers +lags +lag's +lager +lager's +laggard +laggardly +laggards +laggard's +lagged +lagging +lagging's +lagniappe +lagniappes +lagniappe's +lagoon +lagoons +lagoon's +laid +relaid +inlaid +lain +lair +lairs +lair's +laird +lairds +laird's +laity +laity's +lake +lakes +lake's +lakefront +lakefronts +lakeside +lam +lams +lam's +lama +lamas +lama's +lamasery +lamaseries +lamasery's +lamb +lambing +lambed +lambs +lamb's +lambada +lambadas +lambada's +lambaste +lambasting +lambasted +lambastes +lambda +lambdas +lambda's +lambency +lambency's +lambent +lambently +lambkin +lambkins +lambkin's +lambskin +lambskins +lambskin's +lambswool +lame +lamely +laming +lamed +lamest +lamer +lamers +lames +lameness +lame's +lamebrain +lamebrained +lamebrains +lamebrain's +lameness +lameness's +lament +lamenting +lamented +laments +lament's +lamentable +lamentably +lamentation +lamentations +lamentation's +lamina +lamina's +laminae +laminar +laminate +lamination +laminating +laminated +laminates +laminate's +lamination +lamination's +lammed +lamming +lamp +lamps +lamp's +lampblack +lampblack's +lamplight +lamplighter +lamplighters +lamplight's +lamplighter +lamplighter's +lampoon +lampooning +lampooned +lampoons +lampoon's +lamppost +lampposts +lamppost's +lamprey +lampreys +lamprey's +lampshade +lampshades +lampshade's +lanai +lanais +lanai's +lance +lancing +lanced +lancer +lancers +lances +lance's +lancer +lancer's +lancet +lancets +lancet's +land +landing +landings +landed +lander +lands +land's +landau +landaus +landau's +landfall +landfalls +landfall's +landfill +landfills +landfill's +landholder +landholders +landholder's +landholding +landholdings +landholding's +landing +landing's +landlady +landladies +landlady's +landless +landless's +landline +landlines +landline's +landlocked +landlord +landlords +landlord's +landlubber +landlubbers +landlubber's +landmark +landmarks +landmark's +landmass +landmasses +landmass's +landmine +landmines +landowner +landowners +landowner's +landownership +landowning +landownings +landowning's +landscape +landscaping +landscaped +landscaper +landscapers +landscapes +landscape's +landscaper +landscaper's +landslid +landslide +landsliding +landslides +landslide's +landslip +landslips +landsman +landsman's +landsmen +landward +landwards +lane +lanes +lane's +language +languages +language's +languid +languidly +languidness +languidness +languidness's +languish +languishing +languished +languishes +languor +languors +languor's +languorous +languorously +lank +lankly +lankest +lanker +lankness +lankiness +lankiness's +lankness +lankness's +lanky +lankiest +lankier +lankiness +lanolin +lanolin's +lantern +lanterns +lantern's +lanthanum +lanthanum's +lanyard +lanyards +lanyard's +lap +laps +lap's +laparoscopic +laparoscopy +laparotomy +lapboard +lapboards +lapboard's +lapdog +lapdogs +lapdog's +lapel +lapels +lapel's +lapidary +lapidaries +lapidary's +lapin +lapins +lapin's +lapped +lappet +lappets +lappet's +lapping +lapse +lapsing +lapsed +lapses +lapse's +relapsing +prolapsing +relapsed +prolapsed +relapses +prolapses +relapse's +prolapse's +relapse +prolapse +laptop +laptops +laptop's +lapwing +lapwings +lapwing's +larboard +larboards +larboard's +larcenist +larcenists +larcenist's +larcenous +larceny +larcenies +larceny's +larch +larches +larch's +lard +larding +larded +larder +larders +lards +lard's +larder +larder's +lardy +lardiest +lardier +large +largely +largest +larger +larges +largeness +large's +largehearted +largeness +largeness's +largess +largess's +largish +largo +largos +largo's +lariat +lariats +lariat's +lark +larking +larked +larks +lark's +larkspur +larkspurs +larkspur's +larva +larva's +larvae +larval +laryngeal +larynges +laryngitis +laryngitis's +larynx +larynx's +lasagna +lasagnas +lasagna's +lascivious +lasciviously +lasciviousness +lasciviousness +lasciviousness's +lase +lasing +lased +laser +lasers +lases +laser +laser's +lash +lashing +lashings +lashed +lashes +lash's +lashing +lashing's +lass +lasses +lass's +lassie +lassies +lassie's +lassitude +lassitude's +lasso +lassoing +lassoed +lassos +lasso's +last +lastly +lasting +lasted +lasts +last's +lasting +lastingly +lat +lats +latch's +latch +latching +latched +latches +unlatching +unlatched +unlatches +unlatch +latchkey +latchkeys +latchkey's +late +lately +latest +later +lateness +latecomer +latecomers +latecomer's +latency +latency's +lateness +lateness's +latent +lateral +laterally +lateraling +lateraled +laterals +lateral's +latest +latest's +latex +latex's +lath +lathing +lathed +lather +lathers +lathes +lath's +lathe +lathe's +lather +lathering +lathered +lather's +lathery +laths +latices +latish +latitude +latitudes +latitude's +latitudinal +latitudinarian +latitudinarians +latitudinarian's +latrine +latrines +latrine's +latte +latter +lattes +latte's +latter +latterly +latter's +lattice +latticed +lattices +lattice's +latticework +latticeworks +latticework's +laud +lauding +lauded +lauds +laud's +laudable +laudably +laudanum +laudanum's +laudatory +laugh +laughing +laughed +laugh's +laughable +laughably +laughing +laughingly +laughing's +laughingstock +laughingstocks +laughingstock's +laughs +laughter +laughter's +launch +launching +launched +launches +launch's +relaunching +relaunched +relaunches +relaunch's +relaunch +launcher +launchers +launcher's +launchpad +launchpads +launchpad's +launder +laundering +laundered +launderer +launderers +launders +launderer +launderer's +launderette +launderettes +launderette's +laundress +laundresses +laundress's +laundromat +laundromats +laundromat's +laundry +laundries +laundry's +laundryman +laundryman's +laundrymen +laundrywoman +laundrywoman's +laundrywomen +laureate +laureates +laureate's +laureateship +laureateship's +laurel +laurels +laurel's +lav +laving +laved +lavs +lava +lava's +lavage +lavage's +lavaliere +lavalieres +lavaliere's +lavatorial +lavatory +lavatories +lavatory's +lave +laves +lavender +lavenders +lavender's +lavish +lavishly +lavishing +lavished +lavishest +lavisher +lavishes +lavishness +lavishness +lavishness's +law +laws +law's +lawbreaker +lawbreakers +lawbreaker's +lawbreaking +lawbreaking's +lawful +lawfully +lawfulness +unlawfully +unlawfulness +unlawful +lawfulness +lawfulness's +unlawfulness's +unlawfulness +lawgiver +lawgivers +lawgiver's +lawless +lawlessly +lawlessness +lawlessness +lawlessness's +lawmaker +lawmakers +lawmaker's +lawmaking +lawmaking's +lawman +lawman's +lawmen +lawn +lawns +lawn's +lawnmower +lawnmowers +lawnmower's +lawrencium +lawrencium's +lawsuit +lawsuits +lawsuit's +lawyer +lawyers +lawyer's +lax +laxly +laxest +laxer +laxness +laxative +laxatives +laxative's +laxity +laxity's +laxness +laxness's +lay +laying +lays +lay's +relaying +inlaying +delaying +relays +inlays +delays +relay's +inlay's +delay's +relay +inlay +delay +layabout +layabouts +layaway +layaway's +layer +layers +layer's +delayers +delayer's +delayer +layered +layering +layering's +layette +layettes +layette's +layman +layman's +laymen +layoff +layoffs +layoff's +layout +layouts +layout's +layover +layovers +layover's +laypeople +layperson +laypersons +layperson's +layup +layups +layup's +laywoman +laywoman's +laywomen +laze +lazing +lazed +lazes +laze's +lazily +laziness +laziness's +lazy +lazying +lazied +laziest +lazier +lazies +laziness +lazybones +lazybones's +lb +lbs +lbw +lea +leas +lea's +leach +leaching +leached +leaches +lead +leaden +leading +leaded +leader +leaders +leads +lead's +leader +leader's +leaderless +leadership +leaderships +leadership's +leading +leading's +leaf +leafing +leafed +leafs +leaf's +leafage +leafage's +leafless +leaflet +leafleting +leafleted +leaflets +leaflet's +leafstalk +leafstalks +leafstalk's +leafy +leafiest +leafier +league +leaguing +leagued +leagues +league's +leak +leaking +leaked +leaks +leak's +leakage +leakages +leakage's +leakiness +leakiness's +leaky +leakiest +leakier +leakiness +lean +leaning +leanings +leaned +leanest +leaner +leans +leanness +lean's +leaning +leaning's +leanness +leanness's +leap +leaping +leaped +leaper +leapers +leaps +leap's +leaper +leaper's +leapfrog +leapfrogs +leapfrog's +leapfrogged +leapfrogging +learn +learning +learned +learns +relearning +unlearning +relearned +unlearned +relearns +unlearns +relearn +unlearn +learnedly +learner +learners +learner's +learning's +lease +leasing +leased +leases +lease's +releasing +released +releases +release's +release +leaseback +leasebacks +leaseback's +leasehold +leaseholder +leaseholders +leaseholds +leasehold's +leaseholder +leaseholder's +leaser +leasers +leaser's +leash's +leash +leashing +leashed +leashes +unleashing +unleashed +unleashes +unleash +least +least's +leastwise +leather +leathers +leather's +leatherette +leatherette's +leatherneck +leathernecks +leatherneck's +leathery +leave +leaving +leavings +leaved +leaver +leavers +leaves +leave's +leaven +leavening +leavened +leavens +leaven's +leavened +unleavened +leavening +leavening's +leaver +leaver's +leavings +leavings's +lech +leching +leched +lecher +lechers +leches +lech's +lecher +lecher's +lecherous +lecherously +lecherousness +lecherousness +lecherousness's +lechery +lechery's +lecithin +lecithin's +lectern +lecterns +lectern's +lecture +lecturing +lectured +lecturer +lecturers +lectures +lecture's +lecturer +lecturer's +lectureship +lectureships +lectureship's +ledge +ledger +ledgers +ledges +ledge's +ledger +ledger's +lee +leer +leers +lees +lee's +leech +leeching +leeched +leeches +leech's +leek +leeks +leek's +leer +leering +leered +leer's +leeriness +leeriness's +leery +leeriest +leerier +leeriness +leeward +leewards +leeward's +leeway +leeway's +left +leftest +lefter +lefts +left's +leftism +leftism's +leftist +leftists +leftist's +leftmost +leftover +leftovers +leftover's +leftward +leftwards +lefty +lefties +lefty's +leg +legs +leg's +legacy +legacies +legacy's +legal +legally +legals +legal's +legalese +legalese's +legalism +legalisms +legalism's +legalistic +legalistically +legality +legalities +legality's +legalization +legalization's +legalize +legalizing +legalized +legalizes +legate +legation +legations +legates +legate's +delegation +delegations +delegates +delegate's +delegate +legatee +legatees +legatee's +legation's +relegation's +delegation's +legato +legatos +legato's +legend +legends +legend's +legendarily +legendary +legerdemain +legerdemain's +legged +legginess +legginess's +legging +leggings +legging's +leggy +leggiest +leggier +legginess +leghorn +leghorns +leghorn's +legibility +legibility's +legible +legibly +legion +legions +legion's +legionary +legionaries +legionary's +legionnaire +legionnaires +legionnaire's +legislate +legislative +legislation +legislating +legislated +legislates +legislation +legislation's +legislative +legislatively +legislator +legislators +legislator's +legislature +legislatures +legislature's +legit +legitimacy +legitimacy's +legitimate +legitimately +legitimating +legitimated +legitimates +legitimatize +legitimatizing +legitimatized +legitimatizes +legitimization +legitimization's +legitimize +legitimizing +legitimized +legitimizes +legless +legman +legman's +legmen +legroom +legrooms +legroom's +legume +legumes +legume's +leguminous +legwarmer +legwarmers +legwork +legwork's +lei +leis +lei's +leisure +leisurely +leisured +leisure's +leisureliness +leisureliness's +leisurewear +leisurewear's +leitmotif +leitmotifs +leitmotif's +leitmotiv +leitmotivs +leitmotiv's +lemma +lemmas +lemme +lemming +lemmings +lemming +lemming's +lemon +lemons +lemon's +lemonade +lemonades +lemonade's +lemongrass +lemony +lemur +lemurs +lemur's +lend +lending +lender +lenders +lends +lender +lender's +length +lengthen +lengthens +length's +lengthen +lengthening +lengthened +lengthily +lengthiness +lengthiness's +lengths +lengthwise +lengthy +lengthiest +lengthier +lengthiness +lenience +lenience's +leniency +leniency's +lenient +leniently +lenitive +lens +lenses +lens's +lent +lentil +lentils +lentil's +lento +leonine +leopard +leopards +leopard's +leopardess +leopardesses +leopardess's +leotard +leotards +leotard's +leper +lepers +leper's +leprechaun +leprechauns +leprechaun's +leprosy +leprosy's +leprous +lepta +lepton +leptons +lepton's +lesbian +lesbians +lesbian's +lesbianism +lesbianism's +lesion +lesions +lesion's +less +lessen +lessens +lesser +less's +lessee +lessees +lessee's +lessen +lessening +lessened +lesson +lessons +lesson's +lessor +lessors +lessor's +let +lets +let's +inlets +inlet's +inlet +letdown +letdowns +letdown's +lethal +lethally +lethargic +lethargically +lethargy +lethargy's +letter +lettering +lettered +letterer +letterers +letters +letter's +letterbomb +letterbombs +letterbox +letterboxes +lettered +unlettered +letterer +letterer's +letterhead +letterheads +letterhead's +lettering +lettering's +letterpress +letterpress's +letting +lettings +lettuce +lettuces +lettuce's +letup +letups +letup's +leucine +leucotomy +leucotomies +leukemia +leukemia's +leukemic +leukemics +leukemic's +leukocyte +leukocytes +leukocyte's +levee +levees +levee's +level +levelly +leveling +leveled +leveler +levelers +levels +levelness +level's +leveler +leveler's +levelheaded +levelheadedness +levelheadedness +levelheadedness's +levelness +levelness's +lever +levering +levered +levers +lever's +leverage's +leverage +leveraging +leveraged +leverages +deleveraging +deleveraged +deleverages +deleverage +leviathan +leviathans +leviathan's +levier +levier's +levitate +levitation +levitating +levitated +levitates +levitation +levitation's +levity +levity's +levy +levying +levied +levier +leviers +levies +levy's +lewd +lewdly +lewdest +lewder +lewdness +lewdness +lewdness's +lexer +lexers +lexical +lexicographer +lexicographers +lexicographer's +lexicographic +lexicographical +lexicography +lexicography's +lexicon +lexicons +lexicon's +lexis +lg +liabilities +liability +liability's +reliability's +reliability +liable +reliable +liaise +liaising +liaised +liaises +liaison +liaisons +liaison's +liar +liars +liar's +lib +lib's +libation +libations +libation's +libber +libbers +libber's +libel +libeling +libeled +libeler +libelers +libels +libel's +libeler +libeler's +libelous +liberal +liberally +liberals +liberalness +liberal's +liberalism +liberalism's +liberality +liberality's +liberalization +liberalizations +liberalization's +liberalize +liberalizing +liberalized +liberalizes +liberalness +liberalness's +liberate +liberation +liberating +liberated +liberates +deliberation +deliberating +deliberated +deliberates +deliberate +liberation +liberation's +deliberation's +deliberation +liberator +liberators +liberator's +libertarian +libertarians +libertarian's +libertine +libertines +libertine's +liberty +liberties +liberty's +libidinal +libidinous +libido +libidos +libido's +librarian +librarians +librarian's +librarianship +library +libraries +library's +librettist +librettists +librettist's +libretto +librettos +libretto's +lice +license +licensing +licensed +licenses +license's +licensed +unlicensed +licensee +licensees +licensee's +licentiate +licentiates +licentiate's +licentious +licentiously +licentiousness +licentiousness +licentiousness's +lichen +lichens +lichen's +licit +licitly +lick +licking +lickings +licked +licks +lick's +licking +licking's +licorice +licorices +licorice's +lid +lids +lid's +lidded +lidless +lido +lidos +lido's +lie +lied +lies +lie's +lied +lieder +lied's +lief +liefest +liefer +liege +lieges +liege's +lien +liens +lien's +lieu +lieu's +lieutenancy +lieutenancy's +lieutenant +lieutenants +lieutenant's +life +lifer +lifers +life's +lifebelt +lifebelts +lifeblood +lifeblood's +lifeboat +lifeboats +lifeboat's +lifebuoy +lifebuoys +lifebuoy's +lifeforms +lifeguard +lifeguards +lifeguard's +lifeless +lifelessly +lifelessness +lifelessness +lifelessness's +lifelike +lifeline +lifelines +lifeline's +lifelong +lifer +lifer's +lifesaver +lifesavers +lifesaver's +lifesaving +lifesaving's +lifespan +lifespans +lifestyle +lifestyles +lifestyle's +lifetime +lifetimes +lifetime's +lifework +lifeworks +lifework's +lift +lifting +lifted +lifter +lifters +lifts +lift's +lifter +lifter's +liftoff +liftoffs +liftoff's +ligament +ligaments +ligament's +ligate +ligation +ligating +ligated +ligates +ligation +ligation's +ligature +ligaturing +ligatured +ligatures +ligature's +light's +delight's +light +lighting +lighted +lightest +lights +relighting +delighting +relighted +delighted +relights +delights +relight +delight +lighted +unlighted +lighten +lightening +lightened +lightener +lighteners +lightens +lightener +lightener's +lighter +lighters +lighter's +lightface +lightfaced +lightface's +lightheaded +lighthearted +lightheartedly +lightheartedness +lightheartedness +lightheartedness's +lighthouse +lighthouses +lighthouse's +lighting's +lightly +lightness +lightness's +lightning +lightninged +lightnings +lightning's +lightproof +lightship +lightships +lightship's +lightweight +lightweights +lightweight's +ligneous +lignin +lignite +lignite's +lii +likability +likability's +likable +likableness +likableness +likableness's +like +liking +liked +likest +likes +like's +disliking +disliked +dislikes +dislike's +dislike +likelihood +likelihood's +unlikelihood's +unlikelihood +likelihoods +likeliness +likeliness's +unlikeliness's +unlikeliness +likely +likeliest +likelier +likeliness +unlikelier +unlikeliness +unlikely +liken +likening +likened +likens +likeness +likeness's +unlikeness's +unlikeness +likenesses +liker +likewise +liking +liking's +lilac +lilacs +lilac's +lilliputian +lilo +lilos +lilt +lilting +lilted +lilts +lilt's +lily +lilies +lily's +limb +limbs +limb's +limber +limbering +limbered +limbers +unlimbering +unlimbered +unlimbers +unlimber +limberness +limberness's +limbless +limbo +limbos +limbo's +lime +liming +limed +limes +lime's +limeade +limeades +limeade's +limelight +limelight's +limerick +limericks +limerick's +limescale +limestone +limestone's +limey +limeys +limit's +limit +limiting +limited +limiter +limiters +limits +delimiting +delimited +delimiter +delimiters +delimits +delimit +limitation +limitation's +delimitation's +delimitation +limitations +limited +unlimited +limiter's +limiting +limitings +limitless +limitlessness +limitlessness +limitlessness's +limn +limning +limned +limns +limo +limos +limo's +limousine +limousines +limousine's +limp +limply +limping +limped +limpest +limper +limps +limpness +limp's +limpet +limpets +limpet's +limpid +limpidly +limpidness +limpidity +limpidity's +limpidness +limpidness's +limpness +limpness's +limy +limiest +limier +linage +linage's +linchpin +linchpins +linchpin's +linden +lindens +linden's +line +lining +linings +lined +liner +liners +lines +line's +lineage +lineages +lineage's +lineal +lineally +lineament +lineaments +lineament's +linear +linearly +linearity +linearity's +linebacker +linebackers +linebacker's +lined +unlined +linefeed +lineman +lineman's +linemen +linen +linens +linen's +linens +linens's +liner +liner's +linesman +linesman's +linesmen +lineup +lineups +lineup's +ling +ling's +linger +lingering +lingerings +lingered +lingerer +lingerers +lingers +lingerer +lingerer's +lingerie +lingerie's +lingering +lingeringly +lingo +lingo's +lingoes +lingual +linguine +linguine's +linguist +linguists +linguist's +linguistic +linguistics +linguistically +linguistics +linguistics's +liniment +liniments +liniment's +lining +lining's +link +linking +linked +linker +links +link's +linkage +linkages +linkage's +linkman +linkmen +linkup +linkups +linkup's +linnet +linnets +linnet's +lino +linoleum +linoleum's +linseed +linseed's +lint's +lint +linting +linted +delinting +delinted +delint +lintel +lintels +lintel's +lints +linty +lintiest +lintier +lion +lions +lion's +lioness +lionesses +lioness's +lionhearted +lionization +lionization's +lionize +lionizing +lionized +lionizes +lip +lips +lip's +lipid +lipids +lipid's +liposuction +liposuction's +lipped +lippy +lipread +lipreading +lipreader +lipreads +lipreader +lipreader's +lipreading +lipreading's +lipstick +lipsticking +lipsticked +lipsticks +lipstick's +liq +liquefaction +liquefaction's +liquefy +liquefying +liquefied +liquefies +liqueur +liqueurs +liqueur's +liquid +liquids +liquid's +liquidate +liquidation +liquidations +liquidating +liquidated +liquidates +liquidation +liquidation's +liquidator +liquidators +liquidator's +liquidity +liquidity's +liquidize +liquidizing +liquidized +liquidizer +liquidizers +liquidizes +liquidizer +liquidizer's +liquor +liquoring +liquored +liquors +liquor's +lira +lira's +lire +lisle +lisle's +lisp +lisping +lisped +lisper +lispers +lisps +lisp's +lisper +lisper's +lissome +list +listen +listens +listing +listings +listed +lists +list's +listed +unlisted +listen +listening +listened +listener +listeners +listen's +listenable +listener +listener's +listeria +listing +listing's +listless +listlessly +listlessness +listlessness +listlessness's +lit +liter +liters +litany +litanies +litany's +litchi +litchis +litchi's +lite +liter +liter's +literacy +literacy's +literal +literally +literals +literalness +literal's +literalness +literalness's +literariness +literariness's +literary +literariness +literate +literately +literates +literate's +literati +literati's +literature +literature's +lithe +lithely +lithest +lither +litheness +litheness +litheness's +lithesome +lithium +lithium's +lithograph +lithographing +lithographed +lithographer +lithographers +lithograph's +lithographer +lithographer's +lithographic +lithographically +lithographs +lithography +lithography's +lithosphere +lithospheres +lithosphere's +litigant +litigants +litigant's +litigate +litigation +litigating +litigated +litigates +litigation +litigation's +litigator +litigators +litigator's +litigious +litigiousness +litigiousness +litigiousness's +litmus +litmus's +litotes +litotes's +litter +littering +littered +litterer +litterers +litters +litter's +litterateur +litterateurs +litterateur's +litterbug +litterbugs +litterbug's +litterer +litterer's +little +littlest +littler +littleness +little's +littleness +littleness's +littoral +littorals +littoral's +liturgical +liturgically +liturgist +liturgists +liturgist's +liturgy +liturgies +liturgy's +livability +livability's +livable +unlivable +live +living +lived +livest +lives +livable +reliving +relived +relives +relivable +relive +livelihood +livelihoods +livelihood's +liveliness +liveliness's +livelong +livelongs +lively +liveliest +livelier +liveliness +liven +livening +livened +livens +liver's +liver +livers +liveried +liverish +liverwort +liverworts +liverwort's +liverwurst +liverwurst's +livery +liveries +livery's +deliveries +delivery's +delivery +liveryman +liveryman's +deliveryman's +deliveryman +liverymen +deliverymen +livestock +livestock's +liveware +livid +lividly +living +livings +living's +lix +prolix +lizard +lizards +lizard's +ll +llama +llamas +llama's +llano +llanos +llano's +lo +load's +load +loading +loaded +loads +reloading +unloading +reloaded +unloaded +reloads +unloads +reload +unload +loadable +loader +loaders +loader's +loading's +loaf +loafing +loafed +loafer +loafers +loafs +loaf's +loafer +loafer's +loam +loam's +loamy +loamiest +loamier +loan +loaning +loaned +loaner +loaners +loans +loan's +loaner +loaner's +loansharking +loansharking's +loanword +loanwords +loanword's +loath +loathing +loathings +loathed +loather +loathers +loathes +loathe +loather +loather's +loathing +loathing's +loathsome +loathsomely +loathsomeness +loathsomeness +loathsomeness's +loaves +lob +lobed +lobs +lob's +lobar +lobbed +lobber +lobbers +lobber's +lobbing +lobby +lobbying +lobbied +lobbies +lobby's +lobbyist +lobbyists +lobbyist's +lobe +lobes +lobe's +lobotomize +lobotomizing +lobotomized +lobotomizes +lobotomy +lobotomies +lobotomy's +lobster +lobsters +lobster's +local +locally +locals +local's +locale +locales +locale's +locality +localities +locality's +localization +localization's +localize +localizing +localized +localizes +locate +location +locating +located +locates +relocation +dislocation +relocating +dislocating +relocated +dislocated +relocates +dislocates +relocate +dislocate +location's +relocation's +location +locations +location's +dislocations +dislocation's +dislocation +locator +locators +locator's +locavore +locavores +locavore's +loci +lock +locking +locked +locker +lockers +locks +lock's +lockable +locker +locker's +locket +lockets +locket's +lockjaw +lockjaw's +lockout +lockouts +lockout's +locksmith +locksmith's +locksmiths +lockstep +lockstep's +lockup +lockups +lockup's +loco +locos +locomotion +locomotion's +locomotive +locomotives +locomotive's +locoweed +locoweeds +locoweed's +locum +locums +locus +locus's +locust +locusts +locust's +locution +locutions +locution's +lode +lodes +lode's +lodestar +lodestars +lodestar's +lodestone +lodestones +lodestone's +lodge +lodging +lodgings +lodged +lodger +lodgers +lodges +lodge's +lodger +lodger's +lodging +lodging's +lodgings +lodgings's +loft +lofting +lofted +lofts +loft's +loftily +loftiness +loftiness's +lofty +loftiest +loftier +loftiness +log +logs +log's +loganberry +loganberries +loganberry's +logarithm +logarithms +logarithm's +logarithmic +logbook +logbooks +logbook's +loge +loges +loge's +logged +logger +loggers +logger's +loggerhead +loggerheads +loggerhead's +loggia +loggias +loggia's +logging +logging's +logic +logic's +logical +logically +logicality +logicality's +logician +logicians +logician's +login +logins +login's +logistic +logistics +logistical +logistically +logistics +logistics's +logjam +logjams +logjam's +logo +logos +logo's +logoff +logoffs +logoff's +logon +logons +logon's +logotype +logotypes +logotype's +logout +logouts +logout's +logrolling +logrolling's +logy +logiest +logier +loin +loins +loin's +loincloth +loincloth's +loincloths +loiter +loitering +loitered +loiterer +loiterers +loiters +loiterer +loiterer's +loitering +loitering's +lolcat +lolcats +lolcat's +loll +lolling +lolled +lolls +lollipop +lollipops +lollipop's +lollop +lolloping +lolloped +lollops +lolly +lollies +lollygag +lollygags +lollygagged +lollygagging +lone +lonely +loner +loners +loneliness +loneliness's +lonely +loneliest +lonelier +loneliness +loner +loner's +lonesome +lonesomely +lonesomeness +lonesomeness +lonesomeness's +long's +long +longing +longed +longest +longs +prolonging +prolonged +prolongs +prolong +longboat +longboats +longboat's +longbow +longbows +longbow's +longer +longevity +longevity's +longhair +longhairs +longhair's +longhand +longhand's +longhorn +longhorns +longhorn's +longhouse +longhouses +longing +longingly +longings +longing's +longish +longitude +longitudes +longitude's +longitudinal +longitudinally +longshoreman +longshoreman's +longshoremen +longsighted +longstanding +longtime +longueur +longueurs +longueur's +longways +loo +loofah +loofah's +loofahs +look +looking +looked +looker +lookers +looks +look's +lookalike +lookalikes +lookalike's +looker +looker's +lookout +lookouts +lookout's +lookup +loom +looming +loomed +looms +loom's +loon +loons +loon's +loonie +loonie's +loony +looniest +loonier +loonies +loony's +loop +looping +looped +loops +loop's +loophole +loopholes +loophole's +loopy +loopiest +loopier +loos +loosen +loosens +looser +loose +loosing +loosed +loosest +looses +unloosing +unloosed +unlooses +unloose +loosely +loosen +loosening +loosened +loosens +unloosening +unloosened +unloosens +unloosen +looseness +looseness's +loot +looting +looted +looter +looters +loots +loot's +looter +looter's +looting +looting's +lop +lops +lope +loping +loped +lopes +lope's +lopped +lopping +lopsided +lopsidedly +lopsidedness +lopsidedness +lopsidedness's +loquacious +loquaciously +loquaciousness +loquaciousness +loquaciousness's +loquacity +loquacity's +lord +lordly +lording +lorded +lords +lord's +lordliness +lordliness's +lordly +lordliest +lordlier +lordliness +lordship +lordships +lordship's +lore +lore's +lorgnette +lorgnettes +lorgnette's +loris +lorises +loris's +lorn +lorry +lorries +lorry's +lose +losing +losings +loser +losers +loses +loser +loser's +losing +losing's +loss +losses +loss's +lossless +lost +lot +lots +lot's +lotion +lotions +lotion's +lottery +lotteries +lottery's +lotto +lotto's +lotus +lotuses +lotus's +louche +loud +loudly +loudest +louder +loudness +loudhailer +loudhailers +loudhailer's +loudmouth +loudmouthed +loudmouth's +loudmouths +loudness +loudness's +loudspeaker +loudspeakers +loudspeaker's +lough +loughs +lounge +lounging +lounged +lounger +loungers +lounges +lounge's +lounger +lounger's +lour +louring +loured +lours +louse's +louse +lousing +loused +louses +delousing +deloused +delouses +delouse +lousily +lousiness +lousiness's +lousy +lousiest +lousier +lousiness +lout +louts +lout's +loutish +loutishly +loutishness +louver +louvered +louvers +louver's +lovableness +lovableness's +lovably +love +lovely +loving +loved +lover +lovers +loves +love's +lovable +lovebird +lovebirds +lovebird's +lovechild +lovechild's +loved +unloved +loveless +loveliness +loveliness's +lovelorn +lovely +loveliest +lovelier +lovelies +loveliness +lovely's +lovemaking +lovemaking's +lover +lover's +lovesick +lovey +loveys +loving +lovingly +low +lowly +lowing +lowed +lowest +lower +lowers +lows +lowness +low's +lowborn +lowboy +lowboys +lowboy's +lowbrow +lowbrows +lowbrow's +lowdown +lowdown's +lower +lowering +lowered +lowercase +lowercase's +lowermost +lowish +lowland +lowlander +lowlanders +lowlands +lowland's +lowlander +lowlander's +lowlife +lowlifes +lowlife's +lowliness +lowliness's +lowly +lowliest +lowlier +lowliness +lowness +lowness's +lox +lox's +loyal +loyally +loyalest +disloyally +disloyal +loyaler +loyalism +loyalism's +loyalist +loyalists +loyalist's +loyalties +loyalty +loyalty's +disloyalty's +disloyalty +lozenge +lozenges +lozenge's +ltd +luau +luaus +luau's +lubber +lubberly +lubbers +lubber's +lube +lubing +lubed +lubes +lube's +lubricant +lubricants +lubricant's +lubricate +lubrication +lubricating +lubricated +lubricates +lubrication +lubrication's +lubricator +lubricators +lubricator's +lubricious +lubriciously +lubricity +lubricity's +lucid +lucidly +lucidness +lucidity +lucidity's +lucidness +lucidness's +luck +lucking +lucked +lucks +luck's +luckily +unluckily +luckiness +luckiness's +unluckiness's +unluckiness +luckless +lucky +luckiest +luckier +luckiness +unluckier +unluckiness +unlucky +lucrative +lucratively +lucrativeness +lucrativeness +lucrativeness's +lucre +lucre's +lucubrate +lucubration +lucubrating +lucubrated +lucubrates +lucubration +lucubration's +ludicrous +ludicrously +ludicrousness +ludicrousness +ludicrousness's +ludo +luff +luffing +luffed +luffs +lug +lugs +lug's +luge +luges +luggage +luggage's +lugged +lugger +luggers +lugger's +lugging +lughole +lugholes +lugsail +lugsails +lugsail's +lugubrious +lugubriously +lugubriousness +lugubriousness +lugubriousness's +lukewarm +lukewarmly +lukewarmness +lukewarmness +lukewarmness's +lull +lulling +lulled +lulls +lull's +lullaby +lullabies +lullaby's +lulu +lulus +lumbago +lumbago's +lumbar +lumber +lumbering +lumbered +lumberer +lumberers +lumbers +lumber's +lumberer +lumberer's +lumbering +lumbering's +lumberjack +lumberjacks +lumberjack's +lumberman +lumberman's +lumbermen +lumberyard +lumberyards +lumberyard's +lumen +luminary +luminaries +luminary's +luminescence +luminescence's +luminescent +luminosity +luminosity's +luminous +luminously +lummox +lummoxes +lummox's +lump +lumpen +lumping +lumped +lumps +lump's +lumpectomy +lumpectomies +lumpiness +lumpiness's +lumpish +lumpy +lumpiest +lumpier +lumpiness +lunacy +lunacies +lunacy's +lunar +lunatic +lunatics +lunatic's +lunch +lunching +lunched +lunches +lunch's +lunchbox +lunchboxes +luncheon +luncheons +luncheon's +luncheonette +luncheonettes +luncheonette's +lunchroom +lunchrooms +lunchroom's +lunchtime +lunchtimes +lunchtime's +lung +lunging +lunged +lungs +lung's +lunge +lunges +lunge's +lungfish +lungfishes +lungfish's +lungful +lungfuls +lunkhead +lunkheads +lunkhead's +lupine +lupines +lupine's +lupus +lupus's +lurch +lurching +lurched +lurches +lurch's +lure +luring +lured +lures +lure's +lurgy +lurid +luridly +luridness +luridness +luridness's +lurk +lurking +lurked +lurker +lurkers +lurks +luscious +lusciously +lusciousness +lusciousness +lusciousness's +lush +lushly +lushest +lusher +lushes +lushness +lush's +lushness +lushness's +lust +lusting +lusted +luster +lusts +lust's +luster +luster's +lusterless +lustful +lustfully +lustily +lustiness +lustiness's +lustrous +lustrously +lusty +lustiest +lustier +lustiness +lutanist +lutanists +lutanist's +lute +lutes +lute's +lutenist +lutenists +lutenist's +lutetium +lutetium's +lux +luxuriance +luxuriance's +luxuriant +luxuriantly +luxuriate +luxuriation +luxuriating +luxuriated +luxuriates +luxuriation +luxuriation's +luxurious +luxuriously +luxuriousness +luxuriousness +luxuriousness's +luxury +luxuries +luxury's +lvi +lvii +lxi +lxii +lxiv +lxix +lxvi +lxvii +lyceum +lyceums +lyceum's +lychgate +lychgates +lye +lying +lye's +lying +lying's +lymph +lymph's +lymphatic +lymphatics +lymphatic's +lymphocyte +lymphocytes +lymphocyte's +lymphoid +lymphoma +lymphomas +lymphoma's +lynch +lynching +lynchings +lynched +lyncher +lynchers +lynches +lyncher +lyncher's +lynching +lynching's +lynx +lynxes +lynx's +lyre +lyres +lyre's +lyrebird +lyrebirds +lyrebird's +lyric +lyrics +lyric's +lyrical +lyrically +lyricism +lyricism's +lyricist +lyricists +lyricist's +lysosomal +lysosomes +m +ms +rems +proms +rem +prom +ma'am +ma +math +mas +ma's +mac +macing +maced +macs +mac's +macabre +macadam +macadam's +macadamia +macadamias +macadamia's +macadamize +macadamizing +macadamized +macadamizes +macaque +macaques +macaque's +macaroni +macaronis +macaroni's +macaroon +macaroons +macaroon's +macaw +macaws +macaw's +mace +maces +mace's +macerate +maceration +macerating +macerated +macerates +maceration +maceration's +mach +mach's +machete +machetes +machete's +machinate +machination +machinations +machinating +machinated +machinates +machination +machination's +machine +machining +machined +machines +machine's +machinable +machinery +machinery's +machinist +machinists +machinist's +machismo +machismo's +macho +macho's +mackerel +mackerels +mackerel's +mackinaw +mackinaws +mackinaw's +mackintosh +mackintoshes +mackintosh's +macrame +macrame's +macro +macros +macro's +macrobiotic +macrobiotics +macrobiotics +macrobiotics's +macrocosm +macrocosms +macrocosm's +macroeconomic +macroeconomics +macroeconomics +macroeconomics's +macrology +macrologies +macron +macrons +macron's +macrophages +macroscopic +mad +madly +mads +madness +mad's +madam +madams +madam's +madame +madame's +madcap +madcaps +madcap's +madden +maddening +maddened +maddens +maddening +maddeningly +madder +madders +madder's +maddest +madding +made +remade +unmade +mademoiselle +mademoiselles +mademoiselle's +madhouse +madhouses +madhouse's +madman +madman's +madmen +madness +madness's +madras +madrases +madras's +madrasa +madrasas +madrasa's +madrasah +madrasah's +madrasahs +madrassa +madrassas +madrassa's +madrigal +madrigals +madrigal's +madwoman +madwoman's +madwomen +maelstrom +maelstroms +maelstrom's +maestro +maestros +maestro's +mafia +mafias +mafia's +mafiosi +mafioso +mafioso's +mag +mags +mag's +magazine +magazines +magazine's +mage +mages +mage's +magenta +magenta's +maggot +maggots +maggot's +maggoty +magi +magi's +magic +magics +magic's +magical +magically +magician +magicians +magician's +magicked +magicking +magisterial +magisterially +magistracy +magistracy's +magistrate +magistrates +magistrate's +magma +magma's +magnanimity +magnanimity's +magnanimous +magnanimously +magnate +magnates +magnate's +magnesia +magnesia's +magnesium +magnesium's +magnet +magnets +magnet's +magnetic +magnetically +magnetism +magnetism's +magnetite +magnetite's +magnetizable +magnetization +magnetization's +demagnetization's +demagnetization +magnetize +magnetizing +magnetized +magnetizes +demagnetizing +demagnetized +demagnetizes +demagnetize +magneto +magnetos +magneto's +magnetometer +magnetometers +magnetometer's +magnetosphere +magnification +magnification's +magnificence +magnificence's +magnificent +magnificently +magnifier +magnifier's +magnify +magnification +magnifications +magnifying +magnified +magnifier +magnifiers +magnifies +magniloquence +magniloquence's +magniloquent +magnitude +magnitudes +magnitude's +magnolia +magnolias +magnolia's +magnon +magnum +magnums +magnum's +magpie +magpies +magpie's +magus +magus's +maharajah +maharajah's +maharajahs +maharani +maharanis +maharani's +maharishi +maharishis +maharishi's +mahatma +mahatmas +mahatma's +mahogany +mahoganies +mahogany's +mahout +mahouts +mahout's +maid +maiden +maidens +maids +maid's +maiden +maidenly +maiden's +maidenhair +maidenhair's +maidenhead +maidenheads +maidenhead's +maidenhood +maidenhood's +maidservant +maidservants +maidservant's +mail +mailing +mailings +mailed +mailer +mailers +mails +mail's +mailbag +mailbags +mailbag's +mailbomb +mailbombing +mailbombed +mailbombs +mailbox +mailboxes +mailbox's +mailer +mailer's +mailing +mailing's +maillot +maillots +maillot's +mailman +mailman's +mailmen +mailshot +mailshots +maim +maiming +maimed +maims +main +mainly +mains +main's +mainframe +mainframes +mainframe's +mainland +mainlands +mainland's +mainline +mainlining +mainlined +mainlines +mainline's +mainmast +mainmasts +mainmast's +mainsail +mainsails +mainsail's +mainspring +mainsprings +mainspring's +mainstay +mainstays +mainstay's +mainstream +mainstreaming +mainstreamed +mainstreams +mainstream's +maintain +maintaining +maintained +maintainer +maintainers +maintains +maintainable +maintainability +maintainable +unmaintainable +maintained +unmaintained +maintenance +maintenance's +maintop +maintops +maintop's +maisonette +maisonettes +maisonette's +maize +maizes +maize's +majestic +majestically +majesty +majesties +majesty's +majolica +majolica's +major +majorly +majoring +majored +majors +major's +majordomo +majordomos +majordomo's +majorette +majorettes +majorette's +majoritarian +majoritarians +majoritarian's +majoritarianism +majority +majorities +majority's +make's +remake's +make +making +makes +remaking +unmaking +remakes +unmakes +remake +unmake +makeover +makeovers +makeover's +maker +makers +maker's +makeshift +makeshifts +makeshift's +makeup +makeups +makeup's +makeweight +makeweights +making +makings +making's +makings +makings's +malachite +malachite's +maladjusted +maladjustment +maladjustment's +maladministration +maladroit +maladroitly +maladroitness +maladroitness +maladroitness's +malady +maladies +malady's +malaise +malaise's +malamute +malamutes +malamute's +malapropism +malapropisms +malapropism's +malaria +malaria's +malarial +malarkey +malarkey's +malathion +malathion's +malcontent +malcontents +malcontent's +male +males +maleness +male's +malediction +maledictions +malediction's +malefaction +malefaction's +malefactor +malefactors +malefactor's +malefic +maleficence +maleficence's +maleficent +maleness +maleness's +malevolence +malevolence's +malevolent +malevolently +malfeasance +malfeasance's +malformation +malformations +malformation's +malformed +malfunction +malfunctioning +malfunctioned +malfunctions +malfunction's +malice +malice's +malicious +maliciously +maliciousness +maliciousness +maliciousness's +malign +maligning +maligned +maligns +malignancy +malignancies +malignancy's +malignant +malignantly +malignity +malignity's +malinger +malingering +malingered +malingerer +malingerers +malingers +malingerer +malingerer's +mall +malls +mall's +mallard +mallards +mallard's +malleability +malleability's +malleable +mallet +mallets +mallet's +mallow +mallows +mallow's +malnourished +malnutrition +malnutrition's +malocclusion +malocclusion's +malodorous +malpractice +malpractices +malpractice's +malt +malting +malted +malts +malt's +malted +malteds +malted's +maltose +maltose's +maltreat +maltreating +maltreated +maltreats +maltreatment +maltreatment +maltreatment's +malty +maltiest +maltier +malware +malware's +mam +mams +mama +mamas +mama's +mamba +mambas +mamba's +mambo +mamboing +mamboed +mambos +mambo's +mamma +mamma's +mammal +mammals +mammal's +mammalian +mammalians +mammalian's +mammary +mammogram +mammograms +mammogram's +mammography +mammography's +mammon +mammon's +mammoth +mammoth's +mammoths +mammy +mammies +mammy's +man's +conman's +man +manly +mans +unmanly +unmans +unman +manacle +manacling +manacled +manacles +manacle's +manage +managing +managed +manager +managers +manages +management +manageability +manageability's +manageable +unmanageable +management +managements +management's +manager +manager's +manageress +manageresses +managerial +manana +mananas +manana's +manatee +manatees +manatee's +mandala +mandalas +mandala's +mandamus +mandamuses +mandamus's +mandarin +mandarins +mandarin's +mandate +mandating +mandated +mandates +mandate's +mandatory +mandible +mandibles +mandible's +mandibular +mandolin +mandolins +mandolin's +mandrake +mandrakes +mandrake's +mandrel +mandrels +mandrel's +mandrill +mandrills +mandrill's +mane +maned +manes +mane's +manege +manege's +maneuver +maneuvering +maneuverings +maneuvered +maneuvers +maneuver's +maneuverable +maneuverability +maneuverability's +manful +manfully +manga +manga's +manganese +manganese's +mange +manged +manger +mangers +mange's +manger +manger's +mangetout +mangetouts +manginess +manginess's +mangle +mangling +mangled +mangler +manglers +mangles +mangle's +mango +mango's +mangoes +mangrove +mangroves +mangrove's +mangy +mangiest +mangier +manginess +manhandle +manhandling +manhandled +manhandles +manhole +manholes +manhole's +manhood +manhood's +manhunt +manhunts +manhunt's +mania +manias +mania's +maniac +maniacs +maniac's +maniacal +maniacally +manic +manics +manic's +manically +manicure +manicuring +manicured +manicures +manicure's +manicurist +manicurists +manicurist's +manifest +manifestly +manifesting +manifested +manifests +manifest's +manifestation +manifestations +manifestation's +manifesto +manifestos +manifesto's +manifold +manifolding +manifolded +manifolds +manifold's +manikin +manikins +manikin's +manila +manila's +manioc +maniocs +manioc's +manipulable +manipulate +manipulative +manipulation +manipulations +manipulating +manipulated +manipulates +manipulation +manipulation's +manipulative +manipulatively +manipulator +manipulators +manipulator's +mankind +mankind's +manky +manlike +manliness +manliness's +manly +manliest +manlier +unmanlier +unmanly +manna +manna's +manned +unmanned +mannequin +mannequins +mannequin's +manner +mannerly +mannered +manners +manner's +mannerism +mannerisms +mannerism's +mannerly +unmannerly +manning +unmanning +mannish +mannishly +mannishness +mannishness +mannishness's +manometer +manometers +manometer's +manor +manors +manor's +manorial +manpower +manpower's +manque +mansard +mansards +mansard's +manse +mansion +mansions +manses +manse's +manservant +manservant's +mansion +mansion's +manslaughter +manslaughter's +manta +mantas +manta's +mantel +mantels +mantel's +mantelpiece +mantelpieces +mantelpiece's +mantelshelf +mantelshelves +mantes +mantilla +mantillas +mantilla's +mantis +mantises +mantis's +mantissa +mantissas +mantissa's +mantle's +mantle +mantling +mantled +mantles +dismantling +dismantled +dismantles +dismantle +mantra +mantras +mantra's +manual +manually +manuals +manual's +manufacture +manufacturing +manufactured +manufacturer +manufacturers +manufactures +manufacture's +manufacturer +manufacturer's +manufacturing +manufacturing's +manumission +manumissions +manumission's +manumit +manumits +manumitted +manumitting +manure +manuring +manured +manures +manure's +manuscript +manuscripts +manuscript's +many +many's +map's +map +maps +remaps +remap +maple +maples +maple's +mapmaker +mapmakers +mapmaker's +mapped +remapped +mapper +mappers +mapper's +mapping +mappings +mar +mars +marabou +marabous +marabou's +marabout +marabouts +marabout's +maraca +maracas +maraca's +maraschino +maraschinos +maraschino's +marathon +marathoner +marathoners +marathons +marathon's +marathoner +marathoner's +maraud +marauding +marauded +marauder +marauders +marauds +marauder +marauder's +marble +marbling +marbled +marbles +marble's +marbleize +marbleizing +marbleized +marbleizes +marbling +marbling's +march +marching +marched +marcher +marchers +marches +march's +marcher +marcher's +marchioness +marchionesses +marchioness's +mare +mares +mare's +margarine +margarine's +margarita +margaritas +margarita's +marge +margin +margins +margin's +marginal +marginally +marginals +marginalia +marginalia's +marginalization +marginalization's +marginalize +marginalizing +marginalized +marginalizes +maria +maria's +mariachi +mariachis +mariachi's +marigold +marigolds +marigold's +marijuana +marijuana's +marimba +marimbas +marimba's +marina +marinas +marina's +marinade +marinading +marinaded +marinades +marinade's +marinara +marinara's +marinate +marination +marinating +marinated +marinates +marination +marination's +marine +mariner +mariners +marines +marine's +mariner +mariner's +marionette +marionettes +marionette's +marital +maritally +maritime +marjoram +marjoram's +mark +marking +marked +marks +mark's +remarking +remarked +remarks +remark's +remark +markdown +markdowns +markdown's +marked +unmarked +markedly +marker +markers +marker's +market +marketing +marketed +marketer +marketers +markets +market's +marketable +marketability +marketability's +marketable +unmarketable +marketeer +marketeers +marketeer's +marketer +marketer's +marketing +marketing's +marketplace +marketplaces +marketplace's +marking +markings +marking's +markka +markka's +markkaa +marksman +marksman's +marksmanship +marksmanship's +marksmen +markup +markups +markup's +marl +marl's +marlin +marlins +marlin's +marlinespike +marlinespikes +marlinespike's +marmalade +marmalade's +marmoreal +marmoset +marmosets +marmoset's +marmot +marmots +marmot's +maroon +marooning +marooned +maroons +maroon's +marque +marques +marque's +marquee +marquees +marquee's +marquess +marquesses +marquess's +marquetry +marquetry's +marquis +marquises +marquis's +marquise +marquise's +marquisette +marquisette's +marred +unmarred +marriage +marriages +marriage's +remarriages +remarriage's +remarriage +marriageability +marriageability's +marriageable +married +marrieds +married's +marring +marrow +marrows +marrow's +marry +marrying +married +marries +remarrying +remarried +remarries +remarry +marsh +marshes +marsh's +marshal +marshaling +marshaled +marshals +marshal's +marshland +marshlands +marshland's +marshmallow +marshmallows +marshmallow's +marshy +marshiest +marshier +marsupial +marsupials +marsupial's +mart +marten +martens +marts +mart's +marten +marten's +martensite +martial +martially +martian +martians +martin +martins +martin's +martinet +martinets +martinet's +martingale +martingales +martingale's +martini +martinis +martini's +martyr +martyring +martyred +martyrs +martyr's +martyrdom +martyrdom's +marvel +marveling +marveled +marvels +marvel's +marvelous +marvelously +marzipan +marzipan's +masc +mascara +mascaraing +mascaraed +mascaras +mascara's +mascot +mascots +mascot's +masculine +masculines +masculine's +masculinity +masculinity's +maser +masers +maser's +mash +mashing +mashed +masher +mashers +mashes +mash's +masher +masher's +mashup +mashups +mashup's +mask's +mask +masking +masked +masks +unmasking +unmasked +unmasks +unmask +masker +maskers +masker's +masochism +masochism's +masochist +masochists +masochist's +masochistic +masochistically +mason +masons +mason's +masonic +masonry +masonry's +masque +masques +masque's +masquerade +masquerading +masqueraded +masquerader +masqueraders +masquerades +masquerade's +masquerader +masquerader's +mass +massive +massing +massed +masses +mass's +massacre +massacring +massacred +massacres +massacre's +massage +massaging +massaged +massages +massage's +masseur +masseurs +masseur's +masseuse +masseuses +masseuse's +massif +massifs +massif's +massive +massively +massiveness +massiveness +massiveness's +mast +masted +masts +mast's +mastectomy +mastectomies +mastectomy's +master's +master +mastering +mastered +masters +remastering +remastered +remasters +remaster +masterclass +masterclasses +masterful +masterfully +masterly +mastermind +masterminding +masterminded +masterminds +mastermind's +masterpiece +masterpieces +masterpiece's +masterstroke +masterstrokes +masterstroke's +masterwork +masterworks +masterwork's +mastery +mastery's +masthead +mastheads +masthead's +mastic +mastic's +masticate +mastication +masticating +masticated +masticates +mastication +mastication's +mastiff +mastiffs +mastiff's +mastitis +mastodon +mastodons +mastodon's +mastoid +mastoids +mastoid's +masturbate +masturbation +masturbating +masturbated +masturbates +masturbation +masturbation's +masturbatory +mat +mating +mated +mater +maters +mats +mat's +matador +matadors +matador's +match +matches +match's +rematches +rematch's +rematch +matchbook +matchbooks +matchbook's +matchbox +matchboxes +matchbox's +matched +unmatched +matching +matchless +matchlock +matchlocks +matchlock's +matchmaker +matchmakers +matchmaker's +matchmaking +matchmaking's +matchstick +matchsticks +matchstick's +matchwood +matchwood's +mate +mates +mate's +material +materially +materials +material's +materialism +materialism's +materialist +materialists +materialist's +materialistic +materialistically +materialization +materialization's +materialize +materializing +materialized +materializes +materiel +materiel's +maternal +maternally +maternity +maternity's +matey +mateys +mathematical +mathematically +mathematician +mathematicians +mathematician's +mathematics +mathematics's +matinee +matinees +matinee's +mating +mating's +matins +matins's +matriarch +matriarch's +matriarchal +matriarchs +matriarchy +matriarchies +matriarchy's +matrices +matricidal +matricide +matricides +matricide's +matriculate +matriculation +matriculating +matriculated +matriculates +matriculation +matriculation's +matrimonial +matrimony +matrimony's +matrix +matrix's +matron +matronly +matrons +matron's +matte +matting +matted +matter +matters +mattes +matte's +matter +mattering +mattered +matter's +matting +matting's +mattock +mattocks +mattock's +mattress +mattresses +mattress's +maturate +maturation +maturating +maturated +maturates +maturation +maturation's +mature +maturely +maturing +matured +maturest +maturer +matures +maturity +maturities +maturity's +matzo +matzoth +matzos +matzo's +matzoh +matzoh's +matzohs +matzot +maudlin +maul +mauling +mauled +mauler +maulers +mauls +maul's +mauler +mauler's +maunder +maundering +maundered +maunders +mausoleum +mausoleums +mausoleum's +mauve +mauve's +maven +mavens +maven's +maverick +mavericks +maverick's +maw +maws +maw's +mawkish +mawkishly +mawkishness +mawkishness +mawkishness's +max +maxing +maxed +maxes +max's +maxi +maxis +maxi's +maxilla +maxilla's +maxillae +maxillary +maxim +maxims +maxim's +maxima +maximal +maximally +maximization +maximization's +maximize +maximizing +maximized +maximizes +maximum +maximums +maximum's +may +may's +maybe +maybes +maybe's +mayday +maydays +mayday's +mayflower +mayflowers +mayflower's +mayfly +mayflies +mayfly's +mayhem +mayhem's +mayn't +mayo +mayo's +mayonnaise +mayonnaise's +mayor +mayors +mayor's +mayoral +mayoralty +mayoralty's +mayoress +mayoresses +mayoress's +maypole +maypoles +maypole's +mayst +maze +mazes +maze's +mazurka +mazurkas +mazurka's +mdse +me +meth +med +mes +mead +mead's +meadow +meadows +meadow's +meadowlark +meadowlarks +meadowlark's +meager +meagerly +meagerness +meagerness +meagerness's +meal +meals +meal's +mealiness +mealiness's +mealtime +mealtimes +mealtime's +mealy +mealiest +mealier +mealiness +mealybug +mealybugs +mealybug's +mealymouthed +mean +meanly +meaning +meanings +meanest +meaner +means +meanness +mean's +meander +meandering +meanderings +meandered +meanders +meander's +meanderings +meanderings's +meanie +meanie's +meaning +meaning's +meaningful +meaningfully +meaningfulness +meaningfulness +meaningfulness's +meaningless +meaninglessly +meaninglessness +meaninglessness +meaninglessness's +meanness +meanness's +meant +unmeant +meantime +meantime's +meanwhile +meanwhile's +meany +meanies +meany's +meas +measles +measles's +measly +measliest +measlier +measurable +measurably +measure's +measure +measuring +measured +measures +remeasuring +remeasured +remeasures +remeasure +measured +unmeasured +measureless +measurement +measurements +measurement's +meat +meats +meat's +meatball +meatballs +meatball's +meathead +meatheads +meathead's +meatiness +meatiness's +meatless +meatloaf +meatloaf's +meatloaves +meatpacking +meatpacking's +meaty +meatiest +meatier +meatiness +mecca +meccas +mecca's +mechanic +mechanics +mechanic's +mechanical +mechanically +mechanics +mechanics's +mechanism +mechanisms +mechanism's +mechanistic +mechanistically +mechanization +mechanization's +mechanize +mechanizing +mechanized +mechanizes +medal +medals +medal's +medalist +medalists +medalist's +medallion +medallions +medallion's +meddle +meddling +meddled +meddler +meddlers +meddles +meddler +meddler's +meddlesome +media +medias +media's +medial +medially +remedially +remedial +median +medians +median's +mediate +mediation +mediating +mediated +mediates +remediation +remediating +remediated +remediates +remediate +mediated +unmediated +mediation +mediation's +remediation's +remediation +mediator +mediators +mediator's +medic +medics +medic's +medicaid +medicaid's +medical +medically +medicals +medical's +medicament +medicament's +medicare +medicare's +medicate +medication +medications +medicating +medicated +medicates +medication +medication's +medicinal +medicinally +medicine +medicines +medicine's +medico +medicos +medico's +medieval +medievalist +medievalists +medievalist's +mediocre +mediocrity +mediocrities +mediocrity's +meditate +meditative +meditation +meditations +meditating +meditated +meditates +meditation +meditation's +meditative +meditatively +medium +mediums +medium's +medley +medleys +medley's +medulla +medullas +medulla's +medusa +medusae +meed +meed's +meek +meekly +meekest +meeker +meekness +meekness +meekness's +meerschaum +meerschaums +meerschaum's +meet +meeting +meetings +meets +meet's +meeting +meeting's +meetinghouse +meetinghouses +meetinghouse's +meetup +meetups +meetup's +meg +megs +mega +megabit +megabits +megabit's +megabucks +megabucks's +megabyte +megabytes +megabyte's +megachurch +megachurches +megachurch's +megacycle +megacycles +megacycle's +megadeath +megadeath's +megadeaths +megahertz +megahertz's +megalith +megalith's +megalithic +megaliths +megalomania +megalomania's +megalomaniac +megalomaniacs +megalomaniac's +megalopolis +megalopolises +megalopolis's +megaphone +megaphoning +megaphoned +megaphones +megaphone's +megapixel +megapixels +megapixel's +megastar +megastars +megaton +megatons +megaton's +megawatt +megawatts +megawatt's +meh +meiosis +meiosis's +meiotic +melamine +melamine's +melancholia +melancholia's +melancholic +melancholics +melancholy +melancholy's +melange +melanges +melange's +melanin +melanin's +melanoma +melanomas +melanoma's +meld +melding +melded +melds +meld's +melee +melees +melee's +meliorate +meliorative +melioration +meliorating +meliorated +meliorates +melioration +melioration's +mellifluous +mellifluously +mellifluousness +mellifluousness +mellifluousness's +mellow +mellowly +mellowing +mellowed +mellowest +mellower +mellows +mellowness +mellowness +mellowness's +melodic +melodically +melodious +melodiously +melodiousness +melodiousness +melodiousness's +melodrama +melodramas +melodrama's +melodramatic +melodramatics +melodramatically +melodramatics +melodramatics's +melody +melodies +melody's +melon +melons +melon's +melt's +melt +melting +melted +melts +remelting +remelted +remelts +remelt +meltdown +meltdowns +meltdown's +member's +member +members +remembers +dismembers +remember +dismember +membership +memberships +membership's +membrane +membranes +membrane's +membranous +meme +memes +meme's +memento +mementos +memento's +memo +memos +memo's +memoir +memoirs +memoir's +memorabilia +memorabilia's +memorability +memorability's +memorable +unmemorable +memorably +memorandum +memorandums +memorandum's +memorial +memorials +memorial's +memorialize +memorializing +memorialized +memorializes +memorization +memorization's +memorize +memorizing +memorized +memorizes +memory +memories +memory's +memsahib +memsahibs +men +men's +menace +menacing +menaced +menaces +menace's +menacing +menacingly +menage +menages +menage's +menagerie +menageries +menagerie's +mend +mending +mended +mender +menders +mends +mend's +mendacious +mendaciously +mendacity +mendacity's +mendelevium +mendelevium's +mender +mender's +mendicancy +mendicancy's +mendicant +mendicants +mendicant's +mending +mending's +menfolk +menfolks +menfolk's +menfolks +menfolks's +menhaden +menhaden's +menial +menially +menials +menial's +meningeal +meninges +meningitis +meningitis's +meninx +meninx's +menisci +meniscus +meniscus's +menopausal +menopause +menopause's +menorah +menorah's +menorahs +mensch +mensches +mensch's +menservants +menses +menses's +menstrual +menstruate +menstruation +menstruating +menstruated +menstruates +menstruation +menstruation's +mensurable +mensuration +mensuration's +menswear +menswear's +mental +mentally +mentalist +mentalists +mentalist's +mentality +mentalities +mentality's +menthol +menthol's +mentholated +mention +mentioning +mentioned +mentions +mention's +mentioned +unmentioned +mentor +mentoring +mentored +mentors +mentor's +menu +menus +menu's +meow +meowing +meowed +meows +meow's +mercantile +mercantilism +mercantilism's +mercenary +mercenaries +mercenary's +mercer +mercers +mercer's +mercerize +mercerizing +mercerized +mercerizes +merchandise +merchandising +merchandised +merchandiser +merchandisers +merchandises +merchandise's +merchandiser +merchandiser's +merchandising +merchandising's +merchant +merchants +merchant's +merchantable +merchantman +merchantman's +merchantmen +merciful +mercifully +unmercifully +unmerciful +merciless +mercilessly +mercilessness +mercilessness +mercilessness's +mercurial +mercurially +mercuric +mercury +mercury's +mercy +mercies +mercy's +mere +merely +merest +meres +mere's +meretricious +meretriciously +meretriciousness +meretriciousness +meretriciousness's +merganser +mergansers +merganser's +merge +merging +merged +merger +mergers +merges +merger +merger's +meridian +meridians +meridian's +meringue +meringues +meringue's +merino +merinos +merino's +merit +merits +merit's +demerits +demerit's +demerit +merited +unmerited +meriting +meritocracy +meritocracies +meritocracy's +meritocratic +meritorious +meritoriously +meritoriousness +meritoriousness +meritoriousness's +mermaid +mermaids +mermaid's +merman +merman's +mermen +merrily +merriment +merriment's +merriness +merriness's +merry +merriest +merrier +merriness +merrymaker +merrymakers +merrymaker's +merrymaking +merrymaking's +mesa +mesas +mesa's +mescal +mescals +mescal's +mescalin +mescaline +mescaline's +mesdames +mesdemoiselles +mesh +meshing +meshed +meshes +mesh's +mesmeric +mesmerism +mesmerism's +mesmerize +mesmerizing +mesmerized +mesmerizer +mesmerizers +mesmerizes +mesmerizer +mesmerizer's +mesomorph +mesomorph's +mesomorphs +meson +mesons +meson's +mesosphere +mesospheres +mesosphere's +mesquite +mesquites +mesquite's +mess +messing +messed +messes +mess's +message +messaging +messaged +messages +message's +messeigneurs +messenger +messengers +messenger's +messiah +messiah's +messiahs +messianic +messieurs +messily +messiness +messiness's +messmate +messmates +messmate's +messy +messiest +messier +messiness +mestizo +mestizos +mestizo's +met +meta +metabolic +metabolically +metabolism +metabolisms +metabolism's +metabolite +metabolites +metabolite's +metabolize +metabolizing +metabolized +metabolizes +metacarpal +metacarpals +metacarpal's +metacarpi +metacarpus +metacarpus's +metadata +metal +metaled +metals +metal's +metalanguage +metalanguages +metalanguage's +metallic +metallurgic +metallurgical +metallurgist +metallurgists +metallurgist's +metallurgy +metallurgy's +metalwork +metalworking +metalworker +metalworkers +metalwork's +metalworker +metalworker's +metalworking +metalworking's +metamorphic +metamorphism +metamorphism's +metamorphose +metamorphosing +metamorphosed +metamorphoses +metamorphosis +metamorphosis's +metaphor +metaphors +metaphor's +metaphoric +metaphorical +metaphorically +metaphysical +metaphysically +metaphysics +metaphysics's +metastases +metastasis +metastasis's +metastasize +metastasizing +metastasized +metastasizes +metastatic +metatarsal +metatarsals +metatarsal's +metatarsi +metatarsus +metatarsus's +metatheses +metathesis +metathesis's +mete +meting +meted +meter +meters +metes +mete's +metempsychoses +metempsychosis +metempsychosis's +meteor +meteors +meteor's +meteoric +meteorically +meteorite +meteorites +meteorite's +meteoroid +meteoroids +meteoroid's +meteorologic +meteorological +meteorologist +meteorologists +meteorologist's +meteorology +meteorology's +meter +metering +metered +meter's +methadone +methadone's +methamphetamine +methamphetamine's +methane +methane's +methanol +methanol's +methinks +method +methods +method's +methodical +methodically +methodicalness +methodicalness +methodicalness's +methodological +methodologically +methodology +methodologies +methodology's +methotrexate +methought +meths +methyl +methyl's +meticulous +meticulously +meticulousness +meticulousness +meticulousness's +metier +metiers +metier's +metric +metrics +metrical +metrically +metricate +metrication +metricating +metricated +metricates +metrication +metrication's +metricize +metricizing +metricized +metricizes +metro +metros +metro's +metronome +metronomes +metronome's +metropolis +metropolises +metropolis's +metropolitan +mettle +mettle's +mettlesome +mew +mewing +mewed +mews +mew's +mewl +mewling +mewled +mewls +mews +mews's +mezzanine +mezzanines +mezzanine's +mezzo +mezzos +mezzo's +mfg +mfr +mfrs +mg +mgr +mi +mien +miens +mi's +miasma +miasmas +miasma's +mic +mics +mica +mica's +mice +mick +micks +mickey +mickeys +mickey's +micro +micros +micro's +microaggression +microaggressions +microaggression's +microbe +microbes +microbe's +microbial +microbiological +microbiologist +microbiologists +microbiologist's +microbiology +microbiology's +microbrewery +microbreweries +microbrewery's +microchip +microchips +microchip's +microcircuit +microcircuits +microcircuit's +microcode +microcomputer +microcomputers +microcomputer's +microcosm +microcosms +microcosm's +microcosmic +microdot +microdots +microdot's +microeconomics +microeconomics's +microelectronic +microelectronics +microelectronics +microelectronics's +microfiber +microfibers +microfiber's +microfiche +microfiche's +microfilm +microfilming +microfilmed +microfilms +microfilm's +microfinance +microfloppies +microgroove +microgrooves +microgroove's +microlight +microlights +microlight's +microloan +microloans +microloan's +micromanage +micromanaging +micromanaged +micromanager +micromanagers +micromanages +micromanagement +micromanagement +micromanagement's +micromanager +micromanager's +micrometeorite +micrometeorites +micrometeorite's +micrometer +micrometers +micrometer's +micron +microns +micron's +microorganism +microorganisms +microorganism's +microphone +microphones +microphone's +microprocessor +microprocessors +microprocessor's +microscope +microscopes +microscope's +microscopic +microscopical +microscopically +microscopy +microscopy's +microsecond +microseconds +microsecond's +microsurgery +microsurgery's +microwave +microwaving +microwaved +microwaves +microwave's +microwavable +microwaveable +mid +midair +midair's +midday +midday's +midden +middens +midden's +middle +middling +middles +middle's +middlebrow +middlebrows +middlebrow's +middleman +middleman's +middlemen +middlemost +middleweight +middleweights +middleweight's +middy +middies +middy's +midfield +midfielder +midfielders +midge +midges +midge's +midget +midgets +midget's +midi +midis +midi's +midland +midlands +midland's +midlife +midlife's +midmost +midnight +midnight's +midpoint +midpoints +midpoint's +midrib +midribs +midrib's +midriff +midriffs +midriff's +midsection +midsections +midsection's +midshipman +midshipman's +midshipmen +midships +midsize +midst +midst's +midstream +midstream's +midsummer +midsummer's +midterm +midterms +midterm's +midtown +midtown's +midway +midways +midway's +midweek +midweeks +midweek's +midwife +midwifing +midwifed +midwifes +midwife's +midwifery +midwiferies +midwifery's +midwinter +midwinter's +midwives +midyear +midyears +midyear's +mien +mien's +miff +miffing +miffed +miffs +might've +might +might's +mightily +mightiness +mightiness's +mightn't +mighty +mightiest +mightier +mightiness +mignonette +mignonettes +mignonette's +migraine +migraines +migraine's +migrant +migrants +migrant's +migrate +migrating +migrated +migrates +remigrating +remigrated +remigrates +remigrate +migration +migrations +migration's +migratory +mikado +mikados +mikado's +mike +miking +miked +mikes +mike's +mil +miler +milers +mils +mil's +milady +miladies +milady's +milch +mild +mildly +mildest +milder +mildness +mild's +mildew +mildewing +mildewed +mildews +mildew's +mildness +mildness's +mile +miles +mile's +mileage +mileages +mileage's +milepost +mileposts +milepost's +miler +miler's +milestone +milestones +milestone's +milf +milfs +milf's +milieu +milieus +milieu's +militancy +militancy's +militant +militantly +militants +militant's +militarily +militarism +militarism's +militarist +militarists +militarist's +militaristic +militarization +militarization's +demilitarization's +demilitarization +militarize +militarizing +militarized +militarizes +demilitarizing +demilitarized +demilitarizes +demilitarize +military +military's +militate +militating +militated +militates +militia +militias +militia's +militiaman +militiaman's +militiamen +milk +milking +milked +milker +milkers +milks +milk's +milker +milker's +milkiness +milkiness's +milkmaid +milkmaids +milkmaid's +milkman +milkman's +milkmen +milkshake +milkshakes +milkshake's +milksop +milksops +milksop's +milkweed +milkweeds +milkweed's +milky +milkiest +milkier +milkiness +mill +milling +millings +milled +miller +millers +mills +mill's +millage +millage's +millennia +millennial +millennial's +millennium +millenniums +millennium's +miller +miller's +millet +millet's +milliard +milliards +milliard's +millibar +millibars +millibar's +milligram +milligrams +milligram's +milliliter +milliliters +milliliter's +millimeter +millimeters +millimeter's +milliner +milliners +milliner's +millinery +millinery's +milling +milling's +million +millionth +millions +million's +millionaire +millionaires +millionaire's +millionairess +millionairesses +millionth +millionth's +millionths +millipede +millipedes +millipede's +millisecond +milliseconds +millisecond's +millpond +millponds +millpond's +millrace +millraces +millrace's +millstone +millstones +millstone's +millstream +millstreams +millstream's +millwright +millwrights +millwright's +milometer +milometers +milquetoast +milquetoasts +milquetoast's +milt +milting +milted +milts +milt's +mime +miming +mimed +mimes +mime's +mimeograph +mimeographing +mimeographed +mimeograph's +mimeographs +mimetic +mimic +mimics +mimic's +mimicked +mimicker +mimickers +mimicker's +mimicking +mimicry +mimicries +mimicry's +mimosa +mimosas +mimosa's +min +minaret +minarets +minaret's +minatory +mince +mincing +minced +mincer +mincers +minces +mince's +mincemeat +mincemeat's +mincer +mincer's +mind's +mind +minding +minded +minder +minders +minds +reminding +reminded +reminder +reminders +reminds +remind +mindbogglingly +minded +mindedness +mindful +mindfully +mindfulness +mindfulness +mindfulness's +mindless +mindlessly +mindlessness +mindlessness +mindlessness's +mindset +mindsets +mindset's +mine +minion +minions +mining +mined +miner +miners +mines +mine's +minefield +minefields +minefield's +miner +miner's +mineral +minerals +mineral's +mineralogical +mineralogist +mineralogists +mineralogist's +mineralogy +mineralogy's +minestrone +minestrone's +minesweeper +minesweepers +minesweeper's +mingle +mingling +mingled +mingles +mingy +mini +minis +mini's +miniature +miniatures +miniature's +miniaturist +miniaturists +miniaturist's +miniaturization +miniaturization's +miniaturize +miniaturizing +miniaturized +miniaturizes +minibar +minibars +minibike +minibikes +minibike's +minibus +minibuses +minibus's +minicab +minicabs +minicam +minicams +minicam's +minicomputer +minicomputers +minicomputer's +minifloppies +minim +minims +minim's +minima +minimal +minimally +minimalism +minimalism's +minimalist +minimalists +minimalist's +minimization +minimization's +minimize +minimizing +minimized +minimizes +minimum +minimums +minimum's +mining +mining's +minion +minion's +miniseries +miniseries's +miniskirt +miniskirts +miniskirt's +minister +ministering +ministered +ministers +minister's +ministerial +ministrant +ministrants +ministrant's +ministration +ministrations +ministration's +ministry +ministries +ministry's +minivan +minivans +minivan's +mink +minks +mink's +minnesinger +minnesingers +minnesinger's +minnow +minnows +minnow's +minor +minoring +minored +minors +minor's +minority +minorities +minority's +minoxidil +minoxidil's +minster +minsters +minster's +minstrel +minstrels +minstrel's +minstrelsy +minstrelsy's +mint +minting +minted +minter +minters +mints +mint's +mintage +mintage's +minter +minter's +minty +mintiest +mintier +minuend +minuends +minuend's +minuet +minuets +minuet's +minus +minuses +minus's +minuscule +minuscules +minuscule's +minute +minutely +minuting +minuted +minutest +minuter +minutes +minuteness +minute's +minuteman +minuteman's +minutemen +minuteness +minuteness's +minutia +minutia's +minutiae +minx +minxes +minx's +miracle +miracles +miracle's +miraculous +miraculously +mirage +mirages +mirage's +mire +miring +mired +mires +mire's +mirror +mirroring +mirrored +mirrors +mirror's +mirth +mirth's +mirthful +mirthfully +mirthfulness +mirthfulness +mirthfulness's +mirthless +mirthlessly +miry +miriest +mirier +misaddress +misaddressing +misaddressed +misaddresses +misadventure +misadventures +misadventure's +misaligned +misalignment +misalignment's +misalliance +misalliances +misalliance's +misanthrope +misanthropes +misanthrope's +misanthropic +misanthropically +misanthropist +misanthropists +misanthropist's +misanthropy +misanthropy's +misapplication +misapplication's +misapply +misapplication +misapplications +misapplying +misapplied +misapplies +misapprehend +misapprehending +misapprehended +misapprehends +misapprehension +misapprehensions +misapprehension's +misappropriate +misappropriation +misappropriations +misappropriating +misappropriated +misappropriates +misappropriation +misappropriation's +misbegotten +misbehave +misbehaving +misbehaved +misbehaves +misbehavior +misbehavior's +misc +miscalculate +miscalculation +miscalculations +miscalculating +miscalculated +miscalculates +miscalculation +miscalculation's +miscall +miscalling +miscalled +miscalls +miscarriage +miscarriages +miscarriage's +miscarry +miscarrying +miscarried +miscarries +miscast +miscasting +miscasts +miscegenation +miscegenation's +miscellaneous +miscellaneously +miscellany +miscellanies +miscellany's +mischance +mischances +mischance's +mischief +mischief's +mischievous +mischievously +mischievousness +mischievousness +mischievousness's +miscibility +miscibility's +miscible +miscommunication +miscommunications +misconceive +misconceiving +misconceived +misconceives +misconception +misconceptions +misconception's +misconduct +misconducting +misconducted +misconducts +misconduct's +misconstruction +misconstructions +misconstruction's +misconstrue +misconstruing +misconstrued +misconstrues +miscount +miscounting +miscounted +miscounts +miscount's +miscreant +miscreants +miscreant's +miscue +miscuing +miscued +miscues +miscue's +misdeal +misdealing +misdeals +misdeal's +misdealt +misdeed +misdeeds +misdeed's +misdemeanor +misdemeanors +misdemeanor's +misdiagnose +misdiagnosing +misdiagnosed +misdiagnoses +misdiagnosis +misdiagnosis's +misdid +misdirect +misdirecting +misdirected +misdirects +misdirection +misdirection's +misdo +misdoing +misdoings +misdoes +misdoing +misdoing's +misdone +miser +miserly +misers +miser's +miserable +miserableness +miserableness's +miserably +miserliness +miserliness's +misery +miseries +misery's +misfeasance +misfeasance's +misfeature +misfeatures +misfile +misfiling +misfiled +misfiles +misfire +misfiring +misfired +misfires +misfire's +misfit +misfits +misfit's +misfitted +misfitting +misfortune +misfortunes +misfortune's +misgiving +misgivings +misgiving's +misgovern +misgoverning +misgoverned +misgoverns +misgovernment +misgovernment +misgovernment's +misguidance +misguidance's +misguide +misguiding +misguided +misguides +misguided +misguidedly +mishandle +mishandling +mishandled +mishandles +mishap +mishaps +mishap's +mishear +mishearing +mishears +misheard +mishit +mishits +mishitting +mishmash +mishmashes +mishmash's +misidentify +misidentifying +misidentified +misidentifies +misinform +misinforming +misinformed +misinforms +misinformation +misinformation's +misinterpret +misinterpreting +misinterpreted +misinterprets +misinterpretation +misinterpretations +misinterpretation's +misjudge +misjudging +misjudged +misjudges +misjudgment +misjudgments +misjudgment's +mislabel +mislabeling +mislabeled +mislabels +mislaid +mislay +mislaying +mislays +mislead +misleading +misleads +misleading +misleadingly +misled +mismanage +mismanaging +mismanaged +mismanages +mismanagement +mismanagement +mismanagement's +mismatch +mismatching +mismatched +mismatches +mismatch's +misname +misnaming +misnamed +misnames +misnomer +misnomers +misnomer's +misogamist +misogamists +misogamist's +misogamy +misogamy's +misogynist +misogynists +misogynist's +misogynistic +misogynous +misogyny +misogyny's +misplace +misplacing +misplaced +misplaces +misplacement +misplacement +misplacement's +misplay +misplaying +misplayed +misplays +misplay's +misprint +misprinting +misprinted +misprints +misprint's +misprision +misprision's +mispronounce +mispronouncing +mispronounced +mispronounces +mispronunciation +mispronunciations +mispronunciation's +misquotation +misquotations +misquotation's +misquote +misquoting +misquoted +misquotes +misquote's +misread +misreading +misreadings +misreads +misreading +misreading's +misreport +misreporting +misreported +misreports +misreport's +misrepresent +misrepresenting +misrepresented +misrepresents +misrepresentation +misrepresentations +misrepresentation's +misrule +misruling +misruled +misrules +misrule's +miss's +miss +missive +missing +missed +misses +dismissing +dismissed +dismisses +dismiss +missal +missals +missal's +dismissals +dismissal's +dismissal +missed +unmissed +misshape +misshaping +misshaped +misshapes +misshapen +missile +missiles +missile's +missilery +missilery's +mission +missions +mission's +remissions +remission's +remission +missionary +missionaries +missionary's +missioner +missioners +missioner's +missive +missives +missive's +misspeak +misspeaking +misspeaks +misspell +misspelling +misspellings +misspelled +misspells +misspelling +misspelling's +misspend +misspending +misspends +misspent +misspoke +misspoken +misstate +misstating +misstated +misstates +misstatement +misstatement +misstatements +misstatement's +misstep +missteps +misstep's +missus +missuses +missus's +mist's +mist +misting +misted +mister +misters +mists +demisting +demisted +demister +demisters +demists +demist +mistakable +unmistakable +mistake +mistaking +mistakes +mistake's +mistakable +mistaken +mistakenly +mister's +mistily +mistime +mistiming +mistimed +mistimes +mistiness +mistiness's +mistletoe +mistletoe's +mistook +mistral +mistrals +mistral's +mistranslated +mistreat +mistreating +mistreated +mistreats +mistreatment +mistreatment +mistreatment's +mistress +mistresses +mistress's +mistrial +mistrials +mistrial's +mistrust +mistrusting +mistrusted +mistrusts +mistrust's +mistrustful +mistrustfully +misty +mistiest +mistier +mistiness +mistype +mistyping +mistypes +misunderstand +misunderstanding +misunderstandings +misunderstands +misunderstanding +misunderstanding's +misunderstood +misuse +misusing +misused +misuses +misuse's +mite +miter +miters +mites +mite's +miter +mitering +mitered +miter's +mitigate +mitigation +mitigating +mitigated +mitigates +mitigated +unmitigated +mitigation +mitigation's +mitochondria +mitochondrial +mitochondrion +mitoses +mitosis +mitosis's +mitotic +mitral +mitt +mitten +mittens +mitts +mitt's +mitten +mitten's +mitzvah +mix +mixing +mixed +mixer +mixers +mixes +mix's +mixable +mixed +unmixed +mixer +mixer's +mixture +mixtures +mixture's +mizzen +mizzens +mizzen's +mizzenmast +mizzenmasts +mizzenmast's +mkay +mks +ml +mm +mnemonic +mnemonics +mnemonic's +mnemonically +mo +moth +mos +demos +promos +demo +promo +moan +moaning +moaned +moaner +moaners +moans +moan's +moaner +moaner's +moat +moated +moats +moat's +mob's +mob +mobs +demobs +demob +mobbed +demobbed +mobbing +demobbing +mobile +mobiles +mobile's +mobility +mobility's +mobilization +mobilization's +demobilization's +demobilization +mobilizations +mobilize +mobilizing +mobilized +mobilizes +demobilizing +demobilized +demobilizes +demobilize +mobilizer +mobilizers +mobilizer's +mobster +mobsters +mobster's +moccasin +moccasins +moccasin's +mocha +mochas +mocha's +mock +mocking +mocked +mocker +mockers +mocks +mocker +mocker's +mockery +mockeries +mockery's +mocking +mockingly +mockingbird +mockingbirds +mockingbird's +mod +modest +mods +mod's +modal +modals +modal's +modality +modalities +modded +modding +mode +modes +mode's +model +modeling +modelings +modeled +modeler +modelers +models +model's +modeler +modeler's +modeling +modeling's +modem +modems +modem's +moderate +moderation +moderately +moderating +moderated +moderates +moderateness +moderate's +moderateness +moderateness's +moderation +moderation's +moderator +moderators +moderator's +modern +modernly +moderns +modernness +modern's +modernism +modernism's +modernist +modernists +modernist's +modernistic +modernity +modernity's +modernization +modernization's +modernize +modernizing +modernized +modernizer +modernizers +modernizes +modernizer +modernizer's +modernness +modernness's +modest +modestly +modesty +modesty's +modicum +modicums +modicum's +modifiable +modification +modification's +modified +unmodified +modifier +modifier's +modify +modification +modifications +modifying +modified +modifier +modifiers +modifies +modish +modishly +modishness +modishness +modishness's +modular +modulate +modulation +modulating +modulated +modulates +demodulation +demodulating +demodulated +demodulates +demodulate +modulation +modulation's +demodulation's +demodulation +modulations +modulator +modulators +modulator's +module +modules +module's +modulo +modulus +moggy +mogul +moguls +mogul's +mohair +mohair's +moi +moiety +moieties +moiety's +moil +moiling +moiled +moils +moil's +moire +moires +moire's +moist +moisten +moistens +moistly +moistest +moister +moistness +moisten +moistening +moistened +moistener +moisteners +moistener +moistener's +moistness +moistness's +moisture +moisture's +moisturize +moisturizing +moisturized +moisturizer +moisturizers +moisturizes +moisturizer +moisturizer's +molar +molars +molar's +molasses +molasses's +mold +molding +moldings +molded +molder +molders +molds +mold's +moldboard +moldboards +moldboard's +molder +moldering +moldered +molder's +moldiness +moldiness's +molding +molding's +moldy +moldiest +moldier +moldiness +mole +moles +mole's +molecular +molecularity +molecularity's +molecule +molecules +molecule's +molehill +molehills +molehill's +moleskin +moleskin's +molest +molesting +molested +molester +molesters +molests +molestation +molestation's +molested +unmolested +molester +molester's +moll +molls +moll's +mollification +mollification's +mollify +mollification +mollifying +mollified +mollifies +molluscan +mollusk +mollusks +mollusk's +molly +mollies +molly's +mollycoddle +mollycoddling +mollycoddled +mollycoddles +mollycoddle's +molt +molten +molting +molted +molter +molters +molts +molt's +molter +molter's +molybdenum +molybdenum's +mom +moms +mom's +moment +moments +moment's +momenta +momentarily +momentariness +momentariness's +momentary +momentariness +momentous +momentously +momentousness +momentousness +momentousness's +momentum +momentum's +mommy +mommies +mommy's +monad +monarch +monarch's +monarchic +monarchical +monarchism +monarchism's +monarchist +monarchists +monarchist's +monarchistic +monarchs +monarchy +monarchies +monarchy's +monastery +monasteries +monastery's +monastic +monastics +monastic's +monastical +monastically +monasticism +monasticism's +monaural +monetarily +monetarism +monetarism's +monetarist +monetarists +monetarist's +monetary +monetize +monetizing +monetized +monetizes +demonetizing +demonetized +demonetizes +demonetize +money +moneyed +moneys +money's +moneybag +moneybags +moneybag's +moneybox +moneyboxes +moneylender +moneylenders +moneylender's +moneymaker +moneymakers +moneymaker's +moneymaking +moneymaking's +monger +mongering +mongered +mongers +monger's +mongol +mongols +mongolism +mongolism's +mongoloid +mongoloids +mongoloid's +mongoose +mongooses +mongoose's +mongrel +mongrels +mongrel's +monies +moniker +monikers +moniker's +monism +monism's +monist +monists +monist's +monition +monitions +monition's +monitor +monitoring +monitored +monitors +monitor's +monitory +monk +monks +monk's +monkey +monkeying +monkeyed +monkeys +monkey's +monkeyshine +monkeyshines +monkeyshine's +monkish +monkshood +monkshoods +monkshood's +mono +mono's +monochromatic +monochrome +monochromes +monochrome's +monocle +monocled +monocles +monocle's +monoclonal +monocotyledon +monocotyledons +monocotyledon's +monocotyledonous +monocular +monodic +monodist +monodists +monodist's +monody +monodies +monody's +monogamist +monogamists +monogamist's +monogamous +monogamously +monogamy +monogamy's +monogram +monograms +monogram's +monogrammed +monogramming +monograph +monograph's +monographs +monolingual +monolinguals +monolingual's +monolith +monolith's +monolithic +monoliths +monologist +monologists +monologist's +monologue +monologues +monologue's +monomania +monomania's +monomaniac +monomaniacs +monomaniac's +monomaniacal +monomer +monomers +monomer's +mononucleosis +mononucleosis's +monophonic +monoplane +monoplanes +monoplane's +monopolist +monopolists +monopolist's +monopolistic +monopolization +monopolization's +monopolize +monopolizing +monopolized +monopolizer +monopolizers +monopolizes +monopolizer +monopolizer's +monopoly +monopolies +monopoly's +monorail +monorails +monorail's +monosyllabic +monosyllable +monosyllables +monosyllable's +monotheism +monotheism's +monotheist +monotheists +monotheist's +monotheistic +monotone +monotones +monotone's +monotonic +monotonically +monotonous +monotonously +monotonousness +monotonousness +monotonousness's +monotony +monotony's +monounsaturated +monoxide +monoxides +monoxide's +monseigneur +monseigneur's +monsieur +monsieur's +monsignor +monsignors +monsignor's +monsoon +monsoons +monsoon's +monsoonal +monster +monsters +monster's +monstrance +monstrances +monstrance's +remonstrances +remonstrance's +remonstrance +monstrosity +monstrosities +monstrosity's +monstrous +monstrously +montage +montages +montage's +month +monthly +month's +monthly +monthlies +monthly's +months +monument +monuments +monument's +monumental +monumentally +moo +mooing +mooed +moos +moo's +mooch +mooching +mooched +moocher +moochers +mooches +mooch's +moocher +moocher's +mood +moods +mood's +moodily +moodiness +moodiness's +moody +moodiest +moodier +moodiness +moon +mooning +mooned +moons +moon's +moonbeam +moonbeams +moonbeam's +moonless +moonlight +moonlighting +moonlighted +moonlighter +moonlighters +moonlights +moonlight's +moonlighter +moonlighter's +moonlighting +moonlighting's +moonlit +moonscape +moonscapes +moonscape's +moonshine +moonshiner +moonshiners +moonshines +moonshine's +moonshiner +moonshiner's +moonshot +moonshots +moonshot's +moonstone +moonstones +moonstone's +moonstruck +moonwalk +moonwalks +moonwalk's +moor +mooring +moorings +moored +moors +moor's +moorhen +moorhens +mooring +mooring's +moorland +moorlands +moorland's +moose +moose's +moot +mooting +mooted +moots +mop +moping +moped +moper +mopers +mops +mop's +mope +mopes +mope's +moped +mopeds +moped's +moper +moper's +mopey +mopier +mopiest +mopish +mopped +moppet +moppets +moppet's +mopping +moraine +moraines +moraine's +moral +morally +morals +moral's +morale +morale's +moralism +moralist +moralists +moralist's +moralistic +moralistically +moralities +morality +morality's +unmorality's +unmorality +moralization +moralization's +demoralization's +demoralization +moralize +moralizing +moralized +moralizes +demoralizing +demoralized +demoralizes +demoralize +moralizer +moralizers +moralizer's +morass +morasses +morass's +moratorium +moratoriums +moratorium's +moray +morays +moray's +morbid +morbidly +morbidness +morbidity +morbidity's +morbidness +morbidness's +mordancy +mordancy's +mordant +mordantly +mordants +mordant's +more +mores +more's +moreish +morel +morels +morel's +moreover +mores +mores's +morgue +morgues +morgue's +moribund +morn +morning +mornings +morns +morn's +morning +morning's +morocco +morocco's +moron +morons +moron's +moronic +moronically +morose +morosely +moroseness +moroseness +moroseness's +morph +morphing +morphed +morpheme +morphemes +morpheme's +morphemic +morphia +morphia's +morphine +morphine's +morphing +morphing's +morphological +morphology +morphology's +morphs +morrow +morrows +morrow's +morsel +morsels +morsel's +mortal +mortally +mortals +mortal's +mortality +mortality's +mortar +mortaring +mortared +mortars +mortar's +mortarboard +mortarboards +mortarboard's +mortgage's +mortgage +mortgaging +mortgaged +mortgages +remortgaging +remortgaged +remortgages +remortgage +mortgagee +mortgagees +mortgagee's +mortgagor +mortgagors +mortgagor's +mortician +morticians +mortician's +mortification +mortification's +mortify +mortification +mortifying +mortified +mortifies +mortise +mortising +mortised +mortises +mortise's +mortuary +mortuaries +mortuary's +mosaic +mosaics +mosaic's +mosey +moseying +moseyed +moseys +mosh +moshing +moshed +moshes +mosque +mosques +mosque's +mosquito +mosquito's +mosquitoes +moss +mosses +moss's +mossback +mossbacks +mossback's +mossy +mossiest +mossier +most +mostly +most's +mot +mots +mot's +mote's +mote +motive +motion +motions +motes +demotion +promotion +demotions +promotions +demotes +promotes +demote +promote +motel +motels +motel's +motet +motets +motet's +moth +moth's +mothball +mothballing +mothballed +mothballs +mothball's +mother +motherly +mothering +mothered +mothers +mother's +motherboard +motherboards +motherboard's +motherfucker +motherfuckers +motherfucker's +motherfucking +motherhood +motherhood's +motherland +motherlands +motherland's +motherless +motherliness +motherliness's +moths +motif +motifs +motif's +motile +motiles +motility +motility's +motion +motion's +demotion's +promotion's +demotion +promotion +motioned +motioning +motionless +motionlessly +motionlessness +motionlessness +motionlessness's +motivate +motivating +motivated +motivates +demotivating +demotivated +demotivates +demotivate +motivated +unmotivated +motivation +motivations +motivation's +motivational +motivator +motivators +motivator's +motive +motives +motive's +motiveless +motley +motleys +motley's +motlier +motliest +motocross +motocrosses +motocross's +motor +motoring +motored +motors +motor's +motorbike +motorbiking +motorbiked +motorbikes +motorbike's +motorboat +motorboats +motorboat's +motorcade +motorcades +motorcade's +motorcar +motorcars +motorcar's +motorcycle +motorcycling +motorcycled +motorcycles +motorcycle's +motorcyclist +motorcyclists +motorcyclist's +motorist +motorists +motorist's +motorization +motorization's +motorize +motorizing +motorized +motorizes +motorman +motorman's +motormen +motormouth +motormouth's +motormouths +motorway +motorways +motorway's +mottle +mottling +mottled +mottles +motto +motto's +mottoes +moue +moues +moue's +mound +mounding +mounded +mounds +mound's +mount +mounting +mounted +mounts +mount's +remounting +dismounting +remounted +dismounted +remounts +dismounts +remount's +dismount's +remount +dismount +mountable +mountain +mountains +mountain's +mountaineer +mountaineering +mountaineered +mountaineers +mountaineer's +mountaineering +mountaineering's +mountainous +mountainside +mountainsides +mountainside's +mountaintop +mountaintops +mountaintop's +mountebank +mountebanks +mountebank's +mounted +unmounted +mounter +mounters +mounter's +mounting +mountings +mounting's +mourn +mourning +mourned +mourner +mourners +mourns +mourned +unmourned +mourner +mourner's +mournful +mournfully +mournfulness +mournfulness +mournfulness's +mourning +mourning's +mouse +mousing +moused +mouser +mousers +mouses +mouse's +mouser +mouser's +mousetrap +mousetraps +mousetrap's +mousetrapped +mousetrapping +mousiness +mousiness's +moussaka +moussakas +mousse +moussing +moussed +mousses +mousse's +mousy +mousiest +mousier +mousiness +mouth +mouthing +mouthed +mouth's +mouthfeel +mouthful +mouthfuls +mouthful's +mouthiness +mouthiness's +mouthpiece +mouthpieces +mouthpiece's +mouths +mouthwash +mouthwashes +mouthwash's +mouthwatering +mouthy +mouthiest +mouthier +mouthiness +mouton +mouton's +movable +movables +movable's +move +moving +moved +mover +movers +moves +move's +movable +removing +removed +remover +removers +removes +remove's +removable +remove +moved +unmoved +movement +movements +movement's +mover +mover's +remover's +remover +movie +movies +movie's +moviegoer +moviegoers +moviegoer's +moving +movingly +mow +mowing +mowed +mower +mowers +mows +mow's +mower +mower's +moxie +moxie's +mozzarella +mozzarella's +mp +mpg +mph +mt +mtg +mtge +mu +mus +mu's +much +much's +mucilage +mucilage's +mucilaginous +muck +mucking +mucked +mucks +muck's +muckrake +muckraking +muckraked +muckraker +muckrakers +muckrakes +muckraker +muckraker's +mucky +muckiest +muckier +mucous +mucus +mucus's +mud +mud's +muddily +muddiness +muddiness's +muddle +muddling +muddled +muddles +muddle's +muddleheaded +muddy +muddying +muddied +muddiest +muddier +muddies +muddiness +mudflap +mudflaps +mudflat +mudflats +mudflat's +mudguard +mudguards +mudguard's +mudpack +mudpacks +mudroom +mudrooms +mudroom's +mudslide +mudslides +mudslide's +mudslinger +mudslingers +mudslinger's +mudslinging +mudslinging's +muenster +muenster's +muesli +muezzin +muezzins +muezzin's +muff +muffing +muffed +muffs +muff's +muffin +muffins +muffin's +muffle +muffling +muffled +muffler +mufflers +muffles +muffler +muffler's +mufti +muftis +mufti's +mug +mugs +mug's +mugful +mugfuls +mugful's +mugged +mugger +muggers +mugger's +mugginess +mugginess's +mugging +muggings +mugging's +muggins +muggle +muggles +muggle's +muggy +muggiest +muggier +mugginess +mugshot +mugshots +mugshot's +mugwump +mugwumps +mugwump's +mujaheddin +mukluk +mukluks +mukluk's +mulatto +mulatto's +mulattoes +mulberry +mulberries +mulberry's +mulch +mulching +mulched +mulches +mulch's +mulct +mulcting +mulcted +mulcts +mulct's +mule +mules +mule's +muleskinner +muleskinners +muleskinner's +muleteer +muleteers +muleteer's +mulish +mulishly +mulishness +mulishness +mulishness's +mull +mulling +mulled +mulls +mullah +mullah's +mullahs +mullein +mullein's +mullet +mullets +mullet's +mulligan +mulligans +mulligan's +mulligatawny +mulligatawny's +mullion +mullioned +mullions +mullion's +multi +multichannel +multicolored +multicultural +multiculturalism +multiculturalism's +multidimensional +multidisciplinary +multifaceted +multifamily +multifarious +multifariously +multifariousness +multifariousness +multifariousness's +multiform +multigrain +multilateral +multilaterally +multilayered +multilevel +multilingual +multilingualism +multilingualism's +multimedia +multimedia's +multimillionaire +multimillionaires +multimillionaire's +multinational +multinationals +multinational's +multipart +multiparty +multiplayer +multiplayer's +multiple +multiples +multiple's +multiplex +multiplexing +multiplexed +multiplexer +multiplexers +multiplexes +multiplex's +multiplexer +multiplexer's +multiplicand +multiplicands +multiplicand's +multiplication +multiplication's +multiplicative +multiplicity +multiplicities +multiplicity's +multiplier +multiplier's +multiply +multiplication +multiplications +multiplying +multiplied +multiplier +multipliers +multiplies +multiprocessing +multiprocessor +multiprocessors +multiprocessor's +multipurpose +multiracial +multistage +multistory +multitask +multitasking +multitasks +multitasking +multitasking's +multitude +multitudes +multitude's +multitudinous +multivariate +multiverse +multiverses +multiverse's +multivitamin +multivitamins +multivitamin's +multiyear +mum +mumble +mumbling +mumbled +mumbler +mumblers +mumbles +mumble's +mumbler +mumbler's +mumbletypeg +mumbletypeg's +mummer +mummers +mummer's +mummery +mummery's +mummification +mummification's +mummify +mummification +mummifying +mummified +mummifies +mummy +mummies +mummy's +mumps +mumps's +mun +munch +munching +munched +munches +munchie +munchies +munchies +munchies's +munchkin +munchkins +munchkin's +mundane +mundanely +mundanes +mung +munging +munged +mungs +municipal +municipally +municipals +municipal's +municipality +municipalities +municipality's +munificence +munificence's +munificent +munificently +munition +munitioning +munitioned +munitions +munition's +mural +murals +mural's +muralist +muralists +muralist's +murder +murdering +murdered +murderer +murderers +murders +murder's +murderer +murderer's +murderess +murderesses +murderess's +murderous +murderously +murk +murks +murk's +murkily +murkiness +murkiness's +murky +murkiest +murkier +murkiness +murmur +murmuring +murmurings +murmured +murmurer +murmurers +murmurs +murmur's +murmurer +murmurer's +murmuring +murmuring's +murmurous +murrain +murrain's +muscat +muscats +muscat's +muscatel +muscatels +muscatel's +muscle +muscling +muscled +muscles +muscle's +musclebound +muscleman +musclemen +muscly +muscular +muscularly +muscularity +muscularity's +musculature +musculature's +muse +musing +musings +mused +muses +muse's +musette +musettes +musette's +museum +museums +museum's +mush +mushing +mushed +musher +mushers +mushes +mush's +mushiness +mushiness's +mushroom +mushrooming +mushroomed +mushrooms +mushroom's +mushy +mushiest +mushier +mushiness +music +musics +music's +musical +musically +musicals +musical's +musicale +musicales +musicale's +musicality +musicality's +musician +musicianly +musicians +musician's +musicianship +musicianship's +musicological +musicologist +musicologists +musicologist's +musicology +musicology's +musing +musingly +musing's +musk +musk's +muskeg +muskegs +muskeg's +muskellunge +muskellunges +muskellunge's +musket +muskets +musket's +musketeer +musketeers +musketeer's +musketry +musketry's +muskie +muskie's +muskiness +muskiness's +muskmelon +muskmelons +muskmelon's +muskox +muskoxen +muskox's +muskrat +muskrats +muskrat's +musky +muskiest +muskier +muskies +muskiness +muslin +muslin's +muss +mussing +mussed +musses +muss's +mussel +mussels +mussel's +mussy +mussiest +mussier +must've +must +muster +musters +musts +must's +mustache +mustached +mustaches +mustache's +mustachio +mustachioed +mustachios +mustachio's +mustang +mustangs +mustang's +mustard +mustard's +muster +mustering +mustered +muster's +mustily +mustiness +mustiness's +mustn't +musty +mustiest +mustier +mustiness +mutability +mutability's +mutably +mutagen +mutagens +mutagen's +mutagenic +mutant +mutants +mutant's +mutate +mutative +mutation +mutations +mutating +mutated +mutates +mutation +mutation's +mutational +mute +mutely +muting +muted +mutest +muter +mutes +muteness +mute's +mutable +muteness +muteness's +mutilate +mutilation +mutilations +mutilating +mutilated +mutilates +mutilation +mutilation's +mutilator +mutilators +mutilator's +mutineer +mutineers +mutineer's +mutinous +mutinously +mutiny +mutinying +mutinied +mutinies +mutiny's +mutt +mutts +mutt's +mutter +muttering +mutterings +muttered +mutterer +mutterers +mutters +mutter's +mutterer +mutterer's +muttering +muttering's +mutton +mutton's +muttonchops +muttonchops's +muttony +mutual +mutually +mutuality +mutuality's +muumuu +muumuus +muumuu's +muzak +muzzily +muzzle +muzzling +muzzled +muzzles +muzzle's +muzzy +muzziness +my +mycologist +mycologists +mycologist's +mycology +mycology's +myelitis +myelitis's +myna +mynas +myna's +myocardial +myocardium +myopia +myopia's +myopic +myopically +myriad +myriads +myriad's +myrmidon +myrmidons +myrmidon's +myrrh +myrrh's +myrtle +myrtles +myrtle's +myself +mysterious +mysteriously +mysteriousness +mysteriousness +mysteriousness's +mystery +mysteries +mystery's +mystic +mystics +mystic's +mystical +mystically +mysticism +mysticism's +mystification +mystification's +demystification's +demystification +mystify +mystification +mystifying +mystified +mystifies +demystification +demystifying +demystified +demystifies +demystify +mystique +mystique's +myth +myth's +mythic +mythical +mythological +mythologist +mythologists +mythologist's +mythologize +mythologizing +mythologized +mythologizes +mythology +mythologies +mythology's +myths +myxomatosis +n +nth +nest +inn +pron +naan +naans +nab +nabs +nabbed +nabbing +nabob +nabobs +nabob's +nacelle +nacelles +nacelle's +nacho +nachos +nacho's +nacre +nacre's +nacreous +nadir +nadirs +nadir's +nae +naff +naffest +naffer +nag +nags +nag's +nagged +nagger +naggers +nagger's +nagging +nagware +nah +naiad +naiads +naiad's +naif +naifs +naif's +nail +nailing +nailed +nails +nail's +nailbrush +nailbrushes +nailbrush's +naive +naively +naivest +naiver +naivete +naivete's +naivety +naivety's +naked +nakedly +nakedness +nakedness +nakedness's +name's +name +naming +named +names +renaming +renamed +renames +rename +nameable +unnameable +named +unnamed +namedrop +namedropping +namedropping's +nameless +namelessly +namely +nameplate +nameplates +nameplate's +namesake +namesakes +namesake's +namespace +nanny +nannies +nanny's +nanobot +nanobots +nanosecond +nanoseconds +nanosecond's +nanotechnology +nanotechnologies +nanotechnology's +nanotube +nap +naps +nap's +napalm +napalming +napalmed +napalms +napalm's +nape +napes +nape's +naphtha +naphtha's +naphthalene +naphthalene's +napkin +napkins +napkin's +napless +napoleon +napoleons +napoleon's +napped +napper +nappers +napper's +napping +nappy +nappiest +nappier +nappies +nappy's +narc +narcs +narc's +narcissism +narcissism's +narcissist +narcissists +narcissist's +narcissistic +narcissus +narcissus's +narcolepsy +narcolepsy's +narcoleptic +narcoses +narcosis +narcosis's +narcotic +narcotics +narcotic's +narcotization +narcotization's +narcotize +narcotizing +narcotized +narcotizes +nark +narky +narrate +narrative +narration +narrations +narrating +narrated +narrates +narration +narration's +narrative +narratives +narrative's +narrator +narrators +narrator's +narrow +narrowly +narrowing +narrowed +narrowest +narrower +narrows +narrowness +narrow's +narrowness +narrowness's +narwhal +narwhals +narwhal's +nary +nasal +nasally +nasals +nasal's +nasality +nasality's +nasalization +nasalization's +nasalize +nasalizing +nasalized +nasalizes +nascence +nascence's +renascence's +renascence +nascent +renascent +nastily +nastiness +nastiness's +nasturtium +nasturtiums +nasturtium's +nasty +nastiest +nastier +nastiness +natal +natch +nation +nations +nation's +national +nationally +nationals +national's +nationalism +nationalism's +nationalist +nationalists +nationalist's +nationalistic +nationalistically +nationality +nationalities +nationality's +nationalization +nationalizations +nationalization's +nationalize +nationalizing +nationalized +nationalizes +denationalizing +denationalized +denationalizes +denationalize +nationhood +nationhood's +nationwide +native +natives +native's +nativity +nativities +nativity's +natl +natter +nattering +nattered +natters +natter's +nattily +nattiness +nattiness's +natty +nattiest +nattier +nattiness +natural's +natural +naturally +naturalness +unnaturally +unnaturalness +unnatural +naturalism +naturalism's +naturalist +naturalists +naturalist's +naturalistic +naturalization +naturalization's +naturalize +naturalizing +naturalized +naturalizes +naturalness +naturalness's +unnaturalness's +unnaturalness +naturals +nature's +nature +natures +denatures +denature +naturism +naturist +naturists +naught +naughts +naught's +naughtily +naughtiness +naughtiness's +naughty +naughtiest +naughtier +naughtiness +nausea +nausea's +nauseate +nauseating +nauseated +nauseates +nauseating +nauseatingly +nauseous +nauseously +nauseousness +nauseousness +nauseousness's +nautical +nautically +nautilus +nautiluses +nautilus's +naval +nave +naves +nave's +navel +navels +navel's +navigability +navigability's +navigable +navigate +navigation +navigating +navigated +navigates +navigation +navigation's +navigational +navigator +navigators +navigator's +navvy +navvies +navy +navies +navy's +nay +nays +nay's +naysayer +naysayers +naysayer's +ne'er +neanderthal +neanderthals +neanderthal's +neap +neaps +neap's +near +nearly +nearing +neared +nearest +nearer +nears +nearness +nearby +nearness +nearness's +nearshore +nearside +nearsighted +nearsightedly +nearsightedness +nearsightedness +nearsightedness's +neat +neaten +neatens +neatly +neatest +neater +neatness +neaten +neatening +neatened +neath +neatness +neatness's +nebula +nebula's +nebulae +nebular +nebulous +nebulously +nebulousness +nebulousness +nebulousness's +necessarily +unnecessarily +necessary +necessaries +necessary's +necessitate +necessitating +necessitated +necessitates +necessitous +necessity +necessities +necessity's +neck +necking +necked +necks +neck's +neckband +neckbands +neckerchief +neckerchiefs +neckerchief's +necking +necking's +necklace +necklacing +necklacings +necklaced +necklaces +necklace's +neckline +necklines +neckline's +necktie +neckties +necktie's +necrology +necrology's +necromancer +necromancers +necromancer's +necromancy +necromancy's +necrophilia +necrophiliac +necrophiliacs +necropolis +necropolises +necropolis's +necroses +necrosis +necrosis's +necrotic +nectar +nectar's +nectarine +nectarines +nectarine's +nee +need +needing +needed +needs +need's +needed +unneeded +needful +needfully +neediness +neediness's +needle +needling +needled +needles +needle's +needlepoint +needlepoint's +needless +needlessly +needlessness +needlessness +needlessness's +needlewoman +needlewoman's +needlewomen +needlework +needlework's +needn't +needy +neediest +needier +neediness +nefarious +nefariously +nefariousness +nefariousness +nefariousness's +neg +negate +negative +negation +negations +negating +negated +negates +negation +negation's +negative +negatively +negativing +negatived +negatives +negativeness +negative's +negativeness +negativeness's +negativism +negativism's +negativity +negativity's +neglect +neglecting +neglected +neglects +neglect's +neglectful +neglectfully +neglectfulness +neglectfulness +neglectfulness's +negligee +negligees +negligee's +negligence +negligence's +negligent +negligently +negligible +negligibly +negotiability +negotiability's +negotiable +renegotiable +negotiate +negotiation +negotiating +negotiated +negotiates +renegotiation +renegotiating +renegotiated +renegotiates +renegotiate +negotiation +negotiation's +renegotiation's +renegotiation +negotiations +negotiator +negotiators +negotiator's +negritude +negritude's +negro +negroid +neigh +neighing +neighed +neigh's +neighbor +neighborly +neighboring +neighbored +neighbors +neighbor's +neighborhood +neighborhoods +neighborhood's +neighborliness +neighborliness's +neighs +neither +nelson +nelsons +nelson's +nematode +nematodes +nematode's +nemeses +nemesis +nemesis's +neoclassic +neoclassical +neoclassicism +neoclassicism's +neocolonialism +neocolonialism's +neocolonialist +neocolonialists +neocolonialist's +neocon +neocons +neocon's +neoconservative +neoconservatives +neoconservative's +neodymium +neodymium's +neolithic +neologism +neologisms +neologism's +neon +neon's +neonatal +neonate +neonates +neonate's +neophilia +neophyte +neophytes +neophyte's +neoplasm +neoplasms +neoplasm's +neoplastic +neoprene +neoprene's +nepenthe +nepenthe's +nephew +nephews +nephew's +nephrite +nephrite's +nephritic +nephritis +nephritis's +nephropathy +nepotism +nepotism's +nepotist +nepotists +nepotist's +nepotistic +neptunium +neptunium's +nerd +nerds +nerd's +nerdy +nerdiest +nerdier +nerve's +nerve +nerving +nerved +nerves +unnerving +unnerved +unnerves +unnerve +nerveless +nervelessly +nervelessness +nervelessness +nervelessness's +nerviness +nerviness's +nervous +nervously +nervousness +nervousness +nervousness's +nervy +nerviest +nervier +nerviness +nest +nesting +nested +nests +nest's +nestle +nestling +nestlings +nestled +nestles +nestling +nestling's +net +nets +net's +netball +netbook +netbooks +netbook's +nether +nethermost +netherworld +netherworld's +netiquette +netiquettes +netted +netter +netters +netting +netting's +nettle +nettling +nettled +nettles +nettle's +nettlesome +network +networking +networked +networks +network's +networking +networking's +neural +neurally +neuralgia +neuralgia's +neuralgic +neurasthenia +neurasthenia's +neurasthenic +neurasthenics +neurasthenic's +neuritic +neuritics +neuritic's +neuritis +neuritis's +neurological +neurologically +neurologist +neurologists +neurologist's +neurology +neurology's +neuron +neurons +neuron's +neuronal +neuroscience +neuroses +neurosis +neurosis's +neurosurgeon +neurosurgeons +neurosurgeon's +neurosurgery +neurosurgery's +neurosurgical +neurotic +neurotics +neurotic's +neurotically +neuroticism +neurotransmitter +neurotransmitters +neurotransmitter's +neut +neuter +neutering +neutered +neuters +neuter's +neutral +neutrally +neutrals +neutral's +neutralism +neutralism's +neutralist +neutralists +neutralist's +neutrality +neutrality's +neutralization +neutralization's +neutralize +neutralizing +neutralized +neutralizer +neutralizers +neutralizes +neutralizer +neutralizer's +neutrino +neutrinos +neutrino's +neutron +neutrons +neutron's +never +nevermore +nevertheless +nevi +nevus +nevus's +new +newly +newest +newer +news +newness +new's +newbie +newbies +newbie's +newborn +newborns +newborn's +newcomer +newcomers +newcomer's +newel +newels +newel's +newfangled +newfound +newline +newlines +newlywed +newlyweds +newlywed's +newness +newness's +news +news's +newsagent +newsagents +newsboy +newsboys +newsboy's +newscast +newscaster +newscasters +newscasts +newscast's +newscaster +newscaster's +newsdealer +newsdealers +newsdealer's +newsflash +newsflashes +newsgirl +newsgirls +newsgirl's +newsgroup +newsgroups +newsgroup's +newshound +newshounds +newsletter +newsletters +newsletter's +newsman +newsman's +newsmen +newspaper +newspapers +newspaper's +newspaperman +newspaperman's +newspapermen +newspaperwoman +newspaperwoman's +newspaperwomen +newspeak +newsprint +newsprint's +newsreader +newsreaders +newsreel +newsreels +newsreel's +newsroom +newsrooms +newsroom's +newsstand +newsstands +newsstand's +newsweekly +newsweeklies +newsweekly's +newswoman +newswoman's +newswomen +newsworthiness +newsworthiness's +newsworthy +newsworthiness +newsy +newsiest +newsier +newt +newts +newt's +newton +newtons +newton's +next +next's +nexus +nexuses +nexus's +niacin +niacin's +nib +nibs +nib's +nibble +nibbling +nibbled +nibbler +nibblers +nibbles +nibble's +nibbler +nibbler's +nice +nicely +nicest +nicer +niceness +niceness +niceness's +nicety +niceties +nicety's +niche +niches +niche's +nick +nicking +nicked +nicker +nickers +nicks +nick's +nickel +nickels +nickel's +nickelodeon +nickelodeons +nickelodeon's +nicker +nickering +nickered +nicker's +nickle +nickles +nickname +nicknaming +nicknamed +nicknames +nickname's +nicotine +nicotine's +niece +nieces +niece's +niff +niffy +nifty +niftiest +niftier +nigga +niggas +nigga's +niggard +niggardly +niggards +niggard's +niggardliness +niggardliness's +niggaz +nigger +niggers +nigger's +niggle +niggling +niggled +niggler +nigglers +niggles +niggle's +niggler +niggler's +nigh +nighest +nigher +night +nightly +nights +night's +nightcap +nightcaps +nightcap's +nightclothes +nightclothes's +nightclub +nightclubs +nightclub's +nightclubbed +nightclubbing +nightdress +nightdresses +nightdress's +nightfall +nightfall's +nightgown +nightgowns +nightgown's +nighthawk +nighthawks +nighthawk's +nightie +nighties +nightie's +nightingale +nightingales +nightingale's +nightlife +nightlife's +nightlight +nightlights +nightlong +nightmare +nightmares +nightmare's +nightmarish +nightshade +nightshades +nightshade's +nightshirt +nightshirts +nightshirt's +nightspot +nightspots +nightspot's +nightstand +nightstands +nightstand's +nightstick +nightsticks +nightstick's +nighttime +nighttime's +nightwatchman +nightwatchmen +nightwear +nightwear's +nihilism +nihilism's +nihilist +nihilists +nihilist's +nihilistic +nil +nil's +nimbi +nimble +nimblest +nimbler +nimbleness +nimbleness +nimbleness's +nimbly +nimbus +nimbus's +nimby +nimrod +nimrods +nimrod's +nincompoop +nincompoops +nincompoop's +nine +nines +nine's +ninepin +ninepins +ninepin's +ninepins +ninepins's +nineteen +nineteenth +nineteens +nineteen's +nineteenth +nineteenth's +nineteenths +ninetieth +ninetieth's +ninetieths +ninety +ninetieth +nineties +ninety's +ninja +ninjas +ninja's +ninny +ninnies +ninny's +ninth +ninth's +ninths +niobium +niobium's +nip +nips +nip's +nipped +nipper +nippers +nipper's +nippiness +nippiness's +nipping +nipple +nipples +nipple's +nippy +nippiest +nippier +nippiness +nirvana +nirvana's +nisei +nisei's +nit +niter +nits +nit's +niter +niter's +nitpick +nitpicking +nitpicked +nitpicker +nitpickers +nitpicks +nitpicker +nitpicker's +nitpicking +nitpicking's +nitrate +nitration +nitrating +nitrated +nitrates +nitrate's +nitration +nitration's +nitric +nitrification +nitrification's +nitrite +nitrites +nitrite's +nitro +nitrocellulose +nitrocellulose's +nitrogen +nitrogen's +nitrogenous +nitroglycerin +nitroglycerin's +nitwit +nitwits +nitwit's +nix +nixing +nixed +nixes +nix's +no +nos +no's +nob +nobly +nobs +nobble +nobbling +nobbled +nobbles +nobelium +nobelium's +nobility +nobility's +noble +noblest +nobler +nobles +nobleness +noble's +nobleman +nobleman's +noblemen +nobleness +nobleness's +noblewoman +noblewoman's +noblewomen +nobody +nobodies +nobody's +nocturnal +nocturnally +nocturne +nocturnes +nocturne's +nod +nods +nod's +nodal +nodded +nodding +noddle +noddles +noddle's +noddy +node +nodes +node's +nodular +nodule +nodules +nodule's +noel +noels +noel's +noes +noggin +noggins +noggin's +nohow +noise +noising +noised +noises +noise's +noiseless +noiselessly +noiselessness +noiselessness +noiselessness's +noisemaker +noisemakers +noisemaker's +noisily +noisiness +noisiness's +noisome +noisy +noisiest +noisier +noisiness +nomad +nomads +nomad's +nomadic +nomenclature +nomenclatures +nomenclature's +nominal +nominally +nominate +nominative +nomination +nominating +nominated +nominates +renomination +denomination +renominating +denominating +renominated +denominated +renominates +denominates +renominate +denominate +nomination's +renomination's +nomination +nominations +nomination's +denominations +denomination's +denomination +nominative +nominatives +nominative's +nominator +nominators +nominator's +denominators +denominator's +denominator +nominee +nominees +nominee's +non +nonabrasive +nonabsorbent +nonabsorbents +nonabsorbent's +nonacademic +nonacceptance +nonacceptance's +nonacid +nonactive +nonactives +nonactive's +nonaddictive +nonadhesive +nonadjacent +nonadjustable +nonadministrative +nonage +nonages +nonage's +nonagenarian +nonagenarians +nonagenarian's +nonaggression +nonaggression's +nonalcoholic +nonaligned +nonalignment +nonalignment's +nonallergic +nonappearance +nonappearances +nonappearance's +nonassignable +nonathletic +nonattendance +nonattendance's +nonautomotive +nonavailability +nonavailability's +nonbasic +nonbeliever +nonbelievers +nonbeliever's +nonbelligerent +nonbelligerents +nonbelligerent's +nonbinding +nonbreakable +nonburnable +noncaloric +noncancerous +nonce +nonce's +nonchalance +nonchalance's +nonchalant +nonchalantly +nonchargeable +nonclerical +nonclericals +nonclerical's +nonclinical +noncollectable +noncom +noncoms +noncom's +noncombat +noncombatant +noncombatants +noncombatant's +noncombustible +noncommercial +noncommercials +noncommercial's +noncommittal +noncommittally +noncommunicable +noncompeting +noncompetitive +noncompliance +noncompliance's +noncomplying +noncomprehending +nonconducting +nonconductor +nonconductors +nonconductor's +nonconforming +nonconformism +nonconformist +nonconformists +nonconformist's +nonconformity +nonconformity's +nonconsecutive +nonconstructive +noncontagious +noncontinuous +noncontributing +noncontributory +noncontroversial +nonconvertible +noncooperation +noncooperation's +noncorroding +noncorrosive +noncredit +noncriminal +noncriminals +noncriminal's +noncritical +noncrystalline +noncumulative +noncustodial +nondairy +nondeductible +nondeductible's +nondelivery +nondeliveries +nondelivery's +nondemocratic +nondenominational +nondepartmental +nondepreciating +nondescript +nondestructive +nondetachable +nondeterminism +nondeterministic +nondisciplinary +nondisclosure +nondisclosure's +nondiscrimination +nondiscrimination's +nondiscriminatory +nondramatic +nondrinker +nondrinkers +nondrinker's +nondrying +none +noneducational +noneffective +nonelastic +nonelectric +nonelectrical +nonempty +nonenforceable +nonentity +nonentities +nonentity's +nonequivalent +nonequivalents +nonequivalent's +nonessential +nonesuch +nonesuches +nonesuch's +nonetheless +nonevent +nonevents +nonevent's +nonexchangeable +nonexclusive +nonexempt +nonexempt's +nonexistence +nonexistence's +nonexistent +nonexplosive +nonexplosives +nonexplosive's +nonfactual +nonfading +nonfat +nonfatal +nonfattening +nonferrous +nonfiction +nonfiction's +nonfictional +nonflammable +nonflowering +nonfluctuating +nonflying +nonfood +nonfood's +nonfreezing +nonfunctional +nongovernmental +nongranular +nonhazardous +nonhereditary +nonhuman +nonidentical +noninclusive +nonindependent +nonindustrial +noninfectious +noninflammatory +noninflationary +noninflected +nonintellectual +nonintellectuals +nonintellectual's +noninterchangeable +noninterference +noninterference's +nonintervention +nonintervention's +nonintoxicating +noninvasive +nonirritating +nonissue +nonjudgmental +nonjudicial +nonlegal +nonlethal +nonlinear +nonliterary +nonliving +nonliving's +nonmagnetic +nonmalignant +nonmember +nonmembers +nonmember's +nonmetal +nonmetals +nonmetal's +nonmetallic +nonmigratory +nonmilitant +nonmilitary +nonnarcotic +nonnarcotics +nonnarcotic's +nonnative +nonnatives +nonnative's +nonnegotiable +nonnuclear +nonnumerical +nonobjective +nonobligatory +nonobservance +nonobservance's +nonobservant +nonoccupational +nonoccurence +nonofficial +nonoperational +nonoperative +nonparallel +nonparallels +nonparallel's +nonpareil +nonpareils +nonpareil's +nonparticipant +nonparticipants +nonparticipant's +nonparticipating +nonpartisan +nonpartisans +nonpartisan's +nonpaying +nonpayment +nonpayments +nonpayment's +nonperformance +nonperformance's +nonperforming +nonperishable +nonperson +nonpersons +nonperson's +nonphysical +nonphysically +nonplus +nonpluses +nonplussed +nonplussing +nonpoisonous +nonpolitical +nonpolluting +nonporous +nonpracticing +nonprejudicial +nonprescription +nonproductive +nonprofessional +nonprofessionals +nonprofessional's +nonprofit +nonprofits +nonprofit's +nonprofitable +nonproliferation +nonproliferation's +nonpublic +nonpunishable +nonracial +nonradioactive +nonrandom +nonreactive +nonreciprocal +nonreciprocals +nonreciprocal's +nonreciprocating +nonrecognition +nonrecognition's +nonrecoverable +nonrecurring +nonredeemable +nonrefillable +nonrefundable +nonreligious +nonrenewable +nonrepresentational +nonresident +nonresidents +nonresident's +nonresidential +nonresidual +nonresidual's +nonresistance +nonresistance's +nonresistant +nonrestrictive +nonreturnable +nonreturnables +nonreturnable's +nonrhythmic +nonrigid +nonsalaried +nonscheduled +nonscientific +nonscoring +nonseasonal +nonsectarian +nonsecular +nonsegregated +nonsense +nonsense's +nonsensical +nonsensically +nonsensitive +nonsexist +nonsexual +nonskid +nonslip +nonsmoker +nonsmokers +nonsmoker's +nonsmoking +nonsocial +nonspeaking +nonspecialist +nonspecialists +nonspecialist's +nonspecializing +nonspecific +nonspiritual +nonspirituals +nonspiritual's +nonstaining +nonstandard +nonstarter +nonstarters +nonstarter's +nonstick +nonstop +nonstrategic +nonstriking +nonstructural +nonsuccessive +nonsupport +nonsupporting +nonsupport's +nonsurgical +nonsustaining +nonsympathizer +nonsympathizer's +nontarnishable +nontaxable +nontechnical +nontenured +nontheatrical +nonthinking +nonthreatening +nontoxic +nontraditional +nontransferable +nontransparent +nontrivial +nontropical +nonuniform +nonunion +nonuser +nonusers +nonuser's +nonvenomous +nonverbal +nonviable +nonviolence +nonviolence's +nonviolent +nonviolently +nonvirulent +nonvocal +nonvocational +nonvolatile +nonvoter +nonvoters +nonvoter's +nonvoting +nonwhite +nonwhites +nonwhite's +nonworking +nonyielding +nonzero +noodle +noodling +noodled +noodles +noodle's +nook +nooks +nook's +nookie +nooky +noon +noon's +noonday +noonday's +noontide +noontide's +noontime +noontime's +noose +nooses +noose's +nope +nor +nor'easter +norm +norms +norm's +normal +normally +normal's +normalcy +normalcy's +normality +normality's +normalization +normalization's +normalize +normalizing +normalized +normalizes +normative +north +norther +northers +north's +northbound +northeast +northeaster +northeasters +northeast's +northeaster +northeasterly +northeaster's +northeastern +northeastward +northeastwards +norther +northerly +norther's +northerly +northerlies +northerly's +northern +northerner +northerners +northerner +northerner's +northernmost +northward +northwards +northwest +northwester +northwesters +northwest's +northwester +northwesterly +northwester's +northwestern +northwestward +northwestwards +nose +nosing +nosed +noses +nose's +nosebag +nosebags +nosebleed +nosebleeds +nosebleed's +nosecone +nosecones +nosecone's +nosedive +nosediving +nosedived +nosedives +nosedive's +nosegay +nosegays +nosegay's +nosh +noshing +noshed +nosher +noshers +noshes +nosh's +nosher +nosher's +nosily +nosiness +nosiness's +nostalgia +nostalgia's +nostalgic +nostalgically +nostril +nostrils +nostril's +nostrum +nostrums +nostrum's +nosy +nosiest +nosier +nosiness +not +notable +notability +notabilities +notability's +notable +notables +notable's +notably +notarial +notarization +notarization's +notarize +notarizing +notarized +notarizes +notary +notaries +notary's +notate +notating +notated +notates +notation +notations +notation's +denotations +connotations +denotation's +connotation's +denotation +connotation +notch +notching +notched +notches +notch's +note's +note +noting +noted +notes +denoting +connoting +denoted +connoted +denotes +connotes +denote +connote +notebook +notebooks +notebook's +notelet +notelets +notepad +notepads +notepaper +notepaper's +noteworthiness +noteworthiness's +noteworthy +noteworthiness +nothing +nothings +nothingness +nothing's +nothingness +nothingness's +notice +noticing +noticed +notices +notice's +noticeable +unnoticeable +noticeably +noticeboard +noticeboards +noticed +unnoticed +notifiable +notification +notification's +notifier +notifier's +notify +notification +notifications +notifying +notified +notifier +notifiers +notifies +notion +notions +notion's +notional +notionally +notoriety +notoriety's +notorious +notoriously +notwithstanding +notwork +notworks +nougat +nougats +nougat's +noun +nouns +noun's +pronouns +pronoun's +pronoun +nourish +nourishing +nourished +nourishes +nourishment +nourishment +nourishment's +nous +nova +novas +nova's +novae +novel +novels +novel's +novelette +novelettes +novelette's +novelist +novelists +novelist's +novelization +novelizations +novelization's +novelize +novelizing +novelized +novelizes +novella +novellas +novella's +novelty +novelties +novelty's +novena +novenas +novena's +novene +novice +novices +novice's +novitiate +novitiates +novitiate's +now +now's +nowadays +nowadays's +noway +noways +nowhere +nowhere's +nowise +nowt +noxious +nozzle +nozzles +nozzle's +nu +nus +nu's +nuance +nuanced +nuances +nuance's +nub +nubs +nub's +nubbin +nubbins +nubbin's +nubby +nubbiest +nubbier +nubile +nuclear +pronuclear +nucleate +nucleation +nucleating +nucleated +nucleates +nucleation +nucleation's +nuclei +nucleic +nucleoli +nucleolus +nucleolus's +nucleon +nucleons +nucleon's +nucleoside +nucleotide +nucleus +nucleus's +nude +nudest +nuder +nudes +nude's +nudge +nudging +nudged +nudges +nudge's +nudism +nudism's +nudist +nudists +nudist's +nudity +nudity's +nugatory +nugget +nuggets +nugget's +nuisance +nuisances +nuisance's +nuke +nuking +nuked +nukes +nuke's +null +nulls +nullification +nullification's +nullify +nullification +nullifying +nullified +nullifies +nullity +nullity's +numb +numbly +numbing +numbed +numbest +number +numbers +numbs +numbness +number's +number +numbering +numbered +numbers +renumbering +renumbered +renumbers +renumber +numbered +unnumbered +numberless +numbness +numbness's +numerable +innumerable +numeracy +numeracy's +innumeracy's +innumeracy +numeral +numerals +numeral's +numerate +numeration +numerations +numerating +numerated +numerates +numeration +numeration's +numerator +numerators +numerator's +numeric +numerical +numerically +numerologist +numerologists +numerologist's +numerology +numerology's +numerous +numerously +numinous +numismatic +numismatics +numismatics +numismatics's +numismatist +numismatists +numismatist's +numskull +numskulls +numskull's +nun +nuns +nun's +nuncio +nuncios +nuncio's +nunnery +nunneries +nunnery's +nuptial +nuptials +nuptial's +nurse +nursing +nursed +nurser +nursers +nurses +nurse's +nurselings +nursemaid +nursemaids +nursemaid's +nurser +nurser's +nursery +nurseries +nursery's +nurseryman +nurseryman's +nurserymen +nursing +nursing's +nursling +nurslings +nursling's +nurture +nurturing +nurtured +nurturer +nurturers +nurtures +nurture's +nurturer +nurturer's +nut +nuts +nut's +nutcase +nutcases +nutcracker +nutcrackers +nutcracker's +nuthatch +nuthatches +nuthatch's +nuthouse +nuthouses +nutmeat +nutmeats +nutmeat's +nutmeg +nutmegs +nutmeg's +nutpick +nutpicks +nutpick's +nutria +nutrias +nutria's +nutrient +nutrients +nutrient's +nutriment +nutriments +nutriment's +nutrition +nutrition's +nutritional +nutritionally +nutritionist +nutritionists +nutritionist's +nutritious +nutritiously +nutritiousness +nutritiousness +nutritiousness's +nutritive +nutshell +nutshells +nutshell's +nutted +nutter +nutters +nuttiness +nuttiness's +nutting +nutty +nuttiest +nuttier +nuttiness +nuzzle +nuzzling +nuzzled +nuzzler +nuzzlers +nuzzles +nuzzle's +nuzzler +nuzzler's +nybble +nybbles +nylon +nylons +nylon's +nylons +nylons's +nymph +nymph's +nymphet +nymphets +nymphet's +nympho +nymphos +nymphomania +nymphomania's +nymphomaniac +nymphomaniacs +nymphomaniac's +nymphs +o +o'clock +o'er +oaf +oafs +oaf's +oafish +oafishly +oafishness +oafishness +oafishness's +oak +oaken +oaks +oak's +oakum +oakum's +oar +oaring +oared +oars +oar's +oarlock +oarlocks +oarlock's +oarsman +oarsman's +oarsmen +oarswoman +oarswoman's +oarswomen +oases +oasis +oasis's +oat +oaten +oats +oat's +oatcake +oatcakes +oatcake's +oath +oath's +oaths +oatmeal +oatmeal's +oats +oats's +ob +obs +obbligato +obbligatos +obbligato's +obduracy +obduracy's +obdurate +obdurately +obdurateness +obdurateness +obdurateness's +obedience +obedience's +disobedience's +disobedience +obedient +obediently +disobediently +disobedient +obeisance +obeisances +obeisance's +obeisant +obelisk +obelisks +obelisk's +obese +obesity +obesity's +obey +obeying +obeyed +obeys +disobeying +disobeyed +disobeys +disobey +obfuscate +obfuscation +obfuscations +obfuscating +obfuscated +obfuscates +obfuscation +obfuscation's +obi +obis +obi's +obit +obits +obit's +obituary +obituaries +obituary's +obj +object +objective +objecting +objected +objects +object's +objectify +objectification +objectifying +objectified +objectifies +objection +objections +objection's +objectionable +objectionable +unobjectionable +objectionably +objective +objectively +objectives +objectiveness +objective's +objectiveness +objectiveness's +objectivity +objectivity's +objector +objectors +objector's +objurgate +objurgation +objurgations +objurgating +objurgated +objurgates +objurgation +objurgation's +oblate +oblation +oblations +oblation +oblation's +obligate +obligation +obligations +obligating +obligated +obligates +obligation +obligation's +obligatorily +obligatory +oblige +obliging +obliged +obliges +disobliging +disobliged +disobliges +disoblige +obliging +obligingly +oblique +obliquely +obliques +obliqueness +oblique's +obliqueness +obliqueness's +obliquity +obliquity's +obliterate +obliteration +obliterating +obliterated +obliterates +obliteration +obliteration's +oblivion +oblivion's +oblivious +obliviously +obliviousness +obliviousness +obliviousness's +oblong +oblongs +oblong's +obloquy +obloquy's +obnoxious +obnoxiously +obnoxiousness +obnoxiousness +obnoxiousness's +oboe +oboes +oboe's +oboist +oboists +oboist's +obscene +obscenely +obscenest +obscener +obscenity +obscenities +obscenity's +obscurantism +obscurantism's +obscurantist +obscurantists +obscurantist's +obscure +obscurely +obscuring +obscured +obscurest +obscurer +obscures +obscurity +obscurities +obscurity's +obsequies +obsequious +obsequiously +obsequiousness +obsequiousness +obsequiousness's +obsequy +obsequy's +observably +observance +observances +observance's +observant +observantly +observation +observations +observation's +observational +observatory +observatories +observatory's +observe +observing +observed +observer +observers +observes +observable +observed +unobserved +observer +observer's +obsess +obsessive +obsessing +obsessed +obsesses +obsession +obsessions +obsession's +obsessional +obsessionally +obsessive +obsessively +obsessives +obsessiveness +obsessive's +obsessiveness +obsessiveness's +obsidian +obsidian's +obsolesce +obsolescing +obsolesced +obsolesces +obsolescence +obsolescence's +obsolescent +obsolete +obsoleting +obsoleted +obsoletes +obstacle +obstacles +obstacle's +obstetric +obstetrics +obstetrical +obstetrician +obstetricians +obstetrician's +obstetrics +obstetrics's +obstinacy +obstinacy's +obstinate +obstinately +obstreperous +obstreperously +obstreperousness +obstreperousness +obstreperousness's +obstruct +obstructive +obstructing +obstructed +obstructs +obstructed +unobstructed +obstruction +obstructions +obstruction's +obstructionism +obstructionism's +obstructionist +obstructionists +obstructionist's +obstructive +obstructively +obstructiveness +obstructiveness +obstructiveness's +obtain +obtaining +obtained +obtains +obtainable +obtainment +obtainable +unobtainable +obtainment +obtainment's +obtrude +obtruding +obtruded +obtrudes +obtrusion +obtrusion's +obtrusive +obtrusively +obtrusiveness +unobtrusively +unobtrusiveness +unobtrusive +obtrusiveness +obtrusiveness's +unobtrusiveness's +unobtrusiveness +obtuse +obtusely +obtusest +obtuser +obtuseness +obtuseness +obtuseness's +obverse +obverses +obverse's +obviate +obviation +obviating +obviated +obviates +obviation +obviation's +obvious +obviously +obviousness +obviousness +obviousness's +ocarina +ocarinas +ocarina's +occasion +occasioning +occasioned +occasions +occasion's +occasional +occasionally +occidental +occidentals +occidental's +occlude +occluding +occluded +occludes +occlusion +occlusions +occlusion's +occlusive +occult +occult's +occultism +occultism's +occultist +occultists +occultist's +occupancy +occupancy's +occupant +occupants +occupant's +occupation +occupation's +reoccupation's +reoccupation +occupational +occupationally +occupations +occupied +unoccupied +occupier +occupiers +occupier's +occupy +occupying +occupied +occupies +reoccupying +reoccupied +reoccupies +reoccupy +occur +occurs +reoccurs +reoccur +occurred +reoccurred +occurrence +occurrences +occurrence's +occurring +reoccurring +ocean +oceans +ocean's +oceanfront +oceanfronts +oceanfront's +oceangoing +oceanic +oceanic's +oceanographer +oceanographers +oceanographer's +oceanographic +oceanography +oceanography's +oceanology +oceanology's +ocelot +ocelots +ocelot's +och +ocher +ocher +ocher's +ocker +ockers +octagon +octagons +octagon's +octagonal +octal +octane +octanes +octane's +octave +octaves +octave's +octavo +octavos +octavo's +octet +octets +octet's +octogenarian +octogenarians +octogenarian's +octopus +octopuses +octopus's +ocular +oculars +ocular's +oculist +oculists +oculist's +oculomotor +odalisque +odalisques +odalisque's +odd +oddly +oddest +odder +odds +oddness +oddment +oddball +oddballs +oddball's +oddity +oddities +oddity's +oddment +oddments +oddment's +oddness +oddness's +odds +odds's +ode +odes +ode's +odious +odiously +odiousness +odiousness +odiousness's +odium +odium's +odometer +odometers +odometer's +odor +odored +odors +odor's +odoriferous +odorless +odorous +odyssey +odysseys +odyssey's +oedipal +oenology +oenology's +oenophile +oenophiles +oenophile's +oeuvre +oeuvres +oeuvre's +of +off +offing +offings +offed +offer +offers +offs +offal +offal's +offbeat +offbeats +offbeat's +offend +offending +offended +offender +offenders +offends +offender +offender's +offense +offenses +offense's +offensive's +offensive +offensively +offensiveness +inoffensively +inoffensiveness +inoffensive +offensiveness +offensiveness's +inoffensiveness's +inoffensiveness +offensives +offer +offering +offerings +offered +offer's +offering +offering's +offertory +offertories +offertory's +offhand +offhanded +offhandedly +offhandedness +offhandedness +offhandedness's +office +officer +officers +offices +office's +officeholder +officeholders +officeholder's +officer +officer's +official +officially +officials +official's +officialdom +officialdom's +officialese +officialism +officialism's +officiant +officiants +officiant's +officiate +officiating +officiated +officiates +officiator +officiators +officiator's +officious +officiously +officiousness +officiousness +officiousness's +offing +offing's +offish +offline +offload +offloading +offloaded +offloads +offprint +offprints +offprint's +offset +offsets +offset's +offsetting +offshoot +offshoots +offshoot's +offshore +offshoring +offside +offsite +offspring +offspring's +offstage +offstages +offtrack +oft +often +oftenest +oftener +oftentimes +ofttimes +ogle +ogling +ogled +ogler +oglers +ogles +ogle's +ogler +ogler's +ogre +ogres +ogre's +ogreish +ogress +ogresses +ogress's +oh +oh's +ohm +ohms +ohm's +ohmmeter +ohmmeters +ohmmeter's +oho +ohs +oi +oik +oiks +oil +oiling +oiled +oils +oil's +oilcan +oilcans +oilcloth +oilcloth's +oilcloths +oilfield +oilfields +oiliness +oiliness's +oilman +oilmen +oilskin +oilskins +oilskin's +oilskins +oilskins's +oily +oiliest +oilier +oiliness +oink +oinking +oinked +oinks +oink's +ointment +ointments +ointment's +okapi +okapis +okapi's +okay +okaying +okays +okay's +okra +okras +okra's +old +olden +oldest +older +oldness +old's +oldie +oldies +oldie's +oldish +oldness +oldness's +oldster +oldsters +oldster's +ole +olive +oles +ole's +oleaginous +oleander +oleanders +oleander's +oleo +oleo's +oleomargarine +oleomargarine's +olfactory +olfactories +olfactory's +oligarch +oligarch's +oligarchic +oligarchical +oligarchs +oligarchy +oligarchies +oligarchy's +oligonucleotide +oligonucleotides +oligopoly +oligopolies +oligopoly's +olive +olives +olive's +om +omen +omens +oms +om's +ombudsman +ombudsman's +ombudsmen +omega +omegas +omega's +omelet +omelets +omelet's +omen +omen's +omicron +omicrons +omicron's +ominous +ominously +ominousness +ominousness +ominousness's +omission +omissions +omission's +omit +omits +omitted +omitting +omnibus +omnibuses +omnibus's +omnipotence +omnipotence's +omnipotent +omnipresence +omnipresence's +omnipresent +omniscience +omniscience's +omniscient +omnivore +omnivores +omnivore's +omnivorous +omnivorously +omnivorousness +omnivorousness +omnivorousness's +on +only +onboard +once +once's +oncogene +oncogenes +oncogene's +oncologist +oncologists +oncologist's +oncology +oncology's +oncoming +one +onion +onions +ones +oneness +one's +oneness +oneness's +onerous +onerously +onerousness +onerousness +onerousness's +oneself +onetime +ongoing +onion +onion's +onionskin +onionskin's +online +onlooker +onlookers +onlooker's +onlooking +onomatopoeia +onomatopoeia's +onomatopoeic +onomatopoetic +onrush +onrushing +onrushes +onrush's +onscreen +onset +onsets +onset's +onshore +onside +onsite +onslaught +onslaughts +onslaught's +onstage +onto +ontogeny +ontogeny's +ontological +ontology +ontology's +onus +onuses +onus's +onward +onyx +onyxes +onyx's +oodles +oodles's +ooh +oohing +oohed +oohs +oomph +oops +ooze +oozing +oozed +oozes +ooze's +oozy +ooziest +oozier +op +oping +oped +ops +op's +opacity +opacity's +opal +opals +opal's +opalescence +opalescence's +opalescent +opaque +opaquely +opaquing +opaqued +opaquest +opaquer +opaques +opaqueness +opaqueness +opaqueness's +opcode +opcodes +ope +opes +open +openly +opening +openings +opened +openest +opener +openers +opens +openness +open's +opencast +opened +unopened +opener +opener's +openhanded +openhandedness +openhandedness +openhandedness's +openhearted +opening +opening's +openness +openness's +openwork +openwork's +opera +operas +opera's +operable +inoperable +operand +operands +operate +operative +operation +operations +operating +operated +operates +operatic +operatically +operation +operation's +operational +operationally +operative +operatives +operative's +operator +operators +operator's +operetta +operettas +operetta's +ophthalmic +ophthalmologist +ophthalmologists +ophthalmologist's +ophthalmology +ophthalmology's +opiate +opiates +opiate's +opine +opinion +opinions +opining +opined +opines +opinion +opinion's +opinionated +opium +opium's +opossum +opossums +opossum's +opp +opponent +opponents +opponent's +opportune +opportunely +inopportunely +inopportune +opportunism +opportunism's +opportunist +opportunists +opportunist's +opportunistic +opportunistically +opportunity +opportunities +opportunity's +oppose +opposing +opposed +opposes +opposed +unopposed +opposite +opposition +oppositions +oppositely +opposites +opposite's +opposition +opposition's +oppress +oppressive +oppressing +oppressed +oppresses +oppression +oppression's +oppressive +oppressively +oppressiveness +oppressiveness +oppressiveness's +oppressor +oppressors +oppressor's +opprobrious +opprobriously +opprobrium +opprobrium's +opt +opting +opted +opts +optic +optics +optic's +optical +optically +optician +opticians +optician's +optics +optics's +optima +optimal +optimally +optimism +optimisms +optimism's +optimist +optimists +optimist's +optimistic +optimistically +optimization +optimizations +optimization's +optimize +optimizing +optimized +optimizer +optimizes +optimum +optimums +optimum's +option +optioning +optioned +options +option's +optional +optionally +optometrist +optometrists +optometrist's +optometry +optometry's +opulence +opulence's +opulent +opulently +opus +opuses +opus's +or +oracle +oracles +oracle's +oracular +oral +orally +orals +oral's +orality +orange +oranges +orangeness +orange's +orangeade +orangeades +orangeade's +orangery +orangeries +orangery's +orangutan +orangutans +orangutan's +orate +oration +orations +orating +orated +orates +oration +oration's +orator +orators +orator's +oratorical +oratorically +oratorio +oratorios +oratorio's +oratory +oratories +oratory's +orb +orbs +orb's +orbicular +orbit +orbiting +orbited +orbiter +orbiters +orbits +orbit's +orbital +orbitals +orbital's +orbiter +orbiter's +orc +orcs +orc's +orchard +orchards +orchard's +orchestra +orchestras +orchestra's +orchestral +orchestrate +orchestration +orchestrations +orchestrating +orchestrated +orchestrates +orchestration +orchestration's +orchid +orchids +orchid's +ordain +ordaining +ordained +ordains +ordainment +ordainment +ordainment's +ordeal +ordeals +ordeal's +order +ordering +ordered +orders +order's +reordering +disordering +reordered +disordered +reorders +disorders +reorder's +disorder's +reorder +disorder +ordered +unordered +orderings +orderliness +orderliness's +disorderliness's +disorderliness +orderly +orderlies +orderliness +orderly's +ordinal +ordinals +ordinal's +ordinance +ordinances +ordinance's +ordinarily +ordinariness +ordinariness's +ordinary +ordinaries +ordinariness +ordinary's +ordinate +ordination +ordinations +ordinates +ordinate's +ordination +ordination's +ordnance +ordnance's +ordure +ordure's +ore +ores +ore's +oregano +oregano's +org +organ +organs +organ's +organdy +organdy's +organelle +organelles +organelle's +organic +organics +organic's +organically +inorganically +organism +organisms +organism's +organismic +organist +organists +organist's +organization +organizations +organization's +reorganizations +reorganization's +reorganization +organizational +organizationally +organize +organizing +organized +organizes +reorganizing +disorganizing +reorganized +disorganized +reorganizes +disorganizes +reorganize +disorganize +organized +unorganized +organizer +organizers +organizer's +organza +organza's +orgasm +orgasms +orgasm's +orgasmic +orgiastic +orgy +orgies +orgy's +oriel +oriels +oriel's +orient's +orient +orienting +oriented +orients +reorienting +disorienting +reoriented +disoriented +reorients +disorients +reorient +disorient +oriental +orientals +oriental's +orientalist +orientalists +orientate +orientation +orientating +orientated +orientates +disorientation +disorientating +disorientated +disorientates +disorientate +orientation +orientation's +reorientation's +disorientation's +reorientation +disorientation +orientations +orienteering +orifice +orifices +orifice's +orig +origami +origami's +origin +origins +origin's +original +originally +originals +original's +originality +originality's +originate +origination +originating +originated +originates +origination +origination's +originator +originators +originator's +oriole +orioles +oriole's +orison +orisons +orison's +ormolu +ormolu's +ornament +ornamenting +ornamented +ornaments +ornament's +ornamental +ornamentation +ornamentation's +ornate +ornately +ornateness +ornateness +ornateness's +orneriness +orneriness's +ornery +orneriest +ornerier +orneriness +ornithological +ornithologist +ornithologists +ornithologist's +ornithology +ornithology's +orotund +orotundity +orotundities +orotundity's +orphan +orphaning +orphaned +orphans +orphan's +orphanage +orphanages +orphanage's +orris +orrises +orris's +orthodontia +orthodontia's +orthodontic +orthodontics +orthodontics +orthodontics's +orthodontist +orthodontists +orthodontist's +orthodox +unorthodox +orthodoxy +orthodoxies +orthodoxy's +orthogonal +orthogonality +orthographic +orthographically +orthography +orthographies +orthography's +orthopedic +orthopedics +orthopedics +orthopedics's +orthopedist +orthopedists +orthopedist's +orzo +orzo's +oscillate +oscillation +oscillations +oscillating +oscillated +oscillates +oscillation +oscillation's +oscillator +oscillators +oscillator's +oscillatory +oscilloscope +oscilloscopes +oscilloscope's +osculate +osculation +osculations +osculating +osculated +osculates +osculation +osculation's +osier +osiers +osier's +osmium +osmium's +osmosis +osmosis's +osmotic +osprey +ospreys +osprey's +ossicles +ossification +ossification's +ossify +ossification +ossifying +ossified +ossifies +ostensible +ostensibly +ostentation +ostentation's +ostentatious +ostentatiously +osteoarthritis +osteoarthritis's +osteopath +osteopath's +osteopathic +osteopaths +osteopathy +osteopathy's +osteoporosis +osteoporosis's +ostler +ostlers +ostracism +ostracism's +ostracize +ostracizing +ostracized +ostracizes +ostrich +ostriches +ostrich's +other +others +otherness +other's +otherwise +otherworldly +otiose +otter +otters +otter's +ottoman +ottomans +ottoman's +oubliette +oubliettes +oubliette's +ouch +ought +oughtn't +ounce +ounces +ounce's +our +ours +ourselves +oust +ousting +ousted +ouster +ousters +ousts +ouster +ouster's +out +outing +outings +outed +outer +outs +out's +outage +outages +outage's +outargue +outarguing +outargued +outargues +outback +outbacks +outback's +outbalance +outbalancing +outbalanced +outbalances +outbid +outbids +outbidding +outboard +outboards +outboard's +outboast +outboasting +outboasted +outboasts +outbound +outbox +outboxes +outbox's +outbreak +outbreaks +outbreak's +outbuilding +outbuildings +outbuilding's +outburst +outbursts +outburst's +outcast +outcasts +outcast's +outclass +outclassing +outclassed +outclasses +outcome +outcomes +outcome's +outcrop +outcrops +outcrop's +outcropped +outcropping +outcroppings +outcropping's +outcry +outcries +outcry's +outdated +outdid +outdistance +outdistancing +outdistanced +outdistances +outdo +outdoing +outdoes +outdone +outdoor +outdoors +outdoors +outdoors's +outdoorsy +outdraw +outdrawing +outdraws +outdrawn +outdrew +outercourse +outermost +outerwear +outerwear's +outface +outfacing +outfaced +outfaces +outfall +outfalls +outfield +outfielder +outfielders +outfields +outfield's +outfielder +outfielder's +outfight +outfighting +outfights +outfit +outfits +outfit's +outfitted +outfitter +outfitters +outfitter's +outfitting +outflank +outflanking +outflanked +outflanks +outflow +outflows +outflow's +outfought +outfox +outfoxing +outfoxed +outfoxes +outgo +outgoing +outgoings +outgo's +outgoes +outgrew +outgrow +outgrowth +outgrowing +outgrows +outgrown +outgrowth +outgrowth's +outgrowths +outguess +outguessing +outguessed +outguesses +outgun +outguns +outgunned +outgunning +outhit +outhits +outhitting +outhouse +outhouses +outhouse's +outing +outing's +outlaid +outlandish +outlandishly +outlandishness +outlandishness +outlandishness's +outlast +outlasting +outlasted +outlasts +outlaw +outlawing +outlawed +outlaws +outlaw's +outlay +outlaying +outlays +outlay's +outlet +outlets +outlet's +outlier +outliers +outline +outlining +outlined +outlines +outline's +outlive +outliving +outlived +outlives +outlook +outlooks +outlook's +outlying +outmaneuver +outmaneuvering +outmaneuvered +outmaneuvers +outmatch +outmatching +outmatched +outmatches +outmoded +outnumber +outnumbering +outnumbered +outnumbers +outpace +outpacing +outpaced +outpaces +outpatient +outpatients +outpatient's +outperform +outperforming +outperformed +outperforms +outplace +outplacement +outplacement +outplacement's +outplay +outplaying +outplayed +outplays +outpoint +outpointing +outpointed +outpoints +outpost +outposts +outpost's +outpouring +outpourings +outpouring's +outproduce +outproducing +outproduced +outproduces +output +outputs +output's +outputted +outputting +outrace +outracing +outraced +outraces +outrage +outraging +outraged +outrages +outrage's +outrageous +outrageously +outran +outrank +outranking +outranked +outranks +outre +outreach +outreaching +outreached +outreaches +outreach's +outrider +outriders +outrider's +outrigger +outriggers +outrigger's +outright +outrun +outruns +outrunning +outscore +outscoring +outscored +outscores +outsell +outselling +outsells +outset +outsets +outset's +outshine +outshining +outshines +outshone +outshout +outshouting +outshouted +outshouts +outside +outsider +outsiders +outsides +outside's +outsider +outsider's +outsize +outsizes +outsize's +outskirt +outskirts +outskirt's +outsmart +outsmarting +outsmarted +outsmarts +outsold +outsource +outsourcing +outsourced +outsources +outsourcing +outsourcing's +outspend +outspending +outspends +outspent +outspoken +outspokenly +outspokenness +outspokenness +outspokenness's +outspread +outspreading +outspreads +outstanding +outstandingly +outstation +outstations +outstation's +outstay +outstaying +outstayed +outstays +outstretch +outstretching +outstretched +outstretches +outstrip +outstrips +outstripped +outstripping +outta +outtake +outtakes +outtake's +outvote +outvoting +outvoted +outvotes +outward +outwardly +outwards +outwear +outwearing +outwears +outweigh +outweighing +outweighed +outweighs +outwit +outwits +outwith +outwitted +outwitting +outwore +outwork +outworking +outworked +outworker +outworkers +outworks +outwork's +outworn +ouzo +ouzos +ouzo's +ova +oval +ovals +oval's +ovarian +ovary +ovaries +ovary's +ovate +ovation +ovations +ovation +ovation's +oven +ovens +oven's +ovenbird +ovenbirds +ovenbird's +ovenproof +ovenware +over +overly +overs +over's +overabundance +overabundance's +overabundant +overachieve +overachieving +overachieved +overachiever +overachievers +overachieves +overachiever +overachiever's +overact +overactive +overacting +overacted +overacts +overage +overages +overage's +overaggressive +overall +overalls +overall's +overalls +overalls's +overambitious +overanxious +overarching +overarm +overarming +overarmed +overarms +overate +overattentive +overawe +overawing +overawed +overawes +overbalance +overbalancing +overbalanced +overbalances +overbalance's +overbear +overbearing +overbears +overbearing +overbearingly +overbid +overbids +overbid's +overbidding +overbite +overbites +overbite's +overblown +overboard +overbold +overbook +overbooking +overbooked +overbooks +overbore +overborne +overbought +overbuild +overbuilding +overbuilds +overbuilt +overburden +overburdening +overburdened +overburdens +overbuy +overbuying +overbuys +overcame +overcapacity +overcapacity's +overcapitalize +overcapitalizing +overcapitalized +overcapitalizes +overcareful +overcast +overcasting +overcasts +overcast's +overcautious +overcharge +overcharging +overcharged +overcharges +overcharge's +overclock +overclocking +overclocked +overcloud +overclouding +overclouded +overclouds +overcoat +overcoats +overcoat's +overcome +overcoming +overcomes +overcompensate +overcompensation +overcompensating +overcompensated +overcompensates +overcompensation +overcompensation's +overconfidence +overconfidence's +overconfident +overconscientious +overcook +overcooking +overcooked +overcooks +overcritical +overcrowd +overcrowding +overcrowded +overcrowds +overcrowding +overcrowding's +overdecorate +overdecorating +overdecorated +overdecorates +overdependent +overdevelop +overdeveloping +overdeveloped +overdevelops +overdid +overdo +overdoing +overdoes +overdone +overdose +overdosing +overdosed +overdoses +overdose's +overdraft +overdrafts +overdraft's +overdraw +overdrawing +overdraws +overdrawn +overdress +overdressing +overdressed +overdresses +overdress's +overdrew +overdrive +overdrives +overdrive's +overdub +overdubs +overdub's +overdubbed +overdubbing +overdue +overeager +overeat +overeaten +overeating +overeats +overemotional +overemphasis +overemphasis's +overemphasize +overemphasizing +overemphasized +overemphasizes +overenthusiastic +overestimate +overestimation +overestimating +overestimated +overestimates +overestimate's +overestimation +overestimation's +overexcite +overexciting +overexcited +overexcites +overexercise +overexercising +overexercised +overexercises +overexert +overexerting +overexerted +overexerts +overexertion +overexertion's +overexpose +overexposing +overexposed +overexposes +overexposure +overexposure's +overextend +overextending +overextended +overextends +overfed +overfeed +overfeeding +overfeeds +overfill +overfilling +overfilled +overfills +overflew +overflight +overflights +overflight's +overflow +overflowing +overflowed +overflows +overflow's +overflown +overfly +overflying +overflies +overfond +overfull +overgeneralize +overgeneralizing +overgeneralized +overgeneralizes +overgenerous +overgraze +overgrazing +overgrazed +overgrazes +overgrew +overground +overgrow +overgrowth +overgrowing +overgrows +overgrown +overgrowth +overgrowth's +overhand +overhanded +overhands +overhand's +overhang +overhanging +overhangs +overhang's +overhasty +overhaul +overhauling +overhauled +overhauls +overhaul's +overhead +overheads +overhead's +overhear +overhearing +overhears +overheard +overheat +overheating +overheated +overheats +overhung +overindulge +overindulging +overindulged +overindulges +overindulgence +overindulgence's +overindulgent +overinflated +overjoy +overjoying +overjoyed +overjoys +overkill +overkill's +overladen +overlaid +overlain +overland +overlap +overlaps +overlap's +overlapped +overlapping +overlarge +overlay +overlaying +overlays +overlay's +overleaf +overlie +overload +overloading +overloaded +overloads +overload's +overlong +overlook +overlooking +overlooked +overlooks +overlook's +overlord +overlords +overlord's +overly +overlying +overlies +overmanned +overmanning +overmaster +overmastering +overmastered +overmasters +overmodest +overmuch +overmuches +overnice +overnight +overnights +overnight's +overoptimism +overoptimism's +overoptimistic +overpaid +overparticular +overpass +overpasses +overpass's +overpay +overpaying +overpays +overplay +overplaying +overplayed +overplays +overpopulate +overpopulation +overpopulating +overpopulated +overpopulates +overpopulation +overpopulation's +overpower +overpowering +overpowered +overpowers +overpowering +overpoweringly +overpraise +overpraising +overpraised +overpraises +overprecise +overprice +overpricing +overpriced +overprices +overprint +overprinting +overprinted +overprints +overprint's +overproduce +overproducing +overproduced +overproduces +overproduction +overproduction's +overprotect +overprotective +overprotecting +overprotected +overprotects +overqualified +overran +overrate +overrating +overrated +overrates +overreach +overreaching +overreached +overreaches +overreact +overreacting +overreacted +overreacts +overreaction +overreactions +overreaction's +overrefined +overridden +override +overriding +overrides +override's +overripe +overripe's +overrode +overrule +overruling +overruled +overrules +overrun +overruns +overrun's +overrunning +oversampling +oversaw +oversea +overseas +oversee +overseer +overseers +oversees +overseeing +overseen +overseer +overseer's +oversell +overselling +oversells +oversensitive +oversensitiveness +oversensitiveness +oversensitiveness's +oversexed +overshadow +overshadowing +overshadowed +overshadows +overshare +oversharing +overshared +overshares +overshoe +overshoes +overshoe's +overshoot +overshooting +overshoots +overshot +oversight +oversights +oversight's +oversimple +oversimplification +oversimplification's +oversimplify +oversimplification +oversimplifications +oversimplifying +oversimplified +oversimplifies +oversize +oversleep +oversleeping +oversleeps +overslept +oversold +overspecialization +overspecialization's +overspecialize +overspecializing +overspecialized +overspecializes +overspend +overspending +overspends +overspent +overspread +overspreading +overspreads +overstaffed +overstate +overstating +overstated +overstates +overstatement +overstatement +overstatements +overstatement's +overstay +overstaying +overstayed +overstays +overstep +oversteps +overstepped +overstepping +overstimulate +overstimulating +overstimulated +overstimulates +overstock +overstocking +overstocked +overstocks +overstretch +overstretching +overstretched +overstretches +overstrict +overstrung +overstuffed +oversubscribe +oversubscribing +oversubscribed +oversubscribes +oversubtle +oversupply +oversupplying +oversupplied +oversupplies +oversuspicious +overt +overtly +overtake +overtaking +overtakes +overtaken +overtax +overtaxing +overtaxed +overtaxes +overthink +overthinking +overthinks +overthought +overthrew +overthrow +overthrowing +overthrows +overthrow's +overthrown +overtime +overtimes +overtime's +overtire +overtiring +overtired +overtires +overtone +overtones +overtone's +overtook +overture +overtures +overture's +overturn +overturning +overturned +overturns +overuse +overusing +overused +overuses +overuse's +overvaluation +overvaluations +overvalue +overvaluing +overvalued +overvalues +overview +overviews +overview's +overweening +overweeningly +overweight +overweight's +overwhelm +overwhelming +overwhelmed +overwhelms +overwhelming +overwhelmingly +overwinter +overwintering +overwintered +overwinters +overwork +overworking +overworked +overworks +overwork's +overwrite +overwriting +overwrites +overwritten +overwrote +overwrought +overzealous +oviduct +oviducts +oviduct's +oviparous +ovoid +ovoids +ovoid's +ovular +ovulate +ovulation +ovulating +ovulated +ovulates +ovulation +ovulation's +ovule +ovules +ovule's +ovum +ovum's +ow +owe +owing +owed +owes +owl +owls +owl's +owlet +owlets +owlet's +owlish +owlishly +own +owning +owned +owns +disowning +disowned +disowns +disown +owner +owners +owner's +ownership +ownership's +ox +oxen +ox's +oxblood +oxblood's +oxbow +oxbows +oxbow's +oxcart +oxcarts +oxcart's +oxford +oxfords +oxford's +oxidant +oxidants +oxidant's +oxidase +oxidation +oxidation's +oxidative +oxide +oxides +oxide's +oxidization +oxidization's +oxidize +oxidizing +oxidized +oxidizer +oxidizers +oxidizes +oxidizer +oxidizer's +oxtail +oxtails +oxyacetylene +oxyacetylene's +oxygen +oxygen's +oxygenate +oxygenation +oxygenating +oxygenated +oxygenates +oxygenation +oxygenation's +oxymora +oxymoron +oxymoron's +oyster +oysters +oyster's +oz +ozone +ozone's +p +pen +pens +ping +pings +pest +per +pH +pa +path +pas +pa's +pablum +pablum's +pabulum +pabulum's +pace +pacing +paced +pacer +pacers +paces +pace's +pacemaker +pacemakers +pacemaker's +pacer +pacer's +pacesetter +pacesetters +pacesetter's +pacey +pachyderm +pachyderms +pachyderm's +pachysandra +pachysandras +pachysandra's +pacific +pacifically +pacification +pacification's +pacifier +pacifier's +pacifism +pacifism's +pacifist +pacifists +pacifist's +pacifistic +pacify +pacification +pacifying +pacified +pacifier +pacifiers +pacifies +pack's +pack +packing +packed +packs +repacking +unpacking +repacked +unpacked +repacks +unpacks +repack +unpack +package's +package +packaging +packaged +packages +repackaging +repackaged +repackages +repackage +packager +packagers +packager's +packaging +packaging's +packer +packers +packer's +packet +packets +packet's +packing's +packinghouse +packinghouses +packinghouse's +packsaddle +packsaddles +packsaddle's +pact +pacts +pact's +pacy +paciest +pacier +pad +pads +pad's +padded +padding +padding's +paddle +paddling +paddled +paddler +paddlers +paddles +paddle's +paddler +paddler's +paddock +paddocking +paddocked +paddocks +paddock's +paddy +paddies +paddy's +padlock +padlocking +padlocked +padlocks +padlock's +padre +padres +padre's +paean +paeans +paean's +paella +paellas +paella's +pagan +pagans +pagan's +paganism +paganism's +page +paging +paged +pager +pagers +pages +page's +pageant +pageants +pageant's +pageantry +pageantry's +pageboy +pageboys +pageboy's +pager +pager's +paginate +pagination +paginating +paginated +paginates +pagination +pagination's +pagoda +pagodas +pagoda's +pah +paid +repaid +unpaid +pail +pails +pail's +pailful +pailfuls +pailful's +pain +paining +pained +pains +pain's +painful +painfully +painfulness +painfuller +painfullest +painfulness +painfulness's +painkiller +painkillers +painkiller's +painkilling +painless +painlessly +painlessness +painlessness +painlessness's +painstaking +painstakingly +painstaking's +paint +painting +paintings +painted +painter +painters +paints +paint's +paintball +paintbox +paintboxes +paintbox's +paintbrush +paintbrushes +paintbrush's +painted +unpainted +painter +painterly +painter's +painting +painting's +paintwork +pair +pairing +paired +pairs +pair's +repairing +repaired +repairs +repair's +repair +paired +unpaired +pairing +pairings +pairwise +paisley +paisleys +paisley's +pajama +pajamas +pajamas +pajamas's +pal +pally +pals +pal's +palace +palaces +palace's +paladin +paladins +paladin's +palanquin +palanquins +palanquin's +palatable +unpalatable +palatal +palatals +palatal's +palatalization +palatalization's +palatalize +palatalizing +palatalized +palatalizes +palate +palates +palate's +palatable +palatial +palatially +palatinate +palatinates +palatinate's +palatine +palatines +palatine's +palaver +palavering +palavered +palavers +palaver's +palazzi +palazzo +pale +palely +paling +palings +paled +palest +paler +pales +paleness +pale's +paleface +palefaces +paleface's +paleness +paleness's +paleographer +paleographers +paleographer's +paleography +paleography's +paleolithic +paleontologist +paleontologists +paleontologist's +paleontology +paleontology's +palette +palettes +palette's +palfrey +palfreys +palfrey's +palimony +palimony's +palimpsest +palimpsests +palimpsest's +palindrome +palindromes +palindrome's +palindromic +paling +paling's +palisade +palisades +palisade's +palish +pall +palling +palled +palls +pall's +palladium +palladium's +pallbearer +pallbearers +pallbearer's +pallet +pallets +pallet's +palliate +palliative +palliation +palliating +palliated +palliates +palliation +palliation's +palliative +palliatives +palliative's +pallid +pallidly +pallidness +pallidness +pallidness's +pallor +pallor's +palm +palming +palmed +palms +palm's +palmate +palmetto +palmettos +palmetto's +palmist +palmists +palmist's +palmistry +palmistry's +palmtop +palmtops +palmtop's +palmy +palmiest +palmier +palomino +palominos +palomino's +palpable +palpably +palpate +palpation +palpating +palpated +palpates +palpation +palpation's +palpitate +palpitation +palpitations +palpitating +palpitated +palpitates +palpitation +palpitation's +palsy +palsying +palsied +palsies +palsy's +paltriness +paltriness's +paltry +paltriest +paltrier +paltriness +pampas +pampas's +pamper +pampering +pampered +pampers +pamphlet +pamphlets +pamphlet's +pamphleteer +pamphleteers +pamphleteer's +pan +pans +pan's +panacea +panaceas +panacea's +panache +panache's +panama +panamas +panama's +panatella +panatellas +pancake +pancaking +pancaked +pancakes +pancake's +panchromatic +pancreas +pancreases +pancreas's +pancreatic +pancreatitis +panda +pandas +panda's +pandemic +pandemics +pandemic's +pandemonium +pandemonium's +pander +pandering +pandered +panderer +panderers +panders +pander's +panderer +panderer's +pane +pane's +propane's +propane +panegyric +panegyrics +panegyric's +panel +paneling +panelings +paneled +panels +panel's +paneling +paneling's +panelist +panelists +panelist's +panes +pang +pangs +pang's +panhandle +panhandling +panhandled +panhandler +panhandlers +panhandles +panhandle's +panhandler +panhandler's +panic +panics +panic's +panicked +panicking +panicky +panned +pannier +panniers +pannier's +panning +panoply +panoplies +panoply's +panorama +panoramas +panorama's +panoramic +panpipes +panpipes's +pansy +pansies +pansy's +pant +panting +panted +pants +pant's +pantaloons +pantaloons's +pantechnicon +pantechnicons +pantheism +pantheism's +pantheist +pantheists +pantheist's +pantheistic +pantheon +pantheons +pantheon's +panther +panthers +panther's +pantie +panties +pantie's +panto +pantos +pantomime +pantomiming +pantomimed +pantomimes +pantomime's +pantomimic +pantomimist +pantomimists +pantomimist's +pantry +pantries +pantry's +pantsuit +pantsuits +pantsuit's +pantyhose +pantyhose's +pantyliner +pantyliner's +pantywaist +pantywaists +pantywaist's +pap +paps +pap's +papa +papas +papa's +papacy +papacies +papacy's +papal +paparazzi +paparazzi's +paparazzo +papaya +papayas +papaya's +paper +papering +papered +paperer +paperers +papers +paper's +paperback +paperbacks +paperback's +paperbark +paperbarks +paperboard +paperboard's +paperboy +paperboys +paperboy's +paperclip +paperclips +paperer +paperer's +papergirl +papergirls +papergirl's +paperhanger +paperhangers +paperhanger's +paperhanging +paperhanging's +paperless +paperweight +paperweights +paperweight's +paperwork +paperwork's +papery +papilla +papilla's +papillae +papillary +papist +papists +papist's +papoose +papooses +papoose's +pappy +pappies +pappy's +paprika +paprika's +papyri +papyrus +papyrus's +par +paring +parings +pared +parer +parers +pars +par's +parable +para +paras +para's +parable +parables +parable's +parabola +parabolas +parabola's +parabolic +paracetamol +paracetamols +parachute +parachuting +parachuted +parachutes +parachute's +parachutist +parachutists +parachutist's +parade +parading +paraded +parader +paraders +parades +parade's +parader +parader's +paradigm +paradigms +paradigm's +paradigmatic +paradisaical +paradise +paradises +paradise's +paradox +paradoxes +paradox's +paradoxical +paradoxically +paraffin +paraffin's +paragliding +paragon +paragons +paragon's +paragraph +paragraphing +paragraphed +paragraph's +paragraphs +parakeet +parakeets +parakeet's +paralegal +paralegals +paralegal's +parallax +parallaxes +parallax's +parallel +paralleling +paralleled +parallels +parallel's +paralleled +unparalleled +parallelism +parallelisms +parallelism's +parallelogram +parallelograms +parallelogram's +paralyses +paralysis +paralysis's +paralytic +paralytics +paralytic's +paralyze +paralyzing +paralyzed +paralyzes +paralyzing +paralyzingly +paramagnetic +paramecia +paramecium +paramecium's +paramedic +paramedics +paramedic's +paramedical +paramedicals +paramedical's +parameter +parameters +parameter's +parameterize +parameterized +parametric +paramilitary +paramilitaries +paramilitary's +paramount +paramountcy +paramour +paramours +paramour's +paranoia +paranoia's +paranoiac +paranoiacs +paranoiac's +paranoid +paranoids +paranoid's +paranormal +parapet +parapets +parapet's +paraphernalia +paraphernalia's +paraphrase +paraphrasing +paraphrased +paraphrases +paraphrase's +paraplegia +paraplegia's +paraplegic +paraplegics +paraplegic's +paraprofessional +paraprofessionals +paraprofessional's +parapsychologist +parapsychologists +parapsychologist's +parapsychology +parapsychology's +paraquat +paraquat's +parasailing +parascending +parasite +parasites +parasite's +parasitic +parasitical +parasitically +parasitism +parasitism's +parasol +parasols +parasol's +parasympathetic +parasympathetics +parathion +parathion's +parathyroid +parathyroids +parathyroid's +paratroop +paratrooper +paratroopers +paratroops +paratrooper +paratrooper's +paratroops +paratroops's +paratyphoid +paratyphoid's +parboil +parboiling +parboiled +parboils +parcel +parceling +parceled +parcels +parcel's +parch +parching +parched +parches +parchment +parchment +parchments +parchment's +pardner +pardners +pardon +pardoning +pardoned +pardoner +pardoners +pardons +pardon's +pardonable +pardonable +unpardonable +pardonably +unpardonably +pardoner +pardoner's +pare +pares +paregoric +paregoric's +parent +parenting +parented +parents +parent's +parentage +parentage's +parental +parentheses +parenthesis +parenthesis's +parenthesize +parenthesizing +parenthesized +parenthesizes +parenthetic +parenthetical +parenthetically +parenthood +parenthood's +parenting +parenting's +parer +parer's +pares +pareses +paresis +paresis's +parfait +parfaits +parfait's +pariah +pariah's +pariahs +paribus +parietal +parimutuel +parimutuels +parimutuel's +paring +paring's +parish +parishes +parish's +parishioner +parishioners +parishioner's +parity +parities +parity's +disparities +disparity's +disparity +park +parking +parked +parks +park's +parka +parkas +parka's +parking +parking's +parkland +parkour +parkway +parkways +parkway's +parky +parlance +parlance's +parlay +parlaying +parlayed +parlays +parlay's +parley +parleying +parleyed +parleys +parley's +parliament +parliaments +parliament's +parliamentarian +parliamentarians +parliamentarian's +parliamentary +parlor +parlors +parlor's +parlous +parmigiana +parochial +parochially +parochialism +parochialism's +parodist +parodists +parodist's +parody +parodying +parodied +parodies +parody's +parole +paroling +paroled +paroles +parole's +parolee +parolees +parolee's +parotid +paroxysm +paroxysms +paroxysm's +paroxysmal +parquet +parqueting +parqueted +parquets +parquet's +parquetry +parquetry's +parred +parricidal +parricide +parricides +parricide's +parring +parrot +parroting +parroted +parrots +parrot's +parry +parrying +parried +parries +parry's +parse +parsing +parsed +parser +parses +parsec +parsecs +parsec's +parsimonious +parsimoniously +parsimony +parsimony's +parsley +parsley's +parsnip +parsnips +parsnip's +parson +parsons +parson's +parsonage +parsonages +parsonage's +part's +part +parting +parted +parts +departing +departed +departs +depart +partake +partaking +partaker +partakers +partakes +partaken +partaker +partaker's +parterre +parterres +parterre's +parthenogenesis +parthenogenesis's +partial +partially +partials +partial's +partiality +partiality's +participant +participants +participant's +participate +participation +participating +participated +participates +participation +participation's +participator +participators +participator's +participatory +participial +participial's +participle +participles +participle's +particle +particles +particle's +particleboard +particleboard's +particular +particularly +particulars +particular's +particularity +particularities +particularity's +particularization +particularization's +particularize +particularizing +particularized +particularizes +particulate +particulates +particulate's +parting +partings +parting's +partisan +partisans +partisan's +partisanship +partisanship's +partition +partitioning +partitioned +partitions +partition's +partitive +partitives +partitive's +partly +partner +partnering +partnered +partners +partner's +partnership +partnerships +partnership's +partook +partridge +partridges +partridge's +parturition +parturition's +partway +party +partying +partied +parties +party's +parvenu +parvenus +parvenu's +pascal +pascals +pascal's +paschal +pasha +pashas +pasha's +pass +pass's +passably +passage +passages +passage's +passageway +passageways +passageway's +passbook +passbooks +passbook's +passe +passive +passion +passions +passing +passed +passer +passers +passes +passable +passel +passels +passel's +passenger +passengers +passenger's +passer +passer's +passerby +passerby's +passersby +passim +passing +passingly +passing's +passion +passion's +dispassion's +dispassion +passionate +passionately +dispassionately +dispassionate +passionflower +passionflowers +passionflower's +passionless +passive +passively +passives +passiveness +passive's +passiveness +passiveness's +passivity +passivity's +passivization +passivize +passivizing +passivized +passivizes +passkey +passkeys +passkey's +passphrase +passphrases +passport +passports +passport's +password +passwords +password's +past +pasts +past's +repasts +repast's +repast +pasta +pastas +pasta's +paste +pasting +pasted +pastes +paste's +pasteboard +pasteboard's +pastel +pastels +pastel's +pastern +pasterns +pastern's +pasteurization +pasteurization's +pasteurize +pasteurizing +pasteurized +pasteurizer +pasteurizers +pasteurizes +pasteurized +unpasteurized +pasteurizer +pasteurizer's +pastiche +pastiches +pastiche's +pastie +pastille +pastilles +pastille's +pastime +pastimes +pastime's +pastiness +pastiness's +pastor +pastors +pastor's +pastoral +pastorals +pastoral's +pastorate +pastorates +pastorate's +pastrami +pastrami's +pastry +pastries +pastry's +pasturage +pasturage's +pasture +pasturing +pastured +pastures +pasture's +pastureland +pastureland's +pasty +pastiest +pastier +pasties +pastiness +pasty's +pat +pats +pat's +patch +patching +patched +patches +patch's +dispatching +dispatched +dispatches +dispatch's +dispatch +patchily +patchiness +patchiness's +patchouli +patchwork +patchworks +patchwork's +patchy +patchiest +patchier +patchiness +pate +pates +pate's +patella +patellas +patella's +patellae +patent +patently +patenting +patented +patents +patent's +paterfamilias +paterfamiliases +paterfamilias's +paternal +paternally +paternalism +paternalism's +paternalist +paternalists +paternalistic +paternity +paternity's +paternoster +paternosters +paternoster's +path +path's +pathetic +pathetically +pathfinder +pathfinders +pathfinder's +pathless +pathogen +pathogens +pathogen's +pathogenic +pathological +pathologically +pathologist +pathologists +pathologist's +pathology +pathology's +pathos +pathos's +paths +pathway +pathways +pathway's +patience +patience's +patient +patientest +patients +patient's +inpatients +inpatient's +inpatient +patienter +patiently +patina +patinas +patina's +patine +patio +patios +patio's +patisserie +patisseries +patois +patois's +patresfamilias +patriarch +patriarch's +patriarchal +patriarchate +patriarchates +patriarchate's +patriarchs +patriarchy +patriarchies +patriarchy's +patrician +patricians +patrician's +patricidal +patricide +patricides +patricide's +patrimonial +patrimony +patrimonies +patrimony's +patriot +patriots +patriot's +patriotic +unpatriotic +patriotically +patriotism +patriotism's +patrol +patrols +patrol's +patrolled +patrolling +patrolman +patrolman's +patrolmen +patrolwoman +patrolwoman's +patrolwomen +patron +patrons +patron's +patronage +patronages +patronage's +patroness +patronesses +patroness's +patronize +patronizing +patronized +patronizer +patronizers +patronizes +patronizer +patronizer's +patronizing +patronizingly +patronymic +patronymics +patronymic's +patronymically +patroon +patroons +patroon's +patsy +patsies +patsy's +patted +patter +pattering +pattered +patters +patter's +pattern +patterning +patterned +patterns +pattern's +patting +patty +patties +patty's +paucity +paucity's +paunch +paunches +paunch's +paunchy +paunchiest +paunchier +pauper +paupers +pauper's +pauperism +pauperism's +pauperize +pauperizing +pauperized +pauperizes +pause +pausing +paused +pauses +pause's +pave +paving +paved +paves +repaving +repaved +repaves +repave +paved +unpaved +pavement +pavements +pavement's +pavilion +pavilions +pavilion's +paving +pavings +paving's +pavlova +pavlovas +paw +pawing +pawed +paws +paw's +pawl +pawls +pawl's +pawn +pawning +pawned +pawns +pawn's +pawnbroker +pawnbrokers +pawnbroker's +pawnbroking +pawnbroking's +pawnshop +pawnshops +pawnshop's +pawpaw +pawpaws +pawpaw's +pay's +pay +paying +pays +payable +payment +repaying +repays +repayable +repayment +repay +payback +paybacks +payback's +paycheck +paychecks +paycheck's +payday +paydays +payday's +payed +payee +payees +payee's +payer +payers +payer's +payload +payloads +payload's +paymaster +paymasters +paymaster's +payment +payments +payment's +repayments +repayment's +repayment +payoff +payoffs +payoff's +payola +payola's +payout +payouts +payout's +payphone +payphones +payroll +payrolls +payroll's +payslip +payslips +payslip's +paywall +paywalls +paywall's +payware +pct +pd +pea +peas +pea's +peace +peaces +peace's +peaceable +peaceably +peaceful +peacefully +peacefulness +peacefulness +peacefulness's +peacekeeper +peacekeepers +peacekeeper's +peacekeeping +peacekeeping's +peacemaker +peacemakers +peacemaker's +peacemaking +peacemaking's +peacetime +peacetime's +peach +peaches +peach's +peachy +peachiest +peachier +peacock +peacocks +peacock's +peafowl +peafowls +peafowl's +peahen +peahens +peahen's +peak +peaking +peaked +peaks +peak's +peaky +peal +pealing +pealed +peals +peal's +repealing +repealed +repeals +repeal's +repeal +peanut +peanuts +peanut's +pear +pearly +pears +pear's +pearl +pearling +pearled +pearls +pearl's +pearly +pearliest +pearlier +peasant +peasants +peasant's +peasantry +peasantry's +peashooter +peashooters +peashooter's +peat +peat's +peaty +peatiest +peatier +pebble +pebbling +pebbled +pebbles +pebble's +pebbly +pecan +pecans +pecan's +peccadillo +peccadillo's +peccadilloes +peccary +peccaries +peccary's +peck +pecking +pecked +pecker +peckers +pecks +peck's +peckish +pecs +pectic +pectin +pectin's +pectoral +pectorals +pectoral's +pectoralis +peculate +peculation +peculating +peculated +peculates +peculation +peculation's +peculator +peculators +peculator's +peculiar +peculiarly +peculiarity +peculiarities +peculiarity's +pecuniary +pedagogic +pedagogical +pedagogically +pedagogue +pedagogues +pedagogue's +pedagogy +pedagogy's +pedal +pedaling +pedaled +pedals +pedal's +pedalo +pedalos +pedant +pedants +pedant's +pedantic +pedantically +pedantry +pedantry's +peddle +peddling +peddled +peddler +peddlers +peddles +peddler +peddler's +pederast +pederasts +pederast's +pederasty +pederasty's +pedestal +pedestals +pedestal's +pedestrian +pedestrians +pedestrian's +pedestrianization +pedestrianize +pedestrianizing +pedestrianized +pedestrianizes +pediatric +pediatrics +pediatrician +pediatricians +pediatrician's +pediatrics +pediatrics's +pedicab +pedicabs +pedicab's +pedicure +pedicuring +pedicured +pedicures +pedicure's +pedicurist +pedicurists +pedicurist's +pedigree +pedigreed +pedigrees +pedigree's +pediment +pediments +pediment's +pedometer +pedometers +pedometer's +pedophile +pedophiles +pedophilia +peduncle +peduncles +peduncle's +pee +peed +peer +peers +pees +pee's +peeing +peek +peeking +peeked +peeks +peek's +peekaboo +peekaboo's +peel +peeling +peelings +peeled +peeler +peelers +peels +peel's +peeled +unpeeled +peeler +peeler's +peeling +peeling's +peen +peens +peen's +peep +peeping +peeped +peeper +peepers +peeps +peep's +peepbo +peeper +peeper's +peephole +peepholes +peephole's +peepshow +peepshows +peepshow's +peer +peering +peered +peer's +peerage +peerages +peerage's +peeress +peeresses +peeress's +peerless +peeve +peeving +peeved +peeves +peeve's +peevish +peevishly +peevishness +peevishness +peevishness's +peewee +peewees +peewee's +peewit +peewits +peg +pegs +peg's +pegboard +pegboards +pegboard's +pegged +pegging +peignoir +peignoirs +peignoir's +pejoration +pejoration's +pejorative +pejoratively +pejoratives +pejorative's +peke +pekes +peke's +pekineses +pekingese +pekingeses +pekingese's +pekoe +pekoe's +pelagic +pelf +pelf's +pelican +pelicans +pelican's +pellagra +pellagra's +pellet +pelleting +pelleted +pellets +pellet's +pellucid +pelmet +pelmets +pelt +pelting +pelted +pelts +pelt's +pelvic +pelvis +pelvises +pelvis's +pemmican +pemmican's +pen +pen's +penal +penalization +penalization's +penalize +penalizing +penalized +penalizes +penalty +penalties +penalty's +penance +penances +penance's +pence +penchant +penchants +penchant's +pencil +penciling +pencilings +penciled +pencils +pencil's +pend +pending +pended +pends +depending +depended +depends +depend +pendant +pendants +pendant's +pendent +pendents +pendent's +pendulous +pendulum +pendulums +pendulum's +penetrability +penetrability's +penetrable +penetrate +penetrative +penetration +penetrations +penetrating +penetrated +penetrates +penetrating +penetratingly +penetration +penetration's +penfriend +penfriends +penguin +penguins +penguin's +penicillin +penicillin's +penile +peninsula +peninsulas +peninsula's +peninsular +penis +penises +penis's +penitence +penitence's +penitent +penitently +penitents +penitent's +penitential +penitentiary +penitentiaries +penitentiary's +penknife +penknife's +penknives +penlight +penlights +penlight's +penman +penman's +penmanship +penmanship's +penmen +pennant +pennants +pennant's +penned +penniless +penning +pennon +pennons +pennon's +penny +pennies +penny's +pennyweight +pennyweights +pennyweight's +pennyworth +penologist +penologists +penologist's +penology +penology's +pension +pensioning +pensioned +pensioner +pensioners +pensions +pension's +pensionable +pensioner +pensioner's +pensive +pensively +pensiveness +pensiveness +pensiveness's +pent +pentacle +pentacles +pentacle's +pentagon +pentagons +pentagon's +pentagonal +pentagram +pentagrams +pentagram's +pentameter +pentameters +pentameter's +pentathlete +pentathletes +pentathlete's +pentathlon +pentathlons +pentathlon's +penthouse +penthouses +penthouse's +penuche +penuche's +penultimate +penultimates +penultimate's +penumbra +penumbras +penumbra's +penumbrae +penurious +penuriously +penuriousness +penuriousness +penuriousness's +penury +penury's +peon +peons +peon's +peonage +peonage's +peony +peonies +peony's +people +peopling +peopled +peoples +people's +pep +peps +pep's +pepped +pepper +peppering +peppered +peppers +pepper's +peppercorn +peppercorns +peppercorn's +peppermint +peppermints +peppermint's +pepperoni +pepperonis +pepperoni's +peppery +peppiness +peppiness's +pepping +peppy +peppiest +peppier +peppiness +pepsin +pepsin's +peptic +peptics +peptic's +peptide +peptides +peradventure +peradventure's +perambulate +perambulation +perambulations +perambulating +perambulated +perambulates +perambulation +perambulation's +perambulator +perambulators +perambulator's +percale +percales +percale's +perceive +perceiving +perceived +perceives +perceivable +perceived +unperceived +percent +percents +percent's +percentage +percentages +percentage's +percentile +percentiles +percentile's +perceptible +perceptibly +perception +perceptions +perception's +perceptional +perceptive +perceptively +perceptiveness +perceptiveness +perceptiveness's +perceptual +perceptually +perch +perching +perched +perches +perch's +perchance +percipience +percipience's +percipient +percolate +percolation +percolating +percolated +percolates +percolation +percolation's +percolator +percolators +percolator's +percussion +percussion's +repercussion's +repercussion +percussionist +percussionists +percussionist's +percussive +perdition +perdition's +perdurable +peregrinate +peregrination +peregrinations +peregrinating +peregrinated +peregrinates +peregrination +peregrination's +peregrine +peregrines +peregrine's +peremptorily +peremptory +perennial +perennially +perennials +perennial's +perestroika +perestroika's +perfect +perfectly +perfecting +perfected +perfectest +perfecter +perfects +perfectness +perfect's +perfecta +perfectas +perfecta's +perfectibility +perfectibility's +perfectible +perfection +perfections +perfection's +perfectionism +perfectionism's +perfectionist +perfectionists +perfectionist's +perfectness +perfectness's +perfidious +perfidiously +perfidy +perfidies +perfidy's +perforate +perforation +perforations +perforating +perforated +perforates +perforation +perforation's +perforce +perform +performing +performed +performer +performers +performs +performance +performances +performance's +performative +performed +unperformed +performer +performer's +perfume +perfuming +perfumed +perfumer +perfumers +perfumes +perfume's +perfumer +perfumer's +perfumery +perfumeries +perfumery's +perfunctorily +perfunctory +perfusion +pergola +pergolas +pergola's +perhaps +pericardia +pericardial +pericarditis +pericardium +pericardium's +perigee +perigees +perigee's +perihelia +perihelion +perihelion's +peril +periling +periled +perils +peril's +perilous +perilously +perimeter +perimeters +perimeter's +perinatal +perinea +perineum +perineum's +period +periods +period's +periodic +periodical +periodically +periodicals +periodical's +periodicity +periodicity's +periodontal +periodontics +periodontics's +periodontist +periodontists +periodontist's +peripatetic +peripatetics +peripatetic's +peripheral +peripherally +peripherals +peripheral's +periphery +peripheries +periphery's +periphrases +periphrasis +periphrasis's +periphrastic +periscope +periscopes +periscope's +perish +perishing +perished +perisher +perishers +perishes +perishable +perishable +perishables +perishable's +peristalses +peristalsis +peristalsis's +peristaltic +peristyle +peristyles +peristyle's +peritoneal +peritoneum +peritoneums +peritoneum's +peritonitis +peritonitis's +periwig +periwigs +periwig's +periwinkle +periwinkles +periwinkle's +perjure +perjuring +perjured +perjurer +perjurers +perjures +perjurer +perjurer's +perjury +perjuries +perjury's +perk +perking +perked +perks +perk's +perkily +perkiness +perkiness's +perky +perkiest +perkier +perkiness +perm +perming +permed +perms +perm's +permafrost +permafrost's +permanence +permanence's +permanency +permanency's +permanent +permanently +permanents +permanent's +permeability +permeability's +permeable +permeate +permeation +permeating +permeated +permeates +permeation +permeation's +permissible +permissibly +permission +permissions +permission's +permissive +permissively +permissiveness +permissiveness +permissiveness's +permit +permits +permit's +permitted +permitting +permittivity +permutation +permutations +permutation's +permute +permuting +permuted +permutes +pernicious +perniciously +perniciousness +perniciousness +perniciousness's +peroration +perorations +peroration's +peroxide +peroxiding +peroxided +peroxides +peroxide's +perpendicular +perpendicularly +perpendiculars +perpendicular's +perpendicularity +perpendicularity's +perpetrate +perpetration +perpetrating +perpetrated +perpetrates +perpetration +perpetration's +perpetrator +perpetrators +perpetrator's +perpetual +perpetually +perpetuals +perpetual's +perpetuate +perpetuation +perpetuating +perpetuated +perpetuates +perpetuation +perpetuation's +perpetuity +perpetuity's +perplex +perplexing +perplexed +perplexes +perplexed +perplexedly +perplexing +perplexingly +perplexity +perplexities +perplexity's +perquisite +perquisites +perquisite's +persecute +persecution +persecutions +persecuting +persecuted +persecutes +persecution +persecution's +persecutor +persecutors +persecutor's +perseverance +perseverance's +persevere +persevering +persevered +perseveres +persiflage +persiflage's +persimmon +persimmons +persimmon's +persist +persisting +persisted +persists +persistence +persistence's +persistent +persistently +persnickety +person +persons +person's +unpersons +unperson's +unperson +persona +personas +persona's +personable +personae +personage +personages +personage's +personal +personally +personals +personal's +personality +personalities +personality's +personalize +personalizing +personalized +personalizes +depersonalizing +depersonalized +depersonalizes +depersonalize +personalty +personalty's +personification +personification's +personify +personification +personifications +personifying +personified +personifies +personnel +personnel's +perspective +perspectives +perspective's +perspex +perspicacious +perspicaciously +perspicacity +perspicacity's +perspicuity +perspicuity's +perspicuous +perspiration +perspiration's +perspire +perspiring +perspired +perspires +persuade +persuading +persuaded +persuader +persuaders +persuades +persuadable +persuaded +unpersuaded +persuader +persuader's +persuasion +persuasions +persuasion's +persuasive +persuasively +persuasiveness +persuasiveness +persuasiveness's +pert +pertly +pertest +perter +pertness +pertain +pertaining +pertained +pertains +pertinacious +pertinaciously +pertinacity +pertinacity's +pertinence +pertinence's +pertinent +pertinently +pertness +pertness's +perturb +perturbing +perturbed +perturbs +perturbation +perturbations +perturbation's +perturbed +unperturbed +pertussis +pertussis's +peruke +perukes +peruke's +perusal +perusals +perusal's +peruse +perusing +perused +peruses +perv +pervs +pervade +pervading +pervaded +pervades +pervasive +pervasively +pervasiveness +pervasiveness +pervasiveness's +perverse +perversion +perversions +perversely +perverseness +perverseness +perverseness's +perversion +perversion's +perversity +perversity's +pervert +perverting +perverted +perverts +pervert's +peseta +pesetas +peseta's +peskily +peskiness +peskiness's +pesky +peskiest +peskier +peskiness +peso +pesos +peso's +pessary +pessaries +pessimal +pessimism +pessimism's +pessimist +pessimists +pessimist's +pessimistic +pessimistically +pest +pester +pesters +pests +pest's +pester +pestering +pestered +pesticide +pesticides +pesticide's +pestiferous +pestilence +pestilences +pestilence's +pestilent +pestilential +pestle +pestling +pestled +pestles +pestle's +pesto +pesto's +pet +peter +peters +pets +pet's +petabyte +petabytes +petabyte's +petal +petaled +petals +petal's +petard +petards +petard's +petcock +petcocks +petcock's +peter +petering +petered +peter's +petiole +petioles +petiole's +petite +petites +petite's +petition +petitioning +petitioned +petitioner +petitioners +petitions +petition's +petitionary +petitioner +petitioner's +petrel +petrels +petrel's +petrifaction +petrifaction's +petrify +petrifying +petrified +petrifies +petrochemical +petrochemicals +petrochemical's +petrodollar +petrodollars +petrodollar's +petrol +petrol's +petrolatum +petrolatum's +petroleum +petroleum's +petrologist +petrologists +petrologist's +petrology +petrology's +petted +petticoat +petticoats +petticoat's +pettifog +pettifogs +pettifogged +pettifogger +pettifoggers +pettifogger's +pettifoggery +pettifoggery's +pettifogging +pettily +pettiness +pettiness's +petting +petting's +pettish +pettishly +petty +pettiest +pettier +pettiness +petulance +petulance's +petulant +petulantly +petunia +petunias +petunia's +pew +pews +pew's +pewee +pewees +pewee's +pewit +pewits +pewit's +pewter +pewters +pewter's +peyote +peyote's +pf +pfennig +pfennigs +pfennig's +pg +phaeton +phaetons +phaeton's +phage +phages +phagocyte +phagocytes +phagocyte's +phalanger +phalangers +phalanger's +phalanges +phalanx +phalanxes +phalanx's +phalli +phallic +phallocentric +phallocentrism +phallus +phallus's +phantasm +phantasms +phantasm's +phantasmagoria +phantasmagorias +phantasmagoria's +phantasmagorical +phantasmal +phantom +phantoms +phantom's +pharaoh +pharaoh's +pharaohs +pharisaic +pharisee +pharisees +pharisee's +pharmaceutic +pharmaceutics +pharmaceutic's +pharmaceutical +pharmaceuticals +pharmaceutical's +pharmaceutics +pharmaceutics's +pharmacist +pharmacists +pharmacist's +pharmacologic +pharmacological +pharmacologist +pharmacologists +pharmacologist's +pharmacology +pharmacology's +pharmacopoeia +pharmacopoeias +pharmacopoeia's +pharmacotherapy +pharmacy +pharmacies +pharmacy's +pharyngeal +pharynges +pharyngitis +pharyngitis's +pharynx +pharynx's +phase +phasing +phased +phases +phase's +phaseout +phaseouts +phaseout's +phat +pheasant +pheasants +pheasant's +phenacetin +phenacetin's +phenobarbital +phenobarbital's +phenol +phenol's +phenom +phenoms +phenom's +phenomena +phenomenal +phenomenally +phenomenological +phenomenology +phenomenon +phenomenons +phenomenon's +phenotype +pheromone +pheromones +pheromone's +phew +phi +phis +phi's +phial +phials +phial's +philander +philandering +philandered +philanderer +philanderers +philanders +philanderer +philanderer's +philandering +philandering's +philanthropic +philanthropically +philanthropist +philanthropists +philanthropist's +philanthropy +philanthropies +philanthropy's +philatelic +philatelist +philatelists +philatelist's +philately +philately's +philharmonic +philharmonics +philharmonic's +philippic +philippics +philippic's +philistine +philistines +philistine's +philistinism +philistinism's +philodendron +philodendrons +philodendron's +philological +philologist +philologists +philologist's +philology +philology's +philosopher +philosophers +philosopher's +philosophic +philosophical +philosophically +philosophize +philosophizing +philosophized +philosophizer +philosophizers +philosophizes +philosophizer +philosophizer's +philosophy +philosophies +philosophy's +philter +philters +philter's +phish +phishing +phished +phisher +phishers +phisher +phisher's +phlebitis +phlebitis's +phlegm +phlegm's +phlegmatic +phlegmatically +phloem +phloem's +phlox +phlox's +phobia +phobias +phobia's +phobic +phobics +phobic's +phoebe +phoebes +phoebe's +phoenix +phoenixes +phoenix's +phone +phoning +phoned +phones +phone's +phonecard +phonecards +phoneme +phonemes +phoneme's +phonemic +phonemically +phonetic +phonetics +phonetically +phonetician +phoneticians +phonetician's +phonetics +phonetics's +phoneyed +phoneying +phonic +phonics +phonically +phonics +phonics's +phoniness +phoniness's +phonograph +phonograph's +phonographic +phonographs +phonological +phonologically +phonologist +phonologists +phonologist's +phonology +phonology's +phonon +phony +phonying +phonied +phoniest +phonier +phonies +phoniness +phony's +phooey +phosphate +phosphates +phosphate's +phosphodiesterase +phosphor +phosphors +phosphor's +phosphorescence +phosphorescence's +phosphorescent +phosphorescently +phosphoric +phosphorous +phosphorus +phosphorus's +phosphorylation +photo +photoing +photoed +photos +photo's +photocell +photocells +photocell's +photocopier +photocopier's +photocopy +photocopying +photocopied +photocopier +photocopiers +photocopies +photocopy's +photoelectric +photoelectrically +photoengrave +photoengraving +photoengravings +photoengraved +photoengraver +photoengravers +photoengraves +photoengraver +photoengraver's +photoengraving +photoengraving's +photofinishing +photofinishing's +photogenic +photogenically +photograph +photographing +photographed +photographer +photographers +photograph's +photographer +photographer's +photographic +photographically +photographs +rephotographs +photography +photography's +photojournalism +photojournalism's +photojournalist +photojournalists +photojournalist's +photometer +photometers +photometer's +photon +photons +photon's +photosensitive +photostat +photostats +photostat's +photostatic +photostatted +photostatting +photosynthesis +photosynthesis's +photosynthesize +photosynthesizing +photosynthesized +photosynthesizes +photosynthetic +phototropic +phototropism +phototypesetter +phototypesetting +photovoltaic +phrasal +phrase's +phrase +phrasing +phrased +phrases +rephrasing +rephrased +rephrases +rephrase +phrasebook +phrasebooks +phraseology +phraseology's +phrasing +phrasings +phrasing's +phreaking +phrenologist +phrenologists +phrenologist's +phrenology +phrenology's +phyla +phylactery +phylacteries +phylactery's +phylogeny +phylogeny's +phylum +phylum's +phys +physic +physics +physic's +physical +physically +physicals +physical's +physicality +physician +physicians +physician's +physicist +physicists +physicist's +physicked +physicking +physics +physics's +physio +physios +physiognomy +physiognomies +physiognomy's +physiography +physiography's +physiologic +physiological +physiologically +physiologist +physiologists +physiologist's +physiology +physiology's +physiotherapist +physiotherapists +physiotherapist's +physiotherapy +physiotherapy's +physique +physiques +physique's +pi +pith +piing +pied +pier +piers +pis +pi's +pianissimo +pianissimos +pianissimo's +pianist +pianists +pianist's +piano +pianos +piano's +pianoforte +pianofortes +pianoforte's +pianola +pianolas +piaster +piasters +piaster's +piazza +piazzas +piazza's +pibroch +pibroch's +pibrochs +pic +pics +pic's +pica +pica's +picador +picadors +picador's +picante +picaresque +picayune +piccalilli +piccalilli's +piccolo +piccolos +piccolo's +pick +picking +pickings +picked +picker +pickers +picks +pick's +pickax +pickaxing +pickaxed +pickaxes +pickax's +picker +picker's +pickerel +pickerels +pickerel's +picket +picketing +picketed +picketer +picketers +pickets +picket's +pickings +pickings's +pickle +pickling +pickled +pickles +pickle's +pickpocket +pickpockets +pickpocket's +pickup +pickups +pickup's +picky +pickiest +pickier +pickiness +picnic +picnics +picnic's +picnicked +picnicker +picnickers +picnicker's +picnicking +picot +picots +picot's +pictogram +pictograms +pictograph +pictograph's +pictographs +pictorial +pictorially +pictorials +pictorial's +picture +picturing +pictured +pictures +picture's +picturesque +picturesquely +picturesqueness +picturesqueness +picturesqueness's +piddle +piddling +piddled +piddles +piddle's +piddly +pidgin +pidgins +pidgin's +pie +pies +pie's +piebald +piebalds +piebald's +piece +piecing +pieced +pieces +piece's +piecemeal +piecework +pieceworker +pieceworkers +piecework's +pieceworker +pieceworker's +piecrust +piecrusts +piecrust's +pieing +pier +pier's +pierce +piercing +piercings +pierced +pierces +piercing +piercingly +piercing's +piety +piety's +piezoelectric +piffle +piffling +piffle's +pig +pigs +pig's +pigment +pigeon +pigeons +pigeon's +pigeonhole +pigeonholing +pigeonholed +pigeonholes +pigeonhole's +pigged +piggery +piggeries +pigging +piggish +piggishly +piggishness +piggishness +piggishness's +piggy +piggiest +piggier +piggies +piggy's +piggyback +piggybacking +piggybacked +piggybacks +piggyback's +pigheaded +pigheadedly +pigheadedness +pigheadedness +pigheadedness's +piglet +piglets +piglet's +pigment +pigmented +pigments +pigment's +pigmentation +pigmentation's +pigpen +pigpens +pigpen's +pigskin +pigskins +pigskin's +pigsty +pigsties +pigsty's +pigswill +pigtail +pigtails +pigtail's +pike +piking +piked +piker +pikers +pikes +pike's +piker +piker's +pikestaff +pikestaffs +pikestaff's +pilaf +pilafs +pilaf's +pilaster +pilasters +pilaster's +pilchard +pilchards +pilchard's +pile +piling +pilings +piled +piles +pile's +pileup +pileups +pileup's +pilfer +pilfering +pilfered +pilferer +pilferers +pilfers +pilferage +pilferage's +pilferer +pilferer's +pilgrim +pilgrims +pilgrim's +pilgrimage +pilgrimages +pilgrimage's +piling +piling's +pill +pilling +pilled +pills +pill's +pillage +pillaging +pillaged +pillager +pillagers +pillages +pillage's +pillager +pillager's +pillar +pillared +pillars +pillar's +pillbox +pillboxes +pillbox's +pillion +pillions +pillion's +pillock +pillocks +pillory +pillorying +pilloried +pillories +pillory's +pillow +pillowing +pillowed +pillows +pillow's +pillowcase +pillowcases +pillowcase's +pillowslip +pillowslips +pillowslip's +pilot +piloting +piloted +pilots +pilot's +pilothouse +pilothouses +pilothouse's +pimento +pimentos +pimento's +pimiento +pimientos +pimiento's +pimp +pimply +pimping +pimped +pimps +pimp's +pimpernel +pimpernels +pimpernel's +pimple +pimpled +pimples +pimple's +pimply +pimpliest +pimplier +pin +pins +pin's +pinafore +pinafores +pinafore's +pinata +pinatas +pinata's +pinball +pinball's +pincer +pincers +pincer's +pinch +pinching +pinched +pinches +pinch's +pincushion +pincushions +pincushion's +pine's +pine +pining +pined +pines +repining +repined +repines +repine +pineapple +pineapples +pineapple's +pinewood +pinewoods +piney +pinfeather +pinfeathers +pinfeather's +ping +pinging +pinged +ping's +pinhead +pinheads +pinhead's +pinhole +pinholes +pinhole's +pinier +piniest +pinion +pinioning +pinioned +pinions +pinion's +pink +pinking +pinked +pinkest +pinker +pinks +pinkness +pink's +pinkeye +pinkeye's +pinkie +pinkies +pinkie's +pinkish +pinkness +pinkness's +pinko +pinkos +pinko's +pinnacle +pinnacles +pinnacle's +pinnate +pinned +unpinned +pinning +unpinning +pinny +pinnies +pinochle +pinochle's +pinon +pinons +pinon's +pinpoint +pinpointing +pinpointed +pinpoints +pinpoint's +pinprick +pinpricks +pinprick's +pinsetter +pinsetters +pinsetter's +pinstripe +pinstriped +pinstripes +pinstripe's +pint +pints +pint's +pinto +pintos +pinto's +pinup +pinups +pinup's +pinwheel +pinwheeling +pinwheeled +pinwheels +pinwheel's +pinyin +pinyin's +pinyon +pinyons +pinyon's +pioneer +pioneering +pioneered +pioneers +pioneer's +pious +piously +piousness +piousness +piousness's +pip +piping +piped +piper +pipers +pips +pip's +pipe +pipes +pipe's +pipeline +pipelines +pipeline's +piper +piper's +pipette +pipettes +pipette's +pipework +piping +piping's +pipit +pipits +pipit's +pipped +pippin +pippins +pippin's +pipping +pipsqueak +pipsqueaks +pipsqueak's +piquancy +piquancy's +piquant +piquantly +pique +piquing +piqued +piques +pique's +piracy +piracy's +piranha +piranhas +piranha's +pirate +pirating +pirated +pirates +pirate's +piratical +piratically +pirogi +pirogi's +piroshki +piroshki's +pirouette +pirouetting +pirouetted +pirouettes +pirouette's +piscatorial +pismire +pismires +pismire's +piss +pissing +pissed +pisser +pissers +pisses +piss's +pissoir +pissoirs +pistachio +pistachios +pistachio's +piste +pistes +pistil +pistils +pistil's +pistillate +pistol +pistols +pistol's +piston +pistons +piston's +pit +pits +pit's +pita +pitas +pita's +pitapat +pitapats +pitapat's +pitch +pitching +pitched +pitcher +pitchers +pitches +pitch's +pitchblende +pitchblende's +pitcher +pitcher's +pitchfork +pitchforking +pitchforked +pitchforks +pitchfork's +pitchman +pitchman's +pitchmen +piteous +piteously +piteousness +piteousness +piteousness's +pitfall +pitfalls +pitfall's +pith +pith's +pithead +pitheads +pithily +pithiness +pithiness's +pithy +pithiest +pithier +pithiness +pitiable +pitiably +pitiful +pitifully +pitiless +pitilessly +pitilessness +pitilessness +pitilessness's +piton +pitons +piton's +pitta +pittas +pittance +pittances +pittance's +pitted +pitting +pituitary +pituitaries +pituitary's +pity +pitying +pitied +pities +pity's +pitying +pityingly +pivot +pivoting +pivoted +pivots +pivot's +pivotal +pix +pix's +pixel +pixels +pixel's +pixie +pixies +pixie's +pizza +pizzas +pizza's +pizzazz +pizzazz's +pizzeria +pizzerias +pizzeria's +pizzicati +pizzicato +pizzicato's +pj's +pk +pkg +pkt +pkwy +pl +placard +placarding +placarded +placards +placard's +placate +placation +placating +placated +placates +placation +placation's +placatory +place's +place +placing +placed +places +placement +replacing +displacing +replaced +displaced +replaces +displaces +replacement +displacement +replace +displace +placebo +placebos +placebo's +placed +unplaced +placeholder +placeholders +placeholder's +placekick +placekicking +placekicked +placekicker +placekickers +placekicks +placekick's +placekicker +placekicker's +placement +placements +placement's +replacements +displacements +replacement's +displacement's +replacement +displacement +placenta +placentas +placenta's +placental +placentals +placer +placers +placer's +placid +placidly +placidity +placidity's +placings +placket +plackets +placket's +plagiarism +plagiarisms +plagiarism's +plagiarist +plagiarists +plagiarist's +plagiarize +plagiarizing +plagiarized +plagiarizer +plagiarizers +plagiarizes +plagiarizer +plagiarizer's +plagiary +plagiary's +plague +plaguing +plagued +plagues +plague's +plaice +plaid +plaids +plaid's +plain +plainly +plainest +plainer +plains +plainness +plain's +plainchant +plainclothes +plainclothesman +plainclothesman's +plainclothesmen +plainness +plainness's +plainsman +plainsman's +plainsmen +plainsong +plainsong's +plainspoken +plaint +plaintive +plaints +plaint's +plaintiff +plaintiffs +plaintiff's +plaintive +plaintively +plait +plaiting +plaited +plaits +plait's +plan +planer +planers +plans +plan's +planar +plane's +plane +planing +planed +planes +deplaning +deplaned +deplanes +deplane +planeload +planeloads +planeload's +planer +planer's +planet +planets +planet's +planetarium +planetariums +planetarium's +planetary +plangency +plangency's +plangent +plank +planking +planked +planks +plank's +planking +planking's +plankton +plankton's +planned +unplanned +planner +planners +planner's +planning +plannings +plant +planting +plantings +planted +planter +planters +plants +plant's +plantain +plantains +plantain's +plantar +plantation +plantations +plantation's +planter +planter's +planting +planting's +plantlike +plaque +plaques +plaque's +plash +plashing +plashed +plashes +plash's +plasma +plasma's +plasmon +plaster +plastering +plastered +plasterer +plasterers +plasters +plaster's +plasterboard +plasterboard's +plasterer +plasterer's +plastic +plastics +plastic's +plasticity +plasticity's +plasticize +plasticizing +plasticized +plasticizes +plastique +plat +platen +platens +plating +plated +plats +plat's +plate +plates +plate's +plateau +plateauing +plateaued +plateaus +plateau's +plateful +platefuls +plateful's +platelet +platelets +platelet's +platen +platen's +platform +platforming +platformed +platforms +platform's +plating +plating's +platinum +platinum's +platitude +platitudes +platitude's +platitudinous +platonic +platoon +platooning +platooned +platoons +platoon's +platted +platter +platters +platter's +platting +platy +platy's +platypus +platypuses +platypus's +platys +plaudit +plaudits +plaudit's +plausibility +plausibility's +plausible +plausibly +play +playing +played +plays +play's +replaying +displaying +replayed +displayed +replays +displays +replay's +display's +replay +display +playable +unplayable +displayable +playact +playacting +playacted +playacts +playacting +playacting's +playback +playbacks +playback's +playbill +playbills +playbill's +playbook +playbooks +playbook's +playboy +playboys +playboy's +player +players +player's +playfellow +playfellows +playfellow's +playful +playfully +playfulness +playfulness +playfulness's +playgirl +playgirls +playgirl's +playgoer +playgoers +playgoer's +playground +playgrounds +playground's +playgroup +playgroups +playhouse +playhouses +playhouse's +playlist +playlists +playlist's +playmate +playmates +playmate's +playoff +playoffs +playoff's +playpen +playpens +playpen's +playroom +playrooms +playroom's +playschool +playschools +plaything +playthings +plaything's +playtime +playtime's +playwright +playwrights +playwright's +plaza +plazas +plaza's +plea +pleas +plea's +plead +pleading +pleadings +pleaded +pleader +pleaders +pleads +pleader +pleader's +pleading +pleadingly +pleading's +pleasant +pleasantly +pleasantest +pleasantness +unpleasantly +unpleasantness +unpleasant +pleasanter +pleasantness +pleasantness's +unpleasantness's +unpleasantness +pleasantry +pleasantries +pleasantry's +please +pleasing +pleased +pleases +displeasing +displeased +displeases +displease +pleasing +pleasingly +pleasings +pleasurably +pleasure +pleasuring +pleasured +pleasures +pleasure's +pleasurable +pleasureful +pleat +pleating +pleated +pleats +pleat's +pleb +plebs +plebby +plebe +plebes +plebe's +plebeian +plebeians +plebeian's +plebiscite +plebiscites +plebiscite's +plectra +plectrum +plectrums +plectrum's +pledge +pledging +pledged +pledges +pledge's +plenary +plenaries +plenary's +plenipotentiary +plenipotentiaries +plenipotentiary's +plenitude +plenitudes +plenitude's +plenteous +plentiful +plentifully +plenty +plenty's +plenum +plenums +pleonasm +pleonasms +pleonasm's +plethora +plethora's +pleura +pleura's +pleurae +pleurisy +pleurisy's +plexus +plexuses +plexus's +pliability +pliability's +pliable +pliancy +pliancy's +pliant +pliantly +pliers +pliers's +plight +plighting +plighted +plights +plight's +plimsoll +plimsolls +plinth +plinth's +plinths +plod +plods +plodded +plodder +plodders +plodder's +plodding +ploddings +plonk +plonking +plonked +plonker +plonkers +plonks +plop +plops +plop's +plopped +plopping +plosive +plosives +plot +plots +plot's +plotted +plotter +plotters +plotter's +plotting +plover +plovers +plover's +plow +plowing +plowed +plows +plow's +plowman +plowman's +plowmen +plowshare +plowshares +plowshare's +ploy's +ploy +ploys +pluck +plucking +plucked +plucks +pluck's +pluckily +pluckiness +pluckiness's +plucky +pluckiest +pluckier +pluckiness +plug's +plug +plugs +unplugs +unplug +plugged +unplugged +plugging +unplugging +plughole +plugholes +plugin +plugins +plugin's +plum +pluming +plumed +plums +plum's +plumage +plumage's +plumb +plumbing +plumbings +plumbed +plumber +plumbers +plumbs +plumb's +plumbed +unplumbed +plumber +plumber's +plumbing +plumbing's +plume +plumes +plume's +plummet +plummeting +plummeted +plummets +plummet's +plummy +plump +plumply +plumping +plumped +plumpest +plumper +plumps +plumpness +plump's +plumpness +plumpness's +plumy +plumiest +plumier +plunder +plundering +plundered +plunderer +plunderers +plunders +plunder's +plunderer +plunderer's +plunge +plunging +plunged +plunger +plungers +plunges +plunge's +plunger +plunger's +plunk +plunking +plunked +plunks +plunk's +pluperfect +pluperfects +pluperfect's +plural +plurals +plural's +pluralism +pluralism's +pluralist +pluralists +pluralist's +pluralistic +plurality +pluralities +plurality's +pluralization +pluralization's +pluralize +pluralizing +pluralized +pluralizes +plus +pluses +plus's +plush +plushly +plushest +plusher +plushness +plush's +plushness +plushness's +plushy +plushiest +plushier +plutocracy +plutocracies +plutocracy's +plutocrat +plutocrats +plutocrat's +plutocratic +plutonium +plutonium's +pluvial +ply +plying +plied +plies +ply's +replying +replied +replies +reply's +reply +plywood +plywood's +pm +pneumatic +pneumatically +pneumococcal +pneumococci +pneumococcus +pneumonia +pneumonia's +poach +poaching +poached +poacher +poachers +poaches +poacher +poacher's +poaching +poaching's +pock +pocking +pocked +pocks +pock's +pocket +pocketing +pocketed +pockets +pocket's +pocketbook +pocketbooks +pocketbook's +pocketful +pocketfuls +pocketful's +pocketknife +pocketknife's +pocketknives +pockmark +pockmarking +pockmarked +pockmarks +pockmark's +pod +pods +pod's +podcast +podcasting +podcasts +podcast's +podded +podding +podiatrist +podiatrists +podiatrist's +podiatry +podiatry's +podium +podiums +podium's +poem +poems +poem's +poesy +poesy's +poet +poets +poet's +poetaster +poetasters +poetaster's +poetess +poetesses +poetess's +poetic +poetics +poetical +poetically +poetry +poetry's +pogrom +pogroms +pogrom's +poi +poi's +poignancy +poignancy's +poignant +poignantly +poinciana +poincianas +poinciana's +poinsettia +poinsettias +poinsettia's +point +pointing +pointed +pointer +pointers +points +point's +pointblank +pointed +pointedly +pointer +pointer's +pointillism +pointillism's +pointillist +pointillists +pointillist's +pointless +pointlessly +pointlessness +pointlessness +pointlessness's +pointy +pointiest +pointier +poise +poising +poised +poises +poise's +poison +poisoning +poisonings +poisoned +poisoner +poisoners +poisons +poison's +poisoner +poisoner's +poisoning +poisoning's +poisonous +poisonously +poke +poking +poked +poker +pokers +pokes +poke's +poker +poker's +pokey +pokeys +pokey's +poky +pokiest +pokier +pol +poling +poled +pols +pol's +polar +polarity +polarities +polarity's +polarization +polarization's +depolarization's +depolarization +polarize +polarizing +polarized +polarizes +depolarizing +depolarized +depolarizes +depolarize +pole +poles +pole's +poleaxe +poleaxing +poleaxed +poleaxes +polecat +polecats +polecat's +polemic +polemics +polemic's +polemical +polemically +polemicist +polemicists +polemicist's +polemics +polemics's +polestar +polestars +polestar's +police +policing +policed +polices +police's +policeman +policeman's +policemen +policewoman +policewoman's +policewomen +policy +policies +policy's +policyholder +policyholders +policyholder's +policymaker +policymakers +polio +polios +polio's +poliomyelitis +poliomyelitis's +polish +polishing +polished +polisher +polishers +polishes +polish's +polished +unpolished +polisher +polisher's +politburo +politburos +politburo's +polite +politely +politest +politer +politeness +politeness +politeness's +politesse +politesse's +politic +politics +political +politically +politician +politicians +politician's +politicization +politicization's +politicize +politicizing +politicized +politicizes +depoliticizing +depoliticized +depoliticizes +depoliticize +politicking +politicking's +politico +politicos +politico's +politics +politics's +polity +polities +polity's +polka +polkaing +polkaed +polkas +polka's +poll +pollen +polling +polled +polls +poll's +pollack +pollacks +pollack's +pollard +pollards +pollen +pollen's +pollinate +pollination +pollinating +pollinated +pollinates +pollination +pollination's +pollinator +pollinators +pollinator's +polling +polling's +polliwog +polliwogs +polliwog's +pollster +pollsters +pollster's +pollutant +pollutants +pollutant's +pollute +pollution +polluting +polluted +polluter +polluters +pollutes +polluted +unpolluted +polluter +polluter's +pollution +pollution's +polo +polo's +polonaise +polonaises +polonaise's +polonium +polonium's +poltergeist +poltergeists +poltergeist's +poltroon +poltroons +poltroon's +poly +polyacrylamide +polyamory +polyamories +polyandrous +polyandry +polyandry's +polyclinic +polyclinics +polyclinic's +polyester +polyesters +polyester's +polyethylene +polyethylene's +polygamist +polygamists +polygamist's +polygamous +polygamy +polygamy's +polyglot +polyglots +polyglot's +polygon +polygons +polygon's +polygonal +polygraph +polygraphing +polygraphed +polygraph's +polygraphs +polyhedral +polyhedron +polyhedrons +polyhedron's +polymath +polymath's +polymaths +polymer +polymers +polymer's +polymeric +polymerization +polymerization's +polymerize +polymerizing +polymerized +polymerizes +polymorphic +polymorphous +polynomial +polynomials +polynomial's +polyp +polyps +polyp's +polyphonic +polyphony +polyphony's +polypropylene +polypropylene's +polys +polysemous +polystyrene +polystyrene's +polysyllabic +polysyllable +polysyllables +polysyllable's +polytechnic +polytechnics +polytechnic's +polytheism +polytheism's +polytheist +polytheists +polytheist's +polytheistic +polythene +polyunsaturate +polyunsaturated +polyunsaturates +polyurethane +polyurethanes +polyurethane's +polyvinyl +pom +poms +pomade +pomading +pomaded +pomades +pomade's +pomander +pomanders +pomander's +pomegranate +pomegranates +pomegranate's +pommel +pommeling +pommeled +pommels +pommel's +pommy +pommies +pomp +pomp's +pompadour +pompadoured +pompadours +pompadour's +pompano +pompanos +pompano's +pompom +pompoms +pompom's +pomposity +pomposity's +pompous +pompously +pompousness +pompousness +pompousness's +ponce +poncing +ponced +ponces +poncho +ponchos +poncho's +poncy +pond +ponds +pond's +ponder +pondering +pondered +ponderer +ponderers +ponders +ponderer +ponderer's +ponderous +ponderously +ponderousness +ponderousness +ponderousness's +pone +pones +pone's +pong +ponging +ponged +pongs +pongee +pongee's +poniard +poniards +poniard's +pontiff +pontiffs +pontiff's +pontifical +pontifically +pontificate +pontificating +pontificated +pontificates +pontificate's +pontoon +pontoons +pontoon's +pony +ponying +ponied +ponies +pony's +ponytail +ponytails +ponytail's +poo +pooing +pooed +poos +pooch +pooching +pooched +pooches +pooch's +poodle +poodles +poodle's +poof +poofs +poof's +poofter +poofters +pooh +poohing +poohed +pooh's +poohs +pool +pooling +pooled +pools +pool's +poolroom +poolrooms +poolroom's +poolside +poolsides +poop +pooping +pooped +poops +poop's +poor +poorly +poorest +poorer +poorness +poorboy +poorboy's +poorhouse +poorhouses +poorhouse's +poorness +poorness's +pop +pops +pop's +popcorn +popcorn's +pope +popes +pope's +popgun +popguns +popgun's +popinjay +popinjays +popinjay's +poplar +poplars +poplar's +poplin +poplin's +popover +popovers +popover's +poppa +poppas +poppa's +poppadom +poppadoms +popped +popper +poppers +popper's +poppet +poppets +popping +poppy +poppies +poppy's +poppycock +poppycock's +populace +populaces +populace's +popular +popularly +popularity +popularity's +unpopularity's +unpopularity +popularization +popularization's +popularize +popularizing +popularized +popularizes +populate +populating +populated +populates +repopulating +depopulating +repopulated +depopulated +repopulates +depopulates +repopulate +depopulate +populated +unpopulated +population +population's +depopulation's +depopulation +populations +populism +populism's +populist +populists +populist's +populous +populousness +populousness +populousness's +popup +popups +popup's +porcelain +porcelains +porcelain's +porch +porches +porch's +porcine +porcupine +porcupines +porcupine's +pore +poring +pored +pores +pore's +porgy +porgies +porgy's +pork +porker +porkers +pork's +porker +porker's +porky +porkiest +porkier +porkies +porky's +porn +porn's +porno +porno's +pornographer +pornographers +pornographer's +pornographic +pornographically +pornography +pornography's +porosity +porosity's +porous +porousness +porousness +porousness's +porphyritic +porphyry +porphyry's +porpoise +porpoising +porpoised +porpoises +porpoise's +porridge +porridge's +porringer +porringers +porringer's +port's +report's +port +porting +ported +ports +reporting +deporting +disporting +reported +deported +disported +reports +deports +disports +report +deport +disport +portability +portability's +portable +portables +portable's +portage +portaging +portaged +portages +portage's +portal +portals +portal's +portcullis +portcullises +portcullis's +portend +portending +portended +portends +portent +portents +portent's +portentous +portentously +portentousness +porter +porters +porter's +reporters +reporter's +reporter +porterhouse +porterhouses +porterhouse's +portfolio +portfolios +portfolio's +porthole +portholes +porthole's +portico +portico's +porticoes +portiere +portieres +portiere's +portion +portioning +portioned +portions +portion's +proportioning +proportioned +proportions +proportion's +proportion +portliness +portliness's +portly +portliest +portlier +portliness +portmanteau +portmanteaus +portmanteau's +portrait +portraits +portrait's +portraitist +portraitists +portraitist's +portraiture +portraiture's +portray +portraying +portrayed +portrays +portrayal +portrayals +portrayal's +portulaca +portulaca's +pose's +repose's +pose +posing +posed +poses +reposing +deposing +disposing +proposing +reposed +deposed +disposed +proposed +reposes +deposes +disposes +proposes +repose +depose +dispose +propose +poser +posers +poser's +disposers +proposers +disposer's +proposer's +disposer +proposer +poseur +poseurs +poseur's +posh +poshest +posher +posit +positive +positing +posited +posits +position +positions +position's +depositions +dispositions +propositions +deposition's +disposition's +proposition's +deposition +disposition +proposition +positional +dispositional +propositional +positioned +propositioned +positioning +repositioning +propositioning +positive +positively +positives +positiveness +positive's +positiveness +positiveness's +positivism +positivist +positivists +positron +positrons +positron's +poss +posse +posses +posse's +possess +possessive +possessing +possessed +possesses +repossessing +dispossessing +repossessed +dispossessed +repossesses +dispossesses +repossess +dispossess +possession +possessions +possession's +repossessions +repossession's +repossession +possessive +possessively +possessives +possessiveness +possessive's +possessiveness +possessiveness's +possessor +possessors +possessor's +possibility +possibilities +possibility's +possible +possibles +possible's +possibly +possum +possums +possum's +post +posting +postings +posted +poster +posters +posts +post's +postage +postage's +postal +postbag +postbags +postbox +postboxes +postcard +postcards +postcard's +postcode +postcodes +postcolonial +postconsonantal +postdate +postdating +postdated +postdates +postdoc +postdocs +postdoc's +postdoctoral +poster +poster's +posterior +posteriors +posterior's +posterity +posterity's +postgraduate +postgraduates +postgraduate's +posthaste +posthumous +posthumously +posthypnotic +postie +posties +postilion +postilions +postilion's +postindustrial +posting +posting's +postlude +postludes +postlude's +postman +postman's +postmark +postmarking +postmarked +postmarks +postmark's +postmaster +postmasters +postmaster's +postmen +postmenopausal +postmeridian +postmistress +postmistresses +postmistress's +postmodern +postmodernism +postmodernism's +postmodernist +postmodernists +postmodernist's +postmortem +postmortems +postmortem's +postnasal +postnatal +postoperative +postpaid +postpartum +postpone +postponing +postponed +postpones +postponement +postponement +postponements +postponement's +postprandial +postscript +postscripts +postscript's +postseason +postseasons +postseason's +postsynaptic +postulate +postulation +postulations +postulating +postulated +postulates +postulate's +postulation +postulation's +postural +posture +posturing +posturings +postured +postures +posture's +posturing +posturing's +postwar +postwoman +postwomen +posy +posies +posy's +pot +pots +pot's +depots +depot's +depot +potability +potability's +potable +potables +potable's +potash +potash's +potassium +potassium's +potato +potato's +potatoes +potbelly +potbellied +potbellies +potbelly's +potboiler +potboilers +potboiler's +potency +potency's +potent +potently +potentate +potentates +potentate's +potential +potentially +potentials +potential's +potentiality +potentialities +potentiality's +potentiate +potentiating +potentiated +potentiates +potful +potfuls +potful's +pothead +potheads +pothead's +pother +pothering +pothered +pothers +pother's +potherb +potherbs +potherb's +potholder +potholders +potholder's +pothole +potholing +potholed +potholer +potholers +potholes +pothole's +pothook +pothooks +pothook's +potion +potions +potion's +potluck +potlucks +potluck's +potpie +potpies +potpie's +potpourri +potpourris +potpourri's +potsherd +potsherds +potsherd's +potshot +potshots +potshot's +pottage +pottage's +potted +potter +pottering +pottered +potters +potter's +pottery +potteries +pottery's +potting +potty +pottiest +pottier +potties +pottiness +potty's +pouch +pouching +pouched +pouches +pouch's +pouf +poufs +pouffe +pouffes +poulterer +poulterers +poulterer's +poultice +poulticing +poulticed +poultices +poultice's +poultry +poultry's +pounce +pouncing +pounced +pounces +pounce's +pound's +pound +pounding +pounded +pounds +propounding +propounded +propounds +propound +poundage +poundage's +pounding +poundings +pounding's +pour +pouring +pourings +poured +pours +pout +pouting +pouted +pouter +pouters +pouts +pout's +pouter +pouter's +poverty +poverty's +pow +powder +powdering +powdered +powders +powder's +powdery +power +powering +powered +powers +power's +powerboat +powerboats +powerboat's +powerful +powerfully +powerhouse +powerhouses +powerhouse's +powerless +powerlessly +powerlessness +powerlessness +powerlessness's +powwow +powwowing +powwowed +powwows +powwow's +pox +poxes +pox's +pp +ppm +ppr +pr +practicability +practicability's +practicably +practical +practically +practicals +practical's +practicality +practicalities +practicality's +practice +practicing +practiced +practices +practice's +practicable +practiced +unpracticed +practicum +practicums +practicum's +practitioner +practitioners +practitioner's +praetor +praetors +praetor's +praetorian +pragmatic +pragmatics +pragmatic's +pragmatical +pragmatically +pragmatism +pragmatism's +pragmatist +pragmatists +pragmatist's +prairie +prairies +prairie's +praise +praising +praised +praises +praise's +dispraising +dispraised +dispraises +dispraise's +dispraise +praiseworthiness +praiseworthiness's +praiseworthy +praiseworthiness +praline +pralines +praline's +pram +prams +pram's +prance +prancing +pranced +prancer +prancers +prances +prance's +prancer +prancer's +prancing +prancingly +prang +pranging +pranged +prangs +prank +pranks +prank's +prankster +pranksters +prankster's +praseodymium +praseodymium's +prat +prats +prate +prating +prated +prater +praters +prates +prate's +prater +prater's +pratfall +pratfalls +pratfall's +prattle +prattling +prattled +prattler +prattlers +prattles +prattle's +prattler +prattler's +prawn +prawning +prawned +prawns +prawn's +pray +praying +prayed +prayer +prayers +prays +prayer +prayer's +prayerful +prayerfully +preach +preaching +preached +preacher +preachers +preaches +preachment +preacher +preacher's +preachment +preachment's +preachy +preachiest +preachier +preadolescence +preadolescences +preadolescence's +preadolescent +preamble +preambling +preambled +preambles +preamble's +prearrange +prearranging +prearranged +prearranges +prearrangement +prearrangement +prearrangement's +preassigned +precancel +precanceling +precanceled +precancels +precancel's +precancerous +precarious +precariously +precariousness +precariousness +precariousness's +precast +precaution +precautions +precaution's +precautionary +precede +preceding +preceded +precedes +precedence +precedence's +precedent +precedents +precedent's +precept +precepts +precept's +preceptor +preceptors +preceptor's +precinct +precincts +precinct's +preciosity +preciosity's +precious +preciously +preciousness +preciousness +preciousness's +precipice +precipices +precipice's +precipitant +precipitants +precipitant's +precipitate +precipitation +precipitations +precipitately +precipitating +precipitated +precipitates +precipitate's +precipitation +precipitation's +precipitous +precipitously +precis +precis's +precise +precision +precisely +precising +precised +precisest +preciser +precises +preciseness +preciseness +preciseness's +precision +precision's +preclude +precluding +precluded +precludes +preclusion +preclusion's +precocious +precociously +precociousness +precociousness +precociousness's +precocity +precocity's +precognition +precognition's +precognitive +precolonial +preconceive +preconceiving +preconceived +preconceives +preconception +preconceptions +preconception's +precondition +preconditioning +preconditioned +preconditions +precondition's +precook +precooking +precooked +precooks +precursor +precursors +precursor's +precursory +predate +predating +predated +predates +predator +predators +predator's +predatory +predawn +predecease +predeceasing +predeceased +predeceases +predecessor +predecessors +predecessor's +predefined +predesignate +predesignating +predesignated +predesignates +predestination +predestination's +predestine +predestining +predestined +predestines +predetermination +predetermination's +predetermine +predetermining +predetermined +predeterminer +predeterminers +predetermines +predeterminer +predeterminer's +predicable +predicament +predicaments +predicament's +predicate +predicative +predication +predicating +predicated +predicates +predicate's +predication +predication's +predicative +predicatively +predict +predictive +predicting +predicted +predicts +predictable +predictability +predictability's +unpredictability's +unpredictability +predictable +unpredictable +predictably +unpredictably +prediction +predictions +prediction's +predictor +predictors +predictor's +predigest +predigesting +predigested +predigests +predilection +predilections +predilection's +predispose +predisposing +predisposed +predisposes +predisposition +predispositions +predisposition's +predominance +predominance's +predominant +predominantly +predominate +predominately +predominating +predominated +predominates +preemie +preemies +preemie's +preeminence +preeminence's +preeminent +preeminently +preempt +preemptive +preempting +preempted +preempts +preemption +preemption's +preemptive +preemptively +preen +preening +preened +preens +preexist +preexisting +preexisted +preexists +preexistence +preexistence's +pref +prefab +prefabs +prefab's +prefabbed +prefabbing +prefabricate +prefabrication +prefabricating +prefabricated +prefabricates +prefabrication +prefabrication's +preface +prefacing +prefaced +prefaces +preface's +prefatory +prefect +prefects +prefect's +prefecture +prefectures +prefecture's +prefer +prefers +preferable +preferment +preferably +preference +preferences +preference's +preferential +preferentially +preferment +preferment's +preferred +preferring +prefigure +prefiguring +prefigured +prefigures +prefix +prefixing +prefixed +prefixes +prefix's +preform +preforming +preformed +preforms +prefrontal +pregame +pregames +pregame's +pregnancy +pregnancies +pregnancy's +pregnant +preheat +preheating +preheated +preheats +prehensile +prehistorian +prehistorians +prehistoric +prehistorical +prehistorically +prehistory +prehistory's +prehuman +preinstalled +prejudge +prejudging +prejudged +prejudges +prejudgment +prejudgments +prejudgment's +prejudice +prejudicing +prejudiced +prejudices +prejudice's +prejudiced +unprejudiced +prejudicial +prekindergarten +prekindergartens +prekindergarten's +prelacy +prelacy's +prelate +prelates +prelate's +prelim +prelims +prelim's +preliminary +preliminaries +preliminary's +preliterate +prelude +preludes +prelude's +premarital +premature +prematurely +premed +premeds +premed's +premedical +premeditate +premeditation +premeditating +premeditated +premeditates +premeditated +unpremeditated +premeditation +premeditation's +premenstrual +premier +premiering +premiered +premiers +premier's +premiere +premieres +premiere's +premiership +premierships +premiership's +premise +premising +premised +premises +premise's +premium +premiums +premium's +premix +premixing +premixed +premixes +premolar +premolars +premolar's +premonition +premonitions +premonition's +premonitory +prenatal +prenatally +prenup +prenups +prenup's +prenuptial +preoccupation +preoccupations +preoccupation's +preoccupy +preoccupying +preoccupied +preoccupies +preoperative +preordain +preordaining +preordained +preordains +preowned +prep +preps +prep's +prepackage +prepackaging +prepackaged +prepackages +prepacked +prepaid +preparation +preparations +preparation's +preparatory +prepare +preparing +prepared +prepares +prepared +preparedness +unpreparedness +unprepared +preparedness +preparedness's +unpreparedness's +unpreparedness +prepay +prepaying +prepays +prepayment +prepayment +prepayments +prepayment's +preponderance +preponderances +preponderance's +preponderant +preponderantly +preponderate +preponderating +preponderated +preponderates +preposition +prepositions +preposition's +prepositional +prepositionally +prepossess +prepossessing +prepossessed +prepossesses +prepossessing +unprepossessing +prepossession +prepossessions +prepossession's +preposterous +preposterously +prepped +prepping +preppy +preppiest +preppier +preppies +preppy's +prepubescence +prepubescence's +prepubescent +prepubescents +prepubescent's +prepuce +prepuces +prepuce's +prequel +prequels +prequel's +prerecord +prerecording +prerecorded +prerecords +preregister +preregistering +preregistered +preregisters +preregistration +preregistration's +prerequisite +prerequisites +prerequisite's +prerogative +prerogatives +prerogative's +pres +presage +presaging +presaged +presages +presage's +presbyopia +presbyopia's +presbyter +presbyters +presbyter's +presbytery +presbyteries +presbytery's +preschool +preschooler +preschoolers +preschools +preschool's +preschooler +preschooler's +prescience +prescience's +prescient +presciently +prescribe +prescribing +prescribed +prescribes +prescript +prescriptive +prescripts +prescript's +prescription +prescriptions +prescription's +prescriptive +prescriptively +preseason +preseasons +preseason's +presence +presences +presence's +present +presently +presenting +presented +presenter +presenters +presents +present's +presentable +presentment +presentably +presentation +presentations +presentation's +representations +representation's +representation +presenter +presenter's +presentiment +presentiments +presentiment's +presentment +presentments +presentment's +preservation +preservation's +preservationist +preservationists +preservationist's +preservative +preservatives +preservative's +preserve +preserving +preserved +preserver +preservers +preserves +preserve's +preservable +preserver +preserver's +preset +presets +presetting +preshrank +preshrink +preshrinking +preshrinks +preshrunk +preside +presiding +presided +presides +presidency +presidencies +presidency's +president +presidents +president's +presidential +presidium +presidium's +presort +presorting +presorted +presorts +press's +press +pressing +pressed +presses +repressing +depressing +repressed +depressed +represses +depresses +repress +depress +pressed +unpressed +presser +pressers +presser's +pressie +pressies +pressing +pressingly +pressings +pressing's +pressman +pressman's +pressmen +pressure +pressuring +pressured +pressures +pressure's +pressurization +pressurization's +pressurize +pressurizing +pressurized +pressurizes +depressurizing +depressurized +depressurizes +depressurize +pressurizer +pressurizers +pressurizer's +prestidigitation +prestidigitation's +prestige +prestige's +prestigious +presto +prestos +presto's +presumably +presume +presuming +presumed +presumes +presumable +presumption +presumptions +presumption's +presumptive +presumptuous +presumptuously +presumptuousness +presumptuousness +presumptuousness's +presuppose +presupposing +presupposed +presupposes +presupposition +presuppositions +presupposition's +pretax +preteen +preteens +preteen's +pretend +pretending +pretended +pretender +pretenders +pretends +pretender +pretender's +pretense +pretension +pretensions +pretenses +pretense's +pretension +pretension's +pretentious +pretentiously +unpretentiously +unpretentious +pretentiousness +pretentiousness's +preterit +preterits +preterit's +preterm +preternatural +preternaturally +pretest +pretesting +pretested +pretests +pretext +pretexts +pretext's +pretrial +pretrials +prettify +prettifying +prettified +prettifies +prettily +prettiness +prettiness's +pretty +prettying +prettied +prettiest +prettier +pretties +prettiness +pretty's +pretzel +pretzels +pretzel's +prevail +prevailing +prevailed +prevails +prevalence +prevalence's +prevalent +prevaricate +prevarication +prevarications +prevaricating +prevaricated +prevaricates +prevarication +prevarication's +prevaricator +prevaricators +prevaricator's +prevent +preventive +preventing +prevented +prevents +preventable +preventable +unpreventable +preventative +preventatives +preventative's +prevention +prevention's +preventive +preventives +preventive's +preview +previewing +previewed +previewer +previewers +previews +preview's +previous +previously +prevision +previsions +prevision's +prewar +prey +preying +preyed +preys +prey's +prezzie +prezzies +priapic +price's +price +pricing +priced +prices +repricing +repriced +reprices +reprice +priceless +pricey +pricier +priciest +prick +prickly +pricking +pricked +pricker +prickers +pricks +prick's +pricker +pricker's +prickle +prickling +prickled +prickles +prickle's +prickliness +prickliness's +prickly +prickliest +pricklier +prickliness +pride +priding +prided +prides +pride's +prideful +pridefully +prier +prier's +priest +priestly +priests +priest's +priestess +priestesses +priestess's +priesthood +priesthoods +priesthood's +priestliness +priestliness's +priestly +priestliest +priestlier +priestliness +prig +prigs +prig's +priggish +priggishness +priggishness +priggishness's +prim +primly +priming +primed +primer +primers +primness +primacy +primacy's +primal +primarily +primary +primaries +primary's +primate +primates +primate's +prime +primes +prime's +primer +primer's +primeval +priming +priming's +primitive +primitively +primitives +primitiveness +primitive's +primitiveness +primitiveness's +primmer +primmest +primness +primness's +primogenitor +primogenitors +primogenitor's +primogeniture +primogeniture's +primordial +primordially +primp +primping +primped +primps +primrose +primroses +primrose's +primula +primulas +prince +princely +princes +prince's +princedom +princedoms +princedom's +princeliness +princeliness's +princely +princeliest +princelier +princeliness +princess +princesses +princess's +principal +principally +principals +principal's +principality +principalities +principality's +principle +principled +principles +principle's +principled +unprincipled +print +printing +printed +prints +print's +reprinting +reprinted +reprints +reprint's +reprint +printable +unprintable +printer +printers +printer's +printing +printings +printing's +printmaking +printout +printouts +printout's +prion +prions +prior +priors +prior's +prioress +prioresses +prioress's +prioritization +prioritize +prioritizing +prioritized +prioritizes +priority +priorities +priority's +priory +priories +priory's +prism +prisms +prism's +prismatic +prison +prisoner +prisoners +prisons +prison's +prisoner +prisoner's +prissily +prissiness +prissiness's +prissy +prissiest +prissier +prissiness +pristine +prithee +privacy +privacy's +private +privation +privations +privately +privatest +privater +privates +private's +privateer +privateers +privateer's +privation +privations +privation's +deprivations +deprivation's +deprivation +privatization +privatizations +privatization's +privatize +privatizing +privatized +privatizes +privet +privets +privet's +privilege +privileging +privileged +privileges +privilege's +privileged +unprivileged +privily +privy +priviest +privier +privies +privy's +prize +prizing +prized +prizes +prize's +prized +reprized +prizefight +prizefighting +prizefighter +prizefighters +prizefights +prizefight's +prizefighter +prizefighter's +prizefighting +prizefighting's +prizewinner +prizewinners +prizewinner's +prizewinning +pro +pros +pro's +probabilistic +probability +probabilities +probability's +probable +probables +probable's +probably +probate +probation +probate's +probation +probationer +probationers +probation's +probational +probationary +probationer +probationer's +probe +probing +probings +probed +probes +probe's +probable +probity +probity's +problem +problems +problem's +problematic +unproblematic +problematical +problematically +probosces +proboscis +proboscises +proboscis's +procaine +procaine's +procedural +procedure +procedures +procedure's +proceed +proceeding +proceedings +proceeded +proceeds +proceeding +proceeding's +proceeds +proceeds's +process's +process +processing +processed +processes +reprocessing +reprocessed +reprocesses +reprocess +processable +processed +unprocessed +procession +processioning +processioned +processional +processionals +processional's +processor +processors +processor's +proclamation +proclamations +proclamation's +proclivity +proclivities +proclivity's +procrastinate +procrastination +procrastinating +procrastinated +procrastinates +procrastination +procrastination's +procrastinator +procrastinators +procrastinator's +procreate +procreative +proctor +proctoring +proctored +proctors +proctor's +procurement +procurement's +prod +prods +prod's +prodigal +prodigally +prodigals +prodigal's +prodigality +prodigality's +prodigious +prodigiously +prodigy +prodigies +prodigy's +produce's +produce +producing +produced +producer +producers +produces +reproducing +reproduced +reproducer +reproducers +reproduces +reproduce +producer +producer's +reproducer's +reproducer +producible +reproducible +production +productions +production's +reproductions +reproduction's +reproduction +productive +productively +unproductively +unproductive +productiveness +productiveness's +productivity +productivity's +prof +profs +prof's +profanation +profanations +profanation's +profane +profanely +profaning +profaned +profanes +profaneness +profaneness +profaneness's +profanity +profanities +profanity's +professed +professedly +profession +professions +profession's +professional +professionally +professionals +professional's +professionalism +professionalism's +professionalization +professionalize +professionalizing +professionalized +professionalizes +professor +professors +professor's +professorial +professorially +professorship +professorships +professorship's +proffer +proffering +proffered +proffers +proffer's +proficiency +proficiency's +proficient +proficiently +proficients +proficient's +profit +profiting +profited +profitable +profitability +profitability's +profitable +unprofitable +profitably +unprofitably +profiteer +profiteering +profiteered +profiteers +profiteer's +profiteering +profiteering's +profiterole +profiteroles +profiterole's +profitless +profligacy +profligacy's +profligate +profligately +profligates +profligate's +proforma +profound +profoundly +profoundest +profounder +profoundness +profoundness +profoundness's +profundity +profundities +profundity's +profuse +profusely +profuseness +profuseness +profuseness's +progenitor +progenitors +progenitor's +progeny +progeny's +progesterone +progesterone's +progestin +progestins +prognathous +prognoses +prognosis +prognosis's +prognostic +prognostics +prognostic's +prognosticate +prognostication +prognostications +prognosticating +prognosticated +prognosticates +prognostication +prognostication's +prognosticator +prognosticators +prognosticator's +program +programs +reprograms +deprograms +reprogram +deprogram +programmable +programmables +programmable's +programmatic +programmed +reprogrammed +deprogrammed +programmer +programmers +programmer's +programming +programmings +programming's +progress +progressive +progressing +progressed +progresses +progress's +progression +progressions +progression's +progressive +progressively +progressives +progressiveness +progressive's +progressiveness +progressiveness's +prohibit +prohibitive +prohibiting +prohibited +prohibits +prohibition +prohibitions +prohibition's +prohibitionist +prohibitionists +prohibitionist's +prohibitive +prohibitively +prohibitory +project +projecting +projected +projects +project's +projectile +projectiles +projectile's +projection +projections +projection's +projectionist +projectionists +projectionist's +projector +projectors +projector's +prokaryotic +prole +proles +proletarian +proletarians +proletarian's +proletariat +proletariat's +proliferate +proliferation +proliferating +proliferated +proliferates +proliferation +proliferation's +prolific +prolifically +prolix +prolixly +prolixity +prolixity's +prologue +prologues +prologue's +prolongation +prolongations +prolongation's +prom +prom's +promenade +promenading +promenaded +promenades +promenade's +promethium +promethium's +prominence +prominence's +prominent +prominently +promiscuity +promiscuity's +promiscuous +promiscuously +promise +promising +promised +promises +promise's +promising +promisingly +promissory +promo +promo's +promontory +promontories +promontory's +promote +promoting +promoted +promoter +promoters +promoter +promoter's +promotional +prompt +promptly +prompting +promptings +prompted +promptest +prompter +prompters +prompts +promptness +prompt's +prompted +unprompted +prompter +prompter's +prompting +prompting's +promptitude +promptitude's +promptness +promptness's +promulgate +promulgation +promulgating +promulgated +promulgates +promulgation +promulgation's +promulgator +promulgators +promulgator's +prone +proneness +proneness +proneness's +prong +pronged +prongs +prong's +pronghorn +pronghorns +pronghorn's +pronominal +pronominal's +pronounce +pronouncing +pronounced +pronounces +pronouncement +pronounceable +unpronounceable +pronouncement +pronouncements +pronouncement's +pronto +pronunciation +pronunciations +pronunciation's +proof +proofing +proofed +proofs +proof's +reproofing +reproofed +reproofs +reproof's +reproof +proofread +proofreading +proofreader +proofreaders +proofreads +proofreader +proofreader's +prop +props +prop's +propaganda +propaganda's +propagandist +propagandists +propagandist's +propagandize +propagandizing +propagandized +propagandizes +propagate +propagation +propagating +propagated +propagates +propagation +propagation's +propagator +propagators +propagator's +propel +propels +propellant +propellants +propellant's +propelled +propeller +propellers +propeller's +propelling +propensity +propensities +propensity's +proper +properly +properest +properer +proper's +property +propertied +properties +property's +prophecy +prophecies +prophecy's +prophesier +prophesier's +prophesy +prophesying +prophesied +prophesier +prophesiers +prophesies +prophesy's +prophet +prophets +prophet's +prophetess +prophetesses +prophetess's +prophetic +prophetical +prophetically +prophylactic +prophylactics +prophylactic's +prophylaxes +prophylaxis +prophylaxis's +propinquity +propinquity's +propitiate +propitiation +propitiating +propitiated +propitiates +propitiation +propitiation's +propitiatory +propitious +propitiously +proponent +proponents +proponent's +proportion +proportions +proportion's +disproportions +disproportion's +disproportion +proportional +proportionally +proportionals +proportionality +proportionate +proportionately +disproportionately +disproportionate +proposal +proposals +proposal's +propped +propping +proprietary +proprietaries +proprietary's +proprieties +proprieties's +proprietor +proprietors +proprietor's +proprietorial +proprietorially +proprietorship +proprietorship's +proprietress +proprietresses +proprietress's +propriety +proprieties +propriety's +propulsion +propulsion's +propulsive +prorate +prorating +prorated +prorates +prorogation +prorogation's +prorogue +proroguing +prorogued +prosaic +prosaically +proscenium +prosceniums +proscenium's +prosciutto +prosciutto's +proscribe +proscribing +proscribed +proscription +proscriptions +proscription's +prose +prose's +prosecute +prosecution +prosecutions +prosecuting +prosecuted +prosecutes +prosecution +prosecution's +prosecutor +prosecutors +prosecutor's +proselyte +proselyting +proselyted +proselytes +proselyte's +proselytism +proselytism's +proselytize +proselytizing +proselytized +proselytizer +proselytizers +proselytizes +proselytizer +proselytizer's +prosocial +prosody +prosodies +prosody's +prospect +prospective +prospecting +prospected +prospects +prospect's +prospective +prospectively +prospector +prospectors +prospector's +prospectus +prospectuses +prospectus's +prosper +prospering +prospered +prospers +prosperity +prosperity's +prosperous +prosperously +prostate +prostates +prostate's +prostheses +prosthesis +prosthesis's +prosthetic +prostitute +prostitution +prostituting +prostituted +prostitutes +prostitute's +prostitution +prostitution's +prostrate +prostration +prostrations +prostrating +prostrated +prostrates +prostration +prostration's +prosy +prosiest +prosier +protactinium +protactinium's +protagonist +protagonists +protagonist's +protean +protect +protective +protecting +protected +protects +protected +unprotected +protection +protections +protection's +protectionism +protectionism's +protectionist +protectionists +protectionist's +protective +protectively +protectiveness +protectiveness +protectiveness's +protector +protectors +protector's +protectorate +protectorates +protectorate's +protege +proteges +protege's +protegee +protegees +protein +proteins +protein's +protestant +protestants +protestation +protestations +protestation's +protocol +protocols +protocol's +proton +protons +proton's +protoplasm +protoplasm's +protoplasmic +prototype +prototyping +prototypes +prototype's +prototypical +protozoa +protozoan +protozoans +protozoan's +protozoic +protract +protracting +protracted +protrude +protruding +protruded +protrudes +protrusile +protrusion +protrusions +protrusion's +protuberance +protuberances +protuberance's +protuberant +proud +proudly +proudest +prouder +prov +proven +provable +provability +provability's +provably +prove +proving +proved +proves +reproving +disproving +reproved +disproved +reproves +disproves +reprove +disprove +proved +unproved +proven +unproven +provenance +provenances +provenance's +provender +provender's +provenience +provenience's +proverbial +proverbially +provide +providing +provided +provider +providers +provides +provided +unprovided +providence +providence's +provident +providently +providential +providentially +provider +provider's +province +provinces +province's +provincial +provincially +provincials +provincial's +provincialism +provincialism's +provisional +provisionally +proviso +provisos +proviso's +provocateur +provocateurs +provocative +provocatively +provocativeness +provocativeness +provocativeness's +provoke +provoking +provoked +provoker +provokers +provokes +provoked +unprovoked +provoker +provoker's +provoking +provokingly +provolone +provolone's +provost +provosts +provost's +prow +prows +prow's +prowess +prowess's +prowl +prowling +prowled +prowler +prowlers +prowls +prowl's +prowler +prowler's +proximal +proximate +proximity +proximity's +proxy +proxies +proxy's +prude +prudes +prude's +prudence +prudence's +prudent +prudently +prudential +prudentially +prudery +prudery's +prudish +prudishly +prudishness +prudishness +prudishness's +prune +pruning +pruned +pruner +pruners +prunes +prune's +pruner +pruner's +prurience +prurience's +prurient +pruriently +pry +prying +pried +priest +prier +priers +pries +pry's +psalm +psalms +psalm's +psalmist +psalmists +psalmist's +psaltery +psalteries +psaltery's +psephologist +psephologists +psephology +pseud +pseuds +pseudo +pseudos +pseudonym +pseudonyms +pseudonym's +pseudonymous +pseudoscience +pseudosciences +pseudoscience's +pseudy +pshaw +pshaws +pshaw's +psi +psis +psi's +psittacosis +psittacosis's +psoriasis +psoriasis's +psst +psych +psyching +psyched +psyches +psych's +psyche +psyche's +psychedelia +psychedelic +psychedelics +psychedelic's +psychedelically +psychiatric +psychiatrist +psychiatrists +psychiatrist's +psychiatry +psychiatry's +psychic +psychics +psychic's +psychical +psychically +psycho +psychos +psycho's +psychoactive +psychoanalyses +psychoanalysis +psychoanalysis's +psychoanalyst +psychoanalysts +psychoanalyst's +psychoanalytic +psychoanalytical +psychoanalytically +psychoanalyze +psychoanalyzing +psychoanalyzed +psychoanalyzes +psychobabble +psychobabble's +psychodrama +psychodramas +psychodrama's +psychogenic +psychokinesis +psychokinetic +psychological +psychologically +psychologist +psychologists +psychologist's +psychology +psychologies +psychology's +psychometric +psychoneuroses +psychoneurosis +psychoneurosis's +psychopath +psychopath's +psychopathic +psychopathology +psychopaths +psychopathy +psychopathy's +psychopharmacology +psychophysiology +psychos +psychoses +psychosis +psychosis's +psychosomatic +psychotherapist +psychotherapists +psychotherapist's +psychotherapy +psychotherapies +psychotherapy's +psychotic +psychotics +psychotic's +psychotically +psychotropic +psychotropics +psychotropic's +psychs +pt +dept +ptarmigan +ptarmigans +ptarmigan's +pterodactyl +pterodactyls +pterodactyl's +ptomaine +ptomaines +ptomaine's +pub +pubs +pub's +pubertal +puberty +puberty's +pubes +pubes's +pubescence +pubescence's +pubescent +pubic +pubis +pubis's +public +public's +republic's +republic +publican +publicans +publican's +republicans +republican's +republican +publication +publications +publication's +republications +republication's +republication +publicist +publicists +publicist's +publicity +publicity's +publicize +publicizing +publicized +publicizes +publicly +publish +publishing +published +publishes +republishing +republished +republishes +republish +publishable +published +unpublished +publisher +publishers +publisher's +publishing +publishing's +puce +puce's +puck +pucker +puckers +pucks +puck's +pucker +puckering +puckered +pucker's +puckish +puckishly +puckishness +puckishness +puckishness's +pud +puds +pudding +puddings +pudding's +puddle +puddling +puddled +puddles +puddle's +puddling +puddling's +pudenda +pudendum +pudendum's +pudginess +pudginess's +pudgy +pudgiest +pudgier +pudginess +pueblo +pueblos +pueblo's +puerile +puerility +puerility's +puerperal +puff +puffing +puffed +puffer +puffers +puffs +puff's +puffball +puffballs +puffball's +puffer +puffer's +puffin +puffins +puffin's +puffiness +puffiness's +puffy +puffiest +puffier +puffiness +pug +pugs +pug's +pugilism +pugilism's +pugilist +pugilists +pugilist's +pugilistic +pugnacious +pugnaciously +pugnaciousness +pugnaciousness +pugnaciousness's +pugnacity +pugnacity's +puke +puking +puked +pukes +puke's +pukka +pulchritude +pulchritude's +pulchritudinous +pule +puling +puled +pules +pull +pulling +pulled +puller +pullers +pulls +pull's +pullback +pullbacks +pullback's +puller +puller's +pullet +pullets +pullet's +pulley +pulleys +pulley's +pullout +pullouts +pullout's +pullover +pullovers +pullover's +pulmonary +pulp +pulping +pulped +pulps +pulp's +pulpiness +pulpiness's +pulpit +pulpits +pulpit's +pulpwood +pulpwood's +pulpy +pulpiest +pulpier +pulpiness +pulsar +pulsars +pulsar's +pulsate +pulsation +pulsations +pulsating +pulsated +pulsates +pulsation +pulsation's +pulse +pulsing +pulsed +pulses +pulse's +repulsing +repulsed +repulses +repulse's +repulse +pulverization +pulverization's +pulverize +pulverizing +pulverized +pulverizes +puma +pumas +puma's +pumice +pumices +pumice's +pummel +pummeling +pummeled +pummels +pump +pumping +pumped +pumper +pumpers +pumps +pump's +pumper +pumper's +pumpernickel +pumpernickel's +pumpkin +pumpkins +pumpkin's +pun +puns +pun's +punch +punching +punched +puncher +punchers +punches +punch's +punchbag +punchbags +puncheon +puncheons +puncheon's +puncher +puncher's +punchline +punchlines +punchy +punchiest +punchier +punctilio +punctilio's +punctilious +punctiliously +punctiliousness +punctiliousness +punctiliousness's +punctual +punctually +punctuality +punctuality's +punctuate +punctuation +punctuating +punctuated +punctuates +punctuation +punctuation's +puncture +puncturing +punctured +punctures +puncture's +pundit +pundits +pundit's +punditry +punditry's +pungency +pungency's +pungent +pungently +puniness +puniness's +punish +punishing +punished +punishes +punishable +punishment +punished +unpunished +punishing +punishingly +punishment +punishments +punishment's +punitive +punitively +punk +punkest +punker +punks +punk's +punned +punnet +punnets +punning +punster +punsters +punster's +punt +punting +punted +punter +punters +punts +punt's +punter +punter's +puny +puniest +punier +puniness +pup +pups +pup's +pupa +pupa's +pupae +pupal +pupate +pupating +pupated +pupates +pupil +pupils +pupil's +pupped +puppet +puppets +puppet's +puppeteer +puppeteers +puppeteer's +puppetry +puppetry's +pupping +puppy +puppies +puppy's +purblind +purchase +purchasing +purchased +purchaser +purchasers +purchases +purchase's +purchasable +purchaser +purchaser's +purdah +purdah's +pure +purely +purest +purer +pureness +purebred +purebreds +purebred's +puree +pureed +purees +puree's +pureeing +pureness +pureness's +purgative +purgatives +purgative's +purgatorial +purgatory +purgatories +purgatory's +purge +purging +purged +purger +purgers +purges +purge's +purger +purger's +purification +purification's +purifier +purifier's +purify +purification +purifying +purified +purifier +purifiers +purifies +purine +purines +purine's +purism +purism's +purist +purists +purist's +puristic +puritan +puritans +puritan's +puritanical +puritanically +puritanism +puritanism's +purity +purity's +purl +purling +purled +purls +purl's +purlieu +purlieus +purlieu's +purloin +purloining +purloined +purloins +purple +purplest +purpler +purples +purple's +purplish +purport +purporting +purported +purports +purport's +purported +purportedly +purpose +purposely +purposing +purposed +purposes +purpose's +purposed +repurposed +purposeful +purposefully +purposefulness +purposefulness +purposefulness's +purposeless +purposelessly +purposelessness +purr +purring +purred +purrs +purr's +purse +pursing +pursed +purser +pursers +purses +purse's +purser +purser's +pursuance +pursuance's +pursuant +pursue +pursuing +pursued +pursuer +pursuers +pursues +pursuer +pursuer's +pursuit +pursuits +pursuit's +purulence +purulence's +purulent +purvey +purveying +purveyed +purveys +purveyance +purveyance's +purveyor +purveyors +purveyor's +purview +purview's +pus +pus's +push +pushing +pushed +pusher +pushers +pushes +push's +pushbike +pushbikes +pushcart +pushcarts +pushcart's +pushchair +pushchairs +pusher +pusher's +pushily +pushiness +pushiness's +pushover +pushovers +pushover's +pushpin +pushpins +pushy +pushiest +pushier +pushiness +pusillanimity +pusillanimity's +pusillanimous +pusillanimously +puss +pusses +puss's +pussy +pussiest +pussier +pussies +pussy's +pussycat +pussycats +pussycat's +pussyfoot +pussyfooting +pussyfooted +pussyfoots +pustular +pustule +pustules +pustule's +put +puts +put's +inputs +input's +input +putative +putout +putouts +putout's +putrefaction +putrefaction's +putrefactive +putrefy +putrefying +putrefied +putrefies +putrescence +putrescence's +putrescent +putrid +putsch +putsches +putsch's +putt +putting +putted +putter +putters +putts +putt's +putted +inputted +puttee +puttees +puttee's +putter +puttering +puttered +putterer +putterers +putter's +putterer +putterer's +putting +inputting +putty +puttying +puttied +putties +putty's +putz +putzes +puzzle +puzzling +puzzled +puzzler +puzzlers +puzzles +puzzle's +puzzlement +puzzlement +puzzlement's +puzzler +puzzler's +pvt +pwn +pwning +pwned +pwns +pyelonephritis +pygmy +pygmies +pygmy's +pylon +pylons +pylon's +pylori +pyloric +pylorus +pylorus's +pyorrhea +pyorrhea's +pyramid +pyramiding +pyramided +pyramids +pyramid's +pyramidal +pyre +pyres +pyre's +pyrimidine +pyrimidines +pyrimidine's +pyrite +pyrites +pyrite's +pyrites +pyrites's +pyromania +pyromania's +pyromaniac +pyromaniacs +pyromaniac's +pyrotechnic +pyrotechnics +pyrotechnical +pyrotechnics +pyrotechnics's +pyruvate +python +pythons +python's +pyx +pyxes +pyx's +pzazz +q +qr +qt +qts +qty +qua +quack +quacking +quacked +quacks +quack's +quackery +quackery's +quad +quads +quad's +quadrangle +quadrangles +quadrangle's +quadrangular +quadrant +quadrants +quadrant's +quadraphonic +quadratic +quadratics +quadratic's +quadrature +quadrennial +quadrennium +quadrenniums +quadrennium's +quadriceps +quadricepses +quadriceps's +quadrilateral +quadrilaterals +quadrilateral's +quadrille +quadrillion +quadrillions +quadrilles +quadrille's +quadrillion +quadrillion's +quadriplegia +quadriplegia's +quadriplegic +quadriplegics +quadriplegic's +quadrivium +quadrivium's +quadruped +quadrupeds +quadruped's +quadrupedal +quadruple +quadrupling +quadrupled +quadruples +quadruple's +quadruplet +quadruplets +quadruplet's +quadruplicate +quadruplication +quadruplicating +quadruplicated +quadruplicates +quadruplicate's +quadruplication +quadruplication's +quaff +quaffing +quaffed +quaffs +quaff's +quagmire +quagmires +quagmire's +quahog +quahogs +quahog's +quail +quailing +quailed +quails +quail's +quaint +quaintly +quaintest +quainter +quaintness +quaintness +quaintness's +quake +quaking +quaked +quakes +quake's +quaky +qualification +qualification's +disqualification's +disqualification +qualified +unqualified +qualifier +qualifiers +qualifier's +qualify +qualification +qualifications +qualifying +qualified +qualifies +disqualification +disqualifications +disqualifying +disqualified +disqualifies +disqualify +qualitative +qualitatively +quality +qualities +quality's +qualm +qualms +qualm's +qualmish +quandary +quandaries +quandary's +quango +quangos +quanta +quantifiable +quantification +quantification's +quantifier +quantifier's +quantify +quantification +quantifying +quantified +quantifier +quantifiers +quantifies +quantitation +quantitative +quantitatively +quantity +quantities +quantity's +quantization +quantize +quantum +quantum's +quarantine +quarantining +quarantined +quarantines +quarantine's +quark +quarks +quark's +quarrel +quarreling +quarreled +quarreler +quarrelers +quarrels +quarrel's +quarreler +quarreler's +quarrelsome +quarrelsomeness +quarrelsomeness +quarrelsomeness's +quarry +quarrying +quarried +quarries +quarry's +quart +quarts +quart's +quarter +quarterly +quartering +quartered +quarters +quarter's +quarterback +quarterbacking +quarterbacked +quarterbacks +quarterback's +quarterdeck +quarterdecks +quarterdeck's +quarterfinal +quarterfinals +quarterfinal's +quarterly +quarterlies +quarterly's +quartermaster +quartermasters +quartermaster's +quarterstaff +quarterstaff's +quarterstaves +quartet +quartets +quartet's +quarto +quartos +quarto's +quartz +quartz's +quasar +quasars +quasar's +quash +quashing +quashed +quashes +quasi +quatrain +quatrains +quatrain's +quaver +quavering +quavered +quavers +quaver's +quavery +quay +quays +quay's +quayside +quaysides +queasily +queasiness +queasiness's +queasy +queasiest +queasier +queasiness +queen +queenly +queening +queened +queens +queen's +queenly +queenliest +queenlier +queer +queerly +queering +queered +queerest +queerer +queers +queerness +queer's +queerness +queerness's +quell +quelling +quelled +quells +quench +quenching +quenched +quencher +quenchers +quenches +quenchable +quenchable +unquenchable +quencher +quencher's +quenchless +querulous +querulously +querulousness +querulousness +querulousness's +query +querying +queried +queries +query's +ques +quesadilla +quesadillas +quesadilla's +quest +quests +quest's +requests +inquests +conquests +request's +inquest's +conquest's +request +inquest +conquest +quested +questing +question +questioning +questionings +questioned +questioner +questioners +questions +question's +questionable +questionable +unquestionable +questionably +unquestionably +questioned +unquestioned +questioner +questioner's +questioning +questioningly +questioning's +questionnaire +questionnaires +questionnaire's +queue +queuing +queued +queues +queue's +quibble +quibbling +quibbled +quibbler +quibblers +quibbles +quibble's +quibbler +quibbler's +quiche +quiches +quiche's +quick +quicken +quickens +quickly +quickest +quicker +quickness +quick's +quicken +quickening +quickened +quickfire +quickie +quickies +quickie's +quicklime +quicklime's +quickness +quickness's +quicksand +quicksands +quicksand's +quicksilver +quicksilver's +quickstep +quicksteps +quickstep's +quid +quids +quid's +quiescence +quiescence's +quiescent +quiescently +quiet +quieten +quietens +quietly +quieting +quieted +quietest +quieter +quiets +quietness +quiet's +quieten +quietening +quietened +quietism +quietness +quietness's +quietude +quietude's +inquietude's +disquietude's +inquietude +disquietude +quietus +quietuses +quietus's +quiff +quiffs +quill +quills +quill's +quilt +quilting +quilted +quilter +quilters +quilts +quilt's +quilter +quilter's +quilting +quilting's +quin +quins +quince +quinces +quince's +quine +quines +quinidine +quinine +quinine's +quinoa +quinsy +quinsy's +quint +quints +quint's +quintessence +quintessences +quintessence's +quintessential +quintessentially +quintet +quintets +quintet's +quintuple +quintupling +quintupled +quintuples +quintuple's +quintuplet +quintuplets +quintuplet's +quip +quips +quip's +quipped +quipping +quipster +quipsters +quipster's +quire's +quire +quires +requires +inquires +require +inquire +quirk +quirking +quirked +quirks +quirk's +quirkiness +quirkiness's +quirky +quirkiest +quirkier +quirkiness +quirt +quirts +quirt's +quisling +quislings +quisling's +quit +quits +quitclaim +quitclaims +quitclaim's +quite +quittance +quittance's +quitter +quitters +quitter's +quitting +quiver +quivering +quivered +quivers +quiver's +quivery +quixotic +quixotically +quiz +quiz's +quizzed +quizzer +quizzers +quizzer's +quizzes +quizzical +quizzically +quizzing +quo +quoth +quoin +quoins +quoin's +quoit +quoiting +quoited +quoits +quoit's +quondam +quorate +inquorate +quorum +quorums +quorum's +quot +quotable +quota +quotas +quota's +quotability +quotability's +quotation +quotations +quotation's +quote's +quote +quoting +quoted +quotes +unquoting +unquoted +unquotes +unquote +quotidian +quotient +quotients +quotient's +qwerty +r +rs +rabbet +rabbeting +rabbeted +rabbets +rabbet's +rabbi +rabbis +rabbi's +rabbinate +rabbinate's +rabbinic +rabbinical +rabbit +rabbiting +rabbited +rabbits +rabbit's +rabble +rabbles +rabble's +rabid +rabidly +rabidness +rabidness +rabidness's +rabies +rabies's +raccoon +raccoon's +race +racing +raced +racer +racers +races +race's +racecourse +racecourses +racecourse's +racegoer +racegoers +racehorse +racehorses +racehorse's +raceme +racemes +raceme's +racer +racer's +racetrack +racetracks +racetrack's +raceway +raceways +raceway's +racial +racially +racialism +racialism's +racialist +racialists +racialist's +racily +raciness +raciness's +racing +racing's +racism +racism's +racist +racists +racist's +rack +racking +racked +racks +rack's +racket +racketing +racketed +rackets +racket's +racketeer +racketeering +racketeered +racketeers +racketeer's +racketeering +racketeering's +raconteur +raconteurs +raconteur's +racquetball +racquetballs +racquetball's +racy +raciest +racier +raciness +rad +rads +rad's +radar +radars +radar's +radarscope +radarscopes +radarscope's +raddled +radial +radially +radials +radial's +radian +radians +radiance +radiance's +radiant +radiantly +radiate +radiation +radiations +radiating +radiated +radiates +radiation +radiation's +radiator +radiators +radiator's +radical +radically +radicals +radical's +radicalism +radicalism's +radicalization +radicalization's +radicalize +radicalizing +radicalized +radicalizes +radicchio +radicchio's +radii +radio +radioing +radioed +radios +radio's +radioactive +radioactively +radioactivity +radioactivity's +radiocarbon +radiocarbon's +radiogram +radiograms +radiogram's +radiographer +radiographers +radiographer's +radiography +radiography's +radioisotope +radioisotopes +radioisotope's +radiologist +radiologists +radiologist's +radiology +radiology's +radioman +radioman's +radiomen +radiometer +radiometers +radiometer's +radiometric +radiometry +radiometry's +radiophone +radiophones +radiophone's +radioscopy +radioscopy's +radiosonde +radiosondes +radiosonde's +radiosurgery +radiotelegraph +radiotelegraph's +radiotelegraphs +radiotelegraphy +radiotelegraphy's +radiotelephone +radiotelephones +radiotelephone's +radiotherapist +radiotherapists +radiotherapist's +radiotherapy +radiotherapy's +radish +radishes +radish's +radium +radium's +radius +radius's +radon +radon's +raffia +raffia's +raffish +raffishly +raffishness +raffishness +raffishness's +raffle +raffling +raffled +raffles +raffle's +raft +rafting +rafted +rafter +rafters +rafts +raft's +rafter +rafter's +rafting +rafting's +rag +raging +raged +rags +rag's +raga +ragas +raga's +ragamuffin +ragamuffins +ragamuffin's +ragbag +ragbag's +rage +rages +rage's +ragga +ragged +raggedly +raggedest +raggeder +raggedness +raggedness +raggedness's +raggedy +raggediest +raggedier +ragging +raging +ragingly +raglan +raglans +raglan's +ragout +ragouts +ragout's +ragtag +ragtags +ragtime +ragtime's +ragweed +ragweed's +ragwort +rah +raid +raiding +raided +raider +raiders +raids +raid's +raider +raider's +rail's +rail +railing +railed +rails +derailing +derailed +derails +derail +railcard +railcards +railing +railings +railing's +raillery +railleries +raillery's +railroad +railroading +railroaded +railroader +railroaders +railroads +railroad's +railroader +railroader's +railroading +railroading's +railway +railways +railway's +railwayman +railwaymen +raiment +raiment's +rain +raining +rained +rains +rain's +rainbow +rainbows +rainbow's +raincoat +raincoats +raincoat's +raindrop +raindrops +raindrop's +rainfall +rainfalls +rainfall's +rainmaker +rainmakers +rainmaker's +rainmaking +rainmaking's +rainproof +rainstorm +rainstorms +rainstorm's +rainwater +rainwater's +rainy +rainiest +rainier +raise +raising +raised +raiser +raisers +raises +raise's +raiser +raiser's +raisin +raisins +raisin's +rajah +rajah's +rajahs +rake +raking +raked +rakes +rake's +rakish +rakishly +rakishness +rakishness +rakishness's +rally +rallying +rallied +rallies +rally's +ram +rams +ram's +ramble +rambling +ramblings +rambled +rambler +ramblers +rambles +ramble's +rambler +rambler's +rambunctious +rambunctiously +rambunctiousness +rambunctiousness +rambunctiousness's +ramekin +ramekins +ramekin's +ramie +ramie's +ramification +ramification's +ramify +ramification +ramifications +ramifying +ramified +ramifies +ramjet +ramjets +ramjet's +rammed +ramming +ramp +ramping +ramps +ramp's +rampage +rampaging +rampaged +rampages +rampage's +rampancy +rampancy's +rampant +rampantly +rampart +ramparts +rampart's +ramrod +ramrods +ramrod's +ramrodded +ramrodding +ramshackle +ran +reran +ranch +ranching +ranched +rancher +ranchers +ranches +ranch's +rancher +rancher's +ranching +ranching's +rancid +rancidness +rancidity +rancidity's +rancidness +rancidness's +rancor +rancor's +rancorous +rancorously +rand +rand's +randiness +randiness's +random +randomly +randoms +randomness +randomization +randomization's +randomize +randomizing +randomized +randomizes +randomness +randomnesses +randomness's +randy +randiest +randier +randiness +ranee +ranees +ranee's +rang +ranger +rangers +range's +range +ranging +ranged +ranges +deranging +deranged +deranges +derange +rangefinder +rangefinders +ranger +ranger's +ranginess +ranginess's +rangy +rangiest +rangier +ranginess +rank +rankly +ranking +rankings +ranked +rankest +ranker +ranks +rankness +rank's +ranking +ranking's +rankle +rankling +rankled +rankles +rankness +rankness's +ransack +ransacking +ransacked +ransacks +ransom +ransoming +ransomed +ransomer +ransomers +ransoms +ransom's +ransomer +ransomer's +ransomware +rant +ranting +rantings +ranted +ranter +ranters +rants +rant's +ranter +ranter's +rap +raping +raped +raper +rapers +raps +rap's +rapacious +rapaciously +rapaciousness +rapaciousness +rapaciousness's +rapacity +rapacity's +rape +rapes +rape's +raper +raper's +rapeseed +rapeseed's +rapid +rapidly +rapidest +rapider +rapids +rapidness +rapid's +rapidity +rapidity's +rapidness +rapidness's +rapier +rapiers +rapier's +rapine +rapine's +rapist +rapists +rapist's +rapped +rappel +rappels +rappel's +rappelled +rappelling +rapper +rappers +rapper's +rapping +rapport +rapports +rapport's +rapporteur +rapporteurs +rapprochement +rapprochements +rapprochement's +rapscallion +rapscallions +rapscallion's +rapt +raptly +raptness +raptness +raptness's +raptor +raptors +rapture +raptures +rapture's +rapturous +rapturously +rare +rarely +raring +rared +rarest +rarer +rares +rareness +rarebit +rarebits +rarebit's +rarefaction +rarefaction's +rarefy +rarefying +rarefied +rarefies +rareness +rareness's +rarity +rarities +rarity's +rascal +rascally +rascals +rascal's +rash +rashly +rashest +rasher +rashers +rashes +rashness +rash's +rasher +rasher's +rashness +rashness's +rasp +rasping +rasped +rasps +rasp's +raspberry +raspberries +raspberry's +raspy +raspiest +raspier +raster +rat +rats +rat's +ratatouille +ratatouille's +ratbag +ratbags +ratchet +ratcheting +ratcheted +ratchets +ratchet's +rate +ration +rations +rating +ratings +rated +rater +raters +rates +rate's +rated +unrated +ratepayer +ratepayers +rater +rater's +rather +rathskeller +rathskellers +rathskeller's +ratification +ratification's +ratifier +ratifier's +ratify +ratification +ratifying +ratified +ratifier +ratifiers +ratifies +rating +rating's +ratio +ratios +ratio's +ratiocinate +ratiocination +ratiocinating +ratiocinated +ratiocinates +ratiocination +ratiocination's +ration +rationing +rationed +ration's +rational +rationally +rationals +rational's +rationale +rationales +rationale's +rationalism +rationalism's +rationalist +rationalists +rationalist's +rationalistic +rationality +rationality's +rationalization +rationalizations +rationalization's +rationalize +rationalizing +rationalized +rationalizes +ratlike +ratline +ratlines +ratline's +rattan +rattans +rattan's +ratted +ratter +ratters +ratter's +ratting +rattle +rattling +rattlings +rattled +rattler +rattlers +rattles +rattle's +rattlebrain +rattlebrained +rattlebrains +rattlebrain's +rattler +rattler's +rattlesnake +rattlesnakes +rattlesnake's +rattletrap +rattletraps +rattletrap's +rattly +rattrap +rattraps +rattrap's +ratty +rattiest +rattier +raucous +raucously +raucousness +raucousness +raucousness's +raunchily +raunchiness +raunchiness's +raunchy +raunchiest +raunchier +raunchiness +ravage +ravaging +ravaged +ravager +ravagers +ravages +ravage's +ravager +ravager's +ravages +ravages's +rave +raving +ravings +raved +raver +ravers +raves +rave's +ravel's +ravel +raveling +raveled +ravels +unraveling +unraveled +unravels +unravel +raveling +ravelings +raven +ravening +ravened +ravens +raven's +ravenous +ravenously +ravine +ravines +ravine's +raving +raving's +ravioli +raviolis +ravioli's +ravish +ravishing +ravished +ravisher +ravishers +ravishes +ravishment +ravisher +ravisher's +ravishing +ravishingly +ravishment +ravishment's +raw +rawest +rawer +rawness +raw's +rawboned +rawhide +rawhide's +rawness +rawness's +ray +rays +ray's +rayon +rayon's +raze +razing +razed +razes +razor +razors +razor's +razorback +razorbacks +razorback's +razz +razzing +razzed +razzes +razz's +razzmatazz +razzmatazz's +rcpt +rd +re +rive +rely +ring +rings +red +rest +res +re's +reach +reaching +reached +reaches +reach's +reachable +reachable +unreachable +reacquire +reacquiring +reacquired +reacquires +react +reactive +reactance +reactant +reactants +reactant's +reactionary +reactionaries +reactionary's +reactivity +read +reading +readings +reader +readers +reads +read's +readable +readability +readabilities +readability's +reader +reader's +readership +readerships +readership's +readily +readiness +readiness's +reading +reading's +readmitted +readout +readouts +readout's +ready +readying +readied +readiest +readier +readies +readiness +reafforestation +real +really +realest +realer +reals +realness +real's +realism +realism's +realist +realists +realist's +realistic +unrealistic +realistically +unrealistically +realities +reality +reality's +unreality's +unreality +realization +realizations +realization's +realize +realizing +realized +realizes +realizable +realized +unrealized +realm +realms +realm's +realness +realness's +realpolitik +realpolitik's +realty +realty's +ream +reaming +reamed +reamer +reamers +reams +ream's +reamer +reamer's +reap +reaping +reaped +reaper +reapers +reaps +reaper +reaper's +rear +rearing +reared +rears +rear's +rearguard +rearguards +rearguard's +rearmost +rearward +rearwards +reason +reasoning +reasoned +reasoner +reasoners +reasons +reason's +reasonable +reasonable +reasonableness +unreasonableness +unreasonable +reasonableness +reasonableness's +unreasonableness's +unreasonableness +reasonably +unreasonably +reasoner +reasoner's +reasoning +reasoning's +reassuring +reassuringly +rebate +rebate's +rebel +rebels +rebel's +rebellion +rebellions +rebellion's +rebellious +rebelliously +rebelliousness +rebelliousness +rebelliousness's +rebid +rebids +rebidding +rebirth +rebirth's +reboil +reboiling +reboiled +reboils +rebuild +rebuilding +rebuilds +rebuke +rebuking +rebuked +rebukes +rebuke's +rebuking +rebukingly +rebuttal +rebuttals +rebuttal's +rec'd +rec +rec's +recalcitrance +recalcitrance's +recalcitrant +recant +recanting +recanted +recants +recantation +recantations +recantation's +recap +recaps +recap's +recapitalization +recce +recces +recd +receipt +receipting +receipted +receipts +receipt's +receivables +receivables's +receive +receiving +received +receiver +receivers +receives +receivable +receiver +receiver's +receivership +receivership's +recent +recently +recentest +recenter +recentness +recentness +recentness's +receptacle +receptacles +receptacle's +reception +receptions +reception's +receptionist +receptionists +receptionist's +receptive +receptively +receptiveness +receptiveness +receptiveness's +receptivity +receptivity's +receptor +receptors +receptor's +recess +recessive +recessing +recessed +recesses +recess's +recessional +recessionals +recessional's +recessionary +recessive +recessives +recessive's +recherche +recidivism +recidivism's +recidivist +recidivists +recidivist's +recipe +recipes +recipe's +recipient +recipients +recipient's +reciprocal +reciprocally +reciprocals +reciprocal's +reciprocate +reciprocation +reciprocating +reciprocated +reciprocates +reciprocation +reciprocation's +reciprocity +reciprocity's +recital +recitals +recital's +recitalist +recitalists +recitalist's +recitative +recitatives +recitative's +reciter +reciters +reciter's +reckless +recklessly +recklessness +recklessness +recklessness's +reckon +reckoning +reckonings +reckoned +reckons +reckoning +reckoning's +reclamation +reclamation's +recline +reclining +reclined +recliner +recliners +reclines +recliner +recliner's +recluse +reclusive +recluses +recluse's +recognizable +unrecognizable +recognizably +unrecognizably +recognize +recognizing +recognized +recognizer +recognizes +recognizable +recognized +unrecognized +recombination +recompense +recompensing +recompensed +recompenses +recompense's +recompilation +recompile +recompiling +recompiled +recon +recons +reconcile +reconciling +reconciled +reconciles +reconcilable +reconciliation +reconciliations +recondite +reconfiguration +reconfigure +reconfigured +reconnaissance +reconnaissances +reconnaissance's +reconnoiter +reconnoitering +reconnoitered +reconnoiters +reconstruct +reconstructive +reconstructed +unreconstructed +recorded +unrecorded +recorder +recorders +recorder's +recording +recordings +recording's +recoup +recouping +recouped +recourse +recourse's +recoverable +unrecoverable +recovery +recoveries +recovery's +recreant +recreants +recreant's +recreational +recriminate +recrimination +recriminations +recriminating +recriminated +recriminates +recrimination +recrimination's +recriminatory +recrudesce +recrudescing +recrudesced +recrudesces +recrudescence +recrudescence's +recrudescent +recruit +recruiting +recruited +recruiter +recruiters +recruits +recruit's +recruitment +recruiter +recruiter's +recruitment +recruitment's +rectal +rectally +rectangle +rectangles +rectangle's +rectangular +rectifiable +rectification +rectification's +rectifier +rectifier's +rectify +rectification +rectifications +rectifying +rectified +rectifier +rectifiers +rectifies +rectilinear +rectitude +rectitude's +recto +rectos +recto's +rector +rectors +rector's +rectory +rectories +rectory's +rectum +rectums +rectum's +recumbent +recuperate +recuperative +recuperation +recuperating +recuperated +recuperates +recuperation +recuperation's +recur +recurs +recurred +recurrence +recurrences +recurrence's +recurring +recursion +recursions +recuse +recusing +recused +recuses +recyclable +recyclables +recyclable's +recycling +recycling's +red +reds +redness +red's +redact +redacting +redacted +redacts +redaction +redaction's +redactor +redactors +redactor's +redbird +redbirds +redbird's +redbreast +redbreasts +redbreast's +redbrick +redcap +redcaps +redcap's +redcoat +redcoats +redcoat's +redcurrant +redcurrants +redden +reddening +reddened +reddens +redder +reddest +reddish +redeem +redeemer +redeemers +redeemable +redeemer +redeemer's +redemption +redemption's +redemptive +redhead +redheaded +redheads +redhead's +redirection +redistrict +redistricting +redistricted +redivide +redividing +redivided +redivides +redlining +redlining's +redneck +rednecks +redneck's +redness +redness's +redo +redoing +redolence +redolence's +redolent +redoubt +redoubts +redoubt's +redoubtable +redoubtably +redound +redounding +redounded +redounds +redraw +redrawing +redraws +redskin +redskins +redskin's +reduce +reducing +reduced +reducer +reducers +reduces +reducer +reducer's +reducible +reductase +reductase's +reduction +reductions +reduction's +reductionist +reductive +redundancy +redundancies +redundancy's +redundant +redundantly +redwood +redwoods +redwood's +redye +redyed +redyes +reediness +reediness's +reedy +reediest +reedier +reediness +reef +reefing +reefed +reefer +reefers +reefs +reef's +reefer +reefer's +reek +reeking +reeked +reeks +reek's +reel's +reel +reeling +reeled +reels +unreeling +unreeled +unreels +unreel +reeve +reeving +reexport +reexporting +reexported +reexports +ref +refers +refs +ref's +refashion +refashioning +refashioned +refashions +refection +refection's +refectory +refectories +refectory's +refer +referable +referee +refereed +referees +referee's +refereeing +reference +referencing +referenced +references +reference's +referendum +referendums +referendum's +referent +referents +referent's +referential +referral +referrals +referral's +referred +referrer +referrers +referrer's +referring +reffed +reffing +refill +refill's +refillable +refined +unrefined +refinement +refinements +refinement's +refiner +refiners +refiner's +refinery +refineries +refitting +reflate +reflation +reflations +reflating +reflated +reflates +reflationary +reflect +reflective +reflecting +reflected +reflects +reflection +reflections +reflection's +reflective +reflectively +reflectivity +reflector +reflectors +reflector's +reflexive +reflexively +reflexives +reflexive's +reflexivity +reflexology +reforge +reforging +reforged +reforges +reform +reformers +reform's +reformat +reformative +reformatory +reformatories +reformatory's +reformatting +reformed +unreformed +reformist +reformists +refortify +refortifying +refortified +refortifies +refract +refractive +refracting +refracted +refracts +refraction +refraction's +refractory +refractories +refractory's +refrain +refraining +refrained +refrains +refrain's +refresh +refreshing +refreshed +refresher +refreshers +refreshes +refreshment +refresher +refresher's +refreshing +refreshingly +refreshment +refreshments +refreshment's +refreshments +refreshments's +refrigerant +refrigerants +refrigerant's +refrigerate +refrigeration +refrigerating +refrigerated +refrigerates +refrigeration +refrigeration's +refrigerator +refrigerators +refrigerator's +refuge +refuges +refuge's +refugee +refugees +refugee's +refulgence +refulgence's +refulgent +refund +refundable +refurbishment +refurbishments +refurbishment's +refusal +refusals +refusal's +refutation +refutations +refutation's +refute +refuting +refuted +refuter +refuters +refutes +refutable +refuter +refuter's +reg +regal +regally +regaling +regaled +regalement +regalement's +regalia +regalia's +regard +regarding +regarded +regards +regard's +disregarding +disregarded +disregards +disregard's +disregard +regardless +regards +regards's +regather +regathering +regathered +regathers +regatta +regattas +regatta's +regency +regencies +regency's +regeneracy +regeneracy's +regenerate +regenerative +regex +regex's +regexp +regexps +reggae +reggae's +regicidal +regicide +regicides +regicide's +regime +regimes +regime's +regimen +regimens +regimen's +regiment +regimenting +regimented +regiments +regiment's +regimental +regimentation +regimentation's +region +regions +region's +regional +regionally +regionalism +regionalisms +regionalism's +register +registering +registered +registers +register's +registered +unregistered +registrant +registrants +registrant's +registrar +registrars +registrar's +registration +registrations +registration's +registry +registries +registry's +regnant +regress +regressive +regressing +regressed +regresses +regress's +regression +regressions +regression's +regret +regrets +regret's +regretful +regretfully +regrettable +regrettably +regretted +regretting +regrind +regrinding +regrinds +reground +regroup +regrouping +regrouped +regroups +regular +regularly +regulars +regular's +regularity +regularities +regularity's +regularization +regularization's +regularize +regularizing +regularized +regularizes +regulate +regulative +regulation +regulating +regulated +regulates +deregulation +deregulating +deregulated +deregulates +deregulate +regulated +unregulated +regulation +regulation's +deregulation's +deregulation +regulations +regulator +regulators +regulator's +regulatory +regurgitate +regurgitation +regurgitating +regurgitated +regurgitates +regurgitation +regurgitation's +rehab +rehabs +rehab's +rehabbed +rehabbing +rehabilitate +rehabilitative +rehabilitation +rehabilitating +rehabilitated +rehabilitates +rehabilitation +rehabilitation's +rehang +rehanging +rehanged +rehangs +rehears +rehearsing +rehearsed +rehearsal +rehearsals +rehearsal's +rehearsed +unrehearsed +rehi +rehung +reify +reification +reifying +reified +reifies +reign +reigning +reigned +reigns +reign's +reimburse +reimbursing +reimbursed +reimburses +reimbursable +reimbursement +reimbursement +reimbursements +reimbursement's +rein +reining +reined +reindeer +reindeer's +reinforce +reinforcing +reinforced +reinforces +reinforcement +reinforcement +reinforcements +reinforcement's +reinitialize +reinstall +reinstalling +reinstalled +reinstatement +reinstatement's +reinsurance +reiterate +reiterative +reject +rejecting +rejected +rejects +reject's +rejection +rejections +rejection's +rejoice +rejoicing +rejoicings +rejoiced +rejoices +rejoicing +rejoicing's +rejoinder +rejoinders +rejoinder's +rejuvenate +rejuvenation +rejuvenating +rejuvenated +rejuvenates +rejuvenation +rejuvenation's +rel +relate +relative +relation +relations +relating +related +relater +relaters +relates +relatable +relatedness +relatedness's +relater +relater's +relation +relation's +relational +relationship +relationships +relationship's +relative +relatively +relatives +relative's +relativism +relativism's +relativist +relativists +relativistic +relativity +relativity's +relax +relaxing +relaxed +relaxer +relaxers +relaxes +relaxant +relaxants +relaxant's +relaxation +relaxations +relaxation's +relaxer +relaxer's +relay +relayed +release +releasable +released +unreleased +relegate +relegation +relegating +relegated +relegates +relent +relenting +relented +relents +relentless +relentlessly +relentlessness +relentlessness +relentlessness's +relevance +relevance's +relevancy +relevancy's +relevant +relevantly +reliability +reliability's +unreliability's +unreliability +reliable +unreliable +reliably +unreliably +reliance +reliance's +reliant +relic +relics +relic's +relief +reliefs +relief's +relieve +relieving +relieved +reliever +relievers +relieves +reliever +reliever's +religion +religions +religion's +religiosity +religious +religiously +religiousness +religious's +religiousness +religiousness's +reline +relining +relined +relines +relinquish +relinquishing +relinquished +relinquishes +relinquishment +relinquishment +relinquishment's +reliquary +reliquaries +reliquary's +relish +relishing +relished +relishes +relish's +relist +relisting +relisted +relists +relocate +relocatable +reluctance +reluctance's +reluctant +reluctantly +rely +relying +relied +relies +rem +rem's +remain +remaining +remained +remains +remainder +remaindering +remaindered +remainders +remainder's +remand +remanding +remanded +remands +remapping +remark +remarkable +remarkableness +remarkableness's +remarkably +remarked +unremarked +remediable +remedy +remedying +remedied +remedies +remedy's +remember +remembering +remembered +remembered +unremembered +remembrance +remembrances +remembrance's +reminder +reminder's +reminisce +reminiscing +reminisced +reminisces +reminiscence +reminiscences +reminiscence's +reminiscent +reminiscently +remiss +remissly +remissness +remissness +remissness's +remit +remits +remittance +remittances +remittance's +remitted +remitting +unremitting +remix +remixing +remixed +remixes +remnant +remnants +remnant's +remodel +remodeling +remodeled +remodels +remold +remolding +remolded +remolds +remonstrant +remonstrants +remonstrant's +remonstrate +remonstrating +remonstrated +remonstrates +remorse +remorse's +remorseful +remorsefully +remorseless +remorselessly +remorselessness +remorselessness +remorselessness's +remote +remotely +remotest +remoter +remotes +remoteness +remote's +remoteness +remoteness's +removal +removals +removal's +remunerate +remunerative +remuneration +remunerations +remunerating +remunerated +remunerates +remuneration +remuneration's +renaissance +renaissances +renaissance's +renal +renascence +renascences +rend +rending +rends +render +rendering +renderings +rendered +renders +render's +rendering +rendering's +rendezvous +rendezvousing +rendezvoused +rendezvouses +rendezvous's +rendition +renditions +rendition's +renegade +renegading +renegaded +renegades +renegade's +renege +reneging +reneged +reneger +renegers +reneges +reneger +reneger's +renew +renewing +renewed +renews +renewable +renewal +renewals +renewal's +rennet +rennet's +rennin +rennin's +renounce +renouncing +renounced +renounces +renouncement +renouncement +renouncement's +renovate +renovation +renovations +renovating +renovated +renovates +renovation +renovation's +renovator +renovators +renovator's +renown +renowned +renown's +rent +renting +rented +renter +renters +rents +rent's +rental +rentals +rental's +renter +renter's +renunciation +renunciations +renunciation's +reopen +reopening +reopened +reopens +reorg +reorging +reorged +reorgs +reorg's +rep +reps +rep's +repaint +repainting +repainted +repaints +repair +repairer +repairers +repairable +repairer +repairer's +repairman +repairman's +repairmen +reparable +reparation +reparations +reparation's +reparations +reparations's +repartee +repartee's +repatriate +repatriation +repatriations +repatriating +repatriated +repatriates +repatriate's +repatriation +repatriation's +repeat +repeating +repeated +repeater +repeaters +repeats +repeat's +repeatable +repeatability +repeatable +unrepeatable +repeatably +repeated +repeatedly +repeater +repeater's +repeating +repeating's +repel +repels +repelled +repellent +repellents +repellent's +repelling +repent +repenting +repented +repents +repentance +repentance's +repentant +repentantly +repercussion +repercussions +repertoire +repertoires +repertoire's +repertory +repertories +repertory's +repetition +repetitions +repetition's +repetitious +repetitiously +repetitiousness +repetitiousness +repetitiousness's +repetitive +repetitively +repetitiveness +repetitiveness +repetitiveness's +rephotograph +rephotographing +rephotographed +replaceable +replant +replanting +replanted +replants +replenish +replenishing +replenished +replenishes +replenishment +replenishment +replenishment's +replete +repletion +repleting +repleted +repletes +repleteness +repleteness +repleteness's +repletion +repletion's +replica +replicas +replica's +replicate +replication +replications +replicating +replicated +replicates +replication +replication's +replicator +replicators +reportage +reportage's +reported +reportedly +reportorial +reposeful +reposition +repository +repositories +repository's +reprehend +reprehending +reprehended +reprehends +reprehensibility +reprehensibility's +reprehensible +reprehensibly +reprehension +reprehension's +represent +representing +represented +represents +representational +representative +representatives +representative's +represented +unrepresented +repression +repressions +repression's +repressive +repressively +repressiveness +reprieve +reprieving +reprieved +reprieves +reprieve's +reprimand +reprimanding +reprimanded +reprimands +reprimand's +reprisal +reprisals +reprisal's +reprise +reprising +reprises +reprise's +reproach +reproaching +reproached +reproaches +reproach's +reproachable +reproachful +reproachfully +reprobate +reprobates +reprobate's +reproductive +reprogramming +reproving +reprovingly +reptile +reptiles +reptile's +reptilian +reptilians +reptilian's +republic +republics +republicanism +republicanism's +repudiate +repudiation +repudiations +repudiating +repudiated +repudiates +repudiation +repudiation's +repudiator +repudiators +repudiator's +repugnance +repugnance's +repugnant +repulsion +repulsion's +repulsive +repulsively +repulsiveness +repulsiveness +repulsiveness's +repurchase +repurchasing +repurchased +repurchases +reputability +reputability's +reputably +disreputably +reputation +reputations +reputation's +repute +reputing +reputed +reputes +repute's +reputable +reputed +reputedly +request +requesting +requested +requester +requiem +requiems +requiem's +require +requiring +required +requirement +requirement +requirements +requirement's +requisite +requisition +requisitions +requisites +requisite's +requisition +requisitioning +requisitioned +requisition's +requital +requital's +requite +requiting +requited +requiter +requiters +requites +requited +unrequited +requiter +requiter's +reread +rereading +rereads +rerecord +rerecording +rerecorded +rerecords +rerunning +resat +rescind +rescinding +rescinded +rescinds +rescission +rescission's +rescue +rescuing +rescued +rescuer +rescuers +rescues +rescue's +rescuer +rescuer's +reseal +resealable +resemble +resembling +resembled +resembles +resend +resent +resenting +resented +resents +resentment +resentful +resentfully +resentfulness +resentfulness +resentfulness's +resentment +resentments +resentment's +reserpine +reserpine's +reservation +reservations +reservation's +reserved +reservedly +unreservedly +unreserved +reservedness +reservedness's +reservist +reservists +reservist's +reservoir +reservoirs +reservoir's +resetting +reshipping +residence +residences +residence's +residency +residencies +residency's +resident +residents +resident's +residential +residua +residual +residuals +residual's +residue +residues +residue's +residuum +residuum's +resignation +resignations +resignation's +resigned +resignedly +resilience +resilience's +resiliency +resiliency's +resilient +resiliently +resinous +resist +resisting +resisted +resister +resisters +resists +resist's +resistance +resistances +resistance's +resistant +unresistant +resistible +resistivity +resistless +resistor +resistors +resistor's +resit +resits +resitting +resold +resole +resoling +resoled +resoles +resolute +resolutely +resoluteness +resoluteness +resoluteness's +resolve +resolver +resolve's +resolvable +resolved +unresolved +resonance +resonances +resonance's +resonant +resonantly +resonate +resonating +resonated +resonates +resonator +resonators +resonator's +resorption +resorption's +resound +resounding +resounded +resounds +resounding +resoundingly +resourceful +resourcefully +resourcefulness +resourcefulness +resourcefulness's +resp +respect +respective +respecting +respected +respects +respect's +disrespecting +disrespected +disrespects +disrespect's +disrespect +respectability +respectability's +respectable +respectably +respectful +respectfully +disrespectfully +disrespectful +respectfulness +respectfulness's +respective +respectively +respell +respelling +respelled +respells +respiration +respiration's +respirator +respirators +respirator's +respiratory +respire +respiring +respired +resplendence +resplendence's +resplendent +resplendently +respond +responding +responded +responds +respondent +respondents +respondent's +response +responses +response's +responsibility +responsibilities +responsibility's +responsible +responsibly +responsive +responsively +responsiveness +unresponsively +unresponsiveness +unresponsive +responsiveness +responsiveness's +unresponsiveness's +unresponsiveness +rest +restive +resting +rested +rests +rest's +restate +restating +restated +restates +restaurant +restaurants +restaurant's +restaurateur +restaurateurs +restaurateur's +restful +restfully +restfulness +restfuller +restfullest +restfulness +restfulness's +restitution +restitution's +restive +restively +restiveness +restiveness +restiveness's +restless +restlessly +restlessness +restlessness +restlessness's +restoration +restorations +restoration's +restorative +restoratives +restorative's +restorer +restorers +restorer's +restrained +unrestrained +restraint +restraints +restraint's +restrict +restrictive +restricting +restricted +restricts +restricted +unrestricted +restriction +restrictions +restriction's +restrictive +restrictively +restrictiveness +restrictiveness +restrictiveness's +restring +restringing +restrings +restroom +restrooms +restroom's +restructuring +restructurings +restructuring's +result +resulting +resulted +results +result's +resultant +resultants +resultant's +resume +resuming +resumed +resumes +resume's +resumption +resumptions +resumption's +resupply +resupplying +resupplied +resupplies +resurgence +resurgences +resurgence's +resurgent +resurrect +resurrecting +resurrected +resurrects +resurrection +resurrections +resurrection's +resuscitate +resuscitation +resuscitating +resuscitated +resuscitates +resuscitation +resuscitation's +resuscitator +resuscitators +resuscitator's +retailer +retailers +retailer's +retain +retaining +retained +retainer +retainers +retains +retainer +retainer's +retake +retaking +retaliate +retaliative +retaliation +retaliations +retaliating +retaliated +retaliates +retaliation +retaliation's +retaliatory +retard +retarding +retarded +retarder +retarders +retards +retard's +retardant +retardants +retardant's +retardation +retardation's +retarder +retarder's +retch +retching +retched +retches +reteach +reteaching +reteaches +retention +retention's +retentive +retentively +retentiveness +retentiveness +retentiveness's +rethink +rethinking +rethinks +rethink's +rethought +reticence +reticence's +reticent +reticently +reticulated +reticulation +reticulations +reticulation's +retina +retinas +retina's +retinal +retinoblastoma +retinue +retinues +retinue's +retiree +retirees +retiree's +retirement +retirements +retirement's +retort +retorting +retorted +retort's +retrace +retracing +retraced +retraces +retract +retracting +retracted +retractable +retractile +retraction +retractions +retrain +retraining +retrained +retrains +retread +retreaded +retrenchment +retrenchments +retrenchment's +retribution +retributions +retribution's +retributive +retrieval +retrievals +retrieval's +retrieve +retrieving +retrieved +retriever +retrievers +retrieves +retrieve's +retrievable +retriever +retriever's +retro +retros +retro's +retroactive +retroactively +retrofire +retrofiring +retrofired +retrofires +retrofit +retrofits +retrofit's +retrofitted +retrofitting +retrograde +retrograding +retrograded +retrogrades +retrogress +retrogressive +retrogressing +retrogressed +retrogresses +retrogression +retrogression's +retrorocket +retrorockets +retrorocket's +retrospect +retrospective +retrospecting +retrospected +retrospects +retrospect's +retrospection +retrospection's +retrospective +retrospectively +retrospectives +retrospective's +retrovirus +retroviruses +retrovirus's +retsina +retsina's +returnable +returnables +returnable's +returnee +returnees +returnee's +rev +revive +revers +rev's +revamping +revamping's +reveal +revealing +revealings +revealed +reveals +revealed +unrevealed +revealing +revealingly +reveille +reveille's +revel +reveling +revelings +reveled +reveler +revelers +revels +revel's +revelation +revelations +revelation's +revelatory +reveler +reveler's +revelry +revelries +revelry's +revenge +revenging +revenged +revenges +revenge's +revenuer +revenuers +revenuer's +reverb +reverberate +reverberation +reverberations +reverberating +reverberated +reverberates +reverberation +reverberation's +revere +revering +revered +reveres +reverence +reverencing +reverenced +reverences +reverence's +reverend +reverends +reverend's +reverent +reverently +reverential +reverentially +reverie +reveries +reverie's +revers +revers's +reversal +reversals +reversal's +reverse +reversely +reversibility +reversible +reversibly +revert +reverting +reverted +reverts +revertible +revetment +revetments +revetment's +revile +reviling +reviled +reviler +revilers +reviles +revilement +revilement +revilement's +reviler +reviler's +reviser +revisers +reviser's +revision +revisions +revision's +revisionism +revisionism's +revisionist +revisionists +revisionist's +revival +revivals +revival's +revivalism +revivalism's +revivalist +revivalists +revivalist's +revive +reviving +revived +revives +revivification +revivification's +revocable +revoke +revoking +revoked +revokes +revolt +revolting +revolted +revolting +revoltingly +revolution +revolutions +revolution's +revolutionary +revolutionaries +revolutionary's +revolutionist +revolutionists +revolutionist's +revolutionize +revolutionizing +revolutionized +revolutionizes +revolve +revolving +revolved +revolver +revolvers +revolves +revolvable +revolver +revolver's +revue +revues +revue's +revulsion +revulsion's +revved +revving +rewarded +unrewarded +rewarding +unrewarding +rewarm +rewarming +rewarmed +rewarms +rewash +rewashing +rewashed +rewashes +reweave +reweaving +reweaves +rewedding +rewind +rewind's +rewindable +rewound +rewrite +rewriting +rewrites +rewrite's +rhapsodic +rhapsodical +rhapsodize +rhapsodizing +rhapsodized +rhapsodizes +rhapsody +rhapsodies +rhapsody's +rhea +rheas +rhea's +rhenium +rhenium's +rheostat +rheostats +rheostat's +rhesus +rhesuses +rhesus's +rhetoric +rhetoric's +rhetorical +rhetorically +rhetorician +rhetoricians +rhetorician's +rheum +rheum's +rheumatic +rheumatics +rheumatic's +rheumatically +rheumatism +rheumatism's +rheumatoid +rheumy +rhinestone +rhinestones +rhinestone's +rhinitis +rhinitis's +rhino +rhinos +rhino's +rhinoceros +rhinoceroses +rhinoceros's +rhinoplasty +rhinovirus +rhinoviruses +rhinovirus's +rhizome +rhizomes +rhizome's +rho +rhos +rho's +rhodium +rhodium's +rhododendron +rhododendrons +rhododendron's +rhomboid +rhomboids +rhomboid's +rhomboidal +rhombus +rhombuses +rhombus's +rhubarb +rhubarbs +rhubarb's +rhyme +rhyming +rhymed +rhymer +rhymers +rhymes +rhyme's +rhymer +rhymer's +rhymester +rhymesters +rhymester's +rhythm +rhythms +rhythm's +rhythmic +rhythmical +rhythmically +rial +rials +rial's +rib +ribs +rib's +ribald +ribaldry +ribaldry's +ribbed +ribber +ribbers +ribber's +ribbing +ribbon +ribbons +ribbon's +riboflavin +riboflavin's +rice +ricing +riced +ricer +ricers +rices +rice's +ricer +ricer's +rich +richly +richest +richer +riches +richness +rich's +richness +richness's +rick +ricking +ricked +ricks +rick's +rickets +rickets's +rickety +ricketiest +ricketier +rickrack +rickrack's +rickshaw +rickshaws +rickshaw's +ricochet +ricocheting +ricocheted +ricochets +ricochet's +ricotta +ricotta's +rid +rids +riddance +riddance's +ridden +ridding +riddle +riddling +riddled +riddles +riddle's +ride +riding +rider +riders +rides +ride's +rider +rider's +riderless +ridership +ridership's +ridge +ridging +ridged +ridges +ridge's +ridgepole +ridgepoles +ridgepole's +ridgy +ridicule +ridiculing +ridiculed +ridicules +ridicule's +ridiculous +ridiculously +ridiculousness +ridiculousness +ridiculousness's +riding +riding's +rife +rifest +rifer +riff +riffing +riffed +riffs +riff's +riffle +riffling +riffled +riffles +riffle's +riffraff +riffraff's +rifle +rifling +rifled +rifler +riflers +rifles +rifle's +rifleman +rifleman's +riflemen +rifler +rifler's +rifling +rifling's +rift +rifting +rifted +rifts +rift's +rig +rigs +rig's +rigatoni +rigatoni's +rigged +rigger +riggers +rigger's +rigging +rigging's +right +rightly +righting +righted +rightest +righter +rights +rightness +right's +righteous +righteousness +unrighteousness +unrighteous +righteously +righteousness +righteousness's +unrighteousness's +unrighteousness +rightful +rightfully +rightfulness +rightfulness +rightfulness's +rightism +rightism's +rightist +rightists +rightist's +rightmost +rightness +rightness's +righto +rightsize +rightsizing +rightsized +rightsizes +rightward +rightwards +rigid +rigidly +rigidness +rigidity +rigidity's +rigidness +rigidness's +rigmarole +rigmaroles +rigmarole's +rigor +rigors +rigor's +rigorous +rigorously +rigorousness +rigorousness +rigorousness's +rile +riling +riled +riles +rill +rills +rill's +rim +riming +rimed +rims +rim's +rime +rimes +rime's +rimless +rimmed +rimming +rind +rinds +rind's +ring +ringing +ringings +ringed +ringer +ringers +ring's +ringer +ringer's +ringgit +ringgits +ringgit's +ringleader +ringleaders +ringleader's +ringlet +ringlets +ringlet's +ringlike +ringmaster +ringmasters +ringmaster's +ringside +ringside's +ringtone +ringtones +ringtone's +ringworm +ringworm's +rink +rinks +rink's +rinse +rinsing +rinsed +rinses +rinse's +riot +rioting +rioted +rioter +rioters +riots +riot's +rioter +rioter's +rioting +rioting's +riotous +riotously +riotousness +rip +ripen +ripens +ripest +riper +rips +rip's +riparian +ripcord +ripcords +ripcord's +ripe +ripely +ripeness +ripen +ripening +ripened +ripened +unripened +ripeness +ripeness's +ripoff +ripoffs +ripoff's +riposte +riposting +riposted +ripostes +riposte's +ripped +ripper +rippers +ripper's +ripping +ripple +rippling +rippled +ripples +ripple's +ripply +ripsaw +ripsaws +ripsaw's +riptide +riptides +riptide's +rise +rising +risings +riser +risers +rises +rise's +risen +riser +riser's +risibility +risibility's +risible +rising +rising's +risk +risking +risked +risks +risk's +riskily +riskiness +riskiness's +risky +riskiest +riskier +riskiness +risotto +risottos +risotto's +risque +rissole +rissoles +rite +rites +rite's +ritual +ritually +rituals +ritual's +ritualism +ritualism's +ritualistic +ritualistically +ritualized +ritzy +ritziest +ritzier +riv +riven +river +rivers +rival +rivaling +rivaled +rivals +rival's +rivaled +unrivaled +rivalry +rivalries +rivalry's +rive +riving +rived +rives +deriving +derived +derives +derive +river +river's +riverbank +riverbanks +riverbank's +riverbed +riverbeds +riverbed's +riverboat +riverboats +riverboat's +riverfront +riverside +riversides +riverside's +rivet +riveting +riveted +riveter +riveters +rivets +rivet's +riveter +riveter's +riviera +rivieras +rivulet +rivulets +rivulet's +riyal +riyals +riyal's +rm +roach +roaching +roached +roaches +roach's +road +roads +road's +inroads +inroad's +inroad +roadbed +roadbeds +roadbed's +roadblock +roadblocking +roadblocked +roadblocks +roadblock's +roadhouse +roadhouses +roadhouse's +roadie +roadies +roadie's +roadkill +roadkill's +roadrunner +roadrunners +roadrunner's +roadshow +roadshows +roadshow's +roadside +roadsides +roadside's +roadster +roadsters +roadster's +roadway +roadways +roadway's +roadwork +roadworks +roadwork's +roadworthy +roam +roaming +roamed +roamer +roamers +roams +roamer +roamer's +roaming +roaming's +roan +roans +roan's +roar +roaring +roared +roarer +roarers +roars +roar's +roarer +roarer's +roaring +roaring's +roast +roasting +roastings +roasted +roaster +roasters +roasts +roast's +roaster +roaster's +roasting +roasting's +rob +robs +robbed +robber +robbers +robber's +robbery +robberies +robbery's +robbing +robe's +robe +robing +robed +robes +disrobing +disrobed +disrobes +disrobe +robin +robins +robin's +robocall +robocalling +robocalled +robocalls +robocall's +robot +robots +robot's +robotic +robotics +robotics +robotics's +robotize +robotizing +robotized +robotizes +robust +robustly +robustest +robuster +robustness +robustness +robustness's +rock +rocking +rocked +rocker +rockers +rocks +rock's +rockabilly +rockabilly's +rockbound +rocker +rocker's +rockery +rockeries +rocket +rocketing +rocketed +rockets +rocket's +rocketry +rocketry's +rockfall +rockfalls +rockfall's +rockiness +rockiness's +rocky +rockiest +rockier +rockiness +rococo +rococo's +rod +rods +rod's +rode +rodent +rodents +rodent's +rodeo +rodeos +rodeo's +roe +roes +roe's +roebuck +roebucks +roebuck's +roentgen +roentgens +roentgen's +roger +rogering +rogered +rogers +rogue's +rogue +rogues +prorogues +prorogue +roguery +roguery's +roguish +roguishly +roguishness +roguishness +roguishness's +roil +roiling +roiled +roils +roister +roistering +roistered +roisterer +roisterers +roisters +roisterer +roisterer's +role +roles +role's +roll +rolling +rollings +rolled +roller +rollers +rolls +roll's +rollback +rollbacks +rollback's +roller +roller's +rollerblading +rollerskating +rollerskating's +rollick +rollicking +rollicked +rollicks +rollicking +rollicking's +rollmop +rollmops +rollover +rollovers +rollover's +romaine +romaines +romaine's +roman +roman's +romance +romancing +romanced +romancer +romancers +romances +romance's +romancer +romancer's +romantic +romantics +romantic's +romantically +romanticism +romanticism's +romanticist +romanticists +romanticist's +romanticize +romanticizing +romanticized +romanticizes +romeo +romeos +romeo's +romp +romping +romped +romper +rompers +romps +romp's +romper +romper's +rondo +rondos +rondo's +rood +roods +rood's +roof +roofing +roofed +roofer +roofers +roofs +roof's +roofer +roofer's +roofing +roofing's +roofless +rooftop +rooftops +rooftop's +rook +rooking +rooked +rooks +rook's +rookery +rookeries +rookery's +rookie +rookies +rookie's +room +rooming +roomed +roomer +roomers +rooms +room's +roomer +roomer's +roomette +roomettes +roomette's +roomful +roomfuls +roomful's +roominess +roominess's +roommate +roommates +roommate's +roomy +roomiest +roomier +roominess +roost +roosting +roosted +rooster +roosters +roosts +roost's +rooster +rooster's +root +rooting +rooted +rooter +rooters +roots +root's +rooter +rooter's +rootkit +rootkits +rootkit's +rootless +rootlessness +rootlet +rootlets +rootlet's +rope +roping +roped +roper +ropers +ropes +rope's +roper +roper's +ropy +ropiest +ropier +rosary +rosaries +rosary's +rose +roses +rose's +roseate +rosebud +rosebuds +rosebud's +rosebush +rosebushes +rosebush's +rosemary +rosemary's +rosette +rosettes +rosette's +rosewater +rosewater's +rosewood +rosewoods +rosewood's +rosily +rosin +rosining +rosined +rosins +rosin's +rosiness +rosiness's +roster +rosters +roster's +rostrum +rostrums +rostrum's +rosy +rosiest +rosier +rosiness +rot +rots +rot's +rota +rotas +rotary +rotaries +rotary's +rotate +rotation +rotations +rotating +rotated +rotates +rotation +rotation's +rotational +rotatory +rote +rote's +rotgut +rotgut's +rotisserie +rotisseries +rotisserie's +rotogravure +rotogravures +rotogravure's +rotor +rotors +rotor's +rototiller +rototillers +rototiller's +rotted +rotten +rottenly +rottenest +rottener +rottenness +rottenness +rottenness's +rotter +rotters +rotting +rottweiler +rottweilers +rotund +rotundness +rotunda +rotundas +rotunda's +rotundity +rotundity's +rotundness +rotundness's +roue +roues +roue's +rouge +rouging +rouged +rouges +rouge's +rough +roughen +roughens +roughly +roughing +roughed +roughest +rougher +roughness +rough's +roughage +roughage's +roughcast +roughen +roughening +roughened +roughhouse +roughhousing +roughhoused +roughhouses +roughhouse's +roughneck +roughnecking +roughnecked +roughnecks +roughneck's +roughness +roughness's +roughs +roughshod +roulette +roulette's +round +roundly +rounding +rounded +roundest +rounder +rounders +rounds +roundness +round's +roundabout +roundabouts +roundabout's +roundel +roundels +roundelay +roundelays +roundelay's +roundhouse +roundhouses +roundhouse's +roundish +roundness +roundness's +roundup +roundups +roundup's +roundworm +roundworms +roundworm's +rouse +rousing +roused +rouses +roust +rousting +rousted +rousts +roustabout +roustabouts +roustabout's +rout +router +routers +routs +rout's +route's +route +routing +routed +routes +rerouting +rerouted +reroutes +reroute +routeing +router +router's +routine +routinely +routines +routine's +routinize +routinizing +routinized +routinizes +roux +rove +roving +roved +rover +rovers +roves +rover +rover's +row +rowing +rowed +rower +rowers +rows +row's +rowan +rowans +rowboat +rowboats +rowboat's +rowdily +rowdiness +rowdiness's +rowdy +rowdiest +rowdier +rowdies +rowdiness +rowdy's +rowdyism +rowdyism's +rowel +roweling +roweled +rowels +rowel's +rower +rower's +rowing +rowing's +rowlock +rowlocks +royal +royally +royals +royal's +royalist +royalists +royalist's +royalties +royalties's +royalty +royalties +royalty's +rpm +rps +rt +rte +rub +rubs +rub's +rubato +rubatos +rubato's +rubbed +rubber +rubbers +rubber's +rubberize +rubberizing +rubberized +rubberizes +rubberneck +rubbernecking +rubbernecked +rubbernecker +rubberneckers +rubbernecks +rubberneck's +rubbernecker +rubbernecker's +rubbery +rubbing +rubbings +rubbish +rubbishing +rubbished +rubbishes +rubbish's +rubbishy +rubble +rubble's +rubdown +rubdowns +rubdown's +rube +rubes +rube's +rubella +rubella's +rubicund +rubidium +rubidium's +ruble +rubles +ruble's +rubric +rubrics +rubric's +ruby +rubiest +rubier +rubies +ruby's +ruched +ruck +rucking +rucked +rucks +rucksack +rucksacks +rucksack's +ruckus +ruckuses +ruckus's +ructions +rudder +rudders +rudder's +rudderless +ruddiness +ruddiness's +ruddy +ruddiest +ruddier +ruddiness +rude +rudely +rudest +ruder +rudeness +rudeness +rudeness's +rudiment +rudiments +rudiment's +rudimentary +rue +ruing +rued +rues +rue's +rueful +ruefully +ruefulness +ruefulness +ruefulness's +ruff +ruffly +ruffing +ruffed +ruffs +ruff's +ruffian +ruffianly +ruffians +ruffian's +ruffle +ruffling +ruffled +ruffles +ruffle's +ruffled +unruffled +rug +rugs +rug's +rugby +rugby's +rugged +ruggedly +ruggedest +ruggeder +ruggedness +ruggedness +ruggedness's +rugger +rugrat +rugrats +rugrat's +ruin +ruining +ruined +ruins +ruin's +ruination +ruination's +ruinous +ruinously +rule +ruling +rulings +ruled +ruler +rulers +rules +rule's +ruler +ruler's +ruling +ruling's +rum +rums +rum's +rumba +rumbaing +rumbaed +rumbas +rumba's +rumble +rumbling +rumblings +rumbled +rumbles +rumble's +rumbling +rumbling's +rumbustious +ruminant +ruminants +ruminant's +ruminate +ruminative +rumination +ruminations +ruminating +ruminated +ruminates +rumination +rumination's +ruminative +ruminatively +rummage +rummaging +rummaged +rummages +rummage's +rummer +rummest +rummy +rummy's +rumor +rumoring +rumored +rumors +rumor's +rumormonger +rumormongers +rumormonger's +rump +rumply +rumps +rump's +rumple +rumpling +rumpled +rumples +rumple's +rumpus +rumpuses +rumpus's +run +runs +run's +reruns +rerun's +rerun +runabout +runabouts +runabout's +runaround +runarounds +runaround's +runaway +runaways +runaway's +rundown +rundowns +rundown's +rune +runes +rune's +rung +rungs +rung's +runic +runlet +runlets +runlet's +runnel +runnels +runnel's +runner +runners +runner's +running +running's +runny +runniest +runnier +runoff +runoffs +runoff's +runt +runts +runt's +runty +runtiest +runtier +runway +runways +runway's +rupee +rupees +rupee's +rupiah +rupiah's +rupiahs +rupture +rupturing +ruptured +ruptures +rupture's +rural +ruse +ruses +ruse's +rush +rushing +rushed +rusher +rushers +rushes +rush's +rusher +rusher's +rushy +rusk +rusks +rusk's +russet +russets +russet's +rust +rusting +rusted +rusts +rust's +rustic +rustics +rustic's +rustically +rusticate +rusticating +rusticated +rusticates +rustication +rustication's +rusticity +rusticity's +rustiness +rustiness's +rustle +rustling +rustlings +rustled +rustler +rustlers +rustles +rustle's +rustler +rustler's +rustproof +rustproofing +rustproofed +rustproofs +rusty +rustication +rustiest +rustier +rustiness +rut +ruts +rut's +rutabaga +rutabagas +rutabaga's +ruthenium +ruthenium's +rutherfordium +rutherfordium's +ruthless +ruthlessly +ruthlessness +ruthlessness +ruthlessness's +rutted +rutting +rutty +ruttiest +ruttier +rye +rye's +s +sen +sens +sly +sable +sabbath +sabbath's +sabbaths +sabbatical +sabbaticals +sabbatical's +saber +sabers +saber's +sable +sables +sable's +sabot +sabots +sabot's +sabotage +sabotaging +sabotaged +sabotages +sabotage's +saboteur +saboteurs +saboteur's +sabra +sabras +sabra's +sac +sacs +sac's +saccharin +saccharin's +saccharine +sacerdotal +sachem +sachems +sachem's +sachet +sachets +sachet's +sack +sacking +sackings +sacked +sacker +sackers +sacks +sack's +sackcloth +sackcloth's +sacker +sacker's +sackful +sackfuls +sackful's +sacking +sacking's +sacra +sacrament +sacraments +sacrament's +sacramental +sacred +sacredly +sacredness +sacredness +sacredness's +sacrifice +sacrificing +sacrificed +sacrifices +sacrifice's +sacrificial +sacrificially +sacrilege +sacrileges +sacrilege's +sacrilegious +sacrilegiously +sacristan +sacristans +sacristan's +sacristy +sacristies +sacristy's +sacroiliac +sacroiliacs +sacroiliac's +sacrosanct +sacrosanctness +sacrosanctness +sacrosanctness's +sacrum +sacrum's +sad +sadly +sadness +sadden +saddening +saddened +saddens +sadder +saddest +saddle's +saddle +saddling +saddled +saddles +unsaddling +unsaddled +unsaddles +unsaddle +saddlebag +saddlebags +saddlebag's +saddler +saddlers +saddlery +sades +sadhu +sadhus +sadism +sadism's +sadist +sadists +sadist's +sadistic +sadistically +sadness +sadness's +sadomasochism +sadomasochism's +sadomasochist +sadomasochists +sadomasochist's +sadomasochistic +safari +safariing +safaried +safaris +safari's +safe +safely +safest +safer +safes +safeness +safe's +safeguard +safeguarding +safeguarded +safeguards +safeguard's +safekeeping +safekeeping's +safeness +safeness's +safety +safeties +safety's +safflower +safflowers +safflower's +saffron +saffrons +saffron's +sag +sags +sag's +saga +sagas +saga's +sagacious +sagaciously +sagacity +sagacity's +sage +sagely +sagest +sager +sages +sage's +sagebrush +sagebrush's +sagged +sagging +saggy +saggiest +saggier +sago +sago's +saguaro +saguaros +saguaro's +sahib +sahibs +sahib's +said +unsaid +sail +sailing +sailings +sailed +sails +sail's +sailboard +sailboarding +sailboarder +sailboarders +sailboards +sailboard's +sailboarder +sailboarder's +sailboarding +sailboarding's +sailboat +sailboats +sailboat's +sailcloth +sailcloth's +sailfish +sailfishes +sailfish's +sailing +sailing's +sailor +sailors +sailor's +sailplane +sailplanes +sailplane's +saint +saintly +sainted +saints +saint's +sainthood +sainthood's +saintlike +saintliness +saintliness's +saintly +saintliest +saintlier +saintliness +saith +sake +sake's +salaam +salaaming +salaamed +salaams +salaam's +salable +unsalable +salacious +salaciously +salaciousness +salaciousness +salaciousness's +salacity +salacity's +salad +salads +salad's +salamander +salamanders +salamander's +salami +salamis +salami's +salary +salaried +salaries +salary's +sale +sales +sale's +salable +resales +resale's +resalable +resale +saleroom +salerooms +salesclerk +salesclerks +salesclerk's +salesgirl +salesgirls +salesgirl's +saleslady +salesladies +saleslady's +salesman +salesman's +salesmanship +salesmanship's +salesmen +salespeople +salespeople's +salesperson +salespersons +salesperson's +salesroom +salesrooms +saleswoman +saleswoman's +saleswomen +salience +salience's +salient +saliently +salients +salient's +saline +salines +saline's +salinity +salinity's +saliva +saliva's +salivary +salivate +salivation +salivating +salivated +salivates +salivation +salivation's +sallow +sallowest +sallower +sallowness +sallowness +sallowness's +sally +sallying +sallied +sallies +sally's +salmon +salmons +salmon's +salmonella +salmonella's +salmonellae +salon +salons +salon's +saloon +saloons +saloon's +salsa +salsas +salsa's +salt's +salt +salting +salted +saltest +salts +desalting +desalted +desalts +desalt +saltbox +saltboxes +saltbox's +saltcellar +saltcellars +saltcellar's +salted +unsalted +salter +saltine +saltines +saltine's +saltiness +saltiness's +saltpeter +saltpeter's +saltshaker +saltshakers +saltshaker's +saltwater +saltwater's +salty +saltiest +saltier +saltiness +salubrious +insalubrious +salutary +salutation +salutations +salutation's +salutatorian +salutatorians +salutatorian's +salutatory +salute +saluting +saluted +salutes +salute's +salvage +salvaging +salvaged +salvages +salvage's +salvageable +salvation +salvation's +salve +salving +salved +salver +salvers +salves +salve's +salver +salver's +salvo +salvos +salvo's +samarium +samarium's +samba +sambaing +sambaed +sambas +samba's +same +sames +sameness +sameness +sameness's +samey +samizdat +samizdats +samosa +samosas +samovar +samovars +samovar's +sampan +sampans +sampan's +sample +sampling +samplings +sampled +sampler +samplers +samples +sample's +sampler +sampler's +sampling +sampling's +samurai +samurais +samurai's +sanatorium +sanatoriums +sanatorium's +sanctification +sanctification's +sanctify +sanctification +sanctifying +sanctified +sanctifies +sanctimonious +sanctimoniously +sanctimoniousness +sanctimoniousness +sanctimoniousness's +sanctimony +sanctimony's +sanction +sanctioning +sanctioned +sanctions +sanction's +sanctioned +unsanctioned +sanctity +sanctity's +sanctuary +sanctuaries +sanctuary's +sanctum +sanctums +sanctum's +sand +sanding +sanded +sander +sanders +sands +sand's +sandal +sandals +sandal's +sandalwood +sandalwood's +sandbag +sandbags +sandbag's +sandbagged +sandbagger +sandbaggers +sandbagger's +sandbagging +sandbank +sandbanks +sandbank's +sandbar +sandbars +sandbar's +sandblast +sandblasting +sandblasted +sandblaster +sandblasters +sandblasts +sandblast's +sandblaster +sandblaster's +sandbox +sandboxes +sandbox's +sandcastle +sandcastles +sandcastle's +sander +sander's +sandhog +sandhogs +sandhog's +sandiness +sandiness's +sandlot +sandlots +sandlot's +sandlotter +sandlotters +sandlotter's +sandman +sandman's +sandmen +sandpaper +sandpapering +sandpapered +sandpapers +sandpaper's +sandpiper +sandpipers +sandpiper's +sandpit +sandpits +sandstone +sandstone's +sandstorm +sandstorms +sandstorm's +sandwich +sandwiching +sandwiched +sandwiches +sandwich's +sandy +sandiest +sandier +sandiness +sane +sanely +sanest +saner +insanely +insaner +insane +saneness +saneness's +sang +sangfroid +sangfroid's +sangria +sangria's +sanguinary +sanguine +sanguinely +sanitarian +sanitarians +sanitarian's +sanitarium +sanitariums +sanitarium's +sanitary +insanitary +unsanitary +sanitation +sanitation's +sanitize +sanitizing +sanitized +sanitizer +sanitizers +sanitizes +sanity +sanity's +insanity's +insanity +sank +sans +sanserif +sap +saps +sap's +sapience +sapience's +sapiens +sapient +sapless +sapling +saplings +sapling's +sapped +sapper +sappers +sapphire +sapphires +sapphire's +sappiness +sappiness's +sapping +sappy +sappiest +sappier +sappiness +saprophyte +saprophytes +saprophyte's +saprophytic +sapsucker +sapsuckers +sapsucker's +sapwood +sapwood's +saran +saran's +sarcasm +sarcasms +sarcasm's +sarcastic +sarcastically +sarcoma +sarcomas +sarcoma's +sarcophagi +sarcophagus +sarcophagus's +sardine +sardines +sardine's +sardonic +sardonically +sarge +sarges +sarge's +sari +saris +sari's +sarky +sarnie +sarnies +sarong +sarongs +sarong's +sarsaparilla +sarsaparillas +sarsaparilla's +sartorial +sartorially +sash +sashes +sash's +sashay +sashaying +sashayed +sashays +sashay's +sass +sassing +sassed +sasses +sass's +sassafras +sassafrases +sassafras's +sassy +sassiest +sassier +sat +satanic +satanical +satanically +satanism +satanism's +satanist +satanists +satanist's +satay +satchel +satchels +satchel's +sate +sating +sated +sates +sateen +sateen's +satellite +satelliting +satellited +satellites +satellite's +satiable +insatiable +satiate +satiation +satiating +satiated +satiates +satiation +satiation's +satiety +satiety's +satin +satin's +satinwood +satinwoods +satinwood's +satiny +satire +satires +satire's +satiric +satirical +satirically +satirist +satirists +satirist's +satirize +satirizing +satirized +satirizes +satisfaction +satisfaction's +dissatisfaction's +dissatisfaction +satisfactions +satisfactorily +unsatisfactorily +satisfactory +unsatisfactory +satisfied +unsatisfied +satisfy +satisfying +satisfied +satisfies +dissatisfying +dissatisfied +dissatisfies +dissatisfy +satisfying +unsatisfying +satisfyingly +satori +satori's +satrap +satraps +satrap's +satsuma +satsumas +saturate +saturation +saturating +saturated +saturates +saturated +unsaturated +saturation +saturation's +saturnine +satyr +satyrs +satyr's +satyriasis +satyriasis's +satyric +sauce +saucing +sauced +saucer +saucers +sauces +sauce's +saucepan +saucepans +saucepan's +saucer +saucer's +saucily +sauciness +sauciness's +saucy +sauciest +saucier +sauciness +sauerkraut +sauerkraut's +sauna +saunaing +saunaed +saunas +sauna's +saunter +sauntering +sauntered +saunters +saunter's +saurian +sauropod +sauropods +sauropod's +sausage +sausages +sausage's +saute +sautes +saute's +sauteed +sauteing +savage +savagely +savaging +savaged +savagest +savager +savages +savageness +savage's +savageness +savageness's +savagery +savageries +savagery's +savanna +savannas +savanna's +savant +savants +savant's +save +saving +savings +saved +saver +savers +saves +save's +savable +saved +unsaved +saver +saver's +saving +saving's +savings +savings's +savior +saviors +savior's +savor +savoring +savored +savors +savor's +savoriness +savoriness's +savory +savoriest +savorier +savories +savoriness +savory's +savoy +savoys +savoy's +savvy +savvying +savvied +savviest +savvier +savvies +savvy's +saw +sawing +sawed +saws +saw's +sawbones +sawbones's +sawbuck +sawbucks +sawbuck's +sawdust +sawdust's +sawfly +sawflies +sawfly's +sawhorse +sawhorses +sawhorse's +sawmill +sawmills +sawmill's +sawyer +sawyers +sawyer's +sax +saxes +sax's +saxifrage +saxifrages +saxifrage's +saxophone +saxophones +saxophone's +saxophonist +saxophonists +saxophonist's +say's +say +saying +says +unsaying +unsays +unsay +saying +sayings +saying's +scab +scabs +scab's +scabbard +scabbards +scabbard's +scabbed +scabbiness +scabbiness's +scabbing +scabby +scabbiest +scabbier +scabbiness +scabies +scabies's +scabrous +scad +scads +scad's +scaffold +scaffolding +scaffolds +scaffold's +scaffolding +scaffolding's +scag +scags +scagged +scalar +scalars +scalawag +scalawags +scalawag's +scald +scalding +scalded +scalds +scald's +scale's +scale +scaling +scaled +scales +descaling +descaled +descales +descale +scaleless +scalene +scaliness +scaliness's +scallion +scallions +scallion's +scallop +scalloping +scalloped +scallops +scallop's +scalp +scalping +scalped +scalper +scalpers +scalps +scalp's +scalpel +scalpels +scalpel's +scalper +scalper's +scaly +scaliest +scalier +scaliness +scam +scams +scam's +scammed +scammer +scammers +scamming +scamp +scamper +scampers +scamps +scamp's +scamper +scampering +scampered +scamper's +scampi +scampi's +scan +scans +scan's +scandal +scandals +scandal's +scandalize +scandalizing +scandalized +scandalizes +scandalmonger +scandalmongers +scandalmonger's +scandalous +scandalously +scandium +scandium's +scanned +scanner +scanners +scanner's +scanning +scansion +scansion's +scant +scanting +scanted +scantest +scants +descanting +descanted +descants +descant +scanter +scantily +scantiness +scantiness's +scantly +scantness +scantness's +scanty +scantiest +scantier +scanties +scantiness +scapegoat +scapegoating +scapegoated +scapegoats +scapegoat's +scapegrace +scapegraces +scapegrace's +scapula +scapula's +scapulae +scapular +scapulars +scapular's +scar +scaring +scared +scars +scar's +scarab +scarabs +scarab's +scarce +scarcely +scarcest +scarcer +scarceness +scarceness +scarceness's +scarcity +scarcities +scarcity's +scare +scares +scare's +scarecrow +scarecrows +scarecrow's +scaremonger +scaremongering +scaremongers +scaremonger's +scarf +scarfing +scarfed +scarfs +scarf's +scarification +scarification's +scarify +scarification +scarifying +scarified +scarifies +scarily +scariness +scariness's +scarlatina +scarlatina's +scarlet +scarlet's +scarp +scarping +scarped +scarper +scarpers +scarps +scarp's +scarper +scarpering +scarpered +scarred +scarring +scarves +scary +scariest +scarier +scariness +scat +scats +scat's +scathing +scathingly +scatological +scatology +scatology's +scatted +scatter +scattering +scatterings +scattered +scatters +scatter's +scatterbrain +scatterbrained +scatterbrains +scatterbrain's +scattering +scattering's +scattershot +scatting +scatty +scavenge +scavenging +scavenged +scavenger +scavengers +scavenges +scavenger +scavenger's +scenario +scenarios +scenario's +scenarist +scenarists +scenarist's +scene +scenes +scene's +scenery +scenery's +scenic +scenically +scent +scents +scent's +descents +descent's +descent +scented +unscented +scenting +scentless +scepter +scepters +scepter's +sch +schadenfreude +schedule's +schedule +scheduling +scheduled +schedules +rescheduling +rescheduled +reschedules +reschedule +scheduled +unscheduled +scheduler +schedulers +schema +schemata +schematic +schematics +schematic's +schematically +schematize +schematizing +schematized +schematizes +scheme +scheming +schemed +schemer +schemers +schemes +scheme's +schemer +schemer's +scherzo +scherzos +scherzo's +schilling +schillings +schilling's +schism +schisms +schism's +schismatic +schismatics +schismatic's +schist +schist's +schistosomiasis +schizo +schizos +schizo's +schizoid +schizoids +schizoid's +schizophrenia +schizophrenia's +schizophrenic +schizophrenics +schizophrenic's +schlemiel +schlemiels +schlemiel's +schlep +schleps +schlep's +schlepped +schlepping +schlock +schlock's +schmaltz +schmaltz's +schmaltzy +schmaltziest +schmaltzier +schmo +schmo's +schmoes +schmooze +schmoozing +schmoozed +schmoozer +schmoozers +schmoozes +schmuck +schmucks +schmuck's +schnapps +schnapps's +schnauzer +schnauzers +schnauzer's +schnitzel +schnitzels +schnitzel's +schnook +schnooks +schnook's +schnoz +schnozes +schnoz's +schnozzle +schnozzles +schnozzle's +scholar +scholarly +scholars +scholar's +scholarship +scholarships +scholarship's +scholastic +scholastically +scholasticism +school +schooling +schooled +schools +school's +schoolbag +schoolbags +schoolbag's +schoolbook +schoolbooks +schoolbook's +schoolboy +schoolboys +schoolboy's +schoolchild +schoolchild's +schoolchildren +schoolchildren's +schooldays +schooled +unschooled +schoolfellow +schoolfellows +schoolfellow's +schoolgirl +schoolgirls +schoolgirl's +schoolhouse +schoolhouses +schoolhouse's +schooling +schooling's +schoolkid +schoolkids +schoolmarm +schoolmarms +schoolmarm's +schoolmarmish +schoolmaster +schoolmasters +schoolmaster's +schoolmate +schoolmates +schoolmate's +schoolmistress +schoolmistresses +schoolmistress's +schoolroom +schoolrooms +schoolroom's +schoolteacher +schoolteachers +schoolteacher's +schoolwork +schoolwork's +schoolyard +schoolyards +schoolyard's +schooner +schooners +schooner's +schuss +schussing +schussed +schusses +schuss's +schussboomer +schussboomers +schussboomer's +schwa +schwas +schwa's +sci +sciatic +sciatica +sciatica's +science +sciences +science's +consciences +conscience's +conscience +scientific +unscientific +scientifically +unscientifically +scientist +scientists +scientist's +scimitar +scimitars +scimitar's +scintilla +scintillas +scintilla's +scintillate +scintillation +scintillating +scintillated +scintillates +scintillation +scintillation's +scion +scions +scion's +scissor +scissoring +scissored +scissors +scleroses +sclerosis +sclerosis's +sclerotic +scoff +scoffing +scoffed +scoffer +scoffers +scoffs +scoff's +scoffer +scoffer's +scofflaw +scofflaws +scofflaw's +scold +scolding +scoldings +scolded +scolds +scold's +scolding +scolding's +scoliosis +scoliosis's +sconce +sconces +sconce's +scone +scones +scone's +scoop +scooping +scooped +scoops +scoop's +scoopful +scoopfuls +scoopful's +scoot +scooting +scooted +scooter +scooters +scoots +scooter +scooter's +scope +scoping +scoped +scopes +scope's +scorbutic +scorch +scorching +scorched +scorcher +scorchers +scorches +scorch's +scorcher +scorcher's +score +scoring +scored +scorer +scorers +scores +score's +scoreboard +scoreboards +scoreboard's +scorecard +scorecards +scorecard's +scorekeeper +scorekeepers +scorekeeper's +scoreless +scoreline +scorelines +scorer +scorer's +scorn +scorning +scorned +scorner +scorners +scorns +scorn's +scorner +scorner's +scornful +scornfully +scorpion +scorpions +scorpion's +scotch +scotching +scotched +scotches +scotch's +scotchs +scoundrel +scoundrels +scoundrel's +scour +scouring +scoured +scourer +scourers +scours +scourer +scourer's +scourge +scourging +scourged +scourges +scourge's +scout +scouting +scouted +scouter +scouters +scouts +scout's +scouting +scouting's +scoutmaster +scoutmasters +scoutmaster's +scow +scows +scow's +scowl +scowling +scowled +scowls +scowl's +scrabble +scrabbling +scrabbled +scrabbler +scrabblers +scrabbles +scrabble's +scrabbler +scrabbler's +scrag +scrags +scrag's +scraggly +scraggliest +scragglier +scraggy +scraggiest +scraggier +scram +scrams +scramble's +scramble +scrambling +scrambled +scrambles +unscrambling +unscrambled +unscrambles +unscramble +scrambler +scramblers +scrambler's +scrammed +scramming +scrap +scraping +scrapings +scraped +scraper +scrapers +scraps +scrap's +scrapbook +scrapbooks +scrapbook's +scrape +scrapes +scrape's +scraper +scraper's +scrapheap +scrapheaps +scrapheap's +scrapie +scrapped +scrapper +scrappers +scrapper's +scrapping +scrappy +scrappiest +scrappier +scrapyard +scrapyards +scrapyard's +scratch +scratching +scratched +scratches +scratch's +scratchcard +scratchcards +scratched +unscratched +scratchily +scratchiness +scratchiness's +scratchpad +scratchpads +scratchy +scratchiest +scratchier +scratchiness +scrawl +scrawling +scrawled +scrawls +scrawl's +scrawly +scrawniness +scrawniness's +scrawny +scrawniest +scrawnier +scrawniness +scream +screaming +screamed +screamer +screamers +screams +scream's +screamer +screamer's +screaming +screamingly +scree +screed +screes +scree's +screech +screeching +screeched +screeches +screech's +screechy +screechiest +screechier +screed +screeds +screen +screening +screenings +screened +screens +screen's +screening +screening's +screenplay +screenplays +screenplay's +screensaver +screensavers +screensaver's +screenshot +screenshots +screenwriter +screenwriters +screenwriter's +screenwriting +screenwriting's +screw's +screw +screwing +screwed +screws +unscrewing +unscrewed +unscrews +unscrew +screwball +screwballs +screwball's +screwdriver +screwdrivers +screwdriver's +screwiness +screwiness's +screwworm +screwworms +screwworm's +screwy +screwiest +screwier +screwiness +scribal +scribble +scribbling +scribbled +scribbler +scribblers +scribbles +scribble's +scribbler +scribbler's +scribe's +scribe +scribes +inscribes +describes +proscribes +inscribe +describe +proscribe +scrim +scrims +scrim's +scrimmage +scrimmaging +scrimmaged +scrimmages +scrimmage's +scrimp +scrimping +scrimped +scrimps +scrimshaw +scrimshawing +scrimshawed +scrimshaws +scrimshaw's +scrip +scrips +scrip's +script +scripting +scripted +scripts +script's +conscripting +conscripted +conscripts +conscript's +conscript +scripted +unscripted +scriptural +scripture +scriptures +scripture's +scriptwriter +scriptwriters +scriptwriter's +scrivener +scriveners +scrivener's +scrod +scrod's +scrofula +scrofula's +scrofulous +scrog +scrogs +scroll +scrolling +scrolled +scrolls +scroll's +scrooge +scrooges +scrooge's +scrota +scrotal +scrotum +scrotum's +scrounge +scrounging +scrounged +scrounger +scroungers +scrounges +scrounger +scrounger's +scroungy +scroungiest +scroungier +scrub +scrubs +scrub's +scrubbed +scrubber +scrubbers +scrubber's +scrubbing +scrubby +scrubbiest +scrubbier +scruff +scruffs +scruff's +scruffily +scruffiness +scruffiness's +scruffy +scruffiest +scruffier +scruffiness +scrum +scrums +scrumhalf +scrumhalves +scrummage +scrummages +scrummed +scrumming +scrump +scrumping +scrumped +scrumps +scrumptious +scrumptiously +scrumpy +scrunch +scrunching +scrunched +scrunches +scrunch's +scrunchy +scrunchies +scrunchy's +scruple +scrupling +scrupled +scruples +scruple's +scrupulosity +scrupulosity's +scrupulous +scrupulously +scrupulousness +unscrupulously +unscrupulousness +unscrupulous +scrupulousness +scrupulousness's +unscrupulousness's +unscrupulousness +scrutineer +scrutineers +scrutinize +scrutinizing +scrutinized +scrutinizes +scrutiny +scrutiny's +scuba +scubaing +scubaed +scubas +scuba's +scud +scuds +scud's +scudded +scudding +scuff +scuffing +scuffed +scuffs +scuff's +scuffle +scuffling +scuffled +scuffles +scuffle's +scull +sculling +sculled +sculler +scullers +sculls +scull's +sculler +sculler's +scullery +sculleries +scullery's +scullion +scullions +scullion's +sculpt +sculpting +sculpted +sculpts +sculptor +sculptors +sculptor's +sculptress +sculptresses +sculptress's +sculptural +sculpture +sculpturing +sculptured +sculptures +sculpture's +scum +scums +scum's +scumbag +scumbags +scumbag's +scummed +scumming +scummy +scummiest +scummier +scupper +scuppering +scuppered +scuppers +scupper's +scurf +scurf's +scurfy +scurrility +scurrility's +scurrilous +scurrilously +scurrilousness +scurrilousness +scurrilousness's +scurry +scurrying +scurried +scurries +scurry's +scurvily +scurvy +scurviest +scurvier +scurvy's +scutcheon +scutcheons +scutcheon's +scuttle +scuttling +scuttled +scuttles +scuttle's +scuttlebutt +scuttlebutt's +scuzzy +scuzziest +scuzzier +scythe +scything +scythed +scythes +scythe's +sea +seas +sea's +seabed +seabeds +seabed's +seabird +seabirds +seabird's +seaboard +seaboards +seaboard's +seaborne +seacoast +seacoasts +seacoast's +seafarer +seafarers +seafarer's +seafaring +seafaring's +seafloor +seafloors +seafloor's +seafood +seafood's +seafront +seafronts +seafront's +seagoing +seagull +seagulls +seagull's +seahorse +seahorses +seahorse's +seal's +seal +sealing +sealed +seals +resealing +unsealing +resealed +unsealed +reseals +unseals +reseal +unseal +sealant +sealants +sealant's +sealer +sealers +sealer's +sealskin +sealskin's +seam +seamen +seaming +seamed +seams +seam's +seaman +seaman's +seamanship +seamanship's +seamless +seamlessly +seamount +seamounts +seamount's +seamstress +seamstresses +seamstress's +seamy +seamiest +seamier +seance +seances +seance's +seaplane +seaplanes +seaplane's +seaport +seaports +seaport's +sear +searing +seared +sears +sear's +search +searching +searched +searcher +searchers +searches +search's +researching +researched +researcher +researchers +researches +research's +research +searchable +unsearchable +searcher +searcher's +researcher's +researcher +searching +searchingly +searchlight +searchlights +searchlight's +searing +searingly +seascape +seascapes +seascape's +seashell +seashells +seashell's +seashore +seashores +seashore's +seasick +seasickness +seasickness +seasickness's +seaside +seasides +seaside's +season +seasoning +seasonings +seasoned +seasons +season's +seasonable +seasonable +unseasonable +seasonably +unseasonably +seasonal +seasonally +seasonality +seasoned +unseasoned +seasoning +seasoning's +seat's +seat +seating +seated +seats +unseating +unseated +unseats +unseat +seating +seating's +seatmate +seatmates +seatmate's +seawall +seawalls +seawall's +seaward +seawards +seaward's +seawater +seawater's +seaway +seaways +seaway's +seaweed +seaweeds +seaweed's +seaworthiness +seaworthiness's +seaworthy +seaworthiness +sebaceous +seborrhea +seborrhea's +sebum +sec'y +sec +secs +sec's +secant +secants +secant's +secateurs +secede +seceding +seceded +secedes +secession +secession's +secessionist +secessionists +secessionist's +seclude +secluding +secluded +secludes +seclusion +seclusion's +seclusive +second +secondly +seconding +seconded +seconder +seconders +seconds +second's +secondment +secondarily +secondary +secondaries +secondary's +seconder +seconder's +secondhand +secondment +secondments +secrecy +secrecy's +secret +secretive +secretly +secreting +secreted +secrets +secret's +secretarial +secretariat +secretariats +secretariat's +secretary +secretaries +secretary's +secretaryship +secretaryship's +secrete +secretion +secretions +secretes +secretion +secretion's +secretive +secretively +secretiveness +secretiveness +secretiveness's +secretory +sect +sects +sect's +insects +insect's +insect +sectarian +sectarians +sectarian's +sectarianism +sectarianism's +sectary +sectaries +sectary's +section +sections +section's +resections +dissections +resection's +dissection's +resection +dissection +sectional +sectionals +sectional's +sectionalism +sectionalism's +sectioned +sectioning +sector +sectors +sector's +dissectors +dissector's +dissector +secular +secularism +secularism's +secularist +secularists +secularist's +secularization +secularization's +secularize +secularizing +secularized +secularizes +secure +securely +securing +secured +securest +securer +secures +secured +unsecured +security +securities +security's +insecurities +insecurity's +insecurity +secy +sedan +sedans +sedan's +sedate +sedative +sedation +sedately +sedating +sedated +sedatest +sedater +sedates +sedateness +sedateness +sedateness's +sedation +sedation's +sedative +sedatives +sedative's +sedentary +sedge +sedge's +sedgy +sediment +sediments +sediment's +sedimentary +sedimentation +sedimentation's +sedition +sedition's +seditious +seduce +seducing +seduced +seducer +seducers +seduces +seducer +seducer's +seduction +seductions +seduction's +seductive +seductively +seductiveness +seductiveness +seductiveness's +seductress +seductresses +seductress's +sedulous +sedulously +see +seer +seers +sees +see's +seed's +seed +seeding +seeded +seeds +reseeding +reseeded +reseeds +reseed +seedbed +seedbeds +seedbed's +seedcase +seedcases +seedcase's +seeded +unseeded +seeder +seeders +seeder's +seediness +seediness's +seedless +seedling +seedlings +seedling's +seedpod +seedpods +seedpod's +seedy +seediest +seedier +seediness +seeing +seeings +seek +seeking +seeker +seekers +seeks +seeker +seeker's +seem +seeming +seemed +seems +seeming +seemingly +seemliness +seemliness's +unseemliness's +unseemliness +seemly +seemliest +seemlier +seemliness +unseemlier +unseemliness +unseemly +seen +unseen +seep +seeping +seeped +seeps +seepage +seepage's +seer +seer's +seersucker +seersucker's +seesaw +seesawing +seesawed +seesaws +seesaw's +seethe +seething +seethed +seethes +segfault +segfaults +segment +segmenting +segmented +segments +segment's +segmentation +segmentation's +segmented +unsegmented +segregate +segregation +segregating +segregated +segregates +desegregation +desegregating +desegregated +desegregates +desegregate +segregated +unsegregated +segregation +segregation's +desegregation's +desegregation +segregationist +segregationists +segregationist's +segue +seguing +segued +segues +segue's +segueing +seigneur +seigneurs +seigneur's +seignior +seigniors +seignior's +seine +seining +seined +seiner +seiners +seines +seine's +seiner +seiner's +seismic +seismically +seismograph +seismographer +seismographers +seismograph's +seismographer +seismographer's +seismographic +seismographs +seismography +seismography's +seismologic +seismological +seismologist +seismologists +seismologist's +seismology +seismology's +seize +seizing +seized +seizes +seizure +seizures +seizure's +seldom +select +selective +selecting +selected +selects +deselecting +deselected +deselects +deselect +selection +selections +selection's +selective +selectively +selectivity +selectivity's +selectman +selectman's +selectmen +selectness +selectness's +selector +selectors +selector's +selenium +selenium's +selenographer +selenographers +selenographer's +selenography +selenography's +self +self's +selfie +selfies +selfie's +selfish +selfishly +selfishness +unselfishly +unselfishness +unselfish +selfishness +selfishness's +unselfishness's +unselfishness +selfless +selflessly +selflessness +selflessness +selflessness's +selfsame +sell's +sell +selling +seller +sellers +sells +reselling +reseller +resellers +resells +resell +seller's +selloff +selloffs +selloff's +sellotape +sellotaping +sellotaped +sellotapes +sellout +sellouts +sellout's +seltzer +seltzers +seltzer's +selvage +selvages +selvage's +selves +semantic +semantics +semantically +semanticist +semanticists +semanticist's +semantics +semantics's +semaphore +semaphoring +semaphored +semaphores +semaphore's +semblance +semblances +semblance's +resemblances +resemblance's +resemblance +semen +semen's +semester +semesters +semester's +semi +semis +semi's +semiannual +semiannually +semiarid +semiautomatic +semiautomatics +semiautomatic's +semibreve +semibreves +semicircle +semicircles +semicircle's +semicircular +semicolon +semicolons +semicolon's +semiconducting +semiconductor +semiconductors +semiconductor's +semiconscious +semidarkness +semidarkness's +semidetached +semifinal +semifinals +semifinal's +semifinalist +semifinalists +semifinalist's +semigloss +semiglosses +semimonthly +semimonthlies +semimonthly's +seminal +seminar +seminars +seminar's +seminarian +seminarians +seminarian's +seminary +seminaries +seminary's +semiofficial +semiotic +semiotics +semiotics +semiotics's +semipermeable +semiprecious +semiprivate +semipro +semipros +semiprofessional +semiprofessionals +semiprofessional's +semiquaver +semiquavers +semiretired +semiskilled +semisolid +semisweet +semitone +semitones +semitone's +semitrailer +semitrailers +semitrailer's +semitransparent +semitropical +semivowel +semivowels +semivowel's +semiweekly +semiweeklies +semiweekly's +semiyearly +semolina +semolina's +sempstress +sempstresses +sempstress's +senate +senates +senate's +senator +senators +senator's +senatorial +send +sending +sender +senders +sends +sender +sender's +sendoff +sendoffs +sendoff's +senescence +senescence's +senescent +senile +senility +senility's +senior +seniors +senior's +seniority +seniority's +senna +senna's +senor +senors +senor's +senora +senoras +senora's +senorita +senoritas +senorita's +sensation +sensations +sensation's +sensational +sensationally +sensationalism +sensationalism's +sensationalist +sensationalists +sensationalist's +sensationalize +sensationalizing +sensationalized +sensationalizes +sense +sensing +sensed +senses +sense's +senseless +senselessly +senselessness +senselessness +senselessness's +sensibilities +sensibility +sensibility's +insensibility's +insensibility +sensible +sensibleness +sensibleness +sensibleness's +sensibly +insensibly +sensitive +sensitively +sensitives +sensitiveness +sensitive's +sensitiveness +sensitiveness's +sensitivities +sensitivity +sensitivity's +insensitivity's +insensitivity +sensitization +sensitization's +desensitization's +desensitization +sensitize +sensitizing +sensitized +sensitizes +desensitizing +desensitized +desensitizes +desensitize +sensor +sensors +sensor's +sensory +sensual +sensually +sensualist +sensualists +sensualist's +sensuality +sensuality's +sensuous +sensuously +sensuousness +sensuousness +sensuousness's +sent +resent +unsent +consent +sentence +sentencing +sentenced +sentences +sentence's +sententious +sententiously +sentience +sentience's +insentience's +insentience +sentient +insentient +sentiment +sentiments +sentiment's +sentimental +sentimentally +sentimentalism +sentimentalism's +sentimentalist +sentimentalists +sentimentalist's +sentimentality +sentimentality's +sentimentalization +sentimentalization's +sentimentalize +sentimentalizing +sentimentalized +sentimentalizes +sentinel +sentinels +sentinel's +sentry +sentries +sentry's +sepal +sepals +sepal's +separability +separability's +inseparability's +inseparability +separable +separably +inseparably +separate +separative +separation +separations +separately +separating +separated +separates +separateness +separate's +separateness +separateness's +separation +separation's +separatism +separatism's +separatist +separatists +separatist's +separator +separators +separator's +sepia +sepia's +sepsis +sepsis's +septa +septal +septet +septets +septet's +septic +septicemia +septicemia's +septicemic +septuagenarian +septuagenarians +septuagenarian's +septum +septum's +sepulcher +sepulchering +sepulchered +sepulchers +sepulcher's +sepulchral +seq +sequel +sequels +sequel's +sequence +sequencing +sequenced +sequencer +sequencers +sequences +sequence's +sequencing +sequencing's +sequential +sequentially +consequentially +consequential +sequester +sequestering +sequestered +sequesters +sequestrate +sequestration +sequestrations +sequestrating +sequestrated +sequestrates +sequestration +sequestration's +sequin +sequined +sequins +sequin's +sequinned +sequitur +sequoia +sequoias +sequoia's +seraglio +seraglios +seraglio's +serape +serapes +serape's +seraph +seraph's +seraphic +seraphs +sere +serest +serer +serenade +serenading +serenaded +serenades +serenade's +serendipitous +serendipity +serendipity's +serene +serenely +serenest +serener +sereneness +sereneness +sereneness's +serenity +serenity's +serf +serfs +serf's +serfdom +serfdom's +serge +serge's +sergeant +sergeants +sergeant's +serial +serially +serials +serial's +serialization +serializations +serialization's +serialize +serializing +serialized +serializes +series +series's +serif +serifs +serif's +serigraph +serigraph's +serigraphs +serine +serious +seriously +seriousness +seriousness +seriousness's +sermon +sermons +sermon's +sermonize +sermonizing +sermonized +sermonizes +serology +serology's +serotonin +serous +serpent +serpents +serpent's +serpentine +serpentine's +serrate +serration +serrations +serrated +serration +serration's +serried +serum +serums +serum's +servant +servants +servant's +serve's +reserve's +conserve's +serve +serving +served +serves +reserving +deserving +conserving +reserved +deserved +conserved +reserves +deserves +conserves +reserve +deserve +conserve +server +servers +server's +servery +serveries +service +services +service's +disservices +disservice's +disservice +serviceability +serviceability's +serviceable +serviced +serviceman +serviceman's +servicemen +servicewoman +servicewoman's +servicewomen +servicing +serviette +serviettes +serviette's +servile +servility +servility's +serving's +servings +servitor +servitors +servitor's +servitude +servitude's +servo +servos +servo's +servomechanism +servomechanisms +servomechanism's +servomotor +servomotors +servomotor's +sesame +sesames +sesame's +sesquicentennial +sesquicentennials +sesquicentennial's +session +sessions +session's +set +sets +set's +resets +insets +reset's +inset's +reset +inset +setback +setbacks +setback's +setscrew +setscrews +setscrew's +setsquare +setsquares +sett +setting +settings +setter +setters +setts +settable +settee +settees +settee's +setter +setter's +setting +setting's +settle's +settle +settling +settled +settles +resettling +unsettling +resettled +unsettled +resettles +unsettles +resettle +unsettle +settlement +settlement's +resettlement's +resettlement +settlements +settler +settlers +settler's +setup +setups +setup's +seven +seventh +sevens +seven's +seventeen +seventeenth +seventeens +seventeen's +seventeenth +seventeenth's +seventeenths +seventh +seventh's +sevenths +seventieth +seventieth's +seventieths +seventy +seventieth +seventies +seventy's +sever +severing +severed +severest +severs +dissevering +dissevered +dissevers +dissever +several +severally +several's +severance +severances +severance's +severe +severely +severer +severeness +severeness +severeness's +severity +severity's +sew +sewing +sewed +sews +resewing +resewed +resews +resew +sewage +sewage's +sewer +sewers +sewer's +sewerage +sewerage's +sewing +sewing's +sewn +resewn +sex +sexing +sexed +sexes +sex's +sexagenarian +sexagenarians +sexagenarian's +sexily +sexiness +sexiness's +sexism +sexism's +sexist +sexists +sexist's +sexless +sexologist +sexologists +sexologist's +sexology +sexology's +sexpot +sexpots +sexpot's +sextant +sextants +sextant's +sextet +sextets +sextet's +sexting +sexton +sextons +sexton's +sextuplet +sextuplets +sextuplet's +sexual +sexually +sexuality +sexuality's +sexy +sexiest +sexier +sexiness +sf +sh +shabbily +shabbiness +shabbiness's +shabby +shabbiest +shabbier +shabbiness +shack +shacking +shacked +shacks +shack's +shackle's +shackle +shackling +shackled +shackles +unshackling +unshackled +unshackles +unshackle +shad +shading +shadings +shaded +shads +shad's +shade +shades +shade's +shadily +shadiness +shadiness's +shading +shading's +shadow +shadowing +shadowed +shadows +shadow's +shadowbox +shadowboxing +shadowboxed +shadowboxes +shadowy +shadowiest +shadowier +shady +shadiest +shadier +shadiness +shaft +shafting +shafted +shafts +shaft's +shag +shags +shag's +shagged +shagginess +shagginess's +shagging +shaggy +shaggiest +shaggier +shagginess +shah +shah's +shahs +shake +shaking +shaker +shakers +shakes +shake's +shakedown +shakedowns +shakedown's +shaken +unshaken +shakeout +shakeouts +shakeout's +shaker +shaker's +shakeup +shakeups +shakeup's +shakily +shakiness +shakiness's +shaky +shakiest +shakier +shakiness +shale +shale's +shall +shallot +shallots +shallot's +shallow +shallowly +shallowest +shallower +shallows +shallowness +shallow's +shallowness +shallowness's +shalom +shalt +sham +shaming +shamed +shams +sham's +shaman +shamans +shaman's +shamanic +shamanism +shamanistic +shamble +shambling +shambled +shambles +shamble's +shambles +shambles's +shambolic +shame +shames +shame's +shamefaced +shamefacedly +shameful +shamefully +shamefulness +shamefulness +shamefulness's +shameless +shamelessly +shamelessness +shamelessness +shamelessness's +shammed +shamming +shampoo +shampooing +shampooed +shampooer +shampooers +shampoos +shampoo's +shampooer +shampooer's +shamrock +shamrocks +shamrock's +shan't +shandy +shandies +shanghai +shanghaiing +shanghaied +shanghais +shank +shanks +shank's +shantung +shantung's +shanty +shanties +shanty's +shantytown +shantytowns +shantytown's +shape's +shape +shaping +shaped +shapes +reshaping +reshaped +reshapes +reshape +shaped +unshaped +shapeless +shapelessly +shapelessness +shapelessness +shapelessness's +shapeliness +shapeliness's +shapely +shapeliest +shapelier +shapeliness +shard +shards +shard's +share +sharing +shared +sharer +sharers +shares +share's +shareable +sharecrop +sharecrops +sharecropped +sharecropper +sharecroppers +sharecropper's +sharecropping +shareholder +shareholders +shareholder's +shareholding +shareholdings +sharer +sharer's +shareware +shareware's +sharia +sharia's +shariah +shark +sharking +sharked +sharks +shark's +sharkskin +sharkskin's +sharp +sharpen +sharpens +sharply +sharping +sharped +sharpest +sharper +sharpers +sharps +sharpness +sharp's +sharpen +sharpening +sharpened +sharpens +resharpening +resharpened +resharpens +resharpen +sharpener +sharpeners +sharpener's +sharper +sharper's +sharpie +sharpies +sharpie's +sharpish +sharpness +sharpness's +sharpshooter +sharpshooters +sharpshooter's +sharpshooting +sharpshooting's +shatter +shattering +shattered +shatters +shatter's +shatterproof +shave +shaving +shavings +shaved +shaver +shavers +shaves +shave's +shaven +unshaven +shaver +shaver's +shaving +shaving's +shawl +shawls +shawl's +shay +shays +shay's +she'd +she'll +she +shed +shes +she's +sheaf +sheaf's +shear +shearing +sheared +shearer +shearers +shears +shear's +shearer +shearer's +sheath +sheathings +sheath's +sheathe +sheathing +sheathed +sheathes +unsheathing +unsheathed +unsheathes +unsheathe +sheathing +sheathing's +sheaths +sheave +sheaving +sheaved +sheaves +sheave's +shebang +shebangs +shebang's +shebeen +shebeens +shed +sheds +shed's +shedding +sheen +sheen's +sheeny +sheeniest +sheenier +sheep +sheep's +sheepdog +sheepdogs +sheepdog's +sheepfold +sheepfolds +sheepfold's +sheepherder +sheepherders +sheepherder's +sheepish +sheepishly +sheepishness +sheepishness +sheepishness's +sheepskin +sheepskins +sheepskin's +sheer +sheering +sheered +sheerest +sheerer +sheers +sheerness +sheer's +sheerness +sheerness's +sheet +sheeting +sheets +sheet's +sheeting +sheeting's +sheetlike +sheikdom +sheikdoms +sheikdom's +sheikh +sheikh's +sheikhs +sheila +sheilas +shekel +shekels +shekel's +shelf +shelf's +shell +shelling +shelled +sheller +shells +shell's +shellac +shellacs +shellac's +shellacked +shellacking +shellackings +shellacking's +shellfire +shellfire's +shellfish +shellfishes +shellfish's +shelter +sheltering +sheltered +shelters +shelter's +shelve +shelving +shelved +shelves +shelving +shelving's +shenanigan +shenanigans +shenanigan's +shepherd +shepherding +shepherded +shepherds +shepherd's +shepherdess +shepherdesses +shepherdess's +sherbet +sherbets +sherbet's +sheriff +sheriffs +sheriff's +sherry +sherries +sherry's +shew +shewing +shewed +shews +shewn +shh +shiatsu +shiatsu's +shibboleth +shibboleth's +shibboleths +shield +shielding +shielded +shields +shield's +shift +shifting +shifted +shifts +shift's +shiftily +shiftiness +shiftiness's +shiftless +shiftlessly +shiftlessness +shiftlessness +shiftlessness's +shifty +shiftiest +shiftier +shiftiness +shiitake +shiitakes +shiitake's +shill +shilling +shillings +shilled +shills +shill's +shillelagh +shillelagh's +shillelaghs +shilling +shilling's +shim +shims +shim's +shimmed +shimmer +shimmering +shimmered +shimmers +shimmer's +shimmery +shimming +shimmy +shimmying +shimmied +shimmies +shimmy's +shin +shining +shined +shiner +shiners +shins +shin's +shinbone +shinbones +shinbone's +shindig +shindigs +shindig's +shine +shines +shine's +shiner +shiner's +shingle +shingling +shingled +shingles +shingle's +shinguard +shinguard's +shininess +shininess's +shinned +shinning +shinny +shinnying +shinnied +shinnies +shinsplints +shinsplints's +shiny +shiniest +shinier +shininess +ship's +ship +ships +shipment +reships +reshipment +reship +shipboard +shipboards +shipboard's +shipbuilder +shipbuilders +shipbuilder's +shipbuilding +shipbuilding's +shipload +shiploads +shipload's +shipmate +shipmates +shipmate's +shipment +shipment's +reshipment's +reshipment +shipments +shipowner +shipowners +shipowner's +shipped +reshipped +shipper +shippers +shipper's +shipping +shipping's +shipshape +shipwreck +shipwrecking +shipwrecked +shipwrecks +shipwreck's +shipwright +shipwrights +shipwright's +shipyard +shipyards +shipyard's +shire +shires +shire's +shirk +shirking +shirked +shirker +shirkers +shirks +shirker +shirker's +shirr +shirring +shirrings +shirred +shirrs +shirr's +shirring +shirring's +shirt +shirting +shirted +shirts +shirt's +shirtfront +shirtfronts +shirtfront's +shirting +shirting's +shirtless +shirtsleeve +shirtsleeves +shirtsleeve's +shirttail +shirttails +shirttail's +shirtwaist +shirtwaists +shirtwaist's +shirty +shit +shits +shit's +shitfaced +shithead +shitheads +shitload +shitted +shitting +shitty +shittiest +shittier +shiv +shiver +shivers +shivs +shiv's +shiver +shivering +shivered +shiver's +shivery +shoal +shoaling +shoaled +shoals +shoal's +shoat +shoats +shoat's +shock +shocking +shocked +shocker +shockers +shocks +shock's +shocker +shocker's +shocking +shockingly +shockproof +shod +unshod +shoddily +shoddiness +shoddiness's +shoddy +shoddiest +shoddier +shoddiness +shoddy's +shoe +shoes +shoe's +shoehorn +shoehorning +shoehorned +shoehorns +shoehorn's +shoeing +shoelace +shoelaces +shoelace's +shoemaker +shoemakers +shoemaker's +shoeshine +shoeshines +shoeshine's +shoestring +shoestrings +shoestring's +shoetree +shoetrees +shoetree's +shogun +shoguns +shogun's +shogunate +shogunate's +shone +shoo +shooing +shooed +shoos +shook +shoot +shooting +shootings +shooter +shooters +shoots +shoot's +shooter +shooter's +shooting +shooting's +shootout +shootouts +shootout's +shop +shops +shop's +shopaholic +shopaholics +shopaholic's +shopfitter +shopfitters +shopfitting +shopfront +shopfronts +shopkeeper +shopkeepers +shopkeeper's +shoplift +shoplifting +shoplifted +shoplifter +shoplifters +shoplifts +shoplifter +shoplifter's +shoplifting +shoplifting's +shoppe +shopping +shopped +shopper +shoppers +shoppes +shoppe's +shopper +shopper's +shopping +shopping's +shoptalk +shoptalk's +shopworn +shore +shoring +shored +shores +shore's +shorebird +shorebirds +shorebird's +shoreline +shorelines +shoreline's +shoring +shoring's +short +shorten +shortens +shortly +shorting +shorted +shortest +shorter +shorts +shortness +short's +shortage +shortages +shortage's +shortbread +shortbread's +shortcake +shortcakes +shortcake's +shortchange +shortchanging +shortchanged +shortchanges +shortcoming +shortcomings +shortcoming's +shortcrust +shortcut +shortcuts +shortcut's +shorten +shortening +shortenings +shortened +shortening +shortening's +shortfall +shortfalls +shortfall's +shorthand +shorthanded +shorthand's +shorthorn +shorthorns +shorthorn's +shortish +shortlist +shortlisting +shortlisted +shortlists +shortness +shortness's +shortsighted +shortsightedly +shortsightedness +shortsightedness +shortsightedness's +shortstop +shortstops +shortstop's +shortwave +shortwaves +shortwave's +shorty +shorties +shorty's +shot +shots +shot's +shotgun +shotguns +shotgun's +shotgunned +shotgunning +should +should've +shoulder +shouldering +shouldered +shoulders +shoulder's +shouldn't +shout +shouting +shouted +shouter +shouters +shouts +shout's +shouter +shouter's +shove +shoving +shoved +shoves +shove's +shovel +shoveling +shoveled +shovels +shovel's +shovelful +shovelfuls +shovelful's +show +showing +showings +showed +shower +showers +shows +show's +showbiz +showbiz's +showboat +showboating +showboated +showboats +showboat's +showcase +showcasing +showcased +showcases +showcase's +showdown +showdowns +showdown's +shower +showering +showered +shower's +showerproof +showery +showgirl +showgirls +showgirl's +showground +showgrounds +showily +showiness +showiness's +showing +showing's +showjumping +showman +showman's +showmanship +showmanship's +showmen +shown +showoff +showoffs +showoff's +showpiece +showpieces +showpiece's +showplace +showplaces +showplace's +showroom +showrooms +showroom's +showstopper +showstoppers +showstopper's +showstopping +showtime +showy +showiest +showier +showiness +shpt +shrank +shrapnel +shrapnel's +shred +shreds +shred's +shredded +shredder +shredders +shredder's +shredding +shrew +shrews +shrew's +shrewd +shrewdly +shrewdest +shrewder +shrewdness +shrewdness +shrewdness's +shrewish +shriek +shrieking +shrieked +shrieks +shriek's +shrift +shrift's +shrike +shrikes +shrike's +shrill +shrilling +shrilled +shrillest +shriller +shrills +shrillness +shrillness +shrillness's +shrilly +shrimp +shrimping +shrimped +shrimper +shrimpers +shrimps +shrimp's +shrine +shrines +shrine's +shrink +shrinking +shrinks +shrink's +shrinkable +shrinkage +shrinkage's +shrive +shriving +shrived +shrives +shrivel +shriveling +shriveled +shrivels +shriven +shroud +shrouding +shrouded +shrouds +shroud's +shrub +shrubs +shrub's +shrubbery +shrubberies +shrubbery's +shrubby +shrubbiest +shrubbier +shrug +shrugs +shrug's +shrugged +shrugging +shrunk +shrunken +shtick +shticks +shtick's +shuck +shucking +shucked +shucks +shuck's +shucks +shuckses +shudder +shuddering +shuddered +shudders +shudder's +shuffle +shuffling +shuffled +shuffles +shuffle's +reshuffling +reshuffled +reshuffles +reshuffle's +reshuffle +shuffleboard +shuffleboards +shuffleboard's +shuffler +shufflers +shuffler's +shun +shuns +shunned +shunning +shunt +shunting +shunted +shunts +shunt's +shush +shushing +shushed +shushes +shut +shuts +shutdown +shutdowns +shutdown's +shuteye +shuteye's +shutoff +shutoffs +shutoff's +shutout +shutouts +shutout's +shutter +shuttering +shuttered +shutters +shutter's +shutterbug +shutterbugs +shutterbug's +shutting +shuttle +shuttling +shuttled +shuttles +shuttle's +shuttlecock +shuttlecocking +shuttlecocked +shuttlecocks +shuttlecock's +shy +shyly +shying +shied +shiest +shier +shies +shy's +shyer +shyest +shyness +shyness's +shyster +shysters +shyster's +sibilant +sibilants +sibilant's +sibling +siblings +sibling's +sibyl +sibyls +sibyl's +sibylline +sic +sics +sicced +siccing +sick +sicken +sickens +sickly +sicking +sicked +sickest +sicker +sicks +sickness +sickbay +sickbays +sickbed +sickbeds +sickbed's +sicken +sickening +sickened +sickening +sickeningly +sickie +sickies +sickie's +sickish +sickle +sickles +sickle's +sickly +sickliest +sicklier +sickness +sicknesses +sickness's +sicko +sickos +sicko's +sickout +sickouts +sickout's +sickroom +sickrooms +sickroom's +side's +side +siding +sided +sides +residing +resided +resides +reside +sidearm +sidearms +sidearm's +sidebar +sidebars +sidebar's +sideboard +sideboards +sideboard's +sideburns +sideburns's +sidecar +sidecars +sidecar's +sidekick +sidekicks +sidekick's +sidelight +sidelights +sidelight's +sideline +sidelining +sidelined +sidelines +sideline's +sidelong +sideman +sideman's +sidemen +sidepiece +sidepieces +sidepiece's +sidereal +sidesaddle +sidesaddles +sidesaddle's +sideshow +sideshows +sideshow's +sidesplitting +sidestep +sidesteps +sidestep's +sidestepped +sidestepping +sidestroke +sidestroking +sidestroked +sidestrokes +sidestroke's +sideswipe +sideswiping +sideswiped +sideswipes +sideswipe's +sidetrack +sidetracking +sidetracked +sidetracks +sidetrack's +sidewalk +sidewalks +sidewalk's +sidewall +sidewalls +sidewall's +sideways +sidewinder +sidewinders +sidewinder's +siding +sidings +siding's +sidle +sidling +sidled +sidles +sidle's +siege +sieges +siege's +sienna +sienna's +sierra +sierras +sierra's +siesta +siestas +siesta's +sieve +sieving +sieved +sieves +sieve's +sift +sifting +sifted +sifter +sifters +sifts +sifted +unsifted +sifter +sifter's +sigh +sighing +sighed +sigh's +sighs +sight +sightly +sighting +sightings +sighted +sights +sight's +sighting +sighting's +sightless +sightly +sightliest +sightlier +unsightlier +unsightly +sightread +sightseeing +sightseeing's +sightseer +sightseers +sightseer's +sigma +sigmas +sigma's +sign's +design's +sign +signing +signed +signs +resigning +designing +consigning +resigned +designed +consigned +resigns +designs +consigns +resign +design +consign +signage +signage's +signal +signally +signaling +signaled +signaler +signalers +signals +signal's +signaler +signaler's +signalization +signalization's +signalize +signalizing +signalized +signalizes +signalman +signalman's +signalmen +signatory +signatories +signatory's +signature +signatures +signature's +signboard +signboards +signboard's +signed +unsigned +signer +signers +signer's +designers +designer's +designer +signet +signets +signet's +significance +significance's +insignificance's +insignificance +significant +significantly +insignificantly +insignificant +signification +signification's +signify +signification +significations +signifying +signified +signifies +signing's +designing's +signings +signor +signors +signor's +consignors +consignor's +consignor +signora +signoras +signora's +signore +signori +signorina +signorinas +signorina's +signorine +signpost +signposting +signposted +signposts +signpost's +silage +silage's +silence +silencing +silenced +silencer +silencers +silences +silence's +silencer +silencer's +silent +silently +silentest +silenter +silents +silent's +silhouette +silhouetting +silhouetted +silhouettes +silhouette's +silica +silica's +silicate +silicates +silicate's +siliceous +silicon +silicons +silicon's +silicone +silicone's +silicosis +silicosis's +silk +silken +silks +silk's +silkily +silkiness +silkiness's +silkscreen +silkscreens +silkscreen's +silkworm +silkworms +silkworm's +silky +silkiest +silkier +silkiness +sill +sills +sill's +silliness +silliness's +silly +silliest +sillier +sillies +silliness +silly's +silo +silos +silo's +silt +silting +silted +silts +silt's +silty +siltiest +siltier +silver +silvering +silvered +silvers +silver's +silverfish +silverfishes +silverfish's +silversmith +silversmith's +silversmiths +silverware +silverware's +silvery +sim +sims +sim's +simian +simians +simian's +similar +similarly +similarity +similarities +similarity's +dissimilarities +dissimilarity's +dissimilarity +simile +similes +simile's +similitude +similitude's +dissimilitude's +dissimilitude +simmer +simmering +simmered +simmers +simmer's +simonize +simonizing +simonized +simonizes +simony +simony's +simpatico +simper +simpering +simpered +simpers +simper's +simpering +simperingly +simple +simplest +simpler +simpleness +simpleminded +simpleness +simpleness's +simpleton +simpletons +simpleton's +simplex +simplicity +simplicity's +simplification +simplification's +simplify +simplification +simplifications +simplifying +simplified +simplifies +simplistic +simplistically +simply +simulacra +simulacrum +simulacrums +simulate +simulation +simulating +simulated +simulates +dissimulation +dissimulating +dissimulated +dissimulates +dissimulate +simulation +simulation's +dissimulation's +dissimulation +simulations +simulator +simulators +simulator's +dissimulators +dissimulator's +dissimulator +simulcast +simulcasting +simulcasted +simulcasts +simulcast's +simultaneity +simultaneity's +simultaneous +simultaneously +sin +sins +sin's +resins +resin's +resin +since +sincere +sincerely +sincerest +insincerely +insincere +sincerer +sincerity +sincerity's +insincerity's +insincerity +sine +sines +sine's +sinecure +sinecures +sinecure's +sinew +sinews +sinew's +sinewy +sinful +sinfully +sinfulness +sinfulness +sinfulness's +sing +singly +singing +singed +singer +singers +sings +sing's +singable +singalong +singalongs +singe +singes +singe's +singeing +singer +singer's +singing +singing's +single +singling +singled +singles +singleness +single's +singleness +singleness's +singles +singles's +singlet +singlets +singleton +singletons +singleton's +singletree +singletrees +singletree's +singsong +singsonging +singsonged +singsongs +singsong's +singular +singularly +singulars +singular's +singularity +singularities +singularity's +sinister +sink +sinking +sinker +sinkers +sinks +sink's +sinkable +sinkable +unsinkable +sinker +sinker's +sinkhole +sinkholes +sinkhole's +sinless +sinned +sinner +sinners +sinner's +sinning +sinology +sinuosity +sinuosity's +sinuous +sinuously +sinus +sinuses +sinus's +sinusitis +sinusitis's +sinusoidal +sip +sips +sip's +siphon +siphoning +siphoned +siphons +siphon's +sipped +sipper +sippers +sipper's +sipping +sir +siren +sirens +sirs +sir's +sire +siring +sired +sires +sire's +desiring +desired +desires +desire's +desire +siren +siren's +sirloin +sirloins +sirloin's +sirocco +siroccos +sirocco's +sirrah +sirree +sirree's +sis +sises +sis's +sisal +sisal's +sissified +sissy +sissiest +sissier +sissies +sissy's +sister +sisters +sister's +resisters +resister's +resister +sisterhood +sisterhoods +sisterhood's +sisterliness +sisterliness's +sisterly +sisterliness +sit +sits +sitar +sitars +sitar's +sitarist +sitarists +sitarist's +sitcom +sitcoms +sitcom's +site +siting +sited +sites +site's +sitemap +sitemaps +sitemap's +sitter +sitters +sitter's +sitting +sittings +sitting's +situate +situation +situations +situating +situated +situates +situation +situation's +situational +six +sixth +sixes +six's +sixfold +sixpence +sixpences +sixpence's +sixshooter +sixshooter's +sixteen +sixteenth +sixteens +sixteen's +sixteenth +sixteenth's +sixteenths +sixth +sixth's +sixths +sixtieth +sixtieth's +sixtieths +sixty +sixtieth +sixties +sixty's +sizable +size's +size +sizing +sized +sizes +resizing +resized +resizes +resize +sizer +sizing +sizing's +sizzle +sizzling +sizzled +sizzler +sizzlers +sizzles +sizzle's +ska +ska's +skate +skating +skated +skater +skaters +skates +skate's +skateboard +skateboarding +skateboarded +skateboarder +skateboarders +skateboards +skateboard's +skateboarder +skateboarder's +skateboarding +skateboarding's +skater +skater's +skating +skating's +skedaddle +skedaddling +skedaddled +skedaddles +skedaddle's +skeet +skeeter +skeeters +skeet's +skein +skeins +skein's +skeletal +skeleton +skeletons +skeleton's +skeptic +skeptics +skeptic's +skeptical +skeptically +skepticism +skepticism's +sketch +sketching +sketched +sketcher +sketchers +sketches +sketch's +sketchbook +sketchbooks +sketcher +sketcher's +sketchily +sketchiness +sketchiness's +sketchpad +sketchpads +sketchy +sketchiest +sketchier +sketchiness +skew +skewing +skewed +skewer +skewers +skews +skew's +skewbald +skewbalds +skewer +skewering +skewered +skewer's +ski +skiing +skied +skier +skiers +skis +ski's +skibob +skibobs +skid +skids +skid's +skidded +skidding +skidpan +skidpans +skier +skier's +skiff +skiffs +skiff's +skiffle +skiing +skiing's +skill's +skill +skilled +skills +deskilled +deskills +deskill +skilled +unskilled +skillet +skillets +skillet's +skillful +skillfully +unskillfully +unskillful +skillfulness +skillfulness's +skim +skims +skim's +skimmed +skimmer +skimmers +skimmer's +skimming +skimp +skimping +skimped +skimps +skimpily +skimpiness +skimpiness's +skimpy +skimpiest +skimpier +skimpiness +skin +skins +skin's +skincare +skincare's +skinflint +skinflints +skinflint's +skinful +skinhead +skinheads +skinhead's +skinless +skinned +skinniness +skinniness's +skinning +skinny +skinniest +skinnier +skinniness +skinny's +skint +skintight +skip +skips +skip's +skipped +skipper +skippering +skippered +skippers +skipper's +skipping +skirmish +skirmishing +skirmished +skirmisher +skirmishers +skirmishes +skirmish's +skirt +skirting +skirted +skirts +skirt's +skit +skits +skit's +skitter +skittering +skittered +skitters +skittish +skittishly +skittishness +skittishness +skittishness's +skittle +skittles +skive +skiving +skived +skiver +skivers +skives +skivvy +skivvying +skivvied +skivvies +skivvy's +skoal +skoals +skoal's +skua +skuas +skulduggery +skulduggery's +skulk +skulking +skulked +skulker +skulkers +skulks +skulker +skulker's +skull +skulls +skull's +skullcap +skullcaps +skullcap's +skunk +skunking +skunked +skunks +skunk's +sky +skying +skies +sky's +skycap +skycaps +skycap's +skydive +skydiving +skydived +skydiver +skydivers +skydives +skydiver +skydiver's +skydiving +skydiving's +skyjack +skyjacking +skyjackings +skyjacked +skyjacker +skyjackers +skyjacks +skyjacker +skyjacker's +skyjacking +skyjacking's +skylark +skylarking +skylarked +skylarks +skylark's +skylight +skylights +skylight's +skyline +skylines +skyline's +skyrocket +skyrocketing +skyrocketed +skyrockets +skyrocket's +skyscraper +skyscrapers +skyscraper's +skyward +skywards +skywriter +skywriters +skywriter's +skywriting +skywriting's +slab +slabs +slab's +slabbed +slabbing +slack +slacken +slackens +slackly +slacking +slacked +slackest +slacker +slackers +slacks +slackness +slack's +slacken +slackening +slackened +slacker +slacker's +slackness +slackness's +slacks +slacks's +slag +slags +slag's +slagged +slagging +slagheap +slagheaps +slain +slake +slaking +slaked +slakes +slalom +slaloming +slalomed +slaloms +slalom's +slam +slams +slam's +slammed +slammer +slammers +slammer's +slamming +slander +slandering +slandered +slanderer +slanderers +slanders +slander's +slanderer +slanderer's +slanderous +slang +slang's +slangy +slangiest +slangier +slant +slanting +slanted +slants +slant's +slanting +slantingly +slantwise +slap +slaps +slap's +slapdash +slaphappy +slapped +slapper +slappers +slapping +slapstick +slapstick's +slash +slashing +slashed +slasher +slashers +slashes +slash's +slasher +slasher's +slat +slating +slated +slats +slat's +slate +slates +slate's +slather +slathering +slathered +slathers +slatted +slattern +slatternly +slatterns +slattern's +slaughter +slaughtering +slaughtered +slaughterer +slaughterers +slaughters +slaughter's +slaughterer +slaughterer's +slaughterhouse +slaughterhouses +slaughterhouse's +slave +slaving +slaved +slaver +slavers +slaves +slave's +slaveholder +slaveholders +slaveholder's +slaver +slavering +slavered +slaver's +slavery +slavery's +slavish +slavishly +slavishness +slavishness +slavishness's +slaw +slaw's +slay +slaying +slayings +slayed +slayer +slayers +slays +slayer +slayer's +slaying +slaying's +sleaze +sleazes +sleaze's +sleazebag +sleazebags +sleazeball +sleazeballs +sleazily +sleaziness +sleaziness's +sleazy +sleaziest +sleazier +sleaziness +sled +sleds +sled's +sledded +sledder +sledders +sledder's +sledding +sledge +sledging +sledged +sledges +sledge's +sledgehammer +sledgehammering +sledgehammered +sledgehammers +sledgehammer's +sleek +sleekly +sleeking +sleeked +sleekest +sleeker +sleeks +sleekness +sleekness +sleekness's +sleep +sleeping +sleeper +sleepers +sleeps +sleep's +sleeper +sleeper's +sleepily +sleepiness +sleepiness's +sleepless +sleeplessly +sleeplessness +sleeplessness +sleeplessness's +sleepover +sleepovers +sleepover's +sleepwalk +sleepwalking +sleepwalked +sleepwalker +sleepwalkers +sleepwalks +sleepwalker +sleepwalker's +sleepwalking +sleepwalking's +sleepwear +sleepwear's +sleepy +sleepiest +sleepier +sleepiness +sleepyhead +sleepyheads +sleepyhead's +sleet +sleeting +sleeted +sleets +sleet's +sleety +sleeve +sleeved +sleeves +sleeve's +sleeveless +sleigh +sleighing +sleighed +sleigh's +sleighs +sleight +sleights +sleight's +slender +slenderest +slenderer +slenderness +slenderize +slenderizing +slenderized +slenderizes +slenderness +slenderness's +slept +sleuth +sleuthing +sleuth's +sleuths +slew +slewing +slewed +slews +slew's +slice +slicing +sliced +slicer +slicers +slices +slice's +slicer +slicer's +slick +slickly +slicking +slicked +slickest +slicker +slickers +slicks +slickness +slick's +slicker +slicker's +slickness +slickness's +slid +slide +sliding +slider +sliders +slides +slide's +slider +slider's +slideshow +slideshows +slideshow's +slight +slightly +slighting +slighted +slightest +slighter +slights +slightness +slight's +slightness +slightness's +slim +slims +slimness +slime +slime's +sliminess +sliminess's +slimline +slimmed +slimmer +slimmers +slimmest +slimming +slimming's +slimness +slimness's +slimy +slimiest +slimier +sliminess +sling +slinging +slings +sling's +slingback +slingbacks +slingshot +slingshots +slingshot's +slink +slinking +slinks +slinky +slinkiest +slinkier +slip +slips +slip's +slipcase +slipcases +slipcase's +slipcover +slipcovers +slipcover's +slipknot +slipknots +slipknot's +slippage +slippages +slippage's +slipped +slipper +slippers +slipper's +slipperiness +slipperiness's +slippery +slipperiest +slipperier +slipperiness +slipping +slippy +slipshod +slipstream +slipstreams +slipstream's +slipway +slipways +slipway's +slit +slits +slit's +slither +slithering +slithered +slithers +slither's +slithery +slitter +slitting +sliver +slivering +slivered +slivers +sliver's +slob +slobs +slob's +slobbed +slobber +slobbering +slobbered +slobbers +slobber's +slobbery +slobbing +sloe +sloes +sloe's +slog +slogs +slog's +slogan +slogans +slogan's +sloganeering +slogged +slogging +sloop +sloops +sloop's +slop +sloping +sloped +slops +slop's +slope +slopes +slope's +slopped +sloppily +sloppiness +sloppiness's +slopping +sloppy +sloppiest +sloppier +sloppiness +slops +slops's +slosh +sloshing +sloshed +sloshes +slot +slots +slot's +sloth +sloth's +slothful +slothfully +slothfulness +slothfulness +slothfulness's +sloths +slotted +slotting +slouch +slouching +slouched +sloucher +slouchers +slouches +slouch's +sloucher +sloucher's +slouchy +slouchiest +slouchier +slough +sloughing +sloughed +slough's +sloughs +sloven +slovenly +slovens +sloven's +slovenliness +slovenliness's +slovenly +slovenliest +slovenlier +slovenliness +slow +slowly +slowing +slowed +slowest +slower +slows +slowness +slowcoach +slowcoaches +slowdown +slowdowns +slowdown's +slowness +slowness's +slowpoke +slowpokes +slowpoke's +sludge +sludge's +sludgy +sludgiest +sludgier +slue +sluing +slued +slues +slue's +slug +slugs +slug's +sluggard +sluggards +sluggard's +slugged +slugger +sluggers +slugger's +slugging +sluggish +sluggishly +sluggishness +sluggishness +sluggishness's +sluice +sluicing +sluiced +sluices +sluice's +slum +slums +slum's +slumber +slumbering +slumbered +slumbers +slumber's +slumberous +slumdog +slumdogs +slumdog's +slumlord +slumlords +slumlord's +slummed +slummer +slumming +slummy +slummiest +slummier +slump +slumping +slumped +slumps +slump's +slung +slunk +slur +slurs +slur's +slurp +slurping +slurped +slurps +slurp's +slurred +slurring +slurry +slurry's +slush +slush's +slushiness +slushiness's +slushy +slushiest +slushier +slushiness +slut +sluts +slut's +sluttish +slutty +sluttiest +sluttier +sly +slyly +sliest +slier +slyness +slyness's +smack +smacking +smacked +smacker +smackers +smacks +smack's +smacker +smacker's +small +smallest +smaller +smalls +smallness +small's +smallholder +smallholders +smallholding +smallholdings +smallish +smallness +smallness's +smallpox +smallpox's +smarmy +smarmiest +smarmier +smart +smarten +smartens +smartly +smarting +smarted +smartest +smarter +smarts +smartness +smart's +smarten +smartening +smartened +smartness +smartness's +smartphone +smartphones +smartphone's +smarts +smarts's +smartwatch +smartwatches +smartwatch's +smarty +smarties +smarty's +smartypants +smartypants's +smash +smashing +smashed +smasher +smashers +smashes +smash's +smasher +smasher's +smashup +smashups +smashup's +smattering +smatterings +smattering's +smear +smearing +smeared +smears +smear's +smeary +smeariest +smearier +smell +smelling +smelled +smells +smell's +smelliness +smelliness's +smelly +smelliest +smellier +smelliness +smelt +smelting +smelted +smelter +smelters +smelts +smelt's +smelter +smelter's +smidgen +smidgens +smidgen's +smilax +smilax's +smile +smiling +smiled +smiles +smile's +smiley +smileys +smiley's +smiling +smilingly +smirch +smirching +smirched +smirches +smirch's +smirk +smirking +smirked +smirks +smirk's +smite +smiting +smites +smith +smith's +smithereens +smithereens's +smiths +smithy +smithies +smithy's +smitten +smock +smocking +smocked +smocks +smock's +smocking +smocking's +smog +smogs +smog's +smoggy +smoggiest +smoggier +smoke +smoking +smoked +smoker +smokers +smokes +smoke's +smokehouse +smokehouses +smokehouse's +smokeless +smoker +smoker's +smokescreen +smokescreens +smokescreen's +smokestack +smokestacks +smokestack's +smokey +smokiness +smokiness's +smoking +smoking's +smoky +smokiest +smokier +smokiness +smolder +smoldering +smoldered +smolders +smolder's +smooch +smooching +smooched +smooches +smooch's +smoochy +smooth +smoothly +smoothing +smoothed +smoothest +smoother +smoothness +smoothie +smoothies +smoothie's +smoothness +smoothness's +smooths +smorgasbord +smorgasbords +smorgasbord's +smote +smother +smothering +smothered +smothers +smother's +smudge +smudging +smudged +smudges +smudge's +smudgy +smudgiest +smudgier +smug +smugly +smugness +smugger +smuggest +smuggle +smuggling +smuggled +smuggler +smugglers +smuggles +smuggler +smuggler's +smuggling +smuggling's +smugness +smugness's +smurf +smurfs +smut +smuts +smut's +smuttiness +smuttiness's +smutty +smuttiest +smuttier +smuttiness +snack +snacking +snacked +snacks +snack's +snaffle +snaffling +snaffled +snaffles +snaffle's +snafu +snafus +snafu's +snag +snags +snag's +snagged +snagging +snail +snailing +snailed +snails +snail's +snake +snaking +snaked +snakes +snake's +snakebite +snakebites +snakebite's +snakelike +snakeskin +snaky +snakiest +snakier +snap's +snap +snaps +unsnaps +unsnap +snapdragon +snapdragons +snapdragon's +snapped +unsnapped +snapper +snappers +snapper's +snappily +snappiness +snappiness's +snapping +unsnapping +snappish +snappishly +snappishness +snappishness +snappishness's +snappy +snappiest +snappier +snappiness +snapshot +snapshots +snapshot's +snare +snaring +snared +snares +snare's +snarf +snarfing +snarfed +snarfs +snark +snarks +snarky +snarkiest +snarkier +snarl's +snarl +snarling +snarled +snarls +unsnarling +unsnarled +unsnarls +unsnarl +snarling +snarlingly +snarly +snarliest +snarlier +snatch +snatching +snatched +snatcher +snatchers +snatches +snatch's +snatcher +snatcher's +snazzily +snazzy +snazziest +snazzier +sneak +sneaking +sneaked +sneaker +sneakers +sneaks +sneak's +sneaker +sneaker's +sneakily +sneakiness +sneakiness's +sneaking +sneakingly +sneaky +sneakiest +sneakier +sneakiness +sneer +sneering +sneerings +sneered +sneers +sneer's +sneering +sneeringly +sneeze +sneezing +sneezed +sneezes +sneeze's +snick +snicking +snicked +snicker +snickers +snicks +snicker +snickering +snickered +snicker's +snide +snidely +snidest +snider +sniff +sniffing +sniffed +sniffer +sniffers +sniffs +sniff's +sniffer +sniffer's +sniffle +sniffling +sniffled +sniffles +sniffle's +sniffy +sniffiest +sniffier +snifter +snifters +snifter's +snip +sniping +sniped +sniper +snipers +snips +snip's +snipe +snipes +snipe's +sniper +sniper's +snipped +snippet +snippets +snippet's +snipping +snippy +snippiest +snippier +snips +snips's +snit +snits +snit's +snitch +snitching +snitched +snitches +snitch's +snivel +sniveling +sniveled +sniveler +snivelers +snivels +snivel's +sniveler +sniveler's +snob +snobs +snob's +snobbery +snobbery's +snobbish +snobbishly +snobbishness +snobbishness +snobbishness's +snobby +snobbiest +snobbier +snog +snogs +snogged +snogging +snood +snoods +snood's +snooker +snookering +snookered +snookers +snooker's +snoop +snooping +snooped +snooper +snoopers +snoops +snoop's +snooper +snooper's +snoopy +snoopiest +snoopier +snoot +snoots +snoot's +snootily +snootiness +snootiness's +snooty +snootiest +snootier +snootiness +snooze +snoozing +snoozed +snoozes +snooze's +snore +snoring +snored +snorer +snorers +snores +snore's +snorer +snorer's +snorkel +snorkeling +snorkeled +snorkeler +snorkelers +snorkels +snorkel's +snorkeler +snorkeler's +snorkeling +snorkeling's +snort +snorting +snorted +snorter +snorters +snorts +snort's +snorter +snorter's +snot +snots +snot's +snottily +snottiness +snottiness's +snotty +snottiest +snottier +snottiness +snout +snouts +snout's +snow +snowing +snowed +snows +snow's +snowball +snowballing +snowballed +snowballs +snowball's +snowbank +snowbanks +snowbank's +snowbird +snowbirds +snowbird's +snowblower +snowblowers +snowblower's +snowboard +snowboarding +snowboarded +snowboarder +snowboarders +snowboards +snowboard's +snowboarder +snowboarder's +snowboarding +snowboarding's +snowbound +snowdrift +snowdrifts +snowdrift's +snowdrop +snowdrops +snowdrop's +snowfall +snowfalls +snowfall's +snowfield +snowfields +snowfield's +snowflake +snowflakes +snowflake's +snowiness +snowiness's +snowline +snowman +snowman's +snowmen +snowmobile +snowmobiling +snowmobiled +snowmobiles +snowmobile's +snowplow +snowplowing +snowplowed +snowplows +snowplow's +snowshed +snowshoe +snowshoes +snowshoe's +snowshoeing +snowstorm +snowstorms +snowstorm's +snowsuit +snowsuits +snowsuit's +snowy +snowiest +snowier +snowiness +snub +snubs +snub's +snubbed +snubbing +snuff +snuffly +snuffing +snuffed +snuffer +snuffers +snuffs +snuff's +snuffbox +snuffboxes +snuffbox's +snuffer +snuffer's +snuffle +snuffling +snuffled +snuffles +snuffle's +snug +snugly +snugs +snugness +snug's +snugged +snugger +snuggest +snugging +snuggle +snuggling +snuggled +snuggles +snuggle's +snugness +snugness's +so +soak +soaking +soakings +soaked +soaks +soak's +soaking +soaking's +soap +soaping +soaped +soaps +soap's +soapbox +soapboxes +soapbox's +soapiness +soapiness's +soapstone +soapstone's +soapsuds +soapsuds's +soapy +soapiest +soapier +soapiness +soar +soaring +soared +soars +soar's +sob +sobs +sob's +sobbed +sobbing +sobbingly +sober +soberly +sobering +sobered +soberest +soberer +sobers +soberness +soberness +soberness's +sobriety +sobriety's +insobriety's +insobriety +sobriquet +sobriquets +sobriquet's +soc +soccer +soccer's +sociability +sociability's +sociable +sociables +sociable's +sociably +social +socially +socials +social's +socialism +socialism's +socialist +socialists +socialist's +socialistic +socialite +socialites +socialite's +socialization +socialization's +socialize +socializing +socialized +socializes +societal +society +societies +society's +socioeconomic +socioeconomically +sociological +sociologically +sociologist +sociologists +sociologist's +sociology +sociology's +sociopath +sociopath's +sociopaths +sociopolitical +sock +socking +socked +socks +sock's +socket +sockets +socket's +sockeye +sockeyes +sockeye's +sod +sods +sod's +soda +sodas +soda's +sodded +sodden +soddenly +sodding +sodium +sodium's +sodomite +sodomites +sodomite's +sodomize +sodomizing +sodomized +sodomizes +sodomy +sodomy's +soever +sofa +sofas +sofa's +soft +soften +softens +softly +softest +softer +softness +softback +softball +softballs +softball's +softbound +softcover +soften +softening +softened +softener +softeners +softener +softener's +softhearted +softness +softness's +software +software's +softwood +softwoods +softwood's +softy +softies +softy's +soggily +sogginess +sogginess's +soggy +soggiest +soggier +sogginess +soigne +soignee +soil +soiling +soiled +soils +soil's +soiled +unsoiled +soiree +soirees +soiree's +sojourn +sojourning +sojourned +sojourner +sojourners +sojourns +sojourn's +sojourner +sojourner's +sol +sols +sol's +solace +solacing +solaced +solaces +solace's +solar +solaria +solarium +solarium's +sold +solder +soldering +soldered +solderer +solderers +solders +solder's +solderer +solderer's +soldier +soldierly +soldiering +soldiered +soldiers +soldier's +soldiery +soldiery's +sole +soling +soled +soles +sole's +consoling +consoled +consoles +console's +console +solecism +solecisms +solecism's +solely +solemn +solemnly +solemnest +solemner +solemnness +solemness +solemness's +solemnify +solemnifying +solemnified +solemnifies +solemnity +solemnities +solemnity's +solemnization +solemnization's +solemnize +solemnizing +solemnized +solemnizes +solemnness +solemnness's +solenoid +solenoids +solenoid's +solicit +soliciting +solicited +solicits +solicitation +solicitations +solicitation's +solicited +unsolicited +solicitor +solicitors +solicitor's +solicitous +solicitously +solicitousness +solicitousness +solicitousness's +solicitude +solicitude's +solid +solidly +solidest +solider +solids +solidness +solid's +solidarity +solidarity's +solidi +solidification +solidification's +solidify +solidification +solidifying +solidified +solidifies +solidity +solidity's +solidness +solidness's +solidus +solidus's +soliloquies +soliloquize +soliloquizing +soliloquized +soliloquizes +soliloquy +soliloquy's +solipsism +solipsism's +solipsistic +solitaire +solitaires +solitaire's +solitariness +solitariness's +solitary +solitaries +solitariness +solitary's +solitude +solitude's +solo +soloing +soloed +solos +solo's +soloist +soloists +soloist's +solstice +solstices +solstice's +solubility +solubility's +insolubility's +insolubility +soluble +solubles +soluble's +solute's +solute +solution +solutions +resolution +resolutions +resolute +solutes +solution's +resolution's +dissolution's +solvable +insolvable +unsolvable +solve +solving +solved +solves +resolving +dissolving +resolved +dissolved +resolves +dissolves +resolve +dissolve +solved +unsolved +solvency +solvency's +insolvency's +insolvency +solvent +solvents +solvent's +insolvents +insolvent's +insolvent +solver +solvers +solver's +somatic +somatosensory +somber +somberly +somberness +somberness +somberness's +sombrero +sombreros +sombrero's +some +somebody +somebodies +somebody's +someday +somehow +someone +someones +someone's +someplace +somersault +somersaulting +somersaulted +somersaults +somersault's +somerset +somersets +somerset's +somersetted +somersetting +something +somethings +something's +sometime +sometimes +someway +someways +somewhat +somewhats +somewhere +somnambulism +somnambulism's +somnambulist +somnambulists +somnambulist's +somnolence +somnolence's +somnolent +son +sons +son's +sonar +sonars +sonar's +sonata +sonatas +sonata's +sonatina +sonatinas +sonatina's +song +songs +song's +songbird +songbirds +songbird's +songbook +songbooks +songbook's +songfest +songfests +songfest's +songster +songsters +songster's +songstress +songstresses +songstress's +songwriter +songwriters +songwriter's +songwriting +sonic +sonnet +sonnets +sonnet's +sonny +sonnies +sonny's +sonogram +sonograms +sonogram's +sonority +sonority's +sonorous +sonorously +sonorousness +sonorousness +sonorousness's +sonsofbitches +soon +soonest +sooner +soot +soot's +sooth +soothing +soothed +soother +soothers +soothes +sooth's +soothe +soother +soother's +soothing +soothingly +soothsayer +soothsayers +soothsayer's +soothsaying +soothsaying's +sooty +sootiest +sootier +sop +sops +sop's +soph +sophism +sophism's +sophist +sophists +sophist's +sophistic +sophistical +sophisticate +sophistication +sophisticating +sophisticated +sophisticates +sophisticate's +sophisticated +unsophisticated +sophistication +sophistication's +sophistry +sophistries +sophistry's +sophomore +sophomores +sophomore's +sophomoric +soporific +soporifics +soporific's +soporifically +sopped +sopping +soppy +soppiest +soppier +soprano +sopranos +soprano's +sorbet +sorbets +sorbet's +sorcerer +sorcerers +sorcerer's +sorceress +sorceresses +sorceress's +sorcery +sorcery's +sordid +sordidly +sordidness +sordidness +sordidness's +sore +sorely +sorest +sorer +sores +soreness +sore's +sorehead +soreheads +sorehead's +soreness +soreness's +sorghum +sorghum's +sorority +sororities +sorority's +sorrel +sorrels +sorrel's +sorrily +sorriness +sorriness's +sorrow +sorrowing +sorrowed +sorrows +sorrow's +sorrowful +sorrowfully +sorrowfulness +sorrowfulness +sorrowfulness's +sorry +sorriest +sorrier +sorriness +sort +sorting +sorted +sorts +sort's +resorting +consorting +resorted +consorted +resorts +consorts +resort's +consort's +resort +consort +sorta +sorted +unsorted +sorter +sorters +sorter's +sortie +sortied +sorties +sortie's +sortieing +sot +sots +sot's +sottish +sou'wester +sou +south +sous +sou's +souffle +souffles +souffle's +sough +soughing +soughed +sough's +soughs +sought +unsought +souk +souks +soul +souls +soul's +soulful +soulfully +soulfulness +soulfulness +soulfulness's +soulless +soullessly +soullessness +soulmate +soulmates +soulmate's +sound +soundly +sounding +soundings +sounded +soundest +sounder +sounders +sounds +soundness +sound's +soundalike +soundalikes +soundbar +soundbars +soundbite +soundbites +soundboard +soundboards +soundboard's +soundcheck +soundchecks +sounder +sounder's +sounding +sounding's +soundless +soundlessly +soundness +soundness's +unsoundness's +unsoundness +soundproof +soundproofing +soundproofed +soundproofs +soundproofing +soundproofing's +soundscape +soundscapes +soundtrack +soundtracks +soundtrack's +soup +souping +souped +soups +soup's +soupcon +soupcons +soupcon's +soupy +soupiest +soupier +sour +sourly +souring +soured +sourest +sourer +sours +sourness +sour's +source +sourcing +sourced +sources +source's +resourcing +resourced +resources +resource's +resource +sourdough +sourdough's +sourdoughs +sourish +sourness +sourness's +sourpuss +sourpusses +sourpuss's +sousaphone +sousaphones +sousaphone's +souse +sousing +soused +souses +souse's +south +south's +southbound +southeast +southeaster +southeasters +southeast's +southeaster +southeasterly +southeaster's +southeastern +southeastward +southeastwards +southerly +southerlies +southerly's +southern +southerner +southerners +southerns +southern's +southerner +southerner's +southernmost +southpaw +southpaws +southpaw's +southward +southwards +southward's +southwest +southwester +southwesters +southwest's +southwester +southwesterly +southwester's +southwestern +southwestward +southwestwards +souvenir +souvenirs +souvenir's +sovereign +sovereigns +sovereign's +sovereignty +sovereignty's +soviet +soviets +soviet's +sow's +sow +sowing +sowed +sows +resowing +resowed +resows +resow +sower +sowers +sower's +sown +resown +soy +soy's +soybean +soybeans +soybean's +sozzled +spa +spas +spa's +space +spacing +spaced +spacer +spacers +spaces +space's +spacecraft +spacecrafts +spacecraft's +spaceflight +spaceflights +spaceflight's +spaceman +spaceman's +spacemen +spaceport +spaceports +spaceport's +spacer +spacer's +spaceship +spaceships +spaceship's +spacesuit +spacesuits +spacesuit's +spacewalk +spacewalking +spacewalked +spacewalks +spacewalk's +spacewoman +spacewoman's +spacewomen +spacey +spacial +spacier +spaciest +spaciness +spaciness's +spacing +spacing's +spacious +spaciously +spaciousness +spaciousness +spaciousness's +spade +spading +spaded +spades +spade's +spadeful +spadefuls +spadeful's +spadework +spadework's +spadices +spadix +spadix's +spaghetti +spaghetti's +spake +spam +spams +spam's +spammed +spammer +spammers +spammer's +spamming +span +spans +span's +spandex +spandex's +spangle +spangling +spangled +spangles +spangle's +spangly +spaniel +spaniels +spaniel's +spank +spanking +spankings +spanked +spanks +spank's +spanking +spanking's +spanned +spanner +spanners +spanner's +spanning +spar +spars +spar's +spare +sparely +sparing +spared +sparest +sparer +spares +spareness +spare's +spareness +spareness's +spareribs +spareribs's +sparing +sparingly +unsparingly +unsparing +spark +sparkly +sparking +sparked +sparks +spark's +sparkle +sparkling +sparkled +sparkler +sparklers +sparkles +sparkle's +sparkler +sparkler's +sparky +sparkiest +sparkier +sparred +sparring +sparrow +sparrows +sparrow's +sparrowhawk +sparrowhawks +sparse +sparsely +sparsest +sparser +sparseness +sparseness +sparseness's +sparsity +sparsity's +spartan +spasm +spasms +spasm's +spasmodic +spasmodically +spastic +spastics +spastic's +spat +spats +spat's +spate +spates +spate's +spathe +spathes +spathe's +spatial +spatially +spatted +spatter +spattering +spattered +spatters +spatter's +spatting +spatula +spatulas +spatula's +spavin +spavined +spavin's +spawn +spawning +spawned +spawns +spawn's +spay +spaying +spayed +spays +speak +speaking +speakings +speaker +speakers +speaks +speakeasy +speakeasies +speakeasy's +speaker +speaker's +speakerphone +speakerphones +spear +spearing +speared +spears +spear's +spearfish +spearfishing +spearfished +spearfishes +spearfish's +speargun +spearhead +spearheading +spearheaded +spearheads +spearhead's +spearmint +spearmint's +spec +specs +spec's +special +specially +specials +special's +specialism +specialisms +specialist +specialists +specialist's +specialization +specializations +specialization's +specialize +specializing +specialized +specializes +specialty +specialties +specialty's +specie +species +specie's +species +species's +specif +specifiable +specific +specifics +specific's +specifically +specification +specification's +specificity +specificity's +specified +unspecified +specify +specification +specifications +specifying +specified +specifier +specifiers +specifies +specimen +specimens +specimen's +specious +speciously +speciousness +speciousness +speciousness's +speck +specking +specked +specks +speck's +speckle +speckling +speckled +speckles +speckle's +specs +specs's +spectacle +spectacles +spectacle's +spectacles +spectacles's +spectacular +spectacularly +spectaculars +spectacular's +spectate +spectating +spectated +spectates +spectator +spectators +spectator's +specter +specters +specter's +respecters +respecter's +respecter +spectra +spectral +spectrometer +spectrometers +spectrometer's +spectroscope +spectroscopes +spectroscope's +spectroscopic +spectroscopy +spectroscopy's +spectrum +spectrum's +speculate +speculative +speculation +speculations +speculating +speculated +speculates +speculation +speculation's +speculative +speculatively +speculator +speculators +speculator's +sped +speech +speeches +speech's +speechify +speechifying +speechified +speechifies +speechless +speechlessly +speechlessness +speechlessness +speechlessness's +speechwriter +speechwriters +speed +speeding +speeder +speeders +speeds +speed's +speedboat +speedboats +speedboat's +speeder +speeder's +speedily +speediness +speediness's +speeding +speeding's +speedometer +speedometers +speedometer's +speedster +speedsters +speedster's +speedup +speedups +speedup's +speedway +speedways +speedway's +speedwell +speedwell's +speedy +speediest +speedier +speediness +speleological +speleologist +speleologists +speleologist's +speleology +speleology's +spell +spelling +spellings +spelled +speller +spellers +spells +spell's +spellbind +spellbinding +spellbinder +spellbinders +spellbinds +spellbinder +spellbinder's +spellbound +spellcheck +spellchecking +spellchecked +spellchecker +spellcheckers +spellchecks +spellcheck's +spellchecker +spellchecker's +spelldown +spelldowns +spelldown's +speller +speller's +spelling +spelling's +spelunker +spelunkers +spelunker's +spelunking +spelunking's +spend +spending +spender +spenders +spends +spendable +spender +spender's +spending +spending's +spendthrift +spendthrifts +spendthrift's +spent +unspent +sperm +sperms +sperm's +spermatozoa +spermatozoon +spermatozoon's +spermicidal +spermicide +spermicides +spermicide's +spew +spewing +spewed +spewer +spewers +spews +spew's +spewer +spewer's +sphagnum +sphagnums +sphagnum's +sphere +spheres +sphere's +spherical +spherically +spheroid +spheroids +spheroid's +spheroidal +sphincter +sphincters +sphincter's +sphinx +sphinxes +sphinx's +spic +spics +spice +spicing +spiced +spices +spice's +spicily +spiciness +spiciness's +spicule +spicules +spicule's +spicy +spiciest +spicier +spiciness +spider +spiders +spider's +spiderweb +spiderwebs +spiderweb's +spidery +spiel +spieling +spieled +spiels +spiel's +spiff +spiffing +spiffed +spiffs +spiffy +spiffiest +spiffier +spigot +spigots +spigot's +spike +spiking +spiked +spikes +spike's +spikiness +spikiness's +spiky +spikiest +spikier +spikiness +spill +spilling +spilled +spills +spill's +spillage +spillages +spillage's +spillover +spillovers +spillover's +spillway +spillways +spillway's +spin +spins +spin's +spinach +spinach's +spinal +spinally +spinals +spinal's +spindle +spindling +spindled +spindles +spindle's +spindly +spindliest +spindlier +spine +spines +spine's +spineless +spinelessly +spinelessness +spinet +spinets +spinet's +spinless +spinnaker +spinnakers +spinnaker's +spinner +spinners +spinner's +spinneret +spinnerets +spinneret's +spinney +spinneys +spinning +spinning's +spinster +spinsters +spinster's +spinsterhood +spinsterhood's +spinsterish +spiny +spiniest +spinier +spiracle +spiracles +spiracle's +spiral +spirally +spiraling +spiraled +spirals +spiral's +spire's +spire +spires +respires +inspires +conspires +respire +inspire +conspire +spirea +spireas +spirea's +spirit's +spirit +spiriting +spirited +spirits +inspiriting +inspirited +inspirits +inspirit +spirited +spiritedly +spiritless +spiritual +spiritually +spirituals +spiritual's +spiritualism +spiritualism's +spiritualist +spiritualists +spiritualist's +spiritualistic +spirituality +spirituality's +spirituous +spirochete +spirochetes +spirochete's +spiry +spit +spiting +spited +spits +spit's +spitball +spitballs +spitball's +spite +spites +spite's +respites +respite's +respite +spiteful +spitefully +spitefulness +spitefuller +spitefullest +spitefulness +spitefulness's +spitfire +spitfires +spitfire's +spitted +spitting +spittle +spittle's +spittoon +spittoons +spittoon's +spiv +spivs +splash +splashing +splashed +splashes +splash's +splashdown +splashdowns +splashdown's +splashily +splashiness +splashiness's +splashy +splashiest +splashier +splashiness +splat +splats +splat's +splatted +splatter +splattering +splattered +splatters +splatter's +splatting +splay +splaying +splayed +splays +splay's +splayfeet +splayfoot +splayfooted +splayfoot's +spleen +spleens +spleen's +splendid +splendidly +splendidest +splendider +splendor +splendors +splendor's +splendorous +splenectomy +splenetic +splice +splicing +spliced +splicer +splicers +splices +splice's +splicer +splicer's +spliff +spliffs +spline +splines +splint +splinting +splinted +splinter +splinters +splints +splint's +splinter +splintering +splintered +splinter's +splintery +split +splits +split's +splitting +splittings +splitting's +splodge +splodges +splosh +sploshing +sploshed +sploshes +splotch +splotching +splotched +splotches +splotch's +splotchy +splotchiest +splotchier +splurge +splurging +splurged +splurges +splurge's +splutter +spluttering +spluttered +splutters +splutter's +spoil's +spoil +spoiling +spoiled +spoiler +spoilers +spoils +despoiling +despoiled +despoiler +despoilers +despoils +despoil +spoilage +spoilage's +spoiled +unspoiled +spoiler +spoiler's +despoiler's +despoiler +spoilsport +spoilsports +spoilsport's +spoke +spokes +spoke's +spoken +unspoken +spokesman +spokesman's +spokesmen +spokespeople +spokesperson +spokespersons +spokesperson's +spokeswoman +spokeswoman's +spokeswomen +spoliation +spoliation's +despoliation's +despoliation +sponge +sponging +sponged +sponger +spongers +sponges +sponge's +spongecake +spongecake's +sponger +sponger's +sponginess +sponginess's +spongy +spongiest +spongier +sponginess +sponsor +sponsoring +sponsored +sponsors +sponsor's +sponsorship +sponsorship's +spontaneity +spontaneity's +spontaneous +spontaneously +spoof +spoofing +spoofed +spoofs +spoof's +spook +spooking +spooked +spooks +spook's +spookiness +spookiness's +spooky +spookiest +spookier +spookiness +spool +spooling +spooled +spools +spool's +spoon +spooning +spooned +spoons +spoon's +spoonbill +spoonbills +spoonbill's +spoonerism +spoonerisms +spoonerism's +spoonful +spoonfuls +spoonful's +spoor +spooring +spoored +spoors +spoor's +sporadic +sporadically +spore +sporing +spored +spores +spore's +sporran +sporrans +sport +sportive +sporting +sported +sports +sport's +sportiness +sportiness's +sporting +sportingly +sportive +sportively +sportscast +sportscasting +sportscaster +sportscasters +sportscasts +sportscast's +sportscaster +sportscaster's +sportsman +sportsman's +sportsmanlike +unsportsmanlike +sportsmanship +sportsmanship's +sportsmen +sportspeople +sportsperson +sportswear +sportswear's +sportswoman +sportswoman's +sportswomen +sportswriter +sportswriters +sportswriter's +sporty +sportiest +sportier +sportiness +spot +spots +spot's +despots +despot's +despot +spotless +spotlessly +spotlessness +spotlessness +spotlessness's +spotlight +spotlighting +spotlighted +spotlights +spotlight's +spotlit +spotted +spotter +spotters +spotter's +spottily +spottiness +spottiness's +spotting +spotty +spottiest +spottier +spottiness +spousal +spousals +spousal's +spouse +spouses +spouse's +spout +spouting +spouted +spouts +spout's +sprain +spraining +sprained +sprains +sprain's +sprang +sprat +sprats +sprat's +sprawl +sprawling +sprawled +sprawls +sprawl's +spray's +spray +spraying +sprayed +sprays +respraying +resprayed +resprays +respray +sprayer +sprayers +sprayer's +spread +spreading +spreader +spreaders +spreads +spread's +spreadable +spreadeagled +spreader +spreader's +spreadsheet +spreadsheets +spreadsheet's +spree +spreed +sprees +spree's +spreeing +sprig +sprigs +sprig's +sprigged +sprightliness +sprightliness's +sprightly +sprightliest +sprightlier +sprightliness +spring +springing +springs +spring's +springboard +springboards +springboard's +springbok +springboks +springbok's +springily +springiness +springiness's +springlike +springtime +springtime's +springy +springiest +springier +springiness +sprinkle +sprinkling +sprinklings +sprinkled +sprinkler +sprinklers +sprinkles +sprinkle's +sprinkler +sprinkler's +sprinkling +sprinkling's +sprint +sprinting +sprinted +sprinter +sprinters +sprints +sprint's +sprinter +sprinter's +sprite +sprites +sprite's +spritz +spritzing +spritzed +spritzer +spritzers +spritzes +spritz's +spritzer +spritzer's +sprocket +sprockets +sprocket's +sprog +sprogs +sprout +sprouting +sprouted +sprouts +sprout's +spruce +sprucely +sprucing +spruced +sprucest +sprucer +spruces +spruceness +spruce's +spruceness +spruceness's +sprung +spry +spryly +spriest +sprier +spryness +spryness's +spud +spuds +spud's +spume +spuming +spumed +spumes +spume's +spumoni +spumoni's +spumy +spun +spunk +spunks +spunk's +spunky +spunkiest +spunkier +spur +spurs +spur's +spurge +spurge's +spurious +spuriously +spuriousness +spuriousness +spuriousness's +spurn +spurning +spurned +spurns +spurred +spurring +spurt +spurting +spurted +spurts +spurt's +sputa +sputnik +sputniks +sputnik's +sputter +sputtering +sputtered +sputters +sputter's +sputum +sputum's +spy +spying +spied +spies +spy's +spyglass +spyglasses +spyglass's +spymaster +spymasters +spyware +spyware's +sq +sqq +squab +squabs +squab's +squabble +squabbling +squabbled +squabbler +squabblers +squabbles +squabble's +squabbler +squabbler's +squad +squads +squad's +squadron +squadrons +squadron's +squalid +squalidly +squalidest +squalider +squalidness +squalidness +squalidness's +squall +squalling +squalled +squalls +squall's +squally +squalor +squalor's +squamous +squander +squandering +squandered +squanders +square +squarely +squaring +squared +squarest +squarer +squares +squareness +square's +squareness +squareness's +squarish +squash +squashing +squashed +squashes +squash's +squashy +squashiest +squashier +squat +squats +squatness +squat's +squatness +squatness's +squatted +squatter +squatters +squatter's +squattest +squatting +squaw +squaws +squaw's +squawk +squawking +squawked +squawker +squawkers +squawks +squawk's +squawker +squawker's +squeak +squeaking +squeaked +squeaker +squeakers +squeaks +squeak's +squeaker +squeaker's +squeakily +squeakiness +squeakiness's +squeaky +squeakiest +squeakier +squeakiness +squeal +squealing +squealed +squealer +squealers +squeals +squeal's +squealer +squealer's +squeamish +squeamishly +squeamishness +squeamishness +squeamishness's +squeegee +squeegeed +squeegees +squeegee's +squeegeeing +squeeze +squeezing +squeezed +squeezer +squeezers +squeezes +squeeze's +squeezable +squeezebox +squeezeboxes +squeezer +squeezer's +squelch +squelching +squelched +squelches +squelch's +squelchy +squib +squibs +squib's +squid +squids +squid's +squidgy +squiffy +squiggle +squiggling +squiggled +squiggles +squiggle's +squiggly +squint +squinting +squinted +squintest +squinter +squints +squint's +squire +squiring +squired +squires +squire's +squirm +squirming +squirmed +squirms +squirm's +squirmy +squirmiest +squirmier +squirrel +squirreling +squirreled +squirrels +squirrel's +squirt +squirting +squirted +squirts +squirt's +squish +squishing +squished +squishes +squish's +squishy +squishiest +squishier +sriracha +ssh +st +stab +stably +stabs +stab's +stabbed +stabber +stabbers +stabber's +stabbing +stabbings +stabbing's +stability +stability's +instability's +instability +stabilization +stabilization's +destabilization's +destabilization +stabilize +stabilizing +stabilized +stabilizes +destabilizing +destabilized +destabilizes +destabilize +stabilizer +stabilizers +stabilizer's +stable +stabling +stabled +stablest +stabler +stables +stable's +stableman +stableman's +stablemate +stablemates +stablemen +stably +unstably +staccato +staccatos +staccato's +stack +stacking +stacked +stacks +stack's +stadium +stadiums +stadium's +staff's +staff +staffing +staffed +staffs +restaffing +restaffed +restaffs +restaff +staffer +staffers +staffer's +staffing +staffing's +stag +staging +stagings +staged +stags +stag's +stage +stages +stage's +stagecoach +stagecoaches +stagecoach's +stagecraft +stagecraft's +stagehand +stagehands +stagehand's +stagestruck +stagflation +stagflation's +stagger +staggering +staggered +staggers +stagger's +staggering +staggeringly +staging +staging's +stagnancy +stagnancy's +stagnant +stagnantly +stagnate +stagnation +stagnating +stagnated +stagnates +stagnation +stagnation's +stagy +stagiest +stagier +staid +staidly +staidest +staider +staidness +staidness +staidness's +stain +staining +stained +stains +stain's +stained +unstained +stainless +stainless's +stair +stairs +stair's +staircase +staircases +staircase's +stairway +stairways +stairway's +stairwell +stairwells +stairwell's +stake +staking +staked +stakes +stake's +stakeholder +stakeholders +stakeholder's +stakeout +stakeouts +stakeout's +stalactite +stalactites +stalactite's +stalagmite +stalagmites +stalagmite's +stale +staling +staled +stalest +staler +stales +staleness +stalemate +stalemating +stalemated +stalemates +stalemate's +staleness +staleness's +stalk +stalking +stalkings +stalked +stalker +stalkers +stalks +stalk's +stalker +stalker's +stalking +stalking's +stall's +stall +stalling +stalled +stalls +installing +installed +installs +install +stallholder +stallholders +stallion +stallions +stallion's +stalwart +stalwartly +stalwarts +stalwart's +stamen +stamens +stamen's +stamina +stamina's +stammer +stammering +stammered +stammerer +stammerers +stammers +stammer's +stammerer +stammerer's +stammering +stammeringly +stamp +stamping +stamped +stamper +stampers +stamps +stamp's +stampede +stampeding +stampeded +stampedes +stampede's +stamper +stamper's +stance +stances +stance's +instances +instance's +instance +stanch +stanching +stanched +stanchest +stancher +stanches +stanchion +stanchions +stanchion's +stand +standing +standings +stander +standers +stands +stand's +standalone +standard +standards +standard's +standardization +standardization's +standardize +standardizing +standardized +standardizes +standby +standby's +standbys +standee +standees +standee's +stander +stander's +standing +standing's +standoff +standoffs +standoff's +standoffish +standout +standouts +standout's +standpipe +standpipes +standpipe's +standpoint +standpoints +standpoint's +standstill +standstills +standstill's +stank +stanza +stanzas +stanza's +staph +staph's +staphylococcal +staphylococci +staphylococcus +staphylococcus's +staple +stapling +stapled +stapler +staplers +staples +staple's +stapler +stapler's +star +staring +stared +starer +starers +stars +star's +starboard +starboard's +starburst +starbursts +starch +starching +starched +starches +starch's +starchily +starchiness +starchiness's +starchy +starchiest +starchier +starchiness +stardom +stardom's +stardust +stardust's +stare +stares +stare's +starer +starer's +starfish +starfishes +starfish's +starfruit +stargaze +stargazing +stargazed +stargazer +stargazers +stargazes +stargazer +stargazer's +stark +starkly +starkest +starker +starkers +starkness +starkness +starkness's +starless +starlet +starlets +starlet's +starlight +starlight's +starling +starlings +starling's +starlit +starred +starring +starry +starriest +starrier +starstruck +start +starting +started +starts +start's +restarting +restarted +restarts +restart's +restart +starter +starters +starter's +startle +startling +startled +startles +startling +startlingly +startup +startups +startup's +starvation +starvation's +starve +starving +starvings +starved +starves +starveling +starvelings +starveling's +stash +stashing +stashed +stashes +stash's +stasis +stat +stats +stat's +state +station +stations +stately +stating +stated +stater +states +state's +statement +statecraft +statecraft's +stated +unstated +statehood +statehood's +statehouse +statehouses +statehouse's +stateless +statelessness +statelessness +statelessness's +stateliness +stateliness's +stately +stateliest +statelier +stateliness +statement +statements +statement's +restatements +restatement's +restatement +statemented +statementing +stateroom +staterooms +stateroom's +stateside +statesman +statesman's +statesmanlike +statesmanship +statesmanship's +statesmen +stateswoman +stateswoman's +stateswomen +statewide +static +statics +static's +statically +station +stationing +stationed +stationer +stationers +station's +stationary +stationer +stationer's +stationery +stationery's +stationmaster +stationmasters +statistic +statistics +statistic's +statistical +statistically +statistician +statisticians +statistician's +statuary +statuary's +statue +statues +statue's +statuesque +statuette +statuettes +statuette's +stature +statures +stature's +status +statuses +status's +statute +statutes +statute's +statutorily +statutory +staunch +staunchly +staunching +staunched +staunchest +stauncher +staunches +staunchness +staunchness +staunchness's +stave +staving +staved +staves +stave's +stay +staying +stayed +stayer +stayers +stays +stay's +std +stdio +stead +steads +stead's +steadfast +steadfastly +steadfastness +steadfastness +steadfastness's +steadily +unsteadily +steadiness +steadiness's +unsteadiness's +unsteadiness +steady +steadying +steadied +steadiest +steadier +steadies +steadiness +steady's +steak +steaks +steak's +steakhouse +steakhouses +steakhouse's +steal +stealth +stealing +steals +steal's +stealth +stealth's +stealthily +stealthiness +stealthiness's +stealthy +stealthiest +stealthier +stealthiness +steam +steaming +steamed +steamer +steamers +steams +steam's +steamboat +steamboats +steamboat's +steamer +steamer's +steamfitter +steamfitters +steamfitter's +steamfitting +steamfitting's +steaminess +steaminess's +steampunk +steamroll +steamrolling +steamrolled +steamroller +steamrollers +steamrolls +steamroller +steamrollering +steamrollered +steamroller's +steamship +steamships +steamship's +steamy +steamiest +steamier +steaminess +steed +steeds +steed's +steel +steeling +steeled +steels +steel's +steeliness +steeliness's +steelmaker +steelmakers +steelworker +steelworkers +steelworker's +steelworks +steelworks's +steely +steeliest +steelier +steeliness +steelyard +steelyards +steelyard's +steep +steepen +steepens +steeply +steeping +steeped +steepest +steeper +steeps +steepness +steep's +steepen +steepening +steepened +steeple +steeples +steeple's +steeplechase +steeplechases +steeplechase's +steeplejack +steeplejacks +steeplejack's +steepness +steepness's +steer +steering +steered +steers +steer's +steerable +steerage +steerage's +steering +steering's +steersman +steersman's +steersmen +stegosauri +stegosaurus +stegosauruses +stegosaurus's +stein +steins +stein's +stellar +stem +stems +stem's +stemless +stemmed +stemming +stemware +stemware's +stench +stenches +stench's +stencil +stenciling +stenciled +stencils +stencil's +steno +stenos +steno's +stenographer +stenographers +stenographer's +stenographic +stenography +stenography's +stenosis +stent +stents +stent's +stentorian +step +steps +step's +insteps +instep's +instep +stepbrother +stepbrothers +stepbrother's +stepchild +stepchild's +stepchildren +stepchildren's +stepdad +stepdads +stepdad's +stepdaughter +stepdaughters +stepdaughter's +stepfather +stepfathers +stepfather's +stepladder +stepladders +stepladder's +stepmom +stepmoms +stepmom's +stepmother +stepmothers +stepmother's +stepparent +stepparents +stepparent's +steppe +stepping +stepped +stepper +steppers +steppes +steppe's +stepper +stepper's +steppingstone +steppingstones +steppingstone's +stepsister +stepsisters +stepsister's +stepson +stepsons +stepson's +stereo +stereos +stereo's +stereophonic +stereoscope +stereoscopes +stereoscope's +stereoscopic +stereotype +stereotyping +stereotyped +stereotypes +stereotype's +stereotypical +sterile +sterility +sterility's +sterilization +sterilizations +sterilization's +sterilize +sterilizing +sterilized +sterilizer +sterilizers +sterilizes +sterilizer +sterilizer's +sterling +sterling's +stern +sternly +sternest +sterner +sterns +sternness +stern's +sternness +sternness's +sternum +sternums +sternum's +steroid +steroids +steroid's +steroidal +stertorous +stet +stets +stethoscope +stethoscopes +stethoscope's +stetson +stetsons +stetson's +stetted +stetting +stevedore +stevedores +stevedore's +stew +stewing +stewed +stews +stew's +steward +stewarding +stewarded +stewards +steward's +stewardess +stewardesses +stewardess's +stewardship +stewardship's +stick +sticking +sticker +stickers +sticks +stick's +sticker +sticker's +stickily +stickiness +stickiness's +stickleback +sticklebacks +stickleback's +stickler +sticklers +stickler's +stickpin +stickpins +stickpin's +stickup +stickups +stickup's +sticky +stickiest +stickier +stickies +stickiness +sticky's +stiff +stiffen +stiffens +stiffly +stiffing +stiffed +stiffest +stiffer +stiffs +stiffness +stiff's +stiffen +stiffening +stiffened +stiffener +stiffeners +stiffener +stiffener's +stiffening +stiffening's +stiffness +stiffness's +stifle +stifling +stiflings +stifled +stifles +stifling +stiflingly +stigma +stigmas +stigma's +stigmata +stigmatic +stigmatization +stigmatization's +stigmatize +stigmatizing +stigmatized +stigmatizes +stile +stiles +stile's +stiletto +stilettos +stiletto's +still's +still +stilling +stilled +stillest +stills +instilling +instilled +instills +instill +stillbirth +stillbirth's +stillbirths +stillborn +stiller +stillness +stillness's +stilt +stilted +stilts +stilt's +stilted +stiltedly +stimulant +stimulants +stimulant's +stimulate +stimulative +stimulation +stimulating +stimulated +stimulates +stimulation +stimulation's +stimuli +stimulus +stimulus's +sting +stinging +stinger +stingers +stings +sting's +stinger +stinger's +stingily +stinginess +stinginess's +stingray +stingrays +stingray's +stingy +stingiest +stingier +stinginess +stink +stinking +stinker +stinkers +stinks +stink's +stinkbug +stinkbugs +stinkbug's +stinker +stinker's +stinky +stinkiest +stinkier +stint +stinting +stinted +stints +stint's +stipend +stipends +stipend's +stipendiary +stipendiaries +stipple +stippling +stippled +stipples +stipple's +stippling +stippling's +stipulate +stipulation +stipulations +stipulating +stipulated +stipulates +stipulation +stipulation's +stir +stirs +stir's +stirred +stirrer +stirrers +stirrer's +stirring +stirringly +stirrings +stirrup +stirrups +stirrup's +stitch's +stitch +stitching +stitched +stitches +restitching +restitched +restitches +restitch +stitchery +stitchery's +stitching +stitching's +stoat +stoats +stoat's +stochastic +stock's +stock +stocking +stocked +stocks +restocking +restocked +restocks +restock +stockade +stockading +stockaded +stockades +stockade's +stockbreeder +stockbreeders +stockbreeder's +stockbroker +stockbrokers +stockbroker's +stockbroking +stockbroking's +stockholder +stockholders +stockholder's +stockily +stockiness +stockiness's +stockinette +stockinette's +stocking +stockings +stocking's +stockist +stockists +stockpile +stockpiling +stockpiled +stockpiles +stockpile's +stockpot +stockpots +stockpot's +stockroom +stockrooms +stockroom's +stocktaking +stocktaking's +stocky +stockiest +stockier +stockiness +stockyard +stockyards +stockyard's +stodge +stodgily +stodginess +stodginess's +stodgy +stodgiest +stodgier +stodginess +stogie +stogies +stogie's +stoic +stoics +stoic's +stoical +stoically +stoicism +stoicism's +stoke +stoking +stoked +stoker +stokers +stokes +stoker +stoker's +stole +stoles +stole's +stolen +stolid +stolidly +stolidest +stolider +stolidness +stolidity +stolidity's +stolidness +stolidness's +stolon +stolons +stolon's +stomach +stomaching +stomached +stomacher +stomachers +stomach's +stomachache +stomachaches +stomachache's +stomacher +stomacher's +stomachs +stomp +stomping +stomped +stomps +stomp's +stone +stoning +stoned +stoner +stoners +stones +stone's +stonemason +stonemasons +stonemason's +stoner +stoner's +stonewall +stonewalling +stonewalled +stonewalls +stoneware +stoneware's +stonewashed +stonework +stonework's +stonily +stoniness +stoniness's +stonkered +stonking +stony +stoniest +stonier +stoniness +stood +stooge +stooges +stooge's +stool +stools +stool's +stoop +stooping +stooped +stoops +stoop's +stop's +stop +stops +unstops +unstop +stopcock +stopcocks +stopcock's +stopgap +stopgaps +stopgap's +stoplight +stoplights +stoplight's +stopover +stopovers +stopover's +stoppable +unstoppable +stoppage +stoppages +stoppage's +stopped +unstopped +stopper +stoppering +stoppered +stoppers +stopper's +stopping +unstopping +stopple +stoppling +stoppled +stopples +stopple's +stopwatch +stopwatches +stopwatch's +storage +storage's +store's +store +storing +stored +stores +restoring +restored +restores +restore +storefront +storefronts +storefront's +storehouse +storehouses +storehouse's +storekeeper +storekeepers +storekeeper's +storeroom +storerooms +storeroom's +stork +storks +stork's +storm +storming +stormed +storms +storm's +stormily +storminess +storminess's +stormy +stormiest +stormier +storminess +story +storied +stories +story's +storyboard +storyboards +storyboard's +storybook +storybooks +storybook's +storyteller +storytellers +storyteller's +storytelling +storytelling's +stoup +stoups +stoup's +stout +stoutly +stoutest +stouter +stouts +stoutness +stout's +stouthearted +stoutness +stoutness's +stove +stoves +stove's +stovepipe +stovepipes +stovepipe's +stow +stowing +stowed +stows +stowage +stowage's +stowaway +stowaways +stowaway's +straddle +straddling +straddled +straddler +straddlers +straddles +straddle's +straddler +straddler's +strafe +strafing +strafed +strafes +strafe's +straggle +straggling +straggled +straggler +stragglers +straggles +straggler +straggler's +straggly +straggliest +stragglier +straight +straighten +straightens +straightly +straightest +straighter +straights +straightness +straight's +straightaway +straightaways +straightaway's +straightedge +straightedges +straightedge's +straighten +straightening +straightened +straightener +straighteners +straightener +straightener's +straightforward +straightforwardly +straightforwards +straightforwardness +straightforwardness +straightforwardness's +straightness +straightness's +straightway +strain's +strain +straining +strained +strains +restraining +constraining +restrained +constrained +restrains +constrains +restrain +constrain +strainer +strainers +strainer's +restrainers +restrainer's +restrainer +strait +straiten +straitens +straits +strait's +straiten +straitening +straitened +straitjacket +straitjacketing +straitjacketed +straitjackets +straitjacket's +straitlaced +strand +stranding +stranded +strands +strand's +strange +strangely +strangest +stranger +strangers +strangeness +strangeness +strangeness's +stranger +stranger's +strangle +strangling +strangled +strangler +stranglers +strangles +stranglehold +strangleholds +stranglehold's +strangler +strangler's +strangulate +strangulation +strangulating +strangulated +strangulates +strangulation +strangulation's +strap's +strap +straps +unstraps +unstrap +strapless +straplesses +strapless's +strapped +unstrapped +strapping +strapping's +strata +stratagem +stratagems +stratagem's +strategic +strategics +strategical +strategically +strategics +strategics's +strategist +strategists +strategist's +strategy +strategies +strategy's +strati +stratification +stratification's +stratify +stratification +stratifying +stratified +stratifies +stratosphere +stratospheres +stratosphere's +stratospheric +stratum +stratum's +stratus +stratus's +straw +strawing +strawed +straws +straw's +strawberry +strawberries +strawberry's +stray +straying +strayed +strays +stray's +streak +streaking +streaked +streaker +streakers +streaks +streak's +streaker +streaker's +streaky +streakiest +streakier +stream +streaming +streamed +streamer +streamers +streams +stream's +streamer +streamer's +streamline +streamlining +streamlined +streamlines +street +streets +street's +streetcar +streetcars +streetcar's +streetlamp +streetlamps +streetlight +streetlights +streetlight's +streetwalker +streetwalkers +streetwalker's +streetwise +strength +strength's +strengthen +strengthening +strengthened +strengthens +restrengthening +restrengthened +restrengthens +restrengthen +strengthener +strengtheners +strengthener's +strengths +strenuous +strenuously +strenuousness +strenuousness +strenuousness's +strep +strep's +streptococcal +streptococci +streptococcus +streptococcus's +streptomycin +streptomycin's +stress +stressing +stressed +stresses +stress's +stressed +unstressed +stressful +stressors +stretch +stretching +stretched +stretcher +stretchers +stretches +stretch's +stretchable +stretcher +stretchering +stretchered +stretcher's +stretchmarks +stretchy +stretchiest +stretchier +strew +strewth +strewing +strewed +strews +strewn +stria +stria's +striae +striated +striation +striations +striation's +stricken +strict +strictly +strictest +stricter +strictness +strictness +strictness's +stricture +strictures +stricture's +stridden +stride +striding +strides +stride's +stridency +stridency's +strident +stridently +strife +strife's +strike +striking +strikings +striker +strikers +strikes +strike's +strikebound +strikebreaker +strikebreakers +strikebreaker's +strikebreaking +strikeout +strikeouts +strikeout's +striker +striker's +striking +strikingly +string +stringing +stringed +stringer +stringers +strings +string's +stringency +stringency's +stringent +stringently +stringer +stringer's +stringiness +stringiness's +stringy +stringiest +stringier +stringiness +strip +striping +striped +strips +strip's +stripe +stripes +stripe's +stripey +stripling +striplings +stripling's +stripped +stripper +strippers +stripper's +stripping +striptease +stripteasing +stripteased +stripteaser +stripteasers +stripteases +striptease's +stripteaser +stripteaser's +stripy +strive +striving +strives +striven +strobe +strobes +strobe's +stroboscope +stroboscopes +stroboscope's +stroboscopic +strode +stroke +stroking +stroked +strokes +stroke's +stroll +strolling +strolled +stroller +strollers +strolls +stroll's +stroller +stroller's +strong +strongly +strongest +stronger +strongbox +strongboxes +strongbox's +stronghold +strongholds +stronghold's +strongman +strongman's +strongmen +strongroom +strongrooms +strontium +strontium's +strop +strops +strop's +strophe +strophes +strophe's +strophic +stropped +stroppily +stropping +stroppy +stroppiest +stroppier +stroppiness +strove +struck +structural +structurally +structuralism +structuralist +structuralists +structure's +structure +structuring +structured +structures +restructuring +restructured +restructures +restructure +structured +unstructured +strudel +strudels +strudel's +struggle +struggling +struggled +struggles +struggle's +strum +strums +strum's +strummed +strumming +strumpet +strumpets +strumpet's +strung +restrung +unstrung +strut +struts +strut's +strutted +strutting +strychnine +strychnine's +stub +stubs +stub's +stubbed +stubbing +stubble +stubble's +stubbly +stubborn +stubbornly +stubbornest +stubborner +stubbornness +stubbornness +stubbornness's +stubby +stubbiest +stubbier +stucco +stuccoing +stuccoed +stucco's +stuccoes +stuck +unstuck +stud +studly +studs +stud's +studbook +studbooks +studbook's +studded +studding +studding's +student +students +student's +studentship +studentships +studied +unstudied +studiedly +studio +studios +studio's +studious +studiously +studiousness +studiousness +studiousness's +studly +studliest +studlier +study's +study +studying +studied +studies +restudying +restudied +restudies +restudy +stuff +stuffing +stuffings +stuffed +stuffs +stuff's +stuffily +stuffiness +stuffiness's +stuffing +stuffing's +stuffy +stuffiest +stuffier +stuffiness +stultification +stultification's +stultify +stultification +stultifying +stultified +stultifies +stumble +stumbling +stumbled +stumbler +stumblers +stumbles +stumble's +stumbler +stumbler's +stump +stumping +stumped +stumps +stump's +stumpy +stumpiest +stumpier +stun +stuns +stung +stunk +stunned +stunner +stunners +stunning +stunningly +stunt +stunting +stunted +stunts +stunt's +stuntman +stuntmen +stupefaction +stupefaction's +stupefy +stupefying +stupefied +stupefies +stupendous +stupendously +stupid +stupidly +stupidest +stupider +stupids +stupid's +stupidity +stupidities +stupidity's +stupor +stupors +stupor's +sturdily +sturdiness +sturdiness's +sturdy +sturdiest +sturdier +sturdiness +sturgeon +sturgeons +sturgeon's +stutter +stuttering +stuttered +stutterer +stutterers +stutters +stutter's +stutterer +stutterer's +sty +sties +sty's +style's +style +styling +styled +styles +restyling +restyled +restyles +restyle +styli +stylish +stylishly +stylishness +stylishness +stylishness's +stylist +stylists +stylist's +stylistic +stylistics +stylistically +stylize +stylizing +stylized +stylizes +stylus +styluses +stylus's +stymie +stymied +stymies +stymie's +stymieing +styptic +styptics +styptic's +suasion +suasion's +dissuasion's +dissuasion +suave +suavely +suavest +suaver +suaveness +suaveness +suaveness's +suavity +suavity's +sub +subs +sub's +subaltern +subalterns +subaltern's +subaqua +subarctic +subarea +subareas +subarea's +subatomic +subbasement +subbasements +subbasement's +subbed +subbing +subbranch +subbranches +subbranch's +subcategory +subcategories +subcategory's +subclass +subcommittee +subcommittees +subcommittee's +subcompact +subcompacts +subcompact's +subconscious +subconsciously +subconsciousness +subconscious's +subconsciousness +subconsciousness's +subcontinent +subcontinents +subcontinent's +subcontinental +subcontract +subcontracting +subcontracted +subcontracts +subcontract's +subcontractor +subcontractors +subcontractor's +subculture +subcultures +subculture's +subcutaneous +subcutaneously +subdivide +subdividing +subdivided +subdivides +subdivision +subdivisions +subdivision's +subdomain +subdomains +subdomain's +subdominant +subdue +subduing +subdued +subdues +subeditor +subeditors +subfamily +subfamilies +subfamily's +subfreezing +subgroup +subgroups +subgroup's +subhead +subheading +subheadings +subheads +subhead's +subheading +subheading's +subhuman +subhumans +subhuman's +subj +subject +subjective +subjecting +subjected +subjects +subject's +subjection +subjection's +subjective +subjectively +subjectivity +subjectivity's +subjoin +subjoining +subjoined +subjoins +subjugate +subjugation +subjugating +subjugated +subjugates +subjugation +subjugation's +subjunctive +subjunctives +subjunctive's +sublease +subleasing +subleased +subleases +sublease's +sublet +sublets +sublet's +subletting +sublieutenant +sublieutenants +sublimate +sublimation +sublimating +sublimated +sublimates +sublimation +sublimation's +sublime +sublimely +subliming +sublimed +sublimest +sublimer +sublimes +subliminal +subliminally +sublimity +sublimity's +sublingual +submarginal +submarine +submariner +submariners +submarines +submarine's +submariner +submariner's +submerge +submerging +submerged +submerges +submergence +submergence's +submerse +submersion +submersing +submersed +submerses +submersible +submersibles +submersible's +submersion +submersion's +submicroscopic +submission +submissions +submission's +submissive +submissively +submissiveness +submissiveness +submissiveness's +submit +submits +resubmits +resubmit +submitted +resubmitted +submitter +submitting +resubmitting +subnormal +suborbital +suborder +suborders +suborder's +subordinate +subordination +subordinating +subordinated +subordinates +subordinate's +subordination +subordination's +insubordination's +insubordination +suborn +suborning +suborned +suborns +subornation +subornation's +subpar +subparagraph +subpart +subplot +subplots +subplot's +subpoena +subpoenaing +subpoenaed +subpoenas +subpoena's +subprime +subprofessional +subprofessionals +subprofessional's +subprogram +subprograms +subroutine +subroutines +subroutine's +subscribe +subscribing +subscribed +subscribes +resubscribing +unsubscribing +resubscribed +unsubscribed +resubscribes +unsubscribes +resubscribe +unsubscribe +subscriber +subscribers +subscriber's +subscript +subscripts +subscript's +subscription +subscriptions +subscription's +subsection +subsections +subsection's +subsequent +subsequently +subservience +subservience's +subservient +subserviently +subset +subsets +subset's +subside +subsiding +subsided +subsides +subsidence +subsidence's +subsidiarity +subsidiary +subsidiaries +subsidiary's +subsidization +subsidization's +subsidize +subsidizing +subsidized +subsidizer +subsidizers +subsidizes +subsidizer +subsidizer's +subsidy +subsidies +subsidy's +subsist +subsisting +subsisted +subsists +subsistence +subsistence's +subsoil +subsoil's +subsonic +subspace +subspecies +subspecies's +substance +substances +substance's +substandard +substantial +substantially +insubstantially +insubstantial +substantiate +substantiation +substantiations +substantiating +substantiated +substantiates +substantiated +unsubstantiated +substantiation +substantiation's +consubstantiation's +consubstantiation +substantive +substantively +substantives +substantive's +substation +substations +substation's +substituent +substitute +substitution +substitutions +substituting +substituted +substitutes +substitute's +substitution +substitution's +substrata +substrate +substrates +substrate's +substratum +substratum's +substructure +substructures +substructure's +subsume +subsuming +subsumed +subsumes +subsumption +subsurface +subsurface's +subsystem +subsystems +subsystem's +subteen +subteens +subteen's +subtenancy +subtenancy's +subtenant +subtenants +subtenant's +subtend +subtending +subtended +subtends +subterfuge +subterfuges +subterfuge's +subterranean +subtext +subtexts +subtext's +subtitle +subtitling +subtitled +subtitles +subtitle's +subtle +subtlest +subtler +subtlety +subtleties +subtlety's +subtly +subtopic +subtopics +subtopic's +subtotal +subtotaling +subtotaled +subtotals +subtotal's +subtract +subtracting +subtracted +subtracts +subtraction +subtractions +subtraction's +subtrahend +subtrahends +subtrahend's +subtropic +subtropics +subtropical +subtropics +subtropics's +suburb +suburbs +suburb's +suburban +suburbans +suburban's +suburbanite +suburbanites +suburbanite's +suburbia +suburbia's +subvention +subventions +subvention's +subversion +subversion's +subversive +subversively +subversives +subversiveness +subversive's +subversiveness +subversiveness's +subvert +subverting +subverted +subverts +subway +subways +subway's +subzero +succeed +succeeding +succeeded +succeeds +success +successive +successes +success's +successful +successfully +unsuccessfully +unsuccessful +succession +successions +succession's +successive +successively +successor +successors +successor's +succinct +succinctly +succinctest +succincter +succinctness +succinctness +succinctness's +succor +succoring +succored +succors +succor's +succotash +succotash's +succubi +succubus +succulence +succulence's +succulency +succulency's +succulent +succulents +succulent's +succumb +succumbing +succumbed +succumbs +such +suchlike +suck +sucking +sucked +sucker +suckers +sucks +suck's +sucker +suckering +suckered +sucker's +suckle +suckling +sucklings +suckled +suckles +suckling +suckling's +sucrose +sucrose's +suction +suctioning +suctioned +suctions +suction's +sudden +suddenly +suddenness +suddenness +suddenness's +suds +suds's +sudsy +sudsiest +sudsier +sue +suing +sued +sues +suede +suede's +suet +suet's +suety +suffer +suffering +sufferings +suffered +sufferer +sufferers +suffers +sufferance +sufferance's +sufferer +sufferer's +suffering +suffering's +suffice +sufficing +sufficed +suffices +sufficiency +sufficiency's +insufficiency's +insufficiency +sufficient +sufficiently +insufficiently +insufficient +suffix +suffixing +suffixed +suffixes +suffix's +suffixation +suffixation's +suffocate +suffocation +suffocating +suffocated +suffocates +suffocation +suffocation's +suffragan +suffragans +suffragan's +suffrage +suffrage's +suffragette +suffragettes +suffragette's +suffragist +suffragists +suffragist's +suffuse +suffusion +suffusing +suffused +suffuses +suffusion +suffusion's +sugar +sugaring +sugared +sugars +sugar's +sugarcane +sugarcane's +sugarcoat +sugarcoating +sugarcoated +sugarcoats +sugarless +sugarplum +sugarplums +sugarplum's +sugary +sugariest +sugarier +suggest +suggestive +suggesting +suggested +suggester +suggests +suggestibility +suggestibility's +suggestible +suggestion +suggestions +suggestion's +suggestive +suggestively +suggestiveness +suggestiveness +suggestiveness's +suicidal +suicide +suicides +suicide's +suit +suiting +suited +suits +suit's +suitable +suitability +suitability's +unsuitability's +unsuitability +suitableness +suitableness's +suitably +unsuitably +suitcase +suitcases +suitcase's +suite +suites +suite's +suited +unsuited +suiting +suiting's +suitor +suitors +suitor's +sukiyaki +sukiyaki's +sulfa +sulfa's +sulfate +sulfates +sulfate's +sulfide +sulfides +sulfide's +sulfonamides +sulfur +sulfuring +sulfured +sulfurs +sulfur's +sulfuric +sulfurous +sulk +sulking +sulked +sulks +sulk's +sulkily +sulkiness +sulkiness's +sulky +sulkiest +sulkier +sulkies +sulkiness +sulky's +sullen +sullenly +sullenest +sullener +sullenness +sullenness +sullenness's +sullied +unsullied +sully +sullying +sullied +sullies +sultan +sultans +sultan's +sultana +sultanas +sultana's +sultanate +sultanates +sultanate's +sultrily +sultriness +sultriness's +sultry +sultriest +sultrier +sultriness +sum +sums +sum's +sumac +sumac's +summarily +summarize +summarizing +summarized +summarizes +summary +summaries +summary's +summat +summation +summations +summation's +consummations +consummation's +consummation +summed +summer +summering +summered +summers +summer's +summerhouse +summerhouses +summerhouse's +summertime +summertime's +summery +summing +summit +summits +summit's +summitry +summitry's +summon +summoning +summoned +summoner +summoners +summons +summoner +summoner's +summons +summonsing +summonsed +summonses +summons's +sumo +sumo's +sump +sumps +sump's +sumptuous +sumptuously +sumptuousness +sumptuousness +sumptuousness's +sun +suns +sun's +sunbath +sunbathing +sunbathed +sunbather +sunbathers +sunbathes +sunbath's +sunbathe +sunbather +sunbather's +sunbathing +sunbathing's +sunbaths +sunbeam +sunbeams +sunbeam's +sunbed +sunbeds +sunbelt +sunbelts +sunbelt's +sunblock +sunblocks +sunblock's +sunbonnet +sunbonnets +sunbonnet's +sunburn +sunburning +sunburned +sunburns +sunburn's +sunburst +sunbursts +sunburst's +sundae +sundaes +sundae's +sundeck +sundecks +sunder +sundering +sundered +sunders +sundial +sundials +sundial's +sundown +sundowns +sundown's +sundress +sundresses +sundries +sundries's +sundry +sundries +sunfish +sunfishes +sunfish's +sunflower +sunflowers +sunflower's +sung +unsung +sunglasses +sunglasses's +sunhat +sunhats +sunk +sunken +sunlamp +sunlamps +sunlamp's +sunless +sunlight +sunlight's +sunlit +sunned +sunniness +sunniness's +sunning +sunny +sunniest +sunnier +sunniness +sunrise +sunrises +sunrise's +sunroof +sunroofs +sunroof's +sunscreen +sunscreens +sunscreen's +sunset +sunsets +sunset's +sunshade +sunshades +sunshade's +sunshine +sunshine's +sunshiny +sunspot +sunspots +sunspot's +sunstroke +sunstroke's +suntan +suntans +suntan's +suntanned +suntanning +suntrap +suntraps +sunup +sunup's +sup +super +supers +sups +sup's +super +super's +superabundance +superabundances +superabundance's +superabundant +superannuate +superannuation +superannuating +superannuated +superannuates +superannuation +superannuation's +superb +superbly +superbest +superber +supercargo +supercargo's +supercargoes +supercharge +supercharging +supercharged +supercharger +superchargers +supercharges +supercharger +supercharger's +supercilious +superciliously +superciliousness +superciliousness +superciliousness's +supercity +supercities +supercity's +supercomputer +supercomputers +supercomputer's +superconducting +superconductive +superconductivity +superconductivity's +superconductor +superconductors +superconductor's +supercritical +superego +superegos +superego's +supererogation +supererogation's +supererogatory +superficial +superficially +superficiality +superficiality's +superfine +superfluity +superfluity's +superfluous +superfluously +superfluousness +superfluousness +superfluousness's +superglue +supergrass +supergrasses +superhero +superheros +superhero's +superheroes +superhighway +superhighways +superhighway's +superhuman +superimpose +superimposing +superimposed +superimposes +superimposition +superimposition's +superintend +superintending +superintended +superintends +superintendence +superintendence's +superintendency +superintendency's +superintendent +superintendents +superintendent's +superior +superiors +superior's +superiority +superiority's +superlative +superlatively +superlatives +superlative's +superman +superman's +supermarket +supermarkets +supermarket's +supermassive +supermen +supermodel +supermodels +supermodel's +supermom +supermoms +supermom's +supernal +supernatural +supernaturally +supernaturals +supernova +supernovas +supernova's +supernovae +supernumerary +supernumeraries +supernumerary's +superpose +superposing +superposed +superposes +superposition +superposition's +superpower +superpowers +superpower's +supersaturate +supersaturation +supersaturating +supersaturated +supersaturates +supersaturation +supersaturation's +superscribe +superscribing +superscribed +superscribes +superscript +superscripts +superscript's +superscription +superscription's +supersede +superseding +superseded +supersedes +supersize +supersizing +supersized +supersizes +supersonic +superstar +superstars +superstar's +superstardom +superstate +superstates +superstition +superstitions +superstition's +superstitious +superstitiously +superstore +superstores +superstore's +superstructure +superstructures +superstructure's +supertanker +supertankers +supertanker's +superuser +superusers +supervene +supervening +supervened +supervenes +supervention +supervention's +supervise +supervision +supervisions +supervising +supervised +supervises +supervised +unsupervised +supervision +supervision's +supervisor +supervisors +supervisor's +supervisory +superwoman +superwoman's +superwomen +supine +supinely +supp +supping +supped +supper +suppers +supper +supper's +suppertime +suppl +supplant +supplanting +supplanted +supplants +supple +supplest +suppler +suppleness +supplement +supplement +supplementing +supplemented +supplements +supplement's +supplemental +supplementary +supplementation +supplementation's +suppleness +suppleness's +suppliant +suppliants +suppliant's +supplicant +supplicants +supplicant's +supplicate +supplicating +supplicated +supplicates +supplication +supplication's +supplier +supplier's +supply +supplication +supplications +supplying +supplied +supplier +suppliers +supplies +supply's +support +supportive +supporting +supported +supporter +supporters +supports +support's +supportable +supportable +insupportable +unsupportable +supported +unsupported +supporter +supporter's +suppose +supposing +supposed +supposes +supposed +supposedly +supposition +suppositions +supposition's +suppository +suppositories +suppository's +suppress +suppressive +suppressing +suppressed +suppresses +suppressant +suppressants +suppressant's +suppressible +suppression +suppression's +suppressor +suppressors +suppressor's +suppurate +suppuration +suppurating +suppurated +suppurates +suppuration +suppuration's +supra +supranational +supremacist +supremacists +supremacist's +supremacy +supremacy's +supreme +supremely +supremo +supremos +supt +surcease +surceasing +surceased +surceases +surcease's +surcharge +surcharging +surcharged +surcharges +surcharge's +surcingle +surcingles +surcingle's +sure +surely +surest +surer +sureness +surefire +surefooted +sureness +sureness's +surety +sureties +surety's +surf +surfing +surfed +surfer +surfers +surfs +surf's +surface's +surface +surfacing +surfaced +surfaces +resurfacing +resurfaced +resurfaces +resurface +surfboard +surfboarding +surfboarded +surfboards +surfboard's +surfeit +surfeiting +surfeited +surfeits +surfeit's +surfer +surfer's +surfing +surfing's +surge +surging +surged +surges +surge's +surgeon +surgeons +surgeon's +surgery +surgeries +surgery's +surgical +surgically +surliness +surliness's +surly +surliest +surlier +surliness +surmise +surmising +surmised +surmises +surmise's +surmount +surmounting +surmounted +surmounts +surmountable +surmountable +insurmountable +surname +surnames +surname's +surpass +surpassing +surpassed +surpasses +surpassed +unsurpassed +surplice +surplices +surplice's +surplus +surpluses +surplus's +surplussed +surplussing +surprise +surprising +surprisings +surprised +surprises +surprise's +surprising +surprisingly +unsurprisingly +unsurprising +surreal +surrealism +surrealism's +surrealist +surrealists +surrealist's +surrealistic +surrealistically +surrender +surrendering +surrendered +surrenders +surrender's +surreptitious +surreptitiously +surreptitiousness +surreptitiousness +surreptitiousness's +surrey +surreys +surrey's +surrogacy +surrogacy's +surrogate +surrogates +surrogate's +surround +surrounding +surroundings +surrounded +surrounds +surrounding +surrounding's +surroundings +surroundings's +surtax +surtaxing +surtaxed +surtaxes +surtax's +surtitle +surtitles +surveillance +surveillance's +survey's +survey +surveying +surveyed +surveys +resurveying +resurveyed +resurveys +resurvey +surveying +surveying's +surveyor +surveyors +surveyor's +survival +survivals +survival's +survivalist +survivalists +survivalist's +survive +surviving +survived +survives +survivable +survivor +survivors +survivor's +susceptibility +susceptibilities +susceptibility's +susceptible +insusceptible +sushi +sushi's +suspect +suspecting +suspected +suspects +suspect's +suspected +unsuspected +suspend +suspending +suspended +suspender +suspenders +suspends +suspender +suspender's +suspense +suspension +suspensions +suspense's +suspenseful +suspension +suspension's +suspicion +suspicions +suspicion's +suspicious +suspiciously +suss +sussing +sussed +susses +sustain +sustaining +sustained +sustains +sustainable +sustainability +sustainable +unsustainable +sustainably +sustenance +sustenance's +sutler +sutlers +sutler's +suttee +suture +suturing +sutured +sutures +suture's +suzerain +suzerains +suzerain's +suzerainty +suzerainty's +svelte +sveltest +svelter +swab +swabs +swab's +swabbed +swabbing +swaddle +swaddling +swaddled +swaddles +swag +swags +swag's +swagged +swagger +swaggering +swaggered +swaggerer +swaggers +swagger's +swagging +swain +swains +swain's +swallow +swallowing +swallowed +swallows +swallow's +swallowtail +swallowtails +swallowtail's +swam +swami +swamis +swami's +swamp +swamping +swamped +swamps +swamp's +swampland +swampland's +swampy +swampiest +swampier +swan +swans +swan's +swank +swanking +swanked +swankest +swanker +swanks +swank's +swankily +swankiness +swankiness's +swanky +swankiest +swankier +swankiness +swanned +swanning +swansong +swansongs +swap +swaps +swap's +swapped +swapping +sward +swards +sward's +swarm +swarming +swarmed +swarms +swarm's +swarthy +swarthiest +swarthier +swash +swashing +swashed +swashes +swash's +swashbuckler +swashbucklers +swashbuckler's +swashbuckling +swashbuckling's +swastika +swastikas +swastika's +swat +swats +swat's +swatch +swatches +swatch's +swath +swathing +swathed +swathes +swath's +swathe +swathe's +swaths +swatted +swatter +swattering +swattered +swatters +swatter's +swatting +sway +swaying +swayed +sways +sway's +swayback +swaybacked +swayback's +swayed +unswayed +swear +swearing +swearer +swearers +swears +swearer +swearer's +swearword +swearwords +swearword's +sweat +sweating +sweated +sweater +sweaters +sweats +sweat's +sweatband +sweatbands +sweatband's +sweater +sweater's +sweatpants +sweatpants's +sweats +sweats's +sweatshirt +sweatshirts +sweatshirt's +sweatshop +sweatshops +sweatshop's +sweatsuit +sweatsuits +sweaty +sweatiest +sweatier +swede +swedes +swede's +sweep +sweeping +sweepings +sweeper +sweepers +sweeps +sweep's +sweeper +sweeper's +sweeping +sweepingly +sweeping's +sweepings +sweepings's +sweepstakes +sweepstakes's +sweet +sweeten +sweetens +sweetly +sweetest +sweeter +sweets +sweetness +sweet's +sweetbread +sweetbreads +sweetbread's +sweetbrier +sweetbriers +sweetbrier's +sweetcorn +sweetened +unsweetened +sweetener +sweeteners +sweetener's +sweetening +sweetening's +sweetheart +sweethearts +sweetheart's +sweetie +sweeties +sweetie's +sweetish +sweetmeat +sweetmeats +sweetmeat's +sweetness +sweetness's +swell +swelling +swellings +swelled +swellest +sweller +swells +swell's +swellhead +swellheaded +swellheads +swellhead's +swelling +swelling's +swelter +sweltering +sweltered +swelters +swelter's +swept +sweptback +swerve +swerving +swerved +swerves +swerve's +swerving +unswerving +swift +swiftly +swiftest +swifter +swifts +swiftness +swift's +swiftness +swiftness's +swig +swigs +swig's +swigged +swigging +swill +swilling +swilled +swills +swill's +swim +swims +swim's +swimmer +swimmers +swimmer's +swimming +swimmingly +swimming's +swimsuit +swimsuits +swimsuit's +swimwear +swindle +swindling +swindled +swindler +swindlers +swindles +swindle's +swindler +swindler's +swine +swines +swine's +swineherd +swineherds +swineherd's +swing +swinging +swinger +swingers +swings +swing's +swingeing +swinger +swinger's +swinish +swipe +swiping +swiped +swipes +swipe's +swirl +swirling +swirled +swirls +swirl's +swirly +swish +swishing +swished +swishest +swisher +swishes +swish's +switch +switching +switched +switcher +switchers +switches +switch's +switchable +switchback +switchbacks +switchback's +switchblade +switchblades +switchblade's +switchboard +switchboards +switchboard's +switcher +switcher's +switchover +swivel +swiveling +swiveled +swivels +swivel's +swiz +swizz +swizzle +swizzling +swizzled +swizzles +swollen +swoon +swooning +swooned +swoons +swoon's +swoop +swooping +swooped +swoops +swoop's +swoosh +swooshing +swooshed +swooshes +swoosh's +sword +swords +sword's +swordfish +swordfishes +swordfish's +swordplay +swordplay's +swordsman +swordsman's +swordsmanship +swordsmanship's +swordsmen +swore +sworn +swot +swots +swotted +swotting +swum +swung +sybarite +sybarites +sybarite's +sybaritic +sycamore +sycamores +sycamore's +sycophancy +sycophancy's +sycophant +sycophants +sycophant's +sycophantic +syllabic +syllabicate +syllabication +syllabicating +syllabicated +syllabicates +syllabication +syllabication's +syllabification +syllabification's +syllabify +syllabification +syllabifying +syllabified +syllabifies +syllable +syllables +syllable's +syllabub +syllabubs +syllabus +syllabuses +syllabus's +syllogism +syllogisms +syllogism's +syllogistic +sylph +sylph's +sylphic +sylphlike +sylphs +sylvan +symbioses +symbiosis +symbiosis's +symbiotic +symbiotically +symbol +symbols +symbol's +symbolic +symbolical +symbolically +symbolism +symbolism's +symbolization +symbolization's +symbolize +symbolizing +symbolized +symbolizes +symbology +symmetric +symmetrical +symmetrically +symmetry +symmetries +symmetry's +sympathetic +unsympathetic +sympathetically +unsympathetically +sympathies +sympathies's +sympathize +sympathizing +sympathized +sympathizer +sympathizers +sympathizes +sympathizer +sympathizer's +sympathy +sympathies +sympathy's +symphonic +symphony +symphonies +symphony's +symposium +symposiums +symposium's +symptom +symptoms +symptom's +symptomatic +symptomatically +syn +synth +synagogal +synagogue +synagogues +synagogue's +synapse +synapses +synapse's +synaptic +sync +syncing +synced +syncs +sync's +synchronicity +synchronization +synchronizations +synchronization's +synchronize +synchronizing +synchronized +synchronizes +synchronous +synchronously +synchrony +syncopate +syncopation +syncopating +syncopated +syncopates +syncopation +syncopation's +syncope +syncope's +syndicalism +syndicalist +syndicalists +syndicate +syndication +syndicating +syndicated +syndicates +syndicate's +syndication +syndication's +syndrome +syndromes +syndrome's +synergism +synergism's +synergistic +synergy +synergies +synergy's +synfuel +synfuels +synfuel's +synod +synods +synod's +synonym +synonyms +synonym's +synonymous +synonymy +synonymy's +synopses +synopsis +synopsis's +synoptic +synovial +syntactic +syntactical +syntactically +syntax +syntax's +syntheses +synthesis +synthesis's +synthesize +synthesizing +synthesized +synthesizer +synthesizers +synthesizes +synthesizer +synthesizer's +synthetic +synthetics +synthetic's +synthetically +synths +syphilis +syphilis's +syphilitic +syphilitics +syphilitic's +syringe +syringing +syringed +syringes +syringe's +syrup +syrups +syrup's +syrupy +sysadmin +sysadmins +sysop +sysops +system +systems +system's +systematic +unsystematic +systematical +systematically +systematization +systematization's +systematize +systematizing +systematized +systematizes +systemic +systemics +systemic's +systemically +systole +systoles +systole's +systolic +t +ten +tens +ting +tings +ted +ts +table +ta +tab +tabs +tab's +tabbed +tabbing +tabbouleh +tabbouleh's +tabby +tabbies +tabby's +tabernacle +tabernacles +tabernacle's +tabla +tablas +tabla's +table +tabling +tabled +tables +table's +tableau +tableau's +tableaux +tablecloth +tablecloth's +tablecloths +tableland +tablelands +tableland's +tablespoon +tablespoons +tablespoon's +tablespoonful +tablespoonfuls +tablespoonful's +tablet +tablets +tablet's +tabletop +tabletops +tabletop's +tableware +tableware's +tabloid +tabloids +tabloid's +taboo +tabooing +tabooed +taboos +taboo's +tabor +tabors +tabor's +tabular +tabulate +tabulation +tabulations +tabulating +tabulated +tabulates +tabulation +tabulation's +tabulator +tabulators +tabulator's +tachograph +tachographs +tachometer +tachometers +tachometer's +tachycardia +tachycardia's +tachyon +tacit +tacitly +tacitness +tacitness +tacitness's +taciturn +taciturnly +taciturnity +taciturnity's +tack +tacking +tacked +tacker +tackers +tacks +tack's +tacker +tacker's +tackiness +tackiness's +tackle +tackling +tackled +tackler +tacklers +tackles +tackle's +tackler +tackler's +tacky +tackiest +tackier +tackiness +taco +tacos +taco's +tact +tact's +contact's +contact +tactful +tactfully +tactfulness +tactfulness +tactfulness's +tactic +tactics +tactic's +tactical +tactically +tactician +tacticians +tactician's +tactile +tactility +tactility's +tactless +tactlessly +tactlessness +tactlessness +tactlessness's +tad +tads +tad's +tadpole +tadpoles +tadpole's +taffeta +taffeta's +taffrail +taffrails +taffrail's +taffy +taffies +taffy's +tag +tags +tag's +tagged +tagger +taggers +tagger's +tagging +tagliatelle +tagline +taglines +tagline's +taiga +taigas +taiga's +tail +tailing +tailed +tails +tail's +retailing +detailing +retailed +detailed +retails +details +retail's +detail's +retail +detail +tailback +tailbacks +tailback's +tailboard +tailboards +tailbone +tailbones +tailcoat +tailcoats +tailcoat's +tailgate +tailgating +tailgated +tailgater +tailgaters +tailgates +tailgate's +tailgater +tailgater's +tailless +taillight +taillights +taillight's +tailor +tailoring +tailored +tailors +tailor's +tailoring +tailoring's +tailpiece +tailpieces +tailpipe +tailpipes +tailpipe's +tailspin +tailspins +tailspin's +tailwind +tailwinds +tailwind's +taint +tainting +tainted +taints +taint's +tainted +untainted +take +takes +take's +retakes +intakes +retake's +intake's +retake +intake +takeaway +takeaways +taken +retaken +takeoff +takeoffs +takeoff's +takeout +takeouts +takeout's +takeover +takeovers +takeover's +taker +takers +taker's +taking +takings +taking's +takings +takings's +talc +talc's +talcum +talcum's +tale +tales +tale's +talebearer +talebearers +talebearer's +talent +talented +talents +talent's +talented +untalented +tali +talisman +talismans +talisman's +talk +talking +talked +talker +talkers +talks +talk's +talkative +talkatively +talkativeness +talkativeness +talkativeness's +talker +talker's +talkie +talkiest +talkier +talkies +talkie's +talky +tall +tallest +taller +tallness +tallboy +tallboys +tallboy's +tallier +tallier's +tallish +tallness +tallness's +tallow +tallow's +tallowy +tally +tallying +tallied +tallier +talliers +tallies +tally's +tallyho +tallyhoing +tallyhoed +tallyhos +tallyho's +talon +talons +talon's +talus +taluses +talus's +tam +tams +tam's +tamale +tamales +tamale's +tamarack +tamaracks +tamarack's +tamarind +tamarinds +tamarind's +tambourine +tambourines +tambourine's +tame +tamely +taming +tamed +tamest +tamer +tamers +tames +tameness +tamable +tamed +untamed +tameness +tameness's +tamer +tamer's +tamoxifen +tamp +tamping +tamped +tamper +tampers +tamps +tamper +tampering +tampered +tamperer +tamperers +tamperer +tamperer's +tampon +tampons +tampon's +tan +tans +tan's +tanager +tanagers +tanager's +tanbark +tanbark's +tandem +tandems +tandem's +tandoori +tandoori's +tang +tangs +tang's +tangelo +tangelos +tangelo's +tangent +tangents +tangent's +tangential +tangentially +tangerine +tangerines +tangerine's +tangibility +tangibility's +intangibility's +intangibility +tangible +tangibles +tangible's +intangibles +intangible's +intangible +tangibleness +tangibleness's +tangibly +intangibly +tangle's +tangle +tangling +tangled +tangles +untangling +untangled +untangles +untangle +tango +tangoing +tangoed +tangos +tango's +tangy +tangiest +tangier +tank +tanking +tanked +tanker +tankers +tanks +tank's +tankard +tankards +tankard's +tanker +tanker's +tankful +tankfuls +tankful's +tanned +untanned +tanner +tanners +tanner's +tannery +tanneries +tannery's +tannest +tannin +tannin's +tanning +tanning's +tansy +tansy's +tantalization +tantalization's +tantalize +tantalizing +tantalized +tantalizer +tantalizers +tantalizes +tantalizer +tantalizer's +tantalizing +tantalizingly +tantalum +tantalum's +tantamount +tantra +tantra's +tantrum +tantrums +tantrum's +tap +taping +taped +taper +tapers +taps +tap's +tapas +tape +tapes +tape's +tapeline +tapelines +tapeline's +taper +tapering +tapered +taper's +tapestry +tapestries +tapestry's +tapeworm +tapeworms +tapeworm's +tapioca +tapioca's +tapir +tapirs +tapir's +tapped +untapped +tapper +tappers +tapper's +tappet +tappets +tappet's +tapping +taproom +taprooms +taproom's +taproot +taproots +taproot's +tar +taring +tared +tars +tar's +taramasalata +tarantella +tarantellas +tarantella's +tarantula +tarantulas +tarantula's +tarball +tarballs +tardily +tardiness +tardiness's +tardy +tardiest +tardier +tardiness +tare +tares +tare's +target +targeting +targeted +targets +target's +tariff +tariffs +tariff's +tarmac +tarmacs +tarmac's +tarmacadam +tarmacked +tarmacking +tarn +tarns +tarn's +tarnish +tarnishing +tarnished +tarnishes +tarnish's +tarnished +untarnished +taro +taros +taro's +tarot +tarots +tarot's +tarp +tarps +tarp's +tarpaulin +tarpaulins +tarpaulin's +tarpon +tarpons +tarpon's +tarragon +tarragons +tarragon's +tarred +tarring +tarry +tarrying +tarried +tarriest +tarrier +tarries +tarsal +tarsals +tarsal's +tarsi +tarsus +tarsus's +tart +tartly +tarting +tarted +tartest +tarter +tarts +tartness +tart's +tartan +tartans +tartan's +tartar +tartars +tartar's +tartaric +tartness +tartness's +tarty +tartiest +taser +tasering +tasered +tasers +taser's +task +tasking +tasked +tasks +task's +taskbar +taskmaster +taskmasters +taskmaster's +taskmistress +taskmistresses +taskmistress's +tassel +tasseling +tasseled +tassels +tassel's +taste +tasting +tastings +tasted +taster +tasters +tastes +taste's +tasted +untasted +tasteful +tastefully +tastefulness +distastefully +distastefulness +distasteful +tastefulness +tastefulness's +distastefulness's +distastefulness +tasteless +tastelessly +tastelessness +tastelessness +tastelessness's +taster +taster's +tastily +tastiness +tastiness's +tasting +tasting's +tasty +tastiest +tastier +tastiness +tat +tater +taters +tats +tatami +tatamis +tatami's +tater +tater's +tatted +tatter +tattering +tattered +tatters +tatter's +tatterdemalion +tatterdemalions +tatterdemalion's +tattie +tatting +tatting's +tattle +tattling +tattled +tattler +tattlers +tattles +tattle's +tattler +tattler's +tattletale +tattletales +tattletale's +tattoo +tattooing +tattooed +tattooer +tattooers +tattoos +tattoo's +tattooer +tattooer's +tattooist +tattooists +tattooist's +tatty +tattiest +tattier +tatties +tau +taus +tau's +taught +retaught +untaught +taunt +taunting +taunted +taunter +taunters +taunts +taunt's +taunter +taunter's +taunting +tauntingly +taupe +taupe's +taut +tauten +tautens +tautly +tautest +tauter +tautness +tauten +tautening +tautened +tautness +tautness's +tautological +tautologically +tautologous +tautology +tautologies +tautology's +tavern +taverns +tavern's +tawdrily +tawdriness +tawdriness's +tawdry +tawdriest +tawdrier +tawdriness +tawny +tawniest +tawnier +tawny's +tax +taxing +taxed +taxer +taxers +taxes +tax's +taxable +taxa +taxation +taxation's +taxer +taxer's +taxi +taxiing +taxied +taxis +taxi's +taxicab +taxicabs +taxicab's +taxidermist +taxidermists +taxidermist's +taxidermy +taxidermy's +taximeter +taximeters +taximeter's +taxiway +taxiways +taxman +taxmen +taxon +taxonomic +taxonomist +taxonomists +taxonomist's +taxonomy +taxonomies +taxonomy's +taxpayer +taxpayers +taxpayer's +taxpaying +tb +tbs +tbsp +tea +teas +tea's +teabag +teabags +teacake +teacakes +teacake's +teach +teaching +teachings +teacher +teachers +teaches +teachable +teachable +unteachable +teacher +teacher's +teaching +teaching's +teacup +teacups +teacup's +teacupful +teacupfuls +teacupful's +teak +teaks +teak's +teakettle +teakettles +teakettle's +teal +teals +teal's +tealight +tealights +tealight's +team +teaming +teamed +teams +team's +teammate +teammates +teammate's +teamster +teamsters +teamster's +teamwork +teamwork's +teapot +teapots +teapot's +tear +tearing +teared +tears +tear's +tearaway +tearaways +teardrop +teardrops +teardrop's +tearful +tearfully +teargas +teargases +teargas's +teargassed +teargassing +tearjerker +tearjerkers +tearjerker's +tearoom +tearooms +tearoom's +teary +teariest +tearier +tease +teasing +teased +teaser +teasers +teases +tease's +teasel +teasels +teasel's +teaser +teaser's +teasing +teasingly +teaspoon +teaspoons +teaspoon's +teaspoonful +teaspoonfuls +teaspoonful's +teat +teats +teat's +teatime +teatimes +tech +tech's +techie +techies +technetium +technetium's +technical +technically +technicality +technicalities +technicality's +technician +technicians +technician's +technicolor +technique +techniques +technique's +techno +technobabble +technocracy +technocracies +technocracy's +technocrat +technocrats +technocrat's +technocratic +technological +technologically +technologist +technologists +technologist's +technology +technologies +technology's +technophobe +technophobes +techs +tectonic +tectonics +tectonics +tectonics's +ted +teds +teddy +teddies +tedious +tediously +tediousness +tediousness +tediousness's +tedium +tedium's +tee +teeth +teed +tees +tee's +teeing +teem +teeming +teemed +teems +teen +teens +teen's +teenage +teenager +teenagers +teenager +teenager's +teeny +teeniest +teenier +teenybopper +teenyboppers +teenybopper's +teeter +teetering +teetered +teeters +teeter's +teethe +teething +teethed +teethes +teething +teething's +teetotal +teetotaler +teetotalers +teetotaler +teetotaler's +teetotalism +teetotalism's +tektite +tektites +tektite's +tel +telecast +telecasting +telecaster +telecasters +telecasts +telecast's +telecaster +telecaster's +telecommunication +telecommunications +telecommunication's +telecommunications +telecommunications's +telecommute +telecommuting +telecommuted +telecommuter +telecommuters +telecommutes +telecommuter +telecommuter's +telecommuting +telecommuting's +teleconference +teleconferencing +teleconferenced +teleconferences +teleconference's +teleconferencing +teleconferencing's +telegenic +telegram +telegrams +telegram's +telegraph +telegraphing +telegraphed +telegrapher +telegraphers +telegraph's +telegrapher +telegrapher's +telegraphese +telegraphic +telegraphically +telegraphist +telegraphists +telegraphist's +telegraphs +telegraphy +telegraphy's +telekinesis +telekinesis's +telekinetic +telemarketer +telemarketers +telemarketer's +telemarketing +telemarketing's +telemeter +telemeters +telemeter's +telemetry +telemetries +telemetry's +teleological +teleology +telepathic +telepathically +telepathy +telepathy's +telephone +telephoning +telephoned +telephoner +telephoners +telephones +telephone's +telephoner +telephoner's +telephonic +telephonist +telephonists +telephony +telephony's +telephoto +telephotos +telephoto's +telephotography +telephotography's +teleplay +teleplays +teleplay's +teleport +teleportation +teleprinter +teleprinters +teleprinter's +teleprocessing +teleprocessing's +teleprompter +teleprompters +teleprompter's +telesales +telescope +telescoping +telescoped +telescopes +telescope's +telescopic +telescopically +teletext +teletexts +teletext's +telethon +telethons +telethon's +teletype +teletypes +teletypewriter +teletypewriters +teletypewriter's +televangelism +televangelism's +televangelist +televangelists +televangelist's +televise +television +televisions +televising +televised +televises +television +television's +teleworker +teleworkers +teleworking +telex +telexing +telexed +telexes +telex's +tell +telling +tells +retelling +retells +retell +teller +tellers +teller's +telling +tellingly +telltale +telltales +telltale's +tellurium +tellurium's +telly +tellies +telly's +telnet +temblor +temblors +temblor's +temerity +temerity's +temp +temping +temped +tempest +temper +tempers +temps +temp's +temper +tempering +tempered +temper's +tempera +temperas +tempera's +temperament +temperament +temperaments +temperament's +temperamental +temperamentally +temperance +temperance's +intemperance's +intemperance +temperate +temperately +intemperately +intemperate +temperateness +temperateness's +temperature +temperatures +temperature's +tempest +tempests +tempest's +tempestuous +tempestuously +tempestuousness +tempestuousness +tempestuousness's +template's +template +templates +temple +temples +temple's +tempo +tempos +tempo's +temporal +temporally +temporarily +temporariness +temporariness's +temporary +temporaries +temporary's +contemporaries +contemporary's +contemporary +temporize +temporizing +temporized +temporizer +temporizers +temporizes +temporizer +temporizer's +tempt +tempting +tempted +tempter +tempters +tempts +temptation +temptations +temptation's +tempter +tempter's +tempting +temptingly +temptress +temptresses +temptress's +tempura +tempura's +ten +tenth +ten's +tenable +tenability +tenability's +tenable +untenable +tenably +tenacious +tenaciously +tenaciousness +tenaciousness +tenaciousness's +tenacity +tenacity's +tenancy +tenancies +tenancy's +tenant +tenanting +tenanted +tenants +tenant's +tenanted +untenanted +tenantry +tenantry's +tench +tend +tending +tended +tends +intending +distending +contending +intended +distended +contended +intends +distends +contends +intend +distend +contend +tended +untended +tendency +tendencies +tendency's +tendentious +tendentiously +tendentiousness +tendentiousness +tendentiousness's +tender +tenderly +tendering +tendered +tenderest +tenderer +tenders +tenderness +tender's +tenderfoot +tenderfoots +tenderfoot's +tenderhearted +tenderheartedness +tenderheartedness +tenderheartedness's +tenderize +tenderizing +tenderized +tenderizer +tenderizers +tenderizes +tenderizer +tenderizer's +tenderloin +tenderloins +tenderloin's +tenderness +tenderness's +tendinitis +tendinitis's +tendon +tendons +tendon's +tendril +tendrils +tendril's +tenement +tenements +tenement's +tenet +tenets +tenet's +tenfold +tenner +tenners +tennis +tennis's +tenon +tenoning +tenoned +tenons +tenon's +tenor +tenors +tenor's +tenpin +tenpins +tenpin's +tenpins +tenpins's +tense +tension +tensions +tensely +tensing +tensed +tensest +tenser +tenses +tenseness +tense's +tenseness +tenseness's +tensile +tension +tensions +tension's +distensions +distension's +distension +tensity +tensity's +intensity's +intensity +tensor +tensors +tent +tenting +tented +tents +tent's +tentacle +tentacled +tentacles +tentacle's +tentative +tentatively +tentativeness +tentativeness +tentativeness's +tenterhook +tenterhooks +tenterhook's +tenth +tenthly +tenth's +tenths +tenuity +tenuity's +tenuous +tenuously +tenuousness +tenuousness +tenuousness's +tenure +tenuring +tenured +tenures +tenure's +tepee +tepees +tepee's +tepid +tepidly +tepidness +tepidity +tepidity's +tepidness +tepidness's +tequila +tequilas +tequila's +terabit +terabits +terabit's +terabyte +terabytes +terabyte's +terahertz +terahertz's +terapixel +terapixels +terapixel's +terbium +terbium's +tercentenary +tercentenaries +tercentenary's +tercentennial +tercentennials +tercentennial's +teriyaki +term +termly +terming +termed +terms +term's +termagant +termagants +termagant's +terminable +interminable +determinable +terminal +terminally +terminals +terminal's +terminate +termination +terminations +terminating +terminated +terminates +termination +terminations +termination's +determinations +determination's +determination +terminator +terminators +termini +terminological +terminologically +terminology +terminologies +terminology's +terminus +terminus's +termite +termites +termite's +tern +terns +tern's +interns +intern's +intern +ternary +ternaries +ternary's +terr +terrace +terracing +terraced +terraces +terrace's +terracotta +terracotta's +terrain +terrains +terrain's +terrapin +terrapins +terrapin's +terrarium +terrariums +terrarium's +terrazzo +terrazzos +terrazzo's +terrestrial +terrestrially +terrestrials +terrestrial's +terrible +terribleness +terribleness +terribleness's +terribly +terrier +terrier's +terrific +terrifically +terrify +terrifying +terrified +terrifies +terrifying +terrifyingly +terrine +terrines +territorial +territorials +territorial's +territoriality +territory +territories +territory's +terror +terrors +terror's +terrorism +terrorism's +terrorist +terrorists +terrorist's +terrorize +terrorizing +terrorized +terrorizes +terry +terrier +terriers +terry's +terrycloth +terrycloth's +terse +tersely +tersest +terser +terseness +terseness +terseness's +tertiary +tessellate +tessellation +tessellations +tessellating +tessellated +tessellates +tessellation +tessellation's +test's +retest's +contest's +protest's +test +testing +tested +tests +retesting +detesting +contesting +protesting +retested +detested +contested +protested +retests +detests +contests +protests +retest +detest +contest +protest +testable +detestable +contestable +testament +testaments +testament's +testamentary +testate +testates +testator +testators +testator's +testatrices +testatrix +testatrix's +tested +untested +tester +testers +tester's +protesters +protester's +protester +testes +testicle +testicles +testicle's +testicular +testifier +testifier's +testify +testifying +testified +testifier +testifiers +testifies +testily +testimonial +testimonials +testimonial's +testimony +testimonies +testimony's +testiness +testiness's +testings +testis +testis's +testosterone +testosterone's +testy +testiest +testier +testiness +tetanus +tetanus's +tetchily +tetchy +tetchiest +tetchier +tetchiness +tether +tethering +tethered +tethers +tether's +tetra +tetras +tetra's +tetracycline +tetracycline's +tetrahedral +tetrahedron +tetrahedrons +tetrahedron's +tetrameter +tetrameters +tetrameter's +text +texts +text's +contexts +context's +context +textbook +textbooks +textbook's +texted +textile +textiles +textile's +texting +textual +textually +contextually +contextual +textural +texture +texturing +textured +textures +texture's +thalami +thalamus +thalamus's +thalidomide +thalidomide's +thallium +thallium's +than +thane +thanes +thane's +thank +thanking +thanked +thanks +thankful +thankfully +thankfulness +thankfulness +thankfulness's +thankless +thanklessly +thanklessness +thanklessness +thanklessness's +thanksgiving +thanksgivings +thanksgiving's +that'd +that'll +that +that's +thatch +thatching +thatched +thatcher +thatchers +thatches +thatch's +thatcher +thatcher's +thatching +thatching's +thaw +thawing +thawed +thaws +thaw's +the +thing +things +theater +theaters +theater's +theatergoer +theatergoers +theatergoer's +theatrical +theatrically +theatricals +theatricality +theatricality's +theatricals +theatricals's +theatrics +theatrics's +thee +thees +theft +thefts +theft's +their +theirs +theism +theism's +theist +theists +theist's +theistic +them +thematic +thematically +theme +themed +themes +theme's +themselves +then +then's +thence +thenceforth +thenceforward +theocracy +theocracies +theocracy's +theocratic +theodolite +theodolites +theologian +theologians +theologian's +theological +theologically +theology +theologies +theology's +theorem +theorems +theorem's +theoretic +theoretical +theoretically +theoretician +theoreticians +theoretician's +theorist +theorists +theorist's +theorize +theorizing +theorized +theorizes +theory +theories +theory's +theosophic +theosophical +theosophist +theosophists +theosophist's +theosophy +theosophy's +therapeutic +therapeutics +therapeutically +therapeutics +therapeutics's +therapist +therapists +therapist's +therapy +therapies +therapy's +there +there's +thereabout +thereabouts +thereafter +thereat +thereby +therefor +therefore +therefrom +therein +theremin +theremins +theremin's +thereof +thereon +thereto +theretofore +thereunder +thereunto +thereupon +therewith +therm +therms +therm's +thermal +thermally +thermals +thermal's +thermionic +thermodynamic +thermodynamics +thermodynamics +thermodynamics's +thermometer +thermometers +thermometer's +thermometric +thermonuclear +thermoplastic +thermoplastics +thermoplastic's +thermos +thermoses +thermos's +thermostat +thermostats +thermostat's +thermostatic +thermostatically +thesauri +thesaurus +thesauruses +thesaurus's +these +theses +thesis +thesis's +thespian +thespians +thespian's +theta +thetas +theta's +thew +thews +thew's +they +they'd +they'll +they're +they've +thiamine +thiamine's +thick +thicken +thickens +thickly +thickest +thicker +thickness +thick's +thicken +thickening +thickenings +thickened +thickener +thickeners +thickener +thickener's +thickening +thickening's +thicket +thickets +thicket's +thickheaded +thickheaded's +thickness +thicknesses +thickness's +thicko +thickos +thickset +thief +thief's +thieve +thieving +thieved +thieves +thievery +thievery's +thieving +thieving's +thievish +thigh +thigh's +thighbone +thighbones +thighbone's +thighs +thimble +thimbles +thimble's +thimbleful +thimblefuls +thimbleful's +thin +thinly +thins +thinness +thine +thing +thing's +thingamabob +thingamabobs +thingamabob's +thingamajig +thingamajigs +thingamajig's +thingumabob +thingumabobs +thingummy +thingummies +thingy +thingies +think +thinking +thinker +thinkers +thinks +thinkable +thinkable +unthinkable +thinker +thinker's +thinking's +thinned +thinner +thinners +thinner's +thinness +thinness's +thinnest +thinning +third +thirdly +thirds +third's +thirst +thirsting +thirsted +thirsts +thirst's +thirstily +thirstiness +thirstiness's +thirsty +thirstiest +thirstier +thirstiness +thirteen +thirteenth +thirteens +thirteen's +thirteenth +thirteenth's +thirteenths +thirtieth +thirtieth's +thirtieths +thirty +thirtieth +thirties +thirty's +this +thistle +thistles +thistle's +thistledown +thistledown's +thither +tho +thole +tholes +thole's +thong +thongs +thong's +thoracic +thorax +thoraxes +thorax's +thorium +thorium's +thorn +thorns +thorn's +thorniness +thorniness's +thorny +thorniest +thornier +thorniness +thorough +thoroughly +thoroughest +thorougher +thoroughness +thoroughbred +thoroughbreds +thoroughbred's +thoroughfare +thoroughfares +thoroughfare's +thoroughgoing +thoroughness +thoroughness's +those +thou +thous +thou's +though +thought +thoughts +thought's +thoughtful +thoughtfully +thoughtfulness +thoughtfulness +thoughtfulness's +thoughtless +thoughtlessly +thoughtlessness +thoughtlessness +thoughtlessness's +thousand +thousandth +thousands +thousand's +thousandfold +thousandth +thousandth's +thousandths +thrall +thralling +thralled +thralls +thrall's +thralldom +thralldom's +thrash +thrashing +thrashings +thrashed +thrasher +thrashers +thrashes +thrash's +thrasher +thrasher's +thrashing +thrashing's +thread +threading +threaded +threader +threaders +threads +thread's +threadbare +threader +threader's +threadlike +thready +threadiest +threadier +threat +threaten +threatens +threats +threat's +threaten +threatening +threatened +threatening +threateningly +three +threes +three's +threefold +threepence +threepence's +threescore +threescores +threescore's +threesome +threesomes +threesome's +threnody +threnodies +threnody's +thresh +threshing +threshed +thresher +threshers +threshes +thresh's +thresher +thresher's +threshold +thresholds +threshold's +threw +thrice +thrift +thrifts +thrift's +thriftily +thriftiness +thriftiness's +thriftless +thrifty +thriftiest +thriftier +thriftiness +thrill +thrilling +thrilled +thriller +thrillers +thrills +thrill's +thriller +thriller's +thrilling +thrillingly +thrive +thriving +thrived +thrives +throat +throats +throat's +throatily +throatiness +throatiness's +throaty +throatiest +throatier +throatiness +throb +throbs +throb's +throbbed +throbbing +throe +throes +throe's +thrombi +thrombolytic +thromboses +thrombosis +thrombosis's +thrombotic +thrombus +thrombus's +throne's +throne +thrones +throng +thronging +thronged +throngs +throng's +throttle +throttling +throttled +throttler +throttlers +throttles +throttle's +throttler +throttler's +through +throughout +throughput +throughput's +throw +throwing +thrower +throwers +throws +throw's +throwaway +throwaways +throwaway's +throwback +throwbacks +throwback's +thrower +thrower's +thrown +thru +thrum +thrums +thrum's +thrummed +thrumming +thrush +thrushes +thrush's +thrust +thrusting +thrusts +thrust's +thruway +thruways +thruway's +thud +thuds +thud's +thudded +thudding +thug +thugs +thug's +thuggery +thuggery's +thuggish +thulium +thulium's +thumb +thumbing +thumbed +thumbs +thumb's +thumbnail +thumbnails +thumbnail's +thumbprint +thumbprints +thumbprint's +thumbscrew +thumbscrews +thumbscrew's +thumbtack +thumbtacks +thumbtack's +thump +thumping +thumped +thumps +thump's +thumping +thumping's +thunder +thundering +thundered +thunderer +thunderers +thunders +thunder's +thunderbolt +thunderbolts +thunderbolt's +thunderclap +thunderclaps +thunderclap's +thundercloud +thunderclouds +thundercloud's +thunderer +thunderer's +thunderhead +thunderheads +thunderhead's +thunderous +thunderously +thundershower +thundershowers +thundershower's +thunderstorm +thunderstorms +thunderstorm's +thunderstruck +thundery +thunk +thunks +thus +thwack +thwacking +thwacked +thwacker +thwackers +thwacks +thwack's +thwacker +thwacker's +thwart +thwarting +thwarted +thwarts +thwart's +thy +thyme +thyme's +thymine +thymine's +thymus +thymuses +thymus's +thyroid +thyroids +thyroid's +thyroidal +thyself +ti +tier +tiers +ti's +tiara +tiaras +tiara's +tibia +tibia's +tibiae +tibial +tic +tics +tic's +tick +ticking +ticked +ticker +tickers +ticks +tick's +ticker +ticker's +ticket +ticketing +ticketed +tickets +ticket's +ticking +ticking's +tickle +tickling +tickled +tickler +ticklers +tickles +tickle's +tickler +tickler's +ticklish +ticklishly +ticklishness +ticklishness +ticklishness's +ticktacktoe +ticktacktoe's +ticktock +ticktocks +ticktock's +tidal +tidally +tidbit +tidbits +tidbit's +tiddler +tiddlers +tiddly +tiddlywink +tiddlywinks +tiddlywinks +tiddlywinks's +tide +tiding +tidings +tided +tides +tide's +tideland +tidelands +tideland's +tidemark +tidemarks +tidewater +tidewaters +tidewater's +tideway +tideways +tideway's +tidily +untidily +tidiness +tidiness's +untidiness's +untidiness +tidings +tidings's +tidy +tidying +tidied +tidiest +tidier +tidies +tidiness +tidy's +tie's +tie +tied +ties +retied +untied +reties +unties +retie +untie +tieback +tiebacks +tieback's +tiebreak +tiebreaker +tiebreakers +tiebreaks +tiebreaker +tiebreaker's +tiepin +tiepins +tier +tiered +tier's +tiff +tiffing +tiffed +tiffs +tiff's +tiger +tigers +tiger's +tigerish +tight +tighten +tightens +tightly +tightest +tighter +tights +tightness +tighten +tightening +tightened +tightener +tighteners +tightener +tightener's +tightfisted +tightness +tightness's +tightrope +tightropes +tightrope's +tights +tights's +tightwad +tightwads +tightwad's +tigress +tigresses +tigress's +til +tilapia +tilde +tildes +tilde's +tile +tiling +tiled +tiler +tilers +tiles +tile's +tiler +tiler's +tiling +tiling's +till's +till +tilling +tilled +tiller +tillers +tills +distilling +distilled +distiller +distillers +distills +distill +tillable +tillage +tillage's +tiller +tiller's +distiller's +distiller +tilt +tilting +tilted +tilts +tilt's +timber +timbering +timbered +timbers +timber's +timberland +timberland's +timberline +timberlines +timberline's +timbre +timbres +timbre's +timbrel +timbrels +timbrel's +time +timely +timing +timings +timed +timer +timers +times +time's +timekeeper +timekeepers +timekeeper's +timekeeping +timekeeping's +timeless +timelessly +timelessness +timelessness +timelessness's +timeline +timelines +timeline's +timeliness +timeliness's +untimeliness's +untimeliness +timely +timeliest +timelier +timeliness +untimelier +untimeliness +untimely +timeout +timeouts +timeout's +timepiece +timepieces +timepiece's +timer +timer's +timescale +timescales +timeserver +timeservers +timeserver's +timeserving +timeserving's +timeshare +timeshares +timestamp +timestamped +timestamps +timestamp's +timetable +timetabling +timetabled +timetables +timetable's +timeworn +timezone +timid +timidly +timidest +timider +timidness +timidity +timidity's +timidness +timidness's +timing +timing's +timorous +timorously +timorousness +timorousness +timorousness's +timothy +timothy's +timpani +timpani's +timpanist +timpanists +timpanist's +tin +tins +tin's +tincture +tincturing +tinctured +tinctures +tincture's +tinder +tinder's +tinderbox +tinderboxes +tinderbox's +tine +tines +tine's +tinfoil +tinfoil's +ting +tingly +tinging +tinged +ting's +tinge +tinges +tinge's +tingeing +tingle +tingling +tinglings +tingled +tingles +tingle's +tingling +tingling's +tininess +tininess's +tinker +tinkering +tinkered +tinkerer +tinkerers +tinkers +tinker's +tinkerer +tinkerer's +tinkle +tinkling +tinkled +tinkles +tinkle's +tinned +tinniness +tinniness's +tinning +tinnitus +tinnitus's +tinny +tinniest +tinnier +tinniness +tinplate +tinplate's +tinpot +tinsel +tinseling +tinseled +tinsels +tinsel's +tinsmith +tinsmith's +tinsmiths +tint +tinting +tinted +tints +tint's +tintinnabulation +tintinnabulations +tintinnabulation's +tintype +tintypes +tintype's +tinware +tinware's +tiny +tiniest +tinier +tininess +tip +tips +tip's +tipped +tipper +tippers +tipper's +tippet +tippets +tippet's +tippex +tippexing +tippexed +tippexes +tipping +tipple +tippling +tippled +tippler +tipplers +tipples +tipple's +tippler +tippler's +tipsily +tipsiness +tipsiness's +tipster +tipsters +tipster's +tipsy +tipsiest +tipsier +tipsiness +tiptoe +tiptoed +tiptoes +tiptoe's +tiptoeing +tiptop +tiptops +tiptop's +tirade +tirades +tirade's +tiramisu +tiramisus +tiramisu's +tire's +tire +tiring +tired +tires +retiring +retired +retires +retire +tired +tiredly +tiredest +tireder +tiredness +tiredness +tiredness's +tireless +tirelessly +tirelessness +tirelessness +tirelessness's +tiresome +tiresomely +tiresomeness +tiresomeness +tiresomeness's +tissue +tissues +tissue's +tit +tits +tit's +titan +titans +titan's +titanic +titanium +titanium's +titch +titches +titchy +tithe +tithing +tithed +tither +tithers +tithes +tithe's +tither +tither's +titian +titian's +titillate +titillation +titillating +titillated +titillates +titillating +titillatingly +titillation +titillation's +titivate +titivation +titivating +titivated +titivates +titivation +titivation's +title +titling +titled +titles +title's +titled +untitled +titleholder +titleholders +titleholder's +titlist +titlists +titlist's +titmice +titmouse +titmouse's +titter +tittering +tittered +titters +titter's +tittle +tittles +tittle's +titty +titties +titular +tizz +tizzy +tizzies +tizzy's +tn +tnpk +to +into +unto +toad +toads +toad's +toadstool +toadstools +toadstool's +toady +toadying +toadied +toadies +toady's +toadyism +toadyism's +toast +toasting +toasted +toaster +toasters +toasts +toast's +toaster +toaster's +toastmaster +toastmasters +toastmaster's +toastmistress +toastmistresses +toastmistress's +toasty +toastiest +toastier +toasties +tobacco +tobaccos +tobacco's +tobacconist +tobacconists +tobacconist's +toboggan +tobogganing +tobogganed +tobogganer +tobogganers +toboggans +toboggan's +tobogganer +tobogganer's +tobogganing +tobogganing's +toccata +toccatas +tocsin +tocsins +tocsin's +today +today's +toddle +toddling +toddled +toddler +toddlers +toddles +toddle's +toddler +toddler's +toddy +toddies +toddy's +toe +toed +toes +toe's +toecap +toecaps +toecap's +toehold +toeholds +toehold's +toeing +toenail +toenails +toenail's +toerag +toerags +toff +toffs +toffee +toffees +toffee's +tofu +tofu's +tog +togs +tog's +toga +togaed +togas +toga's +together +togetherness +togetherness +togetherness's +togged +togging +toggle +toggling +toggled +toggles +toggle's +togs +togs's +toil +toiling +toiled +toiler +toilers +toils +toil's +toiler +toiler's +toilet +toileting +toileted +toilets +toilet's +toiletry +toiletries +toiletry's +toilette +toilette's +toilsome +toke +toking +toked +tokes +toke's +token +tokens +token's +tokenism +tokenism's +told +retold +untold +tole +tole's +tolerable +intolerable +tolerably +intolerably +tolerance +tolerance's +intolerance's +intolerance +tolerances +tolerant +tolerantly +intolerantly +intolerant +tolerate +toleration +tolerating +tolerated +tolerates +toleration +toleration's +toll +tolling +tolled +tolls +toll's +tollbooth +tollbooth's +tollbooths +tollgate +tollgates +tollgate's +tollway +tollways +tollway's +toluene +toluene's +tom +toms +tom's +tomahawk +tomahawking +tomahawked +tomahawks +tomahawk's +tomato +tomato's +tomatoes +tomb +tombing +tombed +tombs +tomb's +tombola +tombolas +tomboy +tomboys +tomboy's +tomboyish +tombstone +tombstones +tombstone's +tomcat +tomcats +tomcat's +tome +tomes +tome's +tomfoolery +tomfooleries +tomfoolery's +tomographic +tomography +tomography's +tomorrow +tomorrows +tomorrow's +tomtit +tomtits +tomtit's +ton +tons +ton's +tonal +tonally +tonality +tonalities +tonality's +tone's +tone +toning +toned +toner +toners +tones +intoning +intoned +intoner +intoners +intones +intone +tonearm +tonearms +tonearm's +toneless +tonelessly +toner +toner's +intoner's +intoner +tong +tonging +tonged +tongs +tong's +tongue +tonguing +tongued +tongues +tongue's +tongueless +tonic +tonics +tonic's +tonight +tonight's +tonnage +tonnages +tonnage's +tonne +tonnes +tonne's +tonsil +tonsils +tonsil's +tonsillectomy +tonsillectomies +tonsillectomy's +tonsillitis +tonsillitis's +tonsorial +tonsure +tonsuring +tonsured +tonsures +tonsure's +tony +toniest +tonier +too +took +retook +tool's +tool +tooling +tooled +tools +retooling +retooled +retools +retool +toolbar +toolbars +toolbar's +toolbox +toolboxes +toolbox's +toolkit +toolmaker +toolmakers +toolmaker's +toot +tooting +tooted +tooter +tooters +toots +toot's +tooter +tooter's +tooth +toothed +tooth's +toothache +toothaches +toothache's +toothbrush +toothbrushes +toothbrush's +toothily +toothless +toothpaste +toothpastes +toothpaste's +toothpick +toothpicks +toothpick's +toothsome +toothy +toothiest +toothier +tootle +tootling +tootled +tootles +tootsie +tootsies +top +tops +top's +topaz +topazes +topaz's +topcoat +topcoats +topcoat's +topdressing +topdressings +topdressing's +topee +topees +topflight +topi +topiary +topiary's +topic +topics +topic's +topical +topically +topicality +topicality's +topknot +topknots +topknot's +topless +topmast +topmasts +topmast's +topmost +topnotch +topographer +topographers +topographer's +topographic +topographical +topographically +topography +topographies +topography's +topological +topologically +topology +topped +topper +toppers +topper's +topping +toppings +topping's +topple +toppling +toppled +topples +topsail +topsails +topsail's +topside +topsides +topside's +topsoil +topsoil's +topspin +topspin's +toque +toques +toque's +tor +tors +tor's +torch +torching +torched +torches +torch's +torchbearer +torchbearers +torchbearer's +torchlight +torchlight's +tore +toreador +toreadors +toreador's +torment +tormenting +tormented +torments +torment's +tormenting +tormentingly +tormentor +tormentors +tormentor's +torn +tornado +tornado's +tornadoes +torpedo +torpedoing +torpedoed +torpedo's +torpedoes +torpid +torpidly +torpidity +torpidity's +torpor +torpor's +torque +torquing +torqued +torques +torque's +torrent +torrents +torrent's +torrential +torrid +torridly +torridness +torridity +torridity's +torridness +torridness's +torsion +torsion's +torsional +torso +torsos +torso's +tort's +tort +torts +retorts +distorts +contorts +retort +distort +contort +torte +tortes +torte's +tortellini +tortellini's +tortilla +tortillas +tortilla's +tortoise +tortoises +tortoise's +tortoiseshell +tortoiseshells +tortoiseshell's +tortoni +tortoni's +tortuous +tortuously +tortuousness +tortuousness +tortuousness's +torture +torturing +tortured +torturer +torturers +tortures +torture's +torturer +torturer's +torturous +torus +tosh +toss +tossing +tossed +tosser +tossers +tosses +toss's +tossup +tossups +tossup's +tot +toting +toted +tots +tot's +total +totally +totaling +totaled +totals +total's +totalitarian +totalitarians +totalitarian's +totalitarianism +totalitarianism's +totality +totalities +totality's +totalizator +totalizators +totalizator's +tote +totes +tote's +totem +totems +totem's +totemic +totted +totter +tottering +tottered +totterer +totterers +totters +totter's +totterer +totterer's +totting +toucan +toucans +toucan's +touch +touching +touched +touches +touch's +retouching +retouched +retouches +retouch's +retouch +touchdown +touchdowns +touchdown's +touche +touchings +touchable +touched +untouched +touchily +touchiness +touchiness's +touching +touchingly +touchline +touchlines +touchpaper +touchpapers +touchscreen +touchscreens +touchscreen's +touchstone +touchstones +touchstone's +touchy +touchiest +touchier +touchiness +tough +toughen +toughens +toughly +toughing +toughed +toughest +tougher +toughness +tough's +toughen +toughening +toughened +toughener +tougheners +toughener +toughener's +toughie +toughies +toughie's +toughness +toughness's +toughs +toupee +toupees +toupee's +tour +touring +toured +tours +tour's +detouring +contouring +detoured +contoured +detours +contours +detour's +contour's +detour +contour +tourism +tourism's +tourist +tourists +tourist's +touristic +touristy +tourmaline +tourmaline's +tournament +tournaments +tournament's +tourney +tourneys +tourney's +tourniquet +tourniquets +tourniquet's +tousle +tousling +tousled +tousles +tout +touting +touted +touts +tout's +tow +towing +towed +tower +towers +tows +tow's +toward +towards +towboat +towboats +towboat's +towel +toweling +towelings +toweled +towels +towel's +towelette +towelettes +towelette's +toweling +toweling's +tower +towering +towered +tower's +towhead +towheaded +towheads +towhead's +towhee +towhees +towhee's +towline +towlines +towline's +town +towns +town's +townee +townees +townhouse +townhouses +townhouse's +townie +townies +townie's +townsfolk +townsfolk's +township +townships +township's +townsman +townsman's +townsmen +townspeople +townspeople's +townswoman +townswoman's +townswomen +towpath +towpath's +towpaths +towrope +towropes +towrope's +toxemia +toxemia's +toxic +toxicity +toxicities +toxicity's +toxicological +toxicologist +toxicologists +toxicologist's +toxicology +toxicology's +toxin +toxins +toxin's +toy +toying +toyed +toys +toy's +toyboy +toyboys +tr +trabecula +trabecular +trabecule +trace +tracing +tracings +traced +tracer +tracers +traces +trace's +traceability +traceable +untraceable +tracer +tracer's +tracery +traceries +tracery's +trachea +trachea's +tracheae +tracheal +tracheotomy +tracheotomies +tracheotomy's +tracing +tracing's +track +tracking +tracked +tracker +trackers +tracks +track's +trackball +trackballs +trackball's +tracker +tracker's +trackless +tracksuit +tracksuits +tract's +tract +tracts +retracts +detracts +distracts +contracts +protracts +retract +detract +distract +contract +protract +tractability +tractability's +intractability's +intractability +tractable +intractable +tractably +intractably +traction +traction's +retraction's +detraction's +distraction's +contraction's +protraction's +retraction +detraction +distraction +contraction +protraction +tractor +tractors +tractor's +detractors +contractors +protractors +detractor's +contractor's +protractor's +detractor +contractor +protractor +trad +trade +trading +tradings +traded +trader +traders +trades +trade's +trademark +trademarking +trademarked +trademarks +trademark's +trader +trader's +tradesman +tradesman's +tradesmen +tradespeople +tradespeople's +tradeswoman +tradeswoman's +tradeswomen +trading +trading's +tradition +traditions +tradition's +traditional +traditionally +traditionalism +traditionalism's +traditionalist +traditionalists +traditionalist's +traduce +traducing +traduced +traducer +traducers +traduces +traducer +traducer's +traffic +traffics +traffic's +trafficked +trafficker +traffickers +trafficker's +trafficking +trafficking's +tragedian +tragedians +tragedian's +tragedienne +tragediennes +tragedienne's +tragedy +tragedies +tragedy's +tragic +tragically +tragicomedy +tragicomedies +tragicomedy's +tragicomic +trail +trailing +trailed +trailer +trailers +trails +trail's +trailblazer +trailblazers +trailblazer's +trailblazing +trailblazing's +trailer +trailer's +train +training +trained +trainer +trainers +trains +train's +trainable +trained +untrained +trainee +trainees +trainee's +trainer +trainer's +training +training's +trainload +trainloads +trainload's +trainman +trainman's +trainmen +trainspotter +trainspotters +trainspotting +traipse +traipsing +traipsed +traipses +traipse's +trait +traits +trait's +traitor +traitors +traitor's +traitorous +traitorously +trajectory +trajectories +trajectory's +tram +trams +tram's +tramcar +tramcars +tramlines +trammed +trammel +trammeling +trammeled +trammels +trammel's +trammeled +untrammeled +tramming +tramp +tramping +tramped +tramper +trampers +tramps +tramp's +tramper +tramper's +trample +trampling +trampled +trampler +tramplers +tramples +trample's +trampler +trampler's +trampoline +trampolining +trampolined +trampolines +trampoline's +tramway +tramways +trance +trances +trance's +tranche +tranches +tranquil +tranquilly +tranquilest +tranquiler +tranquility +tranquility's +tranquilize +tranquilizing +tranquilized +tranquilizer +tranquilizers +tranquilizes +tranquilizer +tranquilizer's +trans +intrans +transact +transacting +transacted +transacts +transaction +transactions +transaction's +transactor +transactors +transactor's +transatlantic +transceiver +transceivers +transceiver's +transcend +transcending +transcended +transcends +transcendence +transcendence's +transcendent +transcendental +transcendentally +transcendentalism +transcendentalism's +transcendentalist +transcendentalists +transcendentalist's +transcontinental +transcribe +transcribing +transcribed +transcriber +transcribers +transcribes +transcriber +transcriber's +transcript +transcripts +transcript's +transcription +transcriptions +transcription's +transducer +transducers +transducer's +transduction +transect +transecting +transected +transects +transept +transepts +transept's +transfer +transfers +transfer's +transferable +transferal +transferals +transferal's +transference +transference's +transferred +transferring +transfiguration +transfiguration's +transfigure +transfiguring +transfigured +transfigures +transfinite +transfix +transfixing +transfixed +transfixes +transform +transforming +transformed +transformer +transformers +transforms +transform's +transformable +transformation +transformations +transformation's +transformer +transformer's +transfuse +transfusion +transfusions +transfusing +transfused +transfuses +transfusion +transfusion's +transgender +transgenders +transgenic +transgress +transgressing +transgressed +transgresses +transgression +transgressions +transgression's +transgressor +transgressors +transgressor's +transience +transience's +transiency +transiency's +transient +transiently +transients +transient's +transistor +transistors +transistor's +transistorize +transistorizing +transistorized +transistorizes +transit +transiting +transited +transits +transit's +transition +transitioning +transitioned +transitions +transition's +transitional +transitionally +transitive +transitively +transitives +transitive's +intransitively +intransitives +intransitive's +intransitive +transitiveness +transitiveness's +transitivity +transitivity's +transitory +transl +translatable +untranslatable +translate +translation +translations +translating +translated +translates +translatable +translated +untranslated +translation +translation's +translator +translators +translator's +transliterate +transliteration +transliterations +transliterating +transliterated +transliterates +transliteration +transliteration's +translocation +translucence +translucence's +translucency +translucency's +translucent +translucently +transmigrate +transmigration +transmigrating +transmigrated +transmigrates +transmigration +transmigration's +transmissible +transmission +transmissions +transmission's +transmit +transmits +transmittable +transmittal +transmittal's +transmittance +transmittance's +transmitted +transmitter +transmitters +transmitter's +transmitting +transmogrification +transmogrification's +transmogrify +transmogrification +transmogrifying +transmogrified +transmogrifies +transmutation +transmutations +transmutation's +transmute +transmuting +transmuted +transmutes +transmutable +transnational +transnationals +transnational's +transoceanic +transom +transoms +transom's +transpacific +transparency +transparencies +transparency's +transparent +transparently +transpiration +transpiration's +transpire +transpiring +transpired +transpires +transplant +transplanting +transplanted +transplants +transplant's +transplantation +transplantation's +transpolar +transponder +transponders +transponder's +transport +transporting +transported +transporter +transporters +transports +transport's +transportable +transportation +transportation's +transporter +transporter's +transpose +transposing +transposed +transposes +transposition +transpositions +transposition's +transsexual +transsexuals +transsexual's +transsexualism +transsexualism's +transship +transships +transshipment +transshipment +transshipment's +transshipped +transshipping +transubstantiation +transubstantiation's +transversal +transverse +transversely +transverses +transverse's +transvestism +transvestism's +transvestite +transvestites +transvestite's +trap +traps +trap's +trapdoor +trapdoors +trapdoor's +trapeze +trapezes +trapeze's +trapezium +trapeziums +trapezium's +trapezoid +trapezoids +trapezoid's +trapezoidal +trappable +trapped +trapper +trappers +trapper's +trapping +trappings +trappings +trappings's +trapshooting +trapshooting's +trash +trashing +trashed +trashes +trash's +trashcan +trashcans +trashcan's +trashiness +trashiness's +trashy +trashiest +trashier +trashiness +trauma +traumas +trauma's +traumatic +traumatically +traumatize +traumatizing +traumatized +traumatizes +travail +travailing +travailed +travails +travail's +travel +traveling +travelings +traveled +traveler +travelers +travels +travel's +traveled +untraveled +traveler +traveler's +traveling +traveling's +travelogue +travelogues +travelogue's +traversal +traversals +traversal's +traverse +traversing +traversed +traverses +traverse's +travesty +travestying +travestied +travesties +travesty's +trawl +trawling +trawled +trawler +trawlers +trawls +trawl's +trawler +trawler's +tray +trays +tray's +treacherous +treacherously +treacherousness +treacherousness +treacherousness's +treachery +treacheries +treachery's +treacle +treacle's +treacly +tread +treading +treads +tread's +retreading +retreads +retread's +retread +treadle +treadling +treadled +treadles +treadle's +treadmill +treadmills +treadmill's +treas +treason +treason's +treasonable +treasonous +treasure +treasuring +treasured +treasurer +treasurers +treasures +treasure's +treasurer +treasurer's +treasury +treasuries +treasury's +treat +treating +treated +treats +treat's +retreating +retreated +retreats +retreat's +retreat +treatable +treated +untreated +treatise +treatises +treatise's +treatment +treatments +treatment's +treaty +treaties +treaty's +treble +trebling +trebled +trebles +treble's +tree +treed +trees +tree's +treeing +treeless +treelike +treeline +treetop +treetops +treetop's +trefoil +trefoils +trefoil's +trek +treks +trek's +trekked +trekker +trekkers +trekker's +trekking +trellis +trellising +trellised +trellises +trellis's +trematode +trematodes +trematode's +tremble +trembling +trembled +trembles +tremble's +tremendous +tremendously +tremolo +tremolos +tremolo's +tremor +tremors +tremor's +tremulous +tremulously +tremulousness +tremulousness +tremulousness's +trench's +trench +trenching +trenched +trenches +retrenching +retrenched +retrenches +retrench +trenchancy +trenchancy's +trenchant +trenchantly +trencher +trenchers +trencher's +trencherman +trencherman's +trenchermen +trend +trending +trended +trends +trend's +trendily +trendiness +trendiness's +trendsetter +trendsetters +trendsetting +trendy +trendiest +trendier +trendies +trendiness +trendy's +trepidation +trepidation's +trespass +trespassing +trespassed +trespasser +trespassers +trespasses +trespass's +trespasser +trespasser's +tress +tresses +tress's +distresses +distress's +distress +trestle +trestles +trestle's +trews +trey +treys +trey's +triad +triads +triad's +triage +triage's +trial +trials +trial's +retrials +retrial's +retrial +trialed +trialing +triangle +triangles +triangle's +triangular +triangularly +triangulate +triangulation +triangulating +triangulated +triangulates +triangulation +triangulation's +triathlete +triathletes +triathlon +triathlons +triathlon's +tribal +tribalism +tribalism's +tribe +tribes +tribe's +tribesman +tribesman's +tribesmen +tribeswoman +tribeswoman's +tribeswomen +tribulation +tribulations +tribulation's +tribunal +tribunals +tribunal's +tribune +tribunes +tribune's +tributary +tributaries +tributary's +tribute's +tribute +tributes +contributes +contribute +trice +trice's +tricentennial +tricentennials +tricentennial's +triceps +tricepses +triceps's +triceratops +triceratops's +trichina +trichina's +trichinae +trichinosis +trichinosis's +trick +tricking +tricked +tricks +trick's +trickery +trickery's +trickily +trickiness +trickiness's +trickle +trickling +trickled +trickles +trickle's +trickster +tricksters +trickster's +tricky +trickiest +trickier +trickiness +tricolor +tricolors +tricolor's +tricycle +tricycles +tricycle's +trident +tridents +trident's +tried +untried +triennial +triennially +triennials +triennial's +trier +triers +trier's +trifecta +trifectas +trifecta's +trifle +trifling +trifled +trifler +triflers +trifles +trifle's +trifler +trifler's +trifocals +trifocals's +trig +trig's +trigger +triggering +triggered +triggers +trigger's +triglyceride +triglycerides +triglyceride's +trigonometric +trigonometrical +trigonometry +trigonometry's +trike +trikes +trike's +trilateral +trilaterals +trilby +trilbies +trilby's +trill +trilling +trilled +trills +trill's +trillion +trillionth +trillions +trillion's +trillionth +trillionth's +trillionths +trillium +trillium's +trilobite +trilobites +trilobite's +trilogy +trilogies +trilogy's +trim +trimly +trims +trimness +trim's +trimaran +trimarans +trimaran's +trimester +trimesters +trimester's +trimmed +untrimmed +trimmer +trimmers +trimmer's +trimmest +trimming +trimmings +trimming's +trimmings +trimmings's +trimness +trimness's +trimonthly +trinitrotoluene +trinitrotoluene's +trinity +trinities +trinity's +trinket +trinkets +trinket's +trio +trios +trio's +trip +triply +trips +trip's +tripartite +tripe +tripe's +triple +tripling +tripled +triples +triple's +triplet +triplets +triplet's +triplex +triplexes +triplex's +triplicate +triplicating +triplicated +triplicates +triplicate's +tripod +tripods +tripod's +tripodal +tripos +tripped +tripper +trippers +tripper's +tripping +triptych +triptych's +triptychs +tripwire +tripwires +trireme +triremes +trireme's +trisect +trisecting +trisected +trisects +trisection +trisection's +trite +tritely +tritest +triteness +contritely +contriteness +contrite +triteness +triteness's +contriteness's +contriteness +triter +tritium +tritium's +triumph +triumphing +triumphed +triumph's +triumphal +triumphalism +triumphalist +triumphant +triumphantly +triumphs +triumvir +triumvirs +triumvir's +triumvirate +triumvirates +triumvirate's +trivalent +trivet +trivets +trivet's +trivia +trivia's +trivial +trivially +triviality +trivialities +triviality's +trivialization +trivialization's +trivialize +trivializing +trivialized +trivializes +trivium +trivium's +trochaic +trochee +trochees +trochee's +trod +retrod +untrod +trodden +retrodden +troglodyte +troglodytes +troglodyte's +troika +troikas +troika's +troll +trolling +trolled +trolls +troll's +trolley +trolleys +trolley's +trolleybus +trolleybuses +trolleybus's +trollop +trollops +trollop's +trombone +trombones +trombone's +trombonist +trombonists +trombonist's +tromp +tromping +tromped +tromps +tron +trons +troop +trooping +trooped +trooper +troopers +troops +troop's +trooper +trooper's +troopship +troopships +troopship's +trope +tropes +trope's +trophy +trophies +trophy's +tropic +tropics +tropic's +tropical +tropically +tropics +tropics's +tropism +tropisms +tropism's +troposphere +tropospheres +troposphere's +trot +trots +trot's +troth +troth's +trotted +trotter +trotters +trotter's +trotting +troubadour +troubadours +troubadour's +trouble +troubling +troubled +troubles +trouble's +troubled +untroubled +troublemaker +troublemakers +troublemaker's +troubleshoot +troubleshooting +troubleshooted +troubleshooter +troubleshooters +troubleshoots +troubleshooter +troubleshooter's +troubleshooting +troubleshooting's +troubleshot +troublesome +troublesomely +trough +trough's +troughs +trounce +trouncing +trounced +trouncer +trouncers +trounces +trouncer +trouncer's +troupe +trouping +trouped +trouper +troupers +troupes +troupe's +trouper +trouper's +trouser +trousers +trouser's +trousers +trousers's +trousseau +trousseau's +trousseaux +trout +trouts +trout's +trove +troves +trove's +trow +trowing +trowed +trows +trowel +troweling +troweled +trowels +trowel's +troy +troys +truancy +truancy's +truant +truanting +truanted +truants +truant's +truce +truces +truce's +truck +trucking +trucked +trucker +truckers +trucks +truck's +trucker +trucker's +trucking +trucking's +truckle +truckling +truckled +truckles +truckle's +truckload +truckloads +truckload's +truculence +truculence's +truculent +truculently +trudge +trudging +trudged +trudges +trudge's +true +truing +trued +truest +truer +trues +true's +truelove +trueloves +truelove's +truffle +truffles +truffle's +trug +trugs +truism +truisms +truism's +truly +untruly +trump +trumping +trumped +trumps +trump's +trumpery +trumpery's +trumpet +trumpeting +trumpeted +trumpeter +trumpeters +trumpets +trumpet's +trumpeter +trumpeter's +truncate +truncation +truncating +truncated +truncates +truncation +truncation's +truncheon +truncheons +truncheon's +trundle +trundling +trundled +trundler +trundlers +trundles +trundle's +trundler +trundler's +trunk +trunking +trunks +trunk's +truss +trussing +trussed +trusses +truss's +trust +trusting +trusted +trusts +trust's +distrusting +distrusted +distrusts +distrust's +distrust +trustee +trustees +trustee's +trusteeship +trusteeships +trusteeship's +trustful +trustfully +distrustfully +distrustful +trustfulness +trustfulness's +trusting +trustingly +trustworthiness +trustworthiness's +trustworthy +trustworthiest +trustworthier +trustworthiness +trusty +trustiest +trustier +trusties +trusty's +truth +truther +truthers +truth's +truther +truther's +truthful +truthfully +truthfulness +untruthfully +untruthfulness +untruthful +truthfulness +truthfulness's +untruthfulness's +untruthfulness +truthiness +truths +untruths +try's +try +trying +tried +tries +retrying +retried +retries +retry +trying +tryingly +tryout +tryouts +tryout's +tryptophan +tryst +trysting +trysted +trysts +tryst's +tsarists +tsetse +tsetses +tsetse's +tsp +tsunami +tsunamis +tsunami's +ttys +tub +tubing +tubed +tuber +tubers +tubs +tub's +tuba +tubas +tuba's +tubal +tubby +tubbiest +tubbier +tube +tubes +tube's +tubeless +tubeless's +tuber +tuber's +tubercle +tubercles +tubercle's +tubercular +tuberculin +tuberculin's +tuberculosis +tuberculosis's +tuberculous +tuberose +tuberose's +tuberous +tubful +tubfuls +tubful's +tubing +tubing's +tubular +tubule +tubules +tubule's +tuck +tucking +tucked +tucker +tuckers +tucks +tuck's +tucker +tuckering +tuckered +tucker's +tuft +tufting +tufted +tufter +tufters +tufts +tuft's +tufter +tufter's +tug +tugs +tug's +tugboat +tugboats +tugboat's +tugged +tugging +tuition +tuition's +intuition's +intuition +tularemia +tularemia's +tulip +tulips +tulip's +tulle +tulle's +tum +tums +tumble +tumbling +tumbled +tumbler +tumblers +tumbles +tumble's +tumbledown +tumbler +tumbler's +tumbleweed +tumbleweeds +tumbleweed's +tumbling +tumbling's +tumbrel +tumbrels +tumbrel's +tumescence +tumescence's +tumescent +tumid +tumidity +tumidity's +tummy +tummies +tummy's +tumor +tumors +tumor's +tumorous +tumult +tumults +tumult's +tumultuous +tumultuously +tun +tuning +tuned +tuner +tuners +tuns +tun's +tuna +tunas +tuna's +tundra +tundras +tundra's +tune +tunes +tune's +tuneful +tunefully +tunefulness +tunefulness +tunefulness's +tuneless +tunelessly +tuner +tuner's +tuneup +tuneups +tuneup's +tungsten +tungsten's +tunic +tunics +tunic's +tunnel +tunneling +tunnelings +tunneled +tunneler +tunnelers +tunnels +tunnel's +tunneler +tunneler's +tunny +tunnies +tunny's +tuple +tuples +tuppence +tuppenny +tuque +tuques +tuque's +turban +turbaned +turbans +turban's +turbid +turbidity +turbidity's +turbine +turbines +turbine's +turbo +turbos +turbo's +turbocharge +turbocharging +turbocharged +turbocharger +turbochargers +turbocharges +turbocharger +turbocharger's +turbofan +turbofans +turbofan's +turbojet +turbojets +turbojet's +turboprop +turboprops +turboprop's +turbot +turbots +turbot's +turbulence +turbulence's +turbulent +turbulently +turd +turds +turd's +turducken +turduckens +turducken's +tureen +tureens +tureen's +turf +turfing +turfed +turfs +turf's +turfy +turgid +turgidly +turgidity +turgidity's +turkey +turkeys +turkey's +turmeric +turmerics +turmeric's +turmoil +turmoils +turmoil's +turn +turning +turned +turner +turners +turns +turn's +returning +returned +returner +returners +returns +return's +return +turnabout +turnabouts +turnabout's +turnaround +turnarounds +turnaround's +turnbuckle +turnbuckles +turnbuckle's +turncoat +turncoats +turncoat's +turner +turner's +returner's +returner +turning +turnings +turning's +turnip +turnips +turnip's +turnkey +turnkeys +turnkey's +turnoff +turnoffs +turnoff's +turnout +turnouts +turnout's +turnover +turnovers +turnover's +turnpike +turnpikes +turnpike's +turnstile +turnstiles +turnstile's +turntable +turntables +turntable's +turpentine +turpentine's +turpitude +turpitude's +turps +turquoise +turquoises +turquoise's +turret +turreted +turrets +turret's +turtle +turtles +turtle's +turtledove +turtledoves +turtledove's +turtleneck +turtlenecked +turtlenecks +turtleneck's +tush +tushes +tush's +tusk +tusked +tusks +tusk's +tussle +tussling +tussled +tussles +tussle's +tussock +tussocks +tussock's +tussocky +tut +tuts +tut's +tutelage +tutelage's +tutelary +tutor +tutoring +tutored +tutors +tutor's +tutored +untutored +tutorial +tutorials +tutorial's +tutorship +tutorship's +tutted +tutti +tuttis +tutti's +tutting +tutu +tutus +tutu's +tux +tuxes +tux's +tuxedo +tuxedos +tuxedo's +twaddle +twaddling +twaddled +twaddler +twaddlers +twaddles +twaddle's +twaddler +twaddler's +twain +twain's +twang +twanging +twanged +twangs +twang's +twangy +twangiest +twangier +twas +twat +twats +tweak +tweaking +tweaked +tweaks +tweak's +twee +tweed +tweeds +tweed's +tweeds +tweeds's +tweedy +tweediest +tweedier +tween +tweet's +tweet +tweeting +tweeted +tweets +retweeting +retweeted +retweets +retweet +tweeter +tweeters +tweeter's +tweezers +tweezers's +twelfth +twelfth's +twelfths +twelve +twelves +twelve's +twelvemonth +twelvemonth's +twelvemonths +twentieth +twentieth's +twentieths +twenty +twentieth +twenties +twenty's +twerk +twerking +twerked +twerks +twerp +twerps +twerp's +twice +twiddle +twiddling +twiddled +twiddles +twiddle's +twiddly +twig +twigs +twig's +twigged +twigging +twiggy +twiggiest +twiggier +twilight +twilight's +twilit +twill +twilled +twill's +twin +twining +twined +twiner +twiners +twins +twin's +twine +twines +twine's +twiner +twiner's +twinge +twinging +twinged +twinges +twinge's +twink +twinkly +twinks +twinkle +twinkling +twinklings +twinkled +twinkles +twinkle's +twinkling +twinkling's +twinned +twinning +twinset +twinsets +twirl +twirling +twirled +twirler +twirlers +twirls +twirl's +twirler +twirler's +twirly +twist's +twist +twisting +twisted +twists +untwisting +untwisted +untwists +untwist +twister +twisters +twister's +twisty +twistiest +twistier +twit +twits +twit's +twitch +twitching +twitched +twitches +twitch's +twitchy +twitchiest +twitchier +twitted +twitter +twittering +twittered +twitters +twitter's +twittery +twitting +twixt +two +twos +two's +twofer +twofers +twofer's +twofold +twopence +twopences +twopence's +twopenny +twosome +twosomes +twosome's +twp +tycoon +tycoons +tycoon's +tying +retying +untying +tyke +tykes +tyke's +tympani +tympani's +tympanic +tympanist +tympanists +tympanist's +tympanum +tympanums +tympanum's +type's +type +typing +typed +types +retyping +retyped +retypes +retype +typecast +typecasting +typecasts +typeface +typefaces +typeface's +typescript +typescripts +typescript's +typeset +typesets +typesetter +typesetters +typesetter's +typesetting +typesetting's +typewrite +typewriting +typewriter +typewriters +typewrites +typewriter +typewriter's +typewriting +typewriting's +typewritten +typewrote +typhoid +typhoid's +typhoon +typhoons +typhoon's +typhus +typhus's +typical +typically +untypically +untypical +typicality +typicality's +typification +typification's +typify +typification +typifying +typified +typifies +typing +typing's +typist +typists +typist's +typo +typos +typo's +typographer +typographers +typographer's +typographic +typographical +typographically +typography +typography's +typology +typologies +typology's +tyrannic +tyrannical +tyrannically +tyrannicidal +tyrannicide +tyrannicides +tyrannize +tyrannizing +tyrannized +tyrannizes +tyrannosaur +tyrannosaurs +tyrannosaur's +tyrannosaurus +tyrannosauruses +tyrannosaurus's +tyrannous +tyranny +tyrannies +tyranny's +tyrant +tyrants +tyrant's +tyro +tyros +tyro's +tzatziki +u +us +ubiquitous +ubiquitously +ubiquity +ubiquity's +udder +udders +udder's +ufologist +ufologists +ufologist's +ufology +ufology's +ugh +ugliness +ugliness's +ugly +ugliest +uglier +ugliness +uh +uhf +ukase +ukases +ukase's +ukulele +ukuleles +ukulele's +ulcer +ulcers +ulcer's +ulcerate +ulceration +ulcerations +ulcerating +ulcerated +ulcerates +ulceration +ulceration's +ulcerous +ulna +ulna's +ulnae +ulnar +ulster +ulsters +ulster's +ult +ulterior +ultimate +ultimately +ultimate's +ultimatum +ultimatums +ultimatum's +ultimo +ultra +ultras +ultra's +ultraconservative +ultraconservatives +ultraconservative's +ultrahigh +ultralight +ultralights +ultralight's +ultramarine +ultramarine's +ultramodern +ultrasensitive +ultrashort +ultrasonic +ultrasonically +ultrasound +ultrasounds +ultrasound's +ultraviolet +ultraviolet's +ululate +ululation +ululations +ululating +ululated +ululates +ululation +ululation's +um +umbel +umbels +umbel's +umber +umber's +umbilical +umbilici +umbilicus +umbilicus's +umbra +umbras +umbra's +umbrage +umbrage's +umbrella +umbrellas +umbrella's +umiak +umiaks +umiak's +umlaut +umlauts +umlaut's +ump +umping +umped +umps +ump's +umpire +umpiring +umpired +umpires +umpire's +umpteen +umpteenth +unabridged +unabridgeds +unabridged's +unacceptability +unacceptable +unaccommodating +unaccountably +unadventurous +unaesthetic +unalterably +unambitious +unanimity +unanimity's +unanimous +unanimously +unapparent +unappetizing +unappreciative +unassertive +unassuming +unassumingly +unavailing +unavailingly +unaware +unawares +unbeknownst +unbend +unbending +unbends +unbent +unbid +unblinking +unblinkingly +unblushing +unblushingly +unbosom +unbosoming +unbosomed +unbound +unbounded +unbox +unboxing +unboxed +unboxes +unbreakable +unbroken +uncanny +uncanniest +uncap +uncaps +uncaring +uncatalogued +unceasing +unceasingly +unchangeable +uncharacteristic +uncharitable +unchaste +unchastest +unchaster +uncial +uncial's +uncle +uncles +uncle's +unclean +uncleaned +uncleanest +uncleaner +uncleanness +uncleanly +uncleanliest +unclear +uncleared +unclearest +unclearer +uncomfortable +uncommon +uncommonest +uncompelling +uncomplaining +uncomplainingly +uncomplicated +uncomprehending +uncomprehendingly +uncompromising +uncompromisingly +unconditional +unconditionally +uncongenial +unconscionable +unconscionably +unconscious +unconscious's +unconstitutional +unconstitutionally +uncontrollably +uncontroversial +uncool +uncooperative +uncouth +uncouthly +uncrushable +unction +unctions +unction's +unctuous +unctuously +unctuousness +unctuousness +unctuousness's +uncut +undaunted +undauntedly +undecided +undecideds +undecided's +undemonstrative +undemonstratively +undeniably +under +underachieve +underachieving +underachieved +underachiever +underachievers +underachieves +underachievement +underachiever +underachiever's +underact +underacting +underacted +underacts +underage +underappreciated +underarm +underarms +underarm's +underbelly +underbellies +underbelly's +underbid +underbids +underbidding +underbrush +underbrush's +undercarriage +undercarriages +undercarriage's +undercharge +undercharging +undercharged +undercharges +undercharge's +underclass +underclasses +underclass's +underclassman +underclassman's +underclassmen +underclothes +underclothes's +underclothing +underclothing's +undercoat +undercoating +undercoatings +undercoated +undercoats +undercoat's +undercoating +undercoating's +undercover +undercurrent +undercurrents +undercurrent's +undercut +undercuts +undercut's +undercutting +underdeveloped +underdevelopment +underdevelopment's +underdog +underdogs +underdog's +underdone +underemployed +underemployment +underemployment's +underestimate +underestimation +underestimations +underestimating +underestimated +underestimates +underestimate's +underestimation +underestimation's +underexpose +underexposing +underexposed +underexposes +underexposure +underexposures +underexposure's +underfed +underfeed +underfeeding +underfeeds +underfloor +underflow +underfoot +underfunded +underfur +underfur's +undergarment +undergarments +undergarment's +undergo +undergoing +undergoes +undergone +undergrad +undergrads +undergraduate +undergraduates +undergraduate's +underground +undergrounds +underground's +undergrowth +undergrowth's +underhand +underhanded +underhandedly +underhandedness +underhandedness +underhandedness's +underinflated +underlain +underlay +underlays +underlay's +underlie +underlies +underline +underlining +underlined +underlines +underline's +underling +underlings +underling's +underlip +underlips +underlip's +underlying +undermanned +undermentioned +undermine +undermining +undermined +undermines +undermost +underneath +underneath's +underneaths +undernourished +undernourishment +undernourishment's +underpaid +underpants +underpants's +underpart +underparts +underpart's +underpass +underpasses +underpass's +underpay +underpaying +underpays +underpayment +underpayment +underpayments +underpayment's +underpin +underpins +underpinned +underpinning +underpinnings +underpinning's +underplay +underplaying +underplayed +underplays +underpopulated +underprivileged +underproduction +underproduction's +underrate +underrating +underrated +underrates +underrepresented +underscore +underscoring +underscored +underscores +underscore's +undersea +underseas +undersecretary +undersecretaries +undersecretary's +undersell +underselling +undersells +undersexed +undershirt +undershirts +undershirt's +undershoot +undershooting +undershoots +undershorts +undershorts's +undershot +underside +undersides +underside's +undersign +undersigning +undersigned +undersigns +undersigned +undersigned's +undersized +underskirt +underskirts +underskirt's +undersold +understaffed +understand +understanding +understandings +understands +understandable +understandably +understanding +understandingly +understanding's +understate +understating +understated +understates +understatement +understatement +understatements +understatement's +understood +understudy +understudying +understudied +understudies +understudy's +undertake +undertaking +undertakings +undertaker +undertakers +undertakes +undertaken +undertaker +undertaker's +undertaking +undertaking's +underthings +underthings's +undertone +undertones +undertone's +undertook +undertow +undertows +undertow's +underused +underutilized +undervaluation +undervaluation's +undervalue +undervaluing +undervalued +undervalues +underwater +underway +underwear +underwear's +underweight +underweight's +underwent +underwhelm +underwhelming +underwhelmed +underwhelms +underwire +underwired +underwires +underworld +underworlds +underworld's +underwrite +underwriting +underwriter +underwriters +underwrites +underwriter +underwriter's +underwritten +underwrote +undesirable +undesirables +undesirable's +undies +undies's +undo +undoubted +undoubtedly +undramatic +undue +undulant +undulate +undulation +undulations +undulating +undulated +undulates +undulation +undulation's +undying +unearthliness +unearthliness's +unease +unease's +uneasy +uneasiest +uneatable +uneconomic +unemployed +unemployed's +unending +unenterprising +unequal +unequally +unequaled +unerring +unerringly +unessential +uneven +unevenly +unexceptionably +unexcited +unexciting +unexpected +unexpectedly +unexpectedness +unexpectedness +unexpectedness's +unfailing +unfailingly +unfair +unfairly +unfairest +unfairer +unfairness +unfaltering +unfamiliar +unfathomably +unfed +unfeeling +unfeelingly +unfeminine +unfit +unfits +unfitting +unfix +unfixing +unfixed +unfixes +unflagging +unflaggingly +unflappability +unflappability's +unflappable +unflappably +unflattering +unflinching +unflinchingly +unforgettably +unforgivably +unfortunate +unfortunates +unfortunate's +unfriendly +unfriendliest +unfrock +unfrocking +unfrocked +unfruitful +unfunny +ungainliness +ungainliness's +ungainly +ungainliest +ungainlier +ungainliness +ungenerous +ungentle +ungodly +ungodliest +ungraceful +ungracefully +ungrudging +unguarded +unguent +unguents +unguent's +ungulate +ungulates +ungulate's +unhandy +unhandiest +unhappy +unhappiest +unhealthful +unhealthy +unhealthiest +unhistorical +unholy +unholiest +unhurt +unicameral +unicellular +unicorn +unicorns +unicorn's +unicycle +unicycles +unicycle's +unidirectional +unification +unification's +reunification's +reunification +uniform +uniformly +uniforming +uniformed +uniforms +uniform's +uniformity +uniformity's +unify +unification +unifying +unified +unifies +reunification +reunifying +reunified +reunifies +reunify +unilateral +unilaterally +unilateralism +unimportant +unimpressive +uninformative +uninhibited +uninhibitedly +uninstall +uninstalling +uninstalled +uninstaller +uninstallers +uninstalls +uninstallable +uninsured +unintelligent +unintended +uninteresting +uninterrupted +uninterruptedly +uninterruptible +uninviting +union +unions +union's +reunions +reunion's +reunion +unionism +unionism's +unionist +unionists +unionist's +unique +uniquely +uniquest +uniquer +uniqueness +uniqueness +uniqueness's +unisex +unisex's +unison +unison's +unitary +unite +uniting +united +unites +reuniting +disuniting +reunited +disunited +reunites +disunites +reunite +disunite +unitedly +unities +unitize +unitizing +unitized +unitizes +unity +unity's +disunity's +disunity +univalent +univalve +univalves +univalve's +universal +universally +universals +universal's +universalism +universalist +universality +universality's +universalize +universalizing +universalized +universalizes +universe +universes +universe's +university +universities +university's +univocal +unjust +unjustly +unkempt +unkind +unkindest +unkindly +unkindliest +unknowable +unknowable's +unknown +unknowns +unknown's +unleaded +unleaded's +unless +unlike +unlikeness +unlikable +unlikely +unlikeliest +unlit +unlock +unlocking +unlocked +unlocks +unlovable +unlovely +unloveliest +unlovelier +unloving +unlucky +unluckiest +unmanly +unmanliest +unmarried +unmeaning +unmentionable +unmentionables +unmentionable's +unmentionables +unmentionables's +unmet +unmindful +unmissable +unmistakably +unmoral +unmovable +unmusical +unnecessary +unnerving +unnervingly +unobservant +unoffensive +unofficial +unofficially +unoriginal +unpeople +unperceptive +unpersuasive +unpick +unpicking +unpicked +unpicks +unpin +unpins +unpleasing +unpolitical +unpopular +unpractical +unprecedented +unprecedentedly +unprofessional +unprofessionally +unpromising +unpropitious +unquestioning +unquestioningly +unquiet +unquietest +unquieter +unread +unreadable +unready +unreal +unreasoning +unregenerate +unrelated +unrelenting +unrelentingly +unrelieved +unrelievedly +unremarkable +unremitting +unremittingly +unrepentant +unreported +unrepresentative +unrest +unrest's +unrevealing +unripe +unripest +unriper +unroll +unrolling +unrolled +unrolls +unromantic +unruliness +unruliness's +unruly +unruliest +unrulier +unruliness +unsafe +unsafely +unsafest +unsafer +unsaleable +unsavory +unscathed +unseeing +unseeingly +unseemly +unseemliest +unseen +unseen's +unsentimental +unset +unshakable +unshakably +unshapely +unshockable +unshorn +unsightliness +unsightliness's +unsightly +unsightliest +unsightliness +unsmiling +unsociable +unsocial +unsold +unsound +unsoundly +unsoundest +unsounder +unsoundness +unspeakable +unspeakably +unspecific +unspectacular +unsporting +unstable +unsteady +unsteadiest +unsteadier +unsteadiness +unstinting +unstintingly +unstrapping +unsubstantial +unsubtle +unsuitable +unsure +unsuspecting +unsuspectingly +unsymmetrical +untactful +unthinkably +unthinking +unthinkingly +untidy +untidiest +untidier +untidiness +until +untimely +untimeliest +untiring +untiringly +untouchable +untouchables +untouchable's +untoward +untrue +untruest +untruer +untrustworthy +untruth +untruth's +unutterable +unutterably +unwarrantable +unwary +unwariest +unwavering +unwed +unwelcome +unwelcoming +unwell +unwieldiness +unwieldiness's +unwieldy +unwieldiest +unwieldier +unwieldiness +unwise +unwisely +unwisest +unwiser +unworried +unworthy +unworthiest +unwound +unwrapping +unyielding +up +ups +upbeat +upbeats +upbeat's +upbraid +upbraiding +upbraided +upbraids +upbringing +upbringings +upbringing's +upchuck +upchucking +upchucked +upchucks +upcoming +upcountry +upcountry's +update +updating +updated +updater +updates +update's +updraft +updrafts +updraft's +upend +upending +upended +upends +upfront +upgrade +upgrading +upgraded +upgrades +upgrade's +upheaval +upheavals +upheaval's +upheld +uphill +uphills +uphill's +uphold +upholding +upholder +upholders +upholds +upholder +upholder's +upholster +upholstering +upholstered +upholsters +reupholstering +reupholstered +reupholsters +reupholster +upholsterer +upholsterers +upholsterer's +upholstery +upholstery's +upkeep +upkeep's +upland +uplands +upland's +uplift +uplifting +upliftings +uplifted +uplifts +uplift's +upload +uploading +uploaded +uploads +upmarket +upmost +upon +upped +upper +uppers +upper's +uppercase +uppercase's +upperclassman +upperclassman's +upperclassmen +upperclasswoman +upperclasswomen +uppercut +uppercuts +uppercut's +uppercutting +uppermost +upping +uppish +uppity +upraise +upraising +upraised +upraises +uprear +uprearing +upreared +uprears +upright +uprightly +uprights +uprightness +upright's +uprightness +uprightness's +uprising +uprisings +uprising's +upriver +uproar +uproars +uproar's +uproarious +uproariously +uproot +uprooting +uprooted +uproots +upscale +upset +upsets +upset's +upsetting +upshot +upshots +upshot's +upside +upsides +upside's +upsilon +upsilons +upsilon's +upstage +upstaging +upstaged +upstages +upstairs +upstanding +upstart +upstarting +upstarted +upstarts +upstart's +upstate +upstate's +upstream +upstroke +upstrokes +upstroke's +upsurge +upsurging +upsurged +upsurges +upsurge's +upswing +upswings +upswing's +uptake +uptakes +uptake's +uptempo +upthrust +upthrusting +upthrusts +upthrust's +uptick +upticks +uptick's +uptight +uptown +uptown's +uptrend +upturn +upturning +upturned +upturns +upturn's +upward +upwardly +upwards +upwind +uracil +uracil's +uranium +uranium's +urban +urbane +urbanely +urbanest +urbaner +urbanity +urbanity's +urbanization +urbanization's +urbanize +urbanizing +urbanized +urbanizes +urbanologist +urbanologists +urbanologist's +urbanology +urbanology's +urchin +urchins +urchin's +urea +urea's +uremia +uremia's +uremic +ureter +ureters +ureter's +urethane +urethane's +urethra +urethra's +urethrae +urethral +urge +urging +urged +urges +urge's +urgency +urgency's +urgent +urgently +uric +urinal +urinals +urinal's +urinalyses +urinalysis +urinalysis's +urinary +urinate +urination +urinating +urinated +urinates +urination +urination's +urine +urine's +urn +urns +urn's +urogenital +urological +urologist +urologists +urologist's +urology +urology's +ursine +urticaria +urticaria's +usability +usability's +usable +reusable +unusable +usage +usages +usage's +use +using +used +uses +use's +reusing +disusing +reused +disused +reuses +disuses +reuse's +disuse's +reuse +disuse +used +unused +useful +usefully +usefulness +usefulness +usefulness's +useless +uselessly +uselessness +uselessness +uselessness's +user +users +user's +username +usernames +username's +usher +ushering +ushered +ushers +usher's +usherette +usherettes +usherette's +usu +usual's +usual +usually +unusually +unusual +usurer +usurers +usurer's +usurious +usurp +usurping +usurped +usurper +usurpers +usurps +usurpation +usurpation's +usurper +usurper's +usury +usury's +utensil +utensils +utensil's +uteri +uterine +uterus +uterus's +utilitarian +utilitarians +utilitarian's +utilitarianism +utilitarianism's +utility +utilities +utility's +utilization +utilization's +utilize +utilizing +utilized +utilizes +utilizable +utmost +utmost's +utopia +utopias +utopia's +utter +utterly +uttering +uttered +utters +utterance +utterances +utterance's +uttermost +uttermost's +uveitis +uvula +uvulas +uvula's +uvular +uvulars +uvular's +uxorious +v +vs +revs +rev +vac +vacs +vacancy +vacancies +vacancy's +vacant +vacantly +vacate +vacating +vacated +vacates +vacation +vacationing +vacationed +vacationer +vacationers +vacations +vacation's +vacationer +vacationer's +vacationist +vacationists +vacationist's +vaccinate +vaccination +vaccinations +vaccinating +vaccinated +vaccinates +vaccination +vaccination's +vaccine +vaccines +vaccine's +vacillate +vacillation +vacillations +vacillating +vacillated +vacillates +vacillation +vacillation's +vacuity +vacuity's +vacuole +vacuoles +vacuole's +vacuous +vacuously +vacuousness +vacuousness +vacuousness's +vacuum +vacuuming +vacuumed +vacuums +vacuum's +vagabond +vagabonding +vagabonded +vagabonds +vagabond's +vagabondage +vagabondage's +vagarious +vagary +vagaries +vagary's +vagina +vaginas +vagina's +vaginae +vaginal +vaginally +vagrancy +vagrancy's +vagrant +vagrants +vagrant's +vague +vaguely +vaguest +vaguer +vagueness +vagueness +vagueness's +vagus +vain +vainly +vainest +vainer +vainglorious +vaingloriously +vainglory +vainglory's +val +valance +valances +valance's +vale +vales +vale's +valediction +valedictions +valediction's +valedictorian +valedictorians +valedictorian's +valedictory +valedictories +valedictory's +valence +valences +valence's +valency +valencies +valency's +valentine +valentines +valentine's +valet +valeting +valeted +valets +valet's +valetudinarian +valetudinarians +valetudinarian's +valetudinarianism +valetudinarianism's +valiance +valiance's +valiant +valiantly +valid +validly +validate +validation +validating +validated +validates +invalidation +invalidating +invalidated +invalidates +invalidate +validation +validation's +invalidation's +invalidation +validations +validity +validity's +invalidity's +invalidity +validness +validness's +valise +valises +valise's +valley +valleys +valley's +valor +valor's +valorous +valorously +valuable +valuables +valuable's +valuate +valuating +valuated +valuates +valuation +valuations +valuation's +revaluations +devaluations +revaluation's +devaluation's +revaluation +devaluation +value's +value +valuing +valued +values +revaluing +devaluing +revalued +devalued +revalues +devalues +revalue +devalue +valueless +valuer +valuers +valuer's +valve +valving +valved +valves +valve's +valveless +valvular +vamoose +vamoosing +vamoosed +vamooses +vamp +vamping +vamped +vamps +vamp's +revamping +revamped +revamps +revamp's +revamp +vampire +vampires +vampire's +van +vans +van's +vanadium +vanadium's +vandal +vandals +vandal's +vandalism +vandalism's +vandalize +vandalizing +vandalized +vandalizes +vane +vanes +vane's +vanguard +vanguards +vanguard's +vanilla +vanillas +vanilla's +vanish +vanishing +vanishings +vanished +vanishes +vanity +vanities +vanity's +vanned +vanning +vanquish +vanquishing +vanquished +vanquisher +vanquishers +vanquishes +vanquisher +vanquisher's +vantage +vantages +vantage's +vape +vaping +vaped +vapes +vapid +vapidly +vapidness +vapidity +vapidity's +vapidness +vapidness's +vapor +vapors +vapor's +vaporization +vaporization's +vaporize +vaporizing +vaporized +vaporizer +vaporizers +vaporizes +vaporizer +vaporizer's +vaporous +vaporware +vapory +vaquero +vaqueros +vaquero's +var +vars +variability +variability's +invariability's +invariability +variable +variables +variable's +invariables +invariable's +invariable +variably +invariably +variance +variances +variance's +variant +variants +variant's +variate +variation +variations +variation +variation's +varicolored +varicose +varied +unvaried +variegate +variegation +variegating +variegated +variegates +variegation +variegation's +varietal +varietals +varietal's +variety +varieties +variety's +various +variously +varlet +varlets +varlet's +varmint +varmints +varmint's +varnish +varnishing +varnished +varnishes +varnish's +varnished +unvarnished +varsity +varsities +varsity's +vary +varying +varied +varies +varying +unvarying +vascular +vase +vases +vase's +vasectomy +vasectomies +vasectomy's +vasoconstriction +vasomotor +vassal +vassals +vassal's +vassalage +vassalage's +vast +vastly +vastest +vaster +vasts +vastness +vast's +vastness +vastness's +vat +vats +vat's +vatted +vatting +vaudeville +vaudeville's +vaudevillian +vaudevillians +vaudevillian's +vault +vaulting +vaulted +vaulter +vaulters +vaults +vault's +vaulter +vaulter's +vaulting +vaulting's +vaunt +vaunting +vaunted +vaunts +vaunt's +vb +veal +veal's +vector +vectoring +vectored +vectors +vector's +veejay +veejays +veejay's +veep +veeps +veep's +veer +veering +veered +veers +veer's +veg +veg's +vegan +vegans +vegan's +veganism +vegeburger +vegeburgers +veges +vegetable +vegetables +vegetable's +vegetarian +vegetarians +vegetarian's +vegetarianism +vegetarianism's +vegetate +vegetative +vegetation +vegetating +vegetated +vegetates +vegetation +vegetation's +vegged +vegges +veggie +veggies +veggie's +veggieburger +veggieburgers +vegging +vehemence +vehemence's +vehemency +vehemency's +vehement +vehemently +vehicle +vehicles +vehicle's +vehicular +veil's +veil +veiling +veiled +veils +unveiling +unveiled +unveils +unveil +vein +veining +veined +veins +vein's +vela +velar +velars +velar's +veld +velds +veld's +vellum +vellum's +velocipede +velocipedes +velocipede's +velocity +velocities +velocity's +velodrome +velodromes +velour +velours +velour's +velum +velum's +velvet +velvet's +velveteen +velveteen's +velvety +venal +venally +venality +venality's +venation +venation's +vend +vending +vended +vends +vendetta +vendettas +vendetta's +vendible +vendor +vendors +vendor's +veneer +veneering +veneered +veneers +veneer's +venerability +venerability's +venerable +venerate +veneration +venerating +venerated +venerates +veneration +veneration's +venereal +vengeance +vengeance's +vengeful +vengefully +revengefully +revengeful +venial +venireman +venireman's +veniremen +venison +venison's +venom +venom's +venomous +venomously +venous +vent's +vent +venting +vented +vents +ventilate +ventilation +ventilating +ventilated +ventilates +ventilation +ventilation's +ventilator +ventilators +ventilator's +ventilatory +ventral +ventricle +ventricles +ventricle's +ventricular +ventriloquism +ventriloquism's +ventriloquist +ventriloquists +ventriloquist's +ventriloquy +ventriloquy's +venture +venturing +ventured +ventures +venture's +venturesome +venturesomely +venturesomeness +venturesomeness +venturesomeness's +venturous +venturously +venturousness +venturousness +venturousness's +venue +venues +venue's +revenues +revenue's +revenue +veracious +veraciously +veracity +veracity's +veranda +verandas +veranda's +verb +verbs +verb's +proverbs +proverb's +proverb +verbal +verbally +verbals +verbal's +verbalization +verbalization's +verbalize +verbalizing +verbalized +verbalizes +verbatim +verbena +verbenas +verbena's +verbiage +verbiages +verbiage's +verbose +verbosely +verbosity +verbosity's +verboten +verdant +verdantly +verdict +verdicts +verdict's +verdigris +verdigrising +verdigrised +verdigrises +verdigris's +verdure +verdure's +verge's +verge +verging +verged +verges +converging +converged +converges +converge +verger +vergers +verger's +verifiable +unverifiable +verification +verification's +verified +unverified +verify +verification +verifying +verified +verifies +verily +verisimilitude +verisimilitude's +veritable +veritably +verity +verities +verity's +vermicelli +vermicelli's +vermiculite +vermiculite's +vermiform +vermilion +vermilion's +vermin +vermin's +verminous +vermouth +vermouth's +vernacular +vernaculars +vernacular's +vernal +vernier +verniers +vernier's +veronica +veronica's +verruca +verrucas +verruca's +verrucae +versa +versatile +versatility +versatility's +verse +version +versions +versing +versed +verses +verse's +reversion +conversion +reversions +conversions +reversing +conversing +reversed +conversed +reverses +converses +reverse's +converse's +reverse +converse +versed +unversed +versification +versification's +versifier +versifier's +versify +versification +versifying +versified +versifier +versifiers +versifies +version +versions +version's +reversions +inversions +conversions +reversion's +inversion's +conversion's +reversion +inversion +conversion +versioned +versioning +verso +versos +verso's +versus +vert +revert +vertebra +vertebra's +vertebrae +vertebral +vertebrate +vertebrates +vertebrate's +invertebrates +invertebrate's +invertebrate +vertex +vertexes +vertex's +vertical +vertically +verticals +vertical's +vertices +vertiginous +vertigo +vertigo's +verve +verve's +very +veriest +verier +vesicle +vesicles +vesicle's +vesicular +vesiculate +vesper +vespers +vesper's +vessel +vessels +vessel's +vest's +vest +vesting +vested +vests +vestment +investing +invested +invests +investment +invest +vestal +vestals +vestal's +vestibule +vestibules +vestibule's +vestige +vestiges +vestige's +vestigial +vestigially +vesting +vesting's +vestment +vestments +vestment's +investments +investment's +investment +vestry +vestries +vestry's +vestryman +vestryman's +vestrymen +vet +vets +vet's +vetch +vetches +vetch's +veteran +veterans +veteran's +veterinarian +veterinarians +veterinarian's +veterinary +veterinaries +veterinary's +veto +vetoing +vetoed +veto's +vetoes +vetted +vetting +vex +vexing +vexed +vexes +vexation +vexations +vexation's +vexatious +vexatiously +vhf +vi +via +viability +viability's +viable +viably +viaduct +viaducts +viaduct's +vial +vials +vial's +viand +viands +viand's +vibe +vibes +vibe's +vibes +vibes's +vibraharp +vibraharps +vibraharp's +vibrancy +vibrancy's +vibrant +vibrantly +vibraphone +vibraphones +vibraphone's +vibraphonist +vibraphonists +vibraphonist's +vibrate +vibration +vibrations +vibrating +vibrated +vibrates +vibration +vibration's +vibrato +vibratos +vibrato's +vibrator +vibrators +vibrator's +vibratory +viburnum +viburnums +viburnum's +vicar +vicars +vicar's +vicarage +vicarages +vicarage's +vicarious +vicariously +vicariousness +vicariousness +vicariousness's +vice +vices +vice's +devices +device's +device +viced +vicegerent +vicegerents +vicegerent's +vicennial +viceregal +viceroy +viceroys +viceroy's +vichyssoise +vichyssoise's +vicing +vicinity +vicinity's +vicious +viciously +viciousness +viciousness +viciousness's +vicissitude +vicissitudes +vicissitude's +victim +victims +victim's +victimization +victimization's +victimize +victimizing +victimized +victimizes +victimless +victor +victors +victor's +victorious +victoriously +victory +victories +victory's +victual +victualing +victualed +victuals +victual's +vicuna +vicunas +vicuna's +videlicet +video +videoing +videoed +videos +video's +videocassette +videocassettes +videocassette's +videoconferencing +videodisc +videodiscs +videodisc's +videophone +videophones +videophone's +videotape +videotaping +videotaped +videotapes +videotape's +videotex +vie +vied +vies +view +viewing +viewed +viewer +viewers +views +view's +reviewing +reviewed +reviewer +reviewers +reviews +review's +review +viewable +viewer +viewer's +reviewer's +reviewer +viewership +viewership's +viewfinder +viewfinders +viewfinder's +viewing +viewings +viewing's +viewpoint +viewpoints +viewpoint's +vigesimal +vigil +vigils +vigil's +vigilance +vigilance's +vigilant +vigilantly +vigilante +vigilantes +vigilante's +vigilantism +vigilantism's +vigilantist +vigilantist's +vignette +vignetting +vignetted +vignettes +vignette's +vignettist +vignettists +vignettist's +vigor +vigor's +vigorous +vigorously +vii +viii +viking +vikings +viking's +vile +vilely +vilest +viler +vileness +vileness +vileness's +vilification +vilification's +vilify +vilification +vilifying +vilified +vilifies +villa +villas +villa's +village +villager +villagers +villages +village's +villager +villager's +villain +villains +villain's +villainous +villainy +villainies +villainy's +villein +villeins +villein's +villeinage +villeinage's +villi +villus +villus's +vim +vim's +vinaigrette +vinaigrette's +vincible +invincible +vindicate +vindication +vindications +vindicating +vindicated +vindicates +vindication +vindication's +vindicator +vindicators +vindicator's +vindictive +vindictively +vindictiveness +vindictiveness +vindictiveness's +vine +vines +vine's +vinegar +vinegar's +vinegary +vineyard +vineyards +vineyard's +vino +vino's +vinous +vintage +vintages +vintage's +vintner +vintners +vintner's +vinyl +vinyls +vinyl's +viol +viols +viol's +violable +viola +violas +viola's +violable +inviolable +violate +violation +violations +violating +violated +violates +violation +violation's +violator +violators +violator's +violence +violence's +violent +violently +violet +violets +violet's +violin +violins +violin's +violincello +violincellos +violinist +violinists +violinist's +violist +violists +violist's +violoncellist +violoncellists +violoncellist's +violoncello +violoncellos +violoncello's +viper +vipers +viper's +viperous +virago +virago's +viragoes +viral +vireo +vireos +vireo's +virgin +virgins +virgin's +virginal +virginals +virginal's +virginity +virginity's +virgule +virgules +virgule's +virile +virility +virility's +virologist +virologists +virologist's +virology +virology's +virtual +virtually +virtualization +virtue +virtues +virtue's +virtuosity +virtuosity's +virtuoso +virtuoso's +virtuous +virtuously +virtuousness +virtuousness +virtuousness's +virulence +virulence's +virulent +virulently +virus +viruses +virus's +visa +visaing +visaed +visas +visa's +visage +visages +visage's +viscera +visceral +viscerally +viscid +viscose +viscose's +viscosity +viscosity's +viscount +viscounts +viscount's +viscountcy +viscountcies +viscountcy's +viscountess +viscountesses +viscountess's +viscous +viscus +viscus's +vise +vising +vised +vises +vise's +revising +devising +revised +devised +revises +devises +revise's +devise's +revise +devise +visibility +visibility's +invisibility's +invisibility +visible +invisible +visibly +invisibly +vision +visioning +visioned +visions +vision's +provisioning +provisioned +provisions +provision's +provision +visionary +visionaries +visionary's +visit's +visit +visiting +visited +visits +revisiting +revisited +revisits +revisit +visitant +visitants +visitant's +visitation +visitations +visitation's +visitor +visitors +visitor's +visor +visors +visor's +vista +vistas +vista's +visual +visually +visuals +visual's +visualization +visualizations +visualization's +visualize +visualizing +visualized +visualizer +visualizers +visualizes +visualizer +visualizer's +vita +vita's +vitae +vital +vitally +vitals +vitality +vitality's +vitalization +vitalization's +revitalization's +revitalization +vitalize +vitalizing +vitalized +vitalizes +revitalizing +devitalizing +revitalized +devitalized +revitalizes +devitalizes +revitalize +devitalize +vitals +vitals's +vitamin +vitamins +vitamin's +vitiate +vitiation +vitiating +vitiated +vitiates +vitiation +vitiation's +viticulture +viticulture's +viticulturist +viticulturists +viticulturist's +vitreous +vitrifaction +vitrifaction's +vitrification +vitrification's +vitrify +vitrification +vitrifying +vitrified +vitrifies +vitrine +vitrines +vitrine's +vitriol +vitriol's +vitriolic +vitriolically +vittles +vittles's +vituperate +vituperative +vituperation +vituperating +vituperated +vituperates +vituperation +vituperation's +viva +vivas +viva's +vivace +vivacious +vivaciously +vivaciousness +vivaciousness +vivaciousness's +vivacity +vivacity's +vivaria +vivarium +vivariums +vivarium's +vivid +vividly +vividest +vivider +vividness +vividness +vividness's +vivify +vivifying +vivified +vivifies +revivifying +revivified +revivifies +revivify +viviparous +vivisect +vivisecting +vivisected +vivisects +vivisection +vivisection's +vivisectional +vivisectionist +vivisectionists +vivisectionist's +vixen +vixens +vixen's +vixenish +vixenishly +viz +vizier +viziers +vizier's +vlf +vocab +vocable +vocables +vocable's +vocabulary +vocabularies +vocabulary's +vocal +vocally +vocals +vocal's +vocalic +vocalist +vocalists +vocalist's +vocalization +vocalizations +vocalization's +vocalize +vocalizing +vocalized +vocalizes +vocation +vocations +vocation's +revocations +invocations +convocations +provocations +revocation's +invocation's +convocation's +provocation's +revocation +invocation +convocation +provocation +vocational +vocationally +vocative +vocatives +vocative's +vociferate +vociferation +vociferating +vociferated +vociferates +vociferation +vociferation's +vociferous +vociferously +vociferousness +vociferousness +vociferousness's +vodka +vodkas +vodka's +vogue +vogues +vogue's +voguish +voice +voicing +voiced +voices +voice's +invoicing +invoiced +invoices +invoice's +invoice +voiced +unvoiced +voiceless +voicelessly +voicelessness +voicelessness +voicelessness's +voicemail +voicemails +voicemail's +void +voiding +voided +voids +void's +voidable +voila +voile +voile's +vol +vols +volatile +volatility +volatility's +volatilize +volatilizing +volatilized +volatilizes +volcanic +volcano +volcano's +volcanoes +vole +voles +vole's +volition +volition's +volitional +volley +volleying +volleyed +volleys +volley's +volleyball +volleyballs +volleyball's +volt +volts +volt's +revolts +revolt's +revolt +voltage +voltages +voltage's +voltaic +voltmeter +voltmeters +voltmeter's +volubility +volubility's +voluble +volubly +volume +volumes +volume's +volumetric +voluminous +voluminously +voluminousness +voluminousness +voluminousness's +voluntarily +involuntarily +voluntarism +voluntarism's +voluntary +voluntaries +voluntary's +volunteer +volunteering +volunteered +volunteers +volunteer's +volunteerism +volunteerism's +voluptuary +voluptuaries +voluptuary's +voluptuous +voluptuously +voluptuousness +voluptuousness +voluptuousness's +volute +volutes +volute's +vomit +vomiting +vomited +vomits +vomit's +voodoo +voodooing +voodooed +voodoos +voodoo's +voodooism +voodooism's +voracious +voraciously +voraciousness +voraciousness +voraciousness's +voracity +voracity's +vortex +vortexes +vortex's +votary +votaries +votary's +vote's +vote +votive +voting +voted +votes +devoting +devoted +devotes +devote +voter +voters +voter's +vouch +vouching +vouched +voucher +vouchers +vouches +voucher +voucher's +vouchsafe +vouchsafing +vouchsafed +vouchsafes +vow +vowing +vowed +vows +vow's +vowel +vowels +vowel's +voyage +voyaging +voyaged +voyager +voyagers +voyages +voyage's +voyager +voyager's +voyageur +voyageurs +voyageur's +voyeur +voyeurs +voyeur's +voyeurism +voyeurism's +voyeuristic +vulcanization +vulcanization's +vulcanize +vulcanizing +vulcanized +vulcanizes +vulgar +vulgarly +vulgarest +vulgarer +vulgarian +vulgarians +vulgarian's +vulgarism +vulgarisms +vulgarism's +vulgarity +vulgarities +vulgarity's +vulgarization +vulgarization's +vulgarize +vulgarizing +vulgarized +vulgarizer +vulgarizers +vulgarizes +vulgarizer +vulgarizer's +vulnerabilities +vulnerability +vulnerability's +invulnerability's +invulnerability +vulnerable +invulnerable +vulnerably +invulnerably +vulpine +vulture +vultures +vulture's +vulturous +vulva +vulva's +vulvae +vuvuzela +vuvuzelas +vuvuzela's +vying +w +wive +wen +wens +wing +wings +wed +west +wabbit +wabbits +wack +wackest +wacker +wacks +wack's +wackiness +wackiness's +wacko +wackos +wacko's +wacky +wackiest +wackier +wackiness +wad +wading +waded +wader +waders +wads +wad's +wadded +wadding +wadding's +waddle +waddling +waddled +waddles +waddle's +wade +wades +wade's +wader +wader's +waders +waders's +wadge +wadges +wadi +wadis +wadi's +wafer +wafers +wafer's +waffle +waffling +waffled +waffler +wafflers +waffles +waffle's +waffler +waffler's +waft +wafting +wafted +wafts +waft's +wag +waging +waged +wager +wagers +wags +wag's +wage +wages +wage's +waged +unwaged +wager +wagering +wagered +wagerer +wagerers +wager's +wagerer +wagerer's +wagged +waggery +waggeries +waggery's +wagging +waggish +waggishly +waggishness +waggishness +waggishness's +waggle +waggling +waggled +waggles +waggle's +wagon +wagoner +wagoners +wagons +wagon's +wagoner +wagoner's +wagtail +wagtails +wagtail's +waif +waifs +waif's +wail +wailing +wailed +wailer +wailers +wails +wail's +wailer +wailer's +wailing +wailing's +wain +wains +wain's +wainscot +wainscoting +wainscotings +wainscoted +wainscots +wainscot's +wainscoting +wainscoting's +wainwright +wainwrights +wainwright's +waist +waists +waist's +waistband +waistbands +waistband's +waistcoat +waistcoats +waistcoat's +waistline +waistlines +waistline's +wait +waiting +waited +waiter +waiters +waits +wait's +waiter +waiter's +waiting +waiting's +waitperson +waitpersons +waitperson's +waitress +waitresses +waitress's +waitstaff +waitstaff's +waive +waiving +waived +waiver +waivers +waives +waiver +waiver's +wake +waking +wakings +waked +wakes +wake's +wakeful +wakefully +wakefulness +wakefulness +wakefulness's +waken +wakening +wakened +wakens +waldo +waldos +waldoes +wale +waling +waled +wales +wale's +walk +walking +walked +walker +walkers +walks +walk's +walkabout +walkabouts +walkaway +walkaways +walkaway's +walker +walker's +walkies +walking +walking's +walkout +walkouts +walkout's +walkover +walkovers +walkover's +walkway +walkways +walkway's +wall +walling +walled +walls +wall's +wallaby +wallabies +wallaby's +wallah +wallahs +wallboard +wallboard's +wallet +wallets +wallet's +walleye +walleyed +walleyes +walleye's +wallflower +wallflowers +wallflower's +wallop +walloping +wallopings +walloped +wallops +wallop's +walloping +walloping's +wallow +wallowing +wallowed +wallows +wallow's +wallpaper +wallpapering +wallpapered +wallpapers +wallpaper's +wally +wallies +walnut +walnuts +walnut's +walrus +walruses +walrus's +waltz +waltzing +waltzed +waltzer +waltzers +waltzes +waltz's +waltzer +waltzer's +wampum +wampum's +wan +wanly +waning +waned +wanness +wand +wands +wand's +wander +wandering +wanderings +wandered +wanderer +wanderers +wanders +wanderer +wanderer's +wanderings +wanderings's +wanderlust +wanderlusts +wanderlust's +wane +wanes +wane's +wangle +wangling +wangled +wangler +wanglers +wangles +wangle's +wangler +wangler's +wank +wanking +wanked +wanker +wankers +wanks +wanna +wannabe +wannabes +wannabe's +wannabee +wannabees +wanner +wanness +wanness's +wannest +want +wanting +wanted +wants +want's +wanted +unwanted +wanton +wantonly +wantoning +wantoned +wantons +wantonness +wanton's +wantonness +wantonness's +wapiti +wapitis +wapiti's +war +wars +war's +warble +warbling +warbled +warbler +warblers +warbles +warble's +warbler +warbler's +warbonnet +warbonnets +warbonnet's +ward +warding +warded +wards +ward's +rewarding +rewarded +rewards +reward's +reward +warden +wardens +warden's +warder +warders +warder's +wardress +wardresses +wardrobe +wardrobes +wardrobe's +wardroom +wardrooms +wardroom's +ware +wares +ware's +warehouse +warehousing +warehoused +warehouses +warehouse's +warez +warfare +warfare's +warhead +warheads +warhead's +warhorse +warhorses +warhorse's +warily +unwarily +wariness +wariness's +unwariness's +unwariness +warlike +warlock +warlocks +warlock's +warlord +warlords +warlord's +warm +warmth +warmly +warming +warmed +warmest +warmer +warmers +warms +warmness +warmblooded +warmer +warmer's +warmhearted +warmheartedness +warmheartedness +warmheartedness's +warmish +warmness +warmness's +warmonger +warmongering +warmongers +warmonger's +warmongering +warmongering's +warmth +warmth's +warn +warning +warnings +warned +warns +warning +warning's +warp +warping +warped +warps +warp's +warpaint +warpath +warpath's +warpaths +warplane +warplanes +warplane's +warrant +warranting +warranted +warrants +warrant's +warranted +unwarranted +warranty +warrantying +warrantied +warranties +warranty's +warred +warren +warrens +warren's +warring +warrior +warriors +warrior's +warship +warships +warship's +wart +warts +wart's +warthog +warthogs +warthog's +wartime +wartime's +warty +wartiest +wartier +wary +wariest +warier +wariness +unwarier +unwariness +unwary +was +wasabi +wash +washing +washings +washed +washer +washers +washes +wash's +washable +washable +washables +washable's +washbasin +washbasins +washbasin's +washboard +washboards +washboard's +washbowl +washbowls +washbowl's +washcloth +washcloth's +washcloths +washed +unwashed +washer +washer's +washerwoman +washerwoman's +washerwomen +washing +washing's +washout +washouts +washout's +washrag +washrags +washrag's +washroom +washrooms +washroom's +washstand +washstands +washstand's +washtub +washtubs +washtub's +washy +washiest +washier +wasn't +wasp +wasps +wasp's +waspish +waspishly +waspishness +waspishness +waspishness's +wassail +wassailing +wassailed +wassails +wassail's +wast +wastage +wastage's +waste +wasting +wasted +waster +wasters +wastes +waste's +wastebasket +wastebaskets +wastebasket's +wasteful +wastefully +wastefulness +wastefulness +wastefulness's +wasteland +wastelands +wasteland's +wastepaper +wastepaper's +waster +waster's +wastewater +wastrel +wastrels +wastrel's +watch +watching +watched +watcher +watchers +watches +watch's +watchable +watchable +unwatchable +watchband +watchbands +watchband's +watchdog +watchdogs +watchdog's +watcher +watcher's +watchful +watchfully +watchfulness +watchfulness +watchfulness's +watchmaker +watchmakers +watchmaker's +watchmaking +watchmaking's +watchman +watchman's +watchmen +watchstrap +watchstraps +watchtower +watchtowers +watchtower's +watchword +watchwords +watchword's +water +watering +watered +waters +water's +waterbed +waterbeds +waterbed's +waterbird +waterbirds +waterbird's +waterboard +waterboarding +waterboardings +waterboarded +waterboards +waterboard's +waterboarding +waterboarding's +waterborne +watercolor +watercolors +watercolor's +watercourse +watercourses +watercourse's +watercraft +watercraft's +watercress +watercress's +waterfall +waterfalls +waterfall's +waterfowl +waterfowls +waterfowl's +waterfront +waterfronts +waterfront's +waterhole +waterholes +waterhole's +wateriness +wateriness's +waterlily +waterlilies +waterlily's +waterline +waterlines +waterline's +waterlogged +watermark +watermarking +watermarked +watermarks +watermark's +watermelon +watermelons +watermelon's +watermill +watermills +watermill's +waterproof +waterproofing +waterproofed +waterproofs +waterproof's +waterproofing +waterproofing's +waters +waters's +watershed +watersheds +watershed's +waterside +watersides +waterside's +waterspout +waterspouts +waterspout's +watertight +waterway +waterways +waterway's +waterwheel +waterwheels +waterwheel's +waterworks +waterworks's +watery +wateriest +waterier +wateriness +watt +watts +watt's +wattage +wattage's +wattle +wattling +wattled +wattles +wattle's +wave +waving +waved +waver +wavers +waves +wave's +waveband +wavebands +waveform +wavefront +wavelength +wavelength's +wavelengths +wavelet +wavelets +wavelet's +wavelike +waver +wavering +wavered +waverer +waverers +waver's +waverer +waverer's +wavering +waveringly +waviness +waviness's +wavy +waviest +wavier +waviness +wax +waxen +waxing +waxed +waxes +wax's +waxiness +waxiness's +waxwing +waxwings +waxwing's +waxwork +waxworks +waxwork's +waxy +waxiest +waxier +waxiness +way +ways +way's +waybill +waybills +waybill's +wayfarer +wayfarers +wayfarer's +wayfaring +wayfarings +wayfaring's +waylaid +waylay +waylaying +waylayer +waylayers +waylays +waylayer +waylayer's +wayside +waysides +wayside's +wayward +waywardly +waywardness +waywardness +waywardness's +wazoo +wazoos +we +we'd +we'll +we're +we've +weak +weaken +weakens +weakly +weakest +weaker +weakness +weaken +weakening +weakened +weakener +weakeners +weakener +weakener's +weakfish +weakfishes +weakfish's +weakish +weakling +weaklings +weakling's +weakness +weaknesses +weakness's +weal +wealth +weals +weal's +wealth +wealth's +wealthiness +wealthiness's +wealthy +wealthiest +wealthier +wealthiness +wean +weaning +weaned +weans +weapon +weapons +weapon's +weaponize +weaponizing +weaponized +weaponizes +weaponless +weaponry +weaponry's +wear +wearing +wearings +wearer +wearers +wears +wear's +wearable +wearable +unwearable +wearer +wearer's +wearied +unwearied +wearily +weariness +weariness's +wearisome +wearisomely +weary +wearying +wearied +weariest +wearier +wearies +weariness +weasel +weaselly +weaseling +weaseled +weasels +weasel's +weather +weathering +weathered +weathers +weather's +weatherboard +weatherboarding +weatherboards +weathercock +weathercocks +weathercock's +weathering +weathering's +weatherization +weatherization's +weatherize +weatherizing +weatherized +weatherizes +weatherman +weatherman's +weathermen +weatherperson +weatherpersons +weatherperson's +weatherproof +weatherproofing +weatherproofed +weatherproofs +weatherstrip +weatherstrips +weatherstripped +weatherstripping +weatherstripping's +weave +weaving +weaved +weaver +weavers +weaves +weave's +weaver +weaver's +weaving +weaving's +web +webs +web's +webbed +webbing +webbing's +webcam +webcams +webcam's +webcast +webcasting +webcasts +webcast's +webfeet +webfoot +webfoot's +webinar +webinars +webinar's +webisode +webisodes +webisode's +weblog +weblogs +weblog's +webmaster +webmasters +webmaster's +webmistress +webmistresses +webmistress's +website +websites +website's +wed +weds +reweds +rewed +wedded +rewedded +wedder +wedding +weddings +wedding's +wedge +wedging +wedged +wedges +wedge's +wedgie +wedgies +wedgie's +wedlock +wedlock's +wee +weest +weer +wees +wee's +weed +weeding +weeded +weeder +weeders +weeds +weed's +weeder +weeder's +weedkiller +weedkillers +weedless +weedy +weediest +weedier +weeing +week +weekly +weeks +week's +weekday +weekdays +weekday's +weekend +weekending +weekended +weekender +weekenders +weekends +weekend's +weekly +weeklies +weekly's +weeknight +weeknights +weeknight's +ween +weening +weened +weens +weenie +weeniest +weenier +weenies +weenie's +weensy +weensiest +weensier +weeny +weep +weeping +weepings +weeper +weepers +weeps +weep's +weeper +weeper's +weepie +weepy +weepiest +weepier +weepies +weepy's +weevil +weevils +weevil's +weft +wefts +weft's +weigh's +weigh +weighing +weighed +reweighing +reweighed +reweigh +weighbridge +weighbridges +weighs +reweighs +weight +weighting +weightings +weighted +weights +weight's +weighted +unweighted +weightily +weightiness +weightiness's +weightless +weightlessly +weightlessness +weightlessness +weightlessness's +weightlifter +weightlifters +weightlifter's +weightlifting +weightlifting's +weighty +weightiest +weightier +weightiness +weir +weirs +weir's +weird +weirdly +weirdest +weirder +weirdness +weirdie +weirdies +weirdie's +weirdness +weirdness's +weirdo +weirdos +weirdo's +welcome +welcoming +welcomed +welcomes +welcome's +weld +welding +welded +welder +welders +welds +weld's +weldable +welder +welder's +welfare +welfare's +welkin +welkin's +well +welling +welled +wells +wellness +well's +wellhead +wellheads +wellhead's +wellie +wellington +wellingtons +wellington's +wellness +wellness's +wellspring +wellsprings +wellspring's +welly +wellies +welsh +welshing +welshed +welsher +welshers +welshes +welsher +welsher's +welt +welting +welted +welter +welters +welts +welt's +welter +weltering +weltered +welter's +welterweight +welterweights +welterweight's +wen +wen's +wench +wenches +wench's +wend +wending +wended +wends +went +wept +were +weren't +werewolf +werewolf's +werewolves +west +west's +westbound +westerly +westerlies +westerly's +western +westerner +westerners +westerns +western's +westerner +westerner's +westernization +westernization's +westernize +westernizing +westernized +westernizes +westernmost +westward +westwards +wet +wetly +wets +wetness +wet's +wetback +wetbacks +wetback's +wetland +wetlands +wetland's +wetness +wetness's +wetter +wetters +wetter's +wettest +wetting +wetware +wetwares +whack +whacking +whackings +whacked +whacker +whackers +whacks +whack's +whacker +whacker's +whale +whaling +whaled +whaler +whalers +whales +whale's +whaleboat +whaleboats +whaleboat's +whalebone +whalebone's +whaler +whaler's +whaling +whaling's +wham +whams +wham's +whammed +whamming +whammy +whammies +whammy's +wharf +wharf's +wharves +what +whats +what's +whatchamacallit +whatchamacallits +whatchamacallit's +whatever +whatnot +whatnot's +whatshername +whatshisname +whatsit +whatsits +whatsoever +wheal +wheals +wheal's +wheat +wheaten +wheat's +wheatgerm +wheatmeal +whee +wheedle +wheedling +wheedled +wheedler +wheedlers +wheedles +wheedler +wheedler's +wheel +wheeling +wheeled +wheeler +wheels +wheel's +wheelbarrow +wheelbarrows +wheelbarrow's +wheelbase +wheelbases +wheelbase's +wheelchair +wheelchairs +wheelchair's +wheelhouse +wheelhouses +wheelhouse's +wheelie +wheelies +wheelie's +wheelwright +wheelwrights +wheelwright's +wheeze +wheezing +wheezed +wheezes +wheeze's +wheezily +wheeziness +wheeziness's +wheezy +wheeziest +wheezier +wheeziness +whelk +whelked +whelks +whelk's +whelm +whelming +whelmed +whelms +whelp +whelping +whelped +whelps +whelp's +when +whens +when's +whence +whenever +whensoever +where +wheres +where's +whereabouts +whereabouts's +whereas +whereat +whereby +wherefore +wherefores +wherefore's +wherein +whereof +whereon +wheresoever +whereto +whereupon +wherever +wherewith +wherewithal +wherewithal's +wherry +wherries +wherry's +whet +whets +whether +whetstone +whetstones +whetstone's +whetted +whetting +whew +whey +whey's +which +whichever +whiff +whiffing +whiffed +whiffs +whiff's +whiffletree +whiffletrees +whiffletree's +while +whiling +whiled +whiles +while's +whilom +whilst +whim +whims +whim's +whimper +whimpering +whimpered +whimpers +whimper's +whimsical +whimsically +whimsicality +whimsicality's +whimsy +whimsies +whimsy's +whine +whining +whined +whiner +whiners +whines +whine's +whiner +whiner's +whinge +whinging +whinged +whinger +whingers +whinges +whingeing +whinny +whinnying +whinnied +whinnies +whinny's +whiny +whiniest +whinier +whip +whips +whip's +whipcord +whipcord's +whiplash +whiplashes +whiplash's +whipped +whipper +whippers +whipper's +whippersnapper +whippersnappers +whippersnapper's +whippet +whippets +whippet's +whipping +whippings +whipping's +whippletree +whippletrees +whippletree's +whippoorwill +whippoorwills +whippoorwill's +whipsaw +whipsawing +whipsawed +whipsaws +whipsaw's +whir +whirs +whir's +whirl +whirling +whirled +whirls +whirl's +whirligig +whirligigs +whirligig's +whirlpool +whirlpools +whirlpool's +whirlwind +whirlwinds +whirlwind's +whirlybird +whirlybirds +whirlybird's +whirred +whirring +whisk +whisking +whisked +whisker +whiskers +whisks +whisk's +whisker +whiskered +whisker's +whiskery +whiskey +whiskeys +whiskey's +whiskys +whisper +whispering +whispered +whisperer +whisperers +whispers +whisper's +whisperer +whisperer's +whist +whist's +whistle +whistling +whistled +whistler +whistlers +whistles +whistle's +whistler +whistler's +whit +whiten +whitens +whiting +whitings +whited +whitest +whiter +whits +whit's +white +whites +whiteness +white's +whitebait +whiteboard +whiteboards +whitecap +whitecaps +whitecap's +whitefish +whitefishes +whitefish's +whitehead +whiteheads +whitehead's +whitelist +whitelisting +whitelisted +whitelists +whiten +whitening +whitenings +whitened +whitener +whiteners +whitener +whitener's +whiteness +whiteness's +whitening +whitening's +whiteout +whiteouts +whiteout's +whitetail +whitetails +whitetail's +whitewall +whitewalls +whitewall's +whitewash +whitewashing +whitewashed +whitewashes +whitewash's +whitewater +whitewater's +whitey +whiteys +whitey's +whither +whiting +whiting's +whitish +whittle +whittling +whittled +whittler +whittlers +whittles +whittler +whittler's +whiz +whiz's +whizkid +whizkid's +whizzbang +whizzbangs +whizzbang's +whizzed +whizzes +whizzing +who'd +who'll +who're +who've +who +who's +whoa +whodunit +whodunits +whodunit's +whoever +whole +wholes +wholeness +whole's +wholefood +wholefoods +wholegrain +wholehearted +wholeheartedly +wholeheartedness +wholeheartedness +wholeheartedness's +wholemeal +wholeness +wholeness's +wholesale +wholesaling +wholesaled +wholesaler +wholesalers +wholesales +wholesale's +wholesaler +wholesaler's +wholesome +wholesomeness +unwholesomeness +unwholesome +wholesomely +wholesomeness +wholesomeness's +unwholesomeness's +unwholesomeness +wholewheat +wholly +whom +whomever +whomsoever +whoop +whooping +whooped +whooper +whoopers +whoops +whoop's +whoopee +whoopees +whooper +whooper's +whoosh +whooshing +whooshed +whooshes +whoosh's +whop +whops +whopped +whopper +whoppers +whopper's +whopping +whore +whoring +whores +whore's +whorehouse +whorehouses +whorehouse's +whoreish +whorish +whorl +whorled +whorls +whorl's +whose +whoso +whosoever +whup +whups +whupped +whupping +why'd +why +why's +whys +wick +wicked +wicker +wickers +wicks +wick's +wicked +wickedly +wickedest +wickeder +wickedness +wickedness +wickedness's +wicker +wicker's +wickerwork +wickerwork's +wicket +wickets +wicket's +wide +widely +widest +wider +wideness +widemouthed +widen +widening +widened +widener +wideners +widens +widener +widener's +wideness +wideness's +widescreen +widescreens +widescreen's +widespread +widget +widgets +widow +widowing +widowed +widower +widowers +widows +widow's +widower +widower's +widowhood +widowhood's +width +width's +widths +wield +wielding +wielded +wielder +wielders +wields +wielder +wielder's +wiener +wieners +wiener's +wienie +wienies +wienie's +wife +wifely +wife's +wifeless +wig +wigs +wig's +wigeon +wigeon's +wigged +wigging +wiggle +wiggling +wiggled +wiggler +wigglers +wiggles +wiggle's +wiggler +wiggler's +wiggly +wiggliest +wigglier +wight +wights +wight's +wiglet +wiglets +wiglet's +wigwag +wigwags +wigwag's +wigwagged +wigwagging +wigwam +wigwams +wigwam's +wiki +wikis +wiki's +wild +wildly +wildest +wilder +wilds +wildness +wild's +wildcard +wildcards +wildcard's +wildcat +wildcats +wildcat's +wildcatted +wildcatter +wildcatters +wildcatter's +wildcatting +wildebeest +wildebeests +wildebeest's +wilderness +wildernesses +wilderness's +wildfire +wildfires +wildfire's +wildflower +wildflowers +wildflower's +wildfowl +wildfowl's +wildlife +wildlife's +wildness +wildness's +wilds +wilds's +wile +wiling +wiled +wiles +wile's +wiliness +wiliness's +will +willed +wills +will's +willful +willfully +willfulness +willfulness +willfulness's +willies +willies's +willing +willingly +willingness +unwillingly +unwillingness +unwilling +willingness +willingness's +unwillingness's +unwillingness +williwaw +williwaws +williwaw's +willow +willows +willow's +willowy +willpower +willpower's +willy +willies +wilt +wilting +wilted +wilts +wilt's +wily +wiliest +wilier +wiliness +wimp +wimping +wimped +wimps +wimp's +wimpish +wimple +wimpling +wimpled +wimples +wimple's +wimpy +wimpiest +wimpier +win +wining +wined +wins +win's +wince +wincing +winced +winces +wince's +winch +winching +winched +winches +winch's +wind's +wind +winding +winds +rewinding +unwinding +rewinds +unwinds +rewind +unwind +windbag +windbags +windbag's +windblown +windbreak +windbreaker +windbreakers +windbreaks +windbreak's +windbreaker +windbreaker's +windburn +windburned +windburn's +windcheater +windcheaters +windchill +windchill's +winded +winder +winders +winder's +windfall +windfalls +windfall's +windflower +windflowers +windflower's +windily +windiness +windiness's +winding's +windjammer +windjammers +windjammer's +windlass +windlasses +windlass's +windless +windmill +windmilling +windmilled +windmills +windmill's +window +windowing +windowed +windows +window's +windowless +windowpane +windowpanes +windowpane's +windowsill +windowsills +windowsill's +windpipe +windpipes +windpipe's +windproof +windrow +windrows +windrow's +windscreen +windscreens +windscreen's +windshield +windshields +windshield's +windsock +windsocks +windsock's +windstorm +windstorms +windstorm's +windsurf +windsurfing +windsurfed +windsurfer +windsurfers +windsurfs +windsurfer +windsurfer's +windsurfing +windsurfing's +windswept +windup +windups +windup's +windward +windward's +windy +windiest +windier +windiness +wine +wines +wine's +wineglass +wineglasses +wineglass's +winegrower +winegrowers +winegrower's +winemaker +winemakers +winemaker's +winery +wineries +winery's +wing +winging +winged +winger +wingers +wing's +wingding +wingdings +wingding's +wingless +winglike +wingnut +wingnuts +wingnut's +wingspan +wingspans +wingspan's +wingspread +wingspreads +wingspread's +wingtip +wingtips +wingtip's +wink +winking +winked +winker +winkers +winks +wink's +winker +winker's +winkle +winkling +winkled +winkles +winkle's +winnable +unwinnable +winner +winners +winner's +winning +winningly +winnings +winning's +winnow +winnowing +winnowed +winnower +winnowers +winnows +winnower +winnower's +wino +winos +wino's +winsome +winsomely +winsomest +winsomer +winsomeness +winsomeness +winsomeness's +winter +wintering +wintered +winters +winter's +wintergreen +wintergreen's +winterize +winterizing +winterized +winterizes +wintertime +wintertime's +wintry +wintriest +wintrier +winy +winiest +winier +wipe +wiping +wiped +wiper +wipers +wipes +wipe's +wiper +wiper's +wire's +wire +wiring +wired +wires +rewiring +rewired +rewires +rewire +wired +wireds +wirehair +wirehairs +wirehair's +wireless +wirelesses +wireless's +wiretap +wiretaps +wiretap's +wiretapped +wiretapper +wiretappers +wiretapper's +wiretapping +wiretapping's +wiriness +wiriness's +wiring +wiring's +wiry +wiriest +wirier +wiriness +wisdom +wisdom's +wise +wisely +wising +wised +wisest +wiser +wises +wise's +wiseacre +wiseacres +wiseacre's +wisecrack +wisecracking +wisecracked +wisecracks +wisecrack's +wiseguy +wiseguys +wish +wishing +wished +wisher +wishers +wishes +wish's +wishbone +wishbones +wishbone's +wisher +wisher's +wishful +wishfully +wishlist's +wisp +wisps +wisp's +wispy +wispiest +wispier +wist +wisteria +wisterias +wisteria's +wistful +wistfully +wistfulness +wistfulness +wistfulness's +wit +wits +wit's +witch +witching +witched +witches +witch's +witchcraft +witchcraft's +witchery +witchery's +with +withal +withdraw +withdrawing +withdraws +withdrawal +withdrawals +withdrawal's +withdrawn +withdrew +withe +withing +withed +wither +withers +withes +withe's +wither +withering +witherings +withered +withering +witheringly +withers +withers's +withheld +withhold +withholding +withholds +withholding +withholding's +within +within's +without +withstand +withstanding +withstands +withstood +witless +witlessly +witlessness +witlessness +witlessness's +witness +witnessing +witnessed +witnesses +witness's +wits +wits's +witted +witter +wittering +wittered +witters +witticism +witticisms +witticism's +wittily +wittiness +wittiness's +witting +wittingly +unwittingly +unwitting +witty +wittiest +wittier +wittiness +wive +wiving +wived +wives +wiz +wizard +wizardly +wizards +wizard's +wizardry +wizardry's +wizened +wk +wkly +woad +woad's +wobble +wobbling +wobbled +wobbles +wobble's +wobbliness +wobbliness's +wobbly +wobbliest +wobblier +wobbliness +wodge +wodges +woe +woes +woe's +woebegone +woeful +woefully +woefulness +woefuller +woefullest +woefulness +woefulness's +wog +wogs +wok +woken +woks +wok's +woke +wold +wolds +wold's +wolf +wolfing +wolfed +wolfs +wolf's +wolfhound +wolfhounds +wolfhound's +wolfish +wolfram +wolfram's +wolverine +wolverines +wolverine's +wolves +woman +woman's +womanhood +womanhood's +womanish +womanize +womanizing +womanized +womanizer +womanizers +womanizes +womanizer +womanizer's +womankind +womankind's +womanlike +womanlike's +womanliness +womanliness's +womanly +womanliest +womanlier +womanliness +womb +wombs +womb's +wombat +wombats +wombat's +womble +wombles +women +women's +womenfolk +womenfolks +womenfolk's +womenfolks +womenfolks's +won't +won +won's +wonder +wondering +wondered +wonders +wonder's +wonderment +wonderful +wonderfully +wonderfulness +wonderfulness +wonderfulness's +wondering +wonderingly +wonderland +wonderlands +wonderland's +wonderment +wonderment's +wondrous +wondrously +wonk +wonks +wonk's +wonky +wonkiest +wonkier +wont +wonted +wont's +wonted +unwonted +woo +wooing +wooed +wooer +wooers +woos +wood +wooden +wooding +wooded +woods +wood's +woodbine +woodbine's +woodblock +woodblocks +woodblock's +woodcarver +woodcarvers +woodcarver's +woodcarving +woodcarvings +woodcarving's +woodchuck +woodchucks +woodchuck's +woodcock +woodcocks +woodcock's +woodcraft +woodcraft's +woodcut +woodcuts +woodcut's +woodcutter +woodcutters +woodcutter's +woodcutting +woodcutting's +wooden +woodenly +woodenest +woodener +woodenness +woodenness +woodenness's +woodiness +woodiness's +woodland +woodlands +woodland's +woodlice +woodlot +woodlots +woodlot's +woodlouse +woodman +woodman's +woodmen +woodpecker +woodpeckers +woodpecker's +woodpile +woodpiles +woodpile's +woods +woods's +woodshed +woodsheds +woodshed's +woodsiness +woodsiness's +woodsman +woodsman's +woodsmen +woodsy +woodsiest +woodsier +woodsiness +woodwind +woodwinds +woodwind's +woodwork +woodworking +woodworker +woodworkers +woodwork's +woodworker +woodworker's +woodworking +woodworking's +woodworm +woodworms +woody +woodiest +woodier +woodies +woodiness +woody's +wooer +wooer's +woof +woofing +woofed +woofer +woofers +woofs +woof's +woofer +woofer's +wool +woolen +woolens +wool's +woolen +woolen's +woolgathering +woolgathering's +wooliness +woolliness +woolliness's +woolly +woolliest +woollier +woollies +woolliness +woolly's +woozily +wooziness +wooziness's +woozy +wooziest +woozier +wooziness +wop +wops +word's +word +wording +worded +words +rewording +reworded +rewords +reword +wordage +wordage's +wordbook +wordbooks +wordbook's +wordily +wordiness +wordiness's +wording +wordings +wording's +wordless +wordlessly +wordplay +wordplay's +wordsmith +wordsmiths +wordy +wordiest +wordier +wordiness +wore +work's +work +working +workings +worked +works +reworking +reworkings +reworked +reworks +rework +workable +unworkable +workaday +workaholic +workaholics +workaholic's +workaround +workarounds +workbasket +workbaskets +workbench +workbenches +workbench's +workbook +workbooks +workbook's +workday +workdays +workday's +worker +workers +worker's +workfare +workfare's +workflow +workflows +workflow's +workforce +workforce's +workhorse +workhorses +workhorse's +workhouse +workhouses +workhouse's +working's +workingman +workingman's +workingmen +workings +workings's +workingwoman +workingwoman's +workingwomen +workload +workloads +workload's +workman +workman's +workmanlike +workmanship +workmanship's +workmate +workmates +workmen +workout +workouts +workout's +workplace +workplaces +workplace's +workroom +workrooms +workroom's +works +works's +worksheet +worksheets +worksheet's +workshop +workshops +workshop's +workshy +worksite +worksites +workspace +workstation +workstations +workstation's +worktable +worktables +worktable's +worktop +worktops +workup +workups +workup's +workweek +workweeks +workweek's +world +worlds +world's +worldlier +worldliness +worldliness's +unworldliness's +unworldliness +worldly +worldliest +worldliness +unworldliness +unworldly +worldview +worldviews +worldview's +worldwide +worm +worming +wormed +worms +worm's +wormhole +wormholes +wormhole's +wormwood +wormwood's +wormy +wormiest +wormier +worn +unworn +worried +worriedly +worrier +worrier's +worriment +worriment's +worrisome +worry +worrying +worryings +worried +worrier +worriers +worries +worry's +worrying +worryingly +worrywart +worrywarts +worrywart's +worse +worse's +worsen +worsening +worsened +worsens +worship +worshiping +worshiped +worshiper +worshipers +worships +worship's +worshiper +worshiper's +worshipful +worst +worsting +worsted +worsts +worst's +worsted +worsted's +wort +wort's +worth +worth's +worthies +worthily +unworthily +worthiness +worthiness's +unworthiness's +unworthiness +worthless +worthlessly +worthlessness +worthlessness +worthlessness's +worthwhile +worthy's +worthy +worthiest +worthier +worthiness +unworthier +unworthiness +unworthy +wot +wotcha +would've +would +woulds +wouldn't +wouldst +wound +wounding +wounded +wounder +wounds +wound's +wove +rewove +woven +rewoven +unwoven +wow +wowing +wowed +wows +wow's +wpm +wrack +wracking +wracked +wracks +wrack's +wraith +wraith's +wraiths +wrangle +wrangling +wranglings +wrangled +wrangler +wranglers +wrangles +wrangle's +wrangler +wrangler's +wrap's +wrap +wraps +unwraps +unwrap +wraparound +wraparounds +wraparound's +wrapped +unwrapped +wrapper +wrappers +wrapper's +wrapping +wrappings +wrapping's +wrasse +wrasses +wrasse's +wrath +wrath's +wrathful +wrathfully +wreak +wreaking +wreaked +wreaks +wreath +wreathing +wreathed +wreathes +wreath's +wreathe +wreaths +wreck +wrecking +wrecked +wrecker +wreckers +wrecks +wreck's +wreckage +wreckage's +wrecker +wrecker's +wren +wrens +wren's +wrench +wrenching +wrenched +wrenches +wrench's +wrest +wresting +wrested +wrests +wrest's +wrestle +wrestling +wrestled +wrestler +wrestlers +wrestles +wrestle's +wrestler +wrestler's +wrestling +wrestling's +wretch +wretches +wretch's +wretched +wretchedly +wretchedest +wretcheder +wretchedness +wretchedness +wretchedness's +wriggle +wriggling +wriggled +wriggler +wrigglers +wriggles +wriggle's +wriggler +wriggler's +wriggly +wright +wrights +wright's +wring +wringing +wringer +wringers +wrings +wring's +wringer +wringer's +wrinkle +wrinkling +wrinkled +wrinkles +wrinkle's +wrinkled +unwrinkled +wrinkly +wrinkliest +wrinklier +wrinklies +wrinkly's +wrist +wrists +wrist's +wristband +wristbands +wristband's +wristwatch +wristwatches +wristwatch's +writ +writing +writings +writer +writers +writs +writ's +writable +write +writes +writer +writer's +writhe +writhing +writhed +writhes +writhe's +writing +writing's +written +rewritten +unwritten +wrong +wrongly +wronging +wronged +wrongest +wronger +wrongs +wrongness +wrong's +wrongdoer +wrongdoers +wrongdoer's +wrongdoing +wrongdoings +wrongdoing's +wrongful +wrongfully +wrongfulness +wrongfulness +wrongfulness's +wrongheaded +wrongheadedly +wrongheadedness +wrongheadedness +wrongheadedness's +wrongness +wrongness's +wrote +rewrote +wroth +wrought +wrung +wry +wryly +wryer +wryest +wryness +wryness's +wt +wunderkind +wunderkinds +wurst +wursts +wurst's +wuss +wusses +wuss's +wussy +wussiest +wussier +wussies +wussy's +x +xci +xcii +xciv +xcix +xcvi +xcvii +xenon +xenon's +xenophobe +xenophobes +xenophobe's +xenophobia +xenophobia's +xenophobic +xerographic +xerography +xerography's +xerox +xeroxing +xeroxed +xeroxes +xerox's +xi +xis +xi's +xii +xiii +xiv +xix +xor +xref +xrefs +xterm +xterm's +xv +xvi +xvii +xviii +xx +xxi +xxii +xxiii +xxiv +xxix +xxv +xxvi +xxvii +xxviii +xxx +xxxi +xxxii +xxxiii +xxxiv +xxxix +xxxv +xxxvi +xxxvii +xxxviii +xylem +xylem's +xylene +xylophone +xylophones +xylophone's +xylophonist +xylophonists +xylophonist's +y'all +y +cony +ya +yacht +yachting +yachted +yachts +yacht's +yachting +yachting's +yachtsman +yachtsman's +yachtsmen +yachtswoman +yachtswoman's +yachtswomen +yahoo +yahoos +yahoo's +yak +yaks +yak's +yakked +yakking +yam +yams +yam's +yammer +yammering +yammered +yammerer +yammerers +yammers +yammer's +yammerer +yammerer's +yang +yang's +yank +yanking +yanked +yanks +yank's +yap +yaps +yap's +yapped +yapping +yard +yards +yard's +yardage +yardages +yardage's +yardarm +yardarms +yardarm's +yardman +yardman's +yardmaster +yardmasters +yardmaster's +yardmen +yardstick +yardsticks +yardstick's +yarmulke +yarmulkes +yarmulke's +yarn +yarns +yarn's +yarrow +yarrow's +yashmak +yashmaks +yaw +yawing +yawed +yaws +yaw's +yawl +yawls +yawl's +yawn +yawning +yawned +yawner +yawners +yawns +yawn's +yawner +yawner's +yaws +yaws's +yd +ye +yest +yer +yes +yea +yeas +yea's +yeah +yeah's +yeahs +year +yearly +years +year's +yearbook +yearbooks +yearbook's +yearling +yearlings +yearling's +yearlong +yearly +yearlies +yearly's +yearn +yearning +yearnings +yearned +yearns +yearning +yearning's +yeast +yeasts +yeast's +yeasty +yeastiest +yeastier +yegg +yeggs +yegg's +yell +yelling +yelled +yells +yell's +yellow +yellowing +yellowed +yellowest +yellower +yellows +yellowness +yellow's +yellowhammer +yellowhammers +yellowish +yellowness +yellowness's +yellowy +yelp +yelping +yelped +yelps +yelp's +yen +yens +yen's +yeoman +yeoman's +yeomanry +yeomanry's +yeomen +yep +yeps +yep's +yes +yeses +yes's +yeshiva +yeshivas +yeshiva's +yessed +yessing +yesterday +yesterdays +yesterday's +yesteryear +yesteryear's +yet +yeti +yetis +yeti's +yew +yews +yew's +yid +yids +yield +yielding +yieldings +yielded +yields +yield's +yikes +yin +yin's +yip +yips +yip's +yipe +yipped +yippee +yipping +yo +yob +yobs +yobbo +yobbos +yodel +yodeling +yodeled +yodeler +yodelers +yodels +yodel's +yodeler +yodeler's +yoga +yoga's +yogi +yogis +yogi's +yogic +yogurt +yogurts +yogurt's +yoke's +yoke +yoking +yoked +yokes +unyoking +unyoked +unyokes +unyoke +yokel +yokels +yokel's +yolk +yolked +yolks +yolk's +yon +yonder +yonks +yore +yore's +you'd +you'll +you're +you've +you +youth +yous +you's +young +youngest +younger +young's +youngish +youngster +youngsters +youngster's +your +yours +yourself +yourselves +youth +youth's +youthful +youthfully +youthfulness +youthfulness +youthfulness's +youths +yow +yowl +yowling +yowled +yowls +yowl's +yr +yrs +ytterbium +ytterbium's +yttrium +yttrium's +yuan +yuan's +yucca +yuccas +yucca's +yuck +yucky +yuckiest +yuckier +yuk +yuks +yuk's +yukked +yukking +yukky +yule +yule's +yuletide +yuletide's +yum +yummy +yummiest +yummier +yup +yups +yup's +yuppie +yuppies +yuppie's +yuppify +yuppifying +yuppified +yuppifies +yurt +yurts +yurt's +z +zen +zens +zing +zings +zed +zest +zaniness +zaniness's +zany +zaniest +zanier +zanies +zaniness +zany's +zap +zaps +zap's +zapped +zapper +zappers +zapper's +zapping +zappy +zeal +zeal's +zealot +zealots +zealot's +zealotry +zealotry's +zealous +zealously +zealousness +zealousness +zealousness's +zebra +zebras +zebra's +zebu +zebus +zebu's +zed +zeds +zed's +zeitgeist +zeitgeists +zeitgeist's +zenith +zenith's +zeniths +zenned +zeolite +zeolites +zephyr +zephyrs +zephyr's +zeppelin +zeppelins +zeppelin's +zero +zeroth +zeroing +zeroed +zeros +zero's +zeroes +zest +zests +zest's +zestful +zestfully +zestfulness +zestfulness +zestfulness's +zesty +zestiest +zestier +zeta +zetas +zeta's +zigzag +zigzags +zigzag's +zigzagged +zigzagging +zilch +zilch's +zillion +zillions +zillion's +zinc +zincs +zinc's +zincked +zincking +zine +zines +zinfandel +zinfandel's +zing +zinging +zinged +zinger +zingers +zing's +zinger +zinger's +zingy +zingiest +zingier +zinnia +zinnias +zinnia's +zip's +zip +zips +unzips +unzip +zipped +unzipped +zipper +zippering +zippered +zippers +zipper's +zipping +unzipping +zippy +zippiest +zippier +zircon +zircons +zircon's +zirconium +zirconium's +zit +zits +zit's +zither +zithers +zither's +zloty +zloties +zloty's +zlotys +zodiac +zodiacs +zodiac's +zodiacal +zombie +zombies +zombie's +zonal +zonally +zone's +zone +zoning +zoned +zones +rezoning +rezoned +rezones +rezone +zoning +zoning's +zonked +zoo +zoos +zoo's +zookeeper +zookeepers +zookeeper's +zoological +zoologically +zoologist +zoologists +zoologist's +zoology +zoology's +zoom +zooming +zoomed +zooms +zoom's +zoophyte +zoophytes +zoophyte's +zoophytic +zooplankton +zorch +zoster +zounds +zucchini +zucchinis +zucchini's +zwieback +zwieback's +zydeco +zydeco's +zygote +zygotes +zygote's +zygotic +zymurgy +zymurgy's diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/EnumsShouldHavePluralNames.cs b/src/RoslynAnalyzers/Text.Analyzers/Core/EnumsShouldHavePluralNames.cs new file mode 100644 index 0000000000000..f56f35ea5a0f2 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/EnumsShouldHavePluralNames.cs @@ -0,0 +1,117 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Humanizer; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Text.Analyzers +{ + using static TextAnalyzersResources; + + /// + /// CA1714: + /// CA1717: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class EnumsShouldHavePluralNamesAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId_Plural = "CA1714"; + + internal static readonly DiagnosticDescriptor Rule_CA1714 = + DiagnosticDescriptorHelper.Create( + RuleId_Plural, + CreateLocalizableResourceString(nameof(FlagsEnumsShouldHavePluralNamesTitle)), + CreateLocalizableResourceString(nameof(FlagsEnumsShouldHavePluralNamesMessage)), + DiagnosticCategory.Naming, + RuleLevel.Disabled, + description: CreateLocalizableResourceString(nameof(FlagsEnumsShouldHavePluralNamesDescription)), + isPortedFxCopRule: true, + isDataflowRule: false); + + internal const string RuleId_NoPlural = "CA1717"; + + internal static readonly DiagnosticDescriptor Rule_CA1717 = + DiagnosticDescriptorHelper.Create( + RuleId_NoPlural, + CreateLocalizableResourceString(nameof(OnlyFlagsEnumsShouldHavePluralNamesTitle)), + CreateLocalizableResourceString(nameof(OnlyFlagsEnumsShouldHavePluralNamesMessage)), + DiagnosticCategory.Naming, + RuleLevel.Disabled, + description: CreateLocalizableResourceString(nameof(OnlyFlagsEnumsShouldHavePluralNamesDescription)), + isPortedFxCopRule: true, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule_CA1714, Rule_CA1717); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(compilationContext => + { + INamedTypeSymbol? flagsAttribute = compilationContext.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemFlagsAttribute); + if (flagsAttribute == null) + { + return; + } + + compilationContext.RegisterSymbolAction(symbolContext => AnalyzeSymbol(symbolContext, flagsAttribute), SymbolKind.NamedType); + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol flagsAttribute) + { + var symbol = (INamedTypeSymbol)context.Symbol; + if (symbol.TypeKind != TypeKind.Enum) + { + return; + } + + var reportCA1714 = context.Options.MatchesConfiguredVisibility(Rule_CA1714, symbol, context.Compilation); + var reportCA1717 = context.Options.MatchesConfiguredVisibility(Rule_CA1717, symbol, context.Compilation); + if (!reportCA1714 && !reportCA1717) + { + return; + } + + if (symbol.Name.EndsWith("i", StringComparison.OrdinalIgnoreCase) || symbol.Name.EndsWith("ae", StringComparison.OrdinalIgnoreCase)) + { + // Skip words ending with 'i' and 'ae' to avoid flagging irregular plurals. + // Humanizer does not recognize these as plurals, such as 'formulae', 'trophi', etc. + return; + } + + if (!symbol.Name.IsASCII()) + { + // Skip non-ASCII names. + return; + } + + if (symbol.HasAnyAttribute(flagsAttribute)) + { + if (reportCA1714 && !IsPlural(symbol.Name)) // Checking Rule CA1714 + { + context.ReportDiagnostic(symbol.CreateDiagnostic(Rule_CA1714, symbol.OriginalDefinition.Locations.First(), symbol.Name)); + } + } + else + { + if (reportCA1717 && IsPlural(symbol.Name)) // Checking Rule CA1717 + { + context.ReportDiagnostic(symbol.CreateDiagnostic(Rule_CA1717, symbol.OriginalDefinition.Locations.First(), symbol.Name)); + } + } + } + + private static bool IsPlural(string word) + => word.Equals(word.Pluralize(inputIsKnownToBeSingular: false), StringComparison.OrdinalIgnoreCase); + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.Fixer.cs b/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.Fixer.cs new file mode 100644 index 0000000000000..554fa6a98f8ac --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.Fixer.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; + +namespace Text.Analyzers +{ + /// + /// CA1704: Identifiers should be spelled correctly + /// + public abstract class IdentifiersShouldBeSpelledCorrectlyFixer : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Empty; + + public sealed override FixAllProvider GetFixAllProvider() + { + // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/FixAllProvider.md for more information on Fix All Providers + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override Task RegisterCodeFixesAsync(CodeFixContext context) + { + // Fixer not yet implemented. + return Task.CompletedTask; + + } + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.cs b/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.cs new file mode 100644 index 0000000000000..32fd7a6e4f58c --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/IdentifiersShouldBeSpelledCorrectly.cs @@ -0,0 +1,524 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Text; + +namespace Text.Analyzers +{ + using static TextAnalyzersResources; + + /// + /// CA1704: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class IdentifiersShouldBeSpelledCorrectlyAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId = "CA1704"; + + private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyTitle)); + private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyDescription)); + + private static readonly SourceTextValueProvider<(CodeAnalysisDictionary dictionary, Exception? exception)> s_xmlDictionaryProvider = new(static text => ParseDictionary(text, isXml: true)); + private static readonly SourceTextValueProvider<(CodeAnalysisDictionary dictionary, Exception? exception)> s_dicDictionaryProvider = new(static text => ParseDictionary(text, isXml: false)); + private static readonly CodeAnalysisDictionary s_mainDictionary = GetMainDictionary(); + + internal static readonly DiagnosticDescriptor FileParseRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyFileParse)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor AssemblyRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageAssembly)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor NamespaceRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageNamespace)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor TypeRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageType)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor VariableRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageVariable)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MemberRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMember)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MemberParameterRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMemberParameter)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor DelegateParameterRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageDelegateParameter)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor TypeTypeParameterRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageTypeTypeParameter)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MethodTypeParameterRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMethodTypeParameter)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor AssemblyMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageAssemblyMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor NamespaceMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageNamespaceMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor TypeMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageTypeMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MemberMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMemberMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MemberParameterMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMemberParameterMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor DelegateParameterMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageDelegateParameterMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor TypeTypeParameterMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageTypeTypeParameterMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + internal static readonly DiagnosticDescriptor MethodTypeParameterMoreMeaningfulNameRule = DiagnosticDescriptorHelper.Create( + RuleId, + s_localizableTitle, + CreateLocalizableResourceString(nameof(IdentifiersShouldBeSpelledCorrectlyMessageMethodTypeParameterMoreMeaningfulName)), + DiagnosticCategory.Naming, + RuleLevel.BuildWarning, + description: s_localizableDescription, + isPortedFxCopRule: true, + isDataflowRule: false); + + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create( + FileParseRule, + AssemblyRule, + NamespaceRule, + TypeRule, + VariableRule, + MemberRule, + MemberParameterRule, + DelegateParameterRule, + TypeTypeParameterRule, + MethodTypeParameterRule, + AssemblyMoreMeaningfulNameRule, + NamespaceMoreMeaningfulNameRule, + TypeMoreMeaningfulNameRule, + MemberMoreMeaningfulNameRule, + MemberParameterMoreMeaningfulNameRule, + DelegateParameterMoreMeaningfulNameRule, + TypeTypeParameterMoreMeaningfulNameRule, + MethodTypeParameterMoreMeaningfulNameRule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext context) + { + var cancellationToken = context.CancellationToken; + + var dictionaries = ReadDictionaries().Add(s_mainDictionary); + + context.RegisterOperationAction(AnalyzeVariable, OperationKind.VariableDeclarator); + context.RegisterCompilationEndAction(AnalyzeAssembly); + context.RegisterSymbolAction( + AnalyzeSymbol, + SymbolKind.Namespace, + SymbolKind.NamedType, + SymbolKind.Method, + SymbolKind.Property, + SymbolKind.Event, + SymbolKind.Field, + SymbolKind.Parameter); + + ImmutableArray ReadDictionaries() + { + var fileProvider = AdditionalFileProvider.FromOptions(context.Options); + return fileProvider.GetMatchingFiles(@"(?:dictionary|custom).*?\.(?:xml|dic)$") + .Select(GetOrCreateDictionaryFromAdditionalText) + .Where(x => x != null) + .ToImmutableArray(); + } + + CodeAnalysisDictionary GetOrCreateDictionaryFromAdditionalText(AdditionalText additionalText) + { + var isXml = additionalText.Path.EndsWith(".xml", StringComparison.OrdinalIgnoreCase); + var provider = isXml ? s_xmlDictionaryProvider : s_dicDictionaryProvider; + + var (dictionary, exception) = context.TryGetValue(additionalText.GetTextOrEmpty(cancellationToken), provider, out var result) + ? result + : default; + + if (exception != null) + { + var diagnostic = Diagnostic.Create(FileParseRule, Location.None, additionalText.Path, exception.Message); + context.RegisterCompilationEndAction(x => x.ReportDiagnostic(diagnostic)); + } + + return dictionary!; + } + + void AnalyzeVariable(OperationAnalysisContext operationContext) + { + var variableOperation = (IVariableDeclaratorOperation)operationContext.Operation; + var variable = variableOperation.Symbol; + + ReportDiagnosticsForSymbol(variable, variable.Name, operationContext.ReportDiagnostic, checkForUnmeaningful: false); + } + + void AnalyzeAssembly(CompilationAnalysisContext context) + { + var assembly = context.Compilation.Assembly; + + ReportDiagnosticsForSymbol(assembly, assembly.Name, context.ReportDiagnostic); + } + + void AnalyzeSymbol(SymbolAnalysisContext symbolContext) + { + var typeParameterDiagnostics = Enumerable.Empty(); + + ISymbol symbol = symbolContext.Symbol; + if (symbol.IsOverride) + { + return; + } + + var symbolName = symbol.Name; + switch (symbol) + { + case IFieldSymbol: + symbolName = RemovePrefixIfPresent('_', symbolName); + break; + + case IMethodSymbol method: + switch (method.MethodKind) + { + case MethodKind.PropertyGet: + case MethodKind.PropertySet: + return; + + case MethodKind.Constructor: + case MethodKind.StaticConstructor: + symbolName = symbol.ContainingType.Name; + break; + } + + foreach (var typeParameter in method.TypeParameters) + { + ReportDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent('T', typeParameter.Name), symbolContext.ReportDiagnostic); + } + + break; + + case INamedTypeSymbol type: + if (type.TypeKind == TypeKind.Interface) + { + symbolName = RemovePrefixIfPresent('I', symbolName); + } + + foreach (var typeParameter in type.TypeParameters) + { + ReportDiagnosticsForSymbol(typeParameter, RemovePrefixIfPresent('T', typeParameter.Name), symbolContext.ReportDiagnostic); + } + + break; + + case IParameterSymbol parameter: + //check if the member this parameter is part of is an override/interface implementation + if (parameter.ContainingSymbol.IsImplementationOfAnyImplicitInterfaceMember() || parameter.ContainingSymbol.IsImplementationOfAnyExplicitInterfaceMember() || parameter.ContainingSymbol.IsOverride) + { + if (NameMatchesBase(parameter)) + { + return; + } + } + + break; + } + + ReportDiagnosticsForSymbol(symbol, symbolName, symbolContext.ReportDiagnostic); + } + + void ReportDiagnosticsForSymbol(ISymbol symbol, string symbolName, Action reportDiagnostic, bool checkForUnmeaningful = true) + { + foreach (var misspelledWord in GetMisspelledWords(symbolName)) + { + reportDiagnostic(GetMisspelledWordDiagnostic(symbol, misspelledWord)); + } + + if (checkForUnmeaningful && symbolName.Length == 1) + { + reportDiagnostic(GetUnmeaningfulIdentifierDiagnostic(symbol, symbolName)); + } + } + + IEnumerable GetMisspelledWords(string symbolName) + { + var parser = new WordParser(symbolName, WordParserOptions.SplitCompoundWords); + + string? word; + while ((word = parser.NextWord()) != null) + { + if (!IsWordAcronym(word) && !IsWordNumeric(word) && !IsWordSpelledCorrectly(word)) + { + yield return word; + } + } + } + + static bool IsWordAcronym(string word) => word.All(char.IsUpper); + + static bool IsWordNumeric(string word) => char.IsDigit(word[0]); + + bool IsWordSpelledCorrectly(string word) + { + return !dictionaries.Any(static (d, word) => d.ContainsUnrecognizedWord(word), word) && dictionaries.Any(static (d, word) => d.ContainsRecognizedWord(word), word); + } + } + + /// + /// check if the parameter matches the name of the parameter in any base implementation + /// + /// + /// + private static bool NameMatchesBase(IParameterSymbol parameter) + { + if (parameter.ContainingSymbol is IMethodSymbol methodSymbol) + { + ImmutableArray originalDefinitions = methodSymbol.GetOriginalDefinitions(); + + foreach (var methodDefinition in originalDefinitions) + { + if (methodDefinition.Parameters.Length > parameter.Ordinal) + { + if (methodDefinition.Parameters[parameter.Ordinal].Name == parameter.Name) + + return true; + } + } + } + else if (parameter.ContainingSymbol is IPropertySymbol propertySymbol) + { + ImmutableArray originalDefinitions = propertySymbol.GetOriginalDefinitions(); + + foreach (var propertyDefinition in originalDefinitions) + { + if (propertyDefinition.Parameters.Length > parameter.Ordinal) + { + if (propertyDefinition.Parameters[parameter.Ordinal].Name == parameter.Name) + + return true; + } + } + } + + //name either does not match or there was an issue getting the base implementation + return false; + } + + private static CodeAnalysisDictionary GetMainDictionary() + { + // The "main" dictionary, Dictionary.dic, was created in WSL Ubuntu with the following commands: + // + // Install dependencies: + // > sudo apt install hunspell-tools hunspell-en-us + // + // Create dictionary: + // > unmunch /usr/share/hunspell/en_US.dic /usr/share/hunspell/en_US.aff > Dictionary.dic + // + // Tweak: + // Added the words: 'namespace' + var text = SourceText.From(TextAnalyzersResources.Dictionary); + return ParseDicDictionary(text); + } + + private static (CodeAnalysisDictionary dictionary, Exception? exception) ParseDictionary(SourceText text, bool isXml) + { + try + { + return (isXml ? ParseXmlDictionary(text) : ParseDicDictionary(text), exception: null); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + return (null!, ex); + } + } + + private static CodeAnalysisDictionary ParseXmlDictionary(SourceText text) + => text.Parse(CodeAnalysisDictionary.CreateFromXml); + + private static CodeAnalysisDictionary ParseDicDictionary(SourceText text) + => text.Parse(CodeAnalysisDictionary.CreateFromDic); + + private static string RemovePrefixIfPresent(char prefix, string name) + => name.Length > 0 && name[0] == prefix ? name[1..] : name; + + private static Diagnostic GetMisspelledWordDiagnostic(ISymbol symbol, string misspelledWord) + { + return symbol.Kind switch + { + SymbolKind.Assembly => symbol.CreateDiagnostic(AssemblyRule, misspelledWord, symbol.Name), + SymbolKind.Namespace => symbol.CreateDiagnostic(NamespaceRule, misspelledWord, symbol.ToDisplayString()), + SymbolKind.NamedType => symbol.CreateDiagnostic(TypeRule, misspelledWord, symbol.ToDisplayString()), + SymbolKind.Method or SymbolKind.Property or SymbolKind.Event or SymbolKind.Field + => symbol.CreateDiagnostic(MemberRule, misspelledWord, symbol.ToDisplayString()), + SymbolKind.Parameter => symbol.ContainingType.TypeKind == TypeKind.Delegate + ? symbol.CreateDiagnostic(DelegateParameterRule, symbol.ContainingType.ToDisplayString(), misspelledWord, symbol.Name) + : symbol.CreateDiagnostic(MemberParameterRule, symbol.ContainingSymbol.ToDisplayString(), misspelledWord, symbol.Name), + SymbolKind.TypeParameter => symbol.ContainingSymbol.Kind == SymbolKind.Method + ? symbol.CreateDiagnostic(MethodTypeParameterRule, symbol.ContainingSymbol.ToDisplayString(), misspelledWord, symbol.Name) + : symbol.CreateDiagnostic(TypeTypeParameterRule, symbol.ContainingSymbol.ToDisplayString(), misspelledWord, symbol.Name), + SymbolKind.Local => symbol.CreateDiagnostic(VariableRule, misspelledWord, symbol.ToDisplayString()), + _ => throw new NotImplementedException($"Unknown SymbolKind: {symbol.Kind}"), + }; + } + + private static Diagnostic GetUnmeaningfulIdentifierDiagnostic(ISymbol symbol, string symbolName) + { + return symbol.Kind switch + { + SymbolKind.Assembly => symbol.CreateDiagnostic(AssemblyMoreMeaningfulNameRule, symbolName), + SymbolKind.Namespace => symbol.CreateDiagnostic(NamespaceMoreMeaningfulNameRule, symbolName), + SymbolKind.NamedType => symbol.CreateDiagnostic(TypeMoreMeaningfulNameRule, symbolName), + SymbolKind.Method or SymbolKind.Property or SymbolKind.Event or SymbolKind.Field + => symbol.CreateDiagnostic(MemberMoreMeaningfulNameRule, symbolName), + SymbolKind.Parameter => symbol.ContainingType.TypeKind == TypeKind.Delegate + ? symbol.CreateDiagnostic(DelegateParameterMoreMeaningfulNameRule, symbol.ContainingType.ToDisplayString(), symbolName) + : symbol.CreateDiagnostic(MemberParameterMoreMeaningfulNameRule, symbol.ContainingSymbol.ToDisplayString(), symbolName), + SymbolKind.TypeParameter => symbol.ContainingSymbol.Kind == SymbolKind.Method + ? symbol.CreateDiagnostic(MethodTypeParameterMoreMeaningfulNameRule, symbol.ContainingSymbol.ToDisplayString(), symbol.Name) + : symbol.CreateDiagnostic(TypeTypeParameterMoreMeaningfulNameRule, symbol.ContainingSymbol.ToDisplayString(), symbol.Name), + _ => throw new NotImplementedException($"Unknown SymbolKind: {symbol.Kind}"), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/Text.Analyzers.csproj b/src/RoslynAnalyzers/Text.Analyzers/Core/Text.Analyzers.csproj new file mode 100644 index 0000000000000..bd0e5b5d4f6b2 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/Text.Analyzers.csproj @@ -0,0 +1,32 @@ + + + + netstandard2.0 + + *$(MSBuildProjectFile)* + $(MicrosoftCodeAnalysisVersionForTextAnalyzers) + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.cs b/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.cs new file mode 100644 index 0000000000000..5e99665fdc31f --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.cs @@ -0,0 +1,20 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Text.Analyzers +{ + internal partial class TextAnalyzersResources + { + private static readonly Type s_resourcesType = typeof(TextAnalyzersResources); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType); + + public static LocalizableResourceString CreateLocalizableResourceString(string nameOfLocalizableResource, params string[] formatArguments) + => new(nameOfLocalizableResource, ResourceManager, s_resourcesType, formatArguments); + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.resx b/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.resx new file mode 100644 index 0000000000000..b6e09638b6d37 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/TextAnalyzersResources.resx @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Flags enums should have plural names + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + + + Flags enums should have plural names + + + Only FlagsAttribute enums should have plural names + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + + + Only FlagsAttribute enums should have plural names + + + Identifiers should be spelled correctly + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + + + Correct the spelling of '{0}' in assembly name '{1}' + + + Correct the spelling of '{0}' in namespace name '{1}' + + + Correct the spelling of '{0}' in type name '{1}' + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + + + Consider providing a more meaningful name than assembly name '{0}' + + + Consider providing a more meaningful name than namespace name '{0}' + + + Consider providing a more meaningful name than type name '{0}' + + + Consider providing a more meaningful name than member name '{0}' + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + + + Naming + + + Error parsing dictionary '{0}': {1} + + + Correct the spelling of '{0}' in variable name '{1}' + + + + Dictionary.dic;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.cs.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.cs.xlf new file mode 100644 index 0000000000000..2e6c9f5842a2f --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.cs.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Veřejný výčet obsahuje atribut System.FlagsAttribute, ale jeho název nekončí na s. Typy, které jsou označené atributem FlagsAttribute, mají názvy v množném čísle, protože tento atribut označuje, že se dá zadat více než jedna hodnota. + + + + Flags enums should have plural names + Výčty příznaků mají mít názvy v množném čísle. + + + + Flags enums should have plural names + Výčty příznaků mají mít názvy v množném čísle. + + + + Error parsing dictionary '{0}': {1} + Chyba při parsování slovníku {0}: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Opravte pravopis slova {0} v názvu proměnné {1}. + + + + Identifiers should be spelled correctly + Identifikátory nesmí obsahovat pravopisné chyby + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Název externě viditelného identifikátoru obsahuje minimálně jedno slovo, které knihovna kontroly pravopisu Microsoft nerozpozná. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Opravte pravopis slova {0} v názvu sestavení {1}. + + + + Correct the spelling of '{0}' in namespace name '{1}' + Opravte pravopis slova {0} v názvu oboru názvů {1}. + + + + Correct the spelling of '{0}' in type name '{1}' + Opravte pravopis slova {0} v názvu typu {1}. + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Opravte pravopis slova {0} v názvu členu {1}, nebo ho úplně odeberte, pokud představuje jakýkoli druh maďarské notace. + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + V metodě {0} opravte pravopis slova {1} v názvu parametru {2}, nebo ho úplně odeberte, pokud představuje jakýkoli druh maďarské notace. + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + V delegátovi {0} opravte pravopis slova {1} v názvu parametru {2}, nebo ho úplně odeberte, pokud představuje jakýkoli druh maďarské notace. + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + U typu {0} opravte pravopis slova {1} v názvu parametru obecného typu {2}. + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + U metody {0} opravte pravopis slova {1} v názvu parametru obecného typu {2}. + + + + Consider providing a more meaningful name than assembly name '{0}' + Zvažte zadání výstižnějšího názvu, než je název sestavení {0}. + + + + Consider providing a more meaningful name than namespace name '{0}' + Zvažte zadání výstižnějšího názvu, než je název oboru názvů {0}. + + + + Consider providing a more meaningful name than type name '{0}' + Zvažte zadání výstižnějšího názvu, než je název typu {0}. + + + + Consider providing a more meaningful name than member name '{0}' + Zvažte zadání výstižnějšího názvu, než je název členu {0}. + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + V metodě {0} zvažte zadání výstižnějšího názvu, než je název parametru {1}. + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + V delegátovi {0} zvažte zadání výstižnějšího názvu, než je název parametru {1}. + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + U typu {0} zvažte zadání výstižnějšího názvu, než je název parametru obecného typu {1}. + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + U metody {0} zvažte zadání výstižnějšího názvu, než je název parametru obecného typu {1}. + + + + Naming + Pojmenování + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Názvy v množném čísle podle zásad vytváření názvů označují, že lze současně zadat více než jednu hodnotu výčtu. + + + + Only FlagsAttribute enums should have plural names + Názvy v množném čísle mají mít jenom výčty FlagsAttribute. + + + + Only FlagsAttribute enums should have plural names + Názvy v množném čísle mají mít jenom výčty FlagsAttribute. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.de.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.de.xlf new file mode 100644 index 0000000000000..76536fe5baedc --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.de.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Eine öffentliche Enumeration weist das System.FlagsAttribute-Attribut auf, und der Name endet nicht auf ""s"". Mit FlagsAttribute markierte Typen besitzen Namen in Pluralform, weil das Attribut darauf hinweist, dass mehrere Werte angegeben werden können. + + + + Flags enums should have plural names + Flags-Enumerationen müssen Pluralnamen aufweisen + + + + Flags enums should have plural names + Flags-Enumerationen müssen Pluralnamen aufweisen + + + + Error parsing dictionary '{0}': {1} + Fehler beim Analysieren des Wörterbuchs "{0}": {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Korrigieren Sie die Schreibweise von "{0}" im Variablennamen "{1}". + + + + Identifiers should be spelled correctly + Bezeichner müssen richtig geschrieben werden + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Der Name eines extern sichtbaren Bezeichners enthält mindestens ein Wort, das von der Bibliothek der Microsoft-Rechtschreibprüfung nicht erkannt wird. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Korrigieren Sie die Schreibweise von "{0}" im Assemblynamen "{1}". + + + + Correct the spelling of '{0}' in namespace name '{1}' + Korrigieren Sie die Schreibweise von "{0}" im Namespacenamen "{1}". + + + + Correct the spelling of '{0}' in type name '{1}' + Korrigieren Sie die Schreibweise von "{0}" im Typnamen "{1}". + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Korrigieren Sie die Schreibweise von "{0}" im Membernamen "{1}", oder entfernen Sie das Element ganz, wenn es einen beliebigen Typ ungarischer Notation darstellt. + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Korrigieren Sie in der Methode "{0}" die Schreibweise von "{1}" im Parameternamen "{2}", oder entfernen Sie das Element ganz, wenn es einen beliebigen Typ ungarischer Notation darstellt. + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Korrigieren Sie im Delegaten "{0}" die Schreibweise von "{1}" im Parameternamen "{2}", oder entfernen Sie das Element ganz, wenn es einen beliebigen Typ ungarischer Notation darstellt. + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Korrigieren Sie für den Typ "{0}" die Schreibweise von "{1}" im Namen des generischen Typparameters ({2}). + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Korrigieren Sie für die Methode "{0}" die Schreibweise von "{1}" im Namen des generischen Typparameters ({2}). + + + + Consider providing a more meaningful name than assembly name '{0}' + Geben Sie einen aussagekräftigeren Namen als den Assemblynamen "{0}" an. + + + + Consider providing a more meaningful name than namespace name '{0}' + Geben Sie ggf. einen aussagekräftigeren Namen als den Namespacenamen "{0}" an. + + + + Consider providing a more meaningful name than type name '{0}' + Geben Sie einen aussagekräftigeren Namen als den Typnamen "{0}" an. + + + + Consider providing a more meaningful name than member name '{0}' + Geben Sie ggf. einen aussagekräftigeren Namen als den Membernamen "{0}" an. + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + Geben Sie in der Methode "{0}" einen aussagekräftigeren Namen als den Parameternamen "{1}" an. + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + Geben Sie im Delegaten "{0}" einen aussagekräftigeren Namen als den Parameternamen "{1}" an. + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Geben Sie für den Typ "{0}" einen aussagekräftigeren Namen als den Namen des generischen Typparameters ({1}) an. + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Geben Sie für die Methode "{0}" einen aussagekräftigeren Namen als den Namen des generischen Typparameters ({1}) an. + + + + Naming + Benennung + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Die Namenskonventionen schreiben vor, dass ein Pluralname für eine Enumeration darauf hinweist, dass mehrere Werte der Enumeration gleichzeitig angegeben werden können. + + + + Only FlagsAttribute enums should have plural names + Nur FlagsAttribute-Enumerationen dürfen Pluralnamen aufweisen + + + + Only FlagsAttribute enums should have plural names + Nur FlagsAttribute-Enumerationen dürfen Pluralnamen aufweisen + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.es.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.es.xlf new file mode 100644 index 0000000000000..b6762eac6003d --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.es.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Una enumeración pública tiene el atributo System.FlagsAttribute y su nombre no termina en "s". Los tipos marcados con FlagsAttribute tienen nombres en plural porque el atributo indica que se puede especificar más de un valor. + + + + Flags enums should have plural names + Las enumeraciones Flags deben tener nombres en plural + + + + Flags enums should have plural names + Las enumeraciones Flags deben tener nombres en plural + + + + Error parsing dictionary '{0}': {1} + Error al analizar el diccionario "{0}": {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Corrija la ortografía de "{0}" en el nombre de variable "{1}". + + + + Identifiers should be spelled correctly + Los identificadores deben estar escritos correctamente. + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + El nombre de un identificador visible externamente contiene una o varias palabras que la biblioteca del corrector ortográficos de Microsoft no reconoce. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Corrija la ortografía de "{0}" en el nombre de ensamblado "{1}". + + + + Correct the spelling of '{0}' in namespace name '{1}' + Corrija la ortografía de "{0}" en el nombre del espacio de nombres "{1}". + + + + Correct the spelling of '{0}' in type name '{1}' + Corrija la ortografía de "{0}" en el nombre de tipo "{1}". + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Corrija la ortografía de "{0}" en el nombre de miembro "{1}" o quítelo totalmente si representa algún tipo de notación húngara. + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + En el método "{0}", corrija la ortografía de "{1}" en el nombre de parámetro "{2}" o quítelo totalmente si representa algún tipo de notación húngara. + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + En el delegado "{0}", corrija la ortografía de "{1}" en el nombre de parámetro "{2}" o quítelo totalmente si representa algún tipo de notación húngara. + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + En el tipo "{0}", corrija la ortografía de "{1}" en el nombre de parámetro de tipo genérico "{2}". + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + En el método "{0}", corrija la ortografía de "{1}" en el nombre de parámetro de tipo genérico "{2}". + + + + Consider providing a more meaningful name than assembly name '{0}' + Considere proporcionar un nombre más significativo que el nombre del ensamblado "{0}". + + + + Consider providing a more meaningful name than namespace name '{0}' + Considere la posibilidad de proporcionar un nombre más significativo que el nombre del espacio de nombres {0}. + + + + Consider providing a more meaningful name than type name '{0}' + Considere la posibilidad de proporcionar un nombre más significativo que el nombre del tipo "{0}". + + + + Consider providing a more meaningful name than member name '{0}' + Considere la posibilidad de proporcionar un nombre más significativo que el nombre del miembro "{0}". + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + En el método "{0}", puede proporcionar un nombre más significativo que el nombre del parámetro "{1}". + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + En el delegado "{0}", puede proporcionar un nombre más significativo que el nombre del parámetro "{1}". + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + En el tipo "{0}", puede proporcionar un nombre más significativo que el nombre del parámetro de tipo genérico "{1}". + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + En el método "{0}", puede proporcionar un nombre más significativo que el nombre del parámetro de tipo genérico "{1}". + + + + Naming + Nomenclatura + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Las convenciones de nomenclatura dictan que un nombre en plural para una enumeración indica que se pueden especificar varios valores de la enumeración simultáneamente. + + + + Only FlagsAttribute enums should have plural names + Solo las enumeraciones FlagsAttribute deben tener nombres en plural + + + + Only FlagsAttribute enums should have plural names + Solo las enumeraciones FlagsAttribute deben tener nombres en plural + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf new file mode 100644 index 0000000000000..4c8ac81bcc1b9 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.fr.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Une énumération publique a l'attribut System.FlagsAttribute, et son nom finit par ""s"". Les types marqués à l'aide de FlagsAttribute ont des noms au pluriel, car l'attribut indique que plusieurs valeurs peuvent être spécifiées. + + + + Flags enums should have plural names + Les noms des enums Flags doivent être au pluriel + + + + Flags enums should have plural names + Les noms des enums Flags doivent être au pluriel + + + + Error parsing dictionary '{0}': {1} + Erreur durant l'analyse du dictionnaire '{0}' : {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Corrigez l'orthographe de '{0}' dans le nom de variable '{1}' + + + + Identifiers should be spelled correctly + L'orthographe des identificateurs doit être correcte + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Le nom d'un identificateur visible de manière externe contient un ou plusieurs mots qui ne sont pas reconnus par la bibliothèque du vérificateur d'orthographe Microsoft. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Corrigez l'orthographe de '{0}' dans le nom d'assembly '{1}' + + + + Correct the spelling of '{0}' in namespace name '{1}' + Corrigez l'orthographe de '{0}' dans le nom d'espace de noms '{1}' + + + + Correct the spelling of '{0}' in type name '{1}' + Corrigez l'orthographe de '{0}' dans le nom de type '{1}' + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Corrigez l'orthographe de '{0}' dans le nom de membre '{1}', ou supprimez-le intégralement s'il représente une forme quelconque de notation hongroise + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Dans la méthode '{0}', corrigez l'orthographe de '{1}' dans le nom de paramètre '{2}', ou supprimez-le intégralement s'il représente une forme quelconque de notation hongroise + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Dans le délégué '{0}', corrigez l'orthographe de '{1}' dans le nom de paramètre '{2}', ou supprimez-le intégralement s'il représente une forme quelconque de notation hongroise + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Dans le type '{0}', corrigez l'orthographe de '{1}' dans le nom de paramètre de type générique '{2}' + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Dans la méthode '{0}', corrigez l'orthographe de '{1}' dans le nom de paramètre de type générique '{2}' + + + + Consider providing a more meaningful name than assembly name '{0}' + Si possible, indiquez un nom plus significatif que le nom d'assembly '{0}' + + + + Consider providing a more meaningful name than namespace name '{0}' + Indiquez éventuellement un nom plus significatif que le nom d'espace de noms '{0}' + + + + Consider providing a more meaningful name than type name '{0}' + Si possible, indiquez un nom plus significatif que le nom de type '{0}' + + + + Consider providing a more meaningful name than member name '{0}' + Si possible, indiquez un nom plus significatif que le nom de membre '{0}' + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + Dans la méthode '{0}', si possible, indiquez un nom plus significatif que le nom de paramètre '{1}' + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + Dans le délégué '{0}', si possible, indiquez un nom plus significatif que le nom de paramètre '{1}' + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Dans le type '{0}', si possible, indiquez un nom plus significatif que le nom de paramètre de type générique '{1}' + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Dans la méthode '{0}', si possible, indiquez un nom plus significatif que le nom de paramètre de type générique '{1}' + + + + Naming + Nommage + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Conformément aux conventions de nommage, l'utilisation d'un nom pluriel pour une énumération indique que plusieurs valeurs de l'énumération peuvent être spécifiées en même temps. + + + + Only FlagsAttribute enums should have plural names + Seuls les noms des enums FlagsAttribute doivent être au pluriel + + + + Only FlagsAttribute enums should have plural names + Seuls les noms des enums FlagsAttribute doivent être au pluriel + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.it.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.it.xlf new file mode 100644 index 0000000000000..833e1cc210cff --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.it.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Un'enumerazione public include l'attributo System.FlagsAttribute e il relativo nome non termina con ""s"". Ai tipi contrassegnati con FlagsAttribute sono assegnati nomi plurali perché tale attributo indica che è possibile specificare più valori. + + + + Flags enums should have plural names + Alle enumerazioni con Flags devono essere assegnati nomi plurali + + + + Flags enums should have plural names + Alle enumerazioni con Flags devono essere assegnati nomi plurali + + + + Error parsing dictionary '{0}': {1} + Si è verificato un errore durante l'analisi del dizionario '{0}': {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Correggere l'ortografia di '{0}' nel nome di variabile '{1}' + + + + Identifiers should be spelled correctly + L'ortografia degli identificatori deve essere corretta + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Il nome di un identificatore visibile esternamente contiene una o più parole che non sono riconosciute dalla libreria del correttore ortografico Microsoft. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Correggere l'ortografia di '{0}' nel nome di assembly '{1}' + + + + Correct the spelling of '{0}' in namespace name '{1}' + Correggere l'ortografia di '{0}' nel nome di spazio dei nomi '{1}' + + + + Correct the spelling of '{0}' in type name '{1}' + Correggere l'ortografia di '{0}' nel nome di tipo '{1}' + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Correggere l'ortografia di '{0}' nel nome di membro '{1}' oppure rimuoverlo completamente se rappresenta un qualsiasi tipo di notazione ungherese + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Nel metodo '{0}' correggere l'ortografia di '{1}' nel nome di parametro '{2}' oppure rimuoverlo completamente se rappresenta un qualsiasi tipo di notazione ungherese + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + Nel delegato '{0}' correggere l'ortografia di '{1}' nel nome di parametro '{2}' oppure rimuoverlo completamente se rappresenta un qualsiasi tipo di notazione ungherese + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Nel tipo '{0}' correggere l'ortografia di '{1}' nel nome di parametro di tipo generico '{2}' + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + Nel metodo '{0}' correggere l'ortografia di '{1}' nel nome di parametro di tipo generico '{2}' + + + + Consider providing a more meaningful name than assembly name '{0}' + Provare a specificare un nome più significativo del nome di assembly '{0}' + + + + Consider providing a more meaningful name than namespace name '{0}' + Provare a specificare un nome più significativo del nome di spazio dei nomi '{0}' + + + + Consider providing a more meaningful name than type name '{0}' + Provare a specificare un nome più significativo del nome di tipo '{0}' + + + + Consider providing a more meaningful name than member name '{0}' + Provare a specificare un nome più significativo del nome di membro '{0}' + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + Nel metodo '{0}' provare a specificare un nome più significativo del nome di parametro '{1}' + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + Nel delegato '{0}' provare a specificare un nome più significativo del nome di parametro '{1}' + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Nel tipo '{0}' provare a specificare un nome più significativo del nome di parametro di tipo generico '{1}' + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + Nel metodo '{0}' provare a specificare un nome più significativo del nome di parametro di tipo generico '{1}' + + + + Naming + Denominazione + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + In base alle convenzioni di denominazione, un nome plurale in un'enumerazione indica che è possibile specificare contemporaneamente più valori dell'enumerazione. + + + + Only FlagsAttribute enums should have plural names + I nomi plurali devono essere assegnati solo alle enumerazioni con FlagsAttribute + + + + Only FlagsAttribute enums should have plural names + I nomi plurali devono essere assegnati solo alle enumerazioni con FlagsAttribute + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ja.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ja.xlf new file mode 100644 index 0000000000000..eb6d7b82d1996 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ja.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + パブリック列挙型に System.FlagsAttribute 属性があり、その名前の末尾に ""s"" がありません。FlagsAttribute を使用してマークされた型には複数形の名前が付きます。これは、この属性に複数の値を指定できることを示すためです。 + + + + Flags enums should have plural names + フラグ列挙型は、複数形の名前を含んでいなければなりません + + + + Flags enums should have plural names + フラグ列挙型は、複数形の名前を含んでいなければなりません + + + + Error parsing dictionary '{0}': {1} + ディクショナリ '{0}' の解析でエラーが発生しました: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + 変数名 '{1}' の '{0}' のスペルを訂正してください + + + + Identifiers should be spelled correctly + 識別子は正しく入力されなければなりません + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + 外部から参照可能な識別子の名前に、Microsoft スペル チェック ライブラリによって認識されない単語が 1 つ以上含まれています。 + + + + Correct the spelling of '{0}' in assembly name '{1}' + アセンブリ名 '{1}' の '{0}' のスペルを訂正してください + + + + Correct the spelling of '{0}' in namespace name '{1}' + 名前空間名 '{1}' の '{0}' のスペルを訂正してください + + + + Correct the spelling of '{0}' in type name '{1}' + 型名 '{1}' の '{0}' のスペルを訂正してください + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + メンバー名 '{1}' の '{0}' のスペルを訂正するか、または、それが何らかのハンガリアン記法を表す場合はその全体を削除してください + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + メソッド '{0}' で、パラメーター名 '{2}' の '{1}' のスペルを訂正するか、または、それが何らかのハンガリアン記法を表す場合はその全体を削除してください + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + デリゲート '{0}' で、パラメーター名 '{2}' の '{1}' のスペルを訂正するか、または、それが何らかのハンガリアン記法を表す場合はその全体を削除してください + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + 型 '{0}' で、ジェネリック型パラメーター名 '{2}' の '{1}' のスペルを訂正してください + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + メソッド '{0}' で、ジェネリック型パラメーター名 '{2}' の '{1}' のスペルを訂正してください + + + + Consider providing a more meaningful name than assembly name '{0}' + アセンブリ名 '{0}' よりも意味のある名前を指定することを検討してください + + + + Consider providing a more meaningful name than namespace name '{0}' + 名前空間名 '{0}' よりも意味のある名前を指定することを検討してください + + + + Consider providing a more meaningful name than type name '{0}' + 型名 '{0}' よりも意味のある名前を指定することを検討してください + + + + Consider providing a more meaningful name than member name '{0}' + メンバー名 '{0}' よりも意味のある名前を指定することを検討してください + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + メソッド '{0}' で、パラメーター名 '{1}' よりも意味のある名前を指定することを検討してください + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + デリゲート '{0}' で、パラメーター名 '{1}' よりも意味のある名前を指定することを検討してください + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + 型 '{0}' で、ジェネリック型パラメーター名 '{1}' よりも意味のある名前を指定することを検討してください + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + メソッド '{0}' で、ジェネリック型パラメーター名 '{1}' よりも意味のある名前を指定することを検討してください + + + + Naming + 名前付け + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + 名前付け規則では、列挙型の複数形の名前は同時に複数の列挙型を指定できることを意味します。 + + + + Only FlagsAttribute enums should have plural names + FlagsAttribute 列挙型のみが複数形の名前を含んでいなければなりません + + + + Only FlagsAttribute enums should have plural names + FlagsAttribute 列挙型のみが複数形の名前を含んでいなければなりません + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf new file mode 100644 index 0000000000000..ed320baed9d1f --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ko.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Public 열거형에는 System.FlagsAttribute 특성이 있으며 이 특성의 이름이 ""s""로 끝나지 않습니다. FlagsAttribute를 사용하여 표시되는 형식의 경우 이 특성이 하나 이상의 값이 지정될 수 있을 나타내므로 복수형의 이름을 사용합니다. + + + + Flags enums should have plural names + 플래그 열거형에는 복수형 이름을 사용해야 합니다. + + + + Flags enums should have plural names + 플래그 열거형에는 복수형 이름을 사용해야 합니다. + + + + Error parsing dictionary '{0}': {1} + 사전 '{0}'을(를) 구문 분석하는 동안 오류 발생: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + 변수 이름 '{1}'에서 '{0}'의 맞춤법을 수정하세요. + + + + Identifiers should be spelled correctly + 식별자의 맞춤법이 정확해야 합니다. + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + 외부에 표시되는 식별자의 이름에 Microsoft 맞춤법 검사기 라이브러리에서 인식할 수 없는 단어가 1개 이상 포함되어 있습니다. + + + + Correct the spelling of '{0}' in assembly name '{1}' + 어셈블리 이름 {1}에서 '{0}'의 맞춤법을 수정하세요. + + + + Correct the spelling of '{0}' in namespace name '{1}' + 네임스페이스 이름 '{1}'에서 '{0}'의 맞춤법을 수정하세요. + + + + Correct the spelling of '{0}' in type name '{1}' + 형식 이름 {1}에서 '{0}'의 맞춤법을 수정하세요. + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + 멤버 이름 {1}에서 '{0}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + {0} 메서드의 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + {0} 대리자의 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하거나, 헝가리 표기법을 나타내는 경우 완전히 제거하세요. + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + {0} 형식의 제네릭 형식 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하세요. + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + {0} 메서드의 제네릭 형식 매개 변수 이름 {2}에서 '{1}'의 맞춤법을 수정하세요. + + + + Consider providing a more meaningful name than assembly name '{0}' + 어셈블리 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + Consider providing a more meaningful name than namespace name '{0}' + 네임스페이스 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + Consider providing a more meaningful name than type name '{0}' + 형식 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + Consider providing a more meaningful name than member name '{0}' + 멤버 이름 {0}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + {0} 메서드에서 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + {0} 대리자에서 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + {0} 형식에서 제네릭 형식 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + {0} 메서드에서 제네릭 형식 매개 변수 이름 {1}보다 더 의미 있는 이름을 제공하는 것이 좋습니다. + + + + Naming + 명명 + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + 명명 규칙은 열거형의 복수형 이름이 하나 이상의 열거형 값이 동시에 지정될 수 있다는 것을 표시함을 나타냅니다. + + + + Only FlagsAttribute enums should have plural names + FlagsAttribute 열거형만 복수형 이름을 사용할 수 있습니다. + + + + Only FlagsAttribute enums should have plural names + FlagsAttribute 열거형만 복수형 이름을 사용할 수 있습니다. + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pl.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pl.xlf new file mode 100644 index 0000000000000..7a11e4caccf0e --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pl.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Wyliczenie publiczne ma atrybut System.FlagsAttribute, lecz jego nazwa nie kończy się literą „s”. Nazwy typów oznaczonych atrybutem FlagsAttribute mają nazwy w liczbie mnogiej, ponieważ ten atrybut wskazuje, że można określić więcej niż jedną wartość. + + + + Flags enums should have plural names + Wyliczenia z flagami powinny mieć nazwy w liczbie mnogiej + + + + Flags enums should have plural names + Wyliczenia z flagami powinny mieć nazwy w liczbie mnogiej + + + + Error parsing dictionary '{0}': {1} + Wystąpił błąd podczas analizowania słownika „{0}”: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Popraw pisownię wyrazu „{0}” w nazwie zmiennej „{1}” + + + + Identifiers should be spelled correctly + Pisownia identyfikatorów powinna być poprawna + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Nazwa identyfikatora widocznego zewnętrznie zawiera co najmniej jeden wyraz, który nie został rozpoznany przez bibliotekę sprawdzania pisowni Microsoft. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Popraw pisownię wyrazu „{0}” w nazwie zestawu „{1}” + + + + Correct the spelling of '{0}' in namespace name '{1}' + Popraw pisownię wyrazu „{0}” w nazwie przestrzeni nazw „{1}” + + + + Correct the spelling of '{0}' in type name '{1}' + Popraw pisownię wyrazu „{0}” w nazwie typu „{1}” + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Popraw pisownię wyrazu „{0}” w nazwie składowej „{1}” lub usuń go całkowicie, jeśli reprezentuje dowolny rodzaj notacji węgierskiej + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + W metodzie „{0}” popraw pisownię wyrazu „{1}” w nazwie parametru „{2}” lub usuń go całkowicie, jeśli reprezentuje dowolny rodzaj notacji węgierskiej + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + W delegacie „{0}” popraw pisownię wyrazu „{1}” w nazwie parametru „{2}” lub usuń go całkowicie, jeśli reprezentuje dowolny rodzaj notacji węgierskiej + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + W typie „{0}” popraw pisownię wyrazu „{1}” w nazwie parametru typu ogólnego „{2}” + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + W metodzie {0} popraw pisownię wyrazu „{1}” w nazwie parametru typu ogólnego „{2}” + + + + Consider providing a more meaningful name than assembly name '{0}' + Rozważ podanie bardziej zrozumiałej nazwy niż nazwa zestawu „{0}” + + + + Consider providing a more meaningful name than namespace name '{0}' + Rozważ podanie bardziej zrozumiałej nazwy niż nazwa przestrzeni nazw „{0}” + + + + Consider providing a more meaningful name than type name '{0}' + Rozważ podanie bardziej zrozumiałej nazwy niż nazwa typu „{0}” + + + + Consider providing a more meaningful name than member name '{0}' + Rozważ podanie bardziej zrozumiałej nazwy niż nazwa składowej „{0}” + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + W metodzie „{0}” rozważ podanie bardziej zrozumiałej nazwy niż nazwa parametru „{1}” + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + W delegacie „{0}” rozważ podanie bardziej zrozumiałej nazwy niż nazwa parametru „{1}” + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + W typie „{0}” rozważ podanie bardziej zrozumiałej nazwy niż nazwa parametru typu ogólnego „{1}” + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + W metodzie „{0}” rozważ podanie bardziej zrozumiałej nazwy niż nazwa parametru typu ogólnego „{1}” + + + + Naming + Nazewnictwo + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Konwencje nazewnictwa określają, że nazwa wyliczenia w liczbie mnogiej wskazuje, iż jednocześnie można określić wiele wartości wyliczenia. + + + + Only FlagsAttribute enums should have plural names + Tylko wyliczenia z atrybutem FlagsAttribute powinny mieć nazwy w liczbie mnogiej + + + + Only FlagsAttribute enums should have plural names + Tylko wyliczenia z atrybutem FlagsAttribute powinny mieć nazwy w liczbie mnogiej + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pt-BR.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pt-BR.xlf new file mode 100644 index 0000000000000..d4c4b9952fb90 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.pt-BR.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Uma enumeração pública tem o atributo System.FlagsAttribute e seu nome não termina em ""s"". Tipos marcados usando FlagsAttribute têm nomes no plural, porque o atributo indica que mais de um valor pode ser especificado. + + + + Flags enums should have plural names + Enumerações Flags devem ter nomes no plural + + + + Flags enums should have plural names + Enumerações Flags devem ter nomes no plural + + + + Error parsing dictionary '{0}': {1} + Erro ao analisar o dicionário '{0}': {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Corrija a ortografia de '{0}' no nome da variável '{1}' + + + + Identifiers should be spelled correctly + Identificadores devem ter grafia correta + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + O nome de um identificador visível externamente contém uma ou mais palavras não reconhecidas pela biblioteca do verificador ortográfico da Microsoft. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Corrija a ortografia de '{0}' no nome do assembly '{1}' + + + + Correct the spelling of '{0}' in namespace name '{1}' + Corrija a ortografia de '{0}' no nome do namespace '{1}' + + + + Correct the spelling of '{0}' in type name '{1}' + Corrija a ortografia de '{0}' no nome do tipo '{1}' + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Corrija a ortografia de '{0}' no nome do membro '{1}' ou remova-o inteiramente caso ele represente algum tipo de notação húngara + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + No método '{0}', corrija a ortografia de '{1}' no nome do parâmetro '{2}' ou remova-o inteiramente caso ele represente algum tipo de notação húngara + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + No delegado '{0}', corrija a ortografia de '{1}' no nome do parâmetro '{2}' ou remova-o inteiramente caso ele represente algum tipo de notação húngara + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + No tipo '{0}', corrija a ortografia de '{1}' no nome do parâmetro de tipo genérico '{2}' + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + No método '{0}', corrija a ortografia de '{1}' no nome do parâmetro de tipo genérico '{2}' + + + + Consider providing a more meaningful name than assembly name '{0}' + Considere fornecer um nome mais significativo do que o nome do assembly '{0}' + + + + Consider providing a more meaningful name than namespace name '{0}' + Considere fornecer um nome mais relevante do que o nome de namespace '{0}' + + + + Consider providing a more meaningful name than type name '{0}' + Considere fornecer um nome mais significativo do que o nome do tipo '{0}' + + + + Consider providing a more meaningful name than member name '{0}' + Considere fornecer um nome mais significativo do que o nome do membro '{0}' + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + No método '{0}', considere fornecer um nome mais significativo do que o nome do parâmetro '{1}' + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + No delegado '{0}', considere fornecer um nome mais significativo do que o nome do parâmetro '{1}' + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + No tipo '{0}', considere fornecer um nome mais significativo do que o nome do parâmetro de tipo genérico '{1}' + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + No método '{0}', considere fornecer um nome mais significativo do que o nome do parâmetro de tipo genérico '{1}' + + + + Naming + Nomenclatura + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + As convenções de nomenclatura ditam que um nome no plural para uma enumeração indica que mais de um valor da enumeração pode ser especificado ao mesmo tempo. + + + + Only FlagsAttribute enums should have plural names + Apenas enumerações FlagsAttribute devem ter nomes no plural + + + + Only FlagsAttribute enums should have plural names + Apenas enumerações FlagsAttribute devem ter nomes no plural + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf new file mode 100644 index 0000000000000..0d205692184a4 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.ru.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Общее перечисление имеет атрибут System.FlagsAttribute, а его имя не заканчивается на ""s"". Типы, помеченные с помощью FlagsAttribute, имеют имена во множественном числе, так как атрибут указывает, что можно задать несколько значений. + + + + Flags enums should have plural names + У перечислений флагов должны быть имена во множественном числе + + + + Flags enums should have plural names + У перечислений флагов должны быть имена во множественном числе + + + + Error parsing dictionary '{0}': {1} + Ошибка при синтаксическом анализе словаря "{0}": {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + Исправьте написание "{0}" в имени переменной "{1}". + + + + Identifiers should be spelled correctly + Идентификаторы должны иметь правильное правописание + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Имя видимого извне идентификатора содержит одно или несколько слов, не распознаваемых библиотекой средства проверки орфографии Майкрософт. + + + + Correct the spelling of '{0}' in assembly name '{1}' + Исправьте правописание "{0}" в имени сборки {1}. + + + + Correct the spelling of '{0}' in namespace name '{1}' + Исправьте правописание "{0}" в имени пространства имен "{1}". + + + + Correct the spelling of '{0}' in type name '{1}' + Исправьте правописание "{0}" в имени типа {1}. + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + Исправьте правописание "{0}" в имени элемента {1} или удалите его целиком, если используется венгерская нотация. + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + В методе {0} исправьте правописание "{1}" в имени параметра {2} или удалите его целиком, если используется венгерская нотация. + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + В делегате {0} исправьте правописание "{1}" в имени параметра {2} или удалите его целиком, если используется венгерская нотация. + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + В типе {0} исправьте правописание "{1}" в имени параметра универсального типа {2}. + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + В методе {0} исправьте правописание "{1}" в имени параметра универсального типа {2}. + + + + Consider providing a more meaningful name than assembly name '{0}' + Подберите более значимое имя для сборки {0}. + + + + Consider providing a more meaningful name than namespace name '{0}' + Подберите более значимое имя для пространства имен {0}. + + + + Consider providing a more meaningful name than type name '{0}' + Подберите более значимое имя для типа {0}. + + + + Consider providing a more meaningful name than member name '{0}' + Подберите более значимое имя для элемента "{0}". + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + В методе "{0}" подберите более значимое имя для параметра "{1}". + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + В делегате "{0}" подберите более значимое имя для параметра "{1}". + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + В типе "{0}" подберите более значимое имя для параметра универсального типа "{1}". + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + В методе "{0}" подберите более значимое имя для параметра универсального типа "{1}". + + + + Naming + Именование + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + По соглашениям об именовании имя перечисления во множественном числе указывает, что одновременно можно задать несколько значений этого перечисления. + + + + Only FlagsAttribute enums should have plural names + Только перечисления FlagsAttribute должны иметь имена во множественном числе + + + + Only FlagsAttribute enums should have plural names + Только перечисления FlagsAttribute должны иметь имена во множественном числе + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.tr.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.tr.xlf new file mode 100644 index 0000000000000..651f1598299e2 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.tr.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + Bir genel sabit listesi System.FlagsAttribute özniteliğine sahip ve adı ""s"" ile bitmiyor. FlagsAttribute kullanılarak işaretlenen türler, öznitelik birden fazla değerin belirtilebileceğini gösterdiğinden çoğul olan adlara sahip olur. + + + + Flags enums should have plural names + Bayrak sabit listeleri çoğul adlara sahip olmalıdır + + + + Flags enums should have plural names + Bayrak sabit listeleri çoğul adlara sahip olmalıdır + + + + Error parsing dictionary '{0}': {1} + '{0}' sözlüğünü ayrıştırma hatası: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + '{1}' bütünleştirilmiş kod adındaki '{0}' yazımını düzeltin + + + + Identifiers should be spelled correctly + Tanımlayıcıların doğru yazılması gerekir + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + Dışarıdan görünebilir tanımlayıcı adı, Microsoft yazım denetimi kitaplığı tarafından desteklenmeyen bir veya daha fazla sözcük içeriyor. + + + + Correct the spelling of '{0}' in assembly name '{1}' + '{1}' bütünleştirilmiş kod adındaki '{0}' yazımını düzeltin + + + + Correct the spelling of '{0}' in namespace name '{1}' + {1} ad alanı adındaki '{0}' yazımını düzeltin + + + + Correct the spelling of '{0}' in type name '{1}' + '{1}' tür adındaki '{0}' yazımını düzeltin + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + '{1}' üye adındaki '{0}' yazımını düzeltin ya da herhangi bir Macar gösterimini temsil ediyorsa tamamen kaldırın + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + '{0}' metodunda, '{2}' parametre adındaki '{1}' yazımını düzeltin ya da herhangi bir Macar gösterimini temsil ediyorsa tamamen kaldırın + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + '{0}' temsilcisinde, '{2}' parametre adındaki '{1}' yazımını düzeltin ya da herhangi bir Macar gösterimini temsil ediyorsa tamamen kaldırın + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + '{0}' türünde, '{2}' genel tür parametre adındaki '{1}' yazımını düzeltin + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + '{0}' metodunda, '{2}' genel tür parametre adındaki '{1}' yazımını düzeltin + + + + Consider providing a more meaningful name than assembly name '{0}' + '{0}' bütünleştirilmiş kod adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + Consider providing a more meaningful name than namespace name '{0}' + '{0}' ad alanı adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + Consider providing a more meaningful name than type name '{0}' + '{0}' tür adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + Consider providing a more meaningful name than member name '{0}' + '{0}' üye adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + '{0}' metodunda, '{1}' parametre adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + '{0}' temsilcisinde, '{1}' parametre adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + '{0}' türünde, '{1}' genel tür parametre adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + '{0}' metodunda, '{1}' genel tür parametre adı yerine daha anlamlı bir ad sağlamayı deneyin + + + + Naming + Adlandırma + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + Adlandırma kurallarına göre bir sabit listesinin çoğul ada sahip olması, aynı anda birden çok sabit listesi değeri belirtilebileceğini gösterir. + + + + Only FlagsAttribute enums should have plural names + Yalnızca FlagsAttribute sabit listeleri çoğul adlara sahip olmalıdır + + + + Only FlagsAttribute enums should have plural names + Yalnızca FlagsAttribute sabit listeleri çoğul adlara sahip olmalıdır + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf new file mode 100644 index 0000000000000..2e74111dc00e3 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hans.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + 公共枚举具有 System.FlagsAttribute 属性,且其名称不以 "s" 结尾。用 FlagsAttribute 标记的类型具有复数形式的名称,因为该属性表示可以指定多个值。 + + + + Flags enums should have plural names + Flags 枚举应采用复数形式的名称 + + + + Flags enums should have plural names + Flags 枚举应采用复数形式的名称 + + + + Error parsing dictionary '{0}': {1} + 分析目录“{0}”时出错: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + 更正变量名称“{1}”中“{0}”的拼写 + + + + Identifiers should be spelled correctly + 标识符应正确拼写 + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + 对外部可见的标识符的名称包含一个或多个未被 Microsoft 拼写检查库识别的单词。 + + + + Correct the spelling of '{0}' in assembly name '{1}' + 更正程序集名称“{1}”中“{0}”的拼写 + + + + Correct the spelling of '{0}' in namespace name '{1}' + 更正名称空间名称“{1}”中“{0}”的拼写 + + + + Correct the spelling of '{0}' in type name '{1}' + 更正类型名称“{1}”中“{0}”的拼写 + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + 更正成员名称“{1}”中“{0}”的拼写;如果它表示任何类型的匈牙利表示法,可完全删除它 + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + 在方法“{0}”中,更正参数名称“{2}”中“{1}”的拼写;如果它表示任何类型的匈牙利表示法,可完全删除它 + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + 在委托“{0}”中,更正参数名称“{2}”中“{1}”的拼写;如果它表示任何类型的匈牙利表示法,可完全删除它 + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + 在类型“{0}”中,更正泛型类型参数名称“{2}”中“{1}”的拼写 + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + 在方法“{0}”中,更正泛型类型参数名称“{2}”中“{1}”的拼写 + + + + Consider providing a more meaningful name than assembly name '{0}' + 请考虑将程序集名称“{0}”改为一个意义更明确的名称 + + + + Consider providing a more meaningful name than namespace name '{0}' + 请考虑将命名空间名称“{0}”改为一个意义更明确的名称 + + + + Consider providing a more meaningful name than type name '{0}' + 请考虑将类型名称“{0}”改为一个意义更明确的名称 + + + + Consider providing a more meaningful name than member name '{0}' + 请考虑将成员名称“{0}”改为一个意义更明确的名称 + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + 在方法“{0}”中,请考虑将参数名称“{1}”改为一个意义更明确的名称 + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + 在委托“{0}”中,请考虑将参数名称“{1}”改为一个意义更明确的名称 + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + 在类型“{0}”中,请考虑将泛型类型参数名称“{1}”改为一个意义更明确的名称 + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + 在方法“{0}”中,请考虑将泛型类型参数名称“{1}”改为一个意义更明确的名称 + + + + Naming + 命名 + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + 命名惯例规定,枚举的复数形式名称表示可以同时指定枚举的多个值。 + + + + Only FlagsAttribute enums should have plural names + 只有 FlagsAttribute 枚举应采用复数形式的名称 + + + + Only FlagsAttribute enums should have plural names + 只有 FlagsAttribute 枚举应采用复数形式的名称 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf new file mode 100644 index 0000000000000..7e1cb054b88e7 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Core/xlf/TextAnalyzersResources.zh-Hant.xlf @@ -0,0 +1,142 @@ + + + + + + A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + 公用列舉具有 System.FlagsAttribute 屬性,且其名稱不會以 ""s"" 結尾。因為 FlagsAttribute 屬性表示可指定一個以上的值,所以使用該屬性所標記的類型,具有複數名稱。 + + + + Flags enums should have plural names + 旗標列舉應使用複數名稱 + + + + Flags enums should have plural names + 旗標列舉應使用複數名稱 + + + + Error parsing dictionary '{0}': {1} + 剖析字典 '{0}' 時發生錯誤: {1} + + + + Correct the spelling of '{0}' in variable name '{1}' + 更正變數名稱 '{1}' 中 '{0}' 的拼字 + + + + Identifiers should be spelled correctly + 識別項的拼字應正確 + + + + The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + 外部可見的識別項名稱,包含一或多個 Microsoft 拼字檢查程式庫無法辨識的字。 + + + + Correct the spelling of '{0}' in assembly name '{1}' + 更正組件名稱 '{1}' 中 '{0}' 的拼字 + + + + Correct the spelling of '{0}' in namespace name '{1}' + 更正命名空間名稱 '{1}' 中 '{0}' 的拼字 + + + + Correct the spelling of '{0}' in type name '{1}' + 更正型別名稱 '{1}' 中 '{0}' 的拼字 + + + + Correct the spelling of '{0}' in member name '{1}' or remove it entirely if it represents any sort of Hungarian notation + 更正成員名稱 '{1}' 中 '{0}' 的拼字,或是如果其代表任何類型的匈牙利標記法,則整個移除 + + + + In method '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + 在方法 '{0}' 中,更正參數名稱 '{2}' 中 '{1}' 的拼字,或是如果其代表任何類型的匈牙利標記法,則整個移除 + + + + In delegate '{0}', correct the spelling of '{1}' in parameter name '{2}' or remove it entirely if it represents any sort of Hungarian notation + 在委派 '{0}' 中,更正參數名稱 '{2}' 中 '{1}' 的拼字,或是如果其代表任何類型的匈牙利標記法,則整個移除 + + + + On type '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + 在型別 '{0}' 中,更正泛型型別參數名稱 '{2}' 中 '{1}' 的拼字 + + + + On method '{0}', correct the spelling of '{1}' in generic type parameter name '{2}' + 在方法 '{0}' 中,更正泛型型別參數名稱 '{2}' 中 '{1}' 的拼字 + + + + Consider providing a more meaningful name than assembly name '{0}' + 請考慮提供較組件名稱 '{0}' 更具意義的名稱 + + + + Consider providing a more meaningful name than namespace name '{0}' + 請考慮提供較命名空間名稱 '{0}' 更具意義的名稱 + + + + Consider providing a more meaningful name than type name '{0}' + 請考慮提供較型別名稱 '{0}' 更具意義的名稱 + + + + Consider providing a more meaningful name than member name '{0}' + 請考慮提供較成員名稱 '{0}' 更具意義的名稱 + + + + In method '{0}', consider providing a more meaningful name than parameter name '{1}' + 請考慮在方法 '{0}' 中提供較參數名稱 '{1}' 更具意義的名稱 + + + + In delegate '{0}', consider providing a more meaningful name than parameter name '{1}' + 請考慮在委派 '{0}' 中提供較參數名稱 '{1}' 更具意義的名稱 + + + + On type '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + 請考慮在型別 '{0}' 中提供較泛型型別參數名稱 '{1}' 更具意義的名稱 + + + + On method '{0}', consider providing a more meaningful name than generic type parameter name '{1}' + 請考慮在方法 '{0}' 中提供較泛型型別參數名稱 '{1}' 更具意義的名稱 + + + + Naming + 命名 + + + + Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + 依照命名慣例的要求,列舉若是複數的名稱,表示可以同時指定多個列舉值。 + + + + Only FlagsAttribute enums should have plural names + 只有 FlagsAttribute 列舉應使用複數名稱 + + + + Only FlagsAttribute enums should have plural names + 只有 FlagsAttribute 列舉應使用複數名稱 + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/Directory.Build.props b/src/RoslynAnalyzers/Text.Analyzers/Directory.Build.props new file mode 100644 index 0000000000000..2f4c476708f3c --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Directory.Build.props @@ -0,0 +1,10 @@ + + + + + $(DefineConstants),TEXT_ANALYZERS + + + true + + diff --git a/src/RoslynAnalyzers/Text.Analyzers/RulesMissingDocumentation.md b/src/RoslynAnalyzers/Text.Analyzers/RulesMissingDocumentation.md new file mode 100644 index 0000000000000..ef2efcc7efeed --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/RulesMissingDocumentation.md @@ -0,0 +1,5 @@ +# Rules without documentation + +Rule ID | Missing Help Link | Title | +--------|-------------------|-------| +CA1704 | | Identifiers should be spelled correctly | diff --git a/src/RoslynAnalyzers/Text.Analyzers/Setup/Properties/launchSettings.json b/src/RoslynAnalyzers/Text.Analyzers/Setup/Properties/launchSettings.json new file mode 100644 index 0000000000000..183040856937b --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Setup/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Visual Studio Extension": { + "executablePath": "$(DevEnvDir)devenv.exe", + "commandLineArgs": "/rootsuffix $(VSSDKTargetPlatformRegRootSuffix) /log" + } + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/Setup/Text.Analyzers.Setup.csproj b/src/RoslynAnalyzers/Text.Analyzers/Setup/Text.Analyzers.Setup.csproj new file mode 100644 index 0000000000000..2568218d6c113 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Setup/Text.Analyzers.Setup.csproj @@ -0,0 +1,41 @@ + + + + net472 + + true + false + false + false + false + false + true + + + + + Text.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Text.CSharp.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + Text.VisualBasic.Analyzers + BuiltProjectOutputGroup + DebugSymbolsProjectOutputGroup + true + + + + + + Designer + + + diff --git a/src/RoslynAnalyzers/Text.Analyzers/Setup/source.extension.vsixmanifest b/src/RoslynAnalyzers/Text.Analyzers/Setup/source.extension.vsixmanifest new file mode 100644 index 0000000000000..9d58e0d863396 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Setup/source.extension.vsixmanifest @@ -0,0 +1,27 @@ + + + + + + Text Analyzers + Text Analyzers + EULA.txt + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.md b/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.md new file mode 100644 index 0000000000000..60554e56d4c5b --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.md @@ -0,0 +1,37 @@ +# Text.Analyzers + +## [CA1704](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1704): Identifiers should be spelled correctly + +The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library. + +|Item|Value| +|-|-| +|Category|Naming| +|Enabled|True| +|Severity|Warning| +|CodeFix|False| +--- + +## [CA1714](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1714): Flags enums should have plural names + +A public enumeration has the System.FlagsAttribute attribute, and its name does not end in ""s"". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified. + +|Item|Value| +|-|-| +|Category|Naming| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- + +## [CA1717](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1717): Only FlagsAttribute enums should have plural names + +Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time. + +|Item|Value| +|-|-| +|Category|Naming| +|Enabled|False| +|Severity|Warning| +|CodeFix|False| +--- diff --git a/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.sarif b/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.sarif new file mode 100644 index 0000000000000..4fe0ad2dce86f --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/Text.Analyzers.sarif @@ -0,0 +1,105 @@ +{ + "$schema": "http://json.schemastore.org/sarif-1.0.0", + "version": "1.0.0", + "runs": [ + { + "tool": { + "name": "Humanizer", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + }, + { + "tool": { + "name": "Text.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + "CA1704": { + "id": "CA1704", + "shortDescription": "Identifiers should be spelled correctly", + "fullDescription": "The name of an externally visible identifier contains one or more words that are not recognized by the Microsoft spelling checker library.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1704", + "properties": { + "category": "Naming", + "isEnabledByDefault": true, + "typeName": "IdentifiersShouldBeSpelledCorrectlyAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "PortedFromFxCop", + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, + "CA1714": { + "id": "CA1714", + "shortDescription": "Flags enums should have plural names", + "fullDescription": "A public enumeration has the System.FlagsAttribute attribute, and its name does not end in \"\"s\"\". Types that are marked by using FlagsAttribute have names that are plural because the attribute indicates that more than one value can be specified.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1714", + "properties": { + "category": "Naming", + "isEnabledByDefault": false, + "typeName": "EnumsShouldHavePluralNamesAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "PortedFromFxCop", + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, + "CA1717": { + "id": "CA1717", + "shortDescription": "Only FlagsAttribute enums should have plural names", + "fullDescription": "Naming conventions dictate that a plural name for an enumeration indicates that more than one value of the enumeration can be specified at the same time.", + "defaultLevel": "warning", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1717", + "properties": { + "category": "Naming", + "isEnabledByDefault": false, + "typeName": "EnumsShouldHavePluralNamesAnalyzer", + "languages": [ + "C#", + "Visual Basic" + ], + "tags": [ + "PortedFromFxCop", + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + } + } + }, + { + "tool": { + "name": "Text.CSharp.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + }, + { + "tool": { + "name": "Text.VisualBasic.Analyzers", + "version": "3.12.0", + "language": "en-US" + }, + "rules": { + } + } + ] +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/UnitTests/EnumsShouldHavePluralNamesTests.cs b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/EnumsShouldHavePluralNamesTests.cs new file mode 100644 index 0000000000000..61a3f5b1ff9f5 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/EnumsShouldHavePluralNamesTests.cs @@ -0,0 +1,583 @@ +// 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.Globalization; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Test.Utilities; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Text.Analyzers.EnumsShouldHavePluralNamesAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Text.Analyzers.EnumsShouldHavePluralNamesAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Text.Analyzers.UnitTests +{ + public class EnumsShouldHavePluralNamesTests + { + [Fact] + public async Task CA1714_CA1717_Test_EnumWithNoFlags_SingularNameAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + public enum Day + { + Sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }" + ); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + Public Enum Day + Sunday = 0 + Monday = 1 + Tuesday = 2 + + End Enum + End Class + "); + } + + [Fact] + public async Task CA1714_CA1717__Test_EnumWithNoFlags_PluralNameAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + public enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }", + GetCSharpNoPluralResultAt(4, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + Public Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + + End Enum + End Class + ", + GetBasicNoPluralResultAt(3, 38)); + } + + [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")] + public async Task CA1714_CA1717__Test_EnumWithNoFlags_PluralName_InternalAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +class A +{ + enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; +} + +public class A2 +{ + private enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; +} + +internal class A3 +{ + public enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; +} +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Class A + Private Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class + +Public Class A2 + Private Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class + +Friend Class A3 + Public Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class +"); + } + + [Fact] + public async Task CA1714_CA1717__Test_EnumWithNoFlags_PluralName_UpperCaseAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + public enum DAYS + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }", + GetCSharpNoPluralResultAt(4, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + Public Enum DAYS + Sunday = 0 + Monday = 1 + Tuesday = 2 + + End Enum + End Class + ", + GetBasicNoPluralResultAt(3, 38)); + } + + [Fact] + public async Task CA1714_CA1717_Test_EnumWithFlags_SingularNameAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum Day + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }", + GetCSharpPluralResultAt(5, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + _ + Public Enum Day + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum + End Class", + GetBasicPluralResultAt(4, 34)); + } + + [Fact, WorkItem(1432, "https://github.com/dotnet/roslyn-analyzers/issues/1432")] + public async Task CA1714_CA1717_Test_EnumWithFlags_SingularName_InternalAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" +class A +{ + [System.Flags] + enum Day + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + } +} + +public class A2 +{ + [System.Flags] + private enum Day + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + } +} + +internal class A3 +{ + [System.Flags] + public enum Day + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + } +} +"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Class A + _ + Enum Day + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class + +Public Class A2 + _ + Private Enum Day + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class + +Friend Class A3 + _ + Public Enum Day + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum +End Class +"); + } + + [Fact] + public async Task CA1714_CA1717_Test_EnumWithFlags_PluralNameAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + _ + Public Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum + End Class"); + } + + [Fact] + public async Task CA1714_CA1717_Test_EnumWithFlags_PluralName_UpperCaseAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum DAYS + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + _ + Public Enum DAYS + Sunday = 0 + Monday = 1 + Tuesday = 2 + End Enum + End Class"); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithFlags_NonPluralNameEndsWithSAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum Axis + { + x = 0, + y = 1, + z = 2 + + }; + }", + GetCSharpPluralResultAt(5, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + _ + Public Enum Axis + x = 0 + y = 1 + z = 2 + End Enum + End Class", + GetBasicPluralResultAt(4, 34)); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithFlags_PluralNameEndsWithSAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum Axes + { + x = 0, + y = 1, + z = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + _ + Public Enum Axes + x = 0 + y = 1 + z = 2 + End Enum + End Class"); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithFlags_PluralName_NotEndingWithSAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum Men + { + M1 = 0, + M2 = 1, + M3 = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + < System.Flags > _ + Public Enum Men + M1 = 0 + M2 = 1 + M3 = 2 + End Enum + End Class"); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithNoFlags_PluralWord_NotEndingWithSAsync() + { + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + public enum Men + { + M1 = 0, + M2 = 1, + M3 = 2 + + }; + }", + GetCSharpNoPluralResultAt(4, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + Public Enum Men + M1 = 0 + M2 = 1 + M3 = 2 + End Enum + End Class", + GetBasicNoPluralResultAt(3, 37)); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithNoFlags_irregularPluralWord_EndingWith_aeAsync() + { + // Humanizer does not recognize 'formulae' as plural, but we skip words ending with 'ae' + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum formulae + { + M1 = 0, + M2 = 1, + M3 = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + < System.Flags > _ + Public Enum formulae + M1 = 0 + M2 = 1 + M3 = 2 + End Enum + End Class"); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithNoFlags_irregularPluralWord_EndingWith_iAsync() + { + // Humanizer does not recognize 'trophi' as plural, but we skip words ending with 'i' + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum trophi + { + M1 = 0, + M2 = 1, + M3 = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + < System.Flags > _ + Public Enum trophi + M1 = 0 + M2 = 1 + M3 = 2 + End Enum + End Class"); + } + + [Fact, WorkItem(1323, "https://github.com/dotnet/roslyn-analyzers/issues/1323")] + public async Task CA1714_CA1717_Test_EnumWithNoFlags_NonAsciiAsync() + { + // We skip non-ASCII names. + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + [System.Flags] + public enum UnicodeNameΔ + { + M1 = 0, + M2 = 1, + M3 = 2 + + }; + }"); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + < System.Flags > _ + Public Enum UnicodeNameΔ + M1 = 0 + M2 = 1 + M3 = 2 + End Enum + End Class"); + } + + [Theory, WorkItem(2229, "https://github.com/dotnet/roslyn-analyzers/issues/2229")] + [InlineData("en-US")] + [InlineData("es-ES")] + [InlineData("pl-PL")] + [InlineData("fi-FI")] + [InlineData("de-DE")] + public async Task CA1714_CA1717__Test_EnumWithNoFlags_PluralName_MultipleCulturesAsync(string culture) + { + var currentCulture = CultureInfo.DefaultThreadCurrentCulture; + CultureInfo.DefaultThreadCurrentCulture = CultureInfo.GetCultureInfo(culture); + + await VerifyCS.VerifyAnalyzerAsync(@" + public class A + { + public enum Days + { + sunday = 0, + Monday = 1, + Tuesday = 2 + + }; + }", + GetCSharpNoPluralResultAt(4, 44)); + + await VerifyVB.VerifyAnalyzerAsync(@" + Public Class A + Public Enum Days + Sunday = 0 + Monday = 1 + Tuesday = 2 + + End Enum + End Class + ", + GetBasicNoPluralResultAt(3, 38)); + + CultureInfo.DefaultThreadCurrentCulture = currentCulture; + } + + private static DiagnosticResult GetCSharpPluralResultAt(int line, int column) +#pragma warning disable RS0030 // Do not use banned APIs + => VerifyCS.Diagnostic(EnumsShouldHavePluralNamesAnalyzer.Rule_CA1714) + .WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetBasicPluralResultAt(int line, int column) +#pragma warning disable RS0030 // Do not use banned APIs + => VerifyVB.Diagnostic(EnumsShouldHavePluralNamesAnalyzer.Rule_CA1714) + .WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetCSharpNoPluralResultAt(int line, int column) +#pragma warning disable RS0030 // Do not use banned APIs + => VerifyCS.Diagnostic(EnumsShouldHavePluralNamesAnalyzer.Rule_CA1717) + .WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + + private static DiagnosticResult GetBasicNoPluralResultAt(int line, int column) +#pragma warning disable RS0030 // Do not use banned APIs + => VerifyVB.Diagnostic(EnumsShouldHavePluralNamesAnalyzer.Rule_CA1717) + .WithLocation(line, column); +#pragma warning restore RS0030 // Do not use banned APIs + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/UnitTests/IdentifiersShouldBeSpelledCorrectlyTests.cs b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/IdentifiersShouldBeSpelledCorrectlyTests.cs new file mode 100644 index 0000000000000..0160dfd7b191d --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/IdentifiersShouldBeSpelledCorrectlyTests.cs @@ -0,0 +1,875 @@ +// 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.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Text.Analyzers.IdentifiersShouldBeSpelledCorrectlyAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace Text.Analyzers.UnitTests +{ + public class IdentifiersShouldBeSpelledCorrectlyTests + { + public enum DictionaryType + { + Xml, + Dic, + } + + public static IEnumerable MisspelledMembers + => new[] + { + new object[] { CreateTypeWithConstructor("Clazz", constructorName: "{|#0:Clazz|}", isStatic: false), "Clazz", "Clazz.Clazz()" }, + new object[] { CreateTypeWithConstructor("Clazz", constructorName: "{|#0:Clazz|}", isStatic: true), "Clazz", "Clazz.Clazz()" }, + new object[] { CreateTypeWithField("Program", "{|#0:_fild|}"), "fild", "Program._fild" }, + new object[] { CreateTypeWithEvent("Program", "{|#0:Evt|}"), "Evt", "Program.Evt" }, + new object[] { CreateTypeWithProperty("Program", "{|#0:Naem|}"), "Naem", "Program.Naem" }, + new object[] { CreateTypeWithMethod("Program", "{|#0:SomeMathod|}"), "Mathod", "Program.SomeMathod()" }, + }; + + public static IEnumerable UnmeaningfulMembers + => new[] + { + new object[] { CreateTypeWithConstructor("A", constructorName: "{|#0:A|}", isStatic: false), "A" }, + new object[] { CreateTypeWithConstructor("B", constructorName: "{|#0:B|}", isStatic: false), "B" }, + new object[] { CreateTypeWithField("Program", "{|#0:_c|}"), "c" }, + new object[] { CreateTypeWithEvent("Program", "{|#0:D|}"), "D" }, + new object[] { CreateTypeWithProperty("Program", "{|#0:E|}"), "E" }, + new object[] { CreateTypeWithMethod("Program", "{|#0:F|}"), "F" }, + }; + + public static IEnumerable MisspelledMemberParameters + => new[] + { + new object[] { CreateTypeWithConstructor("Program", parameter: "int {|#0:yourNaem|}", isStatic: false), "Naem", "yourNaem", "Program.Program(int)" }, + new object[] { CreateTypeWithMethod("Program", "Method", "int {|#0:yourNaem|}"), "Naem", "yourNaem", "Program.Method(int)" }, + new object[] { CreateTypeWithIndexer("Program", "int {|#0:yourNaem|}"), "Naem", "yourNaem", "Program.this[int]" }, + }; + + [Theory] + [InlineData("namespace Bar { }")] + [InlineData("class Program { }")] + [InlineData("class Program { void Member() { } }")] + [InlineData("class Program { int _variable = 1; }")] + [InlineData("class Program { void Member(string name) { } }")] + [InlineData("class Program { delegate int GetNumber(string name); }")] + [InlineData("class Program { }")] + public async Task NoMisspellings_Verify_NoDiagnosticsAsync(string source) + { + await VerifyCSharpAsync(source); + } + + [Fact] + public async Task MisspellingAllowedByGlobalXmlDictionary_Verify_NoDiagnosticsAsync() + { + var source = "class Clazz { }"; + var dictionary = CreateXmlDictionary(new[] { "clazz" }); + + await VerifyCSharpAsync(source, dictionary); + } + + [Fact] + public async Task MisspellingAllowedByGlobalDicDictionary_Verify_NoDiagnosticsAsync() + { + var source = "class Clazz { }"; + var dictionary = CreateDicDictionary(new[] { "clazz" }); + + await VerifyCSharpAsync(source, dictionary); + } + + [Fact] + public async Task MisspellingsAllowedByMultipleGlobalDictionaries_Verify_NoDiagnosticsAsync() + { + var source = @"class Clazz { const string Naem = ""foo""; }"; + var xmlDictionary = CreateXmlDictionary(new[] { "clazz" }); + var dicDictionary = CreateDicDictionary(new[] { "naem" }); + + await VerifyCSharpAsync(source, new[] { xmlDictionary, dicDictionary }); + } + + [Fact] + public async Task CorrectWordDisallowedByGlobalXmlDictionary_Verify_EmitsDiagnosticAsync() + { + var source = "class {|#0:Program|} { }"; + var dictionary = CreateXmlDictionary(null, new[] { "program" }); + + await VerifyCSharpAsync( + source, + dictionary, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeRule) + .WithLocation(0) + .WithArguments("Program", "Program")); + } + + [Fact] + public async Task MisspellingAllowedByProjectDictionary_Verify_NoDiagnosticsAsync() + { + var source = "class Clazz {}"; + var dictionary = CreateDicDictionary(new[] { "clazz" }); + + await VerifyCSharpAsync(source, dictionary); + } + + [Fact] + public async Task MisspellingAllowedByDifferentProjectDictionary_Verify_EmitsDiagnosticAsync() + { + var source = "class {|#0:Clazz|} {}"; + var dictionary = CreateDicDictionary(new[] { "clazz" }); + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + AdditionalProjects = + { + ["OtherProject"] = + { + AdditionalFiles = { dictionary } + }, + }, + AdditionalProjectReferences = { "OtherProject" } + }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeRule) + .WithLocation(0) + .WithArguments("Clazz", "Clazz") + } + }; + + await test.RunAsync(); + } + + [Fact] + public async Task AssemblyMisspelled_Verify_EmitsDiagnosticAsync() + { + var source = "{|#0:class Program {}|}"; + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + SolutionTransforms = + { + RenameProjectAssembly, + }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.AssemblyRule) + .WithLocation(0) + .WithArguments("Assambly", "MyAssambly") + } + }; + + await test.RunAsync(); + + static Solution RenameProjectAssembly(Solution solution, ProjectId projectId) + { + return solution.WithProjectAssemblyName(projectId, "MyAssambly"); + } + } + + [Fact] + public async Task AssemblyUnmeaningful_Verify_EmitsDiagnosticAsync() + { + var source = "{|#0:class Program {}|}"; + var test = new VerifyCS.Test + { + TestState = + { + Sources = { source }, + }, + SolutionTransforms = + { + RenameProjectAssembly, + }, + ExpectedDiagnostics = + { + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.AssemblyMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("A") + } + }; + + await test.RunAsync(); + + static Solution RenameProjectAssembly(Solution solution, ProjectId projectId) + { + return solution.WithProjectAssemblyName(projectId, "A"); + } + } + + [Fact] + public async Task NamespaceMisspelled_Verify_EmitsDiagnosticAsync() + { + var source = "namespace Tests.{|#0:MyNarmspace|} {}"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.NamespaceRule) + .WithLocation(0) + .WithArguments("Narmspace", "Tests.MyNarmspace")); + } + + [Fact] + public async Task NamespaceUnmeaningful_Verify_EmitsDiagnosticAsync() + { + var source = "namespace Tests.{|#0:A|} {}"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.NamespaceMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("A")); + } + + [Theory] + [InlineData("namespace MyNamespace { class {|#0:MyClazz|} {} }", "Clazz", "MyNamespace.MyClazz")] + [InlineData("namespace MyNamespace { struct {|#0:MyStroct|} {} }", "Stroct", "MyNamespace.MyStroct")] + [InlineData("namespace MyNamespace { enum {|#0:MyEnim|} {} }", "Enim", "MyNamespace.MyEnim")] + [InlineData("namespace MyNamespace { interface {|#0:IMyFase|} {} }", "Fase", "MyNamespace.IMyFase")] + [InlineData("namespace MyNamespace { delegate int {|#0:MyDelegete|}(); }", "Delegete", "MyNamespace.MyDelegete")] + public async Task TypeMisspelled_Verify_EmitsDiagnosticAsync(string source, string misspelling, string typeName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeRule) + .WithLocation(0) + .WithArguments(misspelling, typeName)); + } + + [Theory] + [InlineData("class {|#0:A|} {}", "A")] + [InlineData("struct {|#0:B|} {}", "B")] + [InlineData("enum {|#0:C|} {}", "C")] + [InlineData("interface {|#0:ID|} {}", "D")] + [InlineData("delegate int {|#0:E|}();", "E")] + public async Task TypeUnmeaningful_Verify_EmitsDiagnosticAsync(string source, string typeName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments(typeName)); + } + + [Theory] + [MemberData(nameof(MisspelledMembers))] + public async Task MemberMisspelled_Verify_EmitsDiagnosticAsync(string source, string misspelling, string memberName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberRule) + .WithLocation(0) + .WithArguments(misspelling, memberName)); + } + + [Fact] + public async Task MemberOverrideMisspelled_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + abstract class Parent + { + protected abstract string {|#0:Naem|} { get; } + + public abstract int {|#1:Mathod|}(); + } + + class Child : Parent + { + protected override string Naem => ""child""; + + public override int Mathod() => 0; + } + + class Grandchild : Child + { + protected override string Naem => ""grandchild""; + + public override int Mathod() => 1; + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberRule) + .WithLocation(0) + .WithArguments("Naem", "Parent.Naem"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberRule) + .WithLocation(1) + .WithArguments("Mathod", "Parent.Mathod()")); + } + + [Theory] + [MemberData(nameof(UnmeaningfulMembers))] + public async Task MemberUnmeaningful_Verify_EmitsDiagnosticAsync(string source, string memberName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments(memberName)); + } + + [Fact] + public async Task VariableMisspelled_Verify_EmitsDiagnosticAsync() + { + var source = @" + class Program + { + public Program() + { + var {|#0:myVoriable|} = 5; + } + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.VariableRule) + .WithLocation(0) + .WithArguments("Voriable", "myVoriable")); + } + + [Theory] + [MemberData(nameof(MisspelledMemberParameters))] + public async Task MemberParameterMisspelled_Verify_EmitsDiagnosticAsync(string source, string misspelling, string parameterName, string memberName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments(memberName, misspelling, parameterName)); + } + + [Fact] + public async Task MemberParameterUnmeaningful_Verify_EmitsDiagnosticAsync() + { + var source = @" + class Program + { + public void Method(string {|#0:a|}) + { + } + + public string this[int {|#1:i|}] => null; + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("Program.Method(string)", "a"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(1) + .WithArguments("Program.this[int]", "i")); + } + + [Fact] + public async Task MemberParameterMisspelledInterfaceImplementation_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + interface IProgram + { + void Method(string {|#0:explaintain|}); + + string this[int {|#1:indxe|}] { get; } + } + + class Program : IProgram + { + public void Method(string explaintain) + { + } + + public string this[int indxe] => null; + + public void Method2(long {|#2:enviromentId|}) + { + } + + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments("IProgram.Method(string)", "explaintain", "explaintain"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(1) + .WithArguments("IProgram.this[int]", "indxe", "indxe"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(2) + .WithArguments("Program.Method2(long)", "enviroment", "enviromentId")); + } + + [Fact] + public async Task MemberParameterUnmeaningfulInterfaceImplementation_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + interface IProgram + { + void Method(string {|#0:a|}); + + string this[int {|#1:i|}] { get; } + } + + class Program : IProgram + { + public void Method(string a) + { + } + + public string this[int i] => null; + + public void Method2(long {|#2:x|}) + { + } + + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("IProgram.Method(string)", "a"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(1) + .WithArguments("IProgram.this[int]", "i"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(2) + .WithArguments("Program.Method2(long)", "x")); + } + + [Fact] + public async Task MemberParameterUnmeaningfulExplicitInterfaceImplementation_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + interface IProgram + { + void Method(string {|#0:a|}); + + string this[int {|#1:i|}] { get; } + } + + class Program : IProgram + { + void IProgram.Method(string a) + { + } + + string IProgram.this[int i] => null; + + public void Method2(long {|#2:x|}) + { + } + + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("IProgram.Method(string)", "a"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(1) + .WithArguments("IProgram.this[int]", "i"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterMoreMeaningfulNameRule) + .WithLocation(2) + .WithArguments("Program.Method2(long)", "x")); + } + + [Fact] + public async Task MemberParameterMisspelledExplicitInterfaceImplementation_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + interface IProgram + { + void Method(string {|#0:explaintain|}); + + string this[int {|#1:indxe|}] { get; } + } + + class Program : IProgram + { + void IProgram.Method(string explaintain) + { + } + + string IProgram.this[int indxe] => null; + + public void Method2(long {|#2:enviromentId|}) + { + } + + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments("IProgram.Method(string)", "explaintain", "explaintain"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(1) + .WithArguments("IProgram.this[int]", "indxe", "indxe"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(2) + .WithArguments("Program.Method2(long)", "enviroment", "enviromentId")); + } + + [Fact] + public async Task MemberParameterMisspelledOverrideImplementation_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + public abstract class Base + { + public abstract void Method(string {|#0:explaintain|}); + } + + public class Derived : Base + { + public override void Method(string explaintain) + { + } + + public void Method2(long {|#1:enviromentId|}) + { + } + + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments("Base.Method(string)", "explaintain", "explaintain"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(1) + .WithArguments("Derived.Method2(long)", "enviroment", "enviromentId")); + } + + [Fact] + public async Task MemberParameterMisspelledOverrideImplementationWithNameMismatch_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + public abstract class Base + { + public abstract void Method(string {|#0:explaintain|}); + public abstract void Method2(string {|#1:inupts|}); + } + + public class Mid : Base + { + public override void Method(string explaintain) + { + } + + public override void Method2(string {|#2:paarmeter|}) + { + } + } + + public class Derived : Mid + { + public override void Method(string explanation) + { + } + + public override void Method2(string {|#3:paarmeterId|}) + { + } + + } + + public class Derived2 : Mid + { + public override void Method(string {|#4:explaintaing|}) + { + } + + public override void Method2(string {|#5:strValue|}) + { + } + + } + +"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments("Base.Method(string)", "explaintain", "explaintain"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(1) + .WithArguments("Base.Method2(string)", "inupts", "inupts"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(2) + .WithArguments("Mid.Method2(string)", "paarmeter", "paarmeter"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(3) + .WithArguments("Derived.Method2(string)", "paarmeter", "paarmeterId"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(4) + .WithArguments("Derived2.Method(string)", "explaintaing", "explaintaing"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(5) + .WithArguments("Derived2.Method2(string)", "str", "strValue")); + } + + [Fact] + public async Task MemberParameterMisspelledIndexerOVerrideWithNameMismatch_Verify_EmitsDiagnosticOnlyAtDefinitionAsync() + { + var source = @" + public interface IProgram + { + string this[int {|#0:indxe|}] { get; } + } + + public class Program : IProgram + { + public virtual string this[int indxe] => null; + } + + public class DerivedProgram : Program + { + public override string this[int indxe] => null; + } + + public class DerivedProgram2 : Program + { + public override string this[int {|#1:indexe|}] => null; + } + +"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(0) + .WithArguments("IProgram.this[int]", "indxe", "indxe"), + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MemberParameterRule) + .WithLocation(1) + .WithArguments("DerivedProgram2.this[int]", "indexe", "indexe")); + } + + [Fact] + public async Task DelegateParameterMisspelled_Verify_EmitsDiagnosticAsync() + { + var source = "delegate void MyDelegate(string {|#0:firstNaem|});"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.DelegateParameterRule) + .WithLocation(0) + .WithArguments("MyDelegate", "Naem", "firstNaem")); + } + + [Fact] + public async Task DelegateParameterUnmeaningful_Verify_EmitsDiagnosticAsync() + { + var source = "delegate void MyDelegate(string {|#0:a|});"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.DelegateParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("MyDelegate", "a")); + } + + [Theory] + [InlineData("class MyClass { }", "MyClass", "Wroong", "TWroong")] + [InlineData("struct MyStructure<{|#0:TWroong|}> { }", "MyStructure", "Wroong", "TWroong")] + [InlineData("interface IInterface<{|#0:TWroong|}> { }", "IInterface", "Wroong", "TWroong")] + [InlineData("delegate int MyDelegate<{|#0:TWroong|}>();", "MyDelegate", "Wroong", "TWroong")] + + public async Task TypeTypeParameterMisspelled_Verify_EmitsDiagnosticAsync(string source, string typeName, string misspelling, string typeParameterName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeTypeParameterRule) + .WithLocation(0) + .WithArguments(typeName, misspelling, typeParameterName)); + } + + [Theory] + [InlineData("class MyClass<{|#0:A|}> { }", "MyClass", "A")] + [InlineData("struct MyStructure<{|#0:B|}> { }", "MyStructure", "B")] + [InlineData("interface IInterface<{|#0:C|}> { }", "IInterface", "C")] + [InlineData("delegate int MyDelegate<{|#0:D|}>();", "MyDelegate", "D")] + public async Task TypeTypeParameterUnmeaningful_Verify_EmitsDiagnosticAsync(string source, string typeName, string typeParameterName) + { + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.TypeTypeParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments(typeName, typeParameterName)); + } + + [Fact] + public async Task MethodTypeParameterMisspelled_Verify_EmitsDiagnosticAsync() + { + var source = @" + class Program + { + void Method<{|#0:TTipe|}>(TTipe item) + { + } + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MethodTypeParameterRule) + .WithLocation(0) + .WithArguments("Program.Method(TTipe)", "Tipe", "TTipe")); + } + + [Fact] + public async Task MethodTypeParameterUnmeaningful_Verify_EmitsDiagnosticAsync() + { + var source = @" + class Program + { + void Method<{|#0:TA|}>(TA parameter) + { + } + }"; + + await VerifyCSharpAsync( + source, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.MethodTypeParameterMoreMeaningfulNameRule) + .WithLocation(0) + .WithArguments("Program.Method(TA)", "TA")); + } + + [Fact] + public async Task MisspellingContainsOnlyCapitalizedLetters_Verify_NoDiagnosticsAsync() + { + var source = "class FCCA { }"; + + await VerifyCSharpAsync(source); + } + + [Theory] + [InlineData("0x0")] + [InlineData("0xDEADBEEF")] + public async Task MisspellingStartsWithADigit_Verify_NoDiagnosticsAsync(string misspelling) + { + var source = $"enum Name {{ My{misspelling} }}"; + + await VerifyCSharpAsync(source); + } + + [Fact] + public async Task MalformedXmlDictionary_Verify_EmitsDiagnosticAsync() + { + var contents = @" + + + + okay + bad + + + "; + var dictionary = ("CodeAnalysisDictionary.xml", contents); + + await VerifyCSharpAsync( + "class Program {}", + dictionary, + VerifyCS.Diagnostic(IdentifiersShouldBeSpelledCorrectlyAnalyzer.FileParseRule) + // Ignore diagnostic message comparison because the actual message + // includes localized content from an exception's message + .WithMessage(null)); + } + + private Task VerifyCSharpAsync(string source, params DiagnosticResult[] expected) + => VerifyCSharpAsync(source, Array.Empty<(string Path, string Text)>(), expected); + + private Task VerifyCSharpAsync(string source, (string Path, string Text) additionalText, params DiagnosticResult[] expected) + => VerifyCSharpAsync(source, new[] { additionalText }, expected); + + private async Task VerifyCSharpAsync(string source, (string Path, string Text)[] additionalTexts, params DiagnosticResult[] expected) + { + + var csharpTest = new VerifyCS.Test + { + TestCode = source, + TestState = + { + AdditionalFilesFactories = { () => additionalTexts.Select(x => (x.Path, SourceText.From(x.Text))) } + }, + TestBehaviors = TestBehaviors.SkipSuppressionCheck, + }; + + csharpTest.ExpectedDiagnostics.AddRange(expected); + + await csharpTest.RunAsync(); + } + + private static (string Path, string Text) CreateXmlDictionary(IEnumerable? recognizedWords, IEnumerable? unrecognizedWords = null) + => CreateXmlDictionary("CodeAnalysisDictionary.xml", recognizedWords, unrecognizedWords); + + private static (string Path, string Text) CreateXmlDictionary(string filename, IEnumerable? recognizedWords, IEnumerable? unrecognizedWords = null) + { + var contents = $@" + + + {CreateXml(recognizedWords)} + {CreateXml(unrecognizedWords)} + +"; + + return (filename, contents); + + static string CreateXml(IEnumerable? words) => + string.Join(Environment.NewLine, words?.Select(x => $"{x}") ?? Enumerable.Empty()); + } + + private static (string Path, string Text) CreateDicDictionary(IEnumerable recognizedWords) + { + var contents = string.Join(Environment.NewLine, recognizedWords); + return ("CustomDictionary.dic", contents); + } + + private static string CreateTypeWithConstructor(string typeName, string constructorName = "", string parameter = "", bool isStatic = false) + { + if (string.IsNullOrEmpty(constructorName)) + { + constructorName = typeName; + } + + return $@" +#pragma warning disable {IdentifiersShouldBeSpelledCorrectlyAnalyzer.RuleId} +class {typeName} +#pragma warning restore {IdentifiersShouldBeSpelledCorrectlyAnalyzer.RuleId} +{{ + {(isStatic ? "static " : string.Empty)}{constructorName}({parameter}) {{ }} +}}"; + } + + private static string CreateTypeWithMethod(string typeName, string methodName, string parameter = "") + => $"class {typeName} {{ void {methodName}({parameter}) {{ }} }}"; + + private static string CreateTypeWithIndexer(string typeName, string parameter) + => $"class {typeName} {{ int this[{parameter}] => 0; }}"; + + private static string CreateTypeWithProperty(string typeName, string propertyName) + => $"class {typeName} {{ string {propertyName} {{ get; }} }}"; + + private static string CreateTypeWithField(string typeName, string fieldName) + => $"class {typeName} {{ private string {fieldName}; }}"; + + private static string CreateTypeWithEvent(string typeName, string eventName) + => $@"using System; + +class {typeName} {{ event EventHandler {eventName}; }}"; + } +} diff --git a/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Properties/launchSettings.json b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Properties/launchSettings.json new file mode 100644 index 0000000000000..4e3b720d74ec4 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Properties/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + "TestSetup": { + "commandName": "Executable", + "executablePath": "$(NuGetPackageRoot)\\xunit.runner.console\\$(XUnitVersion)\\tools\\xunit.console.x86.exe", + "commandLineArgs": "$(AssemblyName).dll -noshadow -wait", + "workingDirectory": "$(OutDir)" + } + } +} \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Text.Analyzers.UnitTests.csproj b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Text.Analyzers.UnitTests.csproj new file mode 100644 index 0000000000000..11164c8da90b3 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/Text.Analyzers.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + $(NetRoslyn) + true + $(MicrosoftCodeAnalysisVersionForTests) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Text.Analyzers/UnitTests/app.config b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/app.config new file mode 100644 index 0000000000000..6b281a3a5318d --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/UnitTests/app.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/AnalyzerReleases.Shipped.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md new file mode 100644 index 0000000000000..cdf4f1397e0bd --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/AnalyzerReleases.Unshipped.md @@ -0,0 +1 @@ +; Please do not edit this file manually, it should only be updated through code fix application. diff --git a/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/BasicIdentifiersShouldBeSpelledCorrectly.Fixer.vb b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/BasicIdentifiersShouldBeSpelledCorrectly.Fixer.vb new file mode 100644 index 0000000000000..1e3f58a1bff60 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/BasicIdentifiersShouldBeSpelledCorrectly.Fixer.vb @@ -0,0 +1,23 @@ +' 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. + +Imports System.Composition +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Text.Analyzers + +Namespace Text.VisualBasic.Analyzers + ''' + ''' CA1704: Identifiers should be spelled correctly + ''' + + Public NotInheritable Class BasicIdentifiersShouldBeSpelledCorrectlyFixer + Inherits IdentifiersShouldBeSpelledCorrectlyFixer + + + + Public Sub New() + End Sub + End Class +End Namespace diff --git a/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/Text.VisualBasic.Analyzers.vbproj b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/Text.VisualBasic.Analyzers.vbproj new file mode 100644 index 0000000000000..446e49c2b7626 --- /dev/null +++ b/src/RoslynAnalyzers/Text.Analyzers/VisualBasic/Text.VisualBasic.Analyzers.vbproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + $(MicrosoftCodeAnalysisVersionForTextAnalyzers) + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/GenerateAnalyzerNuspec.csproj b/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/GenerateAnalyzerNuspec.csproj new file mode 100644 index 0000000000000..071bdc74b76f0 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/GenerateAnalyzerNuspec.csproj @@ -0,0 +1,8 @@ + + + Exe + $(NetRoslyn) + true + false + + diff --git a/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/Program.cs b/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/Program.cs new file mode 100644 index 0000000000000..84bf9192516ae --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateAnalyzerNuspec/Program.cs @@ -0,0 +1,300 @@ +// 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.IO; +using System.Linq; +using System.Text; + +if (args.Length != 22) +{ + for (var i = 0; i < args.Length; i++) + { + Console.WriteLine($"Arg {i}: {args[i]}"); + } +} + +string nuspecFile = args[0]; +string assetsDir = args[1]; +string projectDir = args[2]; +string configuration = args[3]; +string[] tfms = args[4].Split(';'); +var metadataList = args[5].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var fileList = args[6].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var readmeFile = args[7]; +var folderList = args[8].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var assemblyList = args[9].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var dependencyList = args[10].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var libraryList = args[11].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); +var rulesetsDir = args[12]; +var editorconfigsDir = args[13]; +var artifactsBinDir = args[14]; +var analyzerDocumentationFileDir = args[15]; +var analyzerDocumentationFileName = args[16]; +var analyzerSarifFileDir = args[17]; +var analyzerSarifFileName = args[18]; +var analyzerConfigurationFileDir = args[19]; +var analyzerConfigurationFileName = args[20]; +var globalAnalyzerConfigsDir = args[21]; + +var result = new StringBuilder(); + +result.AppendLine(@""); +result.AppendLine(@""); +result.AppendLine(@" "); + +string version = string.Empty; +string repositoryType = string.Empty; +string repositoryUrl = string.Empty; +string repositoryCommit = string.Empty; +string readmePackageLocation = string.Empty; + +foreach (string entry in metadataList) +{ + int equals = entry.IndexOf('='); + string name = entry[..equals]; + string value = entry[(equals + 1)..]; + switch (name) + { + case "repositoryType": repositoryType = value; continue; + case "repositoryUrl": repositoryUrl = value; continue; + case "repositoryCommit": repositoryCommit = value; continue; + case "license": result.AppendLine($" {value}"); continue; + case "readme": readmePackageLocation = value; break; + } + + if (value.Length > 0) + { + result.AppendLine($" <{name}>{value}"); + } + + if (name == "version") + { + version = value; + } +} + +if (!string.IsNullOrEmpty(repositoryType)) +{ + result.AppendLine($@" "); +} + +if (dependencyList.Length > 0) +{ + result.AppendLine(@" "); + + foreach (var dependency in dependencyList) + { + result.AppendLine($@" "); + } + + result.AppendLine(@" "); +} + +result.AppendLine(@" "); + +result.AppendLine(@" "); +result.AppendLine(@" $CommonFileElements$"); + +if (fileList.Length > 0 || assemblyList.Length > 0 || libraryList.Length > 0 || folderList.Length > 0 || readmePackageLocation.Length > 0) +{ + const string csName = "CSharp"; + const string vbName = "VisualBasic"; + const string csTarget = @"analyzers\dotnet\cs"; + const string vbTarget = @"analyzers\dotnet\vb"; + const string agnosticTarget = @"analyzers\dotnet"; + + var allTargets = new List(); + if (assemblyList.Any(assembly => assembly.Contains(csName, StringComparison.Ordinal))) + { + allTargets.Add(csTarget); + } + + if (assemblyList.Any(assembly => assembly.Contains(vbName, StringComparison.Ordinal))) + { + allTargets.Add(vbTarget); + } + + if (allTargets.Count == 0) + { + allTargets.Add(agnosticTarget); + } + + foreach (string assembly in assemblyList) + { + IEnumerable targets; + + if (assembly.Contains(csName, StringComparison.Ordinal)) + { + targets = new[] { csTarget }; + } + else if (assembly.Contains(vbName, StringComparison.Ordinal)) + { + targets = new[] { vbTarget }; + } + else + { + targets = allTargets; + } + + string assemblyNameWithoutExtension = Path.GetFileNameWithoutExtension(assembly); + + foreach (var tfm in tfms) + { + string assemblyFolder = Path.Combine(artifactsBinDir, assemblyNameWithoutExtension, configuration, tfm); + string assemblyPathForNuspec = Path.Combine(assemblyFolder, assembly); + + foreach (string target in targets) + { + result.AppendLine(FileElement(assemblyPathForNuspec, target)); + + if (Directory.Exists(assemblyFolder)) + { + string resourceAssemblyName = assemblyNameWithoutExtension + ".resources.dll"; + foreach (var directory in Directory.EnumerateDirectories(assemblyFolder)) + { + var resourceAssemblyFullPath = Path.Combine(directory, resourceAssemblyName); + if (File.Exists(resourceAssemblyFullPath)) + { + var directoryName = Path.GetFileName(directory); + string resourceAssemblyPathForNuspec = Path.Combine(artifactsBinDir, assemblyNameWithoutExtension, configuration, tfm, directoryName, resourceAssemblyName); + string targetForNuspec = Path.Combine(target, directoryName); + result.AppendLine(FileElement(resourceAssemblyPathForNuspec, targetForNuspec)); + } + } + } + } + } + } + + foreach (string file in fileList) + { + var fileWithPath = Path.IsPathRooted(file) ? file : Path.Combine(projectDir, file); + result.AppendLine(FileElement(fileWithPath, "buildTransitive")); + } + + if (readmePackageLocation.Length > 0) + { + readmeFile = Path.IsPathRooted(readmeFile) ? readmeFile : Path.GetFullPath(Path.Combine(projectDir, readmeFile)); + var directoryName = Path.GetDirectoryName(readmePackageLocation) ?? string.Empty; + result.AppendLine(FileElement(readmeFile, directoryName)); + } + + foreach (string file in libraryList) + { + foreach (var tfm in tfms) + { + var fileWithPath = Path.Combine(artifactsBinDir, Path.GetFileNameWithoutExtension(file), configuration, tfm, file); + + // For multi-tfm case, file may not exist for all tfms. + if (File.Exists(fileWithPath)) + { + result.AppendLine(FileElement(fileWithPath, Path.Combine("lib", tfm))); + } + } + } + + // Skip packaging certain well-known third-party assemblies that ship within Microsoft.CodeAnalysis.Features package. + var fileNamesToExclude = new List() { "Humanizer.dll", "MessagePack.dll", "MessagePack.Annotations.dll" }; + + foreach (string folder in folderList) + { + foreach (var tfm in tfms) + { + string folderPath = Path.Combine(artifactsBinDir, folder, configuration, tfm); + foreach (var file in Directory.EnumerateFiles(folderPath)) + { + var fileExtension = Path.GetExtension(file); + if (fileExtension is ".exe" or ".dll" or ".config" or ".xml") + { + var fileName = Path.GetFileName(file); + if (fileNamesToExclude.Contains(fileName, StringComparer.OrdinalIgnoreCase)) + continue; + + var fileWithPath = Path.Combine(folderPath, file); + var targetPath = tfms.Length > 1 ? Path.Combine(folder, tfm) : folder; + result.AppendLine(FileElement(fileWithPath, targetPath)); + } + } + } + } + + result.AppendLine(FileElement(Path.Combine(assetsDir, "Install.ps1"), "tools")); + result.AppendLine(FileElement(Path.Combine(assetsDir, "Uninstall.ps1"), "tools")); +} + +if (rulesetsDir.Length > 0 && Directory.Exists(rulesetsDir)) +{ + foreach (string ruleset in Directory.EnumerateFiles(rulesetsDir)) + { + if (Path.GetExtension(ruleset) == ".ruleset") + { + result.AppendLine(FileElement(Path.Combine(rulesetsDir, ruleset), "rulesets")); + } + } +} + +if (editorconfigsDir.Length > 0 && Directory.Exists(editorconfigsDir)) +{ + foreach (string directory in Directory.EnumerateDirectories(editorconfigsDir)) + { + var directoryName = new DirectoryInfo(directory).Name; + foreach (string editorconfig in Directory.EnumerateFiles(directory)) + { + result.AppendLine(FileElement(Path.Combine(directory, editorconfig), $"editorconfig\\{directoryName}")); + } + } +} + +if (globalAnalyzerConfigsDir.Length > 0 && Directory.Exists(globalAnalyzerConfigsDir)) +{ + foreach (string globalconfig in Directory.EnumerateFiles(globalAnalyzerConfigsDir)) + { + if (Path.GetExtension(globalconfig) == ".globalconfig") + { + result.AppendLine(FileElement(Path.Combine(globalAnalyzerConfigsDir, globalconfig), $"buildTransitive\\config")); + } + else + { + throw new InvalidDataException($"Encountered a file with unexpected extension: {globalconfig}"); + } + } +} + +if (analyzerDocumentationFileDir.Length > 0 && Directory.Exists(analyzerDocumentationFileDir) && analyzerDocumentationFileName.Length > 0) +{ + var fileWithPath = Path.Combine(analyzerDocumentationFileDir, analyzerDocumentationFileName); + if (File.Exists(fileWithPath)) + { + result.AppendLine(FileElement(fileWithPath, "documentation")); + } +} + +if (analyzerSarifFileDir.Length > 0 && Directory.Exists(analyzerSarifFileDir) && analyzerSarifFileName.Length > 0) +{ + var fileWithPath = Path.Combine(analyzerSarifFileDir, analyzerSarifFileName); + if (File.Exists(fileWithPath)) + { + result.AppendLine(FileElement(fileWithPath, "documentation")); + } +} + +if (analyzerConfigurationFileDir.Length > 0 && Directory.Exists(analyzerConfigurationFileDir) && analyzerConfigurationFileName.Length > 0) +{ + var fileWithPath = Path.Combine(analyzerConfigurationFileDir, analyzerConfigurationFileName); + if (File.Exists(fileWithPath)) + { + result.AppendLine(FileElement(fileWithPath, "documentation")); + } +} + +result.AppendLine(FileElement(Path.Combine(assetsDir, "ThirdPartyNotices.txt"), "")); +result.AppendLine(@" "); + +result.AppendLine(@""); + +File.WriteAllText(nuspecFile, result.ToString()); + +static string FileElement(string file, string target) => $@" "; diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CodeFixerExtensions.cs b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CodeFixerExtensions.cs new file mode 100644 index 0000000000000..1a009aefa24f8 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CodeFixerExtensions.cs @@ -0,0 +1,87 @@ +// 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.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; + +#pragma warning disable CA1031 // Do not catch general exception types + +namespace GenerateDocumentationAndConfigFiles +{ + public static class FixerExtensions + { + /// + /// Get all the s that are implemented in the given + /// + /// An array of s + public static ImmutableArray GetFixers(this AnalyzerFileReference analyzerFileReference) + { + if (analyzerFileReference == null) + { + return ImmutableArray.Empty; + } + + ImmutableArray.Builder? builder = null; + + try + { + Assembly analyzerAssembly = analyzerFileReference.GetAssembly(); + IEnumerable typeInfos = analyzerAssembly.DefinedTypes; + + foreach (TypeInfo typeInfo in typeInfos) + { + if (typeInfo.IsSubclassOf(typeof(CodeFixProvider))) + { + try + { + ExportCodeFixProviderAttribute? attribute = typeInfo.GetCustomAttribute(); + if (attribute != null) + { + builder ??= ImmutableArray.CreateBuilder(); + var fixer = (CodeFixProvider?)Activator.CreateInstance(typeInfo.AsType()); + if (HasImplementation(fixer)) + { + builder.Add(fixer); + } + } + } + catch + { + } + } + } + } + catch + { + } + + return builder != null ? builder.ToImmutable() : ImmutableArray.Empty; + } + + /// + /// Check the method body of the Initialize method of an analyzer and if that's empty, + /// then the analyzer hasn't been implemented yet. + /// + private static bool HasImplementation([NotNullWhen(true)] CodeFixProvider? fixer) + { + MethodInfo? method = fixer?.GetType().GetTypeInfo().GetMethod("RegisterCodeFixesAsync"); + AsyncStateMachineAttribute? stateMachineAttr = method?.GetCustomAttribute(); + MethodInfo? moveNextMethod = stateMachineAttr?.StateMachineType.GetTypeInfo().GetDeclaredMethod("MoveNext"); + if (moveNextMethod != null) + { + MethodBody? body = moveNextMethod.GetMethodBody(); + int? ilInstructionCount = body?.GetILAsByteArray()?.Length; + return ilInstructionCount != 177; + } + + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CommonPropertyNames.cs b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CommonPropertyNames.cs new file mode 100644 index 0000000000000..b44de3f1f8f88 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/CommonPropertyNames.cs @@ -0,0 +1,20 @@ +// 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 GenerateDocumentationAndConfigFiles +{ + public static class CommonPropertyNames + { + public const string NetAnalyzersPackageName = "Microsoft.CodeAnalysis.NetAnalyzers"; + public const string NetAnalyzersNugetAssemblyVersionPropertyName = "_NETAnalyzersNuGetAssemblyVersion"; + public const string NetAnalyzersSDKAssemblyVersionPropertyName = "_NETAnalyzersSDKAssemblyVersion"; + + public const string TextAnalyzersPackageName = "Text.Analyzers"; + + public const string CodeAnalysisAnalyzersPackageName = "Microsoft.CodeAnalysis.Analyzers"; + public const string PublicApiAnalyzersPackageName = "Microsoft.CodeAnalysis.PublicApiAnalyzers"; + public const string PerformanceSensitiveAnalyzersPackageName = "Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers"; + public const string ResxSourceGeneratorPackageName = "Microsoft.CodeAnalysis.ResxSourceGenerator"; + } +} diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/GenerateDocumentationAndConfigFiles.csproj b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/GenerateDocumentationAndConfigFiles.csproj new file mode 100644 index 0000000000000..e076eb20401c8 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/GenerateDocumentationAndConfigFiles.csproj @@ -0,0 +1,31 @@ + + + Exe + $(NetRoslyn) + true + false + true + $(MicrosoftCodeAnalysisVersionForExecution) + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/JsonWriter.cs b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/JsonWriter.cs new file mode 100644 index 0000000000000..d6c03412e011a --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/JsonWriter.cs @@ -0,0 +1,251 @@ +// 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. + +// https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/InternalUtilities/JsonWriter.cs + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Text; + +namespace Roslyn.Utilities +{ + /// + /// A simple, forward-only JSON writer to avoid adding dependencies to the compiler. + /// Used to generate /errorlogger output. + /// + /// Does not guarantee well-formed JSON if misused. It is the caller's responsibility + /// to balance array/object start/end, to only write key-value pairs to objects and + /// elements to arrays, etc. + /// + /// Takes ownership of the given at construction and handles its disposal. + /// + internal sealed class JsonWriter : IDisposable + { + private readonly TextWriter _output; + private int _indent; + private Pending _pending; + + private enum Pending { None, NewLineAndIndent, CommaNewLineAndIndent }; + private const string Indentation = " "; + + public JsonWriter(TextWriter output) + { + _output = output; + _pending = Pending.None; + } + + public void WriteObjectStart() + { + WriteStart('{'); + } + + public void WriteObjectStart(string key) + { + WriteKey(key); + WriteObjectStart(); + } + + public void WriteObjectEnd() + { + WriteEnd('}'); + } + + public void WriteArrayStart() + { + WriteStart('['); + } + + public void WriteArrayStart(string key) + { + WriteKey(key); + WriteArrayStart(); + } + + public void WriteArrayEnd() + { + WriteEnd(']'); + } + + public void WriteKey(string key) + { + Write(key); + _output.Write(": "); + _pending = Pending.None; + } + + public void Write(string key, string value) + { + WriteKey(key); + Write(value); + } + + public void Write(string key, int value) + { + WriteKey(key); + Write(value); + } + + public void Write(string key, bool value) + { + WriteKey(key); + Write(value); + } + + public void Write(string value) + { + WritePending(); + _output.Write('"'); + _output.Write(EscapeString(value)); + _output.Write('"'); + _pending = Pending.CommaNewLineAndIndent; + } + + public void Write(int value) + { + WritePending(); + _output.Write(value.ToString(CultureInfo.InvariantCulture)); + _pending = Pending.CommaNewLineAndIndent; + } + + public void Write(bool value) + { + WritePending(); + _output.Write(value ? "true" : "false"); + _pending = Pending.CommaNewLineAndIndent; + } + + private void WritePending() + { + if (_pending == Pending.None) + { + return; + } + + Debug.Assert(_pending is Pending.NewLineAndIndent or Pending.CommaNewLineAndIndent); + if (_pending == Pending.CommaNewLineAndIndent) + { + _output.Write(','); + } + + _output.WriteLine(); + + for (int i = 0; i < _indent; i++) + { + _output.Write(Indentation); + } + } + + private void WriteStart(char c) + { + WritePending(); + _output.Write(c); + _pending = Pending.NewLineAndIndent; + _indent++; + } + + private void WriteEnd(char c) + { + _pending = Pending.NewLineAndIndent; + _indent--; + WritePending(); + _output.Write(c); + _pending = Pending.CommaNewLineAndIndent; + } + + public void Dispose() + { + _output.Dispose(); + } + + // String escaping implementation forked from System.Runtime.Serialization.Json to + // avoid a large dependency graph for this small amount of code: + // + // https://github.com/dotnet/corefx/blob/main/src/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JavaScriptString.cs + // + private static string EscapeString(string value) + { + StringBuilder? b = null; + + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + int startIndex = 0; + int count = 0; + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + + if (c == '\"' || c == '\\' || ShouldAppendAsUnicode(c)) + { + b ??= new StringBuilder(); + + if (count > 0) + { + b.Append(value, startIndex, count); + } + + startIndex = i + 1; + count = 0; + } + + switch (c) + { + case '\"': + b!.Append("\\\""); + break; + case '\\': + b!.Append("\\\\"); + break; + default: + if (ShouldAppendAsUnicode(c)) + { + AppendCharAsUnicode(b!, c); + } + else + { + count++; + } + + break; + } + } + + if (b == null) + { + return value; + } + + if (count > 0) + { + b.Append(value, startIndex, count); + } + + return b.ToString(); + } + + private static void AppendCharAsUnicode(StringBuilder builder, char c) + { + builder.Append("\\u"); + builder.AppendFormat(CultureInfo.InvariantCulture, "{0:x4}", (int)c); + } + + private static bool ShouldAppendAsUnicode(char c) + { + // Note on newline characters: Newline characters in JSON strings need to be encoded on the way out + // See Unicode 6.2, Table 5-1 (http://www.unicode.org/versions/Unicode6.2.0/ch05.pdf]) for the full list. + + // We only care about NEL, LS, and PS, since the other newline characters are all + // control characters so are already encoded. + + return c is < ' ' or + >= ((char)0xfffe) or // max char + >= ((char)0xd800) and <= ((char)0xdfff) or // between high and low surrogate + '\u0085' or '\u2028' or '\u2029'; // Unicode new line characters + } + } +} diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/Program.cs b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/Program.cs new file mode 100644 index 0000000000000..2aae41a83149a --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/Program.cs @@ -0,0 +1,1771 @@ +// 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 System.Globalization; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Analyzer.Utilities.PooledObjects; +using Analyzer.Utilities.PooledObjects.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.ReleaseTracking; +using Microsoft.CodeAnalysis.Text; +using static GenerateDocumentationAndConfigFiles.CommonPropertyNames; + +namespace GenerateDocumentationAndConfigFiles +{ + public static class Program + { + private static readonly HttpClient httpClient = new(); + + public static async Task Main(string[] args) + { + const int expectedArguments = 23; + const string validateOnlyPrefix = "-validateOnly:"; + + if (args.Length != expectedArguments) + { + await Console.Error.WriteLineAsync($"Expected {expectedArguments} arguments, found {args.Length}: {string.Join(';', args)}").ConfigureAwait(false); + return 1; + } + + if (!args[0].StartsWith("-validateOnly:", StringComparison.OrdinalIgnoreCase)) + { + await Console.Error.WriteLineAsync($"Expected the first argument to start with `{validateOnlyPrefix}`. found `{args[0]}`.").ConfigureAwait(false); + return 1; + } + + if (!bool.TryParse(args[0][validateOnlyPrefix.Length..], out var validateOnly)) + { + validateOnly = false; + } + + var fileNamesWithValidationFailures = new List(); + + string analyzerRulesetsDir = args[1]; + string analyzerEditorconfigsDir = args[2]; + string analyzerGlobalconfigsDir = args[3]; + string binDirectory = args[4]; + string configuration = args[5]; + string tfm = args[6]; + var assemblyList = args[7].Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList(); + string propsFileDir = args[8]; + string propsFileName = args[9]; + string targetsFileDir = args[10]; + string targetsFileName = args[11]; + string propsFileToDisableNetAnalyzersInNuGetPackageName = args[12]; + string analyzerDocumentationFileDir = args[13]; + string analyzerDocumentationFileName = args[14]; + string analyzerSarifFileDir = args[15]; + string analyzerSarifFileName = args[16]; + var analyzerVersion = args[17]; + var analyzerPackageName = args[18]; + if (!bool.TryParse(args[19], out var containsPortedFxCopRules)) + { + containsPortedFxCopRules = false; + } + + if (!bool.TryParse(args[20], out var generateAnalyzerRulesMissingDocumentationFile)) + { + generateAnalyzerRulesMissingDocumentationFile = false; + } + + var releaseTrackingOptOutString = args[21]; + if (!bool.TryParse(releaseTrackingOptOutString, out bool releaseTrackingOptOut)) + { + releaseTrackingOptOut = false; + } + + if (!bool.TryParse(args[22], out var validateOffline)) + { + validateOffline = false; + } + + var allRulesById = new SortedList(); + var fixableDiagnosticIds = new HashSet(); + var categories = new HashSet(); + var rulesMetadata = new SortedList rules)>(); + foreach (string assembly in assemblyList) + { + var assemblyName = Path.GetFileNameWithoutExtension(assembly); + string path = Path.Combine(binDirectory, assemblyName, configuration, tfm, assembly); + if (!File.Exists(path)) + { + await Console.Error.WriteLineAsync($"'{path}' does not exist").ConfigureAwait(false); + return 1; + } + + var analyzerFileReference = new AnalyzerFileReference(path, AnalyzerAssemblyLoader.Instance); + analyzerFileReference.AnalyzerLoadFailed += AnalyzerFileReference_AnalyzerLoadFailed; + var analyzers = analyzerFileReference.GetAnalyzersForAllLanguages(); + + var assemblyRulesMetadata = (path, rules: new SortedList()); + + foreach (var analyzer in analyzers) + { + var analyzerType = analyzer.GetType(); + + foreach (var rule in analyzer.SupportedDiagnostics) + { + allRulesById[rule.Id] = rule; + categories.Add(rule.Category); + assemblyRulesMetadata.rules[rule.Id] = (rule, analyzerType.Name, analyzerType.GetCustomAttribute(true)?.Languages); + } + } + + rulesMetadata.Add(assemblyName, assemblyRulesMetadata); + + foreach (var id in analyzerFileReference.GetFixers().SelectMany(fixer => fixer.FixableDiagnosticIds)) + { + fixableDiagnosticIds.Add(id); + } + } + + createRulesetAndEditorconfig( + "AllRulesDefault", + "All Rules with default severity", + @"All Rules with default severity. Rules with IsEnabledByDefault = false are disabled.", + RulesetKind.AllDefault); + + createRulesetAndEditorconfig( + "AllRulesEnabled", + "All Rules Enabled as build warnings", + "All Rules are enabled as build warnings. Rules with IsEnabledByDefault = false are force enabled as build warnings.", + RulesetKind.AllEnabled); + + createRulesetAndEditorconfig( + "AllRulesDisabled", + "All Rules Disabled", + @"All Rules are disabled.", + RulesetKind.AllDisabled); + + foreach (var category in categories) + { + createRulesetAndEditorconfig( + $"{category}RulesDefault", + $"{category} Rules with default severity", + $@"All {category} Rules with default severity. Rules with IsEnabledByDefault = false or from a different category are disabled.", + RulesetKind.CategoryDefault, + category: category); + + createRulesetAndEditorconfig( + $"{category}RulesEnabled", + $"{category} Rules Enabled as build warnings", + $@"All {category} Rules are enabled as build warnings. {category} Rules with IsEnabledByDefault = false are force enabled as build warnings. Rules from a different category are disabled.", + RulesetKind.CategoryEnabled, + category: category); + } + + // We generate custom tag based rulesets only for select custom tags. + var customTagsToGenerateRulesets = ImmutableArray.Create( + WellKnownDiagnosticTagsExtensions.Dataflow, + FxCopWellKnownDiagnosticTags.PortedFromFxCop); + + foreach (var customTag in customTagsToGenerateRulesets) + { + createRulesetAndEditorconfig( + $"{customTag}RulesDefault", + $"{customTag} Rules with default severity", + $@"All {customTag} Rules with default severity. Rules with IsEnabledByDefault = false and non-{customTag} rules are disabled.", + RulesetKind.CustomTagDefault, + customTag: customTag); + + createRulesetAndEditorconfig( + $"{customTag}RulesEnabled", + $"{customTag} Rules Enabled as build warnings", + $@"All {customTag} Rules are enabled as build warnings. {customTag} Rules with IsEnabledByDefault = false are force enabled as build warning. Non-{customTag} Rules are disabled.", + RulesetKind.CustomTagEnabled, + customTag: customTag); + } + + createPropsFiles(); + + createAnalyzerDocumentationFile(); + + createAnalyzerSarifFile(); + + if (generateAnalyzerRulesMissingDocumentationFile) + { + try + { + await createAnalyzerRulesMissingDocumentationFileAsync().ConfigureAwait(false); + } + catch (TimeoutException) + { + await Console.Out.WriteLineAsync($"Failed to create analyzer rules missing documentation file. Http response timed out").ConfigureAwait(false); + } + } + + if (fileNamesWithValidationFailures.Count > 0) + { + await Console.Error.WriteLineAsync("One or more auto-generated documentation files were either edited manually, or not updated. Please revert changes made to the following files (if manually edited) and run `dotnet msbuild /t:pack` at the root of the repo to automatically update them:").ConfigureAwait(false); + fileNamesWithValidationFailures.ForEach(fileName => Console.Error.WriteLine($" {fileName}")); + return 1; + } + + if (!await createGlobalConfigFilesAsync().ConfigureAwait(false)) + { + return 2; + } + + CreateTargetsFile(targetsFileDir, targetsFileName, analyzerPackageName, categories.OrderBy(c => c)); + + return 0; + + // Local functions. + static void AnalyzerFileReference_AnalyzerLoadFailed(object? sender, AnalyzerLoadFailureEventArgs e) + => throw e.Exception ?? new NotSupportedException(e.Message); + + void createRulesetAndEditorconfig( + string fileName, + string title, + string description, + RulesetKind rulesetKind, + string? category = null, + string? customTag = null) + { + CreateRuleset(analyzerRulesetsDir, fileName + ".ruleset", title, description, rulesetKind, category, customTag, allRulesById, analyzerPackageName); + CreateEditorconfig(analyzerEditorconfigsDir, fileName, title, description, rulesetKind, category, customTag, allRulesById); + return; + } + + void createPropsFiles() + { + if (string.IsNullOrEmpty(propsFileDir) || string.IsNullOrEmpty(propsFileName)) + { + Debug.Assert(!containsPortedFxCopRules); + Debug.Assert(string.IsNullOrEmpty(propsFileToDisableNetAnalyzersInNuGetPackageName)); + return; + } + + var disableNetAnalyzersImport = getDisableNetAnalyzersImport(); + + var fileContents = + $""" + + {disableNetAnalyzersImport}{getCodeAnalysisTreatWarningsAsErrors()}{getCompilerVisibleProperties()} + + """; + var directory = Directory.CreateDirectory(propsFileDir); + var fileWithPath = Path.Combine(directory.FullName, propsFileName); + + // This doesn't need validation as the generated file is part of artifacts. + File.WriteAllText(fileWithPath, fileContents); + + if (!string.IsNullOrEmpty(disableNetAnalyzersImport)) + { + Debug.Assert(Version.TryParse(analyzerVersion, out _)); + + fileWithPath = Path.Combine(directory.FullName, propsFileToDisableNetAnalyzersInNuGetPackageName); + fileContents = + $""" + + + + false + <{NetAnalyzersNugetAssemblyVersionPropertyName}>{analyzerVersion} + + + """; + // This doesn't need validation as the generated file is part of artifacts. + File.WriteAllText(fileWithPath, fileContents); + } + + return; + + string getDisableNetAnalyzersImport() + { + if (!string.IsNullOrEmpty(propsFileToDisableNetAnalyzersInNuGetPackageName)) + { + Debug.Assert(analyzerPackageName is NetAnalyzersPackageName or TextAnalyzersPackageName); + + return $""" + + + + + + + <{NetAnalyzersSDKAssemblyVersionPropertyName}>{analyzerVersion} + + + """; + } + + Debug.Assert(!containsPortedFxCopRules); + return string.Empty; + } + } + + string getCodeAnalysisTreatWarningsAsErrors() + { + var allRuleIds = string.Join(';', allRulesById.Keys); + return $""" + + + + {allRuleIds} + $(CodeAnalysisTreatWarningsAsErrors) + $(WarningsNotAsErrors);$(CodeAnalysisRuleIds) + + """; + } + + string getCompilerVisibleProperties() + { + return analyzerPackageName switch + { + ResxSourceGeneratorPackageName => """ + + + + + + + + + + + + + + + + """, + _ => "", + }; + } + + void createAnalyzerDocumentationFile() + { + if (string.IsNullOrEmpty(analyzerDocumentationFileDir) || string.IsNullOrEmpty(analyzerDocumentationFileName) || allRulesById.Count == 0) + { + Debug.Assert(!containsPortedFxCopRules); + return; + } + + var directory = Directory.CreateDirectory(analyzerDocumentationFileDir); + var fileWithPath = Path.Combine(directory.FullName, analyzerDocumentationFileName); + + var builder = new StringBuilder(); + + var fileTitle = Path.GetFileNameWithoutExtension(analyzerDocumentationFileName); + builder.AppendLine($"# {fileTitle}"); + builder.AppendLine(); + + var isFirstEntry = true; + foreach (var ruleById in allRulesById) + { + string ruleId = ruleById.Key; + DiagnosticDescriptor descriptor = ruleById.Value; + + var ruleIdWithHyperLink = descriptor.Id; + if (!string.IsNullOrWhiteSpace(descriptor.HelpLinkUri)) + { + ruleIdWithHyperLink = $"[{ruleIdWithHyperLink}]({descriptor.HelpLinkUri})"; + } + + var title = descriptor.Title.ToString(CultureInfo.InvariantCulture).Trim(); + + title = escapeMarkdown(title); + + if (!isFirstEntry) + { + // Add separation line only when reaching next entry to avoid useless empty line at the end + builder.AppendLine(); + } + + isFirstEntry = false; + builder.AppendLine($"## {ruleIdWithHyperLink}: {title}"); + builder.AppendLine(); + + var description = descriptor.Description.ToString(CultureInfo.InvariantCulture); + if (string.IsNullOrWhiteSpace(description)) + { + description = descriptor.MessageFormat.ToString(CultureInfo.InvariantCulture); + } + + // Double the line breaks to ensure they are rendered properly in markdown + description = Regex.Replace(description, "(\r?\n)", "$1$1"); + description = escapeMarkdown(description); + // Add angle brackets around links to prevent violating MD034: + // https://github.com/DavidAnson/markdownlint/blob/82cf68023f7dbd2948a65c53fc30482432195de4/doc/Rules.md#md034---bare-url-used + // Regex taken from: https://github.com/DavidAnson/markdownlint/blob/59eaa869fc749e381fe9d53d04812dfc759595c6/helpers/helpers.js#L24 + description = Regex.Replace(description, @"(?:http|ftp)s?:\/\/[^\s\]""']*(?:\/|[^\s\]""'\W])", "<$0>"); + description = description.Trim(); + + builder.AppendLine(description); + builder.AppendLine(); + + builder.AppendLine("|Item|Value|"); + builder.AppendLine("|-|-|"); + builder.AppendLine($"|Category|{descriptor.Category}|"); + builder.AppendLine($"|Enabled|{descriptor.IsEnabledByDefault}|"); + builder.AppendLine($"|Severity|{descriptor.DefaultSeverity}|"); + var hasCodeFix = fixableDiagnosticIds.Contains(descriptor.Id); + builder.AppendLine($"|CodeFix|{hasCodeFix}|"); + builder.AppendLine("---"); + } + + if (validateOnly) + { + Validate(fileWithPath, builder.ToString(), fileNamesWithValidationFailures); + } + else + { + File.WriteAllText(fileWithPath, builder.ToString()); + } + } + + // Escape generic arguments to ensure they are not considered as HTML elements, and also escape asterisks. + static string escapeMarkdown(string text) + => Regex.Replace(text, "(<.+?>)", "\\$1").Replace("*", @"\*"); + + // based on https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/CommandLine/ErrorLogger.cs + void createAnalyzerSarifFile() + { + if (string.IsNullOrEmpty(analyzerSarifFileDir) || string.IsNullOrEmpty(analyzerSarifFileName) || allRulesById.Count == 0) + { + Debug.Assert(!containsPortedFxCopRules); + return; + } + + var culture = new CultureInfo("en-us"); + string tempAnalyzerSarifFileName = analyzerSarifFileName; + if (validateOnly) + { + // In validate only mode, we write the sarif file in a temp file and compare it with + // the existing content in `analyzerSarifFileName`. + tempAnalyzerSarifFileName = $"temp-{analyzerSarifFileName}"; + } + + var directory = Directory.CreateDirectory(analyzerSarifFileDir); + var fileWithPath = Path.Combine(directory.FullName, tempAnalyzerSarifFileName); + using var textWriter = new StreamWriter(fileWithPath, false, Encoding.UTF8); + using var writer = new Roslyn.Utilities.JsonWriter(textWriter); + writer.WriteObjectStart(); // root + writer.Write("$schema", "http://json.schemastore.org/sarif-1.0.0"); + writer.Write("version", "1.0.0"); + writer.WriteArrayStart("runs"); + + foreach (var assemblymetadata in rulesMetadata) + { + writer.WriteObjectStart(); // run + + writer.WriteObjectStart("tool"); + writer.Write("name", assemblymetadata.Key); + + if (!string.IsNullOrWhiteSpace(analyzerVersion)) + { + writer.Write("version", analyzerVersion); + } + + writer.Write("language", culture.Name); + writer.WriteObjectEnd(); // tool + + writer.WriteObjectStart("rules"); // rules + + foreach (var rule in assemblymetadata.Value.rules) + { + var ruleId = rule.Key; + var descriptor = rule.Value.rule; + + writer.WriteObjectStart(descriptor.Id); // rule + writer.Write("id", descriptor.Id); + + writer.Write("shortDescription", descriptor.Title.ToString(CultureInfo.InvariantCulture)); + + string fullDescription = descriptor.Description.ToString(CultureInfo.InvariantCulture); + writer.Write("fullDescription", !string.IsNullOrEmpty(fullDescription) ? fullDescription.Replace("\r\n", "\n") : descriptor.MessageFormat.ToString(CultureInfo.InvariantCulture)); + + writer.Write("defaultLevel", getLevel(descriptor.DefaultSeverity)); + + if (!string.IsNullOrEmpty(descriptor.HelpLinkUri)) + { + writer.Write("helpUri", descriptor.HelpLinkUri); + } + + writer.WriteObjectStart("properties"); + + writer.Write("category", descriptor.Category); + + writer.Write("isEnabledByDefault", descriptor.IsEnabledByDefault); + + writer.Write("typeName", rule.Value.typeName); + + if (rule.Value.languages?.Length > 0) + { + writer.WriteArrayStart("languages"); + + foreach (var language in rule.Value.languages.OrderBy(l => l, StringComparer.InvariantCultureIgnoreCase)) + { + writer.Write(language); + } + + writer.WriteArrayEnd(); // languages + } + + if (descriptor.CustomTags.Any()) + { + writer.WriteArrayStart("tags"); + + foreach (string tag in descriptor.CustomTags) + { + writer.Write(tag); + } + + writer.WriteArrayEnd(); // tags + } + + writer.WriteObjectEnd(); // properties + writer.WriteObjectEnd(); // rule + } + + writer.WriteObjectEnd(); // rules + writer.WriteObjectEnd(); // run + } + + writer.WriteArrayEnd(); // runs + writer.WriteObjectEnd(); // root + + if (validateOnly) + { + // Close is needed to be able to read the file. Dispose() should do the same job. + // Note: Although a using statement exists for the textWriter, its scope is the whole method. + // So Dispose isn't called before the whole method returns. + textWriter.Close(); + Validate(Path.Combine(directory.FullName, analyzerSarifFileName), File.ReadAllText(fileWithPath), fileNamesWithValidationFailures); + } + + return; + static string getLevel(DiagnosticSeverity severity) + { + switch (severity) + { + case DiagnosticSeverity.Info: + return "note"; + + case DiagnosticSeverity.Error: + return "error"; + + case DiagnosticSeverity.Warning: + return "warning"; + + case DiagnosticSeverity.Hidden: + return "hidden"; + + default: + Debug.Assert(false); + goto case DiagnosticSeverity.Warning; + } + } + } + + async ValueTask createAnalyzerRulesMissingDocumentationFileAsync() + { + if (string.IsNullOrEmpty(analyzerDocumentationFileDir) || allRulesById.Count == 0) + { + Debug.Assert(!containsPortedFxCopRules); + return; + } + + var directory = Directory.CreateDirectory(analyzerDocumentationFileDir); + var fileWithPath = Path.Combine(directory.FullName, "RulesMissingDocumentation.md"); + + var builder = new StringBuilder(); + builder.Append(""" + # Rules without documentation + + Rule ID | Missing Help Link | Title | + --------|-------------------|-------| + + """); + var actualContent = Array.Empty(); + if (validateOnly) + { + actualContent = File.ReadAllLines(fileWithPath); + } + + foreach (var ruleById in allRulesById) + { + string ruleId = ruleById.Key; + DiagnosticDescriptor descriptor = ruleById.Value; + + var helpLinkUri = descriptor.HelpLinkUri; + if (!string.IsNullOrWhiteSpace(helpLinkUri) && + await checkHelpLinkAsync(helpLinkUri).ConfigureAwait(false)) + { + // Rule with valid documentation link + continue; + } + + // The angle brackets around helpLinkUri are added to follow MD034 rule: + // https://github.com/DavidAnson/markdownlint/blob/82cf68023f7dbd2948a65c53fc30482432195de4/doc/Rules.md#md034---bare-url-used + if (!string.IsNullOrWhiteSpace(helpLinkUri)) + { + helpLinkUri = $"<{helpLinkUri}>"; + } + + var escapedTitle = descriptor.Title.ToString(CultureInfo.InvariantCulture).Replace("<", "\\<"); + var line = $"{ruleId} | {helpLinkUri} | {escapedTitle} |"; + if (validateOnly) + { + // The validation for RulesMissingDocumentation.md is different than others. + // We consider having "extra" entries as valid. This is to prevent CI failures due to rules being documented. + // However, we consider "missing" entries as invalid. This is to force updating the file when new rules are added. + if (!actualContent.Contains(line)) + { + await Console.Error.WriteLineAsync($"Missing entry in {fileWithPath}").ConfigureAwait(false); + await Console.Error.WriteLineAsync(line).ConfigureAwait(false); + // The file is missing an entry. Mark it as invalid and break the loop as there is no need to continue validating. + fileNamesWithValidationFailures.Add(fileWithPath); + break; + } + } + else + { + builder.AppendLine(line); + } + } + + if (!validateOnly) + { + File.WriteAllText(fileWithPath, builder.ToString()); + } + + return; + + async Task checkHelpLinkAsync(string helpLink) + { + try + { + if (!Uri.TryCreate(helpLink, UriKind.Absolute, out var uri)) + { + return false; + } + + if (validateOffline) + { + return true; + } + + var request = new HttpRequestMessage(HttpMethod.Head, uri); + using var response = await httpClient.SendAsync(request).ConfigureAwait(false); + return response?.StatusCode == HttpStatusCode.OK; + } + catch (WebException) + { + return false; + } + } + } + + async Task createGlobalConfigFilesAsync() + { + using var releaseTrackingFilesDataBuilder = ArrayBuilder.GetInstance(); + using var versionsBuilder = PooledHashSet.GetInstance(); + + // Validate all assemblies exist on disk and can be loaded. + foreach (string assembly in assemblyList) + { + var assemblyPath = GetAssemblyPath(assembly); + if (!File.Exists(assemblyPath)) + { + await Console.Error.WriteLineAsync($"'{assemblyPath}' does not exist").ConfigureAwait(false); + return false; + } + + try + { + _ = Assembly.LoadFrom(assemblyPath); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + await Console.Error.WriteLineAsync(ex.Message).ConfigureAwait(false); + return false; + } + } + + // Compute descriptors by rule ID and shipped analyzer release versions and shipped data. + var sawShippedFile = false; + foreach (string assembly in assemblyList) + { + var assemblyPath = GetAssemblyPath(assembly); + var assemblyDir = Path.GetDirectoryName(assemblyPath); + if (assemblyDir is null) + { + continue; + } + + var assemblyName = Path.GetFileNameWithoutExtension(assembly); + var shippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.ShippedFileName); + var unshippedFile = Path.Combine(assemblyDir, "AnalyzerReleases", assemblyName, ReleaseTrackingHelper.UnshippedFileName); + var shippedFileExists = File.Exists(shippedFile); + var unshippedFileExists = File.Exists(unshippedFile); + + if (shippedFileExists ^ unshippedFileExists) + { + var existingFile = shippedFileExists ? shippedFile : unshippedFile; + var nonExistingFile = shippedFileExists ? unshippedFile : shippedFile; + await Console.Error.WriteLineAsync($"Expected both '{shippedFile}' and '{unshippedFile}' to exist or not exist, but '{existingFile}' exists and '{nonExistingFile}' does not exist.").ConfigureAwait(false); + return false; + } + + if (shippedFileExists) + { + sawShippedFile = true; + + if (releaseTrackingOptOut) + { + await Console.Error.WriteLineAsync($"'{shippedFile}' exists but was not expected").ConfigureAwait(false); + return false; + } + + try + { + // Read shipped file + using var fileStream = File.OpenRead(shippedFile); + var sourceText = SourceText.From(fileStream); + var releaseTrackingData = ReleaseTrackingHelper.ReadReleaseTrackingData(shippedFile, sourceText, + onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new InvalidOperationException($"Duplicate entry in {shippedFile} at {line.LineNumber}: '{line}'"), + onInvalidEntry: (line, _2, _3, _4) => throw new InvalidOperationException($"Invalid entry in {shippedFile} at {line.LineNumber}: '{line}'"), + isShippedFile: true); + releaseTrackingFilesDataBuilder.Add(releaseTrackingData); + versionsBuilder.AddRange(releaseTrackingData.Versions); + + // Read unshipped file + using var fileStreamUnshipped = File.OpenRead(unshippedFile); + var sourceTextUnshipped = SourceText.From(fileStreamUnshipped); + var releaseTrackingDataUnshipped = ReleaseTrackingHelper.ReadReleaseTrackingData(unshippedFile, sourceTextUnshipped, + onDuplicateEntryInRelease: (_1, _2, _3, _4, line) => throw new InvalidOperationException($"Duplicate entry in {unshippedFile} at {line.LineNumber}: '{line}'"), + onInvalidEntry: (line, _2, _3, _4) => throw new InvalidOperationException($"Invalid entry in {unshippedFile} at {line.LineNumber}: '{line}'"), + isShippedFile: false); + releaseTrackingFilesDataBuilder.Add(releaseTrackingDataUnshipped); + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception ex) +#pragma warning restore CA1031 // Do not catch general exception types + { + await Console.Error.WriteLineAsync(ex.Message).ConfigureAwait(false); + return false; + } + } + } + + if (!releaseTrackingOptOut && !sawShippedFile) + { + await Console.Error.WriteLineAsync($"Could not find any 'AnalyzerReleases.Shipped.md' file").ConfigureAwait(false); + return false; + } + + if (versionsBuilder.Count > 0) + { + var releaseTrackingData = releaseTrackingFilesDataBuilder.ToImmutableArray(); + + // Generate global analyzer config files for each shipped version. + foreach (var version in versionsBuilder) + { + CreateGlobalConfigsForVersion(version, isShippedVersion: true, releaseTrackingData); + } + + // Generate global analyzer config files for unshipped version. + // See https://github.com/dotnet/roslyn-analyzers/issues/6247 for details. + + // Use 'unshippedVersion = maxShippedVersion + 1' for unshipped data. + var maxShippedVersion = versionsBuilder.Max(); + var unshippedVersion = new Version(maxShippedVersion!.Major + 1, maxShippedVersion.Minor); + CreateGlobalConfigsForVersion(unshippedVersion, isShippedVersion: false, releaseTrackingData); + } + + return true; + + // Local functions. + void CreateGlobalConfigsForVersion( + Version version, + bool isShippedVersion, + ImmutableArray releaseTrackingData) + { + var analysisLevelVersionString = GetNormalizedVersionStringForEditorconfigFileNameSuffix(version); + + foreach (var warnAsError in new[] { true, false }) + { + foreach (var analysisMode in Enum.GetValues(typeof(AnalysisMode))) + { + CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, warnAsError, releaseTrackingData, category: null); + foreach (var category in categories!) + { + CreateGlobalConfig(version, isShippedVersion, analysisLevelVersionString, (AnalysisMode)analysisMode!, warnAsError, releaseTrackingData, category); + } + } + } + } + + void CreateGlobalConfig( + Version version, + bool isShippedVersion, + string analysisLevelVersionString, + AnalysisMode analysisMode, + bool warnAsError, + ImmutableArray releaseTrackingData, + string? category) + { + var analysisLevelPropName = "AnalysisLevel"; + var title = $"Rules from '{version}' release with '{analysisMode}' analysis mode"; + var description = $"Rules with enabled-by-default state from '{version}' release with '{analysisMode}' analysis mode. Rules that are first released in a version later than '{version}' are disabled."; + + if (category != null) + { + analysisLevelPropName += category; + title = $"'{category}' {title}"; + description = $"'{category}' {description}"; + } + +#pragma warning disable CA1308 // Normalize strings to uppercase + var globalconfigFileName = $"{analysisLevelPropName}_{analysisLevelVersionString}_{analysisMode!.ToString()!.ToLowerInvariant()}"; +#pragma warning restore CA1308 // Normalize strings to uppercase + + if (warnAsError) + { + globalconfigFileName += "_warnaserror"; + title += " escalated to 'error' severity"; + description += " Enabled rules with 'warning' severity are escalated to 'error' severity to respect 'CodeAnalysisTreatWarningsAsErrors' MSBuild property."; + } + + CreateGlobalconfig( + analyzerGlobalconfigsDir, + $"{globalconfigFileName}.globalconfig", + title, + description, + warnAsError, + analysisMode, + category, + allRulesById, + (releaseTrackingData, version, isShippedVersion)); + } + + static string GetNormalizedVersionStringForEditorconfigFileNameSuffix(Version version) + { + var fieldCount = GetVersionFieldCount(version); + return version.ToString(fieldCount).Replace(".", "_", StringComparison.Ordinal); + + static int GetVersionFieldCount(Version version) + { + if (version.Revision > 0) + { + return 4; + } + + if (version.Build > 0) + { + return 3; + } + + if (version.Minor > 0) + { + return 2; + } + + return 1; + } + } + + string GetAssemblyPath(string assembly) + { + var assemblyName = Path.GetFileNameWithoutExtension(assembly); + var assemblyDir = Path.Combine(binDirectory, assemblyName, configuration, tfm); + return Path.Combine(assemblyDir, assembly); + } + } + } + + private static void CreateRuleset( + string analyzerRulesetsDir, + string rulesetFileName, + string rulesetTitle, + string rulesetDescription, + RulesetKind rulesetKind, + string? category, + string? customTag, + SortedList sortedRulesById, + string analyzerPackageName) + { + var text = GetRulesetOrEditorconfigText( + rulesetKind, + startRuleset, + endRuleset, + startRulesSection, + endRulesSection, + addRuleEntry, + getSeverityString, + commentStart: " ", + category, + customTag, + sortedRulesById); + + var directory = Directory.CreateDirectory(analyzerRulesetsDir); + var rulesetFilePath = Path.Combine(directory.FullName, rulesetFileName); + + // This doesn't need validation as the generated file is part of artifacts. + File.WriteAllText(rulesetFilePath, text); + return; + + // Local functions + void startRuleset(StringBuilder result) + { + result.AppendLine(@""); + result.AppendLine($@""); + } + + static void endRuleset(StringBuilder result) + { + result.AppendLine(""); + } + + void startRulesSection(StringBuilder result) + { + result.AppendLine($@" "); + } + + static void endRulesSection(StringBuilder result) + { + result.AppendLine(" "); + } + + static void addRuleEntry(StringBuilder result, DiagnosticDescriptor rule, string severity) + { + var spacing = new string(' ', count: 15 - severity.Length); + result.AppendLine($@" {spacing} "); + } + + static string getSeverityString(DiagnosticSeverity? severity) + { + return severity.HasValue ? severity.ToString() ?? "None" : "None"; + } + } + + private static void CreateEditorconfig( + string analyzerEditorconfigsDir, + string editorconfigFolder, + string editorconfigTitle, + string editorconfigDescription, + RulesetKind rulesetKind, + string? category, + string? customTag, + SortedList sortedRulesById) + { + var text = GetRulesetOrEditorconfigText( + rulesetKind, + startEditorconfig, + endEditorconfig, + startRulesSection, + endRulesSection, + addRuleEntry, + GetSeverityString, + commentStart: "# ", + commentEnd: string.Empty, + category, + customTag, + sortedRulesById); + + var directory = Directory.CreateDirectory(Path.Combine(analyzerEditorconfigsDir, editorconfigFolder)); + var editorconfigFilePath = Path.Combine(directory.FullName, ".editorconfig"); + + // This doesn't need validation as the generated file is part of artifacts. + File.WriteAllText(editorconfigFilePath, text); + return; + + // Local functions + void startEditorconfig(StringBuilder result) + { + result.AppendLine(@"# NOTE: Requires **VS2019 16.3** or later"); + result.AppendLine(); + result.AppendLine($@"# {editorconfigTitle}"); + result.AppendLine($@"# Description: {editorconfigDescription}"); + result.AppendLine(); + result.AppendLine(@"# Code files"); + result.AppendLine(@"[*.{cs,vb}]"); + result.AppendLine(); + } + + static void endEditorconfig(StringBuilder _) + { + } + + static void startRulesSection(StringBuilder _) + { + } + + static void endRulesSection(StringBuilder _) + { + } + + static void addRuleEntry(StringBuilder result, DiagnosticDescriptor rule, string severity) + { + result.AppendLine(); + result.AppendLine($"# {rule.Id}: {rule.Title}"); + result.AppendLine($@"dotnet_diagnostic.{rule.Id}.severity = {severity}"); + } + } + + private static string GetRulesetOrEditorconfigText( + RulesetKind rulesetKind, + Action startRulesetOrEditorconfig, + Action endRulesetOrEditorconfig, + Action startRulesSection, + Action endRulesSection, + Action addRuleEntry, + Func getSeverityString, + string commentStart, + string commentEnd, + string? category, + string? customTag, + SortedList sortedRulesById) + { + Debug.Assert(category == null || customTag == null); + Debug.Assert(category != null == (rulesetKind == RulesetKind.CategoryDefault || rulesetKind == RulesetKind.CategoryEnabled)); + Debug.Assert(customTag != null == (rulesetKind == RulesetKind.CustomTagDefault || rulesetKind == RulesetKind.CustomTagEnabled)); + + var result = new StringBuilder(); + startRulesetOrEditorconfig(result); + if (category == null && customTag == null) + { + addRules(categoryPass: false, customTagPass: false); + } + else + { + result.AppendLine($@"{commentStart}{category ?? customTag} Rules{commentEnd}"); + addRules(categoryPass: category != null, customTagPass: customTag != null); + result.AppendLine(); + result.AppendLine(); + result.AppendLine(); + result.AppendLine($@"{commentStart}Other Rules{commentEnd}"); + addRules(categoryPass: false, customTagPass: false); + } + + endRulesetOrEditorconfig(result); + return result.ToString(); + + void addRules(bool categoryPass, bool customTagPass) + { + if (!sortedRulesById.Any(r => !shouldSkipRule(r.Value))) + { + // Bail out if we don't have any rule to be added for this assembly. + return; + } + + startRulesSection(result); + + foreach (var rule in sortedRulesById) + { + addRule(rule.Value); + } + + endRulesSection(result); + + return; + + void addRule(DiagnosticDescriptor rule) + { + if (shouldSkipRule(rule)) + { + return; + } + + string severity = getRuleAction(rule); + addRuleEntry(result, rule, severity); + } + + bool shouldSkipRule(DiagnosticDescriptor rule) + { + switch (rulesetKind) + { + case RulesetKind.CategoryDefault: + case RulesetKind.CategoryEnabled: + if (categoryPass) + { + return rule.Category != category; + } + else + { + return rule.Category == category; + } + + case RulesetKind.CustomTagDefault: + case RulesetKind.CustomTagEnabled: + if (customTagPass) + { + return !rule.CustomTags.Contains(customTag); + } + else + { + return rule.CustomTags.Contains(customTag); + } + + default: + return false; + } + } + + string getRuleAction(DiagnosticDescriptor rule) + { + return rulesetKind switch + { + RulesetKind.CategoryDefault => getRuleActionCore(enable: categoryPass && rule.IsEnabledByDefault), + + RulesetKind.CategoryEnabled => getRuleActionCore(enable: categoryPass, enableAsWarning: categoryPass), + + RulesetKind.CustomTagDefault => getRuleActionCore(enable: customTagPass && rule.IsEnabledByDefault), + + RulesetKind.CustomTagEnabled => getRuleActionCore(enable: customTagPass, enableAsWarning: customTagPass), + + RulesetKind.AllDefault => getRuleActionCore(enable: rule.IsEnabledByDefault), + + RulesetKind.AllEnabled => getRuleActionCore(enable: true, enableAsWarning: true), + + RulesetKind.AllDisabled => getRuleActionCore(enable: false), + + _ => throw new InvalidProgramException(), + }; + + string getRuleActionCore(bool enable, bool enableAsWarning = false) + { + if (!enable && enableAsWarning) + { + throw new ArgumentException($"Unexpected arguments. '{nameof(enable)}' can't be false while '{nameof(enableAsWarning)}' is true."); + } + else if (enable) + { + return getSeverityString(enableAsWarning ? DiagnosticSeverity.Warning : rule.DefaultSeverity); + } + else + { + return getSeverityString(null); + } + } + } + } + } + + /// + /// Validates whether matches the contents of . + /// If they don't match, is added to . + /// The validation process is run within CI, so that the CI build fails when the auto-generated files are out of date. + /// + /// + /// Don't call this method with auto-generated files that are part of the artifacts because it's expected that they don't initially exist. + /// + private static void Validate(string fileWithPath, string fileContents, List fileNamesWithValidationFailures) + { + string actual = File.ReadAllText(fileWithPath); + if (actual != fileContents) + { + fileNamesWithValidationFailures.Add(fileWithPath); + } + } + + private static void CreateGlobalconfig( + string folder, + string fileName, + string title, + string description, + bool warnAsError, + AnalysisMode analysisMode, + string? category, + SortedList sortedRulesById, + (ImmutableArray releaseTrackingData, Version version, bool isShippedVersion) releaseTrackingDataAndVersion) + { + Debug.Assert(fileName.EndsWith(".globalconfig", StringComparison.Ordinal)); + + var text = GetGlobalconfigText( + title, + description, + warnAsError, + analysisMode, + category, + sortedRulesById, + releaseTrackingDataAndVersion); + var directory = Directory.CreateDirectory(folder); +#pragma warning disable CA1308 // Normalize strings to uppercase - Need to use 'ToLowerInvariant' for file names in non-Windows platforms + var configFilePath = Path.Combine(directory.FullName, fileName.ToLowerInvariant()); +#pragma warning restore CA1308 // Normalize strings to uppercase + File.WriteAllText(configFilePath, text); + return; + + // Local functions + static string GetGlobalconfigText( + string title, + string description, + bool warnAsError, + AnalysisMode analysisMode, + string? category, + SortedList sortedRulesById, + (ImmutableArray releaseTrackingData, Version version, bool isShippedVersion)? releaseTrackingDataAndVersion) + { + var result = new StringBuilder(); + StartGlobalconfig(); + AddRules(analysisMode, category); + return result.ToString(); + + void StartGlobalconfig() + { + result.AppendLine(@"# NOTE: Requires **VS2019 16.7** or later"); + result.AppendLine(); + result.AppendLine($@"# {title}"); + result.AppendLine($@"# Description: {description}"); + result.AppendLine(); + result.AppendLine($@"is_global = true"); + result.AppendLine(); + + // Append 'global_level' to ensure conflicts are properly resolved between different global configs: + // 1. Lowest precedence (-100): Category-agnostic config generated by us. + // 2. Higher precedence (-99): Category-specific config generated by us. + // 3. Highest predence (non-negative integer): User provided config. + // See https://github.com/dotnet/roslyn/issues/48634 for further details. + var globalLevel = category != null ? -99 : -100; + result.AppendLine($@"global_level = {globalLevel}"); + result.AppendLine(); + } + + bool AddRules(AnalysisMode analysisMode, string? category) + { + Debug.Assert(sortedRulesById.Count > 0); + + var addedRule = false; + foreach (var rule in sortedRulesById) + { + if (AddRule(rule.Value, category)) + { + addedRule = true; + } + } + + return addedRule; + + bool AddRule(DiagnosticDescriptor rule, string? category) + { + if (category != null && + !string.Equals(rule.Category, category, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + var (isEnabledByDefault, severity) = GetEnabledByDefaultAndSeverity(rule, analysisMode); + if (warnAsError && severity == DiagnosticSeverity.Warning && isEnabledByDefault) + { + severity = DiagnosticSeverity.Error; + } + + if (rule.IsEnabledByDefault == isEnabledByDefault && + severity == rule.DefaultSeverity) + { + // Rule had the same default severity and enabled state in the release. + // We do not need to generate any entry. + return false; + } + + string severityString = GetRuleSeverity(isEnabledByDefault, severity); + + result.AppendLine(); + result.AppendLine($"# {rule.Id}: {rule.Title}"); + result.AppendLine($@"dotnet_diagnostic.{rule.Id}.severity = {severityString}"); + return true; + } + + (bool isEnabledByDefault, DiagnosticSeverity effectiveSeverity) GetEnabledByDefaultAndSeverity(DiagnosticDescriptor rule, AnalysisMode analysisMode) + { + var isEnabledByDefault = rule.IsEnabledByDefault; + var effectiveSeverity = rule.DefaultSeverity; + + bool isEnabledRuleForNonDefaultAnalysisMode; + switch (analysisMode) + { + case AnalysisMode.None: + // Disable all rules by default. + return (isEnabledByDefault: false, DiagnosticSeverity.Warning); + + case AnalysisMode.All: + // Escalate all rules with a special custom tag to be build warnings. + isEnabledRuleForNonDefaultAnalysisMode = rule.CustomTags.Contains(WellKnownDiagnosticTagsExtensions.EnabledRuleInAggressiveMode); + break; + + case AnalysisMode.Minimum: + // Escalate all enabled, non-hidden rules to be build warnings. + isEnabledRuleForNonDefaultAnalysisMode = isEnabledByDefault && effectiveSeverity != DiagnosticSeverity.Hidden; + break; + + case AnalysisMode.Recommended: + // Escalate all enabled rules to be build warnings. + isEnabledRuleForNonDefaultAnalysisMode = isEnabledByDefault; + break; + + case AnalysisMode.Default: + // Retain the default severity and enabled by default values. + isEnabledRuleForNonDefaultAnalysisMode = false; + break; + + default: + throw new NotSupportedException(); + } + + if (isEnabledRuleForNonDefaultAnalysisMode) + { + isEnabledByDefault = true; + effectiveSeverity = DiagnosticSeverity.Warning; + } + + if (releaseTrackingDataAndVersion != null) + { + isEnabledByDefault = isEnabledRuleForNonDefaultAnalysisMode; + var maxVersion = releaseTrackingDataAndVersion.Value.isShippedVersion ? + releaseTrackingDataAndVersion.Value.version : + ReleaseTrackingHelper.UnshippedVersion; + var foundReleaseTrackingEntry = false; + foreach (var releaseTrackingData in releaseTrackingDataAndVersion.Value.releaseTrackingData) + { + if (releaseTrackingData.TryGetLatestReleaseTrackingLine(rule.Id, maxVersion, out _, out var releaseTrackingLine)) + { + foundReleaseTrackingEntry = true; + + if (releaseTrackingLine.EnabledByDefault.HasValue && + releaseTrackingLine.DefaultSeverity.HasValue) + { + isEnabledByDefault = releaseTrackingLine.EnabledByDefault.Value && !releaseTrackingLine.IsRemovedRule; + effectiveSeverity = releaseTrackingLine.DefaultSeverity.Value; + + if (isEnabledRuleForNonDefaultAnalysisMode && !releaseTrackingLine.IsRemovedRule) + { + isEnabledByDefault = true; + effectiveSeverity = DiagnosticSeverity.Warning; + } + + break; + } + } + } + + if (!foundReleaseTrackingEntry) + { + // Rule is unshipped or first shipped in a version later than 'maxVersion', so mark it as disabled. + isEnabledByDefault = false; + } + } + + return (isEnabledByDefault, effectiveSeverity); + } + + static string GetRuleSeverity(bool isEnabledByDefault, DiagnosticSeverity defaultSeverity) + { + if (isEnabledByDefault) + { + return GetSeverityString(defaultSeverity); + } + else + { + return GetSeverityString(null); + } + } + } + } + } + + private static string GetSeverityString(DiagnosticSeverity? severity) + { + if (!severity.HasValue) + { + return "none"; + } + + return severity.Value switch + { + DiagnosticSeverity.Error => "error", + DiagnosticSeverity.Warning => "warning", + DiagnosticSeverity.Info => "suggestion", + DiagnosticSeverity.Hidden => "silent", + _ => throw new NotImplementedException(severity.Value.ToString()), + }; + } + + private static void CreateTargetsFile(string targetsFileDir, string targetsFileName, string packageName, IOrderedEnumerable categories) + { + if (string.IsNullOrEmpty(targetsFileDir) || string.IsNullOrEmpty(targetsFileName)) + { + return; + } + + var fileContents = + $""" + {GetCommonContents(packageName, categories)}{GetPackageSpecificContents(packageName)} + + """; + var directory = Directory.CreateDirectory(targetsFileDir); + var fileWithPath = Path.Combine(directory.FullName, targetsFileName); + File.WriteAllText(fileWithPath, fileContents); + + static string GetCommonContents(string packageName, IOrderedEnumerable categories) + { + var stringBuilder = new StringBuilder(); + + stringBuilder.Append(GetGlobalAnalyzerConfigTargetContents(packageName, category: null)); + foreach (var category in categories) + { + stringBuilder.Append(GetGlobalAnalyzerConfigTargetContents(packageName, category)); + } + + stringBuilder.Append(GetMSBuildContentForPropertyAndItemOptions()); + stringBuilder.Append(GetCodeAnalysisTreatWarningsAsErrorsTargetContents()); + return stringBuilder.ToString(); + } + + static string GetGlobalAnalyzerConfigTargetContents(string packageName, string? category) + { + var analysisLevelPropName = "AnalysisLevel"; + var analysisLevelPrefixPropName = "AnalysisLevelPrefix"; + var analysisLevelSuffixPropName = "AnalysisLevelSuffix"; + var analysisModePropName = nameof(AnalysisMode); + var effectiveAnalysisLevelPropName = "EffectiveAnalysisLevel"; + var targetCondition = "'$(SkipGlobalAnalyzerConfigForPackage)' != 'true'"; + var afterTargets = string.Empty; + var trimmedPackageName = packageName.Replace(".", string.Empty, StringComparison.Ordinal); + + if (!string.IsNullOrEmpty(category)) + { + analysisLevelPropName += category; + analysisLevelPrefixPropName += category; + analysisLevelSuffixPropName += category; + analysisModePropName += category; + effectiveAnalysisLevelPropName += category; + + // For category-specific target, we also check if end-user has overriden category-specific AnalysisLevel or AnalysisMode. + targetCondition += $" and ('$({analysisLevelPropName})' != '' or '$({analysisModePropName})' != '')"; + + // Ensure that category-specific target executes after category-agnostic target + afterTargets += $@"AfterTargets=""AddGlobalAnalyzerConfigForPackage_{trimmedPackageName}"" "; + + trimmedPackageName += category; + } + + var packageVersionPropName = trimmedPackageName + "RulesVersion"; + var propertyStringForSettingDefaultPropertyValues = GetPropertyStringForSettingDefaultPropertyValues( + packageName, packageVersionPropName, category, analysisLevelPropName, + analysisLevelPrefixPropName, analysisLevelSuffixPropName, effectiveAnalysisLevelPropName); + + return $""" + + + + {propertyStringForSettingDefaultPropertyValues} + + <_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName}>$({analysisLevelSuffixPropName}) + <_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == ''">$({analysisModePropName}) + <_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == 'AllEnabledByDefault'">{nameof(AnalysisMode.All)} + <_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == 'AllDisabledByDefault'">{nameof(AnalysisMode.None)} + <_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})' == ''">{nameof(AnalysisMode.Default)} + + + + $(CodeAnalysisTreatWarningsAsErrors) + + <_GlobalAnalyzerConfigFileName_{trimmedPackageName}_WarnAsErrorSuffix Condition="'$(EffectiveCodeAnalysisTreatWarningsAsErrors)' == 'true'">_warnaserror + + + <_GlobalAnalyzerConfigFileName_{trimmedPackageName} Condition="'$({packageVersionPropName})' != ''">{analysisLevelPropName}_$({packageVersionPropName}.Replace(".","_"))_$(_GlobalAnalyzerConfigAnalysisMode_{trimmedPackageName})$(_GlobalAnalyzerConfigFileName_{trimmedPackageName}_WarnAsErrorSuffix).globalconfig + <_GlobalAnalyzerConfigFileName_{trimmedPackageName}>$(_GlobalAnalyzerConfigFileName_{trimmedPackageName}.ToLowerInvariant()) + + <_GlobalAnalyzerConfigDir_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigDir_{trimmedPackageName})' == ''">$(MSBuildThisFileDirectory)config + <_GlobalAnalyzerConfigFile_{trimmedPackageName} Condition="'$(_GlobalAnalyzerConfigFileName_{trimmedPackageName})' != ''">$(_GlobalAnalyzerConfigDir_{trimmedPackageName})\$(_GlobalAnalyzerConfigFileName_{trimmedPackageName}) + + + + + + + + """; + + static string GetPropertyStringForSettingDefaultPropertyValues( + string packageName, + string packageVersionPropName, + string? category, + string analysisLevelPropName, + string analysisLevelPrefixPropName, + string analysisLevelSuffixPropName, + string effectiveAnalysisLevelPropName) + { + if (packageName == NetAnalyzersPackageName) + { + var propertyStr = string.Empty; + + if (!string.IsNullOrEmpty(category)) + { + // For category-specific logic, we need to duplicate logic from SDK targets to set + // category-specific AnalysisLevel property values. In future, we should consider removing similar logic from + // SDK targets for core AnalysisLevel and instead generalize this logic. + + propertyStr += $""" + + + <{analysisLevelPropName} Condition="'$({analysisLevelPropName})' == ''">$(AnalysisLevel) + + + <{analysisLevelPrefixPropName} Condition="$({analysisLevelPropName}.Contains('-'))">$([System.Text.RegularExpressions.Regex]::Replace($({analysisLevelPropName}), '-(.)*', '')) + <{analysisLevelSuffixPropName} Condition="'$({analysisLevelPrefixPropName})' != ''">$([System.Text.RegularExpressions.Regex]::Replace($({analysisLevelPropName}), '$({analysisLevelPrefixPropName})-', '')) + + + <{effectiveAnalysisLevelPropName} Condition="'$({analysisLevelPropName})' == 'none' or '$({analysisLevelPrefixPropName})' == 'none'">$(_NoneAnalysisLevel) + <{effectiveAnalysisLevelPropName} Condition="'$({analysisLevelPropName})' == 'latest' or '$({analysisLevelPrefixPropName})' == 'latest'">$(_LatestAnalysisLevel) + <{effectiveAnalysisLevelPropName} Condition="'$({analysisLevelPropName})' == 'preview' or '$({analysisLevelPrefixPropName})' == 'preview'">$(_PreviewAnalysisLevel) + + + <{effectiveAnalysisLevelPropName} Condition="'$({effectiveAnalysisLevelPropName})' == '' And + '$({analysisLevelPrefixPropName})' != ''">$({analysisLevelPrefixPropName}) + <{effectiveAnalysisLevelPropName} Condition="'$({effectiveAnalysisLevelPropName})' == '' And + '$({analysisLevelPropName})' != ''">$({analysisLevelPropName}) + + """; + } + + propertyStr += $""" + + + <{packageVersionPropName} Condition="'$({packageVersionPropName})' == '' and $({effectiveAnalysisLevelPropName}) != ''">$([System.Text.RegularExpressions.Regex]::Replace($({effectiveAnalysisLevelPropName}), '(.0)*$', '')) + + """; + return propertyStr; + } + + return string.Empty; + } + } + + static string GetMSBuildContentForPropertyAndItemOptions() + { + var builder = new StringBuilder(); + + AddMSBuildContentForPropertyOptions(builder); + AddMSBuildContentForItemOptions(builder); + + return builder.ToString(); + + static void AddMSBuildContentForPropertyOptions(StringBuilder builder) + { + var compilerVisibleProperties = new List(); + foreach (var field in typeof(MSBuildPropertyOptionNames).GetFields()) + { + compilerVisibleProperties.Add(field.Name); + } + + // Add ItemGroup for MSBuild property names that are required to be threaded as analyzer config options. + AddItemGroupForCompilerVisibleProperties(compilerVisibleProperties, builder); + } + + static void AddItemGroupForCompilerVisibleProperties(List compilerVisibleProperties, StringBuilder builder) + { + builder.AppendLine($""" + + + + """); + foreach (var compilerVisibleProperty in compilerVisibleProperties) + { + builder.AppendLine($@" "); + } + + builder.AppendLine($@" "); + } + + static void AddMSBuildContentForItemOptions(StringBuilder builder) + { + // Add ItemGroup and PropertyGroup for MSBuild item names that are required to be treated as analyzer config options. + // The analyzer config option will have the following key/value: + // - Key: Item name prefixed with an '_' and suffixed with a 'List' to reduce chances of conflicts with any existing project property. + // - Value: Concatenated item metadata values, separated by a ',' character. See https://github.com/dotnet/sdk/issues/12706#issuecomment-668219422 for details. + + builder.Append($""" + + + + + """); + var compilerVisibleProperties = new List(); + foreach (var field in typeof(MSBuildItemOptionNames).GetFields()) + { + // Item option name: "SupportedPlatform" + // Generated MSBuild property: "<_SupportedPlatformList>@(SupportedPlatform, '')" + + var itemOptionName = field.Name; + var propertyName = MSBuildItemOptionNamesHelpers.GetPropertyNameForItemOptionName(itemOptionName); + compilerVisibleProperties.Add(propertyName); + builder.AppendLine($@" <{propertyName}>@({itemOptionName}, '{MSBuildItemOptionNamesHelpers.ValuesSeparator}')"); + } + + builder.AppendLine($@" "); + + AddItemGroupForCompilerVisibleProperties(compilerVisibleProperties, builder); + } + } + + static string GetCodeAnalysisTreatWarningsAsErrorsTargetContents() + { + return $""" + + + + + $(CodeAnalysisTreatWarningsAsErrors) + $(WarningsNotAsErrors);$(CodeAnalysisRuleIds) + + + + """; + } + + const string AddAllResxFilesAsAdditionalFilesTarget = """ + + + + + + + + """; + + static string GetPackageSpecificContents(string packageName) + => packageName switch + { + CodeAnalysisAnalyzersPackageName => $""" + + {AddAllResxFilesAsAdditionalFilesTarget} + + + + + + + + + """, + PublicApiAnalyzersPackageName => """ + + + + + + + + + + """, + PerformanceSensitiveAnalyzersPackageName => """ + + + true + $(MSBuildThisFileDirectory)PerformanceSensitiveAttribute$(DefaultLanguageSourceExtension) + + + + + + + + + """, + ResxSourceGeneratorPackageName => $""" + + + + + + + + {AddAllResxFilesAsAdditionalFilesTarget} + + + + + + + + + + """, + _ => string.Empty, + }; + } + + private enum RulesetKind + { + AllDefault, + CategoryDefault, + CustomTagDefault, + AllEnabled, + CategoryEnabled, + CustomTagEnabled, + AllDisabled, + } + + // NOTE: **Do not** change the names of the fields for this enum - that would be a breaking change for user visible property setting for `AnalysisMode` property in MSBuild project file. + private enum AnalysisMode + { + Default, + None, + Minimum, + Recommended, + All + } + + private sealed class AnalyzerAssemblyLoader : IAnalyzerAssemblyLoader + { + public static IAnalyzerAssemblyLoader Instance = new AnalyzerAssemblyLoader(); + + private AnalyzerAssemblyLoader() { } + public void AddDependencyLocation(string fullPath) + { + } + + public Assembly LoadFromPath(string fullPath) + { + return Assembly.LoadFrom(fullPath); + } + } + } +} diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/README.md b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/README.md new file mode 100644 index 0000000000000..6208d9c76ca59 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFiles/README.md @@ -0,0 +1,26 @@ +## CodeAnalysisTreatWarningsAsErrors - Treat code analysis warnings as errors + +If you use the `/warnaserror` flag when you build your projects, all code analysis warnings are also treated as errors. If you do not want code quality warnings (CAxxxx) to be treated as errors in presence of `/warnaserror`, you can set the `CodeAnalysisTreatWarningsAsErrors` MSBuild property to `false` in your project file. Similarly, if want only code analysis warnings to be treated as errors, you can set `CodeAnalysisTreatWarningsAsErrors` MSBuild property to `true` in your project file. + +### Semantics for CodeAnalysisTreatWarningsAsErrors + +We have following important MSBuild properties that determine whether or not CAxxxx rules are bulk escalated (or not escalated) from warnings to errors: + +- `CodeAnalysisTreatWarningsAsErrors` +- `TreatWarningsAsErrors` +- `WarningsAsErrors` +- `WarningsNotAsErrors` + +Following are the precedence rules as per the values of these properties: + +1. For non-CAxxxx rules + + 1. `CodeAnalysisTreatWarningsAsErrors` has no relevance and is ignored. + 2. `TreatWarningsAsErrors`, if not set, defaults to false. If true, this property translates to `/warnaserror` command line switch. + 3. Compiler bumps all warnings to errors iff `TreatWarningsAsErrors` is true. Users can prevent escalation or force escalation of individual warnings to errors by appending the IDs to `WarningsNotAsErrors` or `WarningsAsErrors`, which just translate to `/warnaserror[+|-]:<%rule ids%>` on the command line. + +2. For CAxxxx rules: + + 1. If `CodeAnalysisTreatWarningsAsErrors` is set to true, enabled CA warnings are bulk escalated to errors by choosing the appropriate globalconfig file with the error severity settings. + 2. If `CodeAnalysisTreatWarningsAsErrors` is set to false and `TreatWarningsAsErrors` is set to true, we append all CA rule IDs to `WarningsNotAsErrors` to ensure they are not escalated to errors. Users can still bump individual rule IDs to errors by editorconfig/ruleset entry, etc. + 3. Otherwise, if `TreatWarningsAsErrors` is set to true, this property translates to `/warnaserror` command line switch and the compiler bumps all warnings, including enabled CA warnings, to errors. diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/GenerateDocumentationAndConfigFilesForBrokenRuntime.csproj b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/GenerateDocumentationAndConfigFilesForBrokenRuntime.csproj new file mode 100644 index 0000000000000..7a799044c0514 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/GenerateDocumentationAndConfigFilesForBrokenRuntime.csproj @@ -0,0 +1,25 @@ + + + Exe + $(NetRoslyn) + true + false + true + $(MicrosoftCodeAnalysisVersionForExecution) + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/Program.cs b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/Program.cs new file mode 100644 index 0000000000000..7d6fc98300cb3 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/GenerateDocumentationAndConfigFilesForBrokenRuntime/Program.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; + +namespace GenerateDocumentationAndConfigFilesForBrokenRuntime +{ + public static class Program + { + public static Task Main(string[] args) + { + // Delegate to the actual tool implementation + return GenerateDocumentationAndConfigFiles.Program.Main(args); + } + } +} diff --git a/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj b/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj new file mode 100644 index 0000000000000..d07a2661fc1ab --- /dev/null +++ b/src/RoslynAnalyzers/Tools/Metrics.Legacy/Metrics.Legacy.csproj @@ -0,0 +1,44 @@ + + + + net472 + Exe + $(DefineConstants),LEGACY_CODE_METRICS_MODE + true + + $(NoWarn);CS0436 + true + + $(MicrosoftCodeAnalysisVersionForMetrics) + 1.1.2 + $(MetricsVersionPrefix) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj b/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj new file mode 100644 index 0000000000000..dc2109a396955 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/Metrics/Metrics.csproj @@ -0,0 +1,38 @@ + + + + net472 + Exe + true + true + + $(NoWarn);CS0436 + $(MicrosoftCodeAnalysisVersionForMetrics) + 1.1.2 + $(MetricsVersionPrefix) + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Tools/Metrics/MetricsOutputWriter.cs b/src/RoslynAnalyzers/Tools/Metrics/MetricsOutputWriter.cs new file mode 100644 index 0000000000000..1b072758e1433 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/Metrics/MetricsOutputWriter.cs @@ -0,0 +1,175 @@ +// 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.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeMetrics; + +namespace Metrics +{ + internal static class MetricsOutputWriter + { + private const string Version = "1.0"; + + public static void WriteMetricFile(ImmutableArray<(string, CodeAnalysisMetricData)> data, XmlTextWriter writer) + { + writer.Formatting = Formatting.Indented; + + writer.WriteStartDocument(); + writer.WriteStartElement("CodeMetricsReport"); + writer.WriteAttributeString("Version", Version); + writer.WriteStartElement("Targets"); + writeMetrics(); + writer.WriteEndElement(); + writer.WriteEndElement(); + writer.WriteEndDocument(); + + return; + + void writeMetrics() + { + foreach (var kvp in data) + { + string filePath = kvp.Item1; + CodeAnalysisMetricData metric = kvp.Item2; + + writer.WriteStartElement("Target"); + writer.WriteAttributeString("Name", Path.GetFileName(filePath)); + + WriteMetricData(metric, writer); + + writer.WriteEndElement(); + } + } + } + + private static void WriteMetricData(CodeAnalysisMetricData data, XmlTextWriter writer) + { + writeHeader(); + writeMetrics(); + writeChildren(); + writer.WriteEndElement(); + + return; + + void writeHeader() + { + writer.WriteStartElement(data.Symbol.Kind.ToString()); + switch (data.Symbol.Kind) + { + case SymbolKind.NamedType: + var minimalTypeName = new StringBuilder(data.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + + var containingType = data.Symbol.ContainingType; + while (containingType != null) + { + minimalTypeName.Insert(0, "."); + minimalTypeName.Insert(0, containingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + containingType = containingType.ContainingType; + } + + writer.WriteAttributeString("Name", minimalTypeName.ToString()); + break; + + case SymbolKind.Method: + case SymbolKind.Field: + case SymbolKind.Event: + case SymbolKind.Property: + var location = data.Symbol.Locations.First(); + writer.WriteAttributeString("Name", data.Symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + writer.WriteAttributeString("File", location.SourceTree?.FilePath ?? "UNKNOWN"); + writer.WriteAttributeString("Line", (location.GetLineSpan().StartLinePosition.Line + 1).ToString(CultureInfo.InvariantCulture)); + break; + + default: + writer.WriteAttributeString("Name", data.Symbol.ToDisplayString()); + break; + } + } + + void writeMetrics() + { + writer.WriteStartElement("Metrics"); + + WriteMetric("MaintainabilityIndex", data.MaintainabilityIndex.ToString(CultureInfo.InvariantCulture), writer); + WriteMetric("CyclomaticComplexity", data.CyclomaticComplexity.ToString(CultureInfo.InvariantCulture), writer); + WriteMetric("ClassCoupling", data.CoupledNamedTypes.Count.ToString(CultureInfo.InvariantCulture), writer); + if (data.DepthOfInheritance.HasValue) + { + WriteMetric("DepthOfInheritance", data.DepthOfInheritance.Value.ToString(CultureInfo.InvariantCulture), writer); + } + + // For legacy mode, output only ExecutableLinesOfCode + // For non-legacy mode, output both SourceLinesOfCode and ExecutableLinesOfCode +#if LEGACY_CODE_METRICS_MODE + WriteMetric("LinesOfCode", data.ExecutableLines.ToString(CultureInfo.InvariantCulture), writer); +#else + WriteMetric("SourceLines", data.SourceLines.ToString(CultureInfo.InvariantCulture), writer); + WriteMetric("ExecutableLines", data.ExecutableLines.ToString(CultureInfo.InvariantCulture), writer); +#endif + writer.WriteEndElement(); + } + + void writeChildren() + { + if (data.Children.IsEmpty) + { + return; + } + + bool needsEndElement; + switch (data.Symbol.Kind) + { + case SymbolKind.Assembly: + writer.WriteStartElement("Namespaces"); + needsEndElement = true; + break; + + case SymbolKind.Namespace: + writer.WriteStartElement("Types"); + needsEndElement = true; + break; + + case SymbolKind.NamedType: + writer.WriteStartElement("Members"); + needsEndElement = true; + break; + + case SymbolKind.Property: + case SymbolKind.Event: + writer.WriteStartElement("Accessors"); + needsEndElement = true; + break; + + default: + needsEndElement = false; + break; + } + + foreach (var child in data.Children) + { + WriteMetricData(child, writer); + } + + if (needsEndElement) + { + writer.WriteEndElement(); + } + } + } + + private static void WriteMetric(string name, string value, XmlTextWriter writer) + { + writer.WriteStartElement("Metric"); + writer.WriteAttributeString("Name", name); + writer.WriteAttributeString("Value", value); + writer.WriteEndElement(); + } + } +} diff --git a/src/RoslynAnalyzers/Tools/Metrics/Program.cs b/src/RoslynAnalyzers/Tools/Metrics/Program.cs new file mode 100644 index 0000000000000..2c098b802aaa7 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/Metrics/Program.cs @@ -0,0 +1,371 @@ +// 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 System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using Microsoft.Build.Locator; +using Microsoft.CodeAnalysis.CodeMetrics; +using Microsoft.CodeAnalysis.MSBuild; + +namespace Metrics +{ + internal sealed class Program + { + public static int Main(string[] args) + { + using var tokenSource = new CancellationTokenSource(); + Console.CancelKeyPress += delegate + { + tokenSource.Cancel(); + }; + + try + { + return (int)RunAsync(args, tokenSource.Token).GetAwaiter().GetResult(); + } + catch (OperationCanceledException) + { + Console.WriteLine("Operation Cancelled."); + return -1; + } + } + + private static async Task RunAsync(string[] args, CancellationToken cancellationToken) + { + var projectsOrSolutions = new List(); + string? outputFile = null; + bool quiet = false; + + if (args.Length == 0) + { + return usage(); + } + + var errorCode = parseArguments(); + if (errorCode != ErrorCode.None) + { + return errorCode; + } + + cancellationToken.ThrowIfCancellationRequested(); + MSBuildLocator.RegisterDefaults(); + + cancellationToken.ThrowIfCancellationRequested(); + (ImmutableArray<(string, CodeAnalysisMetricData)> metricDatas, ErrorCode exitCode) = await GetMetricDatasAsync(projectsOrSolutions, quiet, cancellationToken).ConfigureAwait(false); + if (exitCode != ErrorCode.None) + { + return exitCode; + } + + cancellationToken.ThrowIfCancellationRequested(); + errorCode = await writeOutputAsync().ConfigureAwait(false); + if (!quiet && errorCode == ErrorCode.None) + { + Console.WriteLine("Completed Successfully."); + } + + return errorCode; + + ErrorCode parseArguments() + { + // Parse arguments + for (int i = 0; i < args.Length; i++) + { + var arg = args[i]; + if (!arg.StartsWith("/", StringComparison.Ordinal) && !arg.StartsWith("-", StringComparison.Ordinal)) + { + return usage(); + } + + arg = arg[1..]; + switch (arg.ToUpperInvariant()) + { + case "Q": + case "QUIET": + quiet = true; + continue; + + case "?": + case "HELP": + return usage(); + + default: + var index = arg.IndexOf(':'); + if (index == -1 || index == arg.Length - 1) + { + return usage(); + } + + var key = arg[..index].ToUpperInvariant(); + var value = arg[(index + 1)..]; + switch (key) + { + case "P": + case "PROJECT": + if (!File.Exists(value)) + { + return fileNotExists(value); + } + + if (!value.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) && + !value.EndsWith(".vbproj", StringComparison.OrdinalIgnoreCase)) + { + return notASupportedProject(value); + } + + projectsOrSolutions.Add(value); + break; + + case "S": + case "SOLUTION": + if (!File.Exists(value)) + { + return fileNotExists(value); + } + + if (!value.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) + { + return notASolution(value); + } + + projectsOrSolutions.Add(value); + break; + + case "O": + case "OUT": + if (value.Length == 0) + { + return invalidOutputFile(value); + } + + outputFile = value; + break; + + default: + return usage(); + } + + break; + } + } + + if (projectsOrSolutions.Count == 0) + { + return requiresProjectOrSolution(); + } + + return ErrorCode.None; + } + + static ErrorCode usage() + { + Console.WriteLine(@" +Usage: Metrics.exe + +Help for command-line arguments: + +/project: [Short form: /p:] +Project(s) to analyze. + +/solution: [Short form: /s:] +Solution(s) to analyze. + +/out: [Short form: /o:] +Metrics results XML output file. + +/quiet [Short form: /q] +Silence all console output other than error reporting. + +/help [Short form: /?] +Display this help message."); + return ErrorCode.Usage; + } + + static ErrorCode fileNotExists(string path) + { + Console.WriteLine($"Error: File '{path}' does not exist."); + return ErrorCode.FileNotExists; + } + + static ErrorCode requiresProjectOrSolution() + { + Console.WriteLine($"Error: No project or solution provided."); + return ErrorCode.RequiresProjectOrSolution; + } + + static ErrorCode notASolution(string path) + { + Console.WriteLine($"Error: File '{path}' is not a solution file."); + return ErrorCode.NotASolution; + } + + static ErrorCode notASupportedProject(string path) + { + Console.WriteLine($"Error: File '{path}' is not a C# or VB project file."); + return ErrorCode.NotASupportedProject; + } + + static ErrorCode invalidOutputFile(string path) + { + Console.WriteLine($"Error: File '{path}' is not a valid output file."); + return ErrorCode.InvalidOutputFile; + } + + async Task writeOutputAsync() + { + XmlTextWriter? metricFile = null; + try + { + // Create the writer + if (outputFile != null) + { + if (!quiet) + { + Console.WriteLine($"Writing output to '{outputFile}'..."); + } + + metricFile = new XmlTextWriter(outputFile, Encoding.UTF8); + } + else + { + metricFile = new XmlTextWriter(Console.OpenStandardOutput(), Console.OutputEncoding); + } + + MetricsOutputWriter.WriteMetricFile(metricDatas, metricFile); + if (outputFile == null) + { + await metricFile.WriteStringAsync(Environment.NewLine + Environment.NewLine).ConfigureAwait(false); + } + + return ErrorCode.None; + } +#pragma warning disable CA1031 // Do not catch general exception types - gracefully catch exceptions and log them to the console and output file. + catch (Exception ex) + { + Console.WriteLine(ex.Message); + return ErrorCode.WriteException; + } +#pragma warning restore CA1031 // Do not catch general exception types + finally + { + metricFile?.Close(); + } + } + } + + private static async Task<(ImmutableArray<(string, CodeAnalysisMetricData)>, ErrorCode)> GetMetricDatasAsync(List projectsOrSolutions, bool quiet, CancellationToken cancellationToken) + { + var builder = ImmutableArray.CreateBuilder<(string, CodeAnalysisMetricData)>(); + + try + { + using (var workspace = MSBuildWorkspace.Create()) + { + foreach (var projectOrSolution in projectsOrSolutions) + { + if (projectOrSolution.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) + { + await computeSolutionMetricDataAsync(workspace, projectOrSolution, cancellationToken).ConfigureAwait(false); + } + else + { + Debug.Assert(projectOrSolution.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase) || + projectOrSolution.EndsWith(".vbproj", StringComparison.OrdinalIgnoreCase)); + await computeProjectMetricDataAsync(workspace, projectOrSolution, cancellationToken).ConfigureAwait(false); + } + } + } + + return (builder.ToImmutable(), ErrorCode.None); + } +#pragma warning disable CA1031 // Do not catch general exception types - gracefully catch exceptions and log them to the console and output file. + catch (Exception ex) + { + Console.Write(ex.Message); + return (ImmutableArray<(string, CodeAnalysisMetricData)>.Empty, ErrorCode.ComputeException); + } +#pragma warning restore CA1031 // Do not catch general exception types + + async Task computeProjectMetricDataAsync(MSBuildWorkspace workspace, string projectFile, CancellationToken cancellation) + { + cancellation.ThrowIfCancellationRequested(); + if (!quiet) + { + Console.WriteLine($"Loading {Path.GetFileName(projectFile)}..."); + } + + var project = await workspace.OpenProjectAsync(projectFile, cancellationToken: CancellationToken.None).ConfigureAwait(false); + + if (!quiet) + { + Console.WriteLine($"Computing code metrics for {Path.GetFileName(projectFile)}..."); + } + + if (!project.SupportsCompilation) + { + throw new NotSupportedException("Project must support compilation."); + } + + cancellation.ThrowIfCancellationRequested(); + var compilation = await project.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); + var metricData = await CodeAnalysisMetricData.ComputeAsync(compilation!.Assembly, new CodeMetricsAnalysisContext(compilation, CancellationToken.None)).ConfigureAwait(false); + builder.Add((projectFile, metricData)); + } + + async Task computeSolutionMetricDataAsync(MSBuildWorkspace workspace, string solutionFile, CancellationToken cancellation) + { + cancellation.ThrowIfCancellationRequested(); + if (!quiet) + { + Console.WriteLine($"Loading {Path.GetFileName(solutionFile)}..."); + } + + var solution = await workspace.OpenSolutionAsync(solutionFile, cancellationToken: CancellationToken.None).ConfigureAwait(false); + + if (!quiet) + { + Console.WriteLine($"Computing code metrics for {Path.GetFileName(solutionFile)}..."); + } + + foreach (var project in solution.Projects) + { + if (!quiet) + { + Console.WriteLine($" Computing code metrics for {Path.GetFileName(project.FilePath)}..."); + } + + if (!project.SupportsCompilation) + { + throw new NotSupportedException("Project must support compilation."); + } + + cancellation.ThrowIfCancellationRequested(); + var compilation = await project.GetCompilationAsync(CancellationToken.None).ConfigureAwait(false); + var metricData = await CodeAnalysisMetricData.ComputeAsync(compilation!.Assembly, new CodeMetricsAnalysisContext(compilation, CancellationToken.None)).ConfigureAwait(false); + builder.Add((project.FilePath!, metricData)); + } + } + } + + private enum ErrorCode + { + None, + Usage, + FileNotExists, + RequiresProjectOrSolution, + NotASolution, + NotASupportedProject, + InvalidOutputFile, + ComputeException, + WriteException + } + } +} diff --git a/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/Program.cs b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/Program.cs new file mode 100644 index 0000000000000..7f215c136ac4a --- /dev/null +++ b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/Program.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using Microsoft.CodeAnalysis.RulesetToEditorconfig; + +if (args.Length is < 1 or > 2) +{ + ShowUsage(); + return 1; +} + +var rulesetFilePath = args[0]; +var editorconfigFilePath = args.Length == 2 ? + args[1] : + Path.Combine(Environment.CurrentDirectory, ".editorconfig"); +try +{ + Converter.GenerateEditorconfig(rulesetFilePath, editorconfigFilePath); +} +catch (IOException ex) +{ + Console.WriteLine(ex.Message); + return 2; +} + +Console.WriteLine($"Successfully converted to '{editorconfigFilePath}'"); +return 0; + +static void ShowUsage() +{ + Console.WriteLine("Usage: RulesetToEditorconfigConverter.exe <%ruleset_file%> [<%path_to_editorconfig%>]"); + return; +} diff --git a/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/RulesetToEditorconfigConverter.csproj b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/RulesetToEditorconfigConverter.csproj new file mode 100644 index 0000000000000..dfd0bb223b6cf --- /dev/null +++ b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Source/RulesetToEditorconfigConverter.csproj @@ -0,0 +1,23 @@ + + + Exe + net472 + false + true + true + $(MicrosoftCodeAnalysisVersionForExecution) + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverter.UnitTests.csproj b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverter.UnitTests.csproj new file mode 100644 index 0000000000000..7c23401c3f2d9 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverter.UnitTests.csproj @@ -0,0 +1,12 @@ + + + + $(NetRoslyn) + true + false + true + + + + + diff --git a/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverterTests.cs b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverterTests.cs new file mode 100644 index 0000000000000..591963e143b59 --- /dev/null +++ b/src/RoslynAnalyzers/Tools/RulesetToEditorconfigConverter/Tests/RulesetToEditorconfigConverterTests.cs @@ -0,0 +1,449 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using Xunit; + +namespace Microsoft.CodeAnalysis.RulesetToEditorconfig.UnitTests +{ + public class RulesetToEditorconfigConverterTests + { + private const string PrimaryRulesetName = "MyRules.ruleset"; + private const string IncludedRulesetName = "IncludedRules.ruleset"; + + private static void Verify(string rulesetText, string expectedEditorconfigText, string? includedRulesetText = null) + { + var directory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); + + try + { + var ruleset = Path.Combine(directory.FullName, PrimaryRulesetName); + File.WriteAllText(ruleset, rulesetText); + + if (includedRulesetText != null) + { + Assert.Contains(" + + + + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +dotnet_diagnostic.CA1000.severity = error + +dotnet_diagnostic.CA1001.severity = warning + +dotnet_diagnostic.CA1002.severity = suggestion + +dotnet_diagnostic.CA1003.severity = silent + +dotnet_diagnostic.CA1004.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void RuleSeveritiesAcrossRulesGroupsPreserved() + { + var rulesetText = @" + + + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +dotnet_diagnostic.CA1000.severity = error + +dotnet_diagnostic.CA1001.severity = warning +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void RuleSeverityOverrideAfterIncludePreserved() + { + var rulesetText = $@" + + + + + + + +"; + + var includedRulesetText = @" + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +dotnet_diagnostic.CA1000.severity = warning +"; + + Verify(rulesetText, editorconfigText, includedRulesetText); + } + + [Fact] + public void IncludeAllPreserved() + { + var rulesetText = @" + + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Default severity for analyzer diagnostics - Requires **VS2019 16.5** or later +dotnet_analyzer_diagnostic.severity = warning + +dotnet_diagnostic.CA1000.severity = error +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void CommentBeforeRulePreserved() + { + var rulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Comment before rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void MultilineCommentBeforeRulePreserved() + { + var rulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Multiline +# comment +# before +# rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void MultipleCommentsBeforeRulePreserved() + { + var rulesetText = @" + + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Comment1 before rule +# Comment2 before rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void CommentAfterRulePreserved() + { + var rulesetText = @" + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Comment after rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void CommentsBeforeAndAfterRulePreserved() + { + var rulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Comment before rule +# Comment after rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText); + } + + [Fact] + public void CommentsFromIncludedRulesetPreserved() + { + var rulesetText = $@" + + + +"; + var includedRulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Comment before rule +# Comment after rule +dotnet_diagnostic.CA1000.severity = none +"; + + Verify(rulesetText, editorconfigText, includedRulesetText); + } + + [Fact] + public void CommentsFromPrimaryAndIncludedRulesetPreserved() + { + var rulesetText = $@" + + + + + + + + +"; + var includedRulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Before comment from primary ruleset +# After comment from primary ruleset +dotnet_diagnostic.CA1000.severity = warning + +# Before comment from included ruleset +# After comment from included ruleset +dotnet_diagnostic.CA10001.severity = error +"; + + Verify(rulesetText, editorconfigText, includedRulesetText); + } + + [Fact] + public void CommentsFromOverrideAfterIncludedRulesetPreserved() + { + var rulesetText = $@" + + + + + + + + +"; + var includedRulesetText = @" + + + + + + +"; + var editorconfigText = @" +# NOTE: Requires **VS2019 16.3** or later + +# ConfigurationFileName +# Description: Configuration file description + +# Code files +[*.{cs,vb}] + + +# Before comment from primary ruleset +# After comment from primary ruleset +dotnet_diagnostic.CA1000.severity = warning +"; + + Verify(rulesetText, editorconfigText, includedRulesetText); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/AdditionalFileProviderTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/AdditionalFileProviderTests.cs new file mode 100644 index 0000000000000..8589aeaf4dda4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/AdditionalFileProviderTests.cs @@ -0,0 +1,127 @@ +// 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 System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Analyzers.Utilities.UnitTests +{ +#pragma warning disable CS0419 // Ambiguous reference in cref attribute + /// + /// Tests for . + /// +#pragma warning restore CS0419 // Ambiguous reference in cref attribute + public sealed class AdditionalFileProviderTests + { + [Theory] + [InlineData(new object[] { new string[] { } })] + [InlineData(new object[] { new string[] { "b.txt" } })] + [InlineData(new object[] { new string[] { "a.bat" } })] + public void DesiredFileMissing_GetFile_ReturnsNull(string[] fileNames) + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles(fileNames)); + + var file = fileProvider.GetFile("a.txt"); + + Assert.Null(file); + } + + [Theory] + [InlineData("a.txt")] + [InlineData("a.txt", "b.txt")] + [InlineData("b.txt", "a.txt")] + [InlineData("a.bat", "a.txt")] + public void DesiredFilePresent_GetFile_ReturnsFile(params string[] fileNames) + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles(fileNames)); + + var file = fileProvider.GetFile("a.txt"); + + Assert.NotNull(file); + Assert.Equal("a.txt", file.Path); + } + + [Theory] + [InlineData("a")] + [InlineData("a.+")] + [InlineData("a.tx")] + public void DesiredFilePresent_GetFileWithoutExactName_ReturnsNull(string fileName) + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles("a.txt")); + + var file = fileProvider.GetFile(fileName); + + Assert.Null(file); + } + + [Fact] + public void DesiredFilePresentMoreThanOnce_GetFile_ReturnsFirstFile() + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles(("a.txt", "1"), ("b.txt", "2"), ("a.txt", "3"))); + + var file = fileProvider.GetFile("a.txt"); + Assert.NotNull(file); + Assert.Equal("a.txt", file.Path); + + var text = file.GetText(); + Assert.NotNull(text); + Assert.Equal("1", text.ToString()); + } + + [Theory] + [InlineData(new string[] { }, ".")] + [InlineData(new[] { "a.txt" }, "c")] + public void MatchingFilesMissing_GetMatchingFiles_ReturnsEmptyEnumerable(IEnumerable fileNames, string pattern) + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles(fileNames.ToArray())); + + var files = fileProvider.GetMatchingFiles(pattern); + + Assert.Empty(files); + } + + [Theory] + [InlineData(new[] { "a.txt" }, "a", new[] { "a.txt" })] + [InlineData(new[] { "a.txt", "b.txt" }, @"\w\.", new[] { "a.txt", "b.txt" })] + [InlineData(new[] { "a.txt", "b.txt", "c.bat" }, @"\.txt", new[] { "a.txt", "b.txt" })] + public void MatchingFilesPresent_GetMatchingFiles_ReturnsMatchingFiles(IEnumerable fileNames, string pattern, IEnumerable expectedFileNames) + { + var fileProvider = new AdditionalFileProvider(CreateAdditionalFiles(fileNames.ToArray())); + + var files = fileProvider.GetMatchingFiles(pattern); + + Assert.Equal(expectedFileNames, files.Select(x => x.Path)); + } + + private static ImmutableArray CreateAdditionalFiles(params (string FileName, string Content)[] fileNameAndContentGroups) + => ImmutableArray.Create(fileNameAndContentGroups.Select(x => CreateAdditionalFile(x.FileName, x.Content)).ToArray()); + + private static ImmutableArray CreateAdditionalFiles(params string[] fileNames) + => ImmutableArray.Create(fileNames.Select(x => CreateAdditionalFile(x)).ToArray()); + + private static AdditionalText CreateAdditionalFile(string fileName, string content = "") + => new FakeAdditionalText(fileName, content); + + private sealed class FakeAdditionalText : AdditionalText + { + private readonly SourceText _text; + + public FakeAdditionalText(string path, string text = "") + { + Path = path; + _text = SourceText.From(text); + } + + public override string Path { get; } + + public override SourceText GetText(CancellationToken cancellationToken = default) => _text; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/Analyzer.Utilities.UnitTests.csproj b/src/RoslynAnalyzers/Utilities.UnitTests/Analyzer.Utilities.UnitTests.csproj new file mode 100644 index 0000000000000..aabcc009ec859 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/Analyzer.Utilities.UnitTests.csproj @@ -0,0 +1,24 @@ + + + + $(NetRoslyn) + true + true + true + $(MicrosoftCodeAnalysisVersionForToolsAndUtilities) + + + + + + + + + + + + + + + + diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/IEnumerableExensionsTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/IEnumerableExensionsTests.cs new file mode 100644 index 0000000000000..4ad6a5bb0ecba --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/IEnumerableExensionsTests.cs @@ -0,0 +1,154 @@ +// 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; +using System.Collections.Generic; +using Xunit; + +namespace Analyzer.Utilities.Extensions +{ + public class IEnumerableExensionsTests + { + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, true)] + [InlineData(3, false)] + [Theory] + public void IEnumerableHasExactly2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasExactly(CreateIEnumerable(count), 2)); + } + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(3, true)] + [Theory] + public void IEnumerableHasMoreThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasMoreThan(CreateIEnumerable(count), 2)); + } + + [InlineData(0, true)] + [InlineData(1, true)] + [InlineData(2, false)] + [InlineData(3, false)] + [Theory] + public void IEnumerableHasFewerThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasFewerThan(CreateIEnumerable(count), 2)); + } + + private static IEnumerable CreateIEnumerable(int count) + { + if (count < 0) + { + count = 0; + } + + for (var i = count; i > 0; i--) + { + yield return 0; + } + } + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, true)] + [InlineData(3, false)] + [Theory] + public void ICollectionHasExactly2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasExactly(new Collection(count), 2)); + } + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(3, true)] + [Theory] + public void ICollectionHasMoreThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasMoreThan(new Collection(count), 2)); + } + + [InlineData(0, true)] + [InlineData(1, true)] + [InlineData(2, false)] + [InlineData(3, false)] + [Theory] + public void ICollectionHasFewerThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasFewerThan(new Collection(count), 2)); + } + +#pragma warning disable CA1010 // Collections should implement generic interface + private sealed class Collection : ICollection, IEnumerable + { + public Collection(int count) => this.Count = count > 0 ? count : 0; + public int Count { get; } + public object SyncRoot => throw new NotImplementedException(); + public bool IsSynchronized => throw new NotImplementedException(); + public void CopyTo(Array array, int index) => throw new NotImplementedException(); + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + } +#pragma warning restore CA1010 // Collections should implement generic interface + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, true)] + [InlineData(3, false)] + [Theory] + public void IIntCollectionHasExactly2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasExactly(new IntCollection(count), 2)); + } + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(3, true)] + [Theory] + public void IIntCollectionHasMoreThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasMoreThan(new IntCollection(count), 2)); + } + + [InlineData(0, true)] + [InlineData(1, true)] + [InlineData(2, false)] + [InlineData(3, false)] + [Theory] + public void IIntCollectionHasFewerThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, IEnumerableExtensions.HasFewerThan(new IntCollection(count), 2)); + } + + private sealed class IntCollection : ICollection + { + public IntCollection(int count) => this.Count = count > 0 ? count : 0; + public int Count { get; } + public bool IsReadOnly { get; } + public void Add(int item) => throw new NotImplementedException(); + public void Clear() => throw new NotImplementedException(); + public bool Contains(int item) => throw new NotImplementedException(); + public void CopyTo(int[] array, int arrayIndex) => throw new NotImplementedException(); + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + public bool Remove(int item) => throw new NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); + } + + [Fact] + public void Concat_WhenCollectionIsNull_ThrowsDirectly() + { + Assert.Throws(() => + { + IEnumerable collection = null!; + collection.Concat(1); + }); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/ImmutableArrayExensionsTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/ImmutableArrayExensionsTests.cs new file mode 100644 index 0000000000000..88d60c9221809 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/Extensions/ImmutableArrayExensionsTests.cs @@ -0,0 +1,55 @@ +// 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.Linq; +using Xunit; + +namespace Analyzer.Utilities.Extensions +{ + public class ImmutableArrayExensionsTests + { + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, true)] + [InlineData(3, false)] + [Theory] + public void HasExactly2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, global::System.Collections.Immutable.ImmutableArrayExtensions.HasExactly(CreateImmutableArray(count), 2)); + } + + [InlineData(0, false)] + [InlineData(1, false)] + [InlineData(2, false)] + [InlineData(3, true)] + [Theory] + public void HasMoreThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, global::System.Collections.Immutable.ImmutableArrayExtensions.HasMoreThan(CreateImmutableArray(count), 2)); + } + + [InlineData(0, true)] + [InlineData(1, true)] + [InlineData(2, false)] + [InlineData(3, false)] + [Theory] + public void HasFewerThan2_ReturnsTheCorrectValue(int count, bool result) + { + Assert.Equal(result, global::System.Collections.Immutable.ImmutableArrayExtensions.HasFewerThan(CreateImmutableArray(count), 2)); + } + + private static ImmutableArray CreateImmutableArray(int count) + { + if (count > 0) + { + var builder = ImmutableArray.CreateBuilder(count); + builder.AddRange(Enumerable.Range(0, count)); + return builder.ToImmutable(); + } + + return ImmutableArray.Empty; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisTests.cs new file mode 100644 index 0000000000000..54e6ef8601dae --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisTests.cs @@ -0,0 +1,1437 @@ +// 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 System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Test.Utilities; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + [Trait(Traits.DataflowAnalysis, Traits.Dataflow.PropertySetAnalysis)] + public class PropertySetAnalysisTests + { + /// + /// Just a container for parameters necessary for PropertySetAnalysis for unit tests below. + /// + private sealed class PropertySetAnalysisParameters + { + public PropertySetAnalysisParameters(string typeToTrack, ConstructorMapper constructorMapper, PropertyMapperCollection propertyMapperCollection, HazardousUsageEvaluatorCollection hazardousUsageEvaluatorCollection) + { + TypesToTrack = new string[] { typeToTrack }.ToImmutableHashSet() ?? throw new ArgumentNullException(nameof(typeToTrack)); + ConstructorMapper = constructorMapper ?? throw new ArgumentNullException(nameof(constructorMapper)); + PropertyMapperCollection = propertyMapperCollection ?? throw new ArgumentNullException(nameof(propertyMapperCollection)); + HazardousUsageEvaluatorCollection = hazardousUsageEvaluatorCollection ?? throw new ArgumentNullException(nameof(hazardousUsageEvaluatorCollection)); + } + + public ImmutableHashSet TypesToTrack { get; } + public ConstructorMapper ConstructorMapper { get; } + public PropertyMapperCollection PropertyMapperCollection { get; } + public HazardousUsageEvaluatorCollection HazardousUsageEvaluatorCollection { get; } + } + + /// + /// Verification helper. + /// + /// C# source code, with /*<bind>*/ and /*</bind>*/ around the method block to be analyzed. + /// PropertySetAnalysis parameters. + /// Expected hazardous usages (MethodName = null => return statement). + private void VerifyCSharp( + string source, + PropertySetAnalysisParameters propertySetAnalysisParameters, + params (int Line, int Column, string? Method, HazardousUsageEvaluationResult Result)[] expectedResults) + { + expectedResults ??= Array.Empty<(int Line, int Column, string? MethodName, HazardousUsageEvaluationResult Result)>(); + + Project project = CreateProject(new string[] { source, TestTypeToTrackSource }); + Compilation? compilation = project.GetCompilationAsync().Result; + Assert.NotNull(compilation); + CompilationUtils.ValidateNoCompileErrors(compilation.GetDiagnostics()); + (IOperation? operation, SemanticModel? model, SyntaxNode? syntaxNode) = GetOperationAndSyntaxForTest((CSharpCompilation)compilation); + Assert.True( + operation != null, + $"Could not find code block to analyze. Does your test code have {StartString} and {EndString} around the braces of block to analyze?"); + Assert.NotNull(model); + Assert.NotNull(syntaxNode?.Parent); + ISymbol? symbol = model.GetDeclaredSymbol(syntaxNode.Parent) ?? model.GetSymbolInfo(syntaxNode.Parent).Symbol; + Assert.NotNull(symbol); + var success = operation.TryGetEnclosingControlFlowGraph(out var cfg); + Debug.Assert(success); + Debug.Assert(cfg != null); + + DiagnosticDescriptor dummy = new DiagnosticDescriptor("fakeId", null!, null!, "fakeagory", DiagnosticSeverity.Info, true); + PropertySetAnalysisResult? result = + PropertySetAnalysis.GetOrComputeResult( + cfg, + compilation, + symbol, + new AnalyzerOptions(ImmutableArray.Empty), + propertySetAnalysisParameters.TypesToTrack, + propertySetAnalysisParameters.ConstructorMapper, + propertySetAnalysisParameters.PropertyMapperCollection, + propertySetAnalysisParameters.HazardousUsageEvaluatorCollection, + InterproceduralAnalysisConfiguration.Create( + new AnalyzerOptions(ImmutableArray.Empty), + dummy, + cfg, + compilation, + InterproceduralAnalysisKind.ContextSensitive)); + Assert.NotNull(result); + ImmutableDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> actual = + result.HazardousUsages; + try + { + Assert.Equal(expectedResults.Length, actual.Count); + foreach ((int Line, int Column, string? Method, HazardousUsageEvaluationResult Result) in expectedResults) + { + HazardousUsageEvaluationResult? actualResult = null; + foreach (KeyValuePair<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> kvp in actual) + { + FileLinePositionSpan span = kvp.Key.Location.GetLineSpan(); + if (span.Path != CSharpDefaultFilePath) + { + // Only looking in the first file, so that expectedResults doesn't have to specify a filename. + continue; + } + + if (span.StartLinePosition.Line + 1 == Line + && span.StartLinePosition.Character + 1 == Column + && kvp.Key.Method?.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat) == Method) + { + actualResult = kvp.Value; + break; + } + } + + Assert.True( + actualResult.HasValue, + $"Could not find expected result Line {Line} Column {Column} {MethodOrReturnString(Method)} Result {Result}"); + Assert.True( + actualResult == Result, + $"Expected {Result}, Actual {actualResult}, for Line {Line} Column {Column} {MethodOrReturnString(Method)}"); + } + } + catch (XunitException) + { + TestOutput.WriteLine("PropertySetAnalysis actual results:"); + TestOutput.WriteLine("============================"); + if (actual == null) + { + throw; + } + + foreach (KeyValuePair<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> kvp in actual) + { + LinePosition linePosition = kvp.Key.Location.GetLineSpan().StartLinePosition; + int lineNumber = linePosition.Line + 1; + int columnNumber = linePosition.Character + 1; + TestOutput.WriteLine( + $"Line {lineNumber}, Column {columnNumber}, {MethodSymbolOrReturnString(kvp.Key.Method)}: {kvp.Value}"); + } + + TestOutput.WriteLine("============================"); + + throw; + } + + static string MethodSymbolOrReturnString(IMethodSymbol? methodSymbol) + { + return methodSymbol != null ? $"Method {methodSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)}" : "Return/Initialization"; + } + + static string MethodOrReturnString(string? method) + { + return method != null ? $"Method {method}" : "Return/Initialization"; + } + } + + private readonly string TestTypeToTrackSource = @" +public class TestTypeToTrack +{ + public TestEnum AnEnum { get; set; } + public object AnObject { get; set; } + public string AString { get; set; } + + public void Method() + { + } +} + +public class TestTypeToTrackWithConstructor : TestTypeToTrack +{ + private TestTypeToTrackWithConstructor() + { + } + + public TestTypeToTrackWithConstructor(TestEnum enu, object obj, string str) + { + this.AnEnum = enu; + this.AnObject = obj; + this.AString = str; + } +} + +public enum TestEnum +{ + Value0, + Value1, + Value2, +} + +public class OtherClass +{ + public void OtherMethod(TestTypeToTrack t) + { + } + + public void OtherMethod(string s, TestTypeToTrack t) + { + } + + public static void StaticMethod(TestTypeToTrack staticMethodParameter) + { + } +}"; + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrack.AString property is not null + /// when calling its Method() method, OtherClass.OtherMethod() method, or OtherClass.StaticMethod() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrack_HazardousIfStringIsNonNull = + new( + "TestTypeToTrack", + new ConstructorMapper( // Only one constructor, which leaves its AString property as null (not hazardous). + ImmutableArray.Create( + PropertySetAbstractValueKind.Unflagged)), + new PropertyMapperCollection( + new PropertyMapper( // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + "AString", + (PointsToAbstractValue pointsToAbstractValue) => + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrack.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }), + new HazardousUsageEvaluator( // When OtherClass.OtherMethod() is invoked, evaluate its "TypeToTrack t" argument. + "OtherClass", + "OtherMethod", + "t", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }), + new HazardousUsageEvaluator( // When OtherClass.StaticMethod() is invoked, evaluate its "TypeToTrack staticMethodParameter" argument. + "OtherClass", + "StaticMethod", + "staticMethodParameter", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNull_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull, + (8, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNull_StringEmpty_Flagged() + { + VerifyCSharp(@" +using System; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = String.Empty; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull, + (10, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNull_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = null; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNull_OtherMethod_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + OtherClass o = new OtherClass(); + o.OtherMethod(""this string parameter is ignored"", t); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull, + (9, 9, "void OtherClass.OtherMethod(string s, TestTypeToTrack t)", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNull_StaticMethod_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + OtherClass.StaticMethod(t); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull, + (8, 9, "void OtherClass.StaticMethod(TestTypeToTrack staticMethodParameter)", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNull_OtherClassBothMethods_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + OtherClass o = new OtherClass(); + o.OtherMethod(""this string parameter is ignored"", t); + OtherClass.StaticMethod(t); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNull, + (9, 9, "void OtherClass.OtherMethod(string s, TestTypeToTrack t)", HazardousUsageEvaluationResult.Flagged), + (10, 9, "void OtherClass.StaticMethod(TestTypeToTrack staticMethodParameter)", HazardousUsageEvaluationResult.Flagged)); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrackWithConstructor.AString property + /// is not null when calling its Method() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrackWithConstructor_HazardousIfStringIsNonNull = + new( + "TestTypeToTrackWithConstructor", + new ConstructorMapper( + (IMethodSymbol method, IReadOnlyList argumentPointsToAbstractValues) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + PropertySetAbstractValueKind kind = PropertySetAbstractValueKind.Unknown; + if (method.Parameters.Length >= 2) + { + // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + kind = argumentPointsToAbstractValues[2].NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + } + + return PropertySetAbstractValue.GetInstance(kind); + }), + new PropertyMapperCollection( + new PropertyMapper( // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + "AString", + (PointsToAbstractValue pointsToAbstractValue) => + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfStringIsNull_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, ""A non-null string""); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfStringIsNonNull, + (7, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfStringIsNull_StringEmpty_Flagged() + { + VerifyCSharp(@" +using System; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, String.Empty); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfStringIsNonNull, + (9, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfStringIsNull_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, null); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfStringIsNonNull); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfStringIsNull_PropertyAssigned_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, null); + t.AString = """"; + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfStringIsNonNull, + (8, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrack.AnEnum property is Value0 when + /// calling its Method() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrack_HazardousIfEnumIsValue0 = + new( + "TestTypeToTrack", + new ConstructorMapper( // Only one constructor, which leaves its AnEnum property as Value0 (hazardous). + ImmutableArray.Create( + PropertySetAbstractValueKind.Flagged)), + new PropertyMapperCollection( + new PropertyMapper( + "AnEnum", + (ValueContentAbstractValue valueContentAbstractValue) => + { + return PropertySetCallbacks.EvaluateLiteralValues(valueContentAbstractValue, v => v is not null && v.Equals(0)); + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrack.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrack_HazardousIfEnumIsValue0_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AnEnum = TestEnum.Value0; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfEnumIsValue0, + (8, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfEnumIsValue0_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AnEnum = TestEnum.Value2; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfEnumIsValue0); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrackWithConstructor.AnEnum property + /// is Value0 when calling its Method() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrackWithConstructor_HazardousIfEnumIsValue0 = + new( + "TestTypeToTrackWithConstructor", + new ConstructorMapper( + (IMethodSymbol method, IReadOnlyList argumentValueContentAbstractValues, IReadOnlyList argumentPointsToAbstractValues) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + PropertySetAbstractValueKind kind = PropertySetCallbacks.EvaluateLiteralValues( + argumentValueContentAbstractValues[0], + v => v is not null && v.Equals(0)); + return PropertySetAbstractValue.GetInstance(kind); + }), + new PropertyMapperCollection( + new PropertyMapper( + "AnEnum", + (ValueContentAbstractValue valueContentAbstractValue) => + { + return PropertySetCallbacks.EvaluateLiteralValues(valueContentAbstractValue, v => v is not null && v.Equals(0)); + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrack.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfEnumIsValue0_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(TestEnum.Value2, null, null); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfEnumIsValue0); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfEnumIsValue0_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(TestEnum.Value0, null, null); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfEnumIsValue0, + (7, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when both TestTypeToTrack.AString starts with 'T' and + /// TestTypeToTrack.AnEnum is Value2 when calling its Method() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrack_HazardousIfStringStartsWithTAndValue2 = + new( + "TestTypeToTrack", + new ConstructorMapper( + ImmutableArray.Create( // Order is the same as the PropertyMappers below. + PropertySetAbstractValueKind.Unflagged, // AString + PropertySetAbstractValueKind.Unflagged)), // AnEnum + new PropertyMapperCollection( + new PropertyMapper( + "AString", + (ValueContentAbstractValue valueContentAbstractValue) => + { + return PropertySetCallbacks.EvaluateLiteralValues( + valueContentAbstractValue, + v => (v as string)?.StartsWith("T", StringComparison.Ordinal) == true); + }), + new PropertyMapper( + "AnEnum", + (ValueContentAbstractValue valueContentAbstractValue) => + { + return PropertySetCallbacks.EvaluateLiteralValues(valueContentAbstractValue, v => v is not null && v.Equals(2)); + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // PropertyAbstractValueKinds are in the same order as the PropertyMappers that were used to initialize the PropertyMapperCollection. + PropertySetAbstractValueKind aStringKind = abstractValue[0]; + PropertySetAbstractValueKind anEnumKind = abstractValue[1]; + if (aStringKind == PropertySetAbstractValueKind.Flagged && anEnumKind == PropertySetAbstractValueKind.Flagged) + { + return HazardousUsageEvaluationResult.Flagged; + } + else if ((aStringKind == PropertySetAbstractValueKind.Flagged || aStringKind == PropertySetAbstractValueKind.MaybeFlagged) + && (anEnumKind == PropertySetAbstractValueKind.Flagged || anEnumKind == PropertySetAbstractValueKind.MaybeFlagged)) + { + return HazardousUsageEvaluationResult.MaybeFlagged; + } + else + { + return HazardousUsageEvaluationResult.Unflagged; + } + }))); + + [Fact] + public void TestTypeToTrack_HazardousIfStringStartsWithTAndValue2_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""The beginning of knowledge is the discovery of something we do not understand.""; + t.AnEnum = TestEnum.Value2; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringStartsWithTAndValue2, + (9, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringStartsWithTAndValue2_BothMaybe_MaybeFlagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + Random r = new Random(); + t.AString = ""T""; + t.AnEnum = TestEnum.Value2; + if (r.Next(6) == 4) + { + t.AString = ""A different string.""; + } + + if (r.Next(6) == 4) + { + t.AnEnum = TestEnum.Value1; + } + + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringStartsWithTAndValue2, + (21, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.MaybeFlagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringStartsWithTAndValue2_FirstMaybe_MaybeFlagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + Random r = new Random(); + t.AString = ""T""; + t.AnEnum = TestEnum.Value2; + if (r.Next(6) == 4) + { + t.AString = ""A different string.""; + } + + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringStartsWithTAndValue2, + (16, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.MaybeFlagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringStartsWithTAndValue2_SecondMaybe_MaybeFlagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + Random r = new Random(); + t.AString = ""T""; + t.AnEnum = TestEnum.Value2; + if (r.Next(6) == 4) + { + t.AnEnum = TestEnum.Value1; + } + + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringStartsWithTAndValue2, + (16, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.MaybeFlagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringStartsWithTAndValue2_FirstFlagged_Unflagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + Random r = new Random(); + t.AString = ""T""; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringStartsWithTAndValue2); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when both TestTypeToTrack.AnObject is a BitArray. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray = + new( + "TestTypeToTrackWithConstructor", + new ConstructorMapper( + (IMethodSymbol constructorMethodSymbol, IReadOnlyList argumentPointsToAbstractValues) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // Better to compare LocationTypeOpt to INamedTypeSymbol, but for this demonstration, just using MetadataName. + PropertySetAbstractValueKind kind; + if (argumentPointsToAbstractValues[1].Locations.Any(l => + l.LocationType != null + && l.LocationType.MetadataName == "BitArray")) + { + kind = PropertySetAbstractValueKind.Flagged; + } + else + { + kind = PropertySetAbstractValueKind.Unflagged; + } + + return PropertySetAbstractValue.GetInstance(kind); + }), + new PropertyMapperCollection( + new PropertyMapper( + "AnObject", + (PointsToAbstractValue pointsToAbstractValue) => + { + // Better to compare LocationTypeOpt to INamedTypeSymbol, but for this demonstration, just using MetadataName. + PropertySetAbstractValueKind kind; + if (pointsToAbstractValue.Locations.Any(l => + l.LocationType != null + && l.LocationType.MetadataName == "BitArray")) + { + kind = PropertySetAbstractValueKind.Flagged; + } + else + { + kind = PropertySetAbstractValueKind.Unflagged; + } + + return kind; + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrack.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray_Constructor_Flagged() + { + VerifyCSharp(@" +using System; +using System.Collections; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), new BitArray(4), ""string""); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray, + (10, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray_Constructor_TwoPaths_Flagged() + { + VerifyCSharp(@" +using System; +using System.Collections; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t; + if (new Random().Next(6) == 4) + t = new TestTypeToTrackWithConstructor(default(TestEnum), new BitArray(6), ""string""); + else + t = new TestTypeToTrackWithConstructor(default(TestEnum), ""object string"", ""string""); + t.Method(); // PropertySetAnalysis is aggressive--at least one previous code path being Flagged means it's Flagged at this point. + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray, + (14, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray_Constructor_NotFlagged() + { + VerifyCSharp(@" +using System; +using System.Collections; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, ""string""); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfObjectIsBitArray); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when both TestTypeToTrackWithConstructor.AString starts + /// with 'A'. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrackWithConstructor_HazardousIfAStringStartsWithA = + new( + "TestTypeToTrackWithConstructor", + new ConstructorMapper( + (IMethodSymbol constructorMethodSymbol, + IReadOnlyList argumentValueContentAbstractValues, + IReadOnlyList argumentPointsToAbstractValues) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + PropertySetAbstractValueKind kind = PropertySetCallbacks.EvaluateLiteralValues( + argumentValueContentAbstractValues[2], + v => (v as string)?.StartsWith("A", StringComparison.Ordinal) == true); + return PropertySetAbstractValue.GetInstance(kind); + }), + new PropertyMapperCollection( + new PropertyMapper( + "AString", + (ValueContentAbstractValue valueContentAbstractValue) => + { + return PropertySetCallbacks.EvaluateLiteralValues( + valueContentAbstractValue, + v => (v as string)?.StartsWith("A", StringComparison.Ordinal) == true); + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrackWithConstructor.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfAStringStartsWithA_Flagged() + { + VerifyCSharp(@" +using System; +using System.Collections; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = new TestTypeToTrackWithConstructor(default(TestEnum), null, ""A string""); + t.Method(); + }/**/ +}", + TestTypeToTrackWithConstructor_HazardousIfAStringStartsWithA, + (10, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrackWithConstructor_HazardousIfAStringStartsWithA_Interprocedural_Flagged() + { + VerifyCSharp(@" +using System; +using System.Collections; + +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrackWithConstructor t = GetTestType(); + t.Method(); + }/**/ + + TestTypeToTrackWithConstructor GetTestType() + { + return new TestTypeToTrackWithConstructor(default(TestEnum), null, ""A string""); + } +}", + TestTypeToTrackWithConstructor_HazardousIfAStringStartsWithA, + (10, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrack.AString property is not null + /// when returning a TestTypeToTrack. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrack_HazardousIfStringIsNonNullOnReturn = + new( + "TestTypeToTrack", + new ConstructorMapper( // Only one constructor, which leaves its AString property as null (not hazardous). + ImmutableArray.Create( + PropertySetAbstractValueKind.Unflagged)), + new PropertyMapperCollection( + new PropertyMapper( // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + "AString", + (PointsToAbstractValue pointsToAbstractValue) => + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + })), + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( + HazardousUsageEvaluatorKind.Return, + (PropertySetAbstractValue abstractValue) => + { + // With only one property being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNullOnReturn_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + TestTypeToTrack TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + return t; + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNullOnReturn, + (8, 16, null, HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNullOnReturn_StringEmpty_Flagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + TestTypeToTrack TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = String.Empty; + return t; + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNullOnReturn, + (9, 16, null, HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNullOnReturns_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + TestTypeToTrack TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = null; + return t; + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNullOnReturn); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringIsNonNullOnReturns_ReturnObject_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + object TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + return new object(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringIsNonNullOnReturn); + } + + /// + /// Parameters for PropertySetAnalysis to flag hazardous usage when the TestTypeToTrack.AString and + /// TestTypeToTrack.AnObject are aliases, and the aliased value is not null, when calling its Method() method. + /// + private readonly PropertySetAnalysisParameters TestTypeToTrack_HazardousIfStringObjectIsNonNull = + new( + "TestTypeToTrack", + new ConstructorMapper( // Only one constructor, which leaves its AString property as null (not hazardous). + ImmutableArray.Create( + PropertySetAbstractValueKind.Unflagged)), + new PropertyMapperCollection( + new PropertyMapper( // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + "AString", + (PointsToAbstractValue pointsToAbstractValue) => + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + }, + propertyIndex: 0), // Both AString and AnObject point to index 0. + new PropertyMapper( // Definitely null => unflagged, definitely non-null => flagged, otherwise => maybe. + "AnObject", + (PointsToAbstractValue pointsToAbstractValue) => + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Unflagged, + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Flagged, + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + _ => PropertySetAbstractValueKind.Unknown, + }; + }, + propertyIndex: 0)), // Both AString and AnObject point to index 0. + new HazardousUsageEvaluatorCollection( + new HazardousUsageEvaluator( // When TypeToTrack.Method() is invoked, need to evaluate its state. + "Method", + (IMethodSymbol methodSymbol, PropertySetAbstractValue abstractValue) => + { + // When doing this for reals, need to examine the method to make sure we're looking at the right method and arguments. + + // With only underlying value (from the two "aliased" properties) being tracked, this is straightforward. + return abstractValue[0] switch + { + PropertySetAbstractValueKind.Flagged => HazardousUsageEvaluationResult.Flagged, + PropertySetAbstractValueKind.MaybeFlagged => HazardousUsageEvaluationResult.MaybeFlagged, + _ => HazardousUsageEvaluationResult.Unflagged, + }; + }))); + + [Fact] + public void TestTypeToTrack_HazardousIfStringObjectIsNonNull_AStringNonNull_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringObjectIsNonNull, + (8, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringObjectIsNonNull_AnObjectNonNull_Flagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AnObject = new System.Random(); + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringObjectIsNonNull, + (8, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringObjectIsNonNull_StringEmpty_Flagged() + { + VerifyCSharp(@" +using System; +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = String.Empty; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringObjectIsNonNull, + (9, 9, "void TestTypeToTrack.Method()", HazardousUsageEvaluationResult.Flagged)); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringObjectIsNonNull_StringNonNull_ObjectNull_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AString = ""A non-null string""; + t.AnObject = null; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringObjectIsNonNull); + } + + [Fact] + public void TestTypeToTrack_HazardousIfStringObjectIsNonNull_AnObjectNonNull_StringNull_Unflagged() + { + VerifyCSharp(@" +class TestClass +{ + void TestMethod() + /**/{ + TestTypeToTrack t = new TestTypeToTrack(); + t.AnObject = new System.Random(); + t.AString = null; + t.Method(); + }/**/ +}", + TestTypeToTrack_HazardousIfStringObjectIsNonNull); + } + + private ITestOutputHelper TestOutput { get; } + + public PropertySetAnalysisTests(ITestOutputHelper output) + { + this.TestOutput = output; + } + + protected static readonly CompilationOptions s_CSharpDefaultOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary); + + internal const string DefaultFilePathPrefix = "Test"; + internal const string CSharpDefaultFileExt = "cs"; + protected static readonly string CSharpDefaultFilePath = DefaultFilePathPrefix + 0 + "." + CSharpDefaultFileExt; + + private const string TestProjectName = "TestProject"; + + protected static Project CreateProject(string[] sources) + { + string fileNamePrefix = DefaultFilePathPrefix; + string fileExt = CSharpDefaultFileExt; + CompilationOptions options = s_CSharpDefaultOptions; + + ProjectId projectId = ProjectId.CreateNewId(debugName: TestProjectName); + + var defaultReferences = ReferenceAssemblies.NetFramework.Net48.Default; + defaultReferences = defaultReferences.AddPackages(ImmutableArray.Create(new PackageIdentity("System.DirectoryServices", "6.0.1"))); + var references = Task.Run(() => defaultReferences.ResolveAsync(LanguageNames.CSharp, CancellationToken.None)).GetAwaiter().GetResult(); + +#pragma warning disable CA2000 // Dispose objects before losing scope - Current solution/project takes the dispose ownership of the created AdhocWorkspace + Project project = new AdhocWorkspace().CurrentSolution +#pragma warning restore CA2000 // Dispose objects before losing scope + .AddProject(projectId, TestProjectName, TestProjectName, LanguageNames.CSharp) + .AddMetadataReferences(projectId, references) + .AddMetadataReference(projectId, AdditionalMetadataReferences.CodeAnalysisReference) + .AddMetadataReference(projectId, AdditionalMetadataReferences.WorkspacesReference) +#if !NETCOREAPP + .AddMetadataReference(projectId, AdditionalMetadataReferences.SystemWebReference) + .AddMetadataReference(projectId, AdditionalMetadataReferences.SystemRuntimeSerialization) + .AddMetadataReference(projectId, AdditionalMetadataReferences.SystemXaml) + .AddMetadataReference(projectId, AdditionalMetadataReferences.PresentationFramework) + .AddMetadataReference(projectId, AdditionalMetadataReferences.SystemWebExtensions) +#endif + .WithProjectCompilationOptions(projectId, options) + .WithProjectParseOptions(projectId, new CSharpParseOptions()) + .GetProject(projectId)!; + + Assert.NotNull(project.ParseOptions); + + // Enable Flow-Analysis feature on the project + var parseOptions = project.ParseOptions.WithFeatures( + project.ParseOptions.Features.Concat( + new[] { new KeyValuePair("flow-analysis", "true") })); + project = project.WithParseOptions(parseOptions); + + MetadataReference symbolsReference = AdditionalMetadataReferences.CSharpSymbolsReference; + project = project.AddMetadataReference(symbolsReference); + + project = project.AddMetadataReference(AdditionalMetadataReferences.SystemCollectionsImmutableReference); + project = project.AddMetadataReference(AdditionalMetadataReferences.SystemXmlDataReference); + + int count = 0; + foreach (var source in sources) + { + string newFileName = fileNamePrefix + count++ + "." + fileExt; + DocumentId documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); + project = project.AddDocument(newFileName, SourceText.From(source)).Project; + } + + return project; + } + + protected static (IOperation? operation, SemanticModel? model, SyntaxNode? node) GetOperationAndSyntaxForTest(CSharpCompilation compilation) + where TSyntaxNode : SyntaxNode + { + var tree = compilation.SyntaxTrees[0]; + var model = compilation.GetSemanticModel(tree); + SyntaxNode? syntaxNode = GetSyntaxNodeOfTypeForBinding(GetSyntaxNodeList(tree)); + if (syntaxNode == null) + { + return (null, null, null); + } + + var operation = model.GetOperation(syntaxNode); + if (operation != null) + { + Assert.Same(model, operation.SemanticModel); + } + + return (operation, model, syntaxNode); + } + + protected static List GetSyntaxNodeList(SyntaxTree syntaxTree) + { + return GetSyntaxNodeList(syntaxTree.GetRoot(), null); + } + + protected static List GetSyntaxNodeList(SyntaxNode node, List? synList) + { + synList ??= new List(); + + synList.Add(node); + + foreach (var child in node.ChildNodesAndTokens()) + { + if (child.IsNode) + { + var childNode = child.AsNode(); + Assert.NotNull(childNode); + synList = GetSyntaxNodeList(childNode, synList); + } + } + + return synList; + } + + protected const string StartString = "/**/"; + protected const string EndString = "/**/"; + + protected static TNode? GetSyntaxNodeOfTypeForBinding(List synList) where TNode : SyntaxNode + { + foreach (var node in synList.OfType()) + { + string exprFullText = node.ToFullString(); + exprFullText = exprFullText.Trim(); + + if (exprFullText.StartsWith(StartString, StringComparison.Ordinal)) + { + if (exprFullText.Contains(EndString, StringComparison.Ordinal)) + { + if (exprFullText.EndsWith(EndString, StringComparison.Ordinal)) + { + return node; + } + else + { + continue; + } + } + else + { + return node; + } + } + + if (exprFullText.EndsWith(EndString, StringComparison.Ordinal)) + { + if (exprFullText.Contains(StartString, StringComparison.Ordinal)) + { + if (exprFullText.StartsWith(StartString, StringComparison.Ordinal)) + { + return node; + } + else + { + continue; + } + } + else + { + return node; + } + } + } + + return null; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/Lightup/LightupHelpersTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/Lightup/LightupHelpersTests.cs new file mode 100644 index 0000000000000..a8869bb4a1a50 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/Lightup/LightupHelpersTests.cs @@ -0,0 +1,158 @@ +// 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 enable + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Threading; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Xunit; + +namespace Analyzer.Utilities.UnitTests.Lightup +{ + public class LightupHelpersTests + { + [Theory] + [InlineData(null)] + [InlineData(typeof(SyntaxNode))] + public void TestCanAccessNonExistentSyntaxProperty(Type? type) + { + var fallbackResult = new object(); + + var propertyAccessor = LightupHelpers.CreateSyntaxPropertyAccessor(type, "NonExistentProperty", fallbackResult); + Assert.NotNull(propertyAccessor); + Assert.Same(fallbackResult, propertyAccessor(SyntaxFactory.AccessorList())); + Assert.Throws(() => propertyAccessor(null!)); + + var withPropertyAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor(type, "NonExistentProperty", fallbackResult); + Assert.NotNull(withPropertyAccessor); + Assert.NotNull(withPropertyAccessor(SyntaxFactory.AccessorList(), fallbackResult)); + Assert.ThrowsAny(() => withPropertyAccessor(SyntaxFactory.AccessorList(), new object())); + Assert.Throws(() => withPropertyAccessor(null!, new object())); + } + + [Theory] + [InlineData(null)] + [InlineData(typeof(EmptySymbol))] + public void TestCanAccessNonExistentSymbolProperty(Type? type) + { + var fallbackResult = new object(); + + var propertyAccessor = LightupHelpers.CreateSymbolPropertyAccessor(type, "NonExistentProperty", fallbackResult); + Assert.NotNull(propertyAccessor); + Assert.Same(fallbackResult, propertyAccessor(new EmptySymbol())); + Assert.Throws(() => propertyAccessor(null!)); + + var withPropertyAccessor = LightupHelpers.CreateSymbolWithPropertyAccessor(type, "NonExistentProperty", fallbackResult); + Assert.NotNull(withPropertyAccessor); + Assert.NotNull(withPropertyAccessor(new EmptySymbol(), fallbackResult)); + Assert.ThrowsAny(() => withPropertyAccessor(new EmptySymbol(), new object())); + Assert.Throws(() => withPropertyAccessor(null!, new object())); + } + + [Theory] + [InlineData(null)] + [InlineData(typeof(SyntaxNode))] + public void TestCanAccessNonExistentMethodWithArgument(Type? type) + { + var fallbackResult = new object(); + + var accessor = LightupHelpers.CreateAccessorWithArgument(type, "parameterName", typeof(int), "argumentName", "NonExistentMethod", fallbackResult); + Assert.NotNull(accessor); + Assert.Same(fallbackResult, accessor(SyntaxFactory.AccessorList(), 0)); + Assert.Throws(() => accessor(null!, 0)); + } + + [Fact] + public void TestCreateSyntaxPropertyAccessor() + { + // The call *should* have been made with the first generic argument set to `BaseMethodDeclarationSyntax` + // instead of `MethodDeclarationSyntax`. + Assert.ThrowsAny(() => LightupHelpers.CreateSyntaxPropertyAccessor(typeof(BaseMethodDeclarationSyntax), nameof(BaseMethodDeclarationSyntax.Body), fallbackResult: null)); + + // The call *should* have been made with the second generic argument set to `ArrowExpressionClauseSyntax` + // instead of `BlockSyntax`. + Assert.ThrowsAny(() => LightupHelpers.CreateSyntaxPropertyAccessor(typeof(MethodDeclarationSyntax), nameof(MethodDeclarationSyntax.ExpressionBody), fallbackResult: null)); + } + + [Fact] + public void TestCreateSyntaxWithPropertyAccessor() + { + // The call *should* have been made with the first generic argument set to `BaseMethodDeclarationSyntax` + // instead of `MethodDeclarationSyntax`. + Assert.ThrowsAny(() => LightupHelpers.CreateSyntaxWithPropertyAccessor(typeof(BaseMethodDeclarationSyntax), nameof(BaseMethodDeclarationSyntax.Body), fallbackResult: null)); + + // The call *should* have been made with the second generic argument set to `ArrowExpressionClauseSyntax` + // instead of `BlockSyntax`. + Assert.ThrowsAny(() => LightupHelpers.CreateSyntaxWithPropertyAccessor(typeof(MethodDeclarationSyntax), nameof(MethodDeclarationSyntax.ExpressionBody), fallbackResult: null)); + } + + [SuppressMessage("MicrosoftCodeAnalysisCompatibility", "RS1009:Only internal implementations of this interface are allowed.", Justification = "Stub for testing.")] + private sealed class EmptySymbol : ISymbol + { + SymbolKind ISymbol.Kind => throw new NotImplementedException(); + string ISymbol.Language => throw new NotImplementedException(); + string ISymbol.Name => throw new NotImplementedException(); + string ISymbol.MetadataName => throw new NotImplementedException(); + ISymbol ISymbol.ContainingSymbol => throw new NotImplementedException(); + IAssemblySymbol ISymbol.ContainingAssembly => throw new NotImplementedException(); + IModuleSymbol ISymbol.ContainingModule => throw new NotImplementedException(); + INamedTypeSymbol ISymbol.ContainingType => throw new NotImplementedException(); + INamespaceSymbol ISymbol.ContainingNamespace => throw new NotImplementedException(); + bool ISymbol.IsDefinition => throw new NotImplementedException(); + bool ISymbol.IsStatic => throw new NotImplementedException(); + bool ISymbol.IsVirtual => throw new NotImplementedException(); + bool ISymbol.IsOverride => throw new NotImplementedException(); + bool ISymbol.IsAbstract => throw new NotImplementedException(); + bool ISymbol.IsSealed => throw new NotImplementedException(); + bool ISymbol.IsExtern => throw new NotImplementedException(); + bool ISymbol.IsImplicitlyDeclared => throw new NotImplementedException(); + bool ISymbol.CanBeReferencedByName => throw new NotImplementedException(); + ImmutableArray ISymbol.Locations => throw new NotImplementedException(); + ImmutableArray ISymbol.DeclaringSyntaxReferences => throw new NotImplementedException(); + Accessibility ISymbol.DeclaredAccessibility => throw new NotImplementedException(); + ISymbol ISymbol.OriginalDefinition => throw new NotImplementedException(); + bool ISymbol.HasUnsupportedMetadata => throw new NotImplementedException(); + + void ISymbol.Accept(SymbolVisitor visitor) + => throw new NotImplementedException(); + + TResult ISymbol.Accept(SymbolVisitor visitor) + => throw new NotImplementedException(); + + public bool Equals(ISymbol? other) + => throw new NotImplementedException(); + + ImmutableArray ISymbol.GetAttributes() + => throw new NotImplementedException(); + + string ISymbol.GetDocumentationCommentId() + => throw new NotImplementedException(); + + string ISymbol.GetDocumentationCommentXml(CultureInfo? preferredCulture, bool expandIncludes, CancellationToken cancellationToken) + => throw new NotImplementedException(); + + ImmutableArray ISymbol.ToDisplayParts(SymbolDisplayFormat? format) + => throw new NotImplementedException(); + + string ISymbol.ToDisplayString(SymbolDisplayFormat? format) + => throw new NotImplementedException(); + + ImmutableArray ISymbol.ToMinimalDisplayParts(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) + => throw new NotImplementedException(); + + string ISymbol.ToMinimalDisplayString(SemanticModel semanticModel, int position, SymbolDisplayFormat? format) + => throw new NotImplementedException(); + + public bool Equals(ISymbol? other, SymbolEqualityComparer equalityComparer) + => throw new NotImplementedException(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities.UnitTests/Options/SymbolNamesWithValueOptionTests.cs b/src/RoslynAnalyzers/Utilities.UnitTests/Options/SymbolNamesWithValueOptionTests.cs new file mode 100644 index 0000000000000..c47da354f3319 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities.UnitTests/Options/SymbolNamesWithValueOptionTests.cs @@ -0,0 +1,443 @@ +// 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; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Text; +using Test.Utilities; +using Xunit; + +namespace Analyzer.Utilities.UnitTests.Options +{ + public sealed class SymbolNamesWithValueOptionTests + { + [Fact] + public void WhenNoSymbolNames_ReturnsEmpty() + { + // Arrange & act + var options = SymbolNamesWithValueOption.Create(ImmutableArray.Empty, GetCompilation(), null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Equal(SymbolNamesWithValueOption.Empty, options); + } + + [Fact] + public void SymbolNamePartsFuncIsCalledForEachSymbol() + { + // Arrange + var callCount = 0; + var symbolNames = ImmutableArray.Create("a", "b"); + SymbolNamesWithValueOption.NameParts func(string symbolName) + { + if (symbolNames.Contains(symbolName)) + { + callCount++; + } + + return new SymbolNamesWithValueOption.NameParts(symbolName, Unit.Default); + } + + // Act + SymbolNamesWithValueOption.Create(symbolNames, GetCompilation(), null, func); + + // Assert + Assert.Equal(2, callCount); + } + + [Fact] + public void UnqualifiedSymbolNamesAreProcessedAsNames() + { + // Arrange + // Note that there is no check for the existence of the member + var compilation = GetCompilation(); + var symbolNames = ImmutableArray.Create("MyNamespace", "MyClass", "MyMethod()", "MyProperty", "MyEvent", "MyField"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Equal(symbolNames.Length, options.GetTestAccessor().Names.Count); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Empty(options.GetTestAccessor().WildcardNamesBySymbolKind); + } + + [Fact] + public void QualifiedSymbolNamesWithoutPrefixAreIgnored() + { + // Arrange + var compilation = GetCompilation(@" +using System; + +public namespace MyNamespace +{ + public class MyClass + { + public int MyField; + public int MyProperty { get; set; } + public event EventHandler MyEvent; + public void MyMethod() {} + } +}"); + var symbolNames = ImmutableArray.Create( + "MyNamespace.MySubNamespace", + "MyNamespace.MyClass", + "MyNamespace.MyClass.MyField", + "MyNamespace.MyClass.MyProperty", + "MyNamespace.MyClass.MyEvent", + "MyNamespace.MyClass.MyMethod()"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Empty(options.GetTestAccessor().WildcardNamesBySymbolKind); + } + + [Fact] + public void QualifiedSymbolNamesWithPrefixAreProcessedAsSymbols() + { + // Arrange + var compilation = GetCompilation(@" +using System; + +public namespace MyNamespace +{ + public class MyClass + { + public int MyField; + public int MyProperty { get; set; } + public event EventHandler MyEvent; + public void MyMethod() {} + } +}"); + var symbolNames = ImmutableArray.Create( + "N:MyNamespace", + "T:MyNamespace.MyClass", + "F:MyNamespace.MyClass.MyField", + "P:MyNamespace.MyClass.MyProperty", + "E:MyNamespace.MyClass.MyEvent", + "M:MyNamespace.MyClass.MyMethod()"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Equal(symbolNames.Length, options.GetTestAccessor().Symbols.Count); + Assert.Empty(options.GetTestAccessor().WildcardNamesBySymbolKind); + } + + [Fact] + public void UnfoundQualifiedSymbolNamesWithPrefixAreExcluded() + { + // Arrange + var compilation = GetCompilation(); + var symbolNames = ImmutableArray.Create( + "N:MyNamespace", + "T:MyNamespace.MyClass", + "F:MyNamespace.MyClass.MyField", + "P:MyNamespace.MyClass.MyProperty", + "E:MyNamespace.MyClass.MyEvent", + "M:MyNamespace.MyClass.MyMethod()"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Empty(options.GetTestAccessor().WildcardNamesBySymbolKind); + } + + [Fact] + public void UnsupportedWildcardConstructionsAreIgnored() + { + // Arrange + var compilation = GetCompilation(); + var symbolNames = ImmutableArray.Create( + "*", // only wildcard symbol + "*a", // wildcard symbol is not last + "a*a", // wildcard symbol is not last + "*a*"); // more than one wildcard symbol + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Empty(options.GetTestAccessor().WildcardNamesBySymbolKind); + } + + [Fact] + public void WildcardConstructionsWithoutPrefixAreCorrectlyClassified() + { + // Arrange + var compilation = GetCompilation(); + var symbolNames = ImmutableArray.Create( + "MyNamespace*", + "MyNamespace.MyClass*", + "MyNamespace.MyClass.MyField*", + "MyNamespace.MyClass.MyProperty*", + "MyNamespace.MyClass.MyEvent*", + "MyNamespace.MyClass.MyMethod(*"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Single(options.GetTestAccessor().WildcardNamesBySymbolKind); + Assert.Equal(symbolNames.Length, options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].Count); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace.MyClass")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace.MyClass.MyField")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace.MyClass.MyProperty")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace.MyClass.MyEvent")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds].ContainsKey("MyNamespace.MyClass.MyMethod(")); + } + + [Fact] + public void WildcardConstructionsWithPrefixAreCorrectlyClassified() + { + // Arrange + var compilation = GetCompilation(); + var symbolNames = ImmutableArray.Create( + "N:MyNamespace*", + "T:MyNamespace.MyClass*", + "F:MyNamespace.MyClass.MyField*", + "P:MyNamespace.MyClass.MyProperty*", + "E:MyNamespace.MyClass.MyEvent*", + "M:MyNamespace.MyClass.MyMethod(*"); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Assert + Assert.Empty(options.GetTestAccessor().Names); + Assert.Empty(options.GetTestAccessor().Symbols); + Assert.Equal(symbolNames.Length, options.GetTestAccessor().WildcardNamesBySymbolKind.Count); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.Namespace].ContainsKey("MyNamespace")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.NamedType].ContainsKey("MyNamespace.MyClass")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.Field].ContainsKey("MyNamespace.MyClass.MyField")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.Property].ContainsKey("MyNamespace.MyClass.MyProperty")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.Event].ContainsKey("MyNamespace.MyClass.MyEvent")); + Assert.True(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.Method].ContainsKey("MyNamespace.MyClass.MyMethod(")); + } + + [Fact] + public void ValueCanBeAssociatedWithAllSymbolNames() + { + // Arrange + var compilation = GetCompilation(@" +using System; + +public namespace MyNamespace +{ + public class MyClass {} +}"); + var symbolNames = ImmutableArray.Create( + "MyClass->SomeValue1", + "T:MyNamespace.MyClass->SomeValue2", + "MyClass*->SomeValue3", + "T:MyClass*->SomeValue4"); + + var namedTypeSymbol = (INamedTypeSymbol)compilation.GetSymbolsWithName("MyClass").Single(); + + // Act + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, + getSymbolNamePartsFunc: symbolName => + { + var split = symbolName.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries); + return new SymbolNamesWithValueOption.NameParts(split[0], split[1]); + }); + + // Assert + Assert.Single(options.GetTestAccessor().Names); + Assert.Equal("SomeValue1", options.GetTestAccessor().Names["MyClass"]); + Assert.Single(options.GetTestAccessor().Symbols); + Assert.Equal("SomeValue2", options.GetTestAccessor().Symbols[namedTypeSymbol]); + Assert.Equal(2, options.GetTestAccessor().WildcardNamesBySymbolKind.Count); + Assert.Single(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds]); + Assert.Equal("SomeValue3", options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolNamesWithValueOption.AllKinds]["MyClass"]); + Assert.Single(options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.NamedType]); + Assert.Equal("SomeValue4", options.GetTestAccessor().WildcardNamesBySymbolKind[SymbolKind.NamedType]["MyClass"]); + } + + [Fact] + [WorkItem(1242125, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1242125")] + public void FastPathDoesNotCache() + { + var compilation = GetCompilation(@" +using System; + +public namespace MyNamespace +{ + public class MyClass {} +}"); + + var namedTypeSymbol = (INamedTypeSymbol)compilation.GetSymbolsWithName("MyClass").Single(); + + var options = SymbolNamesWithValueOption.Empty; + + // Check for containment + var contained = options.Contains(namedTypeSymbol); + Assert.False(contained); + Assert.Empty(options.GetTestAccessor().WildcardMatchResult); + Assert.Empty(options.GetTestAccessor().SymbolToDeclarationId); + } + + [Theory] + // Symbol name + [InlineData("My*", "MyCompany")] + [InlineData("My*", "MyOuterClass")] + [InlineData("My*", "MyInnerClass")] + [InlineData("My*", "MyField")] + [InlineData("My*", "MyProperty")] + [InlineData("My*", "MyEvent")] + [InlineData("My*", "MyMethod")] + [InlineData("My*", "MyMethod2")] + // Fully qualified name with prefix + [InlineData("N:MyCompany*", "MyCompany")] + [InlineData("N:MyCompany.MyProduct*", "MyProduct")] + [InlineData("N:MyCompany.MyProduct.MyFeature*", "MyFeature")] + [InlineData("T:MyCompany.MyProduct.MyFeature.MyOuterClass*", "MyOuterClass")] + [InlineData("T:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass*", "MyInnerClass")] + [InlineData("F:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyField*", "MyField")] + [InlineData("M:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass..ctor*", ".ctor")] + [InlineData("P:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyProperty*", "MyProperty")] + [InlineData("P:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.Item*", "this[]")] + [InlineData("E:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyEvent*", "MyEvent")] + [InlineData("M:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyMethod*", "MyMethod")] + [InlineData("M:MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyMethod2(System.Str*", "MyMethod2")] + // Fully qualified name without prefix + [InlineData("MyCompany*", "MyCompany")] + [InlineData("MyCompany.MyProduct*", "MyProduct")] + [InlineData("MyCompany.MyProduct.MyFeature*", "MyFeature")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass*", "MyOuterClass")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass*", "MyInnerClass")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyField*", "MyField")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass..ctor*", ".ctor")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyProperty*", "MyProperty")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.Item*", "this[]")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyEvent*", "MyEvent")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyMethod*", "MyMethod")] + [InlineData("MyCompany.MyProduct.MyFeature.MyOuterClass.MyInnerClass.MyMethod2(System.Str*", "MyMethod2")] + public void WildcardMatch(string patternName, string symbolName) + { + // Arrange + var compilation = GetCompilation(@" +public namespace MyCompany.MyProduct.MyFeature +{ + public class MyOuterClass + { + public class MyInnerClass + { + public int MyField; + public MyInnerClass() {} + public MyInnerClass(int i) {} + public int MyProperty { get; set; } + public int this[] + { + get { return 42; } + set {} + } + public int this[string s] + { + get { return 42; } + set {} + } + public event EventHandler MyEvent; + public void MyMethod() {} + public void MyMethod2(string s) {} + } + } +}"); + var symbolNames = ImmutableArray.Create(patternName); + var symbol = FindSymbol(compilation, symbolName); + var options = SymbolNamesWithValueOption.Create(symbolNames, compilation, null, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + // Act + var isFound = options.Contains(symbol); + + // Assert + Assert.True(isFound); + Assert.True(options.GetTestAccessor().WildcardMatchResult.ContainsKey(symbol)); + + static ISymbol FindSymbol(Compilation compilation, string symbolName) + { + var innerClassSymbol = (INamedTypeSymbol)compilation.GetSymbolsWithName("MyInnerClass", SymbolFilter.Type).Single(); + + var currentType = innerClassSymbol; + while (currentType != null) + { + if (currentType.Name == symbolName) + { + return currentType; + } + + currentType = currentType.ContainingType; + } + + var currentNamespace = innerClassSymbol.ContainingNamespace; + while (currentNamespace != null) + { + if (currentNamespace.Name == symbolName) + { + return currentNamespace; + } + + currentNamespace = currentNamespace.ContainingNamespace; + } + + foreach (var member in innerClassSymbol.GetMembers()) + { + if (member.Name == symbolName) + { + return member; + } + } + + throw new InvalidOperationException("Cannot find symbol name: " + symbolName); + } + } + + private static Compilation GetCompilation(params string[] sources) + { + var cancellationToken = CancellationToken.None; + const string TestProjectName = "SymbolNamesTestProject"; + var projectId = ProjectId.CreateNewId(debugName: TestProjectName); + + var references = Task.Run(() => AdditionalMetadataReferences.Default.ResolveAsync(LanguageNames.CSharp, cancellationToken)).GetAwaiter().GetResult(); + +#pragma warning disable CA2000 // Dispose objects before losing scope - Current solution/project takes the dispose ownership of the created AdhocWorkspace + var project = new AdhocWorkspace().CurrentSolution +#pragma warning restore CA2000 // Dispose objects before losing scope + .AddProject(projectId, TestProjectName, TestProjectName, LanguageNames.CSharp) + .AddMetadataReferences(projectId, references) + .WithProjectCompilationOptions(projectId, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) + .WithProjectParseOptions(projectId, new CSharpParseOptions()) + .GetProject(projectId)!; + + int count = 0; + foreach (var source in sources) + { + string newFileName = $"Test{count++}.cs"; + DocumentId documentId = DocumentId.CreateNewId(projectId, debugName: newFileName); + project = project.AddDocument(newFileName, SourceText.From(source)).Project; + } + + Assert.True(project.SupportsCompilation); + return project.GetCompilationAsync(cancellationToken).Result!; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems new file mode 100644 index 0000000000000..a60042f946168 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems @@ -0,0 +1,15 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + be5fe32c-da73-4ec5-809e-e11b05aca398 + + + Analyzer.CSharp.Utilities + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.shproj new file mode 100644 index 0000000000000..98e86460ba903 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.shproj @@ -0,0 +1,13 @@ + + + + be5fe32c-da73-4ec5-809e-e11b05aca398 + 14.0 + + + + + + + + diff --git a/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Extensions/SyntaxNodeExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Extensions/SyntaxNodeExtensions.cs new file mode 100644 index 0000000000000..de2d3937b684d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Extensions/SyntaxNodeExtensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class SyntaxNodeExtensions + { + public static SyntaxNode WalkDownParentheses(this SyntaxNode node) + { + SyntaxNode current = node; + while (current.IsKind(SyntaxKind.ParenthesizedExpression) && current.ChildNodes().FirstOrDefault() is SyntaxNode expression) + { + current = expression; + } + + return current; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Lightup/SyntaxKindEx.cs b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Lightup/SyntaxKindEx.cs new file mode 100644 index 0000000000000..fbe93dcdcc15a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler.CSharp/Lightup/SyntaxKindEx.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis.CSharp; + +namespace Analyzer.Utilities.Lightup +{ + internal static class SyntaxKindEx + { + // https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs + public const SyntaxKind Utf8StringLiteralToken = (SyntaxKind)8520; + public const SyntaxKind Utf8StringLiteralExpression = (SyntaxKind)8756; + public const SyntaxKind CollectionExpression = (SyntaxKind)9076; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/AdditionalFileProvider.cs b/src/RoslynAnalyzers/Utilities/Compiler/AdditionalFileProvider.cs new file mode 100644 index 0000000000000..d8b071ab86c68 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/AdditionalFileProvider.cs @@ -0,0 +1,53 @@ +// 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.IO; +using System.Linq; +using System.Text.RegularExpressions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities +{ + /// + /// Provider that allows analyzers to easily find and use + /// additional files. + /// + internal sealed class AdditionalFileProvider + { + private readonly ImmutableArray _additionalFiles; + + internal AdditionalFileProvider(ImmutableArray additionalFiles) + { + _additionalFiles = additionalFiles; + } + + /// + /// Creates an instance of this provider from the specified . + /// + /// Options passed to a . + /// An instance of . + public static AdditionalFileProvider FromOptions(AnalyzerOptions options) + => new(options.AdditionalFiles); + + /// + /// Returns the first additional file whose name is the specified . + /// + /// Name of the file, including extension, to return. + /// An additional file or null if no file can be found. + public AdditionalText? GetFile(string fileName) + => _additionalFiles.FirstOrDefault(x => Path.GetFileName(x.Path).Equals(fileName, StringComparison.OrdinalIgnoreCase)); + + /// + /// Returns all additional files whose names match the specified . + /// + /// A regular expression. + /// An enumeration of additional files whose names match the pattern. + public IEnumerable GetMatchingFiles(string pattern) + => _additionalFiles.Where(x => Regex.IsMatch(Path.GetFileName(x.Path), pattern, RegexOptions.IgnoreCase)); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.projitems new file mode 100644 index 0000000000000..fe0da97cb9fcb --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.projitems @@ -0,0 +1,143 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + + Analyzer.Utilities + + + $(DefineConstants),CODEANALYSIS_V3_OR_BETTER + + + $(DefineConstants),CODEANALYSIS_V3_7_OR_BETTER + + + $(DefineConstants);HAS_IOPERATION + true + + enable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.shproj new file mode 100644 index 0000000000000..d34e579cfb5fa --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Analyzer.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {08735294-3E6B-4420-9916-E7B8C4EB874D} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Compiler/BoundedCache.cs b/src/RoslynAnalyzers/Utilities/Compiler/BoundedCache.cs new file mode 100644 index 0000000000000..1c429d0e0ebf8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/BoundedCache.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities +{ + /// + /// Provides bounded cache for analyzers. + /// Acts as a good alternative to + /// when the cached value has a cyclic reference to the key preventing early garbage collection of entries. + /// + internal sealed class BoundedCache : BoundedCacheWithFactory + where TKey : class + where TValue : new() + { + public TValue GetOrCreateValue(TKey key) + { + return GetOrCreateValue(key, CreateDefaultValue); + + // Local functions. + static TValue CreateDefaultValue(TKey _) => new(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/BoundedCacheWithFactory.cs b/src/RoslynAnalyzers/Utilities/Compiler/BoundedCacheWithFactory.cs new file mode 100644 index 0000000000000..1c8b39c88c25d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/BoundedCacheWithFactory.cs @@ -0,0 +1,82 @@ +// 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; + +namespace Analyzer.Utilities +{ + /// + /// Provides bounded cache for analyzers. + /// Acts as a good alternative to + /// when the cached value has a cyclic reference to the key preventing early garbage collection of entries. + /// + internal class BoundedCacheWithFactory + where TKey : class + { + // Bounded weak reference cache. + // Size 5 is an arbitrarily chosen bound, which can be tuned in future as required. + private readonly List> _weakReferencedEntries = new() + { + new WeakReference(null), + new WeakReference(null), + new WeakReference(null), + new WeakReference(null), + new WeakReference(null), + }; + + public TValue GetOrCreateValue(TKey key, Func valueFactory) + { + lock (_weakReferencedEntries) + { + var indexToSetTarget = -1; + for (var i = 0; i < _weakReferencedEntries.Count; i++) + { + var weakReferencedEntry = _weakReferencedEntries[i]; + if (!weakReferencedEntry.TryGetTarget(out var cachedEntry) || + cachedEntry == null) + { + if (indexToSetTarget == -1) + { + indexToSetTarget = i; + } + + continue; + } + + if (Equals(cachedEntry.Key, key)) + { + // Move the cache hit item to the end of the list + // so it would be least likely to be evicted on next cache miss. + _weakReferencedEntries.RemoveAt(i); + _weakReferencedEntries.Add(weakReferencedEntry); + return cachedEntry.Value; + } + } + + if (indexToSetTarget == -1) + { + indexToSetTarget = 0; + } + + var newEntry = new Entry(key, valueFactory(key)); + _weakReferencedEntries[indexToSetTarget].SetTarget(newEntry); + return newEntry.Value; + } + } + + private sealed class Entry + { + public Entry(TKey key, TValue value) + { + Key = key; + Value = value; + } + + public TKey Key { get; } + + public TValue Value { get; } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs new file mode 100644 index 0000000000000..5213c364b0a66 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.AssemblyMetricData.cs @@ -0,0 +1,119 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class AssemblyMetricData : CodeAnalysisMetricData + { + private AssemblyMetricData( + IAssemblySymbol symbol, int maintainabilityIndex, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + : base(symbol, maintainabilityIndex, ComputationalComplexityMetrics.Default, + coupledNamedTypes, linesOfCode, cyclomaticComplexity, depthOfInheritance, children) + { + } + + internal static async Task ComputeAsync(IAssemblySymbol assembly, CodeMetricsAnalysisContext context) + { + ImmutableArray children = await ComputeAsync(GetChildSymbols(assembly), context).ConfigureAwait(false); + return ComputeFromChildren(assembly, children, context); + } + + internal static AssemblyMetricData ComputeSynchronously(IAssemblySymbol assembly, CodeMetricsAnalysisContext context) + { + ImmutableArray children = ComputeSynchronously(GetChildSymbols(assembly), context); + return ComputeFromChildren(assembly, children, context); + } + + private static AssemblyMetricData ComputeFromChildren(IAssemblySymbol assembly, ImmutableArray children, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + long linesOfCode = 0; + int maintainabilityIndexTotal = 0; + int cyclomaticComplexity = 0; + int depthOfInheritance = 0; + int grandChildCount = 0; + + foreach (CodeAnalysisMetricData child in children) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); + linesOfCode += child.SourceLines; + cyclomaticComplexity += child.CyclomaticComplexity; + depthOfInheritance = Math.Max(child.DepthOfInheritance.GetValueOrDefault(), depthOfInheritance); + + // Compat: Maintainability index of an assembly is computed based on the values of types, not namespace children. + Debug.Assert(child.Symbol.Kind == SymbolKind.Namespace); + Debug.Assert(!child.Children.IsEmpty); + Debug.Assert(child.Children.All(grandChild => grandChild.Symbol.Kind == SymbolKind.NamedType)); + maintainabilityIndexTotal += child.MaintainabilityIndex * child.Children.Length; + grandChildCount += child.Children.Length; + } + + int maintainabilityIndex = grandChildCount > 0 ? MetricsHelper.GetAverageRoundedMetricValue(maintainabilityIndexTotal, grandChildCount) : 100; + return new AssemblyMetricData(assembly, maintainabilityIndex, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); + } + + private static ImmutableArray GetChildSymbols(IAssemblySymbol assembly) + { + // Compat: We only create child nodes for namespaces which have at least one type member. + var includeGlobalNamespace = false; + var namespacesWithTypeMember = new HashSet(); + + processNamespace(assembly.GlobalNamespace); + + var builder = ImmutableArray.CreateBuilder(); + + if (includeGlobalNamespace) + { + builder.Add(assembly.GlobalNamespace); + } + + foreach (INamespaceSymbol @namespace in namespacesWithTypeMember.OrderBy(ns => ns.ToDisplayString())) + { + builder.Add(@namespace); + } + + return builder.ToImmutable(); + + void processNamespace(INamespaceSymbol @namespace) + { + foreach (INamespaceOrTypeSymbol child in @namespace.GetMembers()) + { + if (child.Kind == SymbolKind.Namespace) + { + processNamespace((INamespaceSymbol)child); + } + else if (@namespace.IsGlobalNamespace) + { + includeGlobalNamespace = true; + } + else if (!child.IsImplicitlyDeclared) + { + namespacesWithTypeMember.Add(@namespace); + } + } + } + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs new file mode 100644 index 0000000000000..8124c4c8fdaab --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.EventMetricData.cs @@ -0,0 +1,78 @@ +// 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. + +#if HAS_IOPERATION + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class EventMetricData : CodeAnalysisMetricData + { + internal EventMetricData( + IEventSymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + : base(symbol, maintainabilityIndex, computationalComplexityMetrics, coupledNamedTypes, + linesOfCode, cyclomaticComplexity, depthOfInheritance, children) + { + } + + internal static EventMetricData Compute(IEventSymbol @event, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = @event.DeclaringSyntaxReferences; + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, @event, context); + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, @event, coupledTypesBuilder, context); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, @event.Type); + + ImmutableArray children = ComputeSynchronously(GetAccessors(@event), context); + int maintainabilityIndexTotal = 0; + foreach (CodeAnalysisMetricData child in children) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); + maintainabilityIndexTotal += child.MaintainabilityIndex; + cyclomaticComplexity += child.CyclomaticComplexity; + computationalComplexityMetrics = computationalComplexityMetrics.Union(child.ComputationalComplexityMetrics); + } + + int? depthOfInheritance = null; + int maintainabilityIndex = !children.IsEmpty ? MetricsHelper.GetAverageRoundedMetricValue(maintainabilityIndexTotal, children.Length) : 100; + MetricsHelper.RemoveContainingTypes(@event, coupledTypesBuilder); + + return new EventMetricData(@event, maintainabilityIndex, computationalComplexityMetrics, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); + } + + private static IEnumerable GetAccessors(IEventSymbol @event) + { + if (@event.AddMethod != null) + { + yield return @event.AddMethod; + } + + if (@event.RemoveMethod != null) + { + yield return @event.RemoveMethod; + } + + if (@event.RaiseMethod != null) + { + yield return @event.RaiseMethod; + } + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs new file mode 100644 index 0000000000000..bb71bdd543094 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.FieldMetricData.cs @@ -0,0 +1,55 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class FieldMetricData : CodeAnalysisMetricData + { + internal FieldMetricData( + IFieldSymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance) + : base(symbol, maintainabilityIndex, computationalComplexityMetrics, coupledNamedTypes, + linesOfCode, cyclomaticComplexity, depthOfInheritance, children: ImmutableArray.Empty) + { + } + + internal static FieldMetricData Compute(IFieldSymbol field, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = field.DeclaringSyntaxReferences; + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, field, context); + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, field, coupledTypesBuilder, context); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, field.Type); + int? depthOfInheritance = null; + int maintainabilityIndex = CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity); + MetricsHelper.RemoveContainingTypes(field, coupledTypesBuilder); + + return new FieldMetricData(field, maintainabilityIndex, computationalComplexityMetrics, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance); + } + + private static int CalculateMaintainabilityIndex(ComputationalComplexityMetrics computationalComplexityMetrics, int cyclomaticComplexity) + { + double computationalComplexityVolume = Math.Max(0.0, Math.Log(computationalComplexityMetrics.Volume)); //avoid Log(0) = -Infinity + double logEffectiveLinesOfCode = Math.Max(0.0, Math.Log(computationalComplexityMetrics.EffectiveLinesOfCode)); //avoid Log(0) = -Infinity + return MetricsHelper.NormalizeAndRoundMaintainabilityIndex(171 - 5.2 * computationalComplexityVolume - 0.23 * cyclomaticComplexity - 16.2 * logEffectiveLinesOfCode); + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs new file mode 100644 index 0000000000000..a4dad882ce406 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.MethodMetricData.cs @@ -0,0 +1,66 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class MethodMetricData : CodeAnalysisMetricData + { + internal MethodMetricData( + IMethodSymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance) + : base(symbol, maintainabilityIndex, computationalComplexityMetrics, coupledNamedTypes, + linesOfCode, cyclomaticComplexity, depthOfInheritance, children: ImmutableArray.Empty) + { + } + + internal static MethodMetricData Compute(IMethodSymbol method, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = method.DeclaringSyntaxReferences; + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, method, context); + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, method, coupledTypesBuilder, context); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, method.Parameters); + if (!method.ReturnsVoid) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, method.ReturnType); + } + + int? depthOfInheritance = null; + int maintainabilityIndex = CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity); + MetricsHelper.RemoveContainingTypes(method, coupledTypesBuilder); + + if (cyclomaticComplexity == 0) + { + // Empty method, such as auto-generated accessor. + cyclomaticComplexity = 1; + } + + return new MethodMetricData(method, maintainabilityIndex, computationalComplexityMetrics, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance); + } + + private static int CalculateMaintainabilityIndex(ComputationalComplexityMetrics computationalComplexityMetrics, int cyclomaticComplexity) + { + double computationalComplexityVolume = Math.Max(0.0, Math.Log(computationalComplexityMetrics.Volume)); //avoid Log(0) = -Infinity + double logEffectiveLinesOfCode = Math.Max(0.0, Math.Log(computationalComplexityMetrics.EffectiveLinesOfCode)); //avoid Log(0) = -Infinity + return MetricsHelper.NormalizeAndRoundMaintainabilityIndex(171 - 5.2 * computationalComplexityVolume - 0.23 * cyclomaticComplexity - 16.2 * logEffectiveLinesOfCode); + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs new file mode 100644 index 0000000000000..a22a642b6eb36 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamedTypeMetricData.cs @@ -0,0 +1,183 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class NamedTypeMetricData : CodeAnalysisMetricData + { + internal NamedTypeMetricData( + INamedTypeSymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + : base(symbol, maintainabilityIndex, computationalComplexityMetrics, + coupledNamedTypes, linesOfCode, cyclomaticComplexity, depthOfInheritance, children) + { + } + + internal static async Task ComputeAsync(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) + { + var members = GetMembers(namedType, context); + + ImmutableArray children = await ComputeAsync(members, context).ConfigureAwait(false); + + return ComputeFromChildren(namedType, children, context); + } + + internal static NamedTypeMetricData ComputeSynchronously(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) + { + var members = GetMembers(namedType, context); + + ImmutableArray children = ComputeSynchronously(members, context); + + return ComputeFromChildren(namedType, children, context); + } + + private static IEnumerable GetMembers(INamedTypeSymbol namedType, CodeMetricsAnalysisContext context) + { + // Compat: Filter out nested types as they are children of most closest containing namespace. + var members = namedType.GetMembers().Where(m => m.Kind != SymbolKind.NamedType); + +#if LEGACY_CODE_METRICS_MODE + // Legacy mode skips metrics for field/property/event symbols, and explicitly includes accessors as methods. + members = members.Where(m => m.Kind is not SymbolKind.Field and not SymbolKind.Property and not SymbolKind.Event); +#else + // Filter out accessors as they are children of their associated symbols, for which we generate a separate node. + members = members.Where(m => m.Kind != SymbolKind.Method || ((IMethodSymbol)m).AssociatedSymbol == null); +#endif + + return members; + } + + private static NamedTypeMetricData ComputeFromChildren(INamedTypeSymbol namedType, ImmutableArray children, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = namedType.DeclaringSyntaxReferences; + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, namedType, coupledTypesBuilder, context); + + // Heuristic to prevent simple fields (no initializer or simple initializer) from skewing the complexity. + ImmutableHashSet filteredFieldsForComplexity = getFilteredFieldsForComplexity(); + + int effectiveChildrenCountForComplexity = 0; + int singleEffectiveChildMaintainabilityIndex = -1; + foreach (CodeAnalysisMetricData child in children) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); + + if (child.Symbol.Kind != SymbolKind.Field || + filteredFieldsForComplexity.Contains((IFieldSymbol)child.Symbol)) + { + singleEffectiveChildMaintainabilityIndex = effectiveChildrenCountForComplexity == 0 && computationalComplexityMetrics.IsDefault ? + child.MaintainabilityIndex : + -1; + effectiveChildrenCountForComplexity++; + cyclomaticComplexity += child.CyclomaticComplexity; + computationalComplexityMetrics = computationalComplexityMetrics.Union(child.ComputationalComplexityMetrics); + } + } + + if (cyclomaticComplexity == 0 && !namedType.IsStatic) + { + // Empty named type, account for implicit constructor. + cyclomaticComplexity = 1; + } + + int depthOfInheritance = CalculateDepthOfInheritance(namedType, context.IsExcludedFromInheritanceCountFunc); + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, namedType, context); + int maintainabilityIndex = singleEffectiveChildMaintainabilityIndex != -1 ? + singleEffectiveChildMaintainabilityIndex : + CalculateMaintainabilityIndex(computationalComplexityMetrics, cyclomaticComplexity, effectiveChildrenCountForComplexity); + MetricsHelper.RemoveContainingTypes(namedType, coupledTypesBuilder); + + return new NamedTypeMetricData(namedType, maintainabilityIndex, computationalComplexityMetrics, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); + + ImmutableHashSet getFilteredFieldsForComplexity() + { + ImmutableHashSet.Builder? builder = null; + var orderedFieldDatas = children.Where(c => c.Symbol.Kind == SymbolKind.Field).OrderBy(c => c.MaintainabilityIndex); + var indexThreshold = 99; + foreach (CodeAnalysisMetricData fieldData in orderedFieldDatas) + { + if (fieldData.MaintainabilityIndex > indexThreshold) + { + break; + } + + builder ??= ImmutableHashSet.CreateBuilder(); + builder.Add((IFieldSymbol)fieldData.Symbol); + indexThreshold -= 4; + } + + return builder?.ToImmutable() ?? ImmutableHashSet.Empty; + } + } + + private static int CalculateDepthOfInheritance(INamedTypeSymbol namedType, Func isExcludedFromInheritanceCount) + { + switch (namedType.TypeKind) + { + case TypeKind.Class: + case TypeKind.Interface: + int depth = 0; + var parent = namedType.BaseType; + while (parent != null && !isExcludedFromInheritanceCount(parent)) + { + depth++; + parent = parent.BaseType; + } + + return depth; + + case TypeKind.Struct: + case TypeKind.Enum: + case TypeKind.Delegate: + // Compat: For structs, enums and delegates, we consider the depth to be 1. + return 1; + + default: + return 0; + } + } + + private static int CalculateMaintainabilityIndex( + ComputationalComplexityMetrics computationalComplexityMetrics, + int cyclomaticComplexity, + int effectiveChildrenCount) + { + double avgComputationalComplexityVolume = 1.0; + double avgEffectiveLinesOfCode = 0.0; + double avgCyclomaticComplexity = 0.0; + + if (effectiveChildrenCount > 0) + { + avgComputationalComplexityVolume = computationalComplexityMetrics.Volume / effectiveChildrenCount; + avgEffectiveLinesOfCode = (double)computationalComplexityMetrics.EffectiveLinesOfCode / effectiveChildrenCount; + avgCyclomaticComplexity = (double)cyclomaticComplexity / effectiveChildrenCount; + } + + double logAvgComputationalComplexityVolume = Math.Max(0.0, Math.Log(avgComputationalComplexityVolume)); //avoid Log(0) = -Infinity + double logAvgLinesOfCode = Math.Max(0.0, Math.Log(avgEffectiveLinesOfCode)); //avoid Log(0) = -Infinity + return MetricsHelper.NormalizeAndRoundMaintainabilityIndex(171 - 5.2 * logAvgComputationalComplexityVolume - 0.23 * avgCyclomaticComplexity - 16.2 * logAvgLinesOfCode); + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs new file mode 100644 index 0000000000000..9a8c292372aef --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.NamespaceMetricData.cs @@ -0,0 +1,105 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class NamespaceMetricData : CodeAnalysisMetricData + { + internal NamespaceMetricData( + INamespaceSymbol symbol, + int maintainabilityIndex, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + : base(symbol, maintainabilityIndex, ComputationalComplexityMetrics.Default, + coupledNamedTypes, linesOfCode, cyclomaticComplexity, depthOfInheritance, children) + { + } + + internal static async Task ComputeAsync(INamespaceSymbol @namespace, CodeMetricsAnalysisContext context) + { + ImmutableArray children = await ComputeAsync(GetChildSymbols(@namespace), context).ConfigureAwait(false); + return ComputeFromChildren(@namespace, children, context); + } + + internal static NamespaceMetricData ComputeSynchronously(INamespaceSymbol @namespace, CodeMetricsAnalysisContext context) + { + ImmutableArray children = ComputeSynchronously(GetChildSymbols(@namespace), context); + return ComputeFromChildren(@namespace, children, context); + } + + private static NamespaceMetricData ComputeFromChildren(INamespaceSymbol @namespace, ImmutableArray children, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + int maintainabilityIndexTotal = 0; + int cyclomaticComplexity = 0; + int depthOfInheritance = 0; + long childrenLinesOfCode = 0; + + foreach (CodeAnalysisMetricData child in children) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); + maintainabilityIndexTotal += child.MaintainabilityIndex; + cyclomaticComplexity += child.CyclomaticComplexity; + depthOfInheritance = Math.Max(child.DepthOfInheritance.GetValueOrDefault(), depthOfInheritance); + + // Avoid double counting lines for nested types. + if (child.Symbol.ContainingType == null) + { + childrenLinesOfCode += child.SourceLines; + } + } + + long linesOfCode = @namespace.IsImplicitlyDeclared ? + childrenLinesOfCode : + MetricsHelper.GetLinesOfCode(@namespace.DeclaringSyntaxReferences, @namespace, context); + int maintainabilityIndex = !children.IsEmpty ? MetricsHelper.GetAverageRoundedMetricValue(maintainabilityIndexTotal, children.Length) : 100; + return new NamespaceMetricData(@namespace, maintainabilityIndex, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); + } + + private static ImmutableArray GetChildSymbols(INamespaceSymbol @namespace) + { + // Compat: Create child nodes for types and nested types within the namespace. + // Child namespaces are directly child nodes of assembly. + var typesInNamespace = new HashSet(); + foreach (INamedTypeSymbol typeMember in @namespace.GetTypeMembers()) + { + processType(typeMember); + } + + var builder = ImmutableArray.CreateBuilder(); + foreach (INamedTypeSymbol namedType in typesInNamespace.OrderBy(t => t.ToDisplayString())) + { + builder.Add(namedType); + } + + return builder.ToImmutable(); + + void processType(INamedTypeSymbol namedType) + { + typesInNamespace.Add(namedType); + foreach (INamedTypeSymbol nestedType in namedType.GetTypeMembers()) + { + processType(nestedType); + } + } + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs new file mode 100644 index 0000000000000..f3d6a570c915c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.PropertyMetricData.cs @@ -0,0 +1,74 @@ +// 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. + +#if HAS_IOPERATION + +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public abstract partial class CodeAnalysisMetricData + { + private sealed class PropertyMetricData : CodeAnalysisMetricData + { + internal PropertyMetricData( + IPropertySymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + : base(symbol, maintainabilityIndex, computationalComplexityMetrics, coupledNamedTypes, + linesOfCode, cyclomaticComplexity, depthOfInheritance, children) + { + } + + internal static PropertyMetricData Compute(IPropertySymbol property, CodeMetricsAnalysisContext context) + { + var coupledTypesBuilder = ImmutableHashSet.CreateBuilder(); + ImmutableArray declarations = property.DeclaringSyntaxReferences; + long linesOfCode = MetricsHelper.GetLinesOfCode(declarations, property, context); + (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) = + MetricsHelper.ComputeCoupledTypesAndComplexityExcludingMemberDecls(declarations, property, coupledTypesBuilder, context); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Parameters); + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, property.Type); + + ImmutableArray children = ComputeSynchronously(GetAccessors(property), context); + int maintainabilityIndexTotal = 0; + foreach (CodeAnalysisMetricData child in children) + { + MetricsHelper.AddCoupledNamedTypes(coupledTypesBuilder, context.WellKnownTypeProvider, child.CoupledNamedTypes); + maintainabilityIndexTotal += child.MaintainabilityIndex; + cyclomaticComplexity += child.CyclomaticComplexity; + computationalComplexityMetrics = computationalComplexityMetrics.Union(child.ComputationalComplexityMetrics); + } + + int? depthOfInheritance = null; + int maintainabilityIndex = !children.IsEmpty ? MetricsHelper.GetAverageRoundedMetricValue(maintainabilityIndexTotal, children.Length) : 100; + MetricsHelper.RemoveContainingTypes(property, coupledTypesBuilder); + + return new PropertyMetricData(property, maintainabilityIndex, computationalComplexityMetrics, + coupledTypesBuilder.ToImmutable(), linesOfCode, cyclomaticComplexity, depthOfInheritance, children); + } + + private static IEnumerable GetAccessors(IPropertySymbol property) + { + if (property.GetMethod != null) + { + yield return property.GetMethod; + } + + if (property.SetMethod != null) + { + yield return property.SetMethod; + } + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs new file mode 100644 index 0000000000000..faabc4e5d936b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeAnalysisMetricData.cs @@ -0,0 +1,342 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +#pragma warning disable CS3001 // Some types from Roslyn are not CLS-Compliant +#pragma warning disable CS3003 // Some types from Roslyn are not CLS-Compliant + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + /// + /// Code analysis metrics data. + /// See https://learn.microsoft.com/visualstudio/code-quality/code-metrics-values for more details + /// + public abstract partial class CodeAnalysisMetricData + { + internal CodeAnalysisMetricData( + ISymbol symbol, + int maintainabilityIndex, + ComputationalComplexityMetrics computationalComplexityMetrics, + ImmutableHashSet coupledNamedTypes, + long linesOfCode, + int cyclomaticComplexity, + int? depthOfInheritance, + ImmutableArray children) + { + Debug.Assert( + symbol.Kind is SymbolKind.Assembly or + SymbolKind.Namespace or + SymbolKind.NamedType or + SymbolKind.Method or + SymbolKind.Field or + SymbolKind.Event or + SymbolKind.Property); + Debug.Assert(depthOfInheritance.HasValue == (symbol.Kind == SymbolKind.Assembly || symbol.Kind == SymbolKind.Namespace || symbol.Kind == SymbolKind.NamedType)); + + var executableLines = !computationalComplexityMetrics.IsDefault ? + computationalComplexityMetrics.ExecutableLines : + children.Sum(c => c.ExecutableLines); + + Symbol = symbol; + MaintainabilityIndex = maintainabilityIndex; + ComputationalComplexityMetrics = computationalComplexityMetrics; + CoupledNamedTypes = coupledNamedTypes; + SourceLines = linesOfCode; + ExecutableLines = executableLines; + CyclomaticComplexity = cyclomaticComplexity; + DepthOfInheritance = depthOfInheritance; + Children = children; + } + + /// + /// Symbol corresponding to the metric data. + /// + public ISymbol Symbol { get; } + + internal ComputationalComplexityMetrics ComputationalComplexityMetrics { get; } + + /// + /// Indicates an index value between 0 and 100 that represents the relative ease of maintaining the code. + /// A high value means better maintainability. + /// + public int MaintainabilityIndex { get; } + + /// + /// Indicates the coupling to unique named types through parameters, local variables, return types, method calls, + /// generic or template instantiations, base classes, interface implementations, fields defined on external types, and attribute decoration. + /// Good software design dictates that types and methods should have high cohesion and low coupling. + /// High coupling indicates a design that is difficult to reuse and maintain because of its many interdependencies on other types. + /// + public ImmutableHashSet CoupledNamedTypes { get; } + + /// + /// Indicates the exact number of lines in source code file. + /// + public long SourceLines { get; } + + /// + /// Indicates the approximate number of executable statements/lines in code. + /// The count is based on the executable s in code and is therefore not the exact number of lines in the source code file. + /// A high count might indicate that a type or method is trying to do too much work and should be split up. + /// It might also indicate that the type or method might be hard to maintain. + /// + public long ExecutableLines { get; } + + /// + /// Measures the structural complexity of the code. + /// It is created by calculating the number of different code paths in the flow of the program. + /// A program that has complex control flow requires more tests to achieve good code coverage and is less maintainable. + /// + public int CyclomaticComplexity { get; } + + /// + /// Indicates the number of different classes that inherit from one another, all the way back to the base class. + /// Depth of Inheritance is similar to class coupling in that a change in a base class can affect any of its inherited classes. + /// The higher this number, the deeper the inheritance and the higher the potential for base class modifications to result in a breaking change. + /// For Depth of Inheritance, a low value is good and a high value is bad. + /// + public int? DepthOfInheritance { get; } + + /// + /// Array of code metrics data for symbolic children of , if any. + /// + public ImmutableArray Children { get; } + + /// + /// Computes string representation of metrics data. + /// + public sealed override string ToString() + { + var builder = new StringBuilder(); + string symbolName; + switch (Symbol.Kind) + { + case SymbolKind.Assembly: + symbolName = "Assembly"; + break; + + case SymbolKind.Namespace: + // Skip explicit display for global namespace. + if (((INamespaceSymbol)Symbol).IsGlobalNamespace) + { + appendChildren(indent: string.Empty); + return builder.ToString(); + } + + symbolName = Symbol.Name; + break; + + case SymbolKind.NamedType: + symbolName = Symbol.ToDisplayString(); + var index = symbolName.LastIndexOf(".", StringComparison.OrdinalIgnoreCase); + if (index >= 0 && index < symbolName.Length) + { + symbolName = symbolName[(index + 1)..]; + } + + break; + + default: + symbolName = Symbol.ToDisplayString(); + break; + } + + builder.Append($"{symbolName}: (Lines: {SourceLines}, ExecutableLines: {ExecutableLines}, MntIndex: {MaintainabilityIndex}, CycCxty: {CyclomaticComplexity}"); + if (!CoupledNamedTypes.IsEmpty) + { + var coupledNamedTypesStr = string.Join(", ", CoupledNamedTypes.Select(t => t.ToDisplayString()).OrderBy(n => n)); + builder.Append($", CoupledTypes: {{{coupledNamedTypesStr}}}"); + } + + if (DepthOfInheritance.HasValue) + { + builder.Append($", DepthInherit: {DepthOfInheritance}"); + } + + builder.Append(')'); + appendChildren(indent: " "); + return builder.ToString(); + + void appendChildren(string indent) + { + foreach (var child in Children) + { + foreach (var line in child.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)) + { + builder.AppendLine(); + builder.Append($"{indent}{line}"); + } + } + } + } + + /// + /// Computes for the given . + /// + [Obsolete("Use ComputeAsync(CodeMetricsAnalysisContext) instead.")] + public static Task ComputeAsync(Compilation compilation, CancellationToken cancellationToken) + { + if (compilation == null) + { + throw new ArgumentNullException(nameof(compilation)); + } + + return ComputeAsync(compilation.Assembly, new CodeMetricsAnalysisContext(compilation, cancellationToken)); + } + + /// + /// Computes for the given . + /// + public static Task ComputeAsync(CodeMetricsAnalysisContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ComputeAsync(context.Compilation.Assembly, context); + } + + /// + /// Computes for the given . + /// + public static CodeAnalysisMetricData ComputeSynchronously(CodeMetricsAnalysisContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + return ComputeSynchronously(context.Compilation.Assembly, context); + } + + /// + /// Computes for the given from the given . + /// + [Obsolete("Use ComputeAsync(ISymbol, CodeMetricsAnalysisContext) instead.")] + public static Task ComputeAsync(ISymbol symbol, Compilation compilation, CancellationToken cancellationToken) + { + if (symbol == null) + { + throw new ArgumentNullException(nameof(symbol)); + } + + if (compilation == null) + { + throw new ArgumentNullException(nameof(compilation)); + } + + return ComputeAsync(symbol, new CodeMetricsAnalysisContext(compilation, cancellationToken)); + } + + /// + /// Computes for the given from the given . + /// + public static Task ComputeAsync(ISymbol symbol, CodeMetricsAnalysisContext context) + { + if (symbol == null) + { + throw new ArgumentNullException(nameof(symbol)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (context.CancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(context.CancellationToken); + } + + return ComputeAsync(symbol, context); + + static async Task ComputeAsync(ISymbol symbol, CodeMetricsAnalysisContext context) + { + return symbol.Kind switch + { + SymbolKind.Assembly => await AssemblyMetricData.ComputeAsync((IAssemblySymbol)symbol, context).ConfigureAwait(false), + + SymbolKind.Namespace => await NamespaceMetricData.ComputeAsync((INamespaceSymbol)symbol, context).ConfigureAwait(false), + + SymbolKind.NamedType => await NamedTypeMetricData.ComputeAsync((INamedTypeSymbol)symbol, context).ConfigureAwait(false), + + SymbolKind.Method => MethodMetricData.Compute((IMethodSymbol)symbol, context), + + SymbolKind.Property => PropertyMetricData.Compute((IPropertySymbol)symbol, context), + + SymbolKind.Field => FieldMetricData.Compute((IFieldSymbol)symbol, context), + + SymbolKind.Event => EventMetricData.Compute((IEventSymbol)symbol, context), + + _ => throw new NotSupportedException(), + }; + } + } + + /// + /// Computes for the given from the given . + /// + public static CodeAnalysisMetricData ComputeSynchronously(ISymbol symbol, CodeMetricsAnalysisContext context) + { + if (symbol == null) + { + throw new ArgumentNullException(nameof(symbol)); + } + + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.CancellationToken.ThrowIfCancellationRequested(); + + return symbol.Kind switch + { + SymbolKind.Assembly => AssemblyMetricData.ComputeSynchronously((IAssemblySymbol)symbol, context), + + SymbolKind.Namespace => NamespaceMetricData.ComputeSynchronously((INamespaceSymbol)symbol, context), + + SymbolKind.NamedType => NamedTypeMetricData.ComputeSynchronously((INamedTypeSymbol)symbol, context), + + SymbolKind.Method => MethodMetricData.Compute((IMethodSymbol)symbol, context), + + SymbolKind.Property => PropertyMetricData.Compute((IPropertySymbol)symbol, context), + + SymbolKind.Field => FieldMetricData.Compute((IFieldSymbol)symbol, context), + + SymbolKind.Event => EventMetricData.Compute((IEventSymbol)symbol, context), + + _ => throw new NotSupportedException(), + }; + } + + internal static async Task> ComputeAsync(IEnumerable children, CodeMetricsAnalysisContext context) + => (await Task.WhenAll( + from child in children +#if !LEGACY_CODE_METRICS_MODE // Skip implicitly declared symbols, such as default constructor, for non-legacy mode. + where !child.IsImplicitlyDeclared || child is INamespaceSymbol { IsGlobalNamespace: true } +#endif + select Task.Run(() => ComputeAsync(child, context))).ConfigureAwait(false)).ToImmutableArray(); + + internal static ImmutableArray ComputeSynchronously(IEnumerable children, CodeMetricsAnalysisContext context) + => (from child in children +#if !LEGACY_CODE_METRICS_MODE // Skip implicitly declared symbols, such as default constructor, for non-legacy mode. + where !child.IsImplicitlyDeclared || child is INamespaceSymbol { IsGlobalNamespace: true } +#endif + select ComputeSynchronously(child, context)).ToImmutableArray(); + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs new file mode 100644 index 0000000000000..768ef65620331 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/CodeMetricsAnalysisContext.cs @@ -0,0 +1,41 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Concurrent; +using System.Threading; +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + public sealed class CodeMetricsAnalysisContext + { + private readonly ConcurrentDictionary _semanticModelMap; + private readonly Func _getSemanticModel; + + public CodeMetricsAnalysisContext(Compilation compilation, CancellationToken cancellationToken, + Func? isExcludedFromInheritanceCountFunc = null) + { + Compilation = compilation; + WellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + CancellationToken = cancellationToken; + _semanticModelMap = new ConcurrentDictionary(); + IsExcludedFromInheritanceCountFunc = isExcludedFromInheritanceCountFunc ?? (x => false); // never excluded by default + + _getSemanticModel = tree => Compilation.GetSemanticModel(tree); + } + + public Compilation Compilation { get; } + public WellKnownTypeProvider WellKnownTypeProvider { get; } + public CancellationToken CancellationToken { get; } + public Func IsExcludedFromInheritanceCountFunc { get; } + + internal SemanticModel GetSemanticModel(SyntaxNode node) + => _semanticModelMap.GetOrAdd(node.SyntaxTree, _getSemanticModel); + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/ComputationalComplexityMetrics.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/ComputationalComplexityMetrics.cs new file mode 100644 index 0000000000000..4a8ab8b1e9240 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/ComputationalComplexityMetrics.cs @@ -0,0 +1,451 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Operations; + +#if LEGACY_CODE_METRICS_MODE +using Analyzer.Utilities.Extensions; +#endif + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + /// + /// Calculates computational complexity metrics based on the number + /// of operators and operands found in the code. + /// + /// This metric is based off of the Halstead metric. + internal sealed class ComputationalComplexityMetrics + { + internal static readonly ComputationalComplexityMetrics Default = new(0, 0, 0, 0, 0, ImmutableHashSet.Empty, + ImmutableHashSet.Empty, ImmutableHashSet.Empty, ImmutableHashSet.Empty, ImmutableHashSet.Empty, ImmutableHashSet.Empty); + private static readonly object s_nullConstantPlaceholder = new(); + private readonly long _symbolUsageCounts; + private readonly long _constantUsageCounts; + private readonly ImmutableHashSet _distinctOperatorKinds; + private readonly ImmutableHashSet _distinctBinaryOperatorKinds; + private readonly ImmutableHashSet _distinctUnaryOperatorKinds; + private readonly ImmutableHashSet _distinctCaseKinds; + private readonly ImmutableHashSet _distinctReferencedSymbols; + private readonly ImmutableHashSet _distinctReferencedConstants; + + private ComputationalComplexityMetrics( + long executableLinesOfCode, + long effectiveLinesOfMaintainableCode, + long operatorUsageCounts, + long symbolUsageCounts, + long constantUsageCounts, + ImmutableHashSet distinctOperatorKinds, + ImmutableHashSet distinctBinaryOperatorKinds, + ImmutableHashSet distinctUnaryOperatorKinds, + ImmutableHashSet distinctCaseKinds, + ImmutableHashSet distinctReferencedSymbols, + ImmutableHashSet distinctReferencedConstants) + { + ExecutableLines = executableLinesOfCode; + EffectiveLinesOfCode = effectiveLinesOfMaintainableCode; + TotalOperators = operatorUsageCounts; + _symbolUsageCounts = symbolUsageCounts; + _constantUsageCounts = constantUsageCounts; + _distinctOperatorKinds = distinctOperatorKinds; + _distinctBinaryOperatorKinds = distinctBinaryOperatorKinds; + _distinctUnaryOperatorKinds = distinctUnaryOperatorKinds; + _distinctCaseKinds = distinctCaseKinds; + _distinctReferencedSymbols = distinctReferencedSymbols; + _distinctReferencedConstants = distinctReferencedConstants; + } + + private static ComputationalComplexityMetrics Create( + long executableLinesOfCode, + long operatorUsageCounts, + long symbolUsageCounts, + long constantUsageCounts, + bool hasSymbolInitializer, + ImmutableHashSet distinctOperatorKinds, + ImmutableHashSet distinctBinaryOperatorKinds, + ImmutableHashSet distinctUnaryOperatorKinds, + ImmutableHashSet distinctCaseKinds, + ImmutableHashSet distinctReferencedSymbols, + ImmutableHashSet distinctReferencedConstants) + { + if (executableLinesOfCode == 0 && operatorUsageCounts == 0 && symbolUsageCounts == 0 && constantUsageCounts == 0 && !hasSymbolInitializer) + { + return Default; + } + + // Use incremented count for maintainable code lines for symbol initializers. + var effectiveLinesOfMaintainableCode = hasSymbolInitializer ? executableLinesOfCode + 1 : executableLinesOfCode; + + return new ComputationalComplexityMetrics(executableLinesOfCode, effectiveLinesOfMaintainableCode, operatorUsageCounts, symbolUsageCounts, constantUsageCounts, + distinctOperatorKinds, distinctBinaryOperatorKinds, distinctUnaryOperatorKinds, distinctCaseKinds, distinctReferencedSymbols, distinctReferencedConstants); + } + + public static ComputationalComplexityMetrics Compute(IOperation operationBlock) + { + bool hasSymbolInitializer = false; + long executableLinesOfCode = 0; + long operatorUsageCounts = 0; + long symbolUsageCounts = 0; + long constantUsageCounts = 0; + ImmutableHashSet.Builder? distinctOperatorKindsBuilder = null; + ImmutableHashSet.Builder? distinctBinaryOperatorKindsBuilder = null; + ImmutableHashSet.Builder? distinctUnaryOperatorKindsBuilder = null; + ImmutableHashSet.Builder? distinctCaseKindsBuilder = null; + ImmutableHashSet.Builder? distinctReferencedSymbolsBuilder = null; + ImmutableHashSet.Builder? distinctReferencedConstantsBuilder = null; + + // Explicit user applied attribute. + if ((operationBlock.Kind is OperationKind.None or OperationKindEx.Attribute) && + hasAnyExplicitExpression(operationBlock)) + { + executableLinesOfCode += 1; + } + + foreach (var operation in operationBlock.Descendants()) + { + executableLinesOfCode += getExecutableLinesOfCode(operation, ref hasSymbolInitializer); + + if (operation.IsImplicit) + { + continue; + } + +#if LEGACY_CODE_METRICS_MODE + // Legacy mode does not account for code within lambdas/local functions for code metrics. + if (operation.IsWithinLambdaOrLocalFunction(out _)) + { + continue; + } +#endif + + if (operation.ConstantValue.HasValue) + { + constantUsageCounts++; + distinctReferencedConstantsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctReferencedConstantsBuilder.Add(operation.ConstantValue.Value ?? s_nullConstantPlaceholder); + continue; + } + + switch (operation.Kind) + { + // Symbol references. + case OperationKind.LocalReference: + countOperand(((ILocalReferenceOperation)operation).Local); + continue; + case OperationKind.ParameterReference: + countOperand(((IParameterReferenceOperation)operation).Parameter); + continue; + case OperationKind.FieldReference: + case OperationKind.MethodReference: + case OperationKind.PropertyReference: + case OperationKind.EventReference: + countOperator(operation); + countOperand(((IMemberReferenceOperation)operation).Member); + continue; + + // Symbol initializers. + case OperationKind.FieldInitializer: + foreach (var field in ((IFieldInitializerOperation)operation).InitializedFields) + { + countOperator(operation); + countOperand(field); + } + + continue; + case OperationKind.PropertyInitializer: + foreach (var property in ((IPropertyInitializerOperation)operation).InitializedProperties) + { + countOperator(operation); + countOperand(property); + } + + continue; + case OperationKind.ParameterInitializer: + countOperator(operation); + countOperand(((IParameterInitializerOperation)operation).Parameter); + continue; + case OperationKind.VariableInitializer: + countOperator(operation); + // We count the operand in the variable declarator. + continue; + case OperationKind.VariableDeclarator: + var variableDeclarator = (IVariableDeclaratorOperation)operation; + if (variableDeclarator.GetVariableInitializer() != null) + { + countOperand(variableDeclarator.Symbol); + } + + continue; + + // Invocations and Object creations. + case OperationKind.Invocation: + countOperator(operation); + var invocation = (IInvocationOperation)operation; + if (!invocation.TargetMethod.ReturnsVoid) + { + countOperand(invocation.TargetMethod); + } + + continue; + case OperationKind.ObjectCreation: + countOperator(operation); + countOperand(((IObjectCreationOperation)operation).Constructor); + continue; + case OperationKind.DelegateCreation: + case OperationKind.AnonymousObjectCreation: + case OperationKind.TypeParameterObjectCreation: + case OperationKind.DynamicObjectCreation: + case OperationKind.DynamicInvocation: + countOperator(operation); + continue; + + // Operators with special operator kinds. + case OperationKind.BinaryOperator: + countBinaryOperator(operation, ((IBinaryOperation)operation).OperatorKind); + continue; + case OperationKind.CompoundAssignment: + countBinaryOperator(operation, ((ICompoundAssignmentOperation)operation).OperatorKind); + continue; + case OperationKind.TupleBinaryOperator: + countBinaryOperator(operation, ((ITupleBinaryOperation)operation).OperatorKind); + continue; + case OperationKind.UnaryOperator: + countUnaryOperator(operation, ((IUnaryOperation)operation).OperatorKind); + continue; + case OperationKind.CaseClause: + var caseClauseOperation = (ICaseClauseOperation)operation; + distinctCaseKindsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctCaseKindsBuilder.Add(caseClauseOperation.CaseKind); + if (caseClauseOperation.CaseKind == CaseKind.Relational) + { + countBinaryOperator(operation, ((IRelationalCaseClauseOperation)operation).Relation); + } + else + { + countOperator(operation); + } + + continue; + + // Other common operators. + case OperationKind.Increment: + case OperationKind.Decrement: + case OperationKind.SimpleAssignment: + case OperationKind.DeconstructionAssignment: + case OperationKind.EventAssignment: + case OperationKind.Coalesce: + case OperationKind.ConditionalAccess: + case OperationKind.Conversion: + case OperationKind.ArrayElementReference: + case OperationKind.Await: + case OperationKind.NameOf: + case OperationKind.SizeOf: + case OperationKind.TypeOf: + case OperationKind.AddressOf: + case OperationKind.MemberInitializer: + case OperationKind.IsType: + case OperationKind.IsPattern: + case OperationKind.Parenthesized: + countOperator(operation); + continue; + + // Following are considered operators for now, but we may want to revisit. + case OperationKind.ArrayCreation: + case OperationKind.ArrayInitializer: + case OperationKind.DynamicMemberReference: + case OperationKind.DynamicIndexerAccess: + case OperationKind.Tuple: + case OperationKind.Lock: + case OperationKind.Using: + case OperationKind.Throw: + case OperationKind.RaiseEvent: + case OperationKind.InterpolatedString: + countOperator(operation); + continue; + + // Return value. + case OperationKind.Return: + case OperationKind.YieldBreak: + case OperationKind.YieldReturn: + if (((IReturnOperation)operation).ReturnedValue != null) + { + countOperator(operation); + } + + continue; + } + } + + return Create( + executableLinesOfCode, + operatorUsageCounts, + symbolUsageCounts, + constantUsageCounts, + hasSymbolInitializer, + distinctOperatorKindsBuilder != null ? distinctOperatorKindsBuilder.ToImmutable() : ImmutableHashSet.Empty, + distinctBinaryOperatorKindsBuilder != null ? distinctBinaryOperatorKindsBuilder.ToImmutable() : ImmutableHashSet.Empty, + distinctUnaryOperatorKindsBuilder != null ? distinctUnaryOperatorKindsBuilder.ToImmutable() : ImmutableHashSet.Empty, + distinctCaseKindsBuilder != null ? distinctCaseKindsBuilder.ToImmutable() : ImmutableHashSet.Empty, + distinctReferencedSymbolsBuilder != null ? distinctReferencedSymbolsBuilder.ToImmutable() : ImmutableHashSet.Empty, + distinctReferencedConstantsBuilder != null ? distinctReferencedConstantsBuilder.ToImmutable() : ImmutableHashSet.Empty); + + static int getExecutableLinesOfCode(IOperation operation, ref bool hasSymbolInitializer) + { + if (operation.Parent != null) + { + switch (operation.Parent.Kind) + { + case OperationKind.Block: + return hasAnyExplicitExpression(operation) ? 1 : 0; + + case OperationKind.FieldInitializer: + case OperationKind.PropertyInitializer: + case OperationKind.ParameterInitializer: + if (hasAnyExplicitExpression(operation)) + { + hasSymbolInitializer = true; + return 1; + } + + break; + + case OperationKind.Conditional: + // Nested conditional + return operation.Kind == OperationKind.Conditional && hasAnyExplicitExpression(operation) ? 1 : 0; + } + } + + return 0; + } + + static bool hasAnyExplicitExpression(IOperation operation) + { + // Check if all descendants are either implicit or are explicit non-branch, non-attribute operations with no constant value or type, indicating it is not user written code. + return !operation.DescendantsAndSelf().All(o => o.IsImplicit || (!o.ConstantValue.HasValue && o.Type == null && o.Kind is not (OperationKind.Branch or OperationKindEx.Attribute))); + } + + void countOperator(IOperation operation) + { + operatorUsageCounts++; + distinctOperatorKindsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctOperatorKindsBuilder.Add(operation.Kind); + } + + void countOperand(ISymbol? symbol) + { + if (symbol is null) + return; + + symbolUsageCounts++; + distinctReferencedSymbolsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctReferencedSymbolsBuilder.Add(symbol); + } + + void countBinaryOperator(IOperation operation, BinaryOperatorKind operatorKind) + { + countOperator(operation); + distinctBinaryOperatorKindsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctBinaryOperatorKindsBuilder.Add(operatorKind); + } + + void countUnaryOperator(IOperation operation, UnaryOperatorKind operatorKind) + { + countOperator(operation); + distinctUnaryOperatorKindsBuilder ??= ImmutableHashSet.CreateBuilder(); + distinctUnaryOperatorKindsBuilder.Add(operatorKind); + } + } + + public ComputationalComplexityMetrics Union(ComputationalComplexityMetrics other) + { + if (ReferenceEquals(this, Default)) + { + return other; + } + else if (ReferenceEquals(other, Default)) + { + return this; + } + + return new ComputationalComplexityMetrics( + executableLinesOfCode: ExecutableLines + other.ExecutableLines, + effectiveLinesOfMaintainableCode: EffectiveLinesOfCode + other.EffectiveLinesOfCode, + operatorUsageCounts: TotalOperators + other.TotalOperators, + symbolUsageCounts: _symbolUsageCounts + other._symbolUsageCounts, + constantUsageCounts: _constantUsageCounts + other._constantUsageCounts, + distinctOperatorKinds: _distinctOperatorKinds.Union(other._distinctOperatorKinds), + distinctBinaryOperatorKinds: _distinctBinaryOperatorKinds.Union(other._distinctBinaryOperatorKinds), + distinctUnaryOperatorKinds: _distinctUnaryOperatorKinds.Union(other._distinctUnaryOperatorKinds), + distinctCaseKinds: _distinctCaseKinds.Union(other._distinctCaseKinds), + distinctReferencedSymbols: _distinctReferencedSymbols.Union(other._distinctReferencedSymbols), + distinctReferencedConstants: _distinctReferencedConstants.Union(other._distinctReferencedConstants)); + } + + public bool IsDefault => ReferenceEquals(this, Default); + + /// The number of unique operators found. + public long DistinctOperators //n1 + { + get + { + var count = _distinctBinaryOperatorKinds.Count; + if (_distinctBinaryOperatorKinds.Count > 1) + { + count += _distinctBinaryOperatorKinds.Count - 1; + } + + if (_distinctUnaryOperatorKinds.Count > 1) + { + count += _distinctUnaryOperatorKinds.Count - 1; + } + + if (_distinctCaseKinds.Count > 1) + { + count += _distinctCaseKinds.Count - 1; + } + + return count; + } + } + + /// The number of unique operands found. + public long DistinctOperands //n2 + => _distinctReferencedSymbols.Count + _distinctReferencedConstants.Count; + + /// The total number of operator usages found. + public long TotalOperators //N1 + { get; } + + /// The total number of operand usages found. + public long TotalOperands //N2 + => _symbolUsageCounts + _constantUsageCounts; + + public long Vocabulary // n = n1 + n2 + => DistinctOperators + DistinctOperands; + + public long Length // N = N1 + N2 + => TotalOperators + TotalOperands; + + public double Volume // V = N * Log2(n) + => Length * Math.Max(0.0, Math.Log(Vocabulary, 2)); + + /// + /// Count of executable lines of code, i.e. basically IOperations parented by IBlockOperation. + /// + public long ExecutableLines { get; } + + /// + /// Count of effective lines of code for computation of maintainability index. + /// + public long EffectiveLinesOfCode { get; } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/MetricsHelper.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/MetricsHelper.cs new file mode 100644 index 0000000000000..fc5cb547da1f3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/MetricsHelper.cs @@ -0,0 +1,424 @@ +// 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 warnings + +#if HAS_IOPERATION + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Lightup; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Operations; + +#if LEGACY_CODE_METRICS_MODE +using Analyzer.Utilities.Extensions; +#endif + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + internal static class MetricsHelper + { + internal static int GetAverageRoundedMetricValue(int total, int childrenCount) + { + Debug.Assert(childrenCount != 0); + return RoundMetricValue((double)total / childrenCount); + } + + private static int RoundMetricValue(double value) => (int)Math.Round(value, 0); + + internal static int NormalizeAndRoundMaintainabilityIndex(double maintIndex) + { + maintIndex = Math.Max(0.0, maintIndex); + return RoundMetricValue(maintIndex / 171.0 * 100.0); + } + + internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, + ImmutableHashSet coupledTypes) + { + foreach (var coupledType in coupledTypes) + { + AddCoupledNamedTypesCore(builder, coupledType, wellKnownTypeProvider); + } + } + + internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, + ITypeSymbol coupledType) + { + AddCoupledNamedTypesCore(builder, coupledType, wellKnownTypeProvider); + } + + internal static void AddCoupledNamedTypes(ImmutableHashSet.Builder builder, WellKnownTypeProvider wellKnownTypeProvider, + ImmutableArray parameters) + { + foreach (var parameter in parameters) + { + AddCoupledNamedTypesCore(builder, parameter.Type, wellKnownTypeProvider); + } + } + + internal static long GetLinesOfCode(ImmutableArray declarations, ISymbol symbol, CodeMetricsAnalysisContext context) + { + long linesOfCode = 0; + foreach (var decl in declarations) + { + SyntaxNode declSyntax = GetTopmostSyntaxNodeForDeclaration(decl, symbol, context); + + // For namespace symbols, don't count lines of code for declarations of child namespaces. + // For example, "namespace N1.N2 { }" is a declaration reference for N1, but the actual declaration is for N2. + if (symbol.Kind == SymbolKind.Namespace) + { + var model = context.GetSemanticModel(declSyntax); + if (!Equals(model.GetDeclaredSymbol(declSyntax, context.CancellationToken), symbol)) + { + continue; + } + } + + FileLinePositionSpan linePosition = declSyntax.SyntaxTree.GetLineSpan(declSyntax.FullSpan, context.CancellationToken); + long delta = linePosition.EndLinePosition.Line - linePosition.StartLinePosition.Line; + if (delta == 0) + { + // Declaration on a single line, we count it as a separate line. + delta = 1; + } + else + { + // Ensure that we do not count the leading and trailing empty new lines. + var additionalNewLines = Math.Max(0, GetNewlineCount(declSyntax.GetLeadingTrivia(), leading: true) + GetNewlineCount(declSyntax.GetTrailingTrivia(), leading: false) - 1); + delta -= additionalNewLines; + } + + linesOfCode += delta; + } + + return linesOfCode; + + static int GetNewlineCount(SyntaxTriviaList trivialList, bool leading) + { + var fullTrivia = trivialList.ToFullString(); + ReadOnlySpan remainingTrivia = fullTrivia.AsSpan(); + + return GetNewlineCount(remainingTrivia, leading); + + static bool TryTakeNextLine(ref ReadOnlySpan remaining, out ReadOnlySpan next, bool leading) + { + if (remaining.IsEmpty) + { + next = ReadOnlySpan.Empty; + return false; + } + + if (leading) + { + var index = remaining.IndexOfAny('\r', '\n'); + if (index < 0) + { + next = remaining; + remaining = ReadOnlySpan.Empty; + return false; + } + + next = remaining[..index]; + if (remaining[index] == '\r' && remaining.Length > index + 1 && remaining[index + 1] == '\n') + { + remaining = remaining[(index + 2)..]; + } + else + { + remaining = remaining[(index + 1)..]; + } + + return true; + } + else + { + var index = remaining.LastIndexOfAny('\r', '\n'); + if (index < 0) + { + next = remaining; + remaining = ReadOnlySpan.Empty; + return false; + } + + next = remaining[(index + 1)..]; + if (remaining[index] == '\n' && index > 0 && remaining[index - 1] == '\r') + { + remaining = remaining[..(index - 1)]; + } + else + { + remaining = remaining[..index]; + } + + return true; + } + } + + static int GetNewlineCount(ReadOnlySpan trivia, bool leading) + { + var count = 0; + while (TryTakeNextLine(ref trivia, out var next, leading)) + { + if (!next.IsWhiteSpace()) + break; + + count++; + } + + return count; + } + } + } + + internal static SyntaxNode GetTopmostSyntaxNodeForDeclaration(SyntaxReference declaration, ISymbol declaredSymbol, CodeMetricsAnalysisContext context) + { + var declSyntax = declaration.GetSyntax(context.CancellationToken); + if (declSyntax.Language == LanguageNames.VisualBasic) + { + SemanticModel model = context.GetSemanticModel(declSyntax); + while (declSyntax.Parent != null && Equals(model.GetDeclaredSymbol(declSyntax.Parent, context.CancellationToken), declaredSymbol)) + { + declSyntax = declSyntax.Parent; + } + } + + return declSyntax; + } + + internal static (int cyclomaticComplexity, ComputationalComplexityMetrics computationalComplexityMetrics) ComputeCoupledTypesAndComplexityExcludingMemberDecls( + ImmutableArray declarations, + ISymbol symbol, + ImmutableHashSet.Builder builder, + CodeMetricsAnalysisContext context) + { + int cyclomaticComplexity = 0; + ComputationalComplexityMetrics computationalComplexityMetrics = ComputationalComplexityMetrics.Default; + + var nodesToProcess = new Queue(); + using var applicableAttributeNodes = PooledHashSet.GetInstance(); + + foreach (var declaration in declarations) + { + SyntaxNode syntax = GetTopmostSyntaxNodeForDeclaration(declaration, symbol, context); + nodesToProcess.Enqueue(syntax); + + // Ensure we process parameter initializers and attributes. + var parameters = GetParameters(symbol); + foreach (var parameter in parameters) + { + var parameterSyntaxRef = parameter.DeclaringSyntaxReferences.FirstOrDefault(); + if (parameterSyntaxRef != null) + { + var parameterSyntax = parameterSyntaxRef.GetSyntax(context.CancellationToken); + nodesToProcess.Enqueue(parameterSyntax); + } + } + + var attributes = symbol.GetAttributes(); + if (symbol is IMethodSymbol methodSymbol) + { + attributes = attributes.AddRange(methodSymbol.GetReturnTypeAttributes()); + } + + foreach (var attribute in attributes) + { + if (attribute.ApplicationSyntaxReference != null && + attribute.ApplicationSyntaxReference.SyntaxTree == declaration.SyntaxTree) + { + var attributeSyntax = attribute.ApplicationSyntaxReference.GetSyntax(context.CancellationToken); + if (applicableAttributeNodes.Add(attributeSyntax)) + { + nodesToProcess.Enqueue(attributeSyntax); + } + } + } + + do + { + var node = nodesToProcess.Dequeue(); + var model = context.GetSemanticModel(node); + + if (!ReferenceEquals(node, syntax)) + { + var declaredSymbol = model.GetDeclaredSymbol(node, context.CancellationToken); + if (declaredSymbol != null && !Equals(symbol, declaredSymbol) && declaredSymbol.Kind != SymbolKind.Parameter) + { + // Skip member declarations. + continue; + } + } + + var typeInfo = model.GetTypeInfo(node, context.CancellationToken); + AddCoupledNamedTypesCore(builder, typeInfo.Type, context.WellKnownTypeProvider); + + var operationBlock = model.GetOperation(node, context.CancellationToken); + if (operationBlock != null && operationBlock.Parent == null) + { + switch (operationBlock.Kind) + { + case OperationKind.Block: + case OperationKind.MethodBodyOperation: + case OperationKind.ConstructorBodyOperation: + cyclomaticComplexity += 1; + break; + + case OperationKindEx.Attribute: + case OperationKind.None: + // Skip non-applicable attributes. + if (!applicableAttributeNodes.Contains(node)) + { + continue; + } + + break; + } + + computationalComplexityMetrics = computationalComplexityMetrics.Union(ComputationalComplexityMetrics.Compute(operationBlock)); + + // Add used types within executable code in the operation tree. + foreach (var operation in operationBlock.DescendantsAndSelf()) + { +#if LEGACY_CODE_METRICS_MODE + // Legacy mode does not account for code within lambdas/local functions for code metrics. + if (operation.IsWithinLambdaOrLocalFunction(out _)) + { + continue; + } +#endif + + if (!operation.IsImplicit && hasConditionalLogic(operation)) + { + cyclomaticComplexity += 1; + } + + AddCoupledNamedTypesCore(builder, operation.Type, context.WellKnownTypeProvider); + + // Handle static member accesses specially as there is no operation for static type off which the member is accessed. + if (operation is IMemberReferenceOperation memberReference && + memberReference.Member.IsStatic) + { + AddCoupledNamedTypesCore(builder, memberReference.Member.ContainingType, context.WellKnownTypeProvider); + } + else if (operation is IInvocationOperation invocation && + (invocation.TargetMethod.IsStatic || invocation.TargetMethod.IsExtensionMethod)) + { + AddCoupledNamedTypesCore(builder, invocation.TargetMethod.ContainingType, context.WellKnownTypeProvider); + } + } + } + else + { + // Enqueue child nodes for further processing. + foreach (var child in node.ChildNodes()) + { + nodesToProcess.Enqueue(child); + } + } + } while (nodesToProcess.Count != 0); + } + + return (cyclomaticComplexity, computationalComplexityMetrics); + static bool hasConditionalLogic(IOperation operation) + { + switch (operation.Kind) + { + case OperationKind.CaseClause: + case OperationKind.Coalesce: + case OperationKind.Conditional: + case OperationKind.ConditionalAccess: + case OperationKind.Loop: + return true; + + case OperationKind.BinaryOperator: + var binaryOperation = (IBinaryOperation)operation; + return binaryOperation.OperatorKind == BinaryOperatorKind.ConditionalAnd || + binaryOperation.OperatorKind == BinaryOperatorKind.ConditionalOr || + (binaryOperation.Type.SpecialType == SpecialType.System_Boolean && + (binaryOperation.OperatorKind == BinaryOperatorKind.Or || binaryOperation.OperatorKind == BinaryOperatorKind.And)); + + default: + return false; + } + } + } + + private static void AddCoupledNamedTypesCore(ImmutableHashSet.Builder builder, ITypeSymbol typeOpt, + WellKnownTypeProvider wellKnownTypeProvider) + { + if (typeOpt is INamedTypeSymbol usedType && + !isIgnoreableType(usedType, wellKnownTypeProvider)) + { + // Save the OriginalDefinition of the type as IEnumerable and IEnumerable + // should register only one IEnumerable... + builder.Add(usedType.OriginalDefinition); + + // ... but always parse the generic type arguments as IEnumerable and IEnumerable + // should register int and float. + if (usedType.IsGenericType) + { + foreach (var type in usedType.TypeArguments) + { + AddCoupledNamedTypesCore(builder, type, wellKnownTypeProvider); + } + } + } + + static bool isIgnoreableType(INamedTypeSymbol namedType, WellKnownTypeProvider wellKnownTypeProvider) + { + return namedType.SpecialType switch + { + SpecialType.System_Boolean + or SpecialType.System_Byte + or SpecialType.System_Char + or SpecialType.System_Double + or SpecialType.System_Int16 + or SpecialType.System_Int32 + or SpecialType.System_Int64 + or SpecialType.System_UInt16 + or SpecialType.System_UInt32 + or SpecialType.System_UInt64 + or SpecialType.System_IntPtr + or SpecialType.System_UIntPtr + or SpecialType.System_SByte + or SpecialType.System_Single + or SpecialType.System_String + or SpecialType.System_Object + or SpecialType.System_ValueType + or SpecialType.System_Void => true, + _ => namedType.IsAnonymousType + || namedType.GetAttributes().Any(static (a, wellKnownTypeProvider) => + a.AttributeClass.Equals(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesCompilerGeneratedAttribute)) || + a.AttributeClass.Equals(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCodeDomCompilerGeneratedCodeAttribute)), wellKnownTypeProvider), + }; + } + } + + internal static void RemoveContainingTypes(ISymbol symbol, ImmutableHashSet.Builder coupledTypesBuilder) + { + var namedType = symbol as INamedTypeSymbol ?? symbol.ContainingType; + while (namedType != null) + { + coupledTypesBuilder.Remove(namedType); + namedType = namedType.ContainingType; + } + } + + internal static ImmutableArray GetParameters(this ISymbol member) + { + return member.Kind switch + { + SymbolKind.Method => ((IMethodSymbol)member).Parameters, + SymbolKind.Property => ((IPropertySymbol)member).Parameters, + _ => ImmutableArray.Empty, + }; + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/SemanticModelProvider.cs b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/SemanticModelProvider.cs new file mode 100644 index 0000000000000..8534e5af2c892 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/CodeMetrics/SemanticModelProvider.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +#if HAS_IOPERATION + +using System.Collections.Concurrent; + +namespace Microsoft.CodeAnalysis.CodeMetrics +{ + internal sealed class SemanticModelProvider + { + private readonly ConcurrentDictionary _semanticModelMap; + public SemanticModelProvider(Compilation compilation) + { + Compilation = compilation; + _semanticModelMap = new ConcurrentDictionary(); + } + + public Compilation Compilation { get; } + + public SemanticModel GetSemanticModel(SyntaxNode node) + => _semanticModelMap.GetOrAdd(node.SyntaxTree, tree => Compilation.GetSemanticModel(node.SyntaxTree)); + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Debug.cs b/src/RoslynAnalyzers/Utilities/Compiler/Debug.cs new file mode 100644 index 0000000000000..939e225c69447 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Debug.cs @@ -0,0 +1,21 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static class RoslynDebug + { + /// + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool b) => Debug.Assert(b); + + /// + [Conditional("DEBUG")] + public static void Assert([DoesNotReturnIf(false)] bool b, string message) + => Debug.Assert(b, message); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategory.cs b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategory.cs new file mode 100644 index 0000000000000..7824198f45318 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategory.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities +{ + internal static class DiagnosticCategory + { + public const string Design = nameof(Design); + public const string Globalization = nameof(Globalization); + public const string Interoperability = nameof(Interoperability); + public const string Mobility = nameof(Mobility); + public const string Performance = nameof(Performance); + public const string Reliability = nameof(Reliability); + public const string Security = nameof(Security); + public const string Usage = nameof(Usage); + public const string Naming = nameof(Naming); + public const string Library = nameof(Library); + public const string Documentation = nameof(Documentation); + public const string Maintainability = nameof(Maintainability); + + public const string RoslynDiagnosticsDesign = nameof(RoslynDiagnosticsDesign); + public const string RoslynDiagnosticsMaintainability = nameof(RoslynDiagnosticsMaintainability); + public const string RoslynDiagnosticsPerformance = nameof(RoslynDiagnosticsPerformance); + public const string RoslynDiagnosticsReliability = nameof(RoslynDiagnosticsReliability); + public const string RoslynDiagnosticsUsage = nameof(RoslynDiagnosticsUsage); + + public const string MicrosoftCodeAnalysisCorrectness = nameof(MicrosoftCodeAnalysisCorrectness); + public const string MicrosoftCodeAnalysisDesign = nameof(MicrosoftCodeAnalysisDesign); + public const string MicrosoftCodeAnalysisDocumentation = nameof(MicrosoftCodeAnalysisDocumentation); + public const string MicrosoftCodeAnalysisLocalization = nameof(MicrosoftCodeAnalysisLocalization); + public const string MicrosoftCodeAnalysisPerformance = nameof(MicrosoftCodeAnalysisPerformance); + public const string MicrosoftCodeAnalysisCompatibility = nameof(MicrosoftCodeAnalysisCompatibility); + public const string MicrosoftCodeAnalysisReleaseTracking = nameof(MicrosoftCodeAnalysisReleaseTracking); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt new file mode 100644 index 0000000000000..7f78652b66d83 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -0,0 +1,38 @@ +# This file contains the allowed analyzer rule "Category" and corresponding "Diagnostic ID range" +# FORMAT: +# 'Category': Comma separate list of 'StartId-EndId' or 'Id' or 'Prefix' + +# Code Analysis (CA) rules - https://learn.microsoft.com/visualstudio/code-quality/code-analysis-for-managed-code-warnings +# +# The ranges below represent the **currently used IDs** for the corresponding category. +# When implementing a new rule for the category: +# 1. Choose the rule ID immediately following the range end. +# 2. Update the range end to the chosen rule ID. +# +Design: CA2210, CA1000-CA1070 +Globalization: CA2101, CA1300-CA1311 +Mobility: CA1600-CA1601 +Performance: HA, CA1800-CA1873 +Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 +Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2265 +Naming: CA1700-CA1727 +Interoperability: CA1400-CA1422 +Maintainability: CA1500-CA1515 +Reliability: CA9998-CA9999, CA2000-CA2024 +Documentation: CA1200-CA1200 + +# Microsoft CodeAnalysis API rules +MicrosoftCodeAnalysisCorrectness: RS1000-RS1999 +MicrosoftCodeAnalysisDesign: RS1000-RS1999 +MicrosoftCodeAnalysisDocumentation: RS1000-RS1999 +MicrosoftCodeAnalysisLocalization: RS1000-RS1999 +MicrosoftCodeAnalysisPerformance: RS1000-RS1999 +MicrosoftCodeAnalysisCompatibility: RS1000-RS1999 +MicrosoftCodeAnalysisReleaseTracking: RS2000-RS2100 + +# Roslyn specific rules +RoslynDiagnosticsDesign: RS0000-RS0999 +RoslynDiagnosticsMaintainability: RS0000-RS0999 +RoslynDiagnosticsPerformance: RS0000-RS0999 +RoslynDiagnosticsReliability: RS0000-RS0999 +RoslynDiagnosticsUsage: RS0000-RS0999 \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticDescriptorHelper.cs b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticDescriptorHelper.cs new file mode 100644 index 0000000000000..47c012f7c2bd5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticDescriptorHelper.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. + +#if NET_ANALYZERS || TEXT_ANALYZERS + +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static partial class DiagnosticDescriptorHelper + { + public static DiagnosticDescriptor Create( + string id, + LocalizableString title, + LocalizableString messageFormat, + string category, + RuleLevel ruleLevel, + LocalizableString? description, + bool isPortedFxCopRule, + bool isDataflowRule, + bool isEnabledByDefaultInAggressiveMode = true, + bool isReportedAtCompilationEnd = false, + params string[] additionalCustomTags) + { + // PERF: Ensure that all DFA rules are disabled by default in NetAnalyzers package. + Debug.Assert(!isDataflowRule || ruleLevel == RuleLevel.Disabled || ruleLevel == RuleLevel.CandidateForRemoval); + + // Ensure 'isEnabledByDefaultInAggressiveMode' is not false for enabled rules in default mode + Debug.Assert(isEnabledByDefaultInAggressiveMode || ruleLevel == RuleLevel.Disabled || ruleLevel == RuleLevel.CandidateForRemoval); + + var (defaultSeverity, enabledByDefault) = GetDefaultSeverityAndEnabledByDefault(ruleLevel); + +#pragma warning disable CA1308 // Normalize strings to uppercase - use lower case ID in help link + var helpLink = $"https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/{id.ToLowerInvariant()}"; +#pragma warning restore CA1308 // Normalize strings to uppercase + + var customTags = GetDefaultCustomTags(isPortedFxCopRule, isDataflowRule, isEnabledByDefaultInAggressiveMode); + if (isReportedAtCompilationEnd) + { + customTags = customTags.Concat(WellKnownDiagnosticTagsExtensions.CompilationEnd).ToArray(); + } + + if (additionalCustomTags.Length > 0) + { + customTags = customTags.Concat(additionalCustomTags).ToArray(); + } + +#pragma warning disable RS0030 // The symbol 'DiagnosticDescriptor.DiagnosticDescriptor.#ctor' is banned in this project: Use 'DiagnosticDescriptorHelper.Create' instead + return new DiagnosticDescriptor(id, title, messageFormat, category, defaultSeverity, enabledByDefault, description, helpLink, customTags); +#pragma warning restore RS0030 + + static (DiagnosticSeverity defaultSeverity, bool enabledByDefault) GetDefaultSeverityAndEnabledByDefault(RuleLevel ruleLevel) + { + return ruleLevel switch + { + RuleLevel.BuildWarning => (DiagnosticSeverity.Warning, true), + RuleLevel.IdeSuggestion => (DiagnosticSeverity.Info, true), + RuleLevel.IdeHidden_BulkConfigurable => (DiagnosticSeverity.Hidden, true), + RuleLevel.Disabled => (DiagnosticSeverity.Warning, false), + RuleLevel.CandidateForRemoval => (DiagnosticSeverity.Warning, false), + RuleLevel.BuildError => (DiagnosticSeverity.Error, true), + _ => throw new System.NotImplementedException(), + }; + } + + static string[] GetDefaultCustomTags( + bool isPortedFxCopRule, + bool isDataflowRule, + bool isEnabledByDefaultInAggressiveMode) + { + if (isEnabledByDefaultInAggressiveMode) + { + return isPortedFxCopRule ? + (isDataflowRule ? FxCopWellKnownDiagnosticTags.PortedFxCopDataflowRuleEnabledInAggressiveMode : FxCopWellKnownDiagnosticTags.PortedFxCopRuleEnabledInAggressiveMode) : + (isDataflowRule ? WellKnownDiagnosticTagsExtensions.DataflowAndTelemetryEnabledInAggressiveMode : WellKnownDiagnosticTagsExtensions.TelemetryEnabledInAggressiveMode); + } + else + { + return isPortedFxCopRule ? + (isDataflowRule ? FxCopWellKnownDiagnosticTags.PortedFxCopDataflowRule : FxCopWellKnownDiagnosticTags.PortedFxCopRule) : + (isDataflowRule ? WellKnownDiagnosticTagsExtensions.DataflowAndTelemetry : WellKnownDiagnosticTagsExtensions.Telemetry); + } + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticHelpers.cs b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticHelpers.cs new file mode 100644 index 0000000000000..a78505998a488 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DiagnosticHelpers.cs @@ -0,0 +1,78 @@ +// 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; + +namespace Analyzer.Utilities +{ + internal static partial class DiagnosticHelpers + { + public static bool TryConvertToUInt64(object? value, SpecialType specialType, out ulong convertedValue) + { + bool success = false; + convertedValue = 0; + if (value != null) + { + switch (specialType) + { + case SpecialType.System_Int16: + convertedValue = unchecked((ulong)(short)value); + success = true; + break; + case SpecialType.System_Int32: + convertedValue = unchecked((ulong)(int)value); + success = true; + break; + case SpecialType.System_Int64: + convertedValue = unchecked((ulong)(long)value); + success = true; + break; + case SpecialType.System_UInt16: + convertedValue = (ushort)value; + success = true; + break; + case SpecialType.System_UInt32: + convertedValue = (uint)value; + success = true; + break; + case SpecialType.System_UInt64: + convertedValue = (ulong)value; + success = true; + break; + case SpecialType.System_Byte: + convertedValue = (byte)value; + success = true; + break; + case SpecialType.System_SByte: + convertedValue = unchecked((ulong)(sbyte)value); + success = true; + break; + case SpecialType.System_Char: + convertedValue = (char)value; + success = true; + break; + case SpecialType.System_Boolean: + convertedValue = (ulong)((bool)value ? 1 : 0); + success = true; + break; + } + } + + return success; + } + + public static string GetMemberName(ISymbol symbol) + { + // For Types + if (symbol is INamedTypeSymbol namedType && + namedType.IsGenericType) + { + return symbol.MetadataName; + } + + // For other language constructs + return symbol.Name; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DisposeMethodKind.cs b/src/RoslynAnalyzers/Utilities/Compiler/DisposeMethodKind.cs new file mode 100644 index 0000000000000..24e7c84d2eee2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DisposeMethodKind.cs @@ -0,0 +1,49 @@ +// 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 Analyzer.Utilities +{ + /// + /// Describes different kinds of Dispose-like methods. + /// + internal enum DisposeMethodKind + { + /// + /// Not a dispose-like method. + /// + None, + + /// + /// An override of . + /// + Dispose, + + /// + /// A virtual method named Dispose that takes a single Boolean parameter, as + /// is used when implementing the standard Dispose pattern. + /// + DisposeBool, + + /// + /// A method named DisposeAsync that has no parameters and returns Task. + /// + DisposeAsync, + + /// + /// An overridden method named DisposeCoreAsync that takes a single Boolean parameter and returns Task, as + /// is used when implementing the standard DisposeAsync pattern. + /// + DisposeCoreAsync, + + /// + /// A method named Close on a type that implements . + /// + Close, + + /// + /// A method named CloseAsync that has no parameters and returns Task. + /// + CloseAsync, + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/DoNotCatchGeneralUnlessRethrown.cs b/src/RoslynAnalyzers/Utilities/Compiler/DoNotCatchGeneralUnlessRethrown.cs new file mode 100644 index 0000000000000..ec3b35faccf66 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/DoNotCatchGeneralUnlessRethrown.cs @@ -0,0 +1,176 @@ +// 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. + +#if HAS_IOPERATION && CODEANALYSIS_V3_OR_BETTER + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities +{ + internal abstract class DoNotCatchGeneralUnlessRethrownAnalyzer : DiagnosticAnalyzer + { + private readonly bool _shouldCheckLambdas; + private readonly string? _enablingMethodAttributeFullyQualifiedName; + private readonly bool _allowExcludedSymbolNames; + + private bool RequiresAttributeOnMethod => !string.IsNullOrEmpty(_enablingMethodAttributeFullyQualifiedName); + + protected DoNotCatchGeneralUnlessRethrownAnalyzer(bool shouldCheckLambdas, string? enablingMethodAttributeFullyQualifiedName = null, + bool allowExcludedSymbolNames = false) + { + _shouldCheckLambdas = shouldCheckLambdas; + _enablingMethodAttributeFullyQualifiedName = enablingMethodAttributeFullyQualifiedName; + _allowExcludedSymbolNames = allowExcludedSymbolNames; + } + + protected abstract Diagnostic CreateDiagnostic(IMethodSymbol containingMethod, SyntaxToken catchKeyword); + protected virtual bool IsConfiguredDisallowedExceptionType(INamedTypeSymbol namedTypeSymbol, IMethodSymbol containingMethod, Compilation compilation, AnalyzerOptions analyzerOptions, CancellationToken cancellationToken) + { + return false; + } + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze); + + context.RegisterCompilationStartAction(compilationStartAnalysisContext => + { + INamedTypeSymbol? requiredAttributeType = null; + if (RequiresAttributeOnMethod && (requiredAttributeType = GetRequiredAttributeType(compilationStartAnalysisContext.Compilation)) == null) + { + return; + } + + var disallowedCatchTypes = GetDisallowedCatchTypes(compilationStartAnalysisContext.Compilation); + + compilationStartAnalysisContext.RegisterOperationBlockAction(operationBlockAnalysisContext => + { + if (operationBlockAnalysisContext.OwningSymbol.Kind != SymbolKind.Method) + { + return; + } + + var method = (IMethodSymbol)operationBlockAnalysisContext.OwningSymbol; + + if (RequiresAttributeOnMethod && !method.HasAnyAttribute(requiredAttributeType)) + { + return; + } + + if (_allowExcludedSymbolNames && + operationBlockAnalysisContext.Options.IsConfiguredToSkipAnalysis(SupportedDiagnostics[0], method, operationBlockAnalysisContext.Compilation)) + { + return; + } + + foreach (var operation in operationBlockAnalysisContext.OperationBlocks) + { + var walker = new DisallowGeneralCatchUnlessRethrowWalker(IsDisallowedCatchType, _shouldCheckLambdas); + walker.Visit(operation); + + foreach (var catchClause in walker.CatchClausesForDisallowedTypesWithoutRethrow) + { + operationBlockAnalysisContext.ReportDiagnostic(CreateDiagnostic(method, catchClause.Syntax.GetFirstToken())); + } + } + + bool IsDisallowedCatchType(INamedTypeSymbol type) => + disallowedCatchTypes.Contains(type) || + IsConfiguredDisallowedExceptionType(type, method, compilationStartAnalysisContext.Compilation, + compilationStartAnalysisContext.Options, compilationStartAnalysisContext.CancellationToken); + }); + }); + } + + private INamedTypeSymbol? GetRequiredAttributeType(Compilation compilation) + { + RoslynDebug.Assert(_enablingMethodAttributeFullyQualifiedName != null); + return compilation.GetOrCreateTypeByMetadataName(_enablingMethodAttributeFullyQualifiedName); + } + + private static IReadOnlyCollection GetDisallowedCatchTypes(Compilation compilation) + { + return ImmutableHashSet.CreateRange( + new[] { + compilation.GetSpecialType(SpecialType.System_Object), + compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemException), + compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemSystemException) + }.WhereNotNull()); + } + + /// + /// Walks an IOperation tree to find catch blocks that handle general types without rethrowing them. + /// + private sealed class DisallowGeneralCatchUnlessRethrowWalker : OperationWalker + { + private readonly Func _isDisallowedCatchType; + private readonly bool _checkAnonymousFunctions; + private readonly Stack _seenRethrowInCatchClauses = new(); + + public ISet CatchClausesForDisallowedTypesWithoutRethrow { get; } = new HashSet(); + + public DisallowGeneralCatchUnlessRethrowWalker(Func isDisallowedCatchType, bool checkAnonymousFunctions) + { + _isDisallowedCatchType = isDisallowedCatchType; + _checkAnonymousFunctions = checkAnonymousFunctions; + } + + public override void VisitAnonymousFunction(IAnonymousFunctionOperation operation) + { + if (_checkAnonymousFunctions) + { + base.VisitAnonymousFunction(operation); + } + } + + public override void VisitCatchClause(ICatchClauseOperation operation) + { + _seenRethrowInCatchClauses.Push(false); + + Visit(operation.Filter); + Visit(operation.Handler); + + bool seenRethrow = _seenRethrowInCatchClauses.Pop(); + + if (!seenRethrow && IsDisallowedCatch(operation) && !MightBeFilteringBasedOnTheCaughtException(operation)) + { + CatchClausesForDisallowedTypesWithoutRethrow.Add(operation); + } + } + + public override void VisitThrow(IThrowOperation operation) + { + if (_seenRethrowInCatchClauses.Count > 0 && !_seenRethrowInCatchClauses.Peek()) + { + _seenRethrowInCatchClauses.Pop(); + _seenRethrowInCatchClauses.Push(true); + } + + base.VisitThrow(operation); + } + + private bool IsDisallowedCatch(ICatchClauseOperation operation) + { + return operation.ExceptionType is INamedTypeSymbol exceptionType && + _isDisallowedCatchType(exceptionType); + } + + private static bool MightBeFilteringBasedOnTheCaughtException(ICatchClauseOperation operation) + { + return operation.ExceptionDeclarationOrExpression != null && operation.Filter != null; + } + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/.editorconfig b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/.editorconfig new file mode 100644 index 0000000000000..0c13481f8b800 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/.editorconfig @@ -0,0 +1,4 @@ +[*.{cs,vb}] + +# CA1510: Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance - API only available on .NET 7 in test projects, so disabling for shared project. +dotnet_diagnostic.CA1510.severity = none diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/AdditionalTextExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/AdditionalTextExtensions.cs new file mode 100644 index 0000000000000..43ac39e38a713 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/AdditionalTextExtensions.cs @@ -0,0 +1,20 @@ +// 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.Text; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static class AdditionalTextExtensions + { + private static readonly Encoding s_utf8bom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true); + private static readonly SourceText s_emptySourceText = SourceText.From("", s_utf8bom, SourceHashAlgorithm.Sha256); + + public static SourceText GetTextOrEmpty(this AdditionalText text, CancellationToken cancellationToken) + => text.GetText(cancellationToken) ?? s_emptySourceText; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ArrayBuilderExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ArrayBuilderExtensions.cs new file mode 100644 index 0000000000000..b9e4efb08fd49 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ArrayBuilderExtensions.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.PooledObjects.Extensions +{ + internal static class ArrayBuilderExtensions + { + public static void AddIfNotNull(this ArrayBuilder builder, T? item) + where T : class + { + if (item != null) + { + builder.Add(item); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CharExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CharExtensions.cs new file mode 100644 index 0000000000000..8327041caa89e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CharExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.Extensions +{ + internal static class CharExtensions + { + // From: https://github.com/dotnet/runtime/blob/d1fc57ea18ee90aee8690697caed2b9f162409eb/src/libraries/System.Private.CoreLib/src/System/Char.cs#L91 + public static bool IsAscii(this char c) => (uint)c <= '\x007f'; + + /// + /// Returns whether the char is a printable ascii character [x0020, x007e]. + /// + public static bool IsPrintableAscii(this char c) => (uint)c is >= '\x0020' and <= '\x007e'; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CompilationExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CompilationExtensions.cs new file mode 100644 index 0000000000000..ca4e78eee7074 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/CompilationExtensions.cs @@ -0,0 +1,101 @@ +// 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. + +#if CODEANALYSIS_V3_OR_BETTER +using System; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis.Diagnostics; +#endif + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + /// + /// Provides extensions to . + /// + internal static class CompilationExtensions + { + private static readonly byte[] mscorlibPublicKeyToken = new byte[] + { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }; + +#if CODEANALYSIS_V3_OR_BETTER + private const string WebAppProjectGuidString = "{349C5851-65DF-11DA-9384-00065B846F21}"; + private const string WebSiteProjectGuidString = "{E24C65DC-7377-472B-9ABA-BC803B73C61A}"; + + /// + /// Gets a value indicating whether the project of the compilation is a Web SDK project based on project properties. + /// + internal static bool IsWebProject(this Compilation compilation, AnalyzerOptions options) + { + var propertyValue = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.UsingMicrosoftNETSdkWeb, compilation); + if (string.Equals(propertyValue?.Trim(), "true", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + propertyValue = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.ProjectTypeGuids, compilation); + if (!RoslynString.IsNullOrEmpty(propertyValue) && + (propertyValue.Contains(WebAppProjectGuidString, StringComparison.OrdinalIgnoreCase) || + propertyValue.Contains(WebSiteProjectGuidString, StringComparison.OrdinalIgnoreCase))) + { + var guids = propertyValue.Split(';').Select(g => g.Trim()).ToImmutableArray(); + return guids.Contains(WebAppProjectGuidString, StringComparer.OrdinalIgnoreCase) || + guids.Contains(WebSiteProjectGuidString, StringComparer.OrdinalIgnoreCase); + } + + return false; + } +#endif + + /// + /// Gets a type by its full type name and cache it at the compilation level. + /// + /// The compilation. + /// Namespace + type name, e.g. "System.Exception". + /// The if found, null otherwise. + internal static INamedTypeSymbol? GetOrCreateTypeByMetadataName(this Compilation compilation, string fullTypeName) => + WellKnownTypeProvider.GetOrCreate(compilation).GetOrCreateTypeByMetadataName(fullTypeName); + + /// + /// Gets a type by its full type name and cache it at the compilation level. + /// + /// The compilation. + /// Namespace + type name, e.g. "System.Exception". + /// The if found, null otherwise. + internal static bool TryGetOrCreateTypeByMetadataName(this Compilation compilation, string fullTypeName, [NotNullWhen(returnValue: true)] out INamedTypeSymbol? namedTypeSymbol) => + WellKnownTypeProvider.GetOrCreate(compilation).TryGetOrCreateTypeByMetadataName(fullTypeName, out namedTypeSymbol); + + /// + /// Gets a value indicating, whether the compilation of assembly targets .NET Framework. + /// This method differentiates between .NET Framework and other frameworks (.NET Core, .NET Standard, .NET 5 in future). + /// + /// The compilation + /// True if the compilation targets .NET Framework; otherwise false. + internal static bool TargetsDotNetFramework(this Compilation compilation) + { + var objectType = compilation.GetSpecialType(SpecialType.System_Object); + var assemblyIdentity = objectType.ContainingAssembly.Identity; + if (assemblyIdentity.Name == "mscorlib" && + assemblyIdentity.IsStrongName && + (assemblyIdentity.Version == new System.Version(4, 0, 0, 0) || assemblyIdentity.Version == new System.Version(2, 0, 0, 0)) && + assemblyIdentity.PublicKeyToken.Length == mscorlibPublicKeyToken.Length) + { + for (int i = 0; i < mscorlibPublicKeyToken.Length; i++) + { + if (assemblyIdentity.PublicKeyToken[i] != mscorlibPublicKeyToken[i]) + { + return false; + } + } + + return true; + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/DiagnosticExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/DiagnosticExtensions.cs new file mode 100644 index 0000000000000..af7c78b18c18d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/DiagnosticExtensions.cs @@ -0,0 +1,283 @@ +// 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.Reflection; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities.Extensions +{ + internal static class DiagnosticExtensions + { + public static Diagnostic CreateDiagnostic( + this SyntaxNode node, + DiagnosticDescriptor rule, + params object[] args) + => node.CreateDiagnostic(rule, properties: null, args); + + public static Diagnostic CreateDiagnostic( + this SyntaxNode node, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + params object[] args) + => node.CreateDiagnostic(rule, additionalLocations: ImmutableArray.Empty, properties, args); + + public static Diagnostic CreateDiagnostic( + this SyntaxNode node, + DiagnosticDescriptor rule, + ImmutableArray additionalLocations, + ImmutableDictionary? properties, + params object[] args) + => node + .GetLocation() + .CreateDiagnostic( + rule: rule, + additionalLocations: additionalLocations, + properties: properties, + args: args); + + public static Diagnostic CreateDiagnostic( + this IOperation operation, + DiagnosticDescriptor rule, + params object[] args) + => operation.CreateDiagnostic(rule, properties: null, args); + + public static Diagnostic CreateDiagnostic( + this IOperation operation, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + params object[] args) + { + return operation.Syntax.CreateDiagnostic(rule, properties, args); + } + + public static Diagnostic CreateDiagnostic( + this IOperation operation, + DiagnosticDescriptor rule, + ImmutableArray additionalLocations, + ImmutableDictionary? properties, + params object[] args) + { + return operation.Syntax.CreateDiagnostic(rule, additionalLocations, properties, args); + } + + public static Diagnostic CreateDiagnostic( + this SyntaxToken token, + DiagnosticDescriptor rule, + params object[] args) + { + return token.GetLocation().CreateDiagnostic(rule, args); + } + + public static Diagnostic CreateDiagnostic( + this ISymbol symbol, + DiagnosticDescriptor rule, + params object[] args) + { + return symbol.Locations.CreateDiagnostic(rule, args); + } + + public static Diagnostic CreateDiagnostic( + this ISymbol symbol, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + params object[] args) + { + return symbol.Locations.CreateDiagnostic(rule, properties, args); + } + + public static Diagnostic CreateDiagnostic( + this Location location, + DiagnosticDescriptor rule, + params object[] args) + => location + .CreateDiagnostic( + rule: rule, + properties: ImmutableDictionary.Empty, + args: args); + + public static Diagnostic CreateDiagnostic( + this Location location, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + params object[] args) + => location.CreateDiagnostic(rule, ImmutableArray.Empty, properties, args); + + public static Diagnostic CreateDiagnostic( + this Location location, + DiagnosticDescriptor rule, + ImmutableArray additionalLocations, + ImmutableDictionary? properties, + params object[] args) + { + if (!location.IsInSource) + { + location = Location.None; + } + + return Diagnostic.Create( + descriptor: rule, + location: location, + additionalLocations: additionalLocations, + properties: properties, + messageArgs: args); + } + + public static Diagnostic CreateDiagnostic( + this IEnumerable locations, + DiagnosticDescriptor rule, + params object[] args) + { + return locations.CreateDiagnostic(rule, null, args); + } + + public static Diagnostic CreateDiagnostic( + this IEnumerable locations, + DiagnosticDescriptor rule, + ImmutableDictionary? properties, + params object[] args) + { + IEnumerable inSource = locations.Where(l => l.IsInSource); + if (!inSource.Any()) + { + return Diagnostic.Create(rule, null, args); + } + + return Diagnostic.Create(rule, + location: inSource.First(), + additionalLocations: inSource.Skip(1), + properties: properties, + messageArgs: args); + } + + /// + /// TODO: Revert this reflection based workaround once we move to Microsoft.CodeAnalysis version 3.0 + /// + private static readonly PropertyInfo? s_syntaxTreeDiagnosticOptionsProperty = + typeof(SyntaxTree).GetTypeInfo().GetDeclaredProperty("DiagnosticOptions"); + + private static readonly PropertyInfo? s_compilationOptionsSyntaxTreeOptionsProviderProperty = + typeof(CompilationOptions).GetTypeInfo().GetDeclaredProperty("SyntaxTreeOptionsProvider"); + + public static void ReportNoLocationDiagnostic( + this CompilationAnalysisContext context, + DiagnosticDescriptor rule, + params object[] args) + => context.Compilation.ReportNoLocationDiagnostic(rule, context.ReportDiagnostic, properties: null, args); + + public static void ReportNoLocationDiagnostic( + this SyntaxNodeAnalysisContext context, + DiagnosticDescriptor rule, + params object[] args) + => context.Compilation.ReportNoLocationDiagnostic(rule, context.ReportDiagnostic, properties: null, args); + + public static void ReportNoLocationDiagnostic( + this Compilation compilation, + DiagnosticDescriptor rule, + Action addDiagnostic, + ImmutableDictionary? properties, + params object[] args) + { + var effectiveSeverity = GetEffectiveSeverity(); + if (!effectiveSeverity.HasValue) + { + // Disabled rule + return; + } + + if (effectiveSeverity.Value != rule.DefaultSeverity) + { +#pragma warning disable RS0030 // The symbol 'DiagnosticDescriptor.DiagnosticDescriptor.#ctor' is banned in this project: Use 'DiagnosticDescriptorHelper.Create' instead + rule = new DiagnosticDescriptor(rule.Id, rule.Title, rule.MessageFormat, rule.Category, + effectiveSeverity.Value, rule.IsEnabledByDefault, rule.Description, rule.HelpLinkUri, rule.CustomTags.ToArray()); +#pragma warning restore RS0030 + } + + var diagnostic = Diagnostic.Create(rule, Location.None, properties, args); + addDiagnostic(diagnostic); + return; + + DiagnosticSeverity? GetEffectiveSeverity() + { + // Microsoft.CodeAnalysis version >= 3.7 exposes options through 'CompilationOptions.SyntaxTreeOptionsProvider.TryGetDiagnosticValue' + // Microsoft.CodeAnalysis version 3.3 - 3.7 exposes options through 'SyntaxTree.DiagnosticOptions'. This API is deprecated in 3.7. + + var syntaxTreeOptionsProvider = s_compilationOptionsSyntaxTreeOptionsProviderProperty?.GetValue(compilation.Options); + var syntaxTreeOptionsProviderTryGetDiagnosticValueMethod = syntaxTreeOptionsProvider?.GetType().GetRuntimeMethods().FirstOrDefault(m => m.Name == "TryGetDiagnosticValue"); + if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod == null && s_syntaxTreeDiagnosticOptionsProperty == null) + { + return rule.DefaultSeverity; + } + + ReportDiagnostic? overriddenSeverity = null; + foreach (var tree in compilation.SyntaxTrees) + { + ReportDiagnostic? configuredValue = null; + + // Prefer 'CompilationOptions.SyntaxTreeOptionsProvider', if available. + if (s_compilationOptionsSyntaxTreeOptionsProviderProperty != null) + { + if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod != null) + { + // public abstract bool TryGetDiagnosticValue(SyntaxTree tree, string diagnosticId, out ReportDiagnostic severity); + // public abstract bool TryGetDiagnosticValue(SyntaxTree tree, string diagnosticId, CancellationToken cancellationToken, out ReportDiagnostic severity); + object?[] parameters; + if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod.GetParameters().Length == 3) + { + parameters = new object?[] { tree, rule.Id, null }; + } + else + { + parameters = new object?[] { tree, rule.Id, CancellationToken.None, null }; + } + + if (syntaxTreeOptionsProviderTryGetDiagnosticValueMethod.Invoke(syntaxTreeOptionsProvider, parameters) is true && + parameters.Last() is ReportDiagnostic value) + { + configuredValue = value; + } + } + } + else + { + RoslynDebug.Assert(s_syntaxTreeDiagnosticOptionsProperty != null); + var options = (ImmutableDictionary)s_syntaxTreeDiagnosticOptionsProperty.GetValue(tree)!; + if (options.TryGetValue(rule.Id, out var value)) + { + configuredValue = value; + } + } + + if (configuredValue == null) + { + continue; + } + + if (configuredValue == ReportDiagnostic.Suppress) + { + // Any suppression entry always wins. + return null; + } + + if (overriddenSeverity == null) + { + overriddenSeverity = configuredValue; + } + else if (overriddenSeverity.Value.IsLessSevereThan(configuredValue.Value)) + { + // Choose the most severe value for conflicts. + overriddenSeverity = configuredValue; + } + } + + return overriddenSeverity.HasValue ? overriddenSeverity.Value.ToDiagnosticSeverity() : rule.DefaultSeverity; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IDictionaryExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IDictionaryExtensions.cs new file mode 100644 index 0000000000000..10d1c96d54915 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IDictionaryExtensions.cs @@ -0,0 +1,41 @@ +// 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.Linq; + +namespace Analyzer.Utilities.Extensions +{ + internal static class IDictionaryExtensions + { + public static void AddKeyValueIfNotNull( + this IDictionary dictionary, + TKey? key, + TValue? value) + where TKey : class + where TValue : class + { + if (key != null && value != null) + { + dictionary.Add(key, value); + } + } + + public static void AddRange( + this IDictionary dictionary, + IEnumerable> items) + where TKey : notnull + { + foreach (var item in items) + { + dictionary.Add(item); + } + } + + public static bool IsEqualTo(this IReadOnlyDictionary dictionary, IReadOnlyDictionary other) + where TKey : notnull + => dictionary.Count == other.Count && + dictionary.Keys.All(key => other.ContainsKey(key) && dictionary[key]?.Equals(other[key]) == true); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableExtensions.cs new file mode 100644 index 0000000000000..b1d8f389bd8ef --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,233 @@ +// 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; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Analyzer.Utilities.Extensions +{ + internal static class IEnumerableExtensions + { + public static IEnumerable Concat(this IEnumerable source, T value) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return ConcatImpl(source, value); + + static IEnumerable ConcatImpl(IEnumerable source, T value) + { + foreach (T v in source) + { + yield return v; + } + + yield return value; + } + } + + public static ISet ToSet(this IEnumerable source) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + return source as ISet ?? new HashSet(source); + } + + public static IEnumerable OrderBy(this IEnumerable source, IComparer comparer) + { + return source.OrderBy(t => t, comparer); + } + + public static IEnumerable OrderBy(this IEnumerable source, Comparison compare) + { + return source.OrderBy(new ComparisonComparer(compare)); + } + + public static IEnumerable Order(this IEnumerable source) where T : IComparable + { + return source.OrderBy((t1, t2) => t1.CompareTo(t2)); + } + + private static readonly Func s_notNullTest = x => x != null; + + public static IEnumerable WhereNotNull(this IEnumerable source) where T : class + { + if (source == null) + { + return ImmutableArray.Empty; + } + + return source.Where((Func)s_notNullTest)!; + } + + public static ImmutableArray WhereAsArray(this IEnumerable source, Func selector) + { + var builder = ImmutableArray.CreateBuilder(); + bool any = false; + foreach (var element in source) + { + if (selector(element)) + { + any = true; + builder.Add(element); + } + } + + if (any) + { + return builder.ToImmutable(); + } + else + { + return ImmutableArray.Empty; + } + } + + public static void Dispose(this IEnumerable collection) + where T : class, IDisposable + { + foreach (var item in collection) + { + item?.Dispose(); + } + } + + /// + /// Determines whether a sequence contains, exactly, elements. + /// + /// The type of the elements of source. + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains, exactly, elements; otherwise, . + /// is null. + public static bool HasExactly(this IEnumerable source, int count) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source is ICollection collectionoft) + { + return collectionoft.Count == count; + } + + if (source is ICollection collection) + { + return collection.Count == count; + } + + using var enumerator = source.GetEnumerator(); + while (count-- > 0) + { + if (!enumerator.MoveNext()) + { + return false; + } + } + + return !enumerator.MoveNext(); + } + + /// + /// Determines whether a sequence contains more than elements. + /// + /// The type of the elements of . + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains more than elements; otherwise, . + /// is null. + public static bool HasMoreThan(this IEnumerable source, int count) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source is ICollection collectionoft) + { + return collectionoft.Count > count; + } + + if (source is ICollection collection) + { + return collection.Count > count; + } + + using var enumerator = source.GetEnumerator(); + while (count-- > 0) + { + if (!enumerator.MoveNext()) + { + return false; + } + } + + return enumerator.MoveNext(); + } + + /// + /// Determines whether a sequence contains fewer than elements. + /// + /// The type of the elements of . + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains less than elements; otherwise, . + /// is null. + public static bool HasFewerThan(this IEnumerable source, int count) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source is ICollection collectionoft) + { + return collectionoft.Count < count; + } + + if (source is ICollection collection) + { + return collection.Count < count; + } + + using var enumerator = source.GetEnumerator(); + while (count > 0 && enumerator.MoveNext()) + { + count--; + } + + return count > 0; + } + + private sealed class ComparisonComparer : Comparer + { + private readonly Comparison _compare; + + public ComparisonComparer(Comparison compare) + { + _compare = compare; + } + + public override int Compare([AllowNull] T x, [AllowNull] T y) + { + if (x is null) + return y is null ? 0 : -1; + else if (y is null) + return 1; + + return _compare(x, y); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableOfIMethodSymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableOfIMethodSymbolExtensions.cs new file mode 100644 index 0000000000000..9d9483ec85f89 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IEnumerableOfIMethodSymbolExtensions.cs @@ -0,0 +1,240 @@ +// 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.Linq; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class IEnumerableOfIMethodSymbolExtensions + { + /// + /// Excludes that have an attribute that precisely matches . + /// + /// List of to filter. + /// The of the attribute class to search. + /// A filtered list of methods. + public static IEnumerable WhereMethodDoesNotContainAttribute( + this IEnumerable methods, + INamedTypeSymbol? attributeType) + { + if (attributeType == null) + { + return methods; + } + + return methods.Where(m => !m.HasAnyAttribute(attributeType)); + } + + /// + /// Returns a list of method symbols from a given list of the method symbols, which has its parameter type as + /// expectedParameterType as its first parameter or the last parameter in addition to matching all the other + /// parameter types of the selectedOverload method symbol + /// + /// List of to scan for possible overloads + /// that is currently picked by the user + /// type of the leading parameter or the trailing parameter + /// If the expected parameter should appear at the trailing position of the parameter list of the method overload + public static IEnumerable GetMethodOverloadsWithDesiredParameterAtLeadingOrTrailing( + this IEnumerable methods, + IMethodSymbol selectedOverload, + INamedTypeSymbol expectedParameterType, + bool trailingOnly = false) + { + return methods.Where(candidateMethod => + { + if (!System.Collections.Immutable.ImmutableArrayExtensions.HasExactly(candidateMethod.Parameters, selectedOverload.Parameters.Length + 1)) + { + return false; + } + + // The expected method overload should either have the expectedParameterType parameter as the first argument or as the last argument + // Assume expectedParameterType is the last parameter so j, which is the index of the parameter + // in candidateMethod to compare against selectedOverload's parameter is set to 0 + int j = 0; + + if (!trailingOnly && candidateMethod.Parameters.First().Type.Equals(expectedParameterType) && candidateMethod.Parameters[0].RefKind == RefKind.None) + { + // If expectedParameterType is the first parameter then the parameters to compare in candidateMethod against selectedOverload + // is offset by 1 + j = 1; + } + else + { + var lastParameter = candidateMethod.Parameters.Last(); + if (!lastParameter.Type.Equals(expectedParameterType) || lastParameter.RefKind != RefKind.None) + { + // expectedParameterType is neither the first parameter nor the last parameter + return false; + } + } + + for (int i = 0; i < selectedOverload.Parameters.Length; i++, j++) + { + if (!selectedOverload.Parameters[i].Type.Equals(candidateMethod.Parameters[j].Type) || + selectedOverload.Parameters[i].IsParams != candidateMethod.Parameters[j].IsParams || + selectedOverload.Parameters[i].RefKind != candidateMethod.Parameters[j].RefKind) + { + return false; + } + } + + return true; + }); + } + + /// + /// Returns a list of method symbols from a given list of the method symbols, which has its parameter type as + /// expectedParameterType as its last parameter in addition to matching all the other parameter types of the + /// selectedOverload method symbol + /// + /// List of to scan for possible overloads + /// that is currently picked by the user + /// type of the leading parameter or the trailing parameter + public static IEnumerable GetMethodOverloadsWithDesiredParameterAtTrailing( + this IEnumerable methods, + IMethodSymbol selectedOverload, + INamedTypeSymbol expectedTrailingParameterType) + { + return GetMethodOverloadsWithDesiredParameterAtLeadingOrTrailing(methods, selectedOverload, expectedTrailingParameterType, trailingOnly: true); + } + + /// + /// Gets the in the sequence who's parameters match . + /// + /// The sequence of s to search. + /// The types of the parameters, in order. + /// + /// The first in the sequence who's parameters match , or null if + /// no method was found. + /// + public static IMethodSymbol? GetFirstOrDefaultMemberWithParameterTypes(this IEnumerable? members, params ITypeSymbol[] expectedParameterTypesInOrder) + { + return members?.FirstOrDefault(member => + { + if (member.Parameters.Length != expectedParameterTypesInOrder.Length) + return false; + + for (int i = 0; i < expectedParameterTypesInOrder.Length; ++i) + { + var parameterType = member.Parameters[i].Type; + + if (!expectedParameterTypesInOrder[i].Equals(parameterType)) + return false; + } + + return true; + }); + } + + /// + /// Given a , this method returns the method symbol which + /// matches the expectedParameterTypesInOrder parameter requirement + /// + /// + /// + /// + public static IMethodSymbol? GetFirstOrDefaultMemberWithParameterInfos(this IEnumerable? members, params ParameterInfo[] expectedParameterTypesInOrder) + { + var expectedParameterCount = expectedParameterTypesInOrder.Length; + return members?.FirstOrDefault(member => + { + if (member.Parameters.Length != expectedParameterCount) + { + return false; + } + + for (int i = 0; i < expectedParameterCount; i++) + { + // check IsParams only on the last parameter + if (i == expectedParameterCount - 1 && + member.Parameters[i].IsParams != expectedParameterTypesInOrder[i].IsParams) + { + return false; + } + + var parameterType = member.Parameters[i].Type; + if (expectedParameterTypesInOrder[i].IsArray) + { + var arrayParameterSymbol = parameterType as IArrayTypeSymbol; + if (arrayParameterSymbol?.Rank != expectedParameterTypesInOrder[i].ArrayRank) + { + return false; + } + + parameterType = arrayParameterSymbol.ElementType; + } + + if (!expectedParameterTypesInOrder[i].ParameterType.Equals(parameterType)) + { + return false; + } + } + + return true; + }); + } + + /// + /// Given an , returns the whose parameter list + /// matches . + /// + /// + /// Expected types of the member's parameters. + /// + /// The first member in the sequence whose parameters match , + /// or null if no matches are found. + /// + public static IMethodSymbol? GetFirstOrDefaultMemberWithParameterTypes(this IEnumerable? members, IReadOnlyList expectedParameterTypesInOrder) + { + if (members is null) + return null; + + foreach (var member in members) + { + if (Predicate(member)) + return member; + } + + return null; + + bool Predicate(IMethodSymbol member) + { + if (member.Parameters.Length != expectedParameterTypesInOrder.Count) + return false; + + for (int index = 0; index < expectedParameterTypesInOrder.Count; index++) + { + if (!member.Parameters[index].Type.Equals(expectedParameterTypesInOrder[index])) + return false; + } + + return true; + } + } + } + + // Contains the expected properties of a parameter + internal sealed class ParameterInfo + { + public int ArrayRank { get; private set; } + public bool IsArray { get; private set; } + public bool IsParams { get; private set; } + public INamedTypeSymbol ParameterType { get; private set; } + + private ParameterInfo(INamedTypeSymbol type, bool isArray, int arrayRank, bool isParams) + { + ParameterType = type; + IsArray = isArray; + ArrayRank = arrayRank; + IsParams = isParams; + } + + public static ParameterInfo GetParameterInfo(INamedTypeSymbol type, bool isArray = false, int arrayRank = 0, bool isParams = false) + { + return new ParameterInfo(type, isArray, arrayRank, isParams); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs new file mode 100644 index 0000000000000..c5c1a0c314e1c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IMethodSymbolExtensions.cs @@ -0,0 +1,831 @@ +// 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 warnings + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; + +#if HAS_IOPERATION +using System.Threading; +using Microsoft.CodeAnalysis.Operations; +#endif + +namespace Analyzer.Utilities.Extensions +{ + internal static class IMethodSymbolExtensions + { + /// + /// Checks if the given method overrides . + /// + public static bool IsObjectEqualsOverride(this IMethodSymbol method) + { + return method != null && + method.IsOverride && + method.Name == WellKnownMemberNames.ObjectEquals && + method.ReturnType.SpecialType == SpecialType.System_Boolean && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object && + IsObjectMethodOverride(method); + } + + /// + /// Checks if the given method is . + /// + public static bool IsObjectEquals(this IMethodSymbol method) + { + return method != null && + method.ContainingType.SpecialType == SpecialType.System_Object && + method.IsVirtual && + method.Name == WellKnownMemberNames.ObjectEquals && + method.ReturnType.SpecialType == SpecialType.System_Boolean && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object; + } + + /// + /// Checks if the given is or . + /// + public static bool IsStaticObjectEqualsOrReferenceEquals(this IMethodSymbol method) + { + return method != null && + method.IsStatic && + method.ContainingType.SpecialType == SpecialType.System_Object && + method.Parameters.Length == 2 && + method.ReturnType.SpecialType == SpecialType.System_Boolean && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object && + method.Parameters[1].Type.SpecialType == SpecialType.System_Object && + (method.Name == WellKnownMemberNames.ObjectEquals || method.Name == "ReferenceEquals"); + } + + /// + /// Checks if the given method overrides Object.GetHashCode. + /// + public static bool IsGetHashCodeOverride(this IMethodSymbol method) + { + return method != null && + method.IsOverride && + method.Name == WellKnownMemberNames.ObjectGetHashCode && + method.ReturnType.SpecialType == SpecialType.System_Int32 && + method.Parameters.IsEmpty && + IsObjectMethodOverride(method); + } + + /// + /// Checks if the given method overrides Object.ToString. + /// + public static bool IsToStringOverride(this IMethodSymbol method) + { + return method != null && + method.IsOverride && + method.ReturnType.SpecialType == SpecialType.System_String && + method.Name == WellKnownMemberNames.ObjectToString && + method.Parameters.IsEmpty && + IsObjectMethodOverride(method); + } + + /// + /// Checks if the given method overrides a method from System.Object + /// + private static bool IsObjectMethodOverride(IMethodSymbol method) + { + IMethodSymbol overriddenMethod = method.OverriddenMethod; + while (overriddenMethod != null) + { + if (overriddenMethod.ContainingType.SpecialType == SpecialType.System_Object) + { + return true; + } + + overriddenMethod = overriddenMethod.OverriddenMethod; + } + + return false; + } + + /// + /// Checks if the given method is a Finalizer implementation. + /// + public static bool IsFinalizer(this IMethodSymbol method) + { + if (method.MethodKind == MethodKind.Destructor) + { + return true; // for C# + } + + if (method.Name != WellKnownMemberNames.DestructorName || !method.Parameters.IsEmpty || !method.ReturnsVoid) + { + return false; + } + + IMethodSymbol overridden = method.OverriddenMethod; + + if (method.ContainingType.SpecialType == SpecialType.System_Object) + { + // This is object.Finalize + return true; + } + + if (overridden == null) + { + return false; + } + + for (IMethodSymbol o = overridden.OverriddenMethod; o != null; o = o.OverriddenMethod) + { + overridden = o; + } + + return overridden.ContainingType.SpecialType == SpecialType.System_Object; // it is object.Finalize + } + + /// + /// Checks if the given method is an implementation of the given interface method + /// Substituted with the given typeargument. + /// + public static bool IsImplementationOfInterfaceMethod(this IMethodSymbol method, ITypeSymbol? typeArgument, [NotNullWhen(returnValue: true)] INamedTypeSymbol? interfaceType, string interfaceMethodName) + { + INamedTypeSymbol? constructedInterface = typeArgument != null ? interfaceType?.Construct(typeArgument) : interfaceType; + + return constructedInterface?.GetMembers(interfaceMethodName).FirstOrDefault() is IMethodSymbol interfaceMethod && + SymbolEqualityComparer.Default.Equals(method, method.ContainingType.FindImplementationForInterfaceMember(interfaceMethod)); + } + + /// + /// Checks if the given method implements IDisposable.Dispose() + /// + public static bool IsDisposeImplementation(this IMethodSymbol method, Compilation compilation) + { + INamedTypeSymbol? iDisposable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable); + return method.IsDisposeImplementation(iDisposable); + } + + /// + /// Checks if the given method implements IAsyncDisposable.Dispose() + /// + public static bool IsAsyncDisposeImplementation(this IMethodSymbol method, Compilation compilation) + { + INamedTypeSymbol? iAsyncDisposable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIAsyncDisposable); + INamedTypeSymbol? valueTaskType = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + return method.IsAsyncDisposeImplementation(iAsyncDisposable, valueTaskType); + } + + /// + /// Checks if the given method implements or overrides an implementation of . + /// + public static bool IsDisposeImplementation([NotNullWhen(returnValue: true)] this IMethodSymbol? method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? iDisposable) + { + if (method == null) + { + return false; + } + + if (method.IsOverride) + { + return method.OverriddenMethod.IsDisposeImplementation(iDisposable); + } + + // Identify the implementor of IDisposable.Dispose in the given method's containing type and check + // if it is the given method. + return method.ReturnsVoid && + method.Parameters.IsEmpty && + method.IsImplementationOfInterfaceMethod(null, iDisposable, "Dispose"); + } + + /// + /// Checks if the given method implements "IAsyncDisposable.Dispose" or overrides an implementation of "IAsyncDisposable.Dispose". + /// + public static bool IsAsyncDisposeImplementation([NotNullWhen(returnValue: true)] this IMethodSymbol? method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? iAsyncDisposable, [NotNullWhen(returnValue: true)] INamedTypeSymbol? valueTaskType) + { + if (method == null) + { + return false; + } + + if (method.IsOverride) + { + return method.OverriddenMethod.IsAsyncDisposeImplementation(iAsyncDisposable, valueTaskType); + } + + // Identify the implementor of IAsyncDisposable.Dispose in the given method's containing type and check + // if it is the given method. + return SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTaskType) && + method.Parameters.IsEmpty && + method.IsImplementationOfInterfaceMethod(null, iAsyncDisposable, "DisposeAsync"); + } + + /// + /// Checks if the given method has the signature "void Dispose()". + /// + private static bool HasDisposeMethodSignature(this IMethodSymbol method) + { + return method.Name == "Dispose" && method.MethodKind == MethodKind.Ordinary && + method.ReturnsVoid && method.Parameters.IsEmpty; + } + + /// + /// Checks if the given method matches Dispose method convention and can be recognized by "using". + /// + public static bool HasDisposeSignatureByConvention(this IMethodSymbol method) + { + return method.HasDisposeMethodSignature() + && !method.IsStatic + && !method.IsPrivate(); + } + + /// + /// Checks if the given method has the signature "void Dispose(bool)". + /// + public static bool HasDisposeBoolMethodSignature(this IMethodSymbol method) + { + if (method.Name == "Dispose" && method.MethodKind == MethodKind.Ordinary && + method.ReturnsVoid && method.Parameters.Length == 1) + { + IParameterSymbol parameter = method.Parameters[0]; + return parameter.Type != null && + parameter.Type.SpecialType == SpecialType.System_Boolean && + parameter.RefKind == RefKind.None; + } + + return false; + } + + /// + /// Checks if the given method has the signature "void Close()". + /// + private static bool HasDisposeCloseMethodSignature(this IMethodSymbol method) + { + return method.Name == "Close" && method.MethodKind == MethodKind.Ordinary && + method.ReturnsVoid && method.Parameters.IsEmpty; + } + + /// + /// Checks if the given method has the signature "Task CloseAsync()". + /// + private static bool HasDisposeCloseAsyncMethodSignature(this IMethodSymbol method, INamedTypeSymbol? taskType) + => taskType != null && method.Parameters.IsEmpty && method.Name == "CloseAsync" && + SymbolEqualityComparer.Default.Equals(method.ReturnType, taskType); + + /// + /// Checks if the given method has the signature "Task DisposeAsync()" or "ValueTask DisposeAsync()" or "ConfiguredValueTaskAwaitable DisposeAsync()". + /// + private static bool HasDisposeAsyncMethodSignature(this IMethodSymbol method, + INamedTypeSymbol? task, + INamedTypeSymbol? valueTask, + INamedTypeSymbol? configuredValueTaskAwaitable) + { + return method.Name == "DisposeAsync" && + method.MethodKind == MethodKind.Ordinary && + method.Parameters.IsEmpty && + (SymbolEqualityComparer.Default.Equals(method.ReturnType, task) || + SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTask) || + SymbolEqualityComparer.Default.Equals(method.ReturnType, configuredValueTaskAwaitable)); + } + + /// + /// Checks if the given method has the signature "override Task DisposeCoreAsync(bool)" or "override Task DisposeAsyncCore(bool)". + /// + private static bool HasOverriddenDisposeCoreAsyncMethodSignature(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? task) + { + return (method.Name == "DisposeAsyncCore" || method.Name == "DisposeCoreAsync") && + method.MethodKind == MethodKind.Ordinary && + method.IsOverride && + SymbolEqualityComparer.Default.Equals(method.ReturnType, task) && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Boolean; + } + + /// + /// Checks if the given method has the signature "{virtual|override} ValueTask DisposeCoreAsync()" or "{virtual|override} ValueTask DisposeAsyncCore()". + /// + private static bool HasVirtualOrOverrideDisposeCoreAsyncMethodSignature(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? valueTask) + { + return (method.Name == "DisposeAsyncCore" || method.Name == "DisposeCoreAsync") && + method.MethodKind == MethodKind.Ordinary && + (method.IsVirtual || method.IsOverride) && + SymbolEqualityComparer.Default.Equals(method.ReturnType, valueTask) && + method.Parameters.Length == 0; + } + + /// + /// Gets the for the given method. + /// + public static DisposeMethodKind GetDisposeMethodKind(this IMethodSymbol method, Compilation compilation) + { + INamedTypeSymbol? iDisposable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable); + INamedTypeSymbol? iAsyncDisposable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIAsyncDisposable); + INamedTypeSymbol? configuredAsyncDisposable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredAsyncDisposable); + INamedTypeSymbol? task = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + INamedTypeSymbol? valueTask = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + INamedTypeSymbol? configuredValueTaskAwaitable = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable); + return method.GetDisposeMethodKind(iDisposable, iAsyncDisposable, configuredAsyncDisposable, task, valueTask, configuredValueTaskAwaitable); + } + + /// + /// Gets the for the given method. + /// + public static DisposeMethodKind GetDisposeMethodKind( + this IMethodSymbol method, + INamedTypeSymbol? iDisposable, + INamedTypeSymbol? iAsyncDisposable, + INamedTypeSymbol? configuredAsyncDisposable, + INamedTypeSymbol? task, + INamedTypeSymbol? valueTask, + INamedTypeSymbol? configuredValueTaskAwaitable) + { + if (method.ContainingType.IsDisposable(iDisposable, iAsyncDisposable, configuredAsyncDisposable)) + { + if (IsDisposeImplementation(method, iDisposable) || + (SymbolEqualityComparer.Default.Equals(method.ContainingType, iDisposable) && + method.HasDisposeMethodSignature()) +#if CODEANALYSIS_V3_OR_BETTER + || (method.ContainingType.IsRefLikeType && + method.HasDisposeSignatureByConvention()) +#endif + ) + { + return DisposeMethodKind.Dispose; + } + else if (method.HasDisposeBoolMethodSignature()) + { + return DisposeMethodKind.DisposeBool; + } + else if (method.IsAsyncDisposeImplementation(iAsyncDisposable, valueTask) || method.HasDisposeAsyncMethodSignature(task, valueTask, configuredValueTaskAwaitable)) + { + return DisposeMethodKind.DisposeAsync; + } + else if (method.HasOverriddenDisposeCoreAsyncMethodSignature(task)) + { + return DisposeMethodKind.DisposeCoreAsync; + } + else if (method.HasVirtualOrOverrideDisposeCoreAsyncMethodSignature(valueTask)) + { + return DisposeMethodKind.DisposeCoreAsync; + } + else if (method.HasDisposeCloseMethodSignature()) + { + return DisposeMethodKind.Close; + } + else if (method.HasDisposeCloseAsyncMethodSignature(task)) + { + return DisposeMethodKind.CloseAsync; + } + } + + return DisposeMethodKind.None; + } + + /// + /// Checks if the given method implements 'System.Runtime.Serialization.IDeserializationCallback.OnDeserialization' or overrides an implementation of 'System.Runtime.Serialization.IDeserializationCallback.OnDeserialization'/>. + /// + public static bool IsOnDeserializationImplementation([NotNullWhen(returnValue: true)] this IMethodSymbol? method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? iDeserializationCallback) + { + if (method == null) + { + return false; + } + + if (method.IsOverride) + { + return method.OverriddenMethod.IsOnDeserializationImplementation(iDeserializationCallback); + } + + // Identify the implementor of IDisposable.Dispose in the given method's containing type and check + // if it is the given method. + return method.ReturnsVoid && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object && + method.IsImplementationOfInterfaceMethod(null, iDeserializationCallback, "OnDeserialization"); + } + + public static bool IsSerializationConstructor([NotNullWhen(returnValue: true)] this IMethodSymbol? method, INamedTypeSymbol? serializationInfoType, INamedTypeSymbol? streamingContextType) + => method.IsConstructor() && + method.Parameters.Length == 2 && + SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, serializationInfoType) && + SymbolEqualityComparer.Default.Equals(method.Parameters[1].Type, streamingContextType); + + public static bool IsGetObjectData([NotNullWhen(returnValue: true)] this IMethodSymbol? method, INamedTypeSymbol? serializationInfoType, INamedTypeSymbol? streamingContextType) + => method?.Name == "GetObjectData" && + method.ReturnsVoid && + method.Parameters.Length == 2 && + SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, serializationInfoType) && + SymbolEqualityComparer.Default.Equals(method.Parameters[1].Type, streamingContextType); + + /// + /// Checks if the method is a property getter. + /// + public static bool IsPropertyGetter(this IMethodSymbol method) + { + return method.MethodKind == MethodKind.PropertyGet && + method.AssociatedSymbol?.GetParameters().Length == 0; + } + + /// + /// Checks if the method is the getter for an indexer. + /// + public static bool IsIndexerGetter(this IMethodSymbol method) + { + return method.MethodKind == MethodKind.PropertyGet && + method.AssociatedSymbol.IsIndexer(); + } + + /// + /// Checks if the method is an accessor for a property. + /// + public static bool IsPropertyAccessor(this IMethodSymbol method) + { + return method.MethodKind is MethodKind.PropertyGet or + MethodKind.PropertySet; + } + + /// + /// Checks if the method is an accessor for an event. + /// + public static bool IsEventAccessor(this IMethodSymbol method) + { + return method.MethodKind is MethodKind.EventAdd or + MethodKind.EventRaise or + MethodKind.EventRemove; + } + + public static bool IsOperator(this IMethodSymbol methodSymbol) + { + return methodSymbol.MethodKind is MethodKind.UserDefinedOperator or MethodKind.BuiltinOperator; + } + + public static bool HasOptionalParameters(this IMethodSymbol methodSymbol) + { + return methodSymbol.Parameters.Any(p => p.IsOptional); + } + + public static IEnumerable GetOverloads(this IMethodSymbol? method) + { + var methods = method?.ContainingType?.GetMembers(method.Name).OfType(); + if (methods != null) + { + foreach (var member in methods) + { + if (!SymbolEqualityComparer.Default.Equals(member, method)) + { + yield return member; + } + } + } + } + + /// + /// Set of well-known collection add method names. + /// Used in heuristic. + /// + private static readonly ImmutableHashSet s_collectionAddMethodNameVariants = + ImmutableHashSet.Create(StringComparer.Ordinal, "Add", "AddOrUpdate", "GetOrAdd", "TryAdd", "TryUpdate"); + + /// + /// Determine if the specific method is an Add method that adds to a collection. + /// + /// The method to test. + /// Collection types. + /// 'true' if is believed to be the add method of a collection. + /// + /// We use the following heuristic to determine if a method is a collection add method: + /// 1. Method's enclosing type implements any of the given . + /// 2. Any of the following name heuristics are met: + /// a. Method's name is from one of the well-known add method names from ("Add", "AddOrUpdate", "GetOrAdd", "TryAdd", or "TryUpdate") + /// b. Method's name begins with "Add" (FxCop compat) + /// + public static bool IsCollectionAddMethod(this IMethodSymbol method, ImmutableHashSet iCollectionTypes) + { + if (iCollectionTypes.IsEmpty) + { + return false; + } + + if (!s_collectionAddMethodNameVariants.Contains(method.Name) && + !method.Name.StartsWith("Add", StringComparison.Ordinal)) + { + return false; + } + + return method.ContainingType.AllInterfaces.Any(i => iCollectionTypes.Contains(i.OriginalDefinition)); + } + + /// + /// Determine if the specific method is a Task.FromResult method that wraps a result in a task. + /// + /// The method to test. + /// Task type. + public static bool IsTaskFromResultMethod(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? taskType) + => method.Name.Equals("FromResult", StringComparison.Ordinal) && + SymbolEqualityComparer.Default.Equals(method.ContainingType, taskType); + + /// + /// Determine if the specific method is a Task.ConfigureAwait(bool) method. + /// + /// The method to test. + /// Generic task type. + public static bool IsTaskConfigureAwaitMethod(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? genericTaskType) + => method.Name.Equals("ConfigureAwait", StringComparison.Ordinal) && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Boolean && + SymbolEqualityComparer.Default.Equals(method.ContainingType.OriginalDefinition, genericTaskType); + + /// + /// Determine if the specific method is a TaskAsyncEnumerableExtensions.ConfigureAwait(this IAsyncDisposable, bool) extension method. + /// + /// The method to test. + /// IAsyncDisposable named type. + /// System.Threading.Tasks.TaskAsyncEnumerableExtensions named type. + public static bool IsAsyncDisposableConfigureAwaitMethod(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? asyncDisposableType, [NotNullWhen(returnValue: true)] INamedTypeSymbol? taskAsyncEnumerableExtensions) + => method.IsExtensionMethod && + method.Name.Equals("ConfigureAwait", StringComparison.Ordinal) && + method.Parameters.Length == 2 && + SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, asyncDisposableType) && + method.Parameters[1].Type.SpecialType == SpecialType.System_Boolean && + SymbolEqualityComparer.Default.Equals(method.ContainingType.OriginalDefinition, taskAsyncEnumerableExtensions) && + taskAsyncEnumerableExtensions.IsStatic; + +#if HAS_IOPERATION + /// + /// PERF: Cache from method symbols to their topmost block operations to enable interprocedural flow analysis + /// across analyzers and analyzer callbacks to re-use the operations, semanticModel and control flow graph. + /// + /// Also see + private static readonly BoundedCache> s_methodToTopmostOperationBlockCache + = new(); + + /// + /// Returns the topmost for given . + /// + public static IBlockOperation? GetTopmostOperationBlock(this IMethodSymbol method, Compilation compilation, CancellationToken cancellationToken = default) + { + var methodToBlockMap = s_methodToTopmostOperationBlockCache.GetOrCreateValue(compilation); + return methodToBlockMap.GetOrAdd(method, ComputeTopmostOperationBlock); + + // Local functions. + IBlockOperation? ComputeTopmostOperationBlock(IMethodSymbol unused) + { + if (!SymbolEqualityComparer.Default.Equals(method.ContainingAssembly, compilation.Assembly)) + { + return null; + } + + foreach (var decl in method.DeclaringSyntaxReferences) + { + var syntax = decl.GetSyntax(cancellationToken); + + // VB Workaround: declaration.GetSyntax returns StatementSyntax nodes instead of BlockSyntax nodes + // GetOperation returns null for StatementSyntax, and the method's operation block for BlockSyntax. + if (compilation.Language == LanguageNames.VisualBasic) + { + syntax = syntax.Parent; + } + + var semanticModel = compilation.GetSemanticModel(syntax.SyntaxTree); + foreach (var descendant in syntax.DescendantNodesAndSelf()) + { + var operation = semanticModel.GetOperation(descendant, cancellationToken); + if (operation is IBlockOperation blockOperation) + { + return blockOperation; + } + } + } + + return null; + } + } +#endif + + public static bool IsLambdaOrLocalFunctionOrDelegate(this IMethodSymbol method) + { + return method.MethodKind switch + { + MethodKind.LambdaMethod or MethodKindEx.LocalFunction or MethodKind.DelegateInvoke => true, + _ => false, + }; + } + + public static bool IsLambdaOrLocalFunction(this IMethodSymbol method) + { + return method.MethodKind switch + { + MethodKind.LambdaMethod or MethodKindEx.LocalFunction => true, + _ => false, + }; + } + + public static int GetParameterIndex(this IMethodSymbol methodSymbol, IParameterSymbol parameterSymbol) + { + for (var i = 0; i < methodSymbol.Parameters.Length; i++) + { + if (SymbolEqualityComparer.Default.Equals(parameterSymbol, methodSymbol.Parameters[i])) + { + return i; + } + } + + throw new ArgumentException("Invalid parameter", nameof(parameterSymbol)); + } + + /// + /// Returns true for void returning methods with two parameters, where + /// the first parameter is of type and the second + /// parameter inherits from or equals type or + /// whose name ends with 'EventArgs'. + /// + public static bool HasEventHandlerSignature(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? eventArgsType) + => eventArgsType != null && + method.ReturnsVoid && + method.Parameters.Length == 2 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object && + // FxCop compat: Struct with name ending with "EventArgs" are allowed + // + UWP has specific EventArgs not inheriting from 'System.EventArgs'. + // See https://github.com/dotnet/roslyn-analyzers/issues/3106 + (method.Parameters[1].Type.DerivesFrom(eventArgsType, baseTypesOnly: true) || method.Parameters[1].Type.Name.EndsWith("EventArgs", StringComparison.Ordinal)); + + public static bool IsLockMethod(this IMethodSymbol method, [NotNullWhen(returnValue: true)] INamedTypeSymbol? systemThreadingMonitor) + { + // "System.Threading.Monitor.Enter(object)" OR "System.Threading.Monitor.Enter(object, bool)" + return method.Name == "Enter" && + SymbolEqualityComparer.Default.Equals(method.ContainingType, systemThreadingMonitor) && + method.ReturnsVoid && + !method.Parameters.IsEmpty && + method.Parameters[0].Type.SpecialType == SpecialType.System_Object; + } + + public static bool IsInterlockedExchangeMethod(this IMethodSymbol method, INamedTypeSymbol? systemThreadingInterlocked) + { + Debug.Assert(SymbolEqualityComparer.Default.Equals(method.ContainingType.OriginalDefinition, systemThreadingInterlocked)); + + // "System.Threading.Interlocked.Exchange(ref T, T)" + return method.Name == "Exchange" && + method.Parameters.Length == 2 && + method.Parameters[0].RefKind == RefKind.Ref && + method.Parameters[1].RefKind != RefKind.Ref && + SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, method.Parameters[1].Type); + } + + public static bool IsInterlockedCompareExchangeMethod(this IMethodSymbol method, INamedTypeSymbol? systemThreadingInterlocked) + { + Debug.Assert(SymbolEqualityComparer.Default.Equals(method.ContainingType.OriginalDefinition, systemThreadingInterlocked)); + + // "System.Threading.Interlocked.CompareExchange(ref T, T, T)" + return method.Name == "CompareExchange" && + method.Parameters.Length == 3 && + method.Parameters[0].RefKind == RefKind.Ref && + method.Parameters[1].RefKind != RefKind.Ref && + method.Parameters[2].RefKind != RefKind.Ref && + SymbolEqualityComparer.Default.Equals(method.Parameters[0].Type, method.Parameters[1].Type) && + SymbolEqualityComparer.Default.Equals(method.Parameters[1].Type, method.Parameters[2].Type); + } + + public static bool HasParameterWithDelegateType(this IMethodSymbol methodSymbol) + => methodSymbol.Parameters.Any(p => p.Type.TypeKind == TypeKind.Delegate); + + /// + /// Find out if the method overrides from target virtual method of a certain type + /// or it is the virtual method itself. + /// + /// The method + /// The type has virtual method + public static bool IsOverrideOrVirtualMethodOf([NotNullWhen(returnValue: true)] this IMethodSymbol? methodSymbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? typeSymbol) + { + if (methodSymbol == null) + { + return false; + } + else + { + if (SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, typeSymbol)) + { + return true; + } + else + { + return IsOverrideOrVirtualMethodOf(methodSymbol.OverriddenMethod, typeSymbol); + } + } + } + + /// + /// Returns true if this is a bool returning static method whose name starts with "IsNull" + /// with a single parameter whose type is not a value type. + /// For example, "static bool string.IsNullOrEmpty()" + /// + public static bool IsArgumentNullCheckMethod(this IMethodSymbol method) + { + return method.IsStatic && + method.ReturnType.SpecialType == SpecialType.System_Boolean && + method.Name.StartsWith("IsNull", StringComparison.Ordinal) && + method.Parameters.Length == 1 && + !method.Parameters[0].Type.IsValueType; + } + + public static bool IsBenchmarkOrXUnitTestMethod(this IMethodSymbol method, ConcurrentDictionary knownTestAttributes, INamedTypeSymbol? benchmarkAttribute, INamedTypeSymbol? xunitFactAttribute) + { + foreach (var attribute in method.GetAttributes()) + { + if (attribute.AttributeClass.IsBenchmarkOrXUnitTestAttribute(knownTestAttributes, benchmarkAttribute, xunitFactAttribute)) + return true; + } + + return false; + } + + /// + /// Check if a method is an auto-property accessor. + /// + public static bool IsAutoPropertyAccessor(this IMethodSymbol methodSymbol) + => methodSymbol.IsPropertyAccessor() + && methodSymbol.AssociatedSymbol is IPropertySymbol propertySymbol + && propertySymbol.IsAutoProperty(); + + /// + /// Check if the given is an implicitly generated method for top level statements. + /// + public static bool IsTopLevelStatementsEntryPointMethod([NotNullWhen(true)] this IMethodSymbol? methodSymbol) + => methodSymbol?.IsStatic == true && methodSymbol.Name switch + { + "$Main" => true, + "
$" => true, + _ => false + }; + + public static bool IsGetAwaiterFromAwaitablePattern([NotNullWhen(true)] this IMethodSymbol? method, + [NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType, + [NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType) + { + if (method is null + || !method.Name.Equals("GetAwaiter", StringComparison.Ordinal) + || method.Parameters.Length != 0) + { + return false; + } + + var returnType = method.ReturnType?.OriginalDefinition; + if (returnType is null) + { + return false; + } + + return returnType.DerivesFrom(inotifyCompletionType) || + returnType.DerivesFrom(icriticalNotifyCompletionType); + } + + public static bool IsGetResultFromAwaiterPattern( + [NotNullWhen(true)] this IMethodSymbol? method, + [NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType, + [NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType) + { + if (method is null + || !method.Name.Equals("GetResult", StringComparison.Ordinal) + || method.Parameters.Length != 0) + { + return false; + } + + var containingType = method.ContainingType?.OriginalDefinition; + if (containingType is null) + { + return false; + } + + return containingType.DerivesFrom(inotifyCompletionType) || + containingType.DerivesFrom(icriticalNotifyCompletionType); + } + + public static ImmutableArray GetOriginalDefinitions(this IMethodSymbol methodSymbol) + { + ImmutableArray.Builder originalDefinitionsBuilder = ImmutableArray.CreateBuilder(); + + if (methodSymbol.IsOverride && (methodSymbol.OverriddenMethod != null)) + { + originalDefinitionsBuilder.Add(methodSymbol.OverriddenMethod); + } + + if (!methodSymbol.ExplicitInterfaceImplementations.IsEmpty) + { + originalDefinitionsBuilder.AddRange(methodSymbol.ExplicitInterfaceImplementations); + } + + var typeSymbol = methodSymbol.ContainingType; + var methodSymbolName = methodSymbol.Name; + + originalDefinitionsBuilder.AddRange(typeSymbol.AllInterfaces + .SelectMany(m => m.GetMembers(methodSymbolName)) + .OfType() + .Where(m => methodSymbol.Parameters.Length == m.Parameters.Length + && methodSymbol.Arity == m.Arity + && typeSymbol.FindImplementationForInterfaceMember(m) != null)); + + return originalDefinitionsBuilder.ToImmutable(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs new file mode 100644 index 0000000000000..e2b083d0b19f4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs @@ -0,0 +1,294 @@ +// 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 warnings + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class INamedTypeSymbolExtensions + { + + private static readonly Func s_isFileLocal = LightupHelpers.CreateSymbolPropertyAccessor(typeof(INamedTypeSymbol), nameof(IsFileLocal), fallbackResult: false); + + public static bool IsFileLocal(this INamedTypeSymbol symbol) => s_isFileLocal(symbol); + + public static IEnumerable GetBaseTypesAndThis(this INamedTypeSymbol type) + { + INamedTypeSymbol current = type; + while (current != null) + { + yield return current; + current = current.BaseType; + } + } + + /// + /// Returns a value indicating whether derives from, or implements + /// any generic construction of, the type defined by . + /// + /// + /// This method only works when is a definition, + /// not a constructed type. + /// + /// + /// + /// If is the class , then this + /// method will return when called on Stack>int> + /// or any type derived it, because Stack>int> is constructed from + /// . + /// + /// + /// Similarly, if is the interface , + /// then this method will return for List>int> + /// or any other class that extends or an class that implements it, + /// because IList>int> is constructed from . + /// + /// + public static bool DerivesFromOrImplementsAnyConstructionOf(this INamedTypeSymbol type, INamedTypeSymbol parentType) + { + if (!parentType.IsDefinition) + { + throw new ArgumentException($"The type {nameof(parentType)} is not a definition; it is a constructed type", nameof(parentType)); + } + + for (INamedTypeSymbol? baseType = type.OriginalDefinition; + baseType != null; + baseType = baseType.BaseType?.OriginalDefinition) + { + if (baseType.Equals(parentType)) + { + return true; + } + } + + if (type.OriginalDefinition.AllInterfaces.Any(baseInterface => baseInterface.OriginalDefinition.Equals(parentType))) + { + return true; + } + + return false; + } + + public static bool ImplementsOperator(this INamedTypeSymbol symbol, string op) + { + // TODO: should this filter on the right-hand-side operator type? + return symbol.GetMembers(op).OfType().Any(m => m.MethodKind == MethodKind.UserDefinedOperator); + } + + /// + /// Returns a value indicating whether the specified type implements both the + /// equality and inequality operators. + /// + /// + /// A symbols specifying the type to examine. + /// + /// + /// true if the type specified by implements both the + /// equality and inequality operators, otherwise false. + /// + public static bool ImplementsEqualityOperators(this INamedTypeSymbol symbol) + { + return symbol.ImplementsOperator(WellKnownMemberNames.EqualityOperatorName) && + symbol.ImplementsOperator(WellKnownMemberNames.InequalityOperatorName); + } + + public static bool OverridesEquals(this INamedTypeSymbol symbol) + { + // Does the symbol override Object.Equals? + return symbol.GetMembers(WellKnownMemberNames.ObjectEquals).OfType().Any(m => m.IsObjectEqualsOverride()); + } + + public static bool OverridesGetHashCode(this INamedTypeSymbol symbol) + { + // Does the symbol override Object.GetHashCode? + return symbol.GetMembers(WellKnownMemberNames.ObjectGetHashCode).OfType().Any(m => m.IsGetHashCodeOverride()); + } + + public static bool HasFinalizer(this INamedTypeSymbol symbol) + { + return symbol.GetMembers() + .Any(m => m is IMethodSymbol method && method.IsFinalizer()); + } + + /// + /// Returns a value indicating whether the specified symbol is a static + /// holder type. + /// + /// + /// The symbol being examined. + /// + /// + /// if is a static holder type; + /// otherwise . + /// + /// + /// A symbol is a static holder type if it is a class with at least one + /// "qualifying member" () and no + /// "disqualifying members" (). + /// + public static bool IsStaticHolderType(this INamedTypeSymbol symbol) + { + if (symbol.TypeKind != TypeKind.Class) + { + return false; + } + + // If the class inherits from another object, or implements some interface, presumably the user meant for the class to be instantiated. This + // will also bail out if the user inherits from an empty interface, typically used as a marker of some kind. We assume that if _any_ interface + // is inherited, the user meant to instantiate the type. + if (symbol.BaseType == null || symbol.BaseType.SpecialType != SpecialType.System_Object || !symbol.AllInterfaces.IsDefaultOrEmpty) + { + return false; + } + + // Sealed objects are presumed to be non-static holder types for C#. + // In VB.NET the type cannot be static and guidelines favor having a sealed (NotInheritable) type + // to act as static holder type. + if (symbol.IsSealed && symbol.Language == LanguageNames.CSharp) + { + return false; + } + + // Same as + // return declaredMembers.Any(IsQualifyingMember) && !declaredMembers.Any(IsDisqualifyingMember); + // but with less enumerations + var hasQualifyingMembers = false; + foreach (var member in symbol.GetMembers()) + { + if (!member.IsImplicitlyDeclared) + { + if (!hasQualifyingMembers && IsQualifyingMember(member)) + { + hasQualifyingMembers = true; + } + + if (IsDisqualifyingMember(member)) + { + return false; + } + } + } + + return hasQualifyingMembers; + } + + /// + /// Returns a value indicating whether the specified symbol qualifies as a + /// member of a static holder class. + /// + /// + /// The member being examined. + /// + /// + /// if qualifies as a member of + /// a static holder class; otherwise . + /// + private static bool IsQualifyingMember(ISymbol member) + { + // A type member *does* qualify as a member of a static holder class, + // because even though it is *not* static, it is nevertheless not + // per-instance. + if (member.IsType()) + { + return true; + } + + // An user-defined operator method is not a valid member of a static holder + // class, because even though it is static, it takes instances as + // parameters, so presumably the author of the class intended for it to be + // instantiated. + if (member.IsUserDefinedOperator()) + { + return false; + } + + // A static constructor does not qualify or disqualify a class from being a + // static holder, because it isn't accessible to any consumers of the class. + if (member.IsConstructor()) + { + return false; + } + + // Private or protected members do not qualify or disqualify a class from + // being a static holder class, because they are not accessible to any + // consumers of the class. + if (member.IsProtected() || member.IsPrivate()) + { + return false; + } + + return member.IsStatic; + } + + /// + /// Returns a value indicating whether the presence of the specified symbol + /// disqualifies a class from being considered a static holder class. + /// + /// + /// The member being examined. + /// + /// + /// if the presence of disqualifies the + /// current type as a static holder class; otherwise . + /// + private static bool IsDisqualifyingMember(ISymbol member) + { + // An user-defined operator method disqualifies a class from being considered + // a static holder, because even though it is static, it takes instances as + // parameters, so presumably the author of the class intended for it to be + // instantiated. + if (member.IsUserDefinedOperator()) + { + return true; + } + + // Like user-defined operators, conversion operators disqualify a class + // from being considered a static holder, because it converts from an instance of + // another class to this class, so presumably the author intended for it to be + // instantiated + if (member.IsConversionOperator()) + { + return true; + } + + // A type member does *not* disqualify a class from being considered a static + // holder, because even though it is *not* static, it is nevertheless not + // per-instance. + if (member.IsType()) + { + return false; + } + + // Any instance member other than a default constructor disqualifies a class + // from being considered a static holder class. + return !member.IsStatic && !member.IsDefaultConstructor(); + } + + public static bool IsBenchmarkOrXUnitTestAttribute(this INamedTypeSymbol attributeClass, ConcurrentDictionary knownTestAttributes, INamedTypeSymbol? benchmarkAttribute, INamedTypeSymbol? xunitFactAttribute) + { + if (knownTestAttributes.TryGetValue(attributeClass, out var isTest)) + return isTest; + + var derivedFromKnown = + (xunitFactAttribute is not null && attributeClass.DerivesFrom(xunitFactAttribute)) + || (benchmarkAttribute is not null && attributeClass.DerivesFrom(benchmarkAttribute)); + return knownTestAttributes.GetOrAdd(attributeClass, derivedFromKnown); + } + + /// + /// Check if the given is an implicitly generated type for top level statements. + /// + public static bool IsTopLevelStatementsEntryPointType([NotNullWhen(true)] this INamedTypeSymbol? typeSymbol) + => typeSymbol is not null && + typeSymbol.GetMembers().OfType().Any(m => m.IsTopLevelStatementsEntryPointMethod()); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IOperationExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IOperationExtensions.cs new file mode 100644 index 0000000000000..2862653cfb163 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IOperationExtensions.cs @@ -0,0 +1,1159 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities.Lightup; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class IOperationExtensions + { + /// + /// Gets the receiver type for an invocation expression (i.e. type of 'A' in invocation 'A.B()') + /// If the invocation actually involves a conversion from A to some other type, say 'C', on which B is invoked, + /// then this method returns type A if is true, and C if false. + /// + public static ITypeSymbol? GetReceiverType(this IInvocationOperation invocation, Compilation compilation, bool beforeConversion, CancellationToken cancellationToken) + { + if (invocation.Instance != null) + { + return beforeConversion ? + GetReceiverType(invocation.Instance.Syntax, compilation, cancellationToken) : + invocation.Instance.Type; + } + else if (invocation.TargetMethod.IsExtensionMethod && !invocation.TargetMethod.Parameters.IsEmpty) + { + var firstArg = invocation.Arguments.FirstOrDefault(); + if (firstArg != null) + { + return beforeConversion ? + GetReceiverType(firstArg.Value.Syntax, compilation, cancellationToken) : + firstArg.Value.Type; + } + else if (invocation.TargetMethod.Parameters[0].IsParams) + { + return invocation.TargetMethod.Parameters[0].Type; + } + } + + return null; + } + + private static ITypeSymbol? GetReceiverType(SyntaxNode receiverSyntax, Compilation compilation, CancellationToken cancellationToken) + { + var model = compilation.GetSemanticModel(receiverSyntax.SyntaxTree); + var typeInfo = model.GetTypeInfo(receiverSyntax, cancellationToken); + return typeInfo.Type; + } + + public static bool HasNullConstantValue(this IOperation operation) + { + return operation.ConstantValue.HasValue && operation.ConstantValue.Value == null; + } + + public static bool TryGetBoolConstantValue(this IOperation operation, out bool constantValue) + { + if (operation.ConstantValue.HasValue && operation.ConstantValue.Value is bool value) + { + constantValue = value; + return true; + } + + constantValue = false; + return false; + } + + public static bool HasConstantValue(this IOperation operation, long comparand) + { + return operation.HasConstantValue(unchecked((ulong)comparand)); + } + + public static bool HasConstantValue(this IOperation operation, ulong comparand) + { + var constantValue = operation.ConstantValue; + if (!constantValue.HasValue) + { + return false; + } + + if (operation.Type == null || operation.Type.IsErrorType()) + { + return false; + } + + if (operation.Type.IsPrimitiveType()) + { + return HasConstantValue(constantValue, operation.Type, comparand); + } + + if (operation.Type.TypeKind == TypeKind.Enum) + { + var enumUnderlyingType = ((INamedTypeSymbol)operation.Type).EnumUnderlyingType; + return enumUnderlyingType != null && + enumUnderlyingType.IsPrimitiveType() && + HasConstantValue(constantValue, enumUnderlyingType, comparand); + } + + return false; + } + + private static bool HasConstantValue(Optional constantValue, ITypeSymbol constantValueType, ulong comparand) + { + if (constantValueType.SpecialType is SpecialType.System_Double or SpecialType.System_Single) + { + return (double?)constantValue.Value == comparand; + } + + return DiagnosticHelpers.TryConvertToUInt64(constantValue.Value, constantValueType.SpecialType, out ulong convertedValue) && convertedValue == comparand; + } + + public static ITypeSymbol? GetElementType(this IArrayCreationOperation? arrayCreation) + { + return (arrayCreation?.Type as IArrayTypeSymbol)?.ElementType; + } + + /// + /// Filters out operations that are implicit and have no explicit descendant with a constant value or a non-null type. + /// + public static ImmutableArray WithoutFullyImplicitOperations(this ImmutableArray operations) + { + ImmutableArray.Builder? builder = null; + for (int i = 0; i < operations.Length; i++) + { + var operation = operations[i]; + + // Check if all descendants are either implicit or are explicit with no constant value or type, indicating it is not user written code. + if (operation.DescendantsAndSelf().All(o => o.IsImplicit || (!o.ConstantValue.HasValue && o.Type == null))) + { + if (builder == null) + { + builder = ImmutableArray.CreateBuilder(); + builder.AddRange(operations, i); + } + } + else + { + builder?.Add(operation); + } + } + + return builder != null ? builder.ToImmutable() : operations; + } + + /// + /// Gets explicit descendants or self of the given that have no explicit ancestor in + /// the operation tree rooted at . + /// + /// Operation + public static ImmutableArray GetTopmostExplicitDescendants(this IOperation operation) + { + if (!operation.IsImplicit) + { + return ImmutableArray.Create(operation); + } + + var builder = ImmutableArray.CreateBuilder(); + var operationsToProcess = new Queue(); + operationsToProcess.Enqueue(operation); + + while (operationsToProcess.Count > 0) + { + operation = operationsToProcess.Dequeue(); + if (!operation.IsImplicit) + { + builder.Add(operation); + } + else + { + foreach (var child in operation.Children) + { + operationsToProcess.Enqueue(child); + } + } + } + + return builder.ToImmutable(); + } + + /// + /// True if this operation has no IOperation API support, i.e. and + /// is the root operation, i.e. is null. + /// For example, this returns true for attribute operations. + /// + public static bool IsOperationNoneRoot(this IOperation operation) + { + return operation.Kind == OperationKind.None && operation.Parent == null; + } + + /// + /// Returns the topmost containing the given . + /// + public static IBlockOperation? GetTopmostParentBlock(this IOperation? operation) + { + IOperation? currentOperation = operation; + IBlockOperation? topmostBlockOperation = null; + while (currentOperation != null) + { + if (currentOperation is IBlockOperation blockOperation) + { + topmostBlockOperation = blockOperation; + } + + currentOperation = currentOperation.Parent; + } + + return topmostBlockOperation; + } + + /// + /// Returns the first in the parent chain of . + /// + public static IBlockOperation? GetFirstParentBlock(this IOperation? operation) + { + IOperation? currentOperation = operation; + while (currentOperation != null) + { + if (currentOperation is IBlockOperation blockOperation) + { + return blockOperation; + } + + currentOperation = currentOperation.Parent; + } + + return null; + } + + /// + /// Gets the first ancestor of this operation with: + /// 1. Specified OperationKind + /// 2. If is non-null, it succeeds for the ancestor. + /// Returns null if there is no such ancestor. + /// + public static TOperation? GetAncestor(this IOperation root, OperationKind ancestorKind, Func? predicate = null) + where TOperation : class, IOperation + { + if (root == null) + { + throw new ArgumentNullException(nameof(root)); + } + + var ancestor = root; + do + { + ancestor = ancestor.Parent; + } while (ancestor != null && ancestor.Kind != ancestorKind); + + if (ancestor != null) + { + if (predicate != null && !predicate((TOperation)ancestor)) + { + return GetAncestor(ancestor, ancestorKind, predicate); + } + + return (TOperation)ancestor; + } + else + { + return null; + } + } + + /// + /// Gets the first ancestor of this operation with: + /// 1. Any OperationKind from the specified . + /// 2. If is non-null, it succeeds for the ancestor. + /// Returns null if there is no such ancestor. + /// + public static IOperation? GetAncestor(this IOperation root, ImmutableArray ancestorKinds, Func? predicate = null) + { + if (root == null) + { + throw new ArgumentNullException(nameof(root)); + } + + var ancestor = root; + do + { + ancestor = ancestor.Parent; + } while (ancestor != null && !ancestorKinds.Contains(ancestor.Kind)); + + if (ancestor != null) + { + if (predicate != null && !predicate(ancestor)) + { + return GetAncestor(ancestor, ancestorKinds, predicate); + } + + return ancestor; + } + else + { + return null; + } + } + + public static IConditionalAccessOperation? GetConditionalAccess(this IConditionalAccessInstanceOperation operation) + { + return operation.GetAncestor(OperationKind.ConditionalAccess, (IConditionalAccessOperation c) => c.Operation.Syntax == operation.Syntax); + } + + /// + /// Gets the operation for the object being created that is being referenced by . + /// If the operation is referencing an implicit or an explicit this/base/Me/MyBase/MyClass instance, then we return "null". + /// + /// + /// Flag to indicate if the operation is a descendant of an . + /// + /// PERF: Note that the parameter is to improve performance by avoiding walking the entire IOperation parent for non-initializer cases. + /// + public static IOperation? GetInstance(this IInstanceReferenceOperation operation, bool isInsideAnonymousObjectInitializer) + { + Debug.Assert(isInsideAnonymousObjectInitializer == + (operation.GetAncestor(OperationKind.AnonymousObjectCreation) != null)); + + if (isInsideAnonymousObjectInitializer) + { + for (IOperation? current = operation; current != null && current.Kind != OperationKind.Block; current = current.Parent) + { + switch (current.Kind) + { + // VB object initializer allows accessing the members of the object being created with "." operator. + // The syntax of such an IInstanceReferenceOperation points to the object being created. + // Check for such an IAnonymousObjectCreationOperation with matching syntax. + // For example, instance reference for members ".Field1" and ".Field2" in "New C() With { .Field1 = 0, .Field2 = .Field1 }". + case OperationKind.AnonymousObjectCreation: + if (current.Syntax == operation.Syntax) + { + return current; + } + + break; + } + } + } + + // For all other cases, IInstanceReferenceOperation refers to the implicit or explicit this/base/Me/MyBase/MyClass reference. + // We return null for such cases. + return null; + } + + public static bool HasAnyOperationDescendant(this ImmutableArray operationBlocks, Func predicate) + { + foreach (var operationBlock in operationBlocks) + { + if (operationBlock.HasAnyOperationDescendant(predicate)) + { + return true; + } + } + + return false; + } + + public static bool HasAnyOperationDescendant(this IOperation operationBlock, Func predicate) + { + return operationBlock.HasAnyOperationDescendant(predicate, out _); + } + + public static bool HasAnyOperationDescendant(this IOperation operationBlock, Func predicate, [NotNullWhen(returnValue: true)] out IOperation? foundOperation) + { + foreach (var descendant in operationBlock.DescendantsAndSelf()) + { + if (predicate(descendant)) + { + foundOperation = descendant; + return true; + } + } + + foundOperation = null; + return false; + } + + public static bool HasAnyOperationDescendant(this ImmutableArray operationBlocks, OperationKind kind) + { + return operationBlocks.HasAnyOperationDescendant(predicate: operation => operation.Kind == kind); + } + + /// + /// Indicates if the given is a predicate operation used in a condition. + /// + /// + /// + public static bool IsComparisonOperator(this IBinaryOperation binaryOperation) + => binaryOperation.OperatorKind switch + { + BinaryOperatorKind.Equals + or BinaryOperatorKind.NotEquals + or BinaryOperatorKind.ObjectValueEquals + or BinaryOperatorKind.ObjectValueNotEquals + or BinaryOperatorKind.LessThan + or BinaryOperatorKind.LessThanOrEqual + or BinaryOperatorKind.GreaterThan + or BinaryOperatorKind.GreaterThanOrEqual => true, + _ => false, + }; + + /// + /// Indicates if the given is an addition or substaction operation. + /// + /// + /// true if the operation is addition or substruction + public static bool IsAdditionOrSubstractionOperation(this IBinaryOperation binaryOperation, out char binaryOperator) + { + binaryOperator = '\0'; + switch (binaryOperation.OperatorKind) + { + case BinaryOperatorKind.Add: + binaryOperator = '+'; return true; + case BinaryOperatorKind.Subtract: + binaryOperator = '-'; return true; + } + + return false; + } + + public static IOperation GetRoot(this IOperation operation) + { + while (operation.Parent != null) + { + operation = operation.Parent; + } + + return operation; + } + + /// + /// PERF: Cache from operation roots to their corresponding to enable interprocedural flow analysis + /// across analyzers and analyzer callbacks to re-use the control flow graph. + /// + /// Also see + private static readonly BoundedCache> s_operationToCfgCache + = new(); + + public static bool TryGetEnclosingControlFlowGraph(this IOperation operation, [NotNullWhen(returnValue: true)] out ControlFlowGraph? cfg) + { + operation = operation.GetRoot(); + RoslynDebug.Assert(operation.SemanticModel is not null); + var operationToCfgMap = s_operationToCfgCache.GetOrCreateValue(operation.SemanticModel.Compilation); + cfg = operationToCfgMap.GetOrAdd(operation, CreateControlFlowGraph); + return cfg != null; + } + + public static ControlFlowGraph? GetEnclosingControlFlowGraph(this IBlockOperation blockOperation) + { + var success = blockOperation.TryGetEnclosingControlFlowGraph(out var cfg); + Debug.Assert(success); + Debug.Assert(cfg != null); + return cfg; + } + + private static ControlFlowGraph? CreateControlFlowGraph(IOperation operation) + { + switch (operation) + { + case IBlockOperation blockOperation: + return ControlFlowGraph.Create(blockOperation); + + case IMethodBodyOperation methodBodyOperation: + return ControlFlowGraph.Create(methodBodyOperation); + + case IConstructorBodyOperation constructorBodyOperation: + return ControlFlowGraph.Create(constructorBodyOperation); + + case IFieldInitializerOperation fieldInitializerOperation: + return ControlFlowGraph.Create(fieldInitializerOperation); + + case IPropertyInitializerOperation propertyInitializerOperation: + return ControlFlowGraph.Create(propertyInitializerOperation); + + case IParameterInitializerOperation: + // We do not support flow analysis for parameter initializers + return null; + + default: + // Attribute blocks have OperationKind.None (prior to IAttributeOperation support) or + // OperationKind.Attribute, but we do not support flow analysis for attributes. + // Gracefully return null for this case and fire an assert for any other OperationKind. + Debug.Assert(operation.Kind is OperationKind.None or OperationKindEx.Attribute, $"Unexpected root operation kind: {operation.Kind}"); + return null; + } + } + + /// + /// Gets the symbols captured from the enclosing function(s) by the given lambda or local function. + /// + /// Operation representing the lambda or local function. + /// Method symbol for the lambda or local function. + public static PooledHashSet GetCaptures(this IOperation operation, IMethodSymbol lambdaOrLocalFunction) + { + Debug.Assert(operation is IAnonymousFunctionOperation anonymousFunction && anonymousFunction.Symbol.OriginalDefinition.ReturnTypeAndParametersAreSame(lambdaOrLocalFunction.OriginalDefinition) || + operation is ILocalFunctionOperation localFunction && localFunction.Symbol.OriginalDefinition.Equals(lambdaOrLocalFunction.OriginalDefinition)); + + lambdaOrLocalFunction = lambdaOrLocalFunction.OriginalDefinition; + + var builder = PooledHashSet.GetInstance(); + using var nestedLambdasAndLocalFunctions = PooledHashSet.GetInstance(); + nestedLambdasAndLocalFunctions.Add(lambdaOrLocalFunction); + + foreach (var child in operation.Descendants()) + { + switch (child.Kind) + { + case OperationKind.LocalReference: + ProcessLocalOrParameter(((ILocalReferenceOperation)child).Local); + break; + + case OperationKind.ParameterReference: + ProcessLocalOrParameter(((IParameterReferenceOperation)child).Parameter); + break; + + case OperationKind.InstanceReference: + builder.Add(lambdaOrLocalFunction.ContainingType); + break; + + case OperationKind.AnonymousFunction: + nestedLambdasAndLocalFunctions.Add(((IAnonymousFunctionOperation)child).Symbol); + break; + + case OperationKind.LocalFunction: + nestedLambdasAndLocalFunctions.Add(((ILocalFunctionOperation)child).Symbol); + break; + } + } + + return builder; + + // Local functions. + void ProcessLocalOrParameter(ISymbol symbol) + { + if (symbol.ContainingSymbol?.Kind == SymbolKind.Method && + !nestedLambdasAndLocalFunctions.Contains(symbol.ContainingSymbol.OriginalDefinition)) + { + builder.Add(symbol); + } + } + } + + private static readonly ImmutableArray s_LambdaAndLocalFunctionKinds = + ImmutableArray.Create(OperationKind.AnonymousFunction, OperationKind.LocalFunction); + + public static bool IsWithinLambdaOrLocalFunction(this IOperation operation, [NotNullWhen(true)] out IOperation? containingLambdaOrLocalFunctionOperation) + { + containingLambdaOrLocalFunctionOperation = operation.GetAncestor(s_LambdaAndLocalFunctionKinds); + return containingLambdaOrLocalFunctionOperation != null; + } + + public static bool IsWithinExpressionTree(this IOperation operation, [NotNullWhen(true)] INamedTypeSymbol? linqExpressionTreeType) + => linqExpressionTreeType != null + && operation.GetAncestor(s_LambdaAndLocalFunctionKinds)?.Parent?.Type?.OriginalDefinition is { } lambdaType + && linqExpressionTreeType.Equals(lambdaType); + + public static ITypeSymbol? GetPatternType(this IPatternOperation pattern) + { + return pattern switch + { +#if CODEANALYSIS_V3_OR_BETTER + IDeclarationPatternOperation declarationPattern => declarationPattern.MatchedType, + IRecursivePatternOperation recursivePattern => recursivePattern.MatchedType, + IDiscardPatternOperation discardPattern => discardPattern.InputType, +#else + IDeclarationPatternOperation declarationPattern => declarationPattern.DeclaredSymbol switch + { + ILocalSymbol local => local.Type, + + IDiscardSymbol discard => discard.Type, + + _ => null, + }, +#endif + IConstantPatternOperation constantPattern => constantPattern.Value.Type, + + _ => null, + }; + } + + /// + /// If the given is a nested tuple, + /// gets the parenting tuple operation and the tuple element of that parenting tuple + /// which contains the given tupleOperation as a descendant operation. + /// + public static bool TryGetParentTupleOperation(this ITupleOperation tupleOperation, + [NotNullWhen(returnValue: true)] out ITupleOperation? parentTupleOperation, + [NotNullWhen(returnValue: true)] out IOperation? elementOfParentTupleContainingTuple) + { + parentTupleOperation = null; + elementOfParentTupleContainingTuple = null; + + IOperation previousOperation = tupleOperation; + var currentOperation = tupleOperation.Parent; + while (currentOperation != null) + { + switch (currentOperation.Kind) + { + case OperationKind.Parenthesized: + case OperationKind.Conversion: + case OperationKind.DeclarationExpression: + previousOperation = currentOperation; + currentOperation = currentOperation.Parent; + continue; + + case OperationKind.Tuple: + parentTupleOperation = (ITupleOperation)currentOperation; + elementOfParentTupleContainingTuple = previousOperation; + return true; + + default: + return false; + } + } + + return false; + } + + public static bool IsExtensionMethodAndHasNoInstance(this IInvocationOperation invocationOperation) + { + // This method exists to abstract away the language specific differences between IInvocationOperation implementations + // See https://github.com/dotnet/roslyn/issues/23625 for more details + return invocationOperation.TargetMethod.IsExtensionMethod && (invocationOperation.Language != LanguageNames.VisualBasic || invocationOperation.Instance == null); + } + + public static IOperation? GetInstance(this IInvocationOperation invocationOperation) + => invocationOperation.IsExtensionMethodAndHasNoInstance() ? invocationOperation.Arguments[0].Value : invocationOperation.Instance; + + public static SyntaxNode? GetInstanceSyntax(this IInvocationOperation invocationOperation) + => invocationOperation.GetInstance()?.Syntax; + + public static ITypeSymbol? GetInstanceType(this IOperation operation) + { + IOperation? instance = operation switch + { + IInvocationOperation invocation => invocation.GetInstance(), + + IPropertyReferenceOperation propertyReference => propertyReference.Instance, + + _ => throw new NotImplementedException() + }; + + return instance?.WalkDownConversion().Type; + } + + public static ISymbol? GetReferencedMemberOrLocalOrParameter(this IOperation? operation) + { + return operation switch + { + IMemberReferenceOperation memberReference => memberReference.Member, + + IParameterReferenceOperation parameterReference => parameterReference.Parameter, + + ILocalReferenceOperation localReference => localReference.Local, + + IParenthesizedOperation parenthesized => parenthesized.Operand.GetReferencedMemberOrLocalOrParameter(), + + IConversionOperation conversion => conversion.Operand.GetReferencedMemberOrLocalOrParameter(), + + _ => null, + }; + } + + /// + /// Walks down consecutive parenthesized operations until an operand is reached that isn't a parenthesized operation. + /// + /// The starting operation. + /// The inner non parenthesized operation or the starting operation if it wasn't a parenthesized operation. + public static IOperation WalkDownParentheses(this IOperation operation) + { + while (operation is IParenthesizedOperation parenthesizedOperation) + { + operation = parenthesizedOperation.Operand; + } + + return operation; + } + + [return: NotNullIfNotNull(nameof(operation))] + public static IOperation? WalkUpParentheses(this IOperation? operation) + { + if (operation is null) + return null; + + while (operation.Parent is IParenthesizedOperation parenthesizedOperation) + { + operation = parenthesizedOperation; + } + + return operation; + } + + /// + /// Walks down consecutive conversion operations until an operand is reached that isn't a conversion operation. + /// + /// The starting operation. + /// The inner non conversion operation or the starting operation if it wasn't a conversion operation. + public static IOperation WalkDownConversion(this IOperation operation) + { + while (operation is IConversionOperation conversionOperation) + { + operation = conversionOperation.Operand; + } + + return operation; + } + + /// + /// Walks down consecutive conversion operations that satisfy until an operand is reached that + /// either isn't a conversion or doesn't satisfy . + /// + /// The starting operation. + /// A predicate to filter conversion operations. + /// The first operation that either isn't a conversion or doesn't satisfy . + public static IOperation WalkDownConversion(this IOperation operation, Func predicate) + { + while (operation is IConversionOperation conversionOperation && predicate(conversionOperation)) + { + operation = conversionOperation.Operand; + } + + return operation; + } + + [return: NotNullIfNotNull(nameof(operation))] + public static IOperation? WalkUpConversion(this IOperation? operation) + { + if (operation is null) + return null; + + while (operation.Parent is IConversionOperation conversionOperation) + { + operation = conversionOperation; + } + + return operation; + } + + public static IOperation? GetThrownException(this IThrowOperation operation) + { + var thrownObject = operation.Exception; + + // Starting C# 8.0, C# compiler wraps the thrown operation within an implicit conversion to System.Exception type. + // We also want to walk down explicit conversions such as "throw (Exception)new ArgumentNullException())". + if (thrownObject is IConversionOperation conversion && + conversion.Conversion.Exists) + { + thrownObject = conversion.Operand; + } + + return thrownObject; + } + + public static ITypeSymbol? GetThrownExceptionType(this IThrowOperation operation) + => operation.GetThrownException()?.Type; + + /// + /// Determines if the one of the invocation's arguments' values is an argument of the specified type, and if so, find + /// the first one. + /// + /// Invocation operation whose arguments to look through. + /// First found IArgumentOperation.Value of the specified type, order by the method's + /// signature's parameters (as opposed to how arguments are specified when invoked). + /// True if one is found, false otherwise. + /// + /// IInvocationOperation.Arguments are ordered by how they are specified, which may differ from the order in the method + /// signature if the caller specifies arguments by name. This will find the first typeof operation ordered by the + /// method signature's parameters. + /// + public static bool HasArgument( + this IInvocationOperation invocationOperation, + [NotNullWhen(returnValue: true)] out TOperation? firstFoundArgument) + where TOperation : class, IOperation + { + firstFoundArgument = null; + int minOrdinal = int.MaxValue; + foreach (IArgumentOperation argumentOperation in invocationOperation.Arguments) + { + if (argumentOperation.Parameter?.Ordinal < minOrdinal && argumentOperation.Value is TOperation to) + { + minOrdinal = argumentOperation.Parameter.Ordinal; + firstFoundArgument = to; + } + } + + return firstFoundArgument != null; + } + + public static bool HasAnyExplicitDescendant(this IOperation operation, Func? descendIntoOperation = null) + { + using var stack = ArrayBuilder>.GetInstance(); + stack.Add(operation.Children.GetEnumerator()); + + while (stack.Any()) + { + var enumerator = stack.Last(); + stack.RemoveLast(); + if (enumerator.MoveNext()) + { + var current = enumerator.Current; + stack.Add(enumerator); + + if (current != null && + (descendIntoOperation == null || descendIntoOperation(current))) + { + if (!current.IsImplicit && + // This prevents non explicit operations like expression to be considered as ok + (current.ConstantValue.HasValue || current.Type != null)) + { + return true; + } + + stack.Add(current.Children.GetEnumerator()); + } + } + } + + return false; + } + + public static bool IsSetMethodInvocation(this IPropertyReferenceOperation operation) + { + if (operation.Property.SetMethod is null) + { + // This is either invalid code, or an assignment through a ref-returning getter + return false; + } + + IOperation potentialLeftSide = operation; + while (potentialLeftSide.Parent is IParenthesizedOperation or ITupleOperation) + { + potentialLeftSide = potentialLeftSide.Parent; + } + + return potentialLeftSide.Parent switch + { + IAssignmentOperation { Target: var target } when target == potentialLeftSide => true, + _ => false, + }; + } + + public static bool TryGetArgumentForParameterAtIndex( + this ImmutableArray arguments, + int parameterIndex, + [NotNullWhen(true)] out IArgumentOperation? result) + { + Debug.Assert(parameterIndex >= 0); + Debug.Assert(parameterIndex < arguments.Length); + + foreach (var argument in arguments) + { + if (argument.Parameter?.Ordinal == parameterIndex) + { + result = argument; + return true; + } + } + + result = null; + return false; + } + + public static IArgumentOperation GetArgumentForParameterAtIndex( + this ImmutableArray arguments, + int parameterIndex) + { + if (TryGetArgumentForParameterAtIndex(arguments, parameterIndex, out var result)) + { + return result; + } + + throw new InvalidOperationException(); + } + + /// + /// Useful when named arguments used for a method call and you need them in the original parameter order. + /// + /// Arguments of the method + /// Returns the arguments in parameter order + public static ImmutableArray GetArgumentsInParameterOrder( + this ImmutableArray arguments) + { + using var parameterOrderedArguments = ArrayBuilder.GetInstance(arguments.Length, null!); + + foreach (var argument in arguments) + { + RoslynDebug.Assert(argument.Parameter is not null); + Debug.Assert(parameterOrderedArguments[argument.Parameter.Ordinal] == null); + parameterOrderedArguments[argument.Parameter.Ordinal] = argument; + } + + return parameterOrderedArguments.ToImmutableArray(); + } + + // Copied from roslyn https://github.com/dotnet/roslyn/blob/main/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/OperationExtensions.cs#L25 + +#if CODEANALYSIS_V3_OR_BETTER + /// + /// Returns the for the given operation. + /// This extension can be removed once https://github.com/dotnet/roslyn/issues/25057 is implemented. + /// + public static ValueUsageInfo GetValueUsageInfo(this IOperation operation, ISymbol containingSymbol) + { + /* + | code | Read | Write | ReadableRef | WritableRef | NonReadWriteRef | + | x.Prop = 1 | | ✔️ | | | | + | x.Prop += 1 | ✔️ | ✔️ | | | | + | x.Prop++ | ✔️ | ✔️ | | | | + | Foo(x.Prop) | ✔️ | | | | | + | Foo(x.Prop), | | | ✔️ | | | + where void Foo(in T v) + | Foo(out x.Prop) | | | | ✔️ | | + | Foo(ref x.Prop) | | | ✔️ | ✔️ | | + | nameof(x) | | | | | ✔️ | ️ + | sizeof(x) | | | | | ✔️ | ️ + | typeof(x) | | | | | ✔️ | ️ + | out var x | | ✔️ | | | | ️ + | case X x: | | ✔️ | | | | ️ + | obj is X x | | ✔️ | | | | + | ref var x = | | | ✔️ | ✔️ | | + | ref readonly var x = | | | ✔️ | | | + + */ + if (operation is ILocalReferenceOperation localReference && + localReference.IsDeclaration && + !localReference.IsImplicit) // Workaround for https://github.com/dotnet/roslyn/issues/30753 + { + // Declaration expression is a definition (write) for the declared local. + return ValueUsageInfo.Write; + } + else if (operation is IDeclarationPatternOperation) + { + switch (operation.Parent) + { + case IPatternCaseClauseOperation: + // A declaration pattern within a pattern case clause is a + // write for the declared local. + // For example, 'x' is defined and assigned the value from 'obj' below: + // switch (obj) + // { + // case X x: + // + return ValueUsageInfo.Write; + + case IRecursivePatternOperation: + // A declaration pattern within a recursive pattern is a + // write for the declared local. + // For example, 'x' is defined and assigned the value from 'obj' below: + // (obj) switch + // { + // (X x) => ... + // }; + // + return ValueUsageInfo.Write; + + case ISwitchExpressionArmOperation: + // A declaration pattern within a switch expression arm is a + // write for the declared local. + // For example, 'x' is defined and assigned the value from 'obj' below: + // obj switch + // { + // X x => ... + // + return ValueUsageInfo.Write; + + case IIsPatternOperation: + // A declaration pattern within an is pattern is a + // write for the declared local. + // For example, 'x' is defined and assigned the value from 'obj' below: + // if (obj is X x) + // + return ValueUsageInfo.Write; + + case IPropertySubpatternOperation: + // A declaration pattern within a property sub-pattern is a + // write for the declared local. + // For example, 'x' is defined and assigned the value from 'obj.Property' below: + // if (obj is { Property : int x }) + // + return ValueUsageInfo.Write; + + default: + Debug.Fail("Unhandled declaration pattern context"); + + // Conservatively assume read/write. + return ValueUsageInfo.ReadWrite; + } + } + + if (operation.Parent is IAssignmentOperation assignmentOperation && + assignmentOperation.Target == operation) + { + return operation.Parent.IsAnyCompoundAssignment() + ? ValueUsageInfo.ReadWrite + : ValueUsageInfo.Write; + } + else if (operation.Parent is IIncrementOrDecrementOperation) + { + return ValueUsageInfo.ReadWrite; + } + else if (operation.Parent is IParenthesizedOperation parenthesizedOperation) + { + // Note: IParenthesizedOperation is specific to VB, where the parens cause a copy, so this cannot be classified as a write. + Debug.Assert(parenthesizedOperation.Language == LanguageNames.VisualBasic); + + return parenthesizedOperation.GetValueUsageInfo(containingSymbol) & + ~(ValueUsageInfo.Write | ValueUsageInfo.Reference); + } + else if (operation.Parent is INameOfOperation or + ITypeOfOperation or + ISizeOfOperation) + { + return ValueUsageInfo.Name; + } + else if (operation.Parent is IArgumentOperation argumentOperation) + { + return argumentOperation.Parameter?.RefKind switch + { + RefKind.RefReadOnly => ValueUsageInfo.ReadableReference, + RefKind.Out => ValueUsageInfo.WritableReference, + RefKind.Ref => ValueUsageInfo.ReadableWritableReference, + _ => ValueUsageInfo.Read, + }; + } + else if (operation.Parent is IReturnOperation returnOperation) + { + return returnOperation.GetRefKind(containingSymbol) switch + { + RefKind.RefReadOnly => ValueUsageInfo.ReadableReference, + RefKind.Ref => ValueUsageInfo.ReadableWritableReference, + _ => ValueUsageInfo.Read, + }; + } + else if (operation.Parent is IConditionalOperation conditionalOperation) + { + if (operation == conditionalOperation.WhenTrue + || operation == conditionalOperation.WhenFalse) + { + return GetValueUsageInfo(conditionalOperation, containingSymbol); + } + else + { + return ValueUsageInfo.Read; + } + } + else if (operation.Parent is IReDimClauseOperation reDimClauseOperation && + reDimClauseOperation.Operand == operation) + { + return reDimClauseOperation.Parent is IReDimOperation { Preserve: true } + ? ValueUsageInfo.ReadWrite + : ValueUsageInfo.Write; + } + else if (operation.Parent is IDeclarationExpressionOperation declarationExpression) + { + return declarationExpression.GetValueUsageInfo(containingSymbol); + } + else if (operation.IsInLeftOfDeconstructionAssignment(out _)) + { + return ValueUsageInfo.Write; + } + else if (operation.Parent is IVariableInitializerOperation variableInitializerOperation && + variableInitializerOperation.Parent is IVariableDeclaratorOperation variableDeclaratorOperation) + { + switch (variableDeclaratorOperation.Symbol.RefKind) + { + case RefKind.Ref: + return ValueUsageInfo.ReadableWritableReference; + + case RefKind.RefReadOnly: + return ValueUsageInfo.ReadableReference; + } + } + + return ValueUsageInfo.Read; + } + + public static bool IsInLeftOfDeconstructionAssignment([DisallowNull] this IOperation? operation, out IDeconstructionAssignmentOperation? deconstructionAssignment) + { + deconstructionAssignment = null; + + var previousOperation = operation; + operation = operation.Parent; + + while (operation != null) + { + switch (operation.Kind) + { + case OperationKind.DeconstructionAssignment: + deconstructionAssignment = (IDeconstructionAssignmentOperation)operation; + return deconstructionAssignment.Target == previousOperation; + + case OperationKind.Tuple: + case OperationKind.Conversion: + case OperationKind.Parenthesized: + previousOperation = operation; + operation = operation.Parent; + continue; + + default: + return false; + } + } + + return false; + } + + public static RefKind GetRefKind(this IReturnOperation operation, ISymbol containingSymbol) + { + var containingMethod = TryGetContainingAnonymousFunctionOrLocalFunction(operation) ?? (containingSymbol as IMethodSymbol); + return containingMethod?.RefKind ?? RefKind.None; + } + + public static IMethodSymbol? TryGetContainingAnonymousFunctionOrLocalFunction(this IOperation? operation) + { + operation = operation?.Parent; + while (operation != null) + { + switch (operation.Kind) + { + case OperationKind.AnonymousFunction: + return ((IAnonymousFunctionOperation)operation).Symbol; + + case OperationKind.LocalFunction: + return ((ILocalFunctionOperation)operation).Symbol; + } + + operation = operation.Parent; + } + + return null; + } + + /// + /// Returns true if the given operation is a regular compound assignment, + /// i.e. such as a += b, + /// or a special null coalescing compound assignment, i.e. + /// such as a ??= b. + /// + public static bool IsAnyCompoundAssignment(this IOperation operation) + => operation switch + { + ICompoundAssignmentOperation or ICoalesceAssignmentOperation => true, + _ => false, + }; +#endif + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IPropertySymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IPropertySymbolExtensions.cs new file mode 100644 index 0000000000000..87b285bbfd305 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/IPropertySymbolExtensions.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class IPropertySymbolExtensions + { + /// + /// Check if a property is an auto-property. + /// TODO: Remove this helper when https://github.com/dotnet/roslyn/issues/46682 is handled. + /// + public static bool IsAutoProperty(this IPropertySymbol propertySymbol) + => propertySymbol.ContainingType.GetMembers().OfType().Any(f => f.IsImplicitlyDeclared && propertySymbol.Equals(f.AssociatedSymbol)); + + public static bool IsIsCompletedFromAwaiterPattern( + [NotNullWhen(true)] this IPropertySymbol? property, + [NotNullWhen(true)] INamedTypeSymbol? inotifyCompletionType, + [NotNullWhen(true)] INamedTypeSymbol? icriticalNotifyCompletionType) + { + if (property is null + || !property.Name.Equals("IsCompleted", StringComparison.Ordinal) + || property.Type?.SpecialType != SpecialType.System_Boolean) + { + return false; + } + + var containingType = property.ContainingType?.OriginalDefinition; + return containingType.DerivesFrom(inotifyCompletionType) + || containingType.DerivesFrom(icriticalNotifyCompletionType); + } + + public static ImmutableArray GetOriginalDefinitions(this IPropertySymbol propertySymbol) + { + ImmutableArray.Builder originalDefinitionsBuilder = ImmutableArray.CreateBuilder(); + + if (propertySymbol.IsOverride && (propertySymbol.OverriddenProperty != null)) + { + originalDefinitionsBuilder.Add(propertySymbol.OverriddenProperty); + } + + if (!propertySymbol.ExplicitInterfaceImplementations.IsEmpty) + { + originalDefinitionsBuilder.AddRange(propertySymbol.ExplicitInterfaceImplementations); + } + + var typeSymbol = propertySymbol.ContainingType; + var methodSymbolName = propertySymbol.Name; + + originalDefinitionsBuilder.AddRange(typeSymbol.AllInterfaces + .SelectMany(m => m.GetMembers(methodSymbolName)) + .OfType() + .Where(m => propertySymbol.Parameters.Length == m.Parameters.Length + && propertySymbol.IsIndexer == m.IsIndexer + && typeSymbol.FindImplementationForInterfaceMember(m) != null)); + + return originalDefinitionsBuilder.ToImmutable(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISetExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISetExtensions.cs new file mode 100644 index 0000000000000..b6edc953c2c3b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISetExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Collections.Generic +{ + internal static class ISetExtensions + { + public static void AddRange(this ISet set, IEnumerable values) + { + foreach (var value in values) + { + set.Add(value); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISymbolExtensions.cs new file mode 100644 index 0000000000000..eb07cd185d687 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ISymbolExtensions.cs @@ -0,0 +1,867 @@ +// 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 warnings + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class ISymbolExtensions + { + public static bool IsType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is ITypeSymbol typeSymbol && typeSymbol.IsType; + } + + public static bool IsAccessorMethod([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is IMethodSymbol accessorSymbol && + (accessorSymbol.IsPropertyAccessor() || accessorSymbol.IsEventAccessor()); + } + + public static IEnumerable GetAccessors(this ISymbol symbol) + { + switch (symbol.Kind) + { + case SymbolKind.Property: + var property = (IPropertySymbol)symbol; + if (property.GetMethod != null) + { + yield return property.GetMethod; + } + + if (property.SetMethod != null) + { + yield return property.SetMethod; + } + + break; + + case SymbolKind.Event: + var eventSymbol = (IEventSymbol)symbol; + if (eventSymbol.AddMethod != null) + { + yield return eventSymbol.AddMethod; + } + + if (eventSymbol.RemoveMethod != null) + { + yield return eventSymbol.RemoveMethod; + } + + if (eventSymbol.RaiseMethod != null) + { + yield return eventSymbol.RaiseMethod; + } + + break; + } + } + + public static bool IsDefaultConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol.IsConstructor() && symbol.GetParameters().IsEmpty; + } + + public static bool IsPublic(this ISymbol symbol) + { + return symbol.DeclaredAccessibility == Accessibility.Public; + } + + public static bool IsProtected(this ISymbol symbol) + { + return symbol.DeclaredAccessibility == Accessibility.Protected; + } + + public static bool IsPrivate(this ISymbol symbol) + { + return symbol.DeclaredAccessibility == Accessibility.Private; + } + + public static bool IsErrorType([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return + symbol is ITypeSymbol typeSymbol && + typeSymbol.TypeKind == TypeKind.Error; + } + + public static bool IsConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is IMethodSymbol { MethodKind: MethodKind.Constructor }; + } + + public static bool IsDestructor([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return (symbol as IMethodSymbol)?.IsFinalizer() ?? false; + } + + public static bool IsIndexer([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is IPropertySymbol { IsIndexer: true }; + } + + public static bool IsPropertyWithBackingField([NotNullWhen(returnValue: true)] this ISymbol? symbol, [NotNullWhen(true)] out IFieldSymbol? backingField) + { + if (symbol is IPropertySymbol propertySymbol) + { + foreach (ISymbol member in propertySymbol.ContainingType.GetMembers()) + { + if (member is IFieldSymbol associated && + associated.IsImplicitlyDeclared && + Equals(associated.AssociatedSymbol, propertySymbol)) + { + backingField = associated; + return true; + } + } + } + + backingField = null; + return false; + } + + /// + /// Determines if the given symbol is a backing field for a property. + /// + /// This symbol to check. + /// The property that this field symbol is backing. + /// True if the given symbol is a backing field for a property, false otherwise. + public static bool IsBackingFieldForProperty( + [NotNullWhen(returnValue: true)] this ISymbol? symbol, + [NotNullWhen(returnValue: true)] out IPropertySymbol? propertySymbol) + { + if (symbol is IFieldSymbol fieldSymbol + && fieldSymbol.IsImplicitlyDeclared + && fieldSymbol.AssociatedSymbol is IPropertySymbol p) + { + propertySymbol = p; + return true; + } + else + { + propertySymbol = null; + return false; + } + } + + public static bool IsUserDefinedOperator([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is IMethodSymbol { MethodKind: MethodKind.UserDefinedOperator }; + } + + public static bool IsConversionOperator([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is IMethodSymbol { MethodKind: MethodKind.Conversion }; + } + + public static ImmutableArray GetParameters(this ISymbol? symbol) + { + return symbol switch + { + IMethodSymbol m => m.Parameters, + IPropertySymbol p => p.Parameters, + _ => ImmutableArray.Empty, + }; + } + + /// + /// True if the symbol is externally visible outside this assembly. + /// + public static bool IsExternallyVisible(this ISymbol symbol) => + symbol.GetResultantVisibility() == SymbolVisibility.Public; + + public static SymbolVisibility GetResultantVisibility(this ISymbol symbol) + { + // Start by assuming it's visible. + SymbolVisibility visibility = SymbolVisibility.Public; + + switch (symbol.Kind) + { + case SymbolKind.Alias: + // Aliases are uber private. They're only visible in the same file that they + // were declared in. + return SymbolVisibility.Private; + + case SymbolKind.Parameter: + // Parameters are only as visible as their containing symbol + return GetResultantVisibility(symbol.ContainingSymbol); + + case SymbolKind.TypeParameter: + // Type Parameters are private. + return SymbolVisibility.Private; + } + + while (symbol != null && symbol.Kind != SymbolKind.Namespace) + { + switch (symbol.DeclaredAccessibility) + { + // If we see anything private, then the symbol is private. + case Accessibility.NotApplicable: + case Accessibility.Private: + return SymbolVisibility.Private; + + // If we see anything internal, then knock it down from public to + // internal. + case Accessibility.Internal: + case Accessibility.ProtectedAndInternal: + visibility = SymbolVisibility.Internal; + break; + + // For anything else (Public, Protected, ProtectedOrInternal), the + // symbol stays at the level we've gotten so far. + } + + symbol = symbol.ContainingSymbol; + } + + return visibility; + } + + public static bool MatchMemberDerivedByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && member.MetadataName == name && member.ContainingType.DerivesFrom(type); + } + + public static bool MatchMethodDerivedByName([NotNullWhen(returnValue: true)] this IMethodSymbol? method, INamedTypeSymbol type, string name) + { + return method != null && method.MatchMemberDerivedByName(type, name); + } + + public static bool MatchMethodByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && member.Kind == SymbolKind.Method && member.MatchMemberByName(type, name); + } + + public static bool MatchPropertyDerivedByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && member.Kind == SymbolKind.Property && member.MatchMemberDerivedByName(type, name); + } + + public static bool MatchMemberByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && Equals(member.ContainingType, type) && member.MetadataName == name; + } + + public static bool MatchPropertyByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && member.Kind == SymbolKind.Property && member.MatchMemberByName(type, name); + } + + public static bool MatchFieldByName([NotNullWhen(returnValue: true)] this ISymbol? member, INamedTypeSymbol type, string name) + { + return member != null && member.Kind == SymbolKind.Field && member.MatchMemberByName(type, name); + } + + // Define the format in for displaying member names. The format is chosen to be consistent + // consistent with FxCop's display format. + private static readonly SymbolDisplayFormat s_memberDisplayFormat = + // This format omits the namespace. + SymbolDisplayFormat.CSharpShortErrorMessageFormat + // Turn off the EscapeKeywordIdentifiers flag (which is on by default), so that + // a method named "@for" is displayed as "for". + // Turn on the UseSpecialTypes flat (which is off by default), so that parameter + // names of "special" types such as Int32 are displayed as their language alias, + // such as int for C# and Integer for VB. + .WithMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + /// + /// Format member names in a way consistent with FxCop's display format. + /// + /// + /// + /// A string representing the name of the member in a format consistent with FxCop. + /// + public static string FormatMemberName(this ISymbol member) + { + return member.ToDisplayString(s_memberDisplayFormat); + } + + /// + /// Check whether given parameters contains any parameter with given type. + /// + public static bool ContainsParameterOfType(this IEnumerable parameters, INamedTypeSymbol type) + { + var parametersOfType = GetParametersOfType(parameters, type); + return parametersOfType.Any(); + } + + /// + /// Get parameters which type is the given type + /// + public static IEnumerable GetParametersOfType(this IEnumerable parameters, INamedTypeSymbol type) + { + return parameters.Where(p => p.Type.Equals(type)); + } + + /// + /// Gets the parameters whose type is equal to the given special type. + /// + public static IEnumerable GetParametersOfType(this IEnumerable parameters, SpecialType specialType) + { + return parameters.Where(p => p.Type.SpecialType == specialType); + } + + /// + /// Check whether given overloads has any overload whose parameters has the given type as its parameter type. + /// + public static bool HasOverloadWithParameterOfType(this IEnumerable overloads, IMethodSymbol self, INamedTypeSymbol type, CancellationToken cancellationToken) + { + foreach (var overload in overloads) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (self?.Equals(overload) == true) + { + continue; + } + + if (overload.Parameters.ContainsParameterOfType(type)) + { + return true; + } + } + + return false; + } + + /// + /// Convert given parameters to the indices to the given method's parameter list. + /// + public static IEnumerable GetParameterIndices(this IMethodSymbol method, IEnumerable parameters, CancellationToken cancellationToken) + { + var set = new HashSet(parameters); + for (var i = 0; i < method.Parameters.Length; i++) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (set.Contains(method.Parameters[i])) + { + yield return i; + } + } + } + + /// + /// Check whether parameter count and parameter types of the given methods are same. + /// + public static bool ParametersAreSame(this IMethodSymbol method1, IMethodSymbol method2) + { + if (method1.Parameters.Length != method2.Parameters.Length) + { + return false; + } + + for (int index = 0; index < method1.Parameters.Length; index++) + { + if (!ParameterTypesAreSame(method1.Parameters[index], method2.Parameters[index])) + { + return false; + } + } + + return true; + } + + /// + /// Check whether parameter types of the given methods are same for given parameter indices. + /// + public static bool ParameterTypesAreSame(this IMethodSymbol method1, IMethodSymbol method2, IEnumerable parameterIndices, CancellationToken cancellationToken) + { + foreach (int index in parameterIndices) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!ParameterTypesAreSame(method1.Parameters[index], method2.Parameters[index])) + { + return false; + } + } + + return true; + } + + public static bool ParameterTypesAreSame(this IParameterSymbol parameter1, IParameterSymbol parameter2) + { + var type1 = parameter1.Type.OriginalDefinition; + var type2 = parameter2.Type.OriginalDefinition; + + if (type1.TypeKind == TypeKind.TypeParameter && + type2.TypeKind == TypeKind.TypeParameter && + ((ITypeParameterSymbol)type1).Ordinal == ((ITypeParameterSymbol)type2).Ordinal) + { + return true; + } + + // this doesn't account for type conversion but FxCop implementation seems doesn't either + // so this should match FxCop implementation. + return SymbolEqualityComparer.Default.Equals(type2, type1); + } + + /// + /// Check whether return type, parameters count and parameter types are same for the given methods. + /// + public static bool ReturnTypeAndParametersAreSame(this IMethodSymbol method, IMethodSymbol otherMethod) + => SymbolEqualityComparer.Default.Equals(method.ReturnType, otherMethod.ReturnType) && + method.ParametersAreSame(otherMethod); + + /// + /// Check whether given symbol is from mscorlib + /// + public static bool IsFromMscorlib(this ISymbol symbol, Compilation compilation) + { + var @object = compilation.GetSpecialType(SpecialType.System_Object); + return SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, @object.ContainingAssembly); + } + + /// + /// Get overload from the given overloads that matches given method signature + given parameter + /// + public static IMethodSymbol? GetMatchingOverload(this IMethodSymbol method, IEnumerable overloads, int parameterIndex, INamedTypeSymbol type, CancellationToken cancellationToken) + { + foreach (IMethodSymbol overload in overloads) + { + cancellationToken.ThrowIfCancellationRequested(); + + // does not account for method with optional parameters + if (SymbolEqualityComparer.Default.Equals(method, overload) || overload.Parameters.Length != method.Parameters.Length) + { + // either itself, or signature is not same + continue; + } + + if (!method.ParameterTypesAreSame(overload, Enumerable.Range(0, method.Parameters.Length).Where(i => i != parameterIndex), cancellationToken)) + { + // check whether remaining parameters match existing types, otherwise, we are not interested + continue; + } + + if (SymbolEqualityComparer.Default.Equals(overload.Parameters[parameterIndex].Type, type)) + { + // we no longer interested in this overload. there can be only 1 match + return overload; + } + } + + return null; + } + + /// + /// Checks if a given symbol implements an interface member implicitly or explicitly + /// + public static bool IsImplementationOfAnyInterfaceMember(this ISymbol symbol) + { + return symbol.IsImplementationOfAnyExplicitInterfaceMember() || symbol.IsImplementationOfAnyImplicitInterfaceMember(); + } + + public static bool IsImplementationOfAnyImplicitInterfaceMember(this ISymbol symbol) + { + return IsImplementationOfAnyImplicitInterfaceMember(symbol); + } + + /// + /// Checks if a given symbol implements an interface member implicitly + /// + public static bool IsImplementationOfAnyImplicitInterfaceMember(this ISymbol symbol) + where TSymbol : ISymbol + { + if (symbol.ContainingType != null) + { + foreach (INamedTypeSymbol interfaceSymbol in symbol.ContainingType.AllInterfaces) + { + foreach (var interfaceMember in interfaceSymbol.GetMembers().OfType()) + { + if (IsImplementationOfInterfaceMember(symbol, interfaceMember)) + { + return true; + } + } + } + } + + return false; + } + + /// + /// Checks if a given symbol implements an interface member implicitly + /// + public static bool IsImplementationOfAnyImplicitInterfaceMember(this ISymbol symbol, out TSymbol interfaceMember) + where TSymbol : ISymbol + { + if (symbol.ContainingType != null) + { + foreach (INamedTypeSymbol interfaceSymbol in symbol.ContainingType.AllInterfaces) + { + foreach (var baseInterfaceMember in interfaceSymbol.GetMembers().OfType()) + { + if (IsImplementationOfInterfaceMember(symbol, baseInterfaceMember)) + { + interfaceMember = baseInterfaceMember; + return true; + } + } + } + } + + interfaceMember = default; + return false; + } + + public static bool IsImplementationOfInterfaceMember(this ISymbol symbol, [NotNullWhen(returnValue: true)] ISymbol? interfaceMember) + { + return interfaceMember != null && + SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(interfaceMember)); + } + + /// + /// Checks if a given symbol implements an interface member or overrides an implementation of an interface member. + /// + public static bool IsOverrideOrImplementationOfInterfaceMember(this ISymbol symbol, [NotNullWhen(returnValue: true)] ISymbol? interfaceMember) + { + if (interfaceMember == null) + { + return false; + } + + if (symbol.IsImplementationOfInterfaceMember(interfaceMember)) + { + return true; + } + + return symbol.IsOverride && + symbol.GetOverriddenMember()?.IsOverrideOrImplementationOfInterfaceMember(interfaceMember) == true; + } + + /// + /// Gets the symbol overridden by the given . + /// + /// Requires that is true for the given . + public static ISymbol GetOverriddenMember(this ISymbol symbol) + { + Debug.Assert(symbol.IsOverride); + + return symbol switch + { + IMethodSymbol methodSymbol => methodSymbol.OverriddenMethod, + + IPropertySymbol propertySymbol => propertySymbol.OverriddenProperty, + + IEventSymbol eventSymbol => eventSymbol.OverriddenEvent, + + _ => throw new NotImplementedException(), + }; + } + + /// + /// Checks if a given symbol implements an interface member explicitly + /// + public static bool IsImplementationOfAnyExplicitInterfaceMember([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + if (symbol is IMethodSymbol methodSymbol && !methodSymbol.ExplicitInterfaceImplementations.IsEmpty) + { + return true; + } + + if (symbol is IPropertySymbol propertySymbol && !propertySymbol.ExplicitInterfaceImplementations.IsEmpty) + { + return true; + } + + if (symbol is IEventSymbol eventSymbol && !eventSymbol.ExplicitInterfaceImplementations.IsEmpty) + { + return true; + } + + return false; + } + + public static ITypeSymbol? GetMemberOrLocalOrParameterType(this ISymbol symbol) + { + return symbol.Kind switch + { + SymbolKind.Local => ((ILocalSymbol)symbol).Type, + + SymbolKind.Parameter => ((IParameterSymbol)symbol).Type, + + _ => GetMemberType(symbol), + }; + } + + public static ITypeSymbol? GetMemberType(this ISymbol? symbol) + { + return symbol switch + { + IEventSymbol eventSymbol => eventSymbol.Type, + + IFieldSymbol fieldSymbol => fieldSymbol.Type, + + IMethodSymbol methodSymbol => methodSymbol.ReturnType, + + IPropertySymbol propertySymbol => propertySymbol.Type, + + _ => null, + }; + } + + public static bool IsReadOnlyFieldOrProperty([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol switch + { + IFieldSymbol field => field.IsReadOnly, + + IPropertySymbol property => property.IsReadOnly, + + _ => false, + }; + } + + public static AttributeData? GetAttribute(this ISymbol symbol, [NotNullWhen(true)] INamedTypeSymbol? attributeType) + { + return symbol.GetAttributes(attributeType).FirstOrDefault(); + } + + public static IEnumerable GetAttributes(this ISymbol symbol, IEnumerable attributesToMatch) + { + foreach (var attribute in symbol.GetAttributes()) + { + if (attribute.AttributeClass == null) + continue; + + foreach (var attributeToMatch in attributesToMatch) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeToMatch)) + { + yield return attribute; + break; + } + } + } + } + + public static IEnumerable GetAttributes(this ISymbol symbol, params INamedTypeSymbol?[] attributeTypesToMatch) + { + return symbol.GetAttributes(attributesToMatch: attributeTypesToMatch); + } + + #region "Overloads to avoid array allocations" + public static IEnumerable GetAttributes(this ISymbol symbol, INamedTypeSymbol? attributeTypeToMatch1) + { + foreach (var attribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeTypeToMatch1)) + { + yield return attribute; + } + } + } + + public static IEnumerable GetAttributes(this ISymbol symbol, INamedTypeSymbol? attributeTypeToMatch1, INamedTypeSymbol? attributeTypeToMatch2) + { + foreach (var attribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeTypeToMatch1) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeTypeToMatch2)) + { + yield return attribute; + } + } + } + #endregion + + public static bool HasAnyAttribute(this ISymbol symbol, IEnumerable attributesToMatch) + { + return symbol.GetAttributes(attributesToMatch).Any(); + } + + public static bool HasAnyAttribute(this ISymbol symbol, params INamedTypeSymbol?[] attributeTypesToMatch) + { + return symbol.GetAttributes(attributeTypesToMatch).Any(); + } + + /// + /// Returns a value indicating whether the specified symbol has the specified + /// attribute. + /// + /// + /// The symbol being examined. + /// + /// + /// The attribute in question. + /// + /// + /// if has an attribute of type + /// ; otherwise . + /// + /// + /// If is a type, this method does not find attributes + /// on its base types. + /// + public static bool HasAnyAttribute(this ISymbol symbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? attribute) + { + if (attribute is null) + return false; + + foreach (var actualAttribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(actualAttribute.AttributeClass, attribute)) + return true; + } + + return false; + } + + /// + /// Returns a value indicating whether the specified or inherited symbol has the specified + /// attribute. + /// + /// + /// The symbol being examined. + /// + /// + /// The attribute in question. + /// + /// + /// if has an attribute of type + /// ; otherwise . + /// + public static bool HasDerivedTypeAttribute(this ITypeSymbol symbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? attribute) + { + if (attribute == null) + { + return false; + } + + while (symbol != null) + { + if (symbol.HasAnyAttribute(attribute)) + { + return true; + } + + if (symbol.BaseType == null) + { + return false; + } + + symbol = symbol.BaseType; + } + + return false; + } + + /// + /// Returns a value indicating whether the specified or inherited method symbol has the specified + /// attribute. + /// + /// + /// The symbol being examined. + /// + /// + /// The attribute in question. + /// + /// + /// if has an attribute of type + /// ; otherwise . + /// + public static bool HasDerivedMethodAttribute(this IMethodSymbol symbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? attribute) + { + if (attribute == null) + { + return false; + } + + while (symbol != null) + { + if (symbol.HasAnyAttribute(attribute)) + { + return true; + } + + if (symbol.OverriddenMethod == null) + { + return false; + } + + symbol = symbol.OverriddenMethod; + } + + return false; + } + + /// + /// Determines if the given symbol has the specified attributes. + /// + /// Symbol to examine. + /// Type symbols of the attributes to check for. + /// Boolean array, same size and order as , indicating that the corresponding + /// attribute is present. + public static bool[] HasAttributes(this ISymbol symbol, params INamedTypeSymbol?[] attributes) + { + bool[] isAttributePresent = new bool[attributes.Length]; + foreach (var attributeData in symbol.GetAttributes()) + { + for (int i = 0; i < attributes.Length; i++) + { + if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, attributes[i])) + { + isAttributePresent[i] = true; + } + } + } + + return isAttributePresent; + } + + /// + /// Indicates if a symbol has at least one location in source. + /// + public static bool IsInSource(this ISymbol symbol) + { + return symbol.Locations.Any(l => l.IsInSource); + } + + public static bool IsLambdaOrLocalFunction([NotNullWhen(returnValue: true)] this ISymbol? symbol) + => (symbol as IMethodSymbol)?.IsLambdaOrLocalFunction() == true; + + /// + /// Returns true for symbols whose name starts with an underscore and + /// are optionally followed by an integer or other underscores, such as '_', '_1', '_2', '__', '___', etc. + /// These symbols can be treated as special discard symbol names. + /// + public static bool IsSymbolWithSpecialDiscardName([NotNullWhen(returnValue: true)] this ISymbol? symbol) + => symbol?.Name.StartsWith("_", StringComparison.Ordinal) == true && + (symbol.Name.Length == 1 || uint.TryParse(symbol.Name[1..], out _) || symbol.Name.All(n => n.Equals('_'))); + + public static bool IsConst([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol switch + { + IFieldSymbol field => field.IsConst, + + ILocalSymbol local => local.IsConst, + + _ => false, + }; + } + + public static bool IsReadOnly([NotNullWhen(returnValue: true)] this ISymbol? symbol) + => symbol switch + { + IFieldSymbol field => field.IsReadOnly, + IPropertySymbol property => property.IsReadOnly, +#if CODEANALYSIS_V3_OR_BETTER + IMethodSymbol method => method.IsReadOnly, + ITypeSymbol type => type.IsReadOnly, +#endif + _ => false, + }; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ITypeSymbolExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ITypeSymbolExtensions.cs new file mode 100644 index 0000000000000..ebc098356a8cc --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ITypeSymbolExtensions.cs @@ -0,0 +1,395 @@ +// 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 warnings + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class ITypeSymbolExtensions + { +#if CODEANALYSIS_V3_OR_BETTER + public static bool IsAssignableTo( + [NotNullWhen(returnValue: true)] this ITypeSymbol? fromSymbol, + [NotNullWhen(returnValue: true)] ITypeSymbol? toSymbol, + Compilation compilation) + => fromSymbol != null && toSymbol != null && compilation.ClassifyCommonConversion(fromSymbol, toSymbol).IsImplicit; +#endif + + public static bool IsPrimitiveType(this ITypeSymbol type) + { + return type.SpecialType switch + { + SpecialType.System_Boolean + or SpecialType.System_Byte + or SpecialType.System_Char + or SpecialType.System_Double + or SpecialType.System_Int16 + or SpecialType.System_Int32 + or SpecialType.System_Int64 + or SpecialType.System_UInt16 + or SpecialType.System_UInt32 + or SpecialType.System_UInt64 + or SpecialType.System_SByte + or SpecialType.System_Single + or SpecialType.System_String => true, + _ => false, + }; + } + + public static bool Inherits([NotNullWhen(returnValue: true)] this ITypeSymbol? type, [NotNullWhen(returnValue: true)] ITypeSymbol? possibleBase) + { + if (type == null || possibleBase == null) + { + return false; + } + + switch (possibleBase.TypeKind) + { + case TypeKind.Class: + if (type.TypeKind == TypeKind.Interface) + { + return false; + } + + return DerivesFrom(type, possibleBase, baseTypesOnly: true); + + case TypeKind.Interface: + return DerivesFrom(type, possibleBase); + + default: + return false; + } + } + + public static IEnumerable GetBaseTypes(this ITypeSymbol type, Func? takeWhilePredicate = null) + { + INamedTypeSymbol current = type.BaseType; + while (current != null && + (takeWhilePredicate == null || takeWhilePredicate(current))) + { + yield return current; + current = current.BaseType; + } + } + + public static IEnumerable GetBaseTypesAndThis(this ITypeSymbol type) + { + ITypeSymbol current = type; + while (current != null) + { + yield return current; + current = current.BaseType; + } + } + + public static bool DerivesFrom([NotNullWhen(returnValue: true)] this ITypeSymbol? symbol, [NotNullWhen(returnValue: true)] ITypeSymbol? candidateBaseType, bool baseTypesOnly = false, bool checkTypeParameterConstraints = true) + { + if (candidateBaseType == null || symbol == null) + { + return false; + } + + if (!baseTypesOnly && candidateBaseType.TypeKind == TypeKind.Interface) + { + var allInterfaces = symbol.AllInterfaces.OfType(); + if (SymbolEqualityComparer.Default.Equals(candidateBaseType.OriginalDefinition, candidateBaseType)) + { + // Candidate base type is not a constructed generic type, so use original definition for interfaces. + allInterfaces = allInterfaces.Select(i => i.OriginalDefinition); + } + + if (allInterfaces.Contains(candidateBaseType)) + { + return true; + } + } + + if (checkTypeParameterConstraints && symbol.TypeKind == TypeKind.TypeParameter) + { + var typeParameterSymbol = (ITypeParameterSymbol)symbol; + foreach (var constraintType in typeParameterSymbol.ConstraintTypes) + { + if (constraintType.DerivesFrom(candidateBaseType, baseTypesOnly, checkTypeParameterConstraints)) + { + return true; + } + } + } + + while (symbol != null) + { + if (SymbolEqualityComparer.Default.Equals(symbol, candidateBaseType)) + { + return true; + } + + symbol = symbol.BaseType; + } + + return false; + } + + /// + /// Indicates if the given is disposable, + /// and thus can be used in a using or await using statement. + /// + public static bool IsDisposable(this ITypeSymbol type, + INamedTypeSymbol? iDisposable, + INamedTypeSymbol? iAsyncDisposable, + INamedTypeSymbol? configuredAsyncDisposable) + { + if (type.IsReferenceType) + { + return IsInterfaceOrImplementsInterface(type, iDisposable) + || IsInterfaceOrImplementsInterface(type, iAsyncDisposable); + } + else if (SymbolEqualityComparer.Default.Equals(type, configuredAsyncDisposable)) + { + return true; + } + +#if CODEANALYSIS_V3_OR_BETTER + if (type.IsRefLikeType) + { + return type.GetMembers("Dispose").OfType() + .Any(method => method.HasDisposeSignatureByConvention()); + } +#endif + + return false; + + static bool IsInterfaceOrImplementsInterface(ITypeSymbol type, INamedTypeSymbol? interfaceType) + => interfaceType != null && + (SymbolEqualityComparer.Default.Equals(type, interfaceType) || type.AllInterfaces.Contains(interfaceType)); + } + + /// + /// Gets all attributes directly applied to the type or inherited from a base type. + /// + /// The type symbol. + /// The compilation symbol for . + public static IEnumerable GetApplicableAttributes(this INamedTypeSymbol type, INamedTypeSymbol? attributeUsageAttribute) + { + var attributes = new List(); + var onlyIncludeInherited = false; + + while (type != null) + { + var current = type.GetAttributes(); + if (!onlyIncludeInherited || attributeUsageAttribute is null) + { + attributes.AddRange(current); + } + else + { + foreach (var attribute in current) + { + if (!IsInheritedAttribute(attribute, attributeUsageAttribute)) + { + continue; + } + + attributes.Add(attribute); + } + } + + type = type.BaseType; + onlyIncludeInherited = true; + } + + return attributes; + + // Local functions + static bool IsInheritedAttribute(AttributeData attributeData, INamedTypeSymbol attributeUsageAttribute) + { + for (var currentAttributeClass = attributeData.AttributeClass; + currentAttributeClass is object; + currentAttributeClass = currentAttributeClass.BaseType) + { + foreach (var attributeClassData in currentAttributeClass.GetAttributes()) + { + if (!SymbolEqualityComparer.Default.Equals(attributeClassData.AttributeClass, attributeUsageAttribute)) + { + continue; + } + + foreach (var (name, typedConstant) in attributeClassData.NamedArguments) + { + if (name != nameof(AttributeUsageAttribute.Inherited)) + { + continue; + } + + // The default is true, so use that when explicitly specified and for cases where the value + // is not a boolean (i.e. compilation error scenarios). + return !Equals(false, typedConstant.Value); + } + + // [AttributeUsage] was found, but did not specify Inherited explicitly. The default is true. + return true; + } + } + + // [AttributeUsage] was not found. The default is true. + return true; + } + } + + public static IEnumerable GetApplicableExportAttributes(this INamedTypeSymbol? type, INamedTypeSymbol? exportAttributeV1, INamedTypeSymbol? exportAttributeV2, INamedTypeSymbol? inheritedExportAttribute) + { + var attributes = new List(); + var onlyIncludeInherited = false; + + while (type != null) + { + var current = type.GetAttributes(); + foreach (var attribute in current) + { + if (attribute.AttributeClass.Inherits(inheritedExportAttribute)) + { + attributes.Add(attribute); + } + else if (!onlyIncludeInherited && + (attribute.AttributeClass.Inherits(exportAttributeV1) || attribute.AttributeClass.Inherits(exportAttributeV2))) + { + attributes.Add(attribute); + } + } + + if (inheritedExportAttribute is null) + { + break; + } + + type = type.BaseType; + onlyIncludeInherited = true; + } + + return attributes; + } + + public static bool IsAttribute(this ITypeSymbol symbol) + { + for (INamedTypeSymbol b = symbol.BaseType; b != null; b = b.BaseType) + { + if (b.MetadataName == "Attribute" && + b.ContainingType == null && + b.ContainingNamespace != null && + b.ContainingNamespace.Name == "System" && + b.ContainingNamespace.ContainingNamespace != null && + b.ContainingNamespace.ContainingNamespace.IsGlobalNamespace) + { + return true; + } + } + + return false; + } + + public static bool HasValueCopySemantics(this ITypeSymbol typeSymbol) + => typeSymbol.IsValueType || typeSymbol.SpecialType == SpecialType.System_String; + +#if !MICROSOFT_CODEANALYSIS_PUBLIC_API_ANALYZERS + public static bool CanHoldNullValue([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol.IsReferenceTypeOrNullableValueType() || + typeSymbol?.IsRefLikeType == true || + typeSymbol is ITypeParameterSymbol typeParameter && !typeParameter.IsValueType; +#endif + + public static bool IsNonNullableValueType([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol != null && typeSymbol.IsValueType && typeSymbol.OriginalDefinition.SpecialType != SpecialType.System_Nullable_T; + + public static bool IsNullableValueType([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol != null && typeSymbol.IsValueType && typeSymbol.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T; + + public static bool IsReferenceTypeOrNullableValueType([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol != null && (typeSymbol.IsReferenceType || typeSymbol.IsNullableValueType()); + + public static bool IsNullableOfBoolean([NotNullWhen(returnValue: true)] this ITypeSymbol? typeSymbol) + => typeSymbol.IsNullableValueType() && ((INamedTypeSymbol)typeSymbol).TypeArguments[0].SpecialType == SpecialType.System_Boolean; + + public static ITypeSymbol? GetNullableValueTypeUnderlyingType(this ITypeSymbol? typeSymbol) + => typeSymbol.IsNullableValueType() ? ((INamedTypeSymbol)typeSymbol).TypeArguments[0] : null; + +#if HAS_IOPERATION + public static ITypeSymbol? GetUnderlyingValueTupleTypeOrThis(this ITypeSymbol? typeSymbol) + => (typeSymbol as INamedTypeSymbol)?.TupleUnderlyingType ?? typeSymbol; +#endif + + /// + /// Checks whether the current type contains one of the following count property: + /// - + /// - + /// - + /// + /// The type to check + /// An instance of the used to access the three described known types. + /// true when the type contains one of the supported collection count property; otherwise false. + public static bool HasAnyCollectionCountProperty([NotNullWhen(returnValue: true)] this ITypeSymbol? invocationTarget, WellKnownTypeProvider wellKnownTypeProvider) + { + const string countPropertyName = "Count"; + + if (invocationTarget == null + || !wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsICollection, out var iCollection) + || !wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericICollection1, out var iCollectionOfT) + || !wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIReadOnlyCollection1, out var iReadOnlyCollectionOfT)) + { + return false; + } + + if (isAnySupportedCollectionType(invocationTarget)) + { + return true; + } + + if (invocationTarget.TypeKind == TypeKind.Interface) + { + if (invocationTarget.GetMembers(countPropertyName).OfType().Any()) + { + return false; + } + + foreach (var @interface in invocationTarget.AllInterfaces) + { + if (isAnySupportedCollectionType(@interface)) + { + return true; + } + } + } + else + { + foreach (var @interface in invocationTarget.AllInterfaces) + { + if (isAnySupportedCollectionType(@interface) + && invocationTarget.FindImplementationForInterfaceMember(@interface.GetMembers(countPropertyName)[0]) is IPropertySymbol propertyImplementation + && !propertyImplementation.ExplicitInterfaceImplementations.Any()) + { + return true; + } + } + } + + return false; + + bool isAnySupportedCollectionType(ITypeSymbol type) + { + RoslynDebug.Assert(iCollection != null); + RoslynDebug.Assert(iCollectionOfT != null); + RoslynDebug.Assert(iReadOnlyCollectionOfT != null); + + return type.OriginalDefinition is INamedTypeSymbol originalDefinition && + (SymbolEqualityComparer.Default.Equals(iCollection, originalDefinition) || + SymbolEqualityComparer.Default.Equals(iCollectionOfT, originalDefinition) || + SymbolEqualityComparer.Default.Equals(iReadOnlyCollectionOfT, originalDefinition)); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableArrayExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableArrayExtensions.cs new file mode 100644 index 0000000000000..24129d219cb2c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableArrayExtensions.cs @@ -0,0 +1,66 @@ +// 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 System.Collections.Immutable +{ + internal static class ImmutableArrayExtensions + { + /// + /// Returns the number of elements in a sequence. + /// + /// he type of the elements of . + /// A sequence that contains elements to be counted. + /// The number of elements in the input sequence. + public static int Count(this ImmutableArray source) => source.Length; + + /// + /// Determines whether a sequence contains, exactly, elements. + /// + /// The type of the elements of source. + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains, exactly, elements; otherwise, . + public static bool HasExactly(this ImmutableArray source, int count) => source.Length == count; + + /// + /// Determines whether a sequence contains more than elements. + /// + /// The type of the elements of source. + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains more than elements; otherwise, . + public static bool HasMoreThan(this ImmutableArray source, int count) => source.Length > count; + + /// + /// Determines whether a sequence contains fewer than elements. + /// + /// The type of the elements of source. + /// The to check for cardinality. + /// The number of elements to ensure exists. + /// the source sequence contains less then elements; otherwise, . + public static bool HasFewerThan(this ImmutableArray source, int count) => source.Length < count; + + /// + /// Determines whether a sequence contains any elements. + /// + /// The type of the elements of array. + /// The type of arg. + /// The whose elements to apply the predicate to. + /// A function to test each element for a condition. + /// The argument to pass into the predicate. + /// true if any elements in the source sequence pass the test in the specified predicate otherwise, false. + public static bool Any(this ImmutableArray array, Func predicate, TArg arg) + { + foreach (var a in array) + { + if (predicate(a, arg)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableHashSetExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableHashSetExtensions.cs new file mode 100644 index 0000000000000..8f371109d4475 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ImmutableHashSetExtensions.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.Linq; +using Analyzer.Utilities.PooledObjects; + +namespace System.Collections.Immutable +{ + internal static class ImmutableHashSetExtensions + { + public static ImmutableHashSet AddRange(this ImmutableHashSet set1, ImmutableHashSet set2) + { + using var builder = PooledHashSet.GetInstance(); + + foreach (var item in set1) + { + builder.Add(item); + } + + foreach (var item in set2) + { + builder.Add(item); + } + + if (builder.Count == set1.Count) + { + return set1; + } + + if (builder.Count == set2.Count) + { + return set2; + } + + return builder.ToImmutable(); + } + + public static ImmutableHashSet IntersectSet(this ImmutableHashSet set1, ImmutableHashSet set2) + { + if (set1.IsEmpty || set2.IsEmpty) + { + return ImmutableHashSet.Empty; + } + else if (set1.Count == 1) + { + return set2.Contains(set1.First()) ? set1 : ImmutableHashSet.Empty; + } + else if (set2.Count == 1) + { + return set1.Contains(set2.First()) ? set2 : ImmutableHashSet.Empty; + } + + using var builder = PooledHashSet.GetInstance(); + foreach (var item in set1) + { + if (set2.Contains(item)) + { + builder.Add(item); + } + } + + if (builder.Count == set1.Count) + { + return set1; + } + else if (builder.Count == set2.Count) + { + return set2; + } + + return builder.ToImmutable(); + } + + public static bool IsSubsetOfSet(this ImmutableHashSet set1, ImmutableHashSet set2) + { + if (set1.Count > set2.Count) + { + return false; + } + + foreach (var item in set1) + { + if (!set2.Contains(item)) + { + return false; + } + } + + return true; + } + + public static void AddIfNotNull(this ImmutableHashSet.Builder builder, T? item) + where T : class + { + if (item != null) + { + builder.Add(item); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/KeyValuePairExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/KeyValuePairExtensions.cs new file mode 100644 index 0000000000000..41eb81678cad8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/KeyValuePairExtensions.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Analyzer.Utilities.Extensions +{ + internal static class KeyValuePairExtensions + { + public static void Deconstruct(this KeyValuePair pair, out TKey key, out TValue value) + { + key = pair.Key; + value = pair.Value; + } + + public static KeyValuePair AsNullable(this KeyValuePair pair) + { + // This conversion is safe + return pair!; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/MethodKindEx.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/MethodKindEx.cs new file mode 100644 index 0000000000000..ba304378ee924 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/MethodKindEx.cs @@ -0,0 +1,29 @@ +// 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; + +namespace Analyzer.Utilities.Extensions +{ + internal static class MethodKindEx + { + public const MethodKind LocalFunction = (MethodKind)17; + +#if HAS_IOPERATION +#pragma warning disable IDE0051 // Remove unused private members +#pragma warning disable IDE0052 // Remove unread private members +#pragma warning disable CA1823 // Remove unused private members + /// + /// This will only compile if and have the + /// same value. + /// + /// + /// The subtraction in will overflow if is greater, and the conversion + /// to an unsigned value after negation in will overflow if is greater. + /// + private const uint LocalFunctionValueAssertion1 = LocalFunction - MethodKind.LocalFunction, + LocalFunctionValueAssertion2 = -(LocalFunction - MethodKind.LocalFunction); +#endif + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationBlockAnalysisContextExtension.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationBlockAnalysisContextExtension.cs new file mode 100644 index 0000000000000..1197e7fd1abc3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationBlockAnalysisContextExtension.cs @@ -0,0 +1,79 @@ +// 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. + +#if HAS_IOPERATION + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.Extensions +{ + internal static class OperationBlockAnalysisContextExtension + { +#pragma warning disable RS1012 // Start action has no registered actions. + public static bool IsMethodNotImplementedOrSupported(this OperationBlockStartAnalysisContext context, bool checkPlatformNotSupported = false) +#pragma warning restore RS1012 // Start action has no registered actions. + { + // Note that VB method bodies with 1 action have 3 operations. + // The first is the actual operation, the second is a label statement, and the third is a return + // statement. The last two are implicit in these scenarios. + + var operationBlocks = context.OperationBlocks.WhereAsArray(operation => !operation.IsOperationNoneRoot()); + + IBlockOperation? methodBlock = null; + if (operationBlocks.Length == 1 && operationBlocks[0].Kind == OperationKind.Block) + { + methodBlock = (IBlockOperation)operationBlocks[0]; + } + else if (operationBlocks.Length > 1) + { + foreach (var block in operationBlocks) + { + if (block.Kind == OperationKind.Block) + { + methodBlock = (IBlockOperation)block; + break; + } + } + } + + if (methodBlock != null) + { + static bool IsSingleStatementBody(IBlockOperation body) + { + return body.Operations.Length == 1 || + (body.Operations.Length == 3 && body.Syntax.Language == LanguageNames.VisualBasic && + body.Operations[1] is ILabeledOperation labeledOp && labeledOp.IsImplicit && + body.Operations[2] is IReturnOperation returnOp && returnOp.IsImplicit); + } + + if (IsSingleStatementBody(methodBlock) && + methodBlock.Operations[0].GetTopmostExplicitDescendants() is { } descendants && + descendants.Length == 1 && + descendants[0] is IThrowOperation throwOperation && + throwOperation.GetThrownExceptionType() is ITypeSymbol createdExceptionType) + { + if (SymbolEqualityComparer.Default.Equals( + context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNotImplementedException), + createdExceptionType.OriginalDefinition) || + SymbolEqualityComparer.Default.Equals( + context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNotSupportedException), + createdExceptionType.OriginalDefinition) || + (checkPlatformNotSupported && + SymbolEqualityComparer.Default.Equals( + context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemPlatformNotSupportedException), + createdExceptionType.OriginalDefinition))) + { + return true; + } + } + } + + return false; + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationKinds.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationKinds.cs new file mode 100644 index 0000000000000..38e493fe4fb7c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/OperationKinds.cs @@ -0,0 +1,23 @@ +// 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. + +#if HAS_IOPERATION + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class OperationKinds + { + public static ImmutableArray MemberReference { get; } + = ImmutableArray.Create( + OperationKind.EventReference, + OperationKind.FieldReference, + OperationKind.MethodReference, + OperationKind.PropertyReference); + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/PooledHashSetExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/PooledHashSetExtensions.cs new file mode 100644 index 0000000000000..8751c9658789e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/PooledHashSetExtensions.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Analyzer.Utilities.PooledObjects.Extensions +{ + internal static class PooledHashSetExtensions + { + public static void AddRange(this PooledHashSet builder, IEnumerable set2) + { + foreach (var item in set2) + { + builder.Add(item); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ReportDiagnosticExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ReportDiagnosticExtensions.cs new file mode 100644 index 0000000000000..1f6ec022ee9a7 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/ReportDiagnosticExtensions.cs @@ -0,0 +1,74 @@ +// 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; + +namespace Microsoft.CodeAnalysis +{ + internal static class ReportDiagnosticExtensions + { + public static string ToAnalyzerConfigString(this ReportDiagnostic reportDiagnostic) + { + return reportDiagnostic switch + { + ReportDiagnostic.Error => "error", + ReportDiagnostic.Warn => "warning", + ReportDiagnostic.Info => "suggestion", + ReportDiagnostic.Hidden => "silent", + ReportDiagnostic.Suppress => "none", + _ => throw new NotImplementedException(), + }; + } + + public static DiagnosticSeverity? ToDiagnosticSeverity(this ReportDiagnostic reportDiagnostic) + { + return reportDiagnostic switch + { + ReportDiagnostic.Error => DiagnosticSeverity.Error, + ReportDiagnostic.Warn => DiagnosticSeverity.Warning, + ReportDiagnostic.Info => DiagnosticSeverity.Info, + ReportDiagnostic.Hidden => DiagnosticSeverity.Hidden, + ReportDiagnostic.Suppress => null, + ReportDiagnostic.Default => null, + _ => throw new NotImplementedException(), + }; + } + + public static bool IsLessSevereThan(this ReportDiagnostic current, ReportDiagnostic other) + { + return current switch + { + ReportDiagnostic.Error => false, + + ReportDiagnostic.Warn => + other switch + { + ReportDiagnostic.Error => true, + _ => false + }, + + ReportDiagnostic.Info => + other switch + { + ReportDiagnostic.Error => true, + ReportDiagnostic.Warn => true, + _ => false + }, + + ReportDiagnostic.Hidden => + other switch + { + ReportDiagnostic.Error => true, + ReportDiagnostic.Warn => true, + ReportDiagnostic.Info => true, + _ => false + }, + + ReportDiagnostic.Suppress => true, + + _ => false + }; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SemanticModelExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SemanticModelExtensions.cs new file mode 100644 index 0000000000000..d88a2465e27f3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SemanticModelExtensions.cs @@ -0,0 +1,32 @@ +// 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 warnings + +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class SemanticModelExtensions + { + public static IOperation? GetOperationWalkingUpParentChain(this SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + { + // Walk up the parent chain to fetch the first non-null operation. + do + { + var operation = semanticModel.GetOperation(node, cancellationToken); + if (operation != null) + { + return operation; + } + + node = node.Parent; + } + while (node != null); + + return null; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SourceTextExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SourceTextExtensions.cs new file mode 100644 index 0000000000000..6547adfe4ab6b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SourceTextExtensions.cs @@ -0,0 +1,49 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Text; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities.Extensions +{ + /// + /// Class that contains extensions to . + /// + internal static partial class SourceTextExtensions + { + /// + /// Reads the contents into a stream and returns the result of calling the + /// function on that stream. + /// + /// Type to deserialize from the . + /// Abstraction for an additional file's contents. + /// Function that will parse into . + /// Output from . + public static T Parse(this SourceText text, Func parser) + { + if (text == null) + { + throw new ArgumentNullException(nameof(text)); + } + + if (parser == null) + { + throw new ArgumentNullException(nameof(parser)); + } + + using var stream = new MemoryStream(); + using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) + { + text.Write(writer); + } + + stream.Position = 0; + + using var reader = new StreamReader(stream); + return parser(reader); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringCompatExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringCompatExtensions.cs new file mode 100644 index 0000000000000..2659ea66670a0 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringCompatExtensions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if !NETCOREAPP + +namespace System +{ + internal static class StringCompatExtensions + { + public static bool Contains(this string str, string value, StringComparison comparisonType) + { + return str.IndexOf(value, comparisonType) >= 0; + } + + public static string Replace(this string str, string oldValue, string? newValue, StringComparison comparisonType) + { + if (comparisonType != StringComparison.Ordinal) + throw new NotSupportedException(); + + return str.Replace(oldValue, newValue); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringExtensions.cs new file mode 100644 index 0000000000000..eb1a5bc93a1c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/StringExtensions.cs @@ -0,0 +1,55 @@ +// 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.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static class StringExtensions + { + public static bool HasSuffix(this string str, string suffix) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (suffix == null) + { + throw new ArgumentNullException(nameof(suffix)); + } + + return str.EndsWith(suffix, StringComparison.Ordinal); + } + + public static string WithoutSuffix(this string str, string suffix) + { + if (str == null) + { + throw new ArgumentNullException(nameof(str)); + } + + if (suffix == null) + { + throw new ArgumentNullException(nameof(suffix)); + } + + if (!str.HasSuffix(suffix)) + { + throw new ArgumentException( + $"The string {str} does not end with the suffix {suffix}.", + nameof(str)); + } + + return str[..^suffix.Length]; + } + + public static bool IsASCII(this string value) + { + // ASCII encoding replaces non-ascii with question marks, so we use UTF8 to see if multi-byte sequences are there + return Encoding.UTF8.GetByteCount(value) == value.Length; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SymbolVisibility.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SymbolVisibility.cs new file mode 100644 index 0000000000000..e99b22abf08c0 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/SymbolVisibility.cs @@ -0,0 +1,44 @@ +// 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; + +namespace Analyzer.Utilities.Extensions +{ +#pragma warning disable CA1027 // Mark enums with FlagsAttribute + internal enum SymbolVisibility +#pragma warning restore CA1027 // Mark enums with FlagsAttribute + { + Public = 0, + Internal = 1, + Private = 2, + Friend = Internal, + } + + /// + /// Extensions for . + /// + internal static class SymbolVisibilityExtensions + { + /// + /// Determines whether is at least as visible as . + /// + /// The visibility to compare against. + /// The visibility to compare with. + /// True if one can say that is at least as visible as . + /// + /// For example, is at least as visible as , but is not as visible as . + /// + public static bool IsAtLeastAsVisibleAs(this SymbolVisibility typeVisibility, SymbolVisibility comparisonVisibility) + { + return typeVisibility switch + { + SymbolVisibility.Public => true, + SymbolVisibility.Internal => comparisonVisibility != SymbolVisibility.Public, + SymbolVisibility.Private => comparisonVisibility == SymbolVisibility.Private, + _ => throw new ArgumentOutOfRangeException(nameof(typeVisibility), typeVisibility, null), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/UriExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/UriExtensions.cs new file mode 100644 index 0000000000000..59b4761317898 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/UriExtensions.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class UriExtensions + { + private static readonly ImmutableHashSet s_uriWords = ImmutableHashSet.Create(StringComparer.OrdinalIgnoreCase, "uri", "urn", "url"); + + public static bool ParameterNamesContainUriWordSubstring(this IEnumerable parameters, CancellationToken cancellationToken) + { + foreach (IParameterSymbol parameter in parameters) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (SymbolNameContainsUriWordSubstring(parameter, cancellationToken)) + { + return true; + } + } + + return false; + } + + public static bool SymbolNameContainsUriWordSubstring(this ISymbol symbol, CancellationToken cancellationToken) + { + foreach (string word in s_uriWords) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (symbol.Name.Contains(word, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + + return false; + } + + public static IEnumerable GetParametersThatContainUriWords(this IEnumerable parameters, CancellationToken cancellationToken) + { + foreach (IParameterSymbol parameter in parameters) + { + if (SymbolNameContainsUriWords(parameter, cancellationToken)) + { + yield return parameter; + } + } + } + + public static bool SymbolNameContainsUriWords(this ISymbol symbol, CancellationToken cancellationToken) + { + if (symbol.Name == null || !symbol.SymbolNameContainsUriWordSubstring(cancellationToken)) + { + // quick check failed + return false; + } + + string? word; + var parser = new WordParser(symbol.Name, WordParserOptions.SplitCompoundWords); + while ((word = parser.NextWord()) != null) + { + if (s_uriWords.Contains(word)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/VersionExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/VersionExtensions.cs new file mode 100644 index 0000000000000..1c5ba0ab2374d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/VersionExtensions.cs @@ -0,0 +1,47 @@ +// 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; + +namespace Analyzer.Utilities.Extensions +{ + internal static class VersionExtension + { + public static bool IsGreaterThanOrEqualTo(this Version? current, Version? compare) + { + if (current == null) + { + return compare == null; + } + + if (compare == null) + { + return true; + } + + if (current.Major != compare.Major) + { + return current.Major > compare.Major; + } + + if (current.Minor != compare.Minor) + { + return current.Minor > compare.Minor; + } + + // For build or revision value of 0 equals to -1 + if (current.Build != compare.Build && (current.Build > 0 || compare.Build > 0)) + { + return current.Build > compare.Build; + } + + if (current.Revision != compare.Revision && (current.Revision > 0 || compare.Revision > 0)) + { + return current.Revision > compare.Revision; + } + + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Extensions/WellKnownDiagnosticTagsExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/WellKnownDiagnosticTagsExtensions.cs new file mode 100644 index 0000000000000..c6cbf04cbed35 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Extensions/WellKnownDiagnosticTagsExtensions.cs @@ -0,0 +1,19 @@ +// 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 +{ + internal static class WellKnownDiagnosticTagsExtensions + { + public const string EnabledRuleInAggressiveMode = nameof(EnabledRuleInAggressiveMode); + public const string Dataflow = nameof(Dataflow); + public const string CompilationEnd = nameof(CompilationEnd); + public static readonly string[] DataflowAndTelemetry = new string[] { Dataflow, WellKnownDiagnosticTags.Telemetry }; + public static readonly string[] DataflowAndTelemetryEnabledInAggressiveMode = new string[] { Dataflow, WellKnownDiagnosticTags.Telemetry, EnabledRuleInAggressiveMode }; + + public static readonly string[] Telemetry = new string[] { WellKnownDiagnosticTags.Telemetry }; + public static readonly string[] TelemetryEnabledInAggressiveMode = new string[] { WellKnownDiagnosticTags.Telemetry, EnabledRuleInAggressiveMode }; + public static readonly string[] CompilationEndAndTelemetry = new string[] { CompilationEnd, WellKnownDiagnosticTags.Telemetry }; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/FxCopWellKnownDiagnosticTags.cs b/src/RoslynAnalyzers/Utilities/Compiler/FxCopWellKnownDiagnosticTags.cs new file mode 100644 index 0000000000000..2bc4e5e741b81 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/FxCopWellKnownDiagnosticTags.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Analyzer.Utilities +{ + internal static class FxCopWellKnownDiagnosticTags + { + public const string PortedFromFxCop = nameof(PortedFromFxCop); + + public static readonly string[] PortedFxCopRule = new string[] { PortedFromFxCop, WellKnownDiagnosticTags.Telemetry }; + public static readonly string[] PortedFxCopRuleEnabledInAggressiveMode = new string[] { PortedFromFxCop, WellKnownDiagnosticTags.Telemetry, WellKnownDiagnosticTagsExtensions.EnabledRuleInAggressiveMode }; + + public static readonly string[] PortedFxCopDataflowRule = new string[] { PortedFromFxCop, WellKnownDiagnosticTagsExtensions.Dataflow, WellKnownDiagnosticTags.Telemetry }; + public static readonly string[] PortedFxCopDataflowRuleEnabledInAggressiveMode = new string[] { PortedFromFxCop, WellKnownDiagnosticTagsExtensions.Dataflow, WellKnownDiagnosticTags.Telemetry, WellKnownDiagnosticTagsExtensions.EnabledRuleInAggressiveMode }; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/HashUtilities.cs b/src/RoslynAnalyzers/Utilities/Compiler/HashUtilities.cs new file mode 100644 index 0000000000000..c489d1807051d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/HashUtilities.cs @@ -0,0 +1,86 @@ +// 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; + +#if !NETCOREAPP +using Analyzer.Utilities.Extensions; +#endif + +namespace Analyzer.Utilities +{ + internal static class HashUtilities + { + internal static int GetHashCodeOrDefault(this T? obj) + where T : class + => obj?.GetHashCode() ?? 0; + + internal static int GetHashCodeOrDefault(this T? obj) + where T : struct + => obj?.GetHashCode() ?? 0; + + internal static int Combine(ImmutableArray array) + { + var hashCode = new RoslynHashCode(); + Combine(array, ref hashCode); + return hashCode.ToHashCode(); + } + + internal static void Combine(ImmutableArray array, ref RoslynHashCode hashCode) + { + foreach (var element in array) + { + hashCode.Add(element); + } + } + + internal static int Combine(ImmutableStack stack) + { + var hashCode = new RoslynHashCode(); + Combine(stack, ref hashCode); + return hashCode.ToHashCode(); + } + + internal static void Combine(ImmutableStack stack, ref RoslynHashCode hashCode) + { + foreach (var element in stack) + { + hashCode.Add(element); + } + } + + internal static int Combine(ImmutableHashSet set) + { + var hashCode = new RoslynHashCode(); + Combine(set, ref hashCode); + return hashCode.ToHashCode(); + } + + internal static void Combine(ImmutableHashSet set, ref RoslynHashCode hashCode) + { + foreach (var element in set) + { + hashCode.Add(element); + } + } + + internal static int Combine(ImmutableDictionary dictionary) + where TKey : notnull + { + var hashCode = new RoslynHashCode(); + Combine(dictionary, ref hashCode); + return hashCode.ToHashCode(); + } + + internal static void Combine(ImmutableDictionary dictionary, ref RoslynHashCode hashCode) + where TKey : notnull + { + foreach (var (key, value) in dictionary) + { + hashCode.Add(key); + hashCode.Add(value); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Index.cs b/src/RoslynAnalyzers/Utilities/Compiler/Index.cs new file mode 100644 index 0000000000000..42fb9f826dbe7 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Index.cs @@ -0,0 +1,150 @@ +// 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. + +#if !NETCOREAPP + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace System +{ + /// Represent a type can be used to index a collection either from the start or the end. + /// + /// Index is used by the C# compiler to support the new index syntax + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 } ; + /// int lastElement = someArray[^1]; // lastElement = 5 + /// + /// + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Matches implementation from dotnet/runtime")] + [SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "Matches implementation from dotnet/runtime")] + internal readonly struct Index : IEquatable + { + private readonly int _value; + + /// Construct an Index using a value and indicating if the index is from the start or from the end. + /// The index value. it has to be zero or positive number. + /// Indicating if the index is from the start or from the end. + /// + /// If the Index constructed from the end, index value 1 means pointing at the last element and index value 0 means pointing at beyond last element. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Index(int value, bool fromEnd = false) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required."); + } + + if (fromEnd) + _value = ~value; + else + _value = value; + } + + // The following private constructors mainly created for perf reason to avoid the checks + private Index(int value) + { + _value = value; + } + + /// Create an Index pointing at first element. + public static Index Start => new(0); + + /// Create an Index pointing at beyond last element. + public static Index End => new(~0); + + /// Create an Index from the start at the position indicated by the value. + /// The index value from the start. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromStart(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required."); + } + + return new Index(value); + } + + /// Create an Index from the end at the position indicated by the value. + /// The index value from the end. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Index FromEnd(int value) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value), value, "Non-negative number required."); + } + + return new Index(~value); + } + + /// Returns the index value. + public int Value + { + get + { + if (_value < 0) + return ~_value; + else + return _value; + } + } + + /// Indicates whether the index is from the start or the end. + public bool IsFromEnd => _value < 0; + + /// Calculate the offset from the start using the giving collection length. + /// The length of the collection that the Index will be used with. length has to be a positive value + /// + /// For performance reason, we don't validate the input length parameter and the returned offset value against negative values. + /// we don't validate either the returned offset is greater than the input length. + /// It is expected Index will be used with collections which always have non negative length/count. If the returned offset is negative and + /// then used to index a collection will get out of range exception which will be same affect as the validation. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetOffset(int length) + { + int offset = _value; + if (IsFromEnd) + { + // offset = length - (~value) + // offset = length + (~(~value) + 1) + // offset = length + value + 1 + + offset += length + 1; + } + + return offset; + } + + /// Indicates whether the current Index object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? obj) => obj is Index index && _value == index._value; + + /// Indicates whether the current Index object is equal to another Index object. + /// An object to compare with this object + public bool Equals(Index other) => _value == other._value; + + /// Returns the hash code for this instance. + public override int GetHashCode() => _value; + + /// Converts integer number to an Index. + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Matches implementation from dotnet/runtime")] + public static implicit operator Index(int value) => FromStart(value); + + /// Converts the value of the current Index object to its equivalent string representation. + [SuppressMessage("Globalization", "CA1305:Specify IFormatProvider", Justification = "Matches implementation from dotnet/runtime")] + public override string ToString() + { + if (IsFromEnd) + return '^' + Value.ToString(); + + return ((uint)Value).ToString(); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/IsExternalInit.cs b/src/RoslynAnalyzers/Utilities/Compiler/IsExternalInit.cs new file mode 100644 index 0000000000000..7d1e003609644 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/IsExternalInit.cs @@ -0,0 +1,20 @@ +// 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. + +// Copied from: +// https://github.com/dotnet/runtime/blob/218ef0f7776c2c20f6c594e3475b80f1fe2d7d08/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/IsExternalInit.cs + +using System.ComponentModel; + +namespace System.Runtime.CompilerServices +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/.editorconfig b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/.editorconfig new file mode 100644 index 0000000000000..17082ebd7e1c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CA2201: Do not raise reserved exception types +dotnet_diagnostic.CA2201.severity = none \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/ICollectionExpressionOperationWrapper.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/ICollectionExpressionOperationWrapper.cs new file mode 100644 index 0000000000000..96117a192b8bf --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/ICollectionExpressionOperationWrapper.cs @@ -0,0 +1,53 @@ +// 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. + +#if HAS_IOPERATION + +namespace Analyzer.Utilities.Lightup +{ + using System; + using System.Collections.Immutable; + using System.Diagnostics.CodeAnalysis; + using Microsoft.CodeAnalysis; + + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not a comparable instance.")] + internal readonly struct ICollectionExpressionOperationWrapper : IOperationWrapper + { + internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Operations.ICollectionExpressionOperation"; + private static readonly Type? WrappedType = OperationWrapperHelper.GetWrappedType(typeof(ICollectionExpressionOperationWrapper)); + + private static readonly Func> ElementsAccessor = LightupHelpers.CreateOperationPropertyAccessor>(WrappedType, nameof(Elements), fallbackResult: default); + + private ICollectionExpressionOperationWrapper(IOperation operation) + { + WrappedOperation = operation; + } + + public IOperation WrappedOperation { get; } + public ITypeSymbol? Type => WrappedOperation.Type; + public ImmutableArray Elements => ElementsAccessor(WrappedOperation); + + public static ICollectionExpressionOperationWrapper FromOperation(IOperation operation) + { + if (operation == null) + { + return default; + } + + if (!IsInstance(operation)) + { + throw new InvalidCastException($"Cannot cast '{operation.GetType().FullName}' to '{WrappedTypeName}'"); + } + + return new ICollectionExpressionOperationWrapper(operation); + } + + public static bool IsInstance(IOperation operation) + { + return operation != null && LightupHelpers.CanWrapOperation(operation, WrappedType); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IFunctionPointerInvocationOperationWrapper.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IFunctionPointerInvocationOperationWrapper.cs new file mode 100644 index 0000000000000..bd9d15f054a44 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IFunctionPointerInvocationOperationWrapper.cs @@ -0,0 +1,81 @@ +// 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. + +#if HAS_IOPERATION + +namespace Analyzer.Utilities.Lightup +{ + using System; + using System.Collections.Immutable; + using System.Diagnostics.CodeAnalysis; + using System.Linq.Expressions; + using System.Reflection; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.Operations; + + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not a comparable instance.")] + internal readonly struct IFunctionPointerInvocationOperationWrapper : IOperationWrapper + { + internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Operations.IFunctionPointerInvocationOperation"; + private static readonly Type? WrappedType = OperationWrapperHelper.GetWrappedType(typeof(IFunctionPointerInvocationOperationWrapper)); + + private static readonly Func> ArgumentsAccessor = LightupHelpers.CreateOperationPropertyAccessor>(WrappedType, nameof(Arguments), fallbackResult: ImmutableArray.Empty); + private static readonly Func TargetAccessor = LightupHelpers.CreateOperationPropertyAccessor(WrappedType, nameof(Target), fallbackResult: null!); + + private static readonly Func GetFunctionPointerSignatureAccessor = CreateFunctionPointerSignatureAccessor(WrappedType); + + private static Func CreateFunctionPointerSignatureAccessor(Type? wrappedType) + { + if (wrappedType == null) + { + return op => null!; + } + + var targetMethod = typeof(OperationExtensions).GetTypeInfo().GetDeclaredMethod("GetFunctionPointerSignature"); + + if (targetMethod is null) + { + return op => null!; + } + + var operation = Expression.Variable(typeof(IOperation)); + + return Expression.Lambda>(Expression.Call(targetMethod, Expression.Convert(operation, wrappedType)), operation).Compile(); + } + + private IFunctionPointerInvocationOperationWrapper(IOperation operation) + { + WrappedOperation = operation; + } + + public IOperation WrappedOperation { get; } + public ITypeSymbol? Type => WrappedOperation.Type; + public ImmutableArray Arguments => ArgumentsAccessor(WrappedOperation); + public IOperation Target => TargetAccessor(WrappedOperation); + + public IMethodSymbol GetFunctionPointerSignature() => GetFunctionPointerSignatureAccessor(WrappedOperation); + + public static IFunctionPointerInvocationOperationWrapper FromOperation(IOperation operation) + { + if (operation == null) + { + return default; + } + + if (!IsInstance(operation)) + { + throw new InvalidCastException($"Cannot cast '{operation.GetType().FullName}' to '{WrappedTypeName}'"); + } + + return new IFunctionPointerInvocationOperationWrapper(operation); + } + + public static bool IsInstance(IOperation operation) + { + return operation != null && LightupHelpers.CanWrapOperation(operation, WrappedType); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IOperationWrapper.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IOperationWrapper.cs new file mode 100644 index 0000000000000..2c718a8d49033 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IOperationWrapper.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if HAS_IOPERATION + +namespace Analyzer.Utilities.Lightup +{ + using Microsoft.CodeAnalysis; + + internal interface IOperationWrapper + { + IOperation? WrappedOperation { get; } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IUtf8StringOperationWrapper.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IUtf8StringOperationWrapper.cs new file mode 100644 index 0000000000000..10747191bad45 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/IUtf8StringOperationWrapper.cs @@ -0,0 +1,52 @@ +// 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. + +#if HAS_IOPERATION + +namespace Analyzer.Utilities.Lightup +{ + using System; + using System.Diagnostics.CodeAnalysis; + using Microsoft.CodeAnalysis; + + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not a comparable instance.")] + internal readonly struct IUtf8StringOperationWrapper : IOperationWrapper + { + internal const string WrappedTypeName = "Microsoft.CodeAnalysis.Operations.IUtf8StringOperation"; + private static readonly Type? WrappedType = OperationWrapperHelper.GetWrappedType(typeof(IUtf8StringOperationWrapper)); + + private static readonly Func ValueAccessor = LightupHelpers.CreateOperationPropertyAccessor(WrappedType, nameof(Value), fallbackResult: null!); + + private IUtf8StringOperationWrapper(IOperation operation) + { + WrappedOperation = operation; + } + + public IOperation WrappedOperation { get; } + public ITypeSymbol? Type => WrappedOperation.Type; + public string Value => ValueAccessor(WrappedOperation); + + public static IUtf8StringOperationWrapper FromOperation(IOperation operation) + { + if (operation == null) + { + return default; + } + + if (!IsInstance(operation)) + { + throw new InvalidCastException($"Cannot cast '{operation.GetType().FullName}' to '{WrappedTypeName}'"); + } + + return new IUtf8StringOperationWrapper(operation); + } + + public static bool IsInstance(IOperation operation) + { + return operation != null && LightupHelpers.CanWrapOperation(operation, WrappedType); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/LightupHelpers.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/LightupHelpers.cs new file mode 100644 index 0000000000000..63d8065c1bdac --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/LightupHelpers.cs @@ -0,0 +1,259 @@ +// 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.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + internal static class LightupHelpers + { + private static readonly ConcurrentDictionary> s_supportedOperationWrappers = new(); + + internal static bool CanWrapOperation(IOperation? operation, Type? underlyingType) + { + if (operation == null) + { + // The wrappers support a null instance + return true; + } + + if (underlyingType == null) + { + // The current runtime doesn't define the target type of the conversion, so no instance of it can exist + return false; + } + + ConcurrentDictionary wrappedSyntax = s_supportedOperationWrappers.GetOrAdd(underlyingType, _ => new ConcurrentDictionary()); + + // Avoid creating the delegate if the value already exists + if (!wrappedSyntax.TryGetValue(operation.Kind, out var canCast)) + { + canCast = wrappedSyntax.GetOrAdd( + operation.Kind, + kind => underlyingType.GetTypeInfo().IsAssignableFrom(operation.GetType().GetTypeInfo())); + } + + return canCast; + } + + internal static Func CreateOperationPropertyAccessor(Type? type, string propertyName, TProperty fallbackResult) + where TOperation : IOperation + => CreatePropertyAccessor(type, "operation", propertyName, fallbackResult); + + internal static Func CreateSyntaxPropertyAccessor(Type? type, string propertyName, TProperty fallbackResult) + where TSyntax : SyntaxNode + => CreatePropertyAccessor(type, "syntax", propertyName, fallbackResult); + + internal static Func CreateSymbolPropertyAccessor(Type? type, string propertyName, TProperty fallbackResult) + where TSymbol : ISymbol + => CreatePropertyAccessor(type, "symbol", propertyName, fallbackResult); + + private static Func CreatePropertyAccessor(Type? type, string parameterName, string propertyName, TProperty fallbackResult) + { + if (!TryGetProperty(type, propertyName, out var property)) + { + return instance => FallbackAccessor(instance, fallbackResult); + } + + var parameter = Expression.Parameter(typeof(T), parameterName); + Expression instance = + type.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()) + ? parameter + : Expression.Convert(parameter, type); + + Expression result = Expression.Call(instance, property.GetMethod!); + if (!typeof(TProperty).GetTypeInfo().IsAssignableFrom(property.PropertyType.GetTypeInfo())) + { + result = Expression.Convert(result, typeof(TProperty)); + } + + Expression> expression = Expression.Lambda>(result, parameter); + return expression.Compile(); + + // Local function + static TProperty FallbackAccessor(T instance, TProperty fallbackResult) + { + if (instance is null) + { + // Unlike an extension method which would throw ArgumentNullException here, the light-up + // behavior needs to match behavior of the underlying property. + throw new NullReferenceException(); + } + + return fallbackResult; + } + } + + internal static Func CreateSyntaxWithPropertyAccessor(Type? type, string propertyName, TProperty fallbackResult) + where TSyntax : SyntaxNode + => CreateWithPropertyAccessor(type, "syntax", propertyName, fallbackResult); + + internal static Func CreateSymbolWithPropertyAccessor(Type? type, string propertyName, TProperty fallbackResult) + where TSymbol : ISymbol + => CreateWithPropertyAccessor(type, "symbol", propertyName, fallbackResult); + + private static Func CreateWithPropertyAccessor(Type? type, string parameterName, string propertyName, TProperty fallbackResult) + { + if (!TryGetProperty(type, propertyName, out var property)) + { + return (instance, value) => FallbackAccessor(instance, value, fallbackResult); + } + + var methodInfo = type.GetTypeInfo().GetDeclaredMethods("With" + propertyName) + .SingleOrDefault(m => !m.IsStatic && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType.Equals(property.PropertyType)); + if (methodInfo is null) + { + return (instance, value) => FallbackAccessor(instance, value, fallbackResult); + } + + var parameter = Expression.Parameter(typeof(T), parameterName); + var valueParameter = Expression.Parameter(typeof(TProperty), methodInfo.GetParameters()[0].Name); + Expression instance = + type.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()) + ? parameter + : Expression.Convert(parameter, type); + Expression value = + property.PropertyType.GetTypeInfo().IsAssignableFrom(typeof(TProperty).GetTypeInfo()) + ? valueParameter + : Expression.Convert(valueParameter, property.PropertyType); + + Expression> expression = + Expression.Lambda>( + Expression.Call(instance, methodInfo, value), + parameter, + valueParameter); + return expression.Compile(); + + // Local function + static T FallbackAccessor(T instance, TProperty newValue, TProperty fallbackResult) + { + if (instance is null) + { + // Unlike an extension method which would throw ArgumentNullException here, the light-up + // behavior needs to match behavior of the underlying property. + throw new NullReferenceException(); + } + + if (Equals(newValue, fallbackResult)) + { + return instance; + } + + throw new NotSupportedException(); + } + } + + internal static Func CreateAccessorWithArgument(Type? type, string parameterName, Type argumentType, string argumentName, string methodName, TValue fallbackResult) + { + if (!TryGetMethod(type, methodName, out var method)) + { + return (instance, _) => FallbackAccessor(instance, fallbackResult); + } + + var parameter = Expression.Parameter(typeof(T), parameterName); + var argument = Expression.Parameter(typeof(TArg), argumentName); + Expression instance = + type.GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo()) + ? parameter + : Expression.Convert(parameter, type); + Expression convertedArgument = + argumentType.GetTypeInfo().IsAssignableFrom(typeof(TArg).GetTypeInfo()) + ? argument + : Expression.Convert(argument, type); + + Expression result = Expression.Call(instance, method, convertedArgument); + if (!typeof(TValue).GetTypeInfo().IsAssignableFrom(method.ReturnType.GetTypeInfo())) + { + result = Expression.Convert(result, typeof(TValue)); + } + + Expression> expression = Expression.Lambda>(result, parameter, argument); + return expression.Compile(); + + // Local function + static TValue FallbackAccessor(T instance, TValue fallbackResult) + { + if (instance is null) + { + // Unlike an extension method which would throw ArgumentNullException here, the light-up + // behavior needs to match behavior of the underlying property. + throw new NullReferenceException(); + } + + return fallbackResult; + } + } + + private static void VerifyTypeArgument(Type type) + { + if (!typeof(T).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + { + throw new InvalidOperationException(); + } + } + + private static void VerifyResultTypeCompatibility(Type resultType) + { + if (!typeof(TValue).GetTypeInfo().IsAssignableFrom(resultType.GetTypeInfo())) + { + if (resultType.GetTypeInfo().IsEnum + && typeof(TValue).GetTypeInfo().IsEnum + && Enum.GetUnderlyingType(typeof(TValue)).GetTypeInfo().IsAssignableFrom(Enum.GetUnderlyingType(resultType).GetTypeInfo())) + { + // Allow this + } + else + { + throw new InvalidOperationException(); + } + } + } + + private static bool TryGetProperty([NotNullWhen(true)] Type? type, string propertyName, [NotNullWhen(true)] out PropertyInfo? propertyInfo) + { + if (type is null) + { + propertyInfo = null; + return false; + } + + VerifyTypeArgument(type); + + propertyInfo = type.GetTypeInfo().GetDeclaredProperty(propertyName); + if (propertyInfo is null) + { + return false; + } + + VerifyResultTypeCompatibility(propertyInfo.PropertyType); + return true; + } + + private static bool TryGetMethod([NotNullWhen(true)] Type? type, string methodName, [NotNullWhen(true)] out MethodInfo? methodInfo) + { + if (type is null) + { + methodInfo = null; + return false; + } + + VerifyTypeArgument(type); + + methodInfo = type.GetTypeInfo().GetDeclaredMethod(methodName); + if (methodInfo is null) + { + return false; + } + + VerifyResultTypeCompatibility(methodInfo.ReturnType); + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationKindEx.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationKindEx.cs new file mode 100644 index 0000000000000..a5df7d039950e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationKindEx.cs @@ -0,0 +1,21 @@ +// 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. + +#if HAS_IOPERATION + +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + internal static class OperationKindEx + { + public const OperationKind FunctionPointerInvocation = (OperationKind)0x78; + public const OperationKind ImplicitIndexerReference = (OperationKind)0x7b; + public const OperationKind Utf8String = (OperationKind)0x7c; + public const OperationKind Attribute = (OperationKind)0x7d; + public const OperationKind CollectionExpression = (OperationKind)0x7f; + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationWrapperHelper.cs b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationWrapperHelper.cs new file mode 100644 index 0000000000000..4a10b650cdf5c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Lightup/OperationWrapperHelper.cs @@ -0,0 +1,40 @@ +// 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. + +#if HAS_IOPERATION + +using System; +using System.Collections.Immutable; +using System.Reflection; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Lightup +{ + internal static class OperationWrapperHelper + { + private static readonly Assembly s_codeAnalysisAssembly = typeof(SyntaxNode).GetTypeInfo().Assembly; + + private static readonly ImmutableDictionary WrappedTypes = ImmutableDictionary.Create() + .Add(typeof(IFunctionPointerInvocationOperationWrapper), s_codeAnalysisAssembly.GetType(IFunctionPointerInvocationOperationWrapper.WrappedTypeName)) + .Add(typeof(IUtf8StringOperationWrapper), s_codeAnalysisAssembly.GetType(IUtf8StringOperationWrapper.WrappedTypeName)) + .Add(typeof(ICollectionExpressionOperationWrapper), s_codeAnalysisAssembly.GetType(ICollectionExpressionOperationWrapper.WrappedTypeName)); + + /// + /// Gets the type that is wrapped by the given wrapper. + /// + /// Type of the wrapper for which the wrapped type should be retrieved. + /// The wrapped type, or if there is no info. + internal static Type? GetWrappedType(Type wrapperType) + { + if (WrappedTypes.TryGetValue(wrapperType, out var wrappedType)) + { + return wrappedType; + } + + return null; + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/NullableAttributes.cs b/src/RoslynAnalyzers/Utilities/Compiler/NullableAttributes.cs new file mode 100644 index 0000000000000..3e69b9cb17980 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/NullableAttributes.cs @@ -0,0 +1,153 @@ +// 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. + +// This was copied from https://github.com/dotnet/coreclr/blob/60f1e6265bd1039f023a82e0643b524d6aaf7845/src/System.Private.CoreLib/shared/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// and updated to have the scope of the attributes be internal. + +#pragma warning disable CA1019 // Define accessors for attribute arguments + +namespace System.Diagnostics.CodeAnalysis +{ +#if !NETCOREAPP + + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + +#endif + +#if !NETCOREAPP || NETCOREAPP3_1 + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } + +#endif +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/AbstractCategorizedAnalyzerConfigOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/AbstractCategorizedAnalyzerConfigOptions.cs new file mode 100644 index 0000000000000..677f050498b13 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/AbstractCategorizedAnalyzerConfigOptions.cs @@ -0,0 +1,150 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Roslyn.Utilities; + +namespace Analyzer.Utilities +{ + using static CategorizedAnalyzerConfigOptionsExtensions; + + internal abstract class AbstractCategorizedAnalyzerConfigOptions : ICategorizedAnalyzerConfigOptions + { + private const string DotnetCodeQualityKeyPrefix = "dotnet_code_quality."; + private const string BuildPropertyKeyPrefix = "build_property."; + + private readonly ConcurrentDictionary _computedOptionValuesMap; + + protected AbstractCategorizedAnalyzerConfigOptions() + { + _computedOptionValuesMap = new ConcurrentDictionary(); + } + + public abstract bool IsEmpty { get; } + protected abstract bool TryGetOptionValue(string optionKeyPrefix, string? optionKeySuffix, string optionName, [NotNullWhen(returnValue: true)] out string? valueString); + + public T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality) + { + if (TryGetOptionValue( + optionName, + kind, + rule, + static (string s, TryParseValue tryParseValue, [MaybeNullWhen(returnValue: false)] out T parsedValue) => tryParseValue(s, out parsedValue), + tryParseValue, + defaultValue, + out var value)) + { + return value; + } + + return defaultValue; + } + + public T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality) + { + if (TryGetOptionValue(optionName, kind, rule, tryParseValue, arg, defaultValue, out var value)) + { + return value; + } + + return defaultValue; + } + + private static string MapOptionKindToKeyPrefix(OptionKind optionKind) + => optionKind switch + { + OptionKind.DotnetCodeQuality => DotnetCodeQualityKeyPrefix, + OptionKind.BuildProperty => BuildPropertyKeyPrefix, + _ => throw new NotImplementedException() + }; + + [PerformanceSensitive("https://github.com/dotnet/roslyn-analyzers/issues/4905", AllowCaptures = false)] + public bool TryGetOptionValue(string optionName, OptionKind kind, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg, T defaultValue, out T value) + { + if (this.IsEmpty) + { + value = defaultValue; + return false; + } + + var key = OptionKey.GetOrCreate(rule?.Id, optionName); + if (!_computedOptionValuesMap.TryGetValue(key, out var computedValue)) + { + computedValue = _computedOptionValuesMap.GetOrAdd(key, ComputeOptionValue(optionName, kind, rule, tryParseValue, arg)); + } + + if (computedValue.found) + { + value = (T)computedValue.value!; + return true; + } + else + { + value = defaultValue; + return false; + } + } + + private (bool found, object? value) ComputeOptionValue(string optionName, OptionKind kind, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg) + { + var optionKeyPrefix = MapOptionKindToKeyPrefix(kind); + + if (rule != null + && (TryGetSpecificOptionValue(rule.Id, optionKeyPrefix, out T? optionValue) + || TryGetSpecificOptionValue(rule.Category, optionKeyPrefix, out optionValue) + || TryGetAnySpecificOptionValue(rule.CustomTags, optionKeyPrefix, out optionValue))) + { + return (true, optionValue); + } + + if (TryGetGeneralOptionValue(optionKeyPrefix, out optionValue)) + { + return (true, optionValue); + } + + return (false, null); + + // Local functions. + bool TryGetSpecificOptionValue(string specificOptionKey, string optionKeyPrefix, [MaybeNullWhen(false)] out T specificOptionValue) + { + if (TryGetOptionValue(optionKeyPrefix, specificOptionKey, optionName, out var valueString)) + { + return tryParseValue(valueString, arg, out specificOptionValue); + } + + specificOptionValue = default; + return false; + } + + bool TryGetAnySpecificOptionValue(IEnumerable specificOptionKeys, string optionKeyPrefix, [MaybeNullWhen(false)] out T specificOptionValue) + { + foreach (var specificOptionKey in specificOptionKeys) + { + if (TryGetSpecificOptionValue(specificOptionKey, optionKeyPrefix, out specificOptionValue)) + { + return true; + } + } + + specificOptionValue = default; + return false; + } + + bool TryGetGeneralOptionValue(string optionKeyPrefix, [MaybeNullWhen(false)] out T generalOptionValue) + { + if (TryGetOptionValue(optionKeyPrefix, optionKeySuffix: null, optionName, out var valueString)) + { + return tryParseValue(valueString, arg, out generalOptionValue); + } + + generalOptionValue = default; + return false; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/AggregateCategorizedAnalyzerConfigOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/AggregateCategorizedAnalyzerConfigOptions.cs new file mode 100644 index 0000000000000..f2c2be47a47c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/AggregateCategorizedAnalyzerConfigOptions.cs @@ -0,0 +1,137 @@ +// 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. + +#if CODEANALYSIS_V3_OR_BETTER + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities +{ + using static CategorizedAnalyzerConfigOptionsExtensions; + + /// + /// Aggregate analyzer configuration options: + /// + /// + /// Per syntax tree options from . + /// Options from an .editorconfig file passed in as an additional file (back compat). + /// + /// + /// + /// + internal sealed class AggregateCategorizedAnalyzerConfigOptions : ICategorizedAnalyzerConfigOptions + { + public static readonly AggregateCategorizedAnalyzerConfigOptions Empty = new( + globalOptions: null, + ImmutableDictionary>.Empty); + + private readonly Lazy? _globalOptions; + private readonly ImmutableDictionary> _perTreeOptions; + + private AggregateCategorizedAnalyzerConfigOptions(Lazy? globalOptions, ImmutableDictionary> perTreeOptions) + { + _globalOptions = globalOptions; + _perTreeOptions = perTreeOptions; + } + + public bool IsEmpty + { + get + { + Debug.Assert(ReferenceEquals(this, Empty) || !_perTreeOptions.IsEmpty); + return ReferenceEquals(this, Empty); + } + } + + public static AggregateCategorizedAnalyzerConfigOptions Create(AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, Compilation compilation) + { + analyzerConfigOptionsProvider = analyzerConfigOptionsProvider ?? throw new ArgumentNullException(nameof(analyzerConfigOptionsProvider)); + + if (analyzerConfigOptionsProvider.IsEmpty()) + { + return Empty; + } + + Lazy? globalOptions; +#if CODEANALYSIS_V3_7_OR_BETTER + globalOptions = new Lazy(() => SyntaxTreeCategorizedAnalyzerConfigOptions.Create(analyzerConfigOptionsProvider.GlobalOptions)); +#else + globalOptions = null; +#endif + + var perTreeOptionsBuilder = PooledDictionary>.GetInstance(); + foreach (var tree in compilation.SyntaxTrees) + { + perTreeOptionsBuilder.Add(tree, new Lazy(() => Create(tree, analyzerConfigOptionsProvider))); + } + + return new AggregateCategorizedAnalyzerConfigOptions(globalOptions, perTreeOptionsBuilder.ToImmutableDictionaryAndFree()); + + static SyntaxTreeCategorizedAnalyzerConfigOptions Create(SyntaxTree tree, AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider) + { + var options = analyzerConfigOptionsProvider.GetOptions(tree); + return SyntaxTreeCategorizedAnalyzerConfigOptions.Create(options); + } + } + + public T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality) + { + if (TryGetOptionValue( + optionName, + kind, + tree, + rule, + static (string s, TryParseValue tryParseValue, [MaybeNullWhen(returnValue: false)] out T parsedValue) => tryParseValue(s, out parsedValue), + tryParseValue, + defaultValue, + out var value)) + { + return value; + } + + return defaultValue; + } + + public T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality) + { + if (TryGetOptionValue(optionName, kind, tree, rule, tryParseValue, arg, defaultValue, out var value)) + { + return value; + } + + return defaultValue; + } + + private bool TryGetOptionValue(string optionName, OptionKind kind, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg, T defaultValue, [MaybeNullWhen(false)] out T value) + { + value = defaultValue; + + if (ReferenceEquals(this, Empty)) + { + return false; + } + + if (tree is null) + { + if (_globalOptions is null) + { + return false; + } + + return _globalOptions.Value.TryGetOptionValue(optionName, kind, rule, tryParseValue, arg, defaultValue, out value); + } + + return _perTreeOptions.TryGetValue(tree, out var lazyTreeOptions) && + lazyTreeOptions.Value.TryGetOptionValue(optionName, kind, rule, tryParseValue, arg, defaultValue, out value); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerConfigOptionsProviderExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerConfigOptionsProviderExtensions.cs new file mode 100644 index 0000000000000..8e693c538d5c8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerConfigOptionsProviderExtensions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if CODEANALYSIS_V3_OR_BETTER + +using System.Collections.Immutable; +using System.Reflection; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities +{ + internal static class AnalyzerConfigOptionsProviderExtensions + { + public static bool IsEmpty(this AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider) + { + // Reflection based optimization for empty analyzer config options. + // Ideally 'AnalyzerConfigOptionsProvider.IsEmpty' would be exposed in the API. + const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; + return analyzerConfigOptionsProvider.GetType().GetField("_treeDict", flags)?.GetValue(analyzerConfigOptionsProvider) is ImmutableDictionary perTreeOptionsMap + && perTreeOptionsMap.IsEmpty; + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs new file mode 100644 index 0000000000000..7591e6a8c8fe2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/AnalyzerOptionsExtensions.cs @@ -0,0 +1,644 @@ +// 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. + +#if CODEANALYSIS_V3_OR_BETTER + +using System; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.Options; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities +{ +#if !TEST_UTILITIES + public static partial class AnalyzerOptionsExtensions +#else + internal static partial class AnalyzerOptionsExtensions +#endif + { + private static readonly ConditionalWeakTable s_cachedOptions = new(); + private static readonly ImmutableHashSet s_defaultOutputKinds = + ImmutableHashSet.CreateRange(Enum.GetValues(typeof(OutputKind)).Cast()); + + private static bool TryGetSyntaxTreeForOption(ISymbol symbol, [NotNullWhen(returnValue: true)] out SyntaxTree? tree) + { + switch (symbol.Kind) + { + case SymbolKind.Assembly: + case SymbolKind.Namespace when ((INamespaceSymbol)symbol).IsGlobalNamespace: + tree = null; + return false; + case SymbolKind.Parameter: + return TryGetSyntaxTreeForOption(symbol.ContainingSymbol, out tree); + default: + tree = symbol.Locations[0].SourceTree; + return tree != null; + } + } + + public static SymbolVisibilityGroup GetSymbolVisibilityGroupOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + SymbolVisibilityGroup defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetSymbolVisibilityGroupOption(rule, tree, compilation, defaultValue) + : defaultValue; + + private static SymbolVisibilityGroup GetSymbolVisibilityGroupOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + SymbolVisibilityGroup defaultValue) + => options.GetFlagsEnumOptionValue(EditorConfigOptionNames.ApiSurface, rule, tree, compilation, defaultValue); + + private static SymbolModifiers GetRequiredModifiersOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + SymbolModifiers defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetRequiredModifiersOption(rule, tree, compilation, defaultValue) + : defaultValue; + + private static SymbolModifiers GetRequiredModifiersOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + SymbolModifiers defaultValue) + => options.GetFlagsEnumOptionValue(EditorConfigOptionNames.RequiredModifiers, rule, tree, compilation, defaultValue); + + public static EnumValuesPrefixTrigger GetEnumValuesPrefixTriggerOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + EnumValuesPrefixTrigger defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetEnumValuesPrefixTriggerOption(rule, tree, compilation, defaultValue) + : defaultValue; + + private static EnumValuesPrefixTrigger GetEnumValuesPrefixTriggerOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + EnumValuesPrefixTrigger defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.EnumValuesPrefixTrigger, rule, tree, compilation, defaultValue); + + public static ImmutableHashSet GetOutputKindsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetOutputKindsOption(rule, tree, compilation, s_defaultOutputKinds); + + public static ImmutableHashSet GetOutputKindsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + ImmutableHashSet defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.OutputKind, rule, tree, compilation, defaultValue); + + public static ImmutableHashSet GetAnalyzedSymbolKindsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + ImmutableHashSet defaultSymbolKinds) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetAnalyzedSymbolKindsOption(rule, tree, compilation, defaultSymbolKinds) + : defaultSymbolKinds; + + private static ImmutableHashSet GetAnalyzedSymbolKindsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + ImmutableHashSet defaultSymbolKinds) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.AnalyzedSymbolKinds, rule, tree, compilation, defaultSymbolKinds); + + private static TEnum GetFlagsEnumOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + TEnum defaultValue) + where TEnum : struct + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue( + optionName, tree, rule, + tryParseValue: static (string value, out TEnum result) => Enum.TryParse(value, ignoreCase: true, result: out result), + defaultValue: defaultValue); + } + + private static ImmutableHashSet GetNonFlagsEnumOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + ImmutableHashSet defaultValue) + where TEnum : struct + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue(optionName, tree, rule, TryParseValue, defaultValue); + static bool TryParseValue(string value, out ImmutableHashSet result) + { + var builder = ImmutableHashSet.CreateBuilder(); + foreach (var kindStr in value.Split(',')) + { + if (Enum.TryParse(kindStr, ignoreCase: true, result: out TEnum kind)) + { + builder.Add(kind); + } + } + + result = builder.ToImmutable(); + return builder.Count > 0; + } + } + + private static TEnum GetNonFlagsEnumOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + TEnum defaultValue) + where TEnum : struct + => GetFlagsEnumOptionValue(options, optionName, rule, tree, compilation, defaultValue); + + public static bool GetBoolOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + bool defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetBoolOptionValue(optionName, rule, tree, compilation, defaultValue) + : defaultValue; + + public static bool GetBoolOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor? rule, + SyntaxTree tree, + Compilation compilation, + bool defaultValue) + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue(optionName, tree, rule, bool.TryParse, defaultValue); + } + + public static uint GetUnsignedIntegralOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + uint defaultValue) + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue(optionName, tree, rule, uint.TryParse, defaultValue); + } + + public static string GetStringOptionValue( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue(optionName, tree, rule, TryParseValue, string.Empty); + + static bool TryParseValue(string value, out string result) + { + result = value; + return !string.IsNullOrEmpty(value); + } + } + + public static SymbolNamesWithValueOption GetNullCheckValidationMethodsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.NullCheckValidationMethods, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "M:"); + + public static SymbolNamesWithValueOption GetAdditionalStringFormattingMethodsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.AdditionalStringFormattingMethods, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "M:"); + + public static bool IsConfiguredToSkipAnalysis( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => options.IsConfiguredToSkipAnalysis(rule, symbol, symbol, compilation); + + public static bool IsConfiguredToSkipAnalysis( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + ISymbol containingContextSymbol, + Compilation compilation) + { + var excludedSymbols = GetExcludedSymbolNamesWithValueOption(options, rule, containingContextSymbol, compilation); + var excludedTypeNamesWithDerivedTypes = GetExcludedTypeNamesWithDerivedTypesOption(options, rule, containingContextSymbol, compilation); + if (excludedSymbols.IsEmpty && excludedTypeNamesWithDerivedTypes.IsEmpty) + { + return false; + } + + while (symbol != null) + { + if (excludedSymbols.Contains(symbol)) + { + return true; + } + + if (symbol is INamedTypeSymbol namedType && !excludedTypeNamesWithDerivedTypes.IsEmpty) + { + foreach (var type in namedType.GetBaseTypesAndThis()) + { + if (excludedTypeNamesWithDerivedTypes.Contains(type)) + { + return true; + } + } + } + + symbol = symbol.ContainingSymbol; + } + + return false; + + static SymbolNamesWithValueOption GetExcludedSymbolNamesWithValueOption( + AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.ExcludedSymbolNames, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)) + : SymbolNamesWithValueOption.Empty; + + static SymbolNamesWithValueOption GetExcludedTypeNamesWithDerivedTypesOption( + AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.ExcludedTypeNamesWithDerivedTypes, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "T:") + : SymbolNamesWithValueOption.Empty; + } + + public static SymbolNamesWithValueOption GetDisallowedSymbolNamesWithValueOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => options.GetDisallowedSymbolNamesWithValueOption(rule, symbol.Locations[0].SourceTree, compilation); + + private static SymbolNamesWithValueOption GetDisallowedSymbolNamesWithValueOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree? tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.DisallowedSymbolNames, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default)); + + public static SymbolNamesWithValueOption GetAdditionalRequiredSuffixesOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => options.GetAdditionalRequiredSuffixesOption(rule, symbol.Locations[0].SourceTree, compilation); + + private static SymbolNamesWithValueOption GetAdditionalRequiredSuffixesOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree? tree, + Compilation compilation) + { + return options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.AdditionalRequiredSuffixes, rule, tree, compilation, getTypeAndSuffixFunc: GetParts, namePrefix: "T:"); + + static SymbolNamesWithValueOption.NameParts GetParts(string name) + { + var split = name.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries); + + // If we don't find exactly one '->', we assume that there is no given suffix. + if (split.Length != 2) + { + return new SymbolNamesWithValueOption.NameParts(name, null); + } + + // Note that we do not validate if the suffix will give a valid class name. + var trimmedSuffix = split[1].Trim(); + + // Check if the given suffix is the special suffix symbol "{[ ]*?}" (opening curly brace '{', 0..N spaces and a closing curly brace '}') + if (trimmedSuffix.Length >= 2 && + trimmedSuffix[0] == '{' && + trimmedSuffix[^1] == '}') + { + for (int i = 1; i < trimmedSuffix.Length - 2; i++) + { + if (trimmedSuffix[i] != ' ') + { + return new SymbolNamesWithValueOption.NameParts(split[0], trimmedSuffix); + } + } + + // Replace the special empty suffix symbol by an empty string + return new SymbolNamesWithValueOption.NameParts(split[0], string.Empty); + } + + return new SymbolNamesWithValueOption.NameParts(split[0], trimmedSuffix); + } + } + + public static SymbolNamesWithValueOption GetAdditionalRequiredGenericInterfaces( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation) + => options.GetAdditionalRequiredGenericInterfaces(rule, symbol.Locations[0].SourceTree, compilation); + + private static SymbolNamesWithValueOption GetAdditionalRequiredGenericInterfaces( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree? tree, + Compilation compilation) + { + return options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.AdditionalRequiredGenericInterfaces, rule, tree, compilation, getTypeAndSuffixFunc: x => GetParts(x, compilation), namePrefix: "T:"); + + static SymbolNamesWithValueOption.NameParts GetParts(string name, Compilation compilation) + { + var split = name.Split(new[] { "->" }, StringSplitOptions.RemoveEmptyEntries); + + // If we don't find exactly one '->', we assume that there is no given suffix. + if (split.Length != 2) + { + return new SymbolNamesWithValueOption.NameParts(name, null); + } + + var genericInterfaceFullName = split[1].Trim(); + if (!genericInterfaceFullName.StartsWith("T:", StringComparison.Ordinal)) + { + genericInterfaceFullName = $"T:{genericInterfaceFullName}"; + } + + var matchingSymbols = DocumentationCommentId.GetSymbolsForDeclarationId(genericInterfaceFullName, compilation); + + if (matchingSymbols.Length != 1 || + matchingSymbols[0] is not INamedTypeSymbol namedType || + namedType.TypeKind != TypeKind.Interface || + !namedType.IsGenericType) + { + // Invalid matching type so we assume there was no associated type + return new SymbolNamesWithValueOption.NameParts(split[0], null); + } + + return new SymbolNamesWithValueOption.NameParts(split[0], namedType); + } + } + + public static SymbolNamesWithValueOption GetInheritanceExcludedSymbolNamesOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + string defaultForcedValue) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.AdditionalInheritanceExcludedSymbolNames, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), optionForcedValue: defaultForcedValue); + + public static SymbolNamesWithValueOption GetAdditionalUseResultsMethodsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.AdditionalUseResultsMethods, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "M:"); + + public static SymbolNamesWithValueOption GetEnumerationMethodsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.EnumerationMethods, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "M:"); + + public static SymbolNamesWithValueOption GetLinqChainMethodsOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation) + => options.GetSymbolNamesWithValueOption(EditorConfigOptionNames.LinqChainMethods, rule, tree, compilation, static name => new SymbolNamesWithValueOption.NameParts(name, Unit.Default), namePrefix: "M:"); + + private static SymbolNamesWithValueOption GetSymbolNamesWithValueOption( + this AnalyzerOptions options, + string optionName, + DiagnosticDescriptor rule, + SyntaxTree? tree, + Compilation compilation, + Func.NameParts> getTypeAndSuffixFunc, + string? namePrefix = null, + string? optionDefaultValue = null, + string? optionForcedValue = null) + { + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue( + optionName, + tree, + rule, + TryParse, + (compilation, getTypeAndSuffixFunc, namePrefix, optionForcedValue), + defaultValue: GetDefaultValue()); + + // Local functions. + static bool TryParse(string s, (Compilation compilation, Func.NameParts> getTypeAndSuffixFunc, string? namePrefix, string? optionForcedValue) arg, out SymbolNamesWithValueOption option) + { + var optionValue = s; + + if (!RoslynString.IsNullOrEmpty(arg.optionForcedValue) && + (optionValue == null || !optionValue.Contains(arg.optionForcedValue, StringComparison.Ordinal))) + { + optionValue = $"{arg.optionForcedValue}|{optionValue}"; + } + + if (string.IsNullOrEmpty(optionValue)) + { + option = SymbolNamesWithValueOption.Empty; + return false; + } + + var names = optionValue.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).ToImmutableArray(); + option = SymbolNamesWithValueOption.Create(names, arg.compilation, arg.namePrefix, arg.getTypeAndSuffixFunc); + return true; + } + + SymbolNamesWithValueOption GetDefaultValue() + { + string optionValue = string.Empty; + + if (!string.IsNullOrEmpty(optionDefaultValue)) + { + RoslynDebug.Assert(optionDefaultValue != null); + optionValue = optionDefaultValue; + } + + if (!RoslynString.IsNullOrEmpty(optionForcedValue) && + (optionValue == null || !optionValue.Contains(optionForcedValue, StringComparison.Ordinal))) + { + optionValue = $"{optionForcedValue}|{optionValue}"; + } + + RoslynDebug.Assert(optionValue != null); + + return TryParse(optionValue, (compilation, getTypeAndSuffixFunc, namePrefix, optionForcedValue), out var option) + ? option + : SymbolNamesWithValueOption.Empty; + } + } + + public static string? GetMSBuildPropertyValue( + this AnalyzerOptions options, + string optionName, + Compilation compilation) + { + MSBuildPropertyOptionNamesHelpers.VerifySupportedPropertyOptionName(optionName); + + // MSBuild property values should be set at compilation level, and cannot have different values per-tree. + // So, we default to first syntax tree. + if (compilation.SyntaxTrees.FirstOrDefault() is not { } tree) + { + return null; + } + + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + return analyzerConfigOptions.GetOptionValue(optionName, tree, rule: null, + tryParseValue: static (string value, out string? result) => + { + result = value; + return true; + }, + defaultValue: null, OptionKind.BuildProperty); + } + + public static ImmutableArray GetMSBuildItemMetadataValues( + this AnalyzerOptions options, + string itemOptionName, + Compilation compilation) + { + MSBuildItemOptionNamesHelpers.VerifySupportedItemOptionName(itemOptionName); + + // MSBuild property values should be set at compilation level, and cannot have different values per-tree. + // So, we default to first syntax tree. + if (compilation.SyntaxTrees.FirstOrDefault() is not { } tree) + { + return ImmutableArray.Empty; + } + + var propertyOptionName = MSBuildItemOptionNamesHelpers.GetPropertyNameForItemOptionName(itemOptionName); + var analyzerConfigOptions = options.GetOrComputeCategorizedAnalyzerConfigOptions(compilation); + var propertyValue = analyzerConfigOptions.GetOptionValue(propertyOptionName, tree, rule: null, + tryParseValue: static (string value, out string? result) => + { + result = value; + return true; + }, + defaultValue: null, OptionKind.BuildProperty); + return MSBuildItemOptionNamesHelpers.ParseItemOptionValue(propertyValue); + } + + /// + /// Returns true if the given source symbol has required visibility based on options: + /// 1. If user has explicitly configured candidate in editor config options and + /// given symbol's visibility is one of the candidate visibilities. + /// 2. Otherwise, if user has not configured visibility, and given symbol's visibility + /// matches the given default symbol visibility. + /// + public static bool MatchesConfiguredVisibility( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + SymbolVisibilityGroup defaultRequiredVisibility = SymbolVisibilityGroup.Public) + => options.MatchesConfiguredVisibility(rule, symbol, symbol, compilation, defaultRequiredVisibility); + + /// + /// Returns true if the given symbol has required visibility based on options in context of the given containing symbol: + /// 1. If user has explicitly configured candidate in editor config options and + /// given symbol's visibility is one of the candidate visibilities. + /// 2. Otherwise, if user has not configured visibility, and given symbol's visibility + /// matches the given default symbol visibility. + /// + public static bool MatchesConfiguredVisibility( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + ISymbol containingContextSymbol, + Compilation compilation, + SymbolVisibilityGroup defaultRequiredVisibility = SymbolVisibilityGroup.Public) + { + var allowedVisibilities = options.GetSymbolVisibilityGroupOption(rule, containingContextSymbol, compilation, defaultRequiredVisibility); + return allowedVisibilities == SymbolVisibilityGroup.All || + allowedVisibilities.Contains(symbol.GetResultantVisibility()); + } + + /// + /// Returns true if the given symbol has required symbol modifiers based on options: + /// 1. If user has explicitly configured candidate in editor config options and + /// given symbol has all the required modifiers. + /// 2. Otherwise, if user has not configured modifiers. + /// + public static bool MatchesConfiguredModifiers( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + SymbolModifiers defaultRequiredModifiers = SymbolModifiers.None) + { + var requiredModifiers = options.GetRequiredModifiersOption(rule, symbol, compilation, defaultRequiredModifiers); + return symbol.GetSymbolModifiers().Contains(requiredModifiers); + } + + private static ICategorizedAnalyzerConfigOptions GetOrComputeCategorizedAnalyzerConfigOptions( + this AnalyzerOptions options, Compilation compilation) + { + // TryGetValue upfront to avoid allocating createValueCallback if the entry already exists. + if (s_cachedOptions.TryGetValue(options, out var categorizedAnalyzerConfigOptions)) + { + return categorizedAnalyzerConfigOptions; + } + + // Fall back to a slow version of the method, which allocates both for the CreateValueCallback and for + // capturing the Compilation argument. + return GetOrComputeCategorizedAnalyzerConfigOptions_Slow(options, compilation); + + static ICategorizedAnalyzerConfigOptions GetOrComputeCategorizedAnalyzerConfigOptions_Slow( + AnalyzerOptions options, + Compilation compilation) + { + return s_cachedOptions.GetValue( + options, + options => AggregateCategorizedAnalyzerConfigOptions.Create(options.AnalyzerConfigOptionsProvider, compilation)); + } + } + } +} +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/CategorizedAnalyzerConfigOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/CategorizedAnalyzerConfigOptions.cs new file mode 100644 index 0000000000000..0566aaf2814fc --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/CategorizedAnalyzerConfigOptions.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; + +namespace Analyzer.Utilities +{ + /// + /// Analyzer configuration options from an .editorconfig file that are parsed into general + /// and specific configuration options. + /// + /// .editorconfig format: + /// 1) General configuration option: + /// (a) "dotnet_code_quality.OptionName = OptionValue" + /// 2) Specific configuration option: + /// (a) "dotnet_code_quality.RuleId.OptionName = OptionValue" + /// (b) "dotnet_code_quality.RuleCategory.OptionName = OptionValue" + /// + /// .editorconfig examples to configure API surface analyzed by analyzers: + /// 1) General configuration option: + /// (a) "dotnet_code_quality.api_surface = all" + /// 2) Specific configuration option: + /// (a) "dotnet_code_quality.CA1040.api_surface = public, internal" + /// (b) "dotnet_code_quality.Naming.api_surface = public" + /// See for allowed symbol visibility value combinations. + /// + internal sealed class CategorizedAnalyzerConfigOptions : AbstractCategorizedAnalyzerConfigOptions + { + public static readonly CategorizedAnalyzerConfigOptions Empty = new( + ImmutableDictionary.Empty, + ImmutableDictionary>.Empty); + + private readonly ImmutableDictionary _generalOptions; + private readonly ImmutableDictionary> _specificOptions; + + private CategorizedAnalyzerConfigOptions( + ImmutableDictionary generalOptions, + ImmutableDictionary> specificOptions) + { + _generalOptions = generalOptions; + _specificOptions = specificOptions; + } + + public override bool IsEmpty + { + get + { + Debug.Assert(ReferenceEquals(this, Empty) || _generalOptions.Count > 0 || _specificOptions.Count > 0); + return ReferenceEquals(this, Empty); + } + } + + public static CategorizedAnalyzerConfigOptions Create(IDictionary options) + { + options = options ?? throw new ArgumentNullException(nameof(options)); + + if (options.Count == 0) + { + return Empty; + } + + var generalOptionsBuilder = ImmutableDictionary.CreateBuilder(StringComparer.OrdinalIgnoreCase); + var specificOptionsBuilder = ImmutableDictionary.CreateBuilder.Builder>(StringComparer.OrdinalIgnoreCase); + foreach (var kvp in options) + { + var key = kvp.Key; + var value = kvp.Value; + + if (!key.StartsWith(KeyPrefix, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + key = key.Substring(KeyPrefix.Length); + var keyParts = key.Split('.').Select(s => s.Trim()).ToImmutableArray(); + switch (keyParts.Length) + { + case 1: + generalOptionsBuilder.Add(keyParts[0], value); + break; + + case 2: + if (!specificOptionsBuilder.TryGetValue(keyParts[0], out var optionsForKeyBuilder)) + { + optionsForKeyBuilder = ImmutableDictionary.CreateBuilder(StringComparer.OrdinalIgnoreCase); + specificOptionsBuilder.Add(keyParts[0], optionsForKeyBuilder); + } + + optionsForKeyBuilder[keyParts[1]] = value; + break; + } + } + + if (generalOptionsBuilder.Count == 0 && specificOptionsBuilder.Count == 0) + { + return Empty; + } + + var generalOptions = generalOptionsBuilder.Count > 0 ? + generalOptionsBuilder.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase) : + ImmutableDictionary.Empty; + + var specificOptions = specificOptionsBuilder.Count > 0 ? + specificOptionsBuilder + .Select(kvp => new KeyValuePair>(kvp.Key, kvp.Value.ToImmutableDictionary(StringComparer.OrdinalIgnoreCase))) + .ToImmutableDictionary(StringComparer.OrdinalIgnoreCase) : + ImmutableDictionary>.Empty; + + return new CategorizedAnalyzerConfigOptions(generalOptions, specificOptions); + } + + protected override bool TryGetOptionValue(string optionKeyPrefix, string? optionKeySuffix, string optionName, [NotNullWhen(returnValue: true)] out string? valueString) + { + if (optionKeySuffix != null) + { + valueString = null; + return _specificOptions.TryGetValue(optionKeySuffix, out var specificRuleOptions) && + specificRuleOptions.TryGetValue(optionName, out valueString); + } + + return _generalOptions.TryGetValue(optionName, out valueString); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/EditorConfigOptionNames.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/EditorConfigOptionNames.cs new file mode 100644 index 0000000000000..0d489b341d08b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/EditorConfigOptionNames.cs @@ -0,0 +1,240 @@ +// 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 Analyzer.Utilities +{ + /// + /// Option names to configure analyzer execution through an .editorconfig file. + /// + internal static partial class EditorConfigOptionNames + { + // ============================================================================================================= + // NOTE: Keep this file in sync with documentation at '<%REPO_ROOT%>\docs\Analyzer Configuration.md' + // ============================================================================================================= + + /// + /// Option to configure analyzed API surface. + /// Allowed option values: One or more fields of flags enum as a comma separated list. + /// + public const string ApiSurface = "api_surface"; + + /// + /// Option to configure required modifiers for analyzed APIs. + /// Allowed option values: One or more fields of flags enum as a comma separated list. + /// + public const string RequiredModifiers = "required_modifiers"; + + /// + /// Boolean option to exclude analysis of async void methods. + /// + public const string ExcludeAsyncVoidMethods = "exclude_async_void_methods"; + + /// + /// Boolean option to enable platform compatibility analyzer for TFMs with lower version than net5.0 (https://learn.microsoft.com/visualstudio/code-quality/ca1416). + /// + public const string EnablePlatformAnalyzerOnPreNet5Target = "enable_platform_analyzer_on_pre_net5_target"; + + /// + /// Option to configure analyzed output kinds, i.e. of the compilation. + /// Allowed option values: One or more fields of as a comma separated list. + /// + public const string OutputKind = "output_kind"; + + /// + /// Boolean option to configure if single letter type parameter names are not flagged for CA1715 (https://learn.microsoft.com/visualstudio/code-quality/ca1715). + /// + public const string ExcludeSingleLetterTypeParameters = "exclude_single_letter_type_parameters"; + + /// + /// Integral option to configure sufficient IterationCount when using weak KDF algorithm. + /// + public const string SufficientIterationCountForWeakKDFAlgorithm = "sufficient_IterationCount_for_weak_KDF_algorithm"; + + /// + /// Boolean option to exclude analysis of 'this' parameter for extension methods. + /// + public const string ExcludeExtensionMethodThisParameter = "exclude_extension_method_this_parameter"; + + /// + /// String option to configure names of null check validation methods (separated by '|') that validate arguments passed to the method are non-null for CA1062 (https://learn.microsoft.com/visualstudio/code-quality/ca1062). + /// Allowed method name formats: + /// 1. Method name only (includes all methods with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format + /// with an optional "M:" prefix. + /// + public const string NullCheckValidationMethods = "null_check_validation_methods"; + + /// + /// String option to configure names of additional string formatting methods (separated by '|') for CA2241 (https://learn.microsoft.com/visualstudio/code-quality/ca2241). + /// Allowed method name formats: + /// 1. Method name only (includes all methods with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format + /// with an optional "M:" prefix. + /// + public const string AdditionalStringFormattingMethods = "additional_string_formatting_methods"; + + /// + /// Boolean option to enable heuristically detecting of additional string formatting methods for CA2241 (https://learn.microsoft.com/visualstudio/code-quality/ca2241). + /// A method is considered a string formatting method if it has a ' format' parameter followed by a []' parameter. + /// The default value of this is false. + /// + public const string TryDetermineAdditionalStringFormattingMethodsAutomatically = "try_determine_additional_string_formatting_methods_automatically"; + + /// + /// String option to configure names of symbols (separated by '|') that are excluded for analysis. + /// Configurable rules: CA1303 (https://learn.microsoft.com/visualstudio/code-quality/ca1303). + /// Allowed method name formats: + /// 1. Symbol name only (includes all symbols with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format. + /// Note that each symbol name requires a symbol kind prefix, such as "M:" prefix for methods, "T:" prefix for types, "N:" prefix for namespaces, etc. + /// 3. ".ctor" for constructors and ".cctor" for static constructors + /// + public const string ExcludedSymbolNames = "excluded_symbol_names"; + + /// + /// String option to configure names of types (separated by '|'), so that the type and all its derived types are excluded for analysis. + /// Configurable rules: CA1303 (https://learn.microsoft.com/visualstudio/code-quality/ca1303). + /// Allowed method name formats: + /// 1. Type name only (includes all types with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format + /// with an optional "T:" prefix. + /// + public const string ExcludedTypeNamesWithDerivedTypes = "excluded_type_names_with_derived_types"; + + /// + /// String option to configure names of symbols (separated by '|') that are disallowed in analysis. + /// Configurable rules: CA1031 (https://learn.microsoft.com/visualstudio/code-quality/ca1031). + /// Allowed method name formats: + /// 1. Symbol name only (includes all symbols with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format. + /// Note that each symbol name requires a symbol kind prefix, such as "M:" prefix for methods, "T:" prefix for types, "N:" prefix for namespaces, etc. + /// 3. ".ctor" for constructors and ".cctor" for static constructors + /// + public const string DisallowedSymbolNames = "disallowed_symbol_names"; + + /// + /// Enumeration option to configure unsafe DllImportSearchPath bits when using DefaultDllImportSearchPaths attribute. + /// Do not use the OR operator to represent the bitwise combination of its member values, use the integral value directly. + /// + public const string UnsafeDllImportSearchPathBits = "unsafe_DllImportSearchPath_bits"; + + /// + /// Boolean option to configure whether to exclude aspnet core mvc ControllerBase when considering CSRF. + /// + public const string ExcludeAspnetCoreMvcControllerBase = "exclude_aspnet_core_mvc_controllerbase"; + + /// + /// String option to configure how many enum values should be prefixed by the enum type name to trigger the rule. + /// Configurable rules: CA1712 (https://learn.microsoft.com/visualstudio/code-quality/ca1712) + /// Allowed method name formats: + /// 1. Any of the enum values starts with the enum type name + /// 2. All of the enum values starts with the enum type name + /// 3. Default FxCop heuristic (75% of enum values) + /// + public const string EnumValuesPrefixTrigger = "enum_values_prefix_trigger"; + + /// + /// String option to configure names of types (separated by '|'), with their suffixes (separated by '->'). + /// Configurable rules: CA1710 (https://learn.microsoft.com/visualstudio/code-quality/ca1710). + /// Allowed type name formats: + /// 1. Type name only (includes all types with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format + /// with an optional "T:" prefix. + /// + public const string AdditionalRequiredSuffixes = "additional_required_suffixes"; + + /// + /// Boolean option to prevent analyzing indirect base types (walking more than one level up) when suggesting suffixes. + /// + public const string ExcludeIndirectBaseTypes = "exclude_indirect_base_types"; + + /// + /// String option to configure names of interfaces (separated by '|'), with their required generic interfaces (separated by '->'). + /// Configurable rules: CA1010 (https://learn.microsoft.com/visualstudio/code-quality/ca1010) + /// Allowed interface formats: + /// 1. Interface name only(includes all interfaces with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format with an optional "T:" prefix. + /// + public const string AdditionalRequiredGenericInterfaces = "additional_required_generic_interfaces"; + + /// + /// Names of types or namespaces (separated by '|'), such that the type or type's namespace doesn't count in the inheritance hierarchy tree. + /// Configurable rules: CA1501 (https://learn.microsoft.com/visualstudio/code-quality/ca1501) + /// Allowed name formats: + /// 1. Type or namespace name (includes all types with the name, regardless of the containing type or namespace and all types whose namespace contains the name) + /// 2. Type or namespace name ending with a wildcard symbol (includes all types whose name starts with the given name, regardless of the containing type or namespace + /// and all types whose namespace contains the name) + /// 3. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format with an optional "T:" prefix for types or "N:" prefix for namespaces. (includes all types with the exact type match or the exact containing namespace match) + /// 4. Fully qualified type or namespace name with an optional "T:" prefix for type or "N:" prefix for namespace and ending with the wildcard symbol (includes all types whose fully qualified name starts with the given suffix) + /// + public const string AdditionalInheritanceExcludedSymbolNames = "additional_inheritance_excluded_symbol_names"; + + /// + /// Option to configure analyzed symbol kinds, i.e. . + /// Allowed option values: One or more fields of as a comma separated list. + /// + public const string AnalyzedSymbolKinds = "analyzed_symbol_kinds"; + + /// + /// Boolean option to configure if the naming heuristic should be used for CA1303 (https://learn.microsoft.com/visualstudio/code-quality/ca1303). + /// + public const string UseNamingHeuristic = "use_naming_heuristic"; + + /// + /// String option to configure names of additional methods (separated by '|') for CA1806 (https://learn.microsoft.com/visualstudio/code-quality/ca1806). + /// Allowed method name formats: + /// 1. Method name only (includes all methods with the name, regardless of the containing type or namespace) + /// 2. Fully qualified names in the symbol's documentation ID format: https://github.com/dotnet/csharplang/blob/main/spec/documentation-comments.md#id-string-format + /// with an optional "M:" prefix. + /// + public const string AdditionalUseResultsMethods = "additional_use_results_methods"; + + /// + /// String option to configure allowed suffixed (separated by '|'). + /// Configurable rule: CA1711 (https://learn.microsoft.com/visualstudio/code-quality/ca1711). + /// + public const string AllowedSuffixes = "allowed_suffixes"; + + /// + /// Boolean option to configure whether to exclude structs when considering public fields. + /// + public const string ExcludeStructs = "exclude_structs"; + + /// + /// Boolean option to configure whether to exclude 'FirstOrDefault' and 'LastOrDefault' methods for + /// CA1826 (Do not use Enumerable methods on indexable collections. Instead use the collection directly). + /// + public const string ExcludeOrDefaultMethods = "exclude_ordefault_methods"; + + /// + /// String option to configure names of method symbols (separated by '|') that marks all of the parameters with IEnumerable type + /// would be enumerated. + /// Configurable rule: CA1851 (https://learn.microsoft.com/visualstudio/code-quality/ca1851). + /// + public const string EnumerationMethods = "enumeration_methods"; + + /// + /// String option to configure names of method symbols (separated by '|') that accepting parameter with IEnumerable type and return a new IEnumerable type, like 'Select' and 'Where'. + /// Configurable rule: CA1851 (https://learn.microsoft.com/visualstudio/code-quality/ca1851). + /// + public const string LinqChainMethods = "linq_chain_methods"; + + /// + /// Boolean option to configure the assumption that IEnumerable type parameters would be enumerated by method invocation or not. + /// It does not affect linq_chain_methods. + /// Configurable rule: CA1851 (https://learn.microsoft.com/visualstudio/code-quality/ca1851). + /// + public const string AssumeMethodEnumeratesParameters = "assume_method_enumerates_parameters"; + + /// + /// String option to configure names of additional "None" enum case (separated by '|') for CA1008. + /// + public const string AdditionalEnumNoneNames = "additional_enum_none_names"; + + /// + /// Boolean option whether to perform the analysis even if the assembly exposes its internals. + /// + public const string IgnoreInternalsVisibleTo = "ignore_internalsvisibleto"; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/EnumValuesPrefixTrigger.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/EnumValuesPrefixTrigger.cs new file mode 100644 index 0000000000000..5feabdf9e872d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/EnumValuesPrefixTrigger.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. + +namespace Analyzer.Utilities.Options +{ + public enum EnumValuesPrefixTrigger + { + // NOTE: Below fields names are used in the .editorconfig specification. + // Hence the names should *not* be modified, as that would be a breaking + // change for .editorconfig specification. + AnyEnumValue, + AllEnumValues, + Heuristic, + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/ICategorizedAnalyzerConfigOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/ICategorizedAnalyzerConfigOptions.cs new file mode 100644 index 0000000000000..b82861dee0470 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/ICategorizedAnalyzerConfigOptions.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + using static CategorizedAnalyzerConfigOptionsExtensions; + + /// + /// Analyzer configuration options that are parsed into general and specific configuration options. + /// + /// .editorconfig format: + /// + /// General configuration option: + /// + /// dotnet_code_quality.OptionName = OptionValue + /// + /// + /// Specific configuration option: + /// + /// dotnet_code_quality.RuleId.OptionName = OptionValue + /// dotnet_code_quality.RuleCategory.OptionName = OptionValue + /// + /// + /// + /// + /// .editorconfig examples to configure API surface analyzed by analyzers: + /// + /// General configuration option: + /// + /// dotnet_code_quality.api_surface = all + /// + /// + /// Specific configuration option: + /// + /// dotnet_code_quality.CA1040.api_surface = public, internal + /// dotnet_code_quality.Naming.api_surface = public + /// + /// + /// + /// + /// See for allowed symbol visibility value combinations. + /// + internal interface ICategorizedAnalyzerConfigOptions + { + bool IsEmpty { get; } + + T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality); + + T GetOptionValue(string optionName, SyntaxTree? tree, DiagnosticDescriptor? rule, TryParseValue tryParseValue, TArg arg, T defaultValue, OptionKind kind = OptionKind.DotnetCodeQuality); + } + + internal static class CategorizedAnalyzerConfigOptionsExtensions + { + public delegate bool TryParseValue(string value, [MaybeNullWhen(returnValue: false)] out T parsedValue); + + public delegate bool TryParseValue(string value, TArg arg, [MaybeNullWhen(returnValue: false)] out T parsedValue); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildItemOptionNames.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildItemOptionNames.cs new file mode 100644 index 0000000000000..258d858f8b83d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildItemOptionNames.cs @@ -0,0 +1,64 @@ +// 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; + +#if CODEANALYSIS_V3_OR_BETTER +using System.Linq; +#endif + +namespace Analyzer.Utilities +{ + /// + /// MSBuild item names that are required to be threaded as analyzer config options. + /// The analyzer config option will have the following key/value: + /// - Key: Item name prefixed with an '_' and suffixed with a 'List' to reduce chances of conflicts with any existing project property. + /// - Value: Concatenated item metadata values, separated by a ',' character. See https://github.com/dotnet/sdk/issues/12706#issuecomment-668219422 for details. + /// + internal static class MSBuildItemOptionNames + { + public const string SupportedPlatform = nameof(SupportedPlatform); + } + + internal static class MSBuildItemOptionNamesHelpers + { + public const char ValuesSeparator = ','; + private static readonly char[] s_itemMetadataValuesSeparators = new[] { ValuesSeparator }; + + public static string GetPropertyNameForItemOptionName(string itemOptionName) + { + VerifySupportedItemOptionName(itemOptionName); + return $"_{itemOptionName}List"; + } + + [Conditional("DEBUG")] + public static void VerifySupportedItemOptionName(string itemOptionName) + { +#if CODEANALYSIS_V3_OR_BETTER + Debug.Assert(typeof(MSBuildItemOptionNames).GetFields().Single(f => f.Name == itemOptionName) != null); +#endif + } + + public static ImmutableArray ParseItemOptionValue(string? itemOptionValue) + { + if (itemOptionValue == null) + { + return ImmutableArray.Empty; + } + + return ProduceTrimmedArray(itemOptionValue).ToImmutableArray(); + } + + private static IEnumerable ProduceTrimmedArray(string itemOptionValue) + { + foreach (var platform in itemOptionValue.Split(s_itemMetadataValuesSeparators, StringSplitOptions.RemoveEmptyEntries)) + { + yield return platform.Trim(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildPropertyOptionNames.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildPropertyOptionNames.cs new file mode 100644 index 0000000000000..e763c85873be4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/MSBuildPropertyOptionNames.cs @@ -0,0 +1,40 @@ +// 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.Diagnostics; + +#if CODEANALYSIS_V3_OR_BETTER +using System.Linq; +#endif + +namespace Analyzer.Utilities +{ + /// + /// MSBuild property names that are required to be threaded as analyzer config options. + /// + /// const fields in this type are automatically discovered and used to generate build_properties entries in the generated .globalconfig + internal static class MSBuildPropertyOptionNames + { + public const string TargetFramework = nameof(TargetFramework); + public const string TargetFrameworkIdentifier = nameof(TargetFrameworkIdentifier); + public const string TargetFrameworkVersion = nameof(TargetFrameworkVersion); + public const string TargetPlatformMinVersion = nameof(TargetPlatformMinVersion); + public const string UsingMicrosoftNETSdkWeb = nameof(UsingMicrosoftNETSdkWeb); + public const string ProjectTypeGuids = nameof(ProjectTypeGuids); + public const string InvariantGlobalization = nameof(InvariantGlobalization); + public const string PlatformNeutralAssembly = nameof(PlatformNeutralAssembly); + public const string EnforceExtendedAnalyzerRules = nameof(EnforceExtendedAnalyzerRules); + } + + internal static class MSBuildPropertyOptionNamesHelpers + { + [Conditional("DEBUG")] + public static void VerifySupportedPropertyOptionName(string propertyOptionName) + { +#if CODEANALYSIS_V3_OR_BETTER + Debug.Assert(typeof(MSBuildPropertyOptionNames).GetFields().Single(f => f.Name == propertyOptionName) != null); +#endif + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKey.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKey.cs new file mode 100644 index 0000000000000..db47dbf5e0bf2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKey.cs @@ -0,0 +1,59 @@ +// 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.Concurrent; +using System.Threading; + +namespace Analyzer.Utilities +{ + internal readonly struct OptionKey : IEquatable + { + private static readonly ConcurrentDictionary<(string ruleId, string optionName), OptionKey> s_keys = new(); + private static int s_lastOrdinal; + + private readonly int _ordinal; + + private OptionKey(string name) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + _ordinal = Interlocked.Increment(ref s_lastOrdinal); + } + + public string Name { get; } + + public static OptionKey GetOrCreate(string? ruleId, string optionName) + { + return s_keys.GetOrAdd( + (ruleId ?? "", optionName), + static pair => new OptionKey(pair.ruleId is not null ? $"{pair.ruleId}.{pair.optionName}" : pair.optionName)); + } + + public static bool operator ==(OptionKey left, OptionKey right) + { + return left.Equals(right); + } + + public static bool operator !=(OptionKey left, OptionKey right) + { + return !(left == right); + } + + public override bool Equals(object? obj) + { + return obj is OptionKey other + && Equals(other); + } + + public override int GetHashCode() + { + return _ordinal; + } + + public bool Equals(OptionKey other) + { + return _ordinal == other._ordinal; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKind.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKind.cs new file mode 100644 index 0000000000000..d39b448531225 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/OptionKind.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities +{ + /// + /// Kind of option to fetch from . + /// + internal enum OptionKind + { + /// + /// Option prefixed with dotnet_code_quality.. + /// Used for custom analyzer config options for analyzers in this repo. + /// + DotnetCodeQuality, + + /// + /// Option prefixed with build_property.. + /// Used for options generated for MSBuild properties. + /// + BuildProperty, + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolModifiers.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolModifiers.cs new file mode 100644 index 0000000000000..d63281e76a920 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolModifiers.cs @@ -0,0 +1,90 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Describes a group of modifiers for symbol declaration. + /// + [Flags] + public enum SymbolModifiers + { + // NOTE: Below fields names are used in the .editorconfig specification + // for symbol modifiers analyzer option. Hence the names should *not* be modified, + // as that would be a breaking change for .editorconfig specification. + None = 0x0000, + Static = 0x0001, + Shared = Static, + Const = 0x0002, + ReadOnly = 0x0004, + Abstract = 0x0008, + Virtual = 0x0010, + Override = 0x0020, + Sealed = 0x0040, + Extern = 0x0080, + Async = 0x0100, + } + + internal static class SymbolModifiersExtensions + { + public static bool Contains(this SymbolModifiers modifiers, SymbolModifiers modifiersToCheck) + => (modifiers & modifiersToCheck) == modifiersToCheck; + + public static SymbolModifiers GetSymbolModifiers(this ISymbol symbol) + { + var modifiers = SymbolModifiers.None; + if (symbol.IsStatic) + { + modifiers |= SymbolModifiers.Static; + } + + if (symbol.IsConst()) + { + modifiers |= SymbolModifiers.Const; + } + + if (symbol.IsReadOnly()) + { + modifiers |= SymbolModifiers.ReadOnly; + } + + if (symbol.IsAbstract) + { + modifiers |= SymbolModifiers.Abstract; + } + + if (symbol.IsVirtual) + { + modifiers |= SymbolModifiers.Virtual; + } + + if (symbol.IsOverride) + { + modifiers |= SymbolModifiers.Override; + } + + if (symbol.IsSealed) + { + modifiers |= SymbolModifiers.Sealed; + } + + if (symbol.IsExtern) + { + modifiers |= SymbolModifiers.Extern; + } + + if (symbol is IMethodSymbol method && + method.IsAsync) + { + modifiers |= SymbolModifiers.Async; + } + + return modifiers; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolNamesWithValueOption.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolNamesWithValueOption.cs new file mode 100644 index 0000000000000..40c47ed95b0cc --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolNamesWithValueOption.cs @@ -0,0 +1,405 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ +#if !TEST_UTILITIES + public sealed class SymbolNamesWithValueOption +#else + internal sealed class SymbolNamesWithValueOption +#endif + : IEquatable?> + { + internal const SymbolKind AllKinds = SymbolKind.ErrorType; + internal const char WildcardChar = '*'; + + public static readonly SymbolNamesWithValueOption Empty = new(); + + private readonly ImmutableDictionary _names; + private readonly ImmutableDictionary _symbols; + + /// + /// Dictionary holding per symbol kind the wildcard entry with its suffix. + /// The implementation only supports the following SymbolKind: Namespace, Type, Event, Field, Method, Property and ErrorType (as a way to hold the non-fully qualified types). + /// + /// + /// ErrorType -> + /// Symbol* -> "some value" + /// Namespace -> + /// Analyzer.Utilities -> "" + /// Type -> + /// Analyzer.Utilities.SymbolNamesWithValueOption -> "" + /// Event -> + /// Analyzer.Utilities.SymbolNamesWithValueOption.MyEvent -> "" + /// Field -> + /// Analyzer.Utilities.SymbolNamesWithValueOption.myField -> "" + /// Method -> + /// Analyzer.Utilities.SymbolNamesWithValueOption.MyMethod() -> "" + /// Property -> + /// Analyzer.Utilities.SymbolNamesWithValueOption.MyProperty -> "" + /// + private readonly ImmutableDictionary> _wildcardNamesBySymbolKind; + + /// + /// Cache for the wildcard matching algorithm. The current implementation can be slow so we want to make sure that once a match is performed we save its result. + /// + private readonly ConcurrentDictionary> _wildcardMatchResult = new(); + + private readonly ConcurrentDictionary _symbolToDeclarationId = new(); + + private SymbolNamesWithValueOption(ImmutableDictionary names, ImmutableDictionary symbols, + ImmutableDictionary> wildcardNamesBySymbolKind) + { + Debug.Assert(!names.IsEmpty || !symbols.IsEmpty || !wildcardNamesBySymbolKind.IsEmpty); + + _names = names; + _symbols = symbols; + _wildcardNamesBySymbolKind = wildcardNamesBySymbolKind; + } + + private SymbolNamesWithValueOption() + { + _names = ImmutableDictionary.Empty; + _symbols = ImmutableDictionary.Empty; + _wildcardNamesBySymbolKind = ImmutableDictionary>.Empty; + } + +#pragma warning disable CA1000 // Do not declare static members on generic types + public static SymbolNamesWithValueOption Create(ImmutableArray symbolNames, Compilation compilation, string? optionalPrefix, +#pragma warning restore CA1000 // Do not declare static members on generic types + Func getSymbolNamePartsFunc) + { + if (symbolNames.IsEmpty) + { + return Empty; + } + + var namesBuilder = PooledDictionary.GetInstance(); + var symbolsBuilder = PooledDictionary.GetInstance(); + var wildcardNamesBuilder = PooledDictionary>.GetInstance(); + + foreach (var symbolName in symbolNames) + { + var parts = getSymbolNamePartsFunc(symbolName); + + var numberOfWildcards = parts.SymbolName.Count(c => c == WildcardChar); + + // More than one wildcard, bail-out. + if (numberOfWildcards > 1) + { + continue; + } + + // Wildcard is not last or is the only char, bail-out + if (numberOfWildcards == 1 && + (parts.SymbolName[^1] != WildcardChar || + parts.SymbolName.Length == 1)) + { + continue; + } + + if (numberOfWildcards == 1) + { + ProcessWildcardName(parts, wildcardNamesBuilder); + } +#pragma warning disable CA1847 // Use 'string.Contains(char)' instead of 'string.Contains(string)' when searching for a single character + else if (parts.SymbolName.Equals(".ctor", StringComparison.Ordinal) || + parts.SymbolName.Equals(".cctor", StringComparison.Ordinal) || + !parts.SymbolName.Contains(".", StringComparison.Ordinal) && !parts.SymbolName.Contains(":", StringComparison.Ordinal)) + { + ProcessName(parts, namesBuilder); + } + else + { + ProcessSymbolName(parts, compilation, optionalPrefix, symbolsBuilder); + } +#pragma warning restore CA1847 // Use 'string.Contains(char)' instead of 'string.Contains(string)' when searching for a single character + } + + if (namesBuilder.Count == 0 && symbolsBuilder.Count == 0 && wildcardNamesBuilder.Count == 0) + { + return Empty; + } + + return new SymbolNamesWithValueOption(namesBuilder.ToImmutableDictionaryAndFree(), + symbolsBuilder.ToImmutableDictionaryAndFree(), + wildcardNamesBuilder.ToImmutableDictionaryAndFree(x => x.Key, x => x.Value.ToImmutableDictionaryAndFree(), wildcardNamesBuilder.Comparer)); + + // Local functions + static void ProcessWildcardName(NameParts parts, PooledDictionary> wildcardNamesBuilder) + { + Debug.Assert(parts.SymbolName[^1] == WildcardChar); + Debug.Assert(parts.SymbolName.Length >= 2); + + if (parts.SymbolName[1] != ':') + { + if (!wildcardNamesBuilder.TryGetValue(AllKinds, out var associatedValues)) + { + associatedValues = PooledDictionary.GetInstance(); + wildcardNamesBuilder.Add(AllKinds, associatedValues); + } + + associatedValues.Add(parts.SymbolName[0..^1], parts.AssociatedValue); + return; + } + + var symbolKind = parts.SymbolName[0] switch + { + 'E' => (SymbolKind?)SymbolKind.Event, + 'F' => SymbolKind.Field, + 'M' => SymbolKind.Method, + 'N' => SymbolKind.Namespace, + 'P' => SymbolKind.Property, + 'T' => SymbolKind.NamedType, + _ => null, + }; + + if (symbolKind != null) + { + if (!wildcardNamesBuilder.TryGetValue(symbolKind.Value, out var associatedValues)) + { + associatedValues = PooledDictionary.GetInstance(); + wildcardNamesBuilder.Add(symbolKind.Value, associatedValues); + } + + associatedValues.Add(parts.SymbolName[2..^1], parts.AssociatedValue); + } + } + + static void ProcessName(NameParts parts, PooledDictionary namesBuilder) + { + if (!namesBuilder.ContainsKey(parts.SymbolName)) + { + namesBuilder.Add(parts.SymbolName, parts.AssociatedValue); + } + } + + static void ProcessSymbolName(NameParts parts, Compilation compilation, string? optionalPrefix, PooledDictionary symbolsBuilder) + { + var nameWithPrefix = (string.IsNullOrEmpty(optionalPrefix) || parts.SymbolName.StartsWith(optionalPrefix, StringComparison.Ordinal)) + ? parts.SymbolName + : optionalPrefix + parts.SymbolName; + + // Documentation comment ID for constructors uses '#ctor', but '#' is a comment start token for editorconfig. + // We instead search for a '..ctor' in editorconfig and replace it with a '.#ctor' here. + // Similarly, handle static constructors ".cctor" + nameWithPrefix = nameWithPrefix.Replace("..ctor", ".#ctor", StringComparison.Ordinal); + nameWithPrefix = nameWithPrefix.Replace("..cctor", ".#cctor", StringComparison.Ordinal); + + foreach (var symbol in DocumentationCommentId.GetSymbolsForDeclarationId(nameWithPrefix, compilation)) + { + if (symbol == null) + { + continue; + } + + if (symbol is INamespaceSymbol namespaceSymbol && + namespaceSymbol.ConstituentNamespaces.Length > 1) + { + foreach (var constituentNamespace in namespaceSymbol.ConstituentNamespaces) + { + if (!symbolsBuilder.ContainsKey(constituentNamespace)) + { + symbolsBuilder.Add(constituentNamespace, parts.AssociatedValue); + } + } + } + + if (!symbolsBuilder.ContainsKey(symbol)) + { + symbolsBuilder.Add(symbol, parts.AssociatedValue); + } + } + } + } + + public bool IsEmpty => ReferenceEquals(this, Empty); + + public bool Contains(ISymbol symbol) + => _symbols.ContainsKey(symbol) || _names.ContainsKey(symbol.Name) || TryGetFirstWildcardMatch(symbol, out _, out _); + + /// + /// Gets the value associated with the specified symbol in the option specification. + /// + public bool TryGetValue(ISymbol symbol, [MaybeNullWhen(false)] out TValue value) + { + if (_symbols.TryGetValue(symbol, out value) || _names.TryGetValue(symbol.Name, out value)) + { + return true; + } + + if (TryGetFirstWildcardMatch(symbol, out _, out value)) + { + return true; + } + + value = default; + return false; + } + + public override bool Equals(object? obj) => Equals(obj as SymbolNamesWithValueOption); + + public bool Equals(SymbolNamesWithValueOption? other) + => other != null && _names.IsEqualTo(other._names) && _symbols.IsEqualTo(other._symbols) && _wildcardNamesBySymbolKind.IsEqualTo(other._wildcardNamesBySymbolKind); + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(_names, ref hashCode); + HashUtilities.Combine(_symbols, ref hashCode); + HashUtilities.Combine(_wildcardNamesBySymbolKind, ref hashCode); + return hashCode.ToHashCode(); + } + + private bool TryGetFirstWildcardMatch(ISymbol symbol, [NotNullWhen(true)] out string? firstMatchName, [MaybeNullWhen(false)] out TValue firstMatchValue) + { + switch (symbol.Kind) + { + case SymbolKind.Event: + case SymbolKind.Field: + case SymbolKind.Method: + case SymbolKind.NamedType: + case SymbolKind.Namespace: + case SymbolKind.Property: + break; + + case SymbolKind.Assembly: + case SymbolKind.ErrorType: + case SymbolKind.NetModule: + firstMatchName = null; + firstMatchValue = default; + return false; + + default: + throw new ArgumentException($"Unsupported symbol kind '{symbol.Kind}' for symbol '{symbol}'"); + } + + // No wildcard entry, let's bail-out + if (_wildcardNamesBySymbolKind.IsEmpty) + { + firstMatchName = null; + firstMatchValue = default; + return false; + } + + // The matching was already processed, use cached result + if (_wildcardMatchResult.TryGetValue(symbol, out var firstMatch)) + { + (firstMatchName, firstMatchValue) = firstMatch; +#pragma warning disable CS8762 // Parameter 'firstMatchValue' must have a non-null value when exiting with 'true' + return firstMatchName is not null; +#pragma warning restore CS8762 // Parameter 'firstMatchValue' must have a non-null value when exiting with 'true' + } + + var symbolDeclarationId = _symbolToDeclarationId.GetOrAdd(symbol, GetDeclarationId); + + // We start by trying to match with the most precise definition (prefix)... + if (_wildcardNamesBySymbolKind.TryGetValue(symbol.Kind, out var names) && + names.FirstOrDefault(kvp => symbolDeclarationId.StartsWith(kvp.Key, StringComparison.Ordinal)) is var prefixedFirstMatchOrDefault && + !string.IsNullOrWhiteSpace(prefixedFirstMatchOrDefault.Key)) + { + (firstMatchName, firstMatchValue) = prefixedFirstMatchOrDefault; + _wildcardMatchResult.AddOrUpdate(symbol, prefixedFirstMatchOrDefault.AsNullable(), (s, match) => prefixedFirstMatchOrDefault.AsNullable()); + return true; + } + + // If not found, then we try to match with the symbol full declaration ID... + if (_wildcardNamesBySymbolKind.TryGetValue(AllKinds, out var value) && + value.FirstOrDefault(kvp => symbolDeclarationId.StartsWith(kvp.Key, StringComparison.Ordinal)) is var unprefixedFirstMatchOrDefault && + !string.IsNullOrWhiteSpace(unprefixedFirstMatchOrDefault.Key)) + { + (firstMatchName, firstMatchValue) = unprefixedFirstMatchOrDefault; + _wildcardMatchResult.AddOrUpdate(symbol, unprefixedFirstMatchOrDefault.AsNullable(), (s, match) => unprefixedFirstMatchOrDefault.AsNullable()); + return true; + } + + // If not found, then we try to match with the symbol name... + if (_wildcardNamesBySymbolKind.TryGetValue(AllKinds, out var allKindsValue) && + allKindsValue.FirstOrDefault(kvp => symbol.Name.StartsWith(kvp.Key, StringComparison.Ordinal)) is var partialFirstMatchOrDefault && + !string.IsNullOrWhiteSpace(partialFirstMatchOrDefault.Key)) + { + (firstMatchName, firstMatchValue) = partialFirstMatchOrDefault; + _wildcardMatchResult.AddOrUpdate(symbol, partialFirstMatchOrDefault.AsNullable(), (s, match) => partialFirstMatchOrDefault.AsNullable()); + return true; + } + + // Nothing was found + firstMatchName = null; + firstMatchValue = default; + _wildcardMatchResult.AddOrUpdate(symbol, new KeyValuePair(null, default), (s, match) => new KeyValuePair(null, default)); + return false; + + static string GetDeclarationId(ISymbol symbol) + { + var declarationIdWithoutPrefix = DocumentationCommentId.CreateDeclarationId(symbol)[2..]; + + // Documentation comment ID for constructors uses '#ctor', but '#' is a comment start token for editorconfig. + declarationIdWithoutPrefix = declarationIdWithoutPrefix + .Replace(".#ctor", "..ctor", StringComparison.Ordinal) + .Replace(".#cctor", "..cctor", StringComparison.Ordinal); + + return declarationIdWithoutPrefix; + } + } + + internal TestAccessor GetTestAccessor() + { + return new TestAccessor(this); + } + + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Does not apply to test accessors")] + internal readonly struct TestAccessor + { + private readonly SymbolNamesWithValueOption _symbolNamesWithValueOption; + + internal TestAccessor(SymbolNamesWithValueOption symbolNamesWithValueOption) + { + _symbolNamesWithValueOption = symbolNamesWithValueOption; + } + + internal ref readonly ImmutableDictionary Names => ref _symbolNamesWithValueOption._names; + + internal ref readonly ImmutableDictionary Symbols => ref _symbolNamesWithValueOption._symbols; + + internal ref readonly ImmutableDictionary> WildcardNamesBySymbolKind => ref _symbolNamesWithValueOption._wildcardNamesBySymbolKind; + + internal ref readonly ConcurrentDictionary> WildcardMatchResult => ref _symbolNamesWithValueOption._wildcardMatchResult; + + internal ref readonly ConcurrentDictionary SymbolToDeclarationId => ref _symbolNamesWithValueOption._symbolToDeclarationId; + } + + /// + /// Represents the two parts of a symbol name option when the symbol name is tighted to some specific value. + /// This allows to link a value to a symbol while following the symbol's documentation ID format. + /// + /// + /// On the rule CA1710, we allow user specific suffix to be registered for symbol names using the following format: + /// MyClass->Suffix or T:MyNamespace.MyClass->Suffix or N:MyNamespace->Suffix. + /// +#pragma warning disable CA1034 // Nested types should not be visible + public sealed class NameParts +#pragma warning restore CA1034 // Nested types should not be visible + { + public NameParts(string symbolName, TValue associatedValue) + { + SymbolName = symbolName.Trim(); + AssociatedValue = associatedValue; + } + + public string SymbolName { get; } + public TValue AssociatedValue { get; } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolVisibilityGroup.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolVisibilityGroup.cs new file mode 100644 index 0000000000000..d5054d0d91d19 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/SymbolVisibilityGroup.cs @@ -0,0 +1,45 @@ +// 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 Analyzer.Utilities.Extensions; + +namespace Analyzer.Utilities +{ + /// + /// Describes a group of effective for symbols. + /// + [Flags] +#pragma warning disable CA1714 // Flags enums should have plural names + public enum SymbolVisibilityGroup +#pragma warning restore CA1714 // Flags enums should have plural names + { + // NOTE: Below fields names are used in the .editorconfig specification + // for symbol visibility analyzer option. Hence the names should *not* be modified, + // as that would be a breaking change for .editorconfig specification. + None = 0x0, + Public = 0x1, + Internal = 0x2, + Private = 0x4, + Friend = Internal, + All = Public | Internal | Private + } + + internal static class SymbolVisibilityGroupExtensions + { + public static bool Contains(this SymbolVisibilityGroup symbolVisibilityGroup, SymbolVisibility symbolVisibility) + { + return symbolVisibility switch + { + SymbolVisibility.Public => (symbolVisibilityGroup & SymbolVisibilityGroup.Public) != 0, + + SymbolVisibility.Internal => (symbolVisibilityGroup & SymbolVisibilityGroup.Internal) != 0, + + SymbolVisibility.Private => (symbolVisibilityGroup & SymbolVisibilityGroup.Private) != 0, + + _ => throw new ArgumentOutOfRangeException(nameof(symbolVisibility), symbolVisibility, null), + }; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/SyntaxTreeCategorizedAnalyzerConfigOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/SyntaxTreeCategorizedAnalyzerConfigOptions.cs new file mode 100644 index 0000000000000..627cc05fce297 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/SyntaxTreeCategorizedAnalyzerConfigOptions.cs @@ -0,0 +1,86 @@ +// 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. + +#if CODEANALYSIS_V3_OR_BETTER + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Analyzer.Utilities +{ + /// + /// Analyzer configuration options for a given syntax tree from + /// + internal sealed class SyntaxTreeCategorizedAnalyzerConfigOptions : AbstractCategorizedAnalyzerConfigOptions + { + private readonly AnalyzerConfigOptions? _analyzerConfigOptions; + private static readonly ConditionalWeakTable, SyntaxTreeCategorizedAnalyzerConfigOptions> s_perTreeOptionsCache = new(); + + public static readonly SyntaxTreeCategorizedAnalyzerConfigOptions Empty = new(analyzerConfigOptions: null); + + private SyntaxTreeCategorizedAnalyzerConfigOptions(AnalyzerConfigOptions? analyzerConfigOptions) + { + _analyzerConfigOptions = analyzerConfigOptions; + } + + public static SyntaxTreeCategorizedAnalyzerConfigOptions Create(AnalyzerConfigOptions? analyzerConfigOptions) + { + if (analyzerConfigOptions == null) + { + return Empty; + } + + var optionsMap = TryGetBackingOptionsDictionary(analyzerConfigOptions); + if (optionsMap == null) + { + return new SyntaxTreeCategorizedAnalyzerConfigOptions(analyzerConfigOptions); + } + + if (optionsMap.IsEmpty) + { + return Empty; + } + + return s_perTreeOptionsCache.GetValue(optionsMap, _ => new SyntaxTreeCategorizedAnalyzerConfigOptions(analyzerConfigOptions)); + + // Local functions. + static ImmutableDictionary? TryGetBackingOptionsDictionary(AnalyzerConfigOptions analyzerConfigOptions) + { + // Reflection based optimization for analyzer config options. + // Ideally 'AnalyzerConfigOptions' would expose such an the API. + var type = analyzerConfigOptions.GetType(); + const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic; + return type.GetField("_backing", flags)?.GetValue(analyzerConfigOptions) as ImmutableDictionary ?? + type.GetField("_analyzerOptions", flags)?.GetValue(analyzerConfigOptions) as ImmutableDictionary; + } + } + + public override bool IsEmpty => ReferenceEquals(this, Empty); + + protected override bool TryGetOptionValue(string optionKeyPrefix, string? optionKeySuffix, string optionName, [NotNullWhen(returnValue: true)] out string? valueString) + { + if (IsEmpty) + { + valueString = null; + return false; + } + + RoslynDebug.Assert(_analyzerConfigOptions != null); + var key = optionKeyPrefix; + if (optionKeySuffix != null) + { + key += $"{optionKeySuffix}."; + } + + key += optionName; + + return _analyzerConfigOptions.TryGetValue(key, out valueString); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/Unit.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/Unit.cs new file mode 100644 index 0000000000000..701b9899ffd36 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/Unit.cs @@ -0,0 +1,76 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Represents a type with a single value. This type is often used to denote the successful completion of a void-returning method (C#) or a Sub procedure (Visual Basic). + /// + /// + /// This class is a duplicate from "https://github.com/dotnet/reactive/blob/main/Rx.NET/Source/src/System.Reactive/Unit.cs + /// +#if !TEST_UTILITIES + public struct Unit +#else + internal struct Unit +#endif + : IEquatable + { + /// + /// Determines whether the specified value is equal to the current . Because has a single value, this always returns true. + /// + /// An object to compare to the current value. + /// Because has a single value, this always returns true. + public readonly bool Equals(Unit other) => true; + + /// + /// Determines whether the specified System.Object is equal to the current . + /// + /// The System.Object to compare with the current . + /// true if the specified System.Object is a value; otherwise, false. + public override readonly bool Equals(object? obj) => obj is Unit; + + /// + /// Returns the hash code for the current value. + /// + /// A hash code for the current value. + public override readonly int GetHashCode() => 0; + + /// + /// Returns a string representation of the current value. + /// + /// String representation of the current value. + public override readonly string ToString() => "()"; + + /// + /// Determines whether the two specified values are equal. Because has a single value, this always returns true. + /// + /// The first value to compare. + /// The second value to compare. + /// Because has a single value, this always returns true. + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "first", Justification = "Parameter required for operator overloading.")] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "second", Justification = "Parameter required for operator overloading.")] + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Parameter required for operator overloading.")] + public static bool operator ==(Unit first, Unit second) => true; + + /// + /// Determines whether the two specified values are not equal. Because has a single value, this always returns false. + /// + /// The first value to compare. + /// The second value to compare. + /// Because has a single value, this always returns false. + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "first", Justification = "Parameter required for operator overloading.")] + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "second", Justification = "Parameter required for operator overloading.")] + [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Parameter required for operator overloading.")] + public static bool operator !=(Unit first, Unit second) => false; + + /// + /// Gets the single value. + /// + public static Unit Default => default; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Options/ValueUsageInfo.cs b/src/RoslynAnalyzers/Utilities/Compiler/Options/ValueUsageInfo.cs new file mode 100644 index 0000000000000..363063ab4dd3e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Options/ValueUsageInfo.cs @@ -0,0 +1,83 @@ +// 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; + +namespace Analyzer.Utilities +{ + [Flags] +#pragma warning disable CA1714 // Flags enums should have plural names + internal enum ValueUsageInfo +#pragma warning restore CA1714 + { + /// + /// Represents default value indicating no usage. + /// + None = 0x0, + + /// + /// Represents a value read. + /// For example, reading the value of a local/field/parameter. + /// + Read = 0x1, + + /// + /// Represents a value write. + /// For example, assigning a value to a local/field/parameter. + /// + Write = 0x2, + + /// + /// Represents a reference being taken for the symbol. + /// For example, passing an argument to an "in", "ref" or "out" parameter. + /// + Reference = 0x4, + + /// + /// Represents a name-only reference that neither reads nor writes the underlying value. + /// For example, 'nameof(x)' or reference to a symbol 'x' in a documentation comment + /// does not read or write the underlying value stored in 'x'. + /// + Name = 0x8, + + /// + /// Represents a value read and/or write. + /// For example, an increment or compound assignment operation. + /// + ReadWrite = Read | Write, + + /// + /// Represents a readable reference being taken to the value. + /// For example, passing an argument to an "in" or "ref readonly" parameter. + /// + ReadableReference = Read | Reference, + + /// + /// Represents a readable reference being taken to the value. + /// For example, passing an argument to an "out" parameter. + /// + WritableReference = Write | Reference, + + /// + /// Represents a value read or write. + /// For example, passing an argument to a "ref" parameter. + /// + ReadableWritableReference = Read | Write | Reference + } + + internal static class ValueUsageInfoExtensions + { + public static bool IsReadFrom(this ValueUsageInfo valueUsageInfo) + => (valueUsageInfo & ValueUsageInfo.Read) != 0; + + public static bool IsWrittenTo(this ValueUsageInfo valueUsageInfo) + => (valueUsageInfo & ValueUsageInfo.Write) != 0; + + public static bool IsNameOnly(this ValueUsageInfo valueUsageInfo) + => (valueUsageInfo & ValueUsageInfo.Name) != 0; + + public static bool IsReference(this ValueUsageInfo valueUsageInfo) + => (valueUsageInfo & ValueUsageInfo.Reference) != 0; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PathHelper.cs b/src/RoslynAnalyzers/Utilities/Compiler/PathHelper.cs new file mode 100644 index 0000000000000..6511470c98496 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PathHelper.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. + +#if CODEANALYSIS_V3_OR_BETTER + +using System; +using System.IO; + +namespace Analyzer.Utilities +{ + internal static class PathHelper + { + private static readonly char[] DirectorySeparatorCharacters = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + public static ReadOnlySpan GetFileName(string? path) + { + if (RoslynString.IsNullOrEmpty(path)) + return ReadOnlySpan.Empty; + + var lastSeparator = path.LastIndexOfAny(DirectorySeparatorCharacters); + if (lastSeparator < 0) + return path.AsSpan(); + + return path.AsSpan()[(lastSeparator + 1)..]; + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.Enumerator.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.Enumerator.cs new file mode 100644 index 0000000000000..0c5b59a765a27 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.Enumerator.cs @@ -0,0 +1,47 @@ +// 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; + +#pragma warning disable CA1710 // Rename Microsoft.CodeAnalysis.ArrayBuilder to end in 'Collection'. + +namespace Analyzer.Utilities.PooledObjects +{ + internal partial class ArrayBuilder + { + /// + /// struct enumerator used in foreach. + /// + internal struct Enumerator : IEnumerator + { + private readonly ArrayBuilder _builder; + private int _index; + + public Enumerator(ArrayBuilder builder) + { + _builder = builder; + _index = -1; + } + + public readonly T Current => _builder[_index]; + + public bool MoveNext() + { + _index++; + return _index < _builder.Count; + } + + public readonly void Dispose() + { + } + + readonly object? System.Collections.IEnumerator.Current => this.Current; + + public void Reset() + { + _index = -1; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.cs new file mode 100644 index 0000000000000..23a34792973e8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ArrayBuilder.cs @@ -0,0 +1,533 @@ +// 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 System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + [DebuggerDisplay("Count = {Count,nq}")] + [DebuggerTypeProxy(typeof(ArrayBuilder<>.DebuggerProxy))] + internal sealed partial class ArrayBuilder : IReadOnlyList, IDisposable + { + #region DebuggerProxy +#pragma warning disable CA1812 // ArrayBuilder.DebuggerProxy is an internal class that is apparently never instantiated - used in DebuggerTypeProxy attribute above. + private sealed class DebuggerProxy + { + private readonly ArrayBuilder _builder; + + public DebuggerProxy(ArrayBuilder builder) + { + _builder = builder; + } + +#pragma warning disable CA1819 // Properties should not return arrays + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] A + { + get + { + var result = new T[_builder.Count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = _builder[i]; + } + + return result; + } + } + } +#pragma warning restore CA1819 +#pragma warning restore CA1812 + #endregion + + private readonly ImmutableArray.Builder _builder; + + private readonly ObjectPool>? _pool; + + public ArrayBuilder(int size) + { + _builder = ImmutableArray.CreateBuilder(size); + } + + public ArrayBuilder() + : this(8) + { } + + private ArrayBuilder(ObjectPool>? pool) + : this() + { + _pool = pool; + } + + /// + /// Realizes the array. + /// + public ImmutableArray ToImmutable() + { + return _builder.ToImmutable(); + } + + public int Count + { + get => _builder.Count; + set => _builder.Count = value; + } + + public T this[int index] + { + get => _builder[index]; + set => _builder[index] = value; + } + + /// + /// Write to slot . + /// Fills in unallocated slots preceding the , if any. + /// + public void SetItem(int index, T value) + { + while (index > _builder.Count) + { + _builder.Add(default!); + } + + if (index == _builder.Count) + { + _builder.Add(value); + } + else + { + _builder[index] = value; + } + } + + public void Add(T item) + { + _builder.Add(item); + } + + public void Insert(int index, T item) + { + _builder.Insert(index, item); + } + + public void EnsureCapacity(int capacity) + { + if (_builder.Capacity < capacity) + { + _builder.Capacity = capacity; + } + } + + public void Clear() + { + _builder.Clear(); + } + + public bool Contains(T item) + { + return _builder.Contains(item); + } + + public int IndexOf(T item) + { + return _builder.IndexOf(item); + } + + public int IndexOf(T item, IEqualityComparer equalityComparer) + { + return _builder.IndexOf(item, 0, _builder.Count, equalityComparer); + } + + public int IndexOf(T item, int startIndex, int count) + { + return _builder.IndexOf(item, startIndex, count); + } + + public int FindIndex(Predicate match) + => FindIndex(0, this.Count, match); + + public int FindIndex(int startIndex, Predicate match) + => FindIndex(startIndex, this.Count - startIndex, match); + + public int FindIndex(int startIndex, int count, Predicate match) + { + int endIndex = startIndex + count; + for (int i = startIndex; i < endIndex; i++) + { + if (match(_builder[i])) + { + return i; + } + } + + return -1; + } + + public void RemoveAt(int index) + { + _builder.RemoveAt(index); + } + + public void RemoveLast() + { + _builder.RemoveAt(_builder.Count - 1); + } + + public void ReverseContents() + { + _builder.Reverse(); + } + + public void Sort() + { + _builder.Sort(); + } + + public void Sort(IComparer comparer) + { + _builder.Sort(comparer); + } + + public void Sort(Comparison compare) + => Sort(Comparer.Create(compare)); + + public void Sort(int startIndex, IComparer comparer) + { + _builder.Sort(startIndex, _builder.Count - startIndex, comparer); + } + + public T[] ToArray() + { + return _builder.ToArray(); + } + + public void CopyTo(T[] array, int start) + { + _builder.CopyTo(array, start); + } + + public T Last() + { +#pragma warning disable IDE0056 + return _builder[_builder.Count - 1]; +#pragma warning restore IDE0056 + } + + public T First() + { + return _builder[0]; + } + + public bool Any() + { + return _builder.Count > 0; + } + + /// + /// Realizes the array. + /// + public ImmutableArray ToImmutableOrNull() + { + if (Count == 0) + { + return default; + } + + return this.ToImmutable(); + } + + /// + /// Realizes the array, downcasting each element to a derived type. + /// + public ImmutableArray ToDowncastedImmutable() + where U : T + { + if (Count == 0) + { + return ImmutableArray.Empty; + } + + var tmp = ArrayBuilder.GetInstance(Count); + foreach (var i in _builder) + { + tmp.Add((U)i!); + } + + return tmp.ToImmutableAndFree(); + } + + /// + /// Realizes the array and disposes the builder in one operation. + /// + public ImmutableArray ToImmutableAndFree() + { + ImmutableArray result; + if (_builder.Capacity == Count) + { + result = _builder.MoveToImmutable(); + } + else + { + result = ToImmutable(); + } + + this.Free(); + return result; + } + + public T[] ToArrayAndFree() + { + var result = this.ToArray(); + this.Free(); + return result; + } + + public void Dispose() => Free(); + + #region Poolable + + // To implement Poolable, you need two things: + // 1) Expose Freeing primitive. + private void Free() + { + var pool = _pool; + if (pool != null) + { + // According to the statistics of a C# compiler self-build, the most commonly used builder size is 0. (808003 uses). + // The distant second is the Count == 1 (455619), then 2 (106362) ... + // After about 50 (just 67) we have a long tail of infrequently used builder sizes. + // However we have builders with size up to 50K (just one such thing) + // + // We do not want to retain (potentially indefinitely) very large builders + // while the chance that we will need their size is diminishingly small. + // It makes sense to constrain the size to some "not too small" number. + // Overall perf does not seem to be very sensitive to this number, so I picked 128 as a limit. + if (_builder.Capacity < 128) + { + if (this.Count != 0) + { + this.Clear(); + } + + pool.Free(this, CancellationToken.None); + return; + } + else + { + ObjectPool>.ForgetTrackedObject(this); + } + } + } + + // 2) Expose the pool or the way to create a pool or the way to get an instance. + // for now we will expose both and figure which way works better + private static readonly ObjectPool> s_poolInstance = CreatePool(); + public static ArrayBuilder GetInstance() + { + var builder = s_poolInstance.Allocate(); + Debug.Assert(builder.Count == 0); + return builder; + } + + public static ArrayBuilder GetInstance(int capacity) + { + var builder = GetInstance(); + builder.EnsureCapacity(capacity); + return builder; + } + + public static ArrayBuilder GetInstance(int capacity, T fillWithValue) + { + var builder = GetInstance(); + builder.EnsureCapacity(capacity); + + for (int i = 0; i < capacity; i++) + { + builder.Add(fillWithValue); + } + + return builder; + } + + internal static ObjectPool> CreatePool() + { + return CreatePool(128); // we rarely need more than 10 + } + + internal static ObjectPool> CreatePool(int size) + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => new ArrayBuilder(pool), size); + return pool; + } + + #endregion + + internal Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal Dictionary> ToDictionary(Func keySelector, IEqualityComparer? comparer = null) + where K : notnull + { + if (this.Count == 1) + { + var dictionary1 = new Dictionary>(1, comparer); + T value = this[0]; + dictionary1.Add(keySelector(value), ImmutableArray.Create(value)); + return dictionary1; + } + + if (this.Count == 0) + { + return new Dictionary>(comparer); + } + + // bucketize + // prevent reallocation. it may not have 'count' entries, but it won't have more. + var accumulator = new Dictionary>(Count, comparer); + for (int i = 0; i < Count; i++) + { + var item = this[i]; + var key = keySelector(item); + if (!accumulator.TryGetValue(key, out var bucket)) + { + bucket = ArrayBuilder.GetInstance(); + accumulator.Add(key, bucket); + } + + bucket.Add(item); + } + + var dictionary = new Dictionary>(accumulator.Count, comparer); + + // freeze + foreach (var pair in accumulator) + { + dictionary.Add(pair.Key, pair.Value.ToImmutableAndFree()); + } + + return dictionary; + } + + public void AddRange(ArrayBuilder items) + { + _builder.AddRange(items._builder); + } + + public void AddRange(ArrayBuilder items) where U : T + { + _builder.AddRange(items._builder); + } + + public void AddRange(ImmutableArray items) + { + _builder.AddRange(items); + } + + public void AddRange(ImmutableArray items, int length) + { + _builder.AddRange(items, length); + } + + public void AddRange(ImmutableArray items) where S : class, T + { + AddRange(ImmutableArray.CastUp(items)); + } + + public void AddRange(T[] items, int start, int length) + { + for (int i = start, end = start + length; i < end; i++) + { + Add(items[i]); + } + } + + public void AddRange(IEnumerable items) + { + _builder.AddRange(items); + } + + public void AddRange(params T[] items) + { + _builder.AddRange(items); + } + + public void AddRange(T[] items, int length) + { + _builder.AddRange(items, length); + } + + public void Clip(int limit) + { + Debug.Assert(limit <= Count); + _builder.Count = limit; + } + + public void ZeroInit(int count) + { + _builder.Clear(); + _builder.Count = count; + } + + public void AddMany(T item, int count) + { + for (int i = 0; i < count; i++) + { + Add(item); + } + } + + public void RemoveDuplicates() + { + using var set = PooledHashSet.GetInstance(); + + int j = 0; + for (int i = 0; i < Count; i++) + { + if (set.Add(this[i])) + { + this[j] = this[i]; + j++; + } + } + + Clip(j); + } + + public ImmutableArray SelectDistinct(Func selector) + { + using var result = ArrayBuilder.GetInstance(Count); + using var set = PooledHashSet.GetInstance(); + + foreach (var item in _builder) + { + var selected = selector(item); + if (set.Add(selected)) + { + result.Add(selected); + } + } + + return result.ToImmutable(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ObjectPool.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ObjectPool.cs new file mode 100644 index 0000000000000..3384576a3222f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/ObjectPool.cs @@ -0,0 +1,288 @@ +// 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. + +// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will +// make everything about 2-3x slower +// +// #define TRACE_LEAKS + +// define DETECT_LEAKS to detect possible leaks +// #if DEBUG +// #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. +// #endif + +using System; +using System.Diagnostics; +using System.Threading; + +#if DETECT_LEAKS +using System.Runtime.CompilerServices; + +#endif +namespace Analyzer.Utilities.PooledObjects +{ + /// + /// Generic implementation of object pooling pattern with predefined pool size limit. The main + /// purpose is that limited number of frequently used objects can be kept in the pool for + /// further recycling. + /// + /// Notes: + /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there + /// is no space in the pool, extra returned objects will be dropped. + /// + /// 2) it is implied that if object was obtained from a pool, the caller will return it back in + /// a relatively short time. Keeping checked out objects for long durations is ok, but + /// reduces usefulness of pooling. Just new up your own. + /// + /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. + /// Rationale: + /// If there is no intent for reusing the object, do not use pool - just use "new". + /// + internal sealed class ObjectPool where T : class + { + [DebuggerDisplay("{Value,nq}")] +#pragma warning disable CA1815 // Override equals and operator equals on value types +#pragma warning disable CA1051 // Do not declare visible instance fields + private struct Element + { + internal T? Value; + } +#pragma warning restore CA1051 // Do not declare visible instance fields +#pragma warning restore CA1815 // Override equals and operator equals on value types + + /// + /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, + /// which does not have that type (since it compiles against .NET 2.0). + /// + internal delegate T Factory(); + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T? _firstItem; + private readonly Element[] _items; + + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Factory _factory; + +#if DETECT_LEAKS + private static readonly ConditionalWeakTable leakTrackers = new ConditionalWeakTable(); + + private class LeakTracker : IDisposable + { + private volatile bool disposed; + +#if TRACE_LEAKS + internal volatile object Trace = null; +#endif + + public void Dispose() + { + disposed = true; + GC.SuppressFinalize(this); + } + + private string GetTrace() + { +#if TRACE_LEAKS + return Trace == null ? "" : Trace.ToString(); +#else + return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n"; +#endif + } + + ~LeakTracker() + { + if (!this.disposed && !Environment.HasShutdownStarted) + { + var trace = GetTrace(); + + // If you are seeing this message it means that object has been allocated from the pool + // and has not been returned back. This is not critical, but turns pool into rather + // inefficient kind of "new". + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nPool detected potential leaking of {typeof(T)}. \n Location of the leak: \n {GetTrace()} TRACEOBJECTPOOLLEAKS_END"); + } + } + } +#endif + + internal ObjectPool(Factory factory) + : this(factory, Environment.ProcessorCount * 2) + { } + + internal ObjectPool(Factory factory, int size) + { + Debug.Assert(size >= 1); + _factory = factory; + _items = new Element[size - 1]; + } + + private T CreateInstance() + { + var inst = _factory(); + return inst; + } + + /// + /// Produces an instance. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// + internal T Allocate() + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T? inst = _firstItem; + if (inst == null || inst != Interlocked.CompareExchange(ref _firstItem, null, inst)) + { + inst = AllocateSlow(); + } + +#if DETECT_LEAKS + var tracker = new LeakTracker(); + leakTrackers.Add(inst, tracker); + +#if TRACE_LEAKS + var frame = CaptureStackTrace(); + tracker.Trace = frame; +#endif +#endif + return inst; + } + + private T AllocateSlow() + { + var items = _items; + + for (int i = 0; i < items.Length; i++) + { + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T? inst = items[i].Value; + if (inst != null && + inst == Interlocked.CompareExchange(ref items[i].Value, null, inst)) + { + return inst; + } + } + + return CreateInstance(); + } + + /// + /// Returns objects to the pool. + /// + /// + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// + internal void Free(T obj, CancellationToken cancellationToken) + { + // Do not free in presence of cancellation. + // See https://github.com/dotnet/roslyn/issues/46859 for details. + if (cancellationToken.IsCancellationRequested) + { + return; + } + + Validate(obj); + ForgetTrackedObject(obj); + + if (_firstItem == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + } + else + { + FreeSlow(obj); + } + } + + private void FreeSlow(T obj) + { + var items = _items; + for (int i = 0; i < items.Length; i++) + { + if (items[i].Value == null) + { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + items[i].Value = obj; + break; + } + } + } + + /// + /// Removes an object from leak tracking. + /// + /// This is called when an object is returned to the pool. It may also be explicitly + /// called if an object allocated from the pool is intentionally not being returned + /// to the pool. This can be of use with pooled arrays if the consumer wants to + /// return a larger array to the pool than was originally allocated. + /// + [Conditional("DEBUG")] + internal static void ForgetTrackedObject(T old, T? replacement = null) + { +#if DETECT_LEAKS + LeakTracker tracker; + if (leakTrackers.TryGetValue(old, out tracker)) + { + tracker.Dispose(); + leakTrackers.Remove(old); + } + else + { + var trace = CaptureStackTrace(); + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nObject of type {typeof(T)} was freed, but was not from pool. \n Callstack: \n {trace} TRACEOBJECTPOOLLEAKS_END"); + } + + if (replacement != null) + { + tracker = new LeakTracker(); + leakTrackers.Add(replacement, tracker); + } +#endif + } + +#if DETECT_LEAKS + private static Lazy _stackTraceType = new Lazy(() => Type.GetType("System.Diagnostics.StackTrace")); + + private static object CaptureStackTrace() + { + return Activator.CreateInstance(_stackTraceType.Value); + } +#endif + + [Conditional("DEBUG")] + private void Validate(object obj) + { + Debug.Assert(_firstItem != obj, "freeing twice?"); + + var items = _items; + for (int i = 0; i < items.Length; i++) + { + var value = items[i].Value; + if (value == null) + { + return; + } + + Debug.Assert(value != obj, "freeing twice?"); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentDictionary.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentDictionary.cs new file mode 100644 index 0000000000000..730cdd13ca505 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentDictionary.cs @@ -0,0 +1,87 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + /// + /// that can be recycled via an object pool. + /// + internal sealed class PooledConcurrentDictionary : ConcurrentDictionary, IDisposable + where K : notnull + { + private readonly ObjectPool>? _pool; + + private PooledConcurrentDictionary(ObjectPool>? pool) + { + _pool = pool; + } + + private PooledConcurrentDictionary(ObjectPool>? pool, IEqualityComparer keyComparer) + : base(keyComparer) + { + _pool = pool; + } + + public void Dispose() => Free(CancellationToken.None); + + public void Free(CancellationToken cancellationToken) + { + // Do not free in presence of cancellation. + // See https://github.com/dotnet/roslyn/issues/46859 for details. + if (cancellationToken.IsCancellationRequested) + { + return; + } + + this.Clear(); + _pool?.Free(this, cancellationToken); + } + + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); + private static readonly ConcurrentDictionary, ObjectPool>> s_poolInstancesByComparer = new(); + + // if someone needs to create a pool; + public static ObjectPool> CreatePool(IEqualityComparer? keyComparer = null) + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => + keyComparer != null ? + new PooledConcurrentDictionary(pool, keyComparer) : + new PooledConcurrentDictionary(pool), + 128); + return pool; + } + + public static PooledConcurrentDictionary GetInstance(IEqualityComparer? keyComparer = null) + { + var pool = keyComparer == null ? + s_poolInstance : + s_poolInstancesByComparer.GetOrAdd(keyComparer, CreatePool); + var instance = pool.Allocate(); + Debug.Assert(instance.IsEmpty); + return instance; + } + + public static PooledConcurrentDictionary GetInstance(IEnumerable> initializer, IEqualityComparer? keyComparer = null) + { + var instance = GetInstance(keyComparer); + foreach (var kvp in initializer) + { + var added = instance.TryAdd(kvp.Key, kvp.Value); + Debug.Assert(added); + } + + return instance; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentSet.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentSet.cs new file mode 100644 index 0000000000000..a355bce9bd288 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledConcurrentSet.cs @@ -0,0 +1,165 @@ +// 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; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + /// + /// that can be recycled via an object pool. + /// +#pragma warning disable CA1710 // Identifiers should have correct suffix + internal sealed class PooledConcurrentSet : ICollection, IDisposable + where T : notnull +#pragma warning restore CA1710 // Identifiers should have correct suffix + { + private readonly PooledConcurrentDictionary _dictionary; + + private PooledConcurrentSet(PooledConcurrentDictionary dictionary) + { + _dictionary = dictionary; + } + + public void Dispose() => Free(CancellationToken.None); + public void Free(CancellationToken cancellationToken) => _dictionary.Free(cancellationToken); + + public static PooledConcurrentSet GetInstance(IEqualityComparer? comparer = null) + { + var dictionary = PooledConcurrentDictionary.GetInstance(comparer); + return new PooledConcurrentSet(dictionary); + } + + public static PooledConcurrentSet GetInstance(IEnumerable initializer, IEqualityComparer? comparer = null) + { + var instance = GetInstance(comparer); + foreach (var item in initializer) + { + instance.Add(item); + } + + return instance; + } + + /// + /// Obtain the number of elements in the set. + /// + /// The number of elements in the set. + public int Count => _dictionary.Count; + + /// + /// Determine whether the set is empty. + /// true if the set is empty; otherwise, false. + public bool IsEmpty => _dictionary.IsEmpty; + + public bool IsReadOnly => false; + + /// + /// Attempts to add a to the set. + /// + /// The value to add. + /// true if the value was added to the set. If the value already exists, this method returns false. + public bool Add(T value) => _dictionary.TryAdd(value, 0); + + /// + /// Adds the given to the set. + /// + public void AddRange(IEnumerable? values) + { + if (values != null) + { + foreach (var v in values) + { + Add(v); + } + } + } + + /// + /// Attempts to remove a value from the set. + /// + /// The value to remove. + /// true if the value was removed successfully; otherwise false. + public bool Remove(T item) => _dictionary.TryRemove(item, out _); + + /// + /// Clears all the elements from the set. + /// + public void Clear() => _dictionary.Clear(); + + /// + /// Returns true if the given is present in the set. + /// + public bool Contains(T item) => _dictionary.ContainsKey(item); + + public void CopyTo(T[] array, int arrayIndex) => throw new NotImplementedException(); + + /// + /// Obtain an enumerator that iterates through the elements in the set. + /// + /// An enumerator for the set. + public KeyEnumerator GetEnumerator() + { + // PERF: Do not use dictionary.Keys here because that creates a snapshot + // of the collection resulting in a List allocation. Instead, use the + // KeyValuePair enumerator and pick off the Key part. + return new KeyEnumerator(_dictionary); + } + + private IEnumerator GetEnumeratorCore() + { + // PERF: Do not use dictionary.Keys here because that creates a snapshot + // of the collection resulting in a List allocation. Instead, use the + // KeyValuePair enumerator and pick off the Key part. + foreach (var kvp in _dictionary) + { + yield return kvp.Key; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumeratorCore(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumeratorCore(); + } + + void ICollection.Add(T item) + { + Add(item); + } + +#pragma warning disable CA1815 // Override equals and operator equals on value types + public readonly struct KeyEnumerator +#pragma warning restore CA1815 // Override equals and operator equals on value types + { + private readonly IEnumerator> _kvpEnumerator; + + internal KeyEnumerator(IEnumerable> data) + { + _kvpEnumerator = data.GetEnumerator(); + } + + public T Current => _kvpEnumerator.Current.Key; + + public bool MoveNext() + { + return _kvpEnumerator.MoveNext(); + } + + public void Reset() + { + _kvpEnumerator.Reset(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledDictionary.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledDictionary.cs new file mode 100644 index 0000000000000..8bb21286eb95f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledDictionary.cs @@ -0,0 +1,114 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + // Dictionary that can be recycled via an object pool + // NOTE: these dictionaries always have the default comparer. + internal sealed class PooledDictionary : Dictionary, IDisposable + where K : notnull + { + private readonly ObjectPool>? _pool; + + private PooledDictionary(ObjectPool>? pool, IEqualityComparer? keyComparer) + : base(keyComparer) + { + _pool = pool; + } + + public void Dispose() => Free(CancellationToken.None); + + public ImmutableDictionary ToImmutableDictionaryAndFree() + { + ImmutableDictionary result; + if (Count == 0) + { + result = ImmutableDictionary.Empty; + } + else + { + result = this.ToImmutableDictionary(Comparer); + this.Clear(); + } + + _pool?.Free(this, CancellationToken.None); + return result; + } + + public ImmutableDictionary ToImmutableDictionaryAndFree( + Func, TKey> keySelector, Func, TValue> elementSelector, IEqualityComparer comparer) + where TKey : notnull + { + ImmutableDictionary result; + if (Count == 0) + { + result = ImmutableDictionary.Empty; + } + else + { + result = this.ToImmutableDictionary(keySelector, elementSelector, comparer); + this.Clear(); + } + + _pool?.Free(this, CancellationToken.None); + return result; + } + + public void Free(CancellationToken cancellationToken) + { + // Do not free in presence of cancellation. + // See https://github.com/dotnet/roslyn/issues/46859 for details. + if (cancellationToken.IsCancellationRequested) + { + return; + } + + this.Clear(); + _pool?.Free(this, cancellationToken); + } + + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); + private static readonly ConcurrentDictionary, ObjectPool>> s_poolInstancesByComparer + = new(); + + // if someone needs to create a pool; + public static ObjectPool> CreatePool(IEqualityComparer? keyComparer = null) + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => new PooledDictionary(pool, keyComparer), 128); + return pool; + } + + public static PooledDictionary GetInstance(IEqualityComparer? keyComparer = null) + { + var pool = keyComparer == null ? + s_poolInstance : + s_poolInstancesByComparer.GetOrAdd(keyComparer, CreatePool); + var instance = pool.Allocate(); + Debug.Assert(instance.Count == 0); + return instance; + } + + public static PooledDictionary GetInstance(IEnumerable> initializer, IEqualityComparer? keyComparer = null) + { + var instance = GetInstance(keyComparer); + foreach (var kvp in initializer) + { + instance.Add(kvp.Key, kvp.Value); + } + + return instance; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledHashSet.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledHashSet.cs new file mode 100644 index 0000000000000..8ecd885f04708 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledHashSet.cs @@ -0,0 +1,95 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + // HashSet that can be recycled via an object pool + internal sealed class PooledHashSet : HashSet, IDisposable + { + private readonly ObjectPool>? _pool; + + private PooledHashSet(ObjectPool>? pool, IEqualityComparer? comparer) + : base(comparer) + { + _pool = pool; + } + + public void Dispose() => Free(CancellationToken.None); + + public void Free(CancellationToken cancellationToken) + { + // Do not free in presence of cancellation. + // See https://github.com/dotnet/roslyn/issues/46859 for details. + if (cancellationToken.IsCancellationRequested) + { + return; + } + + this.Clear(); + _pool?.Free(this, cancellationToken); + } + + public ImmutableHashSet ToImmutableAndFree() + { + ImmutableHashSet result; + if (Count == 0) + { + result = ImmutableHashSet.Empty; + } + else + { + result = this.ToImmutableHashSet(Comparer); + this.Clear(); + } + + _pool?.Free(this, CancellationToken.None); + return result; + } + + public ImmutableHashSet ToImmutable() + => Count == 0 ? ImmutableHashSet.Empty : this.ToImmutableHashSet(Comparer); + + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); + private static readonly ConcurrentDictionary, ObjectPool>> s_poolInstancesByComparer = new(); + + // if someone needs to create a pool; + public static ObjectPool> CreatePool(IEqualityComparer? comparer = null) + { + ObjectPool>? pool = null; + pool = new ObjectPool>(() => new PooledHashSet(pool, comparer), 128); + return pool; + } + + public static PooledHashSet GetInstance(IEqualityComparer? comparer = null) + { + var pool = comparer == null ? + s_poolInstance : + s_poolInstancesByComparer.GetOrAdd(comparer, CreatePool); + var instance = pool.Allocate(); + Debug.Assert(instance.Count == 0); + return instance; + } + + public static PooledHashSet GetInstance(IEnumerable initializer, IEqualityComparer? comparer = null) + { + var instance = GetInstance(comparer); + foreach (var value in initializer) + { + instance.Add(value); + } + + return instance; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledSortedSet.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledSortedSet.cs new file mode 100644 index 0000000000000..7c31cbfaeca80 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/PooledSortedSet.cs @@ -0,0 +1,89 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Analyzer.Utilities.PooledObjects +{ + /// + /// Pooled . + /// + /// Type of elements in the set. + internal sealed class PooledSortedSet : SortedSet, IDisposable + { + private readonly ObjectPool>? _pool; + + public PooledSortedSet(ObjectPool>? pool, IComparer? comparer = null) + : base(comparer) + { + _pool = pool; + } + + public void Dispose() => Free(CancellationToken.None); + + public void Free(CancellationToken cancellationToken) + { + // Do not free in presence of cancellation. + // See https://github.com/dotnet/roslyn/issues/46859 for details. + if (cancellationToken.IsCancellationRequested) + { + return; + } + + this.Clear(); + _pool?.Free(this, cancellationToken); + } + + // global pool + private static readonly ObjectPool> s_poolInstance = CreatePool(); + private static readonly ConcurrentDictionary, ObjectPool>> s_poolInstancesByComparer = new(); + + private static ObjectPool> CreatePool(IComparer? comparer = null) + { + ObjectPool>? pool = null; + pool = new ObjectPool>( + () => new PooledSortedSet(pool, comparer), + 128); + return pool; + } + + /// + /// Gets a pooled instance of a with an optional comparer. + /// + /// Singleton (or at least a bounded number) comparer to use, or null for the element type's default comparer. + /// An empty . + public static PooledSortedSet GetInstance(IComparer? comparer = null) + { + var pool = comparer == null ? + s_poolInstance : + s_poolInstancesByComparer.GetOrAdd(comparer, CreatePool); + var instance = pool.Allocate(); + Debug.Assert(instance.Count == 0); + return instance; + } + + /// + /// Gets a pooled instance of a with the given initializer and an optional comparer. + /// + /// Initializer for the set. + /// Comparer to use, or null for the element type's default comparer. + /// An empty . + public static PooledSortedSet GetInstance(IEnumerable initializer, IComparer? comparer = null) + { + var instance = GetInstance(comparer); + foreach (var value in initializer) + { + instance.Add(value); + } + + return instance; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporaryDictionary`2.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporaryDictionary`2.cs new file mode 100644 index 0000000000000..88f834597dbf1 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporaryDictionary`2.cs @@ -0,0 +1,91 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace Analyzer.Utilities.PooledObjects +{ + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not used in this context")] + [SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix", Justification = "The 'Dictionary' suffix is intentional")] + internal struct TemporaryDictionary + where TKey : notnull + { +#pragma warning disable CS0649 // Field 'TemporaryDictionary.Empty' is never assigned to, and will always have its default value + public static readonly TemporaryDictionary Empty; +#pragma warning restore CS0649 // Field 'TemporaryDictionary.Empty' is never assigned to, and will always have its default value + + /// + /// An empty dictionary used for creating non-null enumerators when no items have been added to the dictionary. + /// + private static readonly Dictionary EmptyDictionary = new(); + + // 🐇 PERF: use PooledDictionary instead of PooledConcurrentDictionary due to + // allocation overhead in clearing the set for returning it to the pool. + private PooledDictionary? _storage; + + public readonly Enumerable NonConcurrentEnumerable + => new(_storage ?? EmptyDictionary); + + public void Free(CancellationToken cancellationToken) + { + Interlocked.Exchange(ref _storage, null)?.Free(cancellationToken); + } + + private PooledDictionary GetOrCreateStorage(CancellationToken cancellationToken) + { + if (_storage is not { } storage) + { + var newStorage = PooledDictionary.GetInstance(); + storage = Interlocked.CompareExchange(ref _storage, newStorage, null) ?? newStorage; + if (storage != newStorage) + { + // Another thread initialized the value. Make sure to release the unused object. + newStorage.Free(cancellationToken); + } + } + + return storage; + } + + internal void Add(TKey key, TValue value, CancellationToken cancellationToken) + { + var storage = GetOrCreateStorage(cancellationToken); + lock (storage) + { + storage.Add(key, value); + } + } + + public readonly struct Enumerable + { + private readonly Dictionary _dictionary; + + public Enumerable(Dictionary dictionary) + { + _dictionary = dictionary; + } + + public Enumerator GetEnumerator() + => new(_dictionary.GetEnumerator()); + } + + public struct Enumerator + { + private Dictionary.Enumerator _enumerator; + + public Enumerator(Dictionary.Enumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + => _enumerator.MoveNext(); + + public KeyValuePair Current + => _enumerator.Current; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporarySet`1.cs b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporarySet`1.cs new file mode 100644 index 0000000000000..56025c914ba96 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/PooledObjects/TemporarySet`1.cs @@ -0,0 +1,127 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; + +namespace Analyzer.Utilities.PooledObjects +{ + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Not used in this context")] + internal struct TemporarySet + { +#pragma warning disable CS0649 // Field 'TemporarySet.Empty' is never assigned to, and will always have its default value + public static readonly TemporarySet Empty; +#pragma warning restore CS0649 // Field 'TemporarySet.Empty' is never assigned to, and will always have its default value + + /// + /// An empty set used for creating non-null enumerators when no items have been added to the set. + /// + private static readonly HashSet EmptyHashSet = new(); + + // 🐇 PERF: use PooledHashSet instead of PooledConcurrentSet due to allocation overhead in + // clearing the set for returning it to the pool. + private PooledHashSet? _storage; + + public readonly Enumerable NonConcurrentEnumerable + => new(_storage ?? EmptyHashSet); + + public void Free(CancellationToken cancellationToken) + { + Interlocked.Exchange(ref _storage, null)?.Free(cancellationToken); + } + + private PooledHashSet GetOrCreateStorage(CancellationToken cancellationToken) + { + if (_storage is not { } storage) + { + var newStorage = PooledHashSet.GetInstance(); + storage = Interlocked.CompareExchange(ref _storage, newStorage, null) ?? newStorage; + if (storage != newStorage) + { + // Another thread initialized the value. Make sure to release the unused object. + newStorage.Free(cancellationToken); + } + } + + return storage; + } + + public bool Add(T item, CancellationToken cancellationToken) + { + var storage = GetOrCreateStorage(cancellationToken); + lock (storage) + { + return storage.Add(item); + } + } + + public readonly bool Contains(T item) + { + if (_storage is not { } storage) + return false; + + lock (storage) + { + return storage.Contains(item); + } + } + + public readonly bool Contains_NonConcurrent(T item) + { + if (_storage is not { } storage) + return false; + + return storage.Contains(item); + } + + public readonly bool Any_NonConcurrent() + { + if (_storage is not { } storage) + return false; + + return storage.Count > 0; + } + + public readonly Enumerator GetEnumerator_NonConcurrent() + { + return new Enumerator((_storage ?? EmptyHashSet).GetEnumerator()); + } + + public readonly IEnumerable AsEnumerable_NonConcurrent() + { + return (_storage ?? EmptyHashSet).AsEnumerable(); + } + + public readonly struct Enumerable + { + private readonly HashSet _set; + + public Enumerable(HashSet set) + { + _set = set; + } + + public Enumerator GetEnumerator() + => new(_set.GetEnumerator()); + } + + public struct Enumerator + { + private HashSet.Enumerator _enumerator; + + public Enumerator(HashSet.Enumerator enumerator) + { + _enumerator = enumerator; + } + + public bool MoveNext() + => _enumerator.MoveNext(); + + public T Current + => _enumerator.Current; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/Range.cs b/src/RoslynAnalyzers/Utilities/Compiler/Range.cs new file mode 100644 index 0000000000000..4a8abe115fa21 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/Range.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. + +#if !NETCOREAPP + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Analyzer.Utilities; + +namespace System +{ + /// Represent a range has start and end indexes. + /// + /// Range is used by the C# compiler to support the range syntax. + /// + /// int[] someArray = new int[5] { 1, 2, 3, 4, 5 }; + /// int[] subArray1 = someArray[0..2]; // { 1, 2 } + /// int[] subArray2 = someArray[1..^0]; // { 2, 3, 4, 5 } + /// + /// + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "Matches implementation from dotnet/runtime")] + [SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "Matches implementation from dotnet/runtime")] + internal readonly struct Range : IEquatable + { + /// Represent the inclusive start index of the Range. + public Index Start { get; } + + /// Represent the exclusive end index of the Range. + public Index End { get; } + + /// Construct a Range object using the start and end indexes. + /// Represent the inclusive start index of the range. + /// Represent the exclusive end index of the range. + public Range(Index start, Index end) + { + Start = start; + End = end; + } + + /// Indicates whether the current Range object is equal to another object of the same type. + /// An object to compare with this object + public override bool Equals(object? obj) => + obj is Range r && + r.Start.Equals(Start) && + r.End.Equals(End); + + /// Indicates whether the current Range object is equal to another Range object. + /// An object to compare with this object + public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End); + + /// Returns the hash code for this instance. + public override int GetHashCode() + { + return RoslynHashCode.Combine(Start.GetHashCode(), End.GetHashCode()); + } + + /// Converts the value of the current Range object to its equivalent string representation. + public override string ToString() + { + return Start.ToString() + ".." + End.ToString(); + } + + /// Create a Range object starting from start index to the end of the collection. + public static Range StartAt(Index start) => new(start, Index.End); + + /// Create a Range object starting from first element in the collection to the end Index. + public static Range EndAt(Index end) => new(Index.Start, end); + + /// Create a Range object starting from first element to the end. + public static Range All => new(Index.Start, Index.End); + + /// Calculate the start offset and length of range object using a collection length. + /// The length of the collection that the range will be used with. length has to be a positive value. + /// + /// For performance reason, we don't validate the input length parameter against negative values. + /// It is expected Range will be used with collections which always have non negative length/count. + /// We validate the range is inside the length scope though. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public (int Offset, int Length) GetOffsetAndLength(int length) + { + int start; + Index startIndex = Start; + if (startIndex.IsFromEnd) + start = length - startIndex.Value; + else + start = startIndex.Value; + + int end; + Index endIndex = End; + if (endIndex.IsFromEnd) + end = length - endIndex.Value; + else + end = endIndex.Value; + + if ((uint)end > (uint)length || (uint)start > (uint)end) + { + throw new ArgumentOutOfRangeException(nameof(length)); + } + + return (start, end - start); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/RoslynHashCode.cs b/src/RoslynAnalyzers/Utilities/Compiler/RoslynHashCode.cs new file mode 100644 index 0000000000000..22e6965ba24a5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/RoslynHashCode.cs @@ -0,0 +1,460 @@ +// 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. + +// NOTE: This code is derived from an implementation originally in dotnet/runtime: +// https://github.com/dotnet/runtime/blob/v5.0.3/src/libraries/System.Private.CoreLib/src/System/HashCode.cs +// +// See the commentary in https://github.com/dotnet/roslyn/pull/50156 for notes on incorporating changes made to the +// reference implementation. + +/* + +The xxHash32 implementation is based on the code published by Yann Collet: +https://raw.githubusercontent.com/Cyan4973/xxHash/5c174cfa4e45a42f94082dc0d4539b39696afea1/xxhash.c + + xxHash - Fast Hash algorithm + Copyright (C) 2012-2016, Yann Collet + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash homepage: http://www.xxhash.com + - xxHash source repository : https://github.com/Cyan4973/xxHash + +*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +namespace Analyzer.Utilities +{ + // xxHash32 is used for the hash code. + // https://github.com/Cyan4973/xxHash + + [SuppressMessage("Design", "CA1066:Implement IEquatable when overriding Object.Equals", Justification = "This type is not equatable.")] + [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types", Justification = "This type is not equatable.")] + [SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals", Justification = "This type is not equatable.")] + public struct RoslynHashCode + { + private static readonly uint s_seed = GenerateGlobalSeed(); + + private const uint Prime1 = 2654435761U; + private const uint Prime2 = 2246822519U; + private const uint Prime3 = 3266489917U; + private const uint Prime4 = 668265263U; + private const uint Prime5 = 374761393U; + + private uint _v1, _v2, _v3, _v4; + private uint _queue1, _queue2, _queue3; + private uint _length; + + private static uint GenerateGlobalSeed() + { + using var randomNumberGenerator = RandomNumberGenerator.Create(); + var array = new byte[sizeof(uint)]; + randomNumberGenerator.GetBytes(array); + return BitConverter.ToUInt32(array, 0); + } + + public static int Combine(T1 value1) + { + // Provide a way of diffusing bits from something with a limited + // input hash space. For example, many enums only have a few + // possible hashes, only using the bottom few bits of the code. Some + // collections are built on the assumption that hashes are spread + // over a larger space, so diffusing the bits may help the + // collection work more efficiently. + + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 4; + + hash = QueueRound(hash, hc1); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 8; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + + var hash = MixEmptyState(); + hash += 12; + + hash = QueueRound(hash, hc1); + hash = QueueRound(hash, hc2); + hash = QueueRound(hash, hc3); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 16; + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 20; + + hash = QueueRound(hash, hc5); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 24; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + var hash = MixState(v1, v2, v3, v4); + hash += 28; + + hash = QueueRound(hash, hc5); + hash = QueueRound(hash, hc6); + hash = QueueRound(hash, hc7); + + hash = MixFinal(hash); + return (int)hash; + } + + public static int Combine(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + var hc1 = (uint)(value1?.GetHashCode() ?? 0); + var hc2 = (uint)(value2?.GetHashCode() ?? 0); + var hc3 = (uint)(value3?.GetHashCode() ?? 0); + var hc4 = (uint)(value4?.GetHashCode() ?? 0); + var hc5 = (uint)(value5?.GetHashCode() ?? 0); + var hc6 = (uint)(value6?.GetHashCode() ?? 0); + var hc7 = (uint)(value7?.GetHashCode() ?? 0); + var hc8 = (uint)(value8?.GetHashCode() ?? 0); + + Initialize(out var v1, out var v2, out var v3, out var v4); + + v1 = Round(v1, hc1); + v2 = Round(v2, hc2); + v3 = Round(v3, hc3); + v4 = Round(v4, hc4); + + v1 = Round(v1, hc5); + v2 = Round(v2, hc6); + v3 = Round(v3, hc7); + v4 = Round(v4, hc8); + + var hash = MixState(v1, v2, v3, v4); + hash += 32; + + hash = MixFinal(hash); + return (int)hash; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4) + { + v1 = s_seed + Prime1 + Prime2; + v2 = s_seed + Prime2; + v3 = s_seed; + v4 = s_seed - Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint Round(uint hash, uint input) + { + return BitOperations.RotateLeft(hash + (input * Prime2), 13) * Prime1; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint QueueRound(uint hash, uint queuedValue) + { + return BitOperations.RotateLeft(hash + (queuedValue * Prime3), 17) * Prime4; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixState(uint v1, uint v2, uint v3, uint v4) + { + return BitOperations.RotateLeft(v1, 1) + BitOperations.RotateLeft(v2, 7) + BitOperations.RotateLeft(v3, 12) + BitOperations.RotateLeft(v4, 18); + } + + private static uint MixEmptyState() + { + return s_seed + Prime5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint MixFinal(uint hash) + { + hash ^= hash >> 15; + hash *= Prime2; + hash ^= hash >> 13; + hash *= Prime3; + hash ^= hash >> 16; + return hash; + } + + public void Add(T value) + { + Add(value?.GetHashCode() ?? 0); + } + + public void Add(T value, IEqualityComparer? comparer) + { + Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode())); + } + + private void Add(int value) + { + // The original xxHash works as follows: + // 0. Initialize immediately. We can't do this in a struct (no + // default ctor). + // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators. + // 2. Accumulate remaining blocks of length 4 (1 uint) into the + // hash. + // 3. Accumulate remaining blocks of length 1 into the hash. + + // There is no need for #3 as this type only accepts ints. _queue1, + // _queue2 and _queue3 are basically a buffer so that when + // ToHashCode is called we can execute #2 correctly. + + // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see + // #0) nd the last place that can be done if you look at the + // original code is just before the first block of 16 bytes is mixed + // in. The xxHash32 state is never used for streams containing fewer + // than 16 bytes. + + // To see what's really going on here, have a look at the Combine + // methods. + + var val = (uint)value; + + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + var previousLength = _length++; + var position = previousLength % 4; + + // Switch can't be inlined. + + if (position == 0) + { + _queue1 = val; + } + else if (position == 1) + { + _queue2 = val; + } + else if (position == 2) + { + _queue3 = val; + } + else // position == 3 + { + if (previousLength == 3) + Initialize(out _v1, out _v2, out _v3, out _v4); + + _v1 = Round(_v1, _queue1); + _v2 = Round(_v2, _queue2); + _v3 = Round(_v3, _queue3); + _v4 = Round(_v4, val); + } + } + + public readonly int ToHashCode() + { + // Storing the value of _length locally shaves of quite a few bytes + // in the resulting machine code. + var length = _length; + + // position refers to the *next* queue position in this method, so + // position == 1 means that _queue1 is populated; _queue2 would have + // been populated on the next call to Add. + var position = length % 4; + + // If the length is less than 4, _v1 to _v4 don't contain anything + // yet. xxHash32 treats this differently. + + var hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4); + + // _length is incremented once per Add(Int32) and is therefore 4 + // times too small (xxHash length is in bytes, not ints). + + hash += length * 4; + + // Mix what remains in the queue + + // Switch can't be inlined right now, so use as few branches as + // possible by manually excluding impossible scenarios (position > 1 + // is always false if position is not > 0). + if (position > 0) + { + hash = QueueRound(hash, _queue1); + if (position > 1) + { + hash = QueueRound(hash, _queue2); + if (position > 2) + hash = QueueRound(hash, _queue3); + } + } + + hash = MixFinal(hash); + return (int)hash; + } + +#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member +#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations + // Obsolete member 'memberA' overrides non-obsolete member 'memberB'. + // Disallowing GetHashCode and Equals is by design + + // * We decided to not override GetHashCode() to produce the hash code + // as this would be weird, both naming-wise as well as from a + // behavioral standpoint (GetHashCode() should return the object's + // hash code, not the one being computed). + + // * Even though ToHashCode() can be called safely multiple times on + // this implementation, it is not part of the contract. If the + // implementation has to change in the future we don't want to worry + // about people who might have incorrectly used this type. + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly int GetHashCode() => throw new NotSupportedException(SR.HashCode_HashCodeNotSupported); + + [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override readonly bool Equals(object? obj) => throw new NotSupportedException(SR.HashCode_EqualityNotSupported); +#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations +#pragma warning restore CS0809 // Obsolete member overrides non-obsolete member + + private static class SR + { + public static string HashCode_HashCodeNotSupported = "HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code."; + public static string HashCode_EqualityNotSupported = "HashCode is a mutable struct and should not be compared with other HashCodes."; + } + + private static class BitOperations + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RotateLeft(uint value, int offset) + => (value << offset) | (value >> (32 - offset)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong RotateLeft(ulong value, int offset) + => (value << offset) | (value >> (64 - offset)); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/RoslynString.cs b/src/RoslynAnalyzers/Utilities/Compiler/RoslynString.cs new file mode 100644 index 0000000000000..6d900a1d7b205 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/RoslynString.cs @@ -0,0 +1,21 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static class RoslynString + { + /// + public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] string? value) + => string.IsNullOrEmpty(value); + +#if !NET20 + /// + public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] string? value) + => string.IsNullOrWhiteSpace(value); +#endif + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/RuleLevel.cs b/src/RoslynAnalyzers/Utilities/Compiler/RuleLevel.cs new file mode 100644 index 0000000000000..9018ea9eee54e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/RuleLevel.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NET_ANALYZERS || TEXT_ANALYZERS || MICROSOFT_CODEANALYSIS_ANALYZERS + +namespace Microsoft.CodeAnalysis +{ +#pragma warning disable CA1008 // Enums should have zero value +#pragma warning disable CA1027 // Mark enums with FlagsAttribute + internal enum RuleLevel +#pragma warning restore CA1027 // Mark enums with FlagsAttribute +#pragma warning restore CA1008 // Enums should have zero value + { + /// + /// Correctness rule which prevents the compiler from producing a well-defined output binary, and must have + /// no false positives. Violations of this rule must be fixed by users before any other testing work can + /// continue. This rule will be enabled in CI and IDE live analysis by default with severity + /// . + /// + /// + /// Since analyzers cannot directly influence output binaries, this value is typically only valid in the + /// implementation of source generators. Rare exceptions may occur at the request of a director in coordination + /// with the core compiler team. + /// + BuildError = 1, + + /// + /// Correctness rule which should have no false positives, and is extremely likely to be fixed by users. + /// This rule will be enabled in CI and IDE live analysis by default with severity . + /// + BuildWarning = 2, + + /// + /// Correctness rule which should have no false positives, and is extremely likely to be fixed by users. + /// This rule is a candidate to be turned into a . + /// Until then, this rule will be an + /// + BuildWarningCandidate = IdeSuggestion, + + /// + /// Rule which should have no false positives, and is a valuable IDE live analysis suggestion for opportunistic improvement, but not something to be enforced in CI. + /// This rule will be enabled by default as an IDE-only suggestion with severity which will be shown in "Messages" tab in Error list. + /// +#pragma warning disable CA1069 // Enums values should not be duplicated - BuildWarningCandidate is used to mark a separate bucket of rules that could be promoted. + IdeSuggestion = 3, +#pragma warning restore CA1069 // Enums values should not be duplicated + + /// + /// Rule which may have some false positives and hence would be noisy to be enabled by default as a suggestion or a warning in IDE live analysis or CI. + /// This rule will be enabled by default with severity, so it will be effectively disabled in both IDE live analysis and CI. + /// However, hidden severity ensures that this rule can be enabled using the category based bulk configuration (TODO: add documentation link on category based bulk configuration). + /// + IdeHidden_BulkConfigurable = 4, + + /// + /// Disabled by default rule. + /// Users would need to explicitly enable this rule with an ID-based severity configuration entry for the rule ID. + /// This rule cannot be enabled using the category based bulk configuration (TODO: add documentation link on category based bulk configuration). + /// + Disabled = 5, + + /// + /// Disabled by default rule, which is a candidate for deprecation. + /// + CandidateForRemoval = 6, + + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/RulesetToEditorconfigConverter.cs b/src/RoslynAnalyzers/Utilities/Compiler/RulesetToEditorconfigConverter.cs new file mode 100644 index 0000000000000..4c41b9ae9986a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/RulesetToEditorconfigConverter.cs @@ -0,0 +1,222 @@ +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml; +using System.Xml.Linq; + +namespace Microsoft.CodeAnalysis.RulesetToEditorconfig +{ + public static class Converter + { + private const string RuleSetNodeName = "RuleSet"; + private const string RuleSetNameAttributeName = "Name"; + private const string RuleSetDescriptionAttributeName = "Description"; + private const string RulesNodeName = "Rules"; + private const string RuleNodeName = "Rule"; + private const string RuleIdAttributeName = "Id"; + + /// + /// Converts a ruleset file at the given , including all its ruleset includes, + /// into an .editorconfig file at the given + /// + /// Exception while performing any I/O on given file paths. + /// Exception for invalid ruleset files. + public static void GenerateEditorconfig(string rulesetFilePath, string editorconfigFilePath) + { + if (Directory.Exists(editorconfigFilePath)) + { + editorconfigFilePath = Path.Combine(editorconfigFilePath, ".editorconfig"); + } + + File.WriteAllText(editorconfigFilePath, GetEditorconfig(rulesetFilePath)); + } + + /// + /// Gets an .editorconfig representation for a ruleset file at the given , + /// including all its ruleset includes. + /// + /// Text for equivalent .editorconfig + /// Exception while performing any I/O on given file paths. + /// Exception for invalid ruleset file. + public static string GetEditorconfig(string rulesetFilePath) + { + // Find the top level rule set node + var rulesetNode = GetTopLevelRulesetNode(rulesetFilePath); + var name = rulesetNode.Attribute(RuleSetNameAttributeName)?.Value ?? Path.GetFileName(rulesetFilePath); + var description = rulesetNode.Attribute(RuleSetDescriptionAttributeName)?.Value ?? Path.GetFileName(rulesetFilePath); + + var builder = new StringBuilder(); + builder.AppendLine(@"# NOTE: Requires **VS2019 16.3** or later"); + builder.AppendLine(); + builder.AppendLine($@"# {name}"); + builder.AppendLine($@"# Description: {description}"); + builder.AppendLine(); + builder.AppendLine(@"# Code files"); + builder.AppendLine(@"[*.{cs,vb}]"); + builder.AppendLine(); + + var ruleset = RuleSet.LoadEffectiveRuleSetFromFile(rulesetFilePath); + var uniqueRulesetPaths = new HashSet(); + var ruleIdToComments = new Dictionary(StringComparer.OrdinalIgnoreCase); + ProcessComments(ruleset, uniqueRulesetPaths, ruleIdToComments); + + if (ruleset.GeneralDiagnosticOption != ReportDiagnostic.Default) + { + builder.AppendLine(); + builder.AppendLine(@"# Default severity for analyzer diagnostics - Requires **VS2019 16.5** or later"); + builder.AppendLine($@"dotnet_analyzer_diagnostic.severity = {ruleset.GeneralDiagnosticOption.ToAnalyzerConfigString()}"); + } + + foreach (var kvp in ruleset.SpecificDiagnosticOptions.OrderBy(kvp => kvp.Key)) + { + var id = kvp.Key; + var severity = kvp.Value; + + builder.AppendLine(); + + if (ruleIdToComments.TryGetValue(id, out var comment)) + { + AppendComment(builder, comment); + } + + builder.AppendLine($@"dotnet_diagnostic.{id}.severity = {severity.ToAnalyzerConfigString()}"); + } + + return builder.ToString(); + + static XElement GetTopLevelRulesetNode(string rulesetFilePath) + { + using Stream stream = File.OpenRead(rulesetFilePath); + using XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings()); + var ruleSetDocument = XDocument.Load(xmlReader); + + // Find the top level rule set node + var rulesetNode = ruleSetDocument.Elements(RuleSetNodeName).First(); + Debug.Assert(rulesetNode.Name == RuleSetNodeName); + return rulesetNode; + } + + static void ProcessComments(RuleSet ruleset, HashSet processedRulesetPaths, Dictionary ruleIdToComments) + { + processedRulesetPaths.Add(ruleset.FilePath); + + foreach (string rulesetIncludePath in RuleSet.GetEffectiveIncludesFromFile(ruleset.FilePath)) + { + if (!processedRulesetPaths.Contains(rulesetIncludePath)) + { + RuleSet includedRuleset = RuleSet.LoadEffectiveRuleSetFromFile(rulesetIncludePath); + ProcessComments(includedRuleset, processedRulesetPaths, ruleIdToComments); + } + } + + PopulateRuleIdToComments(ruleset.FilePath, ruleIdToComments); + } + + static void PopulateRuleIdToComments(string rulesetFilePath, Dictionary ruleIdToComments) + { + // Find the top level rule set node + var rulesetNode = GetTopLevelRulesetNode(rulesetFilePath); + if (rulesetNode == null) + { + return; + } + + Debug.Assert(rulesetNode.Name == RuleSetNodeName); + var currentXmlComment = new StringBuilder(); + string? currentRuleId = null; + foreach (var childNode in rulesetNode.Nodes().OfType()) + { + if (childNode.Name != RulesNodeName) + { + currentXmlComment.Clear(); + currentRuleId = null; + continue; + } + + foreach (var node in childNode.Nodes()) + { + if (node is XElement ruleNode && + ruleNode.Name == RuleNodeName) + { + XAttribute? ruleId = ruleNode.Attribute(RuleIdAttributeName); + if (ruleId != null) + { + foreach (var comment in ruleNode.Nodes().OfType()) + { + AppendComment(comment); + } + + currentRuleId = ruleId.Value; + } + } + else if (node is XComment xComment) + { + AppendComment(xComment); + } + else if (node is XText xtext) + { +#pragma warning disable CA1847 // Use 'string.Contains(char)' instead of 'string.Contains(string)' when searching for a single character - Retained for clarity + if (xtext.Value.Contains("\r", StringComparison.Ordinal) || xtext.Value.Contains("\n", StringComparison.Ordinal)) + { + // Indicates start of new Rule/XmlComment. + UpdateCurrentRuleIdPostCommentAndResetState(); + } +#pragma warning restore CA1847 // Use 'string.Contains(char)' instead of 'string.Contains(string)' when searching for a single character + } + else + { + currentXmlComment.Clear(); + } + } + } + + UpdateCurrentRuleIdPostCommentAndResetState(); + return; + + void AppendComment(XComment comment) + { + if (currentXmlComment.Length > 0) + { + currentXmlComment.AppendLine(); + } + + currentXmlComment.Append(comment.Value); + } + + // Saves the current comment as a post comment for current rule ID + // and reset the current rule ID and current comment. + void UpdateCurrentRuleIdPostCommentAndResetState() + { + if (currentRuleId != null) + { + ruleIdToComments[currentRuleId] = currentXmlComment.ToString(); + currentXmlComment.Clear(); + currentRuleId = null; + } + } + } + + static void AppendComment(StringBuilder builder, string comment) + { + if (comment.Length > 0) + { + foreach (var commentPart in comment.Split('\r', '\n')) + { + var trimmedCommentPart = commentPart.Trim(); + if (trimmedCommentPart.Length > 0) + { + builder.AppendLine($@"# {trimmedCommentPart}"); + } + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SmallDictionary.cs b/src/RoslynAnalyzers/Utilities/Compiler/SmallDictionary.cs new file mode 100644 index 0000000000000..5b49e7c6d7822 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SmallDictionary.cs @@ -0,0 +1,972 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Copied from https://github.com/dotnet/roslyn/blob/main/src/Compilers/Core/Portable/Collections/SmallDictionary.cs + /// Dictionary designed to hold small number of items. + /// Compared to the regular Dictionary, average overhead per-item is roughly the same, but + /// unlike regular dictionary, this one is based on an AVL tree and as such does not require + /// rehashing when items are added. + /// It does require rebalancing, but that is allocation-free. + /// + /// Major caveats: + /// 1) There is no Remove method. (can be added, but we do not seem to use Remove that much) + /// 2) foreach [keys|values|pairs] may allocate a small array. + /// 3) Performance is no longer O(1). At a certain count it becomes slower than regular Dictionary. + /// In comparison to regular Dictionary on my machine: + /// On trivial number of elements (5 or so) it is more than 2x faster. + /// The break even count is about 120 elements for read and 55 for write operations (with unknown initial size). + /// At UShort.MaxValue elements, this dictionary is 6x slower to read and 4x slower to write + /// + /// Generally, this dictionary is a win if number of elements is small, not known beforehand or both. + /// + /// If the size of the dictionary is known at creation and it is likely to contain more than 10 elements, + /// then regular Dictionary is a better choice. + /// +#pragma warning disable CA1051, CA1716 // Do not declare visible instance fields + internal sealed class SmallDictionary : IEnumerable> + where K : notnull + { + private AvlNode? _root; + public readonly IEqualityComparer Comparer; + + // https://github.com/dotnet/roslyn/issues/40344 + public static readonly SmallDictionary Empty = new(null!); + + public SmallDictionary() : this(EqualityComparer.Default) { } + + public SmallDictionary(IEqualityComparer comparer) + { + Comparer = comparer; + } + + public SmallDictionary(SmallDictionary other, IEqualityComparer comparer) + : this(comparer) + { + // TODO: if comparers are same (often they are), then just need to clone the tree. + foreach (var kv in other) + { + this.Add(kv.Key, kv.Value); + } + } + + private bool CompareKeys(K k1, K k2) + { + return Comparer.Equals(k1, k2); + } + + private int GetHashCode(K k) + { + return Comparer.GetHashCode(k); + } + + public bool IsEmpty => _root == null; + + public void Remove(K key) + { + _root = Remove(_root, GetHashCode(key)); + } + + private static AvlNode? Remove(AvlNode? currentNode, int hashCode) + { + if (currentNode == null) + { + return null; + } + + var hc = currentNode.HashCode; + + if (hc > hashCode) + { + currentNode.Left = Remove(currentNode.Left, hashCode); + } + else if (hc < hashCode) + { + currentNode.Right = Remove(currentNode.Right, hashCode); + } + else + { + // node with only one child or no child + if ((currentNode.Left == null) || (currentNode.Right == null)) + { + AvlNode? temp = null; + if (temp == currentNode.Left) + temp = currentNode.Right; + else + temp = currentNode.Left; + + // No child case + if (temp == null) + { + currentNode = null; + } + else // One child case + { + currentNode = temp; + } + } + else + { + // node with two children; get the smallest in the right subtree + AvlNode temp = MinValueNode(currentNode.Right); + + // Copy the smallest in the right successor's data to this node + currentNode.HashCode = temp.HashCode; + currentNode.Value = temp.Value; + currentNode.Key = temp.Key; + + // Delete the smallest in the right successor + currentNode.Right = Remove(currentNode.Right, temp.HashCode); + } + } + + if (currentNode == null) + return null; + + currentNode.Balance = (sbyte)(Height(currentNode.Left) - Height(currentNode.Right)); + + AvlNode rotated; + var balance = currentNode.Balance; + + if (balance == -2) + { + rotated = currentNode.Right!.Balance <= 0 ? + LeftSimple(currentNode) : + LeftComplex(currentNode); + } + else if (balance == 2) + { + rotated = currentNode.Left!.Balance >= 0 ? + RightSimple(currentNode) : + RightComplex(currentNode); + } + else + { + return currentNode; + } + + return rotated; + } + + private static AvlNode MinValueNode(AvlNode node) + { + AvlNode current = node; + + while (current.Left != null) + current = current.Left; + + return current; + } + + private static int Height(AvlNode? node) + { + if (node == null) + return 0; + + int a = Height(node.Left); + int b = Height(node.Right); + + return 1 + Math.Max(a, b); + } + + public bool TryGetValue(K key, [MaybeNullWhen(returnValue: false)] out V value) + { + if (_root != null) + { + return TryGetValue(GetHashCode(key), key, out value!); + } + + value = default!; + return false; + } + + public void Add(K key, V value) + { + Insert(GetHashCode(key), key, value, add: true); + } + + public V this[K key] + { + get + { + V value; + if (!TryGetValue(key, out value!)) + { + throw new KeyNotFoundException($"Could not find key {key}"); + } + + return value; + } + + set => this.Insert(GetHashCode(key), key, value, add: false); + } + + public bool ContainsKey(K key) + { + return TryGetValue(key, out _); + } + +#pragma warning disable CA1822 + [Conditional("DEBUG")] + internal void AssertBalanced() + { +#if DEBUG + AvlNode.AssertBalanced(_root); +#endif + } +#pragma warning restore CA1822 + private abstract class Node + { + public K Key; + public V Value; + + protected Node(K key, V value) + { + this.Key = key; + this.Value = value; + } + + public virtual Node? Next => null; + } + + private sealed class NodeLinked : Node + { + public NodeLinked(K key, V value, Node next) + : base(key, value) + { + this.Next = next; + } + + public override Node Next { get; } + } + +#pragma warning disable CA1708 // Identifiers should differ by more than case + private sealed class AvlNodeHead : AvlNode +#pragma warning restore CA1708 // Identifiers should differ by more than case + { + public Node next; + + public AvlNodeHead(int hashCode, K key, V value, Node next) + : base(hashCode, key, value) + { + this.next = next; + } + + public override Node Next => next; + } + + // separate class to ensure that HashCode field + // is located before other AvlNode fields + // Balance is also here for better packing of AvlNode on 64bit + private abstract class HashedNode : Node + { + public int HashCode; + public sbyte Balance; + + protected HashedNode(int hashCode, K key, V value) + : base(key, value) + { + this.HashCode = hashCode; + } + } + + private class AvlNode : HashedNode + { + public AvlNode? Left; + public AvlNode? Right; + + public AvlNode(int hashCode, K key, V value) + : base(hashCode, key, value) + { } + +#if DEBUG +#pragma warning disable CA1000 // Do not declare static members on generic types + public static int AssertBalanced(AvlNode? V) +#pragma warning restore CA1000 // Do not declare static members on generic types + { + if (V == null) + return 0; + + int a = AssertBalanced(V.Left); + int b = AssertBalanced(V.Right); + + if (a - b != V.Balance || + Math.Abs(a - b) >= 2) + { + throw new InvalidOperationException(); + } + + return 1 + Math.Max(a, b); + } +#endif + } + + private bool TryGetValue(int hashCode, K key, [MaybeNullWhen(returnValue: false)] out V value) + { + RoslynDebug.Assert(_root is object); + AvlNode? b = _root; + + do + { + if (b.HashCode > hashCode) + { + b = b.Left; + } + else if (b.HashCode < hashCode) + { + b = b.Right; + } + else + { + goto hasBucket; + } + } while (b != null); + + value = default!; + return false; + +hasBucket: + if (CompareKeys(b.Key, key)) + { + value = b.Value; + return true; + } + + return GetFromList(b.Next, key, out value!); + } + + private bool GetFromList(Node? next, K key, [MaybeNullWhen(returnValue: false)] out V value) + { + while (next != null) + { + if (CompareKeys(key, next.Key)) + { + value = next.Value; + return true; + } + + next = next.Next; + } + + value = default!; + return false; + } + + private void Insert(int hashCode, K key, V value, bool add) + { + AvlNode? currentNode = _root; + + if (currentNode == null) + { + _root = new AvlNode(hashCode, key, value); + return; + } + + AvlNode? currentNodeParent = null; + AvlNode unbalanced = currentNode; + AvlNode? unbalancedParent = null; + + // ====== insert new node + // also make a note of the last unbalanced node and its parent (for rotation if needed) + // nodes on the search path from rotation candidate downwards will change balances because of the node added + // unbalanced node itself will become balanced or will be rotated + // either way nodes above unbalanced do not change their balance + for (; ; ) + { + // schedule hk read + var hc = currentNode.HashCode; + + if (currentNode.Balance != 0) + { + unbalancedParent = currentNodeParent; + unbalanced = currentNode; + } + + if (hc > hashCode) + { + if (currentNode.Left == null) + { + var previousNode = currentNode; + currentNode = new AvlNode(hashCode, key, value); + previousNode.Left = currentNode; + break; + } + + currentNodeParent = currentNode; + currentNode = currentNode.Left; + } + else if (hc < hashCode) + { + if (currentNode.Right == null) + { + var previousNode = currentNode; + currentNode = new AvlNode(hashCode, key, value); + previousNode.Right = currentNode; + break; + } + + currentNodeParent = currentNode; + currentNode = currentNode.Right; + } + else // (p.HashCode == hashCode) + { + this.HandleInsert(currentNode, currentNodeParent, key, value, add); + return; + } + } + + Debug.Assert(unbalanced != currentNode); + + // ====== update balances on the path from unbalanced downwards + var n = unbalanced; + do + { + Debug.Assert(n.HashCode != hashCode); + + if (n.HashCode < hashCode) + { + n.Balance--; + n = n.Right!; + } + else + { + n.Balance++; + n = n.Left!; + } + } + while (n != currentNode); + + // ====== rotate unbalanced node if needed + AvlNode rotated; + var balance = unbalanced.Balance; + if (balance == -2) + { + rotated = unbalanced.Right!.Balance < 0 ? + LeftSimple(unbalanced) : + LeftComplex(unbalanced); + } + else if (balance == 2) + { + rotated = unbalanced.Left!.Balance > 0 ? + RightSimple(unbalanced) : + RightComplex(unbalanced); + } + else + { + return; + } + + // ===== make parent to point to rotated + if (unbalancedParent == null) + { + _root = rotated; + } + else if (unbalanced == unbalancedParent.Left) + { + unbalancedParent.Left = rotated; + } + else + { + unbalancedParent.Right = rotated; + } + } + + private static AvlNode LeftSimple(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Right is object); + var right = unbalanced.Right; + unbalanced.Right = right.Left; + right.Left = unbalanced; + + unbalanced.Balance = 0; + right.Balance = 0; + return right; + } + + private static AvlNode RightSimple(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Left is object); + var left = unbalanced.Left; + unbalanced.Left = left.Right; + left.Right = unbalanced; + + unbalanced.Balance = 0; + left.Balance = 0; + return left; + } + + private static AvlNode LeftComplex(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Right is object); + RoslynDebug.Assert(unbalanced.Right.Left is object); + var right = unbalanced.Right; + var rightLeft = right.Left; + right.Left = rightLeft.Right; + rightLeft.Right = right; + unbalanced.Right = rightLeft.Left; + rightLeft.Left = unbalanced; + + var rightLeftBalance = rightLeft.Balance; + rightLeft.Balance = 0; + + if (rightLeftBalance < 0) + { + right.Balance = 0; + unbalanced.Balance = 1; + } + else + { + right.Balance = (sbyte)-rightLeftBalance; + unbalanced.Balance = 0; + } + + return rightLeft; + } + + private static AvlNode RightComplex(AvlNode unbalanced) + { + RoslynDebug.Assert(unbalanced.Left != null); + RoslynDebug.Assert(unbalanced.Left.Right != null); + var left = unbalanced.Left; + var leftRight = left.Right; + left.Right = leftRight.Left; + leftRight.Left = left; + unbalanced.Left = leftRight.Right; + leftRight.Right = unbalanced; + + var leftRightBalance = leftRight.Balance; + leftRight.Balance = 0; + + if (leftRightBalance < 0) + { + left.Balance = 1; + unbalanced.Balance = 0; + } + else + { + left.Balance = 0; + unbalanced.Balance = (sbyte)-leftRightBalance; + } + + return leftRight; + } + + private void HandleInsert(AvlNode node, AvlNode? parent, K key, V value, bool add) + { + Node? currentNode = node; + do + { + if (CompareKeys(currentNode.Key, key)) + { + if (add) + { + throw new InvalidOperationException(); + } + + currentNode.Value = value; + return; + } + + currentNode = currentNode.Next; + } while (currentNode != null); + + AddNode(node, parent, key, value); + } + + private void AddNode(AvlNode node, AvlNode? parent, K key, V value) + { + if (node is AvlNodeHead head) + { + var newNext = new NodeLinked(key, value, head.next); + head.next = newNext; + return; + } + + var newHead = new AvlNodeHead(node.HashCode, key, value, node); + newHead.Balance = node.Balance; + newHead.Left = node.Left; + newHead.Right = node.Right; + + if (parent == null) + { + _root = newHead; + return; + } + + if (node == parent.Left) + { + parent.Left = newHead; + } + else + { + parent.Right = newHead; + } + } + + public KeyCollection Keys => new(this); + +#pragma warning disable CA1815 // Override equals and operator equals on value types + internal readonly struct KeyCollection : IEnumerable + { + private readonly SmallDictionary _dict; + + public KeyCollection(SmallDictionary dict) + { + _dict = dict; + } + + public struct Enumerator + { + private readonly Stack? _stack; + private Node? _next; + private Node? _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root != null) + { + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + } + + public readonly K Current => _current!.Key; + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(_stack, curr.Left); + PushIfNotNull(_stack, curr.Right); + + return true; + + static void PushIfNotNull(Stack stack, AvlNode? child) + { + if (child != null) + { + stack.Push(child); + } + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dict); + } + +#pragma warning disable CA1063, CA1816 // Implement IDisposable Correctly + public sealed class EnumerableCore : IEnumerator + { + private Enumerator _e; + + public EnumerableCore(Enumerator e) + { + _e = e; + } + + K IEnumerator.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotSupportedException(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new EnumerableCore(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public ValueCollection Values => new(this); + + internal readonly struct ValueCollection : IEnumerable + { + private readonly SmallDictionary _dict; + + public ValueCollection(SmallDictionary dict) + { + _dict = dict; + } + + public struct Enumerator + { + private readonly Stack? _stack; + private Node? _next; + private Node? _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root == null) + { + return; + } + + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + + public readonly V Current => _current!.Value; + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(_stack, curr.Left); + PushIfNotNull(_stack, curr.Right); + + return true; + + static void PushIfNotNull(Stack stack, AvlNode? child) + { + if (child != null) + { + stack.Push(child); + } + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_dict); + } + + public sealed class EnumerableCore : IEnumerator + { + private Enumerator _e; + + public EnumerableCore(Enumerator e) + { + _e = e; + } + + V IEnumerator.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object? IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotImplementedException(); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return new EnumerableCore(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + public struct Enumerator + { + private readonly Stack? _stack; + private Node? _next; + private Node? _current; + + public Enumerator(SmallDictionary dict) + : this() + { + var root = dict._root; + if (root == null) + { + return; + } + + // left == right only if both are nulls + if (root.Left == root.Right) + { + _next = root; + } + else + { + _stack = new Stack(dict.HeightApprox()); + _stack.Push(root); + } + } + + public readonly KeyValuePair Current => new(_current!.Key, _current!.Value); + + public bool MoveNext() + { + if (_next != null) + { + _current = _next; + _next = _next.Next; + return true; + } + + if (_stack == null || _stack.Count == 0) + { + return false; + } + + var curr = _stack.Pop(); + _current = curr; + _next = curr.Next; + + PushIfNotNull(_stack, curr.Left); + PushIfNotNull(_stack, curr.Right); + + return true; + + static void PushIfNotNull(Stack stack, AvlNode? child) + { + if (child != null) + { + stack.Push(child); + } + } + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } + + public sealed class EnumerableCore : IEnumerator> + { + private Enumerator _e; + + public EnumerableCore(Enumerator e) + { + _e = e; + } + + KeyValuePair IEnumerator>.Current => _e.Current; + + void IDisposable.Dispose() + { + } + + object IEnumerator.Current => _e.Current; + + bool IEnumerator.MoveNext() + { + return _e.MoveNext(); + } + + void IEnumerator.Reset() + { + throw new NotImplementedException(); + } + } +#pragma warning restore CA1063, CA1816 // Implement IDisposable Correctly +#pragma warning restore CA1815 // Override equals and operator equals on value types + + IEnumerator> IEnumerable>.GetEnumerator() + { + return new EnumerableCore(GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + private int HeightApprox() + { + // height is less than 1.5 * depth(leftmost node) + var h = 0; + var cur = _root; + while (cur != null) + { + h++; + cur = cur.Left; + } + + h += h / 2; + return h; + } + } +#pragma warning restore CA1051, CA1716 // Do not declare visible instance fields +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SymbolByDisplayStringComparer.cs b/src/RoslynAnalyzers/Utilities/Compiler/SymbolByDisplayStringComparer.cs new file mode 100644 index 0000000000000..9a265495f7940 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SymbolByDisplayStringComparer.cs @@ -0,0 +1,59 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// for s sorted by display strings. + /// +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class SymbolByDisplayStringComparer : IComparer +#pragma warning restore CA1812 + { + /// + /// Constructs. + /// + /// The compilation containing the types to be compared. + public SymbolByDisplayStringComparer(Compilation compilation) + : this(SymbolDisplayStringCache.GetOrCreate(compilation)) + { + } + + /// + /// Constructs. + /// + /// The cache display strings to use. + public SymbolByDisplayStringComparer(SymbolDisplayStringCache symbolDisplayStringCache) + { + this.SymbolDisplayStringCache = symbolDisplayStringCache ?? throw new ArgumentNullException(nameof(symbolDisplayStringCache)); + } + + /// + /// Cache of symbol display strings. + /// + public SymbolDisplayStringCache SymbolDisplayStringCache { get; } + + /// + /// Compares two type symbols by their display strings. + /// + /// First type symbol to compare. + /// Second type symbol to compare. + /// Less than 0 if is before , 0 if equal, greater than 0 if + /// is after . + public int Compare([AllowNull] ITypeSymbol x, [AllowNull] ITypeSymbol y) + { + RoslynDebug.Assert(x != null); + RoslynDebug.Assert(y != null); + + return StringComparer.Ordinal.Compare( + this.SymbolDisplayStringCache.GetDisplayString(x), + this.SymbolDisplayStringCache.GetDisplayString(y)); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayFormats.cs b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayFormats.cs new file mode 100644 index 0000000000000..07c408e2b9aa8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayFormats.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static class SymbolDisplayFormats + { + public static readonly SymbolDisplayFormat ShortSymbolDisplayFormat = new( + SymbolDisplayGlobalNamespaceStyle.Omitted, + SymbolDisplayTypeQualificationStyle.NameAndContainingTypes, + SymbolDisplayGenericsOptions.IncludeTypeParameters, + SymbolDisplayMemberOptions.IncludeContainingType | SymbolDisplayMemberOptions.IncludeParameters, + SymbolDisplayDelegateStyle.NameAndParameters, + SymbolDisplayExtensionMethodStyle.InstanceMethod, + SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeOptionalBrackets | SymbolDisplayParameterOptions.IncludeParamsRefOut, + SymbolDisplayPropertyStyle.NameOnly, + SymbolDisplayLocalOptions.IncludeType, + SymbolDisplayKindOptions.None, + SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + public static readonly SymbolDisplayFormat QualifiedTypeAndNamespaceSymbolDisplayFormat = new( + SymbolDisplayGlobalNamespaceStyle.Omitted, + SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + SymbolDisplayGenericsOptions.IncludeTypeParameters, + SymbolDisplayMemberOptions.IncludeContainingType, + SymbolDisplayDelegateStyle.NameOnly, + SymbolDisplayExtensionMethodStyle.InstanceMethod, + SymbolDisplayParameterOptions.None, + SymbolDisplayPropertyStyle.NameOnly, + SymbolDisplayLocalOptions.IncludeType, + SymbolDisplayKindOptions.None, + SymbolDisplayMiscellaneousOptions.None); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayNameCache.cs b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayNameCache.cs new file mode 100644 index 0000000000000..6cdef6805ce4e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayNameCache.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Concurrent; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Cache ISymbol.ToDisplayName() results, to avoid performance concerns. + /// + public sealed class SymbolDisplayNameCache + { + private static readonly BoundedCacheWithFactory s_byCompilationCache = + new BoundedCacheWithFactory(); + + /// + /// Mapping of a symbol to its ToDisplayString(). + /// + private readonly ConcurrentDictionary SymbolToDisplayNames = + new ConcurrentDictionary(); + + + private SymbolDisplayNameCache() + { + } + + /// + /// Gets the symbol display string cache for the compilation. + /// + /// + /// + public static SymbolDisplayNameCache GetOrCreate(Compilation compilation) + { + return s_byCompilationCache.GetOrCreateValue(compilation, CreateSymbolDisplayNameCache); + + // Local functions + static SymbolDisplayNameCache CreateSymbolDisplayNameCache(Compilation compilation) + => new SymbolDisplayNameCache(); + } + + /// + /// Gets the symbol's display string. + /// + /// Symbol to get the display string. + /// The symbol's display string. + public string GetDisplayString(ISymbol symbol) + { + return this.SymbolToDisplayNames.GetOrAdd(symbol, s => s.ToDisplayString()); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayStringCache.cs b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayStringCache.cs new file mode 100644 index 0000000000000..2c178d5123554 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SymbolDisplayStringCache.cs @@ -0,0 +1,70 @@ +// 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.Concurrent; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// Cache ISymbol.ToDisplayName() results, to avoid performance concerns. + /// + internal sealed class SymbolDisplayStringCache + { + /// + /// Caches by compilation. + /// + private static readonly BoundedCacheWithFactory> s_byCompilationCache = new(); + + /// + /// ConcurrentDictionary key for a null SymbolDisplayFormat. + /// + private static readonly SymbolDisplayFormat NullSymbolDisplayFormat = new(); + + /// + /// Mapping of a symbol to its ToDisplayString(). + /// + private readonly ConcurrentDictionary SymbolToDisplayNames = new(); + + private readonly SymbolDisplayFormat? Format; + + /// + /// Privately constructs. + /// + /// SymbolDisplayFormat to use, or null for the default. + private SymbolDisplayStringCache(SymbolDisplayFormat? format = null) + { + this.Format = Object.ReferenceEquals(format, NullSymbolDisplayFormat) ? null : format; + } + + /// + /// Gets the symbol display string cache for the compilation. + /// + /// Compilation that this cache is for. + /// A singleton SymbolDisplayFormat to use, or null for the default. + /// A SymbolDisplayStringCache. + public static SymbolDisplayStringCache GetOrCreate(Compilation compilation, SymbolDisplayFormat? format = null) + { + ConcurrentDictionary dict = + s_byCompilationCache.GetOrCreateValue(compilation, CreateConcurrentDictionary); + return dict.GetOrAdd(format ?? NullSymbolDisplayFormat, CreateSymbolDisplayStringCache); + + // Local functions + static ConcurrentDictionary CreateConcurrentDictionary(Compilation compilation) + => new(); + static SymbolDisplayStringCache CreateSymbolDisplayStringCache(SymbolDisplayFormat? format) => new(format); + } + + /// + /// Gets the symbol's display string. + /// + /// Symbol to get the display string. + /// The symbol's display string. + public string GetDisplayString(ISymbol symbol) + { + return this.SymbolToDisplayNames.GetOrAdd(symbol, s => s.ToDisplayString(this.Format)); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/SymbolEqualityComparer.cs b/src/RoslynAnalyzers/Utilities/Compiler/SymbolEqualityComparer.cs new file mode 100644 index 0000000000000..efbf1e0ffa17d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/SymbolEqualityComparer.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. + +#if !CODEANALYSIS_V3_OR_BETTER +namespace Microsoft.CodeAnalysis +{ + using System.Collections.Generic; + + internal sealed class SymbolEqualityComparer : IEqualityComparer + { + private SymbolEqualityComparer() + { + } + + public bool Equals(ISymbol? x, ISymbol? y) + => x is null + ? y is null + : x.Equals(y); + + public int GetHashCode(ISymbol? obj) + => obj?.GetHashCode() ?? 0; + + public static SymbolEqualityComparer Default { get; } = new(); + } +} +#endif diff --git a/src/RoslynAnalyzers/Utilities/Compiler/TypeSymbolByMetadataNameComparer.cs b/src/RoslynAnalyzers/Utilities/Compiler/TypeSymbolByMetadataNameComparer.cs new file mode 100644 index 0000000000000..aec4591ed907d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/TypeSymbolByMetadataNameComparer.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + /// + /// for s sorted by display strings. + /// + internal class TypeSymbolByMetadataNameComparer : IComparer + { + /// + /// Constructs. + /// + public TypeSymbolByMetadataNameComparer(Compilation compilation) + : this(SymbolDisplayStringCache.GetOrCreate(compilation)) + { + } + + /// + /// + /// + /// + public TypeSymbolByMetadataNameComparer(SymbolDisplayStringCache symbolDisplayStringCache) + { + this.SymbolDisplayStringCache = symbolDisplayStringCache ?? throw new ArgumentNullException(nameof(symbolDisplayStringCache)); + } + + public SymbolDisplayStringCache SymbolDisplayStringCache { get; } + + public int Compare(ITypeSymbol x, ITypeSymbol y) + { + return StringComparer.Ordinal.Compare(this.SymbolDisplayStringCache[x], y.ToDisplayString()); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/UnusedValue.cs b/src/RoslynAnalyzers/Utilities/Compiler/UnusedValue.cs new file mode 100644 index 0000000000000..4d6b4556dd5ba --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/UnusedValue.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; + +namespace Analyzer.Utilities +{ + /// + /// A placeholder value type for used as a set. + /// + internal readonly struct UnusedValue + { + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeNames.cs b/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeNames.cs new file mode 100644 index 0000000000000..0b0bc8d31f3db --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeNames.cs @@ -0,0 +1,633 @@ +// 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 Analyzer.Utilities +{ + // IMPORTANT: Keep this file sorted alphabetically. + internal static class WellKnownTypeNames + { + public const string BenchmarkDotNetAttributesBenchmarkAttribute = "BenchmarkDotNet.Attributes.BenchmarkAttribute"; + public const string MicrosoftAspNetCoreAntiforgeryIAntiforgery = "Microsoft.AspNetCore.Antiforgery.IAntiforgery"; + public const string MicrosoftAspNetCoreHttpCookieOptions = "Microsoft.AspNetCore.Http.CookieOptions"; + public const string MicrosoftAspNetCoreHttpHttpRequest = "Microsoft.AspNetCore.Http.HttpRequest"; + public const string MicrosoftAspNetCoreHttpInternalResponseCookies = "Microsoft.AspNetCore.Http.Internal.ResponseCookies"; + public const string MicrosoftAspNetCoreHttpIResponseCookies = "Microsoft.AspNetCore.Http.IResponseCookies"; + public const string MicrosoftAspNetCoreMvcController = "Microsoft.AspNetCore.Mvc.Controller"; + public const string MicrosoftAspNetCoreMvcControllerAttribute = "Microsoft.AspNetCore.Mvc.ControllerAttribute"; + public const string MicrosoftAspNetCoreMvcControllerBase = "Microsoft.AspNetCore.Mvc.ControllerBase"; + public const string MicrosoftAspNetCoreMvcFiltersAuthorizationFilterContext = "Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext"; + public const string MicrosoftAspNetCoreMvcFiltersFilterCollection = "Microsoft.AspNetCore.Mvc.Filters.FilterCollection"; + public const string MicrosoftAspNetCoreMvcFiltersIAsyncAuthorizationFilter = "Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter"; + public const string MicrosoftAspNetCoreMvcFiltersIAuthorizationFilter = "Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter"; + public const string MicrosoftAspNetCoreMvcFiltersIFilterMetadata = "Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata"; + public const string MicrosoftAspNetCoreMvcFromServicesAttribute = "Microsoft.AspNetCore.Mvc.FromServicesAttribute"; + public const string MicrosoftAspNetCoreMvcHttpDeleteAttribute = "Microsoft.AspNetCore.Mvc.HttpDeleteAttribute"; + public const string MicrosoftAspNetCoreMvcHttpGetAttribute = "Microsoft.AspNetCore.Mvc.HttpGetAttribute"; + public const string MicrosoftAspNetCoreMvcHttpHeadAttribute = "Microsoft.AspNetCore.Mvc.HttpHeadAttribute"; + public const string MicrosoftAspNetCoreMvcHttpOptionsAttribute = "Microsoft.AspNetCore.Mvc.HttpOptionsAttribute"; + public const string MicrosoftAspNetCoreMvcHttpPatchAttribute = "Microsoft.AspNetCore.Mvc.HttpPatchAttribute"; + public const string MicrosoftAspNetCoreMvcHttpPostAttribute = "Microsoft.AspNetCore.Mvc.HttpPostAttribute"; + public const string MicrosoftAspNetCoreMvcHttpPutAttribute = "Microsoft.AspNetCore.Mvc.HttpPutAttribute"; + public const string MicrosoftAspNetCoreMvcNonActionAttribute = "Microsoft.AspNetCore.Mvc.NonActionAttribute"; + public const string MicrosoftAspNetCoreMvcNonControllerAttribute = "Microsoft.AspNetCore.Mvc.NonControllerAttribute"; + public const string MicrosoftAspNetCoreMvcRouteAttribute = "Microsoft.AspNetCore.Mvc.RouteAttribute"; + public const string MicrosoftAspNetCoreMvcRoutingHttpMethodAttribute = "Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute"; + public const string MicrosoftAspNetCoreRazorHostingRazorCompiledItemAttribute = "Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute"; + public const string MicrosoftCodeAnalysisCodeActionsCodeAction = "Microsoft.CodeAnalysis.CodeActions.CodeAction"; + public const string MicrosoftCodeAnalysisCodeFixesCodeFixProvider = "Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider"; + public const string MicrosoftCodeAnalysisCodeFixesExportCodeFixProviderAttribute = "Microsoft.CodeAnalysis.CodeFixes.ExportCodeFixProviderAttribute"; + public const string MicrosoftCodeAnalysisCompilation = "Microsoft.CodeAnalysis.Compilation"; + public const string MicrosoftCodeAnalysisCSharpCSharpCompilation = "Microsoft.CodeAnalysis.CSharp.CSharpCompilation"; + public const string MicrosoftCodeAnalysisCSharpCSharpExtensions = "Microsoft.CodeAnalysis.CSharp.CSharpExtensions"; + public const string MicrosoftCodeAnalysisCSharpExtensions = "Microsoft.CodeAnalysis.CSharpExtensions"; + public const string MicrosoftCodeAnalysisCSharpSyntaxBaseFieldDeclarationSyntax = "Microsoft.CodeAnalysis.CSharp.Syntax.BaseFieldDeclarationSyntax"; + public const string MicrosoftCodeAnalysisCSharpSyntaxLocalFunctionStatementSyntax = "Microsoft.CodeAnalysis.CSharp.Syntax.LocalFunctionStatementSyntax"; + public const string MicrosoftCodeAnalysisDiagnostic = "Microsoft.CodeAnalysis.Diagnostic"; + public const string MicrosoftCodeAnalysisDiagnosticDescriptor = "Microsoft.CodeAnalysis.DiagnosticDescriptor"; + public const string MicrosoftCodeAnalysisDiagnosticsAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.AnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsCodeBlockAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.CodeBlockAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsCodeBlockStartAnalysisContext1 = "Microsoft.CodeAnalysis.Diagnostics.CodeBlockStartAnalysisContext`1"; + public const string MicrosoftCodeAnalysisDiagnosticsCompilationEndAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.CompilationAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsCompilationStartAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.CompilationStartAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer = "Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer"; + public const string MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute = "Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzerAttribute"; + public const string MicrosoftCodeAnalysisDiagnosticsGeneratedCodeAnalysisFlags = "Microsoft.CodeAnalysis.Diagnostics.GeneratedCodeAnalysisFlags"; + public const string MicrosoftCodeAnalysisDiagnosticsOperationAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsOperationBlockAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.OperationBlockAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsOperationBlockStartAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.OperationBlockStartAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsSemanticModelAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.SemanticModelAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsSymbolAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.SymbolAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsSyntaxNodeAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext"; + public const string MicrosoftCodeAnalysisDiagnosticsSyntaxTreeAnalysisContext = "Microsoft.CodeAnalysis.Diagnostics.SyntaxTreeAnalysisContext"; + public const string MicrosoftCodeAnalysisGeneratorAttribute = "Microsoft.CodeAnalysis.GeneratorAttribute"; + public const string MicrosoftCodeAnalysisHostMefMefConstruction = "Microsoft.CodeAnalysis.Host.Mef.MefConstruction"; + public const string MicrosoftCodeAnalysisIIncrementalGenerator = "Microsoft.CodeAnalysis.IIncrementalGenerator"; + public const string MicrosoftCodeAnalysisISourceGenerator = "Microsoft.CodeAnalysis.ISourceGenerator"; + public const string MicrosoftCodeAnalysisLocalizableResourceString = "Microsoft.CodeAnalysis.LocalizableResourceString"; + public const string MicrosoftCodeAnalysisLocalizableString = "Microsoft.CodeAnalysis.LocalizableString"; + public const string MicrosoftCodeAnalysisModelExtensions = "Microsoft.CodeAnalysis.ModelExtensions"; + public const string MicrosoftCodeAnalysisSharedCollectionsTemporaryArrayExtensions = "Microsoft.CodeAnalysis.Shared.Collections.TemporaryArrayExtensions"; + public const string MicrosoftCodeAnalysisSymbolKind = "Microsoft.CodeAnalysis.SymbolKind"; + public const string MicrosoftCodeAnalysisSyntaxNode = "Microsoft.CodeAnalysis.SyntaxNode"; + public const string MicrosoftCodeAnalysisVisualBasicExtensions = "Microsoft.CodeAnalysis.VisualBasicExtensions"; + public const string MicrosoftCodeAnalysisVisualBasicVisualBasicCompilation = "Microsoft.CodeAnalysis.VisualBasic.VisualBasicCompilation"; + public const string MicrosoftCodeAnalysisVisualBasicVisualBasicExtensions = "Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions"; + public const string MicrosoftEntityFrameworkCoreDbContext = "Microsoft.EntityFrameworkCore.DbContext"; + public const string MicrosoftEntityFrameworkCoreDbContextFactory = "Microsoft.EntityFrameworkCore.IDbContextFactory`1"; + public const string MicrosoftEntityFrameworkCoreDbSet1 = "Microsoft.EntityFrameworkCore.DbSet`1"; + public const string MicrosoftEntityFrameworkCoreEntityFrameworkQueryableExtensions = "Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions"; + public const string MicrosoftEntityFrameworkCoreRelationalQueryableExtensions = "Microsoft.EntityFrameworkCore.RelationalQueryableExtensions"; + public const string MicrosoftExtensionsLoggingILogger = "Microsoft.Extensions.Logging.ILogger"; + public const string MicrosoftExtensionsLoggingLoggerExtensions = "Microsoft.Extensions.Logging.LoggerExtensions"; + public const string MicrosoftExtensionsLoggingLoggerMessage = "Microsoft.Extensions.Logging.LoggerMessage"; + public const string MicrosoftExtensionsLoggingLoggerMessageAttribute = "Microsoft.Extensions.Logging.LoggerMessageAttribute"; + public const string MicrosoftIdentityModelTokensAudienceValidator = "Microsoft.IdentityModel.Tokens.AudienceValidator"; + public const string MicrosoftIdentityModelTokensLifetimeValidator = "Microsoft.IdentityModel.Tokens.LifetimeValidator"; + public const string MicrosoftIdentityModelTokensSecurityToken = "Microsoft.IdentityModel.Tokens.SecurityToken"; + public const string MicrosoftIdentityModelTokensTokenValidationParameters = "Microsoft.IdentityModel.Tokens.TokenValidationParameters"; + public const string MicrosoftSecurityApplicationAntiXss = "Microsoft.Security.Application.AntiXss"; + public const string MicrosoftSecurityApplicationAntiXssEncoder = "Microsoft.Security.Application.AntiXssEncoder"; + public const string MicrosoftSecurityApplicationEncoder = "Microsoft.Security.Application.Encoder"; + public const string MicrosoftSecurityApplicationUnicodeCharacterEncoder = "Microsoft.Security.Application.UnicodeCharacterEncoder"; + public const string MicrosoftVisualBasicDevicesComputerInfo = "Microsoft.VisualBasic.Devices.ComputerInfo"; + public const string MicrosoftVisualStudioTestToolsUnitTestingAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.Assert"; + public const string MicrosoftVisualStudioTestToolsUnitTestingCollectionAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert"; + public const string MicrosoftVisualStudioTestToolsUnitTestingDataTestMethodAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.DataTestMethodAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingExpectedExceptionAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.ExpectedExceptionAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingStringAssert = "Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert"; + public const string MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute"; + public const string MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute = "Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute"; + public const string MicrosoftWindowsAzureStorageCloudStorageAccount = "Microsoft.WindowsAzure.Storage.CloudStorageAccount"; + public const string MicrosoftWindowsAzureStorageSharedAccessProtocol = "Microsoft.WindowsAzure.Storage.SharedAccessProtocol"; + public const string NewtonsoftJsonJsonConvert = "Newtonsoft.Json.JsonConvert"; + public const string NewtonsoftJsonJsonIgnoreAttribute = "Newtonsoft.Json.JsonIgnoreAttribute"; + public const string NewtonsoftJsonJsonSerializer = "Newtonsoft.Json.JsonSerializer"; + public const string NewtonsoftJsonJsonSerializerSettings = "Newtonsoft.Json.JsonSerializerSettings"; + public const string NewtonsoftJsonTypeNameHandling = "Newtonsoft.Json.TypeNameHandling"; + public const string NUnitFrameworkAssert = "NUnit.Framework.Assert"; + public const string NUnitFrameworkInterfacesITestBuilder = "NUnit.Framework.Interfaces.ITestBuilder"; + public const string NUnitFrameworkOneTimeSetUpAttribute = "NUnit.Framework.OneTimeSetUpAttribute"; + public const string NUnitFrameworkOneTimeTearDownAttribute = "NUnit.Framework.OneTimeTearDownAttribute"; + public const string NUnitFrameworkSetUpAttribute = "NUnit.Framework.SetUpAttribute"; + public const string NUnitFrameworkTearDownAttribute = "NUnit.Framework.TearDownAttribute"; + public const string NUnitFrameworkTestAttribute = "NUnit.Framework.TestAttribute"; + public const string RoslynUtilitiesNonDefaultableAttribute = "Roslyn.Utilities.NonDefaultableAttribute"; + public const string RoslynDebug = "Roslyn.Utilities.RoslynDebug"; + public const string SystemActivator = "System.Activator"; + public const string SystemAppContext = "System.AppContext"; + public const string SystemAppDomain = "System.AppDomain"; + public const string SystemArgumentException = "System.ArgumentException"; + public const string SystemArgumentNullException = "System.ArgumentNullException"; + public const string SystemArgumentOutOfRangeException = "System.ArgumentOutOfRangeException"; + public const string SystemAttribute = "System.Attribute"; + public const string SystemAttributeTargets = "System.AttributeTargets"; + public const string SystemAttributeUsageAttribute = "System.AttributeUsageAttribute"; + public const string SystemBitConverter = "System.BitConverter"; + public const string SystemBoolean = "System.Boolean"; + public const string SystemBuffer = "System.Buffer"; + public const string SystemBuffersMemoryManager1 = "System.Buffers.MemoryManager`1"; + public const string SystemBuffersSearchValues = "System.Buffers.SearchValues"; + public const string SystemBuffersSearchValues1 = "System.Buffers.SearchValues`1"; + public const string SystemByte = "System.Byte"; + public const string SystemChar = "System.Char"; + public const string SystemCLSCompliantAttribute = "System.CLSCompliantAttribute"; + public const string SystemCodeDomCompilerGeneratedCodeAttribute = "System.CodeDom.Compiler.GeneratedCodeAttribute"; + public const string SystemCollectionsConcurrentConcurrentBag1 = "System.Collections.Concurrent.ConcurrentBag`1"; + public const string SystemCollectionsConcurrentConcurrentDictionary2 = "System.Collections.Concurrent.ConcurrentDictionary`2"; + public const string SystemCollectionsConcurrentConcurrentQueue1 = "System.Collections.Concurrent.ConcurrentQueue`1"; + public const string SystemCollectionsConcurrentConcurrentStack1 = "System.Collections.Concurrent.ConcurrentStack`1"; + public const string SystemCollectionsGenericDictionary2 = "System.Collections.Generic.Dictionary`2"; + public const string SystemCollectionsGenericHashSet1 = "System.Collections.Generic.HashSet`1"; + public const string SystemCollectionsGenericIAsyncEnumerable1 = "System.Collections.Generic.IAsyncEnumerable`1"; + public const string SystemCollectionsGenericICollection1 = "System.Collections.Generic.ICollection`1"; + public const string SystemCollectionsGenericIDictionary2 = "System.Collections.Generic.IDictionary`2"; + public const string SystemCollectionsGenericIEnumerable1 = "System.Collections.Generic.IEnumerable`1"; + public const string SystemCollectionsGenericIEnumerator1 = "System.Collections.Generic.IEnumerator`1"; + public const string SystemCollectionsGenericIEqualityComparer1 = "System.Collections.Generic.IEqualityComparer`1"; + public const string SystemCollectionsGenericIList1 = "System.Collections.Generic.IList`1"; + public const string SystemCollectionsGenericIReadOnlyCollection1 = "System.Collections.Generic.IReadOnlyCollection`1"; + public const string SystemCollectionsGenericIReadOnlyDictionary2 = "System.Collections.Generic.IReadOnlyDictionary`2"; + public const string SystemCollectionsGenericIReadOnlyList1 = "System.Collections.Generic.IReadOnlyList`1"; + public const string SystemCollectionsGenericIReadOnlySet1 = "System.Collections.Generic.IReadOnlySet`1"; + public const string SystemCollectionsGenericISet1 = "System.Collections.Generic.ISet`1"; + public const string SystemCollectionsGenericKeyNotFoundException = "System.Collections.Generic.KeyNotFoundException"; + public const string SystemCollectionsGenericLinkedList1 = "System.Collections.Generic.LinkedList`1"; + public const string SystemCollectionsGenericList1 = "System.Collections.Generic.List`1"; + public const string SystemCollectionsGenericPriorityQueue2 = "System.Collections.Generic.PriorityQueue`2"; + public const string SystemCollectionsGenericQueue1 = "System.Collections.Generic.Queue`1"; + public const string SystemCollectionsGenericStack1 = "System.Collections.Generic.Stack`1"; + public const string SystemCollectionsGenericSortedSet1 = "System.Collections.Generic.SortedSet`1"; + public const string SystemCollectionsICollection = "System.Collections.ICollection"; + public const string SystemCollectionsIDictionary = "System.Collections.IDictionary"; + public const string SystemCollectionsIEnumerable = "System.Collections.IEnumerable"; + public const string SystemCollectionsIEnumerator = "System.Collections.IEnumerator"; + public const string SystemCollectionsIHashCodeProvider = "System.Collections.IHashCodeProvider"; + public const string SystemCollectionsIList = "System.Collections.IList"; + public const string SystemCollectionsImmutableIImmutableDictionary2 = "System.Collections.Immutable.IImmutableDictionary`2"; + public const string SystemCollectionsImmutableIImmutableList = "System.Collections.Immutable.IImmutableList"; + public const string SystemCollectionsImmutableIImmutableList1 = "System.Collections.Immutable.IImmutableList`1"; + public const string SystemCollectionsImmutableImmutableList = "System.Collections.Immutable.ImmutableList"; + public const string SystemCollectionsImmutableIImmutableQueue1 = "System.Collections.Immutable.IImmutableQueue`1"; + public const string SystemCollectionsImmutableIImmutableSet1 = "System.Collections.Immutable.IImmutableSet`1"; + public const string SystemCollectionsImmutableIImmutableStack1 = "System.Collections.Immutable.IImmutableStack`1"; + public const string SystemCollectionsImmutableImmutableArray = "System.Collections.Immutable.ImmutableArray"; + public const string SystemCollectionsImmutableImmutableArray1 = "System.Collections.Immutable.ImmutableArray`1"; + public const string SystemCollectionsImmutableImmutableDictionary = "System.Collections.Immutable.ImmutableDictionary"; + public const string SystemCollectionsImmutableImmutableDictionary2 = "System.Collections.Immutable.ImmutableDictionary`2"; + public const string SystemCollectionsImmutableImmutableHashSet = "System.Collections.Immutable.ImmutableHashSet"; + public const string SystemCollectionsImmutableImmutableHashSet1 = "System.Collections.Immutable.ImmutableHashSet`1"; + public const string SystemCollectionsImmutableImmutableList1 = "System.Collections.Immutable.ImmutableList`1"; + public const string SystemCollectionsImmutableImmutableSortedDictionary = "System.Collections.Immutable.ImmutableSortedDictionary"; + public const string SystemCollectionsImmutableImmutableSortedDictionary2 = "System.Collections.Immutable.ImmutableSortedDictionary`2"; + public const string SystemCollectionsImmutableImmutableSortedSet = "System.Collections.Immutable.ImmutableSortedSet"; + public const string SystemCollectionsImmutableImmutableSortedSet1 = "System.Collections.Immutable.ImmutableSortedSet`1"; + public const string SystemCollectionsObjectModelReadOnlyCollection1 = "System.Collections.ObjectModel.ReadOnlyCollection`1"; + public const string SystemCollectionsObjectModelReadOnlyDictionary2 = "System.Collections.ObjectModel.ReadOnlyDictionary`2"; + public const string SystemCollectionsObjectModelReadOnlyObservableCollection1 = "System.Collections.ObjectModel.ReadOnlyObservableCollection`1"; + public const string SystemCollectionsQueue = "System.Collections.Queue"; + public const string SystemCollectionsSpecializedNameValueCollection = "System.Collections.Specialized.NameValueCollection"; + public const string SystemCollectionsStack = "System.Collections.Stack"; + public const string SystemComponentModelComponent = "System.ComponentModel.Component"; + public const string SystemComponentModelCompositionExportAttribute = "System.ComponentModel.Composition.ExportAttribute"; + public const string SystemComponentModelCompositionImportingConstructorAttribute = "System.ComponentModel.Composition.ImportingConstructorAttribute"; + public const string SystemComponentModelCompositionInheritedExportAttribute = "System.ComponentModel.Composition.InheritedExportAttribute"; + public const string SystemComponentModelDesignerAttribute = "System.ComponentModel.DesignerAttribute"; + public const string SystemComponentModelDesignerCategoryAttribute = "System.ComponentModel.DesignerCategoryAttribute"; + public const string SystemComponentModelLocalizableAttribute = "System.ComponentModel.LocalizableAttribute"; + public const string SystemCompositionExportAttribute = "System.Composition.ExportAttribute"; + public const string SystemCompositionImportingConstructorAttribute = "System.Composition.ImportingConstructorAttribute"; + public const string SystemConfigurationConfigurationSection = "System.Configuration.ConfigurationSection"; + public const string SystemConfigurationIConfigurationSectionHandler = "System.Configuration.IConfigurationSectionHandler"; + public const string SystemConsole = "System.Console"; + public const string SystemConvert = "System.Convert"; + public const string SystemDataDataRow = "System.Data.DataRow"; + public const string SystemDataDataSet = "System.Data.DataSet"; + public const string SystemDataDataTable = "System.Data.DataTable"; + public const string SystemDataDataViewManager = "System.Data.DataViewManager"; + public const string SystemDataEntityDbSet1 = "System.Data.Entity.DbSet`1"; + public const string SystemDataEntityQueryableExtensions = "System.Data.Entity.QueryableExtensions"; + public const string SystemDataIDataAdapter = "System.Data.IDataAdapter"; + public const string SystemDataIDbCommand = "System.Data.IDbCommand"; + public const string SystemDataOdbcOdbcParameter = "System.Data.Odbc.OdbcParameter"; + public const string SystemDataOleDbOleDbParameter = "System.Data.OleDb.OleDbParameter"; + public const string SystemDataSqlClientSqlParameter = "System.Data.SqlClient.SqlParameter"; + public const string SystemDataTypedTableBase1 = "System.Data.TypedTableBase`1"; + public const string SystemDateTime = "System.DateTime"; + public const string SystemDateTimeOffset = "System.DateTimeOffset"; + public const string SystemDecimal = "System.Decimal"; + public const string SystemDiagnosticContractsContract = "System.Diagnostics.Contracts.Contract"; + public const string SystemDiagnosticsCodeAnalysisConstantExpectedAttribute = "System.Diagnostics.CodeAnalysis.ConstantExpectedAttribute"; + public const string SystemDiagnosticsCodeAnalysisNotNullAttribute = "System.Diagnostics.CodeAnalysis.NotNullAttribute"; + public const string SystemDiagnosticsCodeAnalysisNotNullIfNotNullAttribute = "System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute"; + public const string SystemDiagnosticsCodeAnalysisStringSyntaxAttributeName = "System.Diagnostics.CodeAnalysis.StringSyntaxAttribute"; + public const string SystemDiagnosticsConditionalAttribute = "System.Diagnostics.ConditionalAttribute"; + public const string SystemDiagnosticsContractsPureAttribute = "System.Diagnostics.Contracts.PureAttribute"; + public const string SystemDiagnosticsDebug = "System.Diagnostics.Debug"; + public const string SystemDiagnosticsDebuggerNonUserCode = "System.Diagnostics.DebuggerNonUserCodeAttribute"; + public const string SystemDiagnosticsDebuggerTypeProxyAttribute = "System.Diagnostics.DebuggerTypeProxyAttribute"; + public const string SystemDiagnosticsProcess = "System.Diagnostics.Process"; + public const string SystemDiagnosticsProcessModule = "System.Diagnostics.ProcessModule"; + public const string SystemDiagnosticsProcessStartInfo = "System.Diagnostics.ProcessStartInfo"; + public const string SystemDiagnosticsTraceListener = "System.Diagnostics.TraceListener"; + public const string SystemDiagnosticsTracingEventSource = "System.Diagnostics.Tracing.EventSource"; + public const string SystemDiagnosticsUnreachableException = "System.Diagnostics.UnreachableException"; + public const string SystemDirectoryDirectoryEntry = "System.DirectoryServices.DirectoryEntry"; + public const string SystemDirectoryServicesActiveDirectoryADSearcher = "System.DirectoryServices.ActiveDirectory.ADSearcher"; + public const string SystemDirectoryServicesDirectorySearcher = "System.DirectoryServices.DirectorySearcher"; + public const string SystemDouble = "System.Double"; + public const string SystemEnvironment = "System.Environment"; + public const string SystemEventArgs = "System.EventArgs"; + public const string SystemEventHandler1 = "System.EventHandler`1"; + public const string SystemException = "System.Exception"; + public const string SystemExecutionEngineException = "System.ExecutionEngineException"; + public const string SystemFlagsAttribute = "System.FlagsAttribute"; + public const string SystemFunc2 = "System.Func`2"; + public const string SystemGC = "System.GC"; + public const string SystemGlobalizationCultureInfo = "System.Globalization.CultureInfo"; + public const string SystemGuid = "System.Guid"; + public const string SystemHashCode = "System.HashCode"; + public const string SystemIAsyncDisposable = "System.IAsyncDisposable"; + public const string SystemIComparable = "System.IComparable"; + public const string SystemIComparable1 = "System.IComparable`1"; + public const string SystemIDisposable = "System.IDisposable"; + public const string SystemIEquatable1 = "System.IEquatable`1"; + public const string SystemIFormatProvider = "System.IFormatProvider"; + public const string SystemIndex = "System.Index"; + public const string SystemInt16 = "System.Int16"; + public const string SystemInt32 = "System.Int32"; + public const string SystemInt64 = "System.Int64"; + public const string SystemInvalidOperationException = "System.InvalidOperationException"; + public const string SystemIOCompressionZipArchiveEntry = "System.IO.Compression.ZipArchiveEntry"; + public const string SystemIOCompressionZipFileExtensions = "System.IO.Compression.ZipFileExtensions"; + public const string SystemIODirectory = "System.IO.Directory"; + public const string SystemIODirectoryInfo = "System.IO.DirectoryInfo"; + public const string SystemIOFile = "System.IO.File"; + public const string SystemIOFileInfo = "System.IO.FileInfo"; + public const string SystemIOFileStream = "System.IO.FileStream"; + public const string SystemIOLogLogStore = "System.IO.Log.LogStore"; + public const string SystemIOMemoryStream = "System.IO.MemoryStream"; + public const string SystemIOPath = "System.IO.Path"; + public const string SystemIOStream = "System.IO.Stream"; + public const string SystemIOStreamReader = "System.IO.StreamReader"; + public const string SystemIOStringReader = "System.IO.StringReader"; + public const string SystemIOUnmanagedMemoryStream = "System.IO.UnmanagedMemoryStream"; + public const string SystemIParsable1 = "System.IParsable`1"; + public const string SystemIProgress1 = "System.IProgress`1"; + public const string SystemLinqEnumerable = "System.Linq.Enumerable"; + public const string SystemLinqExpressionsExpression1 = "System.Linq.Expressions.Expression`1"; + public const string SystemLinqIOrderedEnumerable1 = "System.Linq.IOrderedEnumerable`1"; + public const string SystemLinqQueryable = "System.Linq.Queryable"; + public const string SystemMarshalByRefObject = "System.MarshalByRefObject"; + public const string SystemMemory1 = "System.Memory`1"; + public const string SystemMemoryExtensions = "System.MemoryExtensions"; + public const string SystemNetHttpHttpClient = "System.Net.Http.HttpClient"; + public const string SystemNetHttpHttpClientHandler = "System.Net.Http.HttpClientHandler"; + public const string SystemNetHttpSocketsHttpHandler = "System.Net.Http.SocketsHttpHandler"; + public const string SystemNetHttpWinHttpHandler = "System.Net.Http.WinHttpHandler"; + public const string SystemNetSecurityProtocolType = "System.Net.SecurityProtocolType"; + public const string SystemNetSecurityRemoteCertificateValidationCallback = "System.Net.Security.RemoteCertificateValidationCallback"; + public const string SystemNetSecuritySslPolicyErrors = "System.Net.Security.SslPolicyErrors"; + public const string SystemNetServicePointManager = "System.Net.ServicePointManager"; + public const string SystemNonSerializedAttribute = "System.NonSerializedAttribute"; + public const string SystemNotImplementedException = "System.NotImplementedException"; + public const string SystemNotSupportedException = "System.NotSupportedException"; + public const string SystemNullable1 = "System.Nullable`1"; + public const string SystemNumber = "System.Number"; + public const string SystemNumericsINumber1 = "System.Numerics.INumber`1"; + public const string SystemObject = "System.Object"; + public const string SystemObjectDisposedException = "System.ObjectDisposedException"; + public const string SystemObsoleteAttribute = "System.ObsoleteAttribute"; + public const string SystemOperatingSystem = "System.OperatingSystem"; + public const string SystemOperationCanceledException = "System.OperationCanceledException"; + public const string SystemOutOfMemoryException = "System.OutOfMemoryException"; + public const string SystemPlatformNotSupportedException = "System.PlatformNotSupportedException"; + public const string SystemRandom = "System.Random"; + public const string SystemRange = "System.Range"; + public const string SystemReadOnlyMemory1 = "System.ReadOnlyMemory`1"; + public const string SystemReadOnlySpan1 = "System.ReadOnlySpan`1"; + public const string SystemReflectionAssembly = "System.Reflection.Assembly"; + public const string SystemReflectionAssemblyName = "System.Reflection.AssemblyName"; + public const string SystemReflectionAssemblyVersionAttribute = "System.Reflection.AssemblyVersionAttribute"; + public const string SystemReflectionMemberInfo = "System.Reflection.MemberInfo"; + public const string SystemReflectionParameterInfo = "System.Reflection.ParameterInfo"; + public const string SystemResourcesNeutralResourcesLanguageAttribute = "System.Resources.NeutralResourcesLanguageAttribute"; + public const string SystemResourcesResourceManager = "System.Resources.ResourceManager"; + public const string SystemRuntimeCompilerServicesAsyncMethodBuilderAttribute = "System.Runtime.CompilerServices.AsyncMethodBuilderAttribute"; + public const string SystemRuntimeCompilerServicesCallerFilePathAttribute = "System.Runtime.CompilerServices.CallerFilePathAttribute"; + public const string SystemRuntimeCompilerServicesCallerLineNumberAttribute = "System.Runtime.CompilerServices.CallerLineNumberAttribute"; + public const string SystemRuntimeCompilerServicesCallerMemberNameAttribute = "System.Runtime.CompilerServices.CallerMemberNameAttribute"; + public const string SystemRuntimeCompilerServicesCallerArgumentExpressionAttribute = "System.Runtime.CompilerServices.CallerArgumentExpressionAttribute"; + public const string SystemRuntimeCompilerServicesCompilerGeneratedAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute"; + public const string SystemRuntimeCompilerServicesConfiguredAsyncDisposable = "System.Runtime.CompilerServices.ConfiguredAsyncDisposable"; + public const string SystemRuntimeCompilerServicesConfiguredCancelableAsyncEnumerable = "System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1"; + public const string SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable = "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable"; + public const string SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable1 = "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1"; + public const string SystemRuntimeCompilerServicesDisableRuntimeMarshallingAttribute = "System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute"; + public const string SystemRuntimeCompilerServicesICriticalNotifyCompletion = "System.Runtime.CompilerServices.ICriticalNotifyCompletion"; + public const string SystemRuntimeCompilerServicesInlineArrayAttribute = "System.Runtime.CompilerServices.InlineArrayAttribute"; + public const string SystemRuntimeCompilerServicesINotifyCompletion = "System.Runtime.CompilerServices.INotifyCompletion"; + public const string SystemRuntimeCompilerServicesInternalsVisibleToAttribute = "System.Runtime.CompilerServices.InternalsVisibleToAttribute"; + public const string SystemRuntimeCompilerServicesMethodImplOptions = "System.Runtime.CompilerServices.MethodImplOptions"; + public const string SystemRuntimeCompilerServicesModuleInitializerAttribute = "System.Runtime.CompilerServices.ModuleInitializerAttribute"; + public const string SystemRuntimeCompilerServicesRestrictedInternalsVisibleToAttribute = "System.Runtime.CompilerServices.RestrictedInternalsVisibleToAttribute"; + public const string SystemRuntimeCompilerServicesRuntimeFeature = "System.Runtime.CompilerServices.RuntimeFeature"; + public const string SystemRuntimeCompilerServicesTaskAwaiter = "System.Runtime.CompilerServices.TaskAwaiter"; + public const string SystemRuntimeCompilerServicesTypeForwardedToAttribute = "System.Runtime.CompilerServices.TypeForwardedToAttribute"; + public const string SystemRuntimeCompilerServicesValueTaskAwaiter = "System.Runtime.CompilerServices.ValueTaskAwaiter"; + public const string SystemRuntimeExceptionServicesHandleProcessCorruptedStateExceptionsAttribute = "System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptionsAttribute"; + public const string SystemRuntimeInteropServicesCharSet = "System.Runtime.InteropServices.CharSet"; + public const string SystemRuntimeInteropServicesCoClassAttribute = "System.Runtime.InteropServices.CoClassAttribute"; + public const string SystemRuntimeInteropServicesComImportAttribute = "System.Runtime.InteropServices.ComImportAttribute"; + public const string SystemRuntimeInteropServicesComSourceInterfacesAttribute = "System.Runtime.InteropServices.ComSourceInterfacesAttribute"; + public const string SystemRuntimeInteropServicesComVisibleAttribute = "System.Runtime.InteropServices.ComVisibleAttribute"; + public const string SystemRuntimeInteropServicesDefaultDllImportSearchPathsAttribute = "System.Runtime.InteropServices.DefaultDllImportSearchPathsAttribute"; + public const string SystemRuntimeInteropServicesDllImportAttribute = "System.Runtime.InteropServices.DllImportAttribute"; + public const string SystemRuntimeInteropServicesDynamicInterfaceCastableImplementationAttribute = "System.Runtime.InteropServices.DynamicInterfaceCastableImplementationAttribute"; + public const string SystemRuntimeInteropServicesFieldOffsetAttribute = "System.Runtime.InteropServices.FieldOffsetAttribute"; + public const string SystemRuntimeInteropServicesGCHandle = "System.Runtime.InteropServices.GCHandle"; + public const string SystemRuntimeInteropServicesHandleRef = "System.Runtime.InteropServices.HandleRef"; + public const string SystemRuntimeInteropServicesLCIDConversionAttribute = "System.Runtime.InteropServices.LCIDConversionAttribute"; + public const string SystemRuntimeInteropServicesLibraryImportAttribute = "System.Runtime.InteropServices.LibraryImportAttribute"; + public const string SystemRuntimeInteropServicesMarshal = "System.Runtime.InteropServices.Marshal"; + public const string SystemRuntimeInteropServicesMarshalAsAttribute = "System.Runtime.InteropServices.MarshalAsAttribute"; + public const string SystemRuntimeInteropServicesOSPlatform = "System.Runtime.InteropServices.OSPlatform"; + public const string SystemRuntimeInteropServicesOutAttribute = "System.Runtime.InteropServices.OutAttribute"; + public const string SystemRuntimeInteropServicesRuntimeInformation = "System.Runtime.InteropServices.RuntimeInformation"; + public const string SystemRuntimeInteropServicesSafeHandle = "System.Runtime.InteropServices.SafeHandle"; + public const string SystemRuntimeInteropServicesStructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute"; + public const string SystemRuntimeInteropServicesUnmanagedFunctionPoitnerAttribute = "System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute"; + public const string SystemRuntimeInteropServicesUnmanagedType = "System.Runtime.InteropServices.UnmanagedType"; + public const string SystemRuntimeSerializationDataContractAttribute = "System.Runtime.Serialization.DataContractAttribute"; + public const string SystemRuntimeSerializationDataContractSerializer = "System.Runtime.Serialization.DataContractSerializer"; + public const string SystemRuntimeSerializationDataMemberAttribute = "System.Runtime.Serialization.DataMemberAttribute"; + public const string SystemRuntimeSerializationFormattersBinaryBinaryFormatter = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter"; + public const string SystemRuntimeSerializationFormattersSoapSoapFormatter = "System.Runtime.Serialization.Formatters.Soap.SoapFormatter"; + public const string SystemRuntimeSerializationIDeserializationCallback = "System.Runtime.Serialization.IDeserializationCallback"; + public const string SystemRuntimeSerializationIgnoreDataMemberAttribute = "System.Runtime.Serialization.IgnoreDataMemberAttribute"; + public const string SystemRuntimeSerializationISerializable = "System.Runtime.Serialization.ISerializable"; + public const string SystemRuntimeSerializationJsonDataContractJsonSerializer = "System.Runtime.Serialization.Json"; + public const string SystemRuntimeSerializationKnownTypeAttribute = "System.Runtime.Serialization.KnownTypeAttribute"; + public const string SystemRuntimeSerializationNetDataContractSerializer = "System.Runtime.Serialization.NetDataContractSerializer"; + public const string SystemRuntimeSerializationOnDeserializedAttribute = "System.Runtime.Serialization.OnDeserializedAttribute"; + public const string SystemRuntimeSerializationOnDeserializingAttribute = "System.Runtime.Serialization.OnDeserializingAttribute"; + public const string SystemRuntimeSerializationOnSerializedAttribute = "System.Runtime.Serialization.OnSerializedAttribute"; + public const string SystemRuntimeSerializationOnSerializingAttribute = "System.Runtime.Serialization.OnSerializingAttribute"; + public const string SystemRuntimeSerializationSerializationInfo = "System.Runtime.Serialization.SerializationInfo"; + public const string SystemRuntimeSerializationStreamingContext = "System.Runtime.Serialization.StreamingContext"; + public const string SystemRuntimeVersioningRequiresPreviewFeaturesAttribute = "System.Runtime.Versioning.RequiresPreviewFeaturesAttribute"; + public const string SystemRuntimeVersioningSupportedOSPlatformAttribute = "System.Runtime.Versioning.SupportedOSPlatformAttribute"; + public const string SystemRuntimeVersioningTargetFrameworkAttribute = "System.Runtime.Versioning.TargetFrameworkAttribute"; + public const string SystemRuntimeVersioningUnsupportedOSPlatformAttribute = "System.Runtime.Versioning.UnsupportedOSPlatformAttribute"; + public const string SystemSecurityAuthenticationSslProtocols = "System.Security.Authentication.SslProtocols"; + public const string SystemSecurityCryptographyAesCcm = "System.Security.Cryptography.AesCcm"; + public const string SystemSecurityCryptographyAesGcm = "System.Security.Cryptography.AesGcm"; + public const string SystemSecurityCryptographyAsymmetricAlgorithm = "System.Security.Cryptography.AsymmetricAlgorithm"; + public const string SystemSecurityCryptographyCipherMode = "System.Security.Cryptography.CipherMode"; + public const string SystemSecurityCryptographyCryptoConfig = "System.Security.Cryptography.CryptoConfig"; + public const string SystemSecurityCryptographyDES = "System.Security.Cryptography.DES"; + public const string SystemSecurityCryptographyDSA = "System.Security.Cryptography.DSA"; + public const string SystemSecurityCryptographyDSASignatureFormatter = "System.Security.Cryptography.DSASignatureFormatter"; + public const string SystemSecurityCryptographyHashAlgorithm = "System.Security.Cryptography.HashAlgorithm"; + public const string SystemSecurityCryptographyHashAlgorithmName = "System.Security.Cryptography.HashAlgorithmName"; + public const string SystemSecurityCryptographyHMACMD5 = "System.Security.Cryptography.HMACMD5"; + public const string SystemSecurityCryptographyHMACRIPEMD160 = "System.Security.Cryptography.HMACRIPEMD160"; + public const string SystemSecurityCryptographyHMACSHA1 = "System.Security.Cryptography.HMACSHA1"; + public const string SystemSecurityCryptographyMD5 = "System.Security.Cryptography.MD5"; + public const string SystemSecurityCryptographyPasswordDeriveBytes = "System.Security.Cryptography.PasswordDeriveBytes"; + public const string SystemSecurityCryptographyRC2 = "System.Security.Cryptography.RC2"; + public const string SystemSecurityCryptographyRfc2898DeriveBytes = "System.Security.Cryptography.Rfc2898DeriveBytes"; + public const string SystemSecurityCryptographyRIPEMD160 = "System.Security.Cryptography.RIPEMD160"; + public const string SystemSecurityCryptographyRSA = "System.Security.Cryptography.RSA"; + public const string SystemSecurityCryptographySHA1 = "System.Security.Cryptography.SHA1"; + public const string SystemSecurityCryptographySHA256 = "System.Security.Cryptography.SHA256"; + public const string SystemSecurityCryptographySymmetricAlgorithm = "System.Security.Cryptography.SymmetricAlgorithm"; + public const string SystemSecurityCryptographyTripleDES = "System.Security.Cryptography.TripleDES"; + public const string SystemSecurityCryptographyX509CertificatesStoreName = "System.Security.Cryptography.X509Certificates.StoreName"; + public const string SystemSecurityCryptographyX509CertificatesX509Certificate = "System.Security.Cryptography.X509Certificates.X509Certificate"; + public const string SystemSecurityCryptographyX509CertificatesX509Certificate2 = "System.Security.Cryptography.X509Certificates.X509Certificate2"; + public const string SystemSecurityCryptographyX509CertificatesX509Chain = "System.Security.Cryptography.X509Certificates.X509Chain"; + public const string SystemSecurityCryptographyX509CertificatesX509Store = "System.Security.Cryptography.X509Certificates.X509Store"; + public const string SystemSecurityIPermission = "System.Security.IPermission"; + public const string SystemSecurityPolicyIMembershipCondition = "System.Security.Policy.IMembershipCondition"; + public const string SystemSerializableAttribute = "System.SerializableAttribute"; + public const string SystemServiceModelMessageContractAttribute = "System.ServiceModel.MessageContractAttribute"; + public const string SystemServiceModelOperationContractAttribute = "System.ServiceModel.OperationContractAttribute"; + public const string SystemSingle = "System.Single"; + public const string SystemSpan1 = "System.Span`1"; + public const string SystemStackOverflowException = "System.StackOverflowException"; + public const string SystemString = "System.String"; + public const string SystemStringComparer = "System.StringComparer"; + public const string SystemStringComparison = "System.StringComparison"; + public const string SystemSystemException = "System.SystemException"; + public const string SystemTextCompositeFormat = "System.Text.CompositeFormat"; + public const string SystemTextEncoding = "System.Text.Encoding"; + public const string SystemTextJsonJsonSerializerOptions = "System.Text.Json.JsonSerializerOptions"; + public const string SystemTextJsonJsonSerializer = "System.Text.Json.JsonSerializer"; + public const string SystemTextRegularExpressionsRegex = "System.Text.RegularExpressions.Regex"; + public const string SystemTextStringBuilder = "System.Text.StringBuilder"; + public const string SystemThreadStaticAttribute = "System.ThreadStaticAttribute"; + public const string SystemThreadingCancellationToken = "System.Threading.CancellationToken"; + public const string SystemThreadingInterlocked = "System.Threading.Interlocked"; + public const string SystemThreadingMonitor = "System.Threading.Monitor"; + public const string SystemThreadingSemaphoreSlim = "System.Threading.SemaphoreSlim"; + public const string SystemThreadingSpinLock = "System.Threading.SpinLock"; + public const string SystemThreadingTasksConfigureAwaitOptions = "System.Threading.Tasks.ConfigureAwaitOptions"; + public const string SystemThreadingTasksTaskAsyncEnumerableExtensions = "System.Threading.Tasks.TaskAsyncEnumerableExtensions"; + public const string SystemThreadingTasksTask = "System.Threading.Tasks.Task"; + public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; + public const string SystemThreadingTasksTaskCompletionSource = "System.Threading.Tasks.TaskCompletionSource"; + public const string SystemThreadingTasksTaskCompletionSource1 = "System.Threading.Tasks.TaskCompletionSource`1"; + public const string SystemThreadingTasksTaskContinuationOptions = "System.Threading.Tasks.TaskContinuationOptions"; + public const string SystemThreadingTasksTaskCreationOptions = "System.Threading.Tasks.TaskCreationOptions"; + public const string SystemThreadingTasksTaskFactory = "System.Threading.Tasks.TaskFactory"; + public const string SystemThreadingTasksTaskScheduler = "System.Threading.Tasks.TaskScheduler"; + public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; + public const string SystemThreadingTasksValueTask1 = "System.Threading.Tasks.ValueTask`1"; + public const string SystemThreadingThread = "System.Threading.Thread"; + public const string SystemThreadingVolatile = "System.Threading.Volatile"; + public const string SystemTimeSpan = "System.TimeSpan"; + public const string SystemType = "System.Type"; + public const string SystemUri = "System.Uri"; + public const string SystemWebConfigurationHttpRuntimeSection = "System.Web.Configuration.HttpRuntimeSection"; + public const string SystemWebHttpApplication = "System.Web.HttpApplication"; + public const string SystemWebHttpCookie = "System.Web.HttpCookie"; + public const string SystemWebHttpRequest = "System.Web.HttpRequest"; + public const string SystemWebHttpRequestBase = "System.Web.HttpRequestBase"; + public const string SystemWebHttpRequestWrapper = "System.Web.HttpRequestWrapper"; + public const string SystemWebHttpResponse = "System.Web.HttpResponse"; + public const string SystemWebHttpResponseBase = "System.Web.HttpResponseBase"; + public const string SystemWebHttpRouteAttribute = "System.Web.Http.RouteAttribute"; + public const string SystemWebHttpServerUtility = "System.Web.HttpServerUtility"; + public const string SystemWebHttpServerUtilityBase = "System.Web.HttpServerUtilityBase"; + public const string SystemWebHttpServerUtilityWrapper = "System.Web.HttpServerUtilityWrapper"; + public const string SystemWebHttpUtility = "System.Web.HttpUtility"; + public const string SystemWebMvcAcceptVerbsAttribute = "System.Web.Mvc.AcceptVerbsAttribute"; + public const string SystemWebMvcActionResult = "System.Web.Mvc.ActionResult"; + public const string SystemWebMvcChildActionOnlyAttribute = "System.Web.Mvc.ChildActionOnlyAttribute"; + public const string SystemWebMvcController = "System.Web.Mvc.Controller"; + public const string SystemWebMvcControllerBase = "System.Web.Mvc.ControllerBase"; + public const string SystemWebMvcHttpDeleteAttribute = "System.Web.Mvc.HttpDeleteAttribute"; + public const string SystemWebMvcHttpGetAttribute = "System.Web.Mvc.HttpGetAttribute"; + public const string SystemWebMvcHttpHeadAttribute = "System.Web.Mvc.HttpHeadAttribute"; + public const string SystemWebMvcHttpOptionsAttribute = "System.Web.Mvc.HttpOptionsAttribute"; + public const string SystemWebMvcHttpPatchAttribute = "System.Web.Mvc.HttpPatchAttribute"; + public const string SystemWebMvcHttpPostAttribute = "System.Web.Mvc.HttpPostAttribute"; + public const string SystemWebMvcHttpPutAttribute = "System.Web.Mvc.HttpPutAttribute"; + public const string SystemWebMvcHttpVerbs = "System.Web.Mvc.HttpVerbs"; + public const string SystemWebMvcNonActionAttribute = "System.Web.Mvc.NonActionAttribute"; + public const string SystemWebMvcValidateAntiForgeryTokenAttribute = "System.Web.Mvc.ValidateAntiForgeryTokenAttribute"; + public const string SystemWebMvcValidateInputAttribute = "System.Web.Mvc.ValidateInputAttribute"; + public const string SystemWebScriptSerializationJavaScriptSerializer = "System.Web.Script.Serialization.JavaScriptSerializer"; + public const string SystemWebScriptSerializationJavaScriptTypeResolver = "System.Web.Script.Serialization.JavaScriptTypeResolver"; + public const string SystemWebScriptSerializationSimpleTypeResolver = "System.Web.Script.Serialization.SimpleTypeResolver"; + public const string SystemWebSecurityAntiXssAntiXssEncoder = "System.Web.Security.AntiXss.AntiXssEncoder"; + public const string SystemWebSecurityAntiXssUnicodeCharacterEncoder = "System.Web.Security.AntiXss.UnicodeCharacterEncoder"; + public const string SystemWebServicesWebMethodAttribute = "System.Web.Services.WebMethodAttribute"; + public const string SystemWebUIAdaptersPageAdapter = "System.Web.UI.Adapters.PageAdapter"; + public const string SystemWebUIControl = "System.Web.UI.Control"; + public const string SystemWebUIDataBoundLiteralControl = "System.Web.UI.DataBoundLiteralControl"; + public const string SystemWebUIDesignerDataBoundLiteralControl = "System.Web.UI.DesignerDataBoundLiteralControl"; + public const string SystemWebUIHtmlControlsHtmlContainerControl = "System.Web.UI.HtmlControls.HtmlContainerControl"; + public const string SystemWebUIHtmlControlsHtmlInputControl = "System.Web.UI.HtmlControls.HtmlInputControl"; + public const string SystemWebUIHtmlControlsHtmlTitle = "System.Web.UI.HtmlControls.HtmlTitle"; + public const string SystemWebUIHtmlTextWriter = "System.Web.UI.HtmlTextWriter"; + public const string SystemWebUIIndexedString = "System.Web.UI.IndexedString"; + public const string SystemWebUIITextControl = "System.Web.UI.ITextControl"; + public const string SystemWebUILiteralControl = "System.Web.UI.LiteralControl"; + public const string SystemWebUILosFormatter = "System.Web.UI.LosFormatter"; + public const string SystemWebUIObjectStateFormatter = "System.Web.UI.ObjectStateFormatter"; + public const string SystemWebUIPage = "System.Web.UI.Page"; + public const string SystemWebUIPageTheme = "System.Web.UI.PageTheme"; + public const string SystemWebUIResourceBasedLiteralControl = "System.Web.UI.ResourceBasedLiteralControl"; + public const string SystemWebUISimplePropertyEntry = "System.Web.UI.SimplePropertyEntry"; + public const string SystemWebUIStateItem = "System.Web.UI.StateItem"; + public const string SystemWebUIStringPropertyBuilder = "System.Web.UI.StringPropertyBuilder"; + public const string SystemWebUITemplateBuilder = "System.Web.UI.TemplateBuilder"; + public const string SystemWebUITemplateControl = "System.Web.UI.TemplateControl"; + public const string SystemWebUITemplateParser = "System.Web.UI.TemplateParser"; + public const string SystemWebUIWebControlsBaseDataList = "System.Web.UI.WebControls.BaseDataList"; + public const string SystemWebUIWebControlsBaseValidator = "System.Web.UI.WebControls.BaseValidator"; + public const string SystemWebUIWebControlsBulletedList = "System.Web.UI.WebControls.BulletedList"; + public const string SystemWebUIWebControlsButton = "System.Web.UI.WebControls.Button"; + public const string SystemWebUIWebControlsButtonColumn = "System.Web.UI.WebControls.ButtonColumn"; + public const string SystemWebUIWebControlsButtonField = "System.Web.UI.WebControls.ButtonField"; + public const string SystemWebUIWebControlsCalendar = "System.Web.UI.WebControls.Calendar"; + public const string SystemWebUIWebControlsChangePassword = "System.Web.UI.WebControls.ChangePassword"; + public const string SystemWebUIWebControlsCheckBox = "System.Web.UI.WebControls.CheckBox"; + public const string SystemWebUIWebControlsCheckBoxField = "System.Web.UI.WebControls.CheckBoxField"; + public const string SystemWebUIWebControlsCommandEventArgs = "System.Web.UI.WebControls.CommandEventArgs"; + public const string SystemWebUIWebControlsCreateUserWizard = "System.Web.UI.WebControls.CreateUserWizard"; + public const string SystemWebUIWebControlsDataKey = "System.Web.UI.WebControls.DataKey"; + public const string SystemWebUIWebControlsDataList = "System.Web.UI.WebControls.DataList"; + public const string SystemWebUIWebControlsDetailsView = "System.Web.UI.WebControls.DetailsView"; + public const string SystemWebUIWebControlsDetailsViewInsertEventArgs = "System.Web.UI.WebControls.DetailsViewInsertEventArgs"; + public const string SystemWebUIWebControlsDetailsViewUpdateEventArgs = "System.Web.UI.WebControls.DetailsViewUpdateEventArgs"; + public const string SystemWebUIWebControlsFormView = "System.Web.UI.WebControls.FormView"; + public const string SystemWebUIWebControlsFormViewInsertEventArgs = "System.Web.UI.WebControls.FormViewInsertEventArgs"; + public const string SystemWebUIWebControlsFormViewUpdateEventArgs = "System.Web.UI.WebControls.FormViewUpdateEventArgs"; + public const string SystemWebUIWebControlsGridView = "System.Web.UI.WebControls.GridView"; + public const string SystemWebUIWebControlsHiddenField = "System.Web.UI.WebControls.HiddenField"; + public const string SystemWebUIWebControlsHyperLink = "System.Web.UI.WebControls.HyperLink"; + public const string SystemWebUIWebControlsHyperLinkColumn = "System.Web.UI.WebControls.HyperLinkColumn"; + public const string SystemWebUIWebControlsHyperLinkField = "System.Web.UI.WebControls.HyperLinkField"; + public const string SystemWebUIWebControlsImage = "System.Web.UI.WebControls.Image"; + public const string SystemWebUIWebControlsImageButton = "System.Web.UI.WebControls.ImageButton"; + public const string SystemWebUIWebControlsLabel = "System.Web.UI.WebControls.Label"; + public const string SystemWebUIWebControlsLinkButton = "System.Web.UI.WebControls.LinkButton"; + public const string SystemWebUIWebControlsListControl = "System.Web.UI.WebControls.ListControl"; + public const string SystemWebUIWebControlsListItem = "System.Web.UI.WebControls.ListItem"; + public const string SystemWebUIWebControlsLiteral = "System.Web.UI.WebControls.Literal"; + public const string SystemWebUIWebControlsLogin = "System.Web.UI.WebControls.Login"; + public const string SystemWebUIWebControlsMenu = "System.Web.UI.WebControls.Menu"; + public const string SystemWebUIWebControlsMenuItem = "System.Web.UI.WebControls.MenuItem"; + public const string SystemWebUIWebControlsMenuItemBinding = "System.Web.UI.WebControls.MenuItemBinding"; + public const string SystemWebUIWebControlsPasswordRecovery = "System.Web.UI.WebControls.PasswordRecovery"; + public const string SystemWebUIWebControlsQueryStringParameter = "System.Web.UI.WebControls.QueryStringParameter"; + public const string SystemWebUIWebControlsRadioButtonList = "System.Web.UI.WebControls.RadioButtonList"; + public const string SystemWebUIWebControlsRepeatInfo = "System.Web.UI.WebControls.RepeatInfo"; + public const string SystemWebUIWebControlsServerValidateEventArgs = "System.Web.UI.WebControls.ServerValidateEventArgs"; + public const string SystemWebUIWebControlsSqlDataSource = "System.Web.UI.WebControls.SqlDataSource"; + public const string SystemWebUIWebControlsTable = "System.Web.UI.WebControls.Table"; + public const string SystemWebUIWebControlsTableCell = "System.Web.UI.WebControls.TableCell"; + public const string SystemWebUIWebControlsTextBox = "System.Web.UI.WebControls.TextBox"; + public const string SystemWebUIWebControlsTreeNode = "System.Web.UI.WebControls.TreeNode"; + public const string SystemWebUIWebControlsTreeNodeBinding = "System.Web.UI.WebControls.TreeNodeBinding"; + public const string SystemWebUIWebControlsTreeView = "System.Web.UI.WebControls.TreeView"; + public const string SystemWebUIWebControlsUnit = "System.Web.UI.WebControls.Unit"; + public const string SystemWebUIWebControlsWebPartsAppearanceEditorPart = "System.Web.UI.WebControls.WebParts.AppearanceEditorPart"; + public const string SystemWebUIWebControlsWebPartsPersonalizationEntry = "System.Web.UI.WebControls.WebParts.PersonalizationEntry"; + public const string SystemWebUIWebControlsWebPartsWebPartCatalogAddVerb = "System.Web.UI.WebControls.WebParts.WebPartCatalogAddVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartCatalogCloseVerb = "System.Web.UI.WebControls.WebParts.WebPartCatalogCloseVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartCloseVerb = "System.Web.UI.WebControls.WebParts.WebPartCloseVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectionsCancelVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectionsCancelVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectionsCloseVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectionsCloseVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectionsConfigureVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectionsConfigureVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectionsConnectVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectionsConnectVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectionsDisconnectVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectionsDisconnectVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartConnectVerb = "System.Web.UI.WebControls.WebParts.WebPartConnectVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartDeleteVerb = "System.Web.UI.WebControls.WebParts.WebPartDeleteVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartEditorApplyVerb = "System.Web.UI.WebControls.WebParts.WebPartEditorApplyVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartEditorCancelVerb = "System.Web.UI.WebControls.WebParts.WebPartEditorCancelVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartEditorOKVerb = "System.Web.UI.WebControls.WebParts.WebPartEditorOKVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartEditVerb = "System.Web.UI.WebControls.WebParts.WebPartEditVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartExportVerb = "System.Web.UI.WebControls.WebParts.WebPartExportVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartHeaderCloseVerb = "System.Web.UI.WebControls.WebParts.WebPartHeaderCloseVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartHelpVerb = "System.Web.UI.WebControls.WebParts.WebPartHelpVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartMinimizeVerb = "System.Web.UI.WebControls.WebParts.WebPartMinimizeVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartRestoreVerb = "System.Web.UI.WebControls.WebParts.WebPartRestoreVerb"; + public const string SystemWebUIWebControlsWebPartsWebPartVerb = "System.Web.UI.WebControls.WebParts.WebPartVerb"; + public const string SystemWebUIWebControlsXmlDataSource = "System.Web.UI.WebControls.XmlDataSource"; + public const string SystemWebUIXPathBinder = "System.Web.UI.XPathBinder"; + public const string SystemWebUtilHttpEncoder = "System.Web.Util.HttpEncoder"; + public const string SystemWindowsAssemblyPart = "System.Windows.AssemblyPart"; + public const string SystemWindowsMarkupXamlReader = "System.Windows.Markup.XamlReader"; + public const string SystemXmlDtdProcessing = "System.Xml.DtdProcessing"; + public const string SystemXmlSchemaXmlSchema = "System.Xml.Schema.XmlSchema"; + public const string SystemXmlSchemaXmlSchemaCollection = "System.Xml.Schema.XmlSchemaCollection"; + public const string SystemXmlSchemaXmlSchemaXPath = "System.Xml.Schema.XmlSchemaXPath"; + public const string SystemXmlSerializationXmlAnyAttributeAttribute = "System.Xml.Serialization.XmlAnyAttributeAttribute"; + public const string SystemXmlSerializationXmlAnyElementAttribute = "System.Xml.Serialization.XmlAnyElementAttribute"; + public const string SystemXmlSerializationXmlArrayAttribute = "System.Xml.Serialization.XmlArrayAttribute"; + public const string SystemXmlSerializationXmlArrayItemAttribute = "System.Xml.Serialization.XmlArrayItemAttribute"; + public const string SystemXmlSerializationXmlAttributeAttribute = "System.Xml.Serialization.XmlAttributeAttribute"; + public const string SystemXmlSerializationXmlChoiceIdentifierAttribute = "System.Xml.Serialization.XmlChoiceIdentifierAttribute"; + public const string SystemXmlSerializationXmlElementAttribute = "System.Xml.Serialization.XmlElementAttribute"; + public const string SystemXmlSerializationXmlEnumAttribute = "System.Xml.Serialization.XmlEnumAttribute"; + public const string SystemXmlSerializationXmlIgnoreAttribute = "System.Xml.Serialization.XmlIgnoreAttribute"; + public const string SystemXmlSerializationXmlIncludeAttribute = "System.Xml.Serialization.XmlIncludeAttribute"; + public const string SystemXmlSerializationXmlRootAttribute = "System.Xml.Serialization.XmlRootAttribute"; + public const string SystemXmlSerializationXmlSerializer = "System.Xml.Serialization.XmlSerializer"; + public const string SystemXmlSerializationXmlTextAttribute = "System.Xml.Serialization.XmlTextAttribute"; + public const string SystemXmlSerializationXmlTypeAttribute = "System.Xml.Serialization.XmlTypeAttribute"; + public const string SystemXmlXmlAttribute = "System.Xml.XmlAttribute"; + public const string SystemXmlXmlDocument = "System.Xml.XmlDocument"; + public const string SystemXmlXmlDocumentFragment = "System.Xml.XmlDocumentFragment"; + public const string SystemXmlXmlElement = "System.Xml.XmlElement"; + public const string SystemXmlXmlEntity = "System.Xml.XmlEntity"; + public const string SystemXmlXmlNode = "System.Xml.XmlNode"; + public const string SystemXmlXmlNotation = "System.Xml.XmlNotation"; + public const string SystemXmlXmlReader = "System.Xml.XmlReader"; + public const string SystemXmlXmlReaderSettings = "System.Xml.XmlReaderSettings"; + public const string SystemXmlXmlResolver = "System.Xml.XmlResolver"; + public const string SystemXmlXmlSecureResolver = "System.Xml.XmlSecureResolver"; + public const string SystemXmlXmlTextReader = "System.Xml.XmlTextReader"; + public const string SystemXmlXmlTextWriter = "System.Xml.XmlTextWriter"; + public const string SystemXmlXmlValidatingReader = "System.Xml.XmlValidatingReader"; + public const string SystemXmlXmlWriter = "System.Xml.XmlWriter"; + public const string SystemXmlXPathXPathDocument = "System.Xml.XPath.XPathDocument"; + public const string SystemXmlXPathXPathExpression = "System.Xml.XPath.XPathExpression"; + public const string SystemXmlXPathXPathNavigator = "System.Xml.XPath.XPathNavigator"; + public const string SystemXmlXslXslCompiledTransform = "System.Xml.Xsl.XslCompiledTransform"; + public const string SystemXmlXslXslTransform = "System.Xml.Xsl.XslTransform"; + public const string SystemXmlXslXsltSettings = "System.Xml.Xsl.XsltSettings"; + public const string XunitAssert = "Xunit.Assert"; + public const string XunitCombinatorialDataAttribute = "Xunit.CombinatorialDataAttribute"; + public const string XunitCombinatorialRangeAttribute = "Xunit.CombinatorialRangeAttribute"; + public const string XunitFactAttribute = "Xunit.FactAttribute"; + public const string XunitSdkDataAttribute = "Xunit.Sdk.DataAttribute"; + public const string XunitTraitAttribute = "Xunit.TraitAttribute"; + + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeProvider.cs b/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeProvider.cs new file mode 100644 index 0000000000000..9956f904fd659 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/WellKnownTypeProvider.cs @@ -0,0 +1,313 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Threading; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Roslyn.Utilities; + +namespace Analyzer.Utilities +{ + /// + /// Provides and caches well known types in a compilation. + /// + public class WellKnownTypeProvider + { + private static readonly BoundedCacheWithFactory s_providerCache = new(); + + private WellKnownTypeProvider(Compilation compilation) + { + Compilation = compilation; + _fullNameToTypeMap = new ConcurrentDictionary(StringComparer.Ordinal); + _referencedAssemblies = new Lazy>( + () => + { + return Compilation.Assembly.Modules + .SelectMany(m => m.ReferencedAssemblySymbols) + .Distinct(SymbolEqualityComparer.Default) + .ToImmutableArray(); + }, + LazyThreadSafetyMode.ExecutionAndPublication); + } + + public static WellKnownTypeProvider GetOrCreate(Compilation compilation) + { + return s_providerCache.GetOrCreateValue(compilation, CreateWellKnownTypeProvider); + + // Local functions + static WellKnownTypeProvider CreateWellKnownTypeProvider(Compilation compilation) => new(compilation); + } + + public Compilation Compilation { get; } + + /// + /// All the referenced assembly symbols. + /// + /// + /// Seems to be less memory intensive than: + /// foreach (Compilation.Assembly.Modules) + /// foreach (Module.ReferencedAssemblySymbols) + /// + private readonly Lazy> _referencedAssemblies; + + /// + /// Mapping of full name to . + /// + private readonly ConcurrentDictionary _fullNameToTypeMap; + +#if !NETSTANDARD1_3 // Assuming we're on .NET Standard 2.0 or later, cache the type names that are probably compile time constants. + /// + /// Static cache of full type names (with namespaces) to namespace name parts, + /// so we can query . + /// + /// + /// Example: "System.Collections.Generic.List`1" => [ "System", "Collections", "Generic" ] + /// + /// https://github.com/dotnet/roslyn/blob/9e786147b8cb884af454db081bb747a5bd36a086/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs#L455 + /// suggests the TypeNames collection can be checked to avoid expensive operations. But realizing TypeNames seems to be + /// as memory intensive as unnecessary calls GetTypeByMetadataName() in some cases. So we'll go with namespace names. + /// + private static readonly ConcurrentDictionary> _fullTypeNameToNamespaceNames = + new(StringComparer.Ordinal); +#endif + + /// + /// Attempts to get the type by the full type name. + /// + /// Namespace + type name, e.g. "System.Exception". + /// Named type symbol, if any. + /// True if found in the compilation, false otherwise. + [PerformanceSensitive("https://github.com/dotnet/roslyn-analyzers/issues/4893", AllowCaptures = false)] + public bool TryGetOrCreateTypeByMetadataName( + string fullTypeName, + [NotNullWhen(returnValue: true)] out INamedTypeSymbol? namedTypeSymbol) + { + if (_fullNameToTypeMap.TryGetValue(fullTypeName, out namedTypeSymbol)) + { + return namedTypeSymbol is not null; + } + + return TryGetOrCreateTypeByMetadataNameSlow(fullTypeName, out namedTypeSymbol); + } + + private bool TryGetOrCreateTypeByMetadataNameSlow( + string fullTypeName, + [NotNullWhen(returnValue: true)] out INamedTypeSymbol? namedTypeSymbol) + { + namedTypeSymbol = _fullNameToTypeMap.GetOrAdd( + fullTypeName, + fullyQualifiedMetadataName => + { + // Caching null results is intended. + + // sharwell says: Suppose you reference assembly A with public API X.Y, and you reference assembly B with + // internal API X.Y. Even though you can use X.Y from assembly A, compilation.GetTypeByMetadataName will + // fail outright because it finds two types with the same name. + + INamedTypeSymbol? type = null; + + ImmutableArray namespaceNames; +#if NETSTANDARD1_3 // Probably in 2.9.x branch; just don't cache. + namespaceNames = GetNamespaceNamesFromFullTypeName(fullTypeName); +#else // Assuming we're on .NET Standard 2.0 or later, cache the type names that are probably compile time constants. + if (string.IsInterned(fullTypeName) != null) + { + namespaceNames = _fullTypeNameToNamespaceNames.GetOrAdd( + fullTypeName, + GetNamespaceNamesFromFullTypeName); + } + else + { + namespaceNames = GetNamespaceNamesFromFullTypeName(fullTypeName); + } +#endif + + if (IsSubsetOfCollection(namespaceNames, Compilation.Assembly.NamespaceNames)) + { + type = Compilation.Assembly.GetTypeByMetadataName(fullyQualifiedMetadataName); + } + + if (type is null) + { + RoslynDebug.Assert(namespaceNames != null); + + foreach (IAssemblySymbol? referencedAssembly in _referencedAssemblies.Value) + { + if (!IsSubsetOfCollection(namespaceNames, referencedAssembly.NamespaceNames)) + { + continue; + } + + var currentType = referencedAssembly.GetTypeByMetadataName(fullyQualifiedMetadataName); + if (currentType is null) + { + continue; + } + + switch (currentType.GetResultantVisibility()) + { + case SymbolVisibility.Public: + case SymbolVisibility.Internal when referencedAssembly.GivesAccessTo(Compilation.Assembly): + break; + + default: + continue; + } + + if (type is object) + { + // Multiple visible types with the same metadata name are present. + return null; + } + + type = currentType; + } + } + + return type; + }); + + return namedTypeSymbol != null; + } + + /// + /// Gets a type by its full type name. + /// + /// Namespace + type name, e.g. "System.Exception". + /// The if found, null otherwise. + public INamedTypeSymbol? GetOrCreateTypeByMetadataName(string fullTypeName) + { + TryGetOrCreateTypeByMetadataName(fullTypeName, out INamedTypeSymbol? namedTypeSymbol); + return namedTypeSymbol; + } + + /// + /// Determines if is a with its type + /// argument satisfying . + /// + /// Type potentially representing a . + /// Predicate to check the 's type argument. + /// True if is a with its + /// type argument satisfying , false otherwise. + internal bool IsTaskOfType([NotNullWhen(returnValue: true)] ITypeSymbol? typeSymbol, Func typeArgumentPredicate) + { + return typeSymbol != null + && typeSymbol.OriginalDefinition != null + && SymbolEqualityComparer.Default.Equals(typeSymbol.OriginalDefinition, + GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1)) + && typeSymbol is INamedTypeSymbol namedTypeSymbol + && namedTypeSymbol.TypeArguments.Length == 1 + && typeArgumentPredicate(namedTypeSymbol.TypeArguments[0]); + } + + private static ImmutableArray GetNamespaceNamesFromFullTypeName(string fullTypeName) + { + using ArrayBuilder namespaceNamesBuilder = ArrayBuilder.GetInstance(); + RoslynDebug.Assert(namespaceNamesBuilder != null); + + int prevStartIndex = 0; + for (int i = 0; i < fullTypeName.Length; i++) + { + if (fullTypeName[i] == '.') + { + namespaceNamesBuilder.Add(fullTypeName[prevStartIndex..i]); + prevStartIndex = i + 1; + } + else if (!IsIdentifierPartCharacter(fullTypeName[i])) + { + break; + } + } + + return namespaceNamesBuilder.ToImmutable(); + } + + /// + /// Returns true if the Unicode character can be a part of an identifier. + /// + /// The Unicode character. + private static bool IsIdentifierPartCharacter(char ch) + { + // identifier-part-character: + // letter-character + // decimal-digit-character + // connecting-character + // combining-character + // formatting-character + + if (ch < 'a') // '\u0061' + { + if (ch < 'A') // '\u0041' + { + return ch is >= '0' // '\u0030' + and <= '9'; // '\u0039' + } + + return ch is <= 'Z' // '\u005A' + or '_'; // '\u005F' + } + + if (ch <= 'z') // '\u007A' + { + return true; + } + + if (ch <= '\u007F') // max ASCII + { + return false; + } + + UnicodeCategory cat = CharUnicodeInfo.GetUnicodeCategory(ch); + + ////return IsLetterChar(cat) + //// || IsDecimalDigitChar(cat) + //// || IsConnectingChar(cat) + //// || IsCombiningChar(cat) + //// || IsFormattingChar(cat); + + return cat switch + { + // Letter + UnicodeCategory.UppercaseLetter + or UnicodeCategory.LowercaseLetter + or UnicodeCategory.TitlecaseLetter + or UnicodeCategory.ModifierLetter + or UnicodeCategory.OtherLetter + or UnicodeCategory.LetterNumber + or UnicodeCategory.DecimalDigitNumber + or UnicodeCategory.ConnectorPunctuation + or UnicodeCategory.NonSpacingMark + or UnicodeCategory.SpacingCombiningMark + or UnicodeCategory.Format => true, + _ => false, + }; + } + + private static bool IsSubsetOfCollection(ImmutableArray set1, ICollection set2) + { + if (set1.Length > set2.Count) + { + return false; + } + + for (int i = 0; i < set1.Length; i++) + { + if (!set2.Contains(set1[i])) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/WordParser.cs b/src/RoslynAnalyzers/Utilities/Compiler/WordParser.cs new file mode 100644 index 0000000000000..7403e0cdc2205 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/WordParser.cs @@ -0,0 +1,670 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Text; + +namespace Analyzer.Utilities +{ + /// + /// Provides methods for parsing words from text. + /// + internal sealed class WordParser + { + // WordParser has two distinct modes; one where it breaks up only words in + // a given piece of text, and the other where it breaks up both words + // and individual compounds within words in a piece of text. Passing + // WordParserOptions.None to the constructor (or Parse) causes it to enter + // the former, and WordParserOptions.SplitCompoundWords the later. + // + // If you simply want to iterate over the words, you can avoid the + // allocation of a Collection if you manually construct WordParser + // and use the NextWord method instead of using the static Parse method. + // + // [char]: Represents any Unicode character + // [A-Z]: Represents any Unicode uppercase letter + // [a-z]: Represents any Unicode lowercase letter + // [0-9]: Represents the numbers 0 to 9 + // [letter]: Represents any Unicode letter + // + // -> ( | )+ + // + // -> ! + // + // -> [char] + // + // WordParserOptions.None: + // -> ([0-9] | [letter])+ + // + // WordParserOptions.SplitCompoundWords: + // -> | | | | + // -> ( | ) + // -> [0-9]+ + // -> [0x]([0-9] | [A-F] | [a-f])+ + // -> ([A-Z]( | )) | + // -> [a-z]+ + // -> ([letter] ![A-Z] ![a-z])+ + // -> [A-Z]+(s) (unless next character is [a-z]) + // -> [#]([0-9] | [A-F] | [a-f])+ + + private const char NullChar = '\0'; + private readonly WordParserOptions _options; + private readonly StringBuilder _buffer; + private readonly string _text; + private string? _peekedWord; + private int _index; + private char _prefix; + + /// + /// Initializes a new instance of the class with the specified text and options. + /// + /// + /// A containing the text to parse. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + public WordParser(string text, WordParserOptions options) : this(text, options, NullChar) + { + } + + /// + /// Initializes a new instance of the class with the specified text, options and prefix. + /// + /// + /// A containing the text to parse. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// A representing an optional prefix of , that if present, + /// will be returned as a separate token. + /// + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + public WordParser(string text, WordParserOptions options, char prefix) + { + if (options is < WordParserOptions.None or > (WordParserOptions.IgnoreMnemonicsIndicators | WordParserOptions.SplitCompoundWords)) + { + throw new ArgumentException($"'{(int)options}' is invalid for enum type '{nameof(WordParserOptions)}'", nameof(options)); + } + + _text = text ?? throw new ArgumentNullException(nameof(text)); + _options = options; + _buffer = new StringBuilder(text.Length); + _prefix = prefix; + } + + private bool SkipMnemonics => + (_options & WordParserOptions.IgnoreMnemonicsIndicators) == WordParserOptions.IgnoreMnemonicsIndicators; + + private bool SplitCompoundWords => + (_options & WordParserOptions.SplitCompoundWords) == WordParserOptions.SplitCompoundWords; + + /// + /// Returns the words contained in the specified text, delimiting based on the specified options. + /// + /// + /// A containing the text to parse. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// A of strings containing the words contained in . + /// + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + internal static Collection Parse(string text, WordParserOptions options) + { + return Parse(text, options, NullChar); + } + + /// + /// Returns the words contained in the specified text, delimiting based on the specified options. + /// + /// + /// A containing the text to parse. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// A representing an optional prefix of , that if present, + /// will be returned as a separate token. + /// + /// + /// A of strings containing the words contained in . + /// + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + internal static Collection Parse(string text, WordParserOptions options, char prefix) + { + WordParser parser = new WordParser(text, options, prefix); + Collection words = new Collection(); + + string? word; + while ((word = parser.NextWord()) != null) + { + words.Add(word); + } + + return words; + } + + /// + /// Returns a value indicating whether at least one of the specified words occurs, using a case-insensitive ordinal comparison, within the specified text. + /// + /// + /// A containing the text to check. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// A array containing the words to seek. + /// + /// + /// if at least one of the elements within occurs within , otherwise, . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + public static bool ContainsWord(string text, WordParserOptions options, ImmutableArray words) + { + return ContainsWord(text, options, NullChar, words); + } + + /// + /// Returns a value indicating whether at least one of the specified words occurs, using a case-insensitive ordinal comparison, within the specified text. + /// + /// + /// A containing the text to check. + /// + /// + /// One or more of the specifying parsing and delimiting options. + /// + /// + /// A representing an optional prefix of , that if present, + /// will be returned as a separate token. + /// + /// + /// A array containing the words to seek. + /// + /// + /// if at least one of the elements within occurs within , otherwise, . + /// + /// + /// is . + /// + /// -or- + /// + /// is . + /// + /// + /// is not one or more of the values. + /// + internal static bool ContainsWord(string text, WordParserOptions options, char prefix, ImmutableArray words) + { + if (words.IsDefault) + { + throw new ArgumentNullException(nameof(words)); + } + + WordParser parser = new WordParser(text, options, prefix); + + string? parsedWord; + while ((parsedWord = parser.NextWord()) != null) + { + foreach (string word in words) + { + if (string.Equals(parsedWord, word, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + } + + return false; + } + + /// + /// Returns the next word in the text. + /// + /// + /// A containing the next word or if there are no more words. + /// + public string? NextWord() + { + if (_peekedWord == null) + { + return NextWordCore(); + } + + string? word = _peekedWord; + _peekedWord = null; + return word; + } + + /// + /// Returns the next word in the text without consuming it. + /// + /// + /// A containing the next word or if there are no more words. + /// + public string? PeekWord() + { + _peekedWord ??= NextWordCore(); + + return _peekedWord; + } + + private string? NextWordCore() + { + // Reset buffer + _buffer.Length = 0; + + if (ParseNext()) + { // We parsed something + return _buffer.ToString(); + } + + return null; + } + + private bool ParseNext() + { + if (TryParsePrefix()) + { // Try parse the prefix e.g. 'I' in 'IInterface'. + return true; + } + + char c; + char punctuation = NullChar; + + while ((c = Peek()) != NullChar) + { + if (!TryParseWord(c)) + { + if (punctuation != NullChar) + { // Intra-word punctuation next to unrecognized character e.g. 'Foo-?' + Unread(); + Skip(); + return true; + } + + // Unrecognized character, ignore + Skip(); + continue; + } + + c = Peek(); + + if (IsIntraWordPunctuation(c)) + { // Intra-word punctuation e.g. '-' in 'Foo-Bar' + punctuation = c; + Read(); + continue; + } + + // We parsed something + return true; + } + + if (punctuation != NullChar) + { // Ends with intra-word punctuation e.g. '-' in 'Foo-' + Unread(); + return true; + } + + return false; + } + + private bool TryParseWord(char c) + { + if (SplitCompoundWords) + { // Parse both whole and compound words + if (IsUpper(c)) + { // 'ALLCAPS' or 'PascalCased' + ParseUppercase(); + return true; + } + + if (IsLower(c)) + { // 'foo' + ParseLowercase(); + return true; + } + + if (IsDigit(c)) + { // '123' or '0xABCDEF' + ParseNumeric(); + return true; + } + + if (IsLetterWithoutCase(c)) + { // e.g. Japanese characters + ParseWithoutCase(); + return true; + } + + if (c == '#' && IsHexDigit(Peek(2))) + { // '#ABC123' + ParseHex(); + return true; + } + } + else if (IsLetterOrDigit(c)) + { // Parse only whole words + ParseWholeWord(); + return true; + } + + // Unrecognized character + return false; + } + + private bool TryParsePrefix() + { + if (_prefix == NullChar) + { + return false; + } + + char c = Peek(); + + if (c == _prefix) + { + c = Peek(2); + + if (!IsLower(c)) + { // 'IInterface' or 'T1', but not 'Interface', or 'Type' + // Consume the prefix + Read(); + + // We do not want to try and read the prefix again + _prefix = NullChar; + return true; + } + } + + // We do not want to try and read the prefix again + _prefix = NullChar; + return false; + } + + private void ParseWholeWord() + { + char c; + do + { + Read(); + c = Peek(); + } + while (IsLetterOrDigit(c)); + } + + private void ParseInteger() + { + char c; + do + { + Read(); + c = Peek(); + } + while (IsDigit(c)); + } + + private void ParseHex() + { + char c; + do + { + Read(); + c = Peek(); + } + while (IsHexDigit(c)); + } + + private void ParseNumeric() + { + char c = Peek(); + + if (c == '0') + { + c = Peek(2); + + if ((c == 'x' || c == 'X') && IsHexDigit(Peek(3))) + { // '0xA' or '0XA' + Read(); // Consume '0' + Read(); // Consume 'x' or 'X' + + ParseHex(); + return; + } + } + + ParseInteger(); + } + + private void ParseLowercase() + { + char c; + do + { + Read(); + c = Peek(); + } + while (IsLower(c)); + } + + private void ParseUppercase() + { + Read(); + + char c = Peek(); + + if (IsUpper(c)) + { // 'ALLCAPS' + ParseAllCaps(); + } + else if (IsLower(c)) + { // 'PascalCased' + ParseLowercase(); + } + } + + private void ParseWithoutCase() + { + // Parses letters without any concept of case e.g. Japanese + + char c; + do + { + Read(); + c = Peek(); + } + while (IsLetterWithoutCase(c)); + } + + private void ParseAllCaps() + { + char c; + + // Optimistically consume all consecutive uppercase letters + do + { + Read(); + c = Peek(); + } + while (IsUpper(c)); + + // Optimistically consume a trailing 's' + if (c == 's') + { + Read(); + c = Peek(); + } + + // Reject the final uppercase letter (and trailing 's') + // if they are followed by a lower case letter. + while (IsLower(c)) + { + Unread(); + c = Peek(); + } + } + + private void Read() + { + char c = Peek(); + _buffer.Append(c); + Skip(); + } + + private void Skip() + { + while (_index < _text.Length) + { + char c = _text[_index++]; + + if (!IsIgnored(c)) + { + break; + } + } + } + + private char Peek() + { + return Peek(1); + } + + private char Peek(int lookAhead) + { + for (int index = _index; index < _text.Length; index++) + { + char c = _text[index]; + + if (IsIgnored(c)) + { + continue; + } + + if (--lookAhead == 0) + { + return c; + } + } + + return NullChar; + } + + private void Unread() + { + while (_index >= 0) + { + char c = _text[--_index]; + + if (!IsIgnored(c)) + { + break; + } + } + + _buffer.Length--; + } + + private bool IsIgnored(char c) + { // TODO: We should extend this to handle 'real' mnemonics, + // instead of just blindly skipping all ampersands and + // underscores.For example, '&&OK' should really be + // interpreted as '&OK', instead of 'OK'. + if (SkipMnemonics) + { + return c is '&' or '_'; + } + + return false; + } + + private static bool IsLower(char c) + { + return char.IsLower(c); + } + + private static bool IsUpper(char c) + { + return char.IsUpper(c); + } + + private static bool IsLetterOrDigit(char c) + { + return char.IsLetterOrDigit(c); + } + + private static bool IsLetterWithoutCase(char c) + { + if (char.IsLetter(c) && !char.IsUpper(c)) + { + return !char.IsLower(c); + } + + return false; + } + + private static bool IsDigit(char c) + { + return char.IsDigit(c); + } + + private static bool IsHexDigit(char c) + { + return c switch + { + 'A' or 'a' or 'B' or 'b' or 'C' or 'c' or 'D' or 'd' or 'E' or 'e' or 'F' or 'f' => true, + _ => IsDigit(c), + }; + } + + private static bool IsIntraWordPunctuation(char c) + { // Don't be tempted to add En dash and Em dash to this + // list, as these should be treated as word delimiters. + + return c switch + { + '-' or '\u00AD' or '\'' or '\u2019' => true, + _ => false, + }; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Compiler/WordParserOptions.cs b/src/RoslynAnalyzers/Utilities/Compiler/WordParserOptions.cs new file mode 100644 index 0000000000000..cb6abc0bf3819 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Compiler/WordParserOptions.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; + +namespace Analyzer.Utilities +{ + /// + /// Defines the word parsing and delimiting options for use with . + /// + [Flags] + internal enum WordParserOptions + { + /// + /// Indicates the default options for word parsing. + /// + None = 0, + + /// + /// Indicates that should ignore the mnemonic indicator characters (&) embedded within words. + /// + IgnoreMnemonicsIndicators = 1, + + /// + /// Indicates that should split compound words. + /// + SplitCompoundWords = 2, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/BranchWithInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/BranchWithInfo.cs new file mode 100644 index 0000000000000..c0ad0901fda27 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/BranchWithInfo.cs @@ -0,0 +1,132 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + /// + /// Contains aggregated information about a control flow branch. + /// + public sealed class BranchWithInfo + { + private static readonly Func> s_getTransitiveNestedRegions = GetTransitiveNestedRegions; + + internal BranchWithInfo(ControlFlowBranch branch) + : this(branch.Destination!, branch.EnteringRegions, branch.LeavingRegions, branch.FinallyRegions, + branch.Semantics, branch.Source.BranchValue, + GetControlFlowConditionKind(branch), + leavingRegionLocals: ComputeLeavingRegionLocals(branch.LeavingRegions), + leavingRegionFlowCaptures: ComputeLeavingRegionFlowCaptures(branch.LeavingRegions)) + { + } + + internal BranchWithInfo(BasicBlock destination) + : this(destination, + enteringRegions: ImmutableArray.Empty, + leavingRegions: ImmutableArray.Empty, + finallyRegions: ImmutableArray.Empty, + kind: ControlFlowBranchSemantics.Regular, + branchValue: null, + controlFlowConditionKind: ControlFlowConditionKind.None, + leavingRegionLocals: ImmutableHashSet.Empty, + leavingRegionFlowCaptures: ImmutableHashSet.Empty) + { + } + + private BranchWithInfo( + BasicBlock destination, + ImmutableArray enteringRegions, + ImmutableArray leavingRegions, + ImmutableArray finallyRegions, + ControlFlowBranchSemantics kind, + IOperation? branchValue, + ControlFlowConditionKind controlFlowConditionKind, + IEnumerable leavingRegionLocals, + IEnumerable leavingRegionFlowCaptures) + { + Destination = destination; + Kind = kind; + EnteringRegions = enteringRegions; + LeavingRegions = leavingRegions; + FinallyRegions = finallyRegions; + BranchValue = branchValue; + ControlFlowConditionKind = controlFlowConditionKind; + LeavingRegionLocals = leavingRegionLocals; + LeavingRegionFlowCaptures = leavingRegionFlowCaptures; + } + + public BasicBlock Destination { get; } + public ControlFlowBranchSemantics Kind { get; } + public ImmutableArray EnteringRegions { get; } + public ImmutableArray FinallyRegions { get; } + public ImmutableArray LeavingRegions { get; } + public IOperation? BranchValue { get; } + + public ControlFlowConditionKind ControlFlowConditionKind { get; } + + public IEnumerable LeavingRegionLocals { get; } + public IEnumerable LeavingRegionFlowCaptures { get; } + + internal BranchWithInfo WithEmptyRegions(BasicBlock destination) + { + return new BranchWithInfo( + destination, + enteringRegions: ImmutableArray.Empty, + leavingRegions: ImmutableArray.Empty, + finallyRegions: ImmutableArray.Empty, + kind: Kind, + branchValue: BranchValue, + controlFlowConditionKind: ControlFlowConditionKind, + leavingRegionLocals: ImmutableHashSet.Empty, + leavingRegionFlowCaptures: ImmutableHashSet.Empty); + } + + internal BranchWithInfo With( + IOperation? branchValue, + ControlFlowConditionKind controlFlowConditionKind) + { + return new BranchWithInfo(Destination, EnteringRegions, LeavingRegions, + FinallyRegions, Kind, branchValue, controlFlowConditionKind, + LeavingRegionLocals, LeavingRegionFlowCaptures); + } + + private static IEnumerable GetTransitiveNestedRegions(ControlFlowRegion region) + { + yield return region; + + foreach (var nestedRegion in region.NestedRegions) + { + foreach (var transitiveNestedRegion in GetTransitiveNestedRegions(nestedRegion)) + { + yield return transitiveNestedRegion; + } + } + } + + private static IEnumerable ComputeLeavingRegionLocals(ImmutableArray leavingRegions) + { + return leavingRegions.SelectMany(s_getTransitiveNestedRegions).Distinct().SelectMany(r => r.Locals); + } + + private static IEnumerable ComputeLeavingRegionFlowCaptures(ImmutableArray leavingRegions) + { + return leavingRegions.SelectMany(s_getTransitiveNestedRegions).Distinct().SelectMany(r => r.CaptureIds); + } + + private static ControlFlowConditionKind GetControlFlowConditionKind(ControlFlowBranch branch) + { + if (branch.IsConditionalSuccessor || + branch.Source.ConditionKind == ControlFlowConditionKind.None) + { + return branch.Source.ConditionKind; + } + + return branch.Source.ConditionKind.Negate(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/BasicBlockExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/BasicBlockExtensions.cs new file mode 100644 index 0000000000000..be044b00da2c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/BasicBlockExtensions.cs @@ -0,0 +1,255 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + internal static class BasicBlockExtensions + { + internal static IEnumerable<(BasicBlock predecessorBlock, BranchWithInfo branchWithInfo)> GetPredecessorsWithBranches(this BasicBlock basicBlock, ControlFlowGraph cfg) + { + foreach (ControlFlowBranch predecessorBranch in basicBlock.Predecessors) + { + var branchWithInfo = new BranchWithInfo(predecessorBranch); + if (!predecessorBranch.FinallyRegions.IsEmpty) + { + var lastFinally = predecessorBranch.FinallyRegions[^1]; + yield return (predecessorBlock: cfg.Blocks[lastFinally.LastBlockOrdinal], branchWithInfo); + } + else + { + yield return (predecessorBlock: predecessorBranch.Source, branchWithInfo); + } + } + } + + internal static ITypeSymbol? GetEnclosingRegionExceptionType(this BasicBlock basicBlock) + { + var region = basicBlock.EnclosingRegion; + while (region != null) + { + if (region.ExceptionType != null) + { + return region.ExceptionType; + } + + region = region.EnclosingRegion; + } + + return null; + } + + public static IEnumerable DescendantOperations(this BasicBlock basicBlock) + { + foreach (var statement in basicBlock.Operations) + { + foreach (var operation in statement.DescendantsAndSelf()) + { + yield return operation; + } + } + + if (basicBlock.BranchValue != null) + { + foreach (var operation in basicBlock.BranchValue.DescendantsAndSelf()) + { + yield return operation; + } + } + } + + /// + /// Returns true if the given is contained in a control flow region with the given . + /// + public static bool IsContainedInRegionOfKind(this BasicBlock basicBlock, ControlFlowRegionKind regionKind) + => basicBlock.GetContainingRegionOfKind(regionKind) != null; + + /// + /// Returns the innermost control flow region of the given that contains the given . + /// + public static ControlFlowRegion? GetContainingRegionOfKind(this BasicBlock basicBlock, ControlFlowRegionKind regionKind) + { + var enclosingRegion = basicBlock.EnclosingRegion; + while (enclosingRegion != null) + { + if (enclosingRegion.Kind == regionKind) + { + return enclosingRegion; + } + + enclosingRegion = enclosingRegion.EnclosingRegion; + } + + return null; + } + + /// + /// Returns true if the given basic block is the first block of a finally region. + /// + public static bool IsFirstBlockOfFinally(this BasicBlock basicBlock, [NotNullWhen(returnValue: true)] out ControlFlowRegion? finallyRegion) + => basicBlock.IsFirstBlockOfRegionKind(ControlFlowRegionKind.Finally, out finallyRegion); + + /// + /// Returns true if the given basic block is the last block of a finally region. + /// + public static bool IsLastBlockOfFinally(this BasicBlock basicBlock, [NotNullWhen(returnValue: true)] out ControlFlowRegion? finallyRegion) + => basicBlock.IsLastBlockOfRegionKind(ControlFlowRegionKind.Finally, out finallyRegion); + + /// + /// Returns true if the given basic block is the first block of a region of the given regionKind. + /// + public static bool IsFirstBlockOfRegionKind(this BasicBlock basicBlock, ControlFlowRegionKind regionKind, [NotNullWhen(returnValue: true)] out ControlFlowRegion? region) + => basicBlock.IsFirstOrLastBlockOfRegionKind(regionKind, first: true, out region); + + /// + /// Returns true if the given basic block is the last block of a region of the given regionKind. + /// + public static bool IsLastBlockOfRegionKind(this BasicBlock basicBlock, ControlFlowRegionKind regionKind, [NotNullWhen(returnValue: true)] out ControlFlowRegion? region) + => basicBlock.IsFirstOrLastBlockOfRegionKind(regionKind, first: false, out region); + + private static bool IsFirstOrLastBlockOfRegionKind(this BasicBlock basicBlock, ControlFlowRegionKind regionKind, bool first, [NotNullWhen(returnValue: true)] out ControlFlowRegion? foundRegion) + { + foundRegion = null; + + var enclosingRegion = basicBlock.EnclosingRegion; + while (enclosingRegion != null) + { + var ordinalToCompare = first ? enclosingRegion.FirstBlockOrdinal : enclosingRegion.LastBlockOrdinal; + if (ordinalToCompare != basicBlock.Ordinal) + { + return false; + } + + if (enclosingRegion.Kind == regionKind) + { + foundRegion = enclosingRegion; + return true; + } + + enclosingRegion = enclosingRegion.EnclosingRegion; + } + + return false; + } + + /// + /// Returns true if the given basic block is the first block of a compiler generated finally region. + /// + public static bool IsFirstBlockOfCompilerGeneratedFinally(this BasicBlock basicBlock, ControlFlowGraph cfg) + { + if (!basicBlock.IsFirstBlockOfRegionKind(ControlFlowRegionKind.Finally, out var finallyRegion)) + { + return false; + } + + foreach (var operation in finallyRegion.DescendantOperations(cfg)) + { + if (!operation.IsImplicit) + { + return false; + } + } + + return true; + } + + internal static ControlFlowRegion? GetInnermostRegionStartedByBlock(this BasicBlock basicBlock, ControlFlowRegionKind regionKind) + { + if (basicBlock.EnclosingRegion?.FirstBlockOrdinal != basicBlock.Ordinal) + { + return null; + } + + var enclosingRegion = basicBlock.EnclosingRegion; + while (enclosingRegion.Kind != regionKind) + { + enclosingRegion = enclosingRegion.EnclosingRegion; + if (enclosingRegion?.FirstBlockOrdinal != basicBlock.Ordinal) + { + return null; + } + } + + return enclosingRegion; + } + + /// + /// Gets the maximum ordinal of a conditional or fall through successor of the given basic block. + /// Returns -1 if the block has no conditional or fall through successor, + /// for example, if the block only has a structured exception handling branch for throw operation. + /// + /// + /// + internal static int GetMaxSuccessorOrdinal(this BasicBlock basicBlock) + => Math.Max(basicBlock.FallThroughSuccessor?.Destination?.Ordinal ?? -1, + basicBlock.ConditionalSuccessor?.Destination?.Ordinal ?? -1); + + internal static bool DominatesPredecessors(this BasicBlock? basicBlock, ControlFlowGraph cfg) + { + if (basicBlock == null || + basicBlock.Predecessors.IsEmpty) + { + return false; + } + + using var processedOrdinals = PooledHashSet.GetInstance(); + using var unprocessedOrdinals = ArrayBuilder.GetInstance(); + foreach (var predecessor in basicBlock.Predecessors) + { + var sourceBlock = predecessor.Source; + if (!DominatesBlock(sourceBlock, basicBlock, processedOrdinals, unprocessedOrdinals)) + { + return false; + } + + processedOrdinals.Add(sourceBlock.Ordinal); + } + + while (unprocessedOrdinals.Count > 0) + { + var ordinal = unprocessedOrdinals[0]; + Debug.Assert(ordinal < basicBlock.Ordinal); + unprocessedOrdinals.RemoveAt(0); + + if (processedOrdinals.Add(ordinal)) + { + var sourceBlock = cfg.Blocks[ordinal]; + if (!DominatesBlock(sourceBlock, basicBlock, processedOrdinals, unprocessedOrdinals)) + { + return false; + } + } + } + + return true; + + static bool DominatesBlock(BasicBlock sourceBlock, BasicBlock basicBlock, PooledHashSet processedOrdinals, ArrayBuilder unprocessedOrdinals) + => DominatesBranch(sourceBlock.ConditionalSuccessor, basicBlock, processedOrdinals, unprocessedOrdinals) && + DominatesBranch(sourceBlock.FallThroughSuccessor, basicBlock, processedOrdinals, unprocessedOrdinals); + + static bool DominatesBranch(ControlFlowBranch? branch, BasicBlock basicBlock, PooledHashSet processedOrdinals, ArrayBuilder unprocessedOrdinals) + { + var destinationBlock = branch?.Destination; + if (destinationBlock == null || + destinationBlock == basicBlock) + { + return true; + } + + if (!processedOrdinals.Contains(destinationBlock.Ordinal)) + { + unprocessedOrdinals.Add(destinationBlock.Ordinal); + } + + return destinationBlock.Ordinal <= basicBlock.Ordinal; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowBranchExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowBranchExtensions.cs new file mode 100644 index 0000000000000..ef16615216c0a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowBranchExtensions.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + internal static class ControlFlowBranchExtensions + { + public static bool IsBackEdge(this ControlFlowBranch controlFlowBranch) + => controlFlowBranch?.Source != null && + controlFlowBranch.Destination != null && + controlFlowBranch.Source.Ordinal >= controlFlowBranch.Destination.Ordinal; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowConditionKindExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowConditionKindExtensions.cs new file mode 100644 index 0000000000000..bac9d9f2ee056 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowConditionKindExtensions.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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + internal static class ControlFlowConditionKindExtensions + { + public static ControlFlowConditionKind Negate(this ControlFlowConditionKind controlFlowConditionKind) + { + switch (controlFlowConditionKind) + { + case ControlFlowConditionKind.WhenFalse: + return ControlFlowConditionKind.WhenTrue; + + case ControlFlowConditionKind.WhenTrue: + return ControlFlowConditionKind.WhenFalse; + + default: + Debug.Fail($"Unsupported conditional kind: '{controlFlowConditionKind}'"); + return controlFlowConditionKind; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowGraphExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowGraphExtensions.cs new file mode 100644 index 0000000000000..cfc43f4415346 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowGraphExtensions.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.Lightup; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + internal static class ControlFlowGraphExtensions + { + public static BasicBlock GetEntry(this ControlFlowGraph cfg) => cfg.Blocks.Single(b => b.Kind == BasicBlockKind.Entry); + public static BasicBlock GetExit(this ControlFlowGraph cfg) => cfg.Blocks.Single(b => b.Kind == BasicBlockKind.Exit); + public static IEnumerable DescendantOperations(this ControlFlowGraph cfg) + { + foreach (BasicBlock block in cfg.Blocks) + { + foreach (IOperation operation in block.DescendantOperations()) + { + yield return operation; + } + } + } + + public static IEnumerable DescendantOperations(this ControlFlowGraph cfg, OperationKind operationKind) + where T : IOperation + { + foreach (var descendant in cfg.DescendantOperations()) + { + if (descendant?.Kind == operationKind) + { + yield return (T)descendant; + } + } + } + + internal static bool SupportsFlowAnalysis(this ControlFlowGraph cfg) + { + // Skip flow analysis for following root operation blocks: + // 1. Null root operation (error case) + // 2. OperationKindEx.Attribute or OperationKind.None (used for attributes before IAttributeOperation support). + // 3. OperationKind.ParameterInitialzer (default parameter values). + if (cfg.OriginalOperation == null || + cfg.OriginalOperation.Kind is OperationKindEx.Attribute or OperationKind.None or OperationKind.ParameterInitializer) + { + return false; + } + + // Skip flow analysis for code with syntax/semantic errors + if (cfg.OriginalOperation.Syntax.GetDiagnostics().Any(d => d.DefaultSeverity == DiagnosticSeverity.Error) || + cfg.OriginalOperation.HasAnyOperationDescendant(o => o is IInvalidOperation)) + { + return false; + } + + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowRegionExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowRegionExtensions.cs new file mode 100644 index 0000000000000..91be46fe50c10 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ControlFlowRegionExtensions.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.Generic; + +namespace Microsoft.CodeAnalysis.FlowAnalysis +{ + internal static class ControlFlowRegionExtensions + { + public static bool ContainsRegionOrSelf(this ControlFlowRegion controlFlowRegion, ControlFlowRegion nestedRegion) + => controlFlowRegion.FirstBlockOrdinal <= nestedRegion.FirstBlockOrdinal && + controlFlowRegion.LastBlockOrdinal >= nestedRegion.LastBlockOrdinal; + + public static IEnumerable DescendantOperations(this ControlFlowRegion controlFlowRegion, ControlFlowGraph cfg) + { + for (var i = controlFlowRegion.FirstBlockOrdinal; i <= controlFlowRegion.LastBlockOrdinal; i++) + { + var block = cfg.Blocks[i]; + foreach (var operation in block.DescendantOperations()) + { + yield return operation; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/IOperationExtensions_FlowAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/IOperationExtensions_FlowAnalysis.cs new file mode 100644 index 0000000000000..edad176c26778 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/IOperationExtensions_FlowAnalysis.cs @@ -0,0 +1,56 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class IOperationExtensions + { + public static bool IsInsideCatchRegion([NotNullWhen(returnValue: true)] this IOperation? operation, ControlFlowGraph cfg) + { + if (operation == null) + { + return false; + } + + foreach (var block in cfg.Blocks) + { + var isCatchRegionBlock = false; + var currentRegion = block.EnclosingRegion; + while (currentRegion != null) + { + switch (currentRegion.Kind) + { + case ControlFlowRegionKind.Catch: + isCatchRegionBlock = true; + break; + } + + currentRegion = currentRegion.EnclosingRegion; + } + + if (isCatchRegionBlock) + { + foreach (var descendant in block.DescendantOperations()) + { + if (operation == descendant) + { + return true; + } + } + } + } + + return false; + } + + public static bool IsLValueFlowCaptureReference(this IFlowCaptureReferenceOperation flowCaptureReference) + => flowCaptureReference.Parent is IAssignmentOperation assignment && + assignment.Target == flowCaptureReference; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ListExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ListExtensions.cs new file mode 100644 index 0000000000000..b4ebe2a8a17ab --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/ListExtensions.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; + +namespace Analyzer.Utilities.Extensions +{ + internal static class ListExtensions + { + /// + /// Extract and remove all elements from which are matched by + /// . + /// + /// The type of element in the list. + /// A state argument to pass to . + /// The list. + /// A predicate matching elements to remove from . + /// An additional state argument to pass to . + /// A collection of elements removed from , in the order they were removed. If + /// no elements were removed, this method returns . + public static ImmutableArray ExtractAll(this List list, Func predicate, TArg argument) + { + ImmutableArray.Builder? builder = null; + for (int i = 0; i < list.Count; i++) + { + var value = list[i]; + if (predicate(value, argument)) + { + builder ??= ImmutableArray.CreateBuilder(); + builder.Add(value); + } + else if (builder is not null) + { + list[i - builder.Count] = value; + } + } + + if (builder is null) + { + return ImmutableArray.Empty; + } + + list.RemoveRange(list.Count - builder.Count, builder.Count); + return builder.Capacity == builder.Count ? builder.MoveToImmutable() : builder.ToImmutable(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs new file mode 100644 index 0000000000000..547b53cc79271 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Extensions/OperationBlocksExtensions.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class OperationBlocksExtensions + { + public static ControlFlowGraph? GetControlFlowGraph(this ImmutableArray operationBlocks) + { + foreach (var operationRoot in operationBlocks) + { + if (operationRoot.TryGetEnclosingControlFlowGraph(out var cfg)) + { + return cfg; + } + } + + return null; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.projitems b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.projitems new file mode 100644 index 0000000000000..7922ed9519ecf --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.projitems @@ -0,0 +1,205 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + $(NoWarn);CA1724 + + + Analyzer.Utilities + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.shproj b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.shproj new file mode 100644 index 0000000000000..4ab71c25c3a59 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {FCB56CBA-FA35-46A8-86B7-BAE5433197D9} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValue.cs new file mode 100644 index 0000000000000..c6fe0c2d338a5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValue.cs @@ -0,0 +1,91 @@ +// 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 System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + /// + /// Abstract copy value shared by a set of one of more instances tracked by . + /// + public class CopyAbstractValue : CacheBasedEquatable + { + public static CopyAbstractValue NotApplicable { get; } = new CopyAbstractValue(CopyAbstractValueKind.NotApplicable); + public static CopyAbstractValue Invalid { get; } = new CopyAbstractValue(CopyAbstractValueKind.Invalid); + public static CopyAbstractValue Unknown { get; } = new CopyAbstractValue(CopyAbstractValueKind.Unknown); + + internal CopyAbstractValue(ImmutableHashSet analysisEntities, CopyAbstractValueKind kind) + { + Debug.Assert(analysisEntities.IsEmpty != kind.IsKnown()); + Debug.Assert(kind != CopyAbstractValueKind.KnownReferenceCopy || analysisEntities.All(a => !a.Type.IsValueType)); + + if (kind == CopyAbstractValueKind.KnownValueCopy && + analysisEntities.Count == 1 && + !analysisEntities.First().Type.IsValueType) + { + kind = CopyAbstractValueKind.KnownReferenceCopy; + } + + AnalysisEntities = analysisEntities; + Kind = kind; + } + + private CopyAbstractValue(CopyAbstractValueKind kind) + : this(ImmutableHashSet.Empty, kind) + { + Debug.Assert(!kind.IsKnown()); + } + + internal CopyAbstractValue(AnalysisEntity analysisEntity) + : this(ImmutableHashSet.Create(analysisEntity), + kind: analysisEntity.Type.IsReferenceType ? CopyAbstractValueKind.KnownReferenceCopy : CopyAbstractValueKind.KnownValueCopy) + { + } + + internal CopyAbstractValue(ImmutableHashSet analysisEntities, bool isReferenceCopy) + : this(analysisEntities, + kind: isReferenceCopy ? CopyAbstractValueKind.KnownReferenceCopy : CopyAbstractValueKind.KnownValueCopy) + { + Debug.Assert(!analysisEntities.IsEmpty); + } + + internal CopyAbstractValue WithEntityRemoved(AnalysisEntity entityToRemove) + { + Debug.Assert(AnalysisEntities.Contains(entityToRemove)); + Debug.Assert(AnalysisEntities.Count > 1); + Debug.Assert(Kind.IsKnown()); + + return new CopyAbstractValue(AnalysisEntities.Remove(entityToRemove), Kind); + } + + internal CopyAbstractValue WithEntitiesRemoved(IEnumerable entitiesToRemove) + { + Debug.Assert(entitiesToRemove.All(AnalysisEntities.Contains)); + Debug.Assert(AnalysisEntities.Count > 1); + Debug.Assert(Kind.IsKnown()); + + return new CopyAbstractValue(AnalysisEntities.Except(entitiesToRemove), Kind); + } + + public ImmutableHashSet AnalysisEntities { get; } + public CopyAbstractValueKind Kind { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(AnalysisEntities)); + hashCode.Add(((int)Kind).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (CopyAbstractValue)obj; + return HashUtilities.Combine(AnalysisEntities) == HashUtilities.Combine(other.AnalysisEntities) + && ((int)Kind).GetHashCode() == ((int)other.Kind).GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValueKind.cs new file mode 100644 index 0000000000000..8aea752e1d91c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAbstractValueKind.cs @@ -0,0 +1,64 @@ +// 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.FlowAnalysis.DataFlow.CopyAnalysis +{ + /// + /// Kind for the . + /// + public enum CopyAbstractValueKind + { + /// + /// Not applicable. + /// + NotApplicable, + + /// + /// Copy of a reference shared by one or more instances. + /// + KnownReferenceCopy, + + /// + /// Copy of a value shared by one or more instances. + /// + KnownValueCopy, + + /// + /// Copy may or may not be shared by other instances. + /// + Unknown, + + /// + /// Invalid state for an unreachable path from predicate analysis. + /// + Invalid, + } + + internal static class CopyAbstractValueKindExtensions + { + public static bool IsKnown(this CopyAbstractValueKind kind) + { + return kind switch + { + CopyAbstractValueKind.KnownValueCopy + or CopyAbstractValueKind.KnownReferenceCopy => true, + _ => false, + }; + } + + public static CopyAbstractValueKind MergeIfBothKnown(this CopyAbstractValueKind kind, CopyAbstractValueKind kindToMerge) + { + if (!kind.IsKnown() || + !kindToMerge.IsKnown()) + { + return kind; + } + + // Can only ensure value copy if one of the kinds is a value copy. + return kind == CopyAbstractValueKind.KnownValueCopy || kindToMerge == CopyAbstractValueKind.KnownValueCopy ? + CopyAbstractValueKind.KnownValueCopy : + CopyAbstractValueKind.KnownReferenceCopy; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyAbstractValueDomain.cs new file mode 100644 index 0000000000000..1052cd23cbf2a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyAbstractValueDomain.cs @@ -0,0 +1,97 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + + public partial class CopyAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private sealed class CopyAbstractValueDomain : AbstractValueDomain + { + public static CopyAbstractValueDomain Default = new(); + private readonly SetAbstractDomain _entitiesDomain = SetAbstractDomain.Default; + + private CopyAbstractValueDomain() { } + + public override CopyAbstractValue Bottom => CopyAbstractValue.NotApplicable; + + public override CopyAbstractValue UnknownOrMayBeValue => CopyAbstractValue.Unknown; + + public override int Compare(CopyAbstractValue oldValue, CopyAbstractValue newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (oldValue.Kind == newValue.Kind || + (oldValue.Kind.IsKnown() && newValue.Kind.IsKnown())) + { + return _entitiesDomain.Compare(oldValue.AnalysisEntities, newValue.AnalysisEntities) * -1; + } + else if (oldValue.Kind < newValue.Kind) + { + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + public override CopyAbstractValue Merge(CopyAbstractValue value1, CopyAbstractValue value2) + { + if (value1 == null) + { + return value2; + } + else if (value2 == null) + { + return value1; + } + else if (value1.Kind is CopyAbstractValueKind.Invalid or CopyAbstractValueKind.NotApplicable) + { + return value2; + } + else if (value2.Kind is CopyAbstractValueKind.Invalid or CopyAbstractValueKind.NotApplicable) + { + return value1; + } + else if (value1.Kind == CopyAbstractValueKind.Unknown || value2.Kind == CopyAbstractValueKind.Unknown) + { + return CopyAbstractValue.Unknown; + } + + Debug.Assert(value1.Kind.IsKnown()); + Debug.Assert(value2.Kind.IsKnown()); + + var mergedEntities = _entitiesDomain.Intersect(value1.AnalysisEntities, value2.AnalysisEntities); + if (mergedEntities.IsEmpty) + { + return CopyAbstractValue.Unknown; + } + else if (mergedEntities.Count == value1.AnalysisEntities.Count) + { + Debug.Assert(_entitiesDomain.Equals(mergedEntities, value1.AnalysisEntities)); + return value1; + } + else if (mergedEntities.Count == value2.AnalysisEntities.Count) + { + Debug.Assert(_entitiesDomain.Equals(mergedEntities, value2.AnalysisEntities)); + return value2; + } + + var mergedKind = value1.Kind > value2.Kind ? value1.Kind : value2.Kind; + return new CopyAbstractValue(mergedEntities, mergedKind); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..2e8a9cfc7c053 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CopyDataFlowOperationVisitor.cs @@ -0,0 +1,667 @@ +// 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 System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Analyzer.Utilities.PooledObjects.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CopyAnalysisDomain = PredicatedAnalysisDataDomain; + using CopyAnalysisResult = DataFlowAnalysisResult; + + public partial class CopyAnalysis : ForwardDataFlowAnalysis + { + /// + /// Operation visitor to flow the copy values across a given statement in a basic block. + /// + private sealed class CopyDataFlowOperationVisitor : PredicateAnalysisEntityDataFlowOperationVisitor + { + public CopyDataFlowOperationVisitor(CopyAnalysisContext analysisContext) + : base(analysisContext) + { + var coreAnalysisDomain = new CoreCopyAnalysisDataDomain(CopyAbstractValueDomain.Default, GetDefaultCopyValue); + AnalysisDomain = new CopyAnalysisDomain(coreAnalysisDomain); + + analysisContext.InterproceduralAnalysisData?.InitialAnalysisData?.AssertValidCopyAnalysisData(); + } + + public CopyAnalysisDomain AnalysisDomain { get; } + + public override CopyAnalysisData Flow(IOperation statement, BasicBlock block, CopyAnalysisData input) + { + AssertValidCopyAnalysisData(input); + var output = base.Flow(statement, block, input); + AssertValidCopyAnalysisData(output); + return output; + } + + public override (CopyAnalysisData output, bool isFeasibleBranch) FlowBranch( + BasicBlock fromBlock, + BranchWithInfo branch, + CopyAnalysisData input) + { + AssertValidCopyAnalysisData(input); + (CopyAnalysisData output, bool isFeasibleBranch) result = base.FlowBranch(fromBlock, branch, input); + AssertValidCopyAnalysisData(result.output); + return result; + } + + [Conditional("DEBUG")] + private void AssertValidCopyAnalysisData(CopyAnalysisData copyAnalysisData) + { + copyAnalysisData.AssertValidCopyAnalysisData(GetDefaultCopyValue); + } + + protected override void AddTrackedEntities(CopyAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis) + => analysisData.AddTrackedEntities(builder); + + protected override bool HasAbstractValue(AnalysisEntity analysisEntity) => CurrentAnalysisData.HasAbstractValue(analysisEntity); + + protected override bool HasAnyAbstractValue(CopyAnalysisData data) => data.HasAnyAbstractValue; + + protected override void StopTrackingEntity(AnalysisEntity analysisEntity, CopyAnalysisData analysisData) + => StopTrackingEntity(analysisEntity, analysisData, GetDefaultCopyValue); + + private static void StopTrackingEntity( + AnalysisEntity analysisEntity, + CopyAnalysisData analysisData, + Func getDefaultCopyValue) + { + analysisData.AssertValidCopyAnalysisData(getDefaultCopyValue); + + // First set the value to unknown so we remove the entity from existing copy sets. + // Note that we pass 'tryGetAddressSharedCopyValue = null' to ensure that + // we do not reset the entries for address shared entities. + SetAbstractValue(analysisData, analysisEntity, CopyAbstractValue.Unknown, tryGetAddressSharedCopyValue: _ => null); + analysisData.AssertValidCopyAnalysisData(tryGetDefaultCopyValue: null); + + // Now it should be safe to remove the entry. + analysisData.RemoveEntries(analysisEntity); + analysisData.AssertValidCopyAnalysisData(getDefaultCopyValue); + } + + protected override CopyAbstractValue GetAbstractValue(AnalysisEntity analysisEntity) => CurrentAnalysisData.TryGetValue(analysisEntity, out var value) ? value : CopyAbstractValue.Unknown; + + protected override CopyAbstractValue GetCopyAbstractValue(IOperation operation) => base.GetCachedAbstractValue(operation); + + protected override CopyAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => CopyAbstractValue.NotApplicable; + + protected override void ResetAbstractValue(AnalysisEntity analysisEntity) + => SetAbstractValue(analysisEntity, GetResetValue(analysisEntity)); + + protected override void SetAbstractValue(AnalysisEntity analysisEntity, CopyAbstractValue value) + { + SetAbstractValue(CurrentAnalysisData, analysisEntity, value, TryGetAddressSharedCopyValue); + } + + protected override void SetAbstractValueForAssignment(AnalysisEntity targetAnalysisEntity, IOperation? assignedValueOperation, CopyAbstractValue assignedValue) + { + if (assignedValue.AnalysisEntities.Contains(targetAnalysisEntity)) + { + // Dead assignment (assigning the same value). + return; + } + + base.SetAbstractValueForAssignment(targetAnalysisEntity, assignedValueOperation, assignedValue); + CurrentAnalysisData.AssertValidCopyAnalysisData(); + } + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, CopyAbstractValue assignedValue) + { + // Copy value of assignedValueOperation entity might change between tuple element assignments within the same tuple. + // For example, '(a, a)' + if (!assignedValue.AnalysisEntities.IsEmpty) + { + var currentCopyValue = GetAbstractValue(assignedValue.AnalysisEntities.First()); + if (currentCopyValue.Kind != CopyAbstractValueKind.Unknown) + { + assignedValue = currentCopyValue; + } + } + + base.SetAbstractValueForTupleElementAssignment(tupleElementEntity, assignedValueOperation, assignedValue); + } + + private static void SetAbstractValue( + CopyAnalysisData copyAnalysisData, + AnalysisEntity analysisEntity, + CopyAbstractValue value, + Func tryGetAddressSharedCopyValue, + SetCopyAbstractValuePredicateKind? fromPredicateKind = null, + bool initializingParameters = false) + { + SetAbstractValue(sourceCopyAnalysisData: copyAnalysisData, targetCopyAnalysisData: copyAnalysisData, + analysisEntity, value, tryGetAddressSharedCopyValue, fromPredicateKind, initializingParameters); + } + + private static void SetAbstractValue( + CopyAnalysisData sourceCopyAnalysisData, + CopyAnalysisData targetCopyAnalysisData, + AnalysisEntity analysisEntity, + CopyAbstractValue value, + Func tryGetAddressSharedCopyValue, + SetCopyAbstractValuePredicateKind? fromPredicateKind, + bool initializingParameters) + { + sourceCopyAnalysisData.AssertValidCopyAnalysisData(tryGetAddressSharedCopyValue, initializingParameters); + targetCopyAnalysisData.AssertValidCopyAnalysisData(tryGetAddressSharedCopyValue, initializingParameters); + Debug.Assert(ReferenceEquals(sourceCopyAnalysisData, targetCopyAnalysisData) || fromPredicateKind.HasValue); + + // Don't track entities if do not know about it's instance location. + if (analysisEntity.HasUnknownInstanceLocation) + { + return; + } + + if (!value.AnalysisEntities.IsEmpty) + { + var validEntities = value.AnalysisEntities.Where(entity => !entity.HasUnknownInstanceLocation).ToImmutableHashSet(); + if (validEntities.Count < value.AnalysisEntities.Count) + { + value = !validEntities.IsEmpty ? new CopyAbstractValue(validEntities, value.Kind) : CopyAbstractValue.Unknown; + } + } + + // Handle updating the existing value if not setting the value from predicate analysis. + if (!fromPredicateKind.HasValue && + sourceCopyAnalysisData.TryGetValue(analysisEntity, out var existingValue)) + { + if (existingValue == value) + { + // Assigning the same value to the entity. + Debug.Assert(existingValue.AnalysisEntities.Contains(analysisEntity)); + return; + } + + if (existingValue.AnalysisEntities.Count > 1) + { + CopyAbstractValue? addressSharedCopyValue = tryGetAddressSharedCopyValue(analysisEntity); + if (addressSharedCopyValue == null || addressSharedCopyValue != existingValue) + { + CopyAbstractValue newValueForEntitiesInOldSet = addressSharedCopyValue != null ? + existingValue.WithEntitiesRemoved(addressSharedCopyValue.AnalysisEntities) : + existingValue.WithEntityRemoved(analysisEntity); + targetCopyAnalysisData.SetAbstactValueForEntities(newValueForEntitiesInOldSet, entityBeingAssigned: analysisEntity); + } + } + } + + // Handle setting the new value. + var newAnalysisEntities = value.AnalysisEntities.Add(analysisEntity); + CopyAbstractValueKind newKind; + if (newAnalysisEntities.Count == 1) + { + newKind = analysisEntity.Type.IsValueType ? + CopyAbstractValueKind.KnownValueCopy : + CopyAbstractValueKind.KnownReferenceCopy; + } + else + { + newKind = fromPredicateKind != SetCopyAbstractValuePredicateKind.ValueCompare && + !analysisEntity.Type.IsValueType && + value.Kind == CopyAbstractValueKind.KnownReferenceCopy ? + CopyAbstractValueKind.KnownReferenceCopy : + CopyAbstractValueKind.KnownValueCopy; + } + + if (fromPredicateKind.HasValue) + { + // Also include the existing values for the analysis entity. + if (sourceCopyAnalysisData.TryGetValue(analysisEntity, out existingValue)) + { + if (existingValue.Kind == CopyAbstractValueKind.Invalid) + { + return; + } + + newAnalysisEntities = newAnalysisEntities.Union(existingValue.AnalysisEntities); + newKind = newKind.MergeIfBothKnown(existingValue.Kind); + } + } + else + { + // Include address shared entities, if any. + CopyAbstractValue? addressSharedCopyValue = tryGetAddressSharedCopyValue(analysisEntity); + if (addressSharedCopyValue != null) + { + newAnalysisEntities = newAnalysisEntities.Union(addressSharedCopyValue.AnalysisEntities); + } + } + + var newValue = new CopyAbstractValue(newAnalysisEntities, newKind); + targetCopyAnalysisData.SetAbstactValueForEntities(newValue, entityBeingAssigned: analysisEntity); + + targetCopyAnalysisData.AssertValidCopyAnalysisData(tryGetAddressSharedCopyValue, initializingParameters); + } + + protected override void SetValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity, ArgumentInfo? assignedValue) + { + CopyAbstractValue copyValue; + if (assignedValue != null) + { + var assignedEntities = assignedValue.Value.AnalysisEntities; + if (assignedValue.AnalysisEntity != null && !assignedEntities.Contains(assignedValue.AnalysisEntity)) + { + assignedEntities = assignedEntities.Add(assignedValue.AnalysisEntity); + } + + var newAnalysisEntities = assignedEntities; + CopyAbstractValueKind newKind; + if (assignedValue.Value.Kind.IsKnown()) + { + newKind = assignedValue.Value.Kind; + } + else if (assignedValue.AnalysisEntity == null || assignedValue.AnalysisEntity.Type.IsValueType) + { + newKind = CopyAbstractValueKind.KnownValueCopy; + } + else + { + newKind = CopyAbstractValueKind.KnownReferenceCopy; + } + + foreach (var entity in assignedEntities) + { + if (CurrentAnalysisData.TryGetValue(entity, out var existingValue)) + { + newAnalysisEntities = newAnalysisEntities.Union(existingValue.AnalysisEntities); + newKind = newKind.MergeIfBothKnown(existingValue.Kind); + } + } + + copyValue = assignedValue.Value.AnalysisEntities.Count == newAnalysisEntities.Count ? + assignedValue.Value : + new CopyAbstractValue(newAnalysisEntities, newKind); + } + else + { + copyValue = GetDefaultCopyValue(analysisEntity); + } + + SetAbstractValue(CurrentAnalysisData, analysisEntity, copyValue, TryGetAddressSharedCopyValue, initializingParameters: true); + } + + protected override void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + // Do not escape the copy value for parameter at exit. + } + + private CopyAbstractValue GetResetValue(AnalysisEntity analysisEntity) + => GetResetValue(analysisEntity, GetAbstractValue(analysisEntity)); + private CopyAbstractValue GetResetValue(AnalysisEntity analysisEntity, CopyAbstractValue currentValue) + => currentValue.AnalysisEntities.Count > 1 ? GetDefaultCopyValue(analysisEntity) : currentValue; + + protected override void ResetCurrentAnalysisData() => CurrentAnalysisData.Reset(GetResetValue); + + protected override CopyAbstractValue ComputeAnalysisValueForReferenceOperation(IOperation operation, CopyAbstractValue defaultValue) + { + if (AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + return CurrentAnalysisData.TryGetValue(analysisEntity, out var value) ? value : GetDefaultCopyValue(analysisEntity); + } + else + { + return defaultValue; + } + } + + protected override CopyAbstractValue ComputeAnalysisValueForEscapedRefOrOutArgument(AnalysisEntity analysisEntity, IArgumentOperation operation, CopyAbstractValue defaultValue) + { + Debug.Assert(operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out); + + SetAbstractValue(analysisEntity, ValueDomain.UnknownOrMayBeValue); + return GetAbstractValue(analysisEntity); + } + + #region Predicate analysis + protected override PredicateValueKind SetValueForEqualsOrNotEqualsComparisonOperator( + IOperation leftOperand, + IOperation rightOperand, + bool equals, + bool isReferenceEquality, + CopyAnalysisData targetAnalysisData) + { + var predicateKind = PredicateValueKind.Unknown; + + var leftCopyValue = GetCopyAbstractValue(leftOperand); + var rightCopyValue = GetCopyAbstractValue(rightOperand); + if (leftCopyValue.AnalysisEntities.IsEmpty || + rightCopyValue.AnalysisEntities.IsEmpty) + { + return predicateKind; + } + + if (leftCopyValue == rightCopyValue) + { + // We have "a == b && a == b" or "a == b && a != b" + // For both cases, condition on right is always true or always false and redundant. + if (!isReferenceEquality || + rightCopyValue.Kind == CopyAbstractValueKind.KnownReferenceCopy) + { + predicateKind = equals ? PredicateValueKind.AlwaysTrue : PredicateValueKind.AlwaysFalse; + } + } + + var setCopyValuePredicateKind = isReferenceEquality ? + SetCopyAbstractValuePredicateKind.ReferenceCompare : + SetCopyAbstractValuePredicateKind.ValueCompare; + + if (predicateKind != PredicateValueKind.Unknown) + { + if (!equals) + { + // "a == b && a != b" or "a == b || a != b" + foreach (var entity in rightCopyValue.AnalysisEntities) + { + SetAbstractValue(targetAnalysisData, entity, CopyAbstractValue.Invalid, TryGetAddressSharedCopyValue, setCopyValuePredicateKind); + } + } + + return predicateKind; + } + + if (equals) + { + SetAbstractValue(targetAnalysisData, leftCopyValue.AnalysisEntities.First(), rightCopyValue, TryGetAddressSharedCopyValue, setCopyValuePredicateKind); + } + + return PredicateValueKind.Unknown; + } + + protected override PredicateValueKind SetValueForIsNullComparisonOperator(IOperation leftOperand, bool equals, CopyAnalysisData targetAnalysisData) => PredicateValueKind.Unknown; + #endregion + + protected override CopyAnalysisData MergeAnalysisData(CopyAnalysisData value1, CopyAnalysisData value2) + => AnalysisDomain.Merge(value1, value2); + + protected override void UpdateValuesForAnalysisData(CopyAnalysisData targetAnalysisData) + { + // We need to trim the copy values to only include the entities that are existing keys in targetAnalysisData. + using var processedEntities = PooledHashSet.GetInstance(); + using var builder = ArrayBuilder.GetInstance(targetAnalysisData.CoreAnalysisData.Count); + builder.AddRange(targetAnalysisData.CoreAnalysisData.Keys); + + for (int i = 0; i < builder.Count; i++) + { + var key = builder[i]; + if (!processedEntities.Add(key)) + { + continue; + } + + var existingValue = targetAnalysisData[key]; + if (CurrentAnalysisData.TryGetValue(key, out var newValue)) + { + if (newValue.AnalysisEntities.Count == 1) + { + if (existingValue.AnalysisEntities.Count == 1) + { + continue; + } + } + else if (newValue.AnalysisEntities.Count > 1) + { + var entitiesToExclude = newValue.AnalysisEntities.Where(e => !targetAnalysisData.HasAbstractValue(e)); + if (entitiesToExclude.Any()) + { + newValue = newValue.WithEntitiesRemoved(entitiesToExclude); + } + } + + if (newValue != existingValue) + { + targetAnalysisData.SetAbstactValueForEntities(newValue, entityBeingAssigned: null); + } + + processedEntities.AddRange(newValue.AnalysisEntities); + } + else + { + var entitiesToExclude = existingValue.AnalysisEntities.Where(e => !CurrentAnalysisData.HasAbstractValue(e)); + var excludedCount = 0; + foreach (var entity in entitiesToExclude) + { + targetAnalysisData.RemoveEntries(entity); + processedEntities.Add(entity); + excludedCount++; + } + + if (excludedCount < existingValue.AnalysisEntities.Count) + { + newValue = existingValue.WithEntitiesRemoved(entitiesToExclude); + targetAnalysisData.SetAbstactValueForEntities(newValue, entityBeingAssigned: null); + } + } + } + } + + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(CopyAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + { + Func? predicate = null; + if (throwBranchWithExceptionType.IsDefaultExceptionForExceptionsPathAnalysis) + { + // Only tracking non-child analysis entities for exceptions path analysis for now. + Debug.Assert(SymbolEqualityComparer.Default.Equals(throwBranchWithExceptionType.ExceptionType, ExceptionNamedType)); + predicate = e => !e.IsChildOrInstanceMember; + } + + ApplyMissingCurrentAnalysisDataCore(dataAtException, predicate); + } + + protected override CopyAnalysisData GetClonedAnalysisData(CopyAnalysisData analysisData) + => (CopyAnalysisData)analysisData.Clone(); + public override CopyAnalysisData GetEmptyAnalysisData() + => new(); + protected override CopyAnalysisData GetExitBlockOutputData(CopyAnalysisResult analysisResult) + => new(analysisResult.ExitBlockOutput.Data); + protected override void AssertValidAnalysisData(CopyAnalysisData analysisData) + => AssertValidCopyAnalysisData(analysisData); + protected override bool Equals(CopyAnalysisData value1, CopyAnalysisData value2) + => value1.Equals(value2); + + public override (CopyAbstractValue Value, PredicateValueKind PredicateValueKind)? GetReturnValueAndPredicateKind() + { + // Filter out all the local symbol and flow capture entities from the return value for interprocedural analysis. + var returnValueAndPredicateKind = base.GetReturnValueAndPredicateKind(); + if (returnValueAndPredicateKind.HasValue && + returnValueAndPredicateKind.Value.Value.Kind.IsKnown() && + DataFlowAnalysisContext.InterproceduralAnalysisData != null) + { + using var entitiesToFilterBuilder = PooledHashSet.GetInstance(); + var copyValue = returnValueAndPredicateKind.Value.Value; + var copyValueEntities = copyValue.AnalysisEntities; + + foreach (var entity in copyValueEntities) + { + if (ShouldStopTrackingEntityAtExit(entity)) + { + // Stop tracking entity that is now out of scope. + entitiesToFilterBuilder.Add(entity); + + // Additionally, stop tracking all the child entities if the entity type has value copy semantics. + if (entity.Type.HasValueCopySemantics()) + { + var childEntities = copyValueEntities.Where(e => IsChildAnalysisEntity(e, ancestorEntity: entity)); + entitiesToFilterBuilder.AddRange(childEntities); + } + } + } + + if (entitiesToFilterBuilder.Count > 0) + { + copyValue = entitiesToFilterBuilder.Count == copyValueEntities.Count ? + CopyAbstractValue.Unknown : + copyValue.WithEntitiesRemoved(entitiesToFilterBuilder); + } + + return (copyValue, returnValueAndPredicateKind.Value.PredicateValueKind); + } + + return returnValueAndPredicateKind; + } + + #region Interprocedural analysis + + protected override void ApplyInterproceduralAnalysisResultCore(CopyAnalysisData resultData) + { + using var mergedData = GetClonedAnalysisData(resultData); + ApplyMissingCurrentAnalysisDataCore(mergedData, predicate: null); + CurrentAnalysisData.CoreAnalysisData.Clear(); + CurrentAnalysisData.CoreAnalysisData.AddRange(mergedData.CoreAnalysisData); + } + + private void ApplyMissingCurrentAnalysisDataCore(CopyAnalysisData mergedData, Func? predicate) + { + using var processedEntities = PooledHashSet.GetInstance(); + foreach (var kvp in CurrentAnalysisData.CoreAnalysisData) + { + var key = kvp.Key; + var copyValue = kvp.Value; + if (mergedData.CoreAnalysisData.ContainsKey(key) || + (predicate != null && !predicate(key)) || + !processedEntities.Add(key)) + { + continue; + } + + if (predicate != null && copyValue.AnalysisEntities.Count > 1) + { + var entitiesToRemove = copyValue.AnalysisEntities.Where(entity => key != entity && !predicate(entity)); + if (entitiesToRemove.Any()) + { + copyValue = copyValue.WithEntitiesRemoved(entitiesToRemove); + } + } + + Debug.Assert(copyValue.AnalysisEntities.Contains(key)); + Debug.Assert(predicate == null || copyValue.AnalysisEntities.All(predicate)); + mergedData.SetAbstactValueForEntities(copyValue, entityBeingAssigned: null); + processedEntities.AddRange(copyValue.AnalysisEntities); + } + + AssertValidCopyAnalysisData(mergedData); + } + + protected override CopyAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) + { + using var processedEntities = PooledHashSet.GetInstance(); + var analysisData = new CopyAnalysisData(); + foreach (var entity in withEntities) + { + if (processedEntities.Add(entity)) + { + var copyValue = GetAbstractValue(entity); + analysisData.SetAbstactValueForEntities(copyValue, entityBeingAssigned: null); + processedEntities.AddRange(copyValue.AnalysisEntities); + } + } + + AssertValidCopyAnalysisData(analysisData); + return analysisData; + } + + protected override CopyAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + { + copyValues = CurrentAnalysisData.CoreAnalysisData; + var initialAnalysisData = base.GetInitialInterproceduralAnalysisData(invokedMethod, invocationInstance, + thisOrMeInstanceForCaller, argumentValuesMap, pointsToValues, copyValues, valueContentValues, + isLambdaOrLocalFunction, hasParameterWithDelegateType); + AssertValidCopyAnalysisData(initialAnalysisData); + return initialAnalysisData; + } + + #endregion + + #region Visitor overrides + public override CopyAbstractValue DefaultVisit(IOperation operation, object? argument) + { + _ = base.DefaultVisit(operation, argument); + return CopyAbstractValue.Unknown; + } + + public override CopyAbstractValue VisitConversion(IConversionOperation operation, object? argument) + { + var operandValue = Visit(operation.Operand, argument); + + if (TryInferConversion(operation, out var conversionInference) && + FlowConversionOperandValue(conversionInference, operation.Type)) + { + return operandValue; + } + + return CopyAbstractValue.Unknown; + } + + public override CopyAbstractValue GetAssignedValueForPattern(IIsPatternOperation operation, CopyAbstractValue operandValue) + { + if (TryInferConversion(operation, out var conversionInference)) + { + if (FlowConversionOperandValue(conversionInference, targetType: operation.Pattern.GetPatternType())) + { + return operandValue; + } + } + + return CopyAbstractValue.Unknown; + } + + private static bool FlowConversionOperandValue(ConversionInference inference, ITypeSymbol? targetType) + { + Debug.Assert(!inference.AlwaysSucceed || !inference.AlwaysFail); + + // Flow the copy value of the operand for boxing and unboxing conversions. + if (inference.IsBoxing || inference.IsUnboxing) + { + return true; + } + + // Otherwise, flow the copy value of the operand to the converted operation if conversion may succeed. + if (!inference.AlwaysFail) + { + // For try cast, also ensure conversion always succeeds before flowing copy value. + // TODO: For direct cast, we should check if conversion is implicit. + // For now, we only flow values for reference type direct cast conversions. + if (inference.IsTryCast && inference.AlwaysSucceed || + !inference.IsTryCast && targetType?.IsReferenceType == true) + { + return true; + } + } + + return false; + } + + protected override CopyAbstractValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument) + { + var value = base.VisitAssignmentOperation(operation, argument); + if (AnalysisEntityFactory.TryCreate(operation.Target, out var analysisEntity)) + { + return GetAbstractValue(analysisEntity); + } + + return value; + } + + #endregion + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CoreCopyAnalysisDataDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CoreCopyAnalysisDataDomain.cs new file mode 100644 index 0000000000000..e0d0345e6ad65 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.CoreCopyAnalysisDataDomain.cs @@ -0,0 +1,68 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CoreCopyAnalysisData = DictionaryAnalysisData; + using CopyAnalysisResult = DataFlowAnalysisResult; + + public partial class CopyAnalysis : ForwardDataFlowAnalysis + { + /// + /// An abstract analysis domain implementation for . + /// + private sealed class CoreCopyAnalysisDataDomain : MapAbstractDomain + { + private readonly Func _getDefaultCopyValue; + + public CoreCopyAnalysisDataDomain(AbstractValueDomain valueDomain, Func getDefaultCopyValue) + : base(valueDomain) + { + _getDefaultCopyValue = getDefaultCopyValue; + } + +#pragma warning disable CA1725 // Parameter names should match base declaration + public override CoreCopyAnalysisData Merge(CoreCopyAnalysisData map1, CoreCopyAnalysisData map2) +#pragma warning restore CA1725 // Parameter names should match base declaration + { + CopyAnalysisData.AssertValidCopyAnalysisData(map1); + CopyAnalysisData.AssertValidCopyAnalysisData(map2); + + var result = new DictionaryAnalysisData(); + foreach (var kvp in map1) + { + var key = kvp.Key; + var value1 = kvp.Value; + + // If the key exists in both maps, use the merged value. + // Otherwise, use the default value. + CopyAbstractValue mergedValue; + if (map2.TryGetValue(key, out var value2)) + { + mergedValue = ValueDomain.Merge(value1, value2); + } + else + { + mergedValue = _getDefaultCopyValue(key); + } + + result.Add(key, mergedValue); + } + + foreach (var kvp in map2) + { + if (!result.ContainsKey(kvp.Key)) + { + result.Add(kvp.Key, _getDefaultCopyValue(kvp.Key)); + } + } + + CopyAnalysisData.AssertValidCopyAnalysisData(result); + return result; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.cs new file mode 100644 index 0000000000000..e98f1481819e4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysis.cs @@ -0,0 +1,67 @@ +// 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.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + + /// + /// Dataflow analysis to track instances that share the same value. + /// + public partial class CopyAnalysis : ForwardDataFlowAnalysis + { + internal static readonly AbstractValueDomain ValueDomainInstance = CopyAbstractValueDomain.Default; + + private CopyAnalysis(CopyDataFlowOperationVisitor operationVisitor) + : base(operationVisitor.AnalysisDomain, operationVisitor) + { + } + + public static CopyAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + bool pessimisticAnalysis = true, + PointsToAnalysisKind pointsToAnalysisKind = PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties, + bool exceptionPathsAnalysis = false) + { + if (cfg == null) + { + Debug.Fail("Expected non-null CFG"); + return null; + } + + var pointsToAnalysisResult = pointsToAnalysisKind != PointsToAnalysisKind.None ? + PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, + wellKnownTypeProvider, pointsToAnalysisKind, interproceduralAnalysisConfig, + interproceduralAnalysisPredicate, pessimisticAnalysis, performCopyAnalysis: false, exceptionPathsAnalysis) : + null; + var analysisContext = CopyAnalysisContext.Create(ValueDomainInstance, wellKnownTypeProvider, + cfg, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, exceptionPathsAnalysis, pointsToAnalysisResult, + TryGetOrComputeResultForAnalysisContext, interproceduralAnalysisPredicate); + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static CopyAnalysisResult? TryGetOrComputeResultForAnalysisContext(CopyAnalysisContext analysisContext) + { + var operationVisitor = new CopyDataFlowOperationVisitor(analysisContext); + var copyAnalysis = new CopyAnalysis(operationVisitor); + return copyAnalysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + protected override CopyAnalysisResult ToResult(CopyAnalysisContext analysisContext, CopyAnalysisResult dataFlowAnalysisResult) => dataFlowAnalysisResult; + protected override CopyBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, CopyAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisContext.cs new file mode 100644 index 0000000000000..127269380daa6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisContext.cs @@ -0,0 +1,82 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralCopyAnalysisData = InterproceduralAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + public sealed class CopyAnalysisContext : AbstractDataFlowAnalysisContext + { + private CopyAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralCopyAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, + predicateAnalysis: true, exceptionPathsAnalysis, copyAnalysisResult: null, pointsToAnalysisResult, valueContentAnalysisResult: null, + tryGetOrComputeAnalysisResult, parentControlFlowGraph, interproceduralAnalysisData, interproceduralAnalysisPredicate) + { + } + + internal static CopyAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + { + return new CopyAnalysisContext(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, + interproceduralAnalysisConfig, pessimisticAnalysis, exceptionPathsAnalysis, pointsToAnalysisResult, tryGetOrComputeAnalysisResult, + parentControlFlowGraph: null, interproceduralAnalysisData: null, interproceduralAnalysisPredicate); + } + + public override CopyAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralCopyAnalysisData? interproceduralAnalysisData) + { + return new CopyAnalysisContext(ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions, InterproceduralAnalysisConfiguration, + PessimisticAnalysis, ExceptionPathsAnalysis, pointsToAnalysisResult, TryGetOrComputeAnalysisResult, ControlFlowGraph, interproceduralAnalysisData, + InterproceduralAnalysisPredicate); + } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisData.cs new file mode 100644 index 0000000000000..542e407103605 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyAnalysisData.cs @@ -0,0 +1,242 @@ +// 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.Diagnostics; +using System.Linq; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + using static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis.CopyAnalysis; + using CoreCopyAnalysisData = DictionaryAnalysisData; + + /// + /// Aggregated copy analysis data tracked by . + /// Contains the for entity copy values and + /// the predicated copy values based on true/false runtime values of predicated entities. + /// + public sealed class CopyAnalysisData : AnalysisEntityBasedPredicateAnalysisData + { + internal CopyAnalysisData() + { + } + + internal CopyAnalysisData(IDictionary fromData) + : base(fromData) + { + AssertValidCopyAnalysisData(); + } + + private CopyAnalysisData(CopyAnalysisData fromData) + : base(fromData) + { + AssertValidCopyAnalysisData(); + } + + private CopyAnalysisData(CopyAnalysisData data1, CopyAnalysisData data2, MapAbstractDomain coreDataAnalysisDomain) + : base(data1, data2, coreDataAnalysisDomain) + { + AssertValidCopyAnalysisData(); + } + + protected override AbstractValueDomain ValueDomain => ValueDomainInstance; + public override AnalysisEntityBasedPredicateAnalysisData Clone() => new CopyAnalysisData(this); + + public override int Compare(AnalysisEntityBasedPredicateAnalysisData other, MapAbstractDomain coreDataAnalysisDomain) + => BaseCompareHelper(other, coreDataAnalysisDomain); + + public override AnalysisEntityBasedPredicateAnalysisData WithMergedData(AnalysisEntityBasedPredicateAnalysisData data, MapAbstractDomain coreDataAnalysisDomain) + { + Debug.Assert(IsReachableBlockData || !data.IsReachableBlockData); + return new CopyAnalysisData(this, (CopyAnalysisData)data, coreDataAnalysisDomain); + } + + /// + /// Updates the copy values for all entities that are part of the given set, + /// i.e. . + /// We do not support the overload + /// that updates copy value for each individual entity. + /// + internal void SetAbstactValueForEntities(CopyAbstractValue copyValue, AnalysisEntity? entityBeingAssigned) + { + foreach (var entity in copyValue.AnalysisEntities) + { + // If we have any predicate data based on the previous value of this entity, + // and we are changing the copy value for an assignment (i.e. entity == entityBeingAssigned), + // we need to drop all the predicate data based on this entity. + if (entity == entityBeingAssigned && HasPredicatedDataForEntity(entity)) + { + StopTrackingPredicatedData(entity); + } + + // Remove all predicated values for this entity as we are going to set + // a new value in CoreAnalysisData below, which is non-predicated. + if (HasPredicatedData) + { + RemoveEntriesInPredicatedData(entity); + } + + // Finally, set the value in the core analysis data. + CoreAnalysisData[entity] = copyValue; + } + } + + public override void SetAbstractValue(AnalysisEntity key, CopyAbstractValue value) + { + throw new NotSupportedException("Use SetAbstactValueForEntities API"); + } + + protected override void RemoveEntryInPredicatedData(AnalysisEntity key, CoreCopyAnalysisData predicatedData) + { + Debug.Assert(HasPredicatedData); + + var hasEntry = predicatedData.TryGetValue(key, out var value); + base.RemoveEntryInPredicatedData(key, predicatedData); + + // If we are removing an entity from predicated data, we need to adjust the copy values of its copy entities. + if (hasEntry && value!.AnalysisEntities.Count > 1) + { + var newValueForOldCopyEntities = value.WithEntityRemoved(key); + if (newValueForOldCopyEntities.AnalysisEntities.Count == 1) + { + predicatedData.Remove(newValueForOldCopyEntities.AnalysisEntities.Single()); + } + else + { + foreach (var copyEntity in newValueForOldCopyEntities.AnalysisEntities) + { + predicatedData[copyEntity] = newValueForOldCopyEntities; + } + } + } + } + + protected override void ApplyPredicatedData(CoreCopyAnalysisData coreAnalysisData, CoreCopyAnalysisData predicatedData) + { + if (predicatedData.Count == 0) + { + return; + } + +#if DEBUG + var originalCoreAnalysisData = new Dictionary(coreAnalysisData); +#endif + + AssertValidCopyAnalysisData(coreAnalysisData); + AssertValidCopyAnalysisData(predicatedData); + + // Applying predicated copy data to current copy analysis data needs us to merge the copy sets of an entity from both the maps. + foreach (var kvp in predicatedData) + { + var predicatedValue = kvp.Value; + + // Check if the entity has a copy value in both the predicated and current copy analysis data. + if (coreAnalysisData.TryGetValue(kvp.Key, out var currentValue)) + { + var newCopyEntities = currentValue.AnalysisEntities; + var newKind = currentValue.Kind; + foreach (var predicatedCopyEntity in predicatedValue.AnalysisEntities) + { + // Predicate copy value has an entity which is not part of the current copy value. + // Include this entity and it's copy entities in the new copy value. + if (!newCopyEntities.Contains(predicatedCopyEntity)) + { + if (coreAnalysisData.TryGetValue(predicatedCopyEntity, out var predicatedCopyEntityValue)) + { + newCopyEntities = newCopyEntities.Union(predicatedCopyEntityValue.AnalysisEntities); + newKind = newKind.MergeIfBothKnown(predicatedCopyEntityValue.Kind); + } + else + { + newCopyEntities = newCopyEntities.Add(predicatedCopyEntity); + } + } + } + + // Check if we need to change the current copy value. + if (newCopyEntities.Count != currentValue.AnalysisEntities.Count) + { + var newCopyValue = new CopyAbstractValue(newCopyEntities, newKind); + foreach (var copyEntity in newCopyEntities) + { + coreAnalysisData[copyEntity] = newCopyValue; + } + } + else + { + Debug.Assert(newCopyEntities.SetEquals(currentValue.AnalysisEntities)); + } + } + else + { + // Predicated copy entity has no entry in the current copy analysis data, so just add the entry. + coreAnalysisData[kvp.Key] = kvp.Value; + } + } + + // Ensure that applying predicated data to the current copy data can only increase the copy value entities in the current copy data. + Debug.Assert(predicatedData.All(kvp => kvp.Value.AnalysisEntities.IsSubsetOf(coreAnalysisData[kvp.Key].AnalysisEntities))); + AssertValidCopyAnalysisData(coreAnalysisData); + } + + public override void Reset(Func getResetValue) + { + this.AssertValidCopyAnalysisData(); + base.Reset(getResetValue); + this.AssertValidCopyAnalysisData(); + } + + [Conditional("DEBUG")] + internal void AssertValidCopyAnalysisData(Func? tryGetDefaultCopyValue = null, bool initializingParameters = false) + { + AssertValidCopyAnalysisData(CoreAnalysisData, tryGetDefaultCopyValue, initializingParameters); + AssertValidPredicatedAnalysisData(map => AssertValidCopyAnalysisData(map, tryGetDefaultCopyValue, initializingParameters)); + } + + [Conditional("DEBUG")] + internal static void AssertValidCopyAnalysisData( + IDictionary map, + Func? tryGetDefaultCopyValue = null, + bool initializingParameters = false) + { + if (map is CoreCopyAnalysisData coreCopyAnalysisData) + { + Debug.Assert(!coreCopyAnalysisData.IsDisposed); + } + + foreach (var kvp in map) + { + AssertValidCopyAnalysisEntity(kvp.Key); + Debug.Assert(kvp.Value.AnalysisEntities.Contains(kvp.Key)); + foreach (var analysisEntity in kvp.Value.AnalysisEntities) + { + AssertValidCopyAnalysisEntity(analysisEntity); + Debug.Assert(map[analysisEntity] == kvp.Value); + } + + // Validate consistency for all address shared values, if we are not in + // the middle of initializing parameter input values with address shared entities. + if (!initializingParameters) + { + var defaultCopyValue = tryGetDefaultCopyValue?.Invoke(kvp.Key); + if (defaultCopyValue != null) + { + foreach (var defaultCopyValyeEntity in defaultCopyValue.AnalysisEntities) + { + Debug.Assert(kvp.Value.AnalysisEntities.Contains(defaultCopyValyeEntity)); + Debug.Assert(map.ContainsKey(defaultCopyValyeEntity)); + } + } + } + } + } + + [Conditional("DEBUG")] + private static void AssertValidCopyAnalysisEntity(AnalysisEntity analysisEntity) + { + Debug.Assert(!analysisEntity.HasUnknownInstanceLocation, "Don't track entities if do not know about it's instance location"); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyBlockAnalysisResult.cs new file mode 100644 index 0000000000000..c259a3076e5ed --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/CopyBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.CopyAnalysis +{ + /// + /// Result from execution of on a basic block. + /// It store copy values for each at the start and end of the basic block. + /// + public sealed class CopyBlockAnalysisResult : AbstractBlockAnalysisResult + { + internal CopyBlockAnalysisResult(BasicBlock basicBlock, CopyAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.CoreAnalysisData.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + IsReachable = blockAnalysisData?.IsReachableBlockData ?? true; + } + + public ImmutableDictionary Data { get; } + public bool IsReachable { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/SetCopyAbstractValuePredicateKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/SetCopyAbstractValuePredicateKind.cs new file mode 100644 index 0000000000000..800a75074cebd --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/CopyAnalysis/SetCopyAbstractValuePredicateKind.cs @@ -0,0 +1,21 @@ +// 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.FlowAnalysis.DataFlow.CopyAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + + public partial class CopyAnalysis : ForwardDataFlowAnalysis + { + /// + /// Predicate kind for + /// to indicate if the copy equality check for the SetAbstractValue call is a reference compare or a value compare operation. + /// + internal enum SetCopyAbstractValuePredicateKind + { + ValueCompare, + ReferenceCompare + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValue.cs new file mode 100644 index 0000000000000..5a78e9ab4ddff --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValue.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + /// + /// Abstract dispose data tracked by . + /// It contains the set of s that dispose an associated disposable and + /// the dispose . + /// + public class DisposeAbstractValue : CacheBasedEquatable + { + public static readonly DisposeAbstractValue NotDisposable = new(DisposeAbstractValueKind.NotDisposable); + public static readonly DisposeAbstractValue Invalid = new(DisposeAbstractValueKind.Invalid); + public static readonly DisposeAbstractValue NotDisposed = new(DisposeAbstractValueKind.NotDisposed); + public static readonly DisposeAbstractValue Unknown = new(DisposeAbstractValueKind.Unknown); + + private DisposeAbstractValue(DisposeAbstractValueKind kind) + : this(ImmutableHashSet.Empty, kind) + { + Debug.Assert(kind != DisposeAbstractValueKind.Disposed); + } + + internal DisposeAbstractValue(ImmutableHashSet disposingOrEscapingOperations, DisposeAbstractValueKind kind) + { + VerifyArguments(disposingOrEscapingOperations, kind); + DisposingOrEscapingOperations = disposingOrEscapingOperations; + Kind = kind; + } + + internal DisposeAbstractValue WithNewDisposingOperation(IOperation disposingOperation) + { + Debug.Assert(Kind != DisposeAbstractValueKind.NotDisposable); + + return new DisposeAbstractValue(DisposingOrEscapingOperations.Add(disposingOperation), DisposeAbstractValueKind.Disposed); + } + + internal DisposeAbstractValue WithNewEscapingOperation(IOperation escapingOperation) + { + Debug.Assert(Kind != DisposeAbstractValueKind.NotDisposable); + Debug.Assert(Kind != DisposeAbstractValueKind.Unknown); + + return new DisposeAbstractValue(ImmutableHashSet.Create(escapingOperation), DisposeAbstractValueKind.Escaped); + } + + [Conditional("DEBUG")] + private static void VerifyArguments(ImmutableHashSet disposingOrEscapingOperations, DisposeAbstractValueKind kind) + { + switch (kind) + { + case DisposeAbstractValueKind.NotDisposable: + case DisposeAbstractValueKind.NotDisposed: + case DisposeAbstractValueKind.Invalid: + case DisposeAbstractValueKind.Unknown: + Debug.Assert(disposingOrEscapingOperations.IsEmpty); + break; + + case DisposeAbstractValueKind.Escaped: + case DisposeAbstractValueKind.Disposed: + case DisposeAbstractValueKind.MaybeDisposed: + Debug.Assert(!disposingOrEscapingOperations.IsEmpty); + break; + } + } + + public ImmutableHashSet DisposingOrEscapingOperations { get; } + public DisposeAbstractValueKind Kind { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(DisposingOrEscapingOperations)); + hashCode.Add(((int)Kind).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (DisposeAbstractValue)obj; + return HashUtilities.Combine(DisposingOrEscapingOperations) == HashUtilities.Combine(other.DisposingOrEscapingOperations) + && ((int)Kind).GetHashCode() == ((int)other.Kind).GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValueKind.cs new file mode 100644 index 0000000000000..9cbcea19d1191 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAbstractValueKind.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + /// + /// Abstract dispose value for / tracked by . + /// + public enum DisposeAbstractValueKind + { + /// + /// Indicates locations that are not disposable, e.g. value types, constants, etc. + /// + NotDisposable, + + /// + /// Indicates a value for disposable locations that are not feasible on the given program path. + /// For example, + /// + /// var x = flag ? new Disposable() : null; + /// if (x == null) + /// { + /// // Disposable allocation above cannot exist on this code path. + /// } + /// + /// + Invalid, + + /// + /// Indicates disposable locations that are not disposed. + /// + NotDisposed, + + /// + /// Indicates disposable locations that have escaped the declaring method's scope. + /// For example, a disposable allocation assigned to a field/property or + /// escaped as a return value for a function, or assigned to a ref or out parameter, etc. + /// + Escaped, + + /// + /// Indicates disposable locations that are either not disposed or escaped. + /// + NotDisposedOrEscaped, + + /// + /// Indicates disposable locations that are disposed. + /// + Disposed, + + /// + /// Indicates disposable locations that may be disposed on some program path(s). + /// + MaybeDisposed, + + /// + /// Indicates disposable locations whose dispose state is unknown. + /// + Unknown, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeAbstractValueDomain.cs new file mode 100644 index 0000000000000..b21f68dda9c1b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeAbstractValueDomain.cs @@ -0,0 +1,162 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + using DisposeAnalysisData = DictionaryAnalysisData; + + public partial class DisposeAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private sealed class DisposeAbstractValueDomain : AbstractValueDomain + { + public static DisposeAbstractValueDomain Default = new(); + private readonly SetAbstractDomain _disposingOperationsDomain = SetAbstractDomain.Default; + + private DisposeAbstractValueDomain() { } + + public override DisposeAbstractValue Bottom => DisposeAbstractValue.NotDisposable; + + public override DisposeAbstractValue UnknownOrMayBeValue => DisposeAbstractValue.Unknown; + + public override int Compare(DisposeAbstractValue oldValue, DisposeAbstractValue newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (oldValue.Kind == newValue.Kind) + { + return _disposingOperationsDomain.Compare(oldValue.DisposingOrEscapingOperations, newValue.DisposingOrEscapingOperations); + } + else if (oldValue.Kind < newValue.Kind || + newValue.Kind == DisposeAbstractValueKind.Invalid || + newValue.Kind == DisposeAbstractValueKind.Disposed) + { + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + public override DisposeAbstractValue Merge(DisposeAbstractValue value1, DisposeAbstractValue value2) + { + if (value1 == null) + { + return value2; + } + else if (value2 == null) + { + return value1; + } + else if (value1.Kind == DisposeAbstractValueKind.Invalid) + { + return value2; + } + else if (value2.Kind == DisposeAbstractValueKind.Invalid) + { + return value1; + } + else if (value1.Kind == DisposeAbstractValueKind.NotDisposable || value2.Kind == DisposeAbstractValueKind.NotDisposable) + { + return DisposeAbstractValue.NotDisposable; + } + else if (value1.Kind == DisposeAbstractValueKind.Unknown || value2.Kind == DisposeAbstractValueKind.Unknown) + { + return DisposeAbstractValue.Unknown; + } + else if (value1.Kind == DisposeAbstractValueKind.NotDisposed && value2.Kind == DisposeAbstractValueKind.NotDisposed) + { + return DisposeAbstractValue.NotDisposed; + } + + var mergedDisposingOperations = _disposingOperationsDomain.Merge(value1.DisposingOrEscapingOperations, value2.DisposingOrEscapingOperations); + Debug.Assert(!mergedDisposingOperations.IsEmpty); + return new DisposeAbstractValue(mergedDisposingOperations, GetMergedKind()); + + // Local functions. + DisposeAbstractValueKind GetMergedKind() + { + Debug.Assert(!value1.DisposingOrEscapingOperations.IsEmpty || !value2.DisposingOrEscapingOperations.IsEmpty); + + if (value1.Kind == value2.Kind) + { + return value1.Kind; + } + else if (value1.Kind == DisposeAbstractValueKind.MaybeDisposed || + value2.Kind == DisposeAbstractValueKind.MaybeDisposed) + { + return DisposeAbstractValueKind.MaybeDisposed; + } + + switch (value1.Kind) + { + case DisposeAbstractValueKind.NotDisposed: + switch (value2.Kind) + { + case DisposeAbstractValueKind.Escaped: + case DisposeAbstractValueKind.NotDisposedOrEscaped: + return DisposeAbstractValueKind.NotDisposedOrEscaped; + + case DisposeAbstractValueKind.Disposed: + return DisposeAbstractValueKind.MaybeDisposed; + } + + break; + + case DisposeAbstractValueKind.Escaped: + switch (value2.Kind) + { + case DisposeAbstractValueKind.NotDisposed: + case DisposeAbstractValueKind.NotDisposedOrEscaped: + return DisposeAbstractValueKind.NotDisposedOrEscaped; + + case DisposeAbstractValueKind.Disposed: + return DisposeAbstractValueKind.Disposed; + } + + break; + + case DisposeAbstractValueKind.NotDisposedOrEscaped: + switch (value2.Kind) + { + case DisposeAbstractValueKind.NotDisposed: + case DisposeAbstractValueKind.Escaped: + return DisposeAbstractValueKind.NotDisposedOrEscaped; + + case DisposeAbstractValueKind.Disposed: + return DisposeAbstractValueKind.MaybeDisposed; + } + + break; + + case DisposeAbstractValueKind.Disposed: + switch (value2.Kind) + { + case DisposeAbstractValueKind.Escaped: + return DisposeAbstractValueKind.Disposed; + + case DisposeAbstractValueKind.NotDisposed: + case DisposeAbstractValueKind.NotDisposedOrEscaped: + return DisposeAbstractValueKind.MaybeDisposed; + } + + break; + } + + Debug.Fail($"Unhandled dispose value kind merge: {value1.Kind} and {value2.Kind}"); + return DisposeAbstractValueKind.MaybeDisposed; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..0519ce2cd9c10 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.DisposeDataFlowOperationVisitor.cs @@ -0,0 +1,495 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + using DisposeAnalysisData = DictionaryAnalysisData; + + public partial class DisposeAnalysis : ForwardDataFlowAnalysis + { + /// + /// Operation visitor to flow the dispose values across a given statement in a basic block. + /// + private sealed class DisposeDataFlowOperationVisitor : AbstractLocationDataFlowOperationVisitor + { + private readonly Dictionary? _trackedInstanceFieldLocations; + private ImmutableHashSet DisposeOwnershipTransferLikelyTypes => DataFlowAnalysisContext.DisposeOwnershipTransferLikelyTypes; + private bool DisposeOwnershipTransferAtConstructor => DataFlowAnalysisContext.DisposeOwnershipTransferAtConstructor; + private bool DisposeOwnershipTransferAtMethodCall => DataFlowAnalysisContext.DisposeOwnershipTransferAtMethodCall; + + public DisposeDataFlowOperationVisitor(DisposeAnalysisContext analysisContext) + : base(analysisContext) + { + Debug.Assert(IDisposableNamedType != null); + Debug.Assert(CollectionNamedTypes.All(ct => ct.TypeKind == TypeKind.Interface)); + Debug.Assert(analysisContext.DisposeOwnershipTransferLikelyTypes != null); + Debug.Assert(analysisContext.PointsToAnalysisResult != null); + + if (analysisContext.TrackInstanceFields) + { + _trackedInstanceFieldLocations = new Dictionary(); + } + } + + public ImmutableDictionary TrackedInstanceFieldPointsToMap + { + get + { + if (_trackedInstanceFieldLocations == null) + { + throw new InvalidOperationException(); + } + + return _trackedInstanceFieldLocations.ToImmutableDictionary(); + } + } + + protected override DisposeAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => DisposeAbstractValue.NotDisposable; + + protected override DisposeAbstractValue GetAbstractValue(AbstractLocation location) => CurrentAnalysisData.TryGetValue(location, out var value) ? value : ValueDomain.UnknownOrMayBeValue; + + protected override bool HasAnyAbstractValue(DisposeAnalysisData data) => data.Count > 0; + + protected override void SetAbstractValue(AbstractLocation location, DisposeAbstractValue value) + { + if (!location.IsNull && + location.LocationType != null && + (!location.LocationType.IsValueType || location.LocationType.IsRefLikeType) && + IsDisposable(location.LocationType)) + { + CurrentAnalysisData[location] = value; + } + } + + protected override void StopTrackingAbstractValue(AbstractLocation location) => CurrentAnalysisData.Remove(location); + + protected override void ResetCurrentAnalysisData() => ResetAnalysisData(CurrentAnalysisData); + + protected override DisposeAbstractValue HandleInstanceCreation(IOperation creation, PointsToAbstractValue instanceLocation, DisposeAbstractValue defaultValue) + { + if (ExecutingExceptionPathsAnalysisPostPass) + { + base.HandlePossibleThrowingOperation(creation); + } + + defaultValue = DisposeAbstractValue.NotDisposable; + var instanceType = creation.Type; + + if (!IsDisposable(instanceType) || + !IsCurrentBlockReachable()) + { + return defaultValue; + } + + // Handle special cases where we don't want to track the type although we know + // it is disposable (user option, special types...) + if (DataFlowAnalysisContext.IsDisposableTypeNotRequiringToBeDisposed(instanceType)) + { + return defaultValue; + } + + SetAbstractValue(instanceLocation, DisposeAbstractValue.NotDisposed); + return DisposeAbstractValue.NotDisposed; + } + + private void HandleDisposingOperation(IOperation disposingOperation, IOperation? disposedInstance) + { + if (disposedInstance == null || !IsDisposable(disposedInstance.Type)) + { + return; + } + + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(disposedInstance); + foreach (AbstractLocation location in instanceLocation.Locations) + { + if (CurrentAnalysisData.TryGetValue(location, out var currentDisposeValue)) + { + DisposeAbstractValue disposeValue = currentDisposeValue.WithNewDisposingOperation(disposingOperation); + SetAbstractValue(location, disposeValue); + } + } + } + + private void HandlePossibleInvalidatingOperation(IOperation invalidatedInstance) + { + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(invalidatedInstance); + foreach (AbstractLocation location in instanceLocation.Locations) + { + if (CurrentAnalysisData.TryGetValue(location, out var currentDisposeValue) && + currentDisposeValue.Kind != DisposeAbstractValueKind.NotDisposable) + { + SetAbstractValue(location, DisposeAbstractValue.Invalid); + } + } + } + + private void HandlePossibleEscapingOperation(IOperation escapingOperation, ImmutableHashSet escapedLocations) + { + foreach (AbstractLocation escapedLocation in escapedLocations) + { + if (CurrentAnalysisData.TryGetValue(escapedLocation, out var currentDisposeValue) && + currentDisposeValue.Kind != DisposeAbstractValueKind.Unknown) + { + DisposeAbstractValue newDisposeValue = currentDisposeValue.WithNewEscapingOperation(escapingOperation); + SetAbstractValue(escapedLocation, newDisposeValue); + } + } + } + + protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, DisposeAbstractValue value) + { + // Escaping from array element assignment is handled in PointsTo analysis. + // We do not need to do anything here. + } + + protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, DisposeAbstractValue assignedValue, bool mayBeAssignment = false) + { + // Assignments should automatically transfer PointsTo value. + // We do not need to do anything here. + } + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, DisposeAbstractValue assignedValue) + { + // Assigning to tuple elements should automatically transfer PointsTo value. + // We do not need to do anything here. + } + + protected override void SetValueForParameterPointsToLocationOnEntry(IParameterSymbol parameter, PointsToAbstractValue pointsToAbstractValue) + { + if (DisposeOwnershipTransferLikelyTypes.Contains(parameter.Type) || + (DisposeOwnershipTransferAtConstructor && parameter.ContainingSymbol.IsConstructor())) + { + SetAbstractValue(pointsToAbstractValue, DisposeAbstractValue.NotDisposed); + } + } + + protected override void EscapeValueForParameterPointsToLocationOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity, ImmutableHashSet escapedLocations) + { + Debug.Assert(!escapedLocations.IsEmpty); + Debug.Assert(parameter.RefKind != RefKind.None); + var escapedDisposableLocations = + escapedLocations.Where(l => IsDisposable(l.LocationType)); + SetAbstractValue(escapedDisposableLocations, ValueDomain.UnknownOrMayBeValue); + } + + protected override DisposeAbstractValue ComputeAnalysisValueForEscapedRefOrOutArgument(IArgumentOperation operation, DisposeAbstractValue defaultValue) + { + Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out); + + // Special case: don't flag "out" arguments for "bool TryGetXXX(..., out value)" invocations. + if (operation.Parent is IInvocationOperation invocation && + invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean && + invocation.TargetMethod.Name.StartsWith("TryGet", StringComparison.Ordinal) && + invocation.Arguments[^1] == operation) + { + return DisposeAbstractValue.NotDisposable; + } + + return base.ComputeAnalysisValueForEscapedRefOrOutArgument(operation, defaultValue); + } + + protected override DisposeAnalysisData MergeAnalysisData(DisposeAnalysisData value1, DisposeAnalysisData value2) + => DisposeAnalysisDomainInstance.Merge(value1, value2); + protected override void UpdateValuesForAnalysisData(DisposeAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData, CurrentAnalysisData); + protected override DisposeAnalysisData GetClonedAnalysisData(DisposeAnalysisData analysisData) + => GetClonedAnalysisDataHelper(CurrentAnalysisData); + public override DisposeAnalysisData GetEmptyAnalysisData() + => GetEmptyAnalysisDataHelper(); + protected override DisposeAnalysisData GetExitBlockOutputData(DisposeAnalysisResult analysisResult) + => GetClonedAnalysisDataHelper(analysisResult.ExitBlockOutput.Data); + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(DisposeAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException, CurrentAnalysisData); + protected override bool Equals(DisposeAnalysisData value1, DisposeAnalysisData value2) + => EqualsHelper(value1, value2); + + #region Visitor methods + public override DisposeAbstractValue DefaultVisit(IOperation operation, object? argument) + { + _ = base.DefaultVisit(operation, argument); + return DisposeAbstractValue.NotDisposable; + } + + public override DisposeAbstractValue Visit(IOperation? operation, object? argument) + { + var value = base.Visit(operation, argument); + + if (operation != null) + HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation)); + + return value; + } + + protected override void HandlePossibleThrowingOperation(IOperation operation) + { + // Handle possible throwing operation. + // Note that we handle the cases for object creation and method invocation + // separately as these also lead to NotDisposed allocations, but + // they should not be considered as part of current state when possible exception occurs. + if (operation != null && + operation.Kind != OperationKind.ObjectCreation && + (operation is not IInvocationOperation invocation || + invocation.TargetMethod.IsLambdaOrLocalFunctionOrDelegate())) + { + base.HandlePossibleThrowingOperation(operation); + } + } + + // FxCop compat: Catches things like static calls to File.Open() and Create() + private static bool IsDisposableCreationSpecialCase(IMethodSymbol targetMethod) + => targetMethod.IsStatic && + (targetMethod.Name.StartsWith("create", StringComparison.OrdinalIgnoreCase) || + targetMethod.Name.StartsWith("open", StringComparison.OrdinalIgnoreCase)); + + public override DisposeAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + DisposeAbstractValue defaultValue) + { + var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, + visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + + var disposeMethodKind = GetDisposeMethodKind(method); + switch (disposeMethodKind) + { + case DisposeMethodKind.Dispose: + case DisposeMethodKind.DisposeBool: + case DisposeMethodKind.DisposeAsync: + HandleDisposingOperation(originalOperation, visitedInstance); + return value; + + case DisposeMethodKind.Close: + // FxCop compat: Calling "this.Close" shouldn't count as disposing the object within the implementation of Dispose. + if (visitedInstance?.Kind != OperationKind.InstanceReference) + { + goto case DisposeMethodKind.Dispose; + } + + break; + + default: + // FxCop compat: Catches things like static calls to File.Open() and Create() + if (IsDisposableCreationSpecialCase(method)) + { + var instanceLocation = GetPointsToAbstractValue(originalOperation); + return HandleInstanceCreation(originalOperation, instanceLocation, value); + } + + break; + } + + if (ExecutingExceptionPathsAnalysisPostPass) + { + base.HandlePossibleThrowingOperation(originalOperation); + } + + return value; + } + + protected override void ApplyInterproceduralAnalysisResult( + DisposeAnalysisData resultData, + bool isLambdaOrLocalFunction, + bool hasDelegateTypeArgument, + DisposeAnalysisResult analysisResult) + { + base.ApplyInterproceduralAnalysisResult(resultData, isLambdaOrLocalFunction, hasDelegateTypeArgument, analysisResult); + + // Apply the tracked instance field locations from interprocedural analysis. + if (_trackedInstanceFieldLocations != null && + analysisResult.TrackedInstanceFieldPointsToMap != null) + { + foreach (var (field, pointsToValue) in analysisResult.TrackedInstanceFieldPointsToMap) + { + if (!_trackedInstanceFieldLocations.ContainsKey(field)) + { + _trackedInstanceFieldLocations.Add(field, pointsToValue); + } + } + } + } + + protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) + { + base.PostProcessArgument(operation, isEscaped); + if (isEscaped) + { + // Discover if a disposable object is being passed into the creation method for this new disposable object + // and if the new disposable object assumes ownership of that passed in disposable object. + if (IsDisposeOwnershipTransfer()) + { + var pointsToValue = GetPointsToAbstractValue(operation.Value); + HandlePossibleEscapingOperation(operation, pointsToValue.Locations); + return; + } + else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && + operation.Parameter?.RefKind == RefKind.Out && + operation.Parent is IInvocationOperation invocation && + invocation.TargetMethod.ReturnType.SpecialType == SpecialType.System_Boolean) + { + // Case 1: + // // Assume 'obj' is not a valid object on the 'else' path. + // if (TryXXX(out IDisposable obj)) + // { + // obj.Dispose(); + // } + // + // return; + + // Case 2: + // if (!TryXXX(out IDisposable obj)) + // { + // return; // Assume 'obj' is not a valid object on this path. + // } + // + // obj.Dispose(); + + HandlePossibleInvalidatingOperation(operation); + return; + } + } + + // Ref or out argument values from callee might be escaped by assigning to field. + if (operation.Parameter?.RefKind is RefKind.Out or RefKind.Ref) + { + HandlePossibleEscapingOperation(operation, GetEscapedLocations(operation)); + } + + return; + + // Local functions. + bool IsDisposeOwnershipTransfer() + { + if (operation.Parameter == null || + operation.Parameter.RefKind == RefKind.Out) + { + // Out arguments are always owned by the caller. + return false; + } + + return operation.Parent switch + { + IObjectCreationOperation => DisposeOwnershipTransferAtConstructor || + DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), + + IInvocationOperation invocation => DisposeOwnershipTransferAtMethodCall || + IsDisposableCreationSpecialCase(invocation.TargetMethod) && DisposeOwnershipTransferLikelyTypes.Contains(operation.Parameter.Type), + + _ => false, + }; + } + } + + public override DisposeAbstractValue VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument); + if (_trackedInstanceFieldLocations != null && + !operation.Field.IsStatic && + operation.Instance?.Kind == OperationKind.InstanceReference) + { + var pointsToAbstractValue = GetPointsToAbstractValue(operation); + if (pointsToAbstractValue.Kind == PointsToAbstractValueKind.KnownLocations && + pointsToAbstractValue.Locations.Count == 1) + { + var location = pointsToAbstractValue.Locations.Single(); + if (location.IsAnalysisEntityDefaultLocation) + { + if (!_trackedInstanceFieldLocations.TryGetValue(operation.Field, out _)) + { + // First field reference on any control flow path. + // Create a default instance to represent the object referenced by the field at start of the method and + // check if the instance has NotDisposed state, indicating it is a disposable field that must be tracked. + if (HandleInstanceCreation(operation, pointsToAbstractValue, defaultValue: DisposeAbstractValue.NotDisposable) == DisposeAbstractValue.NotDisposed) + { + _trackedInstanceFieldLocations.Add(operation.Field, pointsToAbstractValue); + } + } + else if (!CurrentAnalysisData.ContainsKey(location)) + { + // This field has already started being tracked on a different control flow path. + // Process the default instance creation on this control flow path as well. + var disposedState = HandleInstanceCreation(operation, pointsToAbstractValue, DisposeAbstractValue.NotDisposable); + Debug.Assert(disposedState == DisposeAbstractValue.NotDisposed); + } + } + } + } + + return value; + } + + public override DisposeAbstractValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument) + { + var value = base.VisitBinaryOperatorCore(operation, argument); + + // Handle null-check for a disposable symbol on a control flow branch. + // var x = flag ? new Disposable() : null; + // if (x == null) + // { + // // Disposable allocation above cannot exist on this code path. + // } + // + + // if (x == null) + // { + // // This code path + // } + var isNullEqualsOnWhenTrue = FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue && + (operation.OperatorKind == BinaryOperatorKind.Equals || operation.OperatorKind == BinaryOperatorKind.ObjectValueEquals); + + // if (x != null) { ... } + // else + // { + // // This code path + // } + var isNullNotEqualsOnWhenFalse = FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && + (operation.OperatorKind == BinaryOperatorKind.NotEquals || operation.OperatorKind == BinaryOperatorKind.ObjectValueNotEquals); + + if (isNullEqualsOnWhenTrue || isNullNotEqualsOnWhenFalse) + { + if (GetNullAbstractValue(operation.RightOperand) == NullAbstractValue.Null) + { + // if (x == null) + HandlePossibleInvalidatingOperation(operation.LeftOperand); + } + else if (GetNullAbstractValue(operation.LeftOperand) == NullAbstractValue.Null) + { + // if (null == x) + HandlePossibleInvalidatingOperation(operation.RightOperand); + } + } + + return value; + } + + public override DisposeAbstractValue VisitIsNull(IIsNullOperation operation, object? argument) + { + var value = base.VisitIsNull(operation, argument); + + // Handle null-check for a disposable symbol on a control flow branch. + // See comments in VisitBinaryOperatorCore override above for further details. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + HandlePossibleInvalidatingOperation(operation.Operand); + } + + return value; + } + + #endregion + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.cs new file mode 100644 index 0000000000000..2809979ab4e9b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysis.cs @@ -0,0 +1,136 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + using DisposeAnalysisData = DictionaryAnalysisData; + using DisposeAnalysisDomain = MapAbstractDomain; + + /// + /// Dataflow analysis to track dispose state of / instances. + /// + public partial class DisposeAnalysis : ForwardDataFlowAnalysis + { + // Invoking an instance method may likely invalidate all the instance field analysis state, i.e. + // reference type fields might be re-assigned to point to different objects in the called method. + // An optimistic points to analysis assumes that the points to values of instance fields don't change on invoking an instance method. + // A pessimistic points to analysis resets all the instance state and assumes the instance field might point to any object, hence has unknown state. + // For dispose analysis, we want to perform an optimistic points to analysis as we assume a disposable field is not likely to be re-assigned to a separate object in helper method invocations in Dispose. + private const bool PessimisticAnalysis = false; + + internal static readonly DisposeAnalysisDomain DisposeAnalysisDomainInstance = new(DisposeAbstractValueDomain.Default); + + private DisposeAnalysis(DisposeAnalysisDomain analysisDomain, DisposeDataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + public static DisposeAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + WellKnownTypeProvider wellKnownTypeProvider, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + ImmutableHashSet disposeOwnershipTransferLikelyTypes, + PointsToAnalysisKind defaultPointsToAnalysisKind, + bool trackInstanceFields, + bool exceptionPathsAnalysis, + out PointsToAnalysisResult? pointsToAnalysisResult, + InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.ContextSensitive, + bool performCopyAnalysisIfNotUserConfigured = false, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, + bool defaultDisposeOwnershipTransferAtConstructor = false, + bool defaultDisposeOwnershipTransferAtMethodCall = false) + { + if (cfg == null) + { + throw new ArgumentNullException(nameof(cfg)); + } + + Debug.Assert(!analyzerOptions.IsConfiguredToSkipAnalysis(rule, owningSymbol, wellKnownTypeProvider.Compilation)); + + var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( + analyzerOptions, rule, cfg, wellKnownTypeProvider.Compilation, interproceduralAnalysisKind); + var disposeOwnershipTransferAtConstructor = analyzerOptions.GetDisposeOwnershipTransferAtConstructorOption( + rule, owningSymbol, wellKnownTypeProvider.Compilation, defaultValue: defaultDisposeOwnershipTransferAtConstructor); + var disposeOwnershipTransferAtMethodCall = analyzerOptions.GetDisposeOwnershipTransferAtMethodCall( + rule, owningSymbol, wellKnownTypeProvider.Compilation, defaultValue: defaultDisposeOwnershipTransferAtMethodCall); + + _ = DisposeAnalysisHelper.TryGetOrCreate(wellKnownTypeProvider.Compilation, out var disposeAnalysisHelper); + + return TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, interproceduralAnalysisConfig, + interproceduralAnalysisPredicate, disposeOwnershipTransferLikelyTypes, disposeOwnershipTransferAtConstructor, + disposeOwnershipTransferAtMethodCall, trackInstanceFields, exceptionPathsAnalysis, + pointsToAnalysisKind: analyzerOptions.GetPointsToAnalysisKindOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, + defaultPointsToAnalysisKind), + performCopyAnalysis: analyzerOptions.GetCopyAnalysisOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, + defaultValue: performCopyAnalysisIfNotUserConfigured), + isDisposableTypeNotRequiringToBeDisposed: (ITypeSymbol typeSymbol) => + disposeAnalysisHelper?.IsDisposableTypeNotRequiringToBeDisposed(typeSymbol) == true + || analyzerOptions.IsConfiguredToSkipAnalysis(rule, typeSymbol, owningSymbol, wellKnownTypeProvider.Compilation), + out pointsToAnalysisResult); + } + + private static DisposeAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + ImmutableHashSet disposeOwnershipTransferLikelyTypes, + bool disposeOwnershipTransferAtConstructor, + bool disposeOwnershipTransferAtMethodCall, + bool trackInstanceFields, + bool exceptionPathsAnalysis, + PointsToAnalysisKind pointsToAnalysisKind, + bool performCopyAnalysis, + Func isDisposableTypeNotRequiringToBeDisposed, + out PointsToAnalysisResult? pointsToAnalysisResult) + { + Debug.Assert(wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable, out _)); + + pointsToAnalysisResult = PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult( + cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, pointsToAnalysisKind, interproceduralAnalysisConfig, + interproceduralAnalysisPredicate, PessimisticAnalysis, performCopyAnalysis, exceptionPathsAnalysis); + if (pointsToAnalysisResult == null) + { + return null; + } + + var analysisContext = DisposeAnalysisContext.Create( + DisposeAbstractValueDomain.Default, wellKnownTypeProvider, cfg, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, + interproceduralAnalysisPredicate, PessimisticAnalysis, exceptionPathsAnalysis, pointsToAnalysisResult, + TryGetOrComputeResultForAnalysisContext, disposeOwnershipTransferLikelyTypes, disposeOwnershipTransferAtConstructor, + disposeOwnershipTransferAtMethodCall, trackInstanceFields, isDisposableTypeNotRequiringToBeDisposed); + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static DisposeAnalysisResult? TryGetOrComputeResultForAnalysisContext(DisposeAnalysisContext disposeAnalysisContext) + { + var operationVisitor = new DisposeDataFlowOperationVisitor(disposeAnalysisContext); + var disposeAnalysis = new DisposeAnalysis(DisposeAnalysisDomainInstance, operationVisitor); + return disposeAnalysis.TryGetOrComputeResultCore(disposeAnalysisContext, cacheResult: false); + } + + protected override DisposeAnalysisResult ToResult(DisposeAnalysisContext analysisContext, DataFlowAnalysisResult dataFlowAnalysisResult) + { + var operationVisitor = (DisposeDataFlowOperationVisitor)OperationVisitor; + var trackedInstanceFieldPointsToMap = analysisContext.TrackInstanceFields ? + operationVisitor.TrackedInstanceFieldPointsToMap : + null; + return new DisposeAnalysisResult(dataFlowAnalysisResult, trackedInstanceFieldPointsToMap); + } + + protected override DisposeBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, DictionaryAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisContext.cs new file mode 100644 index 0000000000000..fa4ea5c73741a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisContext.cs @@ -0,0 +1,131 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using DisposeAnalysisData = DictionaryAnalysisData; + using InterproceduralDisposeAnalysisData = InterproceduralAnalysisData, DisposeAnalysisContext, DisposeAbstractValue>; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + public sealed class DisposeAnalysisContext : AbstractDataFlowAnalysisContext + { + private DisposeAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ImmutableHashSet disposeOwnershipTransferLikelyTypes, + bool disposeOwnershipTransferAtConstructor, + bool disposeOwnershipTransferAtMethodCall, + bool trackInstanceFields, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralDisposeAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + Func isDisposableTypeNotRequiringToBeDisposed) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, + owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, + predicateAnalysis: false, + exceptionPathsAnalysis, + copyAnalysisResult: null, + pointsToAnalysisResult, + valueContentAnalysisResult: null, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph, + interproceduralAnalysisData, + interproceduralAnalysisPredicate) + { + DisposeOwnershipTransferLikelyTypes = disposeOwnershipTransferLikelyTypes; + DisposeOwnershipTransferAtConstructor = disposeOwnershipTransferAtConstructor; + DisposeOwnershipTransferAtMethodCall = disposeOwnershipTransferAtMethodCall; + TrackInstanceFields = trackInstanceFields; + IsDisposableTypeNotRequiringToBeDisposed = isDisposableTypeNotRequiringToBeDisposed; + } + + internal static DisposeAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ImmutableHashSet disposeOwnershipTransferLikelyTypes, + bool disposeOwnershipTransferAtConstructor, + bool disposeOwnershipTransferAtMethodCall, + bool trackInstanceFields, + Func isDisposableTypeNotRequiringToBeDisposed) + { + return new DisposeAnalysisContext( + valueDomain, wellKnownTypeProvider, controlFlowGraph, + owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, + exceptionPathsAnalysis, pointsToAnalysisResult, tryGetOrComputeAnalysisResult, + disposeOwnershipTransferLikelyTypes, disposeOwnershipTransferAtConstructor, disposeOwnershipTransferAtMethodCall, + trackInstanceFields, parentControlFlowGraph: null, interproceduralAnalysisData: null, + interproceduralAnalysisPredicate, isDisposableTypeNotRequiringToBeDisposed); + } + + public override DisposeAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralDisposeAnalysisData? interproceduralAnalysisData) + { + Debug.Assert(pointsToAnalysisResult != null); + Debug.Assert(copyAnalysisResult == null); + Debug.Assert(valueContentAnalysisResult == null); + + return new DisposeAnalysisContext(ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions, InterproceduralAnalysisConfiguration, PessimisticAnalysis, + ExceptionPathsAnalysis, pointsToAnalysisResult, TryGetOrComputeAnalysisResult, DisposeOwnershipTransferLikelyTypes, DisposeOwnershipTransferAtConstructor, + DisposeOwnershipTransferAtMethodCall, TrackInstanceFields, ControlFlowGraph, interproceduralAnalysisData, InterproceduralAnalysisPredicate, IsDisposableTypeNotRequiringToBeDisposed); + } + + internal ImmutableHashSet DisposeOwnershipTransferLikelyTypes { get; } + internal bool DisposeOwnershipTransferAtConstructor { get; } + internal bool DisposeOwnershipTransferAtMethodCall { get; } + internal bool TrackInstanceFields { get; } + internal Func IsDisposableTypeNotRequiringToBeDisposed { get; } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(TrackInstanceFields.GetHashCode()); + hashCode.Add(DisposeOwnershipTransferAtConstructor.GetHashCode()); + hashCode.Add(DisposeOwnershipTransferAtMethodCall.GetHashCode()); + hashCode.Add(HashUtilities.Combine(DisposeOwnershipTransferLikelyTypes)); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (DisposeAnalysisContext)obj; + return TrackInstanceFields.GetHashCode() == other.TrackInstanceFields.GetHashCode() + && DisposeOwnershipTransferAtConstructor.GetHashCode() == other.DisposeOwnershipTransferAtConstructor.GetHashCode() + && DisposeOwnershipTransferAtMethodCall.GetHashCode() == other.DisposeOwnershipTransferAtMethodCall.GetHashCode() + && HashUtilities.Combine(DisposeOwnershipTransferLikelyTypes) == HashUtilities.Combine(other.DisposeOwnershipTransferLikelyTypes); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs new file mode 100644 index 0000000000000..c3ab1697ceae3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisHelper.cs @@ -0,0 +1,225 @@ +// 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.Concurrent; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities +{ + /// + /// Helper for DisposeAnalysis. + /// + internal sealed class DisposeAnalysisHelper + { + private static readonly string[] s_disposeOwnershipTransferLikelyTypes = new string[] + { + "System.IO.Stream", + "System.IO.TextReader", + "System.IO.TextWriter", + "System.Resources.IResourceReader", + }; + private static readonly BoundedCacheWithFactory s_DisposeHelperCache = + new(); + + private static readonly ImmutableHashSet s_DisposableCreationKinds = ImmutableHashSet.Create( + OperationKind.ObjectCreation, + OperationKind.TypeParameterObjectCreation, + OperationKind.DynamicObjectCreation, + OperationKind.Invocation); + + private readonly WellKnownTypeProvider _wellKnownTypeProvider; + private readonly ImmutableHashSet _disposeOwnershipTransferLikelyTypes; + private ConcurrentDictionary>? _lazyDisposableFieldsMap; + public INamedTypeSymbol? IDisposable { get; } + public INamedTypeSymbol? IAsyncDisposable { get; } + public INamedTypeSymbol? ConfiguredAsyncDisposable { get; } + public INamedTypeSymbol? Task { get; } + public INamedTypeSymbol? ValueTask { get; } + public INamedTypeSymbol? ConfiguredValueTaskAwaitable { get; } + public INamedTypeSymbol? StringReader { get; } + public INamedTypeSymbol? MemoryStream { get; } + + private DisposeAnalysisHelper(Compilation compilation) + { + _wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + + IDisposable = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable); + IAsyncDisposable = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIAsyncDisposable); + ConfiguredAsyncDisposable = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredAsyncDisposable); + Task = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + ValueTask = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + ConfiguredValueTaskAwaitable = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable); + StringReader = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOStringReader); + MemoryStream = _wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOMemoryStream); + + _disposeOwnershipTransferLikelyTypes = IDisposable != null ? + GetDisposeOwnershipTransferLikelyTypes(compilation) : + ImmutableHashSet.Empty; + } + + private static ImmutableHashSet GetDisposeOwnershipTransferLikelyTypes(Compilation compilation) + { + var builder = PooledHashSet.GetInstance(); + foreach (var typeName in s_disposeOwnershipTransferLikelyTypes) + { + INamedTypeSymbol? typeSymbol = compilation.GetOrCreateTypeByMetadataName(typeName); + if (typeSymbol != null) + { + builder.Add(typeSymbol); + } + } + + return builder.ToImmutableAndFree(); + } + + private void EnsureDisposableFieldsMap() + { + if (_lazyDisposableFieldsMap == null) + { + Interlocked.CompareExchange(ref _lazyDisposableFieldsMap, new ConcurrentDictionary>(), null); + } + } + + public static bool TryGetOrCreate(Compilation compilation, [NotNullWhen(returnValue: true)] out DisposeAnalysisHelper? disposeHelper) + { + disposeHelper = s_DisposeHelperCache.GetOrCreateValue(compilation, CreateDisposeAnalysisHelper); + if (disposeHelper.IDisposable == null) + { + disposeHelper = null; + return false; + } + + return true; + + // Local functions + static DisposeAnalysisHelper CreateDisposeAnalysisHelper(Compilation compilation) + => new(compilation); + } + + public static Func GetIsDisposableDelegate(Compilation compilation) + { + if (TryGetOrCreate(compilation, out var disposeAnalysisHelper)) + { + return disposeAnalysisHelper.IsDisposable; + } + + return _ => false; + } + + public bool TryGetOrComputeResult( + ImmutableArray operationBlocks, + IMethodSymbol containingMethod, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + PointsToAnalysisKind defaultPointsToAnalysisKind, + bool trackInstanceFields, + bool trackExceptionPaths, + [NotNullWhen(returnValue: true)] out DisposeAnalysisResult? disposeAnalysisResult, + [NotNullWhen(returnValue: true)] out PointsToAnalysisResult? pointsToAnalysisResult, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, + bool defaultDisposeOwnershipTransferAtConstructor = false) + { + var cfg = operationBlocks.GetControlFlowGraph(); + if (cfg != null && IDisposable != null) + { + disposeAnalysisResult = DisposeAnalysis.TryGetOrComputeResult(cfg, containingMethod, _wellKnownTypeProvider, + analyzerOptions, rule, _disposeOwnershipTransferLikelyTypes, defaultPointsToAnalysisKind, trackInstanceFields, + trackExceptionPaths, out pointsToAnalysisResult, interproceduralAnalysisPredicate: interproceduralAnalysisPredicate, + defaultDisposeOwnershipTransferAtConstructor: defaultDisposeOwnershipTransferAtConstructor); + if (disposeAnalysisResult != null) + { + RoslynDebug.Assert(pointsToAnalysisResult is object); + return true; + } + } + + disposeAnalysisResult = null; + pointsToAnalysisResult = null; + return false; + } + + private bool HasDisposableOwnershipTransferForConstructorParameter(IMethodSymbol containingMethod) => + containingMethod.MethodKind == MethodKind.Constructor && + containingMethod.Parameters.Any(p => _disposeOwnershipTransferLikelyTypes.Contains(p.Type)); + + private bool IsDisposableCreation(IOperation operation) + => (s_DisposableCreationKinds.Contains(operation.Kind) || + operation.Parent is IArgumentOperation argument && argument.Parameter?.RefKind == RefKind.Out) && + IsDisposable(operation.Type); + + public bool HasAnyDisposableCreationDescendant(ImmutableArray operationBlocks, IMethodSymbol containingMethod) + { + return operationBlocks.HasAnyOperationDescendant(IsDisposableCreation) || + HasDisposableOwnershipTransferForConstructorParameter(containingMethod); + } + + public bool IsDisposableTypeNotRequiringToBeDisposed(ITypeSymbol typeSymbol) => + // Common case doesn't require dispose. https://learn.microsoft.com/dotnet/api/system.threading.tasks.task.dispose + typeSymbol.DerivesFrom(Task, baseTypesOnly: true) || + // StringReader doesn't need to be disposed: https://learn.microsoft.com/dotnet/api/system.io.stringreader + SymbolEqualityComparer.Default.Equals(typeSymbol, StringReader) || + // MemoryStream doesn't need to be disposed. https://learn.microsoft.com/dotnet/api/system.io.memorystream + // Subclasses *might* need to be disposed, but that is the less common case, + // and the common case is a huge source of noisy warnings. + SymbolEqualityComparer.Default.Equals(typeSymbol, MemoryStream); + + public ImmutableHashSet GetDisposableFields(INamedTypeSymbol namedType) + { + EnsureDisposableFieldsMap(); + RoslynDebug.Assert(_lazyDisposableFieldsMap != null); + + if (_lazyDisposableFieldsMap.TryGetValue(namedType, out ImmutableHashSet disposableFields)) + { + return disposableFields; + } + + if (!namedType.IsDisposable(IDisposable, IAsyncDisposable, ConfiguredAsyncDisposable)) + { + disposableFields = ImmutableHashSet.Empty; + } + else + { + disposableFields = namedType.GetMembers() + .OfType() + .Where(f => IsDisposable(f.Type) && !IsDisposableTypeNotRequiringToBeDisposed(f.Type)) + .ToImmutableHashSet(); + } + + return _lazyDisposableFieldsMap.GetOrAdd(namedType, disposableFields); + } + + /// + /// Returns true if the given was created for an allocation in the + /// or represents a location created for a constructor parameter whose type indicates dispose ownership transfer. + /// + public bool IsDisposableCreationOrDisposeOwnershipTransfer(AbstractLocation location, IMethodSymbol containingMethod) + { + if (location.Creation == null) + { + return location.Symbol?.Kind == SymbolKind.Parameter && + HasDisposableOwnershipTransferForConstructorParameter(containingMethod); + } + + return IsDisposableCreation(location.Creation); + } + + public bool IsDisposable([NotNullWhen(returnValue: true)] ITypeSymbol? type) + => type != null && type.IsDisposable(IDisposable, IAsyncDisposable, ConfiguredAsyncDisposable); + + public DisposeMethodKind GetDisposeMethodKind(IMethodSymbol method) + => method.GetDisposeMethodKind(IDisposable, IAsyncDisposable, ConfiguredAsyncDisposable, Task, ValueTask, ConfiguredValueTaskAwaitable); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisResult.cs new file mode 100644 index 0000000000000..359ac9aca1988 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeAnalysisResult.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.DisposeAnalysis +{ + /// + /// Analysis result from execution of on a control flow graph. + /// + public sealed class DisposeAnalysisResult : DataFlowAnalysisResult + { + internal DisposeAnalysisResult( + DataFlowAnalysisResult coreDisposeAnalysisResult, + ImmutableDictionary? trackedInstanceFieldPointsToMap) + : base(coreDisposeAnalysisResult) + { + TrackedInstanceFieldPointsToMap = trackedInstanceFieldPointsToMap; + } + + public ImmutableDictionary? TrackedInstanceFieldPointsToMap { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeBlockAnalysisResult.cs new file mode 100644 index 0000000000000..5c3e548f02a2e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/DisposeAnalysis/DisposeBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.DisposeAnalysis +{ + using DisposeAnalysisData = DictionaryAnalysisData; + + /// + /// Result from execution of on a basic block. + /// It store dispose values for each at the start and end of the basic block. + /// + public class DisposeBlockAnalysisResult : AbstractBlockAnalysisResult + { + internal DisposeBlockAnalysisResult(BasicBlock basicBlock, DisposeAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + } + + public ImmutableDictionary Data { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.GlobalFlowStateAnalysisValueSetDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.GlobalFlowStateAnalysisValueSetDomain.cs new file mode 100644 index 0000000000000..a68f4ebad234f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.GlobalFlowStateAnalysisValueSetDomain.cs @@ -0,0 +1,254 @@ +// 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 System.Linq; +using Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + using GlobalFlowStateAnalysisData = DictionaryAnalysisData; + using GlobalFlowStateAnalysisResult = DataFlowAnalysisResult; + + internal partial class GlobalFlowStateAnalysis : ForwardDataFlowAnalysis + { + internal sealed class GlobalFlowStateAnalysisValueSetDomain : AbstractValueDomain + { + public static GlobalFlowStateAnalysisValueSetDomain Instance = new(); + + private GlobalFlowStateAnalysisValueSetDomain() { } + + public override GlobalFlowStateAnalysisValueSet Bottom => GlobalFlowStateAnalysisValueSet.Unset; + + public override GlobalFlowStateAnalysisValueSet UnknownOrMayBeValue => GlobalFlowStateAnalysisValueSet.Unknown; + + public override int Compare(GlobalFlowStateAnalysisValueSet oldValue, GlobalFlowStateAnalysisValueSet newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (oldValue.Kind == newValue.Kind) + { + return oldValue.Equals(newValue) ? 0 : -1; + } + else if (oldValue.Kind < newValue.Kind) + { + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + public override GlobalFlowStateAnalysisValueSet Merge(GlobalFlowStateAnalysisValueSet value1, GlobalFlowStateAnalysisValueSet value2) + { + if (value1 == null) + { + return value2; + } + else if (value2 == null) + { + return value1; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + return value2; + } + else if (value2.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + return value1; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown || value2.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) + { + return GlobalFlowStateAnalysisValueSet.Unknown; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value2.Kind == GlobalFlowStateAnalysisValueSetKind.Empty) + { + return GlobalFlowStateAnalysisValueSet.Empty; + } + + Debug.Assert(value1.Kind == GlobalFlowStateAnalysisValueSetKind.Known); + Debug.Assert(value2.Kind == GlobalFlowStateAnalysisValueSetKind.Known); + + // Perform some early bail out checks. + if (Equals(value1, value2)) + { + return value1; + } + + if (value1.Height == 0 && value1.AnalysisValues.IsSubsetOf(value2.AnalysisValues)) + { + return value2; + } + + if (value2.Height == 0 && value2.AnalysisValues.IsSubsetOf(value1.AnalysisValues)) + { + return value1; + } + + // Check if value1 and value2 are negations of each other. + // If so, the analysis values nullify each other and we return an empty set. + if (value1.Height == value2.Height && + value1.AnalysisValues.Count == value2.AnalysisValues.Count && + Equals(value1, value2.GetNegatedValue())) + { + return GlobalFlowStateAnalysisValueSet.Empty; + } + + // Create a new value set with value1 and value2 as parent sets. + return GlobalFlowStateAnalysisValueSet.Create( + ImmutableHashSet.Empty, + ImmutableHashSet.Create(value1, value2), + height: Math.Max(value1.Height, value2.Height) + 1); + } + + public static GlobalFlowStateAnalysisValueSet Intersect(GlobalFlowStateAnalysisValueSet value1, GlobalFlowStateAnalysisValueSet value2) + { + if (value1 == null) + { + return value2; + } + else if (value2 == null) + { + return value1; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + return value2; + } + else if (value2.Kind == GlobalFlowStateAnalysisValueSetKind.Unset) + { + return value1; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown || value2.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown) + { + return GlobalFlowStateAnalysisValueSet.Unknown; + } + else if (value1.Kind == GlobalFlowStateAnalysisValueSetKind.Empty || value2.Kind == GlobalFlowStateAnalysisValueSetKind.Empty) + { + return GlobalFlowStateAnalysisValueSet.Empty; + } + else if (value1 == value2) + { + return value1; + } + + Debug.Assert(value1.Kind == GlobalFlowStateAnalysisValueSetKind.Known); + Debug.Assert(value2.Kind == GlobalFlowStateAnalysisValueSetKind.Known); + + if (value1.Height == 0 && value2.Height == 0) + { + return Intersect(value1, value2); + } + + var currentNodes = new Queue(); + using var candidateNodes = PooledHashSet.GetInstance(); + int candidateHeight = 0; + if (value1.Height <= value2.Height) + { + candidateNodes.Add(value1); + currentNodes.Enqueue(value2); + candidateHeight = value1.Height; + } + + if (value2.Height <= value1.Height) + { + candidateNodes.Add(value2); + currentNodes.Enqueue(value1); + candidateHeight = value2.Height; + } + + while (currentNodes.Count > 0) + { + var node = currentNodes.Dequeue(); + foreach (var parent in node.Parents) + { + if (candidateNodes.Contains(parent)) + { + continue; + } + + if (parent.Height > candidateHeight) + { + currentNodes.Enqueue(parent); + continue; + } + + foreach (var candidate in candidateNodes) + { + currentNodes.Enqueue(candidate); + } + + if (parent.Height < candidateHeight) + { + candidateNodes.Clear(); + } + + candidateNodes.Add(parent); + candidateHeight = parent.Height; + } + } + + if (candidateNodes.Count == 1) + { + return candidateNodes.Single(); + } + + GlobalFlowStateAnalysisValueSet? result = null; + foreach (var candidate in candidateNodes) + { + if (result == null) + { + result = candidate; + } + else if (!TryIntersect(candidate, result, out result)) + { + return GlobalFlowStateAnalysisValueSet.Empty; + } + } + + return result!; + + static GlobalFlowStateAnalysisValueSet Intersect(GlobalFlowStateAnalysisValueSet value1, GlobalFlowStateAnalysisValueSet value2) + { + _ = TryIntersect(value1, value2, out var result); + return result; + } + + static bool TryIntersect(GlobalFlowStateAnalysisValueSet value1, GlobalFlowStateAnalysisValueSet value2, out GlobalFlowStateAnalysisValueSet result) + { + Debug.Assert(value1.Height == value2.Height); + var sets = value1.AnalysisValues.IntersectSet(value2.AnalysisValues); + if (sets.IsEmpty) + { + result = GlobalFlowStateAnalysisValueSet.Empty; + return false; + } + + if (sets.Count == value1.AnalysisValues.Count) + { + result = value1; + } + else if (sets.Count == value2.AnalysisValues.Count) + { + result = value2; + } + else + { + result = GlobalFlowStateAnalysisValueSet.Create(sets, value1.Parents, value1.Height); + } + + return true; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs new file mode 100644 index 0000000000000..13f6b3ba0fd97 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysis.cs @@ -0,0 +1,145 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + using GlobalFlowStateAnalysisDomain = MapAbstractDomain; + using GlobalFlowStateAnalysisData = DictionaryAnalysisData; + using GlobalFlowStateAnalysisResult = DataFlowAnalysisResult; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Dataflow analysis to track set of global s enabled on each control flow path at each in the . + /// + internal sealed partial class GlobalFlowStateAnalysis : ForwardDataFlowAnalysis + { + internal static readonly GlobalFlowStateAnalysisDomain GlobalFlowStateAnalysisDomainInstance = new(GlobalFlowStateAnalysisValueSetDomain.Instance); + + private GlobalFlowStateAnalysis(GlobalFlowStateAnalysisDomain analysisDomain, GlobalFlowStateValueSetFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + /// + /// Performs global flow state analysis and returns the analysis result. + /// + /// Control flow graph to analyze. + /// Owning symbol for the analyzed . + /// Delegate to create a that performs the core analysis. + /// Well-known type provider for the compilation. + /// Analyzer options for analysis + /// for fetching any rule specific analyzer option values from . + /// Flag to indicate if should be performed. + /// + /// This boolean field determines if we should perform an optimistic OR a pessimistic analysis. + /// For example, invoking a lambda method for which we do not know the target method being invoked can change/invalidate the current global flow state. + /// An optimistic points to analysis assumes that the global flow state doesn't change for such scenarios. + /// A pessimistic points to analysis resets the global flow state to an unknown state for such scenarios. + /// + /// Optional value content analysis result, if is true + /// for the analysis. + /// Optional predicate for interprocedural analysis. + /// Additional value types for which the caller wants to track stored values during value content analysis. + /// + /// Optional delegate to compute values for . + /// Must be non-null if is non-empty. + /// + /// + /// Global flow state analysis result, or null if analysis did not succeed. + public static GlobalFlowStateAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + Func createOperationVisitor, + WellKnownTypeProvider wellKnownTypeProvider, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + bool performValueContentAnalysis, + bool pessimisticAnalysis, + out ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.None, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, + ImmutableArray additionalSupportedValueTypes = default, + Func? getValueContentValueForAdditionalSupportedValueTypeOperation = null) + { + if (cfg == null) + { + throw new ArgumentNullException(nameof(cfg)); + } + + var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( + analyzerOptions, rule, cfg, wellKnownTypeProvider.Compilation, interproceduralAnalysisKind); + var pointsToAnalysisKind = analyzerOptions.GetPointsToAnalysisKindOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, + defaultValue: PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties); + return TryGetOrComputeResult(cfg, owningSymbol, createOperationVisitor, wellKnownTypeProvider, analyzerOptions, + interproceduralAnalysisConfig, interproceduralAnalysisPredicate, pointsToAnalysisKind, pessimisticAnalysis, + performValueContentAnalysis, out valueContentAnalysisResult, + additionalSupportedValueTypes, getValueContentValueForAdditionalSupportedValueTypeOperation); + } + + private static GlobalFlowStateAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + Func createOperationVisitor, + WellKnownTypeProvider wellKnownTypeProvider, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + PointsToAnalysisKind pointsToAnalysisKind, + bool pessimisticAnalysis, + bool performValueContentAnalysis, + out ValueContentAnalysisResult? valueContentAnalysisResult, + ImmutableArray additionalSupportedValueTypes = default, + Func? getValueContentValueForAdditionalSupportedValueTypeOperation = null) + { + RoslynDebug.Assert(owningSymbol != null); + + PointsToAnalysisResult? pointsToAnalysisResult = null; + valueContentAnalysisResult = performValueContentAnalysis ? + ValueContentAnalysis.ValueContentAnalysis.TryGetOrComputeResult( + cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind, interproceduralAnalysisConfig, out _, + out pointsToAnalysisResult, pessimisticAnalysis, + performCopyAnalysis: false, interproceduralAnalysisPredicate, + additionalSupportedValueTypes, getValueContentValueForAdditionalSupportedValueTypeOperation) : + null; + + pointsToAnalysisResult ??= PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult( + cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind, interproceduralAnalysisConfig, interproceduralAnalysisPredicate); + + var analysisContext = GlobalFlowStateAnalysisContext.Create( + GlobalFlowStateAnalysisValueSetDomain.Instance, wellKnownTypeProvider, cfg, owningSymbol, + analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult, + valueContentAnalysisResult, c => TryGetOrComputeResultForAnalysisContext(c, createOperationVisitor), interproceduralAnalysisPredicate); + return TryGetOrComputeResultForAnalysisContext(analysisContext, createOperationVisitor); + } + + private static GlobalFlowStateAnalysisResult? TryGetOrComputeResultForAnalysisContext( + GlobalFlowStateAnalysisContext analysisContext, + Func createOperationVisitor) + { + var operationVisitor = createOperationVisitor(analysisContext); + var analysis = new GlobalFlowStateAnalysis(GlobalFlowStateAnalysisDomainInstance, operationVisitor); + return analysis.TryGetOrComputeResultCore(analysisContext, cacheResult: false); + } + + protected override GlobalFlowStateAnalysisResult ToResult(GlobalFlowStateAnalysisContext analysisContext, GlobalFlowStateAnalysisResult dataFlowAnalysisResult) + { + // Update the operation state map with global values map + // These are the values that analyzers care about. + var operationVisitor = (GlobalFlowStateValueSetFlowOperationVisitor)OperationVisitor; + return dataFlowAnalysisResult.With(operationVisitor.GetGlobalValuesMap()); + } + + protected override GlobalFlowStateBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, GlobalFlowStateAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisContext.cs new file mode 100644 index 0000000000000..6a7274f6f90de --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisContext.cs @@ -0,0 +1,97 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralGlobalFlowStateAnalysisData = InterproceduralAnalysisData, GlobalFlowStateAnalysisContext, GlobalFlowStateAnalysisValueSet>; + using GlobalFlowStateAnalysisData = DictionaryAnalysisData; + using GlobalFlowStateAnalysisResult = DataFlowAnalysisResult; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + internal sealed class GlobalFlowStateAnalysisContext : AbstractDataFlowAnalysisContext + { + private GlobalFlowStateAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralGlobalFlowStateAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, + owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, + predicateAnalysis: false, + exceptionPathsAnalysis: false, + copyAnalysisResult: null, + pointsToAnalysisResult, + valueContentAnalysisResult, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph, + interproceduralAnalysisData, + interproceduralAnalysisPredicate) + { + } + + internal static GlobalFlowStateAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + { + return new GlobalFlowStateAnalysisContext( + valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, + analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult, + valueContentAnalysisResult, tryGetOrComputeAnalysisResult, parentControlFlowGraph: null, + interproceduralAnalysisData: null, interproceduralAnalysisPredicate); + } + + public override GlobalFlowStateAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralGlobalFlowStateAnalysisData? interproceduralAnalysisData) + { + RoslynDebug.Assert(copyAnalysisResult == null); + return new GlobalFlowStateAnalysisContext(ValueDomain, WellKnownTypeProvider, invokedCfg, + invokedMethod, AnalyzerOptions, InterproceduralAnalysisConfiguration, PessimisticAnalysis, + pointsToAnalysisResult, valueContentAnalysisResult, TryGetOrComputeAnalysisResult, + ControlFlowGraph, interproceduralAnalysisData, InterproceduralAnalysisPredicate); + } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSet.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSet.cs new file mode 100644 index 0000000000000..15c4ed7d5a03c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSet.cs @@ -0,0 +1,227 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; + +#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable - CacheBasedEquatable handles equality + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + internal sealed class GlobalFlowStateAnalysisValueSet : CacheBasedEquatable + { + public static readonly GlobalFlowStateAnalysisValueSet Unset = new( + ImmutableHashSet.Empty, ImmutableHashSet.Empty, 0, GlobalFlowStateAnalysisValueSetKind.Unset); + public static readonly GlobalFlowStateAnalysisValueSet Empty = new( + ImmutableHashSet.Empty, ImmutableHashSet.Empty, 0, GlobalFlowStateAnalysisValueSetKind.Empty); + public static readonly GlobalFlowStateAnalysisValueSet Unknown = new( + ImmutableHashSet.Empty, ImmutableHashSet.Empty, 0, GlobalFlowStateAnalysisValueSetKind.Unknown); + + private GlobalFlowStateAnalysisValueSet( + ImmutableHashSet analysisValues, + ImmutableHashSet parents, + int height, + GlobalFlowStateAnalysisValueSetKind kind) + { + Debug.Assert((!analysisValues.IsEmpty || !parents.IsEmpty) == (kind == GlobalFlowStateAnalysisValueSetKind.Known)); + Debug.Assert(analysisValues.All(value => value != null)); + Debug.Assert(parents.All(parent => parent != null)); + Debug.Assert(height >= 0); + Debug.Assert(height == 0 || kind == GlobalFlowStateAnalysisValueSetKind.Known); + Debug.Assert(height == 0 == parents.IsEmpty); + + AnalysisValues = analysisValues; + Parents = parents; + Height = height; + Kind = kind; + } + + public static GlobalFlowStateAnalysisValueSet Create( + ImmutableHashSet analysisValues, + ImmutableHashSet parents, + int height) + { + Debug.Assert(!analysisValues.IsEmpty || !parents.IsEmpty); + return new(analysisValues, parents, height, GlobalFlowStateAnalysisValueSetKind.Known); + } + + public static GlobalFlowStateAnalysisValueSet Create(IAbstractAnalysisValue analysisValue) + => new(ImmutableHashSet.Create(analysisValue), ImmutableHashSet.Empty, height: 0, GlobalFlowStateAnalysisValueSetKind.Known); + + public ImmutableHashSet AnalysisValues { get; } + public ImmutableHashSet Parents { get; } + public int Height { get; } + public GlobalFlowStateAnalysisValueSetKind Kind { get; } + + private GlobalFlowStateAnalysisValueSet WithRootParent(GlobalFlowStateAnalysisValueSet newRoot) + { + Debug.Assert(Kind == GlobalFlowStateAnalysisValueSetKind.Known); + + var newHeight = Height + newRoot.Height + 1; + if (Parents.IsEmpty) + { + return GlobalFlowStateAnalysisValueSet.Create(AnalysisValues, ImmutableHashSet.Create(newRoot), newHeight); + } + + using var parentsBuilder = PooledHashSet.GetInstance(); + foreach (var parent in Parents) + { + parentsBuilder.Add(parent.WithRootParent(newRoot)); + } + + return GlobalFlowStateAnalysisValueSet.Create(AnalysisValues, parentsBuilder.ToImmutable(), newHeight); + } + + internal GlobalFlowStateAnalysisValueSet WithAdditionalAnalysisValues(GlobalFlowStateAnalysisValueSet newAnalysisValuesSet, bool negate) + { + return WithAdditionalAnalysisValuesCore(negate ? newAnalysisValuesSet.GetNegatedValue() : newAnalysisValuesSet); + } + + private GlobalFlowStateAnalysisValueSet WithAdditionalAnalysisValuesCore(GlobalFlowStateAnalysisValueSet newAnalysisValues) + { + Debug.Assert(Kind != GlobalFlowStateAnalysisValueSetKind.Unknown); + + if (Kind != GlobalFlowStateAnalysisValueSetKind.Known) + { + return newAnalysisValues; + } + + if (newAnalysisValues.Height == 0) + { + return GlobalFlowStateAnalysisValueSet.Create( + AnalysisValues.AddRange(newAnalysisValues.AnalysisValues), Parents, Height); + } + + return newAnalysisValues.WithRootParent(this); + } + + internal GlobalFlowStateAnalysisValueSet GetNegatedValue() + { + Debug.Assert(Kind == GlobalFlowStateAnalysisValueSetKind.Known); + + if (Height == 0 && AnalysisValues.Count == 1) + { + var negatedAnalysisValues = ImmutableHashSet.Create(AnalysisValues.Single().GetNegatedValue()); + return GlobalFlowStateAnalysisValueSet.Create(negatedAnalysisValues, Parents, Height); + } + else if (Height > 0 && AnalysisValues.Count == 0) + { + return GetNegateValueFromParents(Parents); + } + else + { + var parentsBuilder = ImmutableHashSet.CreateBuilder(); + foreach (var analysisValue in AnalysisValues) + { + parentsBuilder.Add(GlobalFlowStateAnalysisValueSet.Create(analysisValue.GetNegatedValue())); + } + + int height; + if (Height > 0) + { + var negatedValueFromParents = GetNegateValueFromParents(Parents); + parentsBuilder.Add(negatedValueFromParents); + height = negatedValueFromParents.Height + 1; + } + else + { + Debug.Assert(AnalysisValues.Count > 1); + Debug.Assert(parentsBuilder.Count > 1); + height = 1; + } + + return GlobalFlowStateAnalysisValueSet.Create(ImmutableHashSet.Empty, parentsBuilder.ToImmutable(), height); + } + + static GlobalFlowStateAnalysisValueSet GetNegateValueFromParents(ImmutableHashSet parents) + { + Debug.Assert(parents.Count > 0); + var analysisValuesBuilder = ImmutableHashSet.CreateBuilder(); + var parentsBuilder = ImmutableHashSet.CreateBuilder(); + + var height = 0; + foreach (var parent in parents) + { + if (parent.AnalysisValues.Count == 1 && parent.Height == 0) + { + analysisValuesBuilder.Add(parent.AnalysisValues.Single().GetNegatedValue()); + } + else + { + var negatedParent = parent.GetNegatedValue(); + parentsBuilder.Add(negatedParent); + height = Math.Max(height, negatedParent.Height + 1); + } + } + + return GlobalFlowStateAnalysisValueSet.Create(analysisValuesBuilder.ToImmutable(), parentsBuilder.ToImmutable(), height); + } + } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(AnalysisValues)); + hashCode.Add(HashUtilities.Combine(Parents)); + hashCode.Add(Height.GetHashCode()); + hashCode.Add(((int)Kind).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (GlobalFlowStateAnalysisValueSet)obj; + return HashUtilities.Combine(AnalysisValues) == HashUtilities.Combine(other.AnalysisValues) + && HashUtilities.Combine(Parents) == HashUtilities.Combine(other.Parents) + && Height.GetHashCode() == other.Height.GetHashCode() + && ((int)Kind).GetHashCode() == ((int)other.Kind).GetHashCode(); + } + + public override string ToString() + { + return GetParentString() + GetAnalysisValuesString(); + + string GetParentString() + { + if (Parents.IsEmpty) + { + return string.Empty; + } + + using var parentsBuilder = ArrayBuilder.GetInstance(Parents.Count); + foreach (var parent in Parents) + { + parentsBuilder.Add(parent.ToString()); + } + + var result = string.Join(" || ", parentsBuilder.Order()); + if (parentsBuilder.Count > 1) + { + result = $"({result})"; + } + + return result; + } + + string GetAnalysisValuesString() + { + if (AnalysisValues.IsEmpty) + { + return string.Empty; + } + + var result = string.Join(" && ", AnalysisValues.Select(f => f.ToString()).Order()); + if (!Parents.IsEmpty) + { + result = $" && {result}"; + } + + return result; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSetKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSetKind.cs new file mode 100644 index 0000000000000..867208bf6dcf3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateAnalysisValueSetKind.cs @@ -0,0 +1,32 @@ +// 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.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + internal enum GlobalFlowStateAnalysisValueSetKind + { + /// + /// Unset value set. + /// This is needed along with Empty to ensure the following merge results: + /// Unset + Known = Known + /// Empty + Known = Empty + /// + Unset, + + /// + /// One or more known set of s. + /// + Known, + + /// + /// No s. + /// + Empty, + + /// + /// Unknown set of s. + /// + Unknown + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateBlockAnalysisResult.cs new file mode 100644 index 0000000000000..3c76860a248de --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + using GlobalFlowStateAnalysisData = DictionaryAnalysisData; + + /// + /// Result from execution of on a basic block. + /// It store GlobalFlowState value at the start and end of the basic block. + /// + internal sealed class GlobalFlowStateBlockAnalysisResult : AbstractBlockAnalysisResult + { + internal GlobalFlowStateBlockAnalysisResult(BasicBlock basicBlock, GlobalFlowStateAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + } + + public ImmutableDictionary Data { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..51b4c16aa209c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateDataFlowOperationVisitor.cs @@ -0,0 +1,176 @@ +// 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 System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + /// + /// Operation visitor to flow the GlobalFlowState values across a given statement in a basic block. + /// + internal abstract class GlobalFlowStateDataFlowOperationVisitor + : AnalysisEntityDataFlowOperationVisitor, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue> + where TAnalysisContext : AbstractDataFlowAnalysisContext, TAnalysisContext, TAnalysisResult, TAbstractAnalysisValue> + where TAnalysisResult : class, IDataFlowAnalysisResult + where TAbstractAnalysisValue : IEquatable + { + // This is the global entity storing CFG wide state, which gets updated for every visited operation in the visitor. + protected AnalysisEntity GlobalEntity { get; } + protected bool HasPredicatedGlobalState { get; } + + private readonly ImmutableDictionary.Builder _globalValuesMapBuilder; + + protected GlobalFlowStateDataFlowOperationVisitor(TAnalysisContext analysisContext, bool hasPredicatedGlobalState) + : base(analysisContext) + { + GlobalEntity = GetGlobalEntity(analysisContext); + HasPredicatedGlobalState = hasPredicatedGlobalState; + _globalValuesMapBuilder = ImmutableDictionary.CreateBuilder(); + } + + internal override bool SkipExceptionPathsAnalysisPostPass => true; + + protected abstract void SetAbstractValue( + DictionaryAnalysisData analysisData, AnalysisEntity analysisEntity, TAbstractAnalysisValue value); + + internal ImmutableDictionary GetGlobalValuesMap() + => _globalValuesMapBuilder.ToImmutable(); + + private static AnalysisEntity GetGlobalEntity(TAnalysisContext analysisContext) + { + ISymbol owningSymbol; + if (analysisContext.InterproceduralAnalysisData == null) + { + owningSymbol = analysisContext.OwningSymbol; + } + else + { + owningSymbol = analysisContext.InterproceduralAnalysisData.MethodsBeingAnalyzed + .Single(m => m.InterproceduralAnalysisData == null) + .OwningSymbol; + } + + return AnalysisEntity.Create( + owningSymbol, + ImmutableArray.Empty, + owningSymbol.GetMemberOrLocalOrParameterType()!, + instanceLocation: PointsToAbstractValue.Unknown, + parent: null, + entityForInstanceLocation: null); + } + + public sealed override DictionaryAnalysisData Flow( + IOperation statement, BasicBlock block, DictionaryAnalysisData input) + { + EnsureInitialized(input); + return base.Flow(statement, block, input); + } + + private void EnsureInitialized(DictionaryAnalysisData input) + { + if (input.Count == 0) + { + input[GlobalEntity] = ValueDomain.Bottom; + } + else + { + Debug.Assert(input.ContainsKey(GlobalEntity)); + } + } + + protected TAbstractAnalysisValue GlobalState + { + get => GetAbstractValue(GlobalEntity); + set => SetAbstractValue(GlobalEntity, value); + } + + public override (DictionaryAnalysisData output, bool isFeasibleBranch) FlowBranch( + BasicBlock fromBlock, BranchWithInfo branch, DictionaryAnalysisData input) + { + EnsureInitialized(input); + return base.FlowBranch(fromBlock, branch, input); + } + + public override DictionaryAnalysisData GetEmptyAnalysisData() + => new(); + + protected sealed override DictionaryAnalysisData GetClonedAnalysisData(DictionaryAnalysisData analysisData) + => new(analysisData); + + protected sealed override void AddTrackedEntities(DictionaryAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis) + => builder.UnionWith(analysisData.Keys); + + protected sealed override void StopTrackingEntity(AnalysisEntity analysisEntity, DictionaryAnalysisData analysisData) + => analysisData.Remove(analysisEntity); + + protected sealed override TAbstractAnalysisValue GetAbstractValue(AnalysisEntity analysisEntity) + => CurrentAnalysisData.TryGetValue(analysisEntity, out var value) ? value : ValueDomain.UnknownOrMayBeValue; + + protected override void SetAbstractValue(AnalysisEntity analysisEntity, TAbstractAnalysisValue value) + => SetAbstractValue(CurrentAnalysisData, analysisEntity, value); + + protected sealed override bool HasAbstractValue(AnalysisEntity analysisEntity) + => CurrentAnalysisData.ContainsKey(analysisEntity); + + protected sealed override bool HasAnyAbstractValue(DictionaryAnalysisData data) + => data.Count > 0; + + protected sealed override DictionaryAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) + => GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData, SetAbstractValue); + + protected sealed override void ResetAbstractValue(AnalysisEntity analysisEntity) + => SetAbstractValue(analysisEntity, ValueDomain.UnknownOrMayBeValue); + + protected sealed override void ResetCurrentAnalysisData() + => ResetAnalysisData(CurrentAnalysisData); + + protected sealed override void UpdateValuesForAnalysisData(DictionaryAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData, CurrentAnalysisData); + + protected sealed override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(DictionaryAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException, CurrentAnalysisData, throwBranchWithExceptionType); + + protected sealed override void ApplyInterproceduralAnalysisResultCore(DictionaryAnalysisData resultData) + => ApplyInterproceduralAnalysisResultHelper(resultData); + + protected override DictionaryAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + => GetClonedCurrentAnalysisData(); + + #region Visitor methods + + public override TAbstractAnalysisValue Visit(IOperation? operation, object? argument) + { + var value = base.Visit(operation, argument); + + if (operation != null) + { + // Store the current global value in a separate global values builder. + // These values need to be saved into the base operation value builder in the final analysis result. + // This will be done as a post-step after the analysis is complete. + _globalValuesMapBuilder[operation] = GlobalState; + } + + return value; + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateValueSetFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateValueSetFlowOperationVisitor.cs new file mode 100644 index 0000000000000..0225be92eb280 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/GlobalFlowStateValueSetFlowOperationVisitor.cs @@ -0,0 +1,143 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis.Operations; +using static Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis.GlobalFlowStateAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + using GlobalFlowStateAnalysisData = DictionaryAnalysisData; + using GlobalFlowStateAnalysisResult = DataFlowAnalysisResult; + + /// + /// Operation visitor to flow the GlobalFlowState values across a given statement in a basic block. + /// + internal abstract class GlobalFlowStateValueSetFlowOperationVisitor + : GlobalFlowStateDataFlowOperationVisitor + { + protected GlobalFlowStateValueSetFlowOperationVisitor(GlobalFlowStateAnalysisContext analysisContext, bool hasPredicatedGlobalState) + : base(analysisContext, hasPredicatedGlobalState) + { + } + + public sealed override (GlobalFlowStateAnalysisData output, bool isFeasibleBranch) FlowBranch(BasicBlock fromBlock, BranchWithInfo branch, GlobalFlowStateAnalysisData input) + { + var result = base.FlowBranch(fromBlock, branch, input); + + if (HasPredicatedGlobalState && + branch.ControlFlowConditionKind != ControlFlowConditionKind.None && + branch.BranchValue != null && + result.isFeasibleBranch) + { + var branchValue = GetCachedAbstractValue(branch.BranchValue); + var negate = branch.ControlFlowConditionKind == ControlFlowConditionKind.WhenFalse; + MergeAndSetGlobalState(branchValue, negate); + } + + return result; + } + + protected void MergeAndSetGlobalState(GlobalFlowStateAnalysisValueSet value, bool negate = false) + { + Debug.Assert(HasPredicatedGlobalState || !negate); + + if (value.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + { + var currentGlobalValue = GetAbstractValue(GlobalEntity); + if (currentGlobalValue.Kind != GlobalFlowStateAnalysisValueSetKind.Unknown) + { + var newGlobalValue = currentGlobalValue.WithAdditionalAnalysisValues(value, negate); + SetAbstractValue(GlobalEntity, newGlobalValue); + } + } + } + + protected sealed override GlobalFlowStateAnalysisValueSet GetAbstractDefaultValue(ITypeSymbol? type) + => GlobalFlowStateAnalysisValueSet.Unset; + + protected sealed override void SetAbstractValue(AnalysisEntity analysisEntity, GlobalFlowStateAnalysisValueSet value) + { + Debug.Assert(HasPredicatedGlobalState || value.Parents.IsEmpty); + base.SetAbstractValue(analysisEntity, value); + } + + protected override void SetAbstractValue(GlobalFlowStateAnalysisData analysisData, AnalysisEntity analysisEntity, GlobalFlowStateAnalysisValueSet value) + { + // PERF: Avoid creating an entry if the value is the default unknown value. + if (value.Kind != GlobalFlowStateAnalysisValueSetKind.Known && + !analysisData.ContainsKey(analysisEntity)) + { + return; + } + + analysisData[analysisEntity] = value; + } + + protected sealed override GlobalFlowStateAnalysisData MergeAnalysisData(GlobalFlowStateAnalysisData value1, GlobalFlowStateAnalysisData value2) + => GlobalFlowStateAnalysisDomainInstance.Merge(value1, value2); + + protected sealed override GlobalFlowStateAnalysisData MergeAnalysisData(GlobalFlowStateAnalysisData value1, GlobalFlowStateAnalysisData value2, BasicBlock forBlock) + => HasPredicatedGlobalState && forBlock.DominatesPredecessors(DataFlowAnalysisContext.ControlFlowGraph) ? + GlobalFlowStateAnalysisDomainInstance.Intersect(value1, value2, GlobalFlowStateAnalysisValueSetDomain.Intersect) : + GlobalFlowStateAnalysisDomainInstance.Merge(value1, value2); + + protected sealed override GlobalFlowStateAnalysisData MergeAnalysisDataForBackEdge(GlobalFlowStateAnalysisData value1, GlobalFlowStateAnalysisData value2, BasicBlock forBlock) + { + // If we are merging analysis data for back edge, we have done at least one analysis pass for the block + // and should replace 'Unset' value with 'Empty' value for the next pass. + if (value1.TryGetValue(GlobalEntity, out var value) && value == GlobalFlowStateAnalysisValueSet.Unset) + value1[GlobalEntity] = GlobalFlowStateAnalysisValueSet.Empty; + + if (value2.TryGetValue(GlobalEntity, out value) && value == GlobalFlowStateAnalysisValueSet.Unset) + value2[GlobalEntity] = GlobalFlowStateAnalysisValueSet.Empty; + + return base.MergeAnalysisDataForBackEdge(value1, value2, forBlock); + } + + protected sealed override GlobalFlowStateAnalysisData GetExitBlockOutputData(GlobalFlowStateAnalysisResult analysisResult) + => new(analysisResult.ExitBlockOutput.Data); + + protected sealed override bool Equals(GlobalFlowStateAnalysisData value1, GlobalFlowStateAnalysisData value2) + => GlobalFlowStateAnalysisDomainInstance.Equals(value1, value2); + + #region Visitor methods + + public override GlobalFlowStateAnalysisValueSet VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + GlobalFlowStateAnalysisValueSet defaultValue) + { + var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + + if (HasPredicatedGlobalState && + IsAnyAssertMethod(method)) + { + var argumentValue = GetCachedAbstractValue(visitedArguments[0]); + MergeAndSetGlobalState(argumentValue); + } + + return value; + } + + public override GlobalFlowStateAnalysisValueSet VisitUnaryOperatorCore(IUnaryOperation operation, object? argument) + { + var value = base.VisitUnaryOperatorCore(operation, argument); + if (HasPredicatedGlobalState && + operation.OperatorKind == UnaryOperatorKind.Not && + value.Kind == GlobalFlowStateAnalysisValueSetKind.Known) + { + return value.GetNegatedValue(); + } + + return value; + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/IAbstractAnalysisValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/IAbstractAnalysisValue.cs new file mode 100644 index 0000000000000..399bedd7a7f15 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/GlobalFlowStateAnalysis/IAbstractAnalysisValue.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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis +{ + /// + /// Abstract analysis value for . + /// + internal interface IAbstractAnalysisValue : IEquatable + { + /// + /// Return negated value if the analysis value is a predicated value. + /// Otherwise, return the current instance itself. + /// + /// + IAbstractAnalysisValue GetNegatedValue(); + + /// + /// String representation of the abstract value. + /// + /// + string ToString(); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAbstractValue.cs new file mode 100644 index 0000000000000..de2d19e8b907a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAbstractValue.cs @@ -0,0 +1,32 @@ +// 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.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + /// + /// Abstract validation value for / tracked by . + /// + internal enum ParameterValidationAbstractValue + { + /// + /// Analysis is not applicable for this location. + /// + NotApplicable, + + /// + /// Location has not been validated. + /// + NotValidated, + + /// + /// Location has been validated. + /// + Validated, + + /// + /// Location may have been validated. + /// + MayBeValidated + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationAbstractValueDomain.cs new file mode 100644 index 0000000000000..b4d784a3e42b9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationAbstractValueDomain.cs @@ -0,0 +1,47 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + using ParameterValidationAnalysisData = DictionaryAnalysisData; + + internal partial class ParameterValidationAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private class ParameterValidationAbstractValueDomain : AbstractValueDomain + { + public static ParameterValidationAbstractValueDomain Default = new(); + + private ParameterValidationAbstractValueDomain() { } + + public override ParameterValidationAbstractValue Bottom => ParameterValidationAbstractValue.NotApplicable; + + public override ParameterValidationAbstractValue UnknownOrMayBeValue => ParameterValidationAbstractValue.MayBeValidated; + + public override int Compare(ParameterValidationAbstractValue oldValue, ParameterValidationAbstractValue newValue, bool assertMonotonicity) + { + return Comparer.Default.Compare(oldValue, newValue); + } + + public override ParameterValidationAbstractValue Merge(ParameterValidationAbstractValue value1, ParameterValidationAbstractValue value2) + { + if (value1 == value2) + { + return value1; + } + else if (value1 == ParameterValidationAbstractValue.NotApplicable || + value2 == ParameterValidationAbstractValue.NotApplicable) + { + return ParameterValidationAbstractValue.NotApplicable; + } + + return ParameterValidationAbstractValue.MayBeValidated; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..b103feff437c9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.ParameterValidationDataFlowOperationVisitor.cs @@ -0,0 +1,472 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + using ParameterValidationAnalysisData = DictionaryAnalysisData; + + internal partial class ParameterValidationAnalysis : ForwardDataFlowAnalysis + { + /// + /// Operation visitor to flow the location validation values across a given statement in a basic block. + /// + private sealed class ParameterValidationDataFlowOperationVisitor : + AbstractLocationDataFlowOperationVisitor + { + private readonly ImmutableDictionary.Builder? _hazardousParameterUsageBuilder; + private readonly INamedTypeSymbol? _notNullAttributeType; + + public ParameterValidationDataFlowOperationVisitor(ParameterValidationAnalysisContext analysisContext) + : base(analysisContext) + { + Debug.Assert(analysisContext.OwningSymbol.Kind == SymbolKind.Method); + Debug.Assert(analysisContext.PointsToAnalysisResult != null); + + if (analysisContext.TrackHazardousParameterUsages) + { + _hazardousParameterUsageBuilder = ImmutableDictionary.CreateBuilder(); + } + + _notNullAttributeType = analysisContext.WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsCodeAnalysisNotNullAttribute); + } + + public ImmutableDictionary HazardousParameterUsages + { + get + { + RoslynDebug.Assert(_hazardousParameterUsageBuilder != null); + return _hazardousParameterUsageBuilder.ToImmutable(); + } + } + + protected override ParameterValidationAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => ValueDomain.Bottom; + + protected override bool HasAnyAbstractValue(ParameterValidationAnalysisData data) => data.Count > 0; + + protected override ParameterValidationAbstractValue GetAbstractValue(AbstractLocation location) + => CurrentAnalysisData.TryGetValue(location, out var value) ? value : ValueDomain.Bottom; + + protected override void ResetCurrentAnalysisData() => ResetAnalysisData(CurrentAnalysisData); + + private bool IsTrackedLocation(AbstractLocation location) + { + return CurrentAnalysisData.ContainsKey(location) || + location.Symbol is IParameterSymbol parameter && + parameter.Type.IsReferenceType && + Equals(parameter.ContainingSymbol, GetBottomOfStackOwningSymbol()); + + ISymbol GetBottomOfStackOwningSymbol() + { + if (DataFlowAnalysisContext.InterproceduralAnalysisData == null) + { + return OwningSymbol; + } + + return DataFlowAnalysisContext.InterproceduralAnalysisData.MethodsBeingAnalyzed + .Single(m => m.InterproceduralAnalysisData == null) + .OwningSymbol; + } + } + + private bool IsNotOrMaybeValidatedLocation(AbstractLocation location) => + CurrentAnalysisData.TryGetValue(location, out var value) && + (value == ParameterValidationAbstractValue.NotValidated || value == ParameterValidationAbstractValue.MayBeValidated); + + protected override void StopTrackingAbstractValue(AbstractLocation location) => CurrentAnalysisData.Remove(location); + + protected override void SetAbstractValue(AbstractLocation location, ParameterValidationAbstractValue value) + { + if (IsTrackedLocation(location)) + { + CurrentAnalysisData[location] = value; + } + } + + protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, ParameterValidationAbstractValue assignedValue, bool mayBeAssignment = false) + { + // If we are assigning to parameter, mark it as validated on this path. + if (target is IParameterReferenceOperation) + { + MarkValidatedLocations(target); + } + } + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, ParameterValidationAbstractValue assignedValue) + { + // We are only tracking default parameter locations. + } + + protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, ParameterValidationAbstractValue value) + { + // We are only tracking default parameter locations. + } + + protected override void SetValueForParameterPointsToLocationOnEntry(IParameterSymbol parameter, PointsToAbstractValue pointsToAbstractValue) + { + if (pointsToAbstractValue.Kind == PointsToAbstractValueKind.KnownLocations) + { + var value = HasAnyNullValidationAttribute(parameter) ? ParameterValidationAbstractValue.Validated : ParameterValidationAbstractValue.NotValidated; + SetAbstractValue(pointsToAbstractValue.Locations, value); + } + } + + private bool HasAnyNullValidationAttribute(IParameterSymbol? parameter) + => parameter != null && parameter.GetAttributes().Any(attr => attr.AttributeClass != null && + (attr.AttributeClass.Name.Equals("ValidatedNotNullAttribute", StringComparison.OrdinalIgnoreCase) || + attr.AttributeClass.Equals(_notNullAttributeType))); + + protected override void EscapeValueForParameterPointsToLocationOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity, ImmutableHashSet escapedLocations) + { + // Mark parameters as validated if they are non-null at all non-exception return paths and null at one of the unhandled throw operations. + var notValidatedLocations = escapedLocations.Where(IsNotOrMaybeValidatedLocation); + if (notValidatedLocations.Any()) + { + if (TryGetNullAbstractValueAtCurrentBlockEntry(analysisEntity, out NullAbstractValue nullAbstractValue) && + nullAbstractValue == NullAbstractValue.NotNull && + TryGetMergedNullAbstractValueAtUnhandledThrowOperationsInGraph(analysisEntity, out NullAbstractValue mergedValueAtUnhandledThrowOperations) && + mergedValueAtUnhandledThrowOperations != NullAbstractValue.NotNull) + { + SetAbstractValue(notValidatedLocations, ParameterValidationAbstractValue.Validated); + } + } + } + + private void MarkValidatedLocations(IOperation operation) + => SetAbstractValue(GetNotValidatedLocations(operation), ParameterValidationAbstractValue.Validated); + + private IEnumerable GetNotValidatedLocations(IOperation operation) + { + var pointsToLocation = GetPointsToAbstractValue(operation); + return pointsToLocation.Locations.Where(IsNotOrMaybeValidatedLocation); + } + + private static bool IsHazardousIfNull(IOperation operation) + { + if (operation.Kind == OperationKind.ConditionalAccessInstance) + { + return false; + } + + return operation.Parent switch + { + IMemberReferenceOperation memberReference => memberReference.Instance == operation, + + IArrayElementReferenceOperation arrayElementReference => arrayElementReference.ArrayReference == operation, + + IInvocationOperation invocation => invocation.Instance == operation, + + _ => false, + }; + } + + private void HandlePotentiallyHazardousOperation(IOperation operation, IEnumerable nonValidatedLocations) + { + Debug.Assert(_hazardousParameterUsageBuilder != null); + + if (GetNullAbstractValue(operation) == NullAbstractValue.NotNull) + { + // We are sure the value is non-null, so cannot be hazardous. + return; + } + + HandleHazardousOperation(operation.Syntax, nonValidatedLocations); + } + + private void HandleHazardousOperation(SyntaxNode syntaxNode, IEnumerable nonValidatedLocations) + { + RoslynDebug.Assert(_hazardousParameterUsageBuilder != null); + + foreach (var location in nonValidatedLocations) + { + Debug.Assert(IsNotOrMaybeValidatedLocation(location)); + + var parameter = (IParameterSymbol)location.Symbol!; + if (!_hazardousParameterUsageBuilder.TryGetValue(parameter, out var currentSyntaxNode) || + syntaxNode.SpanStart < currentSyntaxNode.SpanStart) + { + _hazardousParameterUsageBuilder[parameter] = syntaxNode; + } + } + } + + protected override ParameterValidationAnalysisData MergeAnalysisData(ParameterValidationAnalysisData value1, ParameterValidationAnalysisData value2) + => ParameterValidationAnalysisDomainInstance.Merge(value1, value2); + protected override void UpdateValuesForAnalysisData(ParameterValidationAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData, CurrentAnalysisData); + protected override ParameterValidationAnalysisData GetClonedAnalysisData(ParameterValidationAnalysisData analysisData) + => GetClonedAnalysisDataHelper(analysisData); + public override ParameterValidationAnalysisData GetEmptyAnalysisData() + => GetEmptyAnalysisDataHelper(); + protected override ParameterValidationAnalysisData GetExitBlockOutputData(ParameterValidationAnalysisResult analysisResult) + => GetClonedAnalysisDataHelper(analysisResult.ExitBlockOutput.Data); + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(ParameterValidationAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException, CurrentAnalysisData); + protected override bool Equals(ParameterValidationAnalysisData value1, ParameterValidationAnalysisData value2) + => EqualsHelper(value1, value2); + + #region Visit overrides + public override ParameterValidationAbstractValue Visit(IOperation? operation, object? argument) + { + var value = base.Visit(operation, argument); + if (operation != null) + { + if (_hazardousParameterUsageBuilder != null && + IsHazardousIfNull(operation)) + { + var notValidatedLocations = GetNotValidatedLocations(operation); + HandlePotentiallyHazardousOperation(operation, notValidatedLocations); + } + } + + return value; + } + + public override ParameterValidationAbstractValue VisitReDimClause(IReDimClauseOperation operation, object? argument) + { + var value = base.VisitReDimClause(operation, argument); + MarkValidatedLocations(operation.Operand); + return value; + } + + public override ParameterValidationAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + var value = base.VisitObjectCreation(operation, argument)!; + ProcessRegularInvocationOrCreation(operation.Constructor, operation.Arguments, operation); + return value; + } + + public override ParameterValidationAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + ParameterValidationAbstractValue defaultValue) + { + var value = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + ProcessRegularInvocationOrCreation(method, visitedArguments, originalOperation); + return value; + } + + public override ParameterValidationAbstractValue VisitInvocation_Lambda( + IFlowAnonymousFunctionOperation lambda, + ImmutableArray visitedArguments, + IOperation originalOperation, + ParameterValidationAbstractValue defaultValue) + { + var value = base.VisitInvocation_Lambda(lambda, visitedArguments, originalOperation, defaultValue); + ProcessLambdaOrLocalFunctionInvocation(lambda.Symbol, originalOperation); + return value; + } + + public override ParameterValidationAbstractValue VisitInvocation_LocalFunction( + IMethodSymbol localFunction, + ImmutableArray visitedArguments, + IOperation originalOperation, + ParameterValidationAbstractValue defaultValue) + { + var value = base.VisitInvocation_LocalFunction(localFunction, visitedArguments, originalOperation, defaultValue); + ProcessLambdaOrLocalFunctionInvocation(localFunction, originalOperation); + return value; + } + + private void ProcessRegularInvocationOrCreation(IMethodSymbol? targetMethod, ImmutableArray arguments, IOperation operation) + { + if (targetMethod == null) + return; + + Debug.Assert(!targetMethod.IsLambdaOrLocalFunctionOrDelegate()); + + if (targetMethod.IsArgumentNullCheckMethod()) + { + if (arguments.Length == 1) + { + // "static bool SomeType.IsNullXXX(obj)" check. + MarkValidatedLocations(arguments[0]); + } + } + else if (!targetMethod.Parameters.IsEmpty && + !arguments.IsEmpty && + ExceptionNamedType != null && + targetMethod.ContainingType.DerivesFrom(ExceptionNamedType)) + { + // FxCop compat: special cases handled by FxCop. + // 1. First argument of type System.Runtime.Serialization.SerializationInfo to System.Exception.GetObjectData or its override is validated. + // 2. First argument of type System.Runtime.Serialization.SerializationInfo to constructor of System.Exception or its subtype is validated. + if (targetMethod.IsGetObjectData(SerializationInfoNamedType, StreamingContextNamedType) || + targetMethod.IsSerializationConstructor(SerializationInfoNamedType, StreamingContextNamedType)) + { + MarkValidatedLocations(arguments[0]); + } + } + else if (_hazardousParameterUsageBuilder != null && + !targetMethod.IsExternallyVisible() && + TryGetInterproceduralAnalysisResult(operation, out var invokedMethodAnalysisResult)) + { + // Check if this private/internal method that has hazardous usages of non-validated argument. + Debug.Assert(!targetMethod.IsVirtual && !targetMethod.IsOverride); + + var hazardousParameterUsagesInInvokedMethod = invokedMethodAnalysisResult.HazardousParameterUsages; + if (!hazardousParameterUsagesInInvokedMethod.IsEmpty) + { + foreach (var argument in arguments) + { + var notValidatedLocations = GetNotValidatedLocations(argument); + foreach (var location in notValidatedLocations) + { + var parameter = (IParameterSymbol)location.Symbol!; + if (hazardousParameterUsagesInInvokedMethod.ContainsKey(parameter)) + { + HandlePotentiallyHazardousOperation(argument, notValidatedLocations); + break; + } + } + } + } + } + + // Mark arguments passed to parameters of null check validation methods as validated. + // Also mark arguments passed to parameters with ValidatedNotNullAttribute as validated. + var isNullCheckValidationMethod = DataFlowAnalysisContext.IsNullCheckValidationMethod(targetMethod.OriginalDefinition); + foreach (var argument in arguments) + { + var notValidatedLocations = GetNotValidatedLocations(argument); + if (notValidatedLocations.Any()) + { + if (isNullCheckValidationMethod || HasAnyNullValidationAttribute(argument.Parameter)) + { + MarkValidatedLocations(argument); + } + } + } + } + + private void ProcessLambdaOrLocalFunctionInvocation(IMethodSymbol targetMethod, IOperation invocation) + { + Debug.Assert(targetMethod.MethodKind is MethodKind.LambdaMethod or MethodKind.LocalFunction); + + // Lambda and local function invocations can access captured variables. + if (_hazardousParameterUsageBuilder != null && + TryGetInterproceduralAnalysisResult(invocation, out var invokedMethodAnalysisResult)) + { + var notValidatedLocations = CurrentAnalysisData.Keys.Where(IsNotOrMaybeValidatedLocation); + if (notValidatedLocations.Any()) + { + var hazardousParameterUsagesInInvokedMethod = invokedMethodAnalysisResult.HazardousParameterUsages; + foreach (var kvp in hazardousParameterUsagesInInvokedMethod) + { + var parameter = kvp.Key; + var syntaxNode = kvp.Value; + if (!_hazardousParameterUsageBuilder.ContainsKey(parameter)) + { + HandleHazardousOperation(syntaxNode, notValidatedLocations.Where(l => Equals(l.Symbol, parameter))); + } + } + } + } + } + + public override ParameterValidationAbstractValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument) + { + var value = base.VisitBinaryOperatorCore(operation, argument); + + // Mark a location as validated on paths where we know it is non-null. + // if (x != null) + // { + // // Validated on this path + // } + + // if (x != null) + // { + // // This code path + // } + var isNullNotEqualsOnWhenTrue = FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue && + (operation.OperatorKind == BinaryOperatorKind.NotEquals || operation.OperatorKind == BinaryOperatorKind.ObjectValueNotEquals); + + // if (x == null) { ... } + // else + // { + // // This code path + // } + var isNullEqualsOnWhenFalse = FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && + (operation.OperatorKind == BinaryOperatorKind.Equals || operation.OperatorKind == BinaryOperatorKind.ObjectValueEquals); + + if (isNullNotEqualsOnWhenTrue || isNullEqualsOnWhenFalse) + { + if (GetNullAbstractValue(operation.RightOperand) == NullAbstractValue.Null) + { + // if (x != null) + MarkValidatedLocations(operation.LeftOperand); + } + else if (GetNullAbstractValue(operation.LeftOperand) == NullAbstractValue.Null) + { + // if (null != x) + MarkValidatedLocations(operation.RightOperand); + } + } + + return value; + } + + public override ParameterValidationAbstractValue VisitIsNull(IIsNullOperation operation, object? argument) + { + var value = base.VisitIsNull(operation, argument); + + // Mark a location as validated on paths where user has performed an IsNull check. + // See comments in VisitBinaryOperatorCore override above for further details. + MarkValidatedLocations(operation.Operand); + + return value; + } + + public override ParameterValidationAbstractValue VisitIsPattern(IIsPatternOperation operation, object? argument) + { + var value = base.VisitIsPattern(operation, argument); + + // Mark a location as validated on false path where user has performed an IsPattern check with null on true path. + // See comments in VisitBinaryOperatorCore override above for further details. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse && + GetNullAbstractValue(operation.Pattern) == NullAbstractValue.Null) + { + MarkValidatedLocations(operation.Value); + } + + // Mark a location as validated on true path where user has performed an IsPattern check with not null on true path. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue && + GetNullAbstractValue(operation.Pattern) == NullAbstractValue.NotNull) + { + MarkValidatedLocations(operation.Value); + } + + return value; + } + + public override ParameterValidationAbstractValue VisitIsType(IIsTypeOperation operation, object? argument) + { + var value = base.VisitIsType(operation, argument); + + // Mark a location as validated on paths where user has performed an IsType check, for example 'x is object'. + // See comments in VisitBinaryOperatorCore override above for further details. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + MarkValidatedLocations(operation.ValueOperand); + } + + return value; + } + + #endregion + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.cs new file mode 100644 index 0000000000000..593375571b198 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysis.cs @@ -0,0 +1,138 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + using ParameterValidationAnalysisData = DictionaryAnalysisData; + using ParameterValidationAnalysisDomain = MapAbstractDomain; + + /// + /// Dataflow analysis to track of / instances. + /// + internal partial class ParameterValidationAnalysis : ForwardDataFlowAnalysis + { + public static readonly ParameterValidationAnalysisDomain ParameterValidationAnalysisDomainInstance = new(ParameterValidationAbstractValueDomain.Default); + + private ParameterValidationAnalysis(ParameterValidationAnalysisDomain analysisDomain, ParameterValidationDataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + public static ImmutableDictionary GetOrComputeHazardousParameterUsages( + IBlockOperation topmostBlock, + Compilation compilation, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + PointsToAnalysisKind defaultPointsToAnalysisKind = PointsToAnalysisKind.PartialWithoutTrackingFieldsAndProperties, + InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.ContextSensitive, + uint defaultMaxInterproceduralMethodCallChain = 1, + bool pessimisticAnalysis = false) + { + Debug.Assert(!analyzerOptions.IsConfiguredToSkipAnalysis(rule, owningSymbol, compilation)); + + var cfg = topmostBlock.GetEnclosingControlFlowGraph(); + if (cfg == null) + { + return ImmutableDictionary.Empty; + } + + var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( + analyzerOptions, rule, cfg, compilation, interproceduralAnalysisKind, defaultMaxInterproceduralMethodCallChain); + var performCopyAnalysis = analyzerOptions.GetCopyAnalysisOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, defaultValue: false); + var nullCheckValidationMethods = analyzerOptions.GetNullCheckValidationMethodsOption(rule, topmostBlock.Syntax.SyntaxTree, compilation); + var pointsToAnalysisKind = analyzerOptions.GetPointsToAnalysisKindOption(rule, topmostBlock.Syntax.SyntaxTree, compilation, defaultPointsToAnalysisKind); + return GetOrComputeHazardousParameterUsages(cfg, compilation, owningSymbol, analyzerOptions, + nullCheckValidationMethods, pointsToAnalysisKind, interproceduralAnalysisConfig, performCopyAnalysis, pessimisticAnalysis); + } + + private static ImmutableDictionary GetOrComputeHazardousParameterUsages( + ControlFlowGraph cfg, + Compilation compilation, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + SymbolNamesWithValueOption nullCheckValidationMethods, + PointsToAnalysisKind pointsToAnalysisKind, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool performCopyAnalysis, + bool pessimisticAnalysis) + { + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + var pointsToAnalysisResult = PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind, interproceduralAnalysisConfig, interproceduralAnalysisPredicate: null, pessimisticAnalysis, performCopyAnalysis); + if (pointsToAnalysisResult != null) + { + var result = TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + nullCheckValidationMethods, interproceduralAnalysisConfig, pessimisticAnalysis, pointsToAnalysisResult); + if (result != null) + { + return result.HazardousParameterUsages; + } + } + + return ImmutableDictionary.Empty; + } + + private static ParameterValidationAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + SymbolNamesWithValueOption nullCheckValidationMethods, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult pointsToAnalysisResult) + { + var analysisContext = ParameterValidationAnalysisContext.Create(ParameterValidationAbstractValueDomain.Default, + wellKnownTypeProvider, cfg, owningSymbol, analyzerOptions, nullCheckValidationMethods, interproceduralAnalysisConfig, + pessimisticAnalysis, pointsToAnalysisResult, TryGetOrComputeResultForAnalysisContext); + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static ParameterValidationAnalysisResult? TryGetOrComputeResultForAnalysisContext(ParameterValidationAnalysisContext analysisContext) + { + var operationVisitor = new ParameterValidationDataFlowOperationVisitor(analysisContext); + var analysis = new ParameterValidationAnalysis(ParameterValidationAnalysisDomainInstance, operationVisitor); + return analysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + protected override ParameterValidationAnalysisResult ToResult( + ParameterValidationAnalysisContext analysisContext, + DataFlowAnalysisResult dataFlowAnalysisResult) + { + analysisContext = analysisContext.WithTrackHazardousParameterUsages(); + var newOperationVisitor = new ParameterValidationDataFlowOperationVisitor(analysisContext); + + foreach (var block in analysisContext.ControlFlowGraph.Blocks) + { + var data = new ParameterValidationAnalysisData(dataFlowAnalysisResult[block].Data); + data = Flow(newOperationVisitor, block, data); + + if (block.FallThroughSuccessor != null) + { + var fallThroughData = block.ConditionalSuccessor != null ? AnalysisDomain.Clone(data) : data; + _ = FlowBranch(newOperationVisitor, block.FallThroughSuccessor, fallThroughData); + } + + if (block.ConditionalSuccessor != null) + { + _ = FlowBranch(newOperationVisitor, block.ConditionalSuccessor, data); + } + } + + return new ParameterValidationAnalysisResult(dataFlowAnalysisResult, newOperationVisitor.HazardousParameterUsages); + } + + protected override ParameterValidationBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, ParameterValidationAnalysisData blockAnalysisData) => new(basicBlock, blockAnalysisData); + + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisContext.cs new file mode 100644 index 0000000000000..964a9092744a5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisContext.cs @@ -0,0 +1,116 @@ +// 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.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralParameterValidationAnalysisData = InterproceduralAnalysisData, ParameterValidationAnalysisContext, ParameterValidationAbstractValue>; + using ParameterValidationAnalysisData = DictionaryAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + internal sealed class ParameterValidationAnalysisContext : AbstractDataFlowAnalysisContext + { + private ParameterValidationAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + SymbolNamesWithValueOption nullCheckValidationMethods, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralParameterValidationAnalysisData? interproceduralAnalysisData, + bool trackHazardousParameterUsages) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, + pessimisticAnalysis, predicateAnalysis: false, exceptionPathsAnalysis: false, + copyAnalysisResult: null, pointsToAnalysisResult, valueContentAnalysisResult: null, + tryGetOrComputeAnalysisResult, parentControlFlowGraph, interproceduralAnalysisData, + interproceduralAnalysisPredicate: null) + { + TrackHazardousParameterUsages = trackHazardousParameterUsages; + NullCheckValidationMethodNames = nullCheckValidationMethods; + } + + public static ParameterValidationAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + SymbolNamesWithValueOption nullCheckValidationMethods, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult) + { + return new ParameterValidationAnalysisContext( + valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, + analyzerOptions, nullCheckValidationMethods, interproceduralAnalysisConfig, + pessimisticAnalysis, pointsToAnalysisResult, tryGetOrComputeAnalysisResult, parentControlFlowGraph: null, + interproceduralAnalysisData: null, trackHazardousParameterUsages: false); + } + + public override ParameterValidationAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralParameterValidationAnalysisData? interproceduralAnalysisData) + { + Debug.Assert(pointsToAnalysisResult != null); + Debug.Assert(copyAnalysisResult == null); + Debug.Assert(valueContentAnalysisResult == null); + + // Do not invoke any interprocedural analysis more than one level down. + // We only care about analyzing validation methods. + return new ParameterValidationAnalysisContext( + ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions, + NullCheckValidationMethodNames, InterproceduralAnalysisConfiguration, + PessimisticAnalysis, pointsToAnalysisResult, TryGetOrComputeAnalysisResult, ControlFlowGraph, + interproceduralAnalysisData, TrackHazardousParameterUsages); + } + + public ParameterValidationAnalysisContext WithTrackHazardousParameterUsages() + => new( + ValueDomain, WellKnownTypeProvider, ControlFlowGraph, + OwningSymbol, AnalyzerOptions, NullCheckValidationMethodNames, + InterproceduralAnalysisConfiguration, PessimisticAnalysis, + PointsToAnalysisResult, TryGetOrComputeAnalysisResult, ParentControlFlowGraph, + InterproceduralAnalysisData, trackHazardousParameterUsages: true); + + public bool TrackHazardousParameterUsages { get; } + + private SymbolNamesWithValueOption NullCheckValidationMethodNames { get; } + public bool IsNullCheckValidationMethod(IMethodSymbol method) + => NullCheckValidationMethodNames.Contains(method); + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(TrackHazardousParameterUsages.GetHashCode()); + hashCode.Add(NullCheckValidationMethodNames.GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (ParameterValidationAnalysisContext)obj; + return TrackHazardousParameterUsages.GetHashCode() == other.TrackHazardousParameterUsages.GetHashCode() + && NullCheckValidationMethodNames.GetHashCode() == other.NullCheckValidationMethodNames.GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisResult.cs new file mode 100644 index 0000000000000..cbab248a0bb72 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationAnalysisResult.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + /// + /// Analysis result from execution of on a control flow graph. + /// + internal sealed class ParameterValidationAnalysisResult : DataFlowAnalysisResult + { + public ParameterValidationAnalysisResult( + DataFlowAnalysisResult parameterValidationAnalysisResult, + ImmutableDictionary hazardousParameterUsages) + : base(parameterValidationAnalysisResult) + { + HazardousParameterUsages = hazardousParameterUsages; + } + + public ImmutableDictionary HazardousParameterUsages { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationBlockAnalysisResult.cs new file mode 100644 index 0000000000000..27b9b92446d88 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ParameterValidationAnalysis/ParameterValidationBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.ParameterValidationAnalysis +{ + using ParameterValidationAnalysisData = DictionaryAnalysisData; + + /// + /// Result from execution of on a basic block. + /// It stores ParameterValidation values for each at the start and end of the basic block. + /// + internal class ParameterValidationBlockAnalysisResult : AbstractBlockAnalysisResult + { + public ParameterValidationBlockAnalysisResult(BasicBlock basicBlock, ParameterValidationAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + } + + public ImmutableDictionary Data { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/DefaultPointsToValueGenerator.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/DefaultPointsToValueGenerator.cs new file mode 100644 index 0000000000000..99e0504fc889c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/DefaultPointsToValueGenerator.cs @@ -0,0 +1,63 @@ +// 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 Analyzer.Utilities.Extensions; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Generates and stores the default for instances generated for member and element reference operations. + /// + internal sealed class DefaultPointsToValueGenerator + { + private readonly TrackedEntitiesBuilder _trackedEntitiesBuilder; + private readonly ImmutableDictionary.Builder _defaultPointsToValueMapBuilder; + + public DefaultPointsToValueGenerator(TrackedEntitiesBuilder trackedEntitiesBuilder) + { + _trackedEntitiesBuilder = trackedEntitiesBuilder; + _defaultPointsToValueMapBuilder = ImmutableDictionary.CreateBuilder(); + } + + public PointsToAnalysisKind PointsToAnalysisKind => _trackedEntitiesBuilder.PointsToAnalysisKind; + + public PointsToAbstractValue GetOrCreateDefaultValue(AnalysisEntity analysisEntity) + { + if (!_defaultPointsToValueMapBuilder.TryGetValue(analysisEntity, out var value)) + { + if (analysisEntity.Symbol?.Kind == SymbolKind.Local || + analysisEntity.Symbol is IParameterSymbol parameter && parameter.RefKind == RefKind.Out || + analysisEntity.CaptureId != null) + { + return PointsToAbstractValue.Undefined; + } + else if (analysisEntity.Type.IsNonNullableValueType()) + { + return PointsToAbstractValue.NoLocation; + } + else if (analysisEntity.HasUnknownInstanceLocation) + { + return PointsToAbstractValue.Unknown; + } + + value = PointsToAbstractValue.Create(AbstractLocation.CreateAnalysisEntityDefaultLocation(analysisEntity), mayBeNull: true); + + // PERF: Do not track entity and its points to value for partial analysis for entities requiring complete analysis. + if (analysisEntity.ShouldBeTrackedForPointsToAnalysis(PointsToAnalysisKind)) + { + _trackedEntitiesBuilder.AddEntityAndPointsToValue(analysisEntity, value); + _defaultPointsToValueMapBuilder.Add(analysisEntity, value); + } + } + + return value; + } + + public bool IsTrackedEntity(AnalysisEntity analysisEntity) => _defaultPointsToValueMapBuilder.ContainsKey(analysisEntity); + public bool IsTrackedPointsToValue(PointsToAbstractValue value) => _trackedEntitiesBuilder.IsTrackedPointsToValue(value); + public void AddTrackedPointsToValue(PointsToAbstractValue value) => _trackedEntitiesBuilder.AddTrackedPointsToValue(value); + public bool HasAnyTrackedEntity => _defaultPointsToValueMapBuilder.Count > 0; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/NullAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/NullAbstractValue.cs new file mode 100644 index 0000000000000..72c9cf520e6eb --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/NullAbstractValue.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Abstract null value for / tracked by . + /// + public enum NullAbstractValue + { + Invalid, + Undefined, + Null, + NotNull, + MaybeNull + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValue.cs new file mode 100644 index 0000000000000..02c3625fad744 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValue.cs @@ -0,0 +1,203 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Abstract PointsTo value for an / tracked by . + /// It contains the set of possible s that the entity or the operation can point to and the of the location(s). + /// + public class PointsToAbstractValue : CacheBasedEquatable + { + // An upper bound on number of underlying locations contained in the tracked PointsTo value. + // This is required to prevent infinite analysis from interprocedural calls within a loop + // The chosen constant value is just an approximate heuristic, which can be fine tuned in future. + private const int LocationThreshold = 20; + + public static PointsToAbstractValue Undefined { get; } = new PointsToAbstractValue(PointsToAbstractValueKind.Undefined, NullAbstractValue.Undefined); + public static PointsToAbstractValue Invalid { get; } = new PointsToAbstractValue(PointsToAbstractValueKind.Invalid, NullAbstractValue.Invalid); + public static PointsToAbstractValue Unknown { get; } = new PointsToAbstractValue(PointsToAbstractValueKind.Unknown, NullAbstractValue.MaybeNull); + public static PointsToAbstractValue UnknownNull { get; } = new PointsToAbstractValue(PointsToAbstractValueKind.UnknownNull, NullAbstractValue.Null); + public static PointsToAbstractValue UnknownNotNull { get; } = new PointsToAbstractValue(PointsToAbstractValueKind.UnknownNotNull, NullAbstractValue.NotNull); + public static PointsToAbstractValue NoLocation { get; } = new PointsToAbstractValue(ImmutableHashSet.Create(AbstractLocation.NoLocation), NullAbstractValue.NotNull); + public static PointsToAbstractValue NullLocation { get; } = new PointsToAbstractValue(ImmutableHashSet.Create(AbstractLocation.Null), NullAbstractValue.Null); + + private PointsToAbstractValue(ImmutableHashSet locations, NullAbstractValue nullState) + { + Debug.Assert(!locations.IsEmpty); + Debug.Assert(locations.All(location => !location.IsNull) || nullState != NullAbstractValue.NotNull); + Debug.Assert(nullState != NullAbstractValue.Undefined); + Debug.Assert(nullState != NullAbstractValue.Invalid); + Debug.Assert(!locations.Any(l => l.IsAnalysisEntityDefaultLocation && l.AnalysisEntity!.HasUnknownInstanceLocation)); + Debug.Assert(locations.Count <= LocationThreshold); + + Locations = locations; + LValueCapturedOperations = ImmutableHashSet.Empty; + Kind = PointsToAbstractValueKind.KnownLocations; + NullState = nullState; + } + + private PointsToAbstractValue(ImmutableHashSet lValueCapturedOperations) + { + Debug.Assert(!lValueCapturedOperations.IsEmpty); + + LValueCapturedOperations = lValueCapturedOperations; + Locations = ImmutableHashSet.Empty; + Kind = PointsToAbstractValueKind.KnownLValueCaptures; + NullState = NullAbstractValue.NotNull; + } + + private PointsToAbstractValue(PointsToAbstractValueKind kind, NullAbstractValue nullState) + { + Debug.Assert(kind != PointsToAbstractValueKind.KnownLocations); + Debug.Assert(kind != PointsToAbstractValueKind.KnownLValueCaptures); + + Locations = ImmutableHashSet.Empty; + LValueCapturedOperations = ImmutableHashSet.Empty; + Kind = kind; + NullState = nullState; + } + + internal static PointsToAbstractValue Create(AbstractLocation location, bool mayBeNull) + { + Debug.Assert(!location.IsNull, "Use 'PointsToAbstractValue.NullLocation' singleton"); + Debug.Assert(!location.IsNoLocation, "Use 'PointsToAbstractValue.NoLocation' singleton"); + + return new PointsToAbstractValue(ImmutableHashSet.Create(location), mayBeNull ? NullAbstractValue.MaybeNull : NullAbstractValue.NotNull); + } + + internal static PointsToAbstractValue Create(IOperation lValueCapturedOperation) + { + return new PointsToAbstractValue(ImmutableHashSet.Create(lValueCapturedOperation)); + } + + internal static PointsToAbstractValue Create(ImmutableHashSet locations, NullAbstractValue nullState) + { + Debug.Assert(!locations.IsEmpty); + + if (locations.Count == 1) + { + var location = locations.Single(); + if (location.IsNull) + { + return NullLocation; + } + + if (location.IsNoLocation) + { + return NoLocation; + } + } + else if (locations.Count > LocationThreshold) + { + return nullState switch + { + NullAbstractValue.Null => UnknownNull, + + NullAbstractValue.NotNull => UnknownNotNull, + + _ => Unknown, + }; + } + + return new PointsToAbstractValue(locations, nullState); + } + + internal static PointsToAbstractValue Create(ImmutableHashSet lValueCapturedOperations) + { + Debug.Assert(!lValueCapturedOperations.IsEmpty); + return new PointsToAbstractValue(lValueCapturedOperations); + } + + internal PointsToAbstractValue MakeNonNull() + { + Debug.Assert(Kind != PointsToAbstractValueKind.KnownLValueCaptures); + + if (NullState == NullAbstractValue.NotNull) + { + return this; + } + + if (Locations.IsEmpty) + { + return UnknownNotNull; + } + + var locations = Locations.Where(location => !location.IsNull).ToImmutableHashSet(); + if (locations.IsEmpty) + { + return UnknownNotNull; + } + else if (locations.Count == Locations.Count) + { + locations = Locations; + } + + return new PointsToAbstractValue(locations, NullAbstractValue.NotNull); + } + + internal PointsToAbstractValue MakeNull() + { + Debug.Assert(Kind != PointsToAbstractValueKind.KnownLValueCaptures); + + if (NullState == NullAbstractValue.Null) + { + return this; + } + + if (Locations.IsEmpty) + { + return UnknownNull; + } + + return new PointsToAbstractValue(Locations, NullAbstractValue.Null); + } + + internal PointsToAbstractValue MakeMayBeNull() + { + Debug.Assert(Kind != PointsToAbstractValueKind.KnownLValueCaptures); + Debug.Assert(NullState != NullAbstractValue.Null); + + if (NullState == NullAbstractValue.MaybeNull) + { + return this; + } + + if (Locations.IsEmpty) + { + return Unknown; + } + + Debug.Assert(Locations.All(location => !location.IsNull)); + return new PointsToAbstractValue(Locations, NullAbstractValue.MaybeNull); + } + + public ImmutableHashSet Locations { get; } + public ImmutableHashSet LValueCapturedOperations { get; } + public PointsToAbstractValueKind Kind { get; } + public NullAbstractValue NullState { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(Locations)); + hashCode.Add(HashUtilities.Combine(LValueCapturedOperations)); + hashCode.Add(((int)Kind).GetHashCode()); + hashCode.Add(((int)NullState).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (PointsToAbstractValue)obj; + return HashUtilities.Combine(Locations) == HashUtilities.Combine(other.Locations) + && HashUtilities.Combine(LValueCapturedOperations) == HashUtilities.Combine(other.LValueCapturedOperations) + && ((int)Kind).GetHashCode() == ((int)other.Kind).GetHashCode() + && ((int)NullState).GetHashCode() == ((int)other.NullState).GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValueKind.cs new file mode 100644 index 0000000000000..1d33b3d33f793 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAbstractValueKind.cs @@ -0,0 +1,50 @@ +// 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.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Kind for the . + /// + public enum PointsToAbstractValueKind + { + /// + /// Invalid value based on predicate analysis. + /// + Invalid, + + /// + /// Undefined value. + /// + Undefined, + + /// + /// Points to one or more known possible locations. + /// + KnownLocations, + + /// + /// Points to one or more known possible l-values. + /// Used for pointers, ref expressions and l-value flow captures. + /// + KnownLValueCaptures, + + /// + /// Points to unknown set of locations, which is known to be null. + /// Note that this value kind is theoretically not needed, as the underlying + /// value is null, but it has been added to ensure monotonicity of value merge. + /// + UnknownNull, + + /// + /// Points to unknown set of locations, which is known to be non-null. + /// + UnknownNotNull, + + /// + /// Points to unknown set of locations, which may or may not be null. + /// + Unknown, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.CorePointsToAnalysisDataDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.CorePointsToAnalysisDataDomain.cs new file mode 100644 index 0000000000000..d36edf17eb1f0 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.CorePointsToAnalysisDataDomain.cs @@ -0,0 +1,127 @@ +// 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 System.Linq; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + using CorePointsToAnalysisData = DictionaryAnalysisData; + + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + /// + /// An abstract analysis domain implementation for tracked by . + /// + private sealed class CorePointsToAnalysisDataDomain : AnalysisEntityMapAbstractDomain + { + private readonly Func _isDisposable; + + public CorePointsToAnalysisDataDomain( + DefaultPointsToValueGenerator defaultPointsToValueGenerator, + AbstractValueDomain valueDomain, + Func isDisposable) + : base(valueDomain, defaultPointsToValueGenerator.IsTrackedEntity, defaultPointsToValueGenerator.IsTrackedPointsToValue) + { + DefaultPointsToValueGenerator = defaultPointsToValueGenerator; + this._isDisposable = isDisposable; + } + + public DefaultPointsToValueGenerator DefaultPointsToValueGenerator { get; } + + protected override PointsToAbstractValue GetDefaultValue(AnalysisEntity analysisEntity) => DefaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity); + + protected override bool CanSkipNewEntry(AnalysisEntity analysisEntity, PointsToAbstractValue value) + => value.Kind == PointsToAbstractValueKind.Unknown || + value == GetDefaultValue(analysisEntity); + + protected override void OnNewMergedValue(PointsToAbstractValue value) + => DefaultPointsToValueGenerator.AddTrackedPointsToValue(value); + + protected override void AssertValidEntryForMergedMap(AnalysisEntity analysisEntity, PointsToAbstractValue value) + { + PointsToAnalysisData.AssertValidPointsToAnalysisKeyValuePair(analysisEntity, value, _isDisposable); + } + + protected override void AssertValidAnalysisData(CorePointsToAnalysisData map) + { + PointsToAnalysisData.AssertValidPointsToAnalysisData(map, _isDisposable); + } + + public CorePointsToAnalysisData MergeCoreAnalysisDataForBackEdge( + PointsToAnalysisData forwardEdgeAnalysisData, + PointsToAnalysisData backEdgeAnalysisData, + Func> getChildAnalysisEntities, + Action resetAbstractValue) + { + // Stop tracking points to values present in both branches if their is an assignment to a may-be null value from the back edge. + // Clone the input forwardEdgeAnalysisData to ensure we don't overwrite the input dictionary. + forwardEdgeAnalysisData = (PointsToAnalysisData)forwardEdgeAnalysisData.Clone(); + List keysInMap1 = forwardEdgeAnalysisData.CoreAnalysisData.Keys.ToList(); + foreach (var key in keysInMap1) + { + var forwardEdgeValue = forwardEdgeAnalysisData[key]; + if (backEdgeAnalysisData.TryGetValue(key, out var backEdgeValue) && + backEdgeValue != forwardEdgeValue) + { + switch (backEdgeValue.NullState) + { + case NullAbstractValue.MaybeNull: + stopTrackingAnalysisDataForKeyAndChildren(); + break; + + case NullAbstractValue.NotNull: + if (backEdgeValue.MakeMayBeNull() != forwardEdgeValue) + { + if (forwardEdgeValue.NullState == NullAbstractValue.NotNull) + { + stopTrackingAnalysisDataForChildren(); + } + else + { + stopTrackingAnalysisDataForKeyAndChildren(); + } + } + + break; + + } + + void stopTrackingAnalysisDataForKeyAndChildren() + { + stopTrackingAnalysisDataForChildren(); + stopTrackingAnalysisDataForEntity(key); + } + + void stopTrackingAnalysisDataForChildren() + { + var childEntities = getChildAnalysisEntities(forwardEdgeValue) + .AddRange(getChildAnalysisEntities(backEdgeValue)); + foreach (var childEntity in childEntities) + { + stopTrackingAnalysisDataForEntity(childEntity); + } + } + + void stopTrackingAnalysisDataForEntity(AnalysisEntity entity) + { + if (entity.IsChildOrInstanceMember) + { + resetAbstractValue(entity, forwardEdgeAnalysisData); + } + } + } + } + + var resultMap = Merge(forwardEdgeAnalysisData.CoreAnalysisData, backEdgeAnalysisData.CoreAnalysisData); + Debug.Assert(Compare(forwardEdgeAnalysisData.CoreAnalysisData, resultMap) <= 0); + Debug.Assert(Compare(backEdgeAnalysisData.CoreAnalysisData, resultMap) <= 0); + return resultMap; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.NullAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.NullAbstractValueDomain.cs new file mode 100644 index 0000000000000..db07693fbf08e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.NullAbstractValueDomain.cs @@ -0,0 +1,60 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain to merge and compare values. + /// + private sealed class NullAbstractValueDomain : AbstractValueDomain + { + public static NullAbstractValueDomain Default = new(); + + private NullAbstractValueDomain() { } + + public override NullAbstractValue Bottom => NullAbstractValue.Undefined; + + public override NullAbstractValue UnknownOrMayBeValue => NullAbstractValue.MaybeNull; + + public override int Compare(NullAbstractValue oldValue, NullAbstractValue newValue, bool assertMonotonicity) + { + return Comparer.Default.Compare(oldValue, newValue); + } + + public override NullAbstractValue Merge(NullAbstractValue value1, NullAbstractValue value2) + { + NullAbstractValue result; + + if (value1 == NullAbstractValue.MaybeNull || + value2 == NullAbstractValue.MaybeNull) + { + result = NullAbstractValue.MaybeNull; + } + else if (value1 is NullAbstractValue.Invalid or NullAbstractValue.Undefined) + { + result = value2; + } + else if (value2 is NullAbstractValue.Invalid or NullAbstractValue.Undefined) + { + result = value1; + } + else if (value1 != value2) + { + // One of the values must be 'Null' and other value must be 'NotNull'. + result = NullAbstractValue.MaybeNull; + } + else + { + result = value1; + } + + return result; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAbstractValueDomain.cs new file mode 100644 index 0000000000000..26976cea4feb4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAbstractValueDomain.cs @@ -0,0 +1,148 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private sealed class PointsToAbstractValueDomain : AbstractValueDomain + { + public static PointsToAbstractValueDomain Default = new(); + private readonly SetAbstractDomain _locationsDomain = SetAbstractDomain.Default; + private readonly SetAbstractDomain _lValueCapturesDomain = SetAbstractDomain.Default; + + private PointsToAbstractValueDomain() { } + + public override PointsToAbstractValue Bottom => PointsToAbstractValue.Undefined; + + public override PointsToAbstractValue UnknownOrMayBeValue => PointsToAbstractValue.Unknown; + + public override int Compare(PointsToAbstractValue oldValue, PointsToAbstractValue newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (oldValue.Kind == newValue.Kind) + { + int locationsCompareResult = _locationsDomain.Compare(oldValue.Locations, newValue.Locations); + int lValueCapturesCompareResult = _lValueCapturesDomain.Compare(oldValue.LValueCapturedOperations, newValue.LValueCapturedOperations); + var nullCompareResult = NullAbstractValueDomain.Default.Compare(oldValue.NullState, newValue.NullState); + if (locationsCompareResult > 0 || lValueCapturesCompareResult > 0 || nullCompareResult > 0) + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + else if (locationsCompareResult < 0 || lValueCapturesCompareResult < 0 || nullCompareResult < 0) + { + return -1; + } + else + { + return 0; + } + } + else if (oldValue.Kind < newValue.Kind) + { +#if DEBUG + if (NullAbstractValueDomain.Default.Compare(oldValue.NullState, newValue.NullState) > 0) + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + } +#endif + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + public override PointsToAbstractValue Merge(PointsToAbstractValue value1, PointsToAbstractValue value2) + { + PointsToAbstractValue result; + if (value1 == value2) + { + result = value1; + } + else if (value1.Kind == PointsToAbstractValueKind.Invalid) + { + result = value2.Kind == PointsToAbstractValueKind.Undefined ? + PointsToAbstractValue.Unknown : + value2; + } + else if (value2.Kind == PointsToAbstractValueKind.Invalid) + { + result = value1.Kind == PointsToAbstractValueKind.Undefined ? + PointsToAbstractValue.Unknown : + value1; + } + else if (value1.Kind == PointsToAbstractValueKind.Undefined) + { + result = value2; + } + else if (value2.Kind == PointsToAbstractValueKind.Undefined) + { + result = value1; + } + else if (value1.Kind == PointsToAbstractValueKind.Unknown || + value2.Kind == PointsToAbstractValueKind.Unknown) + { + result = PointsToAbstractValue.Unknown; + } + else if (value1.Kind == PointsToAbstractValueKind.UnknownNull) + { + return value2.NullState == NullAbstractValue.Null ? + PointsToAbstractValue.UnknownNull : + PointsToAbstractValue.Unknown; + } + else if (value2.Kind == PointsToAbstractValueKind.UnknownNull) + { + return value1.NullState == NullAbstractValue.Null ? + PointsToAbstractValue.UnknownNull : + PointsToAbstractValue.Unknown; + } + else if (value1.Kind == PointsToAbstractValueKind.UnknownNotNull) + { + return value2.NullState == NullAbstractValue.NotNull ? + PointsToAbstractValue.UnknownNotNull : + PointsToAbstractValue.Unknown; + } + else if (value2.Kind == PointsToAbstractValueKind.UnknownNotNull) + { + return value1.NullState == NullAbstractValue.NotNull ? + PointsToAbstractValue.UnknownNotNull : + PointsToAbstractValue.Unknown; + } + else if (value1.Kind == PointsToAbstractValueKind.KnownLValueCaptures) + { + Debug.Assert(value2.Kind == PointsToAbstractValueKind.KnownLValueCaptures); + var mergedLValueCaptures = _lValueCapturesDomain.Merge(value1.LValueCapturedOperations, value2.LValueCapturedOperations); + result = PointsToAbstractValue.Create(mergedLValueCaptures); + } + else + { + Debug.Assert(value1.Kind == PointsToAbstractValueKind.KnownLocations); + Debug.Assert(value2.Kind == PointsToAbstractValueKind.KnownLocations); + + var mergedLocations = _locationsDomain.Merge(value1.Locations, value2.Locations); + var mergedNullState = NullAbstractValueDomain.Default.Merge(value1.NullState, value2.NullState); + result = PointsToAbstractValue.Create(mergedLocations, mergedNullState); + } + + Debug.Assert(Compare(value1, result) <= 0); + Debug.Assert(Compare(value2, result) <= 0); + + return result; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAnalysisDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAnalysisDomain.cs new file mode 100644 index 0000000000000..081f9f208ba62 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToAnalysisDomain.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + /// + /// An abstract analysis domain implementation for tracked by . + /// + private sealed class PointsToAnalysisDomain : PredicatedAnalysisDataDomain + { + public PointsToAnalysisDomain(DefaultPointsToValueGenerator defaultPointsToValueGenerator, Func isDisposable) + : base(new CorePointsToAnalysisDataDomain(defaultPointsToValueGenerator, ValueDomainInstance, isDisposable)) + { + } + + public PointsToAnalysisData MergeAnalysisDataForBackEdge( + PointsToAnalysisData forwardEdgeAnalysisData, + PointsToAnalysisData backEdgeAnalysisData, + Func> getChildAnalysisEntities, + Action resetAbstractValue, + Func isDisposable) + { + if (!forwardEdgeAnalysisData.IsReachableBlockData && backEdgeAnalysisData.IsReachableBlockData) + { + return (PointsToAnalysisData)backEdgeAnalysisData.Clone(); + } + else if (!backEdgeAnalysisData.IsReachableBlockData && forwardEdgeAnalysisData.IsReachableBlockData) + { + return (PointsToAnalysisData)forwardEdgeAnalysisData.Clone(); + } + + Debug.Assert(forwardEdgeAnalysisData.IsReachableBlockData == backEdgeAnalysisData.IsReachableBlockData); + + var mergedCoreAnalysisData = ((CorePointsToAnalysisDataDomain)CoreDataAnalysisDomain).MergeCoreAnalysisDataForBackEdge( + forwardEdgeAnalysisData, + backEdgeAnalysisData, + getChildAnalysisEntities, + resetAbstractValue); + return new PointsToAnalysisData(mergedCoreAnalysisData, forwardEdgeAnalysisData, + backEdgeAnalysisData, forwardEdgeAnalysisData.IsReachableBlockData, CoreDataAnalysisDomain, isDisposable); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..b331bbf84dc5d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.PointsToDataFlowOperationVisitor.cs @@ -0,0 +1,1283 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + /// + /// Operation visitor to flow the PointsTo values across a given statement in a basic block. + /// + private sealed class PointsToDataFlowOperationVisitor : + PredicateAnalysisEntityDataFlowOperationVisitor + { + private readonly DefaultPointsToValueGenerator _defaultPointsToValueGenerator; + private readonly PointsToAnalysisDomain _pointsToAnalysisDomain; + private readonly PooledDictionary.Builder> _escapedOperationLocationsBuilder; + private readonly PooledDictionary.Builder> _escapedReturnValueLocationsBuilder; + private readonly PooledDictionary.Builder> _escapedEntityLocationsBuilder; + + public PointsToDataFlowOperationVisitor( + TrackedEntitiesBuilder trackedEntitiesBuilder, + DefaultPointsToValueGenerator defaultPointsToValueGenerator, + PointsToAnalysisDomain pointsToAnalysisDomain, + PointsToAnalysisContext analysisContext) + : base(analysisContext) + { + TrackedEntitiesBuilder = trackedEntitiesBuilder; + _defaultPointsToValueGenerator = defaultPointsToValueGenerator; + _pointsToAnalysisDomain = pointsToAnalysisDomain; + _escapedOperationLocationsBuilder = PooledDictionary.Builder>.GetInstance(); + _escapedReturnValueLocationsBuilder = PooledDictionary.Builder>.GetInstance(); + _escapedEntityLocationsBuilder = PooledDictionary.Builder>.GetInstance(); + + analysisContext.InterproceduralAnalysisData?.InitialAnalysisData?.AssertValidPointsToAnalysisData(); + } + + internal TrackedEntitiesBuilder TrackedEntitiesBuilder { get; } + + public ImmutableDictionary> GetEscapedLocationsThroughOperationsMap() + => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedOperationLocationsBuilder); + + public ImmutableDictionary> GetEscapedLocationsThroughReturnValuesMap() + => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedReturnValueLocationsBuilder); + + public ImmutableDictionary> GetEscapedLocationsThroughEntitiesMap() + => GetEscapedAbstractLocationsMapAndFreeBuilder(_escapedEntityLocationsBuilder); + + private static ImmutableDictionary> GetEscapedAbstractLocationsMapAndFreeBuilder( + PooledDictionary.Builder> escapedLocationsBuilder) + where T : class + { + try + { + if (escapedLocationsBuilder.Count == 0) + { + return ImmutableDictionary>.Empty; + } + + var builder = ImmutableDictionary.CreateBuilder>(); + foreach ((var key, var valueBuilder) in escapedLocationsBuilder) + { + builder.Add(key, valueBuilder.ToImmutable()); + } + + return builder.ToImmutable(); + } + finally + { + escapedLocationsBuilder.Dispose(); + } + } + + private bool ShouldBeTracked(AnalysisEntity analysisEntity) + => PointsToAnalysis.ShouldBeTracked(analysisEntity, DataFlowAnalysisContext.PointsToAnalysisKind, IsDisposable); + private PointsToAbstractValue GetValueForEntityThatShouldNotBeTracked(AnalysisEntity analysisEntity) + { + Debug.Assert(!ShouldBeTracked(analysisEntity)); + Debug.Assert(!CurrentAnalysisData.TryGetValue(analysisEntity, out var existingValue) || existingValue == PointsToAbstractValue.NoLocation); + + return !PointsToAnalysis.ShouldBeTracked(analysisEntity.Type, IsDisposable) ? + PointsToAbstractValue.NoLocation : + _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity); + } + + public override PointsToAnalysisData Flow(IOperation statement, BasicBlock block, PointsToAnalysisData input) + { + AssertValidPointsToAnalysisData(input); +#if DEBUG + if (block.Kind == BasicBlockKind.Exit) + { + // No flow capture entities should be alive at the end of flow graph. + input.AssertNoFlowCaptureEntitiesTracked(); + } +#endif + + // Ensure PointsTo value is set for the "this" or "Me" instance. + if (!HasAbstractValue(AnalysisEntityFactory.ThisOrMeInstance) && + ShouldBeTracked(AnalysisEntityFactory.ThisOrMeInstance)) + { + input.SetAbstractValue(AnalysisEntityFactory.ThisOrMeInstance, ThisOrMePointsToAbstractValue); + } + + var output = base.Flow(statement, block, input); + AssertValidPointsToAnalysisData(output); + return output; + } + + public override (PointsToAnalysisData output, bool isFeasibleBranch) FlowBranch(BasicBlock fromBlock, BranchWithInfo branch, PointsToAnalysisData input) + { + AssertValidPointsToAnalysisData(input); + (PointsToAnalysisData output, bool isFeasibleBranch) result = base.FlowBranch(fromBlock, branch, input); + AssertValidPointsToAnalysisData(result.output); + return result; + } + + protected override void AddTrackedEntities(PointsToAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis) + { + if (!analysisData.HasAnyAbstractValue && + (forInterproceduralAnalysis || !_defaultPointsToValueGenerator.HasAnyTrackedEntity)) + { + return; + } + + foreach (var entity in TrackedEntitiesBuilder.EnumerateEntities()) + { + if (analysisData.HasAbstractValue(entity) || + !forInterproceduralAnalysis && _defaultPointsToValueGenerator.IsTrackedEntity(entity)) + { + builder.Add(entity); + } + } + } + internal override bool IsPointsToAnalysis => true; + + protected override bool HasAbstractValue(AnalysisEntity analysisEntity) => CurrentAnalysisData.HasAbstractValue(analysisEntity); + + protected override void StopTrackingEntity(AnalysisEntity analysisEntity, PointsToAnalysisData analysisData) + => analysisData.RemoveEntries(analysisEntity); + + protected override PointsToAbstractValue GetAbstractValue(AnalysisEntity analysisEntity) + { + if (!ShouldBeTracked(analysisEntity)) + { + return GetValueForEntityThatShouldNotBeTracked(analysisEntity); + } + + if (!CurrentAnalysisData.TryGetValue(analysisEntity, out var value)) + { + value = _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity); + } + + return value; + } + + protected override PointsToAbstractValue GetPointsToAbstractValue(IOperation operation) => base.GetCachedAbstractValue(operation); + + protected override PointsToAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => !PointsToAnalysis.ShouldBeTracked(type, IsDisposable) ? PointsToAbstractValue.NoLocation : PointsToAbstractValue.NullLocation; + + protected override bool HasAnyAbstractValue(PointsToAnalysisData data) => data.HasAnyAbstractValue; + + protected override void SetAbstractValue(AnalysisEntity analysisEntity, PointsToAbstractValue value) + { + Debug.Assert(ShouldBeTracked(analysisEntity) || !HasAbstractValue(analysisEntity)); + + if (ShouldBeTracked(analysisEntity)) + { + if (value.Kind == PointsToAbstractValueKind.Undefined) + { + Debug.Assert(value == _defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity)); + Debug.Assert(!CurrentAnalysisData.TryGetValue(analysisEntity, out var currentValue) || + currentValue.Kind == PointsToAbstractValueKind.Unknown && + analysisEntity.Symbol is IParameterSymbol { RefKind: RefKind.Out }); + return; + } + + SetAbstractValueCore(CurrentAnalysisData, analysisEntity, value); + TrackedEntitiesBuilder.AddEntityAndPointsToValue(analysisEntity, value); + } + } + + protected override CopyAbstractValue GetCopyAbstractValue(IOperation operation) + { + if (DataFlowAnalysisContext.CopyAnalysisResult == null && + AnalysisEntityFactory.TryCreate(operation, out var entity) && + entity.CaptureId.HasValue && + AnalysisEntityFactory.TryGetCopyValueForFlowCapture(entity.CaptureId.Value.Id, out var copyValue) && + copyValue.Kind == CopyAbstractValueKind.KnownReferenceCopy) + { + return copyValue; + } + + return base.GetCopyAbstractValue(operation); + } + + private static void SetAbstractValueCore(PointsToAnalysisData pointsToAnalysisData, AnalysisEntity analysisEntity, PointsToAbstractValue value) + => pointsToAnalysisData.SetAbstractValue(analysisEntity, value); + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, PointsToAbstractValue assignedValue) + { + if (assignedValue == PointsToAbstractValue.Undefined) + { + return; + } + + base.SetAbstractValueForTupleElementAssignment(tupleElementEntity, assignedValueOperation, assignedValue); + } + + protected override void ResetAbstractValue(AnalysisEntity analysisEntity) + { + if (analysisEntity.IsLValueFlowCaptureEntity) + { + // Flow captures can never be re-assigned. + return; + } + + SetAbstractValue(analysisEntity, PointsToAbstractValue.Unknown); + } + + private static void ResetAbstractValueIfTracked(AnalysisEntity analysisEntity, PointsToAnalysisData pointsToAnalysisData) + { + if (pointsToAnalysisData.TryGetValue(analysisEntity, out var currentValue)) + { + pointsToAnalysisData.SetAbstractValue(analysisEntity, GetResetValue(analysisEntity, currentValue)); + } + } + + // Create a dummy PointsTo value for each reference type parameter. + protected override PointsToAbstractValue GetDefaultValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity) + => PointsToAnalysis.ShouldBeTracked(parameter.Type, IsDisposable) ? + PointsToAbstractValue.Create( + AbstractLocation.CreateSymbolLocation(parameter, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack), + mayBeNull: true) : + PointsToAbstractValue.NoLocation; + + protected override void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + // Mark PointsTo values for ref/out parameters in non-interprocedural context as escaped. + if (parameter.RefKind is RefKind.Ref or RefKind.Out) + { + Debug.Assert(DataFlowAnalysisContext.InterproceduralAnalysisData == null); + var pointsToValue = GetAbstractValue(analysisEntity); + HandleEscapingLocations(analysisEntity, _escapedEntityLocationsBuilder, analysisEntity, pointsToValue); + } + } + + private static PointsToAbstractValue GetResetValue(AnalysisEntity analysisEntity, PointsToAbstractValue currentValue) + { + if (analysisEntity.IsLValueFlowCaptureEntity) + { + // LValue flow capture PointsToAbstractValue can never change. + return currentValue; + } + + return PointsToAbstractValue.Unknown; + } + + protected override void ResetCurrentAnalysisData() => CurrentAnalysisData.Reset(GetResetValue); + + protected override PointsToAbstractValue ComputeAnalysisValueForReferenceOperation(IOperation operation, PointsToAbstractValue defaultValue) + { + if (PointsToAnalysis.ShouldBeTracked(operation.Type, IsDisposable) && + AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + return GetAbstractValue(analysisEntity); + } + else + { + Debug.Assert(operation.Type == null || !operation.Type.IsNonNullableValueType() || defaultValue == PointsToAbstractValue.NoLocation); + return defaultValue; + } + } + + protected override PointsToAbstractValue ComputeAnalysisValueForEscapedRefOrOutArgument(AnalysisEntity analysisEntity, IArgumentOperation operation, PointsToAbstractValue defaultValue) + { + Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out); + + if (!ShouldBeTracked(analysisEntity)) + { + return GetValueForEntityThatShouldNotBeTracked(analysisEntity); + } + + var location = AbstractLocation.CreateAllocationLocation(operation, analysisEntity.Type, DataFlowAnalysisContext); + return PointsToAbstractValue.Create(location, mayBeNull: true); + } + + protected override void PostProcessArgument(IArgumentOperation operation, bool isEscaped) + { + base.PostProcessArgument(operation, isEscaped); + + if (!isEscaped) + { + // Update abstract value for unescaped ref or out argument (interprocedural analysis case). + if ((operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out) && + AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + CacheAbstractValue(operation, GetAbstractValue(analysisEntity)); + + if (analysisEntity.Symbol?.Kind == SymbolKind.Field) + { + // Ref/Out field argument is considered escaped. + HandleEscapingOperation(operation, operation); + } + } + } + else if (operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out) + { + if (operation.Parameter.RefKind == RefKind.Ref) + { + // Input by-ref argument passed to invoked method is considered escaped in non-interprocedural analysis case. + HandleEscapingOperation(operation, operation.Value); + } + + // Output by-ref or out argument might be escaped if assigned to a field. + HandlePossibleEscapingForAssignment(target: operation.Value, value: operation, operation: operation); + } + } + + protected override void ProcessReturnValue(IOperation? returnValue) + { + base.ProcessReturnValue(returnValue); + + // Escape the return value if we are not analyzing an invoked method during interprocedural analysis. + if (returnValue != null && + DataFlowAnalysisContext.InterproceduralAnalysisData == null) + { + HandleEscapingOperation(escapingOperation: returnValue, escapedInstance: returnValue, _escapedReturnValueLocationsBuilder); + } + } + + private protected override PointsToAbstractValue GetAbstractValueForImplicitWrappingTaskCreation(IOperation returnValueOperation, PointsToAbstractValue returnValue, PointsToAbstractValue implicitTaskPointsToValue) + { + return implicitTaskPointsToValue; + } + + #region Predicate analysis + private static bool IsValidValueForPredicateAnalysis(NullAbstractValue value) + { + return value switch + { + NullAbstractValue.Null + or NullAbstractValue.NotNull => true, + _ => false, + }; + } + + protected override PredicateValueKind SetValueForEqualsOrNotEqualsComparisonOperator( + IOperation leftOperand, + IOperation rightOperand, + bool equals, + bool isReferenceEquality, + PointsToAnalysisData targetAnalysisData) + { + var predicateValueKind = PredicateValueKind.Unknown; + + // Handle "a == null" and "a != null" + if (SetValueForNullCompare(leftOperand, rightOperand, equals, ref predicateValueKind, targetAnalysisData)) + { + return predicateValueKind; + } + + // Otherwise, handle "null == a" and "null != a" + SetValueForNullCompare(rightOperand, leftOperand, equals, ref predicateValueKind, targetAnalysisData); + return predicateValueKind; + } + + protected override PredicateValueKind SetValueForIsNullComparisonOperator(IOperation leftOperand, bool equals, PointsToAnalysisData targetAnalysisData) + { + var predicateValueKind = PredicateValueKind.Unknown; + SetValueForNullCompare(leftOperand, value: NullAbstractValue.Null, equals: equals, predicateValueKind: ref predicateValueKind, targetAnalysisData: targetAnalysisData); + return predicateValueKind; + } + + private bool SetValueForNullCompare( + IOperation target, + IOperation assignedValueOperation, + bool equals, + ref PredicateValueKind predicateValueKind, + PointsToAnalysisData targetAnalysisData) + { + NullAbstractValue value = GetNullAbstractValue(assignedValueOperation); + return SetValueForNullCompare(target, value, equals, ref predicateValueKind, targetAnalysisData); + } + + private bool SetValueForNullCompare( + IOperation target, + NullAbstractValue value, + bool equals, + ref PredicateValueKind predicateValueKind, + PointsToAnalysisData targetAnalysisData) + { + if (IsValidValueForPredicateAnalysis(value) && + AnalysisEntityFactory.TryCreate(target, out var targetEntity) && + ShouldBeTracked(targetEntity)) + { + // Comparison with a non-null value guarantees that we can infer result in only one of the branches. + // For example, predicate "a == c", where we know 'c' is non-null, guarantees 'a' is non-null in CurrentAnalysisData, + bool inferInTargetAnalysisData = !(value == NullAbstractValue.NotNull && !equals); + + CopyAbstractValue copyValue = GetCopyAbstractValue(target); + if (copyValue.Kind.IsKnown()) + { + foreach (var analysisEntity in copyValue.AnalysisEntities) + { + SetValueForNullCompareFromPredicate(analysisEntity, value, targetEntity.Type, equals, inferInTargetAnalysisData, + ref predicateValueKind, _defaultPointsToValueGenerator, WellKnownTypeProvider.Compilation, IsDisposable, + sourceAnalysisData: CurrentAnalysisData, targetAnalysisData: targetAnalysisData); + } + } + else + { + SetValueForNullCompareFromPredicate(targetEntity, value, targetEntity.Type, equals, inferInTargetAnalysisData, + ref predicateValueKind, _defaultPointsToValueGenerator, WellKnownTypeProvider.Compilation, IsDisposable, + sourceAnalysisData: CurrentAnalysisData, targetAnalysisData: targetAnalysisData); + } + + return true; + } + + return false; + } + + private static void SetValueForNullCompareFromPredicate( + AnalysisEntity key, + NullAbstractValue value, + ITypeSymbol targetType, + bool equals, + bool inferInTargetAnalysisData, + ref PredicateValueKind predicateValueKind, + DefaultPointsToValueGenerator defaultPointsToValueGenerator, + Compilation compilation, + Func isDisposable, + PointsToAnalysisData sourceAnalysisData, + PointsToAnalysisData targetAnalysisData) + { + if (!PointsToAnalysis.ShouldBeTracked(key, defaultPointsToValueGenerator.PointsToAnalysisKind, isDisposable)) + { + Debug.Assert(!targetAnalysisData.HasAbstractValue(key)); + Debug.Assert(!sourceAnalysisData.HasAbstractValue(key)); + return; + } + + // Compute the negated value. + NullAbstractValue negatedValue = NegatePredicateValue(value); + + // Check if the key already has an existing "Null" or "NotNull" NullState that would make the condition always true or false. + // If so, set the predicateValueKind to always true/false, set the value in branch that can never be taken to NullAbstractValue.Invalid + // and turn off value inference in one of the branch. + if (sourceAnalysisData.TryGetValue(key, out var existingPointsToValue)) + { + NullAbstractValue existingNullValue = existingPointsToValue.NullState; + if (IsValidValueForPredicateAnalysis(existingNullValue) && + (existingNullValue == NullAbstractValue.Null || value == NullAbstractValue.Null)) + { + if (value == existingNullValue && equals || + negatedValue == existingNullValue && !equals) + { + predicateValueKind = PredicateValueKind.AlwaysTrue; + negatedValue = NullAbstractValue.Invalid; + inferInTargetAnalysisData = false; + } + + if (negatedValue == existingNullValue && equals || + value == existingNullValue && !equals) + { + predicateValueKind = PredicateValueKind.AlwaysFalse; + value = NullAbstractValue.Invalid; + } + } + } + + // Swap value and negatedValue if we are processing not-equals operator. + if (!equals) + { + if (value != NullAbstractValue.Invalid && negatedValue != NullAbstractValue.Invalid) + { + value = negatedValue; + } + } + + if (inferInTargetAnalysisData) + { + // Set value for the CurrentAnalysisData. + SetAbstractValueFromPredicate(key, value, targetType, defaultPointsToValueGenerator, compilation, sourceAnalysisData, targetAnalysisData); + } + + return; + + static void SetAbstractValueFromPredicate( + AnalysisEntity analysisEntity, + NullAbstractValue nullState, + ITypeSymbol targetType, + DefaultPointsToValueGenerator defaultPointsToValueGenerator, + Compilation compilation, + PointsToAnalysisData sourceAnalysisData, + PointsToAnalysisData targetAnalysisData) + { + AssertValidPointsToAnalysisData(sourceAnalysisData); + AssertValidPointsToAnalysisData(targetAnalysisData); + + Debug.Assert(IsValidValueForPredicateAnalysis(nullState) || nullState == NullAbstractValue.Invalid); + + // Ensure that the predicated 'value' can be flowed from the target type to the analysis entity. + if (!SymbolEqualityComparer.Default.Equals(targetType, analysisEntity.Type)) + { + var conversion = compilation.ClassifyCommonConversion(targetType, analysisEntity.Type); + if (!conversion.Exists) + { + // No conversion exists, so we bail out from flowing the predicated value. + return; + } + + if (!conversion.IsIdentity && !conversion.IsNumeric && !conversion.IsNullable) + { + // For 'Null' predicated value, there needs to be an explicit conversion + // from targetType to the analysisEntity's type to flow the predicated value. + // For 'NotNull' predicated value, there needs to be an implicit conversion + // from targetType to the analysisEntity's type to flow the predicated value. + if (nullState == NullAbstractValue.Null && conversion.IsImplicit || + nullState == NullAbstractValue.NotNull && !conversion.IsImplicit) + { + return; + } + } + } + + if (!sourceAnalysisData.TryGetValue(analysisEntity, out var existingValue)) + { + existingValue = defaultPointsToValueGenerator.GetOrCreateDefaultValue(analysisEntity); + } + + var newPointsToValue = nullState switch + { + NullAbstractValue.Null => existingValue.MakeNull(), + + NullAbstractValue.NotNull => existingValue.MakeNonNull(), + + NullAbstractValue.Invalid => PointsToAbstractValue.Invalid, + + _ => throw new InvalidProgramException(), + }; + + targetAnalysisData.SetAbstractValue(analysisEntity, newPointsToValue); + AssertValidPointsToAnalysisData(targetAnalysisData); + } + } + + private static NullAbstractValue NegatePredicateValue(NullAbstractValue value) + { + Debug.Assert(IsValidValueForPredicateAnalysis(value)); + + return value switch + { + NullAbstractValue.Null => NullAbstractValue.NotNull, + + NullAbstractValue.NotNull => NullAbstractValue.Null, + + _ => throw new InvalidProgramException(), + }; + } + #endregion + + protected override PointsToAnalysisData MergeAnalysisData(PointsToAnalysisData value1, PointsToAnalysisData value2) + => _pointsToAnalysisDomain.Merge(value1, value2); + protected override PointsToAnalysisData MergeAnalysisDataForBackEdge(PointsToAnalysisData value1, PointsToAnalysisData value2, BasicBlock forBlock) + => _pointsToAnalysisDomain.MergeAnalysisDataForBackEdge(value1, value2, GetChildAnalysisEntities, ResetAbstractValueIfTracked, IsDisposable); + protected override void UpdateValuesForAnalysisData(PointsToAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData); + protected override PointsToAnalysisData GetClonedAnalysisData(PointsToAnalysisData analysisData) + => (PointsToAnalysisData)analysisData.Clone(); + public override PointsToAnalysisData GetEmptyAnalysisData() + => new(IsDisposable); + protected override PointsToAnalysisData GetExitBlockOutputData(PointsToAnalysisResult analysisResult) + => new(analysisResult.ExitBlockOutput.Data, IsDisposable); + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(PointsToAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData, throwBranchWithExceptionType); + protected override void AssertValidAnalysisData(PointsToAnalysisData analysisData) + => AssertValidPointsToAnalysisData(analysisData); + protected override bool Equals(PointsToAnalysisData value1, PointsToAnalysisData value2) + => value1.Equals(value2); + + protected override void ApplyInterproceduralAnalysisResultCore(PointsToAnalysisData resultData) + { + ApplyInterproceduralAnalysisResultHelper(resultData.CoreAnalysisData); + AssertValidPointsToAnalysisData(CurrentAnalysisData); + } + + protected override PointsToAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) + { + var trimmedData = GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData.CoreAnalysisData, SetAbstractValueCore); + AssertValidPointsToAnalysisData(trimmedData); + return trimmedData; + } + + protected override PointsToAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + { + pointsToValues = CurrentAnalysisData.CoreAnalysisData; + var initialAnalysisData = base.GetInitialInterproceduralAnalysisData(invokedMethod, + invocationInstance, thisOrMeInstanceForCaller, argumentValuesMap, pointsToValues, + copyValues, valueContentValues, isLambdaOrLocalFunction, hasParameterWithDelegateType); + AssertValidPointsToAnalysisData(initialAnalysisData); + return initialAnalysisData; + } + + private void HandleEscapingOperation(IOperation escapingOperation, IOperation escapedInstance) + { + HandleEscapingOperation(escapingOperation, escapedInstance, _escapedOperationLocationsBuilder); + } + + private void HandleEscapingOperation(IOperation escapingOperation, IOperation escapedInstance, PooledDictionary.Builder> builder) + { + PointsToAbstractValue escapedInstancePointsToValue = GetPointsToAbstractValue(escapedInstance); + if (escapedInstancePointsToValue.Kind == PointsToAbstractValueKind.KnownLValueCaptures) + { + foreach (var capturedOperation in escapedInstancePointsToValue.LValueCapturedOperations) + { + HandleEscapingOperation(escapingOperation, capturedOperation, builder); + } + + return; + } + + AnalysisEntityFactory.TryCreate(escapedInstance, out var escapedEntityOpt); + HandleEscapingLocations(escapingOperation, builder, escapedEntityOpt, escapedInstancePointsToValue); + } + + private void HandleEscapingLocations( + TKey key, + PooledDictionary.Builder> escapedLocationsBuilder, + AnalysisEntity? escapedEntity, + PointsToAbstractValue escapedInstancePointsToValue) + where TKey : class + { + // Start by clearing escaped locations from previous flow analysis iterations. + if (escapedLocationsBuilder.TryGetValue(key, out var builder)) + { + builder.Clear(); + } + + HandleEscapingLocations(key, escapedLocationsBuilder, escapedInstancePointsToValue); + + // For value type entities, we also need to handle escaping the locations for child entities. + if (escapedEntity?.Type.HasValueCopySemantics() == true) + { + HandleEscapingLocations(key, escapedLocationsBuilder, escapedEntity.InstanceLocation); + } + } + + private void HandleEscapingLocations( + TKey key, + PooledDictionary.Builder> escapedLocationsBuilder, + PointsToAbstractValue pointsToValueOfEscapedInstance) + where TKey : class + { + if (pointsToValueOfEscapedInstance.Locations.IsEmpty || + pointsToValueOfEscapedInstance == PointsToAbstractValue.NoLocation || + pointsToValueOfEscapedInstance == PointsToAbstractValue.NullLocation) + { + return; + } + + if (!escapedLocationsBuilder.TryGetValue(key, out var builder)) + { + builder = ImmutableHashSet.CreateBuilder(); + escapedLocationsBuilder.Add(key, builder); + } + + HandleEscapingLocations(pointsToValueOfEscapedInstance, builder); + foreach (var childEntity in GetChildAnalysisEntities(pointsToValueOfEscapedInstance)) + { + var pointsToValueOfEscapedChild = GetAbstractValue(childEntity); + HandleEscapingLocations(pointsToValueOfEscapedChild, builder); + } + + if (TryGetTaskWrappedValue(pointsToValueOfEscapedInstance, out var wrappedValue)) + { + HandleEscapingLocations(key, escapedLocationsBuilder, wrappedValue); + } + } + + private void HandleEscapingLocations(PointsToAbstractValue pointsToValueOfEscapedInstance, ImmutableHashSet.Builder builder) + { + foreach (var escapedLocation in pointsToValueOfEscapedInstance.Locations) + { + // Only escape locations associated with creations. + // We can expand this for more cases in future if need arises. + if (escapedLocation.Creation != null) + { + builder.Add(escapedLocation); + } + } + + MarkEscapedLambdasAndLocalFunctions(pointsToValueOfEscapedInstance); + } + + private void HandlePossibleEscapingForAssignment(IOperation target, IOperation value, IOperation operation) + { + // FxCop compat: The object assigned to a field or a property or an array element is considered escaped. + // TODO: Perform better analysis for array element assignments as we already track element locations. + // https://github.com/dotnet/roslyn-analyzers/issues/1577 + if (target is IMemberReferenceOperation || + target.Kind == OperationKind.ArrayElementReference) + { + HandleEscapingOperation(operation, value); + } + } + + protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, PointsToAbstractValue assignedValue, bool mayBeAssignment = false) + { + base.SetAbstractValueForAssignment(target, assignedValueOperation, assignedValue, mayBeAssignment); + + if (assignedValueOperation != null) + { + HandlePossibleEscapingForAssignment(target, assignedValueOperation, assignedValueOperation); + } + } + + protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, PointsToAbstractValue value) + { + base.SetAbstractValueForArrayElementInitializer(arrayCreation, indices, elementType, initializer, value); + + // We use the array initializer as the escaping operation instead of arrayCreation + // to ensure we have a unique escaping operation key for each initializer. + HandleEscapingOperation(initializer, initializer); + } + + #region Visitor methods + + public override PointsToAbstractValue DefaultVisit(IOperation operation, object? argument) + { + _ = base.DefaultVisit(operation, argument); + + // Special handling for: + // 1. Null value: NullLocation + // 2. Constants and value types do not point to any location. + if (operation.ConstantValue.HasValue) + { + if (operation.Type == null || + operation.ConstantValue.Value == null) + { + return PointsToAbstractValue.NullLocation; + } + else + { + return PointsToAbstractValue.NoLocation; + } + } + else if (operation.Type.IsNonNullableValueType()) + { + return PointsToAbstractValue.NoLocation; + } + + return ValueDomain.UnknownOrMayBeValue; + } + + public override PointsToAbstractValue VisitIsType(IIsTypeOperation operation, object? argument) + { + _ = base.VisitIsType(operation, argument); + return PointsToAbstractValue.NoLocation; + } + + public override PointsToAbstractValue VisitInstanceReference(IInstanceReferenceOperation operation, object? argument) + { + _ = base.VisitInstanceReference(operation, argument); + IOperation? currentInstanceOperation = operation.GetInstance(IsInsideAnonymousObjectInitializer); + var value = currentInstanceOperation != null ? + GetCachedAbstractValue(currentInstanceOperation) : + ThisOrMePointsToAbstractValue; + Debug.Assert(value.NullState == NullAbstractValue.NotNull || DataFlowAnalysisContext.InterproceduralAnalysisData != null); + return value; + } + + private PointsToAbstractValue VisitTypeCreationWithArgumentsAndInitializer( + TOperation operation, + object? argument, + Func baseVisit) + where TOperation : IOperation + { + AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type!, DataFlowAnalysisContext); + var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false); + CacheAbstractValue(operation, pointsToAbstractValue); + + _ = baseVisit(operation, argument); + + return pointsToAbstractValue; + } + + public override PointsToAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitObjectCreation); + } + + public override PointsToAbstractValue VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation, object? argument) + { + return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitDynamicObjectCreation); + } + + public override PointsToAbstractValue VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation, object? argument) + { + return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitAnonymousObjectCreation); + } + + public override PointsToAbstractValue VisitReDimClause(IReDimClauseOperation operation, object? argument) + { + if (operation.Operand.Type == null) + { + return base.VisitReDimClause(operation, argument)!; + } + + AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Operand.Type, DataFlowAnalysisContext); + var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false); + CacheAbstractValue(operation, pointsToAbstractValue); + + _ = base.VisitReDimClause(operation, argument); + + return pointsToAbstractValue; + } + + public override PointsToAbstractValue VisitTuple(ITupleOperation operation, object? argument) + { + PointsToAbstractValue pointsToAbstractValue; + if (operation.Type.GetUnderlyingValueTupleTypeOrThis() is { } type) + { + AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, type, DataFlowAnalysisContext); + pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: false); + } + else + { + pointsToAbstractValue = PointsToAbstractValue.Unknown; + } + + CacheAbstractValue(operation, pointsToAbstractValue); + _ = base.VisitTuple(operation, argument); + return pointsToAbstractValue; + } + + public override PointsToAbstractValue VisitDelegateCreation(IDelegateCreationOperation operation, object? argument) + { + _ = base.VisitDelegateCreation(operation, argument); + if (operation.Type is null) + return PointsToAbstractValue.Unknown; + + AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext); + return PointsToAbstractValue.Create(location, mayBeNull: false); + } + + public override PointsToAbstractValue VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation, object? argument) + { + return VisitTypeCreationWithArgumentsAndInitializer(operation, argument, base.VisitTypeParameterObjectCreation); + } + + public override PointsToAbstractValue VisitArrayInitializer(IArrayInitializerOperation operation, object? argument) + { + _ = base.VisitArrayInitializer(operation, argument); + + // We should have created a new PointsTo value for the associated array creation operation in non-error code. + // Bail out otherwise. + var arrayCreation = operation.GetAncestor(OperationKind.ArrayCreation); + return arrayCreation != null ? GetCachedAbstractValue(arrayCreation) : ValueDomain.UnknownOrMayBeValue; + } + + public override PointsToAbstractValue VisitArrayCreation(IArrayCreationOperation operation, object? argument) + { + var pointsToAbstractValue = operation.Type != null + ? PointsToAbstractValue.Create(AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext), mayBeNull: false) + : PointsToAbstractValue.Unknown; + CacheAbstractValue(operation, pointsToAbstractValue); + + _ = VisitArray(operation.DimensionSizes, argument); + var initializerValue = Visit(operation.Initializer, argument); + Debug.Assert(operation.Initializer == null || initializerValue == pointsToAbstractValue); + return pointsToAbstractValue; + } + + public override PointsToAbstractValue VisitInterpolatedString(IInterpolatedStringOperation operation, object? argument) + { + _ = base.VisitInterpolatedString(operation, argument); + return PointsToAbstractValue.NoLocation; + } + + public override PointsToAbstractValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument) + { + _ = base.VisitBinaryOperatorCore(operation, argument); + return PointsToAbstractValue.Unknown; + } + + public override PointsToAbstractValue VisitSizeOf(ISizeOfOperation operation, object? argument) + { + _ = base.VisitSizeOf(operation, argument); + return PointsToAbstractValue.NoLocation; + } + + public override PointsToAbstractValue VisitTypeOf(ITypeOfOperation operation, object? argument) + { + _ = base.VisitTypeOf(operation, argument); + return PointsToAbstractValue.NoLocation; + } + + private PointsToAbstractValue VisitInvocationCommon(IOperation operation, IOperation? instance) + { + if (PointsToAnalysis.ShouldBeTracked(operation.Type, IsDisposable)) + { + if (TryGetInterproceduralAnalysisResult(operation, out var interproceduralResult)) + { + return interproceduralResult.ReturnValueAndPredicateKind!.Value.Value; + } + + AbstractLocation location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext); + var pointsToAbstractValue = PointsToAbstractValue.Create(location, mayBeNull: true); + return GetValueBasedOnInstanceOrReferenceValue(referenceOrInstance: instance, operation: operation, defaultValue: pointsToAbstractValue); + } + else + { + return PointsToAbstractValue.NoLocation; + } + } + + public override PointsToAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + PointsToAbstractValue defaultValue) + { + _ = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + + if (!visitedArguments.IsEmpty && + method.IsCollectionAddMethod(CollectionNamedTypes)) + { + // FxCop compat: The object added to a collection is considered escaped. + foreach (var argument in visitedArguments) + { + HandleEscapingOperation(argument, argument.Value); + } + } + + var value = VisitInvocationCommon(originalOperation, visitedInstance); + + if (IsSpecialMethodReturningNonNullValue(method, DataFlowAnalysisContext.WellKnownTypeProvider) && + !TryGetInterproceduralAnalysisResult(originalOperation, out _)) + { + return value.MakeNonNull(); + } + + return value; + } + + private static bool IsSpecialMethodReturningNonNullValue(IMethodSymbol method, WellKnownTypeProvider wellKnownTypeProvider) + => IsSpecialFactoryMethodReturningNonNullValue(method, wellKnownTypeProvider) || IsSpecialEmptyMember(method); + + /// + /// Returns true if this special static factory method whose name starts with "Create", such that + /// method's return type is not nullable, i.e. 'type?', and + /// method's containing type is static OR a special type OR derives from or is same as the type of the field/property/method return. + /// For example: class SomeType { static SomeType CreateXXX(...); } + /// + private static bool IsSpecialFactoryMethodReturningNonNullValue(IMethodSymbol method, WellKnownTypeProvider wellKnownTypeProvider) + { + if (!method.IsStatic || + !method.Name.StartsWith("Create", StringComparison.Ordinal) || + method.ReturnType.NullableAnnotation == NullableAnnotation.Annotated) + { + return false; + } + + // 'Activator.CreateInstance' can return 'null'. + // Even though it is nullable annotated to return 'object?', + // the NullableAnnotation check above fails for VB, so we special case it here. + if (method.Name.Equals("CreateInstance", StringComparison.Ordinal) && + wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemActivator) is { } activatorType && + activatorType.Equals(method.ContainingType)) + { + return false; + } + + return method.ContainingType.IsStatic || + method.ContainingType.SpecialType != SpecialType.None || + method.ReturnType is INamedTypeSymbol namedType && + method.ContainingType.DerivesFromOrImplementsAnyConstructionOf(namedType.OriginalDefinition); + } + + /// + /// Returns true if this special member symbol named "Empty", such that one of the following is true: + /// 1. It is a static method with no parameters or + /// 2. It is a static readonly property or + /// 3. It is static readonly field + /// and symbol's containing type is a special type or derives from or is same as the type of the field/property/method return. + /// For example: + /// 1. class SomeType { static readonly SomeType Empty; } + /// 2. class SomeType { static readonly SomeType Empty { get; } } + /// 3. class SomeType { static SomeType Empty(); } + /// + private static bool IsSpecialEmptyMember(ISymbol symbol) + { + return symbol.IsStatic && + symbol.Name.Equals("Empty", StringComparison.Ordinal) && + (symbol.IsReadOnlyFieldOrProperty() || symbol.Kind == SymbolKind.Method) && + (symbol.ContainingType.SpecialType != SpecialType.None || + symbol.GetMemberType() is INamedTypeSymbol namedType && + symbol.ContainingType.DerivesFromOrImplementsAnyConstructionOf(namedType.OriginalDefinition)); + } + + public override PointsToAbstractValue VisitInvocation_LocalFunction( + IMethodSymbol localFunction, + ImmutableArray visitedArguments, + IOperation originalOperation, + PointsToAbstractValue defaultValue) + { + _ = base.VisitInvocation_LocalFunction(localFunction, visitedArguments, originalOperation, defaultValue); + return VisitInvocationCommon(originalOperation, instance: null); + } + + public override PointsToAbstractValue VisitInvocation_Lambda( + IFlowAnonymousFunctionOperation lambda, + ImmutableArray visitedArguments, + IOperation originalOperation, + PointsToAbstractValue defaultValue) + { + _ = base.VisitInvocation_Lambda(lambda, visitedArguments, originalOperation, defaultValue); + return VisitInvocationCommon(originalOperation, instance: null); + } + + public override PointsToAbstractValue VisitDynamicInvocation(IDynamicInvocationOperation operation, object? argument) + { + _ = base.VisitDynamicInvocation(operation, argument); + return VisitInvocationCommon(operation, operation.Operation); + } + + private NullAbstractValue GetNullStateBasedOnInstanceOrReferenceValue(IOperation? referenceOrInstance, ITypeSymbol? operationType, NullAbstractValue defaultValue) + { + if (operationType.IsNonNullableValueType()) + { + return NullAbstractValue.NotNull; + } + + NullAbstractValue referenceOrInstanceValue = referenceOrInstance != null ? GetNullAbstractValue(referenceOrInstance) : NullAbstractValue.NotNull; + return referenceOrInstanceValue switch + { + NullAbstractValue.Invalid + or NullAbstractValue.Null => referenceOrInstanceValue, + _ => defaultValue, + }; + } + + private PointsToAbstractValue GetValueBasedOnInstanceOrReferenceValue(IOperation? referenceOrInstance, IOperation operation, PointsToAbstractValue defaultValue) + { + NullAbstractValue nullState = GetNullStateBasedOnInstanceOrReferenceValue(referenceOrInstance, operation.Type, defaultValue.NullState); + return nullState switch + { + NullAbstractValue.NotNull => defaultValue.MakeNonNull(), + + NullAbstractValue.Null => defaultValue.MakeNull(), + + NullAbstractValue.Invalid => PointsToAbstractValue.Invalid, + + _ => defaultValue, + }; + } + + public override PointsToAbstractValue VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument); + + // "class SomeType { static readonly SomeType Empty; }" + if (IsSpecialEmptyMember(operation.Field) && + value.NullState != NullAbstractValue.Null) + { + return value.MakeNonNull(); + } + + return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value); + } + + public override PointsToAbstractValue VisitPropertyReference(IPropertyReferenceOperation operation, object? argument) + { + var value = base.VisitPropertyReference(operation, argument); + + // "class SomeType { static SomeType Empty { get; } }" + if (IsSpecialEmptyMember(operation.Property) && + value.NullState != NullAbstractValue.Null) + { + return value.MakeNonNull(); + } + + return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value); + } + + public override PointsToAbstractValue VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation, object? argument) + { + var value = base.VisitDynamicMemberReference(operation, argument); + return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value); + } + + public override PointsToAbstractValue VisitMethodReference(IMethodReferenceOperation operation, object? argument) + { + var value = base.VisitMethodReference(operation, argument); + return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value); + } + + public override PointsToAbstractValue VisitEventReference(IEventReferenceOperation operation, object? argument) + { + var value = base.VisitEventReference(operation, argument); + return GetValueBasedOnInstanceOrReferenceValue(operation.Instance, operation, value); + } + + public override PointsToAbstractValue VisitArrayElementReference(IArrayElementReferenceOperation operation, object? argument) + { + var value = base.VisitArrayElementReference(operation, argument); + return GetValueBasedOnInstanceOrReferenceValue(operation.ArrayReference, operation, value); + } + + public override PointsToAbstractValue VisitDynamicIndexerAccess(IDynamicIndexerAccessOperation operation, object? argument) + { + var value = base.VisitDynamicIndexerAccess(operation, argument)!; + return GetValueBasedOnInstanceOrReferenceValue(operation.Operation, operation, value); + } + + public override PointsToAbstractValue VisitConversion(IConversionOperation operation, object? argument) + { + var value = base.VisitConversion(operation, argument); + + if (operation.OperatorMethod != null) + { + // Conservatively handle user defined conversions as escaping operations. + HandleEscapingOperation(operation, operation.Operand); + return value; + } + + ConversionInference? inference = null; + if (value.NullState == NullAbstractValue.NotNull) + { + if (TryInferConversion(operation, out var conversionInference)) + { + inference = conversionInference; + value = InferConversionCommon(conversionInference, value); + } + else + { + value = value.MakeMayBeNull(); + } + } + + return HandleBoxingUnboxing(value, operation, inference ?? ConversionInference.Create(operation)); + } + + public override PointsToAbstractValue GetAssignedValueForPattern(IIsPatternOperation operation, PointsToAbstractValue operandValue) + { + var value = base.GetAssignedValueForPattern(operation, operandValue); + + ConversionInference? inference = null; + if (operandValue.NullState == NullAbstractValue.NotNull && + PointsToAnalysis.ShouldBeTracked(operation.Value.Type, IsDisposable)) + { + if (TryInferConversion(operation, out var conversionInference)) + { + inference = conversionInference; + value = InferConversionCommon(conversionInference, operandValue); + } + else + { + value = operandValue.MakeMayBeNull(); + } + } + + return HandleBoxingUnboxing(value, operation, inference ?? ConversionInference.Create(operation)); + } + + private static PointsToAbstractValue InferConversionCommon(ConversionInference inference, PointsToAbstractValue operandValue) + { + Debug.Assert(!inference.AlwaysSucceed || !inference.AlwaysFail); + if (inference.AlwaysFail) + { + return operandValue.MakeNull(); + } + else if (inference.IsTryCast && !inference.AlwaysSucceed) + { + // TryCast which may or may not succeed. + return operandValue.MakeMayBeNull(); + } + + return operandValue; + } + + private PointsToAbstractValue HandleBoxingUnboxing( + PointsToAbstractValue value, + IOperation operation, + ConversionInference inference) + { + if (inference.IsBoxing && operation.Type != null) + { + Debug.Assert(!inference.IsUnboxing); + var location = AbstractLocation.CreateAllocationLocation(operation, operation.Type, DataFlowAnalysisContext); + return PointsToAbstractValue.Create(location, mayBeNull: false); + } + else if (inference.IsUnboxing && operation.Type.IsNonNullableValueType()) + { + return PointsToAbstractValue.NoLocation; + } + else + { + return value; + } + } + + public override PointsToAbstractValue VisitFlowCapture(IFlowCaptureOperation operation, object? argument) + { + var value = base.VisitFlowCapture(operation, argument); + if (IsLValueFlowCapture(operation) && + AnalysisEntityFactory.TryCreate(operation, out var flowCaptureEntity)) + { + value = PointsToAbstractValue.Create(operation.Value); + SetAbstractValue(flowCaptureEntity, value); + } + + return value; + } + + public override PointsToAbstractValue VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation, object? argument) + { + var value = base.VisitFlowCaptureReference(operation, argument); + if (IsLValueFlowCaptureReference(operation) && + AnalysisEntityFactory.TryCreate(operation, out var flowCaptureEntity)) + { + return GetAbstractValue(flowCaptureEntity); + } + + return value; + } + + public override PointsToAbstractValue ComputeValueForCompoundAssignment(ICompoundAssignmentOperation operation, PointsToAbstractValue targetValue, PointsToAbstractValue assignedValue, ITypeSymbol? targetType, ITypeSymbol? assignedValueType) + { + if (targetValue.Kind == PointsToAbstractValueKind.KnownLValueCaptures) + { + // Flow captures can never be re-assigned, so reuse the target value. + return targetValue; + } + + return base.ComputeValueForCompoundAssignment(operation, targetValue, assignedValue, targetType, assignedValueType); + } + + protected override PointsToAbstractValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument) + { + var value = base.VisitAssignmentOperation(operation, argument); + HandlePossibleEscapingForAssignment(operation.Target, operation.Value, operation); + return value; + } + + public override PointsToAbstractValue VisitDeclarationExpression(IDeclarationExpressionOperation operation, object? argument) + { + return Visit(operation.Expression, argument); + } + + public override PointsToAbstractValue VisitCaughtException(ICaughtExceptionOperation operation, object? argument) + { + _ = base.VisitCaughtException(operation, argument); + return PointsToAbstractValue.UnknownNotNull; + } + + #endregion + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.cs new file mode 100644 index 0000000000000..158b519f9e994 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysis.cs @@ -0,0 +1,131 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Diagnostics; + +#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + + /// + /// Dataflow analysis to track locations pointed to by and instances. + /// + public partial class PointsToAnalysis : ForwardDataFlowAnalysis + { + internal static readonly AbstractValueDomain ValueDomainInstance = PointsToAbstractValueDomain.Default; + + private PointsToAnalysis(PointsToAnalysisDomain analysisDomain, PointsToDataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + public static PointsToAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + PointsToAnalysisKind pointsToAnalysisKind, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + bool pessimisticAnalysis = true, + bool performCopyAnalysis = false, + bool exceptionPathsAnalysis = false) + { + return TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind, out _, interproceduralAnalysisConfig, interproceduralAnalysisPredicate, + pessimisticAnalysis, performCopyAnalysis, exceptionPathsAnalysis); + } + + public static PointsToAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + PointsToAnalysisKind pointsToAnalysisKind, + out CopyAnalysisResult? copyAnalysisResult, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate, + bool pessimisticAnalysis = true, + bool performCopyAnalysis = false, + bool exceptionPathsAnalysis = false) + { + if (pointsToAnalysisKind == PointsToAnalysisKind.None) + { + copyAnalysisResult = null; + return null; + } + + copyAnalysisResult = performCopyAnalysis ? + CopyAnalysis.CopyAnalysis.TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, interproceduralAnalysisConfig, + interproceduralAnalysisPredicate, pessimisticAnalysis, pointsToAnalysisKind, exceptionPathsAnalysis) : + null; + + if (cfg == null) + { + Debug.Fail("Expected non-null CFG"); + return null; + } + + var analysisContext = PointsToAnalysisContext.Create(PointsToAbstractValueDomain.Default, wellKnownTypeProvider, cfg, + owningSymbol, analyzerOptions, pointsToAnalysisKind, interproceduralAnalysisConfig, pessimisticAnalysis, exceptionPathsAnalysis, copyAnalysisResult, + TryGetOrComputeResultForAnalysisContext, interproceduralAnalysisPredicate); + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static PointsToAnalysisResult? TryGetOrComputeResultForAnalysisContext(PointsToAnalysisContext analysisContext) + { + using var trackedEntitiesBuilder = new TrackedEntitiesBuilder(analysisContext.PointsToAnalysisKind); + var defaultPointsToValueGenerator = new DefaultPointsToValueGenerator(trackedEntitiesBuilder); + var isDisposable = DisposeAnalysisHelper.GetIsDisposableDelegate(analysisContext.ControlFlowGraph.OriginalOperation.SemanticModel!.Compilation); + var analysisDomain = new PointsToAnalysisDomain(defaultPointsToValueGenerator, isDisposable); + var operationVisitor = new PointsToDataFlowOperationVisitor(trackedEntitiesBuilder, defaultPointsToValueGenerator, analysisDomain, analysisContext); + var pointsToAnalysis = new PointsToAnalysis(analysisDomain, operationVisitor); + return pointsToAnalysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + internal static bool ShouldBeTracked([NotNullWhen(returnValue: true)] ITypeSymbol? typeSymbol, Func isDisposable) + => typeSymbol.CanHoldNullValue() || + isDisposable(typeSymbol); + + internal static bool ShouldBeTracked(AnalysisEntity analysisEntity, PointsToAnalysisKind pointsToAnalysisKind, Func isDisposable) + { + Debug.Assert(pointsToAnalysisKind != PointsToAnalysisKind.None); + + if (!ShouldBeTracked(analysisEntity.Type, isDisposable) && + !analysisEntity.IsLValueFlowCaptureEntity && + !analysisEntity.IsThisOrMeInstance) + { + return false; + } + + return analysisEntity.ShouldBeTrackedForPointsToAnalysis(pointsToAnalysisKind); + } + + [Conditional("DEBUG")] + internal static void AssertValidPointsToAnalysisData(PointsToAnalysisData data) + { + data.AssertValidPointsToAnalysisData(); + } + + protected override PointsToAnalysisResult ToResult(PointsToAnalysisContext analysisContext, DataFlowAnalysisResult dataFlowAnalysisResult) + { + var operationVisitor = (PointsToDataFlowOperationVisitor)OperationVisitor; + return new PointsToAnalysisResult( + dataFlowAnalysisResult, + operationVisitor.GetEscapedLocationsThroughOperationsMap(), + operationVisitor.GetEscapedLocationsThroughReturnValuesMap(), + operationVisitor.GetEscapedLocationsThroughEntitiesMap(), + operationVisitor.TrackedEntitiesBuilder); + } + protected override PointsToBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, PointsToAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisContext.cs new file mode 100644 index 0000000000000..4771e183df56b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisContext.cs @@ -0,0 +1,95 @@ +// 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.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralPointsToAnalysisData = InterproceduralAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + public sealed class PointsToAnalysisContext : AbstractDataFlowAnalysisContext + { + private PointsToAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + PointsToAnalysisKind pointsToAnalysisKind, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + CopyAnalysisResult? copyAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralPointsToAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, pessimisticAnalysis, + predicateAnalysis: true, exceptionPathsAnalysis, copyAnalysisResult, pointsToAnalysisResult: null, valueContentAnalysisResult: null, + tryGetOrComputeAnalysisResult, parentControlFlowGraph, interproceduralAnalysisData, interproceduralAnalysisPredicate) + { + Debug.Assert(pointsToAnalysisKind != PointsToAnalysisKind.None); + + PointsToAnalysisKind = pointsToAnalysisKind; + } + + public PointsToAnalysisKind PointsToAnalysisKind { get; } + + internal static PointsToAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + PointsToAnalysisKind pointsToAnalysisKind, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool exceptionPathsAnalysis, + CopyAnalysisResult? copyAnalysisResult, + Func tryGetOrComputeAnalysisResult, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + { + return new PointsToAnalysisContext(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, pointsToAnalysisKind, interproceduralAnalysisConfig, + pessimisticAnalysis, exceptionPathsAnalysis, copyAnalysisResult, tryGetOrComputeAnalysisResult, parentControlFlowGraph: null, + interproceduralAnalysisData: null, interproceduralAnalysisPredicate); + } + + public override PointsToAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralPointsToAnalysisData? interproceduralAnalysisData) + { + Debug.Assert(pointsToAnalysisResult == null); + Debug.Assert(valueContentAnalysisResult == null); + + return new PointsToAnalysisContext(ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions, PointsToAnalysisKind, InterproceduralAnalysisConfiguration, + PessimisticAnalysis, ExceptionPathsAnalysis, copyAnalysisResult, TryGetOrComputeAnalysisResult, ControlFlowGraph, interproceduralAnalysisData, + InterproceduralAnalysisPredicate); + } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(((int)PointsToAnalysisKind).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (PointsToAnalysisContext)obj; + return ((int)PointsToAnalysisKind).GetHashCode() == ((int)other.PointsToAnalysisKind).GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisData.cs new file mode 100644 index 0000000000000..49296b2f443fb --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisData.cs @@ -0,0 +1,146 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + using CorePointsToAnalysisData = DictionaryAnalysisData; + + /// + /// Aggregated PointsTo analysis data tracked by . + /// Contains the for entity PointsTo values and + /// the predicated values based on true/false runtime values of predicated entities. + /// + public sealed class PointsToAnalysisData : AnalysisEntityBasedPredicateAnalysisData + { + private readonly Func _isDisposable; + + internal PointsToAnalysisData(Func isDisposable) + { + _isDisposable = isDisposable; + } + + internal PointsToAnalysisData(IDictionary fromData, Func isDisposable) + : base(fromData) + { + _isDisposable = isDisposable; + + AssertValidPointsToAnalysisData(fromData, isDisposable); + } + + internal PointsToAnalysisData( + CorePointsToAnalysisData mergedCoreAnalysisData, + PredicatedAnalysisData predicatedData1, + PredicatedAnalysisData predicatedData2, + bool isReachableData, + MapAbstractDomain coreDataAnalysisDomain, + Func isDisposable) + : base(mergedCoreAnalysisData, predicatedData1, predicatedData2, isReachableData, coreDataAnalysisDomain) + { + _isDisposable = isDisposable; + + AssertValidPointsToAnalysisData(mergedCoreAnalysisData, isDisposable); + AssertValidPointsToAnalysisData(); + } + + private PointsToAnalysisData(PointsToAnalysisData fromData) + : base(fromData) + { + _isDisposable = fromData._isDisposable; + + fromData.AssertValidPointsToAnalysisData(); + } + + private PointsToAnalysisData(PointsToAnalysisData data1, PointsToAnalysisData data2, MapAbstractDomain coreDataAnalysisDomain) + : base(data1, data2, coreDataAnalysisDomain) + { + _isDisposable = data1._isDisposable; + + data1.AssertValidPointsToAnalysisData(); + data2.AssertValidPointsToAnalysisData(); + AssertValidPointsToAnalysisData(); + } + + protected override AbstractValueDomain ValueDomain => PointsToAnalysis.ValueDomainInstance; + public override AnalysisEntityBasedPredicateAnalysisData Clone() => new PointsToAnalysisData(this); + + public override int Compare(AnalysisEntityBasedPredicateAnalysisData other, MapAbstractDomain coreDataAnalysisDomain) + => BaseCompareHelper(other, coreDataAnalysisDomain); + + public override AnalysisEntityBasedPredicateAnalysisData WithMergedData(AnalysisEntityBasedPredicateAnalysisData data, MapAbstractDomain coreDataAnalysisDomain) + { + Debug.Assert(IsReachableBlockData || !data.IsReachableBlockData); + var mergedData = new PointsToAnalysisData(this, (PointsToAnalysisData)data, coreDataAnalysisDomain); + mergedData.AssertValidPointsToAnalysisData(); + return mergedData; + } + + public override void SetAbstractValue(AnalysisEntity key, PointsToAbstractValue value) + { + AssertValidPointsToAnalysisKeyValuePair(key, value, _isDisposable); + base.SetAbstractValue(key, value); + } + + public override void Reset(Func getResetValue) + { + base.Reset(getResetValue); + AssertValidPointsToAnalysisData(); + } + + [Conditional("DEBUG")] + internal void AssertNoFlowCaptureEntitiesTracked() + { + AssertNoFlowCaptureEntitiesTracked(CoreAnalysisData); +#pragma warning disable IDE0200 // Remove unnecessary lambda expression - https://github.com/dotnet/roslyn/issues/63464 + AssertValidPredicatedAnalysisData(map => AssertNoFlowCaptureEntitiesTracked(map)); +#pragma warning restore IDE0200 // Remove unnecessary lambda expression + } + + [Conditional("DEBUG")] + private static void AssertNoFlowCaptureEntitiesTracked(CorePointsToAnalysisData map) + { + foreach (var key in map.Keys) + { + Debug.Assert(key.CaptureId == null); + } + } + + [Conditional("DEBUG")] + internal void AssertValidPointsToAnalysisData() + { + AssertValidPointsToAnalysisData(CoreAnalysisData, _isDisposable); +#pragma warning disable IDE0200 // Remove unnecessary lambda expression - https://github.com/dotnet/roslyn/issues/63464 + AssertValidPredicatedAnalysisData(map => AssertValidPointsToAnalysisData(map, _isDisposable)); +#pragma warning restore IDE0200 // Remove unnecessary lambda expression + } + + [Conditional("DEBUG")] + internal static void AssertValidPointsToAnalysisData(IDictionary map, Func isDisposable) + { + if (map is CorePointsToAnalysisData corePointsToAnalysisData) + { + Debug.Assert(!corePointsToAnalysisData.IsDisposed); + } + + foreach (var kvp in map) + { + AssertValidPointsToAnalysisKeyValuePair(kvp.Key, kvp.Value, isDisposable); + } + } + + [Conditional("DEBUG")] + internal static void AssertValidPointsToAnalysisKeyValuePair( + AnalysisEntity key, + PointsToAbstractValue value, + Func isDisposable) + { + Debug.Assert(value.Kind != PointsToAbstractValueKind.Undefined); + Debug.Assert(!key.IsLValueFlowCaptureEntity || value.Kind == PointsToAbstractValueKind.KnownLValueCaptures); + Debug.Assert(PointsToAnalysis.ShouldBeTracked(key, PointsToAnalysisKind.Complete, isDisposable)); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisKind.cs new file mode 100644 index 0000000000000..d533fe316827d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisKind.cs @@ -0,0 +1,29 @@ +// 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.FlowAnalysis.DataFlow.PointsToAnalysis +{ + public enum PointsToAnalysisKind + { + // NOTE: Below fields names are used in the .editorconfig specification + // for PointsToAnalysisKind option. Hence the names should *not* be modified, + // as that would be a breaking change for .editorconfig specification. + + /// + /// Analysis is disabled. + /// + None, + + /// + /// Partial analysis that tracks for + /// except fields and properties. + /// + PartialWithoutTrackingFieldsAndProperties, + + /// + /// Complete analysis that tracks for all . + /// + Complete, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisResult.cs new file mode 100644 index 0000000000000..1dc0ab0bec1dd --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToAnalysisResult.cs @@ -0,0 +1,63 @@ +// 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.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Analysis result from execution of on a control flow graph. + /// + public sealed class PointsToAnalysisResult : DataFlowAnalysisResult + { + private readonly ImmutableDictionary> _escapedLocationsThroughOperationsMap; + private readonly ImmutableDictionary> _escapedLocationsThroughReturnValuesMap; + private readonly ImmutableDictionary> _escapedLocationsThroughEntitiesMap; + private readonly ImmutableHashSet _trackedEntities; + private readonly ImmutableHashSet _trackedPointsToValues; + + internal PointsToAnalysisResult( + DataFlowAnalysisResult corePointsToAnalysisResult, + ImmutableDictionary> escapedLocationsThroughOperationsMap, + ImmutableDictionary> escapedLocationsThroughReturnValuesMap, + ImmutableDictionary> escapedLocationsThroughEntitiesMap, + TrackedEntitiesBuilder trackedEntitiesBuilder) + : base(corePointsToAnalysisResult) + { + _escapedLocationsThroughOperationsMap = escapedLocationsThroughOperationsMap; + _escapedLocationsThroughReturnValuesMap = escapedLocationsThroughReturnValuesMap; + _escapedLocationsThroughEntitiesMap = escapedLocationsThroughEntitiesMap; + (_trackedEntities, _trackedPointsToValues) = trackedEntitiesBuilder.ToImmutable(); + PointsToAnalysisKind = trackedEntitiesBuilder.PointsToAnalysisKind; + } + + public PointsToAnalysisKind PointsToAnalysisKind { get; } + + public ImmutableHashSet GetEscapedAbstractLocations(IOperation operation) + => GetEscapedAbstractLocations(operation, _escapedLocationsThroughOperationsMap) + .AddRange(GetEscapedAbstractLocations(operation, _escapedLocationsThroughReturnValuesMap)); + + public ImmutableHashSet GetEscapedAbstractLocations(AnalysisEntity analysisEntity) + => GetEscapedAbstractLocations(analysisEntity, _escapedLocationsThroughEntitiesMap); + + private static ImmutableHashSet GetEscapedAbstractLocations( + TKey key, + ImmutableDictionary> map) + where TKey : class + { + if (map.TryGetValue(key, out var escapedLocations)) + { + return escapedLocations; + } + + return ImmutableHashSet.Empty; + } + + internal bool IsTrackedEntity(AnalysisEntity analysisEntity) + => _trackedEntities.Contains(analysisEntity); + + internal bool IsTrackedPointsToValue(PointsToAbstractValue value) + => _trackedPointsToValues.Contains(value); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToBlockAnalysisResult.cs new file mode 100644 index 0000000000000..2ec621dd87255 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/PointsToBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Result from execution of on a basic block. + /// It stores the PointsTo value for each at the start and end of the basic block. + /// + public class PointsToBlockAnalysisResult : AbstractBlockAnalysisResult + { + internal PointsToBlockAnalysisResult(BasicBlock basicBlock, PointsToAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.CoreAnalysisData.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + IsReachable = blockAnalysisData?.IsReachableBlockData ?? true; + } + + public ImmutableDictionary Data { get; } + public bool IsReachable { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/TrackedEntitiesBuilder.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/TrackedEntitiesBuilder.cs new file mode 100644 index 0000000000000..8b19626011043 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PointsToAnalysis/TrackedEntitiesBuilder.cs @@ -0,0 +1,68 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis +{ + /// + /// Stores all the s and s that got tracked during points to analysis + /// + internal sealed class TrackedEntitiesBuilder : IDisposable + { + /// + /// Stores all the tracked entities. + /// NOTE: Entities added to this set should not be removed. + /// + private PooledHashSet AllEntities { get; } + + /// + /// Stores all the tracked that some entity from + /// points to during points to analysis. + /// NOTE: Values added to this set should not be removed. + /// + private PooledHashSet PointsToValues { get; } + + public TrackedEntitiesBuilder(PointsToAnalysisKind pointsToAnalysisKind) + { + Debug.Assert(pointsToAnalysisKind != PointsToAnalysisKind.None); + + PointsToAnalysisKind = pointsToAnalysisKind; + AllEntities = PooledHashSet.GetInstance(); + PointsToValues = PooledHashSet.GetInstance(); + } + + public PointsToAnalysisKind PointsToAnalysisKind { get; } + + public void Dispose() + { + AllEntities.Dispose(); + PointsToValues.Dispose(); + } + + public void AddEntityAndPointsToValue(AnalysisEntity analysisEntity, PointsToAbstractValue value) + { + Debug.Assert(analysisEntity.ShouldBeTrackedForPointsToAnalysis(PointsToAnalysisKind)); + + AllEntities.Add(analysisEntity); + AddTrackedPointsToValue(value); + } + + public void AddTrackedPointsToValue(PointsToAbstractValue value) + => PointsToValues.Add(value); + + public IEnumerable EnumerateEntities() + => AllEntities; + + public bool IsTrackedPointsToValue(PointsToAbstractValue value) + => PointsToValues.Contains(value); + + public (ImmutableHashSet, ImmutableHashSet) ToImmutable() + => (AllEntities.ToImmutable(), PointsToValues.ToImmutable()); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/ConstructorMapper.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/ConstructorMapper.cs new file mode 100644 index 0000000000000..05d26e15f072e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/ConstructorMapper.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; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Maps a constructor invocation to s for the properties being tracked by PropertySetAnalysis. + /// +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class ConstructorMapper +#pragma warning restore CA1812 + { + /// + /// Mapping a constructor's arguments to a . + /// + /// Invoked constructor. + /// s for the constructor's arguments. + /// s for the constructor's arguments. + /// Abstract value for PropertySetAnalysis, with s in the same order as the s in the . + public delegate PropertySetAbstractValue ValueContentAbstractValueCallback( + IMethodSymbol constructorMethodSymbol, + IReadOnlyList argumentValueContentAbstractValues, + IReadOnlyList argumentPointsToAbstractValues); + + /// + /// Mapping a constructor's arguments to a . + /// + /// Invoked constructor. + /// s for the constructor's arguments. + /// Abstract value for PropertySetAnalysis, with s in the same order as the s in the . + public delegate PropertySetAbstractValue PointsToAbstractValueCallback( + IMethodSymbol constructorMethodSymbol, + IReadOnlyList argumentPointsToAbstractValues); + + /// + /// Initializes a using constant s whenever the type being tracked by PropertySetAnalysis is instantiated. + /// + /// Constant s, in the same order that the corresponding was initialized with. + public ConstructorMapper(ImmutableArray propertyAbstractValues) + { + this.PropertyAbstractValues = propertyAbstractValues; + } + + /// + /// Initializes a that maps a constructor invocation's arguments' s to s for the properties being tracked by PropertySetAnalysis. + /// + /// Callback that implements the mapping. + public ConstructorMapper(ValueContentAbstractValueCallback mapFromValueContentAbstractValueCallback) + { + this.MapFromValueContentAbstractValue = mapFromValueContentAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromValueContentAbstractValueCallback)); + this.PropertyAbstractValues = ImmutableArray.Empty; + } + + /// + /// Initializes a that maps a constructor invocation's arguments' s to s for the properties being tracked by PropertySetAnalysis. + /// + /// Callback that implements the mapping. + public ConstructorMapper(PointsToAbstractValueCallback mapFromPointsToAbstractValueCallback) + { + this.MapFromPointsToAbstractValue = mapFromPointsToAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromPointsToAbstractValueCallback)); + this.PropertyAbstractValues = ImmutableArray.Empty; + } + + /// + /// Doesn't construct. + /// + private ConstructorMapper() + { + } + + internal ValueContentAbstractValueCallback? MapFromValueContentAbstractValue { get; } + + internal PointsToAbstractValueCallback? MapFromPointsToAbstractValue { get; } + + internal ImmutableArray PropertyAbstractValues { get; } + + /// + /// Indicates that this uses s. + /// + internal bool RequiresValueContentAnalysis => this.MapFromValueContentAbstractValue != null; + + internal void Validate(int propertyCount) + { + if (!this.PropertyAbstractValues.IsEmpty) + { + if (this.PropertyAbstractValues.Length != propertyCount) + { + throw new ArgumentException($"ConstructorMapper PropertyAbstractValues has invalid length (expected {propertyCount}, actual length {this.PropertyAbstractValues.Length})"); + } + } + } + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(PropertyAbstractValues, ref hashCode); + hashCode.Add(MapFromValueContentAbstractValue.GetHashCodeOrDefault()); + hashCode.Add(MapFromPointsToAbstractValue.GetHashCodeOrDefault()); + return hashCode.ToHashCode(); + } + + public override bool Equals(object obj) + { + return this.Equals(obj as ConstructorMapper); + } + + public bool Equals(ConstructorMapper? other) + { + return other != null + && this.MapFromValueContentAbstractValue == other.MapFromValueContentAbstractValue + && this.MapFromPointsToAbstractValue == other.MapFromPointsToAbstractValue + && this.PropertyAbstractValues == other.PropertyAbstractValues; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluationResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluationResult.cs new file mode 100644 index 0000000000000..23379ecba34ff --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluationResult.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. + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Result of evaluating potentially hazardous usage. + /// + public enum HazardousUsageEvaluationResult + { + /// + /// The usage is not hazardous. + /// + Unflagged, + + /// + /// The usage might be hazardous. + /// + MaybeFlagged, + + /// + /// The usage is definitely hazardous. + /// + Flagged, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluator.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluator.cs new file mode 100644 index 0000000000000..643d44c348ea8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluator.cs @@ -0,0 +1,144 @@ +// 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 Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Determines if usage of a is hazardous or not. + /// +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class HazardousUsageEvaluator +#pragma warning restore CA1812 + { + /// + /// Evaluates if the method invocation with a given is hazardous or not. + /// + /// Invoked method. + /// Abstract value of the type being tracked by PropertySetAnalysis. + /// Evaluation result of whether the usage is hazardous. + public delegate HazardousUsageEvaluationResult InvocationEvaluationCallback(IMethodSymbol methodSymbol, PropertySetAbstractValue propertySetAbstractValue); + + /// + /// Evaluates if a given is hazardous or not. + /// + /// Abstract value of the type being tracked by PropertySetAnalysis. + /// Evaluation result of whether the usage is hazardous. + public delegate HazardousUsageEvaluationResult EvaluationCallback(PropertySetAbstractValue propertySetAbstractValue); + + /// + /// Initializes a that evaluates a method invocation on the type being tracked by PropertySetAnalysis. + /// + /// Name of the method within the tracked type. + /// Evaluation callback. + /// Whether to consider derived class. + public HazardousUsageEvaluator(string trackedTypeMethodName, InvocationEvaluationCallback evaluator, bool derivedClass = false) + { + MethodName = trackedTypeMethodName ?? throw new ArgumentNullException(nameof(trackedTypeMethodName)); + InvocationEvaluator = evaluator ?? throw new ArgumentNullException(nameof(evaluator)); + Kind = HazardousUsageEvaluatorKind.Invocation; + DerivedClass = derivedClass; + } + + /// + /// Initializes a that evaluates a method invocation with an argument of the type being tracked by PropertySetAnalysis. + /// + /// Name of the instance that the method is invoked on. + /// Name of the method within . + /// Name of the method parameter containing the type being tracked by PropertySetAnalysis. + /// Evaluation callback. + /// Whether to consider derived class. + public HazardousUsageEvaluator(string containingType, string methodName, string parameterNameOfPropertySetObject, InvocationEvaluationCallback evaluator, bool derivedClass = false) + { + ContainingTypeName = containingType ?? throw new ArgumentNullException(nameof(containingType)); + MethodName = methodName ?? throw new ArgumentNullException(nameof(methodName)); + ParameterNameOfPropertySetObject = parameterNameOfPropertySetObject ?? throw new ArgumentNullException(nameof(parameterNameOfPropertySetObject)); + InvocationEvaluator = evaluator ?? throw new ArgumentNullException(nameof(evaluator)); + DerivedClass = derivedClass; + Kind = HazardousUsageEvaluatorKind.Invocation; + } + + /// + /// Initializes a that evaluates a return statement with a return value of the tracked type. + /// + /// Evaluation callback. + /// Whether to consider derived class. + public HazardousUsageEvaluator(HazardousUsageEvaluatorKind kind, EvaluationCallback evaluator, bool derivedClass = false) + { + if (kind is not HazardousUsageEvaluatorKind.Return and not HazardousUsageEvaluatorKind.Initialization and not HazardousUsageEvaluatorKind.Argument) + { + throw new ArgumentException( + "kind must be Return or Initialization or Argument. Use other constructors for Invocation.", + nameof(kind)); + } + + Kind = kind; + DerivedClass = derivedClass; + ValueEvaluator = evaluator ?? throw new ArgumentNullException(nameof(evaluator)); + } + + private HazardousUsageEvaluator() + { + } + + public HazardousUsageEvaluatorKind Kind { get; } + + /// + /// Name of the type containing the method, or null if method is part of the type being tracked by PropertySetAnalysis or this is for a return statement. + /// + public string? ContainingTypeName { get; } + + /// + /// Name of the method being invoked, or null if this is for a return statement. + /// + public string? MethodName { get; } + + /// + /// Name of the parameter containing the object containing the type being tracked by PropertySetAnalysis, or null if the method is part of the type being tracked by PropertySetAnalysis. + /// + public string? ParameterNameOfPropertySetObject { get; } + + /// + /// Evaluates if the method invocation with a given is hazardous or not. + /// + public InvocationEvaluationCallback? InvocationEvaluator { get; } + + /// + /// Determines whether consider the derived classes of the containing type. + /// + public bool DerivedClass { get; } + + /// + /// Evaluates if the return statement or initialization value with a given is hazardous or not. + /// + public EvaluationCallback? ValueEvaluator { get; } + + public override int GetHashCode() + { + return RoslynHashCode.Combine( + this.ContainingTypeName.GetHashCodeOrDefault(), + this.MethodName.GetHashCodeOrDefault(), + this.ParameterNameOfPropertySetObject.GetHashCodeOrDefault(), + this.DerivedClass.GetHashCode(), + this.InvocationEvaluator.GetHashCodeOrDefault()); + } + + public override bool Equals(object obj) + { + return this.Equals(obj as HazardousUsageEvaluator); + } + + public bool Equals(HazardousUsageEvaluator? other) + { + return other != null + && this.ContainingTypeName == other.ContainingTypeName + && this.MethodName == other.MethodName + && this.ParameterNameOfPropertySetObject == other.ParameterNameOfPropertySetObject + && this.DerivedClass == other.DerivedClass + && this.InvocationEvaluator == other.InvocationEvaluator; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorCollection.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorCollection.cs new file mode 100644 index 0000000000000..03e7d23f959b8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorCollection.cs @@ -0,0 +1,124 @@ +// 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.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Collection of s. + /// +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class HazardousUsageEvaluatorCollection +#pragma warning restore CA1812 + { + public HazardousUsageEvaluatorCollection(IEnumerable hazardousUsageEvaluators) + { + if (hazardousUsageEvaluators == null) + { + throw new ArgumentNullException(nameof(hazardousUsageEvaluators)); + } + + if (!hazardousUsageEvaluators.Any()) + { + throw new ArgumentException("No HazardUsageEvaluators specified", nameof(hazardousUsageEvaluators)); + } + + this.HazardousUsageEvaluators = + hazardousUsageEvaluators.ToImmutableDictionary( + h => (h.Kind, h.ContainingTypeName, h.MethodName, h.ParameterNameOfPropertySetObject, h.DerivedClass)); + } + + public HazardousUsageEvaluatorCollection(params HazardousUsageEvaluator[] hazardousUsageEvaluators) + : this((IEnumerable)hazardousUsageEvaluators) + { + } + + private HazardousUsageEvaluatorCollection() + { + throw new NotSupportedException(); + } + + private ImmutableDictionary<(HazardousUsageEvaluatorKind Kind, string? InstanceTypeName, string? MethodName, string? ParameterName, bool DerivedClasses), HazardousUsageEvaluator> HazardousUsageEvaluators { get; } + + internal bool TryGetHazardousUsageEvaluator(string trackedTypeMethodName, out HazardousUsageEvaluator? hazardousUsageEvaluator, bool derivedClasses = false) + { + return this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Invocation, null, trackedTypeMethodName, null, derivedClasses), + out hazardousUsageEvaluator); + } + + internal bool TryGetHazardousUsageEvaluator( + string containingType, + string methodName, + string parameterName, + [NotNullWhen(returnValue: true)] out HazardousUsageEvaluator? hazardousUsageEvaluator) + { + if (this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Invocation, containingType, methodName, parameterName, false), + out hazardousUsageEvaluator) + || this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Invocation, containingType, methodName, parameterName, true), + out hazardousUsageEvaluator)) + { + return true; + } + + return false; + } + + internal bool TryGetReturnHazardousUsageEvaluator( + [NotNullWhen(returnValue: true)] out HazardousUsageEvaluator? hazardousUsageEvaluator, + bool derivedClass = false) + { + return this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Return, null, null, null, derivedClass), + out hazardousUsageEvaluator); + } + + internal bool TryGetInitializationHazardousUsageEvaluator( + [NotNullWhen(returnValue: true)] out HazardousUsageEvaluator? hazardousUsageEvaluator, + bool derivedClass = false) + { + return this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Initialization, null, null, null, derivedClass), + out hazardousUsageEvaluator); + } + + internal bool TryGetArgumentHazardousUsageEvaluator( + [NotNullWhen(returnValue: true)] out HazardousUsageEvaluator? hazardousUsageEvaluator, + bool derivedClass = false) + { + return this.HazardousUsageEvaluators.TryGetValue( + (HazardousUsageEvaluatorKind.Argument, null, null, null, derivedClass), + out hazardousUsageEvaluator); + } + + internal ImmutableDictionary<(INamedTypeSymbol, bool), string> GetTypeToNameMapping(WellKnownTypeProvider wellKnownTypeProvider) + { + using PooledDictionary<(INamedTypeSymbol, bool), string> pooledDictionary = PooledDictionary<(INamedTypeSymbol, bool), string>.GetInstance(); + foreach (KeyValuePair<(HazardousUsageEvaluatorKind Kind, string? InstanceTypeName, string? MethodName, string? ParameterName, bool derivedClasses), HazardousUsageEvaluator> kvp + in this.HazardousUsageEvaluators) + { + if (kvp.Key.InstanceTypeName == null || kvp.Key.Kind != HazardousUsageEvaluatorKind.Invocation) + { + continue; + } + + if (wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(kvp.Key.InstanceTypeName, out INamedTypeSymbol? namedTypeSymbol)) + { + pooledDictionary[(namedTypeSymbol, kvp.Key.derivedClasses)] = kvp.Key.InstanceTypeName; + } + } + + return pooledDictionary.ToImmutableDictionary(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorKind.cs new file mode 100644 index 0000000000000..92d2e3548f8d5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/HazardousUsageEvaluatorKind.cs @@ -0,0 +1,32 @@ +// 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 Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Distinguishes kinds of s. + /// + internal enum HazardousUsageEvaluatorKind + { + /// + /// Evaluated at a method invocation. + /// + Invocation, + + /// + /// Evaluated at a return value. + /// + Return, + + /// + /// Evaluated at field and property initialization, or field or property assignments. + /// + Initialization, + + /// + /// Evaluated at argument passing. + /// + Argument, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapper.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapper.cs new file mode 100644 index 0000000000000..fc79ed56a70a6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapper.cs @@ -0,0 +1,141 @@ +// 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 Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Information for mapping an object's property's assigned value to a . + /// +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class PropertyMapper +#pragma warning restore CA1812 + { + /// + /// Mapping from to a + /// + /// Property's assigned value's . + /// What the property's assigned value should map to. + public delegate PropertySetAbstractValueKind ValueContentAbstractValueCallback(ValueContentAbstractValue valueContentAbstractValue); + + /// + /// Mapping from to a + /// + /// Property's assigned value's . + /// What the property's assigned value should map to. + public delegate PropertySetAbstractValueKind PointsToAbstractValueCallback(PointsToAbstractValue pointsToAbstractValue); + + /// + /// Initializes a that maps a property's assigned value's to a . + /// + /// Name of the property. + /// Callback that implements the mapping. + public PropertyMapper(string propertyName, ValueContentAbstractValueCallback mapFromValueContentAbstractValueCallback) + { + PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + MapFromValueContentAbstractValue = mapFromValueContentAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromValueContentAbstractValueCallback)); + } + + /// + /// Initializes a that maps a property's assigned value's to a . + /// + /// Name of the property. + /// Callback that implements the mapping. + /// Internal index into the array. + /// This overload is useful if there are properties that effectively aliases of the same underlying value. + public PropertyMapper(string propertyName, ValueContentAbstractValueCallback mapFromValueContentAbstractValueCallback, int propertyIndex) + { + PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + MapFromValueContentAbstractValue = mapFromValueContentAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromValueContentAbstractValueCallback)); + PropertyIndex = propertyIndex; + if (propertyIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(propertyIndex), "propertyIndex must be non-negative"); + } + } + + /// + /// Initializes a that maps a property's assigned value's to a . + /// + /// Name of the property. + /// Callback that implements the mapping. + public PropertyMapper(string propertyName, PointsToAbstractValueCallback mapFromPointsToAbstractValueCallback) + { + PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + MapFromPointsToAbstractValue = mapFromPointsToAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromPointsToAbstractValueCallback)); + } + + /// + /// Initializes a that maps a property's assigned value's to a . + /// + /// Name of the property. + /// Callback that implements the mapping. + /// Internal index into the array. + /// This overload is useful if there are properties that effectively aliases of the same underlying value. + public PropertyMapper(string propertyName, PointsToAbstractValueCallback mapFromPointsToAbstractValueCallback, int propertyIndex) + { + PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); + MapFromPointsToAbstractValue = mapFromPointsToAbstractValueCallback ?? throw new ArgumentNullException(nameof(mapFromPointsToAbstractValueCallback)); + PropertyIndex = propertyIndex; + if (propertyIndex < 0) + { + throw new ArgumentOutOfRangeException(nameof(propertyIndex), "propertyIndex must be non-negative"); + } + } + + /// + /// Doesn't construct. + /// + private PropertyMapper() + { + throw new NotSupportedException(); + } + + /// + /// Name of the property. + /// + internal string PropertyName { get; } + + internal int PropertyIndex { get; } = -1; + + /// + /// Callback for mapping from to a , or null. + /// + internal ValueContentAbstractValueCallback? MapFromValueContentAbstractValue { get; } + + /// + /// Callback for mapping from to a , or null. + /// + internal PointsToAbstractValueCallback? MapFromPointsToAbstractValue { get; } + + /// + /// Indicates that this uses s. + /// + internal bool RequiresValueContentAnalysis => this.MapFromValueContentAbstractValue != null; + + public override int GetHashCode() + { + return RoslynHashCode.Combine( + this.PropertyName.GetHashCodeOrDefault(), + this.MapFromValueContentAbstractValue.GetHashCodeOrDefault(), + this.MapFromPointsToAbstractValue.GetHashCodeOrDefault()); + } + + public override bool Equals(object obj) + { + return this.Equals(obj as PropertyMapper); + } + + public bool Equals(PropertyMapper? other) + { + return other != null + && this.PropertyName == other.PropertyName + && this.MapFromValueContentAbstractValue == other.MapFromValueContentAbstractValue + && this.MapFromPointsToAbstractValue == other.MapFromPointsToAbstractValue; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapperCollection.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapperCollection.cs new file mode 100644 index 0000000000000..c2de16ce32e7e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertyMapperCollection.cs @@ -0,0 +1,105 @@ +// 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.Linq; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ +#pragma warning disable CA1812 // Is too instantiated. + internal sealed class PropertyMapperCollection +#pragma warning restore CA1812 + { + public PropertyMapperCollection(IEnumerable propertyMappers) + { + if (propertyMappers == null) + { + throw new ArgumentNullException(nameof(propertyMappers)); + } + + if (!propertyMappers.Any()) + { + throw new ArgumentException("No PropertyMappers specified", nameof(propertyMappers)); + } + + if (propertyMappers.Any(p => p.PropertyIndex >= 0)) + { + if (!propertyMappers.All(p => p.PropertyIndex >= 0)) + { + throw new ArgumentException( + "Either all PropertyMappers must specify a property index, or no PropertyMappers specify a property index", + nameof(propertyMappers)); + } + + int expected = 0; + foreach (int pi in propertyMappers.Select(p => p.PropertyIndex).Distinct().OrderBy(propertyIndex => propertyIndex)) + { + if (pi != expected) + { + throw new ArgumentException( + "PropertyIndex values aren't contiguous starting from 0", + nameof(propertyMappers)); + } + + expected++; + } + + this.PropertyValuesCount = expected; + } + else + { + this.PropertyValuesCount = propertyMappers.Count(); + } + + ImmutableDictionary.Builder builder = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + int index = 0; + foreach (PropertyMapper p in propertyMappers) + { + int indexToAdd = p.PropertyIndex >= 0 ? p.PropertyIndex : index++; + builder.Add(p.PropertyName, (indexToAdd, p)); + } + + this.PropertyMappersWithIndex = builder.ToImmutable(); + this.RequiresValueContentAnalysis = this.PropertyMappersWithIndex.Values.Any(t => t.PropertyMapper.RequiresValueContentAnalysis); + } + + public PropertyMapperCollection(params PropertyMapper[] propertyMappers) + : this((IEnumerable)propertyMappers) + { + } + + private PropertyMapperCollection() + { + throw new NotSupportedException(); + } + + private ImmutableDictionary PropertyMappersWithIndex { get; } + + internal bool RequiresValueContentAnalysis { get; } + + internal int PropertyValuesCount { get; } + + internal bool TryGetPropertyMapper( + string propertyName, + [NotNullWhen(returnValue: true)] out PropertyMapper? propertyMapper, + out int index) + { + if (this.PropertyMappersWithIndex.TryGetValue(propertyName, out (int Index, PropertyMapper PropertyMapper) tuple)) + { + propertyMapper = tuple.PropertyMapper; + index = tuple.Index; + return true; + } + else + { + propertyMapper = null; + index = -1; + return false; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.ValuePool.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.ValuePool.cs new file mode 100644 index 0000000000000..2c2ad9118fd46 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.ValuePool.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + internal partial class PropertySetAbstractValue + { +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional - the arrays will be completely filled. + private class ValuePool + { + /// + /// Index is the . + /// + private readonly PropertySetAbstractValue[] OneDimensionalPool; + + /// + /// Indices are the two s. + /// + private readonly PropertySetAbstractValue[,] TwoDimensionalPool; + + public ValuePool() + { + int[] values = Enum.GetValues(typeof(PropertySetAbstractValueKind)).Cast().ToArray(); + Array.Sort(values); + for (int i = 0; i < values.Length; i++) + { + Debug.Assert(values[i] == i, $"{nameof(PropertySetAbstractValueKind)} isn't a contiguous enum starting at 0"); + } + + this.OneDimensionalPool = new PropertySetAbstractValue[values.Length]; + foreach (int i in values) + { + if (i == (int)PropertySetAbstractValueKind.Unknown) + { + this.OneDimensionalPool[i] = PropertySetAbstractValue.Unknown; + } + else + { + this.OneDimensionalPool[i] = new PropertySetAbstractValue( + ImmutableArray.Create((PropertySetAbstractValueKind)i)); + } + } + + this.TwoDimensionalPool = new PropertySetAbstractValue[values.Length, values.Length]; + foreach (int i in values) + { + foreach (int j in values) + { + if (j == (int)PropertySetAbstractValueKind.Unknown) + { + // Because PropertySetAbstractValueKinds beyond the KnownPropertyAbstractValues array are + // implicitly Unknown, and the second kind (j) is Unknown, we can just reuse the one-dimensional + // pool's instances. + this.TwoDimensionalPool[i, j] = OneDimensionalPool[i]; + } + else + { + this.TwoDimensionalPool[i, j] = new PropertySetAbstractValue( + ImmutableArray.Create( + (PropertySetAbstractValueKind)i, + (PropertySetAbstractValueKind)j)); + } + } + } + } + + public PropertySetAbstractValue GetInstance(PropertySetAbstractValueKind v1) + { + return this.OneDimensionalPool[(int)v1]; + } + + public PropertySetAbstractValue GetInstance(PropertySetAbstractValueKind v1, PropertySetAbstractValueKind v2) + { + return this.TwoDimensionalPool[(int)v1, (int)v2]; + } + } +#pragma warning restore CA1814 // Prefer jagged arrays over multidimensional + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.cs new file mode 100644 index 0000000000000..41e0d43949280 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValue.cs @@ -0,0 +1,178 @@ +// 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 System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Abstract value for a set properties on an object instance, with each + /// individual property represented by a . + /// + /// + /// Note that may be + /// "incomplete", i.e. it doesn't cover all properties. In such cases, + /// missing elements are implicitly . + /// + /// The reason for the "sparse" array is so that the Unknown value doesn't + /// have to be aware of how many properties are being tracked. + /// + internal partial class PropertySetAbstractValue + { + public static readonly PropertySetAbstractValue Unknown = new(); + + private static readonly ValuePool Pool = new(); + + public static PropertySetAbstractValue GetInstance(PropertySetAbstractValueKind v1) + { + return Pool.GetInstance(v1); + } + + public static PropertySetAbstractValue GetInstance(PropertySetAbstractValueKind v1, PropertySetAbstractValueKind v2) + { + return Pool.GetInstance(v1, v2); + } + + public static PropertySetAbstractValue GetInstance(ArrayBuilder propertyAbstractValues) + { + if (TryGetPooledInstance(propertyAbstractValues, out PropertySetAbstractValue? instance)) + { + return instance; + } + else + { + return new PropertySetAbstractValue(propertyAbstractValues.ToImmutable()); + } + } + + public static PropertySetAbstractValue GetInstance(ImmutableArray propertyAbstractValues) + { + if (TryGetPooledInstance(propertyAbstractValues, out PropertySetAbstractValue? instance)) + { + return instance; + } + else + { + return new PropertySetAbstractValue(propertyAbstractValues); + } + } + + private static bool TryGetPooledInstance(IReadOnlyList values, [NotNullWhen(returnValue: true)] out PropertySetAbstractValue? instance) + { + if (values.Count == 0) + { + instance = Unknown; + return true; + } + else if (values.Count == 1) + { + instance = Pool.GetInstance(values[0]); + return true; + } + else if (values.Count == 2) + { + instance = Pool.GetInstance(values[0], values[1]); + return true; + } + else + { + for (int i = 2; i < values.Count; i++) + { + if (values[i] != PropertySetAbstractValueKind.Unknown) + { + instance = null; + return false; + } + } + + instance = Pool.GetInstance(values[0], values[1]); + return true; + } + } + + private PropertySetAbstractValue(ImmutableArray propertyAbstractValues) + { + this.KnownPropertyAbstractValues = propertyAbstractValues; + } + + private PropertySetAbstractValue() + { + this.KnownPropertyAbstractValues = ImmutableArray.Empty; + } + + /// + /// Individual values of the set of properties being tracked. + /// + /// + /// Order of the array is the same as the provided s. + /// + private ImmutableArray KnownPropertyAbstractValues { get; } + + /// + /// Count of how many properties' abstract values are tracked by this instance. + /// + public int KnownValuesCount => this.KnownPropertyAbstractValues.Length; + + /// + /// Gets an individual property's . + /// + /// Index of the property, from the corresponding + /// 's initialization. + /// The property's . + /// If accessing an index greater than or equal to KnownValuesCount, the property's + /// abstract value is implicitly . + public PropertySetAbstractValueKind this[int index] + { + get + { + if (index < 0) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + + if (index >= this.KnownValuesCount) + { + return PropertySetAbstractValueKind.Unknown; + } + else + { + return this.KnownPropertyAbstractValues[index]; + } + } + } + + internal PropertySetAbstractValue ReplaceAt(int index, PropertySetAbstractValueKind kind) + { + Debug.Assert(index >= 0); + + int newLength; + if (index >= this.KnownPropertyAbstractValues.Length) + { + newLength = index + 1; + } + else + { + newLength = this.KnownPropertyAbstractValues.Length; + } + + using ArrayBuilder kinds = ArrayBuilder.GetInstance(newLength); + kinds.AddRange(this.KnownPropertyAbstractValues); + + while (kinds.Count < newLength) + { + kinds.Add(PropertySetAbstractValueKind.Unknown); + } + + kinds[index] = kind; + return GetInstance(kinds); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValueKind.cs new file mode 100644 index 0000000000000..cd1feb9f05f02 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAbstractValueKind.cs @@ -0,0 +1,29 @@ +// 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 Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + internal enum PropertySetAbstractValueKind + { + /// + /// Doesn't matter. + /// + Unknown, + + /// + /// Not flagged for badness. + /// + Unflagged, + + /// + /// Flagged for badness. + /// + Flagged, + + /// + /// Maybe flagged. + /// + MaybeFlagged, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetAbstractValueDomain.cs new file mode 100644 index 0000000000000..c75646da999d6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetAbstractValueDomain.cs @@ -0,0 +1,85 @@ +// 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 Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + internal partial class PropertySetAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private class PropertySetAbstractValueDomain : AbstractValueDomain + { + public static PropertySetAbstractValueDomain Default = new(); + + private PropertySetAbstractValueDomain() { } + + public override PropertySetAbstractValue Bottom => PropertySetAbstractValue.Unknown; + + public override PropertySetAbstractValue UnknownOrMayBeValue => PropertySetAbstractValue.Unknown; + + public override int Compare(PropertySetAbstractValue oldValue, PropertySetAbstractValue newValue, bool assertMonotonicity) + { + if (Object.ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + // The PropertySetAbstractValue indexer allows accessing beyond KnownValuesCount (returns Unknown), + // so looping through the max of the two KnownValuesCount. + int maxKnownCount = Math.Max(oldValue.KnownValuesCount, newValue.KnownValuesCount); + int result = 0; + for (int i = 0; i < maxKnownCount; i++) + { + if (oldValue[i] == newValue[i]) + { + continue; + } + else if (oldValue[i] < newValue[i]) + { + result = -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + return result; + } + + public override PropertySetAbstractValue Merge(PropertySetAbstractValue value1, PropertySetAbstractValue value2) + { + // The PropertySetAbstractValue indexer allows accessing beyond KnownValuesCount (returns Unknown), + // so looping through the max of the two KnownValuesCount. + int maxKnownCount = Math.Max(value1.KnownValuesCount, value2.KnownValuesCount); + using ArrayBuilder builder = ArrayBuilder.GetInstance(maxKnownCount); + + for (int i = 0; i < maxKnownCount; i++) + { + builder.Add(MergeKind(value1[i], value2[i])); + } + + return PropertySetAbstractValue.GetInstance(builder); + } + + private static PropertySetAbstractValueKind MergeKind(PropertySetAbstractValueKind kind1, PropertySetAbstractValueKind kind2) + { + if (kind1 == kind2) + { + return kind1; + } + else + { + return PropertySetAbstractValueKind.MaybeFlagged; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.TrackedAssignmentData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.TrackedAssignmentData.cs new file mode 100644 index 0000000000000..b3b281658b8f3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.TrackedAssignmentData.cs @@ -0,0 +1,77 @@ +// 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 Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + internal partial class PropertySetAnalysis + { + private sealed partial class PropertySetDataFlowOperationVisitor + { + /// + /// For a given analysis entity, keep track of property / field assignment operations and abstract locations, for + /// evaluating hazardous usages on initializations. + /// + private class TrackedAssignmentData + { + public PooledHashSet? AssignmentsWithUnknownLocation + { + get; + private set; + } + + public PooledDictionary>? AbstractLocationsToAssignments + { + get; + private set; + } + + public void Free() + { + this.AssignmentsWithUnknownLocation?.Dispose(); + this.AssignmentsWithUnknownLocation = null; + + if (this.AbstractLocationsToAssignments != null) + { + foreach (PooledHashSet hashSet in this.AbstractLocationsToAssignments.Values) + { + hashSet?.Dispose(); + } + + this.AbstractLocationsToAssignments.Dispose(); + this.AbstractLocationsToAssignments = null; + } + } + + public void TrackAssignmentWithUnknownLocation(IAssignmentOperation assignmentOperation) + { + this.AssignmentsWithUnknownLocation ??= PooledHashSet.GetInstance(); + + this.AssignmentsWithUnknownLocation.Add(assignmentOperation); + } + + public void TrackAssignmentWithAbstractLocation( + IAssignmentOperation assignmentOperation, + AbstractLocation abstractLocation) + { + this.AbstractLocationsToAssignments ??= + PooledDictionary>.GetInstance(); + + if (!this.AbstractLocationsToAssignments.TryGetValue( + abstractLocation, + out PooledHashSet assignments)) + { + assignments = PooledHashSet.GetInstance(); + this.AbstractLocationsToAssignments.Add(abstractLocation, assignments); + } + + assignments.Add(assignmentOperation); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..c3e6515b97212 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.PropertySetDataFlowOperationVisitor.cs @@ -0,0 +1,728 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + using PropertySetAnalysisData = DictionaryAnalysisData; + + internal partial class PropertySetAnalysis + { + /// + /// Operation visitor to flow the location validation values across a given statement in a basic block. + /// + private sealed partial class PropertySetDataFlowOperationVisitor : + AbstractLocationDataFlowOperationVisitor + { + /// + /// Keeps track of hazardous usages detected. + /// + /// Method == null => return / initialization + private readonly ImmutableDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>.Builder _hazardousUsageBuilder; + + private readonly ImmutableHashSet.Builder _visitedLocalFunctions; + + private readonly ImmutableHashSet.Builder _visitedLambdas; + + /// + /// When looking for initialization hazardous usages, track the assignment operations for fields and properties for the tracked type. + /// + /// + /// Mapping of AnalysisEntity (for a field or property of the tracked type) to AbstractLocation(s) to IAssignmentOperation(s) + /// + private PooledDictionary? TrackedFieldPropertyAssignments; + + /// + /// The types containing the property set we're tracking. + /// + private readonly ImmutableHashSet TrackedTypeSymbols; + + public PropertySetDataFlowOperationVisitor(PropertySetAnalysisContext analysisContext) + : base(analysisContext) + { + Debug.Assert(analysisContext.PointsToAnalysisResult != null); + + this._hazardousUsageBuilder = ImmutableDictionary.CreateBuilder<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>(); + + this._visitedLocalFunctions = ImmutableHashSet.CreateBuilder(); + + this._visitedLambdas = ImmutableHashSet.CreateBuilder(); + + ImmutableHashSet.Builder builder = ImmutableHashSet.CreateBuilder(); + foreach (string typeToTrackMetadataName in analysisContext.TypeToTrackMetadataNames) + { + if (this.WellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(typeToTrackMetadataName, out INamedTypeSymbol? trackedTypeSymbol)) + { + builder.Add(trackedTypeSymbol); + } + } + + TrackedTypeSymbols = builder.ToImmutableHashSet(); + Debug.Assert(this.TrackedTypeSymbols.Any()); + + if (this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetInitializationHazardousUsageEvaluator(out _)) + { + this.TrackedFieldPropertyAssignments = PooledDictionary.GetInstance(); + } + } + + public ImmutableDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> HazardousUsages => this._hazardousUsageBuilder.ToImmutable(); + + public ImmutableHashSet VisitedLocalFunctions => this._visitedLocalFunctions.ToImmutable(); + + public ImmutableHashSet VisitedLambdas => this._visitedLambdas.ToImmutable(); + + protected override PropertySetAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) => ValueDomain.Bottom; + + protected override bool HasAnyAbstractValue(PropertySetAnalysisData data) => data.Count > 0; + + protected override PropertySetAbstractValue GetAbstractValue(AbstractLocation location) + => this.CurrentAnalysisData.TryGetValue(location, out var value) ? value : ValueDomain.Bottom; + + protected override void ResetCurrentAnalysisData() => ResetAnalysisData(CurrentAnalysisData); + + protected override void StopTrackingAbstractValue(AbstractLocation location) => CurrentAnalysisData.Remove(location); + + protected override void SetAbstractValue(AbstractLocation location, PropertySetAbstractValue value) + { + if (value != PropertySetAbstractValue.Unknown + || this.CurrentAnalysisData.ContainsKey(location)) + { + this.CurrentAnalysisData[location] = value; + } + } + + protected override PropertySetAnalysisData MergeAnalysisData(PropertySetAnalysisData value1, PropertySetAnalysisData value2) + => PropertySetAnalysisDomainInstance.Merge(value1, value2); + protected override void UpdateValuesForAnalysisData(PropertySetAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData, CurrentAnalysisData); + protected override PropertySetAnalysisData GetClonedAnalysisData(PropertySetAnalysisData analysisData) + => GetClonedAnalysisDataHelper(analysisData); + public override PropertySetAnalysisData GetEmptyAnalysisData() + => GetEmptyAnalysisDataHelper(); + protected override PropertySetAnalysisData GetExitBlockOutputData(PropertySetAnalysisResult analysisResult) + => GetClonedAnalysisDataHelper(analysisResult.ExitBlockOutput.Data); + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(PropertySetAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException, CurrentAnalysisData); + protected override bool Equals(PropertySetAnalysisData value1, PropertySetAnalysisData value2) + => EqualsHelper(value1, value2); + + protected override void SetValueForParameterPointsToLocationOnEntry(IParameterSymbol parameter, PointsToAbstractValue pointsToAbstractValue) + { + } + + protected override void EscapeValueForParameterPointsToLocationOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity, ImmutableHashSet escapedLocations) + { + } + + protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, PropertySetAbstractValue value) + { + } + + protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, PropertySetAbstractValue assignedValue, bool mayBeAssignment = false) + { + } + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, PropertySetAbstractValue assignedValue) + { + } + + public override PropertySetAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + PropertySetAbstractValue abstractValue = base.VisitObjectCreation(operation, argument); + if (operation.Type == null || operation.Constructor == null) + return abstractValue; + + if (this.TrackedTypeSymbols.Any(s => operation.Type.GetBaseTypesAndThis().Contains(s))) + { + ConstructorMapper constructorMapper = this.DataFlowAnalysisContext.ConstructorMapper; + if (!constructorMapper.PropertyAbstractValues.IsEmpty) + { + abstractValue = PropertySetAbstractValue.GetInstance(constructorMapper.PropertyAbstractValues); + } + else if (constructorMapper.MapFromPointsToAbstractValue != null) + { + using ArrayBuilder builder = ArrayBuilder.GetInstance(); + + foreach (IArgumentOperation argumentOperation in operation.Arguments) + { + builder.Add(this.GetPointsToAbstractValue(argumentOperation)); + } + + abstractValue = constructorMapper.MapFromPointsToAbstractValue(operation.Constructor, builder); + } + else if (constructorMapper.MapFromValueContentAbstractValue != null) + { + Debug.Assert(this.DataFlowAnalysisContext.ValueContentAnalysisResult != null); + using ArrayBuilder pointsToBuilder = ArrayBuilder.GetInstance(); + using ArrayBuilder valueContentBuilder = ArrayBuilder.GetInstance(); + + foreach (IArgumentOperation argumentOperation in operation.Arguments) + { + pointsToBuilder.Add(this.GetPointsToAbstractValue(argumentOperation)); + valueContentBuilder.Add(this.GetValueContentAbstractValue(argumentOperation.Value)); + } + + abstractValue = constructorMapper.MapFromValueContentAbstractValue(operation.Constructor, valueContentBuilder, pointsToBuilder); + } + else + { + Debug.Fail("Unhandled ConstructorMapper"); + return abstractValue; + } + + PointsToAbstractValue pointsToAbstractValue = this.GetPointsToAbstractValue(operation); + this.SetAbstractValue(pointsToAbstractValue, abstractValue); + } + else + { + if (TryFindNonTrackedTypeHazardousUsageEvaluator( + operation.Constructor, + operation.Arguments, + out HazardousUsageEvaluator? hazardousUsageEvaluator, + out IOperation? propertySetInstance)) + { + this.EvaluatePotentialHazardousUsage( + operation.Syntax, + operation.Constructor, + propertySetInstance, + (PropertySetAbstractValue abstractValue) => hazardousUsageEvaluator.InvocationEvaluator!( + operation.Constructor, + abstractValue)); + } + } + + return abstractValue; + } + + protected override PropertySetAbstractValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument) + { + PropertySetAbstractValue? baseValue = base.VisitAssignmentOperation(operation, argument); + if (operation.Target.Type == null) + return baseValue; + + // If we need to evaluate hazardous usages on initializations, track assignments of properties and fields, so + // at the end of the CFG we can figure out which assignment operations to flag. + if (this.TrackedFieldPropertyAssignments != null + && this.TrackedTypeSymbols.Any(s => operation.Target.Type.GetBaseTypesAndThis().Contains(s)) + && (operation.Target.Kind == OperationKind.PropertyReference + || operation.Target.Kind == OperationKind.FieldReference + || operation.Target.Kind == OperationKind.FlowCaptureReference)) + { + AnalysisEntity? targetAnalysisEntity = null; + if (operation.Target.Kind == OperationKind.FlowCaptureReference) + { + if (this.TryUnwrapFlowCaptureReference(operation.Target, out IOperation? lValueOperation, OperationKind.PropertyReference, OperationKind.FieldReference)) + { + this.AnalysisEntityFactory.TryCreate(lValueOperation, out targetAnalysisEntity); + } + } + else + { + this.AnalysisEntityFactory.TryCreate(operation.Target, out targetAnalysisEntity); + } + + if (targetAnalysisEntity != null) + { + PointsToAbstractValue pointsToAbstractValue = this.GetPointsToAbstractValue(operation.Value); + if (!this.TrackedFieldPropertyAssignments.TryGetValue( + targetAnalysisEntity, + out TrackedAssignmentData trackedAssignmentData)) + { + trackedAssignmentData = new TrackedAssignmentData(); + this.TrackedFieldPropertyAssignments.Add(targetAnalysisEntity, trackedAssignmentData); + } + + if (pointsToAbstractValue.Kind == PointsToAbstractValueKind.KnownLocations) + { + foreach (AbstractLocation abstractLocation in pointsToAbstractValue.Locations) + { + trackedAssignmentData.TrackAssignmentWithAbstractLocation(operation, abstractLocation); + } + } + else if (pointsToAbstractValue.Kind is PointsToAbstractValueKind.Unknown + or PointsToAbstractValueKind.UnknownNotNull) + { + trackedAssignmentData.TrackAssignmentWithUnknownLocation(operation); + } + else if (pointsToAbstractValue.NullState == NullAbstractValue.Null) + { + // Do nothing. + } + else + { + Debug.Fail($"Unhandled PointsToAbstractValue: Kind = {pointsToAbstractValue.Kind}, NullState = {pointsToAbstractValue.NullState}"); + } + } + } + + IPropertyReferenceOperation? propertyReferenceOperation = operation.Target as IPropertyReferenceOperation; + if (propertyReferenceOperation == null && operation.Target.Kind == OperationKind.FlowCaptureReference) + { + this.TryUnwrapFlowCaptureReference(operation.Target, out IOperation? lValue, OperationKind.PropertyReference); + propertyReferenceOperation = lValue as IPropertyReferenceOperation; + } + + if (propertyReferenceOperation != null + && propertyReferenceOperation.Instance?.Type != null + && this.TrackedTypeSymbols.Any(s => propertyReferenceOperation.Instance.Type.GetBaseTypesAndThis().Contains(s)) + && this.DataFlowAnalysisContext.PropertyMappers.TryGetPropertyMapper( + propertyReferenceOperation.Property.Name, + out PropertyMapper? propertyMapper, + out int index)) + { + PropertySetAbstractValueKind propertySetAbstractValueKind; + + if (propertyMapper.MapFromPointsToAbstractValue != null) + { + propertySetAbstractValueKind = propertyMapper.MapFromPointsToAbstractValue( + this.GetPointsToAbstractValue(operation.Value)); + } + else if (propertyMapper.MapFromValueContentAbstractValue != null) + { + Debug.Assert(this.DataFlowAnalysisContext.ValueContentAnalysisResult != null); + propertySetAbstractValueKind = propertyMapper.MapFromValueContentAbstractValue( + this.GetValueContentAbstractValue(operation.Value)); + } + else + { + Debug.Fail("Unhandled PropertyMapper"); + return baseValue; + } + + baseValue = null; + PointsToAbstractValue pointsToAbstractValue = this.GetPointsToAbstractValue(propertyReferenceOperation.Instance); + foreach (AbstractLocation location in pointsToAbstractValue.Locations) + { + PropertySetAbstractValue propertySetAbstractValue = this.GetAbstractValue(location); + propertySetAbstractValue = propertySetAbstractValue.ReplaceAt(index, propertySetAbstractValueKind); + + if (baseValue == null) + { + baseValue = propertySetAbstractValue; + } + else + { + baseValue = this.DataFlowAnalysisContext.ValueDomain.Merge(baseValue, propertySetAbstractValue); + } + + this.SetAbstractValue(location, propertySetAbstractValue); + } + + return baseValue ?? PropertySetAbstractValue.Unknown.ReplaceAt(index, propertySetAbstractValueKind); + } + + return baseValue; + } + + /// + /// Processes PropertySetAbstractValues at the end of the ControlFlowGraph. + /// + /// Exit block output. + /// When evaluating hazardous usages on initializations. + /// class Class + /// { + /// public static readonly Settings InsecureSettings = new Settings { AllowAnyoneAdminAccess = true }; + /// } + /// + internal void ProcessExitBlock(PropertySetBlockAnalysisResult exitBlockOutput) + { + if (this.TrackedFieldPropertyAssignments == null) + { + return; + } + + try + { + this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetInitializationHazardousUsageEvaluator( + out var initializationHazardousUsageEvaluator); + + foreach (KeyValuePair kvp + in this.TrackedFieldPropertyAssignments) + { + if (!this.DataFlowAnalysisContext.PointsToAnalysisResult!.ExitBlockOutput.Data.TryGetValue( + kvp.Key, out var pointsToAbstractValue)) + { + continue; + } + + if (pointsToAbstractValue.Kind == PointsToAbstractValueKind.KnownLocations) + { + if (kvp.Value.AbstractLocationsToAssignments != null) + { + foreach (AbstractLocation abstractLocation in pointsToAbstractValue.Locations) + { + if (abstractLocation.IsNull || abstractLocation.IsAnalysisEntityDefaultLocation) + { + continue; + } + + if (!kvp.Value.AbstractLocationsToAssignments.TryGetValue( + abstractLocation, + out PooledHashSet assignments)) + { + Debug.Fail("Expected to have tracked assignment operations for a given location"); + continue; + } + + if (!exitBlockOutput.Data.TryGetValue( + abstractLocation, + out var propertySetAbstractValue)) + { + propertySetAbstractValue = PropertySetAbstractValue.Unknown; + } + + HazardousUsageEvaluationResult? result = + initializationHazardousUsageEvaluator?.ValueEvaluator!(propertySetAbstractValue); + if (result is not null and not HazardousUsageEvaluationResult.Unflagged) + { + foreach (IAssignmentOperation assignmentOperation in assignments) + { + this.MergeHazardousUsageResult( + assignmentOperation.Syntax, + methodSymbol: null, // No method invocation; just evaluating initialization value. + result.Value); + } + } + } + } + else + { + Debug.Fail("Expected to have tracked assignment operations with locations"); + } + } + else if (pointsToAbstractValue.Kind is PointsToAbstractValueKind.Unknown + or PointsToAbstractValueKind.UnknownNotNull) + { + if (kvp.Value.AssignmentsWithUnknownLocation != null) + { + HazardousUsageEvaluationResult? result = + initializationHazardousUsageEvaluator?.ValueEvaluator!(PropertySetAbstractValue.Unknown); + if (result is not null and not HazardousUsageEvaluationResult.Unflagged) + { + foreach (IAssignmentOperation assignmentOperation in kvp.Value.AssignmentsWithUnknownLocation) + { + this.MergeHazardousUsageResult( + assignmentOperation.Syntax, + methodSymbol: null, // No method invocation; just evaluating initialization value. + result.Value); + } + } + } + else + { + Debug.Fail("Expected to have tracked assignment operations with unknown PointsTo"); + } + } + else if (pointsToAbstractValue.NullState == NullAbstractValue.Null) + { + // Do nothing. + } + else + { + Debug.Fail($"Unhandled PointsToAbstractValue: Kind = {pointsToAbstractValue.Kind}, NullState = {pointsToAbstractValue.NullState}"); + } + } + } + finally + { + foreach (TrackedAssignmentData trackedAssignmentData in this.TrackedFieldPropertyAssignments.Values) + { + trackedAssignmentData.Free(); + } + + this.TrackedFieldPropertyAssignments.Dispose(); + this.TrackedFieldPropertyAssignments = null; + } + } + + public override PropertySetAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction(IMethodSymbol method, IOperation? visitedInstance, ImmutableArray visitedArguments, bool invokedAsDelegate, IOperation originalOperation, PropertySetAbstractValue defaultValue) + { + PropertySetAbstractValue baseValue = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + + if (this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetArgumentHazardousUsageEvaluator( + out var argumentHazardousUsageEvaluator)) + { + foreach (IArgumentOperation visitedArgument in visitedArguments) + { + if (visitedArgument.Value?.Type != null && this.TrackedTypeSymbols.Any(s => visitedArgument.Value.Type.GetBaseTypesAndThis().Contains(s))) + { + this.EvaluatePotentialHazardousUsage( + visitedArgument.Value.Syntax, + null, + visitedArgument.Value, + (PropertySetAbstractValue abstractValue) => argumentHazardousUsageEvaluator.ValueEvaluator!(abstractValue)); + } + } + } + + // If we have a HazardousUsageEvaluator for a method within the tracked type, + // or for a method within a different type. + IOperation? propertySetInstance = visitedInstance; + if ((visitedInstance?.Type != null + && this.TrackedTypeSymbols.Any(s => visitedInstance.Type.GetBaseTypesAndThis().Contains(s)) + && this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetHazardousUsageEvaluator( + method.MetadataName, + out var hazardousUsageEvaluator)) + || TryFindNonTrackedTypeHazardousUsageEvaluator( + method, + visitedArguments, + out hazardousUsageEvaluator, + out propertySetInstance)) + { + RoslynDebug.Assert(propertySetInstance != null); + this.EvaluatePotentialHazardousUsage( + originalOperation.Syntax, + method, + propertySetInstance, + (PropertySetAbstractValue abstractValue) => hazardousUsageEvaluator!.InvocationEvaluator!(method, abstractValue)); + } + else + { + this.MergeInterproceduralResults(originalOperation); + } + + return baseValue; + } + + /// + /// Find the hazardous usage evaluator for the non tracked type. + /// + /// The potential hazardous method. + /// IArgumentOperations of this invocation. + /// Target evaluator. + /// The tracked argument. + private bool TryFindNonTrackedTypeHazardousUsageEvaluator( + IMethodSymbol method, + ImmutableArray visitedArguments, + [NotNullWhen(returnValue: true)] out HazardousUsageEvaluator? evaluator, + [NotNullWhen(returnValue: true)] out IOperation? instance) + { + evaluator = null; + instance = null; + PooledHashSet? hazardousUsageTypeNames = null; + try + { + if (!GetNamesOfHazardousUsageTypes(method.ContainingType, out hazardousUsageTypeNames)) + { + return false; + } + + // This doesn't handle the case of multiple instances of the type being tracked. + // If that's needed one day, will need to extend this. + foreach (IArgumentOperation argumentOperation in visitedArguments) + { + IOperation value = argumentOperation.Value; + ITypeSymbol? argumentTypeSymbol = value is IConversionOperation conversionOperation ? conversionOperation.Operand.Type : value.Type; + if (argumentTypeSymbol == null || argumentOperation.Parameter == null) + continue; + + foreach (string hazardousUsageTypeName in hazardousUsageTypeNames) + { + if (this.TrackedTypeSymbols.Any(s => argumentTypeSymbol.GetBaseTypesAndThis().Contains(s)) + && this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetHazardousUsageEvaluator( + hazardousUsageTypeName, + method.MetadataName, + argumentOperation.Parameter.MetadataName, + out evaluator)) + { + instance = argumentOperation.Value; + return true; + } + } + } + } + finally + { + hazardousUsageTypeNames?.Dispose(); + } + + return false; + } + + private bool GetNamesOfHazardousUsageTypes(INamedTypeSymbol containingType, [NotNullWhen(returnValue: true)] out PooledHashSet? hazardousUsageTypeNames) + { + hazardousUsageTypeNames = null; + if (this.DataFlowAnalysisContext.HazardousUsageTypesToNames.TryGetValue( + (containingType, false), + out var containingTypeName)) + { + hazardousUsageTypeNames = PooledHashSet.GetInstance(); + hazardousUsageTypeNames.Add(containingTypeName); + } + + foreach (INamedTypeSymbol type in containingType.GetBaseTypesAndThis()) + { + if (this.DataFlowAnalysisContext.HazardousUsageTypesToNames.TryGetValue( + (type, true), + out containingTypeName)) + { + hazardousUsageTypeNames ??= PooledHashSet.GetInstance(); + + hazardousUsageTypeNames.Add(containingTypeName); + } + } + + return hazardousUsageTypeNames != null; + } + + /// + /// Evaluates an operation for potentially being a hazardous usage. + /// + /// SyntaxNode of operation that's being evaluated. + /// Method symbol of the invocation operation that's being evaluated, or null if not an invocation operation (return / initialization). + /// IOperation of the tracked type containing the properties to be evaluated. + /// Function to evaluate a PropertySetAbstractValue to a HazardousUsageEvaluationResult. + /// Optional function to map AbstractLocations to PropertySetAbstractValues. If null, uses this.CurrentAnalysisData. + private void EvaluatePotentialHazardousUsage( + SyntaxNode operationSyntax, + IMethodSymbol? methodSymbol, + IOperation propertySetInstance, + Func evaluationFunction, + Func? locationToAbstractValueMapping = null) + { + locationToAbstractValueMapping ??= this.GetAbstractValue; + + PointsToAbstractValue pointsToAbstractValue = this.GetPointsToAbstractValue(propertySetInstance); + HazardousUsageEvaluationResult result = HazardousUsageEvaluationResult.Unflagged; + foreach (AbstractLocation location in pointsToAbstractValue.Locations) + { + PropertySetAbstractValue locationAbstractValue = locationToAbstractValueMapping(location); + + HazardousUsageEvaluationResult evaluationResult = evaluationFunction(locationAbstractValue); + result = MergeHazardousUsageEvaluationResult(result, evaluationResult); + } + + this.MergeHazardousUsageResult(operationSyntax, methodSymbol, result); + } + + private void MergeHazardousUsageResult( + SyntaxNode operationSyntax, + IMethodSymbol? methodSymbol, + HazardousUsageEvaluationResult result) + { + if (result != HazardousUsageEvaluationResult.Unflagged) + { + (Location, IMethodSymbol?) key = (operationSyntax.GetLocation(), methodSymbol); + if (this._hazardousUsageBuilder.TryGetValue(key, out HazardousUsageEvaluationResult existingResult)) + { + this._hazardousUsageBuilder[key] = MergeHazardousUsageEvaluationResult(result, existingResult); + } + else + { + this._hazardousUsageBuilder.Add(key, result); + } + } + } + + public override PropertySetAbstractValue VisitInvocation_LocalFunction(IMethodSymbol localFunction, ImmutableArray visitedArguments, IOperation originalOperation, PropertySetAbstractValue defaultValue) + { + PropertySetAbstractValue baseValue = base.VisitInvocation_LocalFunction(localFunction, visitedArguments, originalOperation, defaultValue); + this._visitedLocalFunctions.Add(localFunction); + this.MergeInterproceduralResults(originalOperation); + return baseValue; + } + + public override PropertySetAbstractValue VisitInvocation_Lambda(IFlowAnonymousFunctionOperation lambda, ImmutableArray visitedArguments, IOperation originalOperation, PropertySetAbstractValue defaultValue) + { + PropertySetAbstractValue baseValue = base.VisitInvocation_Lambda(lambda, visitedArguments, originalOperation, defaultValue); + this._visitedLambdas.Add(lambda); + this.MergeInterproceduralResults(originalOperation); + return baseValue; + } + + protected override void ProcessReturnValue(IOperation? returnValue) + { + base.ProcessReturnValue(returnValue); + + if (returnValue?.Type != null + && this.TrackedTypeSymbols.Any(s => returnValue.Type.GetBaseTypesAndThis().Contains(s)) + && this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetReturnHazardousUsageEvaluator( + out var hazardousUsageEvaluator)) + { + this.EvaluatePotentialHazardousUsage( + returnValue.Syntax, + null, + returnValue, + (PropertySetAbstractValue abstractValue) => hazardousUsageEvaluator.ValueEvaluator!(abstractValue)); + } + } + + private void MergeInterproceduralResults(IOperation originalOperation) + { + if (!this.TryGetInterproceduralAnalysisResult(originalOperation, out PropertySetAnalysisResult? subResult)) + { + return; + } + + foreach (KeyValuePair<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> kvp in subResult.HazardousUsages) + { + if (this._hazardousUsageBuilder.TryGetValue(kvp.Key, out HazardousUsageEvaluationResult existingValue)) + { + this._hazardousUsageBuilder[kvp.Key] = MergeHazardousUsageEvaluationResult(kvp.Value, existingValue); + } + else + { + this._hazardousUsageBuilder.Add(kvp.Key, kvp.Value); + } + } + + foreach (IMethodSymbol localFunctionSymbol in subResult.VisitedLocalFunctions) + { + this._visitedLocalFunctions.Add(localFunctionSymbol); + } + + foreach (IFlowAnonymousFunctionOperation lambdaOperation in subResult.VisitedLambdas) + { + this._visitedLambdas.Add(lambdaOperation); + } + } + + /// + /// Attempts to find the underlying IOperation that a flow capture reference refers to. + /// + /// Operation that may be a flow capture reference to look at. + /// The found underlying operation, if any. + /// Kinds of operations to look for. + /// True if found, false otherwise. + private bool TryUnwrapFlowCaptureReference(IOperation flowCaptureReferenceOperation, [NotNullWhen(returnValue: true)] out IOperation? unwrappedOperation, params OperationKind[] kinds) + { + unwrappedOperation = null; + if (flowCaptureReferenceOperation != null && flowCaptureReferenceOperation.Kind == OperationKind.FlowCaptureReference) + { + PointsToAbstractValue lValuePointsToAbstractValue = this.GetPointsToAbstractValue(flowCaptureReferenceOperation); + if (lValuePointsToAbstractValue.LValueCapturedOperations.Count == 1) + { + IOperation lValueOperation = lValuePointsToAbstractValue.LValueCapturedOperations.First(); + if (kinds == null || kinds.Contains(lValueOperation.Kind)) + { + unwrappedOperation = lValueOperation; + return true; + } + } + else + { + Debug.Fail("Can LValues FlowCaptureReferences have more than one operation?"); + } + } + + return false; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.cs new file mode 100644 index 0000000000000..bd0634f55b795 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysis.cs @@ -0,0 +1,328 @@ +// 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 Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + using PropertySetAnalysisData = DictionaryAnalysisData; + using PropertySetAnalysisDomain = MapAbstractDomain; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Dataflow analysis to track of / instances. + /// + internal partial class PropertySetAnalysis : ForwardDataFlowAnalysis + { + public static readonly PropertySetAnalysisDomain PropertySetAnalysisDomainInstance = new(PropertySetAbstractValueDomain.Default); + + private PropertySetAnalysis(PropertySetAnalysisDomain analysisDomain, PropertySetDataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + /// + /// Analyzers should use BatchGetOrComputeHazardousUsages instead. Gets hazardous usages of an object based on a set of its properties. + /// + /// Control flow graph of the code. + /// Compilation containing the code. + /// Symbol of the code to examine. + /// Names of the types to track. + /// How constructor invocations map to s. + /// How property assignments map to . + /// When and how to evaluate s to for hazardous usages. + /// Interprocedural dataflow analysis configuration. + /// Whether to be pessimistic. + /// Property set analysis result. + internal static PropertySetAnalysisResult? GetOrComputeResult( + ControlFlowGraph cfg, + Compilation compilation, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + ImmutableHashSet typeToTrackMetadataNames, + ConstructorMapper constructorMapper, + PropertyMapperCollection propertyMappers, + HazardousUsageEvaluatorCollection hazardousUsageEvaluators, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis = false) + { + if (constructorMapper == null) + { + throw new ArgumentNullException(nameof(constructorMapper)); + } + + if (propertyMappers == null) + { + throw new ArgumentNullException(nameof(propertyMappers)); + } + + if (hazardousUsageEvaluators == null) + { + throw new ArgumentNullException(nameof(hazardousUsageEvaluators)); + } + + constructorMapper.Validate(propertyMappers.PropertyValuesCount); + + var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + + PointsToAnalysisResult? pointsToAnalysisResult; + ValueContentAnalysisResult? valueContentAnalysisResult; + if (!constructorMapper.RequiresValueContentAnalysis && !propertyMappers.RequiresValueContentAnalysis) + { + pointsToAnalysisResult = PointsToAnalysis.TryGetOrComputeResult( + cfg, + owningSymbol, + analyzerOptions, + wellKnownTypeProvider, + PointsToAnalysisKind.Complete, + interproceduralAnalysisConfig, + interproceduralAnalysisPredicate: null, + pessimisticAnalysis, + performCopyAnalysis: false); + if (pointsToAnalysisResult == null) + { + return null; + } + + valueContentAnalysisResult = null; + } + else + { + valueContentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( + cfg, + owningSymbol, + analyzerOptions, + wellKnownTypeProvider, + PointsToAnalysisKind.Complete, + interproceduralAnalysisConfig, + out var copyAnalysisResult, + out pointsToAnalysisResult, + pessimisticAnalysis, + performCopyAnalysis: false); + if (valueContentAnalysisResult == null) + { + return null; + } + } + + var analysisContext = PropertySetAnalysisContext.Create( + PropertySetAbstractValueDomain.Default, + wellKnownTypeProvider, + cfg, + owningSymbol, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis, + pointsToAnalysisResult, + valueContentAnalysisResult, + TryGetOrComputeResultForAnalysisContext, + typeToTrackMetadataNames, + constructorMapper, + propertyMappers, + hazardousUsageEvaluators); + var result = TryGetOrComputeResultForAnalysisContext(analysisContext); + return result; + } + + /// + /// Gets hazardous usages of an object based on a set of its properties. + /// + /// Compilation containing the code. + /// Root operations of code blocks to analyze. + /// Name of the type to track. + /// How constructor invocations map to s. + /// How property assignments map to . + /// When and how to evaluate s to for hazardous usages. + /// Interprocedural dataflow analysis configuration. + /// Whether to be pessimistic. + /// Dictionary of and pairs mapping to the kind of hazardous usage (Flagged or MaybeFlagged). The method in the key is null for return/initialization statements. + /// Unlike , this overload also performs DFA on all descendant local and anonymous functions. + public static PooledDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>? BatchGetOrComputeHazardousUsages( + Compilation compilation, + IEnumerable<(IOperation Operation, ISymbol ContainingSymbol)> rootOperationsNeedingAnalysis, + AnalyzerOptions analyzerOptions, + string typeToTrackMetadataName, + ConstructorMapper constructorMapper, + PropertyMapperCollection propertyMappers, + HazardousUsageEvaluatorCollection hazardousUsageEvaluators, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis = false) + { + return BatchGetOrComputeHazardousUsages( + compilation, + rootOperationsNeedingAnalysis, + analyzerOptions, + new string[] { typeToTrackMetadataName }.ToImmutableHashSet(), + constructorMapper, + propertyMappers, + hazardousUsageEvaluators, + interproceduralAnalysisConfig, + pessimisticAnalysis); + } + + /// + /// Gets hazardous usages of an object based on a set of its properties. + /// + /// Compilation containing the code. + /// Root operations of code blocks to analyze. + /// Names of the types to track. + /// How constructor invocations map to s. + /// How property assignments map to . + /// When and how to evaluate s to for hazardous usages. + /// Interprocedural dataflow analysis configuration. + /// Whether to be pessimistic. + /// Dictionary of and pairs mapping to the kind of hazardous usage (Flagged or MaybeFlagged). The method in the key is null for return/initialization statements. + /// Unlike , this overload also performs DFA on all descendant local and anonymous functions. + public static PooledDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>? BatchGetOrComputeHazardousUsages( + Compilation compilation, + IEnumerable<(IOperation Operation, ISymbol ContainingSymbol)> rootOperationsNeedingAnalysis, + AnalyzerOptions analyzerOptions, + ImmutableHashSet typeToTrackMetadataNames, + ConstructorMapper constructorMapper, + PropertyMapperCollection propertyMappers, + HazardousUsageEvaluatorCollection hazardousUsageEvaluators, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis = false) + { + PooledDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>? allResults = null; + foreach ((IOperation Operation, ISymbol ContainingSymbol) in rootOperationsNeedingAnalysis) + { + var success = Operation.TryGetEnclosingControlFlowGraph(out ControlFlowGraph? enclosingControlFlowGraph); + Debug.Assert(success); + if (enclosingControlFlowGraph == null) + { + Debug.Fail("Expected non-null CFG"); + continue; + } + + PropertySetAnalysisResult? enclosingResult = InvokeDfaAndAccumulateResults( + enclosingControlFlowGraph, + ContainingSymbol); + if (enclosingResult == null) + { + continue; + } + + // Also look at local functions and lambdas that weren't visited via interprocedural analysis. + foreach (IMethodSymbol localFunctionSymbol in enclosingControlFlowGraph.LocalFunctions) + { + if (!enclosingResult.VisitedLocalFunctions.Contains(localFunctionSymbol)) + { + InvokeDfaAndAccumulateResults( + enclosingControlFlowGraph.GetLocalFunctionControlFlowGraph(localFunctionSymbol), + localFunctionSymbol); + } + } + + foreach (IFlowAnonymousFunctionOperation flowAnonymousFunctionOperation in + enclosingControlFlowGraph.DescendantOperations( + OperationKind.FlowAnonymousFunction)) + { + if (!enclosingResult.VisitedLambdas.Contains(flowAnonymousFunctionOperation)) + { + InvokeDfaAndAccumulateResults( + enclosingControlFlowGraph.GetAnonymousFunctionControlFlowGraph(flowAnonymousFunctionOperation), + flowAnonymousFunctionOperation.Symbol); + } + } + } + + return allResults; + + // Merges results from single PropertySet DFA invocation into allResults. + PropertySetAnalysisResult? InvokeDfaAndAccumulateResults(ControlFlowGraph cfg, ISymbol owningSymbol) + { + PropertySetAnalysisResult? propertySetAnalysisResult = + PropertySetAnalysis.GetOrComputeResult( + cfg, + compilation, + owningSymbol, + analyzerOptions, + typeToTrackMetadataNames, + constructorMapper, + propertyMappers, + hazardousUsageEvaluators, + interproceduralAnalysisConfig, + pessimisticAnalysis); + if (propertySetAnalysisResult == null || propertySetAnalysisResult.HazardousUsages.IsEmpty) + { + return propertySetAnalysisResult; + } + + allResults ??= PooledDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult>.GetInstance(); + + foreach (KeyValuePair<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> kvp + in propertySetAnalysisResult.HazardousUsages) + { + if (allResults.TryGetValue(kvp.Key, out HazardousUsageEvaluationResult existingValue)) + { + allResults[kvp.Key] = PropertySetAnalysis.MergeHazardousUsageEvaluationResult(existingValue, kvp.Value); + } + else + { + allResults.Add(kvp.Key, kvp.Value); + } + } + + return propertySetAnalysisResult; + } + } + + /// + /// When there are multiple hazardous usage evaluations for the same exact code, this prioritizes Flagged over MaybeFlagged, and MaybeFlagged over Unflagged. + /// + /// First evaluation result. + /// Second evaluation result. + /// Prioritized result. + public static HazardousUsageEvaluationResult MergeHazardousUsageEvaluationResult(HazardousUsageEvaluationResult r1, HazardousUsageEvaluationResult r2) + { + if (r1 == HazardousUsageEvaluationResult.Flagged || r2 == HazardousUsageEvaluationResult.Flagged) + { + return HazardousUsageEvaluationResult.Flagged; + } + else if (r1 == HazardousUsageEvaluationResult.MaybeFlagged || r2 == HazardousUsageEvaluationResult.MaybeFlagged) + { + return HazardousUsageEvaluationResult.MaybeFlagged; + } + else + { + return HazardousUsageEvaluationResult.Unflagged; + } + } + + private static PropertySetAnalysisResult? TryGetOrComputeResultForAnalysisContext(PropertySetAnalysisContext analysisContext) + { + var operationVisitor = new PropertySetDataFlowOperationVisitor(analysisContext); + var analysis = new PropertySetAnalysis(PropertySetAnalysisDomainInstance, operationVisitor); + return analysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + protected override PropertySetAnalysisResult ToResult( + PropertySetAnalysisContext analysisContext, + DataFlowAnalysisResult dataFlowAnalysisResult) + { + PropertySetDataFlowOperationVisitor visitor = (PropertySetDataFlowOperationVisitor)this.OperationVisitor; + visitor.ProcessExitBlock(dataFlowAnalysisResult.ExitBlockOutput); + return new PropertySetAnalysisResult( + dataFlowAnalysisResult, + visitor.HazardousUsages, + visitor.VisitedLocalFunctions, + visitor.VisitedLambdas); + } + + protected override PropertySetBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, PropertySetAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisContext.cs new file mode 100644 index 0000000000000..f8e04dca55d63 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisContext.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralPropertySetAnalysisData = InterproceduralAnalysisData, PropertySetAnalysisContext, PropertySetAbstractValue>; + using PropertySetAnalysisData = DictionaryAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + internal sealed class PropertySetAnalysisContext : AbstractDataFlowAnalysisContext + { + private PropertySetAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralPropertySetAnalysisData? interproceduralAnalysisData, + ImmutableHashSet typeToTrackMetadataNames, + ConstructorMapper constructorMapper, + PropertyMapperCollection propertyMappers, + HazardousUsageEvaluatorCollection hazardousUsageEvaluators, + ImmutableDictionary<(INamedTypeSymbol, bool), string> hazardousUsageTypesToNames) + : base( + valueDomain, + wellKnownTypeProvider, + controlFlowGraph, + owningSymbol, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis, + predicateAnalysis: false, + exceptionPathsAnalysis: false, + copyAnalysisResult: null, + pointsToAnalysisResult, + valueContentAnalysisResult, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph, + interproceduralAnalysisData, + interproceduralAnalysisPredicate: null) + { + this.TypeToTrackMetadataNames = typeToTrackMetadataNames; + this.ConstructorMapper = constructorMapper; + this.PropertyMappers = propertyMappers; + this.HazardousUsageEvaluators = hazardousUsageEvaluators; + this.HazardousUsageTypesToNames = hazardousUsageTypesToNames; + } + + public static PropertySetAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ImmutableHashSet typeToTrackMetadataNames, + ConstructorMapper constructorMapper, + PropertyMapperCollection propertyMappers, + HazardousUsageEvaluatorCollection hazardousUsageEvaluators) + { + return new PropertySetAnalysisContext( + valueDomain, + wellKnownTypeProvider, + controlFlowGraph, + owningSymbol, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis, + pointsToAnalysisResult, + valueContentAnalysisResult, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph: null, + interproceduralAnalysisData: null, + typeToTrackMetadataNames: typeToTrackMetadataNames, + constructorMapper: constructorMapper, + propertyMappers: propertyMappers, + hazardousUsageEvaluators: hazardousUsageEvaluators, + hazardousUsageTypesToNames: hazardousUsageEvaluators.GetTypeToNameMapping(wellKnownTypeProvider)); + } + + public override PropertySetAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralPropertySetAnalysisData? interproceduralAnalysisData) + { + Debug.Assert(pointsToAnalysisResult != null); + Debug.Assert(copyAnalysisResult == null); + + return new PropertySetAnalysisContext( + ValueDomain, + WellKnownTypeProvider, + invokedCfg, + invokedMethod, + AnalyzerOptions, + InterproceduralAnalysisConfiguration, + PessimisticAnalysis, + pointsToAnalysisResult, + valueContentAnalysisResult, + TryGetOrComputeAnalysisResult, + ControlFlowGraph, + interproceduralAnalysisData, + this.TypeToTrackMetadataNames, + this.ConstructorMapper, + this.PropertyMappers, + this.HazardousUsageEvaluators, + this.HazardousUsageTypesToNames); + } + + /// + /// Metadata names of the types to track. + /// + public ImmutableHashSet TypeToTrackMetadataNames { get; } + + /// + /// How constructor invocations map to s. + /// + public ConstructorMapper ConstructorMapper { get; } + + /// + /// How property assignments map to . + /// + public PropertyMapperCollection PropertyMappers { get; } + + /// + /// When and how to evaluate s to for hazardous usages. + /// + public HazardousUsageEvaluatorCollection HazardousUsageEvaluators { get; } + + public ImmutableDictionary<(INamedTypeSymbol, bool), string> HazardousUsageTypesToNames { get; } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(TypeToTrackMetadataNames.GetHashCode()); + hashCode.Add(ConstructorMapper.GetHashCode()); + hashCode.Add(PropertyMappers.GetHashCode()); + hashCode.Add(HazardousUsageEvaluators.GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (PropertySetAnalysisContext)obj; + return TypeToTrackMetadataNames.GetHashCode() == other.TypeToTrackMetadataNames.GetHashCode() + && ConstructorMapper.GetHashCode() == other.ConstructorMapper.GetHashCode() + && PropertyMappers.GetHashCode() == other.PropertyMappers.GetHashCode() + && HazardousUsageEvaluators.GetHashCode() == other.HazardousUsageEvaluators.GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisResult.cs new file mode 100644 index 0000000000000..196e3245af831 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetAnalysisResult.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Analysis result from execution of on a control flow graph. + /// + internal sealed class PropertySetAnalysisResult : DataFlowAnalysisResult + { + public PropertySetAnalysisResult( + DataFlowAnalysisResult propertySetAnalysisResult, + ImmutableDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> hazardousUsages, + ImmutableHashSet visitedLocalFunctions, + ImmutableHashSet visitedLambdas) + : base(propertySetAnalysisResult) + { + this.HazardousUsages = hazardousUsages; + this.VisitedLocalFunctions = visitedLocalFunctions; + this.VisitedLambdas = visitedLambdas; + } + + // Method == null => return / initialization + public ImmutableDictionary<(Location Location, IMethodSymbol? Method), HazardousUsageEvaluationResult> HazardousUsages { get; } + + public ImmutableHashSet VisitedLocalFunctions { get; } + + public ImmutableHashSet VisitedLambdas { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetBlockAnalysisResult.cs new file mode 100644 index 0000000000000..565ab4c74de39 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetBlockAnalysisResult.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.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + using PropertySetAnalysisData = DictionaryAnalysisData; + + /// + /// Result from execution of on a basic block. + /// It stores s for each at the start and end of the basic block. + /// + internal class PropertySetBlockAnalysisResult : AbstractBlockAnalysisResult + { + public PropertySetBlockAnalysisResult(BasicBlock basicBlock, PropertySetAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + } + + public ImmutableDictionary Data { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetCallbacks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetCallbacks.cs new file mode 100644 index 0000000000000..d0f52aebfd77b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/PropertySetAnalysis/PropertySetCallbacks.cs @@ -0,0 +1,222 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.PropertySetAnalysis +{ + /// + /// Common callbacks for . + /// + internal static class PropertySetCallbacks + { + /// + /// A for flagging assigning null to a property. + /// + /// Value assigned to the property. + /// Flagged if null, Unflagged if not null, MaybeFlagged otherwise. + public static PropertySetAbstractValueKind FlagIfNull(PointsToAbstractValue pointsToAbstractValue) + { + return pointsToAbstractValue.NullState switch + { + NullAbstractValue.Null => PropertySetAbstractValueKind.Flagged, + + NullAbstractValue.NotNull => PropertySetAbstractValueKind.Unflagged, + + NullAbstractValue.MaybeNull => PropertySetAbstractValueKind.MaybeFlagged, + + _ => PropertySetAbstractValueKind.Unknown, + }; + } + + /// + /// Enumerates literal values to map to a property set abstract value. + /// + /// Abstract value containing the literal values to examine. + /// Predicate function to determine if a literal value is bad. + /// Mapped kind. + /// + /// All literal values are bad => Flagged + /// Some but not all literal are bad => MaybeFlagged + /// All literal values are known and none are bad => Unflagged + /// Otherwise => Unknown + /// + public static PropertySetAbstractValueKind EvaluateLiteralValues( + ValueContentAbstractValue valueContentAbstractValue, + Func badLiteralValuePredicate) + { + switch (valueContentAbstractValue.NonLiteralState) + { + case ValueContainsNonLiteralState.No: + if (valueContentAbstractValue.LiteralValues.IsEmpty) + { + return PropertySetAbstractValueKind.Unflagged; + } + + bool allValuesBad = true; + bool someValuesBad = false; + foreach (object? literalValue in valueContentAbstractValue.LiteralValues) + { + if (badLiteralValuePredicate(literalValue)) + { + someValuesBad = true; + } + else + { + allValuesBad = false; + } + + if (!allValuesBad && someValuesBad) + { + break; + } + } + + if (allValuesBad) + { + // We know all values are bad, so we can say Flagged. + return PropertySetAbstractValueKind.Flagged; + } + else if (someValuesBad) + { + // We know all values but some values are bad, so we can say MaybeFlagged. + return PropertySetAbstractValueKind.MaybeFlagged; + } + else + { + // We know all values are good, so we can say Unflagged. + return PropertySetAbstractValueKind.Unflagged; + } + + case ValueContainsNonLiteralState.Maybe: + if (valueContentAbstractValue.LiteralValues.Any(badLiteralValuePredicate)) + { + // We don't know all values but know some values are bad, so we can say MaybeFlagged. + return PropertySetAbstractValueKind.MaybeFlagged; + } + else + { + // We don't know all values but didn't find any bad value, so we can say who knows. + return PropertySetAbstractValueKind.Unknown; + } + + default: + return PropertySetAbstractValueKind.Unknown; + } + } + + /// + /// A for all properties flagged being hazardous, treating all + /// unknown as maybe flagged. + /// + /// PropertySetAbstract value. + /// If all properties are flagged, then flagged; if at least one property is unflagged, then unflagged; + /// otherwise (including all unknown) maybe flagged. + public static HazardousUsageEvaluationResult HazardousIfAllFlaggedOrAllUnknown( + PropertySetAbstractValue propertySetAbstractValue) + { + return HazardousIfAllFlagged(propertySetAbstractValue, assumeAllUnknownInsecure: true); + } + + /// + /// A for all properties flagged being hazardous, treating all + /// unknown as unflagged. + /// + /// PropertySetAbstract value. + /// If all properties are flagged, then flagged; if at least one property is unflagged, then unflagged; + /// otherwise (excluding all unknown) maybe flagged. + public static HazardousUsageEvaluationResult HazardousIfAllFlaggedAndAtLeastOneKnown( + PropertySetAbstractValue propertySetAbstractValue) + { + return HazardousIfAllFlagged(propertySetAbstractValue, assumeAllUnknownInsecure: false); + } + + /// + /// A for all properties flagged being hazardous, + /// treating all unknown as maybe flagged. + /// + /// Ignored. If your scenario cares about the method, don't use this. + /// PropertySetAbstract value. + /// If all properties are flagged, then flagged; if all properties are unflagged, then unflagged; otherwise + /// (including all unknown) maybe flagged. + [SuppressMessage("Usage", "CA1801", Justification = "Intentionally ignored; have to match delegate signature")] + [SuppressMessage("Usage", "IDE0060", Justification = "Intentionally ignored; have to match delegate signature")] + public static HazardousUsageEvaluationResult HazardousIfAllFlaggedOrAllUnknown( + IMethodSymbol methodSymbol, + PropertySetAbstractValue propertySetAbstractValue) + { + return HazardousIfAllFlagged(propertySetAbstractValue, assumeAllUnknownInsecure: true); + } + + /// + /// A for all properties flagged being hazardous, + /// treating all unknown as unflagged + /// + /// Ignored. If your scenario cares about the method, don't use this. + /// PropertySetAbstract value. + /// If all properties are flagged, then flagged; if all properties are unflagged, then unflagged; otherwise + /// (excluding all unknown) maybe flagged. + [SuppressMessage("Usage", "CA1801", Justification = "Intentionally ignored; have to match delegate signature")] + [SuppressMessage("Usage", "IDE0060", Justification = "Intentionally ignored; have to match delegate signature")] + public static HazardousUsageEvaluationResult HazardousIfAllFlaggedAndAtLeastOneKnown( + IMethodSymbol methodSymbol, + PropertySetAbstractValue propertySetAbstractValue) + { + return HazardousIfAllFlagged(propertySetAbstractValue, assumeAllUnknownInsecure: false); + } + + /// + /// A for all properties flagged being hazardous. + /// + /// PropertySetAbstract value. + /// If all properties are flagged, then flagged; if at least one property is unflagged, then unflagged; otherwise (including all unknown) maybe flagged. + private static HazardousUsageEvaluationResult HazardousIfAllFlagged( + PropertySetAbstractValue propertySetAbstractValue, + bool assumeAllUnknownInsecure) + { + if (propertySetAbstractValue.KnownValuesCount == 0) + { + // No known values implies all properties are PropertySetAbstractValueKind.Unknown. + return assumeAllUnknownInsecure + ? HazardousUsageEvaluationResult.MaybeFlagged + : HazardousUsageEvaluationResult.Unflagged; + } + + bool allFlagged = true; + bool atLeastOneUnflagged = false; + for (int i = 0; i < propertySetAbstractValue.KnownValuesCount; i++) + { + if (propertySetAbstractValue[i] != PropertySetAbstractValueKind.Flagged) + { + allFlagged = false; + } + + if (propertySetAbstractValue[i] == PropertySetAbstractValueKind.Unflagged) + { + atLeastOneUnflagged = true; + break; + } + } + + if (allFlagged) + { + return HazardousUsageEvaluationResult.Flagged; + } + else if (atLeastOneUnflagged) + { + return HazardousUsageEvaluationResult.Unflagged; + } + else + { + // Mix of flagged and unknown. + return HazardousUsageEvaluationResult.MaybeFlagged; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/AnySanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/AnySanitizers.cs new file mode 100644 index 0000000000000..7caee4e18bd82 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/AnySanitizers.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class AnySanitizers + { + /// + /// s for any tainted data sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static AnySanitizers() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemTextStringBuilder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: (string[]?)null, + sanitizingInstanceMethods: new[] { + "Clear", + }); + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/DllSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/DllSinks.cs new file mode 100644 index 0000000000000..2fa7adecdddce --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/DllSinks.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class DllSinks + { + /// + /// s for tainted data Dll sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static DllSinks() + { + var sinkInfosBuilder = PooledHashSet.GetInstance(); + + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemReflectionAssembly, + SinkKind.Dll, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("LoadFrom", new[] { "assemblyFile" } ), + ("Load", new[] { "assemblyString", "rawAssembly" } ), + ("LoadFile", new[] { "partialName" } ), + ("LoadModule", new[] { "moduleName" } ), + ("UnsafeLoadFrom", new[] { "assemblyFile" } ), + }); + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemAppDomain, + SinkKind.Dll, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("ExecuteAssembly", new[] { "assemblyFile" } ), + ("ExecuteAssemblyByName", new[] { "assemblyName" } ), + ("Load", new[] { "rawAssembly", "assemblyRef", "assemblyString", } ), + }); + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemWindowsAssemblyPart, + SinkKind.Dll, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("Load", new[] { "assemblyStream" } ), + }); + + SinkInfos = sinkInfosBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/FilePathInjectionSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/FilePathInjectionSinks.cs new file mode 100644 index 0000000000000..9357c5e74fba5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/FilePathInjectionSinks.cs @@ -0,0 +1,81 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class FilePathInjectionSinks + { + /// + /// s for tainted data file canonicalization sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static FilePathInjectionSinks() + { + PooledHashSet builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemIODirectory, + SinkKind.FilePathInjection, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Exists", new[] { "path" } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOFile, + SinkKind.FilePathInjection, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "AppendAllLines", new[] { "path" } ), + ( "AppendAllLinesAsync", new[] { "path" } ), + ( "AppendAllText", new[] { "path" } ), + ( "AppendAllTextAsync", new[] { "path" } ), + ( "AppendText", new[] { "path" } ), + ( "Copy", new[] { "sourceFileName", "destFileName" } ), + ( "Create", new[] { "path" } ), + ( "CreateText", new[] { "path" } ), + ( "Delete", new[] { "path" } ), + ( "Exists", new[] { "path" } ), + ( "Move", new[] { "sourceFileName", "destFileName" } ), + ( "Open", new[] { "path" } ), + ( "OpenRead", new[] { "path" } ), + ( "OpenText", new[] { "path" } ), + ( "OpenWrite", new[] { "path" } ), + ( "ReadAllBytes", new[] { "path" } ), + ( "ReadAllBytesAsync", new[] { "path" } ), + ( "ReadAllLines", new[] { "path" } ), + ( "ReadAllLinesAsync", new[] { "path" } ), + ( "ReadAllText", new[] { "path" } ), + ( "ReadAllTextAsync", new[] { "path" } ), + ( "ReadLines", new[] { "path" } ), + ( "WriteAllBytes", new[] { "path" } ), + ( "WriteAllBytesAsync", new[] { "path" } ), + ( "WriteAllLines", new[] { "path" } ), + ( "WriteAllLinesAsync", new[] { "path" } ), + ( "WriteAllText", new[] { "path" } ), + ( "WriteAllTextAsync", new[] { "path" } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOFileInfo, + SinkKind.FilePathInjection, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "CopyTo", new[] { "destFileName" } ), + ( "MoveTo", new[] { "destFileName" } ), + ( "Replace", new[] { "destinationFileName"} ), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedBytesSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedBytesSources.cs new file mode 100644 index 0000000000000..23060630a7ba4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedBytesSources.cs @@ -0,0 +1,105 @@ +// 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.Linq; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class HardcodedBytesSources + { + /// + /// s for hardcoded bytes tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Statically constructs. + /// + static HardcodedBytesSources() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSourceInfo( + WellKnownTypeNames.SystemConvert, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: new (MethodMatcher, ValueContentCheck[])[]{ + ( + (methodName, arguments) => + methodName == "FromBase64String", + new ValueContentCheck[]{ + (argumentPointsTos, argumentValueContents) => argumentValueContents.All(o => o.IsLiteralState), + } + ), + }); + builder.AddSourceInfoSpecifyingTaintedTargets( + WellKnownTypeNames.SystemTextEncoding, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: new (MethodMatcher, (ValueContentCheck, string)[])[]{ + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Count() == 5 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (ValueContentCheck, string)[]{ + ( + (argumentPointsTos, argumentValueContents) => + argumentValueContents[0].IsLiteralState, + "chars" + ), + } + ), + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Count() == 1 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (ValueContentCheck, string)[]{ + ( + (argumentPointsTos, argumentValueContents) => + argumentValueContents[0].IsLiteralState, + TaintedTargetValue.Return + ), + } + ), + }, + transferMethods: new (MethodMatcher, (string, string)[])[]{ + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Count() == 5 && + arguments[0].Parameter?.Type is IArrayTypeSymbol arrayTypeSymbol && + arrayTypeSymbol.ElementType.SpecialType == SpecialType.System_Char, + new (string, string)[]{ + ("chars", "bytes"), + } + ), + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Count() == 5 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (string, string)[]{ + ("chars", "bytes"), + } + ), + }); + builder.AddSourceInfo( + WellKnownTypeNames.SystemByte, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: null, + taintConstantArray: true); + + SourceInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSinks.cs new file mode 100644 index 0000000000000..0d00a9be371a7 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSinks.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class HardcodedCertificateSinks + { + /// + /// s for tainted data process symmetric algorithm sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static HardcodedCertificateSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemSecurityCryptographyX509CertificatesX509Certificate, + SinkKind.HardcodedCertificate, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: null, + sinkMethodParameters: new[] { + ( ".ctor", new[] { "rawData", "data" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemSecurityCryptographyX509CertificatesX509Certificate2, + SinkKind.HardcodedCertificate, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: null, + sinkMethodParameters: new[] { + ( ".ctor", new[] { "rawData", "data" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSources.cs new file mode 100644 index 0000000000000..360363ce0aea2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedCertificateSources.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class HardcodedCertificateSources + { + /// + /// s for hardcoded certificate tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Statically constructs. + /// + static HardcodedCertificateSources() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSourceInfoSpecifyingTaintedTargets( + WellKnownTypeNames.SystemIOFile, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: null, + transferMethods: new (MethodMatcher, (string, string)[])[]{ + ( + (methodName, arguments) => + methodName == "WriteAllBytes", + new (string, string)[]{ + ("bytes", "path"), + } + ), + }); + + SourceInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedEncryptionKeySinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedEncryptionKeySinks.cs new file mode 100644 index 0000000000000..a90ae19056628 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedEncryptionKeySinks.cs @@ -0,0 +1,55 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class HardcodedEncryptionKeySinks + { + /// + /// s for tainted data process symmetric algorithm sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static HardcodedEncryptionKeySinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemSecurityCryptographySymmetricAlgorithm, + SinkKind.HardcodedEncryptionKey, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Key", + }, + sinkMethodParameters: new[] { + ( "CreateEncryptor", new[] { "rgbKey" }), + ( "CreateDecryptor", new[] { "rgbKey" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemSecurityCryptographyAesGcm, + SinkKind.HardcodedEncryptionKey, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( ".ctor", new[] { "key" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemSecurityCryptographyAesCcm, + SinkKind.HardcodedEncryptionKey, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( ".ctor", new[] { "key" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedSymmetricAlgorithmKeysSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedSymmetricAlgorithmKeysSources.cs new file mode 100644 index 0000000000000..703af4f7815bd --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/HardcodedSymmetricAlgorithmKeysSources.cs @@ -0,0 +1,144 @@ +// 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.Linq; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class HardcodedSymmetricAlgorithmKeysSources + { + /// + /// s for hardcoded symmetric algorithm cryptographic keys tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } = BuildSources(); + + /// + /// Statically constructs. + /// + private static ImmutableHashSet BuildSources() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSourceInfo( + WellKnownTypeNames.SystemConvert, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: new (MethodMatcher, ValueContentCheck[])[] + { + ( + (methodName, _) => + methodName == "FromBase64String", + new ValueContentCheck[] + { + (argumentPointsTos, argumentValueContents) => + argumentValueContents.Length == 1 + && argumentValueContents[0].LiteralValues.Any( + (object? v) => v is string s && s.Length % 4 == 0 && IsLegalKeySize(s.Length * 3 / 4)) + } + ), + }); + + // Consider also checking the resulting lengths of Encoding.GetBytes() with IsLegalKeySize(). ValueContentCheck + // would need to be aware of concrete type. + builder.AddSourceInfoSpecifyingTaintedTargets( + WellKnownTypeNames.SystemTextEncoding, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: new (MethodMatcher, (ValueContentCheck, string)[])[] + { + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Length == 5 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (ValueContentCheck, string)[] + { + ( + (argumentPointsTos, argumentValueContents) => argumentValueContents[0].IsLiteralState, + "chars" + ), + } + ), + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Length == 1 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (ValueContentCheck, string)[] + { + ( + (argumentPointsTos, argumentValueContents) => argumentValueContents[0].IsLiteralState, + TaintedTargetValue.Return + ), + } + ), + }, + transferMethods: new (MethodMatcher, (string, string)[])[] + { + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Length == 5 && + arguments[0].Parameter?.Type is IArrayTypeSymbol arrayTypeSymbol && + arrayTypeSymbol.ElementType.SpecialType == SpecialType.System_Char, + new (string, string)[] { ("chars", "bytes"), } + ), + ( + (methodName, arguments) => + methodName == "GetBytes" && + arguments.Length == 5 && + arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String, + new (string, string)[] { ("chars", "bytes"), } + ), + }); + builder.AddSourceInfo( + WellKnownTypeNames.SystemByte, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: null, + taintConstantArray: true, + constantArrayLengthMatcher: IsLegalKeySize); + + return builder.ToImmutableAndFree(); + } + + private static bool IsLegalKeySize(int byteCount) + { + /* + These classes' keys are sinks: + System.Security.Cryptography.AesCng + System.Security.Cryptography.AesCryptoServiceProvider + System.Security.Cryptography.AesManaged + System.Security.Cryptography.DESCryptoServiceProvider + System.Security.Cryptography.RC2CryptoServiceProvider + System.Security.Cryptography.RijndaelManaged + System.Security.Cryptography.TripleDESCng + System.Security.Cryptography.TripleDESCryptoServiceProvider + + LegalKeySizes in bits: + MinSize MaxSize SkipSize + ------- ------- -------- + 128 256 64 + 128 256 64 + 128 256 64 + 64 64 0 + 40 128 8 + 128 256 64 + 128 192 64 + 128 192 64 + + So the set of legal sizes in bytes is: + 5-16, 24, 32 + */ + + return byteCount is (>= 5 and <= 16) or 24 or 32; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ITaintedDataInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ITaintedDataInfo.cs new file mode 100644 index 0000000000000..f1d245a1a0e4b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ITaintedDataInfo.cs @@ -0,0 +1,41 @@ +// 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; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Interface for a tainted data source, sanitizer, or sink information for one concrete type or interface. + /// + internal interface ITaintedDataInfo + { + /// + /// Qualified name of the type. + /// + string FullTypeName { get; } + + /// + /// Qualified names of the optional dependency types. + /// + ImmutableArray DependencyFullTypeNames { get; } + + /// + /// Indicates that the type is an interface, rather than a concrete type. + /// + bool IsInterface { get; } + + /// + /// Indicates that is required. + /// + bool RequiresParameterReferenceAnalysis { get; } + + /// + /// Indicates that this info uses s. + /// + bool RequiresValueContentAnalysis { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/InformationDisclosureSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/InformationDisclosureSources.cs new file mode 100644 index 0000000000000..b6d75a5d8e0f9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/InformationDisclosureSources.cs @@ -0,0 +1,38 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class InformationDisclosureSources + { + /// + /// s for information disclosure tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Statically constructs. + /// + static InformationDisclosureSources() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSourceInfo( + WellKnownTypeNames.SystemException, + isInterface: false, + taintedProperties: new[] { + "Message", + "StackTrace", + }, + taintedMethods: new[] { + "ToString", + }); + + SourceInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSanitizers.cs new file mode 100644 index 0000000000000..ae255b81cd2cc --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSanitizers.cs @@ -0,0 +1,34 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class LdapSanitizers + { + /// + /// s for LDAP injection sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static LdapSanitizers() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "LdapDistinguishedNameEncode", + "LdapEncode", + "LdapFilterEncode", + }); + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSinks.cs new file mode 100644 index 0000000000000..2d8d3df2ec256 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/LdapSinks.cs @@ -0,0 +1,52 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class LdapSinks + { + /// + /// s for tainted data LDAP injection sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static LdapSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemDirectoryServicesActiveDirectoryADSearcher, + SinkKind.Ldap, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new[] { + "Filter", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemDirectoryServicesDirectorySearcher, + SinkKind.Ldap, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new[] { + "Filter", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemDirectoryDirectoryEntry, + SinkKind.Ldap, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + (".ctor", new[] { "path", "adsObject" } ), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PooledHashSetExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PooledHashSetExtensions.cs new file mode 100644 index 0000000000000..252978d5059f1 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PooledHashSetExtensions.cs @@ -0,0 +1,318 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class PooledHashSetExtensions + { + // Just to make hardcoding SinkInfos more convenient. + public static void AddSinkInfo( + this PooledHashSet builder, + string fullTypeName, + SinkKind sinkKind, + bool isInterface, + bool isAnyStringParameterInConstructorASink, + IEnumerable? sinkProperties, + IEnumerable<(string Method, string[] Parameters)>? sinkMethodParameters) + { + builder.AddSinkInfo( + fullTypeName, + new[] { sinkKind }, + isInterface, + isAnyStringParameterInConstructorASink, + sinkProperties, + sinkMethodParameters); + } + + // Just to make hardcoding SinkInfos more convenient. + public static void AddSinkInfo( + this PooledHashSet builder, + string fullTypeName, + IEnumerable sinkKinds, + bool isInterface, + bool isAnyStringParameterInConstructorASink, + IEnumerable? sinkProperties, + IEnumerable<(string Method, string[] Parameters)>? sinkMethodParameters) + { + SinkInfo sinkInfo = new SinkInfo( + fullTypeName, + sinkKinds.ToImmutableHashSet(), + isInterface, + isAnyStringParameterInConstructorASink, + sinkProperties: sinkProperties?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty, + sinkMethodParameters: + sinkMethodParameters + ?.Select(o => new KeyValuePair>(o.Method, o.Parameters.ToImmutableHashSet())) + ?.ToImmutableDictionary(StringComparer.Ordinal) + ?? ImmutableDictionary>.Empty); + builder.Add(sinkInfo); + } + + public static void AddSourceInfo( + this PooledHashSet builder, + string fullTypeName, + IEnumerable taintedArguments) + { + SourceInfo metadata = new SourceInfo( + fullTypeName, + isInterface: false, + taintedProperties: ImmutableHashSet.Empty, + transferProperties: ImmutableHashSet.Empty, + taintedArguments: + taintedArguments.ToImmutableHashSet(), + taintedMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet)>.Empty, + taintedMethodsNeedsPointsToAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)>.Empty, + taintedMethodsNeedsValueContentAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)>.Empty, + transferMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + taintConstantArray: false, + constantArrayLengthMatcher: null); + builder.Add(metadata); + } + + public static void AddSourceInfo( + this PooledHashSet builder, + ImmutableArray dependencyFullTypeNames, + string fullTypeName, + IEnumerable taintedArguments) + { + SourceInfo metadata = new SourceInfo( + fullTypeName, + dependencyFullTypeNames: dependencyFullTypeNames, + isInterface: false, + taintedProperties: ImmutableHashSet.Empty, + transferProperties: ImmutableHashSet.Empty, + taintedArguments: + taintedArguments.ToImmutableHashSet(), + taintedMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet)>.Empty, + taintedMethodsNeedsPointsToAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)>.Empty, + taintedMethodsNeedsValueContentAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)>.Empty, + transferMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + taintConstantArray: false, + constantArrayLengthMatcher: null); + builder.Add(metadata); + } + + // Just to make hardcoding SourceInfos more convenient. + public static void AddSourceInfo( + this PooledHashSet builder, + string fullTypeName, + bool isInterface, + IEnumerable? taintedProperties, + IEnumerable? taintedMethods) + { + SourceInfo metadata = new SourceInfo( + fullTypeName, + isInterface: isInterface, + taintedProperties: taintedProperties?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty, + transferProperties: ImmutableHashSet.Empty, + taintedArguments: + ImmutableHashSet.Empty, + taintedMethods: + taintedMethods + ?.Select)>(o => + ( + (methodName, arguments) => methodName == o, + ImmutableHashSet.Empty.Add(TaintedTargetValue.Return) + )) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet)>.Empty, + taintedMethodsNeedsPointsToAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)>.Empty, + taintedMethodsNeedsValueContentAnalysis: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)>.Empty, + transferMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + taintConstantArray: false, + constantArrayLengthMatcher: null); + builder.Add(metadata); + } + + /// + /// Add SourceInfos which needs extra PointsToAnalysis checks or ValueContentAnalysis checks and specifies the tainted targets explicitly for each check. + /// The tainted targets can be parameter names of the method, or the return value. + /// + /// + /// + /// + /// Specify the check functions and tainted targets for methods which only need PointsToAnalysis check. + /// Specify the check functions and tainted targets for methods which need ValueContentAnalysis check. + /// + public static void AddSourceInfoSpecifyingTaintedTargets( + this PooledHashSet builder, + string fullTypeName, + bool isInterface, + IEnumerable? taintedProperties, + IEnumerable<(MethodMatcher methodMatcher, (PointsToCheck pointsToCheck, string taintedTarget)[] pointsToChecksAndTargets)>? taintedMethodsNeedsPointsToAnalysis, + IEnumerable<(MethodMatcher methodMatcher, (ValueContentCheck valueContentCheck, string taintedTarget)[] valueContentChecksAndTargets)>? taintedMethodsNeedsValueContentAnalysis, + IEnumerable<(MethodMatcher methodMatcher, (string str, string taintedTargets)[] valueContentChecksAndTargets)>? transferMethods, + IEnumerable? transferProperties = null, + bool taintConstantArray = false, + ArrayLengthMatcher? constantArrayLengthMatcher = null) + { + SourceInfo metadata = new SourceInfo( + fullTypeName, + isInterface: isInterface, + taintedProperties: taintedProperties?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty, + transferProperties: transferProperties?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty, + taintedArguments: + ImmutableHashSet.Empty, + taintedMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet)>.Empty, + taintedMethodsNeedsPointsToAnalysis: + taintedMethodsNeedsPointsToAnalysis?.Select(o => + ( + o.methodMatcher, + o.pointsToChecksAndTargets?.ToImmutableHashSet() + ?? ImmutableHashSet<(PointsToCheck, string)>.Empty + )) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)>.Empty, + taintedMethodsNeedsValueContentAnalysis: + taintedMethodsNeedsValueContentAnalysis?.Select(o => + ( + o.methodMatcher, + o.valueContentChecksAndTargets?.ToImmutableHashSet() + ?? ImmutableHashSet<(ValueContentCheck, string)>.Empty + )) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)>.Empty, + transferMethods: + transferMethods + ?.Select(o => + ( + o.methodMatcher, + o.valueContentChecksAndTargets + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(string, string)>.Empty)) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + taintConstantArray: taintConstantArray, + constantArrayLengthMatcher: constantArrayLengthMatcher); + builder.Add(metadata); + } + + /// + /// Add SourceInfos which needs PointsToAnalysis checks or ValueContentAnalysis checks and each check taints return value by default. + /// + public static void AddSourceInfo( + this PooledHashSet builder, + string fullTypeName, + bool isInterface, + IEnumerable? taintedProperties, + IEnumerable<(MethodMatcher methodMatcher, PointsToCheck[] pointsToChecks)>? taintedMethodsNeedsPointsToAnalysis, + IEnumerable<(MethodMatcher methodMatcher, ValueContentCheck[] valueContentChecks)>? taintedMethodsNeedsValueContentAnalysis, + bool taintConstantArray = false, + ArrayLengthMatcher? constantArrayLengthMatcher = null) + { + SourceInfo metadata = new SourceInfo( + fullTypeName, + isInterface: isInterface, + taintedProperties: taintedProperties?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty, + transferProperties: ImmutableHashSet.Empty, + taintedArguments: + ImmutableHashSet.Empty, + taintedMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet)>.Empty, + taintedMethodsNeedsPointsToAnalysis: + taintedMethodsNeedsPointsToAnalysis?.Select(o => + ( + o.methodMatcher, + o.pointsToChecks + ?.Select(s => (s, TaintedTargetValue.Return)) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(PointsToCheck, string)>.Empty + )) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)>.Empty, + taintedMethodsNeedsValueContentAnalysis: + taintedMethodsNeedsValueContentAnalysis?.Select(o => + ( + o.methodMatcher, + o.valueContentChecks + ?.Select(s => (s, TaintedTargetValue.Return)) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(ValueContentCheck, string)>.Empty + )) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)>.Empty, + transferMethods: + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + taintConstantArray: taintConstantArray, + constantArrayLengthMatcher: constantArrayLengthMatcher); + builder.Add(metadata); + } + + // Just to make hardcoding SanitizerInfos more convenient. + public static void AddSanitizerInfo( + this PooledHashSet builder, + string fullTypeName, + bool isInterface, + bool isConstructorSanitizing, + IEnumerable? sanitizingMethods, + IEnumerable? sanitizingInstanceMethods = null) + { + SanitizerInfo info = new SanitizerInfo( + fullTypeName, + isInterface: isInterface, + isConstructorSanitizing: isConstructorSanitizing, + sanitizingMethods: + sanitizingMethods + ?.Select)>(o => + ( + (methodName, arguments) => methodName == o, + ImmutableHashSet<(string, string)>.Empty)) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + sanitizingInstanceMethods: sanitizingInstanceMethods?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty); + builder.Add(info); + } + + public static void AddSanitizerInfo( + this PooledHashSet builder, + string fullTypeName, + bool isInterface, + bool isConstructorSanitizing, + IEnumerable<(MethodMatcher methodMatcher, (string str, string sanitizedTargets)[] taintedArgumentToSanized)>? sanitizingMethods, + IEnumerable? sanitizingInstanceMethods = null) + { + SanitizerInfo info = new SanitizerInfo( + fullTypeName, + isInterface: isInterface, + isConstructorSanitizing: isConstructorSanitizing, + sanitizingMethods: + sanitizingMethods + ?.Select(o => + ( + o.methodMatcher, + o.taintedArgumentToSanized + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(string, string)>.Empty)) + ?.ToImmutableHashSet() + ?? ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)>.Empty, + sanitizingInstanceMethods: sanitizingInstanceMethods?.ToImmutableHashSet(StringComparer.Ordinal) + ?? ImmutableHashSet.Empty); + builder.Add(info); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PrimitiveTypeConverterSanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PrimitiveTypeConverterSanitizers.cs new file mode 100644 index 0000000000000..f0b8b9f35f3ae --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/PrimitiveTypeConverterSanitizers.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.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class PrimitiveTypeConverterSanitizers + { + /// + /// s for primitive type conversion tainted data sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static PrimitiveTypeConverterSanitizers() + { + var builder = PooledHashSet.GetInstance(); + + string[] parseMethods = new string[] { "Parse", "TryParse" }; + + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemBoolean, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemByte, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemChar, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemInt16, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemInt32, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemInt64, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemSingle, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemDouble, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemDecimal, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemDateTime, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemTimeSpan, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: parseMethods); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemNumber, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new string[] { + "ParseInt32", + "ParseInt64", + "TryParseInt32", + "TryParseInt64" + }); + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ProcessCommandSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ProcessCommandSinks.cs new file mode 100644 index 0000000000000..57432561fd708 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ProcessCommandSinks.cs @@ -0,0 +1,45 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class ProcessCommandSinks + { + /// + /// s for tainted data process command sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static ProcessCommandSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemDiagnosticsProcess, + SinkKind.ProcessCommand, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Start", new[] { "fileName", "arguments", } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemDiagnosticsProcessStartInfo, + SinkKind.ProcessCommand, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new[] { + "ArgumentList", + "Arguments", + "FileName", + }, + sinkMethodParameters: null); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RedirectSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RedirectSinks.cs new file mode 100644 index 0000000000000..784a0bd0a8591 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RedirectSinks.cs @@ -0,0 +1,49 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class RedirectSinks + { + /// + /// s for tainted data Redirect injection sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static RedirectSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebHttpResponse, + SinkKind.Redirect, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { "RedirectLocation" }, + sinkMethodParameters: new[] { + ( "Redirect", new[] { "url" }), + ( "RedirectPermanent", new[] { "url" }), + ( "RedirectToRoute", new[] { "routeName" }), + ( "RedirectToRoutePermanent", new[] { "routeName" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebHttpResponseBase, + SinkKind.Redirect, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { "RedirectLocation" }, + sinkMethodParameters: new[] { + ( "Redirect", new[] { "url" }), + ( "RedirectPermanent", new[] { "url" }), + ( "RedirectToRoute", new[] { "routeName" }), + ( "RedirectToRoutePermanent", new[] { "routeName" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RegexSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RegexSinks.cs new file mode 100644 index 0000000000000..14be75bd125ac --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/RegexSinks.cs @@ -0,0 +1,38 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class RegexSinks + { + /// + /// s for tainted data process command sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static RegexSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemTextRegularExpressionsRegex, + SinkKind.Regex, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "IsMatch", new[] { "pattern" }), + ( "Match", new[] { "pattern" }), + ( "Matches", new[] { "pattern" }), + ( "Replace", new[] { "pattern" }), + ( "Split", new[] { "pattern" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SanitizerInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SanitizerInfo.cs new file mode 100644 index 0000000000000..1415b6864c72b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SanitizerInfo.cs @@ -0,0 +1,109 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Info for a tainted data sanitizer type, which makes tainted data untainted. + /// + internal sealed class SanitizerInfo : ITaintedDataInfo, IEquatable + { + public SanitizerInfo( + string fullTypeName, + bool isInterface, + bool isConstructorSanitizing, + ImmutableHashSet<(MethodMatcher methodMatcher, ImmutableHashSet<(string IfTaintedParameter, string ThenUnTaintedTarget)>)> sanitizingMethods, + ImmutableHashSet sanitizingInstanceMethods) + { + FullTypeName = fullTypeName ?? throw new ArgumentNullException(nameof(fullTypeName)); + IsInterface = isInterface; + IsConstructorSanitizing = isConstructorSanitizing; + SanitizingMethods = sanitizingMethods ?? throw new ArgumentNullException(nameof(sanitizingMethods)); + SanitizingInstanceMethods = sanitizingInstanceMethods ?? throw new ArgumentNullException(nameof(sanitizingInstanceMethods)); + } + + /// + /// Full type name of the...type (namespace + type). + /// + public string FullTypeName { get; } + + /// + /// Indicates that this sanitizer type is an interface. + /// + public bool IsInterface { get; } + + /// + /// Indicates that any tainted data entering a constructor becomes untainted. + /// + public bool IsConstructorSanitizing { get; } + + /// + /// Methods that untaint tainted data. + /// + /// + /// MethodMatcher determines if the outermost tuple applies, based on the method names and arguments. + /// (IfTaintedParameter, ThenUnTaintedTarget) determines if the ThenUnTaintedTarget is untainted, based on if the IfTaintedParameter is tainted. + /// + /// Example: + /// ( + /// (methodName, argumentOperations) => methodName == "Bar", // MethodMatcher + /// { + /// ("a", "b") + /// } + /// ) + /// + /// will treat the parameter "b" as untainted when parameter "a" is tainted of the "Bar" method. + /// + public ImmutableHashSet<(MethodMatcher MethodMatcher, ImmutableHashSet<(string IfTaintedParameter, string ThenUnTaintedTarget)>)> SanitizingMethods { get; } + + /// + /// Methods that untaint tainted instance. + /// + public ImmutableHashSet SanitizingInstanceMethods { get; } + + /// + /// Indicates that this uses s. + /// + public bool RequiresValueContentAnalysis => false; + + /// + /// Indicates that is required. + /// + public bool RequiresParameterReferenceAnalysis => false; + + /// + /// Qualified names of the optional dependency types. + /// + public ImmutableArray DependencyFullTypeNames => ImmutableArray.Empty; + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(this.SanitizingMethods, ref hashCode); + HashUtilities.Combine(this.SanitizingInstanceMethods, ref hashCode); + hashCode.Add(StringComparer.Ordinal.GetHashCode(this.FullTypeName)); + hashCode.Add(this.IsConstructorSanitizing.GetHashCode()); + return hashCode.ToHashCode(); + } + + public override bool Equals(object obj) + { + return obj is SanitizerInfo other && this.Equals(other); + } + + public bool Equals(SanitizerInfo other) + { + return other != null + && this.FullTypeName == other.FullTypeName + && this.IsConstructorSanitizing == other.IsConstructorSanitizing + && this.SanitizingMethods == other.SanitizingMethods + && this.SanitizingInstanceMethods == other.SanitizingInstanceMethods; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkInfo.cs new file mode 100644 index 0000000000000..f1c49cd2eb827 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkInfo.cs @@ -0,0 +1,101 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Info for a tainted data sink type. + /// + /// It's bad if tainted data reaches a sink. + internal sealed class SinkInfo : ITaintedDataInfo, IEquatable + { + public SinkInfo(string fullTypeName, ImmutableHashSet sinkKinds, bool isInterface, bool isAnyStringParameterInConstructorASink, ImmutableHashSet sinkProperties, ImmutableDictionary> sinkMethodParameters) + { + FullTypeName = fullTypeName ?? throw new ArgumentNullException(nameof(fullTypeName)); + SinkKinds = sinkKinds ?? throw new ArgumentNullException(nameof(sinkKinds)); + IsInterface = isInterface; + IsAnyStringParameterInConstructorASink = isAnyStringParameterInConstructorASink; + SinkProperties = sinkProperties ?? throw new ArgumentNullException(nameof(sinkProperties)); + SinkMethodParameters = sinkMethodParameters ?? throw new ArgumentNullException(nameof(sinkMethodParameters)); + } + + /// + /// Full name of the type that can lead to sinks. + /// + public string FullTypeName { get; } + + /// + /// Type of sink. + /// + public ImmutableHashSet SinkKinds { get; } + + /// + /// Indicates this sink type is an interface. + /// + public bool IsInterface { get; } + + /// + /// Indicates that any string parameter in the constructor is a sink. + /// + public bool IsAnyStringParameterInConstructorASink { get; } + + /// + /// Set of properties on the type that are sinks. + /// + public ImmutableHashSet SinkProperties { get; } + + /// + /// Mapping of method name to parameter names that are sinks. + /// + public ImmutableDictionary> SinkMethodParameters { get; } + + /// + /// Indicates that this uses s. + /// + public bool RequiresValueContentAnalysis => false; + + /// + /// Indicates that is required. + /// + public bool RequiresParameterReferenceAnalysis => false; + + /// + /// Qualified names of the optional dependency types. + /// + public ImmutableArray DependencyFullTypeNames => ImmutableArray.Empty; + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(this.SinkProperties, ref hashCode); + HashUtilities.Combine(this.SinkMethodParameters, ref hashCode); + hashCode.Add(StringComparer.Ordinal.GetHashCode(this.FullTypeName)); + HashUtilities.Combine(this.SinkKinds, ref hashCode); + hashCode.Add(this.IsInterface.GetHashCode()); + hashCode.Add(this.IsAnyStringParameterInConstructorASink.GetHashCode()); + return hashCode.ToHashCode(); + } + + public override bool Equals(object obj) + { + return obj is SinkInfo other && this.Equals(other); + } + + public bool Equals(SinkInfo other) + { + return other != null + && this.FullTypeName == other.FullTypeName + && this.SinkKinds == other.SinkKinds + && this.IsInterface == other.IsInterface + && this.IsAnyStringParameterInConstructorASink == other.IsAnyStringParameterInConstructorASink + && this.SinkProperties == other.SinkProperties + && this.SinkMethodParameters == other.SinkMethodParameters; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkKind.cs new file mode 100644 index 0000000000000..53822db7a21fd --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SinkKind.cs @@ -0,0 +1,25 @@ +// 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 Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + public enum SinkKind + { + Sql, + Dll, + InformationDisclosure, + Xss, + FilePathInjection, + ProcessCommand, + Regex, + Ldap, + Redirect, + XPath, + Xml, + Xaml, + ZipSlip, + HardcodedEncryptionKey, + HardcodedCertificate, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SourceInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SourceInfo.cs new file mode 100644 index 0000000000000..259a8982ca589 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SourceInfo.cs @@ -0,0 +1,231 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal delegate bool PointsToCheck(ImmutableArray pointsTos); + internal delegate bool ValueContentCheck(ImmutableArray pointsTos, ImmutableArray valueContents); + internal delegate bool MethodMatcher(string methodName, ImmutableArray arguments); + internal delegate bool ParameterMatcher(IParameterSymbol parameter, WellKnownTypeProvider wellKnownTypeProvider); + internal delegate bool ArrayLengthMatcher(int length); + + /// + /// Info for tainted data sources, which generate tainted data. + /// + internal sealed class SourceInfo : ITaintedDataInfo, IEquatable + { + /// + /// Constructs. + /// + /// Full type name of the...type (namespace + type). + /// Properties that generate tainted data. + /// Method's arguments that are tainted sources. + /// Methods that generate tainted data and whose arguments don't need extra analysis. + /// Methods that generate tainted data and whose arguments don't need extra value content analysis. + /// Methods that generate tainted data and whose arguments need extra value content analysis and points to analysis. + /// Properties that transfer taint to `this` on assignment. + /// Methods that could taint another argument when one of its argument is tainted. + /// Indicates that constant arrays of the type should be tainted. + /// Delegate to determine if a constant array's length indicates the value should be tainted. + /// Full type names of the optional dependency/referenced types that should be resolved + public SourceInfo( + string fullTypeName, + bool isInterface, + ImmutableHashSet taintedProperties, + ImmutableHashSet taintedArguments, + ImmutableHashSet<(MethodMatcher, ImmutableHashSet)> taintedMethods, + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(PointsToCheck, string)>)> taintedMethodsNeedsPointsToAnalysis, + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(ValueContentCheck, string)>)> taintedMethodsNeedsValueContentAnalysis, + ImmutableHashSet transferProperties, + ImmutableHashSet<(MethodMatcher, ImmutableHashSet<(string, string)>)> transferMethods, + bool taintConstantArray, + ArrayLengthMatcher? constantArrayLengthMatcher, + ImmutableArray? dependencyFullTypeNames = null) + { + FullTypeName = fullTypeName ?? throw new ArgumentNullException(nameof(fullTypeName)); + IsInterface = isInterface; + TaintedProperties = taintedProperties ?? throw new ArgumentNullException(nameof(taintedProperties)); + TaintedArguments = taintedArguments ?? throw new ArgumentNullException(nameof(taintedArguments)); + TaintedMethods = taintedMethods ?? throw new ArgumentNullException(nameof(taintedMethods)); + TaintedMethodsNeedsPointsToAnalysis = taintedMethodsNeedsPointsToAnalysis ?? throw new ArgumentNullException(nameof(taintedMethodsNeedsPointsToAnalysis)); + TaintedMethodsNeedsValueContentAnalysis = taintedMethodsNeedsValueContentAnalysis ?? throw new ArgumentNullException(nameof(taintedMethodsNeedsValueContentAnalysis)); + TransferProperties = transferProperties ?? throw new ArgumentNullException(nameof(transferProperties)); + TransferMethods = transferMethods ?? throw new ArgumentNullException(nameof(transferMethods)); + TaintConstantArray = taintConstantArray; + ConstantArrayLengthMatcher = constantArrayLengthMatcher; + if (!taintConstantArray && constantArrayLengthMatcher != null) + { + throw new ArgumentException("Can't specify constantArrayLengthMatcher unless taintConstantArray is true"); + } + + DependencyFullTypeNames = dependencyFullTypeNames ?? ImmutableArray.Empty; + } + + /// + /// Full type name of the...type (namespace + type). + /// + public string FullTypeName { get; } + + /// + /// Full type names of the optional dependency/referenced types that should be resolved. + /// + public ImmutableArray DependencyFullTypeNames { get; } + + /// + /// Indicates this type is an interface. + /// + public bool IsInterface { get; } + + /// + /// Properties that transfer taint to `this` on assignment. + /// + public ImmutableHashSet TransferProperties { get; } + + /// + /// Properties that generate tainted data. + /// + public ImmutableHashSet TaintedProperties { get; } + + /// + /// Methods that generate tainted data. + /// TaintedTarget is the tainted target (arguments / return value). + /// + public ImmutableHashSet<(MethodMatcher MethodMatcher, ImmutableHashSet TaintedTargets)> TaintedMethods { get; } + + /// + /// Arguments that generate tainted data. + /// + public ImmutableHashSet TaintedArguments { get; } + + /// + /// Methods that generate tainted data and whose arguments don't need extra value content analysis. + /// + /// + /// MethodMatcher determines if the outermost tuple applies, based on the method names and arguments. + /// PointsToCheck determines if the inner tuple applies, based on the method invocation's arguments PointsToAbstractValues. + /// TaintedTarget is the tainted target (arguments / return value). + /// + /// Example: + /// ( + /// (methodName, argumentOperations) => methodName == "Bar", // MethodMatcher + /// { + /// ( + /// (pointsTos) => true, // PointsToCheck + /// TaintedTargetValue.Return // TaintedTarget + /// ) + /// } + /// ) + /// + /// will treat any invocation of the "Bar" method's return value as tainted. + /// + public ImmutableHashSet<(MethodMatcher MethodMatcher, ImmutableHashSet<(PointsToCheck PointsToCheck, string TaintedTarget)>)> TaintedMethodsNeedsPointsToAnalysis { get; } + + /// + /// Methods that generate tainted data and whose arguments need extra value content analysis and points to analysis. + /// + /// + /// MethodMatcher determines if the outermost tuple applies, based on the method names and arguments. + /// ValueContentCheck determines if the inner tuple applies, based on the method invocation's arguments PointsToAbstractValues and ValueContentAbstractValues. + /// TaintedTarget is the tainted target (arguments / return value). + /// + /// Example: + /// ( + /// (methodName, argumentOperations) => methodName == "Bar", // MethodMatcher + /// { + /// ( + /// (pointsTos, valueContents) => true, // ValueContentCheck + /// TaintedTargetValue.Return // TaintedTarget + /// ) + /// } + /// ) + /// + /// will treat any invocation of the "Bar" method's return value as tainted. + /// + public ImmutableHashSet<(MethodMatcher MethodMatcher, ImmutableHashSet<(ValueContentCheck ValueContentCheck, string TaintedTarget)>)> TaintedMethodsNeedsValueContentAnalysis { get; } + + /// + /// Methods that could taint another argument when one of its argument is tainted. + /// + /// + /// MethodMatcher determines if the outermost tuple applies, based on the method names and arguments. + /// (IfTaintedParameter, ThenTaintedTarget) determines if the ThenTaintedTarget is tainted, based on if the IfTaintedParameter is tainted. + /// + /// Example: + /// ( + /// (methodName, argumentOperations) => methodName == "Bar", // MethodMatcher + /// { + /// ("a", "b") + /// } + /// ) + /// + /// will treat the parameter "b" as tainted when parameter "a" is tainted of the "Bar" method. + /// + public ImmutableHashSet<(MethodMatcher MethodMatcher, ImmutableHashSet<(string IfTaintedParameter, string ThenTaintedTarget)>)> TransferMethods { get; } + + /// + /// Indicates arrays initialized with constant values of this type generates tainted data. + /// + public bool TaintConstantArray { get; } + + /// + /// For constant arrays, checks the length of the array to determine if it's tainted. + /// + public ArrayLengthMatcher? ConstantArrayLengthMatcher { get; } + + /// + /// Indicates that this uses s. + /// + public bool RequiresValueContentAnalysis => !this.TaintedMethodsNeedsValueContentAnalysis.IsEmpty; + + /// + /// Indicates that is required. + /// + public bool RequiresParameterReferenceAnalysis => !this.TaintedArguments.IsEmpty; + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + hashCode.Add(this.TaintConstantArray.GetHashCode()); + HashUtilities.Combine(this.TaintedProperties, ref hashCode); + HashUtilities.Combine(this.TaintedArguments, ref hashCode); + HashUtilities.Combine(this.TaintedMethods, ref hashCode); + HashUtilities.Combine(this.TaintedMethodsNeedsPointsToAnalysis, ref hashCode); + HashUtilities.Combine(this.TaintedMethodsNeedsValueContentAnalysis, ref hashCode); + HashUtilities.Combine(this.TransferMethods, ref hashCode); + HashUtilities.Combine(this.TransferProperties, ref hashCode); + HashUtilities.Combine(this.DependencyFullTypeNames, ref hashCode); + hashCode.Add(this.IsInterface.GetHashCode()); + hashCode.Add(StringComparer.Ordinal.GetHashCode(this.FullTypeName)); + return hashCode.ToHashCode(); + } + + public override bool Equals(object obj) + { + return obj is SourceInfo other && this.Equals(other); + } + + public bool Equals(SourceInfo other) + { + return other != null + && this.FullTypeName == other.FullTypeName + && this.IsInterface == other.IsInterface + && this.TaintedProperties == other.TaintedProperties + && this.TaintedArguments == other.TaintedArguments + && this.TaintedMethods == other.TaintedMethods + && this.TaintedMethodsNeedsPointsToAnalysis == other.TaintedMethodsNeedsPointsToAnalysis + && this.TaintedMethodsNeedsValueContentAnalysis == other.TaintedMethodsNeedsValueContentAnalysis + && this.TransferMethods == other.TransferMethods + && this.TransferProperties == other.TransferProperties + && this.DependencyFullTypeNames == other.DependencyFullTypeNames + && this.TaintConstantArray == other.TaintConstantArray; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SqlSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SqlSinks.cs new file mode 100644 index 0000000000000..f503620dc68ca --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SqlSinks.cs @@ -0,0 +1,75 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class SqlSinks + { + /// + /// s for tainted data SQL sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static SqlSinks() + { + var sinkInfosBuilder = PooledHashSet.GetInstance(); + + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemDataIDbCommand, + SinkKind.Sql, + isInterface: true, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new string[] { + "CommandText", + }, + sinkMethodParameters: null); + + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemDataIDataAdapter, + SinkKind.Sql, + isInterface: true, + isAnyStringParameterInConstructorASink: true, + sinkProperties: null, + sinkMethodParameters: null); + + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsSqlDataSource, + SinkKind.Sql, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new string[] { + "ConnectionString", + "DeleteCommand", + "InsertCommand", + "SelectCommand", + "UpdateCommand", + }, + sinkMethodParameters: null); + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.MicrosoftEntityFrameworkCoreRelationalQueryableExtensions, + SinkKind.Sql, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "FromSql", new[] { "sql", } ), + }); + + sinkInfosBuilder.AddSinkInfo( + WellKnownTypeNames.SystemDataEntityDbSet1, + SinkKind.Sql, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "SqlQuery", new[] { "sql", } ), + }); + + SinkInfos = sinkInfosBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/StringTransferSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/StringTransferSources.cs new file mode 100644 index 0000000000000..5bbd1f19bb417 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/StringTransferSources.cs @@ -0,0 +1,109 @@ +// 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 Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class StringTranferSources + { + /// + /// s for transferring string tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Statically constructs. + /// + static StringTranferSources() + { + var sourceInfosBuilder = PooledHashSet.GetInstance(); + + sourceInfosBuilder.AddSourceInfoSpecifyingTaintedTargets( + WellKnownTypeNames.SystemTextStringBuilder, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: null, + transferProperties: new[] { TaintedDataProperties.IndexerName }, + transferMethods: new (MethodMatcher, (string, string)[])[]{ + ( + (methodName, arguments) => + methodName == "Append" && + !arguments.IsEmpty && + arguments[0].Parameter is { } firstParameter && + (firstParameter.Type.SpecialType == SpecialType.System_String || + firstParameter.Type.SpecialType == SpecialType.System_Char || + (firstParameter.Type is IArrayTypeSymbol arrayType && + arrayType.Rank == 1 && + arrayType.ElementType.SpecialType == SpecialType.System_Char) || + (firstParameter.Type is IPointerTypeSymbol pointerType && + pointerType.PointedAtType.SpecialType == SpecialType.System_Char)), + new (string, string)[]{ + ("value", TaintedTargetValue.This), + } + ), + ( + (methodName, arguments) => + methodName == "AppendFormat", + new (string, string)[]{ + ("format", TaintedTargetValue.This), + ("arg0", TaintedTargetValue.This), + ("arg1", TaintedTargetValue.This), + ("arg2", TaintedTargetValue.This), + ("args", TaintedTargetValue.This), + } + ), + ( + (methodName, arguments) => + methodName == "AppendJoin", + new (string, string)[]{ + ("separator", TaintedTargetValue.This), + ("values", TaintedTargetValue.This), + } + ), + ( + (methodName, arguments) => + methodName == "AppendLine", + new (string, string)[]{ + ("value", TaintedTargetValue.This), + } + ), + ( + (methodName, arguments) => + methodName == "CopyTo", + new (string, string)[]{ + (TaintedTargetValue.This, "destination"), + } + ), + ( + (methodName, arguments) => + methodName == "Insert" && + arguments.Length > 1 && + arguments[1].Parameter is { } secondParameter && + (secondParameter.Type.SpecialType == SpecialType.System_String || + secondParameter.Type.SpecialType == SpecialType.System_Char || + (secondParameter.Type is IArrayTypeSymbol arrayType && + arrayType.Rank == 1 && + arrayType.ElementType.SpecialType == SpecialType.System_Char)), + new (string, string)[]{ + ("value", TaintedTargetValue.This), + } + ), + ( + (methodName, arguments) => + methodName == "Replace", + new (string, string)[]{ + ("newValue", TaintedTargetValue.This), + ("newChar", TaintedTargetValue.This), + } + ), + }); + + SourceInfos = sourceInfosBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SymbolAccess.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SymbolAccess.cs new file mode 100644 index 0000000000000..405bee6ae6412 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/SymbolAccess.cs @@ -0,0 +1,66 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Represents an access to a symbol. + /// + /// This is useful to track where tainted data originated from as a source, or where tainted data entered as a sink. + internal sealed class SymbolAccess : CacheBasedEquatable + { + public SymbolAccess(ISymbol symbol, SyntaxNode syntaxNode, ISymbol accessingMethod) + { + Symbol = symbol ?? throw new ArgumentNullException(nameof(symbol)); + if (syntaxNode == null) + { + throw new ArgumentNullException(nameof(syntaxNode)); + } + + Location = syntaxNode.GetLocation(); + AccessingMethod = accessingMethod ?? throw new ArgumentNullException(nameof(accessingMethod)); + } + + public SymbolAccess(ISymbol symbol, Location location, ISymbol accessingMethod) + { + Symbol = symbol ?? throw new ArgumentNullException(nameof(symbol)); + Location = location ?? throw new ArgumentNullException(nameof(location)); + AccessingMethod = accessingMethod ?? throw new ArgumentNullException(nameof(accessingMethod)); + } + + /// + /// Symbol being accessed. + /// + public ISymbol Symbol { get; } + + /// + /// Location of the access, from the SyntaxNode. + /// + public Location Location { get; } + + /// + /// What method contains the code performing the access. + /// + public ISymbol AccessingMethod { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(Location.GetHashCode()); + hashCode.Add(Symbol.GetHashCode()); + hashCode.Add(AccessingMethod.GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (SymbolAccess)obj; + return Location.GetHashCode() == other.Location.GetHashCode() + && Symbol.GetHashCode() == other.Symbol.GetHashCode() + && AccessingMethod.GetHashCode() == other.AccessingMethod.GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValue.cs new file mode 100644 index 0000000000000..94529f056cae2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValue.cs @@ -0,0 +1,105 @@ +// 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 System.Diagnostics; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Abstract tainted data value shared by a set of one of more instances tracked by . + /// + [DebuggerDisplay("{Kind} ({SourceOrigins.Count} source origins)")] + internal sealed class TaintedDataAbstractValue : CacheBasedEquatable + { + public static readonly TaintedDataAbstractValue NotTainted = new(TaintedDataAbstractValueKind.NotTainted, ImmutableHashSet.Empty); + + private TaintedDataAbstractValue(TaintedDataAbstractValueKind kind, ImmutableHashSet sourceOrigins) + { + this.Kind = kind; + this.SourceOrigins = sourceOrigins; + } + + /// + /// The abstract value that this is representing. + /// + public TaintedDataAbstractValueKind Kind { get; } + + /// + /// SyntaxNodes where the tainted data originated from. + /// + public ImmutableHashSet SourceOrigins { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(SourceOrigins)); + hashCode.Add(((int)Kind).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (TaintedDataAbstractValue)obj; + return HashUtilities.Combine(SourceOrigins) == HashUtilities.Combine(other.SourceOrigins) + && ((int)Kind).GetHashCode() == ((int)other.Kind).GetHashCode(); + } + + /// + /// Creates a TaintedDataAbstractValue that's marked as tainted. + /// + /// Where the tainted data is originally coming from. + /// Symbol that's tainted. + /// Method that's accessing the tainted data. + /// New TaintedDataAbstractValue that's marked as tainted. + internal static TaintedDataAbstractValue CreateTainted(ISymbol taintedSymbol, SyntaxNode accessingSyntax, ISymbol accessingMethod) + { + return new TaintedDataAbstractValue( + TaintedDataAbstractValueKind.Tainted, + ImmutableHashSet.Create( + new SymbolAccess( + taintedSymbol, + accessingSyntax, + accessingMethod))); + } + + /// + /// Merge two tainted abstract values together. + /// + /// First tainted abstract value. + /// Second tainted abstract value. + /// Tainted abstract value containing both sets of source origins. + internal static TaintedDataAbstractValue MergeTainted(TaintedDataAbstractValue value1, TaintedDataAbstractValue value2) + { + Debug.Assert(value1.Kind == TaintedDataAbstractValueKind.Tainted); + Debug.Assert(value2.Kind == TaintedDataAbstractValueKind.Tainted); + + return new TaintedDataAbstractValue( + TaintedDataAbstractValueKind.Tainted, + value1.SourceOrigins.Union(value2.SourceOrigins)); + } + + /// + /// Merges multiple tainted abstract values together. + /// + /// Enumeration of tainted abstract values. + /// Tainted abstract value containing the super set of source origins. + internal static TaintedDataAbstractValue MergeTainted(IEnumerable taintedValues) + { + var sourceOriginsBuilder = PooledHashSet.GetInstance(); + foreach (TaintedDataAbstractValue value in taintedValues) + { + Debug.Assert(value.Kind == TaintedDataAbstractValueKind.Tainted); + + sourceOriginsBuilder.UnionWith(value.SourceOrigins); + } + + return new TaintedDataAbstractValue( + TaintedDataAbstractValueKind.Tainted, + sourceOriginsBuilder.ToImmutableAndFree()); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValueKind.cs new file mode 100644 index 0000000000000..abebc8415bbb4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAbstractValueKind.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Kind for . + /// + internal enum TaintedDataAbstractValueKind + { + /// + /// Indicates the data is definitely untainted (cuz it was sanitized). + /// + NotTainted, + + /// + /// Indicates that data is definitely tainted. + /// + Tainted, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.CoreTaintedDataAnalysisDataDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.CoreTaintedDataAnalysisDataDomain.cs new file mode 100644 index 0000000000000..b9d2f1c88a71c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.CoreTaintedDataAnalysisDataDomain.cs @@ -0,0 +1,34 @@ +// 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.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal partial class TaintedDataAnalysis + { + private sealed class CoreTaintedDataAnalysisDataDomain : AnalysisEntityMapAbstractDomain + { + public CoreTaintedDataAnalysisDataDomain(PointsToAnalysisResult? pointsToAnalysisResult) + : base(TaintedDataAbstractValueDomain.Default, pointsToAnalysisResult) + { + } + + protected override bool CanSkipNewEntry(AnalysisEntity analysisEntity, TaintedDataAbstractValue value) + { + return value.Kind == TaintedDataAbstractValueKind.NotTainted; + } + + protected override TaintedDataAbstractValue GetDefaultValue(AnalysisEntity analysisEntity) + { + return TaintedDataAbstractValue.NotTainted; + } + + protected override void AssertValidEntryForMergedMap(AnalysisEntity analysisEntity, TaintedDataAbstractValue value) + { + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAbstractValueDomain.cs new file mode 100644 index 0000000000000..91322c23448d8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAbstractValueDomain.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal partial class TaintedDataAnalysis + { + private sealed class TaintedDataAbstractValueDomain : AbstractValueDomain + { + public static readonly TaintedDataAbstractValueDomain Default = new(); + + public override TaintedDataAbstractValue UnknownOrMayBeValue => TaintedDataAbstractValue.NotTainted; + + public override TaintedDataAbstractValue Bottom => TaintedDataAbstractValue.NotTainted; + + public override int Compare(TaintedDataAbstractValue oldValue, TaintedDataAbstractValue newValue, bool assertMonotonicity) + { + // The newly computed abstract values for each basic block + // must be always greater or equal than the previous value + // to ensure termination. + + // NotTainted < Tainted + if (oldValue.Kind == TaintedDataAbstractValueKind.Tainted && newValue.Kind == TaintedDataAbstractValueKind.Tainted) + { + return SetAbstractDomain.Default.Compare(oldValue.SourceOrigins, newValue.SourceOrigins); + } + else + { + return oldValue.Kind.CompareTo(newValue.Kind); + } + } + + public override TaintedDataAbstractValue Merge(TaintedDataAbstractValue value1, TaintedDataAbstractValue value2) + { + // N T + // +---- + // N | N T + // T | T T + + if (value1 == null) + { + return value2; + } + else if (value2 == null) + { + return value1; + } + + if (value1.Kind == TaintedDataAbstractValueKind.Tainted && value2.Kind == TaintedDataAbstractValueKind.Tainted) + { + // If both are tainted, we need to merge their origins. + return TaintedDataAbstractValue.MergeTainted(value1, value2); + } + + return value1.Kind > value2.Kind ? value1 : value2; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAnalysisDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAnalysisDomain.cs new file mode 100644 index 0000000000000..cd4b1d44d1d82 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataAnalysisDomain.cs @@ -0,0 +1,19 @@ +// 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 Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + + internal partial class TaintedDataAnalysis + { + private sealed class TaintedDataAnalysisDomain : PredicatedAnalysisDataDomain + { + public TaintedDataAnalysisDomain(MapAbstractDomain coreDataAnalysisDomain) + : base(coreDataAnalysisDomain) + { + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataOperationVisitor.cs new file mode 100644 index 0000000000000..69711dd71e8b1 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.TaintedDataOperationVisitor.cs @@ -0,0 +1,778 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + internal partial class TaintedDataAnalysis + { + private sealed class TaintedDataOperationVisitor : AnalysisEntityDataFlowOperationVisitor + { + private readonly TaintedDataAnalysisDomain _taintedDataAnalysisDomain; + + /// + /// Mapping of a tainted data sinks to their originating sources. + /// + /// Keys are sinks where the tainted data entered, values are s where the tainted data originated from. + private Dictionary.Builder SinkKinds, ImmutableHashSet.Builder SourceOrigins)> TaintedSourcesBySink { get; } + + public TaintedDataOperationVisitor(TaintedDataAnalysisDomain taintedDataAnalysisDomain, TaintedDataAnalysisContext analysisContext) + : base(analysisContext) + { + _taintedDataAnalysisDomain = taintedDataAnalysisDomain; + this.TaintedSourcesBySink = new Dictionary.Builder SinkKinds, ImmutableHashSet.Builder SourceOrigins)>(); + } + + public ImmutableArray GetTaintedDataSourceSinkEntries() + { + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + foreach (KeyValuePair.Builder SinkKinds, ImmutableHashSet.Builder SourceOrigins)> kvp in this.TaintedSourcesBySink) + { + builder.Add( + new TaintedDataSourceSink( + kvp.Key, + kvp.Value.SinkKinds.ToImmutable(), + kvp.Value.SourceOrigins.ToImmutable())); + } + + return builder.ToImmutableArray(); + } + + protected override void AddTrackedEntities(TaintedDataAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis) + => analysisData.AddTrackedEntities(builder); + + protected override bool Equals(TaintedDataAnalysisData value1, TaintedDataAnalysisData value2) + { + return value1.Equals(value2); + } + + protected override TaintedDataAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) + { + return TaintedDataAbstractValue.NotTainted; + } + + protected override TaintedDataAbstractValue GetAbstractValue(AnalysisEntity analysisEntity) + { + return this.CurrentAnalysisData.TryGetValue(analysisEntity, out TaintedDataAbstractValue? value) ? value : TaintedDataAbstractValue.NotTainted; + } + + protected override TaintedDataAnalysisData GetClonedAnalysisData(TaintedDataAnalysisData analysisData) + { + return (TaintedDataAnalysisData)analysisData.Clone(); + } + + protected override bool HasAbstractValue(AnalysisEntity analysisEntity) + { + return this.CurrentAnalysisData.HasAbstractValue(analysisEntity); + } + + protected override bool HasAnyAbstractValue(TaintedDataAnalysisData data) + { + return data.HasAnyAbstractValue; + } + + protected override TaintedDataAnalysisData MergeAnalysisData(TaintedDataAnalysisData value1, TaintedDataAnalysisData value2) + { + return _taintedDataAnalysisDomain.Merge(value1, value2); + } + + protected override void UpdateValuesForAnalysisData(TaintedDataAnalysisData targetAnalysisData) + { + UpdateValuesForAnalysisData(targetAnalysisData.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData); + } + + protected override void ResetCurrentAnalysisData() + { + this.CurrentAnalysisData.Reset(this.ValueDomain.UnknownOrMayBeValue); + } + + public override TaintedDataAnalysisData GetEmptyAnalysisData() + { + return new TaintedDataAnalysisData(); + } + + protected override TaintedDataAnalysisData GetExitBlockOutputData(TaintedDataAnalysisResult analysisResult) + { + return new TaintedDataAnalysisData(analysisResult.ExitBlockOutput.Data); + } + + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(TaintedDataAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + { + base.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData, throwBranchWithExceptionType); + } + + protected override TaintedDataAbstractValue GetDefaultValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + if (this.DataFlowAnalysisContext.SourceInfos.IsSourceParameter(parameter, WellKnownTypeProvider)) + { + // Location of the parameter, so we can track where the tainted data appears in code. + // The parameter itself may not have any DeclaringSyntaxReferences, e.g. 'value' inside property setters. + SyntaxNode parameterSyntaxNode; + if (!parameter.DeclaringSyntaxReferences.IsEmpty) + { + parameterSyntaxNode = parameter.DeclaringSyntaxReferences[0].GetSyntax(); + } + else if (!parameter.ContainingSymbol.DeclaringSyntaxReferences.IsEmpty) + { + parameterSyntaxNode = parameter.ContainingSymbol.DeclaringSyntaxReferences[0].GetSyntax(); + } + else + { + // Unless there are others, the only case we have for parameters being tainted data sources is inside + // ASP.NET Core MVC controller action methods (see WebInputSources.cs), so those parameters should + // always be declared somewhere. + Debug.Fail("Can we have a tainted data parameter with no syntax references?"); + return ValueDomain.UnknownOrMayBeValue; + } + + return TaintedDataAbstractValue.CreateTainted(parameter, parameterSyntaxNode, this.OwningSymbol); + } + + return ValueDomain.UnknownOrMayBeValue; + } + + protected override void SetAbstractValue(AnalysisEntity analysisEntity, TaintedDataAbstractValue value) + { + if (value.Kind == TaintedDataAbstractValueKind.Tainted + || this.CurrentAnalysisData.CoreAnalysisData.ContainsKey(analysisEntity)) + { + // Only track tainted data, or sanitized data. + // If it's new, and it's untainted, we don't care. + SetAbstractValueCore(CurrentAnalysisData, analysisEntity, value); + } + } + + private static void SetAbstractValueCore(TaintedDataAnalysisData taintedAnalysisData, AnalysisEntity analysisEntity, TaintedDataAbstractValue value) + => taintedAnalysisData.SetAbstractValue(analysisEntity, value); + + protected override void ResetAbstractValue(AnalysisEntity analysisEntity) + { + this.SetAbstractValue(analysisEntity, ValueDomain.UnknownOrMayBeValue); + } + + protected override void StopTrackingEntity(AnalysisEntity analysisEntity, TaintedDataAnalysisData analysisData) + { + analysisData.RemoveEntries(analysisEntity); + } + + public override TaintedDataAbstractValue DefaultVisit(IOperation operation, object? argument) + { + // This handles most cases of tainted data flowing from child operations to parent operations. + // Examples: + // - tainted input parameters to method calls returns, and out/ref parameters, tainted (assuming no interprocedural) + // - adding a tainted value to something makes the result tainted + // - instantiating an object with tainted data makes the new object tainted + + List? taintedValues = null; + foreach (IOperation childOperation in operation.Children) + { + TaintedDataAbstractValue childValue = Visit(childOperation, argument); + if (childValue.Kind == TaintedDataAbstractValueKind.Tainted) + { + taintedValues ??= new List(); + + taintedValues.Add(childValue); + } + } + + if (taintedValues != null) + { + if (taintedValues.Count == 1) + { + return taintedValues[0]; + } + else + { + return TaintedDataAbstractValue.MergeTainted(taintedValues); + } + } + else + { + return ValueDomain.UnknownOrMayBeValue; + } + } + + public override TaintedDataAbstractValue VisitConversion(IConversionOperation operation, object? argument) + { + TaintedDataAbstractValue operandValue = Visit(operation.Operand, argument); + + if (!operation.Conversion.Exists) + { + return ValueDomain.UnknownOrMayBeValue; + } + + if (operation.Conversion.IsImplicit) + { + return operandValue; + } + + // Conservative for error code and user defined operator. + return !operation.Conversion.IsUserDefined ? operandValue : ValueDomain.UnknownOrMayBeValue; + } + + protected override TaintedDataAbstractValue ComputeAnalysisValueForReferenceOperation(IOperation operation, TaintedDataAbstractValue defaultValue) + { + // If the property reference itself is a tainted data source + if (operation is IPropertyReferenceOperation propertyReferenceOperation + && this.DataFlowAnalysisContext.SourceInfos.IsSourceProperty(propertyReferenceOperation.Property)) + { + return TaintedDataAbstractValue.CreateTainted(propertyReferenceOperation.Member, propertyReferenceOperation.Syntax, this.OwningSymbol); + } + + if (AnalysisEntityFactory.TryCreate(operation, out AnalysisEntity? analysisEntity)) + { + return this.CurrentAnalysisData.TryGetValue(analysisEntity, out TaintedDataAbstractValue? value) ? value : defaultValue; + } + + return defaultValue; + } + + // So we can hook into constructor calls. + public override TaintedDataAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + TaintedDataAbstractValue baseValue = base.VisitObjectCreation(operation, argument); + IEnumerable taintedArguments = GetTaintedArguments(operation.Arguments); + if (taintedArguments.Any() && operation.Constructor != null) + { + ProcessTaintedDataEnteringInvocationOrCreation(operation.Constructor, taintedArguments, operation); + } + + return baseValue; + } + + public override TaintedDataAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + TaintedDataAbstractValue defaultValue) + { + // Always invoke base visit. + TaintedDataAbstractValue result = base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + method, + visitedInstance, + visitedArguments, + invokedAsDelegate, + originalOperation, + defaultValue); + + IEnumerable taintedArguments = GetTaintedArguments(visitedArguments); + if (taintedArguments.Any()) + { + ProcessTaintedDataEnteringInvocationOrCreation(method, taintedArguments, originalOperation); + } + + PooledHashSet? taintedTargets = null; + PooledHashSet<(string, string)>? taintedParameterPairs = null; + PooledHashSet<(string, string)>? sanitizedParameterPairs = null; + PooledHashSet? taintedParameterNamesCached = null; + try + { + IEnumerable GetTaintedParameterNames() + { + IEnumerable taintedParameterNames = visitedArguments + .Where(s => s.Parameter != null && this.GetCachedAbstractValue(s).Kind == TaintedDataAbstractValueKind.Tainted) + .Select(s => s.Parameter!.Name); + + if (visitedInstance != null && this.GetCachedAbstractValue(visitedInstance).Kind == TaintedDataAbstractValueKind.Tainted) + { + taintedParameterNames = taintedParameterNames.Concat(TaintedTargetValue.This); + } + + return taintedParameterNames; + } + + taintedParameterNamesCached = PooledHashSet.GetInstance(); + taintedParameterNamesCached.UnionWith(GetTaintedParameterNames()); + + if (this.DataFlowAnalysisContext.SourceInfos.IsSourceMethod( + method, + visitedArguments, + new Lazy(() => DataFlowAnalysisContext.PointsToAnalysisResult), + new Lazy<(PointsToAnalysisResult?, ValueContentAnalysisResult?)>(() => (DataFlowAnalysisContext.PointsToAnalysisResult, DataFlowAnalysisContext.ValueContentAnalysisResult)), + out taintedTargets)) + { + bool rebuildTaintedParameterNames = false; + + foreach (string taintedTarget in taintedTargets) + { + if (taintedTarget != TaintedTargetValue.Return) + { + IArgumentOperation? argumentOperation = visitedArguments.FirstOrDefault(o => o.Parameter?.Name == taintedTarget); + if (argumentOperation?.Parameter != null) + { + rebuildTaintedParameterNames = true; + this.CacheAbstractValue(argumentOperation, TaintedDataAbstractValue.CreateTainted(argumentOperation.Parameter, argumentOperation.Syntax, method)); + } + else + { + Debug.Fail("Are the tainted data sources misconfigured?"); + } + } + else + { + result = TaintedDataAbstractValue.CreateTainted(method, originalOperation.Syntax, this.OwningSymbol); + } + } + + if (rebuildTaintedParameterNames) + { + taintedParameterNamesCached.Clear(); + taintedParameterNamesCached.UnionWith(GetTaintedParameterNames()); + } + } + + if (this.DataFlowAnalysisContext.SourceInfos.IsSourceTransferMethod( + method, + visitedArguments, + taintedParameterNamesCached, + out taintedParameterPairs)) + { + foreach ((string ifTaintedParameter, string thenTaintedTarget) in taintedParameterPairs) + { + IOperation? thenTaintedTargetOperation = visitedInstance != null && thenTaintedTarget == TaintedTargetValue.This + ? visitedInstance + : visitedArguments.FirstOrDefault(o => o.Parameter?.Name == thenTaintedTarget); + if (thenTaintedTargetOperation != null) + { + var operation = visitedInstance != null && ifTaintedParameter == TaintedTargetValue.This + ? visitedInstance + : visitedArguments.FirstOrDefault(o => o.Parameter?.Name == ifTaintedParameter); + if (operation != null) + { + SetTaintedForEntity(thenTaintedTargetOperation, this.GetCachedAbstractValue(operation)); + } + } + else + { + Debug.Fail("Are the tainted data sources misconfigured?"); + } + } + } + + if (visitedInstance != null && this.IsSanitizingInstanceMethod(method)) + { + SetTaintedForEntity(visitedInstance, TaintedDataAbstractValue.NotTainted); + } + + if (this.IsSanitizingMethod( + method, + visitedArguments, + taintedParameterNamesCached, + out sanitizedParameterPairs)) + { + if (sanitizedParameterPairs.Count == 0) + { + // it was either sanitizing constructor or + // the short form or registering sanitizer method by just the name + result = TaintedDataAbstractValue.NotTainted; + } + else + { + foreach ((string ifTaintedParameter, string thenSanitizedTarget) in sanitizedParameterPairs) + { + if (thenSanitizedTarget == TaintedTargetValue.Return) + { + result = TaintedDataAbstractValue.NotTainted; + continue; + } + + IArgumentOperation? thenSanitizedTargetOperation = visitedArguments.FirstOrDefault(o => o.Parameter?.Name == thenSanitizedTarget); + if (thenSanitizedTargetOperation != null) + { + SetTaintedForEntity(thenSanitizedTargetOperation, TaintedDataAbstractValue.NotTainted); + } + else + { + Debug.Fail("Are the tainted data sanitizers misconfigured?"); + } + } + } + } + } + finally + { + taintedTargets?.Dispose(); + taintedParameterPairs?.Dispose(); + sanitizedParameterPairs?.Dispose(); + taintedParameterNamesCached?.Dispose(); + } + + return result; + } + + public override TaintedDataAbstractValue VisitInvocation_LocalFunction(IMethodSymbol localFunction, ImmutableArray visitedArguments, IOperation originalOperation, TaintedDataAbstractValue defaultValue) + { + // Always invoke base visit. + TaintedDataAbstractValue baseValue = base.VisitInvocation_LocalFunction(localFunction, visitedArguments, originalOperation, defaultValue); + + IEnumerable taintedArguments = GetTaintedArguments(visitedArguments); + if (taintedArguments.Any()) + { + ProcessTaintedDataEnteringInvocationOrCreation(localFunction, taintedArguments, originalOperation); + } + + return baseValue; + } + + public override TaintedDataAbstractValue VisitInvocation_Lambda(IFlowAnonymousFunctionOperation lambda, ImmutableArray visitedArguments, IOperation originalOperation, TaintedDataAbstractValue defaultValue) + { + // Always invoke base visit. + TaintedDataAbstractValue baseValue = base.VisitInvocation_Lambda(lambda, visitedArguments, originalOperation, defaultValue); + + IEnumerable taintedArguments = GetTaintedArguments(visitedArguments); + if (taintedArguments.Any()) + { + ProcessTaintedDataEnteringInvocationOrCreation(lambda.Symbol, taintedArguments, originalOperation); + } + + return baseValue; + } + + /// + /// Computes abstract value for out or ref arguments when not performing interprocedural analysis. + /// + /// Analysis entity. + /// IArgumentOperation. + /// Default TaintedDataAbstractValue if we don't need to override. + /// Abstract value of the output parameter. + protected override TaintedDataAbstractValue ComputeAnalysisValueForEscapedRefOrOutArgument( + AnalysisEntity analysisEntity, + IArgumentOperation operation, + TaintedDataAbstractValue defaultValue) + { + // Note this method is only called when interprocedural DFA is *NOT* performed. + if (operation.Parent is IInvocationOperation invocationOperation) + { + Debug.Assert(!this.TryGetInterproceduralAnalysisResult(invocationOperation, out _)); + + // Treat ref or out arguments as the same as the invocation operation. + TaintedDataAbstractValue returnValueAbstractValue = this.GetCachedAbstractValue(invocationOperation); + return returnValueAbstractValue; + } + else + { + return defaultValue; + } + } + + // So we can treat the array as tainted when it's passed to other object constructors. + // See HttpRequest_Form_Array_List_Diagnostic and HttpRequest_Form_List_Diagnostic tests. + public override TaintedDataAbstractValue VisitArrayInitializer(IArrayInitializerOperation operation, object? argument) + { + HashSet? sourceOrigins = null; + TaintedDataAbstractValue baseAbstractValue = base.VisitArrayInitializer(operation, argument); + if (baseAbstractValue.Kind == TaintedDataAbstractValueKind.Tainted) + { + sourceOrigins = new HashSet(baseAbstractValue.SourceOrigins); + } + + IEnumerable taintedAbstractValues = + operation.ElementValues + .Select(this.GetCachedAbstractValue) + .Where(v => v.Kind == TaintedDataAbstractValueKind.Tainted); + if (baseAbstractValue.Kind == TaintedDataAbstractValueKind.Tainted) + { + taintedAbstractValues = taintedAbstractValues.Concat(baseAbstractValue); + } + + TaintedDataAbstractValue? result = null; + if (taintedAbstractValues.Any()) + { + result = TaintedDataAbstractValue.MergeTainted(taintedAbstractValues); + } + + IArrayCreationOperation? arrayCreationOperation = operation.GetAncestor(OperationKind.ArrayCreation); + if (arrayCreationOperation?.Type is IArrayTypeSymbol arrayTypeSymbol + && this.DataFlowAnalysisContext.SourceInfos.IsSourceConstantArrayOfType(arrayTypeSymbol, operation) + && operation.ElementValues.All(s => GetValueContentAbstractValue(s).IsLiteralState)) + { + TaintedDataAbstractValue taintedDataAbstractValue = TaintedDataAbstractValue.CreateTainted(arrayTypeSymbol, arrayCreationOperation.Syntax, this.OwningSymbol); + result = result == null ? taintedDataAbstractValue : TaintedDataAbstractValue.MergeTainted(result, taintedDataAbstractValue); + } + + if (result != null) + { + return result; + } + else + { + return baseAbstractValue; + } + } + + protected override TaintedDataAbstractValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument) + { + TaintedDataAbstractValue taintedDataAbstractValue = base.VisitAssignmentOperation(operation, argument); + ProcessAssignmentOperation(operation); + return taintedDataAbstractValue; + } + + private void TrackTaintedDataEnteringSink( + ISymbol sinkSymbol, + Location sinkLocation, + IEnumerable sinkKinds, + IEnumerable sources) + { + SymbolAccess sink = new SymbolAccess(sinkSymbol, sinkLocation, this.OwningSymbol); + this.TrackTaintedDataEnteringSink(sink, sinkKinds, sources); + } + + private void TrackTaintedDataEnteringSink(SymbolAccess sink, IEnumerable sinkKinds, IEnumerable sources) + { + if (!this.TaintedSourcesBySink.TryGetValue(sink, out (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SourceOrigins) data)) + { + data = (ImmutableHashSet.CreateBuilder(), ImmutableHashSet.CreateBuilder()); + this.TaintedSourcesBySink.Add(sink, data); + } + + data.SinkKinds.UnionWith(sinkKinds); + data.SourceOrigins.UnionWith(sources); + } + + /// + /// Determines if tainted data is entering a sink as a method call or constructor argument, and if so, flags it. + /// + /// Method being invoked. + /// Arguments with tainted data to the method. + /// Original IOperation for the method/constructor invocation. + private void ProcessTaintedDataEnteringInvocationOrCreation( + IMethodSymbol targetMethod, + IEnumerable taintedArguments, + IOperation originalOperation) + { + if (targetMethod.ContainingType != null && taintedArguments.Any()) + { + IEnumerable? infosForType = this.DataFlowAnalysisContext.SinkInfos.GetInfosForType(targetMethod.ContainingType); + if (infosForType != null) + { + foreach (IArgumentOperation taintedArgument in taintedArguments) + { + if (IsMethodArgumentASink(targetMethod, infosForType, taintedArgument, out HashSet? sinkKinds)) + { + TaintedDataAbstractValue abstractValue = this.GetCachedAbstractValue(taintedArgument); + this.TrackTaintedDataEnteringSink(targetMethod, originalOperation.Syntax.GetLocation(), sinkKinds, abstractValue.SourceOrigins); + } + } + } + } + + if (this.TryGetInterproceduralAnalysisResult(originalOperation, out TaintedDataAnalysisResult? subResult) + && !subResult.TaintedDataSourceSinks.IsEmpty) + { + foreach (TaintedDataSourceSink sourceSink in subResult.TaintedDataSourceSinks) + { + if (!this.TaintedSourcesBySink.TryGetValue( + sourceSink.Sink, + out (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SourceOrigins) data)) + { + data = (ImmutableHashSet.CreateBuilder(), ImmutableHashSet.CreateBuilder()); + this.TaintedSourcesBySink.Add(sourceSink.Sink, data); + } + + data.SinkKinds.UnionWith(sourceSink.SinkKinds); + data.SourceOrigins.UnionWith(sourceSink.SourceOrigins); + } + } + } + + private void ProcessAssignmentOperation(IAssignmentOperation assignmentOperation) + { + TaintedDataAbstractValue assignmentValueAbstractValue = this.GetCachedAbstractValue(assignmentOperation.Value); + if (assignmentOperation.Target != null + && assignmentValueAbstractValue.Kind == TaintedDataAbstractValueKind.Tainted + && assignmentOperation.Target is IPropertyReferenceOperation propertyReferenceOperation) + { + if (this.IsPropertyASink(propertyReferenceOperation, out HashSet? sinkKinds)) + { + this.TrackTaintedDataEnteringSink( + propertyReferenceOperation.Member, + propertyReferenceOperation.Syntax.GetLocation(), + sinkKinds, + assignmentValueAbstractValue.SourceOrigins); + } + + if (this.DataFlowAnalysisContext.SourceInfos.IsSourceTransferProperty(propertyReferenceOperation)) + { + SetTaintedForEntity(propertyReferenceOperation.Instance!, assignmentValueAbstractValue); + } + } + } + + /// + /// Determines if the instance method call returns tainted data. + /// + /// Instance method being called. + /// Arguments passed to the method. + /// Names of the tainted input parameters. + /// Matched pairs of "tainted parameter name" to "sanitized parameter name". + /// True if the method sanitizes data (returned or as an output parameter), false otherwise. + private bool IsSanitizingMethod( + IMethodSymbol method, + ImmutableArray arguments, + ISet taintedParameterNames, + [NotNullWhen(returnValue: true)] out PooledHashSet<(string, string)>? taintedParameterPairs) + { + taintedParameterPairs = null; + foreach (SanitizerInfo sanitizerInfo in this.DataFlowAnalysisContext.SanitizerInfos.GetInfosForType(method.ContainingType)) + { + if (method.MethodKind == MethodKind.Constructor + && sanitizerInfo.IsConstructorSanitizing) + { + taintedParameterPairs = PooledHashSet<(string, string)>.GetInstance(); + return true; + } + + foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(string source, string end)> sourceToEnds) in sanitizerInfo.SanitizingMethods) + { + if (methodMatcher(method.Name, arguments)) + { + taintedParameterPairs ??= PooledHashSet<(string, string)>.GetInstance(); + + taintedParameterPairs.UnionWith(sourceToEnds.Where(s => taintedParameterNames.Contains(s.source))); + } + } + } + + return taintedParameterPairs != null; + } + + /// + /// Determines if untaint the instance after calling the method. + /// + /// Instance method being called. + /// True if untaint the instance, false otherwise. + private bool IsSanitizingInstanceMethod(IMethodSymbol method) + { + foreach (SanitizerInfo sanitizerInfo in this.DataFlowAnalysisContext.SanitizerInfos.GetInfosForType(method.ContainingType)) + { + if (sanitizerInfo.SanitizingInstanceMethods.Contains(method.MetadataName)) + { + return true; + } + } + + return false; + } + + /// + /// Determines if tainted data passed as arguments to a method enters a tainted data sink. + /// + /// Method being invoked. + /// Argument passed to the method invocation that is tainted. + /// True if any of the tainted data arguments enters a sink, false otherwise. + private static bool IsMethodArgumentASink(IMethodSymbol method, IEnumerable infosForType, IArgumentOperation taintedArgument, [NotNullWhen(returnValue: true)] out HashSet? sinkKinds) + { + sinkKinds = null; + Lazy> lazySinkKinds = new Lazy>(() => new HashSet()); + foreach (SinkInfo sinkInfo in infosForType) + { + if (lazySinkKinds.IsValueCreated && lazySinkKinds.Value.IsSupersetOf(sinkInfo.SinkKinds) || + taintedArgument.Parameter == null) + { + continue; + } + + if (method.MethodKind == MethodKind.Constructor + && sinkInfo.IsAnyStringParameterInConstructorASink + && taintedArgument.Parameter.Type.SpecialType == SpecialType.System_String) + { + lazySinkKinds.Value.UnionWith(sinkInfo.SinkKinds); + } + else if (sinkInfo.SinkMethodParameters.TryGetValue(method.MetadataName, out var sinkParameters) + && sinkParameters.Contains(taintedArgument.Parameter.MetadataName)) + { + lazySinkKinds.Value.UnionWith(sinkInfo.SinkKinds); + } + } + + if (lazySinkKinds.IsValueCreated) + { + sinkKinds = lazySinkKinds.Value; + return true; + } + else + { + return false; + } + } + + /// + /// Determines if a property is a sink. + /// + /// Property to check if it's a sink. + /// If the property is a sink, containing the kinds of sinks; null otherwise. + /// True if the property is a sink, false otherwise. + private bool IsPropertyASink(IPropertyReferenceOperation propertyReferenceOperation, [NotNullWhen(returnValue: true)] out HashSet? sinkKinds) + { + Lazy> lazySinkKinds = new Lazy>(() => new HashSet()); + foreach (SinkInfo sinkInfo in this.DataFlowAnalysisContext.SinkInfos.GetInfosForType(propertyReferenceOperation.Member.ContainingType)) + { + if (lazySinkKinds.IsValueCreated && lazySinkKinds.Value.IsSupersetOf(sinkInfo.SinkKinds)) + { + continue; + } + + if (sinkInfo.SinkProperties.Contains(propertyReferenceOperation.Member.MetadataName)) + { + lazySinkKinds.Value.UnionWith(sinkInfo.SinkKinds); + } + } + + if (lazySinkKinds.IsValueCreated) + { + sinkKinds = lazySinkKinds.Value; + return true; + } + else + { + sinkKinds = null; + return false; + } + } + + private IEnumerable GetTaintedArguments(ImmutableArray arguments) + { + return arguments.Where( + a => this.GetCachedAbstractValue(a).Kind == TaintedDataAbstractValueKind.Tainted + && a.Parameter != null + && (a.Parameter.RefKind == RefKind.None + || a.Parameter.RefKind == RefKind.Ref + || a.Parameter.RefKind == RefKind.In)); + } + + private void SetTaintedForEntity(IOperation operation, TaintedDataAbstractValue value) + { + if (AnalysisEntityFactory.TryCreate(operation, out AnalysisEntity? analysisEntity)) + { + this.CurrentAnalysisData.SetAbstractValue(analysisEntity, value); + } + } + + protected override void ApplyInterproceduralAnalysisResultCore(TaintedDataAnalysisData resultData) + => ApplyInterproceduralAnalysisResultHelper(resultData.CoreAnalysisData); + + protected override TaintedDataAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) + => GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData.CoreAnalysisData, SetAbstractValueCore); + + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.cs new file mode 100644 index 0000000000000..733a007438286 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysis.cs @@ -0,0 +1,140 @@ +// 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; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using System.Diagnostics; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + using CopyAnalysisResult = DataFlowAnalysisResult; + + internal partial class TaintedDataAnalysis : ForwardDataFlowAnalysis + { + internal static readonly AbstractValueDomain ValueDomainInstance = TaintedDataAbstractValueDomain.Default; + + private TaintedDataAnalysis(TaintedDataAnalysisDomain analysisDomain, TaintedDataOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + internal static TaintedDataAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + Compilation compilation, + ISymbol containingMethod, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + TaintedDataSymbolMap taintedSourceInfos, + TaintedDataSymbolMap taintedSanitizerInfos, + TaintedDataSymbolMap taintedSinkInfos) + { + var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( + analyzerOptions, rule, cfg, compilation, InterproceduralAnalysisKind.ContextSensitive); + return TryGetOrComputeResult(cfg, compilation, containingMethod, analyzerOptions, taintedSourceInfos, + taintedSanitizerInfos, taintedSinkInfos, interproceduralAnalysisConfig); + } + + private static TaintedDataAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + Compilation compilation, + ISymbol containingMethod, + AnalyzerOptions analyzerOptions, + TaintedDataSymbolMap taintedSourceInfos, + TaintedDataSymbolMap taintedSanitizerInfos, + TaintedDataSymbolMap taintedSinkInfos, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig) + { + if (cfg == null) + { + Debug.Fail("Expected non-null CFG"); + return null; + } + + WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + ValueContentAnalysisResult? valueContentAnalysisResult = null; + CopyAnalysisResult? copyAnalysisResult = null; + PointsToAnalysisResult? pointsToAnalysisResult = null; + if (taintedSourceInfos.RequiresValueContentAnalysis || taintedSanitizerInfos.RequiresValueContentAnalysis || taintedSinkInfos.RequiresValueContentAnalysis) + { + valueContentAnalysisResult = ValueContentAnalysis.TryGetOrComputeResult( + cfg, + containingMethod, + analyzerOptions, + wellKnownTypeProvider, + PointsToAnalysisKind.Complete, + interproceduralAnalysisConfig, + out copyAnalysisResult, + out pointsToAnalysisResult, + pessimisticAnalysis: true, + performCopyAnalysis: false); + if (valueContentAnalysisResult == null) + { + return null; + } + } + else + { + pointsToAnalysisResult = PointsToAnalysis.TryGetOrComputeResult( + cfg, + containingMethod, + analyzerOptions, + wellKnownTypeProvider, + PointsToAnalysisKind.Complete, + interproceduralAnalysisConfig, + interproceduralAnalysisPredicate: null, + pessimisticAnalysis: true, + performCopyAnalysis: false); + if (pointsToAnalysisResult == null) + { + return null; + } + } + + TaintedDataAnalysisContext analysisContext = TaintedDataAnalysisContext.Create( + ValueDomainInstance, + wellKnownTypeProvider, + cfg, + containingMethod, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis: false, + copyAnalysisResult: copyAnalysisResult, + pointsToAnalysisResult: pointsToAnalysisResult, + valueContentAnalysisResult: valueContentAnalysisResult, + tryGetOrComputeAnalysisResult: TryGetOrComputeResultForAnalysisContext, + taintedSourceInfos: taintedSourceInfos, + taintedSanitizerInfos: taintedSanitizerInfos, + taintedSinkInfos: taintedSinkInfos); + + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static TaintedDataAnalysisResult? TryGetOrComputeResultForAnalysisContext(TaintedDataAnalysisContext analysisContext) + { + TaintedDataAnalysisDomain analysisDomain = new TaintedDataAnalysisDomain(new CoreTaintedDataAnalysisDataDomain(analysisContext.PointsToAnalysisResult)); + TaintedDataOperationVisitor visitor = new TaintedDataOperationVisitor(analysisDomain, analysisContext); + TaintedDataAnalysis analysis = new TaintedDataAnalysis(analysisDomain, visitor); + return analysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + protected override TaintedDataAnalysisResult ToResult( + TaintedDataAnalysisContext analysisContext, + DataFlowAnalysisResult dataFlowAnalysisResult) + { + TaintedDataOperationVisitor visitor = (TaintedDataOperationVisitor)this.OperationVisitor; + return new TaintedDataAnalysisResult(dataFlowAnalysisResult, visitor.GetTaintedDataSourceSinkEntries()); + } + + protected override TaintedDataBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, TaintedDataAnalysisData blockAnalysisData) + { + return new TaintedDataBlockAnalysisResult(basicBlock, blockAnalysisData); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisContext.cs new file mode 100644 index 0000000000000..34dbe274cdf88 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisContext.cs @@ -0,0 +1,159 @@ +// 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.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralTaintedDataAnalysisData = InterproceduralAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + internal sealed class TaintedDataAnalysisContext : AbstractDataFlowAnalysisContext + { + private TaintedDataAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + CopyAnalysisResult? copyAnalysisResult, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralTaintedDataAnalysisData? interproceduralAnalysisData, + TaintedDataSymbolMap taintedSourceInfos, + TaintedDataSymbolMap taintedSanitizerInfos, + TaintedDataSymbolMap taintedSinkInfos) + : base( + valueDomain, + wellKnownTypeProvider, + controlFlowGraph, + owningSymbol, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis, + predicateAnalysis: false, + exceptionPathsAnalysis: false, + copyAnalysisResult, + pointsToAnalysisResult, + valueContentAnalysisResult, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph, + interproceduralAnalysisData, + interproceduralAnalysisPredicate: null) + { + Debug.Assert(pointsToAnalysisResult != null); + + this.SourceInfos = taintedSourceInfos ?? throw new ArgumentNullException(nameof(taintedSourceInfos)); + this.SanitizerInfos = taintedSanitizerInfos ?? throw new ArgumentNullException(nameof(taintedSanitizerInfos)); + this.SinkInfos = taintedSinkInfos ?? throw new ArgumentNullException(nameof(taintedSinkInfos)); + } + + public static TaintedDataAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + CopyAnalysisResult? copyAnalysisResult, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + TaintedDataSymbolMap taintedSourceInfos, + TaintedDataSymbolMap taintedSanitizerInfos, + TaintedDataSymbolMap taintedSinkInfos) + { + Debug.Assert(pointsToAnalysisResult != null); + + return new TaintedDataAnalysisContext( + valueDomain, + wellKnownTypeProvider, + controlFlowGraph, + owningSymbol, + analyzerOptions, + interproceduralAnalysisConfig, + pessimisticAnalysis, + copyAnalysisResult, + pointsToAnalysisResult, + valueContentAnalysisResult, + tryGetOrComputeAnalysisResult, + parentControlFlowGraph: null, + interproceduralAnalysisData: null, + taintedSourceInfos: taintedSourceInfos, + taintedSanitizerInfos: taintedSanitizerInfos, + taintedSinkInfos: taintedSinkInfos); + } + + public override TaintedDataAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + DataFlowAnalysisResult? copyAnalysisResult, + DataFlowAnalysisResult? valueContentAnalysisResult, + InterproceduralTaintedDataAnalysisData? interproceduralAnalysisData) + { + return new TaintedDataAnalysisContext( + this.ValueDomain, + this.WellKnownTypeProvider, + invokedCfg, + invokedMethod, + this.AnalyzerOptions, + this.InterproceduralAnalysisConfiguration, + this.PessimisticAnalysis, + copyAnalysisResult, + pointsToAnalysisResult, + valueContentAnalysisResult, + this.TryGetOrComputeAnalysisResult, + this.ControlFlowGraph, + interproceduralAnalysisData, + this.SourceInfos, + this.SanitizerInfos, + this.SinkInfos); + } + + /// + /// Information about types for tainted data sources. + /// + public TaintedDataSymbolMap SourceInfos { get; } + + /// + /// Information about types for tainted data sanitizers. + /// + public TaintedDataSymbolMap SanitizerInfos { get; } + + /// + /// Information about types for the tainted data sinks. + /// + public TaintedDataSymbolMap SinkInfos { get; } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(SourceInfos.GetHashCode()); + hashCode.Add(SanitizerInfos.GetHashCode()); + hashCode.Add(SinkInfos.GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (TaintedDataAnalysisContext)obj; + return SourceInfos.GetHashCode() == other.SourceInfos.GetHashCode() + && SanitizerInfos.GetHashCode() == other.SanitizerInfos.GetHashCode() + && SinkInfos.GetHashCode() == other.SinkInfos.GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisData.cs new file mode 100644 index 0000000000000..83b7d5b81354f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisData.cs @@ -0,0 +1,55 @@ +// 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 Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + // TODO: We should delete this type and use the following instead: + // using TaintedDataAnalysisData = DictionaryAnalysisData; + internal sealed class TaintedDataAnalysisData : AnalysisEntityBasedPredicateAnalysisData + { + public TaintedDataAnalysisData() + : base() + { + } + + public TaintedDataAnalysisData(IDictionary fromData) + : base(fromData) + { + } + + public TaintedDataAnalysisData(TaintedDataAnalysisData fromData) + : base(fromData) + { + } + + public TaintedDataAnalysisData(TaintedDataAnalysisData fromData, TaintedDataAnalysisData data, MapAbstractDomain coreDataAnalysisDomain) + : base(fromData, data, coreDataAnalysisDomain) + { + } + + protected override AbstractValueDomain ValueDomain => TaintedDataAnalysis.ValueDomainInstance; + public override AnalysisEntityBasedPredicateAnalysisData Clone() + { + return new TaintedDataAnalysisData(this); + } + + public override int Compare(AnalysisEntityBasedPredicateAnalysisData other, MapAbstractDomain coreDataAnalysisDomain) + { + return this.BaseCompareHelper(other, coreDataAnalysisDomain); + } + + public override AnalysisEntityBasedPredicateAnalysisData WithMergedData(AnalysisEntityBasedPredicateAnalysisData data, MapAbstractDomain coreDataAnalysisDomain) + { + return new TaintedDataAnalysisData(this, (TaintedDataAnalysisData)data, coreDataAnalysisDomain); + } + + public void Reset(TaintedDataAbstractValue resetValue) + { + base.Reset((analysisEntity, currentValue) => resetValue); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisResult.cs new file mode 100644 index 0000000000000..15bf7465e5164 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Analysis result from execution of on a control flow graph. + /// + internal sealed class TaintedDataAnalysisResult : DataFlowAnalysisResult + { + public TaintedDataAnalysisResult( + DataFlowAnalysisResult dataFlowAnalysisResult, + ImmutableArray taintedDataSourceSinks) + : base(dataFlowAnalysisResult) + { + this.TaintedDataSourceSinks = taintedDataSourceSinks; + } + + public ImmutableArray TaintedDataSourceSinks { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataBlockAnalysisResult.cs new file mode 100644 index 0000000000000..266bf99d32c70 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataBlockAnalysisResult.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using System.Collections.Immutable; + using Microsoft.CodeAnalysis.FlowAnalysis; + using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; + + /// + /// Result from execution of on a basic block. + /// + internal class TaintedDataBlockAnalysisResult : AbstractBlockAnalysisResult + { + public ImmutableDictionary Data { get; } + + public TaintedDataBlockAnalysisResult(BasicBlock basicBlock, TaintedDataAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.CoreAnalysisData.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataConfig.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataConfig.cs new file mode 100644 index 0000000000000..67d51add730d6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataConfig.cs @@ -0,0 +1,367 @@ +// 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 System.Linq; +using System.Threading; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Manages tainted data sources, sanitizers, and sinks for all the + /// different tainted data analysis rules. + /// + /// + /// This is centralized, so that rules that use the same set of sources + /// and sanitizers but have different sinks, can reuse equivalent s, and thus reuse the same dataflow + /// analysis result, so DFA doesn't have be invoked multiple times. + /// + internal class TaintedDataConfig + { + private static readonly BoundedCacheWithFactory s_ConfigsByCompilation = new(); + + /// + /// Caches the results for . + /// + private static ImmutableDictionary> s_sinkKindToSourceInfo + = ImmutableDictionary.Create>(); + + /// + /// Caches the results for . + /// + private static ImmutableDictionary> s_sinkKindToSanitizerInfo + = ImmutableDictionary.Create>(); + + /// + /// Caches the results for . + /// + private static ImmutableDictionary s_sinkKindHasTaintArraySource + = ImmutableDictionary.Create(); + + /// + /// for this instance's . + /// + private WellKnownTypeProvider WellKnownTypeProvider { get; } + +#pragma warning disable CA1721 // Property names should not match get methods + + /// + /// Mapping of sink kind to source symbol map. + /// + private ImmutableDictionary>> SourceSymbolMap { get; } + + /// + /// Mapping of sink kind to sanitizer symbol map. + /// + private ImmutableDictionary>> SanitizerSymbolMap { get; } + + /// + /// Mapping of sink kind to sink symbol map. + /// + private ImmutableDictionary>> SinkSymbolMap { get; } + +#pragma warning restore CA1721 // Property names should not match get methods + + /// + /// Gets a cached for . + /// + /// Whatever is being compiled. + /// The TaintedDataConfig. + public static TaintedDataConfig GetOrCreate(Compilation compilation) + => s_ConfigsByCompilation.GetOrCreateValue(compilation, Create); + + private TaintedDataConfig( + WellKnownTypeProvider wellKnownTypeProvider, + ImmutableDictionary>> sourceSymbolMap, + ImmutableDictionary>> sanitizerSymbolMap, + ImmutableDictionary>> sinkSymbolMap) + { + WellKnownTypeProvider = wellKnownTypeProvider; + SourceSymbolMap = sourceSymbolMap; + SanitizerSymbolMap = sanitizerSymbolMap; + SinkSymbolMap = sinkSymbolMap; + } + + private static TaintedDataConfig Create(Compilation compilation) + { + WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation); + using PooledDictionary>> sourceSymbolMapBuilder = + PooledDictionary>>.GetInstance(); + using PooledDictionary>> sanitizerSymbolMapBuilder = + PooledDictionary>>.GetInstance(); + using PooledDictionary>> sinkSymbolMapBuilder = + PooledDictionary>>.GetInstance(); + + // For tainted data rules with the same set of sources, we'll reuse the same TaintedDataSymbolMap instance. + // Same for sanitizers. + using PooledDictionary, Lazy>> sourcesToSymbolMap = + PooledDictionary, Lazy>>.GetInstance(); + using PooledDictionary, Lazy>> sanitizersToSymbolMap = + PooledDictionary, Lazy>>.GetInstance(); + + // Build a mapping of (sourceSet, sanitizerSet) -> (sinkKinds, sinkSet), so we'll reuse the same TaintedDataSymbolMap instance. + using PooledDictionary<(ImmutableHashSet SourceInfos, ImmutableHashSet SanitizerInfos), (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SinkInfos)> sourceSanitizersToSinks = + PooledDictionary<(ImmutableHashSet SourceInfos, ImmutableHashSet SanitizerInfos), (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SinkInfos)>.GetInstance(); + + // Using LazyThreadSafetyMode.ExecutionAndPublication to avoid instantiating multiple times. + foreach (SinkKind sinkKind in Enum.GetValues(typeof(SinkKind))) + { + ImmutableHashSet sources = GetSourceInfos(sinkKind); + if (!sourcesToSymbolMap.TryGetValue(sources, out Lazy> lazySourceSymbolMap)) + { + lazySourceSymbolMap = new Lazy>( + () => { return new TaintedDataSymbolMap(wellKnownTypeProvider, sources); }, + LazyThreadSafetyMode.ExecutionAndPublication); + sourcesToSymbolMap.Add(sources, lazySourceSymbolMap); + } + + sourceSymbolMapBuilder.Add(sinkKind, lazySourceSymbolMap); + + ImmutableHashSet sanitizers = GetSanitizerInfos(sinkKind); + if (!sanitizersToSymbolMap.TryGetValue(sanitizers, out Lazy> lazySanitizerSymbolMap)) + { + lazySanitizerSymbolMap = new Lazy>( + () => { return new TaintedDataSymbolMap(wellKnownTypeProvider, sanitizers); }, + LazyThreadSafetyMode.ExecutionAndPublication); + sanitizersToSymbolMap.Add(sanitizers, lazySanitizerSymbolMap); + } + + sanitizerSymbolMapBuilder.Add(sinkKind, lazySanitizerSymbolMap); + + ImmutableHashSet sinks = GetSinkInfos(sinkKind); + if (!sourceSanitizersToSinks.TryGetValue((sources, sanitizers), out (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SinkInfos) sinksPair)) + { + sinksPair = (ImmutableHashSet.CreateBuilder(), ImmutableHashSet.CreateBuilder()); + sourceSanitizersToSinks.Add((sources, sanitizers), sinksPair); + } + + sinksPair.SinkKinds.Add(sinkKind); + sinksPair.SinkInfos.UnionWith(sinks); + } + + foreach (KeyValuePair<(ImmutableHashSet SourceInfos, ImmutableHashSet SanitizerInfos), (ImmutableHashSet.Builder SinkKinds, ImmutableHashSet.Builder SinkInfos)> kvp in sourceSanitizersToSinks) + { + ImmutableHashSet sinks = kvp.Value.SinkInfos.ToImmutable(); + Lazy> lazySinkSymbolMap = new Lazy>( + () => { return new TaintedDataSymbolMap(wellKnownTypeProvider, sinks); }, + LazyThreadSafetyMode.ExecutionAndPublication); + foreach (SinkKind sinkKind in kvp.Value.SinkKinds) + { + sinkSymbolMapBuilder.Add(sinkKind, lazySinkSymbolMap); + } + } + + return new TaintedDataConfig( + wellKnownTypeProvider, + sourceSymbolMapBuilder.ToImmutableDictionary(), + sanitizerSymbolMapBuilder.ToImmutableDictionary(), + sinkSymbolMapBuilder.ToImmutableDictionary()); + } + + public TaintedDataSymbolMap GetSourceSymbolMap(SinkKind sinkKind) + { + return this.GetFromMap(sinkKind, this.SourceSymbolMap); + } + + public TaintedDataSymbolMap GetSanitizerSymbolMap(SinkKind sinkKind) + { + return this.GetFromMap(sinkKind, this.SanitizerSymbolMap); + } + + public TaintedDataSymbolMap GetSinkSymbolMap(SinkKind sinkKind) + { + return this.GetFromMap(sinkKind, this.SinkSymbolMap); + } + + public static bool HasTaintArraySource(SinkKind sinkKind) + { + return ImmutableInterlocked.GetOrAdd( + ref s_sinkKindHasTaintArraySource, + sinkKind, + static sinkKind => GetSourceInfos(sinkKind).Any(static o => o.TaintConstantArray)); + } + + private TaintedDataSymbolMap GetFromMap(SinkKind sinkKind, ImmutableDictionary>> map) + where T : ITaintedDataInfo + { + if (map.TryGetValue(sinkKind, out var lazySourceSymbolMap)) + { + return lazySourceSymbolMap.Value; + } + else + { + Debug.Fail($"SinkKind {sinkKind} entry missing from {typeof(T).Name} map"); + return new TaintedDataSymbolMap(this.WellKnownTypeProvider, Enumerable.Empty()); + } + } + + private static ImmutableHashSet GetSourceInfos(SinkKind sinkKind) + { + if (s_sinkKindToSourceInfo.TryGetValue(sinkKind, out var sourceInfo)) + { + return sourceInfo; + } + + switch (sinkKind) + { + case SinkKind.Dll: + case SinkKind.FilePathInjection: + case SinkKind.ProcessCommand: + case SinkKind.Xss: + case SinkKind.Regex: + case SinkKind.Ldap: + case SinkKind.Redirect: + case SinkKind.XPath: + case SinkKind.Xml: + case SinkKind.Xaml: + // All of these use WebInputSources.SourceInfos.AddRange(StringTranferSources.SourceInfos), which is + // the same set as SinkKind.Sql. Delegate the call to SinkKind.Sql to avoid computing separate hash + // sets for identical cases. + sourceInfo = GetSourceInfos(SinkKind.Sql); + break; + + case SinkKind.Sql: + sourceInfo = WebInputSources.SourceInfos.AddRange(StringTranferSources.SourceInfos); + break; + + case SinkKind.InformationDisclosure: + sourceInfo = InformationDisclosureSources.SourceInfos.AddRange(StringTranferSources.SourceInfos); + break; + + case SinkKind.ZipSlip: + sourceInfo = ZipSlipSources.SourceInfos.AddRange(StringTranferSources.SourceInfos); + break; + + case SinkKind.HardcodedEncryptionKey: + sourceInfo = HardcodedSymmetricAlgorithmKeysSources.SourceInfos.AddRange(StringTranferSources.SourceInfos); + break; + + case SinkKind.HardcodedCertificate: + sourceInfo = HardcodedCertificateSources.SourceInfos.AddRange(HardcodedBytesSources.SourceInfos).AddRange(StringTranferSources.SourceInfos); + break; + + default: + Debug.Fail($"Unhandled SinkKind {sinkKind}"); + return ImmutableHashSet.Empty; + } + + return ImmutableInterlocked.GetOrAdd(ref s_sinkKindToSourceInfo, sinkKind, sourceInfo); + } + + private static ImmutableHashSet GetSanitizerInfos(SinkKind sinkKind) + { + if (s_sinkKindToSanitizerInfo.TryGetValue(sinkKind, out var sanitizerInfo)) + { + return sanitizerInfo; + } + + switch (sinkKind) + { + case SinkKind.XPath: + // All of these use PrimitiveTypeConverterSanitizers.SanitizerInfos.AddRange(AnySanitizers.SanitizerInfos), + // which is the same set as SinkKind.Sql. Delegate the call to SinkKind.Sql to avoid computing + // separate hash sets for identical cases. + sanitizerInfo = GetSanitizerInfos(SinkKind.Sql); + break; + + case SinkKind.Sql: + sanitizerInfo = PrimitiveTypeConverterSanitizers.SanitizerInfos.AddRange(AnySanitizers.SanitizerInfos); + break; + + case SinkKind.Xss: + sanitizerInfo = XssSanitizers.SanitizerInfos.AddRange(PrimitiveTypeConverterSanitizers.SanitizerInfos).AddRange(AnySanitizers.SanitizerInfos); + break; + + case SinkKind.Ldap: + sanitizerInfo = LdapSanitizers.SanitizerInfos.AddRange(AnySanitizers.SanitizerInfos); + break; + + case SinkKind.Xml: + sanitizerInfo = XmlSanitizers.SanitizerInfos.AddRange(PrimitiveTypeConverterSanitizers.SanitizerInfos).AddRange(AnySanitizers.SanitizerInfos); + break; + + case SinkKind.Dll: + case SinkKind.InformationDisclosure: + case SinkKind.FilePathInjection: + case SinkKind.ProcessCommand: + case SinkKind.Regex: + case SinkKind.Redirect: + case SinkKind.Xaml: + case SinkKind.HardcodedEncryptionKey: + case SinkKind.HardcodedCertificate: + sanitizerInfo = AnySanitizers.SanitizerInfos; + break; + + case SinkKind.ZipSlip: + sanitizerInfo = ZipSlipSanitizers.SanitizerInfos.AddRange(AnySanitizers.SanitizerInfos); + break; + + default: + Debug.Fail($"Unhandled SinkKind {sinkKind}"); + return ImmutableHashSet.Empty; + } + + return ImmutableInterlocked.GetOrAdd(ref s_sinkKindToSanitizerInfo, sinkKind, sanitizerInfo); + } + + private static ImmutableHashSet GetSinkInfos(SinkKind sinkKind) + { + switch (sinkKind) + { + case SinkKind.Sql: + return SqlSinks.SinkInfos; + + case SinkKind.Dll: + return DllSinks.SinkInfos; + + case SinkKind.InformationDisclosure: + case SinkKind.Xss: + return WebOutputSinks.SinkInfos; + + case SinkKind.FilePathInjection: + return FilePathInjectionSinks.SinkInfos; + + case SinkKind.ProcessCommand: + return ProcessCommandSinks.SinkInfos; + + case SinkKind.Regex: + return RegexSinks.SinkInfos; + + case SinkKind.Ldap: + return LdapSinks.SinkInfos; + + case SinkKind.Redirect: + return RedirectSinks.SinkInfos; + + case SinkKind.XPath: + return XPathSinks.SinkInfos; + + case SinkKind.Xml: + return XmlSinks.SinkInfos; + + case SinkKind.Xaml: + return XamlSinks.SinkInfos; + + case SinkKind.ZipSlip: + return ZipSlipSinks.SinkInfos; + + case SinkKind.HardcodedEncryptionKey: + return HardcodedEncryptionKeySinks.SinkInfos; + + case SinkKind.HardcodedCertificate: + return HardcodedCertificateSinks.SinkInfos; + + default: + Debug.Fail($"Unhandled SinkKind {sinkKind}"); + return ImmutableHashSet.Empty; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataProperties.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataProperties.cs new file mode 100644 index 0000000000000..4b11780648542 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataProperties.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class TaintedDataProperties + { + /// + /// Language agnostic indexer name used in tainted data rules. + /// + public const string IndexerName = "this[]"; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSourceSink.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSourceSink.cs new file mode 100644 index 0000000000000..41956360e228f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSourceSink.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Represents endpoints of tainted data flowing from sources to a sink. + /// + internal sealed class TaintedDataSourceSink + { + public TaintedDataSourceSink(SymbolAccess sink, ImmutableHashSet sinkKinds, ImmutableHashSet sourceOrigins) + { + Sink = sink ?? throw new ArgumentNullException(nameof(sink)); + SinkKinds = sinkKinds; + SourceOrigins = sourceOrigins; + } + + /// + /// of the sink that the tainted data enters. + /// + public SymbolAccess Sink { get; } + + /// + /// Kind of sink (e.g. SQL). + /// + public ImmutableHashSet SinkKinds { get; } + + /// + /// s of the origins of the tainted data. + /// + public ImmutableHashSet SourceOrigins { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMap.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMap.cs new file mode 100644 index 0000000000000..3e2312cd11b15 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMap.cs @@ -0,0 +1,175 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Mapping of to (tainted data source/sanitizer/sink info). + /// + internal class TaintedDataSymbolMap : IEquatable?> + where TInfo : ITaintedDataInfo + { + private static bool TryResolveDependencies(TInfo info, WellKnownTypeProvider wellKnownTypeProvider) + { + foreach (string dependency in info.DependencyFullTypeNames) + { + if (!wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(dependency, out INamedTypeSymbol? _)) + { + return false; + } + } + + return true; + } + public TaintedDataSymbolMap(WellKnownTypeProvider wellKnownTypeProvider, IEnumerable taintedDataInfos) + { + if (wellKnownTypeProvider == null) + { + throw new ArgumentNullException(nameof(wellKnownTypeProvider)); + } + + if (taintedDataInfos == null) + { + throw new ArgumentNullException(nameof(taintedDataInfos)); + } + + ImmutableDictionary.Builder concreteInfosBuilder = ImmutableDictionary.CreateBuilder(); + ImmutableDictionary.Builder interfaceInfosBuilder = ImmutableDictionary.CreateBuilder(); + + foreach (TInfo info in taintedDataInfos) + { + if (!TryResolveDependencies(info, wellKnownTypeProvider)) + { + continue; + } + + if (wellKnownTypeProvider.TryGetOrCreateTypeByMetadataName(info.FullTypeName, out INamedTypeSymbol? namedTypeSymbol)) + { + if (info.IsInterface) + { + interfaceInfosBuilder[namedTypeSymbol] = info; + } + else + { + concreteInfosBuilder[namedTypeSymbol] = info; + } + + if (info.RequiresValueContentAnalysis) + { + RequiresValueContentAnalysis = true; + } + + if (info.RequiresParameterReferenceAnalysis) + { + RequiresParameterReferenceAnalysis = true; + } + } + } + + this.ConcreteInfos = concreteInfosBuilder.ToImmutable(); + this.InterfaceInfos = interfaceInfosBuilder.ToImmutable(); + } + + /// + /// Mapping for concrete types. + /// + private ImmutableDictionary ConcreteInfos { get; } + + /// + /// Mapping for interface types. + /// + private ImmutableDictionary InterfaceInfos { get; } + + /// + /// Indicates that this mapping is empty, i.e. there are no types referenced by the compilation represented by the . + /// + public bool IsEmpty => this.ConcreteInfos.IsEmpty && this.InterfaceInfos.IsEmpty; + + /// + /// Indicates that any in this uses s. + /// + public bool RequiresValueContentAnalysis { get; } + + /// + /// Indicates that is required. + /// + public bool RequiresParameterReferenceAnalysis { get; } + + /// + /// Gets an enumeration of infos for the given type. + /// + /// Type to find infos for. + /// Relevant infos for the given type. + public IEnumerable GetInfosForType(INamedTypeSymbol namedTypeSymbol) + { + if (namedTypeSymbol == null) + { + Debug.Fail("Expected non-null 'namedTypeSymbol'"); + + yield break; + } + + if (!this.InterfaceInfos.IsEmpty) + { + if (namedTypeSymbol.TypeKind == TypeKind.Interface + && this.InterfaceInfos.TryGetValue(namedTypeSymbol.OriginalDefinition, out var infoForInterfaceSymbol)) + { + yield return infoForInterfaceSymbol; + } + + foreach (INamedTypeSymbol interfaceSymbol in namedTypeSymbol.AllInterfaces) + { + if (this.InterfaceInfos.TryGetValue(interfaceSymbol.OriginalDefinition, out var info)) + { + yield return info; + } + } + } + + if (!this.ConcreteInfos.IsEmpty) + { + foreach (INamedTypeSymbol typeSymbol in namedTypeSymbol.GetBaseTypesAndThis()) + { + if (this.ConcreteInfos.TryGetValue(typeSymbol.OriginalDefinition, out var info)) + { + yield return info; + } + } + } + } + + public bool Equals(TaintedDataSymbolMap? other) + { + if (Object.ReferenceEquals(this, other)) + { + return true; + } + + return other != null + && this.InterfaceInfos == other.InterfaceInfos + && this.ConcreteInfos == other.ConcreteInfos; + } + + public override bool Equals(object obj) + { + return this.Equals(obj as TaintedDataSymbolMap); + } + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(this.InterfaceInfos, ref hashCode); + HashUtilities.Combine(this.ConcreteInfos, ref hashCode); + return hashCode.ToHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMapExtensions.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMapExtensions.cs new file mode 100644 index 0000000000000..d427639baf70e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedDataSymbolMapExtensions.cs @@ -0,0 +1,235 @@ +// 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.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + internal static class TaintedDataSymbolMapExtensions + { + /// + /// Determines if the given method is a tainted data source and get the tainted target set. + /// + /// + /// + /// + /// If the method needs to do PointsToAnalysis, the PointsToAnalysis result will be produced by the passed value factory. + /// If the method needs to do ValueContentAnalysis, the ValueContentAnalysis result will be produced by the passed value factory. + /// + /// + public static bool IsSourceMethod( + this TaintedDataSymbolMap sourceSymbolMap, + IMethodSymbol method, + ImmutableArray arguments, + Lazy pointsToFactory, + Lazy<(PointsToAnalysisResult? p, ValueContentAnalysisResult? v)> valueContentFactory, + [NotNullWhen(returnValue: true)] out PooledHashSet? allTaintedTargets) + { + allTaintedTargets = null; + PointsToAnalysisResult? pointsToAnalysisResult = null; + ValueContentAnalysisResult? valueContentAnalysisResult = null; + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(method.ContainingType)) + { + foreach ((MethodMatcher methodMatcher, ImmutableHashSet taintedTargets) in sourceInfo.TaintedMethods) + { + if (methodMatcher(method.Name, arguments)) + { + allTaintedTargets ??= PooledHashSet.GetInstance(); + + allTaintedTargets.UnionWith(taintedTargets); + } + } + + foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(PointsToCheck pointsToCheck, string)> pointsToTaintedTargets) in sourceInfo.TaintedMethodsNeedsPointsToAnalysis) + { + if (pointsToTaintedTargets.Any() && methodMatcher(method.Name, arguments)) + { + pointsToAnalysisResult ??= pointsToFactory.Value; + if (pointsToAnalysisResult == null) + { + break; + } + + IEnumerable<(PointsToCheck, string target)> positivePointsToTaintedTargets = pointsToTaintedTargets.Where(s => + s.pointsToCheck( + arguments.Select(o => + pointsToAnalysisResult[o.Kind, o.Syntax]).ToImmutableArray())); + if (positivePointsToTaintedTargets.Any()) + { + allTaintedTargets ??= PooledHashSet.GetInstance(); + + allTaintedTargets.UnionWith(positivePointsToTaintedTargets.Select(s => s.target)); + } + } + } + + foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(ValueContentCheck valueContentCheck, string)> valueContentTaintedTargets) in sourceInfo.TaintedMethodsNeedsValueContentAnalysis) + { + if (valueContentTaintedTargets.Any() && methodMatcher(method.Name, arguments)) + { + pointsToAnalysisResult ??= valueContentFactory.Value.p; + valueContentAnalysisResult ??= valueContentFactory.Value.v; + if (pointsToAnalysisResult == null || valueContentAnalysisResult == null) + { + break; + } + + IEnumerable<(ValueContentCheck, string target)> positiveValueContentTaintedTargets = valueContentTaintedTargets.Where(s => + s.valueContentCheck( + arguments.Select(o => pointsToAnalysisResult[o.Kind, o.Syntax]).ToImmutableArray(), + arguments.Select(o => valueContentAnalysisResult[o.Kind, o.Syntax]).ToImmutableArray())); + if (positiveValueContentTaintedTargets.Any()) + { + allTaintedTargets ??= PooledHashSet.GetInstance(); + + allTaintedTargets.UnionWith(positiveValueContentTaintedTargets.Select(s => s.target)); + } + } + } + } + + return allTaintedTargets != null; + } + + /// + /// Determines if the given property is a tainted data source. + /// + /// + /// + /// + public static bool IsSourceProperty(this TaintedDataSymbolMap sourceSymbolMap, IPropertySymbol propertySymbol) + { + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(propertySymbol.ContainingType)) + { + if (sourceInfo.TaintedProperties.Contains(propertySymbol.MetadataName)) + { + return true; + } + } + + return false; + } + + /// + /// Determines if the given parameter is a tainted data source. + /// + /// + /// + /// + public static bool IsSourceParameter(this TaintedDataSymbolMap sourceSymbolMap, IParameterSymbol parameterSymbol, WellKnownTypeProvider wellKnownTypeProvider) + { + ISymbol containingSymbol = parameterSymbol.ContainingSymbol; + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(containingSymbol.ContainingType)) + { + if (sourceInfo.TaintedArguments.Any(match => match(parameterSymbol, wellKnownTypeProvider))) + { + return true; + } + } + + return false; + } + + /// + /// Determines if the given array can be a tainted data source when its elements are all constant. + /// + /// + /// + /// + public static bool IsSourceConstantArrayOfType( + this TaintedDataSymbolMap sourceSymbolMap, + IArrayTypeSymbol arrayTypeSymbol, + IArrayInitializerOperation arrayInitializerOperation) + { + if (arrayTypeSymbol.ElementType is INamedTypeSymbol elementType) + { + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(elementType)) + { + if (sourceInfo.TaintConstantArray + && (sourceInfo.ConstantArrayLengthMatcher == null + || sourceInfo.ConstantArrayLengthMatcher(arrayInitializerOperation.ElementValues.Length))) + { + return true; + } + } + } + + return false; + } + + /// + /// Determines if the method taints other arguments cause some arguments are tainted. + /// + /// + /// + /// + /// The set of parameter pairs (tainted source parameter name, tainted end parameter name). + /// + public static bool IsSourceTransferMethod( + this TaintedDataSymbolMap sourceSymbolMap, + IMethodSymbol method, + ImmutableArray arguments, + ISet taintedParameterNames, + [NotNullWhen(returnValue: true)] out PooledHashSet<(string, string)>? taintedParameterPairs) + { + taintedParameterPairs = null; + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(method.ContainingType)) + { + foreach ((MethodMatcher methodMatcher, ImmutableHashSet<(string source, string end)> sourceToEnds) in sourceInfo.TransferMethods) + { + if (methodMatcher(method.Name, arguments)) + { + taintedParameterPairs ??= PooledHashSet<(string, string)>.GetInstance(); + + taintedParameterPairs.UnionWith(sourceToEnds.Where(s => taintedParameterNames.Contains(s.source))); + } + } + } + + return taintedParameterPairs != null; + } + + /// + /// Determines if the property taints the instance. + /// + public static bool IsSourceTransferProperty( + this TaintedDataSymbolMap sourceSymbolMap, + IPropertyReferenceOperation propertyReferenceOperation) + { + if (propertyReferenceOperation.Instance?.Type is not INamedTypeSymbol namedType) + { + return false; + } + + string name = propertyReferenceOperation.Member.Name; + if (propertyReferenceOperation.Member.Language != LanguageNames.CSharp && propertyReferenceOperation.Member.IsIndexer()) + { + name = TaintedDataProperties.IndexerName; // In VB.NET for example the indexer name is `Item`. However let's keep the SourceInfo configuration language agnostic. + } + + foreach (SourceInfo sourceInfo in sourceSymbolMap.GetInfosForType(namedType)) + { + if (sourceInfo.TransferProperties.Contains(name)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedTargetValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedTargetValue.cs new file mode 100644 index 0000000000000..aee9e7f3f72af --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/TaintedTargetValue.cs @@ -0,0 +1,19 @@ +// 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 Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class TaintedTargetValue + { + /// + /// Taint return value. + /// + public const string Return = ".Return"; + + /// + /// Taint the instance value. + /// + public const string This = ".This"; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs new file mode 100644 index 0000000000000..3e1d960ede4a2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebInputSources.cs @@ -0,0 +1,799 @@ +// 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.Concurrent; +using System.Collections.Immutable; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class WebInputSources + { + /// + /// s for web input tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Cached information if the specified symbol is a Asp.Net Core Controller: (compilation) -> ((class symbol) -> (is Controller)) + /// + private static readonly BoundedCacheWithFactory> s_classIsControllerByCompilation = new(); + + /// + /// Statically constructs. + /// + static WebInputSources() + { + var dependencyFullTypeNames = ImmutableArray.Create(WellKnownTypeNames.MicrosoftAspNetCoreMvcControllerBase, + WellKnownTypeNames.MicrosoftAspNetCoreMvcControllerAttribute, + WellKnownTypeNames.MicrosoftAspNetCoreMvcNonControllerAttribute, + WellKnownTypeNames.MicrosoftAspNetCoreMvcNonActionAttribute, + WellKnownTypeNames.MicrosoftAspNetCoreMvcFromServicesAttribute); + + var sourceInfosBuilder = PooledHashSet.GetInstance(); + + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.MicrosoftAspNetCoreHttpHttpRequest, + isInterface: false, + taintedProperties: new string[] { + "Body", + "ContentType", + "Cookies", + "Form", + "Headers", + "Host", + "Method", + "Path", + "PathBase", + "Protocol", + "Query", + "QueryString", + "RouteValues", + "Scheme", + }, + taintedMethods: new string[] { + "ReadFormAsync", + }); + + sourceInfosBuilder.AddSourceInfoSpecifyingTaintedTargets( + WellKnownTypeNames.SystemWebHttpServerUtility, + isInterface: false, + taintedProperties: null, + taintedMethodsNeedsPointsToAnalysis: null, + taintedMethodsNeedsValueContentAnalysis: null, + transferMethods: new (MethodMatcher, (string, string)[])[]{ + ( + (methodName, arguments) => + methodName == "HtmlEncode" && + arguments.Length == 2, + new (string, string)[]{ + ("s", "output"), + } + ) + }); + + sourceInfosBuilder.AddSourceInfo( + // checking all System.Object derived types is expensive, so it first checks if MicrosoftAspNetCoreMvcControllerBase is resolvable + dependencyFullTypeNames, + WellKnownTypeNames.SystemObject, + new ParameterMatcher[]{ + (parameter, wellKnownTypeProvider) => { + if (parameter.ContainingSymbol is not IMethodSymbol methodSymbol + || methodSymbol.ContainingSymbol is not INamedTypeSymbol typeSymbol) + { + return false; + } + + var classCache = s_classIsControllerByCompilation.GetOrCreateValue(wellKnownTypeProvider.Compilation, (compilation) => new ConcurrentDictionary()); + if (!classCache.TryGetValue(typeSymbol, out bool isController)) + { + if ((!typeSymbol.GetBaseTypesAndThis().Any(x => x.Name.EndsWith("Controller", System.StringComparison.Ordinal)) + && (!typeSymbol.HasDerivedTypeAttribute(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreMvcControllerAttribute)))) + || typeSymbol.HasDerivedTypeAttribute(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreMvcNonControllerAttribute))) + { + isController = false; + } + else + { + isController = true; + } + + classCache.TryAdd(typeSymbol, isController); + } + + if (!isController) + { + return false; + } + + if (methodSymbol.DeclaredAccessibility != Accessibility.Public + || methodSymbol.IsConstructor() + || methodSymbol.IsStatic + || methodSymbol.MethodKind != MethodKind.Ordinary + || methodSymbol.HasDerivedMethodAttribute(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreMvcNonActionAttribute))) + { + return false; + } + + if (parameter.HasAnyAttribute(wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftAspNetCoreMvcFromServicesAttribute))) + { + return false; + } + + return true; + } + }); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebHttpCookie, + isInterface: false, + taintedProperties: new string[] { + "Domain", + "Name", + "Item", + "Path", + "Value", + "Values", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebHttpRequest, + isInterface: false, + taintedProperties: new string[] { + "AcceptTypes", + "AnonymousID", + // Anything potentially bad in Browser? + "ContentType", + "Cookies", + "Files", + "Form", + "Headers", + "HttpMethod", + "InputStream", + "Item", + "Params", + "Path", + "PathInfo", + "QueryString", + "RawUrl", + "RequestType", + "Url", + "UrlReferrer", + "UserAgent", + "UserLanguages", + }, + taintedMethods: new string[] { + "BinaryRead", + "GetBufferedInputStream", + "GetBufferlessInputStream", + }); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebHttpRequestBase, + isInterface: false, + taintedProperties: new string[] { + "AcceptTypes", + "AnonymousID", + // Anything potentially bad in Browser? + "ContentType", + "Cookies", + "Files", + "Form", + "Headers", + "HttpMethod", + "InputStream", + "Item", + "Params", + "Path", + "PathInfo", + "QueryString", + "RawUrl", + "RequestType", + "Url", + "UrlReferrer", + "UserAgent", + "UserLanguages", + }, + taintedMethods: new string[] { + "BinaryRead", + "GetBufferedInputStream", + "GetBufferlessInputStream", + }); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebHttpRequestWrapper, + isInterface: false, + taintedProperties: new string[] { + "AcceptTypes", + "AnonymousID", + // Anything potentially bad in Browser? + "ContentType", + "Cookies", + "Files", + "Form", + "Headers", + "HttpMethod", + "InputStream", + "Item", + "Params", + "Path", + "PathInfo", + "QueryString", + "RawUrl", + "RequestType", + "Url", + "UrlReferrer", + "UserAgent", + "UserLanguages", + }, + taintedMethods: new string[] { + "BinaryRead", + "GetBufferedInputStream", + "GetBufferlessInputStream", + }); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIAdaptersPageAdapter, + isInterface: false, + taintedProperties: new string[] { + "QueryString", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIDataBoundLiteralControl, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIDesignerDataBoundLiteralControl, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIHtmlControlsHtmlInputControl, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIIndexedString, + isInterface: false, + taintedProperties: new string[] { + "Value" }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUILiteralControl, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIResourceBasedLiteralControl, + isInterface: false, + taintedProperties: new string[] { + "Text" + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUISimplePropertyEntry, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIStateItem, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIStringPropertyBuilder, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUITemplateBuilder, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUITemplateParser, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsBaseValidator, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsBulletedList, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsButton, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsButtonColumn, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsButtonField, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsChangePassword, + isInterface: false, + taintedProperties: new string[] { + "TextBoxStyle", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsCheckBox, + isInterface: false, + taintedProperties: new string[] { + "Text", + "TextAlign", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsCheckBoxField, + isInterface: false, + taintedProperties: new string[] { + "Text", + "TextAlign", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsCommandEventArgs, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsCreateUserWizard, + isInterface: false, + taintedProperties: new string[] { + "TextBoxStyle", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsDataKey, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsDataList, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsDetailsView, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsDetailsViewInsertEventArgs, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsDetailsViewUpdateEventArgs, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsFormView, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsFormViewInsertEventArgs, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsFormViewUpdateEventArgs, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsGridView, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsHiddenField, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLink, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLinkColumn, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLinkField, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsImageButton, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsLabel, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsLinkButton, + isInterface: false, + taintedProperties: new string[] { + "CommandArgument", + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsListControl, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsListItem, + isInterface: false, + taintedProperties: new string[] { + "Text", + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsLiteral, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsLogin, + isInterface: false, + taintedProperties: new string[] { + "TextBoxStyle", + "TextLayout", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsMenu, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsMenuItem, + isInterface: false, + taintedProperties: new string[] { + "Text", + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsMenuItemBinding, + isInterface: false, + taintedProperties: new string[] { + "Text", + "TextField", + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsPasswordRecovery, + isInterface: false, + taintedProperties: new string[] { + "TextBoxStyle", + "TextLayout", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsQueryStringParameter, + isInterface: false, + taintedProperties: new string[] { + "QueryStringField", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsRadioButtonList, + isInterface: false, + taintedProperties: new string[] { + "TextAlign", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsServerValidateEventArgs, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsTableCell, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsTextBox, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsTreeNode, + isInterface: false, + taintedProperties: new string[] { + "Text", + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsTreeNodeBinding, + isInterface: false, + taintedProperties: new string[] { + "Text", + "TextField", + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsTreeView, + isInterface: false, + taintedProperties: new string[] { + "SelectedValue", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsUnit, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsAppearanceEditorPart, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsPersonalizationEntry, + isInterface: false, + taintedProperties: new string[] { + "Value", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCatalogAddVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCatalogCloseVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCloseVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsCancelVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsCloseVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsConfigureVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsConnectVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsDisconnectVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartDeleteVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorApplyVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorCancelVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorOKVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartExportVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartHeaderCloseVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartHelpVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartMinimizeVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartRestoreVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartVerb, + isInterface: false, + taintedProperties: new string[] { + "Text", + }, + taintedMethods: null); + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemWebUIITextControl, + isInterface: true, + taintedProperties: new string[] { + "Text" + }, + taintedMethods: null); + SourceInfos = sourceInfosBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebOutputSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebOutputSinks.cs new file mode 100644 index 0000000000000..f7de30645a97a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/WebOutputSinks.cs @@ -0,0 +1,567 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + /// + /// Shared sinks between InformationDisclosure and XSS. + /// + internal static class WebOutputSinks + { + public static ImmutableHashSet SinkInfos { get; } + + static WebOutputSinks() + { + // TODO paulming: Review why InformationDisclosure and XSS sinks are different. + var builder = PooledHashSet.GetInstance(); + + SinkKind[] sinkKinds = new SinkKind[] { SinkKind.InformationDisclosure, SinkKind.Xss }; + + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIITextControl, + sinkKinds, + isInterface: true, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { "Text" }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebHttpResponseBase, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("Write", new[] { "ch", "obj", "s", "buffer" } ), + ("BinaryWrite", new[] { "buffer" } ), + ("TransmitFile", new[] { "filename" } ), + ("WriteFile", new[] { "filename" } ) + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebHttpResponse, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("Write", new[] { "ch", "obj", "s", "buffer" } ), + ("BinaryWrite", new[] { "buffer" } ), + ("TransmitFile", new[] { "filename" } ), + ("WriteFile", new[] { "filename" } ) + }); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIDesignerDataBoundLiteralControl, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIHtmlControlsHtmlContainerControl, // Covers HtmlSelect, HtmlTable, HtmlTableRow + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerHtml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIHtmlControlsHtmlTitle, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIHtmlTextWriter, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "AddAttribute", new[] { "value" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUILiteralControl, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIResourceBasedLiteralControl, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUITemplateBuilder, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUITemplateParser, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsBaseDataList, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsBaseValidator, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsButton, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsButtonColumn, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsButtonField, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsCalendar, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsCheckBox, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsCheckBoxField, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsDetailsView, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsFormView, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsGridView, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLink, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLinkColumn, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsHyperLinkField, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsImageButton, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsLabel, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsLinkButton, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsListControl, // Covers BulletedList, CheckBoxList, RadioButtonList + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsListItem, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsLiteral, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsMenuItem, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsMenuItemBinding, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + "TextField", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsRepeatInfo, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsTable, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Caption", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsTableCell, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsTextBox, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + "TextMode", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsTreeNode, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsTreeNodeBinding, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + "TextField", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCatalogAddVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCatalogCloseVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartCloseVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsCancelVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsCloseVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsConfigureVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsConnectVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectionsDisconnectVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartConnectVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartDeleteVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorApplyVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorCancelVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditorOKVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartEditVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartExportVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartHeaderCloseVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartHelpVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartMinimizeVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartRestoreVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsWebPartsWebPartVerb, + sinkKinds, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "Text", + }, + sinkMethodParameters: null); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XPathSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XPathSinks.cs new file mode 100644 index 0000000000000..df19776f9004c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XPathSinks.cs @@ -0,0 +1,108 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class XPathSinks + { + /// + /// s for tainted data XPath injection sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static XPathSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIPageTheme, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "XPath", new[] { "xPathExpression" }), + ( "XPathSelect", new[] { "xPathExpression" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUITemplateControl, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "XPath", new[] { "xPathExpression" }), + ( "XPathSelect", new[] { "xPathExpression" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIWebControlsXmlDataSource, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "XPath", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemWebUIXPathBinder, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Eval", new[] { "xPath" }), + ( "Select", new[] { "xPath" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlSchemaXmlSchemaXPath, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "XPath", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlNode, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "SelectNodes", new[] { "xpath" }), + ( "SelectSingleNode", new[] { "xpath" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXPathXPathExpression, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Compile", new[] { "xpath" }), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXPathXPathNavigator, + SinkKind.XPath, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Compile", new[] { "xpath" }), + ( "Evaluate", new[] { "xpath" }), + ( "Matches", new[] { "xpath" }), + ( "Select", new[] { "xpath" }), + ( "SelectAncestors", new[] { "name" }), + ( "SelectChildren", new[] { "name" }), + ( "SelectDescendants", new[] { "name" }), + ( "SelectSingleNode", new[] { "xpath" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XamlSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XamlSinks.cs new file mode 100644 index 0000000000000..14e64c4b29036 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XamlSinks.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class XamlSinks + { + /// + /// s for tainted data XAML injection sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static XamlSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemWindowsMarkupXamlReader, + SinkKind.Xaml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "Load", new[] { "stream", "reader", "xaml" }), + ( "LoadAsync", new[] { "stream", "reader" }), + ( "LoadWithInitialTemplateValidation", new[] { "xaml" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSanitizers.cs new file mode 100644 index 0000000000000..ebc32a0969d04 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSanitizers.cs @@ -0,0 +1,51 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class XmlSanitizers + { + /// + /// s for XML injection sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static XmlSanitizers() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationAntiXss, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "XmlAttributeEncode", + "XmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "XmlAttributeEncode", + "XmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebSecurityAntiXssAntiXssEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "XmlAttributeEncode", + "XmlEncode", + }); + + // Consider SecurityElement.Escape(). + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSinks.cs new file mode 100644 index 0000000000000..501c16cf9af40 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XmlSinks.cs @@ -0,0 +1,97 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class XmlSinks + { + /// + /// s for tainted data Xml injection sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static XmlSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlAttribute, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlDocument, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlDocumentFragment, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlElement, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlEntity, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlNode, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlNotation, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: new[] { + "InnerXml", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemXmlXmlTextWriter, + SinkKind.Xml, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ( "WriteRaw", new[] { "buffer", "data" }), + }); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XssSanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XssSanitizers.cs new file mode 100644 index 0000000000000..611d391e07032 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/XssSanitizers.cs @@ -0,0 +1,136 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class XssSanitizers + { + /// + /// s for primitive type conversion tainted data sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static XssSanitizers() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationAntiXss, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + "XmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationAntiXssEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + "XmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.MicrosoftSecurityApplicationUnicodeCharacterEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + "XmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemIDisposable, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "Dispose", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebHttpServerUtility, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new (MethodMatcher, (string taintedArgument, string sanitizedArgument)[])[] { + ( + (methodName, arguments) => methodName == "HtmlEncode" && arguments.Length == 1, + new[] { ("s", TaintedTargetValue.Return) } + ), + ( + (methodName, arguments) => methodName == "HtmlEncode" && arguments.Length == 2, + new[] { ("s", "output") } + ), + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebHttpServerUtilityBase, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebHttpServerUtilityWrapper, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebHttpUtility, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebSecurityAntiXssAntiXssEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + "XmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebSecurityAntiXssUnicodeCharacterEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + "XmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebUIHtmlTextWriter, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "WriteHtmlAttributeEncode", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemWebUtilHttpEncoder, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "HtmlAttributeEncode", + "HtmlEncode", + }); + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSanitizers.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSanitizers.cs new file mode 100644 index 0000000000000..1591b3d3593f7 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSanitizers.cs @@ -0,0 +1,42 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class ZipSlipSanitizers + { + /// + /// s for zip slip tainted data sanitizers. + /// + public static ImmutableHashSet SanitizerInfos { get; } + + static ZipSlipSanitizers() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemIOPath, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "GetFileName", + }); + builder.AddSanitizerInfo( + WellKnownTypeNames.SystemString, + isInterface: false, + isConstructorSanitizing: false, + sanitizingMethods: new[] { + "Substring", + }, + sanitizingInstanceMethods: new[] { + "StartsWith", + }); + + SanitizerInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSinks.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSinks.cs new file mode 100644 index 0000000000000..0c0ecf693a3bf --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSinks.cs @@ -0,0 +1,72 @@ +// 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 Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class ZipSlipSinks + { + /// + /// s for tainted data zip slip sinks. + /// + public static ImmutableHashSet SinkInfos { get; } + + static ZipSlipSinks() + { + var builder = PooledHashSet.GetInstance(); + + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOCompressionZipFileExtensions, + SinkKind.ZipSlip, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("ExtractToFile", new[] { "destinationFileName" } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOFile, + SinkKind.ZipSlip, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + ("Open", new[] { "path" } ), + ("OpenWrite", new[] { "path" } ), + ("OpenCreate", new[] { "path" } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemDirectoryDirectoryEntry, + SinkKind.ZipSlip, + isInterface: false, + isAnyStringParameterInConstructorASink: false, + sinkProperties: null, + sinkMethodParameters: new[] { + (".ctor", new[] { "path", "adsObject" } ), + }); + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOFileStream, + SinkKind.ZipSlip, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new[] { + "path", + }, + sinkMethodParameters: null); + builder.AddSinkInfo( + WellKnownTypeNames.SystemIOFileInfo, + SinkKind.ZipSlip, + isInterface: false, + isAnyStringParameterInConstructorASink: true, + sinkProperties: new[] { + "fileName", + }, + sinkMethodParameters: null); + + SinkInfos = builder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSources.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSources.cs new file mode 100644 index 0000000000000..a2dba46bee41a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/TaintedDataAnalysis/ZipSlipSources.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Analyzer.Utilities.PooledObjects; + +namespace Analyzer.Utilities.FlowAnalysis.Analysis.TaintedDataAnalysis +{ + internal static class ZipSlipSources + { + /// + /// s for zip slip tainted data sources. + /// + public static ImmutableHashSet SourceInfos { get; } + + /// + /// Statically constructs. + /// + static ZipSlipSources() + { + var sourceInfosBuilder = PooledHashSet.GetInstance(); + + sourceInfosBuilder.AddSourceInfo( + WellKnownTypeNames.SystemIOCompressionZipArchiveEntry, + isInterface: false, + taintedProperties: new string[] { + "FullName", + }, + taintedMethods: null); + + SourceInfos = sourceInfosBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContainsNonLiteralState.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContainsNonLiteralState.cs new file mode 100644 index 0000000000000..27341a9e1b20b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContainsNonLiteralState.cs @@ -0,0 +1,21 @@ +// 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.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + /// + /// Value state for presence of non-literal values for / tracked by . + /// + public enum ValueContainsNonLiteralState + { + /// The variable state is invalid due to predicate analysis. + Invalid, + /// State is undefined. + Undefined, + /// The variable does not contain any instances of a non-literal. + No, + /// The variable may or may not contain instances of a non-literal. + Maybe, + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAbstractValue.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAbstractValue.cs new file mode 100644 index 0000000000000..8616793a996ce --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAbstractValue.cs @@ -0,0 +1,617 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + /// + /// Abstract value content data value for / tracked by . + /// + public partial class ValueContentAbstractValue : CacheBasedEquatable + { + // Ensure we bound the number of value content literals and avoid infinite analysis iterations. + private const int LiteralsBound = 10; + + public static ValueContentAbstractValue UndefinedState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Empty, ValueContainsNonLiteralState.Undefined); + public static ValueContentAbstractValue InvalidState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Empty, ValueContainsNonLiteralState.Invalid); + public static ValueContentAbstractValue MayBeContainsNonLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Empty, ValueContainsNonLiteralState.Maybe); + public static ValueContentAbstractValue DoesNotContainLiteralOrNonLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Empty, ValueContainsNonLiteralState.No); + public static ValueContentAbstractValue ContainsNullLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create((object?)null), ValueContainsNonLiteralState.No); + public static ValueContentAbstractValue ContainsEmptyStringLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create(string.Empty), ValueContainsNonLiteralState.No); + public static ValueContentAbstractValue ContainsZeroIntergralLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create(0), ValueContainsNonLiteralState.No); + public static ValueContentAbstractValue ContainsOneIntergralLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create(1), ValueContainsNonLiteralState.No); + private static ValueContentAbstractValue ContainsTrueLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create(true), ValueContainsNonLiteralState.No); + private static ValueContentAbstractValue ContainsFalseLiteralState { get; } = new ValueContentAbstractValue(ImmutableHashSet.Create(false), ValueContainsNonLiteralState.No); + + private ValueContentAbstractValue(ImmutableHashSet literalValues, ValueContainsNonLiteralState nonLiteralState) + { + LiteralValues = literalValues; + NonLiteralState = nonLiteralState; + } + + internal static ValueContentAbstractValue Create(object literal, ITypeSymbol type) + { + switch (type.SpecialType) + { + case SpecialType.System_Byte: + case SpecialType.System_Double: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_Int64: + case SpecialType.System_UInt16: + case SpecialType.System_UInt32: + case SpecialType.System_UInt64: + case SpecialType.System_SByte: + case SpecialType.System_Single: + if (DiagnosticHelpers.TryConvertToUInt64(literal, type.SpecialType, out ulong convertedValue) && + convertedValue == 0) + { + return ContainsZeroIntergralLiteralState; + } + + break; + + case SpecialType.System_String: + if (((string)literal).Length == 0) + { + return ContainsEmptyStringLiteralState; + } + + break; + + case SpecialType.System_Boolean: + return ((bool)literal) ? ContainsTrueLiteralState : ContainsFalseLiteralState; + } + + return new ValueContentAbstractValue(ImmutableHashSet.Create(literal), ValueContainsNonLiteralState.No); + } + + private static ValueContentAbstractValue Create(ImmutableHashSet literalValues, ValueContainsNonLiteralState nonLiteralState) + { + if (literalValues.IsEmpty) + { + return nonLiteralState switch + { + ValueContainsNonLiteralState.Undefined => UndefinedState, + ValueContainsNonLiteralState.Invalid => InvalidState, + ValueContainsNonLiteralState.No => DoesNotContainLiteralOrNonLiteralState, + _ => MayBeContainsNonLiteralState, + }; + } + else if (literalValues.Count == 1 && nonLiteralState == ValueContainsNonLiteralState.No) + { + switch (literalValues.Single()) + { + case bool boolVal: + return boolVal ? ContainsTrueLiteralState : ContainsFalseLiteralState; + + case string stringVal: + if (stringVal.Length == 0) + { + return ContainsEmptyStringLiteralState; + } + + break; + + case int intValue: + if (intValue == 0) + { + return ContainsZeroIntergralLiteralState; + } + + break; + } + } + + return new ValueContentAbstractValue(literalValues, nonLiteralState); + } + + internal static bool IsSupportedType(ITypeSymbol type, [NotNullWhen(returnValue: true)] out ITypeSymbol? valueTypeSymbol) + { + if (type.IsPrimitiveType()) + { + valueTypeSymbol = type; + return true; + } + else if (type is INamedTypeSymbol namedTypeSymbol + && namedTypeSymbol.EnumUnderlyingType != null) + { + valueTypeSymbol = namedTypeSymbol.EnumUnderlyingType; + return true; + } + else + { + valueTypeSymbol = null; + return false; + } + } + + /// + /// Indicates if this variable contains non literal operands or not. + /// + public ValueContainsNonLiteralState NonLiteralState { get; } + + /// + /// Gets a collection of the literals that could possibly make up the contents of this abstract value. + /// + public ImmutableHashSet LiteralValues { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(LiteralValues)); + hashCode.Add(((int)NonLiteralState).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (ValueContentAbstractValue)obj; + return HashUtilities.Combine(LiteralValues) == HashUtilities.Combine(other.LiteralValues) + && ((int)NonLiteralState).GetHashCode() == ((int)other.NonLiteralState).GetHashCode(); + } + + /// + /// Performs the union of this state and the other state + /// and returns a new with the result. + /// + internal ValueContentAbstractValue Merge(ValueContentAbstractValue otherState) + { + if (otherState == null) + { + throw new ArgumentNullException(nameof(otherState)); + } + + ImmutableHashSet mergedLiteralValues = LiteralValues.AddRange(otherState.LiteralValues); + if (mergedLiteralValues.Count > LiteralsBound) + { + return MayBeContainsNonLiteralState; + } + + ValueContainsNonLiteralState mergedNonLiteralState = Merge(NonLiteralState, otherState.NonLiteralState); + return Create(mergedLiteralValues, mergedNonLiteralState); + } + + private static ValueContainsNonLiteralState Merge(ValueContainsNonLiteralState value1, ValueContainsNonLiteralState value2) + { + // + U I M N + // U U U M N + // I U I M N + // M M M M M + // N N N M N + if (value1 == ValueContainsNonLiteralState.Maybe || value2 == ValueContainsNonLiteralState.Maybe) + { + return ValueContainsNonLiteralState.Maybe; + } + else if (value1 is ValueContainsNonLiteralState.Invalid or ValueContainsNonLiteralState.Undefined) + { + return value2; + } + else if (value2 is ValueContainsNonLiteralState.Invalid or ValueContainsNonLiteralState.Undefined) + { + return value1; + } + + Debug.Assert(value1 == ValueContainsNonLiteralState.No); + Debug.Assert(value2 == ValueContainsNonLiteralState.No); + return ValueContainsNonLiteralState.No; + } + + public bool IsLiteralState => !LiteralValues.IsEmpty && NonLiteralState == ValueContainsNonLiteralState.No; + + /// + /// For super simple cases: If this abstract value is a single non-null literal, then get that literal value. + /// + /// Type of the expected literal value. + /// Literal value, or its default if not a single non-null literal value. + /// True if a non-null literal value was found, false otherwise. + /// If you're looking for null, you should be looking at . + public bool TryGetSingleNonNullLiteral([MaybeNullWhen(returnValue: false)] out T literalValue) + { + if (!IsLiteralState || LiteralValues.Count != 1) + { + literalValue = default!; + return false; + } + + object? o = LiteralValues.First(); + if (o is T v) + { + literalValue = v; + return true; + } + else + { + literalValue = default!; + return false; + } + } + + internal ValueContentAbstractValue IntersectLiteralValues(ValueContentAbstractValue value2) + { + Debug.Assert(IsLiteralState); + Debug.Assert(value2.IsLiteralState); + + // Merge Literals + var mergedLiteralValues = this.LiteralValues.Intersect(value2.LiteralValues); + return mergedLiteralValues.IsEmpty ? InvalidState : new ValueContentAbstractValue(mergedLiteralValues, ValueContainsNonLiteralState.No); + } + + /// + /// Performs the union of this state and the other state for a Binary operation + /// and returns a new with the result. + /// + internal ValueContentAbstractValue MergeBinaryOperation( + ValueContentAbstractValue otherState, + BinaryOperatorKind binaryOperatorKind, + ITypeSymbol? leftType, + ITypeSymbol? rightType, + ITypeSymbol? resultType) + { + if (otherState == null) + { + throw new ArgumentNullException(nameof(otherState)); + } + + // Merge Literals + var builder = PooledHashSet.GetInstance(); + foreach (var leftLiteral in LiteralValues) + { + foreach (var rightLiteral in otherState.LiteralValues) + { + if (!TryMerge(leftLiteral, rightLiteral, binaryOperatorKind, leftType, rightType, resultType, out object? result)) + { + return MayBeContainsNonLiteralState; + } + + builder.Add(result); + } + } + + ImmutableHashSet mergedLiteralValues = builder.ToImmutableAndFree(); + ValueContainsNonLiteralState mergedNonLiteralState = Merge(NonLiteralState, otherState.NonLiteralState); + + return Create(mergedLiteralValues, mergedNonLiteralState); + } + + public override string ToString() => + string.Format(CultureInfo.InvariantCulture, "L({0}) NL:{1}", LiteralValues.Count, NonLiteralState.ToString()[0]); + + private static bool TryMerge(object? value1, object? value2, BinaryOperatorKind binaryOperatorKind, ITypeSymbol? type1, ITypeSymbol? type2, ITypeSymbol? resultType, [NotNullWhen(returnValue: true)] out object? result) + { + result = null; + + if (value1 == null || value2 == null || type1 == null || type2 == null || resultType == null) + { + return false; + } + + try + { + switch (type1.SpecialType) + { + case SpecialType.System_String: + return type2.SpecialType == SpecialType.System_String && + TryMerge((string)value1, (string)value2, binaryOperatorKind, out result); + + case SpecialType.System_Char: + return type2.SpecialType == SpecialType.System_Char && + TryMerge((char)value1, (char)value2, binaryOperatorKind, out result); + + case SpecialType.System_Boolean: + return type2.SpecialType == SpecialType.System_Boolean && + TryMerge((bool)value1, (bool)value2, binaryOperatorKind, out result); + + case SpecialType.System_Byte: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_Int64: + case SpecialType.System_UInt16: + case SpecialType.System_UInt32: + case SpecialType.System_SByte: + case SpecialType.System_UInt64: + if (DiagnosticHelpers.TryConvertToUInt64(value1, type1.SpecialType, out ulong convertedValue1) && + DiagnosticHelpers.TryConvertToUInt64(value2, type2.SpecialType, out ulong convertedValue2) && + TryMerge(convertedValue1, convertedValue2, binaryOperatorKind, out var convertedResult)) + { + switch (resultType.SpecialType) + { + case SpecialType.System_SByte: + result = (sbyte)convertedResult; + return true; + + case SpecialType.System_Int16: + result = (short)convertedResult; + return true; + + case SpecialType.System_Int32: + result = (int)convertedResult; + return true; + + case SpecialType.System_Int64: + result = (long)convertedResult; + return true; + + case SpecialType.System_Byte: + result = (byte)convertedResult; + return true; + + case SpecialType.System_UInt16: + result = (ushort)convertedResult; + return true; + + case SpecialType.System_UInt32: + result = (uint)convertedResult; + return true; + + case SpecialType.System_UInt64: + result = convertedResult; + return true; + + case SpecialType.System_Boolean: + result = convertedResult != 0UL; + return true; + } + } + + break; + + case SpecialType.System_Double: + case SpecialType.System_Single: + switch (type2.SpecialType) + { + case SpecialType.System_Single: + case SpecialType.System_Double: + if (TryMerge((double)value1, (double)value2, binaryOperatorKind, out double doubleResult)) + { + switch (resultType.SpecialType) + { + case SpecialType.System_Single: + result = (float)doubleResult; + return true; + + case SpecialType.System_Double: + result = doubleResult; + return true; + + case SpecialType.System_Boolean: + result = doubleResult != 0; + return true; + } + } + + break; + } + + break; + } + } +#pragma warning disable CA1031 // Do not catch general exception types + catch (Exception) + { + // Catch all arithmetic exceptions, and conservatively bail out. + } +#pragma warning restore CA1031 // Do not catch general exception types + + return false; + } + + private static bool TryMerge(char value1, char value2, BinaryOperatorKind binaryOperatorKind, [NotNullWhen(returnValue: true)] out object? result) + { + switch (binaryOperatorKind) + { + case BinaryOperatorKind.Add: + case BinaryOperatorKind.Concatenate: + result = value1 + value2; + return true; + } + + result = null; + return false; + } + + private static bool TryMerge(string value1, string value2, BinaryOperatorKind binaryOperatorKind, [NotNullWhen(returnValue: true)] out object? result) + { + switch (binaryOperatorKind) + { + case BinaryOperatorKind.Add: + case BinaryOperatorKind.Concatenate: + result = value1 + value2; + return true; + } + + result = null; + return false; + } + + private static bool TryMerge(bool value1, bool value2, BinaryOperatorKind binaryOperatorKind, [NotNullWhen(returnValue: true)] out object? result) + { + switch (binaryOperatorKind) + { + case BinaryOperatorKind.And: + case BinaryOperatorKind.ConditionalAnd: + result = value1 && value2; + return true; + + case BinaryOperatorKind.Or: + case BinaryOperatorKind.ConditionalOr: + result = value1 || value2; + return true; + + case BinaryOperatorKind.Equals: + result = value1 == value2; + return true; + + case BinaryOperatorKind.NotEquals: + result = value1 != value2; + return true; + } + + result = null; + return false; + } + + private static bool TryMerge(ulong value1, ulong value2, BinaryOperatorKind binaryOperatorKind, out ulong result) + { + switch (binaryOperatorKind) + { + case BinaryOperatorKind.Add: + result = value1 + value2; + return true; + + case BinaryOperatorKind.Subtract: + result = value1 - value2; + return true; + + case BinaryOperatorKind.Multiply: + result = value1 * value2; + return true; + + case BinaryOperatorKind.Divide: + if (value2 != 0) + { + result = value1 / value2; + return true; + } + + break; + + case BinaryOperatorKind.And: + result = value1 & value2; + return true; + + case BinaryOperatorKind.Or: + result = value1 | value2; + return true; + + case BinaryOperatorKind.Remainder: + result = value1 % value2; + return true; + + case BinaryOperatorKind.Power: + result = (ulong)Math.Pow(value1, value2); + return true; + + case BinaryOperatorKind.LeftShift: + if ((uint)value2 == value2) + { + result = value1 << (int)value2; + return true; + } + + break; + + case BinaryOperatorKind.RightShift: + if ((uint)value2 == value2) + { + result = value1 >> (int)value2; + return true; + } + + break; + + case BinaryOperatorKind.ExclusiveOr: + result = value1 ^ value2; + return true; + + case BinaryOperatorKind.Equals: + result = value1 == value2 ? 1UL : 0UL; + return true; + + case BinaryOperatorKind.NotEquals: + result = value1 != value2 ? 1UL : 0UL; + return true; + + case BinaryOperatorKind.LessThan: + result = value1 < value2 ? 1UL : 0UL; + return true; + + case BinaryOperatorKind.LessThanOrEqual: + result = value1 <= value2 ? 1UL : 0UL; + return true; + + case BinaryOperatorKind.GreaterThan: + result = value1 > value2 ? 1UL : 0UL; + return true; + + case BinaryOperatorKind.GreaterThanOrEqual: + result = value1 >= value2 ? 1UL : 0UL; + return true; + } + + result = 0; + return false; + } + + private static bool TryMerge(double value1, double value2, BinaryOperatorKind binaryOperatorKind, out double result) + { + switch (binaryOperatorKind) + { + case BinaryOperatorKind.Add: + result = value1 + value2; + return true; + + case BinaryOperatorKind.Subtract: + result = value1 - value2; + return true; + + case BinaryOperatorKind.Multiply: + result = value1 * value2; + return true; + + case BinaryOperatorKind.Divide: + if (value2 != 0) + { + result = value1 / value2; + return true; + } + + break; + + case BinaryOperatorKind.Remainder: + result = value1 % value2; + return true; + + case BinaryOperatorKind.Power: + result = Math.Pow(value1, value2); + return true; + + case BinaryOperatorKind.Equals: + result = value1 == value2 ? 1.0 : 0.0; + return true; + + case BinaryOperatorKind.NotEquals: + result = value1 != value2 ? 1.0 : 0.0; + return true; + + case BinaryOperatorKind.LessThan: + result = value1 < value2 ? 1.0 : 0.0; + return true; + + case BinaryOperatorKind.LessThanOrEqual: + result = value1 <= value2 ? 1.0 : 0.0; + return true; + + case BinaryOperatorKind.GreaterThan: + result = value1 > value2 ? 1.0 : 0.0; + return true; + + case BinaryOperatorKind.GreaterThanOrEqual: + result = value1 >= value2 ? 1.0 : 0.0; + return true; + } + + result = 0; + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.CoreAnalysisDataDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.CoreAnalysisDataDomain.cs new file mode 100644 index 0000000000000..3bcc4b86e0992 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.CoreAnalysisDataDomain.cs @@ -0,0 +1,59 @@ +// 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.Diagnostics; +using System.Linq; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using CoreValueContentAnalysisData = DictionaryAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + public partial class ValueContentAnalysis : ForwardDataFlowAnalysis + { + /// + /// An abstract analysis domain implementation for core analysis data tracked by . + /// + private sealed class CoreAnalysisDataDomain : AnalysisEntityMapAbstractDomain + { + public CoreAnalysisDataDomain(AbstractValueDomain valueDomain, PointsToAnalysisResult? pointsToAnalysisResult) + : base(valueDomain, pointsToAnalysisResult) + { + } + + protected override ValueContentAbstractValue GetDefaultValue(AnalysisEntity analysisEntity) => ValueContentAbstractValue.MayBeContainsNonLiteralState; + protected override bool CanSkipNewEntry(AnalysisEntity analysisEntity, ValueContentAbstractValue value) => value.NonLiteralState == ValueContainsNonLiteralState.Maybe; + protected override void AssertValidEntryForMergedMap(AnalysisEntity analysisEntity, ValueContentAbstractValue value) + { + // No validation. + } + + public CoreValueContentAnalysisData MergeAnalysisDataForBackEdge(CoreValueContentAnalysisData forwardEdgeAnalysisData, CoreValueContentAnalysisData backEdgeAnalysisData) + { + // Stop tracking values present in both branches if their is an assignment to different literal values from the back edge. + // Clone the input forwardEdgeAnalysisData to ensure we don't overwrite the input dictionary. + using (forwardEdgeAnalysisData = new CoreValueContentAnalysisData(forwardEdgeAnalysisData)) + { + var keysInMap1 = forwardEdgeAnalysisData.Keys.ToList(); + foreach (var key in keysInMap1) + { + var forwardEdgeValue = forwardEdgeAnalysisData[key]; + if (backEdgeAnalysisData.TryGetValue(key, out var backEdgeValue) && + backEdgeValue != forwardEdgeValue && + backEdgeValue.NonLiteralState == forwardEdgeValue.NonLiteralState) + { + forwardEdgeAnalysisData[key] = ValueContentAbstractValue.MayBeContainsNonLiteralState; + } + } + + var resultMap = Merge(forwardEdgeAnalysisData, backEdgeAnalysisData); + Debug.Assert(Compare(forwardEdgeAnalysisData, resultMap) <= 0); + Debug.Assert(Compare(backEdgeAnalysisData, resultMap) <= 0); + return resultMap; + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAbstractDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAbstractDomain.cs new file mode 100644 index 0000000000000..6dceed862275c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAbstractDomain.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + public partial class ValueContentAnalysis : ForwardDataFlowAnalysis + { + /// + /// Abstract value domain for to merge and compare values. + /// + private sealed class ValueContentAbstractValueDomain : AbstractValueDomain + { + public static ValueContentAbstractValueDomain Default = new(); + + private ValueContentAbstractValueDomain() { } + + public override ValueContentAbstractValue Bottom => ValueContentAbstractValue.UndefinedState; + + public override ValueContentAbstractValue UnknownOrMayBeValue => ValueContentAbstractValue.MayBeContainsNonLiteralState; + + public override int Compare(ValueContentAbstractValue oldValue, ValueContentAbstractValue newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (oldValue.NonLiteralState == newValue.NonLiteralState) + { + if (oldValue.IsLiteralState) + { + if (oldValue.LiteralValues.SetEquals(newValue.LiteralValues)) + { + return 0; + } + else if (oldValue.LiteralValues.IsSubsetOf(newValue.LiteralValues)) + { + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + else + { + return 0; + } + } + else if (oldValue.NonLiteralState < newValue.NonLiteralState) + { + return -1; + } + else + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + } + + public override ValueContentAbstractValue Merge(ValueContentAbstractValue value1, ValueContentAbstractValue value2) + { + return value1.Merge(value2); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAnalysisDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAnalysisDomain.cs new file mode 100644 index 0000000000000..a52d8ef1e63e3 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentAnalysisDomain.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + public partial class ValueContentAnalysis : ForwardDataFlowAnalysis + { + /// + /// An abstract analysis domain implementation for tracked by . + /// + private sealed class ValueContentAnalysisDomain : PredicatedAnalysisDataDomain + { + public ValueContentAnalysisDomain(PointsToAnalysisResult? pointsToAnalysisResult) + : base(new CoreAnalysisDataDomain(ValueContentAbstractValueDomain.Default, pointsToAnalysisResult)) + { + } + + public ValueContentAnalysisData MergeAnalysisDataForBackEdge(ValueContentAnalysisData forwardEdgeAnalysisData, ValueContentAnalysisData backEdgeAnalysisData) + { + if (!forwardEdgeAnalysisData.IsReachableBlockData && backEdgeAnalysisData.IsReachableBlockData) + { + return (ValueContentAnalysisData)backEdgeAnalysisData.Clone(); + } + else if (!backEdgeAnalysisData.IsReachableBlockData && forwardEdgeAnalysisData.IsReachableBlockData) + { + return (ValueContentAnalysisData)forwardEdgeAnalysisData.Clone(); + } + + Debug.Assert(forwardEdgeAnalysisData.IsReachableBlockData == backEdgeAnalysisData.IsReachableBlockData); + + var mergedCoreAnalysisData = ((CoreAnalysisDataDomain)CoreDataAnalysisDomain).MergeAnalysisDataForBackEdge(forwardEdgeAnalysisData.CoreAnalysisData, backEdgeAnalysisData.CoreAnalysisData); + return new ValueContentAnalysisData(mergedCoreAnalysisData, forwardEdgeAnalysisData, + backEdgeAnalysisData, forwardEdgeAnalysisData.IsReachableBlockData, CoreDataAnalysisDomain); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..4a13955d154f9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.ValueContentDataFlowOperationVisitor.cs @@ -0,0 +1,303 @@ +// 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 Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + public partial class ValueContentAnalysis : ForwardDataFlowAnalysis + { + /// + /// Operation visitor to flow the data values across a given statement in a basic block. + /// + private sealed class ValueContentDataFlowOperationVisitor : PredicateAnalysisEntityDataFlowOperationVisitor + { + private readonly ValueContentAnalysisDomain _valueContentAnalysisDomain; + + public ValueContentDataFlowOperationVisitor(ValueContentAnalysisDomain valueContentAnalysisDomain, ValueContentAnalysisContext analysisContext) + : base(analysisContext) + { + _valueContentAnalysisDomain = valueContentAnalysisDomain; + } + + protected override void AddTrackedEntities(ValueContentAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis) + => analysisData.AddTrackedEntities(builder); + + protected override void ResetAbstractValue(AnalysisEntity analysisEntity) + => SetAbstractValue(analysisEntity, ValueDomain.UnknownOrMayBeValue); + + protected override void SetAbstractValue(AnalysisEntity analysisEntity, ValueContentAbstractValue value) + => SetAbstractValue(CurrentAnalysisData, analysisEntity, value); + + private void SetAbstractValue(ValueContentAnalysisData analysisData, AnalysisEntity analysisEntity, ValueContentAbstractValue value) + => SetAbstractValue(analysisData, analysisEntity, value, HasCompletePointsToAnalysisResult); + + private static void SetAbstractValue(ValueContentAnalysisData analysisData, AnalysisEntity analysisEntity, ValueContentAbstractValue value, bool hasCompletePointsToAnalysisResult) + { + // PERF: Avoid creating an entry if the value is the default unknown value. + if (value == ValueContentAbstractValue.MayBeContainsNonLiteralState && + !analysisData.HasAbstractValue(analysisEntity)) + { + return; + } + + if (analysisEntity.ShouldBeTrackedForAnalysis(hasCompletePointsToAnalysisResult)) + { + analysisData.SetAbstractValue(analysisEntity, value); + } + } + + protected override bool HasAbstractValue(AnalysisEntity analysisEntity) + => CurrentAnalysisData.HasAbstractValue(analysisEntity); + + protected override void StopTrackingEntity(AnalysisEntity analysisEntity, ValueContentAnalysisData analysisData) + => analysisData.RemoveEntries(analysisEntity); + + protected override ValueContentAbstractValue GetAbstractValue(AnalysisEntity analysisEntity) + => CurrentAnalysisData.TryGetValue(analysisEntity, out var value) ? value : ValueDomain.UnknownOrMayBeValue; + + protected override ValueContentAbstractValue GetAbstractDefaultValue(ITypeSymbol? type) + => type != null && !type.CanHoldNullValue() ? + ValueContentAbstractValue.DoesNotContainLiteralOrNonLiteralState : + ValueContentAbstractValue.ContainsNullLiteralState; + + protected override bool HasAnyAbstractValue(ValueContentAnalysisData data) + => data.HasAnyAbstractValue; + + protected override void ResetCurrentAnalysisData() + => CurrentAnalysisData.Reset(ValueDomain.UnknownOrMayBeValue); + + protected override CopyAbstractValue GetCopyAbstractValue(IOperation operation) + { + if (DataFlowAnalysisContext.CopyAnalysisResult == null && + AnalysisEntityFactory.TryCreate(operation, out var entity) && + entity.CaptureId.HasValue && + AnalysisEntityFactory.TryGetCopyValueForFlowCapture(entity.CaptureId.Value.Id, out var copyValue) && + copyValue.Kind == CopyAbstractValueKind.KnownValueCopy) + { + return copyValue; + } + + return base.GetCopyAbstractValue(operation); + } + + #region Predicate analysis + protected override PredicateValueKind SetValueForIsNullComparisonOperator(IOperation leftOperand, bool equals, ValueContentAnalysisData targetAnalysisData) + => PredicateValueKind.Unknown; + + protected override PredicateValueKind SetValueForEqualsOrNotEqualsComparisonOperator( + IOperation leftOperand, + IOperation rightOperand, + bool equals, + bool isReferenceEquality, + ValueContentAnalysisData targetAnalysisData) + { + var predicateValueKind = PredicateValueKind.Unknown; + + // Handle 'a == "SomeValue"' and 'a != "SomeValue"' + SetValueForComparisonOperator(leftOperand, rightOperand, equals, ref predicateValueKind, targetAnalysisData); + + // Handle '"SomeValue" == a' and '"SomeValue" != a' + SetValueForComparisonOperator(rightOperand, leftOperand, equals, ref predicateValueKind, targetAnalysisData); + + return predicateValueKind; + } + + private void SetValueForComparisonOperator(IOperation target, IOperation assignedValue, bool equals, ref PredicateValueKind predicateValueKind, ValueContentAnalysisData targetAnalysisData) + { + ValueContentAbstractValue currentAssignedValue = GetCachedAbstractValue(assignedValue); + if (currentAssignedValue.IsLiteralState && + AnalysisEntityFactory.TryCreate(target, out var targetEntity)) + { + if (CurrentAnalysisData.TryGetValue(targetEntity, out var existingTargetValue) && + existingTargetValue.IsLiteralState) + { + var newValue = currentAssignedValue.IntersectLiteralValues(existingTargetValue); + if (newValue.NonLiteralState == ValueContainsNonLiteralState.Invalid) + { + predicateValueKind = equals ? PredicateValueKind.AlwaysFalse : PredicateValueKind.AlwaysTrue; + } + else if (predicateValueKind != PredicateValueKind.AlwaysFalse && + newValue.IsLiteralState && + newValue.LiteralValues.Count == 1 && + currentAssignedValue.LiteralValues.Count == 1 && + existingTargetValue.LiteralValues.Count == 1) + { + predicateValueKind = equals ? PredicateValueKind.AlwaysTrue : PredicateValueKind.AlwaysFalse; + } + + currentAssignedValue = newValue; + } + + if (equals) + { + CopyAbstractValue copyValue = GetCopyAbstractValue(target); + if (copyValue.Kind.IsKnown()) + { + // https://github.com/dotnet/roslyn-analyzers/issues/2106 tracks enabling the below assert. + //Debug.Assert(copyValue.AnalysisEntities.Contains(targetEntity)); + foreach (var analysisEntity in copyValue.AnalysisEntities) + { + SetAbstractValue(targetAnalysisData, analysisEntity, currentAssignedValue); + } + } + else + { + SetAbstractValue(targetAnalysisData, targetEntity, currentAssignedValue); + } + } + } + } + + #endregion + + protected override ValueContentAnalysisData MergeAnalysisData(ValueContentAnalysisData value1, ValueContentAnalysisData value2) + => _valueContentAnalysisDomain.Merge(value1, value2); + protected override ValueContentAnalysisData MergeAnalysisDataForBackEdge(ValueContentAnalysisData value1, ValueContentAnalysisData value2, BasicBlock forBlock) + => _valueContentAnalysisDomain.MergeAnalysisDataForBackEdge(value1, value2); + protected override void UpdateValuesForAnalysisData(ValueContentAnalysisData targetAnalysisData) + => UpdateValuesForAnalysisData(targetAnalysisData.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData); + protected override ValueContentAnalysisData GetClonedAnalysisData(ValueContentAnalysisData analysisData) + => (ValueContentAnalysisData)analysisData.Clone(); + public override ValueContentAnalysisData GetEmptyAnalysisData() + => new(); + protected override ValueContentAnalysisData GetExitBlockOutputData(ValueContentAnalysisResult analysisResult) + => new(analysisResult.ExitBlockOutput.Data); + protected override void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(ValueContentAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType) + => ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(dataAtException.CoreAnalysisData, CurrentAnalysisData.CoreAnalysisData, throwBranchWithExceptionType); + protected override bool Equals(ValueContentAnalysisData value1, ValueContentAnalysisData value2) + => value1.Equals(value2); + protected override void ApplyInterproceduralAnalysisResultCore(ValueContentAnalysisData resultData) + => ApplyInterproceduralAnalysisResultHelper(resultData.CoreAnalysisData); + protected override ValueContentAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities) + => GetTrimmedCurrentAnalysisDataHelper(withEntities, CurrentAnalysisData.CoreAnalysisData, SetAbstractValue); + + #region Visitor methods + public override ValueContentAbstractValue DefaultVisit(IOperation operation, object? argument) + { + _ = base.DefaultVisit(operation, argument); + if (operation.Type == null) + { + return operation.Kind == OperationKind.None ? + ValueContentAbstractValue.MayBeContainsNonLiteralState : + ValueContentAbstractValue.ContainsNullLiteralState; + } + + if (ValueContentAbstractValue.IsSupportedType(operation.Type, out var valueTypeSymbol)) + { + if (operation.ConstantValue.HasValue) + { + return operation.ConstantValue.Value != null ? + ValueContentAbstractValue.Create(operation.ConstantValue.Value, valueTypeSymbol) : + ValueContentAbstractValue.ContainsNullLiteralState; + } + else + { + return GetNullAbstractValue(operation) switch + { + PointsToAnalysis.NullAbstractValue.Invalid => ValueContentAbstractValue.InvalidState, + + PointsToAnalysis.NullAbstractValue.Null => ValueContentAbstractValue.ContainsNullLiteralState, + + _ => ValueContentAbstractValue.MayBeContainsNonLiteralState, + }; + } + } + else if (DataFlowAnalysisContext.GetValueForAdditionalSupportedValueTypeOperation is { } getValueFunc && + operation.Type is INamedTypeSymbol namedType && + DataFlowAnalysisContext.AdditionalSupportedValueTypes.Contains(namedType)) + { + return getValueFunc(operation); + } + + return ValueDomain.UnknownOrMayBeValue; + } + + public override ValueContentAbstractValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument) + { + var leftValue = Visit(operation.LeftOperand, argument); + var rightValue = Visit(operation.RightOperand, argument); + return leftValue.MergeBinaryOperation(rightValue, operation.OperatorKind, operation.LeftOperand.Type, operation.RightOperand.Type, operation.Type); + } + + public override ValueContentAbstractValue ComputeValueForCompoundAssignment( + ICompoundAssignmentOperation operation, + ValueContentAbstractValue targetValue, + ValueContentAbstractValue assignedValue, + ITypeSymbol? targetType, + ITypeSymbol? assignedValueType) + { + return targetValue.MergeBinaryOperation(assignedValue, operation.OperatorKind, targetType, assignedValueType, operation.Type); + } + + public override ValueContentAbstractValue ComputeValueForIncrementOrDecrementOperation(IIncrementOrDecrementOperation operation, ValueContentAbstractValue targetValue) + { + var incrementValue = ValueContentAbstractValue.ContainsOneIntergralLiteralState; + var incrementValueType = WellKnownTypeProvider.Compilation.GetSpecialType(SpecialType.System_Int32); + var operationKind = operation.Kind == OperationKind.Increment ? BinaryOperatorKind.Add : BinaryOperatorKind.Subtract; + return targetValue.MergeBinaryOperation(incrementValue, operationKind, operation.Target.Type, incrementValueType, operation.Type); + } + + public override ValueContentAbstractValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + // TODO: Analyze string constructor + // https://github.com/dotnet/roslyn-analyzers/issues/1547 + return base.VisitObjectCreation(operation, argument); + } + + public override ValueContentAbstractValue VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument); + + // Handle "string.Empty" + if (operation.Field.Name.Equals("Empty", StringComparison.Ordinal) && + operation.Field.ContainingType.SpecialType == SpecialType.System_String) + { + return ValueContentAbstractValue.ContainsEmptyStringLiteralState; + } + + return value; + } + + public override ValueContentAbstractValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + ValueContentAbstractValue defaultValue) + { + // TODO: Handle invocations of string methods (Format, SubString, Replace, Concat, etc.) + // https://github.com/dotnet/roslyn-analyzers/issues/1547 + return base.VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, visitedInstance, visitedArguments, invokedAsDelegate, originalOperation, defaultValue); + } + + public override ValueContentAbstractValue VisitInterpolatedString(IInterpolatedStringOperation operation, object? argument) + { + if (operation.Parts.IsEmpty) + { + return ValueContentAbstractValue.ContainsEmptyStringLiteralState; + } + + ValueContentAbstractValue mergedValue = Visit(operation.Parts[0], argument); + for (int i = 1; i < operation.Parts.Length; i++) + { + var newValue = Visit(operation.Parts[i], argument); + mergedValue = mergedValue.MergeBinaryOperation(newValue, BinaryOperatorKind.Add, leftType: operation.Type, rightType: operation.Type, resultType: operation.Type); + } + + return mergedValue; + } + + #endregion + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.cs new file mode 100644 index 0000000000000..26dc15fcfdad7 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysis.cs @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using ValueContentAnalysisResult = DataFlowAnalysisResult; + using CopyAnalysisResult = DataFlowAnalysisResult; + + /// + /// Dataflow analysis to track value content of /. + /// + public partial class ValueContentAnalysis : ForwardDataFlowAnalysis + { + internal static readonly AbstractValueDomain ValueDomainInstance = ValueContentAbstractValueDomain.Default; + + private ValueContentAnalysis(ValueContentAnalysisDomain analysisDomain, ValueContentDataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + + public static ValueContentAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + WellKnownTypeProvider wellKnownTypeProvider, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + PointsToAnalysisKind defaultPointsToAnalysisKind, + InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.None, + bool pessimisticAnalysis = true) + { + return TryGetOrComputeResult(cfg, owningSymbol, wellKnownTypeProvider, analyzerOptions, rule, + defaultPointsToAnalysisKind, out var _, out var _, interproceduralAnalysisKind, pessimisticAnalysis); + } + + public static ValueContentAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + WellKnownTypeProvider wellKnownTypeProvider, + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + PointsToAnalysisKind defaultPointsToAnalysisKind, + out CopyAnalysisResult? copyAnalysisResult, + out PointsToAnalysisResult? pointsToAnalysisResult, + InterproceduralAnalysisKind interproceduralAnalysisKind = InterproceduralAnalysisKind.None, + bool pessimisticAnalysis = true, + bool performCopyAnalysisIfNotUserConfigured = false, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, + ImmutableArray additionalSupportedValueTypes = default, + Func? getValueContentValueForAdditionalSupportedValueTypeOperation = null) + { + if (cfg == null) + { + throw new ArgumentNullException(nameof(cfg)); + } + + Debug.Assert(!analyzerOptions.IsConfiguredToSkipAnalysis(rule, owningSymbol, wellKnownTypeProvider.Compilation)); + + var interproceduralAnalysisConfig = InterproceduralAnalysisConfiguration.Create( + analyzerOptions, rule, cfg, wellKnownTypeProvider.Compilation, interproceduralAnalysisKind); + return TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, + pointsToAnalysisKind: analyzerOptions.GetPointsToAnalysisKindOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, defaultPointsToAnalysisKind), + interproceduralAnalysisConfig, out copyAnalysisResult, + out pointsToAnalysisResult, pessimisticAnalysis, + performCopyAnalysis: analyzerOptions.GetCopyAnalysisOption(rule, owningSymbol, wellKnownTypeProvider.Compilation, defaultValue: performCopyAnalysisIfNotUserConfigured), + interproceduralAnalysisPredicate, + additionalSupportedValueTypes, + getValueContentValueForAdditionalSupportedValueTypeOperation); + } + + internal static ValueContentAnalysisResult? TryGetOrComputeResult( + ControlFlowGraph cfg, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + WellKnownTypeProvider wellKnownTypeProvider, + PointsToAnalysisKind pointsToAnalysisKind, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + out CopyAnalysisResult? copyAnalysisResult, + out PointsToAnalysisResult? pointsToAnalysisResult, + bool pessimisticAnalysis = true, + bool performCopyAnalysis = false, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate = null, + ImmutableArray additionalSupportedValueTypes = default, + Func? getValueContentValueForAdditionalSupportedValueTypeOperation = null) + { + if (cfg == null) + { + throw new ArgumentNullException(nameof(cfg)); + } + + copyAnalysisResult = null; + pointsToAnalysisResult = pointsToAnalysisKind != PointsToAnalysisKind.None ? + PointsToAnalysis.PointsToAnalysis.TryGetOrComputeResult(cfg, owningSymbol, analyzerOptions, wellKnownTypeProvider, pointsToAnalysisKind, out copyAnalysisResult, + interproceduralAnalysisConfig, interproceduralAnalysisPredicate, pessimisticAnalysis, performCopyAnalysis) : + null; + + var analysisContext = ValueContentAnalysisContext.Create( + ValueDomainInstance, wellKnownTypeProvider, cfg, owningSymbol, analyzerOptions, + interproceduralAnalysisConfig, pessimisticAnalysis, copyAnalysisResult, + pointsToAnalysisResult, TryGetOrComputeResultForAnalysisContext, + additionalSupportedValueTypes, getValueContentValueForAdditionalSupportedValueTypeOperation, + interproceduralAnalysisPredicate); + return TryGetOrComputeResultForAnalysisContext(analysisContext); + } + + private static ValueContentAnalysisResult? TryGetOrComputeResultForAnalysisContext(ValueContentAnalysisContext analysisContext) + { + var analysisDomain = new ValueContentAnalysisDomain(analysisContext.PointsToAnalysisResult); + var operationVisitor = new ValueContentDataFlowOperationVisitor(analysisDomain, analysisContext); + var nullAnalysis = new ValueContentAnalysis(analysisDomain, operationVisitor); + return nullAnalysis.TryGetOrComputeResultCore(analysisContext, cacheResult: true); + } + + protected override ValueContentAnalysisResult ToResult(ValueContentAnalysisContext analysisContext, ValueContentAnalysisResult dataFlowAnalysisResult) + => dataFlowAnalysisResult; + + protected override ValueContentBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, ValueContentAnalysisData blockAnalysisData) + => new(basicBlock, blockAnalysisData); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisContext.cs new file mode 100644 index 0000000000000..96a94551bc43d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisContext.cs @@ -0,0 +1,101 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using InterproceduralValueContentAnalysisData = InterproceduralAnalysisData; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Analysis context for execution of on a control flow graph. + /// + public sealed class ValueContentAnalysisContext : AbstractDataFlowAnalysisContext + { + private ValueContentAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + CopyAnalysisResult? copyAnalysisResult, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ImmutableArray additionalSupportedValueTypes, + Func? getValueForAdditionalSupportedValueTypeOperation, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralValueContentAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + : base(valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, interproceduralAnalysisConfig, + pessimisticAnalysis, predicateAnalysis: true, exceptionPathsAnalysis: false, copyAnalysisResult, + pointsToAnalysisResult, valueContentAnalysisResult: null, tryGetOrComputeAnalysisResult, parentControlFlowGraph, + interproceduralAnalysisData, interproceduralAnalysisPredicate) + { + AdditionalSupportedValueTypes = additionalSupportedValueTypes.IsDefault ? ImmutableArray.Empty : additionalSupportedValueTypes; + GetValueForAdditionalSupportedValueTypeOperation = getValueForAdditionalSupportedValueTypeOperation; + } + + public ImmutableArray AdditionalSupportedValueTypes { get; } + public Func? GetValueForAdditionalSupportedValueTypeOperation { get; } + + internal static ValueContentAnalysisContext Create( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + CopyAnalysisResult? copyAnalysisResult, + PointsToAnalysisResult? pointsToAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ImmutableArray additionalSupportedValueTypes, + Func? getValueContentValueForAdditionalSupportedValueTypeOperation, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + { + return new ValueContentAnalysisContext( + valueDomain, wellKnownTypeProvider, controlFlowGraph, owningSymbol, analyzerOptions, + interproceduralAnalysisConfig, pessimisticAnalysis, copyAnalysisResult, pointsToAnalysisResult, + tryGetOrComputeAnalysisResult, additionalSupportedValueTypes, getValueContentValueForAdditionalSupportedValueTypeOperation, + parentControlFlowGraph: null, interproceduralAnalysisData: null, interproceduralAnalysisPredicate); + } + + public override ValueContentAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralValueContentAnalysisData? interproceduralAnalysisData) + { + Debug.Assert(valueContentAnalysisResult == null); + + return new ValueContentAnalysisContext(ValueDomain, WellKnownTypeProvider, invokedCfg, invokedMethod, AnalyzerOptions, InterproceduralAnalysisConfiguration, + PessimisticAnalysis, copyAnalysisResult, pointsToAnalysisResult, TryGetOrComputeAnalysisResult, + AdditionalSupportedValueTypes, GetValueForAdditionalSupportedValueTypeOperation, ControlFlowGraph, interproceduralAnalysisData, + InterproceduralAnalysisPredicate); + } + + protected override void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode) + { + hashCode.Add(HashUtilities.Combine(AdditionalSupportedValueTypes)); + } + + protected override bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj) + { + var other = (ValueContentAnalysisContext)obj; + return HashUtilities.Combine(AdditionalSupportedValueTypes) == HashUtilities.Combine(other.AdditionalSupportedValueTypes); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisData.cs new file mode 100644 index 0000000000000..3a01f30f73af4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentAnalysisData.cs @@ -0,0 +1,61 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + using CoreValueContentAnalysisData = DictionaryAnalysisData; + + /// + /// Aggregated value content analysis data tracked by . + /// Contains the for entity data values and + /// the predicated values based on true/false runtime values of predicated entities. + /// + public sealed class ValueContentAnalysisData : AnalysisEntityBasedPredicateAnalysisData + { + internal ValueContentAnalysisData() + { + } + + internal ValueContentAnalysisData(IDictionary fromData) + : base(fromData) + { + } + + private ValueContentAnalysisData(ValueContentAnalysisData fromData) + : base(fromData) + { + } + + private ValueContentAnalysisData(ValueContentAnalysisData data1, ValueContentAnalysisData data2, MapAbstractDomain coreDataAnalysisDomain) + : base(data1, data2, coreDataAnalysisDomain) + { + } + + internal ValueContentAnalysisData( + CoreValueContentAnalysisData mergedCoreAnalysisData, + PredicatedAnalysisData predicatedData1, + PredicatedAnalysisData predicatedData2, + bool isReachableData, + MapAbstractDomain coreDataAnalysisDomain) + : base(mergedCoreAnalysisData, predicatedData1, predicatedData2, isReachableData, coreDataAnalysisDomain) + { + } + + protected override AbstractValueDomain ValueDomain => ValueContentAnalysis.ValueDomainInstance; + public override AnalysisEntityBasedPredicateAnalysisData Clone() => new ValueContentAnalysisData(this); + + public override int Compare(AnalysisEntityBasedPredicateAnalysisData other, MapAbstractDomain coreDataAnalysisDomain) + => BaseCompareHelper(other, coreDataAnalysisDomain); + + public override AnalysisEntityBasedPredicateAnalysisData WithMergedData(AnalysisEntityBasedPredicateAnalysisData data, MapAbstractDomain coreDataAnalysisDomain) + => new ValueContentAnalysisData(this, (ValueContentAnalysisData)data, coreDataAnalysisDomain); + + internal void Reset(ValueContentAbstractValue resetValue) + { + base.Reset((analysisEntity, currentValue) => resetValue); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentBlockAnalysisResult.cs new file mode 100644 index 0000000000000..9973d4416ef05 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Analysis/ValueContentAnalysis/ValueContentBlockAnalysisResult.cs @@ -0,0 +1,25 @@ +// 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.FlowAnalysis.DataFlow.ValueContentAnalysis +{ + /// + /// Result from execution of on a basic block. + /// It stores data values for each at the start and end of the basic block. + /// + public class ValueContentBlockAnalysisResult : AbstractBlockAnalysisResult + { + internal ValueContentBlockAnalysisResult(BasicBlock basicBlock, ValueContentAnalysisData blockAnalysisData) + : base(basicBlock) + { + Data = blockAnalysisData?.CoreAnalysisData.ToImmutableDictionary() ?? ImmutableDictionary.Empty; + IsReachable = blockAnalysisData?.IsReachableBlockData ?? true; + } + + public ImmutableDictionary Data { get; } + public bool IsReachable { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisData.cs new file mode 100644 index 0000000000000..783a447b3de64 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisData.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public abstract class AbstractAnalysisData : IDisposable + { + public bool IsDisposed { get; private set; } + + protected virtual void Dispose(bool disposing) + { + IsDisposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisDomain.cs new file mode 100644 index 0000000000000..c0ac55514932a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractAnalysisDomain.cs @@ -0,0 +1,49 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Abstract analysis domain for a to merge and compare analysis data. + /// + public abstract class AbstractAnalysisDomain where TAnalysisData : AbstractAnalysisData + { + /// + /// Creates a clone of the analysis data. + /// + /// + /// + public abstract TAnalysisData Clone(TAnalysisData value); + + /// + /// Returns a value that is greater than and . + /// + /// A value to be merged + /// A value to be merged + /// A value that is greater than and + public abstract TAnalysisData Merge(TAnalysisData value1, TAnalysisData value2); + + /// + /// Compares with + /// and returns a value indicating whether one value is less than, + /// equal to, or greater than the other. + /// + /// A value to compare + /// A value to compare + /// A signed integer that indicates the relative values of + /// and . + /// Less than zero: is less than . + /// Zero: equals . + /// Greater than zero: is greater than . + /// + public abstract int Compare(TAnalysisData oldValue, TAnalysisData newValue); + + /// + /// Indicates if with are equal. + /// + /// A value to compare + /// A value to compare + public abstract bool Equals(TAnalysisData value1, TAnalysisData value2); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractBlockAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractBlockAnalysisResult.cs new file mode 100644 index 0000000000000..bbd3ca75957f4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractBlockAnalysisResult.cs @@ -0,0 +1,19 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Result from execution of a on a basic block. + /// + public abstract class AbstractBlockAnalysisResult + { + protected AbstractBlockAnalysisResult(BasicBlock basicBlock) + { + BasicBlock = basicBlock; + } + + public BasicBlock BasicBlock { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDataFlowAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDataFlowAnalysisContext.cs new file mode 100644 index 0000000000000..58a0ad9d245a8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDataFlowAnalysisContext.cs @@ -0,0 +1,196 @@ +// 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.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + using CopyAnalysisResult = DataFlowAnalysisResult; + using ValueContentAnalysisResult = DataFlowAnalysisResult; + + /// + /// Base type for analysis contexts for execution of on a control flow graph. + /// + public abstract class AbstractDataFlowAnalysisContext + : CacheBasedEquatable, IDataFlowAnalysisContext + where TAnalysisContext : class, IDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + where TAnalysisData : AbstractAnalysisData + { + protected AbstractDataFlowAnalysisContext( + AbstractValueDomain valueDomain, + WellKnownTypeProvider wellKnownTypeProvider, + ControlFlowGraph controlFlowGraph, + ISymbol owningSymbol, + AnalyzerOptions analyzerOptions, + InterproceduralAnalysisConfiguration interproceduralAnalysisConfig, + bool pessimisticAnalysis, + bool predicateAnalysis, + bool exceptionPathsAnalysis, + CopyAnalysisResult? copyAnalysisResult, + PointsToAnalysisResult? pointsToAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + Func tryGetOrComputeAnalysisResult, + ControlFlowGraph? parentControlFlowGraph, + InterproceduralAnalysisData? interproceduralAnalysisData, + InterproceduralAnalysisPredicate? interproceduralAnalysisPredicate) + { + Debug.Assert(owningSymbol.Kind is SymbolKind.Method or + SymbolKind.Field or + SymbolKind.Property or + SymbolKind.Event); + Debug.Assert(Equals(owningSymbol.OriginalDefinition, owningSymbol)); + Debug.Assert(pointsToAnalysisResult == null || + pointsToAnalysisResult.ControlFlowGraph == controlFlowGraph); + Debug.Assert(copyAnalysisResult == null || + copyAnalysisResult.ControlFlowGraph == controlFlowGraph); + Debug.Assert(valueContentAnalysisResult == null || + valueContentAnalysisResult.ControlFlowGraph == controlFlowGraph); + + ValueDomain = valueDomain; + WellKnownTypeProvider = wellKnownTypeProvider; + ControlFlowGraph = controlFlowGraph; + ParentControlFlowGraph = parentControlFlowGraph; + OwningSymbol = owningSymbol; + AnalyzerOptions = analyzerOptions; + InterproceduralAnalysisConfiguration = interproceduralAnalysisConfig; + PessimisticAnalysis = pessimisticAnalysis; + PredicateAnalysis = predicateAnalysis; + ExceptionPathsAnalysis = exceptionPathsAnalysis; + CopyAnalysisResult = copyAnalysisResult; + PointsToAnalysisResult = pointsToAnalysisResult; + ValueContentAnalysisResult = valueContentAnalysisResult; + TryGetOrComputeAnalysisResult = tryGetOrComputeAnalysisResult; + InterproceduralAnalysisData = interproceduralAnalysisData; + InterproceduralAnalysisPredicate = interproceduralAnalysisPredicate; + } + + public AbstractValueDomain ValueDomain { get; } + public WellKnownTypeProvider WellKnownTypeProvider { get; } + public ControlFlowGraph ControlFlowGraph { get; } + public ISymbol OwningSymbol { get; } + public AnalyzerOptions AnalyzerOptions { get; } + public InterproceduralAnalysisConfiguration InterproceduralAnalysisConfiguration { get; } + public bool PessimisticAnalysis { get; } + public bool PredicateAnalysis { get; } + public bool ExceptionPathsAnalysis { get; } + public CopyAnalysisResult? CopyAnalysisResult { get; } + public PointsToAnalysisResult? PointsToAnalysisResult { get; } + public ValueContentAnalysisResult? ValueContentAnalysisResult { get; } + + public Func TryGetOrComputeAnalysisResult { get; } + protected ControlFlowGraph? ParentControlFlowGraph { get; } + + // Optional data for context sensitive analysis. + public InterproceduralAnalysisData? InterproceduralAnalysisData { get; } + public InterproceduralAnalysisPredicate? InterproceduralAnalysisPredicate { get; } + + public abstract TAnalysisContext ForkForInterproceduralAnalysis( + IMethodSymbol invokedMethod, + ControlFlowGraph invokedCfg, + PointsToAnalysisResult? pointsToAnalysisResult, + CopyAnalysisResult? copyAnalysisResult, + ValueContentAnalysisResult? valueContentAnalysisResult, + InterproceduralAnalysisData? interproceduralAnalysisData); + + public ControlFlowGraph? GetLocalFunctionControlFlowGraph(IMethodSymbol localFunction) + { + if (localFunction.Equals(OwningSymbol)) + { + return ControlFlowGraph; + } + + if (ControlFlowGraph.LocalFunctions.Contains(localFunction)) + { + return ControlFlowGraph.GetLocalFunctionControlFlowGraph(localFunction); + } + + if (ParentControlFlowGraph != null && InterproceduralAnalysisData != null) + { + var parentAnalysisContext = InterproceduralAnalysisData.MethodsBeingAnalyzed.FirstOrDefault(context => context.ControlFlowGraph == ParentControlFlowGraph); + return parentAnalysisContext?.GetLocalFunctionControlFlowGraph(localFunction); + } + + // Unable to find control flow graph for local function. + // This can happen for cases where local function creation and invocations are in different interprocedural call trees. + // See unit test "DisposeObjectsBeforeLosingScopeTests.InvocationOfLocalFunctionCachedOntoField_InterproceduralAnalysis" + // for an example. + // Currently, we don't support interprocedural analysis of such local function invocations. + return null; + } + + public ControlFlowGraph? GetAnonymousFunctionControlFlowGraph(IFlowAnonymousFunctionOperation lambda) + { + // TODO: https://github.com/dotnet/roslyn-analyzers/issues/1812 + // Remove the below workaround. + try + { + return ControlFlowGraph.GetAnonymousFunctionControlFlowGraph(lambda); + } + catch (ArgumentOutOfRangeException) + { + if (ParentControlFlowGraph != null && InterproceduralAnalysisData != null) + { + var parentAnalysisContext = InterproceduralAnalysisData.MethodsBeingAnalyzed.FirstOrDefault(context => context.ControlFlowGraph == ParentControlFlowGraph); + return parentAnalysisContext?.GetAnonymousFunctionControlFlowGraph(lambda); + } + + // Unable to find control flow graph for lambda. + // This can happen for cases where lambda creation and invocations are in different interprocedural call trees. + // See unit test "DisposeObjectsBeforeLosingScopeTests.InvocationOfLambdaCachedOntoField_InterproceduralAnalysis" + // for an example. + // Currently, we don't support interprocedural analysis of such lambda invocations. + return null; + } + } + + protected abstract void ComputeHashCodePartsSpecific(ref RoslynHashCode hashCode); + + protected abstract bool ComputeEqualsByHashCodeParts(AbstractDataFlowAnalysisContext obj); + + protected sealed override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(ValueDomain.GetHashCode()); + hashCode.Add(OwningSymbol.GetHashCode()); + hashCode.Add(ControlFlowGraph.GetHashCode()); + hashCode.Add(AnalyzerOptions.GetHashCode()); + hashCode.Add(InterproceduralAnalysisConfiguration.GetHashCode()); + hashCode.Add(PessimisticAnalysis.GetHashCode()); + hashCode.Add(PredicateAnalysis.GetHashCode()); + hashCode.Add(ExceptionPathsAnalysis.GetHashCode()); + hashCode.Add(CopyAnalysisResult.GetHashCodeOrDefault()); + hashCode.Add(PointsToAnalysisResult.GetHashCodeOrDefault()); + hashCode.Add(ValueContentAnalysisResult.GetHashCodeOrDefault()); + hashCode.Add(InterproceduralAnalysisData.GetHashCodeOrDefault()); + hashCode.Add(InterproceduralAnalysisPredicate.GetHashCodeOrDefault()); + ComputeHashCodePartsSpecific(ref hashCode); + } + + protected sealed override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (AbstractDataFlowAnalysisContext)obj; + return ValueDomain.GetHashCode() == other.ValueDomain.GetHashCode() + && OwningSymbol.GetHashCode() == other.OwningSymbol.GetHashCode() + && ControlFlowGraph.GetHashCode() == other.ControlFlowGraph.GetHashCode() + && AnalyzerOptions.GetHashCode() == other.AnalyzerOptions.GetHashCode() + && InterproceduralAnalysisConfiguration.GetHashCode() == other.InterproceduralAnalysisConfiguration.GetHashCode() + && PessimisticAnalysis.GetHashCode() == other.PessimisticAnalysis.GetHashCode() + && PredicateAnalysis.GetHashCode() == other.PredicateAnalysis.GetHashCode() + && ExceptionPathsAnalysis.GetHashCode() == other.ExceptionPathsAnalysis.GetHashCode() + && CopyAnalysisResult.GetHashCodeOrDefault() == other.CopyAnalysisResult.GetHashCodeOrDefault() + && PointsToAnalysisResult.GetHashCodeOrDefault() == other.PointsToAnalysisResult.GetHashCodeOrDefault() + && ValueContentAnalysisResult.GetHashCodeOrDefault() == other.ValueContentAnalysisResult.GetHashCodeOrDefault() + && InterproceduralAnalysisData.GetHashCodeOrDefault() == other.InterproceduralAnalysisData.GetHashCodeOrDefault() + && InterproceduralAnalysisPredicate.GetHashCodeOrDefault() == other.InterproceduralAnalysisPredicate.GetHashCodeOrDefault() + && ComputeEqualsByHashCodeParts(other); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDomain.cs new file mode 100644 index 0000000000000..dd9924f113e56 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractDomain.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Abstract domain for a to merge and compare values. + /// + public abstract class AbstractDomain + { + /// + /// Returns the minor value of the domain. + /// + public abstract T Bottom { get; } + + /// + /// Returns a value that is greater than and . + /// + /// A value to be merged + /// A value to be merged + /// A value that is greater than and + public abstract T Merge(T value1, T value2); + + /// + /// Compares with + /// and returns a value indicating whether one value is less than, + /// equal to, or greater than the other. + /// + /// A value to compare + /// A value to compare + /// A signed integer that indicates the relative values of + /// and . + /// Less than zero: is less than . + /// Zero: equals . + /// Greater than zero: is greater than . + /// + public int Compare(T oldValue, T newValue) + => Compare(oldValue, newValue, assertMonotonicity: true); + + /// + /// Indicates if and are equal. + /// + /// A value to compare + /// A value to compare + public bool Equals(T value1, T value2) + => Compare(value1, value2, assertMonotonicity: false) == 0; + + public abstract int Compare(T oldValue, T newValue, bool assertMonotonicity); + +#pragma warning disable CA1030 // Use events where appropriate + [Conditional("DEBUG")] + protected static void FireNonMonotonicAssertIfNeeded(bool assertMonotonicity) + { + if (assertMonotonicity) + { + Debug.Fail("Non-monotonic merge"); + } + } +#pragma warning restore CA1030 // Use events where appropriate + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.ConstantValueIndex.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.ConstantValueIndex.cs new file mode 100644 index 0000000000000..dba1c86ed52f4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.ConstantValueIndex.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public abstract partial class AbstractIndex + { + private sealed class ConstantValueIndex : AbstractIndex + { + public ConstantValueIndex(int index) + { + Index = index; + } + + public int Index { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(Index.GetHashCode()); + hashCode.Add(nameof(ConstantValueIndex).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (ConstantValueIndex)obj; + return Index.GetHashCode() == other.Index.GetHashCode(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.OperationBasedIndex.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.OperationBasedIndex.cs new file mode 100644 index 0000000000000..597a45703cc5f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.OperationBasedIndex.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public abstract partial class AbstractIndex + { + private sealed class OperationBasedIndex : AbstractIndex + { + public OperationBasedIndex(IOperation operation) + { + Operation = operation; + } + + public IOperation Operation { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(Operation.GetHashCode()); + hashCode.Add(nameof(OperationBasedIndex).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (OperationBasedIndex)obj; + return Operation.GetHashCode() == other.Operation.GetHashCode(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.SymbolBasedIndex.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.SymbolBasedIndex.cs new file mode 100644 index 0000000000000..56a4dd923779b --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.SymbolBasedIndex.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public abstract partial class AbstractIndex + { + private sealed class AnalysisEntityBasedIndex : AbstractIndex + { + public AnalysisEntityBasedIndex(AnalysisEntity analysisEntity) + { + AnalysisEntity = analysisEntity; + } + + public AnalysisEntity AnalysisEntity { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(AnalysisEntity.GetHashCode()); + hashCode.Add(nameof(AnalysisEntityBasedIndex).GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (AnalysisEntityBasedIndex)obj; + return AnalysisEntity.GetHashCode() == other.AnalysisEntity.GetHashCode(); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.cs new file mode 100644 index 0000000000000..24a6f161e5dc6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractIndex.cs @@ -0,0 +1,19 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Represents an abstract index into a location. + /// It is used by an for operations such as an , index access , etc. + /// + public abstract partial class AbstractIndex : CacheBasedEquatable + { + public static AbstractIndex Create(int index) => new ConstantValueIndex(index); + public static AbstractIndex Create(AnalysisEntity analysisEntity) => new AnalysisEntityBasedIndex(analysisEntity); + public static AbstractIndex Create(IOperation operation) => new OperationBasedIndex(operation); + + internal bool IsConstant() => this is ConstantValueIndex; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocation.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocation.cs new file mode 100644 index 0000000000000..457aa5ffd962a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocation.cs @@ -0,0 +1,204 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// + /// Represents an abstract analysis location. + /// This is may be used to represent a location where an resides, i.e. or + /// a location that is pointed to by a reference type variable, and tracked with . + /// + /// + /// An analysis location can be created for one of the following cases: + /// 1. An allocation or an object creation operation (). + /// 2. Location for the implicit 'this' or 'Me' instance being analyzed (). + /// 3. Location created for certain symbols which do not have a declaration in executable code, i.e. no for declaration (such as parameter symbols, member symbols, etc. - />). + /// 4. Location created for flow capture entities, i.e. for created for or . + /// See + /// + /// + public sealed class AbstractLocation : CacheBasedEquatable + { + private readonly bool _isSpecialSingleton; + public static readonly AbstractLocation Null = new(creation: null, creationCallStack: null, analysisEntity: null, symbol: null, captureId: null, locationType: null, isSpecialSingleton: true); + public static readonly AbstractLocation NoLocation = new(creation: null, creationCallStack: null, analysisEntity: null, symbol: null, captureId: null, locationType: null, isSpecialSingleton: true); + + private AbstractLocation(IOperation? creation, ImmutableStack? creationCallStack, AnalysisEntity? analysisEntity, ISymbol? symbol, InterproceduralCaptureId? captureId, ITypeSymbol? locationType, bool isSpecialSingleton) + { + Debug.Assert(isSpecialSingleton ^ (locationType != null)); + + Creation = creation; + CreationCallStack = creationCallStack ?? ImmutableStack.Empty; + AnalysisEntity = analysisEntity; + Symbol = symbol; + CaptureId = captureId; + LocationType = locationType; + _isSpecialSingleton = isSpecialSingleton; + } + + private static AbstractLocation Create(IOperation? creation, ImmutableStack? creationCallStack, AnalysisEntity? analysisEntity, ISymbol? symbol, InterproceduralCaptureId? captureId, ITypeSymbol? locationType) + { + Debug.Assert(creation != null ^ symbol != null ^ analysisEntity != null ^ captureId != null); + Debug.Assert(locationType != null); + + return new AbstractLocation(creation, creationCallStack, analysisEntity, symbol, captureId, locationType, isSpecialSingleton: false); + } + + public static AbstractLocation CreateAllocationLocation(IOperation creation, ITypeSymbol locationType, PointsToAnalysisContext analysisContext) + => CreateAllocationLocation(creation, locationType, analysisContext.InterproceduralAnalysisData?.CallStack); + internal static AbstractLocation CreateAllocationLocation(IOperation creation, ITypeSymbol locationType, ImmutableStack? callStack) + => Create(creation, callStack, analysisEntity: null, symbol: null, captureId: null, locationType: locationType); + public static AbstractLocation CreateAnalysisEntityDefaultLocation(AnalysisEntity analysisEntity) + => Create(creation: null, creationCallStack: null, analysisEntity: analysisEntity, symbol: null, captureId: null, locationType: analysisEntity.Type); + public static AbstractLocation CreateThisOrMeLocation(INamedTypeSymbol namedTypeSymbol, ImmutableStack? creationCallStack) + => Create(creation: null, creationCallStack: creationCallStack, analysisEntity: null, symbol: namedTypeSymbol, captureId: null, locationType: namedTypeSymbol); + public static AbstractLocation CreateSymbolLocation(ISymbol symbol, ImmutableStack? creationCallStack) + => Create(creation: null, creationCallStack: creationCallStack, analysisEntity: null, symbol: symbol, captureId: null, locationType: symbol.GetMemberOrLocalOrParameterType()); + public static AbstractLocation CreateFlowCaptureLocation(InterproceduralCaptureId captureId, ITypeSymbol locationType, ImmutableStack? creationCallStack) + => Create(creation: null, creationCallStack: creationCallStack, analysisEntity: null, symbol: null, captureId: captureId, locationType: locationType); + + public IOperation? Creation { get; } + public ImmutableStack CreationCallStack { get; } + + /// + /// Returns the top of if this location was created through an interprocedural method invocation, i.e. is non-empty. + /// Otherwise, returns . + /// + public IOperation? GetTopOfCreationCallStackOrCreation() + { + if (CreationCallStack.IsEmpty) + { + return Creation; + } + + return CreationCallStack.Peek(); + } + + public AnalysisEntity? AnalysisEntity { get; } + public ISymbol? Symbol { get; } + public InterproceduralCaptureId? CaptureId { get; } + public ITypeSymbol? LocationType { get; } + public bool IsNull => ReferenceEquals(this, Null); + public bool IsNoLocation => ReferenceEquals(this, NoLocation); + + /// + /// Indicates this represents the initial unknown but distinct location for an analysis entity. + /// + public bool IsAnalysisEntityDefaultLocation => AnalysisEntity != null; + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(Creation.GetHashCodeOrDefault()); + hashCode.Add(HashUtilities.Combine(CreationCallStack)); + hashCode.Add(Symbol.GetHashCodeOrDefault()); + hashCode.Add(CaptureId.GetHashCodeOrDefault()); + hashCode.Add(AnalysisEntity.GetHashCodeOrDefault()); + hashCode.Add(LocationType.GetHashCodeOrDefault()); + hashCode.Add(_isSpecialSingleton.GetHashCode()); + hashCode.Add(IsNull.GetHashCode()); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (AbstractLocation)obj; + return Creation.GetHashCodeOrDefault() == other.Creation.GetHashCodeOrDefault() + && HashUtilities.Combine(CreationCallStack) == HashUtilities.Combine(other.CreationCallStack) + && Symbol.GetHashCodeOrDefault() == other.Symbol.GetHashCodeOrDefault() + && CaptureId.GetHashCodeOrDefault() == other.CaptureId.GetHashCodeOrDefault() + && AnalysisEntity.GetHashCodeOrDefault() == other.AnalysisEntity.GetHashCodeOrDefault() + && LocationType.GetHashCodeOrDefault() == other.LocationType.GetHashCodeOrDefault() + && _isSpecialSingleton.GetHashCode() == other._isSpecialSingleton.GetHashCode() + && IsNull.GetHashCode() == other.IsNull.GetHashCode(); + } + + /// + /// Attempts to get the syntax node to report diagnostic for this abstract location + /// Returns null if the location is owned by another method invoked through interprocedural analysis. + /// + public SyntaxNode? TryGetNodeToReportDiagnostic(PointsToAnalysisResult? pointsToAnalysisResult) + { + Debug.Assert(Creation != null); + + if (pointsToAnalysisResult != null) + { + // Attempt to report diagnostic at the bottommost stack frame that owns the location. + foreach (var creation in CreationCallStack) + { + var syntaxNode = TryGetSyntaxNodeToReportDiagnostic(creation, pointsToAnalysisResult); + if (syntaxNode != null) + { + return syntaxNode; + } + + if (creation is not IInvocationOperation invocation || + !invocation.TargetMethod.IsLambdaOrLocalFunctionOrDelegate()) + { + return null; + } + } + } + + // Fallback to reporting the diagnostic on the allocation location. + return Creation?.Syntax; + + // Local functions. + SyntaxNode? TryGetSyntaxNodeToReportDiagnostic(IOperation creation, PointsToAnalysisResult pointsToAnalysisResult) + { + // If any of the argument to creation points to this location, then use the argument. + var arguments = creation switch + { + IInvocationOperation invocation => invocation.Arguments, + + IObjectCreationOperation objectCreation => objectCreation.Arguments, + + _ => ImmutableArray.Empty, + }; + + foreach (var argument in arguments) + { + var syntaxNode = TryGetSyntaxNodeToReportDiagnosticCore(argument); + if (syntaxNode != null) + { + return syntaxNode; + } + } + + return TryGetSyntaxNodeToReportDiagnosticCore(creation); + + SyntaxNode? TryGetSyntaxNodeToReportDiagnosticCore(IOperation operation) + { + var pointsToValue = pointsToAnalysisResult[operation]; + return TryGetSyntaxNodeToReportDiagnosticForPointsValue(pointsToValue, operation); + + SyntaxNode? TryGetSyntaxNodeToReportDiagnosticForPointsValue(PointsToAbstractValue pointsToValue, IOperation operation) + { + foreach (var location in pointsToValue.Locations) + { + if (location == this) + { + return operation.Syntax; + } + } + + if (pointsToAnalysisResult.TaskWrappedValuesMap != null && + pointsToAnalysisResult.TaskWrappedValuesMap.TryGetValue(pointsToValue, out var wrappedValue)) + { + return TryGetSyntaxNodeToReportDiagnosticForPointsValue(wrappedValue, operation); + } + + return null; + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocationDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocationDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..46246c77215e2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractLocationDataFlowOperationVisitor.cs @@ -0,0 +1,196 @@ +// 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 System.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Operation visitor to flow the abstract dataflow analysis values for s across a given statement in a basic block. + /// + public abstract class AbstractLocationDataFlowOperationVisitor + : DataFlowOperationVisitor + where TAnalysisData : AbstractAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + { + protected AbstractLocationDataFlowOperationVisitor(TAnalysisContext analysisContext) + : base(analysisContext) + { + Debug.Assert(analysisContext.PointsToAnalysisResult != null); + } + + protected abstract TAbstractAnalysisValue GetAbstractValue(AbstractLocation location); + protected abstract void SetAbstractValue(AbstractLocation location, TAbstractAnalysisValue value); + protected void SetAbstractValue(PointsToAbstractValue instanceLocation, TAbstractAnalysisValue value) + => SetAbstractValue(instanceLocation.Locations, value); + protected void SetAbstractValue(IEnumerable locations, TAbstractAnalysisValue value) + { + foreach (var location in locations) + { + SetAbstractValue(location, value); + } + } + + protected abstract void StopTrackingAbstractValue(AbstractLocation location); + protected override void StopTrackingDataForParameter(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + Debug.Assert(DataFlowAnalysisContext.InterproceduralAnalysisData != null); + if (parameter.RefKind == RefKind.None) + { + foreach (var location in analysisEntity.InstanceLocation.Locations) + { + StopTrackingAbstractValue(location); + } + } + } + + protected override void ResetValueTypeInstanceAnalysisData(AnalysisEntity analysisEntity) + { + } + + protected override void ResetReferenceTypeInstanceAnalysisData(PointsToAbstractValue pointsToAbstractValue) + { + } + + protected virtual TAbstractAnalysisValue HandleInstanceCreation(IOperation creation, PointsToAbstractValue instanceLocation, TAbstractAnalysisValue defaultValue) + { + SetAbstractValue(instanceLocation, defaultValue); + return defaultValue; + } + + protected override TAbstractAnalysisValue ComputeAnalysisValueForEscapedRefOrOutArgument(IArgumentOperation operation, TAbstractAnalysisValue defaultValue) + { + Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out); + + if (operation.Value.Type != null) + { + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + var value = HandleInstanceCreation(operation.Value, instanceLocation, defaultValue); + if (operation.Parameter.RefKind == RefKind.Ref) + { + // Escaped ref argument must be set to unknown value. + SetAbstractValue(instanceLocation, ValueDomain.UnknownOrMayBeValue); + return defaultValue; + } + else + { + // Escaped out argument is caller's responsibility and must not be set to unknown value. + return value; + } + } + + return defaultValue; + } + + protected abstract void SetValueForParameterPointsToLocationOnEntry(IParameterSymbol parameter, PointsToAbstractValue pointsToAbstractValue); + protected abstract void EscapeValueForParameterPointsToLocationOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity, ImmutableHashSet escapedLocations); + + protected override void SetValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity, ArgumentInfo? assignedValue) + { + // Only set the value for non-interprocedural case. + // For interprocedural case, we have already initialized values for the underlying locations + // of arguments from the input analysis data. + Debug.Assert(Equals(analysisEntity.Symbol, parameter)); + if (DataFlowAnalysisContext.InterproceduralAnalysisData == null && + TryGetPointsToAbstractValueAtEntryBlockEnd(analysisEntity, out var pointsToAbstractValue)) + { + SetValueForParameterPointsToLocationOnEntry(parameter, pointsToAbstractValue); + } + } + + protected override void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + Debug.Assert(SymbolEqualityComparer.Default.Equals(analysisEntity.Symbol, parameter)); + var escapedLocationsForParameter = GetEscapedLocations(analysisEntity); + if (!escapedLocationsForParameter.IsEmpty) + { + EscapeValueForParameterPointsToLocationOnExit(parameter, analysisEntity, escapedLocationsForParameter); + } + } + + /// + /// Helper method to reset analysis data for analysis locations. + /// + protected void ResetAnalysisData(DictionaryAnalysisData currentAnalysisData) + { + // Reset the current analysis data, while ensuring that we don't violate the monotonicity, i.e. we cannot remove any existing key from currentAnalysisData. + // Just set the values for existing keys to ValueDomain.UnknownOrMayBeValue. + var keys = currentAnalysisData.Keys.ToImmutableArray(); + foreach (var key in keys) + { + SetAbstractValue(key, ValueDomain.UnknownOrMayBeValue); + } + } + + protected static DictionaryAnalysisData GetClonedAnalysisDataHelper(IDictionary analysisData) + => new(analysisData); + protected static DictionaryAnalysisData GetEmptyAnalysisDataHelper() + => GetClonedAnalysisDataHelper(ImmutableDictionary.Empty); + + protected void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData( + DictionaryAnalysisData coreDataAtException, + DictionaryAnalysisData coreCurrentAnalysisData) + { + base.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(coreDataAtException, coreCurrentAnalysisData, predicate: null); + } + + #region Visitor methods + + public override TAbstractAnalysisValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + var value = base.VisitObjectCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitTypeParameterObjectCreation(ITypeParameterObjectCreationOperation operation, object? argument) + { + var value = base.VisitTypeParameterObjectCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitDynamicObjectCreation(IDynamicObjectCreationOperation operation, object? argument) + { + var value = base.VisitDynamicObjectCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation, object? argument) + { + var value = base.VisitAnonymousObjectCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitArrayCreation(IArrayCreationOperation operation, object? argument) + { + var value = base.VisitArrayCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitDelegateCreation(IDelegateCreationOperation operation, object? argument) + { + var value = base.VisitDelegateCreation(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + public override TAbstractAnalysisValue VisitReDimClause(IReDimClauseOperation operation, object? argument) + { + var value = base.VisitReDimClause(operation, argument)!; + PointsToAbstractValue instanceLocation = GetPointsToAbstractValue(operation); + return HandleInstanceCreation(operation, instanceLocation, value); + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractValueDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractValueDomain.cs new file mode 100644 index 0000000000000..f2696f933df3d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AbstractValueDomain.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Abstract value domain for a to merge and compare values. + /// + public abstract class AbstractValueDomain : AbstractDomain + { + /// + /// Returns the major Unknown or MayBe top value of the domain. + /// + public abstract T UnknownOrMayBeValue { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AddressSharedEntitiesProvider.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AddressSharedEntitiesProvider.cs new file mode 100644 index 0000000000000..4467e574d8a51 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AddressSharedEntitiesProvider.cs @@ -0,0 +1,109 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis +{ + /// + /// Generates and stores the default for instances generated for member and element reference operations. + /// + internal sealed class AddressSharedEntitiesProvider + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + where TAnalysisData : AbstractAnalysisData + { + /// + /// Map builder from entity to set of entities that share the same instance location. + /// Primarily used for ref arguments for context sensitive interprocedural analysis + /// to ensure that PointsTo value updates to any of the mapped entities is reflected in the others in the set. + /// + private readonly ImmutableDictionary.Builder _addressSharedEntitiesBuilder; + + public AddressSharedEntitiesProvider(TAnalysisContext analysisContext) + { + _addressSharedEntitiesBuilder = ImmutableDictionary.CreateBuilder(); + SetAddressSharedEntities(analysisContext.InterproceduralAnalysisData?.AddressSharedEntities); + } + + public void SetAddressSharedEntities(ImmutableDictionary? addressSharedEntities) + { + _addressSharedEntitiesBuilder.Clear(); + if (addressSharedEntities != null) + { + _addressSharedEntitiesBuilder.AddRange(addressSharedEntities); + } + } + + public void UpdateAddressSharedEntitiesForParameter(IParameterSymbol parameter, AnalysisEntity analysisEntity, ArgumentInfo? assignedValue) + { + if (parameter.RefKind != RefKind.None && + assignedValue?.AnalysisEntity != null) + { + var addressSharedEntities = ComputeAddressSharedEntities(); + var isReferenceCopy = !addressSharedEntities.Any(a => a.Type.IsValueType); + var copyValue = new CopyAbstractValue(addressSharedEntities, isReferenceCopy); + foreach (var entity in copyValue.AnalysisEntities) + { + _addressSharedEntitiesBuilder[entity] = copyValue; + } + } + + ImmutableHashSet ComputeAddressSharedEntities() + { + RoslynDebug.Assert(assignedValue?.AnalysisEntity != null); + + var builder = PooledHashSet.GetInstance(); + AddIfHasKnownInstanceLocation(analysisEntity, builder); + AddIfHasKnownInstanceLocation(assignedValue.AnalysisEntity, builder); + + // We need to handle multiple ref/out parameters passed the same location. + // For example, "M(ref a, ref a);" + if (_addressSharedEntitiesBuilder.TryGetValue(assignedValue.AnalysisEntity, out var existingValue)) + { + foreach (var entity in existingValue.AnalysisEntities) + { + AddIfHasKnownInstanceLocation(entity, builder); + } + } + + // Also handle case where the passed in argument is also a ref/out parameter and has address shared entities. + if (_addressSharedEntitiesBuilder.TryGetValue(analysisEntity, out existingValue)) + { + foreach (var entity in existingValue.AnalysisEntities) + { + AddIfHasKnownInstanceLocation(entity, builder); + } + } + + Debug.Assert(builder.All(e => !e.HasUnknownInstanceLocation)); + return builder.ToImmutableAndFree(); + } + + static void AddIfHasKnownInstanceLocation(AnalysisEntity entity, PooledHashSet builder) + { + // Only add entity to address shared entities if they have known instance location. + if (!entity.HasUnknownInstanceLocation) + { + builder.Add(entity); + } + } + } + + public CopyAbstractValue GetDefaultCopyValue(AnalysisEntity analysisEntity) + => TryGetAddressSharedCopyValue(analysisEntity) ?? new CopyAbstractValue(analysisEntity); + + public CopyAbstractValue? TryGetAddressSharedCopyValue(AnalysisEntity analysisEntity) + => _addressSharedEntitiesBuilder.TryGetValue(analysisEntity, out var addressSharedEntities) ? + addressSharedEntities : + null; + + public ImmutableDictionary GetAddressedSharedEntityMap() + => _addressSharedEntitiesBuilder.ToImmutable(); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntity.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntity.cs new file mode 100644 index 0000000000000..0bb65ecdfeea6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntity.cs @@ -0,0 +1,299 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// + /// Primary entity for which analysis data is tracked by . + /// + /// + /// The entity is based on one or more of the following: + /// 1. An . + /// 2. One or more indices to index into the parent key. + /// 3. "this" or "Me" instance. + /// 4. An allocation or an object creation. + /// + /// + /// Each entity has: + /// 1. An associated non-null and + /// 2. A non-null indicating the abstract location at which the entity is located and + /// 3. An optional parent key if this key has the same as the parent (i.e. parent is a value type). + /// 4. An optional entity for reference typed if the points to value for the instance location has more than one possible locations. + /// + /// + public sealed class AnalysisEntity : CacheBasedEquatable + { + private readonly int _ignoringLocationHashCode; + + private AnalysisEntity( + ISymbol? symbol, + ImmutableArray indices, + SyntaxNode? instanceReferenceOperationSyntax, + InterproceduralCaptureId? captureId, + PointsToAbstractValue location, + ITypeSymbol type, + AnalysisEntity? parent, + AnalysisEntity? entityForInstanceLocation, + bool isThisOrMeInstance) + { + Debug.Assert(!indices.IsDefault); + Debug.Assert(symbol != null || !indices.IsEmpty || instanceReferenceOperationSyntax != null || captureId.HasValue); + Debug.Assert(parent == null || parent.Type.HasValueCopySemantics() || !indices.IsEmpty); + Debug.Assert(entityForInstanceLocation == null || parent == null); + Debug.Assert(entityForInstanceLocation == null || location.Kind == PointsToAbstractValueKind.KnownLocations); + + Symbol = symbol; + Indices = indices; + InstanceReferenceOperationSyntax = instanceReferenceOperationSyntax; + CaptureId = captureId; + InstanceLocation = location; + Type = type; + Parent = parent; + EntityForInstanceLocation = entityForInstanceLocation; + IsThisOrMeInstance = isThisOrMeInstance; + + _ignoringLocationHashCode = ComputeIgnoringLocationHashCode(); + EqualsIgnoringInstanceLocationId = _ignoringLocationHashCode; + } + + private AnalysisEntity(ISymbol? symbol, ImmutableArray indices, PointsToAbstractValue location, ITypeSymbol type, AnalysisEntity? parent, AnalysisEntity? entityForInstanceLocation) + : this(symbol, indices, instanceReferenceOperationSyntax: null, captureId: null, location: location, type: type, parent: parent, entityForInstanceLocation: entityForInstanceLocation, isThisOrMeInstance: false) + { + Debug.Assert(symbol != null || !indices.IsEmpty); + } + + private AnalysisEntity(IInstanceReferenceOperation instanceReferenceOperation, PointsToAbstractValue location) + : this(symbol: null, indices: ImmutableArray.Empty, instanceReferenceOperationSyntax: instanceReferenceOperation.Syntax, + captureId: null, location: location, type: instanceReferenceOperation.Type!, parent: null, entityForInstanceLocation: null, isThisOrMeInstance: false) + { + Debug.Assert(instanceReferenceOperation != null); + } + + private AnalysisEntity(InterproceduralCaptureId captureId, ITypeSymbol capturedType, PointsToAbstractValue location) + : this(symbol: null, indices: ImmutableArray.Empty, instanceReferenceOperationSyntax: null, + captureId: captureId, location: location, type: capturedType, parent: null, entityForInstanceLocation: null, isThisOrMeInstance: false) + { + } + + private AnalysisEntity(INamedTypeSymbol namedType, PointsToAbstractValue location, bool isThisOrMeInstance) + : this(symbol: namedType, indices: ImmutableArray.Empty, instanceReferenceOperationSyntax: null, + captureId: null, location: location, type: namedType, parent: null, entityForInstanceLocation: null, isThisOrMeInstance: isThisOrMeInstance) + { + } + + public static AnalysisEntity Create(ISymbol? symbol, ImmutableArray indices, + ITypeSymbol type, PointsToAbstractValue instanceLocation, AnalysisEntity? parent, AnalysisEntity? entityForInstanceLocation) + { + Debug.Assert(symbol != null || !indices.IsEmpty); + Debug.Assert(parent == null || parent.InstanceLocation == instanceLocation); + Debug.Assert(entityForInstanceLocation == null || instanceLocation.Kind == PointsToAbstractValueKind.KnownLocations); + + return new AnalysisEntity(symbol, indices, instanceLocation, type, parent, entityForInstanceLocation); + } + + public static AnalysisEntity Create(IInstanceReferenceOperation instanceReferenceOperation, PointsToAbstractValue instanceLocation) + { + return new AnalysisEntity(instanceReferenceOperation, instanceLocation); + } + + public static AnalysisEntity Create( + InterproceduralCaptureId interproceduralCaptureId, + ITypeSymbol type, + PointsToAbstractValue instanceLocation) + { + return new AnalysisEntity(interproceduralCaptureId, type, instanceLocation); + } + + public static AnalysisEntity CreateThisOrMeInstance(INamedTypeSymbol typeSymbol, PointsToAbstractValue instanceLocation) + { + Debug.Assert(instanceLocation.Locations.Count == 1); + Debug.Assert(instanceLocation.Locations.Single().Creation == null); + Debug.Assert(Equals(instanceLocation.Locations.Single().Symbol, typeSymbol)); + + return new AnalysisEntity(typeSymbol, instanceLocation, isThisOrMeInstance: true); + } + + public AnalysisEntity WithMergedInstanceLocation(AnalysisEntity analysisEntityToMerge) + { + Debug.Assert(EqualsIgnoringInstanceLocation(analysisEntityToMerge)); + Debug.Assert(!InstanceLocation.Equals(analysisEntityToMerge.InstanceLocation)); + + var mergedInstanceLocation = PointsToAnalysis.PointsToAnalysis.ValueDomainInstance.Merge(InstanceLocation, analysisEntityToMerge.InstanceLocation); + return new AnalysisEntity(Symbol, Indices, InstanceReferenceOperationSyntax, CaptureId, mergedInstanceLocation, Type, Parent, EntityForInstanceLocation, IsThisOrMeInstance); + } + + public bool IsChildOrInstanceMember + { + get + { + if (IsThisOrMeInstance) + { + return false; + } + + bool result; + if (Symbol != null) + { + result = Symbol.Kind != SymbolKind.Parameter && + Symbol.Kind != SymbolKind.Local && + !Symbol.IsStatic; + } + else if (!Indices.IsEmpty) + { + result = true; + } + else + { + result = false; + } + + Debug.Assert(Parent == null || result); + return result; + } + } + + internal bool ShouldBeTrackedForPointsToAnalysis(PointsToAnalysisKind pointsToAnalysisKind) + => ShouldBeTrackedForAnalysis(pointsToAnalysisKind == PointsToAnalysisKind.Complete); + + internal bool ShouldBeTrackedForAnalysis(bool hasCompletePointsToAnalysisResult) + { + if (!IsChildOrInstanceMember) + { + // We can always perform analysis for entities which are not child or instance members. + return true; + } + + // If we have complete points to analysis result, and this child or instance member has a + // known instance location, we can perform analysis for this entity. + if (hasCompletePointsToAnalysisResult && !HasUnknownInstanceLocation) + { + return true; + } + + // PERF: This is the core performance optimization when we have partial points to analysis result. + // We avoid tracking analysis values for all entities that are child or instance members, + // except when they are fields or members of a value type (for example, tuple elements or struct members). + return Parent != null && Parent.Type.HasValueCopySemantics(); + } + + public bool HasConstantValue => Symbol switch + { + IFieldSymbol fieldSymbol => fieldSymbol.HasConstantValue, + ILocalSymbol localSymbol => localSymbol.HasConstantValue, + _ => false, + }; + + public ISymbol? Symbol { get; } + public ImmutableArray Indices { get; } + public SyntaxNode? InstanceReferenceOperationSyntax { get; } + public InterproceduralCaptureId? CaptureId { get; } + public PointsToAbstractValue InstanceLocation { get; } + public AnalysisEntity? EntityForInstanceLocation { get; } + public ITypeSymbol Type { get; } + public AnalysisEntity? Parent { get; } + public bool IsThisOrMeInstance { get; } + + public bool HasUnknownInstanceLocation => InstanceLocation.Kind switch + { + PointsToAbstractValueKind.Unknown + or PointsToAbstractValueKind.UnknownNull + or PointsToAbstractValueKind.UnknownNotNull => true, + _ => false, + }; + + public bool IsLValueFlowCaptureEntity => CaptureId.HasValue && CaptureId.Value.IsLValueFlowCapture; + + internal AnalysisEntity WithIndices(ImmutableArray indices) + => new(Symbol, indices, InstanceReferenceOperationSyntax, CaptureId, InstanceLocation, Type, Parent, EntityForInstanceLocation, IsThisOrMeInstance); + + public bool EqualsIgnoringInstanceLocation(AnalysisEntity? other) + { + // Perform fast equality checks first. + if (ReferenceEquals(this, other)) + { + return true; + } + + if (other == null || + EqualsIgnoringInstanceLocationId != other.EqualsIgnoringInstanceLocationId) + { + return false; + } + + // Now perform slow check that compares individual hash code parts sequences. + return Symbol.GetHashCodeOrDefault() == other.Symbol.GetHashCodeOrDefault() + && HashUtilities.Combine(Indices) == HashUtilities.Combine(other.Indices) + && InstanceReferenceOperationSyntax.GetHashCodeOrDefault() == other.InstanceReferenceOperationSyntax.GetHashCodeOrDefault() + && CaptureId.GetHashCodeOrDefault() == other.CaptureId.GetHashCodeOrDefault() + && Type.GetHashCodeOrDefault() == other.Type.GetHashCodeOrDefault() + && Parent.GetHashCodeOrDefault() == other.Parent.GetHashCodeOrDefault() + && EntityForInstanceLocation.GetHashCodeOrDefault() == other.EntityForInstanceLocation.GetHashCodeOrDefault() + && IsThisOrMeInstance.GetHashCode() == other.IsThisOrMeInstance.GetHashCode(); + } + + public int EqualsIgnoringInstanceLocationId { get; private set; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(InstanceLocation.GetHashCode()); + ComputeHashCodePartsIgnoringLocation(ref hashCode); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj) + { + var other = (AnalysisEntity)obj; + return InstanceLocation.GetHashCode() == other.InstanceLocation.GetHashCode() + && EqualsIgnoringInstanceLocation(other); + } + + private void ComputeHashCodePartsIgnoringLocation(ref RoslynHashCode hashCode) + { + hashCode.Add(Symbol.GetHashCodeOrDefault()); + hashCode.Add(HashUtilities.Combine(Indices)); + hashCode.Add(InstanceReferenceOperationSyntax.GetHashCodeOrDefault()); + hashCode.Add(CaptureId.GetHashCodeOrDefault()); + hashCode.Add(Type.GetHashCode()); + hashCode.Add(Parent.GetHashCodeOrDefault()); + hashCode.Add(EntityForInstanceLocation.GetHashCodeOrDefault()); + hashCode.Add(IsThisOrMeInstance.GetHashCode()); + } + + private int ComputeIgnoringLocationHashCode() + { + var hashCode = new RoslynHashCode(); + ComputeHashCodePartsIgnoringLocation(ref hashCode); + return hashCode.ToHashCode(); + } + + public bool HasAncestor(AnalysisEntity ancestor) + { + AnalysisEntity? current = this.Parent; + while (current != null) + { + if (current == ancestor) + { + return true; + } + + current = current.Parent; + } + + return false; + } + + internal bool IsCandidatePredicateEntity() + => Type.SpecialType == SpecialType.System_Boolean || + Type.IsNullableOfBoolean() || + Type.Language == LanguageNames.VisualBasic && Type.SpecialType == SpecialType.System_Object; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityBasedPredicateAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityBasedPredicateAnalysisData.cs new file mode 100644 index 0000000000000..c23af0ab078c0 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityBasedPredicateAnalysisData.cs @@ -0,0 +1,234 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Base class for all the aggregate analysis data with predicated analysis data, + /// whose is keyed by an . + /// + public abstract class AnalysisEntityBasedPredicateAnalysisData : PredicatedAnalysisData + { + protected AnalysisEntityBasedPredicateAnalysisData() + { + CoreAnalysisData = new DictionaryAnalysisData(); + } + + protected AnalysisEntityBasedPredicateAnalysisData(IDictionary fromData) + { + CoreAnalysisData = new DictionaryAnalysisData(fromData); + } + + protected AnalysisEntityBasedPredicateAnalysisData(AnalysisEntityBasedPredicateAnalysisData fromData) + : base(fromData) + { + CoreAnalysisData = new DictionaryAnalysisData(fromData.CoreAnalysisData); + } + + protected AnalysisEntityBasedPredicateAnalysisData( + AnalysisEntityBasedPredicateAnalysisData data1, + AnalysisEntityBasedPredicateAnalysisData data2, + MapAbstractDomain coreDataAnalysisDomain) + : base(data1, data2, data1.CoreAnalysisData, + data2.CoreAnalysisData, data1.IsReachableBlockData, coreDataAnalysisDomain) + { + Debug.Assert(data1.IsReachableBlockData == data2.IsReachableBlockData); + + CoreAnalysisData = coreDataAnalysisDomain.Merge(data1.CoreAnalysisData, data2.CoreAnalysisData); + } + + protected AnalysisEntityBasedPredicateAnalysisData( + DictionaryAnalysisData mergedCoreAnalysisData, + PredicatedAnalysisData predicatedData1, + PredicatedAnalysisData predicatedData2, + bool isReachableData, + MapAbstractDomain coreDataAnalysisDomain) + : base(predicatedData1, predicatedData2, mergedCoreAnalysisData, + mergedCoreAnalysisData, isReachableData, coreDataAnalysisDomain) + { + CoreAnalysisData = mergedCoreAnalysisData; + } + + public DictionaryAnalysisData CoreAnalysisData { get; } + + public virtual bool HasAnyAbstractValue => CoreAnalysisData.Count > 0 || HasPredicatedData; + + protected abstract AbstractValueDomain ValueDomain { get; } + public abstract AnalysisEntityBasedPredicateAnalysisData Clone(); + public abstract AnalysisEntityBasedPredicateAnalysisData WithMergedData(AnalysisEntityBasedPredicateAnalysisData data, MapAbstractDomain coreDataAnalysisDomain); + public abstract int Compare(AnalysisEntityBasedPredicateAnalysisData other, MapAbstractDomain coreDataAnalysisDomain); + + protected int BaseCompareHelper(AnalysisEntityBasedPredicateAnalysisData newData, MapAbstractDomain coreDataAnalysisDomain) + { + var baseCompareResult = base.BaseCompareHelper(newData); + if (baseCompareResult != 0) + { + Debug.Assert(baseCompareResult < 0, "Non-monotonic Merge function"); + return baseCompareResult; + } + + var coreAnalysisDataCompareResult = coreDataAnalysisDomain.Compare(CoreAnalysisData, newData.CoreAnalysisData); + Debug.Assert(coreAnalysisDataCompareResult <= 0, "Non-monotonic Merge function"); + return coreAnalysisDataCompareResult; + } + + public bool HasAbstractValue(AnalysisEntity analysisEntity) => CoreAnalysisData.ContainsKey(analysisEntity); + + public bool TryGetValue(AnalysisEntity key, [MaybeNullWhen(false)] out TValue value) => CoreAnalysisData.TryGetValue(key, out value); + +#pragma warning disable CA1043 // Use Integral Or String Argument For Indexers + public TValue this[AnalysisEntity key] => CoreAnalysisData[key]; +#pragma warning restore CA1043 // Use Integral Or String Argument For Indexers + + [Conditional("DEBUG")] + private void AssertValidAnalysisData() + { + Debug.Assert(!CoreAnalysisData.IsDisposed); + AssertValidPredicatedAnalysisData(map => Debug.Assert(!map.IsDisposed)); + } + + public virtual void SetAbstractValue(AnalysisEntity key, TValue value) + { + AssertValidAnalysisData(); + if (HasPredicatedData) + { + RemoveEntriesInPredicatedData(key); + } + + CoreAnalysisData[key] = value; + + ClearOverlappingAnalysisDataForIndexedEntity(key, value); + } + + public void RemoveEntries(AnalysisEntity key) + { + AssertValidAnalysisData(); + + CoreAnalysisData.Remove(key); + if (HasPredicatedData) + { + RemoveEntriesInPredicatedData(key); + } + } + + public bool Equals(AnalysisEntityBasedPredicateAnalysisData other) + { + AssertValidAnalysisData(); + + return base.Equals(other) && + EqualsHelper(CoreAnalysisData, other.CoreAnalysisData); + } + + public virtual void Reset(Func getResetValue) + { + AssertValidAnalysisData(); + + // Reset the current analysis data, while ensuring that we don't violate the monotonicity, i.e. we cannot remove any existing key from currentAnalysisData. + // Just set the values for existing keys to ValueDomain.UnknownOrMayBeValue. + if (CoreAnalysisData.Count > 0) + { + var keys = CoreAnalysisData.Keys.ToImmutableArray(); + foreach (var key in keys) + { + CoreAnalysisData[key] = getResetValue(key, CoreAnalysisData[key]); + } + } + + ResetPredicatedData(); + AssertValidAnalysisData(); + } + + public void StartTrackingPredicatedData(AnalysisEntity predicatedEntity, AnalysisEntityBasedPredicateAnalysisData? truePredicateData, AnalysisEntityBasedPredicateAnalysisData? falsePredicateData) + { + AssertValidAnalysisData(); + + StartTrackingPredicatedData(predicatedEntity, truePredicateData?.CoreAnalysisData, falsePredicateData?.CoreAnalysisData); + AssertValidAnalysisData(); + } + + public PredicateValueKind ApplyPredicatedDataForEntity(AnalysisEntity predicatedEntity, bool trueData) + { + AssertValidAnalysisData(); + + var result = ApplyPredicatedDataForEntity(CoreAnalysisData, predicatedEntity, trueData); + AssertValidAnalysisData(); + return result; + } + + public void AddTrackedEntities(HashSet builder) => builder.UnionWith(CoreAnalysisData.Keys); + + private void ClearOverlappingAnalysisDataForIndexedEntity(AnalysisEntity analysisEntity, TValue value) + { + if (!analysisEntity.Indices.Any(index => !index.IsConstant())) + { + return; + } + + // Collect all the overlapping indexed entities whose value needs to be updated into a builder. + // Ensure that we perform these state updates after the foreach loop to avoid modifying the + // underlying CoreAnalysisData within the loop. + // See https://github.com/dotnet/roslyn-analyzers/issues/6929 for more details. + using var builder = ArrayBuilder<(AnalysisEntity, TValue)>.GetInstance(CoreAnalysisData.Count); + foreach (var entity in CoreAnalysisData.Keys) + { + if (entity.Indices.Length != analysisEntity.Indices.Length || + entity == analysisEntity) + { + continue; + } + + var canOverlap = true; + for (var i = 0; i < entity.Indices.Length; i++) + { + if (entity.Indices[i].IsConstant() && + analysisEntity.Indices[i].IsConstant() && + !entity.Indices[i].Equals(analysisEntity.Indices[i])) + { + canOverlap = false; + break; + } + } + + if (canOverlap && + entity.WithIndices(analysisEntity.Indices).Equals(analysisEntity) && + CoreAnalysisData.TryGetValue(entity, out var existingValue)) + { + var mergedValue = ValueDomain.Merge(value, existingValue); + if (!existingValue!.Equals(mergedValue)) + { + builder.Add((entity, mergedValue)); + } + } + } + + foreach (var (entity, newValue) in builder.AsEnumerable()) + { + SetAbstractValue(entity, newValue); + } + } + + protected override void Dispose(bool disposing) + { + if (IsDisposed) + { + return; + } + + if (disposing) + { + CoreAnalysisData.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..b23f83bfd98ff --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityDataFlowOperationVisitor.cs @@ -0,0 +1,815 @@ +// 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 System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Analyzer.Utilities.PooledObjects.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Operation visitor to flow the abstract dataflow analysis values for instances across a given statement in a basic block. + /// + public abstract class AnalysisEntityDataFlowOperationVisitor + : DataFlowOperationVisitor + where TAnalysisData : AbstractAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + where TAbstractAnalysisValue : IEquatable + { + protected AnalysisEntityDataFlowOperationVisitor(TAnalysisContext analysisContext) + : base(analysisContext) + { + Debug.Assert(!analysisContext.PredicateAnalysis || SupportsPredicateAnalysis); + } + + protected virtual bool SupportsPredicateAnalysis => false; + protected void AddTrackedEntities(HashSet builder, bool forInterproceduralAnalysis = false) + => AddTrackedEntities(CurrentAnalysisData, builder, forInterproceduralAnalysis); + protected abstract void AddTrackedEntities(TAnalysisData analysisData, HashSet builder, bool forInterproceduralAnalysis = false); + protected abstract void SetAbstractValue(AnalysisEntity analysisEntity, TAbstractAnalysisValue value); + protected abstract void ResetAbstractValue(AnalysisEntity analysisEntity); + protected abstract TAbstractAnalysisValue GetAbstractValue(AnalysisEntity analysisEntity); + protected abstract bool HasAbstractValue(AnalysisEntity analysisEntity); + protected abstract void StopTrackingEntity(AnalysisEntity analysisEntity, TAnalysisData analysisData); + + protected override TAbstractAnalysisValue ComputeAnalysisValueForReferenceOperation(IOperation operation, TAbstractAnalysisValue defaultValue) + { + if (AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + if (!HasAbstractValue(analysisEntity)) + { + SetAbstractValue(analysisEntity, defaultValue); + } + + return GetAbstractValue(analysisEntity); + } + else + { + return defaultValue; + } + } + + protected sealed override TAbstractAnalysisValue ComputeAnalysisValueForEscapedRefOrOutArgument(IArgumentOperation operation, TAbstractAnalysisValue defaultValue) + { + Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out); + + if (AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + var value = ComputeAnalysisValueForEscapedRefOrOutArgument(analysisEntity, operation, defaultValue); + SetAbstractValueForAssignment(analysisEntity, operation, value); + return GetAbstractValue(analysisEntity); + } + else + { + return defaultValue; + } + } + + protected virtual TAbstractAnalysisValue ComputeAnalysisValueForEscapedRefOrOutArgument(AnalysisEntity analysisEntity, IArgumentOperation operation, TAbstractAnalysisValue defaultValue) + { + Debug.Assert(operation.Parameter!.RefKind is RefKind.Ref or RefKind.Out); + + return defaultValue; + } + + /// + /// Helper method to reset analysis data for analysis entities. + /// + protected void ResetAnalysisData(DictionaryAnalysisData currentAnalysisData) + { + // Reset the current analysis data, while ensuring that we don't violate the monotonicity, i.e. we cannot remove any existing key from currentAnalysisData. + // Just set the values for existing keys to ValueDomain.UnknownOrMayBeValue. + var keys = currentAnalysisData.Keys.ToImmutableArray(); + foreach (var key in keys) + { + ResetAbstractValue(key); + } + } + + protected override void ProcessOutOfScopeLocalsAndFlowCaptures(IEnumerable locals, IEnumerable flowCaptures) + { + Debug.Assert(locals.Any() || flowCaptures.Any()); + + base.ProcessOutOfScopeLocalsAndFlowCaptures(locals, flowCaptures); + + using var allEntities = PooledHashSet.GetInstance(); + AddTrackedEntities(allEntities); + + // Stop tracking entities for locals and capture Ids that are now out of scope. + foreach (var local in locals) + { + if (AnalysisEntityFactory.TryCreateForSymbolDeclaration(local, out var analysisEntity)) + { + StopTrackingDataForEntity(analysisEntity, allEntities); + } + else + { + Debug.Fail("TryCreateForSymbolDeclaration failed"); + } + } + + foreach (var captureId in flowCaptures) + { + if (AnalysisEntityFactory.TryGetForFlowCapture(captureId, out var analysisEntity)) + { + StopTrackingDataForEntity(analysisEntity, allEntities); + } + } + } + + private void StopTrackingDataForEntity(AnalysisEntity analysisEntity, PooledHashSet allEntities) + => StopTrackingDataForEntity(analysisEntity, CurrentAnalysisData, allEntities); + + private void StopTrackingDataForEntity(AnalysisEntity analysisEntity, TAnalysisData analysisData, PooledHashSet allEntities) + { + if (!allEntities.Contains(analysisEntity)) + { + return; + } + + // Stop tracking entity that is now out of scope. + StopTrackingEntity(analysisEntity, analysisData); + + // Additionally, stop tracking all the child entities if the entity type has value copy semantics. + if (analysisEntity.Type.HasValueCopySemantics()) + { + foreach (var childEntity in GetChildAnalysisEntities(analysisEntity, allEntities)) + { + StopTrackingEntity(childEntity, analysisData); + } + } + } + + private void StopTrackingDataForParamArrayParameterIndices(AnalysisEntity analysisEntity, TAnalysisData analysisData, PooledHashSet allEntities) + { + Debug.Assert(analysisEntity.Symbol is IParameterSymbol parameter && parameter.IsParams); + + foreach (var entity in allEntities) + { + if (!entity.Indices.IsEmpty && + entity.InstanceLocation.Equals(analysisEntity.InstanceLocation)) + { + StopTrackingEntity(entity, analysisData); + } + } + } + + protected sealed override void StopTrackingDataForParameter(IParameterSymbol parameter, AnalysisEntity analysisEntity) + => throw new InvalidOperationException("Unreachable"); + + protected sealed override void StopTrackingDataForParameters(ImmutableDictionary parameterEntities) + { + if (!parameterEntities.IsEmpty) + { + using var allEntities = PooledHashSet.GetInstance(); + AddTrackedEntities(allEntities); + + foreach (var (parameter, parameterEntity) in parameterEntities) + { + StopTrackingDataForEntity(parameterEntity, CurrentAnalysisData, allEntities); + + if (parameter.IsParams) + { + StopTrackingDataForParamArrayParameterIndices(parameterEntity, CurrentAnalysisData, allEntities); + } + } + } + } + + protected override TAnalysisData GetMergedAnalysisDataForPossibleThrowingOperation(TAnalysisData? existingData, IOperation operation) + { + // Get tracked entities. + using var entitiesBuilder = PooledHashSet.GetInstance(); + AddTrackedEntities(entitiesBuilder); + + // Only non-child entities are tracked for now. + var resultAnalysisData = GetTrimmedCurrentAnalysisData(entitiesBuilder.Where(e => !e.IsChildOrInstanceMember && HasAbstractValue(e))); + if (existingData != null) + { + var mergedAnalysisData = MergeAnalysisData(resultAnalysisData, existingData); + resultAnalysisData.Dispose(); + resultAnalysisData = mergedAnalysisData; + } + + return resultAnalysisData; + } + + #region Helper methods to handle initialization/assignment operations + protected override void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, TAbstractAnalysisValue value) + { + if (AnalysisEntityFactory.TryCreateForArrayElementInitializer(arrayCreation, indices, elementType, out var analysisEntity)) + { + SetAbstractValueForAssignment(analysisEntity, initializer, value); + } + } + + protected override void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue, bool mayBeAssignment = false) + { + if (AnalysisEntityFactory.TryCreate(target, out var targetAnalysisEntity)) + { + if (!targetAnalysisEntity.ShouldBeTrackedForAnalysis(HasCompletePointsToAnalysisResult)) + { + // We are not tracking points to values for fields and properties. + // So, it is not possible to accurately track value changes to target entity which is a member. + // Conservatively assume that the entity is assigned an unknown value. + assignedValue = ValueDomain.UnknownOrMayBeValue; + } + else if (mayBeAssignment) + { + assignedValue = ValueDomain.Merge(GetAbstractValue(targetAnalysisEntity), assignedValue); + } + + SetAbstractValueForAssignment(targetAnalysisEntity, assignedValueOperation, assignedValue); + } + } + + protected override void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, TAbstractAnalysisValue assignedValue) + { + if (tupleElementEntity.Type.IsTupleType) + { + // For nested tuple entity, we only want to flow the value for the tuple, not it's children. + // Children of nested tuples should have already been assigned when visiting the nested tuple operation in the base dataflow visitor. + SetAbstractValue(tupleElementEntity, assignedValue); + } + else + { + SetAbstractValueForAssignment(tupleElementEntity, assignedValueOperation, assignedValue); + } + } + + protected virtual void SetAbstractValueForAssignment(AnalysisEntity targetAnalysisEntity, IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue) + { + AnalysisEntity? assignedValueEntity = null; + if (assignedValueOperation != null) + { + var success = AnalysisEntityFactory.TryCreate(assignedValueOperation, out assignedValueEntity); + Debug.Assert(success || assignedValueEntity == null); + } + + SetAbstractValueForAssignment(targetAnalysisEntity, assignedValueEntity, assignedValueOperation, assignedValue); + } + + private void SetAbstractValueForAssignment(AnalysisEntity targetAnalysisEntity, AnalysisEntity? assignedValueEntity, IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue) + { + // Value type and string type assignment has copy semantics. + if (HasPointsToAnalysisResult && + targetAnalysisEntity.Type.HasValueCopySemantics()) + { + // Reset the analysis values for analysis entities within the target instance. + ResetValueTypeInstanceAnalysisData(targetAnalysisEntity); + + // Transfer the values of symbols from the assigned instance to the analysis entities in the target instance. + TransferValueTypeInstanceAnalysisDataForAssignment(targetAnalysisEntity, assignedValueEntity, assignedValueOperation); + } + + var addressSharedCopyValue = TryGetAddressSharedCopyValue(targetAnalysisEntity); + if (addressSharedCopyValue != null) + { + Debug.Assert(addressSharedCopyValue.AnalysisEntities.Contains(targetAnalysisEntity)); + foreach (var entity in addressSharedCopyValue.AnalysisEntities) + { + SetAbstractValue(entity, assignedValue); + } + } + else + { + SetAbstractValue(targetAnalysisEntity, assignedValue); + } + } + + protected override void SetValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity, ArgumentInfo? assignedValue) + { + Debug.Assert(SymbolEqualityComparer.Default.Equals(analysisEntity.Symbol, parameter)); + if (assignedValue != null) + { + SetAbstractValueForAssignment(analysisEntity, assignedValue.Operation, assignedValue.Value); + } + else + { + SetAbstractValue(analysisEntity, GetDefaultValueForParameterOnEntry(parameter, analysisEntity)); + } + } + + protected override void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity) + { + Debug.Assert(Equals(analysisEntity.Symbol, parameter)); + if (parameter.RefKind != RefKind.None) + { + SetAbstractValue(analysisEntity, GetDefaultValueForParameterOnExit(analysisEntity.Type)); + } + } + + protected virtual TAbstractAnalysisValue GetDefaultValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity) => ValueDomain.UnknownOrMayBeValue; + protected virtual TAbstractAnalysisValue GetDefaultValueForParameterOnExit(ITypeSymbol parameterType) => ValueDomain.UnknownOrMayBeValue; + + #endregion + + #region Helper methods for reseting/transfer instance analysis data when PointsTo analysis results are available + + /// + /// Resets all the analysis data for all instances that share the same + /// as the given . + /// + /// + protected override void ResetValueTypeInstanceAnalysisData(AnalysisEntity analysisEntity) + { + Debug.Assert(HasPointsToAnalysisResult); + Debug.Assert(analysisEntity.Type.HasValueCopySemantics()); + + IEnumerable dependantAnalysisEntities = GetChildAnalysisEntities(analysisEntity); + ResetInstanceAnalysisDataCore(dependantAnalysisEntities.Concat(analysisEntity)); + } + + protected override void ResetReferenceTypeInstanceAnalysisData(PointsToAbstractValue pointsToAbstractValue) + { + Debug.Assert(HasPointsToAnalysisResult); + + IEnumerable dependantAnalysisEntities = GetChildAnalysisEntities(pointsToAbstractValue); + ResetInstanceAnalysisDataCore(dependantAnalysisEntities); + } + + /// + /// Resets the analysis data for the given . + /// + /// + private void ResetInstanceAnalysisDataCore(IEnumerable dependantAnalysisEntities) + { + foreach (var dependentAnalysisEntity in dependantAnalysisEntities) + { + ResetAbstractValue(dependentAnalysisEntity); + } + } + + /// + /// Transfers the analysis data rooted from or to , for a value type assignment operation. + /// This involves transfer of data for of all instances that share the same as or allocation for the + /// to all instances that share the same as . + /// + private void TransferValueTypeInstanceAnalysisDataForAssignment(AnalysisEntity targetAnalysisEntity, AnalysisEntity? valueAnalysisEntity, IOperation? assignedValueOperation) + { + Debug.Assert(HasPointsToAnalysisResult); + Debug.Assert(targetAnalysisEntity.Type.HasValueCopySemantics()); + + IEnumerable dependentAnalysisEntities; + if (valueAnalysisEntity != null) + { + if (!valueAnalysisEntity.Type.HasValueCopySemantics()) + { + // Unboxing conversion from assigned value (reference type) to target (value copy semantics). + // We do not need to transfer any data for such a case as there is no entity for unboxed value. + return; + } + + dependentAnalysisEntities = GetChildAnalysisEntities(valueAnalysisEntity); + } + else if (assignedValueOperation != null) + { + // For allocations. + PointsToAbstractValue newValueLocation = GetPointsToAbstractValue(assignedValueOperation); + dependentAnalysisEntities = GetChildAnalysisEntities(newValueLocation); + } + else + { + return; + } + + foreach (AnalysisEntity dependentInstance in dependentAnalysisEntities) + { + // Clone the dependent instance but with target as the root. + AnalysisEntity newAnalysisEntity = AnalysisEntityFactory.CreateWithNewInstanceRoot(dependentInstance, targetAnalysisEntity); + var dependentValue = GetAbstractValue(dependentInstance); + SetAbstractValue(newAnalysisEntity, dependentValue); + } + } + + private ImmutableHashSet GetChildAnalysisEntities(AnalysisEntity analysisEntity) + { + // PERF: If we do not have complete points to analysis data, then there cannot be any + // child entities for reference type entities. + if (!HasCompletePointsToAnalysisResult && analysisEntity.Type.IsReferenceType) + { + return ImmutableHashSet.Empty; + } + + return GetChildAnalysisEntities(analysisEntity.InstanceLocation, entity => IsChildAnalysisEntity(entity, analysisEntity)); + } + + private static IEnumerable GetChildAnalysisEntities(AnalysisEntity analysisEntity, HashSet allEntities) + { + Debug.Assert(analysisEntity.Type.HasValueCopySemantics()); + + foreach (var entity in allEntities) + { + if (IsChildAnalysisEntity(entity, ancestorEntity: analysisEntity)) + { + yield return entity; + } + } + } + + protected static bool IsChildAnalysisEntity(AnalysisEntity entity, AnalysisEntity ancestorEntity) + { + return (!ancestorEntity.Type.HasValueCopySemantics() || entity.HasAncestor(ancestorEntity)) && + IsChildAnalysisEntity(entity, ancestorEntity.InstanceLocation); + } + + protected ImmutableHashSet GetChildAnalysisEntities(PointsToAbstractValue? instanceLocation) + { + return GetChildAnalysisEntities(instanceLocation, predicate: null); + } + + private ImmutableHashSet GetChildAnalysisEntities(PointsToAbstractValue? instanceLocation, Func? predicate) + { + // We are interested only in dependent child/member infos, not the root info. + if (instanceLocation == null || instanceLocation.Kind == PointsToAbstractValueKind.Unknown) + { + return ImmutableHashSet.Empty; + } + + predicate ??= entity => IsChildAnalysisEntity(entity, instanceLocation); + + return GetChildAnalysisEntities(predicate); + } + + protected static bool IsChildAnalysisEntity(AnalysisEntity entity, PointsToAbstractValue instanceLocation) + { + return instanceLocation != PointsToAbstractValue.NoLocation && + entity.InstanceLocation.Equals(instanceLocation) && + entity.IsChildOrInstanceMember; + } + + private ImmutableHashSet GetChildAnalysisEntities(Func predicate) + { + var trackedEntitiesBuilder = PooledHashSet.GetInstance(); + AddTrackedEntities(trackedEntitiesBuilder); + trackedEntitiesBuilder.RemoveWhere(entity => !predicate(entity)); + return trackedEntitiesBuilder.ToImmutableAndFree(); + } + + #endregion + + #region Interprocedural analysis + protected override TAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + { + // PERF: For non-lambda and local functions + presence of points to values, we trim down + // the initial analysis data passed as input to interprocedural analysis. + // We retain the analysis entities for the invocation instance, arguments and this or me instance. + // Additionally, we also retain the transitive closure of analysis entities reachable from these + // entities via the PointsTo values chain (i.e., recursively compute child analysis entities). + // All the remaining entities are not accessible in the callee and are excluded from the initial + // interprocedural analysis data. + + if (isLambdaOrLocalFunction || hasParameterWithDelegateType || pointsToValues == null) + { + return base.GetInitialInterproceduralAnalysisData(invokedMethod, invocationInstance, + thisOrMeInstanceForCaller, argumentValuesMap, pointsToValues, copyValues, valueContentValues, + isLambdaOrLocalFunction, hasParameterWithDelegateType); + } + + using var candidateEntitiesBuilder = PooledHashSet.GetInstance(); + using var interproceduralEntitiesToRetainBuilder = PooledHashSet.GetInstance(); + using var worklistEntities = PooledHashSet.GetInstance(); + using var worklistPointsToValues = PooledHashSet.GetInstance(); + using var processedPointsToValues = PooledHashSet.GetInstance(); + using var childWorklistEntities = PooledHashSet.GetInstance(); + + // All tracked entities are candidates to be retained for initial interprocedural + // analysis data. + AddTrackedEntities(candidateEntitiesBuilder, forInterproceduralAnalysis: true); + var candidateEntitiesCount = candidateEntitiesBuilder.Count; + + // Add entities and PointsTo values for invocation instance, this or me instance + // and argument values to the initial worklist + + if (invocationInstance.HasValue) + { + AddWorklistEntityAndPointsToValue(invocationInstance.Value.Instance); + AddWorklistPointsToValue(invocationInstance.Value.PointsToValue); + } + + if (thisOrMeInstanceForCaller.HasValue) + { + AddWorklistEntityAndPointsToValue(thisOrMeInstanceForCaller.Value.Instance); + AddWorklistPointsToValue(thisOrMeInstanceForCaller.Value.PointsToValue); + } + + foreach (var argument in argumentValuesMap.Values) + { + if (!AddWorklistEntityAndPointsToValue(argument.AnalysisEntity)) + { + // For allocations passed as arguments. + AddWorklistPointsToValue(argument.InstanceLocation); + } + } + + // Worklist based algorithm to compute the transitive closure of analysis entities + // that are accessible in the callee via the PointsTo value chain. + while (worklistEntities.Count > 0 || worklistPointsToValues.Count > 0) + { + if (worklistEntities.Count > 0) + { + // Add all the worklistEntities to interproceduralEntitiesBuilder + // to ensure these entities are retained. + interproceduralEntitiesToRetainBuilder.AddRange(worklistEntities); + + // Remove the worklistEntities from tracked candidate entities. + candidateEntitiesBuilder.ExceptWith(worklistEntities); + + // Add child entities of worklistEntities to childWorklistEntities. + // PERF: We cannot have any child entities for PointsToAnalysis if we + // not computing complete PointsToAnalysis data. + if (HasCompletePointsToAnalysisResult || !IsPointsToAnalysis) + { + foreach (var candidateEntity in candidateEntitiesBuilder) + { + foreach (var ancestorEntity in worklistEntities) + { + if (IsChildAnalysisEntity(candidateEntity, ancestorEntity)) + { + childWorklistEntities.Add(candidateEntity); + break; + } + } + } + } + + worklistEntities.Clear(); + } + + if (worklistPointsToValues.Count > 0) + { + // Add child entities which are accessible from PointsTo chain to childWorklistEntities. + // PERF: We cannot have any child entities for PointsToAnalysis if we + // not computing complete PointsToAnalysis data. + if (HasCompletePointsToAnalysisResult || !IsPointsToAnalysis) + { + foreach (var candidateEntity in candidateEntitiesBuilder) + { + foreach (var pointsToValue in worklistPointsToValues) + { + Debug.Assert(ShouldProcessPointsToValue(pointsToValue)); + if (IsChildAnalysisEntity(candidateEntity, pointsToValue)) + { + childWorklistEntities.Add(candidateEntity); + break; + } + } + } + } + + worklistPointsToValues.Clear(); + } + + // Move all the child work list entities and their PointsTo values to the worklist. + foreach (var childEntity in childWorklistEntities) + { + AddWorklistEntityAndPointsToValue(childEntity); + } + + childWorklistEntities.Clear(); + } + + // If all candidates being retained, just retain the cloned current analysis data. + if (interproceduralEntitiesToRetainBuilder.Count == candidateEntitiesCount) + { + return GetClonedCurrentAnalysisData(); + } + + // Otherwise, return cloned current analysis data with trimmed keys. + return GetTrimmedCurrentAnalysisData(interproceduralEntitiesToRetainBuilder); + + // Local functions. + bool AddWorklistEntityAndPointsToValue(AnalysisEntity? analysisEntity) + { + RoslynDebug.Assert(pointsToValues != null); + + if (analysisEntity != null && candidateEntitiesBuilder.Contains(analysisEntity)) + { + worklistEntities.Add(analysisEntity); + + if (pointsToValues.TryGetValue(analysisEntity, out var pointsToValue)) + { + AddWorklistPointsToValue(pointsToValue); + } + + return true; + } + + return false; + } + + void AddWorklistPointsToValue(PointsToAbstractValue pointsToValue) + { + if (ShouldProcessPointsToValue(pointsToValue) && + processedPointsToValues.Add(pointsToValue)) + { + worklistPointsToValues.Add(pointsToValue); + } + } + + static bool ShouldProcessPointsToValue(PointsToAbstractValue pointsToValue) + => pointsToValue.Kind == PointsToAbstractValueKind.KnownLocations && + pointsToValue != PointsToAbstractValue.NoLocation; + } + + /// + /// Returns a cloned CurrentAnalysisData, trimmed down to only have key-value pairs for the given . + /// + protected abstract TAnalysisData GetTrimmedCurrentAnalysisData(IEnumerable withEntities); + + protected TAnalysisData GetTrimmedCurrentAnalysisDataHelper( + IEnumerable withEntities, + IDictionary existingValues, + Action setAbstractValue) + { + var initialAnalysisData = GetEmptyAnalysisData(); + foreach (var entity in withEntities) + { + setAbstractValue(initialAnalysisData, entity, existingValues[entity]); + } + + return initialAnalysisData; + } + + protected abstract void ApplyInterproceduralAnalysisResultCore(TAnalysisData resultData); + + protected sealed override void ApplyInterproceduralAnalysisResult( + TAnalysisData resultData, + bool isLambdaOrLocalFunction, + bool hasDelegateTypeArgument, + TAnalysisResult analysisResult) + { + if (isLambdaOrLocalFunction || hasDelegateTypeArgument) + { + base.ApplyInterproceduralAnalysisResult(resultData, isLambdaOrLocalFunction, hasDelegateTypeArgument, analysisResult); + return; + } + + ApplyInterproceduralAnalysisResultCore(resultData); + } + + protected void ApplyInterproceduralAnalysisResultHelper(IDictionary resultToApply) + { + foreach (var kvp in resultToApply) + { + var entity = kvp.Key; + var newValue = kvp.Value; + var currentValue = GetAbstractValue(entity); + if (!currentValue.Equals(newValue)) + { + SetAbstractValue(entity, newValue); + } + } + } + + internal bool ShouldStopTrackingEntityAtExit(AnalysisEntity entity) + { + Debug.Assert(DataFlowAnalysisContext.InterproceduralAnalysisData != null); + + // Filter out all the parameter, local symbol and flow capture entities from the analysis data. + return IsParameterEntityForCurrentMethod(entity) || + entity.Symbol?.Kind == SymbolKind.Local && + entity.Symbol.ContainingSymbol.Equals(DataFlowAnalysisContext.OwningSymbol) || + entity.CaptureId.HasValue && + entity.CaptureId.Value.ControlFlowGraph == DataFlowAnalysisContext.ControlFlowGraph; + } + + public override TAnalysisData? GetMergedDataForUnhandledThrowOperations() + { + // For interprocedural analysis, prune analysis data for unhandled exceptions + // to remove analysis entities that are only valid in the callee. + if (DataFlowAnalysisContext.InterproceduralAnalysisData != null && + AnalysisDataForUnhandledThrowOperations != null && + AnalysisDataForUnhandledThrowOperations.Values.Any(HasAnyAbstractValue)) + { + using var allAnalysisEntities = PooledHashSet.GetInstance(); + + foreach (var dataAtException in AnalysisDataForUnhandledThrowOperations.Values) + { + AddTrackedEntities(dataAtException, allAnalysisEntities, forInterproceduralAnalysis: true); + } + + foreach (var entity in allAnalysisEntities) + { + if (ShouldStopTrackingEntityAtExit(entity)) + { + foreach (var dataAtException in AnalysisDataForUnhandledThrowOperations.Values) + { + StopTrackingDataForEntity(entity, dataAtException, allAnalysisEntities); + } + } + } + } + + return base.GetMergedDataForUnhandledThrowOperations(); + } + + #endregion + + protected DictionaryAnalysisData GetClonedAnalysisDataHelper(IDictionary analysisData) + => new(analysisData); + + protected void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData( + DictionaryAnalysisData coreDataAtException, + DictionaryAnalysisData coreCurrentAnalysisData, + ThrownExceptionInfo throwBranchWithExceptionType) + { + Func? predicate = null; + if (throwBranchWithExceptionType.IsDefaultExceptionForExceptionsPathAnalysis) + { + // Only tracking non-child analysis entities for exceptions path analysis for now. + Debug.Assert(throwBranchWithExceptionType.ExceptionType.Equals(ExceptionNamedType)); + predicate = e => !e.IsChildOrInstanceMember; + } + + base.ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(coreDataAtException, coreCurrentAnalysisData, predicate); + } + + #region Visitor methods + + public override TAbstractAnalysisValue VisitDeconstructionAssignment(IDeconstructionAssignmentOperation operation, object? argument) + { + var value = base.VisitDeconstructionAssignment(operation, argument); + var assignedInstance = GetPointsToAbstractValue(operation.Value); + HandleDeconstructionAssignment(operation.Target, GetChildAnalysisEntities(assignedInstance)); + return value; + } + + private void HandleDeconstructionAssignment(IOperation target, ImmutableHashSet childEntities) + { + if (target is IDeclarationExpressionOperation declarationExpressionOperation) + { + target = declarationExpressionOperation.Expression; + } + + if (target is ITupleOperation tupleOperation && + AnalysisEntityFactory.TryCreateForTupleElements(tupleOperation, out var tupleElementEntities)) + { + Debug.Assert(tupleOperation.Elements.Length == tupleElementEntities.Length); + for (int i = 0; i < tupleOperation.Elements.Length; i++) + { + var element = tupleOperation.Elements[i]; + var tupleElementEntity = tupleElementEntities[i]; + if (element is ITupleOperation tupleElement) + { + Debug.Assert(tupleElementEntity.Symbol is IFieldSymbol field); + HandleDeconstructionAssignment(tupleElement, childEntities); + } + else if (AnalysisEntityFactory.TryCreate(element, out var elementEntity)) + { + AnalysisEntity? assignedValueEntity = childEntities.FirstOrDefault(c => IsMatchingAssignedEntity(tupleElementEntity, c)); + var assignedValue = assignedValueEntity != null ? GetAbstractValue(assignedValueEntity) : ValueDomain.UnknownOrMayBeValue; + SetAbstractValueForAssignment(elementEntity, assignedValueEntity, assignedValueOperation: null, assignedValue); + } + } + } + + return; + + // Local function + static bool IsMatchingAssignedEntity(AnalysisEntity tupleElementEntity, AnalysisEntity? childEntity) + { + if (childEntity == null) + { + return false; + } + + if (tupleElementEntity.Parent == null) + { + // Root tuple entity, compare the underlying tuple types. + return childEntity.Parent == null && + SymbolEqualityComparer.Default.Equals(tupleElementEntity.Type.OriginalDefinition, childEntity.Type.OriginalDefinition); + } + + // Must be a tuple element field entity. + return tupleElementEntity.Symbol is IFieldSymbol tupleElementField && + childEntity.Symbol is IFieldSymbol childEntityField && + SymbolEqualityComparer.Default.Equals(tupleElementField.OriginalDefinition, childEntityField.OriginalDefinition) && + IsMatchingAssignedEntity(tupleElementEntity.Parent, childEntity.Parent); + } + } + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityFactory.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityFactory.cs new file mode 100644 index 0000000000000..c739df53b6cc8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityFactory.cs @@ -0,0 +1,579 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Factory to create objects for operations, symbol declarations, etc. + /// This factory also tracks analysis entities that share the same instance location (e.g. value type members). + /// NOTE: This factory must only be used from within an , as it is tied to the visitor's state tracking via delegate. + /// + public sealed class AnalysisEntityFactory + { + private readonly ControlFlowGraph _controlFlowGraph; + private readonly WellKnownTypeProvider _wellKnownTypeProvider; + private readonly Dictionary _analysisEntityMap; + private readonly Dictionary> _tupleElementEntitiesMap; + private readonly Dictionary _captureIdEntityMap; + private readonly Dictionary _captureIdCopyValueMap; + private readonly Dictionary _instanceLocationsForSymbols; + private readonly Func? _getPointsToAbstractValue; + private readonly Func _getIsInsideAnonymousObjectInitializer; + private readonly Func _getIsLValueFlowCapture; + private readonly AnalysisEntity? _interproceduralThisOrMeInstanceForCaller; + private readonly ImmutableStack? _interproceduralCallStack; + private readonly Func? _interproceduralGetAnalysisEntityForFlowCapture; + private readonly Func?> _getInterproceduralCallStackForOwningSymbol; + + internal AnalysisEntityFactory( + ControlFlowGraph controlFlowGraph, + WellKnownTypeProvider wellKnownTypeProvider, + Func? getPointsToAbstractValue, + Func getIsInsideAnonymousObjectInitializer, + Func getIsLValueFlowCapture, + INamedTypeSymbol containingTypeSymbol, + AnalysisEntity? interproceduralInvocationInstance, + AnalysisEntity? interproceduralThisOrMeInstanceForCaller, + ImmutableStack? interproceduralCallStack, + ImmutableDictionary? interproceduralCapturedVariablesMap, + Func? interproceduralGetAnalysisEntityForFlowCapture, + Func?> getInterproceduralCallStackForOwningSymbol) + { + _controlFlowGraph = controlFlowGraph; + _wellKnownTypeProvider = wellKnownTypeProvider; + _getPointsToAbstractValue = getPointsToAbstractValue; + _getIsInsideAnonymousObjectInitializer = getIsInsideAnonymousObjectInitializer; + _getIsLValueFlowCapture = getIsLValueFlowCapture; + _interproceduralThisOrMeInstanceForCaller = interproceduralThisOrMeInstanceForCaller; + _interproceduralCallStack = interproceduralCallStack; + _interproceduralGetAnalysisEntityForFlowCapture = interproceduralGetAnalysisEntityForFlowCapture; + _getInterproceduralCallStackForOwningSymbol = getInterproceduralCallStackForOwningSymbol; + + _analysisEntityMap = new Dictionary(); + _tupleElementEntitiesMap = new Dictionary>(); + _captureIdEntityMap = new Dictionary(); + _captureIdCopyValueMap = new Dictionary(); + + _instanceLocationsForSymbols = new Dictionary(); + if (interproceduralCapturedVariablesMap != null) + { + _instanceLocationsForSymbols.AddRange(interproceduralCapturedVariablesMap); + } + + if (interproceduralInvocationInstance != null) + { + ThisOrMeInstance = interproceduralInvocationInstance; + } + else + { + var thisOrMeInstanceLocation = AbstractLocation.CreateThisOrMeLocation(containingTypeSymbol, interproceduralCallStack); + var instanceLocation = PointsToAbstractValue.Create(thisOrMeInstanceLocation, mayBeNull: false); + ThisOrMeInstance = AnalysisEntity.CreateThisOrMeInstance(containingTypeSymbol, instanceLocation); + } + } + + public AnalysisEntity ThisOrMeInstance { get; } + + private static ImmutableArray CreateAbstractIndices(ImmutableArray indices) + where T : IOperation + { + if (!indices.IsEmpty) + { + var builder = ArrayBuilder.GetInstance(indices.Length); + foreach (var index in indices) + { + builder.Add(CreateAbstractIndex(index)); + } + + return builder.ToImmutableAndFree(); + } + + return ImmutableArray.Empty; + } + + private static AbstractIndex CreateAbstractIndex(IOperation operation) + { + if (operation.ConstantValue.HasValue && operation.ConstantValue.Value is int index) + { + return AbstractIndex.Create(index); + } + // TODO: We need to find the abstract value for the entity to use it for indexing. + // https://github.com/dotnet/roslyn-analyzers/issues/1577 + //else if (TryCreate(operation, out AnalysisEntity analysisEntity)) + //{ + // return AbstractIndex.Create(analysisEntity); + //} + + return AbstractIndex.Create(operation); + } + + public bool TryCreate(IOperation operation, [NotNullWhen(returnValue: true)] out AnalysisEntity? analysisEntity) + { + if (_analysisEntityMap.TryGetValue(operation, out analysisEntity)) + { + return analysisEntity != null; + } + + analysisEntity = null; + ISymbol? symbol = null; + ImmutableArray indices = ImmutableArray.Empty; + IOperation? instance = null; + ITypeSymbol? type = operation.Type; + switch (operation) + { + case ILocalReferenceOperation localReference: + symbol = localReference.Local; + break; + + case IParameterReferenceOperation parameterReference: + symbol = parameterReference.Parameter; + break; + + case IMemberReferenceOperation memberReference: + instance = memberReference.Instance; + GetSymbolAndIndicesForMemberReference(memberReference, ref symbol, ref indices); + break; + + case IArrayElementReferenceOperation arrayElementReference: + instance = arrayElementReference.ArrayReference; + indices = CreateAbstractIndices(arrayElementReference.Indices); + break; + + case IDynamicIndexerAccessOperation dynamicIndexerAccess: + instance = dynamicIndexerAccess.Operation; + indices = CreateAbstractIndices(dynamicIndexerAccess.Arguments); + break; + + case IConditionalAccessInstanceOperation conditionalAccessInstance: + IConditionalAccessOperation? conditionalAccess = conditionalAccessInstance.GetConditionalAccess(); + instance = conditionalAccess?.Operation; + if (conditionalAccessInstance.Parent is IMemberReferenceOperation memberReferenceParent) + { + GetSymbolAndIndicesForMemberReference(memberReferenceParent, ref symbol, ref indices); + } + + break; + + case IInstanceReferenceOperation instanceReference: + if (_getPointsToAbstractValue != null) + { + instance = instanceReference.GetInstance(_getIsInsideAnonymousObjectInitializer()); + if (instance == null) + { + // Reference to this or base instance. + analysisEntity = _interproceduralCallStack != null && _interproceduralCallStack.Peek().DescendantsAndSelf().Contains(instanceReference) ? + _interproceduralThisOrMeInstanceForCaller : + ThisOrMeInstance; + } + else + { + var instanceLocation = _getPointsToAbstractValue(instanceReference); + analysisEntity = AnalysisEntity.Create(instanceReference, instanceLocation); + } + } + + break; + + case IConversionOperation conversion: + return TryCreate(conversion.Operand, out analysisEntity); + + case IParenthesizedOperation parenthesized: + return TryCreate(parenthesized.Operand, out analysisEntity); + + case IArgumentOperation argument: + return TryCreate(argument.Value, out analysisEntity); + + case IFlowCaptureOperation flowCapture: + var isLvalueFlowCapture = _getIsLValueFlowCapture(flowCapture); + analysisEntity = GetOrCreateForFlowCapture(flowCapture.Id, flowCapture.Value.Type, flowCapture, isLvalueFlowCapture); + + // Store flow capture copy values for simple flow captures of non-flow captured entity. + // This enables pseudo copy-analysis of values of these two entities in absence of true copy analysis, which is expensive. + if (!isLvalueFlowCapture && + TryCreate(flowCapture.Value, out var capturedEntity) && + capturedEntity.CaptureId == null && + !_captureIdCopyValueMap.ContainsKey(flowCapture.Id) && + analysisEntity.Type.IsValueType == capturedEntity.Type.IsValueType) + { + // Skip flow capture for conversions unless we know the points to value + // for conversion and operand is identical. + if (flowCapture.Value is IConversionOperation conversion) + { + if (_getPointsToAbstractValue == null || + _getPointsToAbstractValue(conversion) != _getPointsToAbstractValue(conversion.Operand)) + { + break; + } + } + + var kind = capturedEntity.Type.IsValueType ? CopyAbstractValueKind.KnownValueCopy : CopyAbstractValueKind.KnownReferenceCopy; + var copyValue = new CopyAbstractValue(ImmutableHashSet.Create(analysisEntity, capturedEntity), kind); + _captureIdCopyValueMap.Add(flowCapture.Id, copyValue); + } + + break; + + case IFlowCaptureReferenceOperation flowCaptureReference: + analysisEntity = GetOrCreateForFlowCapture(flowCaptureReference.Id, flowCaptureReference.Type, flowCaptureReference, flowCaptureReference.IsLValueFlowCaptureReference()); + break; + + case IDeclarationExpressionOperation declarationExpression: + switch (declarationExpression.Expression) + { + case ILocalReferenceOperation localReference: + return TryCreateForSymbolDeclaration(localReference.Local, out analysisEntity); + + case ITupleOperation tupleOperation: + return TryCreate(tupleOperation, out analysisEntity); + } + + break; + + case IVariableDeclaratorOperation variableDeclarator: + symbol = variableDeclarator.Symbol; + type = variableDeclarator.Symbol.Type; + break; + + case IDeclarationPatternOperation declarationPattern: + var declaredLocal = declarationPattern.DeclaredSymbol as ILocalSymbol; + symbol = declaredLocal; + type = declaredLocal?.Type; + break; + + default: + break; + } + + if (symbol != null || !indices.IsEmpty) + { + TryCreate(symbol, indices, type!, instance, out analysisEntity); + } + + _analysisEntityMap[operation] = analysisEntity; + return analysisEntity != null; + } + + private static void GetSymbolAndIndicesForMemberReference(IMemberReferenceOperation memberReference, ref ISymbol? symbol, ref ImmutableArray indices) + { + switch (memberReference) + { + case IFieldReferenceOperation fieldReference: + symbol = fieldReference.Field; + if (fieldReference.Field.CorrespondingTupleField != null) + { + // For tuple fields, always use the CorrespondingTupleField (i.e. Item1, Item2, etc.) from the underlying value tuple type. + // This allows seamless operation between named tuple elements and use of Item1, Item2, etc. to access tuple elements. + var name = fieldReference.Field.CorrespondingTupleField.Name; + symbol = fieldReference.Field.ContainingType.GetUnderlyingValueTupleTypeOrThis()?.GetMembers(name).OfType().FirstOrDefault() + ?? symbol; + } + + break; + + case IEventReferenceOperation eventReference: + symbol = eventReference.Member; + break; + + case IPropertyReferenceOperation propertyReference: + // We are only tracking: + // 1) Indexers + // 2) Read-only properties. + // 3) Properties with a backing field (auto-generated properties) + if (!propertyReference.Arguments.IsEmpty || + propertyReference.Property.IsReadOnly || + propertyReference.Property.IsPropertyWithBackingField(out _)) + { + symbol = propertyReference.Property; + indices = !propertyReference.Arguments.IsEmpty ? + CreateAbstractIndices(propertyReference.Arguments.Select(a => a.Value).ToImmutableArray()) : + ImmutableArray.Empty; + } + + break; + } + } + + public bool TryCreateForSymbolDeclaration(ISymbol symbol, [NotNullWhen(returnValue: true)] out AnalysisEntity? analysisEntity) + { + Debug.Assert(symbol.Kind is SymbolKind.Local or SymbolKind.Parameter or SymbolKind.Field or SymbolKind.Property); + + var indices = ImmutableArray.Empty; + IOperation? instance = null; + var type = symbol.GetMemberOrLocalOrParameterType(); + RoslynDebug.Assert(type != null); + + return TryCreate(symbol, indices, type, instance, out analysisEntity); + } + + public bool TryCreateForTupleElements(ITupleOperation tupleOperation, [NotNullWhen(returnValue: true)] out ImmutableArray elementEntities) + { + if (_tupleElementEntitiesMap.TryGetValue(tupleOperation, out elementEntities)) + { + return !elementEntities.IsDefault; + } + + try + { + elementEntities = default; + if (tupleOperation.Type?.IsTupleType != true || + _getPointsToAbstractValue == null) + { + return false; + } + + var tupleType = (INamedTypeSymbol)tupleOperation.Type; + if (tupleType.TupleElements.IsDefault) + { + return false; + } + + PointsToAbstractValue instanceLocation = _getPointsToAbstractValue(tupleOperation); + AnalysisEntity? entityForInstanceLocation = null; + var underlyingValueTupleType = tupleType.GetUnderlyingValueTupleTypeOrThis()!; + AnalysisEntity? parentEntity = null; + if (tupleOperation.TryGetParentTupleOperation(out var parentTupleOperationOpt, out var elementOfParentTupleContainingTuple) && + TryCreateForTupleElements(parentTupleOperationOpt, out var parentTupleElementEntities)) + { + Debug.Assert(parentTupleOperationOpt.Elements.Length == parentTupleElementEntities.Length); + for (int i = 0; i < parentTupleOperationOpt.Elements.Length; i++) + { + if (parentTupleOperationOpt.Elements[i] == elementOfParentTupleContainingTuple) + { + parentEntity = parentTupleElementEntities[i]; + instanceLocation = parentEntity.InstanceLocation; + entityForInstanceLocation = parentEntity.EntityForInstanceLocation; + break; + } + } + + RoslynDebug.Assert(parentEntity != null); + } + else + { + parentEntity = AnalysisEntity.Create(underlyingValueTupleType, ImmutableArray.Empty, + underlyingValueTupleType, instanceLocation, parent: null, entityForInstanceLocation: null); + } + + Debug.Assert(parentEntity.InstanceLocation == instanceLocation); + + using var builder = ArrayBuilder.GetInstance(tupleType.TupleElements.Length); + foreach (var field in tupleType.TupleElements) + { + var tupleFieldName = field.CorrespondingTupleField!.Name; + var mappedValueTupleField = underlyingValueTupleType.GetMembers(tupleFieldName).OfType().FirstOrDefault(); + if (mappedValueTupleField == null) + { + return false; + } + + builder.Add(AnalysisEntity.Create(mappedValueTupleField, indices: ImmutableArray.Empty, + type: mappedValueTupleField.Type, instanceLocation, parentEntity, entityForInstanceLocation)); + } + + elementEntities = builder.ToImmutable(); + return true; + } + finally + { + _tupleElementEntitiesMap[tupleOperation] = elementEntities; + } + } + + public bool TryCreateForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, [NotNullWhen(returnValue: true)] out AnalysisEntity? analysisEntity) + { + Debug.Assert(!indices.IsEmpty); + + return TryCreate(symbol: null, indices, elementType, arrayCreation, out analysisEntity); + } + + public bool TryGetForFlowCapture(CaptureId captureId, out AnalysisEntity analysisEntity) + => _captureIdEntityMap.TryGetValue(captureId, out analysisEntity); + + public bool TryGetCopyValueForFlowCapture(CaptureId captureId, out CopyAbstractValue copyValue) + => _captureIdCopyValueMap.TryGetValue(captureId, out copyValue); + + public bool TryGetForInterproceduralAnalysis(IOperation operation, out AnalysisEntity? analysisEntity) + => _analysisEntityMap.TryGetValue(operation, out analysisEntity); + + private AnalysisEntity GetOrCreateForFlowCapture(CaptureId captureId, ITypeSymbol? type, IOperation flowCaptureOrReference, bool isLValueFlowCapture) + { + // Type can be null for capture of operations with OperationKind.None + type ??= _wellKnownTypeProvider.Compilation.GetSpecialType(SpecialType.System_Object); + + var interproceduralFlowCaptureEntity = _interproceduralGetAnalysisEntityForFlowCapture?.Invoke(flowCaptureOrReference); + if (interproceduralFlowCaptureEntity != null) + { + Debug.Assert(_interproceduralCallStack.Last().Descendants().Contains(flowCaptureOrReference)); + return interproceduralFlowCaptureEntity; + } + + Debug.Assert(_controlFlowGraph.DescendantOperations().Contains(flowCaptureOrReference)); + if (!_captureIdEntityMap.TryGetValue(captureId, out var entity)) + { + var interproceduralCaptureId = new InterproceduralCaptureId(captureId, _controlFlowGraph, isLValueFlowCapture); + var instanceLocation = PointsToAbstractValue.Create( + AbstractLocation.CreateFlowCaptureLocation(interproceduralCaptureId, type, _interproceduralCallStack), + mayBeNull: false); + entity = AnalysisEntity.Create(interproceduralCaptureId, type, instanceLocation); + _captureIdEntityMap.Add(captureId, entity); + } + + return entity; + } + + private bool TryCreate(ISymbol? symbol, ImmutableArray indices, + ITypeSymbol type, IOperation? instance, [NotNullWhen(returnValue: true)] out AnalysisEntity? analysisEntity) + { + Debug.Assert(symbol != null || !indices.IsEmpty); + + analysisEntity = null; + + // Only analyze member symbols if we have points to analysis result. + if (_getPointsToAbstractValue == null && + symbol?.Kind != SymbolKind.Local && + symbol?.Kind != SymbolKind.Parameter) + { + return false; + } + + PointsToAbstractValue? instanceLocation = null; + AnalysisEntity? entityForReferenceTypeInstance = null; + AnalysisEntity? parent = null; + if (instance?.Type != null) + { + if (instance.Type.IsValueType) + { + if (TryCreate(instance, out var instanceEntityOpt) && + instanceEntityOpt.Type.IsValueType) + { + parent = instanceEntityOpt; + instanceLocation = parent.InstanceLocation; + } + else + { + // For value type allocations, we store the points to location. + var instancePointsToValue = _getPointsToAbstractValue!(instance); + if (!ReferenceEquals(instancePointsToValue, PointsToAbstractValue.NoLocation)) + { + instanceLocation = instancePointsToValue; + } + } + + if (instanceLocation == null) + { + return false; + } + } + else + { + instanceLocation = _getPointsToAbstractValue!(instance); + + // If the instanceLocation can point to multiple potential locations, then we also store the + // entity for the instance location in the analysis entity. This is done to ensure that we + // can distinguish this entity from any other entity which can also point to the same set of + // potential locations, but the actual runtime location for both these entities can be different. + // See https://github.com/dotnet/roslyn-analyzers/issues/6520 for an example. + if (instanceLocation.Kind == PointsToAbstractValueKind.KnownLocations && + instanceLocation.Locations.Count > 1) + { + if (TryCreate(instance, out var instanceEntity)) + { + entityForReferenceTypeInstance = instanceEntity; + } + else + { + instanceLocation = instanceLocation.NullState switch + { + NullAbstractValue.Null => PointsToAbstractValue.UnknownNull, + NullAbstractValue.NotNull => PointsToAbstractValue.UnknownNotNull, + _ => PointsToAbstractValue.Unknown, + }; + } + } + } + } + + analysisEntity = Create(symbol, indices, type, instanceLocation, parent, entityForReferenceTypeInstance); + return true; + } + + private PointsToAbstractValue? EnsureLocation(PointsToAbstractValue? instanceLocation, ISymbol? symbol, AnalysisEntity? parent) + { + if (instanceLocation == null && symbol != null) + { + Debug.Assert(symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter || symbol.IsStatic || symbol.IsLambdaOrLocalFunction()); + + if (!_instanceLocationsForSymbols.TryGetValue(symbol, out instanceLocation)) + { + if (parent != null) + { + instanceLocation = parent.InstanceLocation; + } + else + { + // Symbol instance location for locals and parameters should also include the interprocedural call stack because + // we might have recursive invocations to the same method and the symbol declarations + // from both the current and prior invocation of the method in the call stack should be distinct entities. + ImmutableStack? interproceduralCallStackForSymbolDeclaration; + if (_interproceduralCallStack != null && + (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)) + { + interproceduralCallStackForSymbolDeclaration = _getInterproceduralCallStackForOwningSymbol(symbol.ContainingSymbol); + } + else + { + interproceduralCallStackForSymbolDeclaration = ImmutableStack.Empty; + } + + var location = AbstractLocation.CreateSymbolLocation(symbol, interproceduralCallStackForSymbolDeclaration); + instanceLocation = PointsToAbstractValue.Create(location, mayBeNull: false); + } + + _instanceLocationsForSymbols.Add(symbol, instanceLocation); + } + } + + return instanceLocation; + } + + private AnalysisEntity Create(ISymbol? symbol, ImmutableArray indices, ITypeSymbol type, PointsToAbstractValue? instanceLocation, AnalysisEntity? parent, AnalysisEntity? entityForInstanceLocation) + { + instanceLocation = EnsureLocation(instanceLocation, symbol, parent); + RoslynDebug.Assert(instanceLocation != null); + var analysisEntity = AnalysisEntity.Create(symbol, indices, type, instanceLocation, parent, entityForInstanceLocation); + return analysisEntity; + } + + public AnalysisEntity CreateWithNewInstanceRoot(AnalysisEntity analysisEntity, AnalysisEntity newRootInstance) + { + if (analysisEntity.InstanceLocation == newRootInstance.InstanceLocation && + analysisEntity.Parent == newRootInstance.Parent && + analysisEntity.EntityForInstanceLocation == newRootInstance.EntityForInstanceLocation) + { + return analysisEntity; + } + + if (analysisEntity.Parent == null) + { + return newRootInstance; + } + + AnalysisEntity parentOpt = CreateWithNewInstanceRoot(analysisEntity.Parent, newRootInstance); + return Create(analysisEntity.Symbol, analysisEntity.Indices, analysisEntity.Type, newRootInstance.InstanceLocation, parentOpt, newRootInstance.EntityForInstanceLocation); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityMapAbstractDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityMapAbstractDomain.cs new file mode 100644 index 0000000000000..58149f793a148 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/AnalysisEntityMapAbstractDomain.cs @@ -0,0 +1,277 @@ +// 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.Diagnostics; +using System.Linq; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// An abstract domain implementation for analyses that store dictionary typed data. + /// + public abstract class AnalysisEntityMapAbstractDomain : MapAbstractDomain + { + private static readonly Func s_defaultIsTrackedEntity = new(_ => true); + private static readonly Func s_defaultIsTrackedPointsToValue = new(_ => true); + + private readonly Func _isTrackedEntity; + private readonly Func _isTrackedPointsToValue; + + private protected AnalysisEntityMapAbstractDomain( + AbstractValueDomain valueDomain, + Func isTrackedEntity, + Func isTrackedPointsToValue) + : base(valueDomain) + { + _isTrackedEntity = isTrackedEntity ?? throw new ArgumentNullException(nameof(isTrackedEntity)); + _isTrackedPointsToValue = isTrackedPointsToValue ?? throw new ArgumentNullException(nameof(isTrackedPointsToValue)); + } + + protected AnalysisEntityMapAbstractDomain(AbstractValueDomain valueDomain, PointsToAnalysisResult? pointsToAnalysisResult) + : this(valueDomain, + pointsToAnalysisResult != null ? pointsToAnalysisResult.IsTrackedEntity : s_defaultIsTrackedEntity, + pointsToAnalysisResult != null ? pointsToAnalysisResult.IsTrackedPointsToValue : s_defaultIsTrackedPointsToValue) + { + } + + protected abstract TValue GetDefaultValue(AnalysisEntity analysisEntity); + protected abstract bool CanSkipNewEntry(AnalysisEntity analysisEntity, TValue value); + protected virtual void OnNewMergedValue(TValue value) + { + } + + private bool CanSkipNewEntity(AnalysisEntity analysisEntity) + { + if (_isTrackedEntity(analysisEntity) || + _isTrackedPointsToValue(analysisEntity.InstanceLocation)) + { + return false; + } + + if (analysisEntity.Parent != null && + !CanSkipNewEntity(analysisEntity.Parent)) + { + return false; + } + + return true; + } + + protected abstract void AssertValidEntryForMergedMap(AnalysisEntity analysisEntity, TValue value); + protected virtual void AssertValidAnalysisData(DictionaryAnalysisData map) + { +#if DEBUG + foreach (var kvp in map) + { + AssertValidEntryForMergedMap(kvp.Key, kvp.Value); + } +#endif + } + +#pragma warning disable CA1725 // Parameter names should match base declaration + public override DictionaryAnalysisData Merge(DictionaryAnalysisData map1, DictionaryAnalysisData map2) +#pragma warning restore CA1725 // Parameter names should match base declaration + { + AssertValidAnalysisData(map1); + AssertValidAnalysisData(map2); + + var resultMap = new DictionaryAnalysisData(); + using var newKeys = PooledHashSet.GetInstance(); + using var valuesToMergeBuilder = ArrayBuilder.GetInstance(5); + + var map2LookupIgnoringInstanceLocation = map2.Keys.Where(IsAnalysisEntityForFieldOrProperty) + .ToLookup(entity => entity.EqualsIgnoringInstanceLocationId); + foreach (var entry1 in map1) + { + AnalysisEntity key1 = entry1.Key; + TValue value1 = entry1.Value; + + if (map2LookupIgnoringInstanceLocation.Count > 0 && IsAnalysisEntityForFieldOrProperty(key1)) + { + var equivalentKeys2 = map2LookupIgnoringInstanceLocation[key1.EqualsIgnoringInstanceLocationId]; + if (!equivalentKeys2.Any()) + { + TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key1, value1); + Debug.Assert(!map2.ContainsKey(key1)); + Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); + AddNewEntryToResultMap(key1, mergedValue); + continue; + } + + foreach (AnalysisEntity key2 in equivalentKeys2) + { + // Confirm that key2 and key1 are indeed EqualsIgnoringInstanceLocation + // This ensures that we handle hash code clashes of EqualsIgnoringInstanceLocationId. + if (!key1.EqualsIgnoringInstanceLocation(key2)) + { + continue; + } + + TValue value2 = map2[key2]; + + valuesToMergeBuilder.Clear(); + valuesToMergeBuilder.Add(value1); + valuesToMergeBuilder.Add(value2); + + if (key1.InstanceLocation.Equals(key2.InstanceLocation)) + { + var mergedValue = GetMergedValue(valuesToMergeBuilder); + AddNewEntryToResultMap(key1, mergedValue); + } + else + { + if (key1.Symbol == null || !SymbolEqualityComparer.Default.Equals(key1.Symbol, key2.Symbol)) + { + // PERF: Do not add a new key-value pair to the resultMap for unrelated entities or non-symbol based entities. + continue; + } + + AnalysisEntity mergedKey = key1.WithMergedInstanceLocation(key2); + + var isExistingKeyInInput = false; + var isExistingKeyInResult = false; + if (resultMap.TryGetValue(mergedKey, out var existingValue)) + { + valuesToMergeBuilder.Add(existingValue); + isExistingKeyInResult = true; + } + + if (map1.TryGetValue(mergedKey, out existingValue)) + { + valuesToMergeBuilder.Add(existingValue); + isExistingKeyInInput = true; + } + + if (map2.TryGetValue(mergedKey, out existingValue)) + { + valuesToMergeBuilder.Add(existingValue); + isExistingKeyInInput = true; + } + + var isCandidateToBeSkipped = !isExistingKeyInInput && !isExistingKeyInResult; + if (isCandidateToBeSkipped && CanSkipNewEntity(mergedKey)) + { + // PERF: Do not add a new key-value pair to the resultMap if the key is not reachable from tracked entities and PointsTo values. + continue; + } + + var mergedValue = GetMergedValue(valuesToMergeBuilder); + + Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); + Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); + + if (isCandidateToBeSkipped && CanSkipNewEntry(mergedKey, mergedValue)) + { + // PERF: Do not add a new key-value pair to the resultMap if the value can be skipped. + continue; + } + + if (!isExistingKeyInInput) + { + newKeys.Add(mergedKey); + } + + AddNewEntryToResultMap(mergedKey, mergedValue, isNewKey: !isExistingKeyInInput); + } + } + } + else if (map2.TryGetValue(key1, out var value2)) + { + TValue mergedValue = ValueDomain.Merge(value1, value2); + Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); + Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); + AddNewEntryToResultMap(key1, mergedValue); + continue; + } + + if (!resultMap.ContainsKey(key1)) + { + TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key1, value1); + Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); + AddNewEntryToResultMap(key1, mergedValue); + } + } + + foreach (var kvp in map2) + { + var key2 = kvp.Key; + var value2 = kvp.Value; + if (!resultMap.ContainsKey(key2)) + { + TValue mergedValue = GetMergedValueForEntityPresentInOneMap(key2, value2); + Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); + AddNewEntryToResultMap(key2, mergedValue); + } + } + + foreach (var newKey in newKeys) + { + Debug.Assert(!map1.ContainsKey(newKey)); + Debug.Assert(!map2.ContainsKey(newKey)); + var value = resultMap[newKey]; + if (ReferenceEquals(value, GetDefaultValue(newKey))) + { + resultMap.Remove(newKey); + } + else + { + OnNewMergedValue(value); + } + } + + Debug.Assert(Compare(map1, resultMap) <= 0); + Debug.Assert(Compare(map2, resultMap) <= 0); + AssertValidAnalysisData(resultMap); + + return resultMap; + static bool IsAnalysisEntityForFieldOrProperty(AnalysisEntity entity) + => entity.Symbol?.Kind is SymbolKind.Field or SymbolKind.Property; + + TValue GetMergedValueForEntityPresentInOneMap(AnalysisEntity key, TValue value) + { + if (key.HasConstantValue) + { + return value; + } + + var defaultValue = GetDefaultValue(key); + return ValueDomain.Merge(value, defaultValue); + } + + TValue GetMergedValue(ArrayBuilder values) + { + Debug.Assert(values.Count > 0); + var mergedValue = values[0]; + for (var i = 1; i < values.Count; i++) + { + mergedValue = GetMergedValueCore(mergedValue, values[i]); + } + + return mergedValue; + + TValue GetMergedValueCore(TValue value1, TValue value2) + { + TValue mergedValue = ValueDomain.Merge(value1, value2); + Debug.Assert(ValueDomain.Compare(value1, mergedValue) <= 0); + Debug.Assert(ValueDomain.Compare(value2, mergedValue) <= 0); + return mergedValue; + } + } + + void AddNewEntryToResultMap(AnalysisEntity key, TValue value, bool isNewKey = false) + { + Debug.Assert(isNewKey == (!map1.ContainsKey(key) && !map2.ContainsKey(key))); + AssertValidEntryForMergedMap(key, value); + resultMap[key] = value; + if (!isNewKey) + { + OnNewMergedValue(value); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ArgumentInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ArgumentInfo.cs new file mode 100644 index 0000000000000..2bbe475907549 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ArgumentInfo.cs @@ -0,0 +1,50 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Contains information about an argument passed to interprocedural analysis. + /// + public sealed class ArgumentInfo : CacheBasedEquatable> + { + public ArgumentInfo( + IOperation operation, + AnalysisEntity? analysisEntity, + PointsToAbstractValue instanceLocation, + TAbstractAnalysisValue value) + { + Operation = operation; + AnalysisEntity = analysisEntity; + InstanceLocation = instanceLocation; + Value = value; + } + + public IOperation Operation { get; } + // Can be null for allocations. + public AnalysisEntity? AnalysisEntity { get; } + public PointsToAbstractValue InstanceLocation { get; } + public TAbstractAnalysisValue Value { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(Operation.GetHashCode()); + hashCode.Add(AnalysisEntity.GetHashCodeOrDefault()); + hashCode.Add(InstanceLocation.GetHashCode()); + hashCode.Add(Value?.GetHashCode() ?? 0); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable> obj) + { + var other = (ArgumentInfo)obj; + return Operation.GetHashCode() == other.Operation.GetHashCode() + && AnalysisEntity.GetHashCodeOrDefault() == other.AnalysisEntity.GetHashCodeOrDefault() + && InstanceLocation.GetHashCode() == other.InstanceLocation.GetHashCode() + && (Value?.GetHashCode() ?? 0) == (other.Value?.GetHashCode() ?? 0); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/CacheBasedEquatable.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/CacheBasedEquatable.cs new file mode 100644 index 0000000000000..63a3fa729e0f9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/CacheBasedEquatable.cs @@ -0,0 +1,84 @@ +// 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 Analyzer.Utilities; + +#pragma warning disable CA2002 + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Abstract cache based equatable implementation for objects that are compared frequently and hence need a performance optimization of using a cached hash code. + /// + public abstract class CacheBasedEquatable : IEquatable + where T : class + { + private int _lazyHashCode; + + protected CacheBasedEquatable() + { + } + + private int GetOrComputeHashCode() + { + if (_lazyHashCode == 0) + { + var hashCode = new RoslynHashCode(); + ComputeHashCodeParts(ref hashCode); + var result = hashCode.ToHashCode(); + Interlocked.CompareExchange(ref _lazyHashCode, result, 0); + } + + return _lazyHashCode; + } + + protected abstract void ComputeHashCodeParts(ref RoslynHashCode hashCode); + + protected abstract bool ComputeEqualsByHashCodeParts(CacheBasedEquatable obj); + + public sealed override int GetHashCode() => GetOrComputeHashCode(); + + public sealed override bool Equals(object? obj) => Equals(obj as T); + public bool Equals(T? other) + { + // Perform fast equality checks first. + if (ReferenceEquals(this, other)) + { + return true; + } + + var otherEquatable = other as CacheBasedEquatable; + if (otherEquatable == null + || GetType() != otherEquatable.GetType() + || GetHashCode() != otherEquatable.GetHashCode()) + { + return false; + } + + // Now perform slow check that compares individual hash code parts sequences. + return ComputeEqualsByHashCodeParts(otherEquatable); + } + + public static bool operator ==(CacheBasedEquatable? value1, CacheBasedEquatable? value2) + { + if (value1 is null) + { + return value2 is null; + } + else if (value2 is null) + { + return false; + } + + return value1.Equals(value2); + } + + public static bool operator !=(CacheBasedEquatable? value1, CacheBasedEquatable? value2) + { + return !(value1 == value2); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ConversionInference.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ConversionInference.cs new file mode 100644 index 0000000000000..8e3ed9812ee06 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ConversionInference.cs @@ -0,0 +1,76 @@ +// 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 Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Conversion inference result. + /// + internal struct ConversionInference : IEquatable + { + public static ConversionInference Create(IConversionOperation operation) + => Create( + targetType: operation.Type, + sourceType: operation.Operand.Type, + isTryCast: operation.IsTryCast); + + public static ConversionInference Create(IIsPatternOperation operation) + => Create( + targetType: operation.Pattern.GetPatternType(), + sourceType: operation.Value.Type, + isTryCast: true); + + public static ConversionInference Create( + ITypeSymbol? targetType, + ITypeSymbol? sourceType, + bool isTryCast) + { + return new ConversionInference + { + IsTryCast = isTryCast, + AlwaysSucceed = !isTryCast, // For direct cast, we assume the cast will always succeed as the initial default value. + AlwaysFail = false, + IsBoxing = targetType != null && + !targetType.IsValueType && + sourceType?.IsValueType == true, + IsUnboxing = targetType != null && + targetType.IsValueType && + sourceType != null && + !sourceType.IsValueType + }; + } + + public bool IsTryCast { get; set; } + public bool AlwaysSucceed { get; set; } + public bool AlwaysFail { get; set; } + public bool IsBoxing { get; set; } + public bool IsUnboxing { get; set; } + + public override readonly bool Equals(object obj) + => obj is ConversionInference other && Equals(other); + + public readonly bool Equals(ConversionInference other) + { + return IsTryCast == other.IsTryCast && + AlwaysSucceed == other.AlwaysSucceed && + AlwaysFail == other.AlwaysFail && + IsBoxing == other.IsBoxing && + IsUnboxing == other.IsUnboxing; + } + + public override readonly int GetHashCode() + => RoslynHashCode.Combine(IsTryCast, AlwaysSucceed, AlwaysFail, IsBoxing, IsUnboxing); + + public static bool operator ==(ConversionInference left, ConversionInference right) + => left.Equals(right); + + public static bool operator !=(ConversionInference left, ConversionInference right) + => !(left == right); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs new file mode 100644 index 0000000000000..3a2b0803f3b0d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysis.cs @@ -0,0 +1,830 @@ +// 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.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Subtype for all dataflow analyses on a control flow graph. + /// It performs a worklist based approach to flow abstract data values for / across the basic blocks until a fix point is reached. + /// + public abstract class DataFlowAnalysis + where TAnalysisData : AbstractAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : DataFlowAnalysisResult + where TBlockAnalysisResult : AbstractBlockAnalysisResult + { + private static readonly BoundedCache> s_resultCache = + new(); + + protected DataFlowAnalysis(AbstractAnalysisDomain analysisDomain, DataFlowOperationVisitor operationVisitor) + { + AnalysisDomain = analysisDomain; + OperationVisitor = operationVisitor; + } + + protected AbstractAnalysisDomain AnalysisDomain { get; } + protected DataFlowOperationVisitor OperationVisitor { get; } + + protected TAnalysisResult? TryGetOrComputeResultCore(TAnalysisContext analysisContext, bool cacheResult) + { + if (analysisContext == null) + { + throw new ArgumentNullException(nameof(analysisContext)); + } + + // Don't add interprocedural analysis result to our static results cache. + if (!cacheResult || analysisContext.InterproceduralAnalysisData != null) + { + return Run(analysisContext); + } + + var analysisResultsMap = s_resultCache.GetOrCreateValue(analysisContext.ControlFlowGraph.OriginalOperation); + return analysisResultsMap.GetOrAdd(analysisContext, _ => Run(analysisContext)); + } + + private TAnalysisResult? Run(TAnalysisContext analysisContext) + { + var cfg = analysisContext.ControlFlowGraph; + if (cfg?.SupportsFlowAnalysis() != true) + { + return null; + } + + using var resultBuilder = new DataFlowAnalysisResultBuilder(); + using var uniqueSuccessors = PooledHashSet.GetInstance(); + using var finallyBlockSuccessorsMap = PooledDictionary>.GetInstance(); + var catchBlockInputDataMap = PooledDictionary.GetInstance(); + var inputDataFromInfeasibleBranchesMap = PooledDictionary.GetInstance(); + using var unreachableBlocks = PooledHashSet.GetInstance(); + using var worklist = PooledSortedSet.GetInstance(); + using var pendingBlocksNeedingAtLeastOnePass = PooledSortedSet.GetInstance(cfg.Blocks.Select(b => b.Ordinal)); + + // Map from Ordinal -> (Ordinal, ControlFlowConditionKind)? with following semantics: + // 1. Key is a valid basic block ordinal. + // 2. Value tuple indicates the following: + // a. Non-null tuple value: Indicates a unique branch entering the block, with following tuple values: + // i. Ordinal of the single unique block from which analysis data has been transferred into the Key, + // which is normally a predecessor but can be a non-predecessor block for finally/catch. + // ii. ControlFlowConditionKind indicating the nature of branch, i.e. conditional or fall through. + // This is required as CFG can have both conditional and fall through branches + // with the same source and destination blocks. + // b. Null tuple value: Block had analysis data flowing into it from multiple different branches. + // + // This map allows us to optimize the number of merge operations. We can avoid merge and directly + // overwrite analysis data into a successor if successor block has no entry or entry with non-null tuple value + // with the matching input branch. + using var blockToUniqueInputFlowMap = PooledDictionary.GetInstance(); + + // Map from basic block ordinals that are destination of back edge(s) to the minimum block ordinal that dominates it, + // i.e. for every '{key, value}' pair in the dictionary, 'key' is the destination of at least one back edge + // and 'value' is the minimum ordinal such that there is no back edge to 'key' from any basic block with ordinal > 'value'. + using var loopRangeMap = PooledDictionary.GetInstance(); + var hasAnyTryBlock = ComputeLoopRangeMap(cfg, loopRangeMap); + + TAnalysisData? normalPathsExitBlockData = null, exceptionPathsExitBlockData = null; + + try + { + + // Add each basic block to the result. + foreach (var block in cfg.Blocks) + { + resultBuilder.Add(block); + } + + var entry = cfg.GetEntry(); + + // Initialize the input of the entry block. + // For context sensitive inter-procedural analysis, use the provided initial analysis data. + // Otherwise, initialize with the default bottom value of the analysis domain. + var initialAnalysisData = analysisContext.InterproceduralAnalysisData?.InitialAnalysisData; + UpdateInput(resultBuilder, entry, GetClonedAnalysisDataOrEmptyData(initialAnalysisData)); + + // Add the block to the worklist. + worklist.Add(entry.Ordinal); + + RunCore(cfg, worklist, pendingBlocksNeedingAtLeastOnePass, initialAnalysisData, resultBuilder, + uniqueSuccessors, finallyBlockSuccessorsMap, catchBlockInputDataMap, inputDataFromInfeasibleBranchesMap, + blockToUniqueInputFlowMap, loopRangeMap, exceptionPathsAnalysisPostPass: false); + normalPathsExitBlockData = resultBuilder.ExitBlockOutputData; + + // If we are executing exception paths analysis OR have at least one try/catch/finally block + // in the method, execute an exception path analysis post pass. + // This post pass will handle all possible operations within the control flow graph that can + // throw an exception and merge analysis data after all such operation analyses into the + // catch blocks reachable from those operations. + if ((analysisContext.ExceptionPathsAnalysis || hasAnyTryBlock) && + !OperationVisitor.SkipExceptionPathsAnalysisPostPass) + { + RoslynDebug.Assert(normalPathsExitBlockData != null); + + // Clone and save exit block data + normalPathsExitBlockData = AnalysisDomain.Clone(normalPathsExitBlockData); + + OperationVisitor.ExecutingExceptionPathsAnalysisPostPass = true; + foreach (var block in cfg.Blocks) + { + blockToUniqueInputFlowMap[block.Ordinal] = null; + + // Skip entry block analysis. + if (block.Kind == BasicBlockKind.Entry) + { + continue; + } + + if (block.IsReachable) + { + worklist.Add(block.Ordinal); + } + else + { + pendingBlocksNeedingAtLeastOnePass.Add(block.Ordinal); + } + } + + RunCore(cfg, worklist, pendingBlocksNeedingAtLeastOnePass, initialAnalysisData, resultBuilder, uniqueSuccessors, + finallyBlockSuccessorsMap, catchBlockInputDataMap, inputDataFromInfeasibleBranchesMap, + blockToUniqueInputFlowMap, loopRangeMap, exceptionPathsAnalysisPostPass: true); + exceptionPathsExitBlockData = resultBuilder.ExitBlockOutputData; + OperationVisitor.ExecutingExceptionPathsAnalysisPostPass = false; + } + + var mergedDataForUnhandledThrowOperations = OperationVisitor.GetMergedDataForUnhandledThrowOperations(); + + var dataflowAnalysisResult = resultBuilder.ToResult(ToBlockResult, OperationVisitor.GetStateMap(), + OperationVisitor.GetPredicateValueKindMap(), OperationVisitor.GetReturnValueAndPredicateKind(), + OperationVisitor.InterproceduralResultsMap, OperationVisitor.StandaloneLocalFunctionAnalysisResultsMap, + OperationVisitor.LambdaAndLocalFunctionAnalysisInfo, + resultBuilder.EntryBlockOutputData!, normalPathsExitBlockData!, exceptionPathsExitBlockData, + mergedDataForUnhandledThrowOperations, OperationVisitor.AnalysisDataForUnhandledThrowOperations, + OperationVisitor.TaskWrappedValuesMap, cfg, OperationVisitor.ValueDomain.UnknownOrMayBeValue); + return ToResult(analysisContext, dataflowAnalysisResult); + } + finally + { + catchBlockInputDataMap.Values.Dispose(); + catchBlockInputDataMap.Dispose(); + inputDataFromInfeasibleBranchesMap.Values.Dispose(); + inputDataFromInfeasibleBranchesMap.Dispose(); + } + } + + private void RunCore( + ControlFlowGraph cfg, + PooledSortedSet worklist, + PooledSortedSet pendingBlocksNeedingAtLeastOnePass, + TAnalysisData? initialAnalysisData, + DataFlowAnalysisResultBuilder resultBuilder, + PooledHashSet uniqueSuccessors, + PooledDictionary> finallyBlockSuccessorsMap, + PooledDictionary catchBlockInputDataMap, + PooledDictionary inputDataFromInfeasibleBranchesMap, + PooledDictionary blockToUniqueInputFlowMap, + PooledDictionary loopRangeMap, + bool exceptionPathsAnalysisPostPass) + { + using var unreachableBlocks = PooledHashSet.GetInstance(); + // Add each basic block to the result. + foreach (var block in cfg.Blocks) + { + if (!block.IsReachable) + { + unreachableBlocks.Add(block.Ordinal); + } + } + + while (worklist.Count > 0 || pendingBlocksNeedingAtLeastOnePass.Count > 0) + { + UpdateUnreachableBlocks(); + + // Get the next block to process from the worklist. + // If worklist is empty, get any one of the pendingBlocksNeedingAtLeastOnePass, which must be unreachable from Entry block. + int blockOrdinal; + if (worklist.Count > 0) + { + blockOrdinal = worklist.Min; + worklist.Remove(blockOrdinal); + } + else + { + blockOrdinal = pendingBlocksNeedingAtLeastOnePass.Min; + } + + var block = cfg.Blocks[blockOrdinal]; + + // Ensure that we execute potential nested catch blocks before the finally region. + if (pendingBlocksNeedingAtLeastOnePass.Any()) + { + var finallyRegion = block.GetInnermostRegionStartedByBlock(ControlFlowRegionKind.Finally); + if (finallyRegion?.EnclosingRegion!.Kind == ControlFlowRegionKind.TryAndFinally) + { + // Add all catch blocks in the try region corresponding to the finally. + var tryRegion = finallyRegion.EnclosingRegion.NestedRegions[0]; + Debug.Assert(tryRegion.Kind == ControlFlowRegionKind.Try); + + var nestedCatchBlockOrdinals = pendingBlocksNeedingAtLeastOnePass.Where( + p => p >= tryRegion.FirstBlockOrdinal && + p <= tryRegion.LastBlockOrdinal && + cfg.Blocks[p].GetInnermostRegionStartedByBlock(ControlFlowRegionKind.Catch) != null); + if (nestedCatchBlockOrdinals.Any()) + { + foreach (var catchBlockOrdinal in nestedCatchBlockOrdinals) + { + worklist.Add(catchBlockOrdinal); + } + + // Also add back the finally start block to be processed after catch blocks. + worklist.Add(blockOrdinal); + continue; + } + } + } + + var needsAtLeastOnePass = pendingBlocksNeedingAtLeastOnePass.Remove(blockOrdinal); + var isUnreachableBlock = unreachableBlocks.Contains(block.Ordinal); + + // Get the input data for the block. + var input = resultBuilder[block]; + if (input == null) + { + Debug.Assert(needsAtLeastOnePass); + + if (isUnreachableBlock && + inputDataFromInfeasibleBranchesMap.TryGetValue(block.Ordinal, out var currentInfeasibleData)) + { + // Block is unreachable due to predicate analysis. + // Initialize the input from predecessors to avoid false reports in unreachable code. + Debug.Assert(!currentInfeasibleData.IsDisposed); + input = currentInfeasibleData; + inputDataFromInfeasibleBranchesMap.Remove(block.Ordinal); + } + else + { + // For catch and filter regions, we track the initial input data in the catchBlockInputDataMap. + ControlFlowRegion? enclosingTryAndCatchRegion = GetEnclosingTryAndCatchRegionIfStartsHandler(block); + if (enclosingTryAndCatchRegion != null && + catchBlockInputDataMap.TryGetValue(enclosingTryAndCatchRegion, out var catchBlockInput)) + { + Debug.Assert(enclosingTryAndCatchRegion.Kind == ControlFlowRegionKind.TryAndCatch); + Debug.Assert(block.EnclosingRegion.Kind is ControlFlowRegionKind.Catch or ControlFlowRegionKind.Filter); + Debug.Assert(block.EnclosingRegion.FirstBlockOrdinal == block.Ordinal); + Debug.Assert(!catchBlockInput.IsDisposed); + input = catchBlockInput; + + // Mark that all input into successorBlockOpt requires a merge. + blockToUniqueInputFlowMap[block.Ordinal] = null; + } + } + + input = input != null ? + AnalysisDomain.Clone(input) : + GetClonedAnalysisDataOrEmptyData(initialAnalysisData); + + UpdateInput(resultBuilder, block, input); + } + + // Check if we are starting a try region which has one or more associated catch/filter regions. + // If so, we conservatively merge the input data for try region into the input data for the associated catch/filter regions. + if (block.EnclosingRegion?.Kind == ControlFlowRegionKind.Try && + block.EnclosingRegion.EnclosingRegion?.Kind == ControlFlowRegionKind.TryAndCatch && + block.EnclosingRegion.EnclosingRegion.FirstBlockOrdinal == block.Ordinal) + { + MergeIntoCatchInputData(block.EnclosingRegion.EnclosingRegion, input, block); + } + + // Flow the new input through the block to get a new output. + var output = Flow(OperationVisitor, block, AnalysisDomain.Clone(input)); + + try + { + // Update the current block result's + // output values with the new ones. + CloneAndUpdateOutputIfEntryOrExitBlock(resultBuilder, block, output); + + // Propagate the output data to all the successor blocks of the current block. + uniqueSuccessors.Clear(); + + // Get the successors with corresponding flow branches. + // CONSIDER: Currently we need to do a bunch of branch adjustments for branches to/from finally, catch and filter regions. + // We should revisit the overall CFG API and the walker to avoid such adjustments. + var successorsWithAdjustedBranches = GetSuccessorsWithAdjustedBranches(block).ToArray(); + foreach ((BranchWithInfo successorWithBranch, BranchWithInfo? preadjustSuccessorWithBranch) in successorsWithAdjustedBranches) + { + // successorWithAdjustedBranch returns a pair of branches: + // 1. successorWithBranch - This is the adjusted branch for a branch from inside a try region to outside the try region, where we don't flow into finally region. + // The adjusted branch is targeted into the finally. + // 2. preadjustSuccessorWithBranch - This is the original branch, which is primarily used to update the input data and successors of finally and catch region regions. + // Currently, these blocks have no branch coming out from it. + + // Flow the current analysis data through the branch. + (TAnalysisData newSuccessorInput, bool isFeasibleBranch) = OperationVisitor.FlowBranch(block, successorWithBranch, AnalysisDomain.Clone(output)); + + if (preadjustSuccessorWithBranch != null) + { + UpdateFinallySuccessorsAndCatchInput(preadjustSuccessorWithBranch, newSuccessorInput, block); + } + + // Certain branches have no destination (e.g. BranchKind.Throw), so we don't need to update the input data for the branch destination block. + var successorBlock = successorWithBranch.Destination; + if (successorBlock == null) + { + newSuccessorInput.Dispose(); + continue; + } + + if (exceptionPathsAnalysisPostPass) + { + // For exception paths analysis, we need to force re-analysis of entire finally region + // whenever we start try region analysis so analysis data for unhandled exceptions at end of the finally region is correctly updated. + if (successorBlock.IsFirstBlockOfRegionKind(ControlFlowRegionKind.TryAndFinally, out var tryAndFinally)) + { + var finallyRegion = tryAndFinally.NestedRegions[1]; + Debug.Assert(finallyRegion.Kind == ControlFlowRegionKind.Finally); + + for (int i = finallyRegion.FirstBlockOrdinal; i <= finallyRegion.LastBlockOrdinal; i++) + { + worklist.Add(i); + } + } + } + + // Perf: We can stop tracking data for entities whose lifetime is limited by the leaving regions. + // Below invocation explicitly drops such data from destination input. + newSuccessorInput = OperationVisitor.OnLeavingRegions(successorWithBranch.LeavingRegionLocals, + successorWithBranch.LeavingRegionFlowCaptures, block, newSuccessorInput); + + var isBackEdge = block.Ordinal >= successorBlock.Ordinal; + if (isUnreachableBlock && !unreachableBlocks.Contains(successorBlock.Ordinal)) + { + // Skip processing successor input for branch from an unreachable block to a reachable block. + newSuccessorInput.Dispose(); + continue; + } + else if (!isFeasibleBranch) + { + // Skip processing the successor input for conditional branch that can never be taken. + if (inputDataFromInfeasibleBranchesMap.TryGetValue(successorBlock.Ordinal, out TAnalysisData currentInfeasibleData)) + { + var dataToDispose = newSuccessorInput; + newSuccessorInput = OperationVisitor.MergeAnalysisData(currentInfeasibleData, newSuccessorInput, successorBlock, isBackEdge); + Debug.Assert(!ReferenceEquals(dataToDispose, newSuccessorInput)); + dataToDispose.Dispose(); + } + + inputDataFromInfeasibleBranchesMap[successorBlock.Ordinal] = newSuccessorInput; + continue; + } + + var blockToSuccessorBranchKind = successorWithBranch.ControlFlowConditionKind; + var currentSuccessorInput = resultBuilder[successorBlock]; + + // We need to merge the incoming analysis data if both the following conditions are satisfied: + // 1. Successor already has a non-null input from prior analysis iteration. + // 2. 'blockToPreviousInputBlockMap' has an entry for successor block such that one of the following conditions are satisfied: + // a. Value is null, indicating it already had analysis data flow in from multiple branches OR + // b. Value is non-null, indicating it has unique input from prior analysis, but the prior input + // analysis data was from a different branch, i.e. either different source block or different condition kind. + var needsMerge = currentSuccessorInput != null && + blockToUniqueInputFlowMap.TryGetValue(successorBlock.Ordinal, out var uniqueInputBranchOpt) && + (uniqueInputBranchOpt == null || + uniqueInputBranchOpt.Value.Ordinal != block.Ordinal || + uniqueInputBranchOpt.Value.BranchKind != blockToSuccessorBranchKind); + + TAnalysisData mergedSuccessorInput; + if (needsMerge) + { + RoslynDebug.Assert(currentSuccessorInput != null); + + // Mark that all input into successorBlockOpt requires a merge as we have non-unique input flow branches into successor block. + blockToUniqueInputFlowMap[successorBlock.Ordinal] = null; + + // Check if the current input data for the successor block is equal to the new input data from this branch. + // If so, we don't need to propagate new input data from this branch. + if (AnalysisDomain.Equals(currentSuccessorInput, newSuccessorInput)) + { + newSuccessorInput.Dispose(); + continue; + } + + // Otherwise, check if the input data for the successor block changes after merging with the new input data. + mergedSuccessorInput = OperationVisitor.MergeAnalysisData(currentSuccessorInput, newSuccessorInput, successorBlock, isBackEdge); + newSuccessorInput.Dispose(); + + int compare = AnalysisDomain.Compare(currentSuccessorInput, mergedSuccessorInput); + + // The newly computed abstract values for each basic block + // must be always greater or equal than the previous value + // to ensure termination. + Debug.Assert(compare <= 0, "The newly computed abstract value must be greater or equal than the previous one."); + + // Is old input value >= new input value + if (compare >= 0) + { + mergedSuccessorInput.Dispose(); + continue; + } + } + else + { + Debug.Assert(currentSuccessorInput == null || AnalysisDomain.Compare(currentSuccessorInput, newSuccessorInput) <= 0); + mergedSuccessorInput = newSuccessorInput; + + // Mark that all input into successorBlockOpt can skip merge as long as it from the current input flow branch. + blockToUniqueInputFlowMap[successorBlock.Ordinal] = (block.Ordinal, blockToSuccessorBranchKind); + } + + // Input to successor has changed, so we need to update its new input and + // reprocess the successor by adding it to the worklist. + UpdateInput(resultBuilder, successorBlock, mergedSuccessorInput); + + if (isBackEdge) + { + // For back edges, analysis data in subsequent iterations needs + // to be merged with analysis data from previous iterations. + var dominatorBlockOrdinal = loopRangeMap[successorBlock.Ordinal]; + Debug.Assert(dominatorBlockOrdinal >= block.Ordinal); + Debug.Assert(dominatorBlockOrdinal >= successorBlock.Ordinal); + + for (int i = successorBlock.Ordinal; i <= dominatorBlockOrdinal + 1; i++) + { + blockToUniqueInputFlowMap[i] = null; + } + } + + if (uniqueSuccessors.Add(successorBlock)) + { + worklist.Add(successorBlock.Ordinal); + } + } + + Debug.Assert(IsValidWorklistState()); + } + finally + { + output.Dispose(); + } + } + + // Local functions. + void UpdateUnreachableBlocks() + { + if (worklist.Count == 0) + { + foreach (var blockOrdinal in pendingBlocksNeedingAtLeastOnePass) + { + unreachableBlocks.Add(blockOrdinal); + } + } + } + + static ControlFlowRegion? TryGetReachableCatchRegionStartingHandler(ControlFlowRegion tryAndCatchRegion, BasicBlock sourceBlock) + { + Debug.Assert(tryAndCatchRegion.Kind == ControlFlowRegionKind.TryAndCatch); + + // Get the catch region to merge input data. + // Ensure that the source block is not itself within the catch region, + // in which case a throw cannot enter the catch region. + var catchRegion = tryAndCatchRegion.NestedRegions.FirstOrDefault(region => region.Kind is ControlFlowRegionKind.Catch or ControlFlowRegionKind.FilterAndHandler); + if (catchRegion == null || sourceBlock.Ordinal >= catchRegion.FirstBlockOrdinal) + { + return null; + } + + return catchRegion; + } + + ControlFlowRegion? MergeIntoCatchInputData(ControlFlowRegion tryAndCatchRegion, TAnalysisData dataToMerge, BasicBlock sourceBlock) + { + var catchRegion = TryGetReachableCatchRegionStartingHandler(tryAndCatchRegion, sourceBlock); + if (catchRegion == null) + { + return null; + } + + var catchBlock = cfg.Blocks[catchRegion.FirstBlockOrdinal]; + + // Check if we have already visited the catch block once, and hence have a non-null input. + // If so, just update the resultBuilder input. + // Otherwise, update the catchBlockInputDataMap. + + var catchBlockInputData = resultBuilder[catchBlock]; + if (catchBlockInputData != null) + { + // Check if the current input data for the catch block is equal to the new input data from this branch. + // If so, we don't need to propagate new input data from this branch. + if (AnalysisDomain.Equals(catchBlockInputData, dataToMerge)) + { + return null; + } + + // Otherwise, check if the input data for the catch block changes after merging with the new input data. + var mergedData = AnalysisDomain.Merge(catchBlockInputData, dataToMerge); + int compare = AnalysisDomain.Compare(catchBlockInputData, mergedData); + + // The newly computed abstract values for each basic block + // must be always greater or equal than the previous value + // to ensure termination. + Debug.Assert(compare <= 0, "The newly computed abstract value must be greater or equal than the previous one."); + + if (compare == 0) + { + return null; + } + + UpdateInput(resultBuilder, catchBlock, mergedData); + } + else + { + if (!catchBlockInputDataMap.TryGetValue(tryAndCatchRegion, out catchBlockInputData)) + { + catchBlockInputData = AnalysisDomain.Clone(dataToMerge); + } + else + { + catchBlockInputData = AnalysisDomain.Merge(catchBlockInputData, dataToMerge); + } + + catchBlockInputDataMap[tryAndCatchRegion] = catchBlockInputData; + } + + return catchRegion; + } + + // Ensures that we have a valid worklist/pendingBlocksNeedingAtLeastOnePass state. + bool IsValidWorklistState() + { + if (worklist.Count == 0 && pendingBlocksNeedingAtLeastOnePass.Count == 0) + { + return true; + } + + foreach (var blockOrdinal in worklist.Concat(pendingBlocksNeedingAtLeastOnePass)) + { + var block = cfg.Blocks[blockOrdinal]; + if (block.Predecessors.IsEmpty || !HasUnprocessedPredecessorBlock(block)) + { + return true; + } + } + + return false; + } + + bool HasUnprocessedPredecessorBlock(BasicBlock block) + { + var predecessorsWithBranches = block.GetPredecessorsWithBranches(cfg); + return predecessorsWithBranches.Any(predecessorWithBranch => + predecessorWithBranch.predecessorBlock.Ordinal < block.Ordinal && + pendingBlocksNeedingAtLeastOnePass.Contains(predecessorWithBranch.predecessorBlock.Ordinal)); + } + + // If this block starts a catch/filter region, return the enclosing TryAndCatch region. + static ControlFlowRegion? GetEnclosingTryAndCatchRegionIfStartsHandler(BasicBlock block) + { + if (block.EnclosingRegion?.FirstBlockOrdinal == block.Ordinal) + { + switch (block.EnclosingRegion.Kind) + { + case ControlFlowRegionKind.Catch: + if (block.EnclosingRegion!.EnclosingRegion!.Kind == ControlFlowRegionKind.TryAndCatch) + { + return block.EnclosingRegion.EnclosingRegion; + } + + break; + + case ControlFlowRegionKind.Filter: + if (block.EnclosingRegion!.EnclosingRegion!.Kind == ControlFlowRegionKind.FilterAndHandler && + block.EnclosingRegion.EnclosingRegion.EnclosingRegion?.Kind == ControlFlowRegionKind.TryAndCatch) + { + return block.EnclosingRegion.EnclosingRegion.EnclosingRegion; + } + + break; + } + } + + return null; + } + + IEnumerable<(BranchWithInfo successorWithBranch, BranchWithInfo? preadjustSuccessorWithBranch)> GetSuccessorsWithAdjustedBranches(BasicBlock basicBlock) + { + if (basicBlock.Kind != BasicBlockKind.Exit) + { + // If this is the last block of finally region, use the finallyBlockSuccessorsMap to get its successors. + if (finallyBlockSuccessorsMap.TryGetValue(basicBlock.Ordinal, out var finallySuccessors)) + { + Debug.Assert(basicBlock.EnclosingRegion.Kind == ControlFlowRegionKind.Finally); + foreach (var successor in finallySuccessors) + { + yield return (successor, null); + } + } + else + { + var preadjustSuccessorWithbranch = new BranchWithInfo(basicBlock.FallThroughSuccessor!); + var adjustedSuccessorWithBranch = AdjustBranchIfFinalizing(preadjustSuccessorWithbranch); + yield return (successorWithBranch: adjustedSuccessorWithBranch, preadjustSuccessorWithBranch: preadjustSuccessorWithbranch); + + if (basicBlock.ConditionalSuccessor?.Destination != null) + { + preadjustSuccessorWithbranch = new BranchWithInfo(basicBlock.ConditionalSuccessor); + adjustedSuccessorWithBranch = AdjustBranchIfFinalizing(preadjustSuccessorWithbranch); + yield return (successorWithBranch: adjustedSuccessorWithBranch, preadjustSuccessorWithBranch: preadjustSuccessorWithbranch); + } + } + } + } + + // Adjust the branch if we are going to be executing one or more finally regions, but the CFG's branch doesn't account for these. + BranchWithInfo AdjustBranchIfFinalizing(BranchWithInfo branch) + { + if (!branch.FinallyRegions.IsEmpty) + { + var firstFinally = branch.FinallyRegions[0]; + var destination = cfg.Blocks[firstFinally.FirstBlockOrdinal]; + return branch.WithEmptyRegions(destination); + } + else + { + return branch; + } + } + + // Updates the successors of finally blocks. + // Also updates the merged input data tracked for catch blocks. + void UpdateFinallySuccessorsAndCatchInput(BranchWithInfo branch, TAnalysisData branchData, BasicBlock sourceBlock) + { + // Compute and update finally successors. + if (!branch.FinallyRegions.IsEmpty) + { + var successor = branch.With(branchValue: null, controlFlowConditionKind: ControlFlowConditionKind.None); + for (var i = branch.FinallyRegions.Length - 1; i >= 0; i--) + { + ControlFlowRegion finallyRegion = branch.FinallyRegions[i]; + AddFinallySuccessor(finallyRegion, successor); + successor = new BranchWithInfo(destination: cfg.Blocks[finallyRegion.FirstBlockOrdinal]); + } + } + + // Update catch input data. + if (!branch.LeavingRegions.IsEmpty) + { + foreach (var region in branch.LeavingRegions) + { + // If we have any nested finally region inside this try-catch, then mark the catch region + // as a successor of that nested finally region. + // Otherwise, merge the current data directly into the catch region. + + if (region.Kind == ControlFlowRegionKind.TryAndCatch) + { + var hasNestedFinally = false; + if (!branch.FinallyRegions.IsEmpty) + { + var catchRegion = TryGetReachableCatchRegionStartingHandler(region, sourceBlock); + if (catchRegion != null) + { + for (var i = branch.FinallyRegions.Length - 1; i >= 0; i--) + { + ControlFlowRegion finallyRegion = branch.FinallyRegions[i]; + if (finallyRegion.LastBlockOrdinal < catchRegion.FirstBlockOrdinal) + { + var successor = new BranchWithInfo(destination: cfg.Blocks[catchRegion.FirstBlockOrdinal]); + AddFinallySuccessor(finallyRegion, successor); + hasNestedFinally = true; + break; + } + } + } + } + + if (!hasNestedFinally) + { + // No nested finally regions inside this try-catch. + // Merge the current data directly into the catch region. + var catchRegion = MergeIntoCatchInputData(region, branchData, sourceBlock); + if (catchRegion != null) + { + // We also need to enqueue the catch block into the worklist as there is no direct branch into catch. + worklist.Add(catchRegion.FirstBlockOrdinal); + } + } + } + } + } + } + + void AddFinallySuccessor(ControlFlowRegion finallyRegion, BranchWithInfo successor) + { + Debug.Assert(finallyRegion.Kind == ControlFlowRegionKind.Finally); + if (!finallyBlockSuccessorsMap.TryGetValue(finallyRegion.LastBlockOrdinal, out var lastBlockSuccessors)) + { + lastBlockSuccessors = new List(); + finallyBlockSuccessorsMap.Add(finallyRegion.LastBlockOrdinal, lastBlockSuccessors); + } + + lastBlockSuccessors.Add(successor); + } + } + + private TAnalysisData GetClonedAnalysisDataOrEmptyData(TAnalysisData? initialAnalysisData) + { + if (initialAnalysisData != null) + { + return AnalysisDomain.Clone(initialAnalysisData); + } + + return OperationVisitor.GetEmptyAnalysisData(); + } + +#pragma warning disable CA1000 // Do not declare static members on generic types + public static TAnalysisData Flow( + DataFlowOperationVisitor operationVisitor, + BasicBlock block, + TAnalysisData data) + { + data = operationVisitor.OnStartBlockAnalysis(block, data); + + foreach (var statement in block.Operations) + { + data = operationVisitor.Flow(statement, block, data); + } + + data = operationVisitor.OnEndBlockAnalysis(block, data); + + return data; + } + + public static TAnalysisData FlowBranch( + DataFlowOperationVisitor operationVisitor, + ControlFlowBranch branch, + TAnalysisData data) + { + (data, _) = operationVisitor.FlowBranch(branch.Source, new BranchWithInfo(branch), data); + return data; + } +#pragma warning restore CA1000 // Do not declare static members on generic types + + protected abstract TAnalysisResult ToResult(TAnalysisContext analysisContext, DataFlowAnalysisResult dataFlowAnalysisResult); + protected abstract TBlockAnalysisResult ToBlockResult(BasicBlock basicBlock, TAnalysisData blockAnalysisData); + + private void UpdateInput(DataFlowAnalysisResultBuilder builder, BasicBlock block, TAnalysisData newInput) + { + Debug.Assert(builder[block] == null || AnalysisDomain.Compare(builder[block]!, newInput) <= 0, "Non-monotonic update"); + builder.Update(block, newInput); + } + + private void CloneAndUpdateOutputIfEntryOrExitBlock(DataFlowAnalysisResultBuilder builder, BasicBlock block, TAnalysisData newOutput) + { + switch (block.Kind) + { + case BasicBlockKind.Entry: + builder.EntryBlockOutputData = AnalysisDomain.Clone(newOutput); + break; + + case BasicBlockKind.Exit: + builder.ExitBlockOutputData = AnalysisDomain.Clone(newOutput); + break; + } + } + + private static bool ComputeLoopRangeMap(ControlFlowGraph cfg, PooledDictionary loopRangeMap) + { + var hasAnyTryBlock = false; + for (int i = cfg.Blocks.Length - 1; i > 0; i--) + { + var block = cfg.Blocks[i]; + HandleBranch(block.FallThroughSuccessor); + HandleBranch(block.ConditionalSuccessor); + + hasAnyTryBlock |= block.EnclosingRegion.Kind == ControlFlowRegionKind.Try; + } + + return hasAnyTryBlock; + + void HandleBranch(ControlFlowBranch? branch) + { + if (branch?.Destination != null && branch.IsBackEdge() && !loopRangeMap.ContainsKey(branch.Destination.Ordinal)) + { + var maxSuccessorOrdinal = Math.Max(branch.Destination.GetMaxSuccessorOrdinal(), branch.Source.Ordinal); + + if (!branch.FinallyRegions.IsEmpty) + { + maxSuccessorOrdinal = Math.Max(maxSuccessorOrdinal, branch.FinallyRegions[^1].LastBlockOrdinal); + } + + loopRangeMap.Add(branch.Destination.Ordinal, maxSuccessorOrdinal); + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs new file mode 100644 index 0000000000000..851e81493225f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResult.cs @@ -0,0 +1,225 @@ +// 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 System.Diagnostics; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Result from execution of a on a control flow graph. + /// It stores: + /// (1) Analysis values for all operations in the graph and + /// (2) for every basic block in the graph. + /// (3) Merged analysis state for all the unhandled throw operations in the graph. + /// + public class DataFlowAnalysisResult : IDataFlowAnalysisResult + where TBlockAnalysisResult : AbstractBlockAnalysisResult + { + private readonly ImmutableDictionary _basicBlockStateMap; + private readonly ImmutableDictionary _operationStateMap; + private readonly ImmutableDictionary _predicateValueKindMap; + private readonly ImmutableDictionary> _interproceduralResultsMap; + private readonly ImmutableDictionary> _standaloneLocalFunctionAnalysisResultsMap; + private readonly TAbstractAnalysisValue _defaultUnknownValue; + private readonly object? _analysisDataForUnhandledThrowOperations; + + internal DataFlowAnalysisResult( + ImmutableDictionary basicBlockStateMap, + ImmutableDictionary operationStateMap, + ImmutableDictionary predicateValueKindMap, + (TAbstractAnalysisValue, PredicateValueKind)? returnValueAndPredicateKind, + ImmutableDictionary> interproceduralResultsMap, + ImmutableDictionary> standaloneLocalFunctionAnalysisResultsMap, + LambdaAndLocalFunctionAnalysisInfo lambdaAndLocalFunctionAnalysisInfo, + TBlockAnalysisResult entryBlockOutput, + TBlockAnalysisResult exitBlockOutput, + TBlockAnalysisResult? exceptionPathsExitBlockOutput, + TBlockAnalysisResult? mergedStateForUnhandledThrowOperations, + object? analysisDataForUnhandledThrowOperations, + Dictionary? taskWrappedValuesMap, + ControlFlowGraph cfg, + TAbstractAnalysisValue defaultUnknownValue) + { + _basicBlockStateMap = basicBlockStateMap; + _operationStateMap = operationStateMap; + _predicateValueKindMap = predicateValueKindMap; + ReturnValueAndPredicateKind = returnValueAndPredicateKind; + _interproceduralResultsMap = interproceduralResultsMap; + _standaloneLocalFunctionAnalysisResultsMap = standaloneLocalFunctionAnalysisResultsMap; + LambdaAndLocalFunctionAnalysisInfo = lambdaAndLocalFunctionAnalysisInfo; + EntryBlockOutput = entryBlockOutput; + ExitBlockOutput = exitBlockOutput; + ExceptionPathsExitBlockOutput = exceptionPathsExitBlockOutput; + MergedStateForUnhandledThrowOperations = mergedStateForUnhandledThrowOperations; + _analysisDataForUnhandledThrowOperations = analysisDataForUnhandledThrowOperations; + TaskWrappedValuesMap = taskWrappedValuesMap; + ControlFlowGraph = cfg; + _defaultUnknownValue = defaultUnknownValue; + } + + protected DataFlowAnalysisResult(DataFlowAnalysisResult other) + : this(other._basicBlockStateMap, other._operationStateMap, other._predicateValueKindMap, other.ReturnValueAndPredicateKind, + other._interproceduralResultsMap, other._standaloneLocalFunctionAnalysisResultsMap, other.LambdaAndLocalFunctionAnalysisInfo, + other.EntryBlockOutput, other.ExitBlockOutput, other.ExceptionPathsExitBlockOutput, + other.MergedStateForUnhandledThrowOperations, other._analysisDataForUnhandledThrowOperations, other.TaskWrappedValuesMap, + other.ControlFlowGraph, other._defaultUnknownValue) + { + } + + internal DataFlowAnalysisResult With( + TBlockAnalysisResult mergedStateForUnhandledThrowOperationsOpt, + object analysisDataForUnhandledThrowOperations) + { + return new DataFlowAnalysisResult( + _basicBlockStateMap, _operationStateMap, _predicateValueKindMap, ReturnValueAndPredicateKind, + _interproceduralResultsMap, _standaloneLocalFunctionAnalysisResultsMap, LambdaAndLocalFunctionAnalysisInfo, + EntryBlockOutput, ExitBlockOutput, ExceptionPathsExitBlockOutput, mergedStateForUnhandledThrowOperationsOpt, + analysisDataForUnhandledThrowOperations, TaskWrappedValuesMap, ControlFlowGraph, _defaultUnknownValue); + } + + internal DataFlowAnalysisResult With(ImmutableDictionary operationStateMap) + { + return new DataFlowAnalysisResult( + _basicBlockStateMap, operationStateMap, _predicateValueKindMap, ReturnValueAndPredicateKind, + _interproceduralResultsMap, _standaloneLocalFunctionAnalysisResultsMap, LambdaAndLocalFunctionAnalysisInfo, + EntryBlockOutput, ExitBlockOutput, ExceptionPathsExitBlockOutput, MergedStateForUnhandledThrowOperations, + _analysisDataForUnhandledThrowOperations, TaskWrappedValuesMap, ControlFlowGraph, _defaultUnknownValue); + } + +#pragma warning disable CA1043 // Use Integral Or String Argument For Indexers + public TBlockAnalysisResult this[BasicBlock block] => _basicBlockStateMap[block]; + public TAbstractAnalysisValue this[IOperation operation] +#pragma warning restore CA1043 // Use Integral Or String Argument For Indexers + { + get + { + // This accessor is only meant for use by the DFA analysis for operations within the CFG, + // which have a completely different operation tree. + // Make sure the analyzers don't invoke this accessor with operations from the original operation tree + // They should instead by invoking the accessor 'this[OperationKind operationKind, SyntaxNode syntax]' + // with operation's kind and syntax as arguments. + Debug.Assert(operation.GetRoot() != ControlFlowGraph.OriginalOperation, + "Did you mean to invoke the accessor that takes operation's kind and syntax as arguments?"); + + if (_operationStateMap.TryGetValue(operation, out var value)) + { + return value; + } + + // We were requested for value of an operation in non-method body context (e.g. initializer), which is currently not supported. + // See https://github.com/dotnet/roslyn-analyzers/issues/1650 (Support for dataflow analysis for non-method body executable code) + Debug.Assert(operation.GetAncestor(OperationKind.Block, predicate: b => b.Parent == null) == null); + return _defaultUnknownValue; + } + } + + public TAbstractAnalysisValue this[OperationKind operationKind, SyntaxNode syntax] + { + get + { + var value = _defaultUnknownValue; + foreach (var kvp in _operationStateMap) + { + if (kvp.Key.Kind == operationKind && kvp.Key.Syntax == syntax) + { + if (!kvp.Key.IsImplicit) + { + return kvp.Value; + } + else + { + value = kvp.Value; + } + } + } + + return value; + } + } + + internal DataFlowAnalysisResult? TryGetInterproceduralResult(IOperation operation) + { + if (_interproceduralResultsMap.TryGetValue(operation, out var result)) + { + return (DataFlowAnalysisResult)result; + } + + return null; + } + + internal DataFlowAnalysisResult? TryGetStandaloneLocalFunctionAnalysisResult(IMethodSymbol localFunction) + { + Debug.Assert(localFunction.MethodKind == MethodKind.LocalFunction); + if (_standaloneLocalFunctionAnalysisResultsMap.TryGetValue(localFunction, out var result)) + { + return (DataFlowAnalysisResult)result; + } + + return null; + } + + internal IEnumerable> TryGetLambdaOrLocalFunctionResults(IOperation lambdaOrLocalFunctionOperation) + { + Debug.Assert(lambdaOrLocalFunctionOperation.Kind is OperationKind.AnonymousFunction or OperationKind.LocalFunction); + + var isNestedLambdaOrLocalFunction = lambdaOrLocalFunctionOperation.IsWithinLambdaOrLocalFunction(out _); + + foreach (DataFlowAnalysisResult result in _interproceduralResultsMap.Values.Cast>()) + { + if (result.ControlFlowGraph.OriginalOperation == lambdaOrLocalFunctionOperation) + { + yield return result; + } + else if (isNestedLambdaOrLocalFunction) + { + foreach (var nestedResult in result.TryGetLambdaOrLocalFunctionResults(lambdaOrLocalFunctionOperation)) + { + yield return nestedResult; + } + } + } + + if (lambdaOrLocalFunctionOperation.Kind == OperationKind.LocalFunction) + { + foreach (DataFlowAnalysisResult result in _standaloneLocalFunctionAnalysisResultsMap.Values.Cast>()) + { + if (result.ControlFlowGraph.OriginalOperation == lambdaOrLocalFunctionOperation) + { + yield return result; + } + else if (isNestedLambdaOrLocalFunction) + { + foreach (var nestedResult in result.TryGetLambdaOrLocalFunctionResults(lambdaOrLocalFunctionOperation)) + { + yield return nestedResult; + } + } + } + } + } + + public ControlFlowGraph ControlFlowGraph { get; } + public (TAbstractAnalysisValue Value, PredicateValueKind PredicateValueKind)? ReturnValueAndPredicateKind { get; } + public TBlockAnalysisResult EntryBlockOutput { get; } + public TBlockAnalysisResult ExitBlockOutput { get; } + public TBlockAnalysisResult? ExceptionPathsExitBlockOutput { get; } + public LambdaAndLocalFunctionAnalysisInfo LambdaAndLocalFunctionAnalysisInfo { get; } + + object? IDataFlowAnalysisResult.AnalysisDataForUnhandledThrowOperations + => _analysisDataForUnhandledThrowOperations; + + object? IDataFlowAnalysisResult.TaskWrappedValuesMap + => TaskWrappedValuesMap; + + public TBlockAnalysisResult? MergedStateForUnhandledThrowOperations { get; } + public PredicateValueKind GetPredicateKind(IOperation operation) => _predicateValueKindMap.TryGetValue(operation, out var valueKind) ? valueKind : PredicateValueKind.Unknown; + internal Dictionary? TaskWrappedValuesMap { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResultBuilder.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResultBuilder.cs new file mode 100644 index 0000000000000..2f508315119f9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowAnalysisResultBuilder.cs @@ -0,0 +1,95 @@ +// 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 Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Used by to store intermediate dataflow results while executing data flow analysis + /// and also to compute the final exposed as the result. + /// + internal sealed class DataFlowAnalysisResultBuilder : IDisposable + where TAnalysisData : AbstractAnalysisData + { +#pragma warning disable CA2213 // Disposable fields should be disposed + private readonly PooledDictionary _info; +#pragma warning restore + + public DataFlowAnalysisResultBuilder() + { + _info = PooledDictionary.GetInstance(); + } + + public TAnalysisData? this[BasicBlock block] => _info[block]; + public TAnalysisData? EntryBlockOutputData { get; set; } + public TAnalysisData? ExitBlockOutputData { get; set; } + + internal void Add(BasicBlock block) + { + _info.Add(block, null); + } + + internal void Update(BasicBlock block, TAnalysisData newData) + { + _info[block] = newData; + } + + public DataFlowAnalysisResult ToResult( + Func getBlockResult, + ImmutableDictionary stateMap, + ImmutableDictionary predicateValueKindMap, + (TAbstractAnalysisValue, PredicateValueKind)? returnValueAndPredicateKind, + ImmutableDictionary> interproceduralResultsMap, + ImmutableDictionary> standaloneLocalFunctionAnalysisResultsMap, + LambdaAndLocalFunctionAnalysisInfo lambdaAndLocalFunctionAnalysisInfo, + TAnalysisData entryBlockOutputData, + TAnalysisData exitBlockData, + TAnalysisData? exceptionPathsExitBlockData, + TAnalysisData? mergedDataForUnhandledThrowOperations, + Dictionary? analysisDataForUnhandledThrowOperations, + Dictionary? taskWrappedValuesMap, + ControlFlowGraph cfg, + TAbstractAnalysisValue defaultUnknownValue) + where TBlockAnalysisResult : AbstractBlockAnalysisResult + { + var resultBuilder = PooledDictionary.GetInstance(); + foreach (var kvp in _info) + { + var block = kvp.Key; + var blockAnalysisData = kvp.Value; + var result = getBlockResult(block, blockAnalysisData!); + resultBuilder.Add(block, result); + } + + var mergedStateForUnhandledThrowOperations = mergedDataForUnhandledThrowOperations != null ? + getBlockResult(cfg.GetExit(), mergedDataForUnhandledThrowOperations) : + null; + + var entryBlockOutputResult = getBlockResult(cfg.GetEntry(), entryBlockOutputData); + var exitBlockOutputResult = getBlockResult(cfg.GetExit(), exitBlockData); + var exceptionPathsExitBlockOutputResult = exceptionPathsExitBlockData != null ? + getBlockResult(cfg.GetExit(), exceptionPathsExitBlockData) : + null; + + return new DataFlowAnalysisResult(resultBuilder.ToImmutableDictionaryAndFree(), stateMap, + predicateValueKindMap, returnValueAndPredicateKind, interproceduralResultsMap, + standaloneLocalFunctionAnalysisResultsMap, lambdaAndLocalFunctionAnalysisInfo, + entryBlockOutputResult, exitBlockOutputResult, exceptionPathsExitBlockOutputResult, + mergedStateForUnhandledThrowOperations, analysisDataForUnhandledThrowOperations, + taskWrappedValuesMap, cfg, defaultUnknownValue); + } + + public void Dispose() + { + _info.Values.Dispose(); + _info.Dispose(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..d9a1a92bfeab8 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DataFlowOperationVisitor.cs @@ -0,0 +1,4226 @@ +// 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 System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis; +using Microsoft.CodeAnalysis.Operations; + +#pragma warning disable CA1707 // Identifiers should not contain underscores + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Operation visitor to flow the abstract dataflow analysis values across a given statement in a basic block. + /// + public abstract class DataFlowOperationVisitor : OperationVisitor + where TAnalysisData : AbstractAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + { +#pragma warning disable RS0030 // The symbol 'DiagnosticDescriptor.DiagnosticDescriptor.#ctor' is banned in this project: Use 'DiagnosticDescriptorHelper.Create' instead +#pragma warning disable RS2000 // Add analyzer diagnostic IDs to analyzer release + private static readonly DiagnosticDescriptor s_dummyDataflowAnalysisDescriptor = new( + id: "InterproceduralDataflow", + title: string.Empty, + messageFormat: string.Empty, + category: string.Empty, + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true, + customTags: WellKnownDiagnosticTagsExtensions.DataflowAndTelemetry); +#pragma warning restore RS2000 // Add analyzer diagnostic IDs to analyzer release +#pragma warning restore RS0030 + + private readonly ImmutableHashSet _lValueFlowCaptures; + private readonly ImmutableDictionary.Builder _valueCacheBuilder; + private readonly ImmutableDictionary.Builder _predicateValueKindCacheBuilder; + private readonly HashSet _pendingArgumentsToReset; + private readonly List _pendingArgumentsToPostProcess; + private readonly HashSet _visitedFlowBranchConditions; + private readonly HashSet _visitedLambdas; + private readonly HashSet? _returnValueOperations; + private ImmutableDictionary? _lazyParameterEntities; + private ImmutableHashSet? _lazyContractCheckMethods; + private TAnalysisData? _currentAnalysisData; + private BasicBlock? _currentBasicBlock; + private int _recursionDepth; + + #region Fields specific to lambda/local function analysis + + /// + /// Local functions that escaped from this method. + /// + private readonly ImmutableHashSet.Builder _escapedLocalFunctions; + + /// + /// Local functions for which interprocedural analysis was performed at least once in this method. + /// + private readonly ImmutableHashSet.Builder _analyzedLocalFunctions; + + /// + /// Lambda methods that escaped from this method. + /// + private readonly ImmutableHashSet.Builder _escapedLambdas; + + /// + /// Lambda methods for which interprocedural analysis was performed at least once in this method. + /// + private readonly ImmutableHashSet.Builder _analyzedLambdas; + + #endregion + + #region Fields specific to Interprocedural analysis + + private InterproceduralAnalysisKind InterproceduralAnalysisKind + => DataFlowAnalysisContext.InterproceduralAnalysisConfiguration.InterproceduralAnalysisKind; + + /// + /// Defines the max length for method call chain (call stack size) for interprocedural analysis. + /// This is done for performance reasons for analyzing methods with extremely large call trees. + /// + private uint MaxInterproceduralMethodCallChain + => DataFlowAnalysisContext.InterproceduralAnalysisConfiguration.MaxInterproceduralMethodCallChain; + + /// + /// Defines the max length for lambda/local function method call chain (call stack size) for interprocedural analysis. + /// This is done for performance reasons for analyzing methods with extremely large call trees. + /// + private uint MaxInterproceduralLambdaOrLocalFunctionCallChain + => DataFlowAnalysisContext.InterproceduralAnalysisConfiguration.MaxInterproceduralLambdaOrLocalFunctionCallChain; + + /// + /// Stores a map from entity to set of entities that share the same instance location. + /// Primarily used for ref arguments for context sensitive interprocedural analysis + /// to ensure that PointsTo value updates to any of the mapped entities is reflected in the others in the set. + /// + private readonly AddressSharedEntitiesProvider _addressSharedEntitiesProvider; + + /// + /// Current interprocedural operation call stack. + /// + private readonly Stack _interproceduralCallStack; + + /// + /// Dictionary storing context sensitive interprocedural analysis results for each callsite. + /// + private readonly ImmutableDictionary>.Builder _interproceduralResultsBuilder; + + /// + /// Dictionary storing context insensitive interprocedural analysis results for escaped local function. + /// + private readonly ImmutableDictionary>.Builder _standaloneLocalFunctionAnalysisResultsBuilder; + + /// + /// Dictionary from interprocedural method symbols invoked to their corresponding . + /// + private readonly Dictionary? _interproceduralMethodToCfgMap; + #endregion + + protected abstract TAbstractAnalysisValue GetAbstractDefaultValue(ITypeSymbol? type); + protected virtual TAbstractAnalysisValue GetAbstractDefaultValueForCatchVariable(ICatchClauseOperation catchClause) => ValueDomain.UnknownOrMayBeValue; + protected abstract bool HasAnyAbstractValue(TAnalysisData data); + protected abstract void SetValueForParameterOnEntry(IParameterSymbol parameter, AnalysisEntity analysisEntity, ArgumentInfo? assignedValue); + protected abstract void EscapeValueForParameterOnExit(IParameterSymbol parameter, AnalysisEntity analysisEntity); + protected abstract void ResetCurrentAnalysisData(); + + /// + /// Indicates if we have any points to analysis data, with or without tracking for fields and properties, i.e. either + /// or + /// + protected bool HasPointsToAnalysisResult { get; } + + /// + /// Indicates if we have complete points to analysis data with . + /// + protected bool HasCompletePointsToAnalysisResult { get; } + + internal virtual bool IsPointsToAnalysis => false; + + internal Dictionary? AnalysisDataForUnhandledThrowOperations { get; private set; } + public ImmutableDictionary> InterproceduralResultsMap => _interproceduralResultsBuilder.ToImmutable(); + public ImmutableDictionary> StandaloneLocalFunctionAnalysisResultsMap => _standaloneLocalFunctionAnalysisResultsBuilder.ToImmutable(); + internal LambdaAndLocalFunctionAnalysisInfo LambdaAndLocalFunctionAnalysisInfo => + new(_escapedLocalFunctions, _analyzedLocalFunctions, _escapedLambdas, _analyzedLambdas); + + /// + /// Optional map from points to values of tasks to the underlying abstract value returned by the task. + /// Awaiting the task produces the task wrapped value from this map. + /// + internal Dictionary? TaskWrappedValuesMap { get; private set; } + + protected TAnalysisContext DataFlowAnalysisContext { get; } + public AbstractValueDomain ValueDomain => DataFlowAnalysisContext.ValueDomain; + protected ISymbol OwningSymbol => DataFlowAnalysisContext.OwningSymbol; + protected WellKnownTypeProvider WellKnownTypeProvider => DataFlowAnalysisContext.WellKnownTypeProvider; + protected Func TryGetOrComputeAnalysisResult + => DataFlowAnalysisContext.TryGetOrComputeAnalysisResult; + internal bool ExecutingExceptionPathsAnalysisPostPass { get; set; } + internal virtual bool SkipExceptionPathsAnalysisPostPass => false; + + protected TAnalysisData CurrentAnalysisData + { + get + { + RoslynDebug.Assert(_currentAnalysisData != null); + Debug.Assert(!_currentAnalysisData.IsDisposed); + return _currentAnalysisData; + } + private set + { + Debug.Assert(!value.IsDisposed); + _currentAnalysisData = value; + } + } + + protected BasicBlock CurrentBasicBlock + { + get + { + Debug.Assert(_currentBasicBlock != null); + return _currentBasicBlock!; + } + private set => _currentBasicBlock = value; + } + protected ControlFlowConditionKind FlowBranchConditionKind { get; private set; } + protected PointsToAbstractValue ThisOrMePointsToAbstractValue { get; } + protected AnalysisEntityFactory AnalysisEntityFactory { get; } + + /// + /// This boolean field determines if the caller requires an optimistic OR a pessimistic analysis for such cases. + /// For example, invoking an instance method may likely invalidate all the instance field analysis state, i.e. + /// reference type fields might be re-assigned to point to different objects in the called method. + /// An optimistic points to analysis assumes that the points to values of instance fields don't change on invoking an instance method. + /// A pessimistic points to analysis resets all the instance state and assumes the instance field might point to any object, hence has unknown state. + /// + /// + /// For dispose analysis, we want to perform an optimistic points to analysis as we assume a disposable field is not likely to be re-assigned to a separate object in helper method invocations in Dispose. + /// For value content analysis, we want to perform a pessimistic points to analysis to be conservative and avoid missing out true violations. + /// + protected bool PessimisticAnalysis => DataFlowAnalysisContext.PessimisticAnalysis; + + /// + /// Indicates if we this visitor needs to analyze predicates of conditions. + /// + protected bool PredicateAnalysis => DataFlowAnalysisContext.PredicateAnalysis; + + /// + /// PERF: Track if we are within an . + /// + protected bool IsInsideAnonymousObjectInitializer { get; private set; } + + protected bool IsLValueFlowCapture(IFlowCaptureOperation flowCapture) + => _lValueFlowCaptures.Contains(flowCapture.Id); + + protected bool IsLValueFlowCaptureReference(IFlowCaptureReferenceOperation flowCaptureReference) + => flowCaptureReference.IsLValueFlowCaptureReference(); + + private Dictionary? _exceptionPathsThrownExceptionInfoMap; + private ThrownExceptionInfo DefaultThrownExceptionInfo + { + get + { + Debug.Assert(ExceptionNamedType != null); + + _exceptionPathsThrownExceptionInfoMap ??= new Dictionary(); + if (!_exceptionPathsThrownExceptionInfoMap.TryGetValue(CurrentBasicBlock, out var info)) + { + info = ThrownExceptionInfo.CreateDefaultInfoForExceptionsPathAnalysis( + CurrentBasicBlock, WellKnownTypeProvider, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack); + } + + return info; + } + } + + protected DataFlowOperationVisitor(TAnalysisContext analysisContext) + { + DataFlowAnalysisContext = analysisContext; + + // All of these named type are very commonly accessed in the dataflow analysis so we want to ensure + // the fastest access (even though we know we have a cached access). + ExceptionNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemException); + ContractNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticContractsContract); + IDisposableNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIDisposable); + IAsyncDisposableNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIAsyncDisposable); + ConfiguredAsyncDisposable = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredAsyncDisposable); + ConfiguredValueTaskAwaitable = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesConfiguredValueTaskAwaitable); + TaskNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask); + TaskAsyncEnumerableExtensions = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTaskAsyncEnumerableExtensions); + MemoryStreamNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOMemoryStream); + ValueTaskNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksValueTask); + GenericTaskNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask1); + MonitorNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingMonitor); + InterlockedNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingInterlocked); + SerializationInfoNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationSerializationInfo); + StreamingContextNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeSerializationStreamingContext); + GenericIEquatableNamedType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIEquatable1); + StringReaderType = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIOStringReader); + CollectionNamedTypes = GetWellKnownCollectionTypes(); + DebugAssertMethod = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDiagnosticsDebug)?.GetMembers("Assert") + .OfType().FirstOrDefault(HasDebugAssertSignature); + + _lValueFlowCaptures = LValueFlowCapturesProvider.GetOrCreateLValueFlowCaptures(analysisContext.ControlFlowGraph); + _valueCacheBuilder = ImmutableDictionary.CreateBuilder(); + _predicateValueKindCacheBuilder = ImmutableDictionary.CreateBuilder(); + _pendingArgumentsToReset = new HashSet(); + _pendingArgumentsToPostProcess = new List(); + _visitedFlowBranchConditions = new HashSet(); + _visitedLambdas = new HashSet(); + _returnValueOperations = OwningSymbol is IMethodSymbol method && !method.ReturnsVoid ? new HashSet() : null; + _interproceduralResultsBuilder = ImmutableDictionary.CreateBuilder>(); + _standaloneLocalFunctionAnalysisResultsBuilder = ImmutableDictionary.CreateBuilder>(); + _escapedLocalFunctions = ImmutableHashSet.CreateBuilder(); + _analyzedLocalFunctions = ImmutableHashSet.CreateBuilder(); + _escapedLambdas = ImmutableHashSet.CreateBuilder(); + _analyzedLambdas = ImmutableHashSet.CreateBuilder(); + + _interproceduralCallStack = new Stack(); + _addressSharedEntitiesProvider = new AddressSharedEntitiesProvider(analysisContext); + if (analysisContext.InterproceduralAnalysisData != null) + { + foreach (var argumentInfo in analysisContext.InterproceduralAnalysisData.ArgumentValuesMap.Values) + { + CacheAbstractValue(argumentInfo.Operation, argumentInfo.Value); + } + + foreach (var operation in analysisContext.InterproceduralAnalysisData.CallStack) + { + _interproceduralCallStack.Push(operation); + } + + _interproceduralMethodToCfgMap = null; + } + else + { + _interproceduralMethodToCfgMap = new Dictionary(); + } + + AnalysisEntity? interproceduralInvocationInstance; + if (analysisContext.InterproceduralAnalysisData?.InvocationInstance.HasValue == true) + { + (interproceduralInvocationInstance, ThisOrMePointsToAbstractValue) = analysisContext.InterproceduralAnalysisData.InvocationInstance!.Value; + } + else + { + ThisOrMePointsToAbstractValue = GetThisOrMeInstancePointsToValue(analysisContext); + interproceduralInvocationInstance = null; + } + + var pointsToAnalysisKind = analysisContext is PointsToAnalysisContext pointsToAnalysisContext + ? pointsToAnalysisContext.PointsToAnalysisKind + : analysisContext.PointsToAnalysisResult?.PointsToAnalysisKind ?? PointsToAnalysisKind.None; + HasPointsToAnalysisResult = pointsToAnalysisKind != PointsToAnalysisKind.None; + HasCompletePointsToAnalysisResult = pointsToAnalysisKind == PointsToAnalysisKind.Complete; + + AnalysisEntityFactory = new AnalysisEntityFactory( + DataFlowAnalysisContext.ControlFlowGraph, + DataFlowAnalysisContext.WellKnownTypeProvider, + getPointsToAbstractValue: HasPointsToAnalysisResult ? + GetPointsToAbstractValue : + null, + getIsInsideAnonymousObjectInitializer: () => IsInsideAnonymousObjectInitializer, + getIsLValueFlowCapture: IsLValueFlowCapture, + containingTypeSymbol: analysisContext.OwningSymbol.ContainingType, + interproceduralInvocationInstance: interproceduralInvocationInstance, + interproceduralThisOrMeInstanceForCaller: analysisContext.InterproceduralAnalysisData?.ThisOrMeInstanceForCaller?.Instance, + interproceduralCallStack: analysisContext.InterproceduralAnalysisData?.CallStack, + interproceduralCapturedVariablesMap: analysisContext.InterproceduralAnalysisData?.CapturedVariablesMap, + interproceduralGetAnalysisEntityForFlowCapture: analysisContext.InterproceduralAnalysisData?.GetAnalysisEntityForFlowCapture, + getInterproceduralCallStackForOwningSymbol: GetInterproceduralCallStackForOwningSymbol); + + return; + + static bool HasDebugAssertSignature(IMethodSymbol method) + { + return method.IsStatic && + method.ReturnsVoid && + method.Parameters.Length == 1 && + method.Parameters[0].Type.SpecialType == SpecialType.System_Boolean; + } + } + + protected CopyAbstractValue GetDefaultCopyValue(AnalysisEntity analysisEntity) + => _addressSharedEntitiesProvider.GetDefaultCopyValue(analysisEntity); + + protected CopyAbstractValue? TryGetAddressSharedCopyValue(AnalysisEntity analysisEntity) + => _addressSharedEntitiesProvider.TryGetAddressSharedCopyValue(analysisEntity); + + public virtual (TAbstractAnalysisValue Value, PredicateValueKind PredicateValueKind)? GetReturnValueAndPredicateKind() + { + if (_returnValueOperations == null || + _returnValueOperations.Count == 0) + { + if (OwningSymbol is IMethodSymbol method && + !method.ReturnsVoid) + { + // Non-void method without any return statements + return (GetAbstractDefaultValue(method.ReturnType), PredicateValueKind.Unknown); + } + + return null; + } + + TAbstractAnalysisValue mergedValue = ValueDomain.Bottom; + PredicateValueKind? mergedPredicateValueKind = null; + foreach (var operation in _returnValueOperations) + { + mergedValue = ValueDomain.Merge(mergedValue, GetAbstractValueForReturnOperation(operation, out _)); + if (PredicateAnalysis) + { + if (!_predicateValueKindCacheBuilder.TryGetValue(operation, out var predicateValueKind)) + { + predicateValueKind = PredicateValueKind.Unknown; + } + + if (!mergedPredicateValueKind.HasValue) + { + mergedPredicateValueKind = predicateValueKind; + } + else if (mergedPredicateValueKind.Value != predicateValueKind) + { + mergedPredicateValueKind = PredicateValueKind.Unknown; + } + } + } + + return (mergedValue, mergedPredicateValueKind ?? PredicateValueKind.Unknown); + } + + private static PointsToAbstractValue GetThisOrMeInstancePointsToValue(TAnalysisContext analysisContext) + { + var owningSymbol = analysisContext.OwningSymbol; + if (!owningSymbol.IsStatic && + !owningSymbol.ContainingType.HasValueCopySemantics()) + { + var thisOrMeLocation = AbstractLocation.CreateThisOrMeLocation(owningSymbol.ContainingType, analysisContext.InterproceduralAnalysisData?.CallStack); + return PointsToAbstractValue.Create(thisOrMeLocation, mayBeNull: false); + } + else + { + return PointsToAbstractValue.NoLocation; + } + } + + /// + /// Primary method that flows analysis data through the given statement. + /// + public virtual TAnalysisData Flow(IOperation statement, BasicBlock block, TAnalysisData input) + { + CurrentAnalysisData = input; + Visit(statement, null); + AfterVisitRoot(statement); + return CurrentAnalysisData; + } + + [Conditional("DEBUG")] + private void AfterVisitRoot(IOperation operation) + { + Debug.Assert(_pendingArgumentsToReset.Count == 0); + Debug.Assert(_pendingArgumentsToPostProcess.Count == 0); + + // Ensure that we visited and cached values for all operation descendants. + foreach (var descendant in operation.DescendantsAndSelf()) + { + // GetState will throw an InvalidOperationException if the visitor did not visit the operation or cache it's abstract value. + var _ = GetCachedAbstractValue(descendant); + } + } + + public TAnalysisData OnStartBlockAnalysis(BasicBlock block, TAnalysisData input) + { + CurrentBasicBlock = block; + CurrentAnalysisData = input; + + if (PredicateAnalysis && IsReachableBlockData(input)) + { + UpdateReachability(block, input, isReachable: GetBlockReachability(block)); + } + + switch (block.Kind) + { + case BasicBlockKind.Entry: + OnStartEntryBlockAnalysis(block); + break; + + case BasicBlockKind.Exit: + OnStartExitBlockAnalysis(block); + break; + + default: + if (AnalysisDataForUnhandledThrowOperations != null && block.IsFirstBlockOfFinally(out _)) + { + MergeAnalysisDataFromUnhandledThrowOperations(caughtExceptionType: null); + } + + break; + } + + return CurrentAnalysisData; + } + + public TAnalysisData OnEndBlockAnalysis(BasicBlock block, TAnalysisData analysisData) + { + _currentBasicBlock = block; + CurrentAnalysisData = analysisData; + + if (block.EnclosingRegion != null && + block.EnclosingRegion.LastBlockOrdinal == block.Ordinal) + { + // Update analysis data for unhandled throw exceptions if we are at the end of a finally region. + // Note: We must do this before invoking OnLeavingRegion below as that might remove tracking data + // for keys that are out of scope. + if (AnalysisDataForUnhandledThrowOperations != null && block.IsLastBlockOfFinally(out var finallyRegion)) + { + foreach (var (exceptionInfo, dataAtException) in AnalysisDataForUnhandledThrowOperations) + { + if (exceptionInfo.ContainingFinallyRegion == null || + !finallyRegion.ContainsRegionOrSelf(exceptionInfo.ContainingFinallyRegion)) + { + AssertValidAnalysisData(dataAtException); + UpdateValuesForAnalysisData(dataAtException); + AssertValidAnalysisData(dataAtException); + } + } + } + } + + if (block.Kind == BasicBlockKind.Exit) + { + OnEndExitBlockAnalysis(block); + } + + _currentBasicBlock = null; + return CurrentAnalysisData; + } + + /// + /// Updates values for existing entries in with newer values from CurrentAnalysisData. + /// + protected abstract void UpdateValuesForAnalysisData(TAnalysisData targetAnalysisData); + + /// + /// Helper method to update analysis data for existing entries in + /// with newer values from . + /// + protected void UpdateValuesForAnalysisData( + DictionaryAnalysisData targetAnalysisData, + DictionaryAnalysisData newAnalysisData) + where TKey : notnull + { + using var builder = ArrayBuilder.GetInstance(targetAnalysisData.Count); + builder.AddRange(targetAnalysisData.Keys); + + for (int i = 0; i < builder.Count; i++) + { + var key = builder[i]; + if (newAnalysisData.TryGetValue(key, out var newValue)) + { + targetAnalysisData[key] = newValue; + } + } + } + + protected abstract void StopTrackingDataForParameter(IParameterSymbol parameter, AnalysisEntity analysisEntity); + + protected virtual void StopTrackingDataForParameters(ImmutableDictionary parameterEntities) + { + foreach (var kvp in parameterEntities) + { + IParameterSymbol parameter = kvp.Key; + AnalysisEntity analysisEntity = kvp.Value; + + // Stop tracking parameter values on exit. + StopTrackingDataForParameter(parameter, analysisEntity); + } + } + + private void OnStartEntryBlockAnalysis(BasicBlock entryBlock) + { + Debug.Assert(entryBlock.Kind == BasicBlockKind.Entry); + + if (_lazyParameterEntities == null && + OwningSymbol is IMethodSymbol method && + !method.Parameters.IsEmpty) + { + var builder = ImmutableDictionary.CreateBuilder(); + var argumentValuesMap = DataFlowAnalysisContext.InterproceduralAnalysisData?.ArgumentValuesMap ?? + ImmutableDictionary>.Empty; + + foreach (var parameter in method.Parameters) + { + var result = AnalysisEntityFactory.TryCreateForSymbolDeclaration(parameter, out var analysisEntity); + Debug.Assert(result); + RoslynDebug.Assert(analysisEntity != null); + builder.Add(parameter, analysisEntity); + + ArgumentInfo? assignedValue = null; + if (argumentValuesMap.TryGetValue(parameter.OriginalDefinition, out var argumentInfo)) + { + assignedValue = argumentInfo; + } + + _addressSharedEntitiesProvider.UpdateAddressSharedEntitiesForParameter(parameter, analysisEntity!, assignedValue); + SetValueForParameterOnEntry(parameter, analysisEntity!, assignedValue); + } + + _lazyParameterEntities = builder.ToImmutable(); + } + } + + private void OnStartExitBlockAnalysis(BasicBlock exitBlock) + { + Debug.Assert(exitBlock.Kind == BasicBlockKind.Exit); + + PerformStandaloneLambdaOrLocalFunctionAnalysisOnExit(); + + if (_lazyParameterEntities != null) + { + foreach (var kvp in _lazyParameterEntities) + { + IParameterSymbol parameter = kvp.Key; + AnalysisEntity analysisEntity = kvp.Value; + + // Escape parameter values on exit, except for ref/out parameters in interprocedural analysis. + if (parameter.RefKind == RefKind.None || DataFlowAnalysisContext.InterproceduralAnalysisData == null) + { + EscapeValueForParameterOnExit(parameter, analysisEntity); + } + } + } + } + + private void PerformStandaloneLambdaOrLocalFunctionAnalysisOnExit() + { + // First append the escaped local functions and lambdas from points to result, if any. + if (DataFlowAnalysisContext.PointsToAnalysisResult is { } pointsToAnalysisResult) + { + _escapedLocalFunctions.AddRange(pointsToAnalysisResult.LambdaAndLocalFunctionAnalysisInfo.EscapedLocalFunctions); + _escapedLambdas.AddRange(pointsToAnalysisResult.LambdaAndLocalFunctionAnalysisInfo.EscapedLambdas); + } + + // Perform standalone analysis for local functions, if required. + foreach (var localFunction in DataFlowAnalysisContext.ControlFlowGraph.LocalFunctions) + { + if (IsStandaloneAnalysisRequiredForLocalFunction(localFunction)) + { + PerformStandaloneLocalFunctionInterproceduralAnalysis(localFunction); + } + } + + // Perform standalone analysis for lambdas, if required. + foreach (var lambda in _visitedLambdas) + { + if (IsStandaloneAnalysisRequiredForLambda(lambda)) + { + PerformStandaloneLambdaInterproceduralAnalysis(lambda); + } + } + } + + private bool IsStandaloneAnalysisRequiredForLocalFunction(IMethodSymbol localFunction) + { + Debug.Assert(localFunction.MethodKind == MethodKind.LocalFunction); + Debug.Assert(DataFlowAnalysisContext.ControlFlowGraph.LocalFunctions.Contains(localFunction)); + + // Perform standalone analysis for local functions that escaped or were not analyzed. + return _escapedLocalFunctions.Contains(localFunction) || !_analyzedLocalFunctions.Contains(localFunction); + } + + private bool IsStandaloneAnalysisRequiredForLambda(IFlowAnonymousFunctionOperation lambda) + { + Debug.Assert(_visitedLambdas.Contains(lambda)); + + // Perform standalone analysis for lambdas that escaped or were not analyzed. + return _escapedLambdas.Contains(lambda) || !_analyzedLambdas.Contains(lambda); + } + + private void OnEndExitBlockAnalysis(BasicBlock exitBlock) + { + Debug.Assert(exitBlock.Kind == BasicBlockKind.Exit); + + if (DataFlowAnalysisContext.ExceptionPathsAnalysis && !ExecutingExceptionPathsAnalysisPostPass) + { + // We are going to perform another analysis pass for computing data on exception paths. + // So delay all the exit block analysis until that is done. + return; + } + + // For context-sensitive interprocedural analysis, we need to stop tracking data for the parameters + // as they will no longer be in caller's analysis scope. + if (_lazyParameterEntities != null && DataFlowAnalysisContext.InterproceduralAnalysisData != null) + { + // Reset address shared entities to caller's address shared entities. + _addressSharedEntitiesProvider.SetAddressSharedEntities(DataFlowAnalysisContext.InterproceduralAnalysisData.AddressSharedEntities); + StopTrackingDataForParameters(_lazyParameterEntities); + } + } + + protected bool IsParameterEntityForCurrentMethod(AnalysisEntity analysisEntity) + => analysisEntity.Symbol is IParameterSymbol parameter && + _lazyParameterEntities != null && + _lazyParameterEntities.TryGetValue(parameter, out var parameterEntity) && + parameterEntity == analysisEntity; + + /// + /// Primary method that flows analysis data through the given flow edge/branch. + /// Returns false if the branch is conditional and the branch value always evaluates to false. + /// + public virtual (TAnalysisData output, bool isFeasibleBranch) FlowBranch( + BasicBlock fromBlock, + BranchWithInfo branch, + TAnalysisData input) + { + var isFeasibleBranch = true; + CurrentBasicBlock = fromBlock; + CurrentAnalysisData = input; + + if (branch.BranchValue != null) + { + FlowBranchConditionKind = branch.ControlFlowConditionKind; + Visit(branch.BranchValue, null); + + if (branch.ControlFlowConditionKind != ControlFlowConditionKind.None) + { + // We visit the condition twice - once for the condition true branch, and once for the condition false branch. + // Below check ensures we execute AfterVisitRoot only once. + if (!_visitedFlowBranchConditions.Add(branch.BranchValue)) + { + AfterVisitRoot(branch.BranchValue); + _visitedFlowBranchConditions.Remove(branch.BranchValue); + } + + if (isConditionalBranchNeverTaken()) + { + isFeasibleBranch = false; + } + } + else + { + AfterVisitRoot(branch.BranchValue); + } + + FlowBranchConditionKind = ControlFlowConditionKind.None; + } + + // Special handling for return and throw branches. + switch (branch.Kind) + { + case ControlFlowBranchSemantics.Return: + ProcessReturnValue(branch.BranchValue); + break; + + case ControlFlowBranchSemantics.Throw: + case ControlFlowBranchSemantics.Rethrow: + // Update the tracked merged analysis data at throw branches. + var thrownExceptionType = branch.BranchValue?.Type ?? CurrentBasicBlock.GetEnclosingRegionExceptionType(); + if (thrownExceptionType is INamedTypeSymbol exceptionType && + exceptionType.DerivesFrom(ExceptionNamedType, baseTypesOnly: true)) + { + AnalysisDataForUnhandledThrowOperations ??= new Dictionary(); + var info = ThrownExceptionInfo.Create(CurrentBasicBlock, exceptionType, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack); + AnalysisDataForUnhandledThrowOperations[info] = GetClonedCurrentAnalysisData(); + } + + ProcessThrowValue(branch.BranchValue); + break; + } + + return (CurrentAnalysisData, isFeasibleBranch); + + bool isConditionalBranchNeverTaken() + { + RoslynDebug.Assert(branch.BranchValue != null); + Debug.Assert(branch.ControlFlowConditionKind != ControlFlowConditionKind.None); + + if (branch.BranchValue.Type?.SpecialType == SpecialType.System_Boolean && + branch.BranchValue.ConstantValue.HasValue) + { + var alwaysTrue = (bool)branch.BranchValue.ConstantValue.Value!; + if (alwaysTrue && branch.ControlFlowConditionKind == ControlFlowConditionKind.WhenFalse || + !alwaysTrue && branch.ControlFlowConditionKind == ControlFlowConditionKind.WhenTrue) + { + return true; + } + } + + if (PredicateAnalysis && + _predicateValueKindCacheBuilder.TryGetValue(branch.BranchValue, out PredicateValueKind valueKind) && + isPredicateAlwaysFalseForBranch(valueKind)) + { + return true; + } + + if (DataFlowAnalysisContext.PointsToAnalysisResult != null && + isPredicateAlwaysFalseForBranch(DataFlowAnalysisContext.PointsToAnalysisResult.GetPredicateKind(branch.BranchValue))) + { + return true; + } + + if (DataFlowAnalysisContext.CopyAnalysisResult != null && + isPredicateAlwaysFalseForBranch(DataFlowAnalysisContext.CopyAnalysisResult.GetPredicateKind(branch.BranchValue))) + { + return true; + } + + if (DataFlowAnalysisContext.ValueContentAnalysisResult != null && + isPredicateAlwaysFalseForBranch(DataFlowAnalysisContext.ValueContentAnalysisResult.GetPredicateKind(branch.BranchValue))) + { + return true; + } + + return false; + } + + bool isPredicateAlwaysFalseForBranch(PredicateValueKind predicateValueKind) + { + Debug.Assert(branch.ControlFlowConditionKind != ControlFlowConditionKind.None); + + return predicateValueKind switch + { + PredicateValueKind.AlwaysFalse => branch.ControlFlowConditionKind == ControlFlowConditionKind.WhenTrue, + + PredicateValueKind.AlwaysTrue => branch.ControlFlowConditionKind == ControlFlowConditionKind.WhenFalse, + + _ => false, + }; + } + } + + /// + /// Get analysis value for an implicitly created completed task wrapping a returned value in an async method. + /// For example, "return 0;" in an async method returning "Task(Of int)". + /// + private protected virtual TAbstractAnalysisValue GetAbstractValueForImplicitWrappingTaskCreation( + IOperation returnValueOperation, + TAbstractAnalysisValue returnValue, + PointsToAbstractValue implicitTaskPointsToValue) + { + // Conservatively assume default unknown value for implicit task. + return ValueDomain.UnknownOrMayBeValue; + } + + protected virtual void ProcessReturnValue(IOperation? returnValue) + { + if (returnValue != null) + { + _returnValueOperations?.Add(returnValue); + + _ = GetAbstractValueForReturnOperation(returnValue, out var implicitTaskPointsToValueOpt); + if (implicitTaskPointsToValueOpt != null) + { + Debug.Assert(implicitTaskPointsToValueOpt.Kind == PointsToAbstractValueKind.KnownLocations); + SetTaskWrappedValue(implicitTaskPointsToValueOpt, GetCachedAbstractValue(returnValue)); + } + } + } + + private TAbstractAnalysisValue GetAbstractValueForReturnOperation(IOperation returnValueOperation, out PointsToAbstractValue? implicitTaskPointsToValue) + { + implicitTaskPointsToValue = null; + var returnValue = GetCachedAbstractValue(returnValueOperation); + + // Check if returned value is wrapped in an implicitly created completed task in an async method. + // For example, "return 0;" in an async method returning "Task". + // If so, we return the abstract value for the task wrapping the underlying return value. + if (OwningSymbol is IMethodSymbol method && + method.IsAsync && + SymbolEqualityComparer.Default.Equals(method.ReturnType.OriginalDefinition, GenericTaskNamedType) && + !SymbolEqualityComparer.Default.Equals(method.ReturnType, returnValueOperation.Type)) + { + var location = AbstractLocation.CreateAllocationLocation(returnValueOperation, method.ReturnType, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack); + implicitTaskPointsToValue = PointsToAbstractValue.Create(location, mayBeNull: false); + return GetAbstractValueForImplicitWrappingTaskCreation(returnValueOperation, returnValue, implicitTaskPointsToValue); + } + + return returnValue; + } + + protected virtual void HandlePossibleThrowingOperation(IOperation operation) + { + Debug.Assert(ExecutingExceptionPathsAnalysisPostPass); + Debug.Assert(!SkipExceptionPathsAnalysisPostPass); + + // Bail out if we are not analyzing an interprocedural call and there is no + // tracked analysis data. + if (!HasAnyAbstractValue(CurrentAnalysisData) && + DataFlowAnalysisContext.InterproceduralAnalysisData == null) + { + return; + } + + // Bail out if System.Exception is not defined. + if (ExceptionNamedType == null) + { + return; + } + + IOperation? instance = null; + IOperation? invocation = null; + switch (operation) + { + case IMemberReferenceOperation memberReference: + instance = memberReference.Instance; + break; + + case IDynamicMemberReferenceOperation dynamicMemberReference: + instance = dynamicMemberReference.Instance; + break; + + case IArrayElementReferenceOperation arrayElementReference: + instance = arrayElementReference.ArrayReference; + break; + + case IInvocationOperation invocationOp: + instance = invocationOp.Instance; + invocation = operation; + break; + + case IObjectCreationOperation objectCreation: + if (objectCreation.Constructor?.IsImplicitlyDeclared == true) + { + // Implicitly generated constructor should not throw. + return; + } + + invocation = operation; + break; + + default: + // Optimistically assume the operation cannot throw. + return; + } + + var invocationInstanceAccessCanThrow = instance != null && + instance.Kind != OperationKind.InstanceReference && + GetNullAbstractValue(instance) != NullAbstractValue.NotNull; + var invocationCanThrow = invocation != null && !TryGetInterproceduralAnalysisResult(operation, out _); + if (!invocationInstanceAccessCanThrow && !invocationCanThrow) + { + // Cannot throw an exception from instance access and + // interprocedural analysis already handles possible exception from invoked code. + return; + } + + // This operation can throw, so update the analysis data for unhandled exception with 'System.Exception' type. + AnalysisDataForUnhandledThrowOperations ??= new Dictionary(); + if (!AnalysisDataForUnhandledThrowOperations.TryGetValue(DefaultThrownExceptionInfo, out TAnalysisData? data) || + CurrentBasicBlock.IsContainedInRegionOfKind(ControlFlowRegionKind.Finally)) + { + data = null; + } + + data = GetMergedAnalysisDataForPossibleThrowingOperation(data, operation); + RoslynDebug.Assert(data != null); + AssertValidAnalysisData(data); + AnalysisDataForUnhandledThrowOperations[DefaultThrownExceptionInfo] = data!; + } + + protected virtual TAnalysisData GetMergedAnalysisDataForPossibleThrowingOperation(TAnalysisData? existingData, IOperation operation) + { + Debug.Assert(ExecutingExceptionPathsAnalysisPostPass); + Debug.Assert(!SkipExceptionPathsAnalysisPostPass); + + return existingData == null ? + GetClonedCurrentAnalysisData() : + MergeAnalysisData(CurrentAnalysisData, existingData); + } + + public TAnalysisData OnLeavingRegions( + IEnumerable leavingRegionLocals, + IEnumerable leavingRegionFlowCaptures, + BasicBlock currentBasicBlock, + TAnalysisData input) + { + if (!leavingRegionLocals.Any() && !leavingRegionFlowCaptures.Any()) + { + return input; + } + + CurrentBasicBlock = currentBasicBlock; + CurrentAnalysisData = input; + + ProcessOutOfScopeLocalsAndFlowCaptures(leavingRegionLocals, leavingRegionFlowCaptures); + + _currentBasicBlock = null; + return CurrentAnalysisData; + } + + protected virtual void ProcessOutOfScopeLocalsAndFlowCaptures(IEnumerable locals, IEnumerable flowCaptures) + { + Debug.Assert(locals.Any() || flowCaptures.Any()); + + if (PredicateAnalysis) + { + foreach (var captureId in flowCaptures) + { + if (AnalysisEntityFactory.TryGetForFlowCapture(captureId, out var analysisEntity) && + HasPredicatedDataForEntity(analysisEntity)) + { + StopTrackingPredicatedData(analysisEntity); + } + } + } + } + + private bool IsContractCheckArgument(IArgumentOperation operation) + => operation.Parent is IInvocationOperation invocation && + invocation.Arguments[0] == operation && + (IsAnyDebugAssertMethod(invocation.TargetMethod) || IsContractCheckMethod(invocation.TargetMethod)); + + private bool IsContractCheckMethod(IMethodSymbol method) + { + if (Equals(method.ContainingType, ContractNamedType) && + method.IsStatic) + { + if (_lazyContractCheckMethods == null) + { + // Contract.Requires check. + var requiresMethods = ContractNamedType.GetMembers("Requires"); + var assumeMethods = ContractNamedType.GetMembers("Assume"); + var assertMethods = ContractNamedType.GetMembers("Assert"); + var validationMethods = requiresMethods.Concat(assumeMethods).Concat(assertMethods).OfType().Where(m => m.IsStatic && m.ReturnsVoid && !m.Parameters.IsEmpty && (m.Parameters[0].Type.SpecialType == SpecialType.System_Boolean)); + _lazyContractCheckMethods = ImmutableHashSet.CreateRange(validationMethods); + } + + return _lazyContractCheckMethods.Contains(method); + } + + return false; + } + + /// + /// Checks if the method is an overload of the method. + /// + /// The IMethodSymbol to test. + /// True if the method is an overlaod of the method. + private bool IsAnyDebugAssertMethod(IMethodSymbol method) => + DebugAssertMethod != null && + method.ContainingSymbol.Equals(DebugAssertMethod.ContainingSymbol, SymbolEqualityComparer.Default) && + method.Name == DebugAssertMethod.Name && + method.ReturnType == DebugAssertMethod.ReturnType; + + protected bool IsAnyAssertMethod(IMethodSymbol method) + => IsAnyDebugAssertMethod(method) || IsContractCheckMethod(method); + + #region Helper methods to get or cache analysis data for visited operations. + + internal ImmutableDictionary GetStateMap() => _valueCacheBuilder.ToImmutable(); + + internal ImmutableDictionary GetPredicateValueKindMap() => _predicateValueKindCacheBuilder.ToImmutable(); + + public virtual TAnalysisData? GetMergedDataForUnhandledThrowOperations() + { + if (AnalysisDataForUnhandledThrowOperations == null) + { + return null; + } + + TAnalysisData? mergedData = null; + foreach (TAnalysisData data in AnalysisDataForUnhandledThrowOperations.Values) + { + mergedData = mergedData != null ? MergeAnalysisData(mergedData, data) : data; + } + +#if DEBUG + if (mergedData != null) + { + AssertValidAnalysisData(mergedData); + } +#endif + return mergedData; + } + + public TAbstractAnalysisValue GetCachedAbstractValue(IOperation operation) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + if (_valueCacheBuilder.TryGetValue(operation, out var state)) + { + return state; + } + + if (DataFlowAnalysisContext.InterproceduralAnalysisData != null) + { + return DataFlowAnalysisContext.InterproceduralAnalysisData.GetCachedAbstractValueFromCaller(operation); + } + + // We were unable to find cached abstract value for requested operation. + // We should never reach this path, except for one known case. + // For interprocedural analysis, we might reach here when attempting to access abstract value for instance receiver + // of a method delegate which was saved in a prior interprocedural call chain, that returned back to the root caller + // and a different interprocedural call tree indirectly invokes that saved method delegate. + // We correctly resolve the method delegate target, but would need pretty complicated implementation to reach that + // operation's analysis result. + // See unit test 'DisposeObjectsBeforeLosingScopeTests.InvocationOfMethodDelegate_PriorInterproceduralCallChain' for an example. + // For now, we just gracefully return an unknown abstract value for this case. + if (DataFlowAnalysisContext.InterproceduralAnalysisConfiguration.InterproceduralAnalysisKind != InterproceduralAnalysisKind.None) + { + return ValueDomain.UnknownOrMayBeValue; + } + + throw new InvalidOperationException(); + } + + protected void CacheAbstractValue(IOperation operation, TAbstractAnalysisValue value) + { + if (operation == null) + { + throw new ArgumentNullException(nameof(operation)); + } + + _valueCacheBuilder[operation] = value; + } + + protected NullAbstractValue GetNullAbstractValue(IOperation operation) => GetPointsToAbstractValue(operation).NullState; + + protected virtual CopyAbstractValue GetCopyAbstractValue(IOperation operation) + { + if (DataFlowAnalysisContext.CopyAnalysisResult == null) + { + return CopyAbstractValue.Unknown; + } + else + { + return DataFlowAnalysisContext.CopyAnalysisResult[operation]; + } + } + + protected virtual PointsToAbstractValue GetPointsToAbstractValue(IOperation operation) + { + if (DataFlowAnalysisContext.PointsToAnalysisResult == null) + { + return PointsToAbstractValue.Unknown; + } + else + { + return DataFlowAnalysisContext.PointsToAnalysisResult[operation]; + } + } + + protected virtual ValueContentAbstractValue GetValueContentAbstractValue(IOperation operation) + { + if (DataFlowAnalysisContext.ValueContentAnalysisResult == null) + { + return ValueContentAbstractValue.MayBeContainsNonLiteralState; + } + else + { + return DataFlowAnalysisContext.ValueContentAnalysisResult[operation]; + } + } + + protected ImmutableHashSet GetEscapedLocations(IOperation operation) + { + if (operation == null || DataFlowAnalysisContext.PointsToAnalysisResult == null) + { + return ImmutableHashSet.Empty; + } + else + { + return DataFlowAnalysisContext.PointsToAnalysisResult.GetEscapedAbstractLocations(operation); + } + } + + protected ImmutableHashSet GetEscapedLocations(AnalysisEntity parameterEntity) + { + Debug.Assert(parameterEntity.Symbol?.Kind == SymbolKind.Parameter); + if (parameterEntity == null || DataFlowAnalysisContext.PointsToAnalysisResult == null) + { + return ImmutableHashSet.Empty; + } + else + { + return DataFlowAnalysisContext.PointsToAnalysisResult.GetEscapedAbstractLocations(parameterEntity); + } + } + + protected bool TryGetPointsToAbstractValueAtEntryBlockEnd(AnalysisEntity analysisEntity, [NotNullWhen(true)] out PointsToAbstractValue? pointsToAbstractValue) + { + Debug.Assert(CurrentBasicBlock.Kind == BasicBlockKind.Entry); + RoslynDebug.Assert(DataFlowAnalysisContext.PointsToAnalysisResult != null); + + var outputData = DataFlowAnalysisContext.PointsToAnalysisResult.EntryBlockOutput.Data; + return outputData.TryGetValue(analysisEntity, out pointsToAbstractValue); + } + + protected bool TryGetNullAbstractValueAtCurrentBlockEntry(AnalysisEntity analysisEntity, out NullAbstractValue nullAbstractValue) + { + RoslynDebug.Assert(DataFlowAnalysisContext.PointsToAnalysisResult != null); + var inputData = DataFlowAnalysisContext.PointsToAnalysisResult[CurrentBasicBlock].Data; + if (inputData.TryGetValue(analysisEntity, out PointsToAbstractValue? pointsToAbstractValue)) + { + nullAbstractValue = pointsToAbstractValue.NullState; + return true; + } + + nullAbstractValue = NullAbstractValue.MaybeNull; + return false; + } + + protected bool TryGetMergedNullAbstractValueAtUnhandledThrowOperationsInGraph(AnalysisEntity analysisEntity, out NullAbstractValue nullAbstractValue) + { + RoslynDebug.Assert(DataFlowAnalysisContext.PointsToAnalysisResult != null); + var inputData = DataFlowAnalysisContext.PointsToAnalysisResult.MergedStateForUnhandledThrowOperations?.Data; + if (inputData == null || !inputData.TryGetValue(analysisEntity, out PointsToAbstractValue? pointsToAbstractValue)) + { + nullAbstractValue = NullAbstractValue.MaybeNull; + return false; + } + + nullAbstractValue = pointsToAbstractValue.NullState; + return true; + } + + private protected void SetTaskWrappedValue(PointsToAbstractValue pointsToValueForTask, TAbstractAnalysisValue wrappedValue) + { + if (pointsToValueForTask.Kind == PointsToAbstractValueKind.Unknown) + { + return; + } + + TaskWrappedValuesMap ??= new Dictionary(); + TaskWrappedValuesMap[pointsToValueForTask] = wrappedValue; + } + + private protected bool TryGetTaskWrappedValue(PointsToAbstractValue pointsToAbstractValue, out TAbstractAnalysisValue wrappedValue) + { + if (TaskWrappedValuesMap == null) + { + wrappedValue = ValueDomain.UnknownOrMayBeValue; + return false; + } + + return TaskWrappedValuesMap.TryGetValue(pointsToAbstractValue, out wrappedValue); + } + + protected virtual TAbstractAnalysisValue ComputeAnalysisValueForReferenceOperation(IOperation operation, TAbstractAnalysisValue defaultValue) + { + return defaultValue; + } + + protected virtual TAbstractAnalysisValue ComputeAnalysisValueForEscapedRefOrOutArgument(IArgumentOperation operation, TAbstractAnalysisValue defaultValue) + { + return defaultValue; + } + + internal bool TryInferConversion(IConversionOperation operation, out ConversionInference inference) + { + inference = ConversionInference.Create(operation); + + // Bail out for user defined conversions. + if (operation.Conversion.IsUserDefined) + { + return true; + } + + // Bail out if conversion does not exist (error code). + if (!operation.Conversion.Exists) + { + return false; + } + + return TryInferConversion(operation.Operand, operation.Type, operation.IsTryCast, operation, out inference); + } + + internal bool TryInferConversion(IIsPatternOperation operation, out ConversionInference inference) + { + var targetType = operation.Pattern.GetPatternType(); + return TryInferConversion(operation.Value, targetType, isTryCast: true, operation, out inference); + } + + private bool TryInferConversion( + IOperation sourceOperand, + ITypeSymbol? targetType, + bool isTryCast, + IOperation operation, + out ConversionInference inference) + { + inference = ConversionInference.Create(targetType, sourceOperand.Type, isTryCast); + + // Bail out for throw expression conversion. + if (sourceOperand.Kind == OperationKind.Throw) + { + return true; + } + + // Analyze if cast might always succeed or fail based on points to analysis result. + var pointsToValue = GetPointsToAbstractValue(sourceOperand); + if (pointsToValue.Kind == PointsToAbstractValueKind.KnownLocations) + { + // Bail out if we have a possible null location for direct cast. + if (!isTryCast && pointsToValue.Locations.Any(location => location.IsNull)) + { + return true; + } + + // Handle is pattern operations with constant pattern. + if (operation is IIsPatternOperation isPatternOperation) + { + IPatternOperation patternOperation = isPatternOperation.Pattern; + bool direct = true; + + if (patternOperation is INegatedPatternOperation negatedPattern) + { + patternOperation = negatedPattern.Pattern; + direct = false; + } + + if (patternOperation is IConstantPatternOperation { Value.ConstantValue: { HasValue: true, Value: null } }) + { + switch (pointsToValue.NullState) + { + case NullAbstractValue.Null: + inference.AlwaysSucceed = direct; + break; + + case NullAbstractValue.NotNull: + inference.AlwaysFail = direct; + break; + } + + return true; + } + } + + if (targetType == null) + { + Debug.Fail($"Unexpected 'null' target type for '{operation.Syntax}'"); + return false; + } + + // Infer if a cast will always fail. + if (!inference.IsBoxing && + !inference.IsUnboxing && + !IsInterfaceOrTypeParameter(targetType) && + pointsToValue.Locations.All(location => location.IsNull || + (!location.IsNoLocation && + !IsInterfaceOrTypeParameter(location.LocationType) && + !targetType.DerivesFrom(location.LocationType) && + !location.LocationType.DerivesFrom(targetType)))) + { + if (PredicateAnalysis) + { + _predicateValueKindCacheBuilder[operation] = PredicateValueKind.AlwaysFalse; + } + + // We only set the alwaysFail flag for TryCast as direct casts that are guaranteed to fail will throw an exception and subsequent code will not execute. + if (isTryCast) + { + inference.AlwaysFail = true; + } + } + else + { + // Infer if a TryCast will always succeed. + if (isTryCast && + pointsToValue.Locations.All(location => location.IsNoLocation || !location.IsNull && location.LocationType.DerivesFrom(targetType))) + { + // TryCast which is guaranteed to succeed, and potentially can be changed to DirectCast. + if (PredicateAnalysis) + { + _predicateValueKindCacheBuilder[operation] = PredicateValueKind.AlwaysTrue; + } + + inference.AlwaysSucceed = true; + } + } + + return true; + } + + return false; + + // We are currently bailing out if an interface or type parameter is involved. + static bool IsInterfaceOrTypeParameter(ITypeSymbol? type) => type?.TypeKind is TypeKind.Interface or TypeKind.TypeParameter; + } + + #endregion + + #region Predicate analysis + + protected virtual void UpdateReachability(BasicBlock basicBlock, TAnalysisData analysisData, bool isReachable) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual bool IsReachableBlockData(TAnalysisData analysisData) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + private bool GetBlockReachability(BasicBlock basicBlock) + { + return basicBlock.IsReachable && + (DataFlowAnalysisContext.CopyAnalysisResult == null || DataFlowAnalysisContext.CopyAnalysisResult[basicBlock].IsReachable) && + (DataFlowAnalysisContext.PointsToAnalysisResult == null || DataFlowAnalysisContext.PointsToAnalysisResult[basicBlock].IsReachable) && + (DataFlowAnalysisContext.ValueContentAnalysisResult == null || DataFlowAnalysisContext.ValueContentAnalysisResult[basicBlock].IsReachable); + } + + protected bool IsCurrentBlockReachable() + { + if (PredicateAnalysis) + { + return IsReachableBlockData(CurrentAnalysisData); + } + + return GetBlockReachability(CurrentBasicBlock); + } + + private void PerformPredicateAnalysis(IOperation operation) + { + Debug.Assert(PredicateAnalysis); + Debug.Assert(operation.Kind is OperationKind.BinaryOperator or + OperationKind.UnaryOperator or + OperationKind.IsNull or + OperationKind.Invocation or + OperationKind.Argument or + OperationKind.FlowCaptureReference or + OperationKind.IsPattern); + + if (FlowBranchConditionKind == ControlFlowConditionKind.None || !IsRootOfCondition()) + { + // Operation is a predicate which is not a conditional. + // For example, "x = operation", where operation is "a == b". + // Check if we need to perform predicate analysis for the operation and/or set/transfer predicate data. + + // First find out if this operation is being captured. + AnalysisEntity? predicatedFlowCaptureEntity = GetPredicatedFlowCaptureEntity(); + if (predicatedFlowCaptureEntity == null) + { + // Operation is not being flow captured, we may have to perform predicate analysis. + if (operation.Kind == OperationKind.FlowCaptureReference) + { + // "x = FCR0" + // No predicate analysis required. + return; + } + + // Perform predicate analysis with dummy CurrentAnalysisData to set predicate value kind (always true/false/null). +#if DEBUG + TAnalysisData savedCurrentAnalysisData = GetClonedCurrentAnalysisData(); +#endif + FlowBranchConditionKind = ControlFlowConditionKind.WhenTrue; + var dummyTargetPredicateData = GetClonedCurrentAnalysisData(); + PerformPredicateAnalysisCore(operation, dummyTargetPredicateData); + FlowBranchConditionKind = ControlFlowConditionKind.None; +#if DEBUG + Debug.Assert(Equals(savedCurrentAnalysisData, CurrentAnalysisData), "Expected no updates to CurrentAnalysisData"); + savedCurrentAnalysisData.Dispose(); +#endif + dummyTargetPredicateData.Dispose(); + } + else + { + // Operation is being flow captured, i.e. "FC = operation" + if (operation.Kind == OperationKind.FlowCaptureReference) + { + // FC = FCR0 + var result = AnalysisEntityFactory.TryCreate(operation, out AnalysisEntity? flowCaptureReferenceEntity); + Debug.Assert(result); + RoslynDebug.Assert(flowCaptureReferenceEntity != null); + RoslynDebug.Assert(flowCaptureReferenceEntity.CaptureId != null); + Debug.Assert(HasPredicatedDataForEntity(flowCaptureReferenceEntity)); + TransferPredicatedData(fromEntity: flowCaptureReferenceEntity, toEntity: predicatedFlowCaptureEntity); + } + else + { + // "FC = (a == b)" + // Perform predicate analysis for both true/false result and start tracking data predicated on flow capture variable. + +#if DEBUG + TAnalysisData savedCurrentAnalysisData = GetClonedCurrentAnalysisData(); +#endif + TAnalysisData truePredicatedData = GetEmptyAnalysisData(); + FlowBranchConditionKind = ControlFlowConditionKind.WhenTrue; + PerformPredicateAnalysisCore(operation, truePredicatedData); + Debug.Assert(!ReferenceEquals(truePredicatedData, CurrentAnalysisData)); + + TAnalysisData falsePredicatedData = GetEmptyAnalysisData(); + FlowBranchConditionKind = ControlFlowConditionKind.WhenFalse; + PerformPredicateAnalysisCore(operation, falsePredicatedData); + Debug.Assert(!ReferenceEquals(falsePredicatedData, CurrentAnalysisData)); + FlowBranchConditionKind = ControlFlowConditionKind.None; + +#if DEBUG + Debug.Assert(Equals(savedCurrentAnalysisData, CurrentAnalysisData), "Expected no updates to CurrentAnalysisData"); + savedCurrentAnalysisData.Dispose(); +#endif + + if (HasAnyAbstractValue(truePredicatedData) || HasAnyAbstractValue(falsePredicatedData)) + { + StartTrackingPredicatedData(predicatedFlowCaptureEntity, truePredicatedData, falsePredicatedData); + } + else + { + truePredicatedData.Dispose(); + falsePredicatedData.Dispose(); + } + } + } + } + else + { + PerformPredicateAnalysisCore(operation, CurrentAnalysisData); + } + + return; + + // local functions + bool IsRootOfCondition() + { + // Special case for contract check argument + if (operation.Kind == OperationKind.Argument) + { + Debug.Assert(IsContractCheckArgument((IArgumentOperation)operation)); + return true; + } + + var current = operation.Parent; + while (current != null) + { + switch (current.Kind) + { + case OperationKind.Conversion: + case OperationKind.Parenthesized: + current = current.Parent; + continue; + + default: + return false; + } + } + + return current == null; + } + + AnalysisEntity? GetPredicatedFlowCaptureEntity() + { + var current = operation.Parent; + while (current != null) + { + switch (current.Kind) + { + case OperationKind.Conversion: + case OperationKind.Parenthesized: + current = current.Parent; + continue; + + case OperationKind.FlowCapture: + if (AnalysisEntityFactory.TryCreate(current, out var targetEntity) && + targetEntity.IsCandidatePredicateEntity()) + { + Debug.Assert(targetEntity.CaptureId != null); + return targetEntity; + } + + return null; + + default: + return null; + } + } + + return null; + } + } + + private void PerformPredicateAnalysisCore(IOperation operation, TAnalysisData targetAnalysisData) + { + Debug.Assert(PredicateAnalysis); + Debug.Assert(FlowBranchConditionKind != ControlFlowConditionKind.None); + + PredicateValueKind predicateValueKind = PredicateValueKind.Unknown; + switch (operation) + { + case IIsNullOperation isNullOperation: + // Predicate analysis for null checks. + predicateValueKind = SetValueForIsNullComparisonOperator(isNullOperation.Operand, equals: FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue, targetAnalysisData: targetAnalysisData); + break; + + case IIsPatternOperation isPatternOperation: + // Predicate analysis for "is pattern" checks: + // 1. Non-null value check for declaration pattern, i.e. "c is D d" + // 2. Non-null value check for discard pattern, i.e. "c is D _" + // 3. Non-null value check for recursive pattern, i.e. "c is D { SomeProperty: 0 }" + // 4. Equality value check for constant pattern, i.e. "x is 1" + switch (isPatternOperation.Pattern.Kind) + { + case OperationKind.DeclarationPattern: + if (!((IDeclarationPatternOperation)isPatternOperation.Pattern).MatchesNull) + { + // Set predicated null/non-null value for declared pattern variable, i.e. for 'd' in "c is D d". + predicateValueKind = SetValueForIsNullComparisonOperator(isPatternOperation.Pattern, equals: FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse, targetAnalysisData: targetAnalysisData); + } + + // Also set the predicated value for pattern value for true branch, i.e. for 'c' in "c is D d", + // while explicitly ignore the returned 'predicateValueKind'. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + _ = SetValueForIsNullComparisonOperator(isPatternOperation.Value, equals: false, targetAnalysisData: targetAnalysisData); + } + + break; + + case OperationKind.DiscardPattern: + case OperationKind.RecursivePattern: + // For the true branch, set the pattern operation value to NotNull. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + predicateValueKind = SetValueForIsNullComparisonOperator(isPatternOperation.Value, equals: false, targetAnalysisData: targetAnalysisData); + } + + break; + + case OperationKind.ConstantPattern: + var constantPattern = (IConstantPatternOperation)isPatternOperation.Pattern; + predicateValueKind = SetValueForEqualsOrNotEqualsComparisonOperator(isPatternOperation.Value, constantPattern.Value, + equals: FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue, isReferenceEquality: false, targetAnalysisData: targetAnalysisData); + break; + + case OperationKind.NegatedPattern: + var negatedPattern = (INegatedPatternOperation)isPatternOperation.Pattern; + if (negatedPattern.Pattern is IConstantPatternOperation negatedConstantPattern) + { + predicateValueKind = SetValueForEqualsOrNotEqualsComparisonOperator(isPatternOperation.Value, negatedConstantPattern.Value, + equals: FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse, isReferenceEquality: false, targetAnalysisData: targetAnalysisData); + } + + break; + + case OperationKind.RelationalPattern: + // For the true branch, set the pattern operation value to NotNull. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + predicateValueKind = SetValueForIsNullComparisonOperator(isPatternOperation.Value, equals: false, targetAnalysisData: targetAnalysisData); + } + + break; + + case OperationKind.BinaryPattern: + // These high level patterns should not be present in the lowered CFG: https://github.com/dotnet/roslyn/issues/47068 + predicateValueKind = PredicateValueKind.Unknown; + + // We special case common null check to reduce false positives. But this implementation for BinaryPattern is very incomplete. + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse) + { + var binaryPattern = (IBinaryPatternOperation)isPatternOperation.Pattern; + if (IsNotNullWhenFalse(binaryPattern)) + { + predicateValueKind = SetValueForIsNullComparisonOperator(isPatternOperation.Value, equals: false, targetAnalysisData: targetAnalysisData); + } + } + else if (FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue) + { + var binaryPattern = (IBinaryPatternOperation)isPatternOperation.Pattern; + if (IsNotNullWhenTrue(binaryPattern)) + { + predicateValueKind = SetValueForIsNullComparisonOperator(isPatternOperation.Value, equals: false, targetAnalysisData: targetAnalysisData); + } + } + + break; + + default: + Debug.Fail($"Unknown pattern kind '{isPatternOperation.Pattern.Kind}'"); + predicateValueKind = PredicateValueKind.Unknown; + break; + } + + break; + + case IBinaryOperation binaryOperation: + // Predicate analysis for different equality comparison operators. + if (!binaryOperation.IsComparisonOperator()) + { + return; + } + + predicateValueKind = SetValueForComparisonOperator(binaryOperation, targetAnalysisData); + break; + + case IUnaryOperation unaryOperation: + // Predicate analysis for unary not operator. + if (unaryOperation.OperatorKind == UnaryOperatorKind.Not) + { + FlowBranchConditionKind = FlowBranchConditionKind.Negate(); + PerformPredicateAnalysisCore(unaryOperation.Operand, targetAnalysisData); + FlowBranchConditionKind = FlowBranchConditionKind.Negate(); + } + + break; + + case IArgumentOperation argument: + Debug.Assert(IsContractCheckArgument(argument)); + PerformPredicateAnalysisCore(argument.Value, targetAnalysisData); + return; + + case IConversionOperation conversion: + PerformPredicateAnalysisCore(conversion.Operand, targetAnalysisData); + return; + + case IParenthesizedOperation parenthesizedOperation: + PerformPredicateAnalysisCore(parenthesizedOperation.Operand, targetAnalysisData); + return; + + case IFlowCaptureReferenceOperation: + var result = AnalysisEntityFactory.TryCreate(operation, out AnalysisEntity? flowCaptureReferenceEntity); + Debug.Assert(result); + RoslynDebug.Assert(flowCaptureReferenceEntity != null); + RoslynDebug.Assert(flowCaptureReferenceEntity.CaptureId != null); + if (!HasPredicatedDataForEntity(targetAnalysisData, flowCaptureReferenceEntity)) + { + return; + } + + predicateValueKind = ApplyPredicatedDataForEntity(targetAnalysisData, flowCaptureReferenceEntity, trueData: FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue); + break; + + case IInvocationOperation invocation: + // Predicate analysis for different equality comparison methods and argument null check methods. + if (invocation.Type?.SpecialType != SpecialType.System_Boolean) + { + return; + } + + if (invocation.TargetMethod.IsArgumentNullCheckMethod()) + { + // Predicate analysis for null checks, e.g. 'IsNullOrEmpty', 'IsNullOrWhiteSpace', etc. + // The method guarantees non-null value on 'WhenFalse' path, but does not guarantee null value on 'WhenTrue' path. + // Additionally, predicateValueKind cannot be determined to be AlwaysTrue or AlwaysFalse on either of these paths. + if (invocation.Arguments.Length == 1 && FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse) + { + _ = SetValueForIsNullComparisonOperator(invocation.Arguments[0].Value, equals: false, targetAnalysisData: targetAnalysisData); + } + + break; + } + + IOperation? leftOperand = null; + IOperation? rightOperand = null; + bool isReferenceEquality = false; + if (invocation.Arguments.Length == 2 && + invocation.TargetMethod.IsStaticObjectEqualsOrReferenceEquals()) + { + // 1. "static bool object.ReferenceEquals(o1, o2)" + // 2. "static bool object.Equals(o1, o2)" + leftOperand = invocation.Arguments[0].Value; + rightOperand = invocation.Arguments[1].Value; + isReferenceEquality = invocation.TargetMethod.Name == "ReferenceEquals" || + (AnalysisEntityFactory.TryCreate(invocation.Arguments[0].Value, out var analysisEntity) && + !analysisEntity.Type.HasValueCopySemantics() && + (analysisEntity.Type as INamedTypeSymbol)?.OverridesEquals() == false); + } + else + { + // 1. "bool virtual object.Equals(other)" + // 2. "bool override Equals(other)" + // 3. "bool IEquatable.Equals(other)" + if (invocation.Arguments.Length == 1 && + (invocation.TargetMethod.IsObjectEquals() || + invocation.TargetMethod.IsObjectEqualsOverride() || + IsOverrideOrImplementationOfEquatableEquals(invocation.TargetMethod))) + { + leftOperand = invocation.Instance; + rightOperand = invocation.Arguments[0].Value; + isReferenceEquality = invocation.TargetMethod.IsObjectEquals(); + } + } + + if (leftOperand != null && rightOperand != null) + { + predicateValueKind = SetValueForEqualsOrNotEqualsComparisonOperator( + leftOperand, + rightOperand, + equals: FlowBranchConditionKind == ControlFlowConditionKind.WhenTrue, + isReferenceEquality: isReferenceEquality, + targetAnalysisData: targetAnalysisData); + } + + break; + + default: + return; + } + + SetPredicateValueKind(operation, targetAnalysisData, predicateValueKind); + return; + + // local functions. + bool IsOverrideOrImplementationOfEquatableEquals(IMethodSymbol methodSymbol) + { + if (GenericIEquatableNamedType == null) + { + return false; + } + + foreach (var interfaceType in methodSymbol.ContainingType.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(interfaceType.OriginalDefinition, GenericIEquatableNamedType)) + { + var equalsMember = interfaceType.GetMembers("Equals").OfType().FirstOrDefault(); + if (equalsMember != null && methodSymbol.IsOverrideOrImplementationOfInterfaceMember(equalsMember)) + { + return true; + } + } + } + + return false; + } + + bool IsNotNullWhenFalse(IOperation operation) + { + if (operation is IConstantPatternOperation constant && constant.Value.ConstantValue.HasValue && constant.Value.ConstantValue.Value is null) + { + // This is a null check. So we are not null on the false branch. + return true; + } + + if (operation is IBinaryPatternOperation { OperatorKind: BinaryOperatorKind.Or } binaryOrOperation) + { + // Example: if (c is null or "") + // The whole operation is not null when false, because one of the OR branches is not null when false. + return IsNotNullWhenFalse(binaryOrOperation.LeftPattern) || IsNotNullWhenFalse(binaryOrOperation.RightPattern); + } + + return false; + } + + bool IsNotNullWhenTrue(IOperation operation) + { + if (operation is INegatedPatternOperation negated && negated.Pattern is IConstantPatternOperation constant && constant.Value.ConstantValue.HasValue && constant.Value.ConstantValue.Value is null) + { + // This is a not null check. So we are not null on the true branch. + return true; + } + + if (operation is IBinaryPatternOperation { OperatorKind: BinaryOperatorKind.And } binaryOrOperation) + { + // Example: if (c is not null and "") + // The whole operation is not null when true, because one of the AND branches is not null when true. + return IsNotNullWhenTrue(binaryOrOperation.LeftPattern) || IsNotNullWhenTrue(binaryOrOperation.RightPattern); + } + + return false; + } + } + + protected virtual void SetPredicateValueKind(IOperation operation, TAnalysisData analysisData, PredicateValueKind predicateValueKind) + { + Debug.Assert(PredicateAnalysis); + + if (predicateValueKind != PredicateValueKind.Unknown || + _predicateValueKindCacheBuilder.ContainsKey(operation)) + { + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse) + { + switch (predicateValueKind) + { + case PredicateValueKind.AlwaysFalse: + predicateValueKind = PredicateValueKind.AlwaysTrue; + break; + + case PredicateValueKind.AlwaysTrue: + predicateValueKind = PredicateValueKind.AlwaysFalse; + break; + } + } + + _predicateValueKindCacheBuilder[operation] = predicateValueKind; + } + } + + protected virtual PredicateValueKind SetValueForComparisonOperator(IBinaryOperation operation, TAnalysisData targetAnalysisData) + { + Debug.Assert(PredicateAnalysis); + Debug.Assert(operation.IsComparisonOperator()); + Debug.Assert(FlowBranchConditionKind != ControlFlowConditionKind.None); + + var leftType = operation.LeftOperand.Type; + var leftConstantValueOpt = operation.LeftOperand.ConstantValue; + var rightType = operation.RightOperand.Type; + var rightConstantValueOpt = operation.RightOperand.ConstantValue; + var isReferenceEquality = operation.OperatorMethod == null && + operation.Type?.SpecialType == SpecialType.System_Boolean && + leftType != null && + !leftType.HasValueCopySemantics() && + rightType != null && + !rightType.HasValueCopySemantics() && + (!leftConstantValueOpt.HasValue || leftConstantValueOpt.Value != null) && + (!rightConstantValueOpt.HasValue || rightConstantValueOpt.Value != null); + + bool equals; + switch (operation.OperatorKind) + { + case BinaryOperatorKind.Equals: + case BinaryOperatorKind.ObjectValueEquals: + equals = true; + break; + + case BinaryOperatorKind.NotEquals: + case BinaryOperatorKind.ObjectValueNotEquals: + equals = false; + break; + + default: + return PredicateValueKind.Unknown; + } + + if (FlowBranchConditionKind == ControlFlowConditionKind.WhenFalse) + { + equals = !equals; + } + + return SetValueForEqualsOrNotEqualsComparisonOperator(operation.LeftOperand, operation.RightOperand, equals, isReferenceEquality, targetAnalysisData); + } + + protected virtual PredicateValueKind SetValueForEqualsOrNotEqualsComparisonOperator(IOperation leftOperand, IOperation rightOperand, bool equals, bool isReferenceEquality, TAnalysisData targetAnalysisData) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual PredicateValueKind SetValueForIsNullComparisonOperator(IOperation leftOperand, bool equals, TAnalysisData targetAnalysisData) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual void StartTrackingPredicatedData(AnalysisEntity predicatedEntity, TAnalysisData? truePredicateData, TAnalysisData? falsePredicateData) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual void StopTrackingPredicatedData(AnalysisEntity predicatedEntity) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + private bool HasPredicatedDataForEntity(AnalysisEntity predicatedEntity) + => HasPredicatedDataForEntity(CurrentAnalysisData, predicatedEntity); + + protected virtual bool HasPredicatedDataForEntity(TAnalysisData analysisData, AnalysisEntity predicatedEntity) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual void TransferPredicatedData(AnalysisEntity fromEntity, AnalysisEntity toEntity) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual PredicateValueKind ApplyPredicatedDataForEntity(TAnalysisData analysisData, AnalysisEntity predicatedEntity, bool trueData) + { + Debug.Assert(PredicateAnalysis); + throw new NotImplementedException(); + } + + protected virtual void ProcessThrowValue(IOperation? thrownValue) + { + } + + #endregion + + #region Helper methods to handle initialization/assignment operations + protected abstract void SetAbstractValueForArrayElementInitializer(IArrayCreationOperation arrayCreation, ImmutableArray indices, ITypeSymbol elementType, IOperation initializer, TAbstractAnalysisValue value); + protected abstract void SetAbstractValueForAssignment(IOperation target, IOperation? assignedValueOperation, TAbstractAnalysisValue assignedValue, bool mayBeAssignment = false); + protected abstract void SetAbstractValueForTupleElementAssignment(AnalysisEntity tupleElementEntity, IOperation assignedValueOperation, TAbstractAnalysisValue assignedValue); + private void HandleFlowCaptureReferenceAssignment(IFlowCaptureReferenceOperation flowCaptureReference, IOperation assignedValueOperation, TAbstractAnalysisValue assignedValue) + { + Debug.Assert(IsLValueFlowCaptureReference(flowCaptureReference)); + + var pointsToValue = GetPointsToAbstractValue(flowCaptureReference); + if (pointsToValue.Kind == PointsToAbstractValueKind.KnownLValueCaptures) + { + switch (pointsToValue.LValueCapturedOperations.Count) + { + case 0: + throw new InvalidProgramException(); + + case 1: + var target = pointsToValue.LValueCapturedOperations.First(); + SetAbstractValueForAssignment(target, assignedValueOperation, assignedValue); + break; + + default: + if (HasUniqueCapturedEntity()) + { + goto case 1; + } + else + { + foreach (var capturedOperation in pointsToValue.LValueCapturedOperations) + { + SetAbstractValueForAssignment(capturedOperation, assignedValueOperation, assignedValue, mayBeAssignment: true); + } + } + + break; + + bool HasUniqueCapturedEntity() + { + AnalysisEntity? uniqueAnalysisEntity = null; + foreach (var capturedOperation in pointsToValue.LValueCapturedOperations) + { + if (AnalysisEntityFactory.TryCreate(capturedOperation, out var entity)) + { + if (uniqueAnalysisEntity == null) + { + uniqueAnalysisEntity = entity; + } + else if (entity != uniqueAnalysisEntity) + { + return false; + } + } + else + { + return false; + } + } + + return uniqueAnalysisEntity != null; + } + } + } + } + #endregion + + #region Helper methods for reseting/transfer instance analysis data when PointsTo analysis results are available + /// + /// Resets all the analysis data for all instances that share the same + /// as the given . + /// + /// + protected abstract void ResetValueTypeInstanceAnalysisData(AnalysisEntity analysisEntity); + + /// + /// Resets all the analysis data for all instances that share the same + /// as the given . + /// + protected abstract void ResetReferenceTypeInstanceAnalysisData(PointsToAbstractValue pointsToAbstractValue); + + private void ResetValueTypeInstanceAnalysisData(IOperation operation) + { + Debug.Assert(HasPointsToAnalysisResult); + Debug.Assert(operation.Type!.HasValueCopySemantics()); + + if (AnalysisEntityFactory.TryCreate(operation, out var analysisEntity)) + { + if (analysisEntity.Type.HasValueCopySemantics()) + { + ResetValueTypeInstanceAnalysisData(analysisEntity); + } + } + } + + /// + /// Resets all the analysis data for all instances that share the same + /// as pointed to by given reference type . + /// + /// + private void ResetReferenceTypeInstanceAnalysisData(IOperation operation) + { + Debug.Assert(HasPointsToAnalysisResult); + Debug.Assert(!operation.Type!.HasValueCopySemantics()); + + var pointsToValue = GetPointsToAbstractValue(operation); + if (pointsToValue.Locations.IsEmpty) + { + return; + } + + ResetReferenceTypeInstanceAnalysisData(pointsToValue); + } + + /// + /// Reset all the instance analysis data if is true and is also true. + /// If we are using or performing points to analysis, certain operations can invalidate all the analysis data off the containing instance. + /// + private void ResetInstanceAnalysisData(IOperation? operation) + { + if (operation == null || operation.Type == null || !HasPointsToAnalysisResult || !PessimisticAnalysis) + { + return; + } + + if (operation.Type.HasValueCopySemantics()) + { + ResetValueTypeInstanceAnalysisData(operation); + } + else + { + ResetReferenceTypeInstanceAnalysisData(operation); + } + } + + #endregion + + public TAnalysisData MergeAnalysisData(TAnalysisData value1, TAnalysisData value2, BasicBlock forBlock, bool forBackEdge) + => forBackEdge ? MergeAnalysisDataForBackEdge(value1, value2, forBlock) : MergeAnalysisData(value1, value2, forBlock); + protected abstract TAnalysisData MergeAnalysisData(TAnalysisData value1, TAnalysisData value2); + protected virtual TAnalysisData MergeAnalysisData(TAnalysisData value1, TAnalysisData value2, BasicBlock forBlock) + => MergeAnalysisData(value1, value2); + protected virtual TAnalysisData MergeAnalysisDataForBackEdge(TAnalysisData value1, TAnalysisData value2, BasicBlock forBlock) + => MergeAnalysisData(value1, value2, forBlock); + protected abstract TAnalysisData GetClonedAnalysisData(TAnalysisData analysisData); + protected TAnalysisData GetClonedCurrentAnalysisData() => GetClonedAnalysisData(CurrentAnalysisData); + public abstract TAnalysisData GetEmptyAnalysisData(); + protected abstract TAnalysisData GetExitBlockOutputData(TAnalysisResult analysisResult); + protected abstract bool Equals(TAnalysisData value1, TAnalysisData value2); + protected static bool EqualsHelper(IDictionary dict1, IDictionary dict2) + => dict1.Count == dict2.Count && + dict1.Keys.All(key => dict2.TryGetValue(key, out TValue value2) && EqualityComparer.Default.Equals(dict1[key], value2)); + protected abstract void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(TAnalysisData dataAtException, ThrownExceptionInfo throwBranchWithExceptionType); + protected virtual void AssertValidAnalysisData(TAnalysisData analysisData) + { + // No validation by default, all the subtypes can override for validation. + } + + protected void ApplyMissingCurrentAnalysisDataForUnhandledExceptionData( + DictionaryAnalysisData coreDataAtException, + DictionaryAnalysisData coreCurrentAnalysisData, + Func? predicate) + where TKey : notnull + { + foreach (var (key, value) in coreCurrentAnalysisData) + { + if (coreDataAtException.ContainsKey(key) || + predicate != null && !predicate(key)) + { + continue; + } + + coreDataAtException.Add(key, value); + } + } + + #region Interprocedural analysis + + /// + /// Gets a new instance of analysis data that should be passed as initial analysis data + /// for interprocedural analysis. + /// The default implementation returns cloned CurrentAnalysisData. + /// + protected virtual TAnalysisData GetInitialInterproceduralAnalysisData( + IMethodSymbol invokedMethod, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? invocationInstance, + (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + IDictionary? pointsToValues, + IDictionary? copyValues, + IDictionary? valueContentValues, + bool isLambdaOrLocalFunction, + bool hasParameterWithDelegateType) + => GetClonedCurrentAnalysisData(); + + /// + /// Apply the result data from interprocedural analysis to CurrentAnalysisData. + /// Default implementation is designed for the default implementation of GetInitialInterproceduralAnalysisData. + /// and overwrites the CurrentAnalysisData with the given . + /// + protected virtual void ApplyInterproceduralAnalysisResult(TAnalysisData resultData, bool isLambdaOrLocalFunction, bool hasDelegateTypeArgument, TAnalysisResult analysisResult) + => CurrentAnalysisData = resultData; + + private void ApplyInterproceduralAnalysisDataForUnhandledThrowOperations(Dictionary interproceduralUnhandledThrowOperationsData) + { + if (interproceduralUnhandledThrowOperationsData.Count == 0) + { + // All interprocedural exceptions were handled. + return; + } + + AnalysisDataForUnhandledThrowOperations ??= new Dictionary(); + foreach (var (exceptionInfo, analysisDataAtException) in interproceduralUnhandledThrowOperationsData) + { + // Adjust the thrown exception info from the interprocedural context to current context. + var adjustedExceptionInfo = exceptionInfo.With(CurrentBasicBlock, DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack); + + // Used cloned analysis data + var clonedAnalysisDataAtException = GetClonedAnalysisData(analysisDataAtException); + + ApplyInterproceduralAnalysisDataForUnhandledThrowOperation(adjustedExceptionInfo, clonedAnalysisDataAtException); + } + + // Local functions + void ApplyInterproceduralAnalysisDataForUnhandledThrowOperation(ThrownExceptionInfo exceptionInfo, TAnalysisData analysisDataAtException) + { + RoslynDebug.Assert(AnalysisDataForUnhandledThrowOperations != null); + AssertValidAnalysisData(analysisDataAtException); + ApplyMissingCurrentAnalysisDataForUnhandledExceptionData(analysisDataAtException, exceptionInfo); + AssertValidAnalysisData(analysisDataAtException); + + if (!AnalysisDataForUnhandledThrowOperations.TryGetValue(exceptionInfo, out var existingAnalysisDataAtException)) + { + AnalysisDataForUnhandledThrowOperations.Add(exceptionInfo, analysisDataAtException); + } + else + { + var mergedAnalysisDataAtException = MergeAnalysisData(existingAnalysisDataAtException, analysisDataAtException); + AssertValidAnalysisData(mergedAnalysisDataAtException); + AnalysisDataForUnhandledThrowOperations[exceptionInfo] = mergedAnalysisDataAtException; + existingAnalysisDataAtException.Dispose(); + analysisDataAtException.Dispose(); + } + } + } + + protected bool TryGetInterproceduralAnalysisResult(IOperation operation, [NotNullWhen(returnValue: true)] out TAnalysisResult? analysisResult) + { + if (_interproceduralResultsBuilder.TryGetValue(operation, out var computedAnalysisResult)) + { + analysisResult = (TAnalysisResult)computedAnalysisResult; + return true; + } + + analysisResult = null; + return false; + } + + private TAbstractAnalysisValue PerformInterproceduralAnalysis( + Func getCfg, + IMethodSymbol invokedMethod, + IOperation? instanceReceiver, + ImmutableArray arguments, + IOperation originalOperation, + TAbstractAnalysisValue defaultValue, + bool isLambdaOrLocalFunction, + out bool wasAnalyzed) + { + wasAnalyzed = false; + + // Use the original method definition for interprocedural analysis as the ControlFlowGraph can only be created for the original definition. + invokedMethod = invokedMethod.OriginalDefinition; + + // Bail out if configured not to execute interprocedural analysis. + var skipInterproceduralAnalysis = !isLambdaOrLocalFunction && InterproceduralAnalysisKind == InterproceduralAnalysisKind.None || + DataFlowAnalysisContext.InterproceduralAnalysisPredicate?.SkipInterproceduralAnalysis(invokedMethod, isLambdaOrLocalFunction) == true || + DataFlowAnalysisContext.AnalyzerOptions.IsConfiguredToSkipAnalysis(s_dummyDataflowAnalysisDescriptor, invokedMethod, OwningSymbol, WellKnownTypeProvider.Compilation); + + // Also bail out for non-source methods and methods where we are not sure about the actual runtime target method. + if (skipInterproceduralAnalysis || + invokedMethod.Locations.All(l => !l.IsInSource) || + invokedMethod.IsAbstract || + invokedMethod.IsVirtual || + invokedMethod.IsOverride || + invokedMethod.IsImplicitlyDeclared) + { + return ResetAnalysisDataAndReturnDefaultValue(); + } + + // Bail out if we are already analyzing the current context. + var currentMethodsBeingAnalyzed = DataFlowAnalysisContext.InterproceduralAnalysisData?.MethodsBeingAnalyzed ?? ImmutableHashSet.Empty; + var newMethodsBeingAnalyzed = currentMethodsBeingAnalyzed.Add(DataFlowAnalysisContext); + if (currentMethodsBeingAnalyzed.Count == newMethodsBeingAnalyzed.Count) + { + return ResetAnalysisDataAndReturnDefaultValue(); + } + + // Check if we are already at the maximum allowed interprocedural call chain length. + int currentMethodCallCount = currentMethodsBeingAnalyzed.Count(m => !(m.OwningSymbol is IMethodSymbol ms && ms.IsLambdaOrLocalFunctionOrDelegate())); + int currentLambdaOrLocalFunctionCallCount = currentMethodsBeingAnalyzed.Count - currentMethodCallCount; + + if (currentMethodCallCount >= MaxInterproceduralMethodCallChain || + currentLambdaOrLocalFunctionCallCount >= MaxInterproceduralLambdaOrLocalFunctionCallChain) + { + return ResetAnalysisDataAndReturnDefaultValue(); + } + + // Compute the dependent interprocedural PointsTo and Copy analysis results, if any. + var pointsToAnalysisResult = (PointsToAnalysisResult?)DataFlowAnalysisContext.PointsToAnalysisResult?.TryGetInterproceduralResult(originalOperation); + var copyAnalysisResult = DataFlowAnalysisContext.CopyAnalysisResult?.TryGetInterproceduralResult(originalOperation); + var valueContentAnalysisResult = DataFlowAnalysisContext.ValueContentAnalysisResult?.TryGetInterproceduralResult(originalOperation); + + // Compute the CFG for the invoked method. + var cfg = pointsToAnalysisResult?.ControlFlowGraph ?? + copyAnalysisResult?.ControlFlowGraph ?? + valueContentAnalysisResult?.ControlFlowGraph ?? + getCfg(); + if (cfg == null || !cfg.SupportsFlowAnalysis()) + { + return ResetAnalysisDataAndReturnDefaultValue(); + } + + var hasParameterWithDelegateType = invokedMethod.HasParameterWithDelegateType(); + + // Ensure we are using the same control flow graphs across analyses. + Debug.Assert(pointsToAnalysisResult?.ControlFlowGraph == null || cfg == pointsToAnalysisResult?.ControlFlowGraph); + Debug.Assert(copyAnalysisResult?.ControlFlowGraph == null || cfg == copyAnalysisResult?.ControlFlowGraph); + Debug.Assert(valueContentAnalysisResult?.ControlFlowGraph == null || cfg == valueContentAnalysisResult?.ControlFlowGraph); + + // Append operation to interprocedural call stack. + _interproceduralCallStack.Push(originalOperation); + + // Compute optional interprocedural analysis data for context-sensitive analysis. + bool isContextSensitive = isLambdaOrLocalFunction || InterproceduralAnalysisKind == InterproceduralAnalysisKind.ContextSensitive; + var interproceduralAnalysisData = isContextSensitive ? ComputeInterproceduralAnalysisData() : null; + TAnalysisResult? analysisResult; + + try + { + // Create analysis context for interprocedural analysis. + var interproceduralDataFlowAnalysisContext = DataFlowAnalysisContext.ForkForInterproceduralAnalysis( + invokedMethod, cfg, pointsToAnalysisResult, copyAnalysisResult, valueContentAnalysisResult, interproceduralAnalysisData); + + // Check if the client configured skipping analysis for the given interprocedural analysis context. + if (DataFlowAnalysisContext.InterproceduralAnalysisPredicate?.SkipInterproceduralAnalysis(interproceduralDataFlowAnalysisContext) == true) + { + return ResetAnalysisDataAndReturnDefaultValue(); + } + else + { + // Execute interprocedural analysis and get result. + analysisResult = TryGetOrComputeAnalysisResult(interproceduralDataFlowAnalysisContext); + if (analysisResult == null) + { + return defaultValue; + } + + wasAnalyzed = true; + + // Save the interprocedural result for the invocation/creation operation. + // Note that we Update instead of invoking .Add as we may execute the analysis multiple times for fixed point computation. + _interproceduralResultsBuilder[originalOperation] = analysisResult; + + _escapedLocalFunctions.AddRange(analysisResult.LambdaAndLocalFunctionAnalysisInfo.EscapedLocalFunctions); + _analyzedLocalFunctions.AddRange(analysisResult.LambdaAndLocalFunctionAnalysisInfo.AnalyzedLocalFunctions); + _escapedLambdas.AddRange(analysisResult.LambdaAndLocalFunctionAnalysisInfo.EscapedLambdas); + _analyzedLambdas.AddRange(analysisResult.LambdaAndLocalFunctionAnalysisInfo.AnalyzedLambdas); + } + + // Update the current analysis data based on interprocedural analysis result. + if (isContextSensitive) + { + // Apply any interprocedural analysis data for unhandled exceptions paths. + if (analysisResult.AnalysisDataForUnhandledThrowOperations is Dictionary interproceduralUnhandledThrowOperationsDataOpt) + { + ApplyInterproceduralAnalysisDataForUnhandledThrowOperations(interproceduralUnhandledThrowOperationsDataOpt); + } + + if (analysisResult.TaskWrappedValuesMap is Dictionary taskWrappedValuesMap) + { + foreach (var (key, value) in taskWrappedValuesMap) + { + SetTaskWrappedValue(key, value); + } + } + + // Apply interprocedural result analysis data for non-exception paths. + var resultData = GetExitBlockOutputData(analysisResult); + ApplyInterproceduralAnalysisResult(resultData, isLambdaOrLocalFunction, hasParameterWithDelegateType, analysisResult); + + Debug.Assert(arguments.All(arg => !_pendingArgumentsToReset.Contains(arg))); + } + else + { + // TODO: https://github.com/dotnet/roslyn-analyzers/issues/1810 + // Implement Non-context sensitive interprocedural analysis to + // merge the relevant data from invoked method's analysis result into CurrentAnalysisData. + // For now, retain the original logic of resetting the analysis data. + ResetAnalysisData(hasEscapedLambdaOrLocalFunctions: false); + } + } + finally + { + // Remove the operation from interprocedural call stack. + var popped = _interproceduralCallStack.Pop(); + Debug.Assert(popped == originalOperation); + + interproceduralAnalysisData?.InitialAnalysisData?.Dispose(); + } + + RoslynDebug.Assert(invokedMethod.ReturnsVoid == !analysisResult.ReturnValueAndPredicateKind.HasValue); + if (invokedMethod.ReturnsVoid) + { + return defaultValue; + } + + RoslynDebug.Assert(analysisResult.ReturnValueAndPredicateKind != null); + + if (PredicateAnalysis) + { + SetPredicateValueKind(originalOperation, CurrentAnalysisData, analysisResult.ReturnValueAndPredicateKind.Value.PredicateValueKind); + } + + return analysisResult.ReturnValueAndPredicateKind.Value.Value; + + // Local functions + TAbstractAnalysisValue ResetAnalysisDataAndReturnDefaultValue() + { + var hasEscapes = MarkEscapedLambdasAndLocalFunctionsFromArguments(); + ResetAnalysisData(hasEscapes); + return defaultValue; + } + + void ResetAnalysisData(bool hasEscapedLambdaOrLocalFunctions) + { + // Interprocedural analysis did not succeed, so we need to conservatively reset relevant analysis data. + if (!PessimisticAnalysis) + { + // We are performing an optimistic analysis, so we should not reset any data. + return; + } + + if (isLambdaOrLocalFunction || hasEscapedLambdaOrLocalFunctions) + { + // For local/lambda cases, we reset all analysis data. + ResetCurrentAnalysisData(); + } + else + { + // For regular invocation cases, we reset instance analysis data and argument data. + // Note that arguments are reset later by processing '_pendingArgumentsToReset'. + ResetInstanceAnalysisData(instanceReceiver); + Debug.Assert(arguments.All(_pendingArgumentsToReset.Contains)); + } + } + + bool MarkEscapedLambdasAndLocalFunctionsFromArguments() + { + var hasEscapes = false; + foreach (var argument in arguments) + { + if (argument.Parameter?.Type.TypeKind == TypeKind.Delegate || + argument.Parameter?.Type.SpecialType == SpecialType.System_Object) + { + if (!IsPointsToAnalysis) + { + // For non-points to analysis, pessimistically assume delegate arguments + // lead to escaped lambda or local function target which may get invoked. + if (argument.Parameter.Type.TypeKind == TypeKind.Delegate) + { + hasEscapes = true; + break; + } + } + else + { + // For points to analysis, we try to compute the target lambda or local function + // to determine if we have an escape. + var pointsToValue = GetPointsToAbstractValue(argument); + if (MarkEscapedLambdasAndLocalFunctions(pointsToValue)) + hasEscapes = true; + } + } + } + + return hasEscapes; + } + + InterproceduralAnalysisData ComputeInterproceduralAnalysisData() + { + RoslynDebug.Assert(cfg != null); + + var invocationInstance = GetInvocationInstance(); + var thisOrMeInstance = GetThisOrMeInstance(); + var argumentValuesMap = GetArgumentValues(ref invocationInstance); + var pointsToValues = pointsToAnalysisResult?[cfg.GetEntry()].Data; + var copyValues = copyAnalysisResult?[cfg.GetEntry()].Data; + var valueContentValues = valueContentAnalysisResult?[cfg.GetEntry()].Data; + var initialAnalysisData = GetInitialInterproceduralAnalysisData(invokedMethod, invocationInstance, + thisOrMeInstance, argumentValuesMap, pointsToValues, copyValues, valueContentValues, isLambdaOrLocalFunction, hasParameterWithDelegateType); + + return new InterproceduralAnalysisData( + initialAnalysisData, + invocationInstance, + thisOrMeInstance, + argumentValuesMap, + GetCapturedVariablesMap(cfg, invokedMethod, isLambdaOrLocalFunction), + _addressSharedEntitiesProvider.GetAddressedSharedEntityMap(), + ImmutableStack.CreateRange(_interproceduralCallStack), + newMethodsBeingAnalyzed, + getCachedAbstractValueFromCaller: GetCachedAbstractValue, + getInterproceduralControlFlowGraph: GetInterproceduralControlFlowGraph, + getAnalysisEntityForFlowCapture: GetAnalysisEntityForFlowCapture, + getInterproceduralCallStackForOwningSymbol: GetInterproceduralCallStackForOwningSymbol); + + // Local functions. + (AnalysisEntity?, PointsToAbstractValue)? GetInvocationInstance() + { + if (isLambdaOrLocalFunction) + { + return (AnalysisEntityFactory.ThisOrMeInstance, ThisOrMePointsToAbstractValue); + } + else if (instanceReceiver != null) + { + if (!AnalysisEntityFactory.TryCreate(instanceReceiver, out var receiverAnalysisEntityOpt)) + { + receiverAnalysisEntityOpt = null; + } + + var instancePointsToValue = GetPointsToAbstractValue(instanceReceiver); + if (instancePointsToValue.Kind == PointsToAbstractValueKind.Undefined) + { + // Error case: Invocation through an uninitialized local. + // Use Unknown PointsTo value for interprocedural analysis. + instancePointsToValue = PointsToAbstractValue.Unknown; + } + + return (receiverAnalysisEntityOpt, instancePointsToValue); + } + else if (invokedMethod.MethodKind == MethodKind.Constructor) + { + // We currently only support interprocedural constructor analysis for object creation operations. + Debug.Assert(originalOperation.Kind == OperationKind.ObjectCreation); + + // Pass in the allocation location as interprocedural instance. + // There is no analysis entity for the allocation. + var instancePointsToValue = GetPointsToAbstractValue(originalOperation); + return (null, instancePointsToValue); + } + + return null; + } + + (AnalysisEntity, PointsToAbstractValue)? GetThisOrMeInstance() + => (AnalysisEntityFactory.ThisOrMeInstance, ThisOrMePointsToAbstractValue); + + ImmutableDictionary> GetArgumentValues(ref (AnalysisEntity? entity, PointsToAbstractValue pointsToValue)? invocationInstance) + { + var builder = PooledDictionary>.GetInstance(); + var isExtensionMethodInvocationWithOneLessArgument = invokedMethod.IsExtensionMethod && arguments.Length == invokedMethod.Parameters.Length - 1; + + if (isExtensionMethodInvocationWithOneLessArgument) + { + var extraArgument = new ArgumentInfo( + operation: instanceReceiver ?? originalOperation, + analysisEntity: invocationInstance?.entity, + instanceLocation: invocationInstance?.pointsToValue ?? PointsToAbstractValue.Unknown, + value: instanceReceiver != null ? GetCachedAbstractValue(instanceReceiver) : ValueDomain.UnknownOrMayBeValue); + builder.Add(invokedMethod.Parameters[0], extraArgument); + invocationInstance = null; + } + else + { + Debug.Assert(arguments.Length == invokedMethod.Parameters.Length); + } + + foreach (var argument in arguments) + { + PointsToAbstractValue instanceLocation; + if (AnalysisEntityFactory.TryCreate(argument, out var argumentEntity)) + { + instanceLocation = argumentEntity.InstanceLocation; + } + else + { + // For allocations, such as "new A()", which have no associated entity, but a valid PointsTo value. + instanceLocation = GetPointsToAbstractValue(argument); + argumentEntity = null; + } + + var argumentValue = GetCachedAbstractValue(argument); + if (ReferenceEquals(argumentValue, ValueDomain.Bottom)) + { + argumentValue = ValueDomain.UnknownOrMayBeValue; + } + + if (argument.Parameter != null) + { + builder.Add(GetMappedParameterForArgument(argument), new ArgumentInfo(argument, argumentEntity, instanceLocation, argumentValue)); + } + + _pendingArgumentsToReset.Remove(argument); + } + + return builder.ToImmutableDictionaryAndFree(); + + // Local function + IParameterSymbol GetMappedParameterForArgument(IArgumentOperation argumentOperation) + { + if (argumentOperation.Parameter!.ContainingSymbol is IMethodSymbol method && + method.MethodKind == MethodKind.DelegateInvoke) + { + // Parameter associated with IArgumentOperation for delegate invocations + // is the DelegateInvoke method parameter. + // So we need to map it to the parameter of the invoked method by using ordinals. + Debug.Assert(invokedMethod.Parameters.Length == method.GetParameters().Length || + isExtensionMethodInvocationWithOneLessArgument); + + var ordinal = argumentOperation.Parameter.Ordinal; + if (isExtensionMethodInvocationWithOneLessArgument) + { + ordinal++; + } + + return invokedMethod.Parameters[ordinal].OriginalDefinition; + } + + return argumentOperation.Parameter.OriginalDefinition; + } + } + + AnalysisEntity? GetAnalysisEntityForFlowCapture(IOperation operation) + { + switch (operation.Kind) + { + case OperationKind.FlowCapture: + case OperationKind.FlowCaptureReference: + if (AnalysisEntityFactory.TryGetForInterproceduralAnalysis(operation, out var analysisEntity) && + originalOperation.Descendants().Contains(operation)) + { + return analysisEntity; + } + + break; + } + + return null; + } + } + } + + private ImmutableDictionary GetCapturedVariablesMap( + ControlFlowGraph cfg, + IMethodSymbol invokedMethod, + bool isLambdaOrLocalFunction) + { + RoslynDebug.Assert(cfg != null); + + if (!isLambdaOrLocalFunction) + { + return ImmutableDictionary.Empty; + } + + using var capturedVariables = cfg.OriginalOperation.GetCaptures(invokedMethod); + if (capturedVariables.Count == 0) + { + return ImmutableDictionary.Empty; + } + else + { + var builder = ImmutableDictionary.CreateBuilder(); + foreach (var capturedVariable in capturedVariables) + { + if (capturedVariable.Kind == SymbolKind.NamedType) + { + // ThisOrMeInstance capture can be skipped here + // as we already pass down the invocation instance through "GetInvocationInstance". + continue; + } + + var success = AnalysisEntityFactory.TryCreateForSymbolDeclaration(capturedVariable, out var capturedEntity); + Debug.Assert(success); + RoslynDebug.Assert(capturedEntity != null); + builder.Add(capturedVariable, capturedEntity.InstanceLocation); + } + + return builder.ToImmutable(); + } + } + + private void PerformStandaloneLocalFunctionInterproceduralAnalysis(IMethodSymbol localFunction) + { + Debug.Assert(IsStandaloneAnalysisRequiredForLocalFunction(localFunction)); + + // Compute the dependent interprocedural PointsTo and Copy analysis results, if any. + var pointsToAnalysisResult = (PointsToAnalysisResult?)DataFlowAnalysisContext.PointsToAnalysisResult?.TryGetStandaloneLocalFunctionAnalysisResult(localFunction); + var copyAnalysisResult = DataFlowAnalysisContext.CopyAnalysisResult?.TryGetStandaloneLocalFunctionAnalysisResult(localFunction); + var valueContentAnalysisResult = DataFlowAnalysisContext.ValueContentAnalysisResult?.TryGetStandaloneLocalFunctionAnalysisResult(localFunction); + + // Compute the CFG for the invoked method. + var cfg = pointsToAnalysisResult?.ControlFlowGraph ?? + copyAnalysisResult?.ControlFlowGraph ?? + valueContentAnalysisResult?.ControlFlowGraph ?? + DataFlowAnalysisContext.GetLocalFunctionControlFlowGraph(localFunction); + if (cfg == null || !cfg.SupportsFlowAnalysis()) + { + return; + } + + // Ensure we are using the same control flow graphs across analyses. + Debug.Assert(pointsToAnalysisResult?.ControlFlowGraph == null || cfg == pointsToAnalysisResult?.ControlFlowGraph); + Debug.Assert(copyAnalysisResult?.ControlFlowGraph == null || cfg == copyAnalysisResult?.ControlFlowGraph); + Debug.Assert(valueContentAnalysisResult?.ControlFlowGraph == null || cfg == valueContentAnalysisResult?.ControlFlowGraph); + + // Push a dummy operation for standalone local function analysis. + _interproceduralCallStack.Push(DataFlowAnalysisContext.ControlFlowGraph.OriginalOperation); + + try + { + var interproceduralAnalysisData = GetInterproceduralAnalysisDataForStandaloneLambdaOrLocalFunctionAnalysis(cfg, localFunction); + + // Create analysis context for interprocedural analysis. + var interproceduralDataFlowAnalysisContext = DataFlowAnalysisContext.ForkForInterproceduralAnalysis( + localFunction, cfg, pointsToAnalysisResult, copyAnalysisResult, valueContentAnalysisResult, interproceduralAnalysisData); + + // Execute interprocedural analysis and get result. + var analysisResult = TryGetOrComputeAnalysisResult(interproceduralDataFlowAnalysisContext); + if (analysisResult != null) + { + // Save the interprocedural result for the local function. + // Note that we Update instead of invoking .Add as we may execute the analysis multiple times for fixed point computation. + _standaloneLocalFunctionAnalysisResultsBuilder[localFunction] = analysisResult; + } + } + finally + { + _interproceduralCallStack.Pop(); + } + } + + private void PerformStandaloneLambdaInterproceduralAnalysis(IFlowAnonymousFunctionOperation lambda) + { + Debug.Assert(IsStandaloneAnalysisRequiredForLambda(lambda)); + + // Compute the dependent interprocedural PointsTo and Copy analysis results, if any. + var pointsToAnalysisResult = (PointsToAnalysisResult?)DataFlowAnalysisContext.PointsToAnalysisResult?.TryGetInterproceduralResult(lambda); + var copyAnalysisResult = DataFlowAnalysisContext.CopyAnalysisResult?.TryGetInterproceduralResult(lambda); + var valueContentAnalysisResult = DataFlowAnalysisContext.ValueContentAnalysisResult?.TryGetInterproceduralResult(lambda); + + // Compute the CFG for the invoked method. + var cfg = pointsToAnalysisResult?.ControlFlowGraph ?? + copyAnalysisResult?.ControlFlowGraph ?? + valueContentAnalysisResult?.ControlFlowGraph ?? + DataFlowAnalysisContext.GetAnonymousFunctionControlFlowGraph(lambda); + if (cfg == null || !cfg.SupportsFlowAnalysis()) + { + return; + } + + // Ensure we are using the same control flow graphs across analyses. + Debug.Assert(pointsToAnalysisResult?.ControlFlowGraph == null || cfg == pointsToAnalysisResult?.ControlFlowGraph); + Debug.Assert(copyAnalysisResult?.ControlFlowGraph == null || cfg == copyAnalysisResult?.ControlFlowGraph); + Debug.Assert(valueContentAnalysisResult?.ControlFlowGraph == null || cfg == valueContentAnalysisResult?.ControlFlowGraph); + + _interproceduralCallStack.Push(lambda); + + try + { + var interproceduralAnalysisData = GetInterproceduralAnalysisDataForStandaloneLambdaOrLocalFunctionAnalysis(cfg, lambda.Symbol); + + // Create analysis context for interprocedural analysis. + var interproceduralDataFlowAnalysisContext = DataFlowAnalysisContext.ForkForInterproceduralAnalysis( + lambda.Symbol, cfg, pointsToAnalysisResult, copyAnalysisResult, valueContentAnalysisResult, interproceduralAnalysisData); + + // Execute interprocedural analysis and get result. + var analysisResult = TryGetOrComputeAnalysisResult(interproceduralDataFlowAnalysisContext); + if (analysisResult != null) + { + // Save the interprocedural result for the lambda operation. + // Note that we Update instead of invoking .Add as we may execute the analysis multiple times for fixed point computation. + _interproceduralResultsBuilder[lambda] = analysisResult; + } + } + finally + { + _interproceduralCallStack.Pop(); + } + } + + private InterproceduralAnalysisData GetInterproceduralAnalysisDataForStandaloneLambdaOrLocalFunctionAnalysis( + ControlFlowGraph cfg, + IMethodSymbol invokedMethod) + { + var invocationInstance = (AnalysisEntityFactory.ThisOrMeInstance, ThisOrMePointsToAbstractValue); + var thisOrMeInstance = invocationInstance; + var currentMethodsBeingAnalyzed = DataFlowAnalysisContext.InterproceduralAnalysisData?.MethodsBeingAnalyzed ?? ImmutableHashSet.Empty; + var newMethodsBeingAnalyzed = currentMethodsBeingAnalyzed.Add(DataFlowAnalysisContext); + + return new InterproceduralAnalysisData( + initialAnalysisData: null, + invocationInstance, + thisOrMeInstance, + argumentValuesMap: ImmutableDictionary>.Empty, + GetCapturedVariablesMap(cfg, invokedMethod, isLambdaOrLocalFunction: true), + addressSharedEntities: ImmutableDictionary.Empty, + ImmutableStack.CreateRange(_interproceduralCallStack), + newMethodsBeingAnalyzed, + getCachedAbstractValueFromCaller: _ => ValueDomain.UnknownOrMayBeValue, + getInterproceduralControlFlowGraph: GetInterproceduralControlFlowGraph, + getAnalysisEntityForFlowCapture: _ => null, + getInterproceduralCallStackForOwningSymbol: GetInterproceduralCallStackForOwningSymbol); + } + + #endregion + + #region Visitor methods + + protected TAbstractAnalysisValue VisitArray(IEnumerable operations, object? argument) + { + foreach (var operation in operations) + { + _ = Visit(operation, argument); + } + + return ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue Visit(IOperation? operation, object? argument) + { + if (operation != null) + { + var value = VisitCore(operation, argument); + CacheAbstractValue(operation, value); + + if (ExecutingExceptionPathsAnalysisPostPass) + { + HandlePossibleThrowingOperation(operation); + } + + var pendingArguments = _pendingArgumentsToPostProcess.ExtractAll(static (arg, operation) => arg.Parent == operation, operation); + foreach (IArgumentOperation argumentOperation in pendingArguments) + { + bool isEscaped; + if (_pendingArgumentsToReset.Remove(argumentOperation)) + { + PostProcessEscapedArgument(argumentOperation); + isEscaped = true; + } + else + { + isEscaped = false; + } + + PostProcessArgument(argumentOperation, isEscaped); + } + + return value; + } + + return ValueDomain.UnknownOrMayBeValue; + } + + private TAbstractAnalysisValue VisitCore(IOperation operation, object? argument) + { + if (operation.Kind == OperationKind.None) + { + return DefaultVisit(operation, argument); + } + + _recursionDepth++; + try + { + StackGuard.EnsureSufficientExecutionStack(_recursionDepth); + return operation.Accept(this, argument!)!; + } + finally + { + _recursionDepth--; + } + } + + public override TAbstractAnalysisValue DefaultVisit(IOperation operation, object? argument) + { + return VisitArray(operation.Children, argument); + } + + public override TAbstractAnalysisValue VisitSimpleAssignment(ISimpleAssignmentOperation operation, object? argument) + { + return VisitAssignmentOperation(operation, argument); + } + + public override TAbstractAnalysisValue VisitCompoundAssignment(ICompoundAssignmentOperation operation, object? argument) + { + TAbstractAnalysisValue targetValue = Visit(operation.Target, argument); + TAbstractAnalysisValue assignedValue = Visit(operation.Value, argument); + var value = ComputeValueForCompoundAssignment(operation, targetValue, assignedValue, operation.Target.Type, operation.Value.Type); + if (operation.Target is IFlowCaptureReferenceOperation flowCaptureReference) + { + HandleFlowCaptureReferenceAssignment(flowCaptureReference, operation.Value, value); + } + else + { + SetAbstractValueForAssignment(operation.Target, operation.Value, value); + } + + return value; + } + + public virtual TAbstractAnalysisValue ComputeValueForCompoundAssignment( + ICompoundAssignmentOperation operation, + TAbstractAnalysisValue targetValue, + TAbstractAnalysisValue assignedValue, + ITypeSymbol? targetType, + ITypeSymbol? assignedValueType) + { + return ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue VisitIncrementOrDecrement(IIncrementOrDecrementOperation operation, object? argument) + { + TAbstractAnalysisValue targetValue = Visit(operation.Target, argument); + var value = ComputeValueForIncrementOrDecrementOperation(operation, targetValue); + SetAbstractValueForAssignment(operation.Target, assignedValueOperation: null, assignedValue: value); + return value; + } + + public virtual TAbstractAnalysisValue ComputeValueForIncrementOrDecrementOperation(IIncrementOrDecrementOperation operation, TAbstractAnalysisValue targetValue) + { + return ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue VisitDeconstructionAssignment(IDeconstructionAssignmentOperation operation, object? argument) + { + return VisitAssignmentOperation(operation, argument); + } + + protected virtual TAbstractAnalysisValue VisitAssignmentOperation(IAssignmentOperation operation, object? argument) + { + _ = Visit(operation.Target, argument); + TAbstractAnalysisValue assignedValue = Visit(operation.Value, argument); + + if (operation.Target is IFlowCaptureReferenceOperation flowCaptureReference) + { + HandleFlowCaptureReferenceAssignment(flowCaptureReference, operation.Value, assignedValue); + } + else + { + SetAbstractValueForAssignment(operation.Target, operation.Value, assignedValue); + } + + return assignedValue; + } + + public override TAbstractAnalysisValue VisitArrayInitializer(IArrayInitializerOperation operation, object? argument) + { + var arrayCreation = operation.GetAncestor(OperationKind.ArrayCreation); + if (arrayCreation != null) + { + var elementType = ((IArrayTypeSymbol)arrayCreation.Type!).ElementType; + for (int index = 0; index < operation.ElementValues.Length; index++) + { + var abstractIndex = AbstractIndex.Create(index); + IOperation elementInitializer = operation.ElementValues[index]; + TAbstractAnalysisValue initializerValue = Visit(elementInitializer, argument); + SetAbstractValueForArrayElementInitializer(arrayCreation, ImmutableArray.Create(abstractIndex), elementType, elementInitializer, initializerValue); + } + } + else + { + _ = base.VisitArrayInitializer(operation, argument); + } + + return ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue VisitLocalReference(ILocalReferenceOperation operation, object? argument) + { + var value = base.VisitLocalReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitParameterReference(IParameterReferenceOperation operation, object? argument) + { + var value = base.VisitParameterReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitArrayElementReference(IArrayElementReferenceOperation operation, object? argument) + { + var value = base.VisitArrayElementReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitDynamicMemberReference(IDynamicMemberReferenceOperation operation, object? argument) + { + var value = base.VisitDynamicMemberReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitEventReference(IEventReferenceOperation operation, object? argument) + { + var value = base.VisitEventReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitFieldReference(IFieldReferenceOperation operation, object? argument) + { + var value = base.VisitFieldReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitMethodReference(IMethodReferenceOperation operation, object? argument) + { + var value = base.VisitMethodReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitPropertyReference(IPropertyReferenceOperation operation, object? argument) + { + var value = base.VisitPropertyReference(operation, argument)!; + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + + public override TAbstractAnalysisValue VisitFlowCaptureReference(IFlowCaptureReferenceOperation operation, object? argument) + { + var value = base.VisitFlowCaptureReference(operation, argument)!; + if (!IsLValueFlowCaptureReference(operation)) + { + if (_lValueFlowCaptures.Contains(operation.Id)) + { + // Special flow capture reference operation where the corresponding flow capture + // is an LValue flow capture but the flow capture reference is not lvalue capture reference. + var flowCaptureForCaptureId = DataFlowAnalysisContext.ControlFlowGraph + .DescendantOperations(OperationKind.FlowCapture) + .FirstOrDefault(fc => fc.Id.Equals(operation.Id)); + if (flowCaptureForCaptureId != null) + { + return GetCachedAbstractValue(flowCaptureForCaptureId.Value); + } + } + else + { + PerformFlowCaptureReferencePredicateAnalysis(); + return ComputeAnalysisValueForReferenceOperation(operation, value); + } + } + + return ValueDomain.UnknownOrMayBeValue; + + void PerformFlowCaptureReferencePredicateAnalysis() + { + if (!PredicateAnalysis) + { + return; + } + + var result = AnalysisEntityFactory.TryCreate(operation, out var flowCaptureReferenceEntity); + Debug.Assert(result); + RoslynDebug.Assert(flowCaptureReferenceEntity != null); + RoslynDebug.Assert(flowCaptureReferenceEntity.CaptureId != null); + if (!HasPredicatedDataForEntity(flowCaptureReferenceEntity)) + { + return; + } + + PerformPredicateAnalysis(operation); + Debug.Assert(HasPredicatedDataForEntity(flowCaptureReferenceEntity)); + } + } + + public override TAbstractAnalysisValue VisitFlowCapture(IFlowCaptureOperation operation, object? argument) + { + var value = Visit(operation.Value, argument); + if (!IsLValueFlowCapture(operation)) + { + SetAbstractValueForAssignment(target: operation, assignedValueOperation: operation.Value, assignedValue: value); + PerformFlowCapturePredicateAnalysis(); + return value; + } + + return ValueDomain.UnknownOrMayBeValue; + + void PerformFlowCapturePredicateAnalysis() + { + if (!PredicateAnalysis) + { + return; + } + + if (operation.Value.TryGetBoolConstantValue(out bool constantValue) && + AnalysisEntityFactory.TryCreate(operation, out var flowCaptureEntity)) + { + Debug.Assert(flowCaptureEntity.CaptureId != null); + TAnalysisData predicatedData = GetEmptyAnalysisData(); + TAnalysisData? truePredicatedData, falsePredicatedData; + if (constantValue) + { + truePredicatedData = predicatedData; + falsePredicatedData = null; + } + else + { + falsePredicatedData = predicatedData; + truePredicatedData = null; + } + + StartTrackingPredicatedData(flowCaptureEntity, truePredicatedData, falsePredicatedData); + } + } + } + + public override TAbstractAnalysisValue VisitDefaultValue(IDefaultValueOperation operation, object? argument) + { + return GetAbstractDefaultValue(operation.Type); + } + + public override TAbstractAnalysisValue VisitInterpolation(IInterpolationOperation operation, object? argument) + { + var expressionValue = Visit(operation.Expression, argument); + _ = Visit(operation.FormatString, argument); + _ = Visit(operation.Alignment, argument); + return expressionValue; + } + + public override TAbstractAnalysisValue VisitInterpolatedStringText(IInterpolatedStringTextOperation operation, object? argument) + { + return Visit(operation.Text, argument); + } + + public sealed override TAbstractAnalysisValue VisitArgument(IArgumentOperation operation, object? argument) + { + var value = Visit(operation.Value, argument); + + // Is first argument of a Contract check invocation? + if (PredicateAnalysis && IsContractCheckArgument(operation)) + { + Debug.Assert(FlowBranchConditionKind == ControlFlowConditionKind.None); + + // Force true branch. + FlowBranchConditionKind = ControlFlowConditionKind.WhenTrue; + PerformPredicateAnalysis(operation); + FlowBranchConditionKind = ControlFlowConditionKind.None; + } + + _pendingArgumentsToPostProcess.Add(operation); + _pendingArgumentsToReset.Add(operation); + return value; + } + + /// + /// Invoked after the parent invocation/creation operation of the given argument has been visited. + /// + /// Argument to be post-processed. + /// Boolean flag indicating if the argument was escaped due to lack of interprocedural analysis or not. + protected virtual void PostProcessArgument(IArgumentOperation operation, bool isEscaped) + { + } + + /// + /// Post process argument which needs to be escaped/reset after being passed to an invocation/creation target + /// without interprocedural analysis. + /// This method resets the analysis data for an object instance passed around as an + /// and also handles resetting the argument value for ref/out parameter. + /// + private void PostProcessEscapedArgument(IArgumentOperation operation) + { + // For reference types passed as arguments, + // reset all analysis data for the instance members as the content might change for them. + if (HasPointsToAnalysisResult && + PessimisticAnalysis && + operation.Value.Type != null && + !operation.Value.Type.HasValueCopySemantics()) + { + ResetReferenceTypeInstanceAnalysisData(operation.Value); + } + + // Handle ref/out arguments as escapes. + if (operation.Parameter?.RefKind is RefKind.Ref or RefKind.Out) + { + var value = ComputeAnalysisValueForEscapedRefOrOutArgument(operation, defaultValue: ValueDomain.UnknownOrMayBeValue); + if (operation.Parameter.RefKind != RefKind.Out) + { + value = ValueDomain.Merge(value, GetCachedAbstractValue(operation.Value)); + } + + CacheAbstractValue(operation, value); + SetAbstractValueForAssignment(operation.Value, operation, value); + } + } + + public override TAbstractAnalysisValue VisitConstantPattern(IConstantPatternOperation operation, object? argument) + { + return Visit(operation.Value, argument); + } + + public override TAbstractAnalysisValue VisitParenthesized(IParenthesizedOperation operation, object? argument) + { + return Visit(operation.Operand, argument); + } + + public override TAbstractAnalysisValue VisitTranslatedQuery(ITranslatedQueryOperation operation, object? argument) + { + return Visit(operation.Operation, argument); + } + + public override TAbstractAnalysisValue VisitConversion(IConversionOperation operation, object? argument) + { + var operandValue = Visit(operation.Operand, argument); + + // Conservative for error code and user defined operator. + return operation.Conversion.Exists && !operation.Conversion.IsUserDefined ? operandValue : ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue VisitObjectCreation(IObjectCreationOperation operation, object? argument) + { + Debug.Assert(operation.Initializer == null, "Object or collection initializer must have been lowered in the CFG"); + + var defaultValue = base.VisitObjectCreation(operation, argument)!; + + var method = operation.Constructor!; + ControlFlowGraph? getCfg() => GetInterproceduralControlFlowGraph(method); + + return PerformInterproceduralAnalysis(getCfg, method, instanceReceiver: null, + operation.Arguments, operation, defaultValue, isLambdaOrLocalFunction: false, out _); + } + + public sealed override TAbstractAnalysisValue VisitInvocation(IInvocationOperation operation, object? argument) + { + TAbstractAnalysisValue value; + if (operation.TargetMethod.IsLambdaOrLocalFunctionOrDelegate()) + { + // Invocation of a lambda or delegate or local function. + value = VisitInvocation_LambdaOrDelegateOrLocalFunction(operation, argument, out var resolvedMethodTargetsOpt); + CacheAbstractValue(operation, value); + + // Check if we have known possible set of invoked methods. + if (resolvedMethodTargetsOpt != null) + { + foreach ((IMethodSymbol method, _) in resolvedMethodTargetsOpt) + { + PostVisitInvocation(method, operation.Arguments); + } + } + } + else + { + value = VisitInvocation_NonLambdaOrDelegateOrLocalFunction(operation, argument); + CacheAbstractValue(operation, value); + + switch (operation.Arguments.Length) + { + case 1: + if (operation.Instance != null && operation.TargetMethod.IsTaskConfigureAwaitMethod(GenericTaskNamedType)) + { + // ConfigureAwait invocation - just return the abstract value of the visited instance on which it is invoked. + value = GetCachedAbstractValue(operation.Instance); + } + else if (operation.TargetMethod.IsTaskFromResultMethod(TaskNamedType)) + { + // Result wrapped within a task. + var wrappedOperationValue = GetCachedAbstractValue(operation.Arguments[0].Value); + var pointsToValueOfTask = GetPointsToAbstractValue(operation); + SetTaskWrappedValue(pointsToValueOfTask, wrappedOperationValue); + } + + break; + + case 2: + if (operation.Instance == null && + operation.TargetMethod.IsAsyncDisposableConfigureAwaitMethod(IAsyncDisposableNamedType, TaskAsyncEnumerableExtensions)) + { + // ConfigureAwait invocation - just return the abstract value of the visited instance on which it is invoked. + value = GetCachedAbstractValue(operation.Arguments.GetArgumentForParameterAtIndex(0)); + } + + break; + } + + PostVisitInvocation(operation.TargetMethod, operation.Arguments); + } + + return value; + + // Local functions. + void PostVisitInvocation(IMethodSymbol targetMethod, ImmutableArray arguments) + { + // Predicate analysis for different equality compare method invocations. + if (PredicateAnalysis && + operation.Type?.SpecialType == SpecialType.System_Boolean && + (targetMethod.Name.EndsWith("Equals", StringComparison.Ordinal) || + targetMethod.IsArgumentNullCheckMethod())) + { + PerformPredicateAnalysis(operation); + } + + if (targetMethod.IsLockMethod(MonitorNamedType)) + { + // "System.Threading.Monitor.Enter(object)" OR "System.Threading.Monitor.Enter(object, bool)" + Debug.Assert(!arguments.IsEmpty); + + HandleEnterLockOperation(arguments[0].Value); + } + else if (InterlockedNamedType != null && + SymbolEqualityComparer.Default.Equals(targetMethod.ContainingType.OriginalDefinition, InterlockedNamedType)) + { + ProcessInterlockedOperation(targetMethod, arguments, InterlockedNamedType); + } + } + + void ProcessInterlockedOperation(IMethodSymbol targetMethod, ImmutableArray arguments, INamedTypeSymbol interlockedType) + { + var isExchangeMethod = targetMethod.IsInterlockedExchangeMethod(interlockedType); + var isCompareExchangeMethod = targetMethod.IsInterlockedCompareExchangeMethod(interlockedType); + + if (isExchangeMethod || isCompareExchangeMethod) + { + // "System.Threading.Interlocked.Exchange(ref T, T)" OR "System.Threading.Interlocked.CompareExchange(ref T, T, T)" + Debug.Assert(arguments.Length >= 2); + + SetAbstractValueForAssignment( + target: arguments[0].Value, + assignedValueOperation: arguments[1].Value, + assignedValue: GetCachedAbstractValue(arguments[1].Value), + mayBeAssignment: isCompareExchangeMethod); + foreach (var argument in arguments) + { + _pendingArgumentsToReset.Remove(argument); + } + } + } + } + + private TAbstractAnalysisValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction(IInvocationOperation operation, object? argument) + { + var value = base.VisitInvocation(operation, argument)!; + return VisitInvocation_NonLambdaOrDelegateOrLocalFunction(operation.TargetMethod, operation.Instance, operation.Arguments, + invokedAsDelegate: false, originalOperation: operation, defaultValue: value); + } + + private protected bool MarkEscapedLambdasAndLocalFunctions(PointsToAbstractValue pointsToAbstractValue) + { + Debug.Assert(IsPointsToAnalysis); + + var hasEscapes = false; + using var methodTargetsOptBuilder = PooledHashSet<(IMethodSymbol method, IOperation? instance)>.GetInstance(); + using var lambdaTargets = PooledHashSet.GetInstance(); + if (ResolveLambdaOrDelegateOrLocalFunctionTargets(pointsToAbstractValue, methodTargetsOptBuilder, lambdaTargets)) + { + foreach (var (targetMethod, _) in methodTargetsOptBuilder) + { + if (targetMethod.MethodKind == MethodKind.LocalFunction) + { + _escapedLocalFunctions.Add(targetMethod); + hasEscapes = true; + } + } + + foreach (var flowAnonymousFunctionOperation in lambdaTargets) + { + _escapedLambdas.Add(flowAnonymousFunctionOperation); + hasEscapes = true; + } + } + + return hasEscapes; + } + + private bool ResolveLambdaOrDelegateOrLocalFunctionTargets( + PointsToAbstractValue invocationTarget, + PooledHashSet<(IMethodSymbol method, IOperation? instance)> methodTargetsOptBuilder, + PooledHashSet lambdaTargets) + => ResolveLambdaOrDelegateOrLocalFunctionTargetsCore(operation: null, invocationTarget, methodTargetsOptBuilder, lambdaTargets); + + private bool ResolveLambdaOrDelegateOrLocalFunctionTargets( + IOperation operation, + PooledHashSet<(IMethodSymbol method, IOperation? instance)> methodTargetsOptBuilder, + PooledHashSet lambdaTargets) + => ResolveLambdaOrDelegateOrLocalFunctionTargetsCore(operation, invocationTarget: null, methodTargetsOptBuilder, lambdaTargets); + + private bool ResolveLambdaOrDelegateOrLocalFunctionTargetsCore( + IOperation? operation, + PointsToAbstractValue? invocationTarget, + PooledHashSet<(IMethodSymbol method, IOperation? instance)> methodTargetsOptBuilder, + PooledHashSet lambdaTargets) + { + Debug.Assert(operation != null ^ invocationTarget != null); + + var knownTargetInvocations = false; + IOperation? instance; + if (operation is IInvocationOperation invocation) + { + instance = invocation.Instance; + + var targetMethod = invocation.TargetMethod; + if (targetMethod.MethodKind == MethodKind.LocalFunction) + { + Debug.Assert(invocation.Instance == null); + + knownTargetInvocations = true; + AddMethodTarget(targetMethod, instance: null); + return knownTargetInvocations; + } + else + { + Debug.Assert(targetMethod.MethodKind is MethodKind.LambdaMethod or + MethodKind.DelegateInvoke); + } + } + else + { + instance = operation; + } + + if (invocationTarget == null && + HasPointsToAnalysisResult && + instance != null) + { + invocationTarget = GetPointsToAbstractValue(instance); + } + + if (invocationTarget?.Kind == PointsToAbstractValueKind.KnownLocations) + { + knownTargetInvocations = true; + foreach (var location in invocationTarget.Locations) + { + if (!HandleCreationOpt(location.Creation)) + { + knownTargetInvocations = false; + break; + } + } + } + + return knownTargetInvocations; + + // Local functions. + void AddMethodTarget(IMethodSymbol method, IOperation? instance) + { + Debug.Assert(knownTargetInvocations); + + methodTargetsOptBuilder.Add((method, instance)); + } + + void AddLambdaTarget(IFlowAnonymousFunctionOperation lambda) + { + Debug.Assert(knownTargetInvocations); + + lambdaTargets.Add(lambda); + } + + bool HandleCreationOpt(IOperation? creation) + { + Debug.Assert(knownTargetInvocations); + + switch (creation) + { + case ILocalFunctionOperation localFunctionOperation: + AddMethodTarget(localFunctionOperation.Symbol, instance: null); + return true; + + case IMethodReferenceOperation methodReferenceOperation: + AddMethodTarget(methodReferenceOperation.Method, methodReferenceOperation.Instance); + return true; + + case IDelegateCreationOperation delegateCreationOperation: + return HandleDelegateCreationTarget(delegateCreationOperation); + + default: + return false; + } + } + + bool HandleDelegateCreationTarget(IDelegateCreationOperation delegateCreationOperation) + { + Debug.Assert(knownTargetInvocations); + + switch (delegateCreationOperation.Target) + { + case IFlowAnonymousFunctionOperation lambdaOperation: + AddLambdaTarget(lambdaOperation); + return true; + + case IMethodReferenceOperation methodReferenceOperation: + AddMethodTarget(methodReferenceOperation.Method, methodReferenceOperation.Instance); + return true; + + default: + return false; + } + } + } + + private TAbstractAnalysisValue VisitInvocation_LambdaOrDelegateOrLocalFunction( + IInvocationOperation operation, + object? argument, + out ImmutableHashSet<(IMethodSymbol method, IOperation? instance)>? resolvedMethodTargets) + { + var value = base.VisitInvocation(operation, argument)!; + + using var methodTargetsOptBuilder = PooledHashSet<(IMethodSymbol method, IOperation? instance)>.GetInstance(); + using var lambdaTargets = PooledHashSet.GetInstance(); + if (ResolveLambdaOrDelegateOrLocalFunctionTargets(operation, methodTargetsOptBuilder, lambdaTargets)) + { + resolvedMethodTargets = methodTargetsOptBuilder.ToImmutable(); + AnalyzePossibleTargetInvocations(); + } + else + { + resolvedMethodTargets = null; + if (PessimisticAnalysis) + { + ResetCurrentAnalysisData(); + } + } + + return value; + + void AnalyzePossibleTargetInvocations() + { + Debug.Assert(methodTargetsOptBuilder.Count > 0 || lambdaTargets.Count > 0); + + TAnalysisData? mergedCurrentAnalysisData = null; + var first = true; + var defaultValue = value; + + using var savedCurrentAnalysisData = GetClonedCurrentAnalysisData(); + foreach (var (method, instance) in methodTargetsOptBuilder) + { + var oldMergedAnalysisData = mergedCurrentAnalysisData; + mergedCurrentAnalysisData = AnalyzePossibleTargetInvocation( + computeValueForInvocation: () => method.MethodKind == MethodKind.LocalFunction ? + VisitInvocation_LocalFunction(method, operation.Arguments, operation, defaultValue) : + VisitInvocation_NonLambdaOrDelegateOrLocalFunction(method, instance, operation.Arguments, + invokedAsDelegate: true, originalOperation: operation, defaultValue: defaultValue), + inputAnalysisData: savedCurrentAnalysisData, + mergedAnalysisData: mergedCurrentAnalysisData, + first: ref first); + Debug.Assert(!ReferenceEquals(oldMergedAnalysisData, CurrentAnalysisData)); + oldMergedAnalysisData?.Dispose(); + } + + foreach (var lambda in lambdaTargets) + { + var oldMergedAnalysisData = mergedCurrentAnalysisData; + mergedCurrentAnalysisData = AnalyzePossibleTargetInvocation( + computeValueForInvocation: () => VisitInvocation_Lambda(lambda, operation.Arguments, operation, defaultValue), + inputAnalysisData: savedCurrentAnalysisData, + mergedAnalysisData: mergedCurrentAnalysisData, + first: ref first); + Debug.Assert(!ReferenceEquals(oldMergedAnalysisData, CurrentAnalysisData)); + oldMergedAnalysisData?.Dispose(); + } + + Debug.Assert(mergedCurrentAnalysisData == null || ReferenceEquals(mergedCurrentAnalysisData, CurrentAnalysisData)); + } + + TAnalysisData AnalyzePossibleTargetInvocation(Func computeValueForInvocation, TAnalysisData inputAnalysisData, TAnalysisData? mergedAnalysisData, ref bool first) + { + CurrentAnalysisData = GetClonedAnalysisData(inputAnalysisData); + var invocationValue = computeValueForInvocation(); + + if (first) + { + first = false; + value = invocationValue; + } + else + { + RoslynDebug.Assert(mergedAnalysisData != null); + + value = ValueDomain.Merge(value, invocationValue); + var result = MergeAnalysisData(mergedAnalysisData, CurrentAnalysisData); + CurrentAnalysisData.Dispose(); + CurrentAnalysisData = result; + } + + return CurrentAnalysisData; + } + } + + /// + /// Visits an invocation, either as a direct method call, or intermediately through a delegate. + /// + /// Method that is invoked. + /// Instance that the method is invoked on, if any. + /// Arguments to the invoked method. + /// Indicates that invocation is a delegate invocation. + /// Original invocation operation, which may be a delegate invocation. + /// Default abstract value to return. + /// Abstract value of return value. + public virtual TAbstractAnalysisValue VisitInvocation_NonLambdaOrDelegateOrLocalFunction( + IMethodSymbol method, + IOperation? visitedInstance, + ImmutableArray visitedArguments, + bool invokedAsDelegate, + IOperation originalOperation, + TAbstractAnalysisValue defaultValue) + { + ControlFlowGraph? getCfg() => GetInterproceduralControlFlowGraph(method); + + return PerformInterproceduralAnalysis(getCfg, method, visitedInstance, + visitedArguments, originalOperation, defaultValue, isLambdaOrLocalFunction: false, out _); + } + + private ControlFlowGraph? GetInterproceduralControlFlowGraph(IMethodSymbol method) + { + if (DataFlowAnalysisContext.InterproceduralAnalysisData != null) + { + return DataFlowAnalysisContext.InterproceduralAnalysisData.GetInterproceduralControlFlowGraph(method); + } + + RoslynDebug.Assert(_interproceduralMethodToCfgMap != null); + + if (!_interproceduralMethodToCfgMap.TryGetValue(method, out var cfg)) + { + var operation = method.GetTopmostOperationBlock(WellKnownTypeProvider.Compilation); + cfg = operation?.GetEnclosingControlFlowGraph(); + _interproceduralMethodToCfgMap.Add(method, cfg); + } + + return cfg; + } + + private ImmutableStack? GetInterproceduralCallStackForOwningSymbol(ISymbol forOwningSymbol) + { + if (OwningSymbol.Equals(forOwningSymbol)) + { + return DataFlowAnalysisContext.InterproceduralAnalysisData?.CallStack; + } + + return DataFlowAnalysisContext.InterproceduralAnalysisData?.GetInterproceduralCallStackForOwningSymbol(forOwningSymbol); + } + + public virtual TAbstractAnalysisValue VisitInvocation_LocalFunction( + IMethodSymbol localFunction, + ImmutableArray visitedArguments, + IOperation originalOperation, + TAbstractAnalysisValue defaultValue) + { + ControlFlowGraph? getCfg() => DataFlowAnalysisContext.GetLocalFunctionControlFlowGraph(localFunction); + var value = PerformInterproceduralAnalysis(getCfg, localFunction, instanceReceiver: null, arguments: visitedArguments, + originalOperation: originalOperation, defaultValue: defaultValue, isLambdaOrLocalFunction: true, out var wasAnalyzed); + if (wasAnalyzed) + { + Debug.Assert(_interproceduralResultsBuilder.ContainsKey(originalOperation)); + _analyzedLocalFunctions.Add(localFunction); + } + + return value; + } + + public virtual TAbstractAnalysisValue VisitInvocation_Lambda( + IFlowAnonymousFunctionOperation lambda, + ImmutableArray visitedArguments, + IOperation originalOperation, + TAbstractAnalysisValue defaultValue) + { + ControlFlowGraph? getCfg() => DataFlowAnalysisContext.GetAnonymousFunctionControlFlowGraph(lambda); + var value = PerformInterproceduralAnalysis(getCfg, lambda.Symbol, instanceReceiver: null, arguments: visitedArguments, + originalOperation: originalOperation, defaultValue: defaultValue, isLambdaOrLocalFunction: true, out var wasAnalyzed); + if (wasAnalyzed) + { + Debug.Assert(_interproceduralResultsBuilder.ContainsKey(originalOperation)); + _analyzedLambdas.Add(lambda); + } + + return value; + } + + public override TAbstractAnalysisValue VisitDelegateCreation(IDelegateCreationOperation operation, object? argument) + { + var value = base.VisitDelegateCreation(operation, argument)!; + if (!HasPointsToAnalysisResult) + { + switch (operation.Target) + { + case IFlowAnonymousFunctionOperation flowAnonymousFunction: + _escapedLambdas.Add(flowAnonymousFunction); + break; + + case IMethodReferenceOperation methodReference: + if (methodReference.Method.MethodKind == MethodKind.LocalFunction) + { + _escapedLocalFunctions.Add(methodReference.Method); + _escapedLocalFunctions.Add(methodReference.Method); + } + + break; + } + } + + return value; + } + + public virtual void HandleEnterLockOperation(IOperation lockedObject) + { + // Multi-threaded instance method. + // Conservatively reset all the instance analysis data for the ThisOrMeInstance. + ResetThisOrMeInstanceAnalysisData(); + } + + /// + /// Reset all the instance analysis data for if is true and is also true. + /// If we are using or performing points to analysis, certain operations can invalidate all the analysis data off the containing instance. + /// + private void ResetThisOrMeInstanceAnalysisData() + { + if (!HasPointsToAnalysisResult || !PessimisticAnalysis) + { + return; + } + + if (AnalysisEntityFactory.ThisOrMeInstance.Type.HasValueCopySemantics()) + { + ResetValueTypeInstanceAnalysisData(AnalysisEntityFactory.ThisOrMeInstance); + } + else + { + ResetReferenceTypeInstanceAnalysisData(ThisOrMePointsToAbstractValue); + } + } + + public override TAbstractAnalysisValue VisitTuple(ITupleOperation operation, object? argument) + { + using var elementValueBuilder = ArrayBuilder.GetInstance(operation.Elements.Length); + + foreach (var element in operation.Elements) + { + elementValueBuilder.Add(Visit(element, argument)); + } + + // Set abstract value for tuple element/field assignment if the tuple is not target of a deconstruction assignment. + // For deconstruction assignment, the value would be assigned from the computed value for the right side of the assignment. + var deconstructionAncestor = operation.GetAncestor(OperationKind.DeconstructionAssignment); + if (deconstructionAncestor == null || + !deconstructionAncestor.Target.Descendants().Contains(operation)) + { + if (AnalysisEntityFactory.TryCreateForTupleElements(operation, out var elementEntities)) + { + Debug.Assert(elementEntities.Length == elementValueBuilder.Count); + Debug.Assert(elementEntities.Length == operation.Elements.Length); + for (int i = 0; i < elementEntities.Length; i++) + { + var tupleElementEntity = elementEntities[i]; + var assignedValueOperation = operation.Elements[i]; + var assignedValue = elementValueBuilder[i]; + SetAbstractValueForTupleElementAssignment(tupleElementEntity, assignedValueOperation, assignedValue); + } + } + else + { + // Reset data for elements. + foreach (var element in operation.Elements) + { + SetAbstractValueForAssignment(element, operation, ValueDomain.UnknownOrMayBeValue); + } + } + } + + return GetAbstractDefaultValue(operation.Type); + } + + public virtual TAbstractAnalysisValue VisitUnaryOperatorCore(IUnaryOperation operation, object? argument) + { + return Visit(operation.Operand, argument); + } + + public sealed override TAbstractAnalysisValue VisitUnaryOperator(IUnaryOperation operation, object? argument) + { + var value = VisitUnaryOperatorCore(operation, argument); + if (PredicateAnalysis && operation.OperatorKind == UnaryOperatorKind.Not) + { + PerformPredicateAnalysis(operation); + } + + return value; + } + + public virtual TAbstractAnalysisValue VisitBinaryOperatorCore(IBinaryOperation operation, object? argument) + { + return base.VisitBinaryOperator(operation, argument)!; + } + + public sealed override TAbstractAnalysisValue VisitBinaryOperator(IBinaryOperation operation, object? argument) + { + var value = VisitBinaryOperatorCore(operation, argument)!; + if (PredicateAnalysis && operation.IsComparisonOperator()) + { + PerformPredicateAnalysis(operation); + } + + return value; + } + + public override TAbstractAnalysisValue VisitIsNull(IIsNullOperation operation, object? argument) + { + var value = base.VisitIsNull(operation, argument)!; + if (PredicateAnalysis) + { + PerformPredicateAnalysis(operation); + } + + return value; + } + + public override TAbstractAnalysisValue VisitCaughtException(ICaughtExceptionOperation operation, object? argument) + { + // Merge data from unhandled exception paths within try that match the caught exception type. + if (operation.Type != null) + { + MergeAnalysisDataFromUnhandledThrowOperations(operation.Type); + } + + return base.VisitCaughtException(operation, argument)!; + } + + private void MergeAnalysisDataFromUnhandledThrowOperations(ITypeSymbol? caughtExceptionType) + { + Debug.Assert(caughtExceptionType != null || CurrentBasicBlock.IsFirstBlockOfFinally(out _)); + + if (AnalysisDataForUnhandledThrowOperations?.Count > 0) + { + foreach (ThrownExceptionInfo pendingThrow in AnalysisDataForUnhandledThrowOperations.Keys.ToArray()) + { + if (ShouldHandlePendingThrow(pendingThrow)) + { + var previousCurrentAnalysisData = CurrentAnalysisData; + var exceptionData = AnalysisDataForUnhandledThrowOperations[pendingThrow]; + CurrentAnalysisData = MergeAnalysisData(previousCurrentAnalysisData, exceptionData); + AssertValidAnalysisData(CurrentAnalysisData); + previousCurrentAnalysisData.Dispose(); + if (caughtExceptionType != null) + { + AnalysisDataForUnhandledThrowOperations.Remove(pendingThrow); + exceptionData.Dispose(); + } + } + } + } + + bool ShouldHandlePendingThrow(ThrownExceptionInfo pendingThrow) + { + if (pendingThrow.HandlingCatchRegion == CurrentBasicBlock.EnclosingRegion) + { + // Catch region explicitly handling the thrown exception. + return true; + } + + if (caughtExceptionType == null) + { + // Check if finally region is executed for pending throw. + Debug.Assert(CurrentBasicBlock.IsFirstBlockOfFinally(out _)); + var tryFinallyRegion = CurrentBasicBlock.GetContainingRegionOfKind(ControlFlowRegionKind.TryAndFinally)!; + var tryRegion = tryFinallyRegion.NestedRegions[0]; + return tryRegion.FirstBlockOrdinal <= pendingThrow.BasicBlockOrdinal && tryRegion.LastBlockOrdinal >= pendingThrow.BasicBlockOrdinal; + } + + return false; + } + } + + public override TAbstractAnalysisValue VisitFlowAnonymousFunction(IFlowAnonymousFunctionOperation operation, object? argument) + { + var value = base.VisitFlowAnonymousFunction(operation, argument)!; + _visitedLambdas.Add(operation); + return value; + } + + public override TAbstractAnalysisValue VisitStaticLocalInitializationSemaphore(IStaticLocalInitializationSemaphoreOperation operation, object? argument) + { + // https://github.com/dotnet/roslyn-analyzers/issues/1571 tracks adding support. + return base.VisitStaticLocalInitializationSemaphore(operation, argument)!; + } + + public override TAbstractAnalysisValue VisitAnonymousObjectCreation(IAnonymousObjectCreationOperation operation, object? argument) + { + var savedIsInsideAnonymousObjectInitializer = IsInsideAnonymousObjectInitializer; + IsInsideAnonymousObjectInitializer = true; + var value = base.VisitAnonymousObjectCreation(operation, argument)!; + IsInsideAnonymousObjectInitializer = savedIsInsideAnonymousObjectInitializer; + return value; + } + + public sealed override TAbstractAnalysisValue VisitReturn(IReturnOperation operation, object? argument) + { + Debug.Assert(operation.Kind == OperationKind.YieldReturn, "IReturnOperation must have been lowered in the CFG"); + + var value = Visit(operation.ReturnedValue, argument); + ProcessReturnValue(operation.ReturnedValue); + + return value; + } + + public virtual TAbstractAnalysisValue GetAssignedValueForPattern(IIsPatternOperation operation, TAbstractAnalysisValue operandValue) + { + return operandValue; + } + + public override TAbstractAnalysisValue VisitIsPattern(IIsPatternOperation operation, object? argument) + { + // "c is D d" OR "x is 1" + var operandValue = Visit(operation.Value, argument); + _ = Visit(operation.Pattern, argument); + + var patternValue = GetAssignedValueForPattern(operation, operandValue); + if (operation.Pattern is IDeclarationPatternOperation) + { + SetAbstractValueForAssignment( + target: operation.Pattern, + assignedValueOperation: operation.Value, + assignedValue: patternValue); + } + + if (PredicateAnalysis) + { + PerformPredicateAnalysis(operation); + } + + return ValueDomain.UnknownOrMayBeValue; + } + + public override TAbstractAnalysisValue VisitAwait(IAwaitOperation operation, object? argument) + { + var value = base.VisitAwait(operation, argument)!; + + var pointsToValue = GetPointsToAbstractValue(operation.Operation); + return TryGetTaskWrappedValue(pointsToValue, out var awaitedValue) ? + awaitedValue : + value; + } + + #region Overrides for lowered IOperations + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitUsing(IUsingOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IUsingOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitWhileLoop(IWhileLoopOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IWhileLoopOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitForEachLoop(IForEachLoopOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IForEachLoopOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitForLoop(IForLoopOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IForLoopOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitForToLoop(IForToLoopOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IForToLoopOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitCoalesce(ICoalesceOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ICoalesceOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitConditional(IConditionalOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IConditionalOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitConditionalAccess(IConditionalAccessOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IConditionalAccessOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitConditionalAccessInstance(IConditionalAccessInstanceOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IConditionalAccessInstanceOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitThrow(IThrowOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IThrowOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitVariableDeclaration(IVariableDeclarationOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IVariableDeclarationOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitVariableDeclarationGroup(IVariableDeclarationGroupOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IVariableDeclarationOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitVariableDeclarator(IVariableDeclaratorOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IVariableDeclaratorOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitTry(ITryOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ITryOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitCatchClause(ICatchClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ICatchClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public override TAbstractAnalysisValue VisitLock(ILockOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ILockOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitBranch(IBranchOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IBranchOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitLabeled(ILabeledOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ILabeledOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitSwitch(ISwitchOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ISwitchOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitSwitchCase(ISwitchCaseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ISwitchCaseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitDefaultCaseClause(IDefaultCaseClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IDefaultCaseClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitPatternCaseClause(IPatternCaseClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IPatternCaseClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitRangeCaseClause(IRangeCaseClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IRangeCaseClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitRelationalCaseClause(IRelationalCaseClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IRelationalCaseClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitSingleValueCaseClause(ISingleValueCaseClauseOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ISingleValueCaseClauseOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitObjectOrCollectionInitializer(IObjectOrCollectionInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IObjectOrCollectionInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitMemberInitializer(IMemberInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IMemberInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitBlock(IBlockOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IBlockOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitVariableInitializer(IVariableInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IVariableInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitFieldInitializer(IFieldInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IFieldInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitParameterInitializer(IParameterInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IParameterInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitPropertyInitializer(IPropertyInitializerOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IPropertyInitializerOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitEnd(IEndOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IEndOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitEmpty(IEmptyOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IEmptyOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitNameOf(INameOfOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(INameOfOperation)}' must have been lowered in the CFG"); + } + + public sealed override TAbstractAnalysisValue VisitAnonymousFunction(IAnonymousFunctionOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(IAnonymousFunctionOperation)}' must have been lowered in the CFG"); + } + + [ExcludeFromCodeCoverage] + public sealed override TAbstractAnalysisValue VisitLocalFunction(ILocalFunctionOperation operation, object? argument) + { + throw new NotSupportedException($"'{nameof(ILocalFunctionOperation)}' must have been lowered in the CFG"); + } + + #endregion + + #endregion + + /// + /// for + /// + protected INamedTypeSymbol? ExceptionNamedType { get; } + + /// + /// for 'System.Diagnostics.Contracts.Contract' type. /> + /// + protected INamedTypeSymbol? ContractNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? IDisposableNamedType { get; } + + /// + /// for "System.IAsyncDisposable" + /// + private INamedTypeSymbol? IAsyncDisposableNamedType { get; } + + /// + /// for "System.Runtime.CompilerServices.ConfiguredAsyncDisposable" + /// + private INamedTypeSymbol? ConfiguredAsyncDisposable { get; } + + /// + /// for "System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable" + /// + private INamedTypeSymbol? ConfiguredValueTaskAwaitable { get; } + + /// + /// for + /// + protected INamedTypeSymbol? TaskNamedType { get; } + +#pragma warning disable CA1200 // Avoid using cref tags with a prefix - cref prefix required for one of the project contexts + /// + /// for + /// + private INamedTypeSymbol? TaskAsyncEnumerableExtensions { get; } +#pragma warning restore CA1200 // Avoid using cref tags with a prefix + + /// + /// for + /// + protected INamedTypeSymbol? MemoryStreamNamedType { get; } + + /// + /// for System.Threading.Tasks.ValueTask/> + /// + private INamedTypeSymbol? ValueTaskNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? GenericTaskNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? MonitorNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? InterlockedNamedType { get; } + + /// + /// for 'System.Runtime.Serialization.SerializationInfo' type /> + /// + protected INamedTypeSymbol? SerializationInfoNamedType { get; } + + /// + /// for 'System.Runtime.Serialization.StreamingContext' type /> + /// + protected INamedTypeSymbol? StreamingContextNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? GenericIEquatableNamedType { get; } + + /// + /// for + /// + protected INamedTypeSymbol? StringReaderType { get; } + + /// + /// Set containing following named types, if not null: + /// 1. for + /// 2. for + /// 3. for + /// + protected ImmutableHashSet CollectionNamedTypes { get; } + + private IMethodSymbol? DebugAssertMethod { get; } + + private ImmutableHashSet GetWellKnownCollectionTypes() + { + var builder = PooledHashSet.GetInstance(); + var iCollection = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsICollection); + if (iCollection != null) + { + builder.Add(iCollection); + } + + var genericICollection = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericICollection1); + if (genericICollection != null) + { + builder.Add(genericICollection); + } + + var genericIReadOnlyCollection = WellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericIReadOnlyCollection1); + if (genericIReadOnlyCollection != null) + { + builder.Add(genericIReadOnlyCollection); + } + + return builder.ToImmutableAndFree(); + } + + private protected bool IsDisposable([NotNullWhen(returnValue: true)] ITypeSymbol? type) + => type != null && type.IsDisposable(IDisposableNamedType, IAsyncDisposableNamedType, ConfiguredAsyncDisposable); + + private protected DisposeMethodKind GetDisposeMethodKind(IMethodSymbol method) + => method.GetDisposeMethodKind(IDisposableNamedType, IAsyncDisposableNamedType, ConfiguredAsyncDisposable, TaskNamedType, ValueTaskNamedType, ConfiguredValueTaskAwaitable); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DictionaryAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DictionaryAnalysisData.cs new file mode 100644 index 0000000000000..d8c02f93c03e9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/DictionaryAnalysisData.cs @@ -0,0 +1,169 @@ +// 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; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities.PooledObjects; + +#pragma warning disable CA1710 // Rename DictionaryAnalysisData to end in 'Dictionary' + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public sealed class DictionaryAnalysisData : AbstractAnalysisData, IDictionary + where TKey : notnull + { +#pragma warning disable CA2213 // Disposable fields should be disposed + private PooledDictionary _coreAnalysisData; +#pragma warning restore + + public DictionaryAnalysisData() + { + _coreAnalysisData = PooledDictionary.GetInstance(); + } + + public DictionaryAnalysisData(IDictionary initializer) + { + _coreAnalysisData = PooledDictionary.GetInstance(initializer); + } + + public ImmutableDictionary ToImmutableDictionary() + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.ToImmutableDictionary(); + } + + public TValue this[TKey key] + { + get + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData[key]; + } + set + { + Debug.Assert(!IsDisposed); + _coreAnalysisData[key] = value; + } + } + + public ICollection Keys + { + get + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.Keys; + } + } + + public ICollection Values => + // "Values" might be accessed during dispose. + //Debug.Assert(!IsDisposed); + _coreAnalysisData.Values; + + public int Count + { + get + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.Count; + } + } + + public bool IsReadOnly + { + get + { + Debug.Assert(!IsDisposed); + return ((IDictionary)_coreAnalysisData).IsReadOnly; + } + } + + public void Add(TKey key, TValue value) + { + Debug.Assert(!IsDisposed); + _coreAnalysisData.Add(key, value); + } + + public void Add(KeyValuePair item) + { + Debug.Assert(!IsDisposed); + _coreAnalysisData.Add(item.Key, item.Value); + } + + public void Clear() + { + Debug.Assert(!IsDisposed); + _coreAnalysisData.Clear(); + } + + public bool Contains(KeyValuePair item) + { + Debug.Assert(!IsDisposed); + return ((IDictionary)_coreAnalysisData).Contains(item); + } + + public bool ContainsKey(TKey key) + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + Debug.Assert(!IsDisposed); + ((IDictionary)_coreAnalysisData).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.GetEnumerator(); + } + + public bool Remove(TKey key) + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.Remove(key); + } + + public bool Remove(KeyValuePair item) + { + Debug.Assert(!IsDisposed); + return Remove(item.Key); + } + +#pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes. https://github.com/dotnet/roslyn/issues/42552 + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) +#pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member because of nullability attributes. + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + Debug.Assert(!IsDisposed); + return _coreAnalysisData.GetEnumerator(); + } + + protected override void Dispose(bool disposing) + { + if (IsDisposed) + { + return; + } + + if (disposing) + { + _coreAnalysisData.Dispose(); + _coreAnalysisData = null!; + } + + base.Dispose(disposing); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ForwardDataFlowAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ForwardDataFlowAnalysis.cs new file mode 100644 index 0000000000000..9292ec5322723 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ForwardDataFlowAnalysis.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Subtype for all forward dataflow analyses. + /// These analyses operate on the control flow graph starting from the entry block, + /// flowing the dataflow values forward to the successor blocks until a fix point is reached. + /// + public abstract class ForwardDataFlowAnalysis + : DataFlowAnalysis + where TAnalysisData : AbstractAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : DataFlowAnalysisResult + where TBlockAnalysisResult : AbstractBlockAnalysisResult + { + protected ForwardDataFlowAnalysis(AbstractAnalysisDomain analysisDomain, DataFlowOperationVisitor operationVisitor) + : base(analysisDomain, operationVisitor) + { + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisContext.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisContext.cs new file mode 100644 index 0000000000000..defc4b77edf17 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisContext.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Marker interface for analysis contexts for execution of on a control flow graph. + /// Primarily exists for specifying constraints on analysis context type parameters. + /// + public interface IDataFlowAnalysisContext + { + ControlFlowGraph ControlFlowGraph { get; } + ISymbol OwningSymbol { get; } + ControlFlowGraph? GetLocalFunctionControlFlowGraph(IMethodSymbol localFunction); + ControlFlowGraph? GetAnonymousFunctionControlFlowGraph(IFlowAnonymousFunctionOperation lambda); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisResult.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisResult.cs new file mode 100644 index 0000000000000..cb00032c288ba --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/IDataFlowAnalysisResult.cs @@ -0,0 +1,19 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Marker interface for analysis results from execution of on a control flow graph. + /// Primarily exists for specifying constraints on analysis result type parameters. + /// + public interface IDataFlowAnalysisResult + { + ControlFlowGraph ControlFlowGraph { get; } + (TAbstractAnalysisValue Value, PredicateValueKind PredicateValueKind)? ReturnValueAndPredicateKind { get; } + object? AnalysisDataForUnhandledThrowOperations { get; } + object? TaskWrappedValuesMap { get; } + LambdaAndLocalFunctionAnalysisInfo LambdaAndLocalFunctionAnalysisInfo { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisConfiguration.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisConfiguration.cs new file mode 100644 index 0000000000000..eeaaacf4b19a9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisConfiguration.cs @@ -0,0 +1,154 @@ +// 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 Analyzer.Utilities; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Interprocedural analysis configuration parameters. + /// + public struct InterproceduralAnalysisConfiguration : IEquatable + { + /// + /// Defines the max length for method call chain (call stack size) for interprocedural analysis. + /// This is done for performance reasons for analyzing methods with extremely large call trees. + /// https://github.com/dotnet/roslyn-analyzers/issues/1809 tracks improving this heuristic. + /// + private const uint DefaultMaxInterproceduralMethodCallChain = 3; + + /// + /// Defines the max length for lambda/local function method call chain (call stack size) for interprocedural analysis. + /// This is done for performance reasons for analyzing methods with extremely large call trees. + /// https://github.com/dotnet/roslyn-analyzers/issues/1809 tracks improving this heuristic. + /// + private const uint DefaultMaxInterproceduralLambdaOrLocalFunctionCallChain = 3; + + private InterproceduralAnalysisConfiguration( + InterproceduralAnalysisKind interproceduralAnalysisKind, + uint maxInterproceduralMethodCallChain, + uint maxInterproceduralLambdaOrLocalFunctionCallChain) + { + InterproceduralAnalysisKind = interproceduralAnalysisKind; + MaxInterproceduralMethodCallChain = maxInterproceduralMethodCallChain; + MaxInterproceduralLambdaOrLocalFunctionCallChain = maxInterproceduralLambdaOrLocalFunctionCallChain; + } + + public static InterproceduralAnalysisConfiguration Create( + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + ControlFlowGraph cfg, + Compilation compilation, + InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, + uint defaultMaxInterproceduralMethodCallChain = DefaultMaxInterproceduralMethodCallChain, + uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = DefaultMaxInterproceduralLambdaOrLocalFunctionCallChain) + => Create(analyzerOptions, rule, cfg.OriginalOperation.Syntax.SyntaxTree, compilation, defaultInterproceduralAnalysisKind, + defaultMaxInterproceduralMethodCallChain, defaultMaxInterproceduralLambdaOrLocalFunctionCallChain); + + private static InterproceduralAnalysisConfiguration Create( + AnalyzerOptions analyzerOptions, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, + uint defaultMaxInterproceduralMethodCallChain = DefaultMaxInterproceduralMethodCallChain, + uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = DefaultMaxInterproceduralLambdaOrLocalFunctionCallChain) + { + var kind = analyzerOptions.GetInterproceduralAnalysisKindOption(rule, tree, compilation, defaultInterproceduralAnalysisKind); + + var maxInterproceduralMethodCallChain = analyzerOptions.GetUnsignedIntegralOptionValue( + optionName: EditorConfigOptionNames.MaxInterproceduralMethodCallChain, + rule: rule, + tree, + compilation, + defaultValue: defaultMaxInterproceduralMethodCallChain); + + var maxInterproceduralLambdaOrLocalFunctionCallChain = analyzerOptions.GetUnsignedIntegralOptionValue( + optionName: EditorConfigOptionNames.MaxInterproceduralLambdaOrLocalFunctionCallChain, + rule: rule, + tree, + compilation, + defaultValue: defaultMaxInterproceduralLambdaOrLocalFunctionCallChain); + + return new InterproceduralAnalysisConfiguration( + kind, maxInterproceduralMethodCallChain, maxInterproceduralLambdaOrLocalFunctionCallChain); + } + + public static InterproceduralAnalysisConfiguration Create( + AnalyzerOptions analyzerOptions, + ImmutableArray rules, + ControlFlowGraph cfg, + Compilation compilation, + InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, + uint defaultMaxInterproceduralMethodCallChain = DefaultMaxInterproceduralMethodCallChain, + uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = DefaultMaxInterproceduralLambdaOrLocalFunctionCallChain) + => Create(analyzerOptions, rules, cfg.OriginalOperation, compilation, defaultInterproceduralAnalysisKind, + defaultMaxInterproceduralMethodCallChain, defaultMaxInterproceduralLambdaOrLocalFunctionCallChain); + + internal static InterproceduralAnalysisConfiguration Create( + AnalyzerOptions analyzerOptions, + ImmutableArray rules, + IOperation operation, + Compilation compilation, + InterproceduralAnalysisKind defaultInterproceduralAnalysisKind, + uint defaultMaxInterproceduralMethodCallChain = DefaultMaxInterproceduralMethodCallChain, + uint defaultMaxInterproceduralLambdaOrLocalFunctionCallChain = DefaultMaxInterproceduralLambdaOrLocalFunctionCallChain) + { + var tree = operation.Syntax.SyntaxTree; + InterproceduralAnalysisKind maxKind = InterproceduralAnalysisKind.None; + uint maxMethodCallChain = 0; + uint maxLambdaorLocalFunctionCallChain = 0; + foreach (var rule in rules) + { + var interproceduralAnalysisConfig = Create(analyzerOptions, rule, tree, compilation, defaultInterproceduralAnalysisKind, + defaultMaxInterproceduralMethodCallChain, defaultMaxInterproceduralLambdaOrLocalFunctionCallChain); + maxKind = (InterproceduralAnalysisKind)Math.Max((int)maxKind, (int)interproceduralAnalysisConfig.InterproceduralAnalysisKind); + maxMethodCallChain = Math.Max(maxMethodCallChain, interproceduralAnalysisConfig.MaxInterproceduralMethodCallChain); + maxLambdaorLocalFunctionCallChain = Math.Max(maxLambdaorLocalFunctionCallChain, interproceduralAnalysisConfig.MaxInterproceduralLambdaOrLocalFunctionCallChain); + } + + return new InterproceduralAnalysisConfiguration(maxKind, maxMethodCallChain, maxLambdaorLocalFunctionCallChain); + } + + public InterproceduralAnalysisKind InterproceduralAnalysisKind { get; } + + public uint MaxInterproceduralMethodCallChain { get; } + + public uint MaxInterproceduralLambdaOrLocalFunctionCallChain { get; } + + public override readonly bool Equals(object obj) + { + return obj is InterproceduralAnalysisConfiguration otherParameters && + Equals(otherParameters); + } + + public readonly bool Equals(InterproceduralAnalysisConfiguration other) + { + return InterproceduralAnalysisKind == other.InterproceduralAnalysisKind && + MaxInterproceduralMethodCallChain == other.MaxInterproceduralMethodCallChain && + MaxInterproceduralLambdaOrLocalFunctionCallChain == other.MaxInterproceduralLambdaOrLocalFunctionCallChain; + } + + public override readonly int GetHashCode() + { + return RoslynHashCode.Combine( + ((int)InterproceduralAnalysisKind).GetHashCode(), + MaxInterproceduralMethodCallChain.GetHashCode(), + MaxInterproceduralLambdaOrLocalFunctionCallChain.GetHashCode()); + } + + public static bool operator ==(InterproceduralAnalysisConfiguration left, InterproceduralAnalysisConfiguration right) + { + return left.Equals(right); + } + + public static bool operator !=(InterproceduralAnalysisConfiguration left, InterproceduralAnalysisConfiguration right) + { + return !(left == right); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisData.cs new file mode 100644 index 0000000000000..338e7a74bcfe6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisData.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; +using System.Collections.Immutable; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.CopyAnalysis; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Contains the caller's analysis context data passed to context sensitive interprocedural analysis, . + /// This includes the following: + /// 1. Caller's tracked analysis data at the call site, which is the initial analysis data for callee. + /// 2. Information about the invocation instance on which the method is invoked. + /// 3. Information about arguments for the invocation. + /// 4. Captured variables (for lambda/local function invocations). + /// 5. Information about ref/out parameter entities that share address with callee's parameters. + /// 6. Operation call stack for the current interprocedural analysis. + /// 7. Set of analysis contexts currently being analyzed. + /// + public sealed class InterproceduralAnalysisData + : CacheBasedEquatable> + where TAnalysisContext : class, IDataFlowAnalysisContext + where TAnalysisData : AbstractAnalysisData + { + public InterproceduralAnalysisData( + TAnalysisData? initialAnalysisData, + (AnalysisEntity?, PointsToAbstractValue)? invocationInstance, + (AnalysisEntity, PointsToAbstractValue)? thisOrMeInstanceForCaller, + ImmutableDictionary> argumentValuesMap, + ImmutableDictionary capturedVariablesMap, + ImmutableDictionary addressSharedEntities, + ImmutableStack callStack, + ImmutableHashSet methodsBeingAnalyzed, + Func getCachedAbstractValueFromCaller, + Func getInterproceduralControlFlowGraph, + Func getAnalysisEntityForFlowCapture, + Func?> getInterproceduralCallStackForOwningSymbol) + { + InitialAnalysisData = initialAnalysisData; + InvocationInstance = invocationInstance; + ThisOrMeInstanceForCaller = thisOrMeInstanceForCaller; + ArgumentValuesMap = argumentValuesMap; + CapturedVariablesMap = capturedVariablesMap; + AddressSharedEntities = addressSharedEntities; + CallStack = callStack; + MethodsBeingAnalyzed = methodsBeingAnalyzed; + GetCachedAbstractValueFromCaller = getCachedAbstractValueFromCaller; + GetInterproceduralControlFlowGraph = getInterproceduralControlFlowGraph; + GetAnalysisEntityForFlowCapture = getAnalysisEntityForFlowCapture; + GetInterproceduralCallStackForOwningSymbol = getInterproceduralCallStackForOwningSymbol; + } + + public TAnalysisData? InitialAnalysisData { get; } + public (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? InvocationInstance { get; } + public (AnalysisEntity Instance, PointsToAbstractValue PointsToValue)? ThisOrMeInstanceForCaller { get; } + public ImmutableDictionary> ArgumentValuesMap { get; } + public ImmutableDictionary CapturedVariablesMap { get; } + public ImmutableDictionary AddressSharedEntities { get; } + public ImmutableStack CallStack { get; } + public ImmutableHashSet MethodsBeingAnalyzed { get; } + public Func GetCachedAbstractValueFromCaller { get; } + public Func GetInterproceduralControlFlowGraph { get; } + public Func GetAnalysisEntityForFlowCapture { get; } + public Func?> GetInterproceduralCallStackForOwningSymbol { get; } + + protected override void ComputeHashCodeParts(ref RoslynHashCode hashCode) + { + hashCode.Add(InitialAnalysisData.GetHashCodeOrDefault()); + AddHashCodeParts(InvocationInstance, ref hashCode); + AddHashCodeParts(ThisOrMeInstanceForCaller, ref hashCode); + hashCode.Add(HashUtilities.Combine(ArgumentValuesMap)); + hashCode.Add(HashUtilities.Combine(CapturedVariablesMap)); + hashCode.Add(HashUtilities.Combine(AddressSharedEntities)); + hashCode.Add(HashUtilities.Combine(CallStack)); + hashCode.Add(HashUtilities.Combine(MethodsBeingAnalyzed)); + } + + protected override bool ComputeEqualsByHashCodeParts(CacheBasedEquatable> obj) + { + var other = (InterproceduralAnalysisData)obj; + return InitialAnalysisData.GetHashCodeOrDefault() == other.InitialAnalysisData.GetHashCodeOrDefault() + && EqualsByHashCodeParts(InvocationInstance, other.InvocationInstance) + && EqualsByHashCodeParts(ThisOrMeInstanceForCaller, other.ThisOrMeInstanceForCaller) + && HashUtilities.Combine(ArgumentValuesMap) == HashUtilities.Combine(other.ArgumentValuesMap) + && HashUtilities.Combine(CapturedVariablesMap) == HashUtilities.Combine(other.CapturedVariablesMap) + && HashUtilities.Combine(AddressSharedEntities) == HashUtilities.Combine(other.AddressSharedEntities) + && HashUtilities.Combine(CallStack) == HashUtilities.Combine(other.CallStack) + && HashUtilities.Combine(MethodsBeingAnalyzed) == HashUtilities.Combine(other.MethodsBeingAnalyzed); + } + + private static void AddHashCodeParts( + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? instanceAndPointsToValue, + ref RoslynHashCode hashCode) + { + if (instanceAndPointsToValue.HasValue) + { + hashCode.Add(instanceAndPointsToValue.Value.Instance.GetHashCodeOrDefault()); + hashCode.Add(instanceAndPointsToValue.Value.PointsToValue.GetHashCode()); + } + else + { + hashCode.Add(0); + } + } + + private static bool EqualsByHashCodeParts( + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? left, + (AnalysisEntity? Instance, PointsToAbstractValue PointsToValue)? right) + { + if (left is null) + return right is null; + else if (right is null) + return false; + + return left.Value.Instance.GetHashCodeOrDefault() == right.Value.Instance.GetHashCodeOrDefault() + && left.Value.PointsToValue.GetHashCode() == right.Value.PointsToValue.GetHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisKind.cs new file mode 100644 index 0000000000000..27a7d9239241e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisKind.cs @@ -0,0 +1,45 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Determines the kind of interprocedural dataflow analysis to perform for method invocations. + /// + public enum InterproceduralAnalysisKind + { + /// + /// Skip interprocedural analysis for source method invocations, except for lambda and location function invocations, + /// which are always analyzed in a context sensitive fashion. + /// All the analysis data for invocation receiver and arguments is conservatively reset at call sites. + /// Produces least precise results from amongst the possible interprocedural modes, but is the most performant mode. + /// + None, + + /* NOT YET IMPLEMENTED: https://github.com/dotnet/roslyn-analyzers/issues/1810 + /// + /// Perform non-context sensitive interprocedural analysis for source method invocations, + /// except for lambda and location function invocations, which are always analyzed in a context sensitive fashion. + /// Non-context sensitive interprocedural analysis analyzes invoked method without considering the calling context, + /// i.e. the argument values and instance receiver, hence is less precise then ContextSensitive mode. + /// Analysis result from such an interprocedural analysis is applied at every call site, hence the analysis results + /// are more precise then None mode. + /// This mode is generally much less performance intensive then the ContextSensitive mode, + /// but more intensive then None mode. + /// + NonContextSensitive, + */ + + /// + /// Performs context sensitive interprocedural analysis for all source method invocations, + /// including lambda and location function invocations. + /// Context sensitive interprocedural analysis analyzes invoked method at each call site by considering the calling context, + /// i.e. the argument values and instance receiver, hence is the most precise analysis mode. + /// This mode is also the most performance intensive mode. + /// Note that we apply a max threshold to the length of interprocedural call chain to analyze in + /// a context sensitive fashion to avoid infinite computation for huge call graphs. + /// + ContextSensitive + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisPredicate.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisPredicate.cs new file mode 100644 index 0000000000000..cb92889a8ba04 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralAnalysisPredicate.cs @@ -0,0 +1,46 @@ +// 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; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Interprocedural analysis predicate to decide whether interprocedural analysis can be skipped. + /// + public sealed class InterproceduralAnalysisPredicate + { + private readonly Func? _skipAnalysisForInvokedMethodPredicate; + private readonly Func? _skipAnalysisForInvokedLambdaOrLocalFunctionPredicate; + private readonly Func? _skipAnalysisForInvokedContextPredicate; + + public InterproceduralAnalysisPredicate( + Func? skipAnalysisForInvokedMethodPredicate, + Func? skipAnalysisForInvokedLambdaOrLocalFunctionPredicate, + Func? skipAnalysisForInvokedContextPredicate) + { + if (skipAnalysisForInvokedMethodPredicate == null && + skipAnalysisForInvokedLambdaOrLocalFunctionPredicate == null && + skipAnalysisForInvokedContextPredicate == null) + { + throw new ArgumentException("You must provide at least one non-null predicate argument"); + } + + _skipAnalysisForInvokedMethodPredicate = skipAnalysisForInvokedMethodPredicate; + _skipAnalysisForInvokedLambdaOrLocalFunctionPredicate = skipAnalysisForInvokedLambdaOrLocalFunctionPredicate; + _skipAnalysisForInvokedContextPredicate = skipAnalysisForInvokedContextPredicate; + } + + public bool SkipInterproceduralAnalysis(IMethodSymbol invokedMethod, bool isLambdaOrLocalFunction) + { + var predicate = isLambdaOrLocalFunction ? + _skipAnalysisForInvokedLambdaOrLocalFunctionPredicate : + _skipAnalysisForInvokedMethodPredicate; + return predicate?.Invoke(invokedMethod) == true; + } + + public bool SkipInterproceduralAnalysis(IDataFlowAnalysisContext interproceduralAnalysisContext) + => _skipAnalysisForInvokedContextPredicate?.Invoke(interproceduralAnalysisContext) == true; + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralCaptureId.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralCaptureId.cs new file mode 100644 index 0000000000000..89bef0e8d433d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/InterproceduralCaptureId.cs @@ -0,0 +1,47 @@ +// 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 Analyzer.Utilities; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Unique flow capture Id across interprocedural flow graph. + /// This type essentially wraps each , which is unique for each control flow graph, + /// with its owning . + /// + public readonly struct InterproceduralCaptureId : IEquatable + { + internal InterproceduralCaptureId(CaptureId captureId, ControlFlowGraph controlFlowGraph, bool isLValueFlowCapture) + { + Id = captureId; + ControlFlowGraph = controlFlowGraph; + IsLValueFlowCapture = isLValueFlowCapture; + } + + public CaptureId Id { get; } + public ControlFlowGraph ControlFlowGraph { get; } + public bool IsLValueFlowCapture { get; } + + public bool Equals(InterproceduralCaptureId other) + => Id.Equals(other.Id) && ControlFlowGraph == other.ControlFlowGraph; + + public override bool Equals(object obj) + => obj is InterproceduralCaptureId id && Equals(id); + + public override int GetHashCode() + => RoslynHashCode.Combine(Id.GetHashCode(), ControlFlowGraph.GetHashCode()); + + public static bool operator ==(InterproceduralCaptureId left, InterproceduralCaptureId right) + { + return left.Equals(right); + } + + public static bool operator !=(InterproceduralCaptureId left, InterproceduralCaptureId right) + { + return !(left == right); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs new file mode 100644 index 0000000000000..9d3f09c26f9b5 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LValueFlowCapturesProvider.cs @@ -0,0 +1,80 @@ +// 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.Runtime.CompilerServices; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; + +#if DEBUG +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +#endif + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Helper class to detect s that are l-value captures. + /// + internal static class LValueFlowCapturesProvider + { + private static readonly ConditionalWeakTable> s_lValueFlowCapturesCache = + new(); + + public static ImmutableHashSet GetOrCreateLValueFlowCaptures(ControlFlowGraph cfg) + => s_lValueFlowCapturesCache.GetValue(cfg, CreateLValueFlowCaptures); + + private static ImmutableHashSet CreateLValueFlowCaptures(ControlFlowGraph cfg) + { + var lvalueFlowCaptureIdBuilder = PooledHashSet.GetInstance(); +#if DEBUG + var rvalueFlowCaptureIds = new Dictionary>(); +#endif + foreach (var flowCaptureReference in cfg.DescendantOperations(OperationKind.FlowCaptureReference)) + { + if (flowCaptureReference.IsLValueFlowCaptureReference()) + { + lvalueFlowCaptureIdBuilder.Add(flowCaptureReference.Id); + } +#if DEBUG + else + { + if (!rvalueFlowCaptureIds.TryGetValue(flowCaptureReference.Id, out var operations)) + { + operations = new HashSet(); + rvalueFlowCaptureIds[flowCaptureReference.Id] = operations; + } + + operations.Add(flowCaptureReference); + } +#endif + } + +#if DEBUG + if (lvalueFlowCaptureIdBuilder.Count > 0) + { + foreach (var captureId in lvalueFlowCaptureIdBuilder) + { + if (rvalueFlowCaptureIds.TryGetValue(captureId, out var operations)) + { + // Flow capture reference is used on left side as well as right side for + // CFG generated for coalesce assignment operation ('??=') + // Do not fire an assert for this known anomaly. + if (operations.Count == 1 && + operations.Single().Parent?.Kind == OperationKind.FlowCapture) + { + continue; + } + + Debug.Fail("Flow capture used as both an r-value and an l-value?"); + } + } + } +#endif + + return lvalueFlowCaptureIdBuilder.ToImmutableAndFree(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LambdaAndLocalFunctionAnalysisInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LambdaAndLocalFunctionAnalysisInfo.cs new file mode 100644 index 0000000000000..96aa86acb37e6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/LambdaAndLocalFunctionAnalysisInfo.cs @@ -0,0 +1,47 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// Contains information about escaped and analyzed lambda methods and local functions + /// in context of analyzing its containing method. + /// + public sealed class LambdaAndLocalFunctionAnalysisInfo + { + /// + /// Local functions that escaped analysis scope of the containing method. + /// + public ImmutableHashSet EscapedLocalFunctions { get; } + + /// + /// Local functions for which interprocedural analysis was performed at least once during analysis of the containing method. + /// + public ImmutableHashSet AnalyzedLocalFunctions { get; } + + /// + /// Lambda methods that escaped analysis scope of the containing method. + /// + public ImmutableHashSet EscapedLambdas { get; } + + /// + /// Lambda methods for which interprocedural analysis was performed at least once during analysis of the containing method. + /// + public ImmutableHashSet AnalyzedLambdas { get; } + + internal LambdaAndLocalFunctionAnalysisInfo( + ImmutableHashSet.Builder escapedLocalFunctions, + ImmutableHashSet.Builder analyzedLocalFunctions, + ImmutableHashSet.Builder escapedLambdas, + ImmutableHashSet.Builder analyzedLambdas) + { + EscapedLocalFunctions = escapedLocalFunctions.ToImmutable(); + AnalyzedLocalFunctions = analyzedLocalFunctions.ToImmutable(); + EscapedLambdas = escapedLambdas.ToImmutable(); + AnalyzedLambdas = analyzedLambdas.ToImmutable(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/MapAbstractDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/MapAbstractDomain.cs new file mode 100644 index 0000000000000..f37bcc8fc9644 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/MapAbstractDomain.cs @@ -0,0 +1,152 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// An abstract domain implementation for analyses that store dictionary typed data. + /// + public class MapAbstractDomain : AbstractAnalysisDomain> + where TKey : notnull + { + public MapAbstractDomain(AbstractValueDomain valueDomain) + { + ValueDomain = valueDomain; + } + + protected AbstractValueDomain ValueDomain { get; } + public override DictionaryAnalysisData Clone(DictionaryAnalysisData value) => new(value); + + /// + /// Compares if the abstract dataflow values in against the values in to ensure + /// dataflow function is a monotically increasing function. See https://en.wikipedia.org/wiki/Monotonic_function for understanding monotonic functions. + /// + /// + /// 1) 0, if both the dictionaries are identical. + /// 2) -1, if dictionaries are not identical and for every key in , the corresponding key exists in and + /// the value of each such key in is lesser than or equals the value in . + /// 3) 1, otherwise. + /// + public sealed override int Compare(DictionaryAnalysisData oldValue, DictionaryAnalysisData newValue) + => Compare(oldValue, newValue, assertMonotonicity: true); + + public sealed override bool Equals(DictionaryAnalysisData value1, DictionaryAnalysisData value2) + => Compare(value1, value2, assertMonotonicity: false) == 0; + + private int Compare(DictionaryAnalysisData oldValue, DictionaryAnalysisData newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + if (newValue.Count < oldValue.Count) + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + + // Ensure that every key in oldValue exists in newValue and the value corresponding to that key + // is not greater in oldValue as compared to the value in newValue + bool newValueIsBigger = false; + foreach (var kvp in oldValue) + { + var key = kvp.Key; + var value = kvp.Value; + if (!newValue.TryGetValue(key, out var otherValue)) + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + + var result = ValueDomain.Compare(value, otherValue, assertMonotonicity); + + if (result > 0) + { + FireNonMonotonicAssertIfNeeded(assertMonotonicity); + return 1; + } + else if (result < 0) + { + newValueIsBigger = true; + } + } + + if (!newValueIsBigger) + { + newValueIsBigger = newValue.Count > oldValue.Count; + } + + return newValueIsBigger ? -1 : 0; + } + +#pragma warning disable CA1030 // Use events where appropriate + [Conditional("DEBUG")] + private static void FireNonMonotonicAssertIfNeeded(bool assertMonotonicity) + { + if (assertMonotonicity) + { + Debug.Fail("Non-monotonic merge"); + } + } +#pragma warning restore CA1030 // Use events where appropriate + + public override DictionaryAnalysisData Merge(DictionaryAnalysisData value1, DictionaryAnalysisData value2) + { + var result = new DictionaryAnalysisData(value1); + foreach (var entry in value2) + { + if (result.TryGetValue(entry.Key, out var value)) + { + value = ValueDomain.Merge(value, entry.Value); + + if (value != null) + { + result[entry.Key] = value; + } + else + { + result.Remove(entry.Key); + } + } + else + { + result.Add(entry.Key, entry.Value); + } + } + + return result; + } + + internal DictionaryAnalysisData Intersect( + DictionaryAnalysisData map1, + DictionaryAnalysisData map2, + Func intersect) + { + var result = new DictionaryAnalysisData(); + foreach (var kvp in map1) + { + if (!map2.TryGetValue(kvp.Key, out var value2)) + { + value2 = ValueDomain.UnknownOrMayBeValue; + } + + result.Add(kvp.Key, intersect(kvp.Value, value2)); + } + + foreach (var key in map2.Keys) + { + if (!result.ContainsKey(key)) + { + result.Add(key, ValueDomain.UnknownOrMayBeValue); + } + } + + return result; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PerEntityPredicatedAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PerEntityPredicatedAnalysisData.cs new file mode 100644 index 0000000000000..5f6cc6bf38df9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PerEntityPredicatedAnalysisData.cs @@ -0,0 +1,61 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public abstract partial class PredicatedAnalysisData + where TKey : notnull + { + /// + /// Analysis data predicated by true/false value of an . + /// Used to improve the preciseness of analysis when we can apply the or + /// on the control flow paths where the corresponding is known to have or value respectively. + /// + protected sealed class PerEntityPredicatedAnalysisData : IDisposable + { + public PerEntityPredicatedAnalysisData(DictionaryAnalysisData? truePredicatedData, DictionaryAnalysisData? falsePredicatedData) + { + Debug.Assert(truePredicatedData != null || falsePredicatedData != null); + + if (truePredicatedData != null) + { + TruePredicatedData = new DictionaryAnalysisData(truePredicatedData); + } + + if (falsePredicatedData != null) + { + FalsePredicatedData = new DictionaryAnalysisData(falsePredicatedData); + } + } + + public PerEntityPredicatedAnalysisData(PerEntityPredicatedAnalysisData fromData) + : this(fromData.TruePredicatedData, fromData.FalsePredicatedData) + { + } + + /// + /// Analysis data for value of the corresponding on which this data is predicated. + /// value indicates the corresponding can never be . + /// + public DictionaryAnalysisData? TruePredicatedData { get; private set; } + + /// + /// Analysis data for value of the corresponding on which this data is predicated. + /// value indicates the corresponding can never be . + /// + public DictionaryAnalysisData? FalsePredicatedData { get; private set; } + + public void Dispose() + { + TruePredicatedData?.Dispose(); + TruePredicatedData = null; + FalsePredicatedData?.Dispose(); + FalsePredicatedData = null; + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateAnalysisEntityDataFlowOperationVisitor.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateAnalysisEntityDataFlowOperationVisitor.cs new file mode 100644 index 0000000000000..f4dc0d7a1c79c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateAnalysisEntityDataFlowOperationVisitor.cs @@ -0,0 +1,64 @@ +// 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.Diagnostics; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Predicate analysis based Operation visitor to flow the abstract dataflow analysis values for instances across a given statement in a basic block. + /// + public abstract class PredicateAnalysisEntityDataFlowOperationVisitor + : AnalysisEntityDataFlowOperationVisitor + where TAnalysisData : AnalysisEntityBasedPredicateAnalysisData + where TAnalysisContext : AbstractDataFlowAnalysisContext + where TAnalysisResult : class, IDataFlowAnalysisResult + where TAbstractAnalysisValue : IEquatable + { + protected PredicateAnalysisEntityDataFlowOperationVisitor(TAnalysisContext analysisContext) + : base(analysisContext) + { + } + + #region Predicate analysis + protected sealed override bool SupportsPredicateAnalysis => true; + + protected override void UpdateReachability(BasicBlock basicBlock, TAnalysisData analysisData, bool isReachable) + { + Debug.Assert(PredicateAnalysis); + if (analysisData is AnalysisEntityBasedPredicateAnalysisData predicatedData) + { + Debug.Assert(!isReachable || predicatedData.IsReachableBlockData); + predicatedData.IsReachableBlockData = isReachable; + } + } + + protected override bool IsReachableBlockData(TAnalysisData analysisData) + => analysisData?.IsReachableBlockData ?? true; + + protected sealed override void StartTrackingPredicatedData(AnalysisEntity predicatedEntity, TAnalysisData? truePredicateData, TAnalysisData? falsePredicateData) + => CurrentAnalysisData?.StartTrackingPredicatedData( + predicatedEntity, + truePredicateData, + falsePredicateData); + protected sealed override void StopTrackingPredicatedData(AnalysisEntity predicatedEntity) + => CurrentAnalysisData?.StopTrackingPredicatedData(predicatedEntity); + protected sealed override bool HasPredicatedDataForEntity(TAnalysisData analysisData, AnalysisEntity predicatedEntity) + => analysisData?.HasPredicatedDataForEntity(predicatedEntity) == true; + protected sealed override void TransferPredicatedData(AnalysisEntity fromEntity, AnalysisEntity toEntity) + => CurrentAnalysisData?.TransferPredicatedData(fromEntity, toEntity); + protected sealed override PredicateValueKind ApplyPredicatedDataForEntity(TAnalysisData analysisData, AnalysisEntity predicatedEntity, bool trueData) + => analysisData?.ApplyPredicatedDataForEntity(predicatedEntity, trueData) ?? PredicateValueKind.Unknown; + protected override void SetPredicateValueKind(IOperation operation, TAnalysisData analysisData, PredicateValueKind predicateValueKind) + { + base.SetPredicateValueKind(operation, analysisData, predicateValueKind); + if (predicateValueKind == PredicateValueKind.AlwaysFalse) + { + analysisData.IsReachableBlockData = false; + } + } + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateValueKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateValueKind.cs new file mode 100644 index 0000000000000..55344962078c9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicateValueKind.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public enum PredicateValueKind + { + /// + /// Predicate always evaluates to true. + /// + AlwaysTrue, + + /// + /// Predicate always evaluates to false. + /// + AlwaysFalse, + + /// + /// Predicate might evaluate to true or false. + /// + Unknown + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisData.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisData.cs new file mode 100644 index 0000000000000..308889a27184e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisData.cs @@ -0,0 +1,478 @@ +// 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.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ +#pragma warning disable CA2000 // Dispose objects before losing scope - https://github.com/dotnet/roslyn-analyzers/issues/2715 + + /// + /// Base class for all the predicated analysis data. + /// It tracks , which contains the true/false for every predicated , and + /// , which tracks if the current data is for a reachable code path based on the predicate analysis. + /// Predicate analysis data is used to improve the preciseness of analysis when we can apply the or + /// on the control flow paths where the corresponding is known to have or value respectively. + /// + public abstract partial class PredicatedAnalysisData : AbstractAnalysisData + { + private DictionaryAnalysisData? _lazyPredicateDataMap; + + protected PredicatedAnalysisData() + { + IsReachableBlockData = true; + } + + protected PredicatedAnalysisData(PredicatedAnalysisData fromData) + { + IsReachableBlockData = fromData.IsReachableBlockData; + _lazyPredicateDataMap = Clone(fromData._lazyPredicateDataMap); + AssertValidAnalysisData(); + } + + protected PredicatedAnalysisData( + PredicatedAnalysisData predicatedData1, + PredicatedAnalysisData predicatedData2, + DictionaryAnalysisData coreAnalysisData1, + DictionaryAnalysisData coreAnalysisData2, + bool isReachableData, + MapAbstractDomain coreDataAnalysisDomain) + { + IsReachableBlockData = isReachableData; + _lazyPredicateDataMap = Merge(predicatedData1._lazyPredicateDataMap, predicatedData2._lazyPredicateDataMap, + coreAnalysisData1, coreAnalysisData2, coreDataAnalysisDomain, ApplyPredicatedData); + AssertValidAnalysisData(); + } + + public bool IsReachableBlockData { get; set; } + + public bool HasPredicatedData => _lazyPredicateDataMap != null; + + [Conditional("DEBUG")] + private void AssertValidAnalysisData() + { + if (_lazyPredicateDataMap != null) + { + Debug.Assert(!_lazyPredicateDataMap.IsDisposed); + using var builder = PooledHashSet>.GetInstance(); + foreach (var value in _lazyPredicateDataMap.Values) + { + if (value.TruePredicatedData != null) + { + Debug.Assert(!value.TruePredicatedData.IsDisposed); + Debug.Assert(builder.Add(value.TruePredicatedData)); + } + + if (value.FalsePredicatedData != null) + { + Debug.Assert(!value.FalsePredicatedData.IsDisposed); + Debug.Assert(builder.Add(value.FalsePredicatedData)); + } + } + } + } + + private void EnsurePredicatedData() + { + _lazyPredicateDataMap ??= new DictionaryAnalysisData(); + } + + protected void StartTrackingPredicatedData(AnalysisEntity predicatedEntity, DictionaryAnalysisData? truePredicatedData, DictionaryAnalysisData? falsePredicatedData) + { + Debug.Assert(predicatedEntity.IsCandidatePredicateEntity()); + Debug.Assert(predicatedEntity.CaptureId != null, "Currently we only support predicated data tracking for flow captures"); + + AssertValidAnalysisData(); + + EnsurePredicatedData(); + _lazyPredicateDataMap![predicatedEntity] = new PerEntityPredicatedAnalysisData(truePredicatedData, falsePredicatedData); + + AssertValidAnalysisData(); + } + + public void StopTrackingPredicatedData(AnalysisEntity predicatedEntity) + { + RoslynDebug.Assert(_lazyPredicateDataMap != null); + Debug.Assert(HasPredicatedDataForEntity(predicatedEntity)); + RoslynDebug.Assert(predicatedEntity.CaptureId != null, "Currently we only support predicated data tracking for flow captures"); + AssertValidAnalysisData(); + + if (_lazyPredicateDataMap.TryGetValue(predicatedEntity, out var perEntityPredicatedAnalysisData)) + { + perEntityPredicatedAnalysisData.Dispose(); + } + + _lazyPredicateDataMap.Remove(predicatedEntity); + if (_lazyPredicateDataMap.Count == 0) + { + ResetPredicatedData(); + } + + AssertValidAnalysisData(); + } + + public bool HasPredicatedDataForEntity(AnalysisEntity predicatedEntity) + => HasPredicatedData && _lazyPredicateDataMap!.ContainsKey(predicatedEntity); + + public void TransferPredicatedData(AnalysisEntity fromEntity, AnalysisEntity toEntity) + { + Debug.Assert(HasPredicatedDataForEntity(fromEntity)); + RoslynDebug.Assert(_lazyPredicateDataMap != null); + RoslynDebug.Assert(fromEntity.CaptureId != null, "Currently we only support predicated data tracking for flow captures"); + RoslynDebug.Assert(toEntity.CaptureId != null, "Currently we only support predicated data tracking for flow captures"); + AssertValidAnalysisData(); + + if (_lazyPredicateDataMap!.TryGetValue(fromEntity, out var fromEntityPredicatedData)) + { + _lazyPredicateDataMap[toEntity] = new PerEntityPredicatedAnalysisData(fromEntityPredicatedData); + } + + AssertValidAnalysisData(); + } + + protected PredicateValueKind ApplyPredicatedDataForEntity(DictionaryAnalysisData coreAnalysisData, AnalysisEntity predicatedEntity, bool trueData) + { + Debug.Assert(HasPredicatedDataForEntity(predicatedEntity)); + AssertValidAnalysisData(); + + var perEntityPredicateData = _lazyPredicateDataMap![predicatedEntity]; + var predicatedDataToApply = trueData ? perEntityPredicateData.TruePredicatedData : perEntityPredicateData.FalsePredicatedData; + if (predicatedDataToApply == null) + { + // Infeasible branch. + return PredicateValueKind.AlwaysFalse; + } + + ApplyPredicatedData(coreAnalysisData, predicatedDataToApply); + + // Predicate is always true if other branch predicate data is null. + var otherBranchPredicatedData = trueData ? perEntityPredicateData.FalsePredicatedData : perEntityPredicateData.TruePredicatedData; + return otherBranchPredicatedData == null ? + PredicateValueKind.AlwaysTrue : + PredicateValueKind.Unknown; + } + + protected virtual void ApplyPredicatedData(DictionaryAnalysisData coreAnalysisData, DictionaryAnalysisData predicatedData) + { + foreach (var kvp in predicatedData) + { + coreAnalysisData[kvp.Key] = kvp.Value; + } + } + + protected void RemoveEntriesInPredicatedData(TKey key) + { + RoslynDebug.Assert(_lazyPredicateDataMap != null); + Debug.Assert(HasPredicatedData); + AssertValidAnalysisData(); + + foreach (var kvp in _lazyPredicateDataMap) + { + if (kvp.Value.TruePredicatedData != null) + { + RemoveEntryInPredicatedData(key, kvp.Value.TruePredicatedData); + } + + if (kvp.Value.FalsePredicatedData != null) + { + RemoveEntryInPredicatedData(key, kvp.Value.FalsePredicatedData); + } + } + + AssertValidAnalysisData(); + } + + protected virtual void RemoveEntryInPredicatedData(TKey key, DictionaryAnalysisData predicatedData) + { + predicatedData.Remove(key); + } + + [return: NotNullIfNotNull(parameterName: nameof(fromData))] + private static DictionaryAnalysisData? Clone(DictionaryAnalysisData? fromData) + { + if (fromData == null) + { + return null; + } + + var clonedMap = new DictionaryAnalysisData(); + foreach (var kvp in fromData) + { + clonedMap.Add(kvp.Key, new PerEntityPredicatedAnalysisData(kvp.Value)); + } + + return clonedMap; + } + + private static DictionaryAnalysisData? Merge( + DictionaryAnalysisData? predicatedData1, + DictionaryAnalysisData? predicatedData2, + DictionaryAnalysisData coreAnalysisData1, + DictionaryAnalysisData coreAnalysisData2, + MapAbstractDomain coreDataAnalysisDomain, + Action, DictionaryAnalysisData> applyPredicatedData) + { + if (predicatedData1 == null) + { + if (predicatedData2 == null) + { + return null; + } + + return MergeForPredicatedDataInOneBranch(predicatedData2, coreAnalysisData1, coreDataAnalysisDomain); + } + else if (predicatedData2 == null) + { + return MergeForPredicatedDataInOneBranch(predicatedData1, coreAnalysisData2, coreDataAnalysisDomain); + } + + return MergeForPredicatedDataInBothBranches(predicatedData1, predicatedData2, + coreAnalysisData1, coreAnalysisData2, coreDataAnalysisDomain, applyPredicatedData); + } + + private static DictionaryAnalysisData MergeForPredicatedDataInOneBranch( + DictionaryAnalysisData predicatedData, + DictionaryAnalysisData coreAnalysisDataForOtherBranch, + MapAbstractDomain coreDataAnalysisDomain) + { + var result = new DictionaryAnalysisData(); + foreach (var kvp in predicatedData) + { + var resultTruePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.TruePredicatedData, coreAnalysisDataForOtherBranch, coreDataAnalysisDomain); + var resultFalsePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.FalsePredicatedData, coreAnalysisDataForOtherBranch, coreDataAnalysisDomain); + var perEntityPredicatedData = new PerEntityPredicatedAnalysisData(resultTruePredicatedData, resultFalsePredicatedData); + result.Add(kvp.Key, perEntityPredicatedData); + } + + return result; + } + + private static DictionaryAnalysisData? MergeForPredicatedDataInOneBranch( + DictionaryAnalysisData? predicatedData, + DictionaryAnalysisData coreAnalysisDataForOtherBranch, + MapAbstractDomain coreDataAnalysisDomain) + { + if (predicatedData == null) + { + return null; + } + + return coreDataAnalysisDomain.Merge(predicatedData, coreAnalysisDataForOtherBranch); + } + + private static DictionaryAnalysisData MergeForPredicatedDataInBothBranches( + DictionaryAnalysisData predicatedData1, + DictionaryAnalysisData predicatedData2, + DictionaryAnalysisData coreAnalysisData1, + DictionaryAnalysisData coreAnalysisData2, + MapAbstractDomain coreDataAnalysisDomain, + Action, DictionaryAnalysisData> applyPredicatedData) + { + var result = new DictionaryAnalysisData(); + foreach (var kvp in predicatedData1) + { + DictionaryAnalysisData? resultTruePredicatedData; + DictionaryAnalysisData? resultFalsePredicatedData; + if (!predicatedData2.TryGetValue(kvp.Key, out var value2)) + { + // Data predicated by the analysis entity present in only one branch. + // We should merge with the core non-predicate data in other branch. + resultTruePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.TruePredicatedData, coreAnalysisData2, coreDataAnalysisDomain); + resultFalsePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.FalsePredicatedData, coreAnalysisData2, coreDataAnalysisDomain); + } + else + { + // Data predicated by the analysis entity present in both branches. + resultTruePredicatedData = Merge(kvp.Value.TruePredicatedData, value2.TruePredicatedData, + coreAnalysisData1, coreAnalysisData2, coreDataAnalysisDomain, applyPredicatedData); + resultFalsePredicatedData = Merge(kvp.Value.FalsePredicatedData, value2.FalsePredicatedData, + coreAnalysisData1, coreAnalysisData2, coreDataAnalysisDomain, applyPredicatedData); + } + + var perEntityPredicatedData = new PerEntityPredicatedAnalysisData(resultTruePredicatedData, resultFalsePredicatedData); + result.Add(kvp.Key, perEntityPredicatedData); + } + + foreach (var kvp in predicatedData2) + { + if (!predicatedData1.TryGetValue(kvp.Key, out _)) + { + // Data predicated by the analysis entity present in only one branch. + // We should merge with the core non-predicate data in other branch. + var resultTruePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.TruePredicatedData, coreAnalysisData1, coreDataAnalysisDomain); + var resultFalsePredicatedData = MergeForPredicatedDataInOneBranch(kvp.Value.FalsePredicatedData, coreAnalysisData1, coreDataAnalysisDomain); + var perEntityPredicatedData = new PerEntityPredicatedAnalysisData(resultTruePredicatedData, resultFalsePredicatedData); + result.Add(kvp.Key, perEntityPredicatedData); + } + } + + return result; + } + + private static DictionaryAnalysisData? Merge( + DictionaryAnalysisData? predicateTrueOrFalseData1, + DictionaryAnalysisData? predicateTrueOrFalseData2, + DictionaryAnalysisData coreAnalysisData1, + DictionaryAnalysisData coreAnalysisData2, + MapAbstractDomain coreDataAnalysisDomain, + Action, DictionaryAnalysisData> applyPredicatedData) + { + if (predicateTrueOrFalseData1 == null) + { + return predicateTrueOrFalseData2 != null ? + CloneAndApplyPredicatedData(coreAnalysisData2, predicateTrueOrFalseData2, applyPredicatedData) : + null; + } + else if (predicateTrueOrFalseData2 == null) + { + return CloneAndApplyPredicatedData(coreAnalysisData1, predicateTrueOrFalseData1, applyPredicatedData); + } + + var appliedPredicatedData1 = CloneAndApplyPredicatedData(coreAnalysisData1, predicateTrueOrFalseData1, applyPredicatedData); + var appliedPredicatedData2 = CloneAndApplyPredicatedData(coreAnalysisData2, predicateTrueOrFalseData2, applyPredicatedData); + + return coreDataAnalysisDomain.Merge(appliedPredicatedData1, appliedPredicatedData2); + } + + private static DictionaryAnalysisData CloneAndApplyPredicatedData( + DictionaryAnalysisData coreAnalysisData, + DictionaryAnalysisData predicateTrueOrFalseData, + Action, DictionaryAnalysisData> applyPredicatedData) + { + var result = new DictionaryAnalysisData(coreAnalysisData); + applyPredicatedData(result, predicateTrueOrFalseData); + return result; + } + + protected int BaseCompareHelper(PredicatedAnalysisData newData) + { + if (!IsReachableBlockData && newData.IsReachableBlockData) + { + return -1; + } + + if (_lazyPredicateDataMap == null) + { + return newData._lazyPredicateDataMap == null ? 0 : -1; + } + else if (newData._lazyPredicateDataMap == null) + { + return 1; + } + + if (ReferenceEquals(this, newData)) + { + return 0; + } + + // Note that predicate maps can add or remove entries based on core analysis data entries. + // We can only determine if the predicate data is equal or not. + return Equals(newData) ? 0 : -1; + } + + protected bool Equals(PredicatedAnalysisData other) + { + if (_lazyPredicateDataMap == null) + { + return other._lazyPredicateDataMap == null; + } + else if (other._lazyPredicateDataMap == null || + _lazyPredicateDataMap.Count != other._lazyPredicateDataMap.Count) + { + return false; + } + else + { + foreach (var kvp in _lazyPredicateDataMap) + { + if (!other._lazyPredicateDataMap.TryGetValue(kvp.Key, out var otherValue) || + !EqualsHelper(kvp.Value.TruePredicatedData, otherValue.TruePredicatedData) || + !EqualsHelper(kvp.Value.FalsePredicatedData, otherValue.FalsePredicatedData)) + { + return false; + } + } + } + + return true; + } + + protected static bool EqualsHelper(DictionaryAnalysisData? dict1, DictionaryAnalysisData? dict2) + { + if (dict1 == null) + { + return dict2 == null; + } + else if (dict2 == null || dict1.Count != dict2.Count) + { + return false; + } + + return dict1.Keys.All(key => dict2.TryGetValue(key, out var value2) && + EqualityComparer.Default.Equals(dict1[key], value2)); + } + + protected void ResetPredicatedData() + { + if (_lazyPredicateDataMap == null) + { + return; + } + + if (!_lazyPredicateDataMap.IsDisposed) + { + _lazyPredicateDataMap.Values.Dispose(); + _lazyPredicateDataMap.Dispose(); + } + + Debug.Assert(_lazyPredicateDataMap.IsDisposed); + _lazyPredicateDataMap = null; + } + + [Conditional("DEBUG")] + protected void AssertValidPredicatedAnalysisData(Action> assertValidAnalysisData) + { + if (HasPredicatedData) + { + RoslynDebug.Assert(_lazyPredicateDataMap != null); + AssertValidAnalysisData(); + + foreach (var kvp in _lazyPredicateDataMap) + { + if (kvp.Value.TruePredicatedData != null) + { + assertValidAnalysisData(kvp.Value.TruePredicatedData); + } + + if (kvp.Value.FalsePredicatedData != null) + { + assertValidAnalysisData(kvp.Value.FalsePredicatedData); + } + } + } + } + + protected override void Dispose(bool disposing) + { + if (IsDisposed) + { + return; + } + + if (disposing) + { + ResetPredicatedData(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisDataDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisDataDomain.cs new file mode 100644 index 0000000000000..16a4613237d88 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/PredicatedAnalysisDataDomain.cs @@ -0,0 +1,49 @@ +// 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.FlowAnalysis.DataFlow +{ + /// + /// An abstract domain implementation for analyses that store dictionary typed data along with predicated data. + /// + public class PredicatedAnalysisDataDomain : AbstractAnalysisDomain + where TAnalysisData : AnalysisEntityBasedPredicateAnalysisData + { + public PredicatedAnalysisDataDomain(MapAbstractDomain coreDataAnalysisDomain) + { + CoreDataAnalysisDomain = coreDataAnalysisDomain; + } + + protected MapAbstractDomain CoreDataAnalysisDomain { get; } + + public override TAnalysisData Clone(TAnalysisData value) => (TAnalysisData)value.Clone(); + + public override int Compare(TAnalysisData oldValue, TAnalysisData newValue) => oldValue.Compare(newValue, CoreDataAnalysisDomain); + + public override bool Equals(TAnalysisData value1, TAnalysisData value2) => value1.Equals(value2); + + public override TAnalysisData Merge(TAnalysisData value1, TAnalysisData value2) + { + AnalysisEntityBasedPredicateAnalysisData result; + if (ReferenceEquals(value1, value2)) + { + result = value1.Clone(); + } + else if (!value1.IsReachableBlockData && value2.IsReachableBlockData) + { + result = value2.Clone(); + } + else if (!value2.IsReachableBlockData && value1.IsReachableBlockData) + { + result = value1.Clone(); + } + else + { + result = value1.WithMergedData(value2, CoreDataAnalysisDomain); + } + + return (TAnalysisData)result; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/SetAbstractDomain.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/SetAbstractDomain.cs new file mode 100644 index 0000000000000..96ea8e00076be --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/SetAbstractDomain.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; + +#pragma warning disable CA1000 // Do not declare static members on generic types + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public class SetAbstractDomain : AbstractDomain> + { + private SetAbstractDomain() { } + + public static SetAbstractDomain Default { get; } = new SetAbstractDomain(); + + public override ImmutableHashSet Bottom => ImmutableHashSet.Empty; + + public override int Compare(ImmutableHashSet oldValue, ImmutableHashSet newValue, bool assertMonotonicity) + { + if (ReferenceEquals(oldValue, newValue)) + { + return 0; + } + + int result; + // PERF: Avoid additional hash set allocation by using overload which takes + // a set argument instead of IEnumerable argument. + var isSubset = oldValue.IsSubsetOfSet(newValue); + + if (isSubset && + oldValue.Count == newValue.Count) + { + // oldValue == newValue + result = 0; + } + else if (isSubset) + { + // oldValue < newValue + result = -1; + } + else + { + // oldValue > newValue + result = 1; + } + + return result; + } + + public override ImmutableHashSet Merge(ImmutableHashSet value1, ImmutableHashSet value2) => MergeOrIntersect(value1, value2, merge: true); + + public ImmutableHashSet Intersect(ImmutableHashSet value1, ImmutableHashSet value2) => MergeOrIntersect(value1, value2, merge: false); + + private static ImmutableHashSet MergeOrIntersect(ImmutableHashSet value1, ImmutableHashSet value2, bool merge) + { + if (value1.IsEmpty) + { + return merge ? value2 : value1; + } + else if (value2.IsEmpty || ReferenceEquals(value1, value2)) + { + return merge ? value1 : value2; + } + + // PERF: Avoid additional allocations by using the overload that takes a set argument + // instead of IEnumerable argument. + return merge ? value1.AddRange(value2) : value1.IntersectSet(value2); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/StackGuard.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/StackGuard.cs new file mode 100644 index 0000000000000..a264210b14d37 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/StackGuard.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.Runtime.CompilerServices; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + /// + /// Stack guard for to ensure sufficient stack while recursively visiting the operation tree. + /// + internal static class StackGuard + { + public const int MaxUncheckedRecursionDepth = 20; + + public static void EnsureSufficientExecutionStack(int recursionDepth) + { + if (recursionDepth > MaxUncheckedRecursionDepth) + { + RuntimeHelpers.EnsureSufficientExecutionStack(); + } + } + + public static bool IsInsufficientExecutionStackException(Exception ex) + { + return ex.GetType().Name == "InsufficientExecutionStackException"; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ThrownExceptionInfo.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ThrownExceptionInfo.cs new file mode 100644 index 0000000000000..6bd1c8261ff02 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/FlowAnalysis/Framework/DataFlow/ThrownExceptionInfo.cs @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; + +namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow +{ + public sealed class ThrownExceptionInfo : IEquatable + { + private ThrownExceptionInfo( + BasicBlock block, + INamedTypeSymbol exceptionType, + ImmutableStack? interproceduralCallStack, + bool isDefaultExceptionForExceptionsPathAnalysis) + { + BasicBlockOrdinal = block.Ordinal; + HandlingCatchRegion = GetHandlerRegion(block, exceptionType); + ContainingFinallyRegion = block.GetContainingRegionOfKind(ControlFlowRegionKind.Finally); + ExceptionType = exceptionType ?? throw new ArgumentNullException(nameof(exceptionType)); + InterproceduralCallStack = interproceduralCallStack ?? ImmutableStack.Empty; + IsDefaultExceptionForExceptionsPathAnalysis = isDefaultExceptionForExceptionsPathAnalysis; + } + + internal static ThrownExceptionInfo Create(BasicBlock block, INamedTypeSymbol exceptionType, ImmutableStack? interproceduralCallStack) + { + return new ThrownExceptionInfo(block, exceptionType, interproceduralCallStack, isDefaultExceptionForExceptionsPathAnalysis: false); + } + + internal static ThrownExceptionInfo CreateDefaultInfoForExceptionsPathAnalysis(BasicBlock block, WellKnownTypeProvider wellKnownTypeProvider, ImmutableStack? interproceduralCallStack) + { + var exceptionNamedType = wellKnownTypeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemException); + RoslynDebug.Assert(exceptionNamedType != null); + return new ThrownExceptionInfo(block, exceptionNamedType, interproceduralCallStack, isDefaultExceptionForExceptionsPathAnalysis: true); + } + + private static ControlFlowRegion? GetHandlerRegion(BasicBlock block, INamedTypeSymbol exceptionType) + { + var enclosingRegion = block.EnclosingRegion; + while (enclosingRegion != null) + { + if (enclosingRegion.Kind == ControlFlowRegionKind.TryAndCatch) + { + Debug.Assert(enclosingRegion.NestedRegions[0].Kind == ControlFlowRegionKind.Try); + foreach (var nestedRegion in enclosingRegion.NestedRegions.Skip(1)) + { + if (nestedRegion.Kind == ControlFlowRegionKind.Catch && + (nestedRegion.ExceptionType == null || + nestedRegion.ExceptionType.SpecialType == SpecialType.System_Object || + exceptionType.DerivesFrom(nestedRegion.ExceptionType, baseTypesOnly: true))) + { + return nestedRegion; + } + } + } + + enclosingRegion = enclosingRegion.EnclosingRegion; + } + + return null; + } + + internal ThrownExceptionInfo With(BasicBlock block, ImmutableStack? interproceduralCallStack) + { + Debug.Assert(interproceduralCallStack != InterproceduralCallStack); + return new ThrownExceptionInfo(block, ExceptionType, interproceduralCallStack, IsDefaultExceptionForExceptionsPathAnalysis); + } + + /// + /// Ordinal of the basic block where this exception is thrown. + /// + internal int BasicBlockOrdinal { get; } + + /// + /// Optional catch handler that handles this exception. + /// + internal ControlFlowRegion? HandlingCatchRegion { get; } + + /// + /// If the exception happens within a finally region, this points to that finally. + /// + internal ControlFlowRegion? ContainingFinallyRegion { get; } + + internal INamedTypeSymbol ExceptionType { get; } + internal ImmutableStack InterproceduralCallStack { get; } + internal bool IsDefaultExceptionForExceptionsPathAnalysis { get; } + + public bool Equals(ThrownExceptionInfo? other) + { + return other != null && + BasicBlockOrdinal == other.BasicBlockOrdinal && + HandlingCatchRegion == other.HandlingCatchRegion && + ContainingFinallyRegion == other.ContainingFinallyRegion && + SymbolEqualityComparer.Default.Equals(ExceptionType, other.ExceptionType) && + InterproceduralCallStack.SequenceEqual(other.InterproceduralCallStack) && + IsDefaultExceptionForExceptionsPathAnalysis == other.IsDefaultExceptionForExceptionsPathAnalysis; + } + + public override bool Equals(object obj) + => Equals(obj as ThrownExceptionInfo); + + public override int GetHashCode() + { + var hashCode = new RoslynHashCode(); + HashUtilities.Combine(InterproceduralCallStack, ref hashCode); + hashCode.Add(BasicBlockOrdinal.GetHashCode()); + hashCode.Add(HandlingCatchRegion.GetHashCodeOrDefault()); + hashCode.Add(ContainingFinallyRegion.GetHashCodeOrDefault()); + hashCode.Add(ExceptionType.GetHashCode()); + hashCode.Add(IsDefaultExceptionForExceptionsPathAnalysis.GetHashCode()); + return hashCode.ToHashCode(); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/AnalyzerOptionsExtensions_FlowAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/AnalyzerOptionsExtensions_FlowAnalysis.cs new file mode 100644 index 0000000000000..62facda5a5d19 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/AnalyzerOptionsExtensions_FlowAnalysis.cs @@ -0,0 +1,122 @@ +// 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; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow; +using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.PointsToAnalysis; + +namespace Analyzer.Utilities +{ + public static partial class AnalyzerOptionsExtensions + { + public static InterproceduralAnalysisKind GetInterproceduralAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + InterproceduralAnalysisKind defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetInterproceduralAnalysisKindOption(rule, tree, compilation, defaultValue) + : defaultValue; + + public static InterproceduralAnalysisKind GetInterproceduralAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + InterproceduralAnalysisKind defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.InterproceduralAnalysisKind, rule, tree, compilation, defaultValue); + + public static DisposeAnalysisKind GetDisposeAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + DisposeAnalysisKind defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetDisposeAnalysisKindOption(rule, tree, compilation, defaultValue) + : defaultValue; + + public static DisposeAnalysisKind GetDisposeAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + DisposeAnalysisKind defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.DisposeAnalysisKind, rule, tree, compilation, defaultValue); + + public static bool GetDisposeOwnershipTransferAtConstructorOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + bool defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetDisposeOwnershipTransferAtConstructorOption(rule, tree, compilation, defaultValue) + : defaultValue; + + public static bool GetDisposeOwnershipTransferAtConstructorOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + bool defaultValue) + => options.GetBoolOptionValue(EditorConfigOptionNames.DisposeOwnershipTransferAtConstructor, rule, tree, compilation, defaultValue); + + public static bool GetDisposeOwnershipTransferAtMethodCall( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + bool defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetDisposeOwnershipTransferAtMethodCall(rule, tree, compilation, defaultValue) + : defaultValue; + + public static bool GetDisposeOwnershipTransferAtMethodCall( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + bool defaultValue) + => options.GetBoolOptionValue(EditorConfigOptionNames.DisposeOwnershipTransferAtMethodCall, rule, tree, compilation, defaultValue); + + public static bool GetCopyAnalysisOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + bool defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetCopyAnalysisOption(rule, tree, compilation, defaultValue) + : defaultValue; + + public static bool GetCopyAnalysisOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + bool defaultValue) + => options.GetBoolOptionValue(EditorConfigOptionNames.CopyAnalysis, rule, tree, compilation, defaultValue); + + public static PointsToAnalysisKind GetPointsToAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + ISymbol symbol, + Compilation compilation, + PointsToAnalysisKind defaultValue) + => TryGetSyntaxTreeForOption(symbol, out var tree) + ? options.GetPointsToAnalysisKindOption(rule, tree, compilation, defaultValue) + : defaultValue; + + public static PointsToAnalysisKind GetPointsToAnalysisKindOption( + this AnalyzerOptions options, + DiagnosticDescriptor rule, + SyntaxTree tree, + Compilation compilation, + PointsToAnalysisKind defaultValue) + => options.GetNonFlagsEnumOptionValue(EditorConfigOptionNames.PointsToAnalysisKind, rule, tree, compilation, defaultValue); + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/DisposeAnalysisKind.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/DisposeAnalysisKind.cs new file mode 100644 index 0000000000000..7165029f30208 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/DisposeAnalysisKind.cs @@ -0,0 +1,72 @@ +// 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 Analyzer.Utilities.Extensions; + +namespace Analyzer.Utilities +{ + /// + /// Describes a group of effective for symbols. + /// + public enum DisposeAnalysisKind + { + // NOTE: Below fields names are used in the .editorconfig specification + // for DisposeAnalysisKind option. Hence the names should *not* be modified, + // as that would be a breaking change for .editorconfig specification. + + /// + /// Track and report missing dispose violations on all paths (non-exception and exception paths). + /// Additionally, also flag use of non-recommended dispose patterns that may cause + /// potential dispose leaks. + /// + AllPaths, + + /// + /// Track and report missing dispose violations on all paths (non-exception and exception paths). + /// Do not flag use of non-recommended dispose patterns that may cause + /// potential dispose leaks. + /// + AllPathsOnlyNotDisposed, + + /// + /// Track and report missing dispose violations only on non-exception program paths. + /// Additionally, also flag use of non-recommended dispose patterns that may cause + /// potential dispose leaks. + /// + NonExceptionPaths, + + /// + /// Track and report missing dispose violations only on non-exception program paths. + /// Do not flag use of non-recommended dispose patterns that may cause + /// potential dispose leaks. + /// + NonExceptionPathsOnlyNotDisposed, + } + + internal static class DisposeAnalysisKindExtensions + { + public static bool AreExceptionPathsAndMayBeNotDisposedViolationsEnabled(this DisposeAnalysisKind disposeAnalysisKind) + => disposeAnalysisKind.AreExceptionPathsEnabled() && disposeAnalysisKind.AreMayBeNotDisposedViolationsEnabled(); + + public static bool AreExceptionPathsEnabled(this DisposeAnalysisKind disposeAnalysisKind) + { + return disposeAnalysisKind switch + { + DisposeAnalysisKind.NonExceptionPaths + or DisposeAnalysisKind.NonExceptionPathsOnlyNotDisposed => false, + _ => true, + }; + } + + public static bool AreMayBeNotDisposedViolationsEnabled(this DisposeAnalysisKind disposeAnalysisKind) + { + return disposeAnalysisKind switch + { + DisposeAnalysisKind.AllPathsOnlyNotDisposed + or DisposeAnalysisKind.NonExceptionPathsOnlyNotDisposed => false, + _ => true, + }; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/EditorConfigOptionNames_FlowAnalysis.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/EditorConfigOptionNames_FlowAnalysis.cs new file mode 100644 index 0000000000000..f0ca38b5f183d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/Options/EditorConfigOptionNames_FlowAnalysis.cs @@ -0,0 +1,80 @@ +// 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 Analyzer.Utilities +{ + /// + /// Option names to configure analyzer execution through an .editorconfig file. + /// + internal static partial class EditorConfigOptionNames + { + // ============================================================================================================= + // NOTE: Keep this file in sync with documentation at '<%REPO_ROOT%>\docs\Analyzer Configuration.md' + // ============================================================================================================= + + #region Flow analysis options + + /// + /// Option to configure interprocedural dataflow analysis kind, i.e. . + /// Allowed option values: Fields from . + /// + public const string InterproceduralAnalysisKind = "interprocedural_analysis_kind"; + + /// + /// Integral option to configure maximum method call chain for interprocedural dataflow analysis. + /// + public const string MaxInterproceduralMethodCallChain = "max_interprocedural_method_call_chain"; + + /// + /// Integral option to configure maximum lambda or local function call chain for interprocedural dataflow analysis. + /// + public const string MaxInterproceduralLambdaOrLocalFunctionCallChain = "max_interprocedural_lambda_or_local_function_call_chain"; + + /// + /// String option to configure dispose analysis kind, primarily for CA2000 (DisposeObjectsBeforeLosingScope). + /// Allowed option values: Fields from DisposeAnalysisKind enum. + /// + public const string DisposeAnalysisKind = "dispose_analysis_kind"; + + /// + /// Boolean option to configure if passing a disposable object as a constructor argument should be considered + /// as a dispose ownership transfer, primarily for CA2000 (DisposeObjectsBeforeLosingScope). + /// + public const string DisposeOwnershipTransferAtConstructor = "dispose_ownership_transfer_at_constructor"; + + /// + /// Boolean option to configure if passing a disposable object as an argument to a method invocation should be considered + /// as a dispose ownership transfer to the caller, primarily for CA2000 (DisposeObjectsBeforeLosingScope) + /// + public const string DisposeOwnershipTransferAtMethodCall = "dispose_ownership_transfer_at_method_call"; + + /// + /// Option to configure whether copy analysis should be executed during dataflow analysis. + /// Copy analysis tracks value and reference copies. For example, + /// + /// void M(string str1, string str2) + /// { + /// if (str1 != null) + /// { + /// if (str1 == str2) + /// { + /// if (str2 != null) // This is redundant as 'str1' and 'str2' are value copies on this code path. This requires copy analysis. + /// { + /// } + /// } + /// } + /// } + /// + /// + public const string CopyAnalysis = "copy_analysis"; + + /// + /// Option to configure points to analysis kind, i.e. . + /// Allowed option values: Fields from . + /// + public const string PointsToAnalysisKind = "points_to_analysis_kind"; + + #endregion + } +} diff --git a/src/RoslynAnalyzers/Utilities/FlowAnalysis/SingleThreadedConcurrentDictionary.cs b/src/RoslynAnalyzers/Utilities/FlowAnalysis/SingleThreadedConcurrentDictionary.cs new file mode 100644 index 0000000000000..fea2cf65bc0db --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/FlowAnalysis/SingleThreadedConcurrentDictionary.cs @@ -0,0 +1,70 @@ +// 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.Concurrent; + +namespace Analyzer.Utilities +{ + /// + /// Like ConcurrentDictionary, but single threaded valueFactory execution in GetOrAdd. + /// + /// Useful for long running valueFactory functions, like say performing + /// dataflow analysis. This way DFA is invoked only once per key, even if multiple + /// threads simultaneously request the same key. +#pragma warning disable CA1812 // SingleThreadedConcurrentDictionary is too used. + internal sealed class SingleThreadedConcurrentDictionary + where TValue : class +#pragma warning restore CA1812 + { + /// + /// An Entry itself serves a lock object, and contains the real value. + /// + private sealed class Entry + { + public TValue? Value { get; set; } + } + + /// + /// Holds entries, which contain the actual values. + /// + private readonly ConcurrentDictionary BackingDictionary = new(); + + /// + /// Adds a key/value pair using the specified function if the key does not already exist. Returns the new value, or the existing value if the key exists. + /// + /// Key to add. + /// Function to be invoked to generate the key, if necessary. + /// Value of the key, which will either be the existing value, or new value if the key was not in the dictionary. + public TValue? GetOrAdd(TKey key, Func valueFactory) + { + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + if (valueFactory == null) + { + throw new ArgumentNullException(nameof(valueFactory)); + } + + // The return value of ConcurrentDictionary.GetOrAdd() is + // consistent, i.e. the same instance no matter how many times + // this valueFactory gets executed. So for a given key, we'll + // always get back the same Entry instance. + Entry entry = this.BackingDictionary.GetOrAdd(key, (_) => new Entry()); + if (entry.Value != null) + { + return entry.Value; + } + + lock (entry) + { + entry.Value ??= valueFactory(key); + } + + return entry.Value; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpRefactoringHelpers.cs b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpRefactoringHelpers.cs new file mode 100644 index 0000000000000..865f29d8eaee6 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpRefactoringHelpers.cs @@ -0,0 +1,59 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Analyzer.Utilities +{ + internal sealed class CSharpRefactoringHelpers : AbstractRefactoringHelpers + { + public static CSharpRefactoringHelpers Instance { get; } = new CSharpRefactoringHelpers(); + + private CSharpRefactoringHelpers() + { + } + + protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; + + protected override IEnumerable ExtractNodesSimple(SyntaxNode? node, ISyntaxFacts syntaxFacts) + { + if (node == null) + { + yield break; + } + + foreach (var extractedNode in base.ExtractNodesSimple(node, syntaxFacts)) + { + yield return extractedNode; + } + + // `var a = b;` + // -> `var a = b`; + if (node is LocalDeclarationStatementSyntax localDeclaration) + { + yield return localDeclaration.Declaration; + } + + // var `a = b`; + if (node is VariableDeclaratorSyntax declarator) + { + var declaration = declarator.Parent; + if (declaration?.Parent is LocalDeclarationStatementSyntax localDeclarationStatement) + { + var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement); + if (variables.Count == 1) + { + // -> `var a = b`; + yield return declaration; + + // -> `var a = b;` + yield return localDeclarationStatement; + } + } + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxFacts.cs b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxFacts.cs new file mode 100644 index 0000000000000..53129a8631557 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxFacts.cs @@ -0,0 +1,174 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable disable warnings + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Analyzer.Utilities +{ + internal sealed class CSharpSyntaxFacts : AbstractSyntaxFacts, ISyntaxFacts + { + public static CSharpSyntaxFacts Instance { get; } = new CSharpSyntaxFacts(); + + private CSharpSyntaxFacts() + { + } + + public override ISyntaxKinds SyntaxKinds => CSharpSyntaxKinds.Instance; + + public SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node) + => ((ExpressionStatementSyntax)node).Expression; + + public bool IsSimpleAssignmentStatement(SyntaxNode statement) + { + return statement is ExpressionStatementSyntax exprStatement + && exprStatement.Expression.IsKind(SyntaxKind.SimpleAssignmentExpression); + } + + public void GetPartsOfAssignmentExpressionOrStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right) + { + var expression = statement; + if (statement is ExpressionStatementSyntax expressionStatement) + { + expression = expressionStatement.Expression; + } + + var assignment = (AssignmentExpressionSyntax)expression; + left = assignment.Left; + operatorToken = assignment.OperatorToken; + right = assignment.Right; + } + + public override SyntaxList GetAttributeLists(SyntaxNode node) + => node.GetAttributeLists(); + + public SeparatedSyntaxList GetVariablesOfLocalDeclarationStatement(SyntaxNode node) + => ((LocalDeclarationStatementSyntax)node).Declaration.Variables; + + public SyntaxNode GetInitializerOfVariableDeclarator(SyntaxNode node) + => ((VariableDeclaratorSyntax)node).Initializer; + + public SyntaxNode? GetValueOfEqualsValueClause(SyntaxNode? node) + => ((EqualsValueClauseSyntax?)node)?.Value; + + public bool IsOnTypeHeader(SyntaxNode root, int position, bool fullHeader, [NotNullWhen(true)] out SyntaxNode? typeDeclaration) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + typeDeclaration = null; + return false; + } + + typeDeclaration = node; + var lastToken = (node as TypeDeclarationSyntax)?.TypeParameterList?.GetLastToken() ?? node.Identifier; + if (fullHeader) + lastToken = node.BaseList?.GetLastToken() ?? lastToken; + + return IsOnHeader(root, position, node, lastToken); + } + + public bool IsOnPropertyDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? propertyDeclaration) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + propertyDeclaration = null; + return false; + } + + propertyDeclaration = node; + return IsOnHeader(root, position, node, node.Identifier); + } + + public bool IsOnParameterHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? parameter) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + parameter = null; + return false; + } + + parameter = node; + return IsOnHeader(root, position, node, node); + } + + public bool IsOnMethodHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? method) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + method = null; + return false; + } + + method = node; + return IsOnHeader(root, position, node, node.ParameterList); + } + + public bool IsOnLocalFunctionHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localFunction) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + localFunction = null; + return false; + } + + localFunction = node; + return IsOnHeader(root, position, node, node.ParameterList); + } + + public bool IsOnLocalDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localDeclaration) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + localDeclaration = null; + return false; + } + + localDeclaration = node; + var initializersExpressions = node.Declaration.Variables + .Where(v => v.Initializer != null) + .Select(initializedV => initializedV.Initializer.Value) + .ToImmutableArray(); + return IsOnHeader(root, position, node, node, holes: initializersExpressions); + } + + public bool IsOnIfStatementHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? ifStatement) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + ifStatement = null; + return false; + } + + ifStatement = node; + return IsOnHeader(root, position, node, node.CloseParenToken); + } + + public bool IsOnForeachHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? foreachStatement) + { + var node = TryGetAncestorForLocation(root, position); + if (node is null) + { + foreachStatement = null; + return false; + } + + foreachStatement = node; + return IsOnHeader(root, position, node, node.CloseParenToken); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxKinds.cs b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxKinds.cs new file mode 100644 index 0000000000000..dc5aaad2aa219 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/CSharpSyntaxKinds.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.CSharp; + +namespace Analyzer.Utilities +{ + internal sealed class CSharpSyntaxKinds : ISyntaxKinds + { + public static CSharpSyntaxKinds Instance { get; } = new CSharpSyntaxKinds(); + + private CSharpSyntaxKinds() + { + } + + public int EndOfFileToken => (int)SyntaxKind.EndOfFileToken; + + public int ExpressionStatement => (int)SyntaxKind.ExpressionStatement; + public int LocalDeclarationStatement => (int)SyntaxKind.LocalDeclarationStatement; + + public int VariableDeclarator => (int)SyntaxKind.VariableDeclarator; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Extensions/SyntaxNodeExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Extensions/SyntaxNodeExtensions.cs new file mode 100644 index 0000000000000..a1bbc7e6c04c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Extensions/SyntaxNodeExtensions.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class SyntaxNodeExtensions + { + public static SyntaxList GetAttributeLists(this SyntaxNode declaration) + => declaration switch + { + MemberDeclarationSyntax memberDecl => memberDecl.AttributeLists, + AccessorDeclarationSyntax accessor => accessor.AttributeLists, + ParameterSyntax parameter => parameter.AttributeLists, + CompilationUnitSyntax compilationUnit => compilationUnit.AttributeLists, + _ => default, + }; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.projitems new file mode 100644 index 0000000000000..2378d4887cee1 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.projitems @@ -0,0 +1,17 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + + Refactoring.Utilities + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.shproj new file mode 100644 index 0000000000000..c4a40a213ce0c --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.CSharp/Refactoring.CSharp.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {3055F932-0D1E-4823-A03A-7B62C7639BDA} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/StatementSyntaxExtensions.vb b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/StatementSyntaxExtensions.vb new file mode 100644 index 0000000000000..a5f8f00e091a9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/StatementSyntaxExtensions.vb @@ -0,0 +1,56 @@ +' 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. + +Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Global.Analyzer.Utilities.Extensions + + Public Module StatementSyntaxExtensions + + + Public Function GetAsClause(member As StatementSyntax) As AsClauseSyntax + If member IsNot Nothing Then + Select Case member.Kind + Case SyntaxKind.FunctionBlock + Return DirectCast(member, MethodBlockSyntax).SubOrFunctionStatement.AsClause + Case SyntaxKind.OperatorBlock + Return DirectCast(member, OperatorBlockSyntax).OperatorStatement.AsClause + Case SyntaxKind.FunctionStatement + Return DirectCast(member, MethodStatementSyntax).AsClause + Case SyntaxKind.OperatorStatement + Return DirectCast(member, OperatorStatementSyntax).AsClause + Case SyntaxKind.DeclareFunctionStatement + Return DirectCast(member, DeclareStatementSyntax).AsClause + Case SyntaxKind.DelegateFunctionStatement + Return DirectCast(member, DelegateStatementSyntax).AsClause + Case SyntaxKind.PropertyBlock + Return DirectCast(member, PropertyBlockSyntax).PropertyStatement.AsClause + Case SyntaxKind.PropertyStatement + Return DirectCast(member, PropertyStatementSyntax).AsClause + Case SyntaxKind.EventBlock + Return DirectCast(member, EventBlockSyntax).EventStatement.AsClause + Case SyntaxKind.EventStatement + Return DirectCast(member, EventStatementSyntax).AsClause + End Select + End If + + Return Nothing + End Function + + + Public Function GetReturnType(member As StatementSyntax) As TypeSyntax + Dim asClause = member.GetAsClause() + Return asClause?.Type + End Function + + + Public Function HasReturnType(member As StatementSyntax) As Boolean + Return member.GetReturnType() IsNot Nothing + End Function + + End Module + +End Namespace diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/SyntaxNodeExtensions.vb b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/SyntaxNodeExtensions.vb new file mode 100644 index 0000000000000..44a3f4628a7b4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Extensions/SyntaxNodeExtensions.vb @@ -0,0 +1,85 @@ +' 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. + +Imports System.Runtime.CompilerServices +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Global.Analyzer.Utilities.Extensions + + Friend Module SyntaxNodeExtensions + + + Public Function GetAttributeLists(node As SyntaxNode) As SyntaxList(Of AttributeListSyntax) + Select Case node.Kind + Case SyntaxKind.CompilationUnit + Return SyntaxFactory.List(DirectCast(node, CompilationUnitSyntax).Attributes.SelectMany(Function(s) s.AttributeLists)) + Case SyntaxKind.ClassBlock + Return DirectCast(node, ClassBlockSyntax).BlockStatement.AttributeLists + Case SyntaxKind.ClassStatement + Return DirectCast(node, ClassStatementSyntax).AttributeLists + Case SyntaxKind.StructureBlock + Return DirectCast(node, StructureBlockSyntax).BlockStatement.AttributeLists + Case SyntaxKind.StructureStatement + Return DirectCast(node, StructureStatementSyntax).AttributeLists + Case SyntaxKind.InterfaceBlock + Return DirectCast(node, InterfaceBlockSyntax).BlockStatement.AttributeLists + Case SyntaxKind.InterfaceStatement + Return DirectCast(node, InterfaceStatementSyntax).AttributeLists + Case SyntaxKind.EnumBlock + Return DirectCast(node, EnumBlockSyntax).EnumStatement.AttributeLists + Case SyntaxKind.EnumStatement + Return DirectCast(node, EnumStatementSyntax).AttributeLists + Case SyntaxKind.EnumMemberDeclaration + Return DirectCast(node, EnumMemberDeclarationSyntax).AttributeLists + Case SyntaxKind.DelegateFunctionStatement, + SyntaxKind.DelegateSubStatement + Return DirectCast(node, DelegateStatementSyntax).AttributeLists + Case SyntaxKind.FieldDeclaration + Return DirectCast(node, FieldDeclarationSyntax).AttributeLists + Case SyntaxKind.FunctionBlock, + SyntaxKind.SubBlock, + SyntaxKind.ConstructorBlock + Return DirectCast(node, MethodBlockBaseSyntax).BlockStatement.AttributeLists + Case SyntaxKind.FunctionStatement, + SyntaxKind.SubStatement + Return DirectCast(node, MethodStatementSyntax).AttributeLists + Case SyntaxKind.SubNewStatement + Return DirectCast(node, SubNewStatementSyntax).AttributeLists + Case SyntaxKind.Parameter + Return DirectCast(node, ParameterSyntax).AttributeLists + Case SyntaxKind.PropertyBlock + Return DirectCast(node, PropertyBlockSyntax).PropertyStatement.AttributeLists + Case SyntaxKind.PropertyStatement + Return DirectCast(node, PropertyStatementSyntax).AttributeLists + Case SyntaxKind.OperatorBlock + Return DirectCast(node, OperatorBlockSyntax).BlockStatement.AttributeLists + Case SyntaxKind.OperatorStatement + Return DirectCast(node, OperatorStatementSyntax).AttributeLists + Case SyntaxKind.EventBlock + Return DirectCast(node, EventBlockSyntax).EventStatement.AttributeLists + Case SyntaxKind.EventStatement + Return DirectCast(node, EventStatementSyntax).AttributeLists + Case SyntaxKind.GetAccessorBlock, + SyntaxKind.SetAccessorBlock, + SyntaxKind.AddHandlerAccessorBlock, + SyntaxKind.RemoveHandlerAccessorBlock, + SyntaxKind.RaiseEventAccessorBlock + Return DirectCast(node, AccessorBlockSyntax).AccessorStatement.AttributeLists + Case SyntaxKind.GetAccessorStatement, + SyntaxKind.SetAccessorStatement, + SyntaxKind.AddHandlerAccessorStatement, + SyntaxKind.RemoveHandlerAccessorStatement, + SyntaxKind.RaiseEventAccessorStatement + Return DirectCast(node, AccessorStatementSyntax).AttributeLists + Case Else + Return Nothing + End Select + End Function + + End Module + +End Namespace + diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.projitems new file mode 100644 index 0000000000000..63a1fd4b1f86f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.projitems @@ -0,0 +1,18 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + + Refactoring.Utilities + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.shproj new file mode 100644 index 0000000000000..d1ec8781a6dab --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/Refactoring.VisualBasic.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {4C362C30-C4B1-4C4B-A545-DBF67C7E9153} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicRefactoringHelpers.vb b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicRefactoringHelpers.vb new file mode 100644 index 0000000000000..91f2e11240276 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicRefactoringHelpers.vb @@ -0,0 +1,66 @@ +' 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. + +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Global.Analyzer.Utilities + + Friend NotInheritable Class VisualBasicRefactoringHelpers + Inherits AbstractRefactoringHelpers(Of ExpressionSyntax, ArgumentSyntax, ExpressionStatementSyntax) + + Public Shared ReadOnly Property Instance As New VisualBasicRefactoringHelpers() + + Private Sub New() + End Sub + + Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts + Get + Return VisualBasicSyntaxFacts.Instance + End Get + End Property + + Protected Overrides Iterator Function ExtractNodesSimple(node As SyntaxNode, syntaxFacts As ISyntaxFacts) As IEnumerable(Of SyntaxNode) + For Each baseExtraction In MyBase.ExtractNodesSimple(node, syntaxFacts) + Yield baseExtraction + Next + + ' VB's arguments can have identifiers nested in ModifiedArgument -> we want + ' identifiers to represent parent node -> need to extract. + If IsIdentifierOfParameter(node) Then + Yield node.Parent + End If + + ' In VB Statement both for/foreach are split into Statement (header) and the rest + ' selecting the header should still count for the whole blockSyntax + If TypeOf node Is ForEachStatementSyntax And TypeOf node.Parent Is ForEachBlockSyntax Then + Dim foreachStatement = CType(node, ForEachStatementSyntax) + Yield foreachStatement.Parent + End If + + If TypeOf node Is ForStatementSyntax And TypeOf node.Parent Is ForBlockSyntax Then + Dim forStatement = CType(node, ForStatementSyntax) + Yield forStatement.Parent + End If + + If TypeOf node Is VariableDeclaratorSyntax Then + Dim declarator = CType(node, VariableDeclaratorSyntax) + If TypeOf declarator.Parent Is LocalDeclarationStatementSyntax Then + Dim localDeclarationStatement = CType(declarator.Parent, LocalDeclarationStatementSyntax) + ' Only return the whole localDeclarationStatement if there's just one declarator with just one name + If localDeclarationStatement.Declarators.Count = 1 And localDeclarationStatement.Declarators.First().Names.Count = 1 Then + Yield localDeclarationStatement + End If + End If + End If + + End Function + + Private Shared Function IsIdentifierOfParameter(node As SyntaxNode) As Boolean + Return (TypeOf node Is ModifiedIdentifierSyntax) AndAlso (TypeOf node.Parent Is ParameterSyntax) AndAlso (CType(node.Parent, ParameterSyntax).Identifier Is node) + End Function + + End Class + +End Namespace diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxFacts.vb b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxFacts.vb new file mode 100644 index 0000000000000..b364516952ad0 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxFacts.vb @@ -0,0 +1,178 @@ +' 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. + +Imports System.Collections.Immutable +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Global.Analyzer.Utilities + + Friend NotInheritable Class VisualBasicSyntaxFacts + Inherits AbstractSyntaxFacts + Implements ISyntaxFacts + + Public Shared ReadOnly Property Instance As New VisualBasicSyntaxFacts() + + Private Sub New() + End Sub + + Public Overrides ReadOnly Property SyntaxKinds As ISyntaxKinds Implements ISyntaxFacts.SyntaxKinds + Get + Return VisualBasicSyntaxKinds.Instance + End Get + End Property + + Public Sub GetPartsOfAssignmentExpressionOrStatement(statement As SyntaxNode, ByRef left As SyntaxNode, ByRef operatorToken As SyntaxToken, ByRef right As SyntaxNode) Implements ISyntaxFacts.GetPartsOfAssignmentExpressionOrStatement + Dim assignment = DirectCast(statement, AssignmentStatementSyntax) + left = assignment.Left + operatorToken = assignment.OperatorToken + right = assignment.Right + End Sub + + Public Overrides Function GetAttributeLists(node As SyntaxNode) As SyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetAttributeLists + Return node.GetAttributeLists() + End Function + + Public Function GetExpressionOfExpressionStatement(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetExpressionOfExpressionStatement + Return DirectCast(node, ExpressionStatementSyntax).Expression + End Function + + Public Function IsSimpleAssignmentStatement(statement As SyntaxNode) As Boolean Implements ISyntaxFacts.IsSimpleAssignmentStatement + Return statement.IsKind(SyntaxKind.SimpleAssignmentStatement) + End Function + + Public Function GetVariablesOfLocalDeclarationStatement(node As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetVariablesOfLocalDeclarationStatement + Return DirectCast(node, LocalDeclarationStatementSyntax).Declarators + End Function + + Public Function GetInitializerOfVariableDeclarator(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetInitializerOfVariableDeclarator + Return DirectCast(node, VariableDeclaratorSyntax).Initializer + End Function + + Public Function GetValueOfEqualsValueClause(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetValueOfEqualsValueClause + Return DirectCast(node, EqualsValueSyntax).Value + End Function + + Public Function IsOnTypeHeader( + root As SyntaxNode, + position As Integer, + fullHeader As Boolean, + ByRef typeDeclaration As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnTypeHeader + Dim typeBlock = TryGetAncestorForLocation(Of TypeBlockSyntax)(root, position) + If typeBlock Is Nothing Then + Return Nothing + End If + + Dim typeStatement = typeBlock.BlockStatement + typeDeclaration = typeStatement + + Dim lastToken = If(typeStatement.TypeParameterList?.GetLastToken(), typeStatement.Identifier) + If fullHeader Then + lastToken = If(typeBlock.Implements.LastOrDefault()?.GetLastToken(), + If(typeBlock.Inherits.LastOrDefault()?.GetLastToken(), + lastToken)) + End If + + Return IsOnHeader(root, position, typeBlock, lastToken) + End Function + + Public Function IsOnPropertyDeclarationHeader(root As SyntaxNode, position As Integer, ByRef propertyDeclaration As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnPropertyDeclarationHeader + Dim node = TryGetAncestorForLocation(Of PropertyStatementSyntax)(root, position) + propertyDeclaration = node + + If propertyDeclaration Is Nothing Then + Return False + End If + + If node.AsClause IsNot Nothing Then + Return IsOnHeader(root, position, node, node.AsClause) + End If + + Return IsOnHeader(root, position, node, node.Identifier) + End Function + + Public Function IsOnParameterHeader(root As SyntaxNode, position As Integer, ByRef parameter As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnParameterHeader + Dim node = TryGetAncestorForLocation(Of ParameterSyntax)(root, position) + parameter = node + + If parameter Is Nothing Then + Return False + End If + + Return IsOnHeader(root, position, node, node) + End Function + + Public Function IsOnMethodHeader(root As SyntaxNode, position As Integer, ByRef method As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnMethodHeader + Dim node = TryGetAncestorForLocation(Of MethodStatementSyntax)(root, position) + method = node + + If method Is Nothing Then + Return False + End If + + If node.HasReturnType() Then + Return IsOnHeader(root, position, method, node.GetReturnType()) + End If + + If node.ParameterList IsNot Nothing Then + Return IsOnHeader(root, position, method, node.ParameterList) + End If + + Return IsOnHeader(root, position, node, node) + End Function + + Public Function IsOnLocalFunctionHeader(root As SyntaxNode, position As Integer, ByRef localFunction As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnLocalFunctionHeader + ' No local functions in VisualBasic + Return False + End Function + + Public Function IsOnLocalDeclarationHeader(root As SyntaxNode, position As Integer, ByRef localDeclaration As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnLocalDeclarationHeader + Dim node = TryGetAncestorForLocation(Of LocalDeclarationStatementSyntax)(root, position) + localDeclaration = node + + If localDeclaration Is Nothing Then + Return False + End If + + Dim initializersExpressions = node.Declarators. + Where(Function(d) d.Initializer IsNot Nothing). + Select(Function(initialized) initialized.Initializer.Value). + ToImmutableArray() + Return IsOnHeader(root, position, node, node, initializersExpressions) + End Function + + Public Function IsOnIfStatementHeader(root As SyntaxNode, position As Integer, ByRef ifStatement As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnIfStatementHeader + ifStatement = Nothing + + Dim multipleLineNode = TryGetAncestorForLocation(Of MultiLineIfBlockSyntax)(root, position) + If multipleLineNode IsNot Nothing Then + ifStatement = multipleLineNode + Return IsOnHeader(root, position, multipleLineNode.IfStatement, multipleLineNode.IfStatement) + End If + + Dim singleLineNode = TryGetAncestorForLocation(Of SingleLineIfStatementSyntax)(root, position) + If singleLineNode IsNot Nothing Then + ifStatement = singleLineNode + Return IsOnHeader(root, position, singleLineNode, singleLineNode.Condition) + End If + + Return False + End Function + + Public Function IsOnForeachHeader(root As SyntaxNode, position As Integer, ByRef foreachStatement As SyntaxNode) As Boolean Implements ISyntaxFacts.IsOnForeachHeader + Dim node = TryGetAncestorForLocation(Of ForEachBlockSyntax)(root, position) + foreachStatement = node + + If foreachStatement Is Nothing Then + Return False + End If + + Return IsOnHeader(root, position, node, node.ForEachStatement) + End Function + + End Class + +End Namespace diff --git a/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxKinds.vb b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxKinds.vb new file mode 100644 index 0000000000000..c19f4af1acf18 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring.VisualBasic/VisualBasicSyntaxKinds.vb @@ -0,0 +1,42 @@ +' 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. + +Imports Microsoft.CodeAnalysis.VisualBasic + +Namespace Global.Analyzer.Utilities + + Friend NotInheritable Class VisualBasicSyntaxKinds + Implements ISyntaxKinds + + Public Shared ReadOnly Property Instance As New VisualBasicSyntaxKinds() + + Private Sub New() + End Sub + + Public ReadOnly Property EndOfFileToken As Integer Implements ISyntaxKinds.EndOfFileToken + Get + Return SyntaxKind.EndOfFileToken + End Get + End Property + + Public ReadOnly Property ExpressionStatement As Integer Implements ISyntaxKinds.ExpressionStatement + Get + Return SyntaxKind.ExpressionStatement + End Get + End Property + + Public ReadOnly Property LocalDeclarationStatement As Integer Implements ISyntaxKinds.LocalDeclarationStatement + Get + Return SyntaxKind.LocalDeclarationStatement + End Get + End Property + + Public ReadOnly Property VariableDeclarator As Integer Implements ISyntaxKinds.VariableDeclarator + Get + Return SyntaxKind.VariableDeclarator + End Get + End Property + End Class + +End Namespace diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/AbstractRefactoringHelpers`3.cs b/src/RoslynAnalyzers/Utilities/Refactoring/AbstractRefactoringHelpers`3.cs new file mode 100644 index 0000000000000..e5ef57cf1d004 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/AbstractRefactoringHelpers`3.cs @@ -0,0 +1,524 @@ +// 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 warnings + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities.Extensions; +using Analyzer.Utilities.PooledObjects; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities +{ + internal abstract class AbstractRefactoringHelpers + : IRefactoringHelpers + where TExpressionSyntax : SyntaxNode + where TArgumentSyntax : SyntaxNode + where TExpressionStatementSyntax : SyntaxNode + { + protected abstract ISyntaxFacts SyntaxFacts { get; } + + public async Task> GetRelevantNodesAsync( + Document document, + TextSpan selection, + CancellationToken cancellationToken) + where TSyntaxNode : SyntaxNode + { + // Given selection is trimmed first to enable over-selection that spans multiple lines. Since trailing whitespace ends + // at newline boundary over-selection to e.g. a line after LocalFunctionStatement would cause FindNode to find enclosing + // block's Node. That is because in addition to LocalFunctionStatement the selection would also contain trailing trivia + // (whitespace) of following statement. + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + if (root == null) + { + return ImmutableArray.Empty; + } + + var syntaxFacts = SyntaxFacts; + var selectionTrimmed = await CodeRefactoringHelpers.GetTrimmedTextSpanAsync(document, selection, cancellationToken).ConfigureAwait(false); + + // If user selected only whitespace we don't want to return anything. We could do following: + // 1) Consider token that owns (as its trivia) the whitespace. + // 2) Consider start/beginning of whitespace as location (empty selection) + // Option 1) can't be used all the time and 2) can be confusing for users. Therefore bailing out is the + // most consistent option. + if (selectionTrimmed.IsEmpty && !selection.IsEmpty) + { + return ImmutableArray.Empty; + } + + var relevantNodesBuilder = ArrayBuilder.GetInstance(); + + // Every time a Node is considered an extractNodes method is called to add all nodes around the original one + // that should also be considered. + // + // That enables us to e.g. return node `b` when Node `var a = b;` is being considered without a complex (and potentially + // lang. & situation dependent) into Children descending code here. We can't just try extracted Node because we might + // want the whole node `var a = b;` + + // Handle selections: + // - Most/the whole wanted Node is selected (e.g. `C [|Fun() {}|]` + // - The smallest node whose FullSpan includes the whole (trimmed) selection + // - Using FullSpan is important because it handles over-selection with comments + // - Travels upwards through same-sized (FullSpan) nodes, extracting + // - Token with wanted Node as direct parent is selected (e.g. IdentifierToken for LocalFunctionStatement: `C [|Fun|]() {}`) + // Note: Whether we have selection or location has to be checked against original selection because selecting just + // whitespace could collapse selectionTrimmed into and empty Location. But we don't want `[| |]token` + // registering as ` [||]token`. + if (!selectionTrimmed.IsEmpty) + { + AddRelevantNodesForSelection(syntaxFacts, root, selectionTrimmed, relevantNodesBuilder, cancellationToken); + } + else + { + // No more selection -> Handle what current selection is touching: + // + // Consider touching only for empty selections. Otherwise `[|C|] methodName(){}` would be considered as + // touching the Method's Node (through the left edge, see below) which is something the user probably + // didn't want since they specifically selected only the return type. + // + // What the selection is touching is used in two ways. + // - Firstly, it is used to handle situation where it touches a Token whose direct ancestor is wanted Node. + // While having the (even empty) selection inside such token or to left of such Token is already handle + // by code above touching it from right `C methodName[||](){}` isn't (the FindNode for that returns Args node). + // - Secondly, it is used for left/right edge climbing. E.g. `[||]C methodName(){}` the touching token's direct + // ancestor is TypeNode for the return type but it is still reasonable to expect that the user might want to + // be given refactorings for the whole method (as he has caret on the edge of it). Therefore we travel the + // Node tree upwards and as long as we're on the left edge of a Node's span we consider such node & potentially + // continue traveling upwards. The situation for right edge (`C methodName(){}[||]`) is analogical. + // E.g. for right edge `C methodName(){}[||]`: CloseBraceToken -> BlockSyntax -> LocalFunctionStatement -> null (higher + // node doesn't end on position anymore) + // Note: left-edge climbing needs to handle AttributeLists explicitly, see below for more information. + // - Thirdly, if location isn't touching anything, we move the location to the token in whose trivia location is in. + // more about that below. + // - Fourthly, if we're in an expression / argument we consider touching a parent expression whenever we're within it + // as long as it is on the first line of such expression (arbitrary heuristic). + + // First we need to get tokens we might potentially be touching, tokenToRightOrIn and tokenToLeft. + var (tokenToRightOrIn, tokenToLeft, location) = await GetTokensToRightOrInToLeftAndUpdatedLocationAsync( + document, root, selectionTrimmed, cancellationToken).ConfigureAwait(false); + + // In addition to per-node extr also check if current location (if selection is empty) is in a header of higher level + // desired node once. We do that only for locations because otherwise `[|int|] A { get; set; }) would trigger all refactorings for + // Property Decl. + // We cannot check this any sooner because the above code could've changed current location. + AddNonHiddenCorrectTypeNodes(ExtractNodesInHeader(root, location, syntaxFacts), relevantNodesBuilder, cancellationToken); + + // Add Nodes for touching tokens as described above. + AddNodesForTokenToRightOrIn(syntaxFacts, root, relevantNodesBuilder, location, tokenToRightOrIn, cancellationToken); + AddNodesForTokenToLeft(syntaxFacts, relevantNodesBuilder, location, tokenToLeft, cancellationToken); + + // If the wanted node is an expression syntax -> traverse upwards even if location is deep within a SyntaxNode. + // We want to treat more types like expressions, e.g.: ArgumentSyntax should still trigger even if deep-in. + if (IsWantedTypeExpressionLike()) + { + // Reason to treat Arguments (and potentially others) as Expression-like: + // https://github.com/dotnet/roslyn/pull/37295#issuecomment-516145904 + await AddNodesDeepInAsync(document, location, relevantNodesBuilder, cancellationToken).ConfigureAwait(false); + } + } + + return relevantNodesBuilder.ToImmutableAndFree(); + } + + private static bool IsWantedTypeExpressionLike() where TSyntaxNode : SyntaxNode + { + var wantedType = typeof(TSyntaxNode); + + var expressionType = typeof(TExpressionSyntax); + var argumentType = typeof(TArgumentSyntax); + var expressionStatementType = typeof(TExpressionStatementSyntax); + + return IsAEqualOrSubclassOfB(wantedType, expressionType) || + IsAEqualOrSubclassOfB(wantedType, argumentType) || + IsAEqualOrSubclassOfB(wantedType, expressionStatementType); + + static bool IsAEqualOrSubclassOfB(Type a, Type b) + { + return a.GetTypeInfo().IsSubclassOf(b) || a == b; + } + } + + private static async Task<(SyntaxToken tokenToRightOrIn, SyntaxToken tokenToLeft, int location)> GetTokensToRightOrInToLeftAndUpdatedLocationAsync( + Document document, + SyntaxNode root, + TextSpan selectionTrimmed, + CancellationToken cancellationToken) + { + // get Token for current location + var location = selectionTrimmed.Start; + var tokenOnLocation = root.FindToken(location); + + // Gets a token that is directly to the right of current location or that encompasses current location (`[||]tokenToRightOrIn` or `tok[||]enToRightOrIn`) + var tokenToRightOrIn = tokenOnLocation.Span.Contains(location) + ? tokenOnLocation + : default; + + // A token can be to the left only when there's either no tokenDirectlyToRightOrIn or there's one directly starting at current location. + // Otherwise (otherwise tokenToRightOrIn is also left from location, e.g: `tok[||]enToRightOrIn`) + var tokenToLeft = default(SyntaxToken); + if (tokenToRightOrIn == default || tokenToRightOrIn.FullSpan.Start == location) + { + var tokenPreLocation = (tokenOnLocation.Span.End == location) + ? tokenOnLocation + : tokenOnLocation.GetPreviousToken(includeZeroWidth: true); + + tokenToLeft = (tokenPreLocation.Span.End == location) + ? tokenPreLocation + : default; + } + + // If both tokens directly to left & right are empty -> we're somewhere in the middle of whitespace. + // Since there wouldn't be (m)any other refactorings we can try to offer at least the ones for (semantically) + // closest token/Node. Thus, we move the location to the token in whose `.FullSpan` the original location was. + if (tokenToLeft == default && tokenToRightOrIn == default) + { + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + if (IsAcceptableLineDistanceAway(sourceText, tokenOnLocation, location)) + { + // tokenOnLocation: token in whose trivia location is at + if (tokenOnLocation.Span.Start >= location) + { + tokenToRightOrIn = tokenOnLocation; + location = tokenToRightOrIn.Span.Start; + } + else + { + tokenToLeft = tokenOnLocation; + location = tokenToLeft.Span.End; + } + } + } + + return (tokenToRightOrIn, tokenToLeft, location); + + static bool IsAcceptableLineDistanceAway( + SourceText sourceText, SyntaxToken tokenOnLocation, int location) + { + // assume non-trivia token can't span multiple lines + var tokenLine = sourceText.Lines.GetLineFromPosition(tokenOnLocation.Span.Start); + var locationLine = sourceText.Lines.GetLineFromPosition(location); + + // Change location to nearest token only if the token is off by one line or less + var lineDistance = tokenLine.LineNumber - locationLine.LineNumber; + if (lineDistance is not 0 and not 1) + return false; + + // Note: being a line below a tokenOnLocation is impossible in current model as whitespace + // trailing trivia ends on new line. Which is fine because if you're a line _after_ some node + // you usually don't want refactorings for what's above you. + + if (lineDistance == 1) + { + // position is one line above the node of interest. This is fine if that + // line is blank. Otherwise, if it isn't (i.e. it contains comments, + // directives, or other trivia), then it's not likely the user is selecting + // this entry. + return locationLine.IsEmptyOrWhitespace(); + } + + // On the same line. This position is acceptable. + return true; + } + } + + private void AddNodesForTokenToLeft(ISyntaxFacts syntaxFacts, ArrayBuilder relevantNodesBuilder, int location, SyntaxToken tokenToLeft, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode + { + // there could be multiple (n) tokens to the left if first n-1 are Empty -> iterate over all of them + while (tokenToLeft != default) + { + var leftNode = tokenToLeft.Parent!; + do + { + // Consider either a Node that is: + // - Ancestor Node of such Token as long as their span ends on location (it's still on the edge) + AddNonHiddenCorrectTypeNodes(ExtractNodesSimple(leftNode, syntaxFacts), relevantNodesBuilder, cancellationToken); + + leftNode = leftNode.Parent; + if (leftNode == null || !(leftNode.GetLastToken().Span.End == location || leftNode.Span.End == location)) + { + break; + } + } + while (true); + + // as long as current tokenToLeft is empty -> its previous token is also tokenToLeft + tokenToLeft = tokenToLeft.Span.IsEmpty + ? tokenToLeft.GetPreviousToken(includeZeroWidth: true) + : default; + } + } + + private void AddNodesForTokenToRightOrIn(ISyntaxFacts syntaxFacts, SyntaxNode root, ArrayBuilder relevantNodesBuilder, int location, SyntaxToken tokenToRightOrIn, CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode + { + if (tokenToRightOrIn != default) + { + var rightNode = tokenToRightOrIn.Parent!; + do + { + // Consider either a Node that is: + // - Parent of touched Token (location can be within) + // - Ancestor Node of such Token as long as their span starts on location (it's still on the edge) + AddNonHiddenCorrectTypeNodes(ExtractNodesSimple(rightNode, syntaxFacts), relevantNodesBuilder, cancellationToken); + + rightNode = rightNode.Parent; + if (rightNode == null) + { + break; + } + + // The edge climbing for node to the right needs to handle Attributes e.g.: + // [Test1] + // //Comment1 + // [||]object Property1 { get; set; } + // In essence: + // - On the left edge of the node (-> left edge of first AttributeLists) + // - On the left edge of the node sans AttributeLists (& as everywhere comments) + if (rightNode.Span.Start != location) + { + var rightNodeSpanWithoutAttributes = syntaxFacts.GetSpanWithoutAttributes(root, rightNode); + if (rightNodeSpanWithoutAttributes.Start != location) + { + break; + } + } + } + while (true); + } + } + + private void AddRelevantNodesForSelection(ISyntaxFacts syntaxFacts, SyntaxNode root, TextSpan selectionTrimmed, ArrayBuilder relevantNodesBuilder, CancellationToken cancellationToken) + where TSyntaxNode : SyntaxNode + { + var selectionNode = root.FindNode(selectionTrimmed, getInnermostNodeForTie: true); + var prevNode = selectionNode; + do + { + var nonHiddenExtractedSelectedNodes = ExtractNodesSimple(selectionNode, syntaxFacts).OfType().Where(n => !n.OverlapsHiddenPosition(cancellationToken)); + foreach (var nonHiddenExtractedNode in nonHiddenExtractedSelectedNodes) + { + // For selections we need to handle an edge case where only AttributeLists are within selection (e.g. `Func([|[in][out]|] arg1);`). + // In that case the smallest encompassing node is still the whole argument node but it's hard to justify showing refactorings for it + // if user selected only its attributes. + + // Selection contains only AttributeLists -> don't consider current Node + var spanWithoutAttributes = syntaxFacts.GetSpanWithoutAttributes(root, nonHiddenExtractedNode); + if (!selectionTrimmed.IntersectsWith(spanWithoutAttributes)) + { + break; + } + + relevantNodesBuilder.Add(nonHiddenExtractedNode); + } + + prevNode = selectionNode; + selectionNode = selectionNode.Parent; + } + while (selectionNode != null && prevNode.FullWidth() == selectionNode.FullWidth()); + } + + /// + /// Extractor function that retrieves all nodes that should be considered for extraction of given current node. + /// + /// The rationale is that when user selects e.g. entire local declaration statement [|var a = b;|] it is reasonable + /// to provide refactoring for `b` node. Similarly for other types of refactorings. + /// + /// + /// + /// Should also return given node. + /// + protected virtual IEnumerable ExtractNodesSimple(SyntaxNode? node, ISyntaxFacts syntaxFacts) + { + if (node == null) + { + yield break; + } + + // First return the node itself so that it is considered + yield return node; + + // REMARKS: + // The set of currently attempted extractions is in no way exhaustive and covers only cases + // that were found to be relevant for refactorings that were moved to `TryGetSelectedNodeAsync`. + // Feel free to extend it / refine current heuristics. + + // `var a = b;` | `var a = b`; + if (syntaxFacts.IsLocalDeclarationStatement(node) || syntaxFacts.IsLocalDeclarationStatement(node.Parent)) + { + var localDeclarationStatement = syntaxFacts.IsLocalDeclarationStatement(node) ? node : node.Parent; + + // Check if there's only one variable being declared, otherwise following transformation + // would go through which isn't reasonable since we can't say the first one specifically + // is wanted. + // `var a = 1, `c = 2, d = 3`; + // -> `var a = 1`, c = 2, d = 3; + var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement); + if (variables.Count == 1) + { + var declaredVariable = variables.First(); + + // -> `a = b` + yield return declaredVariable; + + // -> `b` + var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(declaredVariable); + if (initializer != null) + { + var value = syntaxFacts.GetValueOfEqualsValueClause(initializer); + if (value != null) + { + yield return value; + } + } + } + } + + // var `a = b`; + if (syntaxFacts.IsVariableDeclarator(node)) + { + // -> `b` + var initializer = syntaxFacts.GetInitializerOfVariableDeclarator(node); + if (initializer != null) + { + var value = syntaxFacts.GetValueOfEqualsValueClause(initializer); + if (value != null) + { + yield return value; + } + } + } + + // `a = b;` + // -> `b` + if (syntaxFacts.IsSimpleAssignmentStatement(node)) + { + syntaxFacts.GetPartsOfAssignmentExpressionOrStatement(node, out _, out _, out var rightSide); + yield return rightSide; + } + + // `a();` + // -> a() + if (syntaxFacts.IsExpressionStatement(node)) + { + yield return syntaxFacts.GetExpressionOfExpressionStatement(node); + } + + // `a()`; + // -> `a();` + if (syntaxFacts.IsExpressionStatement(node.Parent)) + { + yield return node.Parent; + } + } + + /// + /// Extractor function that checks and retrieves all nodes current location is in a header. + /// + protected virtual IEnumerable ExtractNodesInHeader(SyntaxNode root, int location, ISyntaxFacts syntaxFacts) + { + // Header: [Test] `public int a` { get; set; } + if (syntaxFacts.IsOnPropertyDeclarationHeader(root, location, out var propertyDeclaration)) + { + yield return propertyDeclaration; + } + + // Header: public C([Test]`int a = 42`) {} + if (syntaxFacts.IsOnParameterHeader(root, location, out var parameter)) + { + yield return parameter; + } + + // Header: `public I.C([Test]int a = 42)` {} + if (syntaxFacts.IsOnMethodHeader(root, location, out var method)) + { + yield return method; + } + + // Header: `static C([Test]int a = 42)` {} + if (syntaxFacts.IsOnLocalFunctionHeader(root, location, out var localFunction)) + { + yield return localFunction; + } + + // Header: `var a = `3,` b = `5,` c = `7 + 3``; + if (syntaxFacts.IsOnLocalDeclarationHeader(root, location, out var localDeclaration)) + { + yield return localDeclaration; + } + + // Header: `if(...)`{ }; + if (syntaxFacts.IsOnIfStatementHeader(root, location, out var ifStatement)) + { + yield return ifStatement; + } + + // Header: `foreach (var a in b)` { } + if (syntaxFacts.IsOnForeachHeader(root, location, out var foreachStatement)) + { + yield return foreachStatement; + } + + if (syntaxFacts.IsOnTypeHeader(root, location, out var typeDeclaration)) + { + yield return typeDeclaration; + } + } + + protected virtual async Task AddNodesDeepInAsync( + Document document, int position, + ArrayBuilder relevantNodesBuilder, + CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode + { + // If we're deep inside we don't have to deal with being on edges (that gets dealt by TryGetSelectedNodeAsync) + // -> can simply FindToken -> proceed testing its ancestors + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false) + ?? throw new NotSupportedException("Document does not support syntax trees"); + var token = root.FindTokenOnRightOfPosition(position, true); + + // traverse upwards and add all parents if of correct type + var ancestor = token.Parent; + while (ancestor != null) + { + if (ancestor is TSyntaxNode correctTypeNode) + { + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + + var argumentStartLine = sourceText.Lines.GetLineFromPosition(correctTypeNode.Span.Start).LineNumber; + var caretLine = sourceText.Lines.GetLineFromPosition(position).LineNumber; + + if (argumentStartLine == caretLine && !correctTypeNode.OverlapsHiddenPosition(cancellationToken)) + { + relevantNodesBuilder.Add(correctTypeNode); + } + else if (argumentStartLine < caretLine) + { + // higher level nodes will have Span starting at least on the same line -> can bail out + return; + } + } + + ancestor = ancestor.Parent; + } + } + + private static void AddNonHiddenCorrectTypeNodes(IEnumerable nodes, ArrayBuilder resultBuilder, CancellationToken cancellationToken) + where TSyntaxNode : SyntaxNode + { + var correctTypeNonHiddenNodes = nodes.OfType().Where(n => !n.OverlapsHiddenPosition(cancellationToken)); + foreach (var nodeToBeAdded in correctTypeNonHiddenNodes) + { + resultBuilder.Add(nodeToBeAdded); + } + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/AbstractSyntaxFacts.cs b/src/RoslynAnalyzers/Utilities/Refactoring/AbstractSyntaxFacts.cs new file mode 100644 index 0000000000000..eb9ad08a0d989 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/AbstractSyntaxFacts.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; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities +{ + internal abstract class AbstractSyntaxFacts + { + public abstract ISyntaxKinds SyntaxKinds { get; } + + public bool IsOnHeader(SyntaxNode root, int position, SyntaxNode ownerOfHeader, SyntaxNodeOrToken lastTokenOrNodeOfHeader) + => IsOnHeader(root, position, ownerOfHeader, lastTokenOrNodeOfHeader, ImmutableArray.Empty); + + public bool IsOnHeader( + SyntaxNode root, + int position, + SyntaxNode ownerOfHeader, + SyntaxNodeOrToken lastTokenOrNodeOfHeader, + ImmutableArray holes) + where THoleSyntax : SyntaxNode + { + Debug.Assert(ownerOfHeader.FullSpan.Contains(lastTokenOrNodeOfHeader.Span)); + + var headerSpan = TextSpan.FromBounds( + start: GetStartOfNodeExcludingAttributes(root, ownerOfHeader), + end: lastTokenOrNodeOfHeader.FullSpan.End); + + // Is in header check is inclusive, being on the end edge of an header still counts + if (!headerSpan.IntersectsWith(position)) + { + return false; + } + + // Holes are exclusive: + // To be consistent with other 'being on the edge' of Tokens/Nodes a position is + // in a hole (not in a header) only if it's inside _inside_ a hole, not only on the edge. + if (holes.Any(h => h.Span.Contains(position) && position > h.Span.Start)) + { + return false; + } + + return true; + } + + /// + /// Tries to get an ancestor of a Token on current position or of Token directly to left: + /// e.g.: tokenWithWantedAncestor[||]tokenWithoutWantedAncestor + /// + protected TNode? TryGetAncestorForLocation(SyntaxNode root, int position) + where TNode : SyntaxNode + { + var tokenToRightOrIn = root.FindToken(position); + var nodeToRightOrIn = tokenToRightOrIn.GetAncestor(); + if (nodeToRightOrIn != null) + { + return nodeToRightOrIn; + } + + // not at the beginning of a Token -> no (different) token to the left + if (tokenToRightOrIn.FullSpan.Start != position && tokenToRightOrIn.RawKind != SyntaxKinds.EndOfFileToken) + { + return null; + } + + return tokenToRightOrIn.GetPreviousToken().GetAncestor(); + } + + protected int GetStartOfNodeExcludingAttributes(SyntaxNode root, SyntaxNode node) + { + var attributeList = GetAttributeLists(node); + if (attributeList.Any()) + { + var endOfAttributeLists = attributeList.Last().Span.End; + var afterAttributesToken = root.FindTokenOnRightOfPosition(endOfAttributeLists); + + return Math.Min(afterAttributesToken.Span.Start, node.Span.End); + } + + return node.SpanStart; + } + + public abstract SyntaxList GetAttributeLists(SyntaxNode node); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringContextExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringContextExtensions.cs new file mode 100644 index 0000000000000..8fb63d87f9432 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringContextExtensions.cs @@ -0,0 +1,45 @@ +// 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.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities +{ + internal static class CodeRefactoringContextExtensions + { + internal static Task TryGetRelevantNodeAsync(this CodeRefactoringContext context, IRefactoringHelpers helpers) + where TSyntaxNode : SyntaxNode + => TryGetRelevantNodeAsync(context.Document, helpers, context.Span, context.CancellationToken); + + internal static Task> GetRelevantNodesAsync(this CodeRefactoringContext context, IRefactoringHelpers helpers) + where TSyntaxNode : SyntaxNode + => GetRelevantNodesAsync(context.Document, helpers, context.Span, context.CancellationToken); + + internal static async Task TryGetRelevantNodeAsync( + this Document document, + IRefactoringHelpers helpers, + TextSpan span, + CancellationToken cancellationToken) + where TSyntaxNode : SyntaxNode + { + var potentialNodes = await GetRelevantNodesAsync(document, helpers, span, cancellationToken).ConfigureAwait(false); + return potentialNodes.FirstOrDefault(); + } + + internal static Task> GetRelevantNodesAsync( + this Document document, + IRefactoringHelpers helpers, + TextSpan span, + CancellationToken cancellationToken) where TSyntaxNode : SyntaxNode + { + return helpers.GetRelevantNodesAsync(document, span, cancellationToken); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringHelpers.cs b/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringHelpers.cs new file mode 100644 index 0000000000000..827c4a3b0da7f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/CodeRefactoringHelpers.cs @@ -0,0 +1,45 @@ +// 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; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities +{ + internal static class CodeRefactoringHelpers + { + /// + /// Trims leading and trailing whitespace from . + /// + /// + /// Returns unchanged in case . + /// Returns empty Span with original in case it contains only whitespace. + /// + public static async Task GetTrimmedTextSpanAsync(Document document, TextSpan span, CancellationToken cancellationToken) + { + if (span.IsEmpty) + { + return span; + } + + var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var start = span.Start; + var end = span.End; + + while (start < end && char.IsWhiteSpace(sourceText[end - 1])) + { + end--; + } + + while (start < end && char.IsWhiteSpace(sourceText[start])) + { + start++; + } + + return TextSpan.FromBounds(start, end); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/ISyntaxFactsExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/ISyntaxFactsExtensions.cs new file mode 100644 index 0000000000000..85d7f5f196868 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/ISyntaxFactsExtensions.cs @@ -0,0 +1,55 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static class ISyntaxFactsExtensions + { + public static TextSpan GetSpanWithoutAttributes(this ISyntaxFacts syntaxFacts, SyntaxNode root, SyntaxNode node) + { + // Span without AttributeLists + // - No AttributeLists -> original .Span + // - Some AttributeLists -> (first non-trivia/comment Token.Span.Begin, original.Span.End) + // - We need to be mindful about comments due to: + // // [Test1] + // //Comment1 + // [||]object Property1 { get; set; } + // the comment node being part of the next token's (`object`) leading trivia and not the AttributeList's node. + // - In case only attribute is written we need to be careful to not to use next (unrelated) token as beginning current the node. + var attributeList = syntaxFacts.GetAttributeLists(node); + if (attributeList.Any()) + { + var endOfAttributeLists = attributeList.Last().Span.End; + var afterAttributesToken = root.FindTokenOnRightOfPosition(endOfAttributeLists); + + var endOfNode = node.Span.End; + var startOfNodeWithoutAttributes = Math.Min(afterAttributesToken.Span.Start, endOfNode); + + return TextSpan.FromBounds(startOfNodeWithoutAttributes, endOfNode); + } + + return node.Span; + } + + /// + /// Checks if the position is on the header of a type (from the start of the type up through it's name). + /// + public static bool IsOnTypeHeader(this ISyntaxFacts syntaxFacts, SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? typeDeclaration) + => syntaxFacts.IsOnTypeHeader(root, position, fullHeader: false, out typeDeclaration); + + public static bool IsExpressionStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.ExpressionStatement; + + public static bool IsLocalDeclarationStatement(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.LocalDeclarationStatement; + + public static bool IsVariableDeclarator(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node) + => node?.RawKind == syntaxFacts.SyntaxKinds.VariableDeclarator; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SourceTextExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SourceTextExtensions.cs new file mode 100644 index 0000000000000..c7125c0c3a67f --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SourceTextExtensions.cs @@ -0,0 +1,69 @@ +// 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.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class SourceTextExtensions + { + public static bool OverlapsHiddenPosition( + this SourceText text, + TextSpan span, + Func isPositionHidden, + CancellationToken cancellationToken) + { + var result = TryOverlapsHiddenPosition(text, span, isPositionHidden, cancellationToken); + cancellationToken.ThrowIfCancellationRequested(); + return result; + } + + /// + /// Same as OverlapsHiddenPosition but doesn't throw on cancellation. Instead, returns false + /// in that case. + /// + public static bool TryOverlapsHiddenPosition( + this SourceText text, + TextSpan span, + Func isPositionHidden, + CancellationToken cancellationToken) + { + var startLineNumber = text.Lines.IndexOf(span.Start); + var endLineNumber = text.Lines.IndexOf(span.End); + + // NOTE(cyrusn): It's safe to examine the start of a line because you can't have a line + // with both a pp directive and code on it. so, for example, if a node crosses a region + // then it must be the case that the start of some line from the start of the node to + // the end is hidden. i.e.: +#if false +' class C +' { +'#line hidden +' } +'#line default +#endif + // The start of the line with the } on it is hidden, and thus the node overlaps a hidden + // region. + + for (var lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) + { + if (cancellationToken.IsCancellationRequested) + { + break; + } + + var linePosition = text.Lines[lineNumber].Start; + var isHidden = isPositionHidden(linePosition, cancellationToken); + if (isHidden) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxNodeExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxNodeExtensions.cs new file mode 100644 index 0000000000000..f8541bc215c55 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxNodeExtensions.cs @@ -0,0 +1,173 @@ +// 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; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static partial class SyntaxNodeExtensions + { + /// + /// Look inside a trivia list for a skipped token that contains the given position. + /// + private static readonly Func s_findSkippedTokenForward = FindSkippedTokenForward; + + /// + /// Look inside a trivia list for a skipped token that contains the given position. + /// + private static readonly Func s_findSkippedTokenBackward = FindSkippedTokenBackward; + + public static int Width(this SyntaxNode node) + => node.Span.Length; + + public static int FullWidth(this SyntaxNode node) + => node.FullSpan.Length; + + public static bool OverlapsHiddenPosition(this SyntaxNode node, CancellationToken cancellationToken) + => node.OverlapsHiddenPosition(node.Span, cancellationToken); + + public static bool OverlapsHiddenPosition(this SyntaxNode node, TextSpan span, CancellationToken cancellationToken) + => node.SyntaxTree.OverlapsHiddenPosition(span, cancellationToken); + + /// + /// If the position is inside of token, return that token; otherwise, return the token to the right. + /// + public static SyntaxToken FindTokenOnRightOfPosition( + this SyntaxNode root, + int position, + bool includeSkipped = false, + bool includeDirectives = false, + bool includeDocumentationComments = false) + { + var findSkippedToken = includeSkipped ? s_findSkippedTokenForward : ((l, p) => default); + + var token = GetInitialToken(root, position, includeSkipped, includeDirectives, includeDocumentationComments); + + if (position < token.SpanStart) + { + var skippedToken = findSkippedToken(token.LeadingTrivia, position); + token = skippedToken.RawKind != 0 ? skippedToken : token; + } + else if (token.Span.End <= position) + { + do + { + var skippedToken = findSkippedToken(token.TrailingTrivia, position); + token = skippedToken.RawKind != 0 + ? skippedToken + : token.GetNextToken(includeZeroWidth: false, includeSkipped: includeSkipped, includeDirectives: includeDirectives, includeDocumentationComments: includeDocumentationComments); + } + while (token.RawKind != 0 && token.Span.End <= position && token.Span.End <= root.FullSpan.End); + } + + if (token.Span.IsEmpty) + { + token = token.GetNextToken(); + } + + return token; + } + + /// + /// If the position is inside of token, return that token; otherwise, return the token to the left. + /// + public static SyntaxToken FindTokenOnLeftOfPosition( + this SyntaxNode root, + int position, + bool includeSkipped = false, + bool includeDirectives = false, + bool includeDocumentationComments = false) + { + var findSkippedToken = includeSkipped ? s_findSkippedTokenBackward : ((l, p) => default); + + var token = GetInitialToken(root, position, includeSkipped, includeDirectives, includeDocumentationComments); + + if (position <= token.SpanStart) + { + do + { + var skippedToken = findSkippedToken(token.LeadingTrivia, position); + token = skippedToken.RawKind != 0 + ? skippedToken + : token.GetPreviousToken(includeZeroWidth: false, includeSkipped: includeSkipped, includeDirectives: includeDirectives, includeDocumentationComments: includeDocumentationComments); + } + while (position <= token.SpanStart && root.FullSpan.Start < token.SpanStart); + } + else if (token.Span.End < position) + { + var skippedToken = findSkippedToken(token.TrailingTrivia, position); + token = skippedToken.RawKind != 0 ? skippedToken : token; + } + + if (token.Span.IsEmpty) + { + token = token.GetPreviousToken(); + } + + return token; + } + + private static SyntaxToken GetInitialToken( + SyntaxNode root, + int position, + bool includeSkipped = false, + bool includeDirectives = false, + bool includeDocumentationComments = false) + { + return (position < root.FullSpan.End || !(root is ICompilationUnitSyntax)) + ? root.FindToken(position, includeSkipped || includeDirectives || includeDocumentationComments) + : root.GetLastToken(includeZeroWidth: true, includeSkipped: true, includeDirectives: true, includeDocumentationComments: true) + .GetPreviousToken(includeZeroWidth: false, includeSkipped: includeSkipped, includeDirectives: includeDirectives, includeDocumentationComments: includeDocumentationComments); + } + + /// + /// Look inside a trivia list for a skipped token that contains the given position. + /// + private static SyntaxToken FindSkippedTokenForward(SyntaxTriviaList triviaList, int position) + { + foreach (var trivia in triviaList) + { + if (trivia.HasStructure && + trivia.GetStructure() is ISkippedTokensTriviaSyntax skippedTokensTrivia) + { + foreach (var token in skippedTokensTrivia.Tokens) + { + if (!token.Span.IsEmpty && position <= token.Span.End) + { + return token; + } + } + } + } + + return default; + } + + /// + /// Look inside a trivia list for a skipped token that contains the given position. + /// + private static SyntaxToken FindSkippedTokenBackward(SyntaxTriviaList triviaList, int position) + { + foreach (var trivia in triviaList.Reverse()) + { + if (trivia.HasStructure && + trivia.GetStructure() is ISkippedTokensTriviaSyntax skippedTokensTrivia) + { + foreach (var token in skippedTokensTrivia.Tokens) + { + if (!token.Span.IsEmpty && token.SpanStart <= position) + { + return token; + } + } + } + } + + return default; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTokenExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTokenExtensions.cs new file mode 100644 index 0000000000000..7b85ef9e41967 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTokenExtensions.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities.Extensions +{ + internal static class SyntaxTokenExtensions + { + public static T? GetAncestor(this SyntaxToken token, Func? predicate = null) + where T : SyntaxNode + => token.Parent?.FirstAncestorOrSelf(predicate); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTreeExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTreeExtensions.cs new file mode 100644 index 0000000000000..d36e821ed863a --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/SyntaxTreeExtensions.cs @@ -0,0 +1,34 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities.Extensions +{ + internal static class SyntaxTreeExtensions + { + public static bool OverlapsHiddenPosition([NotNullWhen(returnValue: true)] this SyntaxTree? tree, TextSpan span, CancellationToken cancellationToken) + { + if (tree == null) + { + return false; + } + + var text = tree.GetText(cancellationToken); + + return text.OverlapsHiddenPosition( + span, + (position, cancellationToken2) => + { + // implements the ASP.NET IsHidden rule + var lineVisibility = tree.GetLineVisibility(position, cancellationToken2); + return lineVisibility is LineVisibility.Hidden or LineVisibility.BeforeFirstLineDirective; + }, + cancellationToken); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/TextLineExtensions.cs b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/TextLineExtensions.cs new file mode 100644 index 0000000000000..055c3a81f1a86 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Extensions/TextLineExtensions.cs @@ -0,0 +1,29 @@ +// 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; + +namespace Analyzer.Utilities.Extensions +{ + internal static class TextLineExtensions + { + /// + /// Determines whether the specified line is empty or contains whitespace only. + /// + public static bool IsEmptyOrWhitespace(this TextLine line) + { + var text = line.Text; + RoslynDebug.Assert(text is object); + for (var i = line.Span.Start; i < line.Span.End; i++) + { + if (!char.IsWhiteSpace(text[i])) + { + return false; + } + } + + return true; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/IRefactoringHelpers.cs b/src/RoslynAnalyzers/Utilities/Refactoring/IRefactoringHelpers.cs new file mode 100644 index 0000000000000..cb1c950ddeedb --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/IRefactoringHelpers.cs @@ -0,0 +1,44 @@ +// 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; +using Microsoft.CodeAnalysis.Text; + +namespace Analyzer.Utilities +{ + internal interface IRefactoringHelpers + { + /// + /// + /// Returns an array of instances for refactoring given specified selection in document. + /// + /// + /// A instance is returned if: + /// + /// + /// Selection is zero-width and inside/touching a Token with direct parent of type . + /// Selection is zero-width and touching a Token whose ancestor of type ends/starts precisely on current selection. + /// Selection is zero-width and in whitespace that corresponds to a Token whose direct ancestor is of type of type . + /// Selection is zero-width and in a header (defined by ISyntaxFacts helpers) of an node of type of type . + /// Token whose direct parent of type is selected. + /// Selection is zero-width and wanted node is an expression / argument with selection within such syntax node (arbitrarily deep) on its first line. + /// Whole node of a type is selected. + /// + /// + /// Attempts extracting a Node of type for each Node it considers (see above). + /// E.g. extracts initializer expressions from declarations and assignments, Property declaration from any header node, etc. + /// + /// + /// Note: this function trims all whitespace from both the beginning and the end of given . + /// The trimmed version is then used to determine relevant . It also handles incomplete selections + /// of tokens gracefully. Over-selection containing leading comments is also handled correctly. + /// + /// + Task> GetRelevantNodesAsync(Document document, TextSpan selection, CancellationToken cancellationToken) + where TSyntaxNode : SyntaxNode; + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxFacts.cs b/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxFacts.cs new file mode 100644 index 0000000000000..225a800eaa955 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxFacts.cs @@ -0,0 +1,40 @@ +// 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.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal interface ISyntaxFacts + { + ISyntaxKinds SyntaxKinds { get; } + + SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node); + + bool IsSimpleAssignmentStatement(SyntaxNode statement); + void GetPartsOfAssignmentExpressionOrStatement(SyntaxNode statement, out SyntaxNode left, out SyntaxToken operatorToken, out SyntaxNode right); + + SyntaxList GetAttributeLists(SyntaxNode node); + + SeparatedSyntaxList GetVariablesOfLocalDeclarationStatement(SyntaxNode node); + SyntaxNode GetInitializerOfVariableDeclarator(SyntaxNode node); + SyntaxNode? GetValueOfEqualsValueClause(SyntaxNode? node); + + /// + /// controls how much of the type header should be considered. If only the span up through the type name will be considered. If + /// then the span through the base-list will be considered. + /// + bool IsOnTypeHeader(SyntaxNode root, int position, bool fullHeader, [NotNullWhen(true)] out SyntaxNode? typeDeclaration); + + bool IsOnPropertyDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? propertyDeclaration); + bool IsOnParameterHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? parameter); + bool IsOnMethodHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? method); + bool IsOnLocalFunctionHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localFunction); + bool IsOnLocalDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? localDeclaration); + bool IsOnIfStatementHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? ifStatement); + bool IsOnForeachHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? foreachStatement); + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxKinds.cs b/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxKinds.cs new file mode 100644 index 0000000000000..0c519462dd977 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/ISyntaxKinds.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. + +namespace Analyzer.Utilities +{ + internal interface ISyntaxKinds + { + int EndOfFileToken { get; } + + int ExpressionStatement { get; } + int LocalDeclarationStatement { get; } + + int VariableDeclarator { get; } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.projitems new file mode 100644 index 0000000000000..9f79f9e27927d --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.projitems @@ -0,0 +1,26 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + + Refactoring.Utilities + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.shproj new file mode 100644 index 0000000000000..bb06b3ef869c4 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Refactoring/Refactoring.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {68528C1C-B163-49A6-A19D-24E10F500F90} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Workspaces/DocumentExtensions.cs b/src/RoslynAnalyzers/Utilities/Workspaces/DocumentExtensions.cs new file mode 100644 index 0000000000000..5391685f430d9 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Workspaces/DocumentExtensions.cs @@ -0,0 +1,45 @@ +// 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. + +#if !MICROSOFT_CODEANALYSIS_PUBLIC_API_ANALYZERS + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; + +namespace Analyzer.Utilities +{ + internal static class DocumentExtensions + { + public static async ValueTask GetRequiredSemanticModelAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSemanticModel(out var semanticModel)) + return semanticModel; + + semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + return semanticModel ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + + public static async ValueTask GetRequiredSyntaxTreeAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSyntaxTree(out var syntaxTree)) + return syntaxTree; + + syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + return syntaxTree ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + + public static async ValueTask GetRequiredSyntaxRootAsync(this Document document, CancellationToken cancellationToken) + { + if (document.TryGetSyntaxRoot(out var root)) + return root; + + root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + return root ?? throw new InvalidOperationException("SyntaxTree is required to accomplish the task but is not supported by document"); + } + } +} + +#endif diff --git a/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxGeneratorExtensions.cs b/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxGeneratorExtensions.cs new file mode 100644 index 0000000000000..84bbbd30c5434 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxGeneratorExtensions.cs @@ -0,0 +1,495 @@ +// 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.Linq; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Editing; + +namespace Analyzer.Utilities +{ + internal static class SyntaxGeneratorExtensions + { + private const string LeftIdentifierName = "left"; + private const string RightIdentifierName = "right"; + private const string ReferenceEqualsMethodName = "ReferenceEquals"; + private const string EqualsMethodName = "Equals"; + private const string CompareToMethodName = "CompareTo"; + private const string SystemNotImplementedExceptionTypeName = "System.NotImplementedException"; + + /// + /// Creates a default declaration for an operator equality overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorEqualityDeclaration(this SyntaxGenerator generator, + INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + List statements = new List(); + + if (containingType.TypeKind == TypeKind.Class) + { + statements.Add( + generator.IfStatement( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + leftArgument, + generator.NullLiteralExpression()), + new[] + { + generator.ReturnStatement( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + rightArgument, + generator.NullLiteralExpression())) + })); + } + + statements.Add( + generator.ReturnStatement( + generator.InvocationExpression( + generator.MemberAccessExpression( + leftArgument, EqualsMethodName), + rightArgument))); + + return generator.ComparisonOperatorDeclaration(OperatorKind.Equality, containingType, statements.ToArray()); + } + + /// + /// Creates a reference to a named type suitable for use in accessing a static member of the type. + /// + /// The used to create the type reference. + /// The named type to reference. + /// A representing the type reference expression. + public static SyntaxNode TypeExpressionForStaticMemberAccess(this SyntaxGenerator generator, INamedTypeSymbol typeSymbol) + { + var qualifiedNameSyntaxKind = generator.QualifiedName(generator.IdentifierName("ignored"), generator.IdentifierName("ignored")).RawKind; + var memberAccessExpressionSyntaxKind = generator.MemberAccessExpression(generator.IdentifierName("ignored"), "ignored").RawKind; + + var typeExpression = generator.TypeExpression(typeSymbol); + return QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, typeExpression, generator); + + // Local function + static SyntaxNode QualifiedNameToMemberAccess(int qualifiedNameSyntaxKind, int memberAccessExpressionSyntaxKind, SyntaxNode expression, SyntaxGenerator generator) + { + if (expression.RawKind == qualifiedNameSyntaxKind) + { + var left = QualifiedNameToMemberAccess(qualifiedNameSyntaxKind, memberAccessExpressionSyntaxKind, expression.ChildNodes().First(), generator); + var right = expression.ChildNodes().Last(); + return generator.MemberAccessExpression(left, right); + } + + return expression; + } + } + + /// + /// Creates a default declaration for an operator inequality overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorInequalityDeclaration(this SyntaxGenerator generator, INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + var returnStatement = generator.ReturnStatement( + generator.LogicalNotExpression( + generator.ValueEqualsExpression( + leftArgument, + rightArgument))); + + return generator.ComparisonOperatorDeclaration(OperatorKind.Inequality, containingType, returnStatement); + } + + /// + /// Creates a default declaration for an operator less than overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorLessThanDeclaration(this SyntaxGenerator generator, INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + SyntaxNode expression; + + if (containingType.TypeKind == TypeKind.Class) + { + expression = + generator.ConditionalExpression( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + leftArgument, + generator.NullLiteralExpression()), + generator.LogicalNotExpression( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + rightArgument, + generator.NullLiteralExpression())), + generator.LessThanExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0))); + } + else + { + expression = + generator.LessThanExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0)); + } + + var returnStatement = generator.ReturnStatement(expression); + return generator.ComparisonOperatorDeclaration(OperatorKind.LessThan, containingType, returnStatement); + } + + /// + /// Creates a default declaration for an operator less than or equal overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorLessThanOrEqualDeclaration(this SyntaxGenerator generator, INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + SyntaxNode expression; + + if (containingType.TypeKind == TypeKind.Class) + { + expression = + generator.LogicalOrExpression( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + leftArgument, + generator.NullLiteralExpression()), + generator.LessThanOrEqualExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0))); + } + else + { + expression = + generator.LessThanOrEqualExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0)); + } + + var returnStatement = generator.ReturnStatement(expression); + return generator.ComparisonOperatorDeclaration(OperatorKind.LessThanOrEqual, containingType, returnStatement); + } + + /// + /// Creates a default declaration for an operator greater than overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorGreaterThanDeclaration(this SyntaxGenerator generator, INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + SyntaxNode expression; + + if (containingType.TypeKind == TypeKind.Class) + { + expression = + generator.LogicalAndExpression( + generator.LogicalNotExpression( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + leftArgument, + generator.NullLiteralExpression())), + generator.GreaterThanExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0))); + } + else + { + expression = + generator.GreaterThanExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0)); + } + + var returnStatement = generator.ReturnStatement(expression); + return generator.ComparisonOperatorDeclaration(OperatorKind.GreaterThan, containingType, returnStatement); + } + + /// + /// Creates a default declaration for an operator greater than or equal overload. + /// + /// + /// The used to create the declaration. + /// + /// + /// A symbol specifying the type of the operands of the comparison operator. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultOperatorGreaterThanOrEqualDeclaration(this SyntaxGenerator generator, INamedTypeSymbol containingType) + { + var leftArgument = generator.IdentifierName(LeftIdentifierName); + var rightArgument = generator.IdentifierName(RightIdentifierName); + + SyntaxNode expression; + + if (containingType.TypeKind == TypeKind.Class) + { + expression = + generator.ConditionalExpression( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + leftArgument, + generator.NullLiteralExpression()), + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + rightArgument, + generator.NullLiteralExpression()), + generator.GreaterThanOrEqualExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0))); + } + else + { + expression = + generator.GreaterThanOrEqualExpression( + generator.InvocationExpression( + generator.MemberAccessExpression(leftArgument, generator.IdentifierName(CompareToMethodName)), + rightArgument), + generator.LiteralExpression(0)); + } + + var returnStatement = generator.ReturnStatement(expression); + return generator.ComparisonOperatorDeclaration(OperatorKind.GreaterThanOrEqual, containingType, returnStatement); + } + + private static SyntaxNode ComparisonOperatorDeclaration(this SyntaxGenerator generator, OperatorKind operatorKind, INamedTypeSymbol containingType, params SyntaxNode[] statements) + { + return generator.OperatorDeclaration( + operatorKind, + new[] + { + generator.ParameterDeclaration(LeftIdentifierName, generator.TypeExpression(containingType)), + generator.ParameterDeclaration(RightIdentifierName, generator.TypeExpression(containingType)) + }, + generator.TypeExpression(SpecialType.System_Boolean), + Accessibility.Public, + DeclarationModifiers.Static, + statements); + } + + /// + /// Creates a default declaration for an override of . + /// + /// + /// The used to create the declaration. + /// + /// The compilation + /// + /// A symbol specifying the type in which the declaration is to be created. + /// + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultEqualsOverrideDeclaration(this SyntaxGenerator generator, Compilation compilation, INamedTypeSymbol containingType) + { + var argumentName = generator.IdentifierName("obj"); + + List statements = new List(); + + if (containingType.TypeKind == TypeKind.Class) + { + statements.AddRange(new[] + { + generator.IfStatement( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + generator.ThisExpression(), + argumentName), + new[] + { + generator.ReturnStatement(generator.TrueLiteralExpression()) + }), + generator.IfStatement( + generator.InvocationExpression( + generator.IdentifierName(ReferenceEqualsMethodName), + argumentName, + generator.NullLiteralExpression()), + new[] + { + generator.ReturnStatement(generator.FalseLiteralExpression()) + }) + }); + } + + statements.AddRange(generator.DefaultMethodBody(compilation)); + + return generator.MethodDeclaration( + WellKnownMemberNames.ObjectEquals, + new[] + { + generator.ParameterDeclaration(argumentName.ToString(), generator.TypeExpression(SpecialType.System_Object)) + }, + returnType: generator.TypeExpression(SpecialType.System_Boolean), + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override, + statements: statements); + } + + /// + /// Creates a default declaration for an override of . + /// + /// + /// The used to create the declaration. + /// + /// The compilation + /// + /// A representing the declaration. + /// + public static SyntaxNode DefaultGetHashCodeOverrideDeclaration( + this SyntaxGenerator generator, Compilation compilation) + { + return generator.MethodDeclaration( + WellKnownMemberNames.ObjectGetHashCode, + returnType: generator.TypeExpression(SpecialType.System_Int32), + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override, + statements: generator.DefaultMethodBody(compilation)); + } + + /// + /// Creates a default set of statements to place within a generated method body. + /// + /// + /// The used to create the statements. + /// + /// The compilation + /// + /// An sequence containing a single statement that throws . + /// + public static IEnumerable DefaultMethodBody( + this SyntaxGenerator generator, Compilation compilation) + { + yield return DefaultMethodStatement(generator, compilation); + } + + public static SyntaxNode DefaultMethodStatement(this SyntaxGenerator generator, Compilation compilation) + { + return generator.ThrowStatement(generator.ObjectCreationExpression( + generator.TypeExpression( + compilation.GetOrCreateTypeByMetadataName(SystemNotImplementedExceptionTypeName)))); + } + + public static SyntaxNode? TryGetContainingDeclaration(this SyntaxGenerator generator, SyntaxNode? node, DeclarationKind kind) + { + if (node is null) + { + return null; + } + + var declarationKind = generator.GetDeclarationKind(node); + while (declarationKind != kind) + { + node = generator.GetDeclaration(node.Parent); + if (node is null) + { + return null; + } + + declarationKind = generator.GetDeclarationKind(node); + } + + return node; + } + + /// + /// Creates the first unused identifier name based on the provided base name. + /// + /// The used to create the identifier name. + /// The semantic model. + /// The position in the code. + /// The base name to use. + /// Maximum number of tries. + /// + /// A representing an unused identifier name. + /// This can be either the base name itself or a variation of it with a number appended to make it unique. + /// + public static SyntaxNode FirstUnusedIdentifierName(this SyntaxGenerator generator, SemanticModel semanticModel, int position, string baseName, int maxTries = int.MaxValue) + { + var identifierName = generator.IdentifierName(baseName); + + if (semanticModel.GetSpeculativeSymbolInfo(position, identifierName, SpeculativeBindingOption.BindAsExpression).Symbol is null) + { + return identifierName; + } + + for (int i = 1; i < maxTries; i++) + { + identifierName = generator.IdentifierName($"{baseName}{i}"); + + if (semanticModel.GetSpeculativeSymbolInfo(position, identifierName, SpeculativeBindingOption.BindAsExpression).Symbol is null) + { + break; + } + } + + return identifierName; + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxNodeExtensions.cs b/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxNodeExtensions.cs new file mode 100644 index 0000000000000..19466896dcdfc --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Workspaces/SyntaxNodeExtensions.cs @@ -0,0 +1,44 @@ +// 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.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Simplification; + +namespace Analyzer.Utilities +{ + internal static class SyntaxNodeExtensions + { + private static Optional s_addImportsAnnotation; + + private static SyntaxAnnotation? AddImportsAnnotation + { + get + { + if (!s_addImportsAnnotation.HasValue) + { + var property = typeof(Simplifier).GetTypeInfo().GetDeclaredProperty("AddImportsAnnotation"); + s_addImportsAnnotation = property?.GetValue(null) as SyntaxAnnotation; + } + + return s_addImportsAnnotation.Value; + } + } + + /// + /// Annotates a syntax node representing a type so that any missing imports get automatically added. Does not work in any other kinds of nodes. + /// + /// The type node to annotate. + /// The annotated type node. + public static SyntaxNode WithAddImportsAnnotation(this SyntaxNode syntaxNode) + { + if (AddImportsAnnotation is null) + { + return syntaxNode; + } + + return syntaxNode.WithAdditionalAnnotations(AddImportsAnnotation); + } + } +} diff --git a/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.projitems b/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.projitems new file mode 100644 index 0000000000000..0b671fa217d1e --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.projitems @@ -0,0 +1,16 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + ec946164-1e17-410b-b7d9-7de7e6268d63 + + + Analyzer.Utilities + + + + + + + \ No newline at end of file diff --git a/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.shproj b/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.shproj new file mode 100644 index 0000000000000..b8268f197a3c2 --- /dev/null +++ b/src/RoslynAnalyzers/Utilities/Workspaces/Workspaces.Utilities.shproj @@ -0,0 +1,13 @@ + + + + {9C4BC501-C0A6-4130-BA68-EF5FB0C640B0} + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index f78d4d5b7a29e..a04c40871cabc 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -1067,6 +1067,38 @@ public async Task SwitchPatternWithVar_WhenInvalidArm_ShouldReturnTheNameNotInCo Assert.True(exceptionThrown); } + [Fact] + public void Function_ReturningPartialType() + { + var script = CSharpScript.Create("class partial;", ScriptOptions.WithLanguageVersion(LanguageVersion.Preview)) + .ContinueWith("partial M() => new();"); + script.GetCompilation().VerifyDiagnostics( + // (1,9): error CS1520: Method must have a return type + // partial M() => new(); + Diagnostic(ErrorCode.ERR_MemberNeedsType, "M").WithLocation(1, 9), + // (1,9): error CS0759: No defining declaration found for implementing declaration of partial method 'M()' + // partial M() => new(); + Diagnostic(ErrorCode.ERR_PartialMethodMustHaveLatent, "M").WithArguments("M()").WithLocation(1, 9), + // (1,9): error CS0751: A partial member must be declared within a partial type + // partial M() => new(); + Diagnostic(ErrorCode.ERR_PartialMemberOnlyInPartialClass, "M").WithLocation(1, 9), + // (1,16): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement + // partial M() => new(); + Diagnostic(ErrorCode.ERR_IllegalStatement, "new()").WithLocation(1, 16)); + } + + [Fact] + public async Task Function_ReturningPartialType_CSharp13() + { + var script = CSharpScript.Create("class partial;", ScriptOptions.WithLanguageVersion(LanguageVersion.CSharp13)) + .ContinueWith("partial M() => new();") + .ContinueWith("M()"); + script.GetCompilation().VerifyDiagnostics(); + + var result = await script.EvaluateAsync(); + Assert.Equal("partial", result.GetType().Name); + } + private class StreamOffsetResolver : SourceReferenceResolver { public override bool Equals(object other) => ReferenceEquals(this, other); diff --git a/src/Scripting/Core/Hosting/AssemblyLoader/MetadataShadowCopyProvider.cs b/src/Scripting/Core/Hosting/AssemblyLoader/MetadataShadowCopyProvider.cs index 4dbf0433b998d..77efeffc1033b 100644 --- a/src/Scripting/Core/Hosting/AssemblyLoader/MetadataShadowCopyProvider.cs +++ b/src/Scripting/Core/Hosting/AssemblyLoader/MetadataShadowCopyProvider.cs @@ -617,7 +617,7 @@ private static FileStream CopyFile(string originalPath, string shadowCopyPath, b StripReadOnlyAttributeFromFile(new FileInfo(shadowCopyPath)); return new FileStream(shadowCopyPath, FileMode.Open, FileAccess.Read, FileShare.Read); } - catch (Exception e) when (fileMayNotExist && (e is FileNotFoundException || e is DirectoryNotFoundException)) + catch (Exception e) when (fileMayNotExist && e is FileNotFoundException or DirectoryNotFoundException) { return null; } diff --git a/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj b/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj index a3fa69f565bc3..98dd67e1545b3 100644 --- a/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj +++ b/src/Setup/DevDivInsertionFiles/DevDivInsertionFiles.csproj @@ -109,6 +109,7 @@ <_Dependency Remove="MessagePack"/> <_Dependency Remove="MessagePack.Annotations"/> <_Dependency Remove="Microsoft.Bcl.AsyncInterfaces"/> + <_Dependency Remove="Microsoft.Bcl.HashCode" /> <_Dependency Remove="Microsoft.Build"/> <_Dependency Remove="Microsoft.Build.Framework"/> <_Dependency Remove="Microsoft.Build.Tasks.Core"/> @@ -132,6 +133,7 @@ <_Dependency Remove="System.Configuration.ConfigurationManager"/> <_Dependency Remove="System.Diagnostics.DiagnosticSource"/> <_Dependency Remove="System.Drawing.Common"/> + <_Dependency Remove="System.Formats.Nrbf" /> <_Dependency Remove="System.IO.Packaging"/> <_Dependency Remove="System.IO.Pipelines"/> <_Dependency Remove="System.Memory"/> diff --git a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs index 4814d40299b30..17cf4dbc1d53b 100644 --- a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs +++ b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTest.GenerationVerifier.cs @@ -11,7 +11,6 @@ using System.Reflection.Metadata.Ecma335; using Microsoft.CodeAnalysis.Symbols; using Microsoft.CodeAnalysis.Test.Utilities; -using Roslyn.Utilities; using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests diff --git a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTestUtilities.cs b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTestUtilities.cs index a1df0ee1beab5..3328f7876dc92 100644 --- a/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTestUtilities.cs +++ b/src/Test/PdbUtilities/EditAndContinue/EditAndContinueTestUtilities.cs @@ -24,7 +24,7 @@ public static EmitBaseline CreateInitialBaseline(Compilation compilation, Module { return module.Module.GetMethodBodyOrThrow(methodHandle)?.LocalSignature ?? default; } - catch (Exception e) when (e is BadImageFormatException || e is IOException) + catch (Exception e) when (e is BadImageFormatException or IOException) { throw new InvalidDataException(e.Message, e); } diff --git a/src/Test/PdbUtilities/Reader/SymReaderFactory.cs b/src/Test/PdbUtilities/Reader/SymReaderFactory.cs index d15171d69a9cd..f6523b0a5b211 100644 --- a/src/Test/PdbUtilities/Reader/SymReaderFactory.cs +++ b/src/Test/PdbUtilities/Reader/SymReaderFactory.cs @@ -9,7 +9,6 @@ using System; using System.Collections.Immutable; using System.IO; -using System.Linq; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; diff --git a/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj b/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj index 76e418c1e9cd8..45f578b7966f7 100644 --- a/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj +++ b/src/Tools/BuildActionTelemetryTable/BuildActionTelemetryTable.csproj @@ -2,7 +2,7 @@ Exe - $(NetVS)-windows + $(NetRoslyn) false @@ -19,6 +19,10 @@ + + + + diff --git a/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs b/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs new file mode 100644 index 0000000000000..e6c1c69979a34 --- /dev/null +++ b/src/Tools/BuildActionTelemetryTable/CodeActionDescriptions.cs @@ -0,0 +1,432 @@ +// 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.Collections.Generic; + +namespace BuildActionTelemetryTable; + +internal static class CodeActionDescriptions +{ + public static ImmutableDictionary CodeActionDescriptionMap { get; } = new Dictionary() + { + { "Microsoft.CodeAnalysis.AddConstructorParametersFromMembers.AddConstructorParametersFromMembersCodeRefactoringProvider", "Add Constructor Parameters From Members (Refactoring)" }, + { "Microsoft.CodeAnalysis.AddConstructorParametersFromMembers.AddConstructorParametersFromMembersCodeRefactoringProvider+AddConstructorParametersCodeAction", "Add Constructor Parameters From Members: Add Constructor Parameters (Refactoring)" }, + { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+AssemblyReferenceCodeAction", "Add Import: Assembly Reference" }, + { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+InstallWithPackageManagerCodeAction", "Add Import: Install With Package Manager" }, + { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+MetadataSymbolReferenceCodeAction", "Add Import: Metadata Symbol Reference" }, + { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+ProjectSymbolReferenceCodeAction", "Add Import: Project Symbol Reference" }, + { "Microsoft.CodeAnalysis.AddImport.InstallPackageAndAddImportCodeAction", "Add Import: Install Package And Add Import" }, + { "Microsoft.CodeAnalysis.AddMissingReference.AddMissingReferenceCodeAction", "Add Missing Reference" }, + { "Microsoft.CodeAnalysis.AddPackage.InstallPackageDirectlyCodeAction", "Add Package: Install Package Directly" }, + { "Microsoft.CodeAnalysis.AddPackage.InstallPackageParentCodeAction", "Add Package: Install Package Parent" }, + { "Microsoft.CodeAnalysis.AddPackage.InstallWithPackageManagerCodeAction", "Add Package: Install With Package Manager" }, + { "Microsoft.CodeAnalysis.AddPackage.ParentInstallPackageCodeAction", "Add Package: Parent Install Package" }, + { "Microsoft.CodeAnalysis.AddRequiredParentheses.AddRequiredParenthesesCodeFixProvider", "Add Required Parentheses" }, + { "Microsoft.CodeAnalysis.ChangeSignature.ChangeSignatureCodeAction", "Change Signature" }, + { "Microsoft.CodeAnalysis.ChangeSignature.ChangeSignatureCodeRefactoringProvider", "Change Signature (Refactoring)" }, + { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureCodeStyle.ConfigureCodeStyleOptionCodeFixProvider+TopLevelConfigureCodeStyleOptionCodeAction", "Configure Code Style Option: Top Level Configure Code Style Option" }, + { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureSeverity.ConfigureSeverityLevelCodeFixProvider+TopLevelBulkConfigureSeverityCodeAction", "Configure Severity Level: Top Level Bulk Configure Severity" }, + { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureSeverity.ConfigureSeverityLevelCodeFixProvider+TopLevelConfigureSeverityCodeAction", "Configure Severity Level: Top Level Configure Severity" }, + { "Microsoft.CodeAnalysis.CodeFixes.FixMultipleCodeAction", "Fix Multiple" }, + { "Microsoft.CodeAnalysis.CodeFixes.NamingStyles.NamingStyleCodeFixProvider", "Naming Styles: Naming Style" }, + { "Microsoft.CodeAnalysis.CodeFixes.NamingStyles.NamingStyleCodeFixProvider+FixNameCodeAction", "Naming Style: Fix Name" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageCodeAction", "Suppression: Global Suppress Message" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageFixAllCodeAction", "Suppression: Global Suppress Message Fix All" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageFixAllCodeAction+GlobalSuppressionSolutionChangeAction", "Global Suppress Message Fix All: Global Suppression Solution Change" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+LocalSuppressMessageCodeAction", "Suppression: Local Suppress Message" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+PragmaWarningCodeAction", "Suppression: Pragma Warning" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+RemoveSuppressionCodeAction+AttributeRemoveAction", "Remove Suppression: Attribute Remove" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+RemoveSuppressionCodeAction+PragmaRemoveAction", "Remove Suppression: Pragma Remove" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.TopLevelSuppressionCodeAction", "Suppression: Top Level Suppression" }, + { "Microsoft.CodeAnalysis.CodeFixes.Suppression.WrapperCodeFixProvider", "Suppression: Wrapper" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.ExtractMethod.ExtractMethodCodeRefactoringProvider", "Extract Method (Refactoring)" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.FixAllCodeRefactoringCodeAction", "Code Refactorings: Fix All Code Refactoring" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.MoveType.AbstractMoveTypeService`4+MoveTypeCodeAction", "Move Type" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.MoveType.MoveTypeCodeRefactoringProvider", "Move Type (Refactoring)" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.PullMemberUp.AbstractPullMemberUpRefactoringProvider+PullMemberUpWithDialogCodeAction", "Pull Member Up: With Dialog (Refactoring)" }, + { "Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace.AbstractSyncNamespaceCodeRefactoringProvider`3+MoveFileCodeAction", "Sync Namespace: Move File (Refactoring)" }, + { "Microsoft.CodeAnalysis.CodeStyle.CSharpFormattingCodeFixProvider", "Formatting" }, + { "Microsoft.CodeAnalysis.CodeStyle.VisualBasicFormattingCodeFixProvider", "Formatting" }, + { "Microsoft.CodeAnalysis.ConvertToInterpolatedString.ConvertRegularStringToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Regular String To Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.AddAnonymousTypeMemberName.CSharpAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, + { "Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay.CSharpAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.AddFileBanner.CSharpAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.AddImport.CSharpAddImportCodeFixProvider", "Add Import" }, + { "Microsoft.CodeAnalysis.CSharp.AddMissingReference.CSharpAddMissingReferenceCodeFixProvider", "Add Missing Reference" }, + { "Microsoft.CodeAnalysis.CSharp.AddObsoleteAttribute.CSharpAddObsoleteAttributeCodeFixProvider", "Add Obsolete Attribute" }, + { "Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers.CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, + { "Microsoft.CodeAnalysis.CSharp.AddPackage.CSharpAddSpecificPackageCodeFixProvider", "Add Package: Add Specific Package" }, + { "Microsoft.CodeAnalysis.CSharp.AddParameter.CSharpAddParameterCodeFixProvider", "Add Parameter" }, + { "Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType.CSharpAliasAmbiguousTypeCodeFixProvider", "Alias Ambiguous Type" }, + { "Microsoft.CodeAnalysis.CSharp.AssignOutParameters.AssignOutParametersAboveReturnCodeFixProvider", "Assign Out Parameters: Above Return" }, + { "Microsoft.CodeAnalysis.CSharp.AssignOutParameters.AssignOutParametersAtStartCodeFixProvider", "Assign Out Parameters: At Start" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.AddExplicitCast.CSharpAddExplicitCastCodeFixProvider", "Add Explicit Cast" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.AddInheritdoc.AddInheritdocCodeFixProvider", "Add Inheritdoc" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.ConvertToAsync.CSharpConvertToAsyncMethodCodeFixProvider", "Convert To Async: Method" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.DeclareAsNullable.CSharpDeclareAsNullableCodeFixProvider", "Declare As Nullable" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FixIncorrectConstraint.CSharpFixIncorrectConstraintCodeFixProvider", "Fix Incorrect Constraint" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FixReturnType.CSharpFixReturnTypeCodeFixProvider", "Fix Return Type" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.ForEachCast.CSharpForEachCastCodeFixProvider", "For Each Cast" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FullyQualify.CSharpFullyQualifyCodeFixProvider", "Fully Qualify" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateDeconstructMethod.GenerateDeconstructMethodCodeFixProvider", "Generate Deconstruct Method" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateEnumMember.GenerateEnumMemberCodeFixProvider", "Generate Enum Member" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateConversionCodeFixProvider", "Generate Method: Generate Conversion" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateMethodCodeFixProvider", "Generate Method" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateType.GenerateTypeCodeFixProvider", "Generate Type" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase.HideBaseCodeFixProvider", "Hide Base" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase.HideBaseCodeFixProvider+AddNewKeywordAction", "Hide Base: Add New Keyword" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.Iterator.CSharpAddYieldCodeFixProvider", "Iterator: Add Yield" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.Iterator.CSharpChangeToIEnumerableCodeFixProvider", "Iterator: Change To IEnumerable" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeMemberRequired.CSharpMakeMemberRequiredCodeFixProvider", "Make Member Required" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeStatementAsynchronous.CSharpMakeStatementAsynchronousCodeFixProvider", "Make Statement Asynchronous" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.MatchFolderAndNamespace.CSharpChangeNamespaceToMatchFolderCodeFixProvider", "Match Folder And Namespace: Change Namespace To Match Folder" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveNewModifier.RemoveNewModifierCodeFixProvider", "Remove New Modifier" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnnecessaryNullableDirective.CSharpRemoveUnnecessaryNullableDirectiveCodeFixProvider", "Remove Unnecessary Nullable Directive" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.TransposeRecordKeyword.CSharpTransposeRecordKeywordCodeFixProvider", "Transpose Record Keyword" }, + { "Microsoft.CodeAnalysis.CSharp.CodeFixes.UseNameofInAttribute.CSharpUseNameofInAttributeCodeFixProvider", "Use Nameof In Attribute" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.AddAwait.CSharpAddAwaitCodeRefactoringProvider", "Add Await (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.AddMissingImports.CSharpAddMissingImportsRefactoringProvider", "Add Missing Imports (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ConvertLocalFunctionToMethod.CSharpConvertLocalFunctionToMethodCodeRefactoringProvider", "Convert Local Function To Method (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider", "Enable Nullable (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider+CustomCodeAction", "Enable Nullable (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider+FixAllProvider+FixAllCodeAction", "Enable Nullable Fix All (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ExtractClass.CSharpExtractClassCodeRefactoringProvider", "Extract Class (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineMethod.CSharpInlineMethodRefactoringProvider", "Inline Method (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary.CSharpInlineTemporaryCodeRefactoringProvider", "Inline Temporary (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveStaticMembers.CSharpMoveStaticMembersRefactoringProvider", "Move Static Members (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.PullMemberUp.CSharpPullMemberUpCodeRefactoringProvider", "Pull Member Up (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace.CSharpSyncNamespaceCodeRefactoringProvider", "Sync Namespace (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseExplicitType.UseExplicitTypeCodeRefactoringProvider", "Use Explicit Type (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseImplicitType.UseImplicitTypeCodeRefactoringProvider", "Use Implicit Type (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseRecursivePatterns.UseRecursivePatternsCodeRefactoringProvider", "Use Recursive Patterns (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConditionalExpressionInStringInterpolation.CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider", "Add Parentheses Around Conditional Expression In Interpolated String" }, + { "Microsoft.CodeAnalysis.CSharp.ConflictMarkerResolution.CSharpResolveConflictMarkerCodeFixProvider", "Conflict Marker Resolution: Resolve Conflict Marker" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType.CSharpConvertAnonymousTypeToClassCodeRefactoringProvider", "Convert Anonymous Type: To Class (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType.CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider", "Convert Anonymous Type: To Tuple (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertAutoPropertyToFullProperty.CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider", "Convert Auto Property To Full Property (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertBetweenRegularAndVerbatimString.ConvertBetweenRegularAndVerbatimInterpolatedStringCodeRefactoringProvider", "Convert Between Regular And Verbatim String: Convert Between Regular And Verbatim Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertBetweenRegularAndVerbatimString.ConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider", "Convert Between Regular And Verbatim String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertCast.CSharpConvertDirectCastToTryCastCodeRefactoringProvider", "Convert Cast: Convert Direct Cast To Try Cast (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertCast.CSharpConvertTryCastToDirectCastCodeRefactoringProvider", "Convert Cast: Convert Try Cast To Direct Cast (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertForEachToFor.CSharpConvertForEachToForCodeRefactoringProvider", "Convert For Each To For (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertForToForEach.CSharpConvertForToForEachCodeRefactoringProvider", "Convert For To For Each (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertIfToSwitch.CSharpConvertIfToSwitchCodeRefactoringProvider", "Convert If To Switch (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertLinq.ConvertForEachToLinqQuery.CSharpConvertForEachToLinqQueryProvider", "Convert For Each To Linq Query" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertLinq.CSharpConvertLinqQueryToForEachProvider", "Convert Linq: Query To For Each" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertNamespaceCodeFixProvider", "Convert Namespace" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertNamespaceCodeRefactoringProvider", "Convert Namespace (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertNumericLiteral.CSharpConvertNumericLiteralCodeRefactoringProvider", "Convert Numeric Literal (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertPrimaryToRegularConstructor.ConvertPrimaryToRegularConstructorCodeRefactoringProvider", "Convert Primary To Regular Constructor (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToProgramMainCodeFixProvider", "Convert Program: Convert To Program Main" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToProgramMainCodeRefactoringProvider", "Convert Program: Convert To Program Main (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToTopLevelStatementsCodeFixProvider", "Convert Program: Convert To Top Level Statements" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToTopLevelStatementsCodeRefactoringProvider", "Convert Program: Convert To Top Level Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertSwitchStatementToExpression.ConvertSwitchStatementToExpressionCodeFixProvider", "Convert Switch Statement To Expression" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString.CSharpConvertConcatenationToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Concatenation To Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString.CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Placeholder To Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertToRawString.ConvertStringToRawStringCodeRefactoringProvider", "Convert String To Raw String (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertToRecord.CSharpConvertToRecordCodeFixProvider", "Convert To Record" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertToRecord.CSharpConvertToRecordRefactoringProvider", "Convert To Record (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertTupleToStruct.CSharpConvertTupleToStructCodeRefactoringProvider", "Convert Tuple To Struct (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ConvertTypeOfToNameOf.CSharpConvertTypeOfToNameOfCodeFixProvider", "Convert Type Of To Name Of" }, + { "Microsoft.CodeAnalysis.CSharp.Copilot.CSharpCopilotCodeFixProvider", "Copilot" }, + { "Microsoft.CodeAnalysis.CSharp.Copilot.CSharpCopilotCodeFixProvider+CopilotDismissChangesCodeAction", "Copilot: Dismiss Changes" }, + { "Microsoft.CodeAnalysis.CSharp.Copilot.CSharpCopilotCodeFixProvider+CopilotDocumentChangeCodeAction", "Copilot: Document Change" }, + { "Microsoft.CodeAnalysis.CSharp.Copilot.CSharpImplementNotImplementedExceptionFixProvider", "Implement with Copilot" }, + { "Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesCodeFixProvider", "Add Braces" }, + { "Microsoft.CodeAnalysis.CSharp.DisambiguateSameVariable.CSharpDisambiguateSameVariableCodeFixProvider", "Disambiguate Same Variable" }, + { "Microsoft.CodeAnalysis.CSharp.DocumentationComments.CSharpAddDocCommentNodesCodeFixProvider", "Documentation Comments: Add DocComment Nodes" }, + { "Microsoft.CodeAnalysis.CSharp.DocumentationComments.CSharpRemoveDocCommentNodeCodeFixProvider", "Documentation Comments: Remove DocComment Node" }, + { "Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.CSharpJsonDetectionCodeFixProvider", "Embedded Languages: Json Detection" }, + { "Microsoft.CodeAnalysis.CSharp.FileHeaders.CSharpFileHeaderCodeFixProvider", "File Headers: File Header" }, + { "Microsoft.CodeAnalysis.CSharp.GenerateConstructor.GenerateConstructorCodeFixProvider", "Generate Constructor" }, + { "Microsoft.CodeAnalysis.CSharp.GenerateConstructors.CSharpGenerateConstructorsCodeRefactoringProvider", "Generate Constructors (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.GenerateDefaultConstructors.CSharpGenerateDefaultConstructorsCodeFixProvider", "Generate Default Constructors" }, + { "Microsoft.CodeAnalysis.CSharp.GenerateVariable.CSharpGenerateVariableCodeFixProvider", "Generate Variable" }, + { "Microsoft.CodeAnalysis.CSharp.ImplementAbstractClass.CSharpImplementAbstractClassCodeFixProvider", "Implement Abstract Class" }, + { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementExplicitlyCodeRefactoringProvider", "Implement Interface: Implement Explicitly (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementImplicitlyCodeRefactoringProvider", "Implement Interface: Implement Implicitly (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementInterfaceCodeFixProvider", "Implement Interface" }, + { "Microsoft.CodeAnalysis.CSharp.InitializeParameter.CSharpAddParameterCheckCodeRefactoringProvider", "Initialize Parameter: Add Parameter Check (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InitializeParameter.CSharpInitializeMemberFromParameterCodeRefactoringProvider", "Initialize Parameter: Initialize Member From Parameter (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InitializeParameter.CSharpInitializeMemberFromPrimaryConstructorParameterCodeRefactoringProvider", "Initialize Parameter: Initialize Member From Primary Constructor Parameter (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InlineDeclaration.CSharpInlineDeclarationCodeFixProvider", "Inline Declaration" }, + { "Microsoft.CodeAnalysis.CSharp.IntroduceParameter.CSharpIntroduceParameterCodeRefactoringProvider", "Introduce Parameter (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.IntroduceUsingStatement.CSharpIntroduceUsingStatementCodeRefactoringProvider", "Introduce Using Statement (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.IntroduceVariable.CSharpIntroduceLocalForExpressionCodeRefactoringProvider", "Introduce Variable: Introduce Local For Expression (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InvertConditional.CSharpInvertConditionalCodeRefactoringProvider", "Invert Conditional (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InvertIf.CSharpInvertIfCodeRefactoringProvider", "Invert If (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InvertLogical.CSharpInvertLogicalCodeRefactoringProvider", "Invert Logical (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess.InvokeDelegateWithConditionalAccessCodeFixProvider", "Invoke Delegate With Conditional Access" }, + { "Microsoft.CodeAnalysis.CSharp.MakeAnonymousFunctionStatic.CSharpMakeAnonymousFunctionStaticCodeFixProvider", "Make Anonymous Function Static" }, + { "Microsoft.CodeAnalysis.CSharp.MakeFieldReadonly.CSharpMakeFieldReadonlyCodeFixProvider", "Make Field Readonly" }, + { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.MakeLocalFunctionStaticCodeFixProvider", "Make Local Function Static" }, + { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.MakeLocalFunctionStaticCodeRefactoringProvider", "Make Local Function Static (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.PassInCapturedVariablesAsArgumentsCodeFixProvider", "Make Local Function Static: Pass In Captured Variables As Arguments" }, + { "Microsoft.CodeAnalysis.CSharp.MakeMemberStatic.CSharpMakeMemberStaticCodeFixProvider", "Make Member Static" }, + { "Microsoft.CodeAnalysis.CSharp.MakeMethodAsynchronous.CSharpMakeMethodAsynchronousCodeFixProvider", "Make Method Asynchronous" }, + { "Microsoft.CodeAnalysis.CSharp.MakeMethodSynchronous.CSharpMakeMethodSynchronousCodeFixProvider", "Make Method Synchronous" }, + { "Microsoft.CodeAnalysis.CSharp.MakeRefStruct.MakeRefStructCodeFixProvider", "Make Ref Struct" }, + { "Microsoft.CodeAnalysis.CSharp.MakeStructFieldsWritable.CSharpMakeStructFieldsWritableCodeFixProvider", "Make Struct Fields Writable" }, + { "Microsoft.CodeAnalysis.CSharp.MakeStructMemberReadOnly.CSharpMakeStructMemberReadOnlyCodeFixProvider", "Make Struct Member Read Only" }, + { "Microsoft.CodeAnalysis.CSharp.MakeStructReadOnly.CSharpMakeStructReadOnlyCodeFixProvider", "Make Struct Read Only" }, + { "Microsoft.CodeAnalysis.CSharp.MakeTypeAbstract.CSharpMakeTypeAbstractCodeFixProvider", "Make Type Abstract" }, + { "Microsoft.CodeAnalysis.CSharp.MakeTypePartial.CSharpMakeTypePartialCodeFixProvider", "Make Type Partial" }, + { "Microsoft.CodeAnalysis.CSharp.MisplacedUsingDirectives.MisplacedUsingDirectivesCodeFixProvider", "Misplaced Using Directives" }, + { "Microsoft.CodeAnalysis.CSharp.MoveDeclarationNearReference.CSharpMoveDeclarationNearReferenceCodeRefactoringProvider", "Move Declaration Near Reference (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.NameTupleElement.CSharpNameTupleElementCodeRefactoringProvider", "Name Tuple Element (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.NewLines.ArrowExpressionClausePlacement.ArrowExpressionClausePlacementCodeFixProvider", "Arrow Expression Clause Placement" }, + { "Microsoft.CodeAnalysis.CSharp.NewLines.ConditionalExpressionPlacement.ConditionalExpressionPlacementCodeFixProvider", "Conditional Expression Placement" }, + { "Microsoft.CodeAnalysis.CSharp.NewLines.ConsecutiveBracePlacement.ConsecutiveBracePlacementCodeFixProvider", "Consecutive Brace Placement" }, + { "Microsoft.CodeAnalysis.CSharp.NewLines.ConstructorInitializerPlacement.ConstructorInitializerPlacementCodeFixProvider", "Constructor Initializer Placement" }, + { "Microsoft.CodeAnalysis.CSharp.NewLines.EmbeddedStatementPlacement.EmbeddedStatementPlacementCodeFixProvider", "Embedded Statement Placement" }, + { "Microsoft.CodeAnalysis.CSharp.OrderModifiers.CSharpOrderModifiersCodeFixProvider", "Order Modifiers" }, + { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchExpressionCodeFixProvider", "Populate Switch: Expression" }, + { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchStatementCodeFixProvider", "Populate Switch: Statement" }, + { "Microsoft.CodeAnalysis.CSharp.QualifyMemberAccess.CSharpQualifyMemberAccessCodeFixProvider", "Qualify Member Access" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier.CSharpRemoveAsyncModifierCodeFixProvider", "Remove Async Modifier" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveConfusingSuppression.CSharpRemoveConfusingSuppressionCodeFixProvider", "Remove Confusing Suppression" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveInKeyword.RemoveInKeywordCodeFixProvider", "Remove In Keyword" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryCast.CSharpRemoveUnnecessaryCastCodeFixProvider", "Remove Unnecessary Cast" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryDiscardDesignation.CSharpRemoveUnnecessaryDiscardDesignationCodeFixProvider", "Remove Unnecessary Discard Designation" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports.CSharpRemoveUnnecessaryImportsCodeFixProvider", "Remove unnecessary imports" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression.CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider", "Remove Unnecessary Lambda Expression" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses.CSharpRemoveUnnecessaryParenthesesCodeFixProvider", "Remove Unnecessary Parentheses" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnreachableCode.CSharpRemoveUnreachableCodeCodeFixProvider", "Remove Unreachable Code" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedLocalFunction.CSharpRemoveUnusedLocalFunctionCodeFixProvider", "Remove Unused Local Function" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedMembers.CSharpRemoveUnusedMembersCodeFixProvider", "Remove Unused Members" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues.CSharpRemoveUnusedValuesCodeFixProvider", "Remove Unused Parameters And Values: Remove Unused Values" }, + { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedVariable.CSharpRemoveUnusedVariableCodeFixProvider", "Remove Unused Variable" }, + { "Microsoft.CodeAnalysis.CSharp.ReplaceConditionalWithStatements.CSharpReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ReplaceDefaultLiteral.CSharpReplaceDefaultLiteralCodeFixProvider", "Replace Default Literal" }, + { "Microsoft.CodeAnalysis.CSharp.ReplaceDocCommentTextWithTag.CSharpReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.ReverseForStatement.CSharpReverseForStatementCodeRefactoringProvider", "Reverse For Statement (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.SimplifyInterpolation.CSharpSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, + { "Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression.CSharpSimplifyLinqTypeCheckAndCastCodeFixProvider", "Simplify Linq Expression: Simplify Linq Type Check And Cast" }, + { "Microsoft.CodeAnalysis.CSharp.SimplifyPropertyPattern.CSharpSimplifyPropertyPatternCodeFixProvider", "Simplify Property Pattern" }, + { "Microsoft.CodeAnalysis.CSharp.SimplifyThisOrMe.CSharpSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, + { "Microsoft.CodeAnalysis.CSharp.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, + { "Microsoft.CodeAnalysis.CSharp.SpellCheck.CSharpSpellCheckCodeFixProvider", "Spell Check" }, + { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpMergeConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Consecutive If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpMergeNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Nested If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpSplitIntoConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Consecutive If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpSplitIntoNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Nested If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.TypeStyle.UseExplicitTypeCodeFixProvider", "Use Explicit Type" }, + { "Microsoft.CodeAnalysis.CSharp.TypeStyle.UseImplicitTypeCodeFixProvider", "Use Implicit Type" }, + { "Microsoft.CodeAnalysis.CSharp.UnsealClass.CSharpUnsealClassCodeFixProvider", "Unseal Class" }, + { "Microsoft.CodeAnalysis.CSharp.UpdateProjectToAllowUnsafe.CSharpUpdateProjectToAllowUnsafeCodeFixProvider", "Update Project To Allow Unsafe" }, + { "Microsoft.CodeAnalysis.CSharp.UpgradeProject.CSharpUpgradeProjectCodeFixProvider", "Upgrade Project" }, + { "Microsoft.CodeAnalysis.CSharp.UseAutoProperty.CSharpUseAutoPropertyCodeFixProvider", "Use Auto Property" }, + { "Microsoft.CodeAnalysis.CSharp.UseCoalesceExpression.CSharpUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider", "Use Coalesce Expression: For If Null Statement Check" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForArrayCodeFixProvider", "Use Collection Expression: For Array" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForBuilderCodeFixProvider", "Use Collection Expression: For Builder" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForCreateCodeFixProvider", "Use Collection Expression: For Create" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForEmptyCodeFixProvider", "Use Collection Expression: For Empty" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForFluentCodeFixProvider", "Use Collection Expression: For Fluent" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForNewCodeFixProvider", "Use Collection Expression: For New" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionExpression.CSharpUseCollectionExpressionForStackAllocCodeFixProvider", "Use Collection Expression: For Stack Alloc" }, + { "Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer.CSharpUseCollectionInitializerCodeFixProvider", "Use Collection Initializer" }, + { "Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment.CSharpUseCompoundAssignmentCodeFixProvider", "Use Compound Assignment" }, + { "Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment.CSharpUseCompoundCoalesceAssignmentCodeFixProvider", "Use Compound Assignment: Use Compound Coalesce Assignment" }, + { "Microsoft.CodeAnalysis.CSharp.UseConditionalExpression.CSharpUseConditionalExpressionForAssignmentCodeFixProvider", "Use Conditional Expression: For Assignment" }, + { "Microsoft.CodeAnalysis.CSharp.UseConditionalExpression.CSharpUseConditionalExpressionForReturnCodeFixProvider", "Use Conditional Expression: For Return" }, + { "Microsoft.CodeAnalysis.CSharp.UseDeconstruction.CSharpUseDeconstructionCodeFixProvider", "Use Deconstruction" }, + { "Microsoft.CodeAnalysis.CSharp.UseDefaultLiteral.CSharpUseDefaultLiteralCodeFixProvider", "Use Default Literal" }, + { "Microsoft.CodeAnalysis.CSharp.UseExplicitArrayInExpressionTree.CSharpUseExplicitArrayInExpressionTreeCodeFixProvider", "Use Explicit Array In Expression Tree" }, + { "Microsoft.CodeAnalysis.CSharp.UseExplicitTypeForConst.UseExplicitTypeForConstCodeFixProvider", "Use Explicit Type For Const" }, + { "Microsoft.CodeAnalysis.CSharp.UseExpressionBody.UseExpressionBodyCodeFixProvider", "Use Expression Body" }, + { "Microsoft.CodeAnalysis.CSharp.UseExpressionBody.UseExpressionBodyCodeRefactoringProvider", "Use Expression Body (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.UseExpressionBodyForLambda.UseExpressionBodyForLambdaCodeFixProvider", "Use Expression Body For Lambda" }, + { "Microsoft.CodeAnalysis.CSharp.UseExpressionBodyForLambda.UseExpressionBodyForLambdaCodeRefactoringProvider", "Use Expression Body For Lambda (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.UseImplicitlyTypedLambdaExpression.CSharpUseImplicitlyTypedLambdaExpressionCodeFixProvider", "Use Implicitly Typed Lambda Expression" }, + { "Microsoft.CodeAnalysis.CSharp.UseImplicitObjectCreation.CSharpUseImplicitObjectCreationCodeFixProvider", "Use Implicit Object Creation" }, + { "Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator.CSharpUseIndexOperatorCodeFixProvider", "Use Index Or Range Operator: Use Index Operator" }, + { "Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator.CSharpUseRangeOperatorCodeFixProvider", "Use Index Or Range Operator: Use Range Operator" }, + { "Microsoft.CodeAnalysis.CSharp.UseInferredMemberName.CSharpUseInferredMemberNameCodeFixProvider", "Use Inferred Member Name" }, + { "Microsoft.CodeAnalysis.CSharp.UseInterpolatedVerbatimString.CSharpUseInterpolatedVerbatimStringCodeFixProvider", "Use Interpolated Verbatim String" }, + { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseIsNullCheckForCastAndEqualityOperatorCodeFixProvider", "Use Is Null Check: For Cast And Equality Operator" }, + { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseIsNullCheckForReferenceEqualsCodeFixProvider", "Use Is Null Check: For Reference Equals" }, + { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseNullCheckOverTypeCheckCodeFixProvider", "Use Is Null Check: Use Null Check Over Type Check" }, + { "Microsoft.CodeAnalysis.CSharp.UseLocalFunction.CSharpUseLocalFunctionCodeFixProvider", "Use Local Function" }, + { "Microsoft.CodeAnalysis.CSharp.UseNamedArguments.CSharpUseNamedArgumentsCodeRefactoringProvider", "Use Named Arguments (Refactoring)" }, + { "Microsoft.CodeAnalysis.CSharp.UseNullPropagation.CSharpUseNullPropagationCodeFixProvider", "Use Null Propagation" }, + { "Microsoft.CodeAnalysis.CSharp.UseObjectInitializer.CSharpUseObjectInitializerCodeFixProvider", "Use Object Initializer" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternCombinators.CSharpUsePatternCombinatorsCodeFixProvider", "Use Pattern Combinators" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpAsAndMemberAccessCodeFixProvider", "Use Pattern Matching: As And Member Access" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpAsAndNullCheckCodeFixProvider", "Use Pattern Matching: As And Null Check" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpIsAndCastCheckCodeFixProvider", "Use Pattern Matching: Is And Cast Check" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpIsAndCastCheckWithoutNameCodeFixProvider", "Use Pattern Matching: Is And Cast Check Without Name" }, + { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpUseNotPatternCodeFixProvider", "Use Pattern Matching: Use Not Pattern" }, + { "Microsoft.CodeAnalysis.CSharp.UsePrimaryConstructor.CSharpUsePrimaryConstructorCodeFixProvider", "Use Primary Constructor" }, + { "Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement.UseSimpleUsingStatementCodeFixProvider", "Use Simple Using Statement" }, + { "Microsoft.CodeAnalysis.CSharp.UseSystemThreadingLock.CSharpUseSystemThreadingLockCodeFixProvider", "Use System.Threading.Lock" }, + { "Microsoft.CodeAnalysis.CSharp.UseThrowExpression.UseThrowExpressionCodeFixProvider", "Use Throw Expression" }, + { "Microsoft.CodeAnalysis.CSharp.UseTupleSwap.CSharpUseTupleSwapCodeFixProvider", "Use Tuple Swap" }, + { "Microsoft.CodeAnalysis.CSharp.UseUnboundGenericTypeInNameOf.CSharpUseUnboundGenericTypeInNameOfCodeFixProvider", "Use Unbound Generic Type In Name Of" }, + { "Microsoft.CodeAnalysis.CSharp.UseUtf8StringLiteral.UseUtf8StringLiteralCodeFixProvider", "Use UTF-8 String Literal" }, + { "Microsoft.CodeAnalysis.CSharp.Wrapping.CSharpWrappingCodeRefactoringProvider", "Wrapping (Refactoring)" }, + { "Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking.RenameTrackingCodeRefactoringProvider", "Rename Tracking (Refactoring)" }, + { "Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking.RenameTrackingTaggerProvider+RenameTrackingCodeAction", "Rename Tracking" }, + { "Microsoft.CodeAnalysis.EncapsulateField.EncapsulateFieldRefactoringProvider", "Encapsulate Field (Refactoring)" }, + { "Microsoft.CodeAnalysis.ExtractClass.ExtractClassWithDialogCodeAction", "Extract Class: With Dialog" }, + { "Microsoft.CodeAnalysis.ExtractInterface.ExtractInterfaceCodeAction", "Extract Interface" }, + { "Microsoft.CodeAnalysis.ExtractInterface.ExtractInterfaceCodeRefactoringProvider", "Extract Interface (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateComparisonOperators.GenerateComparisonOperatorsCodeRefactoringProvider", "Generate Comparison Operators (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateConstructors.AbstractGenerateConstructorsCodeRefactoringProvider+ConstructorDelegatingCodeAction", "Generate Constructors: Constructor Delegating (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateConstructors.AbstractGenerateConstructorsCodeRefactoringProvider+FieldDelegatingCodeAction", "Generate Constructors: Field Delegating (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateConstructors.AbstractGenerateConstructorsCodeRefactoringProvider+GenerateConstructorWithDialogCodeAction", "Generate Constructors: Generate Constructor With Dialog (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateDefaultConstructors.AbstractGenerateDefaultConstructorsService`1+GenerateDefaultConstructorsCodeAction", "Generate Default Constructors" }, + { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider", "Generate Equals And Get Hash Code From Members (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider+GenerateEqualsAndGetHashCodeAction", "Generate Equals And Get Hash Code From Members: Generate Equals And Get Hash (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider+GenerateEqualsAndGetHashCodeWithDialogCodeAction", "Generate Equals And Get Hash Code From Members: Generate Equals And Get Hash Code With Dialog (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateMember.GenerateEnumMember.AbstractGenerateEnumMemberService`3+GenerateEnumMemberCodeAction", "Generate Enum Member" }, + { "Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember.AbstractGenerateParameterizedMemberService`4+GenerateParameterizedMemberCodeAction", "Generate Parameterized Member" }, + { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateLocalCodeAction", "Generate Variable: Generate Local" }, + { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateParameterCodeAction", "Generate Variable: Generate Parameter" }, + { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateVariableCodeAction", "Generate Variable" }, + { "Microsoft.CodeAnalysis.GenerateOverrides.GenerateOverridesCodeRefactoringProvider", "Generate Overrides (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateOverrides.GenerateOverridesCodeRefactoringProvider+GenerateOverridesWithDialogCodeAction", "Generate Overrides: With Dialog (Refactoring)" }, + { "Microsoft.CodeAnalysis.GenerateType.AbstractGenerateTypeService`6+GenerateTypeCodeAction", "Generate Type" }, + { "Microsoft.CodeAnalysis.GenerateType.AbstractGenerateTypeService`6+GenerateTypeCodeActionWithOption", "Generate Type: With Option" }, + { "Microsoft.CodeAnalysis.IntroduceVariable.AbstractIntroduceVariableService`6+IntroduceVariableCodeAction", "Introduce Variable" }, + { "Microsoft.CodeAnalysis.IntroduceVariable.IntroduceVariableCodeRefactoringProvider", "Introduce Variable (Refactoring)" }, + { "Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions.FixAllCodeAction", "Language Server: Fix All" }, + { "Microsoft.CodeAnalysis.MoveStaticMembers.MoveStaticMembersWithDialogCodeAction", "Move Static Members: With Dialog" }, + { "Microsoft.CodeAnalysis.MoveToNamespace.MoveToNamespaceCodeAction", "Move To Namespace" }, + { "Microsoft.CodeAnalysis.MoveToNamespace.MoveToNamespaceCodeActionProvider", "Move To Namespace" }, + { "Microsoft.CodeAnalysis.NewLines.ConsecutiveStatementPlacement.ConsecutiveStatementPlacementCodeFixProvider", "Consecutive Statement Placement" }, + { "Microsoft.CodeAnalysis.NewLines.MultipleBlankLines.MultipleBlankLinesCodeFixProvider", "Multiple Blank Lines" }, + { "Microsoft.CodeAnalysis.PreferFrameworkType.PreferFrameworkTypeCodeFixProvider", "Prefer Framework Type" }, + { "Microsoft.CodeAnalysis.RemoveRedundantEquality.RemoveRedundantEqualityCodeFixProvider", "Remove Redundant Equality" }, + { "Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions.RemoveUnnecessaryAttributeSuppressionsCodeFixProvider", "Remove Unnecessary Suppressions: Remove Unnecessary Attribute Suppressions" }, + { "Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions.RemoveUnnecessaryInlineSuppressionsCodeFixProvider", "Remove Unnecessary Suppressions: Remove Unnecessary Inline Suppressions" }, + { "Microsoft.CodeAnalysis.ReplaceMethodWithProperty.ReplaceMethodWithPropertyCodeRefactoringProvider", "Replace Method With Property (Refactoring)" }, + { "Microsoft.CodeAnalysis.ReplacePropertyWithMethods.ReplacePropertyWithMethodsCodeRefactoringProvider", "Replace Property With Methods (Refactoring)" }, + { "Microsoft.CodeAnalysis.SimplifyBooleanExpression.SimplifyConditionalCodeFixProvider", "Simplify Boolean Expression: Simplify Conditional" }, + { "Microsoft.CodeAnalysis.SimplifyLinqExpression.SimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, + { "Microsoft.CodeAnalysis.UpdateLegacySuppressions.UpdateLegacySuppressionsCodeFixProvider", "Update Legacy Suppressions" }, + { "Microsoft.CodeAnalysis.UpgradeProject.ProjectOptionsChangeAction", "Upgrade Project: Project Options Change" }, + { "Microsoft.CodeAnalysis.UseCoalesceExpression.UseCoalesceExpressionForNullableTernaryConditionalCheckCodeFixProvider", "Use Coalesce Expression: For Nullable Ternary Conditional Check" }, + { "Microsoft.CodeAnalysis.UseCoalesceExpression.UseCoalesceExpressionForTernaryConditionalCheckCodeFixProvider", "Use Coalesce Expression: For Ternary Conditional Check" }, + { "Microsoft.CodeAnalysis.UseExplicitTupleName.UseExplicitTupleNameCodeFixProvider", "Use Explicit Tuple Name" }, + { "Microsoft.CodeAnalysis.UseSystemHashCode.UseSystemHashCodeCodeFixProvider", "Use System Hash Code" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddAnonymousTypeMemberName.VisualBasicAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddDebuggerDisplay.VisualBasicAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddFileBanner.VisualBasicAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddImport.VisualBasicAddImportCodeFixProvider", "Add Import" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddMissingReference.VisualBasicAddMissingReferenceCodeFixProvider", "Add Missing Reference" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddObsoleteAttribute.VisualBasicAddObsoleteAttributeCodeFixProvider", "Add Obsolete Attribute" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers.VisualBasicAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddPackage.VisualBasicAddSpecificPackageCodeFixProvider", "Add Package: Add Specific Package" }, + { "Microsoft.CodeAnalysis.VisualBasic.AddParameter.VisualBasicAddParameterCodeFixProvider", "Add Parameter" }, + { "Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType.VisualBasicAliasAmbiguousTypeCodeFixProvider", "Alias Ambiguous Type" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeActions.RemoveStatementCodeAction", "Code Actions: Remove Statement" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddExplicitCast.VisualBasicAddExplicitCastCodeFixProvider", "Add Explicit Cast" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ConvertToAsync.VisualBasicConvertToAsyncFunctionCodeFixProvider", "Convert To Async: Function" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.CorrectNextControlVariable.CorrectNextControlVariableCodeFixProvider", "Correct Next Control Variable" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.CorrectNextControlVariable.CorrectNextControlVariableCodeFixProvider+CorrectNextControlVariableCodeAction", "Correct Next Control Variable" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify.VisualBasicFullyQualifyCodeFixProvider", "Fully Qualify" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEndConstruct.GenerateEndConstructCodeFixProvider", "Generate End Construct" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEnumMember.GenerateEnumMemberCodeFixProvider", "Generate Enum Member" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider", "Generate Event" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider+GenerateEventCodeAction", "Generate Event" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateConversionCodeFixProvider", "Generate Method: Generate Conversion" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateParameterizedMemberCodeFixProvider", "Generate Method: Generate Parameterized Member" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType.GenerateTypeCodeFixProvider", "Generate Type" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider", "Incorrect Exit Continue" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+AddKeywordCodeAction", "Incorrect Exit Continue: Add Keyword" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+ReplaceKeywordCodeAction", "Incorrect Exit Continue: Replace Keyword" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+ReplaceTokenKeywordCodeAction", "Incorrect Exit Continue: Replace Token Keyword" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectFunctionReturnType.IncorrectFunctionReturnTypeCodeFixProvider", "Incorrect Function Return Type" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator.VisualBasicChangeToYieldCodeFixProvider", "Iterator: Change To Yield" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator.VisualBasicConvertToIteratorCodeFixProvider", "Iterator: Convert To Iterator" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.MoveToTopOfFile.MoveToTopOfFileCodeFixProvider", "Move To Top Of File" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.MoveToTopOfFile.MoveToTopOfFileCodeFixProvider+MoveToLineCodeAction", "Move To Top Of File: Move To Line" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase.OverloadBaseCodeFixProvider", "Overload Base" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase.OverloadBaseCodeFixProvider+AddKeywordAction", "Overload Base: Add Keyword" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.AddAwait.VisualBasicAddAwaitCodeRefactoringProvider", "Add Await (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary.VisualBasicInlineMethodRefactoringProvider", "Inline Temporary: Inline Method (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary.VisualBasicInlineTemporaryCodeRefactoringProvider", "Inline Temporary (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.MoveStaticMembers.VisualBasicMoveStaticMembersRefactoringProvider", "Move Static Members (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConflictMarkerResolution.VisualBasicResolveConflictMarkerCodeFixProvider", "Conflict Marker Resolution: Resolve Conflict Marker" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousType.VisualBasicConvertAnonymousTypeToClassCodeRefactoringProvider", "Convert Anonymous Type: To Class (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple.VisualBasicConvertAnonymousTypeToTupleCodeRefactoringProvider", "Convert Anonymous Type To Tuple (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertAutoPropertyToFullProperty.VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider", "Convert Auto Property To Full Property (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertCast.VisualBasicConvertDirectCastToTryCastCodeRefactoringProvider", "Convert Cast: Convert Direct Cast To Try Cast (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertConversionOperators.VisualBasicConvertTryCastToDirectCastCodeRefactoringProvider", "Convert Conversion Operators: Convert Try Cast To Direct Cast (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertForEachToFor.VisualBasicConvertForEachToForCodeRefactoringProvider", "Convert For Each To For (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertForToForEach.VisualBasicConvertForToForEachCodeRefactoringProvider", "Convert For To For Each (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertIfToSwitch.VisualBasicConvertIfToSwitchCodeRefactoringProvider", "Convert If To Switch (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertNumericLiteral.VisualBasicConvertNumericLiteralCodeRefactoringProvider", "Convert Numeric Literal (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString.VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Concatenation To Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString.VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Placeholder To Interpolated String (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct.VisualBasicConvertTupleToStructCodeRefactoringProvider", "Convert Tuple To Struct (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ConvertTypeOfToNameOf.VisualBasicConvertGetTypeToNameOfCodeFixProvider", "Convert Type Of To Name Of: Convert Get Type To Name Of" }, + { "Microsoft.CodeAnalysis.VisualBasic.DocumentationComments.VisualBasicRemoveDocCommentNodeCodeFixProvider", "Documentation Comments: Remove Doc Comment Node" }, + { "Microsoft.CodeAnalysis.VisualBasic.Features.EmbeddedLanguages.VisualBasicJsonDetectionCodeFixProvider", "Embedded Languages: Json Detection" }, + { "Microsoft.CodeAnalysis.VisualBasic.FileHeaders.VisualBasicFileHeaderCodeFixProvider", "File Headers: File Header" }, + { "Microsoft.CodeAnalysis.VisualBasic.GenerateConstructor.GenerateConstructorCodeFixProvider", "Generate Constructor" }, + { "Microsoft.CodeAnalysis.VisualBasic.GenerateConstructors.VisualBasicGenerateConstructorsCodeRefactoringProvider", "Generate Constructors (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.GenerateDefaultConstructors.VisualBasicGenerateDefaultConstructorsCodeFixProvider", "Generate Default Constructors" }, + { "Microsoft.CodeAnalysis.VisualBasic.GenerateVariable.VisualBasicGenerateVariableCodeFixProvider", "Generate Variable" }, + { "Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass.VisualBasicImplementAbstractClassCodeFixProvider", "Implement Abstract Class" }, + { "Microsoft.CodeAnalysis.VisualBasic.ImplementInterface.VisualBasicImplementInterfaceCodeFixProvider", "Implement Interface" }, + { "Microsoft.CodeAnalysis.VisualBasic.InitializeParameter.VisualBasicAddParameterCheckCodeRefactoringProvider", "Initialize Parameter: Add Parameter Check (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.InitializeParameter.VisualBasicInitializeMemberFromParameterCodeRefactoringProvider", "Initialize Parameter: Initialize Member From Parameter (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.IntroduceParameter.VisualBasicIntroduceParameterCodeRefactoringProvider", "Introduce Parameter (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.IntroduceUsingStatement.VisualBasicIntroduceUsingStatementCodeRefactoringProvider", "Introduce Using Statement (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.IntroduceVariable.VisualBasicIntroduceLocalForExpressionCodeRefactoringProvider", "Introduce Variable: Introduce Local For Expression (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.InvertConditional.VisualBasicInvertConditionalCodeRefactoringProvider", "Invert Conditional (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertMultiLineIfCodeRefactoringProvider", "Invert If: Invert Multi Line If (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertSingleLineIfCodeRefactoringProvider", "Invert If: Invert Single Line If (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.InvertLogical.VisualBasicInvertLogicalCodeRefactoringProvider", "Invert Logical (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.MakeFieldReadonly.VisualBasicMakeFieldReadonlyCodeFixProvider", "Make Field Readonly" }, + { "Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous.VisualBasicMakeMethodAsynchronousCodeFixProvider", "Make Method Asynchronous" }, + { "Microsoft.CodeAnalysis.VisualBasic.MakeMethodSynchronous.VisualBasicMakeMethodSynchronousCodeFixProvider", "Make Method Synchronous" }, + { "Microsoft.CodeAnalysis.VisualBasic.MakeTypeAbstract.VisualBasicMakeTypeAbstractCodeFixProvider", "Make Type Abstract" }, + { "Microsoft.CodeAnalysis.VisualBasic.MakeTypePartial.VisualBasicMakeTypePartialCodeFixProvider", "Make Type Partial" }, + { "Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference.VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider", "Move Declaration Near Reference (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.NameTupleElement.VisualBasicNameTupleElementCodeRefactoringProvider", "Name Tuple Element (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.OrderModifiers.VisualBasicOrderModifiersCodeFixProvider", "Order Modifiers" }, + { "Microsoft.CodeAnalysis.VisualBasic.PopulateSwitch.VisualBasicPopulateSwitchStatementCodeFixProvider", "Populate Switch: Statement" }, + { "Microsoft.CodeAnalysis.VisualBasic.QualifyMemberAccess.VisualBasicQualifyMemberAccessCodeFixProvider", "Qualify Member Access" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveAsyncModifier.VisualBasicRemoveAsyncModifierCodeFixProvider", "Remove Async Modifier" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveSharedFromModuleMembers.VisualBasicRemoveSharedFromModuleMembersCodeFixProvider", "Remove Shared From Module Members" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal.VisualBasicRemoveUnnecessaryByValCodeFixProvider", "Remove Unnecessary By Val" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryCast.VisualBasicRemoveUnnecessaryCastCodeFixProvider", "Remove Unnecessary Cast" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports.VisualBasicRemoveUnnecessaryImportsCodeFixProvider", "Remove unnecessary imports" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryParentheses.VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider", "Remove Unnecessary Parentheses" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedMembers.VisualBasicRemoveUnusedMembersCodeFixProvider", "Remove Unused Members" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedParametersAndValues.VisualBasicRemoveUnusedValuesCodeFixProvider", "Remove Unused Parameters And Values: Remove Unused Values" }, + { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedVariable.VisualBasicRemoveUnusedVariableCodeFixProvider", "Remove Unused Variable" }, + { "Microsoft.CodeAnalysis.VisualBasic.ReplaceConditionalWithStatements.VisualBasicReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.ReplaceDocCommentTextWithTag.VisualBasicReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyInterpolation.VisualBasicSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyObjectCreation.VisualBasicSimplifyObjectCreationCodeFixProvider", "Simplify Object Creation" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyThisOrMe.VisualBasicSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, + { "Microsoft.CodeAnalysis.VisualBasic.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, + { "Microsoft.CodeAnalysis.VisualBasic.SpellCheck.VisualBasicSpellCheckCodeFixProvider", "Spell Check" }, + { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicMergeConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Consecutive If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicMergeNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Nested If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicSplitIntoConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Consecutive If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicSplitIntoNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Nested If Statements (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.UnsealClass.VisualBasicUnsealClassCodeFixProvider", "Unseal Class" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty.VisualBasicUseAutoPropertyCodeFixProvider", "Use Auto Property" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseCoalesceExpression.VisualBasicUseCoalesceExpressionForIfNullStatementCheckCodeFixProvider", "Use Coalesce Expression: For If Null Statement Check" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer.VisualBasicUseCollectionInitializerCodeFixProvider", "Use Collection Initializer" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseCompoundAssignment.VisualBasicUseCompoundAssignmentCodeFixProvider", "Use Compound Assignment" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseConditionalExpression.VisualBasicUseConditionalExpressionForAssignmentCodeFixProvider", "Use Conditional Expression: For Assignment" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseConditionalExpression.VisualBasicUseConditionalExpressionForReturnCodeFixProvider", "Use Conditional Expression: For Return" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseInferredMemberName.VisualBasicUseInferredMemberNameCodeFixProvider", "Use Inferred Member Name" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseIsNotExpression.VisualBasicUseIsNotExpressionCodeFixProvider", "Use Is Not Expression" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck.VisualBasicUseIsNullCheckForReferenceEqualsCodeFixProvider", "Use Is Null Check: For Reference Equals" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseNamedArguments.VisualBasicUseNamedArgumentsCodeRefactoringProvider", "Use Named Arguments (Refactoring)" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation.VisualBasicUseNullPropagationCodeFixProvider", "Use Null Propagation" }, + { "Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer.VisualBasicUseObjectInitializerCodeFixProvider", "Use Object Initializer" }, + { "Microsoft.CodeAnalysis.VisualBasic.Wrapping.VisualBasicWrappingCodeRefactoringProvider", "Wrapping (Refactoring)" }, + { "Microsoft.CodeAnalysis.Wrapping.WrapItemsAction", "Wrapping: Wrap Items" }, + { "VisualBasicAddMissingImportsRefactoringProvider", "Add Missing Imports (Refactoring)" }, + }.ToImmutableDictionary(); +} diff --git a/src/Tools/BuildActionTelemetryTable/Program.cs b/src/Tools/BuildActionTelemetryTable/Program.cs index 57e8189c34faa..d14eee81a71d5 100644 --- a/src/Tools/BuildActionTelemetryTable/Program.cs +++ b/src/Tools/BuildActionTelemetryTable/Program.cs @@ -15,685 +15,360 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; +using static BuildActionTelemetryTable.CodeActionDescriptions; -namespace BuildActionTelemetryTable +namespace BuildActionTelemetryTable; + +public static partial class Program { - public static class Program + private static readonly string s_executingPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + + private static ImmutableHashSet IgnoredCodeActions { get; } = new HashSet() { - private static readonly string s_executingPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + "Microsoft.CodeAnalysis.CodeActions.CodeAction+CodeActionWithNestedActions", + "Microsoft.CodeAnalysis.CodeActions.CodeAction+DocumentChangeAction", + "Microsoft.CodeAnalysis.CodeActions.CodeAction+NoChangeAction", + "Microsoft.CodeAnalysis.CodeActions.CodeAction+SolutionChangeAction", + "Microsoft.CodeAnalysis.CodeActions.CustomCodeActions+DocumentChangeAction", + "Microsoft.CodeAnalysis.CodeActions.CustomCodeActions+SolutionChangeAction", + "Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider+PostProcessCodeAction", + }.ToImmutableHashSet(); + + public static void Main(string[] args) + { + Console.WriteLine("Loading assemblies and finding CodeActions ..."); - private static ImmutableHashSet IgnoredCodeActions { get; } = new HashSet() - { - "Microsoft.CodeAnalysis.CodeActions.CodeAction+CodeActionWithNestedActions", - "Microsoft.CodeAnalysis.CodeActions.CodeAction+DocumentChangeAction", - "Microsoft.CodeAnalysis.CodeActions.CodeAction+NoChangeAction", - "Microsoft.CodeAnalysis.CodeActions.CodeAction+SolutionChangeAction", - "Microsoft.CodeAnalysis.CodeActions.CustomCodeActions+DocumentChangeAction", - "Microsoft.CodeAnalysis.CodeActions.CustomCodeActions+SolutionChangeAction", - "Microsoft.CodeAnalysis.CodeFixes.DocumentBasedFixAllProvider+PostProcessCodeAction", - }.ToImmutableHashSet(); - - private static ImmutableDictionary CodeActionDescriptionMap { get; } = new Dictionary() + var assemblies = GetAssemblies(args); + var codeActionAndProviderTypes = GetCodeActionAndProviderTypes(assemblies); + + var telemetryInfos = GetTelemetryInfos(codeActionAndProviderTypes); + + if (!IsDescriptionMapComplete(telemetryInfos)) { - { "Microsoft.CodeAnalysis.AddConstructorParametersFromMembers.AddConstructorParametersFromMembersCodeRefactoringProvider", "Add Constructor Parameters From Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.AddConstructorParametersFromMembers.AddConstructorParametersFromMembersCodeRefactoringProvider+AddConstructorParametersCodeAction", "Add Constructor Parameters From Members: Add Constructor Parameters (Refactoring)" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+AssemblyReferenceCodeAction", "Add Import: Assembly Reference" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+InstallPackageAndAddImportCodeAction", "Add Import: Install Package And Add Import" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+InstallWithPackageManagerCodeAction", "Add Import: Install With Package Manager" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+MetadataSymbolReferenceCodeAction", "Add Import: Metadata Symbol Reference" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+ParentInstallPackageCodeAction", "Add Import: Parent Install Package" }, - { "Microsoft.CodeAnalysis.AddImport.AbstractAddImportFeatureService`1+ProjectSymbolReferenceCodeAction", "Add Import: Project Symbol Reference" }, - { "Microsoft.CodeAnalysis.AddMissingReference.AddMissingReferenceCodeAction", "Add Missing Reference" }, - { "Microsoft.CodeAnalysis.AddPackage.InstallPackageDirectlyCodeAction", "Add Package: Install Package Directly" }, - { "Microsoft.CodeAnalysis.AddPackage.InstallPackageParentCodeAction", "Add Package: Install Package Parent" }, - { "Microsoft.CodeAnalysis.AddPackage.InstallWithPackageManagerCodeAction", "Add Package: Install With Package Manager" }, - { "Microsoft.CodeAnalysis.AddRequiredParentheses.AddRequiredParenthesesCodeFixProvider", "Add Required Parentheses" }, - { "Microsoft.CodeAnalysis.ChangeSignature.ChangeSignatureCodeAction", "Change Signature" }, - { "Microsoft.CodeAnalysis.ChangeSignature.ChangeSignatureCodeRefactoringProvider", "Change Signature (Refactoring)" }, - { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureCodeStyle.ConfigureCodeStyleOptionCodeFixProvider+TopLevelConfigureCodeStyleOptionCodeAction", "Configure Code Style Option: Top Level Configure Code Style Option" }, - { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureSeverity.ConfigureSeverityLevelCodeFixProvider+TopLevelBulkConfigureSeverityCodeAction", "Configure Severity Level: Top Level Bulk Configure Severity" }, - { "Microsoft.CodeAnalysis.CodeFixes.Configuration.ConfigureSeverity.ConfigureSeverityLevelCodeFixProvider+TopLevelConfigureSeverityCodeAction", "Configure Severity Level: Top Level Configure Severity" }, - { "Microsoft.CodeAnalysis.CodeFixes.FixMultipleCodeAction", "Fix Multiple" }, - { "Microsoft.CodeAnalysis.CodeFixes.FullyQualify.AbstractFullyQualifyCodeFixProvider+GroupingCodeAction", "Fully Qualify: Grouping" }, - { "Microsoft.CodeAnalysis.CodeFixes.NamingStyles.NamingStyleCodeFixProvider", "Naming Styles: Naming Style" }, - { "Microsoft.CodeAnalysis.CodeFixes.NamingStyles.NamingStyleCodeFixProvider+FixNameCodeAction", "Naming Style: Fix Name" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageCodeAction", "Suppression: Global Suppress Message" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageFixAllCodeAction", "Suppression: Global Suppress Message Fix All" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+GlobalSuppressMessageFixAllCodeAction+GlobalSuppressionSolutionChangeAction", "Global Suppress Message Fix All: Global Suppression Solution Change" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+LocalSuppressMessageCodeAction", "Suppression: Local Suppress Message" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+PragmaWarningCodeAction", "Suppression: Pragma Warning" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+RemoveSuppressionCodeAction+AttributeRemoveAction", "Remove Suppression: Attribute Remove" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.AbstractSuppressionCodeFixProvider+RemoveSuppressionCodeAction+PragmaRemoveAction", "Remove Suppression: Pragma Remove" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.TopLevelSuppressionCodeAction", "Suppression: Top Level Suppression" }, - { "Microsoft.CodeAnalysis.CodeFixes.Suppression.WrapperCodeFixProvider", "Suppression: Wrapper" }, - { "Microsoft.CodeAnalysis.CodeFixesAndRefactorings.DocumentBasedFixAllProviderHelpers+PostProcessCodeAction", "Document Based Fix All: Post Process" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.ExtractMethod.ExtractMethodCodeRefactoringProvider", "Extract Method (Refactoring)" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.FixAllCodeRefactoringCodeAction", "Code Refactorings: Fix All Code Refactoring" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.MoveType.AbstractMoveTypeService`5+MoveTypeCodeAction", "Move Type" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.MoveType.MoveTypeCodeRefactoringProvider", "Move Type (Refactoring)" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.PullMemberUp.AbstractPullMemberUpRefactoringProvider+PullMemberUpWithDialogCodeAction", "Pull Member Up: With Dialog (Refactoring)" }, - { "Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace.AbstractSyncNamespaceCodeRefactoringProvider`3+MoveFileCodeAction", "Sync Namespace: Move File (Refactoring)" }, - { "Microsoft.CodeAnalysis.CodeStyle.CSharpFormattingCodeFixProvider", "Formatting" }, - { "Microsoft.CodeAnalysis.CodeStyle.VisualBasicFormattingCodeFixProvider", "Formatting" }, - { "Microsoft.CodeAnalysis.ConvertToInterpolatedString.ConvertRegularStringToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Regular String To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.AddOrRemoveAccessibilityModifiers.CSharpAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, - { "Microsoft.CodeAnalysis.CSharp.AddAnonymousTypeMemberName.CSharpAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, - { "Microsoft.CodeAnalysis.CSharp.AddDebuggerDisplay.CSharpAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.AddFileBanner.CSharpAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.AddImport.CSharpAddImportCodeFixProvider", "Add Import" }, - { "Microsoft.CodeAnalysis.CSharp.AddMissingReference.CSharpAddMissingReferenceCodeFixProvider", "Add Missing Reference" }, - { "Microsoft.CodeAnalysis.CSharp.AddObsoleteAttribute.CSharpAddObsoleteAttributeCodeFixProvider", "Add Obsolete Attribute" }, - { "Microsoft.CodeAnalysis.CSharp.AddPackage.CSharpAddSpecificPackageCodeFixProvider", "Add Package: Add Specific Package" }, - { "Microsoft.CodeAnalysis.CSharp.AddParameter.CSharpAddParameterCodeFixProvider", "Add Parameter" }, - { "Microsoft.CodeAnalysis.CSharp.AliasAmbiguousType.CSharpAliasAmbiguousTypeCodeFixProvider", "Alias Ambiguous Type" }, - { "Microsoft.CodeAnalysis.CSharp.AssignOutParameters.AssignOutParametersAboveReturnCodeFixProvider", "Assign Out Parameters: Above Return" }, - { "Microsoft.CodeAnalysis.CSharp.AssignOutParameters.AssignOutParametersAtStartCodeFixProvider", "Assign Out Parameters: At Start" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.AddExplicitCast.CSharpAddExplicitCastCodeFixProvider", "Add Explicit Cast" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.AddInheritdoc.AddInheritdocCodeFixProvider", "Add Inheritdoc" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.ConditionalExpressionInStringInterpolation.CSharpAddParenthesesAroundConditionalExpressionInInterpolatedStringCodeFixProvider", "Conditional Expression In String Interpolation: Add Parentheses Around Conditional Expression In Interpolated String" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.ConvertToAsync.CSharpConvertToAsyncMethodCodeFixProvider", "Convert To Async: Method" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.DeclareAsNullable.CSharpDeclareAsNullableCodeFixProvider", "Declare As Nullable" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FixIncorrectConstraint.CSharpFixIncorrectConstraintCodeFixProvider", "Fix Incorrect Constraint" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FixReturnType.CSharpFixReturnTypeCodeFixProvider", "Fix Return Type" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.ForEachCast.CSharpForEachCastCodeFixProvider", "For Each Cast" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.FullyQualify.CSharpFullyQualifyCodeFixProvider", "Fully Qualify" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateDeconstructMethod.GenerateDeconstructMethodCodeFixProvider", "Generate Deconstruct Method" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateEnumMember.GenerateEnumMemberCodeFixProvider", "Generate Enum Member" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateConversionCodeFixProvider", "Generate Method: Generate Conversion" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod.GenerateMethodCodeFixProvider", "Generate Method" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateType.GenerateTypeCodeFixProvider", "Generate Type" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase.HideBaseCodeFixProvider", "Hide Base" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.HideBase.HideBaseCodeFixProvider+AddNewKeywordAction", "Hide Base: Add New Keyword" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.Iterator.CSharpAddYieldCodeFixProvider", "Iterator: Add Yield" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.Iterator.CSharpChangeToIEnumerableCodeFixProvider", "Iterator: Change To IEnumerable" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.MakeStatementAsynchronous.CSharpMakeStatementAsynchronousCodeFixProvider", "Make Statement Asynchronous" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.MatchFolderAndNamespace.CSharpChangeNamespaceToMatchFolderCodeFixProvider", "Match Folder And Namespace: Change Namespace To Match Folder" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveInKeyword.RemoveInKeywordCodeFixProvider", "Remove In Keyword" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveNewModifier.RemoveNewModifierCodeFixProvider", "Remove New Modifier" }, - { "Microsoft.CodeAnalysis.CSharp.CodeFixes.TransposeRecordKeyword.CSharpTransposeRecordKeywordCodeFixProvider", "Transpose Record Keyword" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.AddAwait.CSharpAddAwaitCodeRefactoringProvider", "Add Await (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.AddMissingImports.CSharpAddMissingImportsRefactoringProvider", "Add Missing Imports (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ConvertLocalFunctionToMethod.CSharpConvertLocalFunctionToMethodCodeRefactoringProvider", "Convert Local Function To Method (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider", "Enable Nullable (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.EnableNullable.EnableNullableCodeRefactoringProvider+CustomCodeAction", "Enable Nullable (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.ExtractClass.CSharpExtractClassCodeRefactoringProvider", "Extract Class (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineMethod.CSharpInlineMethodRefactoringProvider", "Inline Method (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.InlineTemporary.CSharpInlineTemporaryCodeRefactoringProvider", "Inline Temporary (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.MoveStaticMembers.CSharpMoveStaticMembersRefactoringProvider", "Move Static Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.PullMemberUp.CSharpPullMemberUpCodeRefactoringProvider", "Pull Member Up (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace.CSharpSyncNamespaceCodeRefactoringProvider", "Sync Namespace (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseExplicitType.UseExplicitTypeCodeRefactoringProvider", "Use Explicit Type (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseImplicitType.UseImplicitTypeCodeRefactoringProvider", "Use Implicit Type (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseRecursivePatterns.UseRecursivePatternsCodeRefactoringProvider", "Use Recursive Patterns (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConflictMarkerResolution.CSharpResolveConflictMarkerCodeFixProvider", "Conflict Marker Resolution: Resolve Conflict Marker" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType.CSharpConvertAnonymousTypeToClassCodeRefactoringProvider", "Convert Anonymous Type: To Class (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertAnonymousType.CSharpConvertAnonymousTypeToTupleCodeRefactoringProvider", "Convert Anonymous Type: To Tuple (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertAutoPropertyToFullProperty.CSharpConvertAutoPropertyToFullPropertyCodeRefactoringProvider", "Convert Auto Property To Full Property (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertBetweenRegularAndVerbatimString.ConvertBetweenRegularAndVerbatimInterpolatedStringCodeRefactoringProvider", "Convert Between Regular And Verbatim String: Convert Between Regular And Verbatim Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertBetweenRegularAndVerbatimString.ConvertBetweenRegularAndVerbatimStringCodeRefactoringProvider", "Convert Between Regular And Verbatim String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertCast.CSharpConvertDirectCastToTryCastCodeRefactoringProvider", "Convert Cast: Convert Direct Cast To Try Cast (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertCast.CSharpConvertTryCastToDirectCastCodeRefactoringProvider", "Convert Cast: Convert Try Cast To Direct Cast (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertForEachToFor.CSharpConvertForEachToForCodeRefactoringProvider", "Convert For Each To For (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertForToForEach.CSharpConvertForToForEachCodeRefactoringProvider", "Convert For To For Each (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertIfToSwitch.CSharpConvertIfToSwitchCodeRefactoringProvider", "Convert If To Switch (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertLinq.ConvertForEachToLinqQuery.CSharpConvertForEachToLinqQueryProvider", "Convert For Each To Linq Query" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertLinq.CSharpConvertLinqQueryToForEachProvider", "Convert Linq: Query To For Each" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertNamespaceCodeFixProvider", "Convert Namespace" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertNamespace.ConvertNamespaceCodeRefactoringProvider", "Convert Namespace (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertNumericLiteral.CSharpConvertNumericLiteralCodeRefactoringProvider", "Convert Numeric Literal (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToProgramMainCodeFixProvider", "Convert Program: Convert To Program Main" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToProgramMainCodeRefactoringProvider", "Convert Program: Convert To Program Main (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToTopLevelStatementsCodeFixProvider", "Convert Program: Convert To Top Level Statements" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertProgram.ConvertToTopLevelStatementsCodeRefactoringProvider", "Convert Program: Convert To Top Level Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertSwitchStatementToExpression.ConvertSwitchStatementToExpressionCodeFixProvider", "Convert Switch Statement To Expression" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString.CSharpConvertConcatenationToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Concatenation To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertToInterpolatedString.CSharpConvertPlaceholderToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Placeholder To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertToRawString.ConvertRegularStringToRawStringCodeRefactoringProvider", "Convert To Raw String: Convert Regular String To Raw String (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertTupleToStruct.CSharpConvertTupleToStructCodeRefactoringProvider", "Convert Tuple To Struct (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ConvertTypeOfToNameOf.CSharpConvertTypeOfToNameOfCodeFixProvider", "Convert Type Of To Name Of" }, - { "Microsoft.CodeAnalysis.CSharp.Diagnostics.AddBraces.CSharpAddBracesCodeFixProvider", "Add Braces" }, - { "Microsoft.CodeAnalysis.CSharp.DisambiguateSameVariable.CSharpDisambiguateSameVariableCodeFixProvider", "Disambiguate Same Variable" }, - { "Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.CSharpJsonDetectionCodeFixProvider", "Embedded Languages: Json Detection" }, - { "Microsoft.CodeAnalysis.CSharp.FileHeaders.CSharpFileHeaderCodeFixProvider", "File Headers: File Header" }, - { "Microsoft.CodeAnalysis.CSharp.GenerateConstructor.GenerateConstructorCodeFixProvider", "Generate Constructor" }, - { "Microsoft.CodeAnalysis.CSharp.GenerateConstructorFromMembers.CSharpGenerateConstructorFromMembersCodeRefactoringProvider", "Generate Constructor From Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.GenerateDefaultConstructors.CSharpGenerateDefaultConstructorsCodeFixProvider", "Generate Default Constructors" }, - { "Microsoft.CodeAnalysis.CSharp.GenerateVariable.CSharpGenerateVariableCodeFixProvider", "Generate Variable" }, - { "Microsoft.CodeAnalysis.CSharp.ImplementAbstractClass.CSharpImplementAbstractClassCodeFixProvider", "Implement Abstract Class" }, - { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementExplicitlyCodeRefactoringProvider", "Implement Interface: Implement Explicitly (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementImplicitlyCodeRefactoringProvider", "Implement Interface: Implement Implicitly (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ImplementInterface.CSharpImplementInterfaceCodeFixProvider", "Implement Interface" }, - { "Microsoft.CodeAnalysis.CSharp.InitializeParameter.CSharpAddParameterCheckCodeRefactoringProvider", "Initialize Parameter: Add Parameter Check (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InitializeParameter.CSharpInitializeMemberFromParameterCodeRefactoringProvider", "Initialize Parameter: Initialize Member From Parameter (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InlineDeclaration.CSharpInlineDeclarationCodeFixProvider", "Inline Declaration" }, - { "Microsoft.CodeAnalysis.CSharp.IntroduceUsingStatement.CSharpIntroduceUsingStatementCodeRefactoringProvider", "Introduce Using Statement (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.IntroduceVariable.CSharpIntroduceLocalForExpressionCodeRefactoringProvider", "Introduce Variable: Introduce Local For Expression (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.IntroduceVariable.CSharpIntroduceParameterCodeRefactoringProvider", "Introduce Variable: Introduce Parameter (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InvertConditional.CSharpInvertConditionalCodeRefactoringProvider", "Invert Conditional (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InvertIf.CSharpInvertIfCodeRefactoringProvider", "Invert If (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InvertLogical.CSharpInvertLogicalCodeRefactoringProvider", "Invert Logical (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.InvokeDelegateWithConditionalAccess.InvokeDelegateWithConditionalAccessCodeFixProvider", "Invoke Delegate With Conditional Access" }, - { "Microsoft.CodeAnalysis.CSharp.MakeFieldReadonly.CSharpMakeFieldReadonlyCodeFixProvider", "Make Field Readonly" }, - { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.MakeLocalFunctionStaticCodeFixProvider", "Make Local Function Static" }, - { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.MakeLocalFunctionStaticCodeRefactoringProvider", "Make Local Function Static (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic.PassInCapturedVariablesAsArgumentsCodeFixProvider", "Make Local Function Static: Pass In Captured Variables As Arguments" }, - { "Microsoft.CodeAnalysis.CSharp.MakeMemberStatic.CSharpMakeMemberStaticCodeFixProvider", "Make Member Static" }, - { "Microsoft.CodeAnalysis.CSharp.MakeMethodAsynchronous.CSharpMakeMethodAsynchronousCodeFixProvider", "Make Method Asynchronous" }, - { "Microsoft.CodeAnalysis.CSharp.MakeMethodSynchronous.CSharpMakeMethodSynchronousCodeFixProvider", "Make Method Synchronous" }, - { "Microsoft.CodeAnalysis.CSharp.MakeRefStruct.MakeRefStructCodeFixProvider", "Make Ref Struct" }, - { "Microsoft.CodeAnalysis.CSharp.MakeStructFieldsWritable.CSharpMakeStructFieldsWritableCodeFixProvider", "Make Struct Fields Writable" }, - { "Microsoft.CodeAnalysis.CSharp.MakeTypeAbstract.CSharpMakeTypeAbstractCodeFixProvider", "Make Type Abstract" }, - { "Microsoft.CodeAnalysis.CSharp.MisplacedUsingDirectives.MisplacedUsingDirectivesCodeFixProvider", "Misplaced Using Directives" }, - { "Microsoft.CodeAnalysis.CSharp.MoveDeclarationNearReference.CSharpMoveDeclarationNearReferenceCodeRefactoringProvider", "Move Declaration Near Reference (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.NameTupleElement.CSharpNameTupleElementCodeRefactoringProvider", "Name Tuple Element (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.NewLines.ConsecutiveBracePlacement.ConsecutiveBracePlacementCodeFixProvider", "Consecutive Brace Placement" }, - { "Microsoft.CodeAnalysis.CSharp.NewLines.ConstructorInitializerPlacement.ConstructorInitializerPlacementCodeFixProvider", "Constructor Initializer Placement" }, - { "Microsoft.CodeAnalysis.CSharp.NewLines.EmbeddedStatementPlacement.EmbeddedStatementPlacementCodeFixProvider", "Embedded Statement Placement" }, - { "Microsoft.CodeAnalysis.CSharp.OrderModifiers.CSharpOrderModifiersCodeFixProvider", "Order Modifiers" }, - { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchExpressionCodeFixProvider", "Populate Switch: Expression" }, - { "Microsoft.CodeAnalysis.CSharp.PopulateSwitch.CSharpPopulateSwitchStatementCodeFixProvider", "Populate Switch: Statement" }, - { "Microsoft.CodeAnalysis.CSharp.QualifyMemberAccess.CSharpQualifyMemberAccessCodeFixProvider", "Qualify Member Access" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveAsyncModifier.CSharpRemoveAsyncModifierCodeFixProvider", "Remove Async Modifier" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveConfusingSuppression.CSharpRemoveConfusingSuppressionCodeFixProvider", "Remove Confusing Suppression" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryCast.CSharpRemoveUnnecessaryCastCodeFixProvider", "Remove Unnecessary Cast" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryDiscardDesignation.CSharpRemoveUnnecessaryDiscardDesignationCodeFixProvider", "Remove Unnecessary Discard Designation" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryImports.CSharpRemoveUnnecessaryImportsCodeFixProvider", "Remove unnecessary imports" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryLambdaExpression.CSharpRemoveUnnecessaryLambdaExpressionCodeFixProvider", "Remove Unnecessary Lambda Expression" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses.CSharpRemoveUnnecessaryParenthesesCodeFixProvider", "Remove Unnecessary Parentheses" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnreachableCode.CSharpRemoveUnreachableCodeCodeFixProvider", "Remove Unreachable Code" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedLocalFunction.CSharpRemoveUnusedLocalFunctionCodeFixProvider", "Remove Unused Local Function" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedMembers.CSharpRemoveUnusedMembersCodeFixProvider", "Remove Unused Members" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedParametersAndValues.CSharpRemoveUnusedValuesCodeFixProvider", "Remove Unused Parameters And Values: Remove Unused Values" }, - { "Microsoft.CodeAnalysis.CSharp.RemoveUnusedVariable.CSharpRemoveUnusedVariableCodeFixProvider", "Remove Unused Variable" }, - { "Microsoft.CodeAnalysis.CSharp.ReplaceConditionalWithStatements.CSharpReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ReplaceDefaultLiteral.CSharpReplaceDefaultLiteralCodeFixProvider", "Replace Default Literal" }, - { "Microsoft.CodeAnalysis.CSharp.ReplaceDocCommentTextWithTag.CSharpReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.ReverseForStatement.CSharpReverseForStatementCodeRefactoringProvider", "Reverse For Statement (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.SimplifyInterpolation.CSharpSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, - { "Microsoft.CodeAnalysis.CSharp.SimplifyLinqExpression.CSharpSimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, - { "Microsoft.CodeAnalysis.CSharp.SimplifyPropertyPattern.CSharpSimplifyPropertyPatternCodeFixProvider", "Simplify Property Pattern" }, - { "Microsoft.CodeAnalysis.CSharp.SimplifyThisOrMe.CSharpSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, - { "Microsoft.CodeAnalysis.CSharp.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, - { "Microsoft.CodeAnalysis.CSharp.SpellCheck.CSharpSpellCheckCodeFixProvider", "Spell Check" }, - { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpMergeConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Consecutive If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpMergeNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Nested If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpSplitIntoConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Consecutive If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.SplitOrMergeIfStatements.CSharpSplitIntoNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Nested If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.TypeStyle.UseExplicitTypeCodeFixProvider", "Use Explicit Type" }, - { "Microsoft.CodeAnalysis.CSharp.TypeStyle.UseImplicitTypeCodeFixProvider", "Use Implicit Type" }, - { "Microsoft.CodeAnalysis.CSharp.UnsealClass.CSharpUnsealClassCodeFixProvider", "Unseal Class" }, - { "Microsoft.CodeAnalysis.CSharp.UpdateProjectToAllowUnsafe.CSharpUpdateProjectToAllowUnsafeCodeFixProvider", "Update Project To Allow Unsafe" }, - { "Microsoft.CodeAnalysis.CSharp.UpgradeProject.CSharpUpgradeProjectCodeFixProvider", "Upgrade Project" }, - { "Microsoft.CodeAnalysis.CSharp.UseAutoProperty.CSharpUseAutoPropertyCodeFixProvider", "Use Auto Property" }, - { "Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer.CSharpUseCollectionInitializerCodeFixProvider", "Use Collection Initializer" }, - { "Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment.CSharpUseCompoundAssignmentCodeFixProvider", "Use Compound Assignment" }, - { "Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment.CSharpUseCompoundCoalesceAssignmentCodeFixProvider", "Use Compound Assignment: Use Compound Coalesce Assignment" }, - { "Microsoft.CodeAnalysis.CSharp.UseConditionalExpression.CSharpUseConditionalExpressionForAssignmentCodeFixProvider", "Use Conditional Expression: For Assignment" }, - { "Microsoft.CodeAnalysis.CSharp.UseConditionalExpression.CSharpUseConditionalExpressionForReturnCodeFixProvider", "Use Conditional Expression: For Return" }, - { "Microsoft.CodeAnalysis.CSharp.UseDeconstruction.CSharpUseDeconstructionCodeFixProvider", "Use Deconstruction" }, - { "Microsoft.CodeAnalysis.CSharp.UseDefaultLiteral.CSharpUseDefaultLiteralCodeFixProvider", "Use Default Literal" }, - { "Microsoft.CodeAnalysis.CSharp.UseExplicitTypeForConst.UseExplicitTypeForConstCodeFixProvider", "Use Explicit Type For Const" }, - { "Microsoft.CodeAnalysis.CSharp.UseExpressionBody.UseExpressionBodyCodeFixProvider", "Use Expression Body" }, - { "Microsoft.CodeAnalysis.CSharp.UseExpressionBody.UseExpressionBodyCodeRefactoringProvider", "Use Expression Body (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.UseExpressionBodyForLambda.UseExpressionBodyForLambdaCodeFixProvider", "Use Expression Body For Lambda" }, - { "Microsoft.CodeAnalysis.CSharp.UseExpressionBodyForLambda.UseExpressionBodyForLambdaCodeRefactoringProvider", "Use Expression Body For Lambda (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.UseImplicitObjectCreation.CSharpUseImplicitObjectCreationCodeFixProvider", "Use Implicit Object Creation" }, - { "Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator.CSharpUseIndexOperatorCodeFixProvider", "Use Index Or Range Operator: Use Index Operator" }, - { "Microsoft.CodeAnalysis.CSharp.UseIndexOrRangeOperator.CSharpUseRangeOperatorCodeFixProvider", "Use Index Or Range Operator: Use Range Operator" }, - { "Microsoft.CodeAnalysis.CSharp.UseInferredMemberName.CSharpUseInferredMemberNameCodeFixProvider", "Use Inferred Member Name" }, - { "Microsoft.CodeAnalysis.CSharp.UseInterpolatedVerbatimString.CSharpUseInterpolatedVerbatimStringCodeFixProvider", "Use Interpolated Verbatim String" }, - { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseIsNullCheckForCastAndEqualityOperatorCodeFixProvider", "Use Is Null Check: For Cast And Equality Operator" }, - { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseIsNullCheckForReferenceEqualsCodeFixProvider", "Use Is Null Check: For Reference Equals" }, - { "Microsoft.CodeAnalysis.CSharp.UseIsNullCheck.CSharpUseNullCheckOverTypeCheckCodeFixProvider", "Use Is Null Check: Use Null Check Over Type Check" }, - { "Microsoft.CodeAnalysis.CSharp.UseLocalFunction.CSharpUseLocalFunctionCodeFixProvider", "Use Local Function" }, - { "Microsoft.CodeAnalysis.CSharp.UseNamedArguments.CSharpUseNamedArgumentsCodeRefactoringProvider", "Use Named Arguments (Refactoring)" }, - { "Microsoft.CodeAnalysis.CSharp.UseNullPropagation.CSharpUseNullPropagationCodeFixProvider", "Use Null Propagation" }, - { "Microsoft.CodeAnalysis.CSharp.UseObjectInitializer.CSharpUseObjectInitializerCodeFixProvider", "Use Object Initializer" }, - { "Microsoft.CodeAnalysis.CSharp.UsePatternCombinators.CSharpUsePatternCombinatorsCodeFixProvider", "Use Pattern Combinators" }, - { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpAsAndNullCheckCodeFixProvider", "Use Pattern Matching: As And Null Check" }, - { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpIsAndCastCheckCodeFixProvider", "Use Pattern Matching: Is And Cast Check" }, - { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpIsAndCastCheckWithoutNameCodeFixProvider", "Use Pattern Matching: Is And Cast Check Without Name" }, - { "Microsoft.CodeAnalysis.CSharp.UsePatternMatching.CSharpUseNotPatternCodeFixProvider", "Use Pattern Matching: Use Not Pattern" }, - { "Microsoft.CodeAnalysis.CSharp.UseSimpleUsingStatement.UseSimpleUsingStatementCodeFixProvider", "Use Simple Using Statement" }, - { "Microsoft.CodeAnalysis.CSharp.UseTupleSwap.CSharpUseTupleSwapCodeFixProvider", "Use Tuple Swap" }, - { "Microsoft.CodeAnalysis.CSharp.UseUtf8StringLiteral.UseUtf8StringLiteralCodeFixProvider", "Use UTF-8 String Literal" }, - { "Microsoft.CodeAnalysis.CSharp.Wrapping.CSharpWrappingCodeRefactoringProvider", "Wrapping (Refactoring)" }, - { "Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes.CSharpAddDocCommentNodesCodeFixProvider", "Add Doc Comment Nodes" }, - { "Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes.CSharpRemoveDocCommentNodeCodeFixProvider", "Remove Doc Comment Node" }, - { "Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes.VisualBasicRemoveDocCommentNodeCodeFixProvider", "Remove Doc Comment Node" }, - { "Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking.RenameTrackingCodeRefactoringProvider", "Rename Tracking (Refactoring)" }, - { "Microsoft.CodeAnalysis.Editor.Implementation.RenameTracking.RenameTrackingTaggerProvider+RenameTrackingCodeAction", "Rename Tracking" }, - { "Microsoft.CodeAnalysis.EncapsulateField.EncapsulateFieldRefactoringProvider", "Encapsulate Field (Refactoring)" }, - { "Microsoft.CodeAnalysis.ExtractClass.ExtractClassWithDialogCodeAction", "Extract Class: With Dialog" }, - { "Microsoft.CodeAnalysis.ExtractInterface.ExtractInterfaceCodeAction", "Extract Interface" }, - { "Microsoft.CodeAnalysis.ExtractInterface.ExtractInterfaceCodeRefactoringProvider", "Extract Interface (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateComparisonOperators.GenerateComparisonOperatorsCodeRefactoringProvider", "Generate Comparison Operators (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateConstructorFromMembers.AbstractGenerateConstructorFromMembersCodeRefactoringProvider+ConstructorDelegatingCodeAction", "Generate Constructor From Members: Constructor Delegating (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateConstructorFromMembers.AbstractGenerateConstructorFromMembersCodeRefactoringProvider+FieldDelegatingCodeAction", "Generate Constructor From Members: Field Delegating (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateConstructorFromMembers.AbstractGenerateConstructorFromMembersCodeRefactoringProvider+GenerateConstructorWithDialogCodeAction", "Generate Constructor From Members: Generate Constructor With Dialog (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateDefaultConstructors.AbstractGenerateDefaultConstructorsService`1+CodeActionAll", "Generate Default Constructors: Code Action All" }, - { "Microsoft.CodeAnalysis.GenerateDefaultConstructors.AbstractGenerateDefaultConstructorsService`1+GenerateDefaultConstructorCodeAction", "Generate Default Constructors: Generate Default Constructor" }, - { "Microsoft.CodeAnalysis.GenerateDefaultConstructors.GenerateDefaultConstructorsCodeRefactoringProvider", "Generate Default Constructors (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider", "Generate Equals And Get Hash Code From Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider+GenerateEqualsAndGetHashCodeAction", "Generate Equals And Get Hash Code From Members: Generate Equals And Get Hash (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateEqualsAndGetHashCodeFromMembers.GenerateEqualsAndGetHashCodeFromMembersCodeRefactoringProvider+GenerateEqualsAndGetHashCodeWithDialogCodeAction", "Generate Equals And Get Hash Code From Members: Generate Equals And Get Hash Code With Dialog (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateMember.GenerateEnumMember.AbstractGenerateEnumMemberService`3+GenerateEnumMemberCodeAction", "Generate Enum Member" }, - { "Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember.AbstractGenerateParameterizedMemberService`4+GenerateParameterizedMemberCodeAction", "Generate Parameterized Member" }, - { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateLocalCodeAction", "Generate Variable: Generate Local" }, - { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateParameterCodeAction", "Generate Variable: Generate Parameter" }, - { "Microsoft.CodeAnalysis.GenerateMember.GenerateVariable.AbstractGenerateVariableService`3+GenerateVariableCodeAction", "Generate Variable" }, - { "Microsoft.CodeAnalysis.GenerateOverrides.GenerateOverridesCodeRefactoringProvider", "Generate Overrides (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateOverrides.GenerateOverridesCodeRefactoringProvider+GenerateOverridesWithDialogCodeAction", "Generate Overrides: With Dialog (Refactoring)" }, - { "Microsoft.CodeAnalysis.GenerateType.AbstractGenerateTypeService`6+GenerateTypeCodeAction", "Generate Type" }, - { "Microsoft.CodeAnalysis.GenerateType.AbstractGenerateTypeService`6+GenerateTypeCodeActionWithOption", "Generate Type: With Option" }, - { "Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceCodeAction", "Implement Interface" }, - { "Microsoft.CodeAnalysis.ImplementInterface.AbstractImplementInterfaceService+ImplementInterfaceWithDisposePatternCodeAction", "Implement Interface: With Dispose Pattern" }, - { "Microsoft.CodeAnalysis.IntroduceVariable.AbstractIntroduceVariableService`6+IntroduceVariableAllOccurrenceCodeAction", "Introduce Variable: All Occurrence" }, - { "Microsoft.CodeAnalysis.IntroduceVariable.AbstractIntroduceVariableService`6+IntroduceVariableCodeAction", "Introduce Variable" }, - { "Microsoft.CodeAnalysis.IntroduceVariable.IntroduceVariableCodeRefactoringProvider", "Introduce Variable (Refactoring)" }, - { "Microsoft.CodeAnalysis.MoveStaticMembers.MoveStaticMembersWithDialogCodeAction", "Move Static Members: With Dialog" }, - { "Microsoft.CodeAnalysis.MoveToNamespace.AbstractMoveToNamespaceCodeAction+MoveItemsToNamespaceCodeAction", "Move To Namespace: Move Items To Namespace" }, - { "Microsoft.CodeAnalysis.MoveToNamespace.AbstractMoveToNamespaceCodeAction+MoveTypeToNamespaceCodeAction", "Move To Namespace: Move Type To Namespace" }, - { "Microsoft.CodeAnalysis.MoveToNamespace.MoveToNamespaceCodeActionProvider", "Move To Namespace" }, - { "Microsoft.CodeAnalysis.NewLines.ConsecutiveStatementPlacement.ConsecutiveStatementPlacementCodeFixProvider", "Consecutive Statement Placement" }, - { "Microsoft.CodeAnalysis.NewLines.MultipleBlankLines.MultipleBlankLinesCodeFixProvider", "Multiple Blank Lines" }, - { "Microsoft.CodeAnalysis.PreferFrameworkType.PreferFrameworkTypeCodeFixProvider", "Prefer Framework Type" }, - { "Microsoft.CodeAnalysis.RemoveRedundantEquality.RemoveRedundantEqualityCodeFixProvider", "Remove Redundant Equality" }, - { "Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions.RemoveUnnecessaryAttributeSuppressionsCodeFixProvider", "Remove Unnecessary Suppressions: Remove Unnecessary Attribute Suppressions" }, - { "Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions.RemoveUnnecessaryInlineSuppressionsCodeFixProvider", "Remove Unnecessary Suppressions: Remove Unnecessary Inline Suppressions" }, - { "Microsoft.CodeAnalysis.ReplaceMethodWithProperty.ReplaceMethodWithPropertyCodeRefactoringProvider", "Replace Method With Property (Refactoring)" }, - { "Microsoft.CodeAnalysis.ReplacePropertyWithMethods.ReplacePropertyWithMethodsCodeRefactoringProvider", "Replace Property With Methods (Refactoring)" }, - { "Microsoft.CodeAnalysis.SimplifyBooleanExpression.SimplifyConditionalCodeFixProvider", "Simplify Boolean Expression: Simplify Conditional" }, - { "Microsoft.CodeAnalysis.UpdateLegacySuppressions.UpdateLegacySuppressionsCodeFixProvider", "Update Legacy Suppressions" }, - { "Microsoft.CodeAnalysis.UpgradeProject.ProjectOptionsChangeAction", "Upgrade Project: Project Options Change" }, - { "Microsoft.CodeAnalysis.UseAutoProperty.AbstractUseAutoPropertyCodeFixProvider`5+UseAutoPropertyCodeAction", "Use Auto Property" }, - { "Microsoft.CodeAnalysis.UseCoalesceExpression.UseCoalesceExpressionCodeFixProvider", "Use Coalesce Expression" }, - { "Microsoft.CodeAnalysis.UseCoalesceExpression.UseCoalesceExpressionForNullableCodeFixProvider", "Use Coalesce Expression: For Nullable" }, - { "Microsoft.CodeAnalysis.UseExplicitTupleName.UseExplicitTupleNameCodeFixProvider", "Use Explicit Tuple Name" }, - { "Microsoft.CodeAnalysis.UseSystemHashCode.UseSystemHashCodeCodeFixProvider", "Use System Hash Code" }, - { "Microsoft.CodeAnalysis.UseThrowExpression.UseThrowExpressionCodeFixProvider", "Use Throw Expression" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddOrRemoveAccessibilityModifiers.VisualBasicAddOrRemoveAccessibilityModifiersCodeFixProvider", "Add Accessibility Modifiers" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddAnonymousTypeMemberName.VisualBasicAddAnonymousTypeMemberNameCodeFixProvider", "Add Anonymous Type Member Name" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddDebuggerDisplay.VisualBasicAddDebuggerDisplayCodeRefactoringProvider", "Add Debugger Display (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddFileBanner.VisualBasicAddFileBannerCodeRefactoringProvider", "Add File Banner (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddImport.VisualBasicAddImportCodeFixProvider", "Add Import" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddMissingReference.VisualBasicAddMissingReferenceCodeFixProvider", "Add Missing Reference" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddObsoleteAttribute.VisualBasicAddObsoleteAttributeCodeFixProvider", "Add Obsolete Attribute" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddPackage.VisualBasicAddSpecificPackageCodeFixProvider", "Add Package: Add Specific Package" }, - { "Microsoft.CodeAnalysis.VisualBasic.AddParameter.VisualBasicAddParameterCodeFixProvider", "Add Parameter" }, - { "Microsoft.CodeAnalysis.VisualBasic.AliasAmbiguousType.VisualBasicAliasAmbiguousTypeCodeFixProvider", "Alias Ambiguous Type" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeActions.RemoveStatementCodeAction", "Code Actions: Remove Statement" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddExplicitCast.VisualBasicAddExplicitCastCodeFixProvider", "Add Explicit Cast" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ConvertToAsync.VisualBasicConvertToAsyncFunctionCodeFixProvider", "Convert To Async: Function" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.CorrectNextControlVariable.CorrectNextControlVariableCodeFixProvider", "Correct Next Control Variable" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.CorrectNextControlVariable.CorrectNextControlVariableCodeFixProvider+CorrectNextControlVariableCodeAction", "Correct Next Control Variable" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify.VisualBasicFullyQualifyCodeFixProvider", "Fully Qualify" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEndConstruct.GenerateEndConstructCodeFixProvider", "Generate End Construct" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEnumMember.GenerateEnumMemberCodeFixProvider", "Generate Enum Member" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider", "Generate Event" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEvent.GenerateEventCodeFixProvider+GenerateEventCodeAction", "Generate Event" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateConversionCodeFixProvider", "Generate Method: Generate Conversion" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod.GenerateParameterizedMemberCodeFixProvider", "Generate Method: Generate Parameterized Member" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType.GenerateTypeCodeFixProvider", "Generate Type" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider", "Incorrect Exit Continue" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+AddKeywordCodeAction", "Incorrect Exit Continue: Add Keyword" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+ReplaceKeywordCodeAction", "Incorrect Exit Continue: Replace Keyword" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectExitContinue.IncorrectExitContinueCodeFixProvider+ReplaceTokenKeywordCodeAction", "Incorrect Exit Continue: Replace Token Keyword" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.IncorrectFunctionReturnType.IncorrectFunctionReturnTypeCodeFixProvider", "Incorrect Function Return Type" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator.VisualBasicChangeToYieldCodeFixProvider", "Iterator: Change To Yield" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Iterator.VisualBasicConvertToIteratorCodeFixProvider", "Iterator: Convert To Iterator" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.MoveToTopOfFile.MoveToTopOfFileCodeFixProvider", "Move To Top Of File" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.MoveToTopOfFile.MoveToTopOfFileCodeFixProvider+MoveToLineCodeAction", "Move To Top Of File: Move To Line" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase.OverloadBaseCodeFixProvider", "Overload Base" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeFixes.OverloadBase.OverloadBaseCodeFixProvider+AddKeywordAction", "Overload Base: Add Keyword" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.AddAwait.VisualBasicAddAwaitCodeRefactoringProvider", "Add Await (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary.VisualBasicInlineMethodRefactoringProvider", "Inline Temporary: Inline Method (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.InlineTemporary.VisualBasicInlineTemporaryCodeRefactoringProvider", "Inline Temporary (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.MoveStaticMembers.VisualBasicMoveStaticMembersRefactoringProvider", "Move Static Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConflictMarkerResolution.VisualBasicResolveConflictMarkerCodeFixProvider", "Conflict Marker Resolution: Resolve Conflict Marker" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousType.VisualBasicConvertAnonymousTypeToClassCodeRefactoringProvider", "Convert Anonymous Type: To Class (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertAnonymousTypeToTuple.VisualBasicConvertAnonymousTypeToTupleCodeRefactoringProvider", "Convert Anonymous Type To Tuple (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertAutoPropertyToFullProperty.VisualBasicConvertAutoPropertyToFullPropertyCodeRefactoringProvider", "Convert Auto Property To Full Property (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertCast.VisualBasicConvertDirectCastToTryCastCodeRefactoringProvider", "Convert Cast: Convert Direct Cast To Try Cast (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertConversionOperators.VisualBasicConvertTryCastToDirectCastCodeRefactoringProvider", "Convert Conversion Operators: Convert Try Cast To Direct Cast (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertForEachToFor.VisualBasicConvertForEachToForCodeRefactoringProvider", "Convert For Each To For (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertForToForEach.VisualBasicConvertForToForEachCodeRefactoringProvider", "Convert For To For Each (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertIfToSwitch.VisualBasicConvertIfToSwitchCodeRefactoringProvider", "Convert If To Switch (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertNumericLiteral.VisualBasicConvertNumericLiteralCodeRefactoringProvider", "Convert Numeric Literal (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString.VisualBasicConvertConcatenationToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Concatenation To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertToInterpolatedString.VisualBasicConvertPlaceholderToInterpolatedStringRefactoringProvider", "Convert To Interpolated String: Convert Placeholder To Interpolated String (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertTupleToStruct.VisualBasicConvertTupleToStructCodeRefactoringProvider", "Convert Tuple To Struct (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ConvertTypeOfToNameOf.VisualBasicConvertGetTypeToNameOfCodeFixProvider", "Convert Type Of To Name Of: Convert Get Type To Name Of" }, - { "Microsoft.CodeAnalysis.VisualBasic.Features.EmbeddedLanguages.VisualBasicJsonDetectionCodeFixProvider", "Embedded Languages: Json Detection" }, - { "Microsoft.CodeAnalysis.VisualBasic.FileHeaders.VisualBasicFileHeaderCodeFixProvider", "File Headers: File Header" }, - { "Microsoft.CodeAnalysis.VisualBasic.GenerateConstructor.GenerateConstructorCodeFixProvider", "Generate Constructor" }, - { "Microsoft.CodeAnalysis.VisualBasic.GenerateConstructorFromMembers.VisualBasicGenerateConstructorFromMembersCodeRefactoringProvider", "Generate Constructor From Members (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.GenerateDefaultConstructors.VisualBasicGenerateDefaultConstructorsCodeFixProvider", "Generate Default Constructors" }, - { "Microsoft.CodeAnalysis.VisualBasic.GenerateVariable.VisualBasicGenerateVariableCodeFixProvider", "Generate Variable" }, - { "Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass.VisualBasicImplementAbstractClassCodeFixProvider", "Implement Abstract Class" }, - { "Microsoft.CodeAnalysis.VisualBasic.ImplementInterface.VisualBasicImplementInterfaceCodeFixProvider", "Implement Interface" }, - { "Microsoft.CodeAnalysis.VisualBasic.InitializeParameter.VisualBasicAddParameterCheckCodeRefactoringProvider", "Initialize Parameter: Add Parameter Check (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.InitializeParameter.VisualBasicInitializeMemberFromParameterCodeRefactoringProvider", "Initialize Parameter: Initialize Member From Parameter (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.IntroduceUsingStatement.VisualBasicIntroduceUsingStatementCodeRefactoringProvider", "Introduce Using Statement (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.IntroduceVariable.VisualBasicIntroduceLocalForExpressionCodeRefactoringProvider", "Introduce Variable: Introduce Local For Expression (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.IntroduceVariable.VisualBasicIntroduceParameterCodeRefactoringProvider", "Introduce Variable: Introduce Parameter (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.InvertConditional.VisualBasicInvertConditionalCodeRefactoringProvider", "Invert Conditional (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertMultiLineIfCodeRefactoringProvider", "Invert If: Invert Multi Line If (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.InvertIf.VisualBasicInvertSingleLineIfCodeRefactoringProvider", "Invert If: Invert Single Line If (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.InvertLogical.VisualBasicInvertLogicalCodeRefactoringProvider", "Invert Logical (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.MakeFieldReadonly.VisualBasicMakeFieldReadonlyCodeFixProvider", "Make Field Readonly" }, - { "Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous.VisualBasicMakeMethodAsynchronousCodeFixProvider", "Make Method Asynchronous" }, - { "Microsoft.CodeAnalysis.VisualBasic.MakeMethodSynchronous.VisualBasicMakeMethodSynchronousCodeFixProvider", "Make Method Synchronous" }, - { "Microsoft.CodeAnalysis.VisualBasic.MakeTypeAbstract.VisualBasicMakeTypeAbstractCodeFixProvider", "Make Type Abstract" }, - { "Microsoft.CodeAnalysis.VisualBasic.MoveDeclarationNearReference.VisualBasicMoveDeclarationNearReferenceCodeRefactoringProvider", "Move Declaration Near Reference (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.NameTupleElement.VisualBasicNameTupleElementCodeRefactoringProvider", "Name Tuple Element (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.OrderModifiers.VisualBasicOrderModifiersCodeFixProvider", "Order Modifiers" }, - { "Microsoft.CodeAnalysis.VisualBasic.PopulateSwitch.VisualBasicPopulateSwitchStatementCodeFixProvider", "Populate Switch: Statement" }, - { "Microsoft.CodeAnalysis.VisualBasic.QualifyMemberAccess.VisualBasicQualifyMemberAccessCodeFixProvider", "Qualify Member Access" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveAsyncModifier.VisualBasicRemoveAsyncModifierCodeFixProvider", "Remove Async Modifier" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveSharedFromModuleMembers.VisualBasicRemoveSharedFromModuleMembersCodeFixProvider", "Remove Shared From Module Members" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryByVal.VisualBasicRemoveUnnecessaryByValCodeFixProvider", "Remove Unnecessary By Val" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryCast.VisualBasicRemoveUnnecessaryCastCodeFixProvider", "Remove Unnecessary Cast" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryImports.VisualBasicRemoveUnnecessaryImportsCodeFixProvider", "Remove unnecessary imports" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryParentheses.VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider", "Remove Unnecessary Parentheses" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedMembers.VisualBasicRemoveUnusedMembersCodeFixProvider", "Remove Unused Members" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedParametersAndValues.VisualBasicRemoveUnusedValuesCodeFixProvider", "Remove Unused Parameters And Values: Remove Unused Values" }, - { "Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedVariable.VisualBasicRemoveUnusedVariableCodeFixProvider", "Remove Unused Variable" }, - { "Microsoft.CodeAnalysis.VisualBasic.ReplaceConditionalWithStatements.VisualBasicReplaceConditionalWithStatementsCodeRefactoringProvider", "Replace Conditional With Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.ReplaceDocCommentTextWithTag.VisualBasicReplaceDocCommentTextWithTagCodeRefactoringProvider", "Replace Doc Comment Text With Tag (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyInterpolation.VisualBasicSimplifyInterpolationCodeFixProvider", "Simplify Interpolation" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyLinqExpression.SimplifyLinqExpressionCodeFixProvider", "Simplify Linq Expression" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyObjectCreation.VisualBasicSimplifyObjectCreationCodeFixProvider", "Simplify Object Creation" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyThisOrMe.VisualBasicSimplifyThisOrMeCodeFixProvider", "Simplify This Or Me" }, - { "Microsoft.CodeAnalysis.VisualBasic.SimplifyTypeNames.SimplifyTypeNamesCodeFixProvider", "Simplify Type Names" }, - { "Microsoft.CodeAnalysis.VisualBasic.SpellCheck.VisualBasicSpellCheckCodeFixProvider", "Spell Check" }, - { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicMergeConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Consecutive If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicMergeNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Merge Nested If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicSplitIntoConsecutiveIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Consecutive If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.SplitOrMergeIfStatements.VisualBasicSplitIntoNestedIfStatementsCodeRefactoringProvider", "Split Or Merge If Statements: Split Into Nested If Statements (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.UnsealClass.VisualBasicUnsealClassCodeFixProvider", "Unseal Class" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty.VisualBasicUseAutoPropertyCodeFixProvider", "Use Auto Property" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer.VisualBasicUseCollectionInitializerCodeFixProvider", "Use Collection Initializer" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseCompoundAssignment.VisualBasicUseCompoundAssignmentCodeFixProvider", "Use Compound Assignment" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseConditionalExpression.VisualBasicUseConditionalExpressionForAssignmentCodeFixProvider", "Use Conditional Expression: For Assignment" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseConditionalExpression.VisualBasicUseConditionalExpressionForReturnCodeFixProvider", "Use Conditional Expression: For Return" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseInferredMemberName.VisualBasicUseInferredMemberNameCodeFixProvider", "Use Inferred Member Name" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseIsNotExpression.VisualBasicUseIsNotExpressionCodeFixProvider", "Use Is Not Expression" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck.VisualBasicUseIsNullCheckForReferenceEqualsCodeFixProvider", "Use Is Null Check: For Reference Equals" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseNamedArguments.VisualBasicUseNamedArgumentsCodeRefactoringProvider", "Use Named Arguments (Refactoring)" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseNullPropagation.VisualBasicUseNullPropagationCodeFixProvider", "Use Null Propagation" }, - { "Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer.VisualBasicUseObjectInitializerCodeFixProvider", "Use Object Initializer" }, - { "Microsoft.CodeAnalysis.VisualBasic.Wrapping.VisualBasicWrappingCodeRefactoringProvider", "Wrapping (Refactoring)" }, - { "Microsoft.CodeAnalysis.Wrapping.WrapItemsAction", "Wrapping: Wrap Items" }, - { "VisualBasicAddMissingImportsRefactoringProvider", "Add Missing Imports (Refactoring)" }, - }.ToImmutableDictionary(); - - public static void Main(string[] args) + WriteCodeActionDescriptionMap(telemetryInfos); + + Console.WriteLine("Please update the CodeActionDescriptions.cs file and re-run the tool."); + } + else { - Console.WriteLine("Loading assemblies and finding CodeActions ..."); + WriteKustoDatatable(codeActionAndProviderTypes, telemetryInfos); + } - var assemblies = GetAssemblies(args); - var codeActionAndProviderTypes = GetCodeActionAndProviderTypes(assemblies); + Console.WriteLine("Complete."); + static void WriteKustoDatatable(ImmutableArray codeActionAndProviderTypes, ImmutableArray<(string TypeName, string Hash)> telemetryInfos) + { Console.WriteLine($"Generating Kusto datatable of {codeActionAndProviderTypes.Length} CodeAction and provider hashes ..."); - var telemetryInfos = GetTelemetryInfos(codeActionAndProviderTypes); - var datatable = GenerateKustoDatatable(telemetryInfos); // GenerateCodeActionsDescriptionMap(telemetryInfos); + var datatable = GenerateKustoDatatable(telemetryInfos); var filepath = Path.GetFullPath("ActionTable.txt"); Console.WriteLine($"Writing datatable to {filepath} ..."); File.WriteAllText(filepath, datatable); - - Console.WriteLine("Complete."); } - internal static ImmutableArray GetAssemblies(string[] paths) + static void WriteCodeActionDescriptionMap(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) { - if (paths.Length == 0) - { - // By default inspect the Roslyn assemblies - paths = Directory.EnumerateFiles(s_executingPath, "Microsoft.CodeAnalysis*.dll") - .ToArray(); - } + Console.WriteLine($"Generating new CodeAction Description Map ..."); - var currentDirectory = new Uri(Environment.CurrentDirectory + "\\"); - return paths.Select(path => - { - Console.WriteLine($"Loading assembly from {GetRelativePath(path, currentDirectory)}."); - return Assembly.LoadFrom(path); - }).ToImmutableArray(); + var descriptionMap = GenerateCodeActionsDescriptionMap(telemetryInfos); - static string GetRelativePath(string path, Uri baseUri) - { - var rootedPath = Path.IsPathRooted(path) - ? path - : Path.GetFullPath(path); - var relativePath = baseUri.MakeRelativeUri(new Uri(rootedPath)); - return relativePath.ToString(); - } + var filepath = Path.GetFullPath("CodeActionDescriptions.Review.cs"); + + Console.WriteLine($"Writing code file to {filepath} ..."); + + File.WriteAllText(filepath, descriptionMap); } + } - internal static ImmutableArray GetCodeActionAndProviderTypes(IEnumerable assemblies) + internal static ImmutableArray GetAssemblies(string[] paths) + { + if (paths.Length == 0) { - var types = assemblies.SelectMany( - assembly => assembly.GetTypes().Where( - type => !type.GetTypeInfo().IsInterface && !type.GetTypeInfo().IsAbstract)); - - return types - .Where(t => isCodeActionType(t) || isCodeActionProviderType(t)) - .ToImmutableArray(); + // By default inspect the Roslyn assemblies + paths = [.. Directory.EnumerateFiles(s_executingPath, "Microsoft.CodeAnalysis*.dll")]; + } - static bool isCodeActionType(Type t) => typeof(CodeAction).IsAssignableFrom(t); + var currentDirectory = new Uri(Environment.CurrentDirectory + "\\"); + return [.. paths.Select(path => + { + Console.WriteLine($"Loading assembly from {GetRelativePath(path, currentDirectory)}."); + return Assembly.LoadFrom(path); + })]; - static bool isCodeActionProviderType(Type t) => typeof(CodeFixProvider).IsAssignableFrom(t) - || typeof(CodeRefactoringProvider).IsAssignableFrom(t); + static string GetRelativePath(string path, Uri baseUri) + { + var rootedPath = Path.IsPathRooted(path) + ? path + : Path.GetFullPath(path); + var relativePath = baseUri.MakeRelativeUri(new Uri(rootedPath)); + return relativePath.ToString(); } + } + + internal static ImmutableArray GetCodeActionAndProviderTypes(IEnumerable assemblies) + { + var types = assemblies.SelectMany( + assembly => assembly.GetTypes().Where( + type => !type.GetTypeInfo().IsInterface && !type.GetTypeInfo().IsAbstract)); + + return [.. types.Where(t => IsCodeActionType(t) || IsCodeActionProviderType(t))]; - internal static ImmutableArray<(string TypeName, string Hash)> GetTelemetryInfos(ImmutableArray codeActionAndProviderTypes) + static bool IsCodeActionType(Type t) => typeof(CodeAction).IsAssignableFrom(t); + + static bool IsCodeActionProviderType(Type t) => typeof(CodeFixProvider).IsAssignableFrom(t) + || typeof(CodeRefactoringProvider).IsAssignableFrom(t); + } + + internal static ImmutableArray<(string TypeName, string Hash)> GetTelemetryInfos(ImmutableArray codeActionAndProviderTypes) + { + return [.. codeActionAndProviderTypes + .Distinct(FullNameTypeComparer.Instance) + .Select(GetTelemetryInfo) + .OrderBy(info => info.TypeName)]; + + static (string TypeName, string Hash) GetTelemetryInfo(Type type) { - return codeActionAndProviderTypes - .Distinct(FullNameTypeComparer.Instance) - .Select(GetTelemetryInfo) - .OrderBy(info => info.TypeName) - .ToImmutableArray(); + // Generate dev17 telemetry hash + var telemetryId = type.GetTelemetryId().ToString(); + var fnvHash = telemetryId.Substring(19); + + return (type.FullName!, fnvHash); + } + } + + internal static bool IsDescriptionMapComplete(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) + { + var missingDescriptions = new List(); - static (string TypeName, string Hash) GetTelemetryInfo(Type type) + foreach (var (actionTypeName, _) in telemetryInfos) + { + if (IgnoredCodeActions.Contains(actionTypeName)) { - // Generate dev17 telemetry hash - var telemetryId = type.GetTelemetryId().ToString(); - var fnvHash = telemetryId.Substring(19); + continue; + } - return (type.FullName!, fnvHash); + if (!CodeActionDescriptionMap.TryGetValue(actionTypeName, out var description)) + { + missingDescriptions.Add(actionTypeName); } } - internal static string GenerateKustoDatatable(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) + if (missingDescriptions.Count == 0) { - var missingDescriptions = new List(); + return true; + } - var table = new StringBuilder(); + Console.WriteLine($"The following Actions are new and need their description reviewed:{Environment.NewLine}{string.Join(Environment.NewLine, missingDescriptions)}"); - table.AppendLine("let actions = datatable(Description: string, ActionName: string, FnvHash: string)"); - table.AppendLine("["); + return false; + } - foreach (var (actionTypeName, fnvHash) in telemetryInfos) - { - if (IgnoredCodeActions.Contains(actionTypeName)) - { - continue; - } + internal static string GenerateKustoDatatable(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) + { + var table = new StringBuilder(); - if (!CodeActionDescriptionMap.TryGetValue(actionTypeName, out var description)) - { - description = "**MISSING**"; - missingDescriptions.Add(actionTypeName); - } + table.AppendLine("let actions = datatable(Description: string, ActionName: string, FnvHash: string)"); + table.AppendLine("["); - table.AppendLine(@$" ""{description}"", ""{actionTypeName}"", ""{fnvHash}"","); + foreach (var (actionTypeName, fnvHash) in telemetryInfos) + { + if (IgnoredCodeActions.Contains(actionTypeName)) + { + continue; } - table.Append("];"); - - if (missingDescriptions.Count > 0) + if (!CodeActionDescriptionMap.TryGetValue(actionTypeName, out var description)) { - Console.WriteLine($"Descriptions were missing for the following type names:{Environment.NewLine}{string.Join(Environment.NewLine, missingDescriptions)}"); + description = $"**NEEDS REVIEW** {GenerateCodeActionDescription(actionTypeName)}"; } - return table.ToString(); + table.AppendLine(@$" ""{description}"", ""{actionTypeName}"", ""{fnvHash}"","); } - // NOTE: This method is unused but still present in case we want to auto-generate and refresh the "CodeActionsDescriptionMap". - internal static string GenerateCodeActionsDescriptionMap(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) - { - var builder = new StringBuilder(); + table.Append("];"); - builder.AppendLine("{"); - - // Regex to split where letter capitalization changes. Try not to split up interface names such as IEnumerable. - var regex = new Regex(@" - (?<=[I])(?=[A-Z]) & - (?<=[A-Z])(?=[A-Z][a-z]) | - (?<=[^A-Z])(?=[A-Z]) | - (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace); + return table.ToString(); + } - // Prefixes and suffixes to trim out. - var prefixStrings = new[] - { - "Abstract", - "CSharp", - "VisualBasic", - "CodeFixes", - "CodeStyle", - "TypeStyle", - }; - - var suffixStrings = new[] - { - "CodeFixProvider", - "CodeRefactoringProvider", - "RefactoringProvider", - "TaggerProvider", - "CustomCodeAction", - "CodeAction", - "CodeActionWithOption", - "CodeActionProvider", - "Action", - "FeatureService", - "Service", - "ProviderHelpers", - "Provider", - }; - - foreach (var (actionOrProviderTypeName, _) in telemetryInfos) + internal static string GenerateCodeActionsDescriptionMap(ImmutableArray<(string TypeName, string Hash)> telemetryInfos) + { + var builder = new StringBuilder(); + + builder.AppendLine("// Licensed to the .NET Foundation under one or more agreements."); + builder.AppendLine("// The .NET Foundation licenses this file to you under the MIT license."); + builder.AppendLine("// See the LICENSE file in the project root for more information."); + builder.AppendLine(); + builder.AppendLine("using System.Collections.Immutable;"); + builder.AppendLine("using System.Collections.Generic;"); + builder.AppendLine(); + builder.AppendLine("namespace BuildActionTelemetryTable;"); + builder.AppendLine(); + builder.AppendLine("internal static class CodeActionDescriptions"); + builder.AppendLine("{"); + + builder.AppendLine(" public static ImmutableDictionary CodeActionDescriptionMap { get; } = new Dictionary()"); + builder.AppendLine(" {"); + + foreach (var (actionOrProviderTypeName, _) in telemetryInfos) + { + if (IgnoredCodeActions.Contains(actionOrProviderTypeName)) { - if (IgnoredCodeActions.Contains(actionOrProviderTypeName)) - { - continue; - } - - // We create the description string from the namespace and type name after omitting the well-known prefixes and suffixes. - var descriptionParts = actionOrProviderTypeName.Replace('.', '+').Split('+'); + continue; + } - // When there is deep nesting, construct the descriptions from the inner two names. - var startIndex = Math.Max(0, descriptionParts.Length - 2); + if (!CodeActionDescriptionMap.TryGetValue(actionOrProviderTypeName, out var description)) + { + description = $"**NEEDS REVIEW** {GenerateCodeActionDescription(actionOrProviderTypeName)}"; + } - var description = string.Empty; - var isRefactoring = false; + builder.AppendLine(@$" {{ ""{actionOrProviderTypeName}"", ""{description}"" }},"); + } - for (int index = startIndex; index < descriptionParts.Length; index++) - { - var part = descriptionParts[index]; + builder.AppendLine(" }.ToImmutableDictionary();"); + builder.AppendLine("}"); - // Remove TypeParameter count - if (part.Contains('`')) - { - part = part.Split('`')[0]; - } + return builder.ToString(); + } - foreach (var prefix in prefixStrings) - { - if (part.StartsWith(prefix)) - { - part = part.Substring(prefix.Length); - break; - } - } + private static string GenerateCodeActionDescription(string actionOrProviderTypeName) + { + // Regex to split where letter capitalization changes. Try not to split up interface names such as IEnumerable. + var regex = ChangeOfCaseRegex(); - foreach (var suffix in suffixStrings) - { - if (part.EndsWith(suffix)) - { - part = part.Substring(0, part.LastIndexOf(suffix)); - - if (suffix == "CodeActionWithOption") - { - part += "WithOption"; - } - else if (suffix == "CodeRefactoringProvider" || suffix == "RefactoringProvider") - { - isRefactoring = true; - } - - break; - } - } + // Prefixes and suffixes to trim out. + var prefixStrings = new[] + { + "Abstract", + "CSharp", + "VisualBasic", + "CodeFixes", + "CodeStyle", + "TypeStyle", + }; + + var suffixStrings = new[] + { + "CodeFixProvider", + "CodeRefactoringProvider", + "RefactoringProvider", + "TaggerProvider", + "CustomCodeAction", + "CodeAction", + "CodeActionWithOption", + "CodeActionProvider", + "Action", + "FeatureService", + "Service", + "ProviderHelpers", + "Provider", + }; + + // We create the description string from the namespace and type name after omitting the well-known prefixes and suffixes. + var descriptionParts = actionOrProviderTypeName.Replace('.', '+').Split('+'); + + // When there is deep nesting, construct the descriptions from the inner two names. + var startIndex = Math.Max(0, descriptionParts.Length - 2); + + var description = string.Empty; + var isRefactoring = false; + + for (var index = startIndex; index < descriptionParts.Length; index++) + { + var part = descriptionParts[index]; - if (part.Length == 0) - { - continue; - } + // Remove TypeParameter count + if (part.Contains('`')) + { + part = part.Split('`')[0]; + } - // Split type name into words - part = regex.Replace(part, " "); + foreach (var prefix in prefixStrings) + { + if (part.StartsWith(prefix)) + { + part = part.Substring(prefix.Length); + break; + } + } - if (description.Length == 0) - { - description = part; - continue; - } + foreach (var suffix in suffixStrings) + { + if (part.EndsWith(suffix)) + { + part = part.Substring(0, part.LastIndexOf(suffix)); - if (description == part) + if (suffix == "CodeActionWithOption") { - // Don't repeat the containing type name. - continue; + part += "WithOption"; } - - if (part.StartsWith(description)) + else if (suffix == "CodeRefactoringProvider" || suffix == "RefactoringProvider") { - // Don't repeat the containing type name. - part = part.Substring(description.Length).TrimStart(); + isRefactoring = true; } - description = $"{description}: {part}"; + break; } + } - if (isRefactoring) - { - // Ensure we differentiate refactorings from similar named fixes. - description += " (Refactoring)"; - } + if (part.Length == 0) + { + continue; + } + + // Split type name into words + part = regex.Replace(part, " "); + + if (description.Length == 0) + { + description = part; + continue; + } - builder.AppendLine(@$" {{ ""{actionOrProviderTypeName}"", ""{description}"" }},"); + if (description == part) + { + // Don't repeat the containing type name. + continue; } - builder.Append('}'); + if (part.StartsWith(description)) + { + // Don't repeat the containing type name. + part = part.Substring(description.Length).TrimStart(); + } - return builder.ToString(); + description = $"{description}: {part}"; } - private class FullNameTypeComparer : IEqualityComparer + if (isRefactoring) { - public static FullNameTypeComparer Instance { get; } = new FullNameTypeComparer(); + // Ensure we differentiate refactorings from similar named fixes. + description += " (Refactoring)"; + } + + return description; + } - public bool Equals(Type? x, Type? y) - => Equals(x?.FullName, y?.FullName); + private class FullNameTypeComparer : IEqualityComparer + { + public static FullNameTypeComparer Instance { get; } = new FullNameTypeComparer(); - public int GetHashCode([DisallowNull] Type obj) - { - return obj.FullName!.GetHashCode(); - } + public bool Equals(Type? x, Type? y) + => Equals(x?.FullName, y?.FullName); + + public int GetHashCode([DisallowNull] Type obj) + { + return obj.FullName!.GetHashCode(); } } + + [GeneratedRegex(""" + (?<=[I])(?=[A-Z]) & + (?<=[A-Z])(?=[A-Z][a-z]) | + (?<=[^A-Z])(?=[A-Z]) | + (?<=[A-Za-z])(?=[^A-Za-z]) + """, RegexOptions.IgnorePatternWhitespace)] + private static partial Regex ChangeOfCaseRegex(); } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/IRazorCohostClientLanguageServerManager.cs b/src/Tools/ExternalAccess/Razor/Cohost/IRazorCohostClientLanguageServerManager.cs deleted file mode 100644 index 40f56f01e0c85..0000000000000 --- a/src/Tools/ExternalAccess/Razor/Cohost/IRazorCohostClientLanguageServerManager.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 System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.LanguageServer; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost -{ - internal interface IRazorCohostClientLanguageServerManager : ILspService - { - ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken); - ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken); - ValueTask SendRequestAsync(string methodName, CancellationToken cancellationToken); - Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); - ValueTask SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); - } -} diff --git a/src/Tools/ExternalAccess/Razor/ChecksumWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/ChecksumWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/ChecksumWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/ChecksumWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorCohostDocumentRequestHandler.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorCohostDocumentRequestHandler.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorCohostDocumentRequestHandler.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorCohostDocumentRequestHandler.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorLspService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorLspService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorLspService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorLspService.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorRequestHandler.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/AbstractRazorRequestHandler.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/AbstractRazorRequestHandler.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Constants.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Constants.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Constants.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Constants.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/ExportCohostLspServiceFactoryAttribute.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/ExportCohostLspServiceFactoryAttribute.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/ExportCohostLspServiceFactoryAttribute.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/ExportCohostLspServiceFactoryAttribute.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/ExportCohostStatelessLspServiceAttribute.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/ExportCohostStatelessLspServiceAttribute.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/ExportCohostStatelessLspServiceAttribute.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/ExportCohostStatelessLspServiceAttribute.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/CodeActions.cs similarity index 99% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/CodeActions.cs index 1fe5e62b48853..a245ee3da8b32 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/CodeActions.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/CodeActions.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CodeAnalysis.LanguageServer.Handler.CodeActions; using Roslyn.LanguageServer.Protocol; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost.Handlers; diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/Completion.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Completion.cs similarity index 83% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/Completion.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Completion.cs index 6498b9ca8adce..889e52317d313 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/Completion.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Completion.cs @@ -75,36 +75,9 @@ private static CompletionListCache GetCache() CancellationToken cancellationToken) { // Razor can't construct a RazorCohostRequestContext so we need to handle the null case, for their tests - var logger = context is { } razorContext ? razorContext.GetRequiredService() : new EmptyLogger(); + var logger = context is { } razorContext ? razorContext.GetRequiredService() : NoOpLspLogger.Instance; var xmlSnippetParser = document.Project.Solution.Services.ExportProvider.GetService(); return InlineCompletionsHandler.GetInlineCompletionItemsAsync(logger, document, position, options, xmlSnippetParser, cancellationToken); } - - private sealed class EmptyLogger : ILspLogger - { - public void LogStartContext(string message, params object[] @params) - { - } - - public void LogEndContext(string message, params object[] @params) - { - } - - public void LogInformation(string message, params object[] @params) - { - } - - public void LogWarning(string message, params object[] @params) - { - } - - public void LogError(string message, params object[] @params) - { - } - - public void LogException(Exception exception, string? message = null, params object[] @params) - { - } - } } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/Diagnostics.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Diagnostics.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/Diagnostics.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Diagnostics.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentHighlights.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentHighlights.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentHighlights.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentHighlights.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentSpellCheck.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentSpellCheck.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentSpellCheck.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentSpellCheck.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentSymbols.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentSymbols.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentSymbols.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentSymbols.cs index dfc26d5ecce9e..24a871204fa66 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/DocumentSymbols.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/DocumentSymbols.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer.Handler; diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/FindAllReferences.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/FindAllReferences.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/FindAllReferences.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/FindAllReferences.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/FoldingRanges.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/FoldingRanges.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/FoldingRanges.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/FoldingRanges.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/GoToDefinition.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/GoToDefinition.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/GoToDefinition.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/GoToDefinition.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/GoToImplementation.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/GoToImplementation.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/GoToImplementation.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/GoToImplementation.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/Hover.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Hover.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/Hover.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Hover.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/InlayHints.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/InlayHints.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/InlayHints.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/OnAutoInsert.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/OnAutoInsert.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/OnAutoInsert.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/OnAutoInsert.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/Rename.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Rename.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/Rename.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/Rename.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/SemanticTokensRange.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/SemanticTokensRange.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/SemanticTokensRange.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/SemanticTokensRange.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/SignatureHelp.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/SignatureHelp.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/SignatureHelp.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/SignatureHelp.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/Handlers/ValidateBreakableRange.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/ValidateBreakableRange.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/Handlers/ValidateBreakableRange.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/Handlers/ValidateBreakableRange.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/IRazorCohostDynamicRegistrationService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorCohostDynamicRegistrationService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/IRazorCohostDynamicRegistrationService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorCohostDynamicRegistrationService.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/IRazorCustomMessageTarget.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorCustomMessageTarget.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/IRazorCustomMessageTarget.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorCustomMessageTarget.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/IRazorSemanticTokensRefreshQueue.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorSemanticTokensRefreshQueue.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/IRazorSemanticTokensRefreshQueue.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/IRazorSemanticTokensRefreshQueue.cs diff --git a/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorClientLanguageServerManagerFactory.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorClientLanguageServerManagerFactory.cs new file mode 100644 index 0000000000000..add71370018ba --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorClientLanguageServerManagerFactory.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; + +[Shared] +[ExportCohostLspServiceFactory(typeof(IRazorClientLanguageServerManager))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal class RazorClientLanguageServerManagerFactory() : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + var notificationManager = lspServices.GetRequiredService(); + + return new RazorClientLanguageServerManager(notificationManager); + } +} diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManagerFactory.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorCohostClientLanguageServerManagerFactory.cs similarity index 91% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManagerFactory.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorCohostClientLanguageServerManagerFactory.cs index 2a06fa9b68cbd..67480de030be4 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManagerFactory.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorCohostClientLanguageServerManagerFactory.cs @@ -19,6 +19,6 @@ public ILspService CreateILspService(LspServices lspServices, WellKnownLspServer { var notificationManager = lspServices.GetRequiredService(); - return new RazorCohostClientLanguageServerManager(notificationManager); + return new RazorClientLanguageServerManager(notificationManager); } } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorCohostRequestContext.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorCohostRequestContext.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorCohostRequestContext.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorCohostRequestContext.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorDynamicRegistrationServiceFactory.cs similarity index 86% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorDynamicRegistrationServiceFactory.cs index 3b12143b2c23a..398ab41cf682b 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/RazorDynamicRegistrationServiceFactory.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorDynamicRegistrationServiceFactory.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.VisualStudio.Threading; using Roslyn.LanguageServer.Protocol; using Roslyn.Utilities; @@ -66,23 +67,25 @@ public Task OnInitializedAsync(ClientCapabilities clientCapabilities, RequestCon void InitializeRazor() { - this.InitializeRazor(clientCapabilities, context, _disposalTokenSource.Token); + this.InitializeRazorAsync(clientCapabilities, context, _disposalTokenSource.Token).ReportNonFatalErrorAsync(); } } - private void InitializeRazor(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) + private async Task InitializeRazorAsync(ClientCapabilities clientCapabilities, RequestContext context, CancellationToken cancellationToken) { // 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. if (cancellationToken.IsCancellationRequested) return; + await TaskScheduler.Default.SwitchTo(alwaysYield: true); + // 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); - var razorCohostClientLanguageServerManager = new RazorCohostClientLanguageServerManager(clientLanguageServerManager!); + var razorCohostClientLanguageServerManager = new RazorClientLanguageServerManager(clientLanguageServerManager!); var requestContext = new RazorCohostRequestContext(context); - dynamicRegistrationService!.Value.RegisterAsync(serializedClientCapabilities, requestContext, cancellationToken).ReportNonFatalErrorAsync(); + await dynamicRegistrationService!.Value.RegisterAsync(serializedClientCapabilities, requestContext, cancellationToken).ConfigureAwait(false); } } } diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorMethodAttribute.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorMethodAttribute.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorMethodAttribute.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorMethodAttribute.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorSemanticTokensRefreshQueueWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorSemanticTokensRefreshQueueWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorSemanticTokensRefreshQueueWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Cohost/RazorSemanticTokensRefreshQueueWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/IMefHostServicesExtensions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/IMefHostServicesExtensions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IMefHostServicesExtensions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/IMefHostServicesExtensions.cs diff --git a/src/Tools/ExternalAccess/Razor/IRazorAsynchronousOperationListenerProviderAccessor.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorAsynchronousOperationListenerProviderAccessor.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorAsynchronousOperationListenerProviderAccessor.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorAsynchronousOperationListenerProviderAccessor.cs diff --git a/src/Tools/ExternalAccess/Razor/IRazorCSharpInterceptionMiddleLayer.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorCSharpInterceptionMiddleLayer.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorCSharpInterceptionMiddleLayer.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorCSharpInterceptionMiddleLayer.cs diff --git a/src/Tools/ExternalAccess/Razor/IRazorLanguageServerTarget.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorLanguageServerTarget.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorLanguageServerTarget.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorLanguageServerTarget.cs diff --git a/src/Tools/ExternalAccess/Razor/IRazorSpanMappingService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorSpanMappingService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorSpanMappingService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/IRazorSpanMappingService.cs diff --git a/src/Tools/ExternalAccess/Razor/ITextBufferExtensions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/ITextBufferExtensions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/ITextBufferExtensions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/ITextBufferExtensions.cs diff --git a/src/Tools/ExternalAccess/Razor/InternalAPI.Shipped.txt b/src/Tools/ExternalAccess/Razor/EditorFeatures/InternalAPI.Shipped.txt similarity index 100% rename from src/Tools/ExternalAccess/Razor/InternalAPI.Shipped.txt rename to src/Tools/ExternalAccess/Razor/EditorFeatures/InternalAPI.Shipped.txt diff --git a/src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt b/src/Tools/ExternalAccess/Razor/EditorFeatures/InternalAPI.Unshipped.txt similarity index 100% rename from src/Tools/ExternalAccess/Razor/InternalAPI.Unshipped.txt rename to src/Tools/ExternalAccess/Razor/EditorFeatures/InternalAPI.Unshipped.txt diff --git a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj b/src/Tools/ExternalAccess/Razor/EditorFeatures/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj similarity index 75% rename from src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj index 92be22f2f3e09..68ae9daf38674 100644 --- a/src/Tools/ExternalAccess/Razor/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Microsoft.CodeAnalysis.ExternalAccess.Razor.csproj @@ -15,7 +15,7 @@ - + @@ -41,11 +41,11 @@ - - - - - + + + + + @@ -59,4 +59,6 @@ + + diff --git a/src/Tools/ExternalAccess/Razor/EditorFeatures/PublicAPI.Shipped.txt b/src/Tools/ExternalAccess/Razor/EditorFeatures/PublicAPI.Shipped.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Tools/ExternalAccess/Razor/EditorFeatures/PublicAPI.Unshipped.txt b/src/Tools/ExternalAccess/Razor/EditorFeatures/PublicAPI.Unshipped.txt new file mode 100644 index 0000000000000..815c92006af70 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +#nullable enable \ No newline at end of file diff --git a/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAnalyzerAssemblyResolver.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAnalyzerAssemblyResolver.cs new file mode 100644 index 0000000000000..021e443098c2e --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAnalyzerAssemblyResolver.cs @@ -0,0 +1,106 @@ +// 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. +#if NET + +using System; +using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.VisualStudio.Composition; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor +{ + [Export(typeof(IAnalyzerAssemblyResolver)), Shared] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class RazorAnalyzerAssemblyResolver() : IAnalyzerAssemblyResolver + { + public const string RazorCompilerAssemblyName = "Microsoft.CodeAnalysis.Razor.Compiler"; + public const string RazorUtilsAssemblyName = "Microsoft.AspNetCore.Razor.Utilities.Shared"; + public const string ObjectPoolAssemblyName = "Microsoft.Extensions.ObjectPool"; + + internal const string ServiceHubCoreFolderName = "ServiceHubCore"; + + internal static readonly ImmutableArray RazorAssemblyNames = [RazorCompilerAssemblyName, RazorUtilsAssemblyName, ObjectPoolAssemblyName]; + + public Assembly? Resolve(AnalyzerAssemblyLoader loader, AssemblyName assemblyName, AssemblyLoadContext directoryContext, string directory) => + ResolveCore(loader.CompilerLoadContext, assemblyName, directory); + + public static Assembly? ResolveRazorAssembly(AssemblyName assemblyName, string rootDirectory) => + ResolveCore( + AssemblyLoadContext.GetLoadContext(typeof(Microsoft.CodeAnalysis.Compilation).Assembly)!, + assemblyName, + rootDirectory); + + /// + /// This will resolve the razor generator assembly specified by in the specified + /// . + /// + internal static Assembly? ResolveCore(AssemblyLoadContext compilerLoadContext, AssemblyName assemblyName, string directory) + { + if (assemblyName.Name is not (RazorCompilerAssemblyName or RazorUtilsAssemblyName or ObjectPoolAssemblyName)) + { + return null; + } + + var assembly = compilerLoadContext.Assemblies.FirstOrDefault(a => a.GetName().Name == assemblyName.Name); + if (assembly is not null) + { + return assembly; + } + + var assemblyFileName = $"{assemblyName.Name}.dll"; + + // Depending on who wins the race to load these assemblies, the base directory will either be the tooling root (if Roslyn wins) + // or the ServiceHubCore subfolder (razor). In the root directory these are netstandard2.0 targeted, in ServiceHubCore they are + // .net targeted. We need to always pick the same set of assemblies regardless of who causes us to load. Because this code only + // runs in a .net based host, it's safe to always choose the .net targeted ServiceHubCore versions. + if (!Path.GetFileName(directory.AsSpan().TrimEnd(Path.DirectorySeparatorChar)).Equals(ServiceHubCoreFolderName, StringComparison.OrdinalIgnoreCase)) + { + directory = Path.Combine(directory, ServiceHubCoreFolderName); + } + + var assemblyPath = Path.Combine(directory, assemblyFileName); + if (File.Exists(assemblyPath)) + { + // https://github.com/dotnet/roslyn/issues/76868 + // + // There is a subtle race condition in this logic as another thread could load the assembly in between + // the above calls and this one. Short term will just catch and grab the loaded assembly but longer + // term need to think about creating a dedicated AssemblyLoadContext for the razor assemblies + // which avoids this race condition. + try + { + assembly = compilerLoadContext.LoadFromAssemblyPath(assemblyPath); + } + catch + { + assembly = compilerLoadContext.Assemblies.Single(a => a.GetName().Name == assemblyName.Name); + } + } + else + { + // There are assemblies in the razor sdk generator directory that do not exist in the VS installation. That + // means when the paths are redirected, it's possible that the assembly is not found. In that case, we should + // load the assembly from the VS installation by querying through the compiler context. + try + { + assembly = compilerLoadContext.LoadFromAssemblyName(assemblyName); + } + catch (FileNotFoundException) + { + assembly = null; + } + } + + return assembly; + } + } +} +#endif diff --git a/src/Tools/ExternalAccess/Razor/RazorAsynchronousOperationListenerProviderAccessor.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAsynchronousOperationListenerProviderAccessor.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorAsynchronousOperationListenerProviderAccessor.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAsynchronousOperationListenerProviderAccessor.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorAsynchronousOperationListenerWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAsynchronousOperationListenerWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorAsynchronousOperationListenerWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAsynchronousOperationListenerWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAutoFormattingOptions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorAutoFormattingOptions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorAutoFormattingOptions.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorBreakpointSpans.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorBreakpointSpans.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorBreakpointSpans.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorBreakpointSpans.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpFormattingInteractionService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorCSharpFormattingInteractionService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpFormattingInteractionService.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorCSharpInterceptionMiddleLayer.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpInterceptionMiddleLayer.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorCSharpInterceptionMiddleLayer.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpInterceptionMiddleLayer.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorCSharpProximityExpressionResolverService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpProximityExpressionResolverService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorCSharpProximityExpressionResolverService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorCSharpProximityExpressionResolverService.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorClassifierAccessor.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorClassifierAccessor.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorClassifierAccessor.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorClassifierAccessor.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorDynamicFileInfoProviderWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorDynamicFileInfoProviderWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorDynamicFileInfoProviderWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorDynamicFileInfoProviderWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorGlobalOptions.cs similarity index 97% rename from src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorGlobalOptions.cs index 39fc16ce1217f..f84d58518281b 100644 --- a/src/Tools/ExternalAccess/Razor/RazorGlobalOptions.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorGlobalOptions.cs @@ -4,14 +4,11 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Formatting; -using System.Linq; using System.Collections.Immutable; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { diff --git a/src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorIndentationOptions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorIndentationOptions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorIndentationOptions.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorLanguageServerTargetWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorLanguageServerTargetWrapper.cs similarity index 90% rename from src/Tools/ExternalAccess/Razor/RazorLanguageServerTargetWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorLanguageServerTargetWrapper.cs index 5f384df2ed102..0107a9ee3a3f3 100644 --- a/src/Tools/ExternalAccess/Razor/RazorLanguageServerTargetWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorLanguageServerTargetWrapper.cs @@ -2,9 +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.Threading.Tasks; using Microsoft.CommonLanguageServerProtocol.Framework; -using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor diff --git a/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorPredefinedCodeFixProviderNames.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorPredefinedCodeFixProviderNames.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorPredefinedCodeFixProviderNames.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorPredefinedCodeRefactoringProviderNames.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorPredefinedCodeRefactoringProviderNames.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorPredefinedCodeRefactoringProviderNames.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorPredefinedCodeRefactoringProviderNames.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorProjectExtensions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorProjectExtensions.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/RazorProjectExtensions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorProjectExtensions.cs index 25ec3e65f3f4d..6fb24a0a756e1 100644 --- a/src/Tools/ExternalAccess/Razor/RazorProjectExtensions.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorProjectExtensions.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorSemanticTokensAccessor.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorSemanticTokensAccessor.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/RazorSemanticTokensAccessor.cs diff --git a/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorUri.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorUri.cs new file mode 100644 index 0000000000000..98fc55b7e25c4 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/RazorUri.cs @@ -0,0 +1,32 @@ +// 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 Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal static class RazorUri +{ + public static Uri CreateAbsoluteUri(string absolutePath) + => ProtocolConversions.CreateAbsoluteUri(absolutePath); + + public static string GetDocumentFilePathFromUri(Uri uri) + => ProtocolConversions.GetDocumentFilePathFromUri(uri); + + public static bool IsGeneratedDocumentUri(Uri generatedDocumentUri) + => generatedDocumentUri.Scheme == SourceGeneratedDocumentUri.Scheme; + + public static string GetHintNameFromGeneratedDocumentUri(Solution solution, Uri generatedDocumentUri) + { + Contract.ThrowIfFalse(IsGeneratedDocumentUri(generatedDocumentUri)); + + if (SourceGeneratedDocumentUri.DeserializeIdentity(solution, generatedDocumentUri) is not { } identity) + { + throw new InvalidOperationException($"Could not deserialize Uri into a source generated Uri: {generatedDocumentUri}"); + } + + return identity.HintName; + } +} diff --git a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/JsonSerializableDocumentId.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/JsonSerializableDocumentId.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/JsonSerializableDocumentId.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs index 33c57d04c581b..a248fd113208a 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/JsonSerializableRazorPinnedSolutionInfoWrapper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Text.Json.Serialization; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorPinnedSolutionInfoWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorPinnedSolutionInfoWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorPinnedSolutionInfoWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteCallbackWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteCallbackWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteCallbackWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteCallbackWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteHostClient.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteHostClient.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteHostClient.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteHostClient.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackDispatcher.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackDispatcher.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackDispatcher.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackDispatcher.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackDispatcherRegistry.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackDispatcherRegistry.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackDispatcherRegistry.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackDispatcherRegistry.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackIdWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackIdWrapper.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackIdWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackIdWrapper.cs index 907a1e16677de..4ef451dbe7671 100644 --- a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceCallbackIdWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceCallbackIdWrapper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Remote; diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceConnectionWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceConnectionWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorRemoteServiceConnectionWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorRemoteServiceConnectionWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorServiceDescriptorsWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Remote/RazorServiceDescriptorsWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Remote/RazorServiceDescriptorsWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/SolutionExtensions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/SolutionExtensions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/SolutionExtensions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/SolutionExtensions.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/AbstractRazorLanguageServerFactoryWrapper.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/AbstractRazorLanguageServerFactoryWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Testing/AbstractRazorLanguageServerFactoryWrapper.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/AbstractRazorLanguageServerFactoryWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/IRazorTestCapabilitiesProvider.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/IRazorTestCapabilitiesProvider.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Testing/IRazorTestCapabilitiesProvider.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/IRazorTestCapabilitiesProvider.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/RazorTestAnalyzerLoader.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestAnalyzerLoader.cs similarity index 94% rename from src/Tools/ExternalAccess/Razor/Testing/RazorTestAnalyzerLoader.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestAnalyzerLoader.cs index 03b807c66d944..3bfd57f29a11e 100644 --- a/src/Tools/ExternalAccess/Razor/Testing/RazorTestAnalyzerLoader.cs +++ b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestAnalyzerLoader.cs @@ -27,6 +27,6 @@ public void InitializeDiagnosticsServices(Workspace workspace) public static IAnalyzerAssemblyLoader CreateAnalyzerAssemblyLoader() { - return new DefaultAnalyzerAssemblyLoader(); + return new AnalyzerAssemblyLoader(); } } diff --git a/src/Tools/ExternalAccess/Razor/Testing/RazorTestLanguageServerFactory.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestLanguageServerFactory.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Testing/RazorTestLanguageServerFactory.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestLanguageServerFactory.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/RazorTestWorkspaceRegistrationService.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestWorkspaceRegistrationService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Testing/RazorTestWorkspaceRegistrationService.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/RazorTestWorkspaceRegistrationService.cs diff --git a/src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/TestSolutionStore.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/Testing/TestSolutionStore.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/Testing/TestSolutionStore.cs diff --git a/src/Tools/ExternalAccess/Razor/TextDocumentExtensions.cs b/src/Tools/ExternalAccess/Razor/EditorFeatures/TextDocumentExtensions.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/TextDocumentExtensions.cs rename to src/Tools/ExternalAccess/Razor/EditorFeatures/TextDocumentExtensions.cs diff --git a/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspService.cs b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspService.cs new file mode 100644 index 0000000000000..45cb8623674ea --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspService.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 Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class AbstractRazorLspService : ILspService +{ +} diff --git a/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspServiceFactory.cs b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspServiceFactory.cs new file mode 100644 index 0000000000000..2dbc3f73b5140 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorLspServiceFactory.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.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class AbstractRazorLspServiceFactory : ILspServiceFactory +{ + public ILspService CreateILspService(LspServices lspServices, WellKnownLspServerKinds serverKind) + { + return CreateService(new RazorLspServices(lspServices)); + } + + protected abstract AbstractRazorLspService CreateService(IRazorLspServices lspServices); + + private sealed class RazorLspServices(LspServices lspServices) : IRazorLspServices + { + public void Dispose() + { + } + + public T GetRequiredService() where T : notnull => lspServices.GetRequiredService(); + public IEnumerable GetRequiredServices() => lspServices.GetRequiredServices(); + public T? GetService() where T : notnull => lspServices.GetService(); + public bool TryGetService(Type type, [NotNullWhen(true)] out object? service) => lspServices.TryGetService(type, out service); + } +} diff --git a/src/Tools/ExternalAccess/Razor/Features/AbstractRazorNotificationHandler.cs b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorNotificationHandler.cs new file mode 100644 index 0000000000000..60d8ca67dc4a6 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorNotificationHandler.cs @@ -0,0 +1,21 @@ +// 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.LanguageServer.Handler; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class AbstractRazorNotificationHandler : ILspServiceNotificationHandler +{ + public abstract bool MutatesSolutionState { get; } + public abstract bool RequiresLSPSolution { get; } + + public Task HandleNotificationAsync(TRequestType request, RequestContext requestContext, CancellationToken cancellationToken) + { + var razorRequestContext = new RazorRequestContext(requestContext); + return HandleNotificationAsync(request, razorRequestContext, cancellationToken); + } + + protected abstract Task HandleNotificationAsync(TRequestType request, RazorRequestContext razorRequestContext, CancellationToken cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/AbstractRazorRequestHandler.cs b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorRequestHandler.cs new file mode 100644 index 0000000000000..d465bd4ad7fed --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/AbstractRazorRequestHandler.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 Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class AbstractRazorRequestHandler : ILspServiceRequestHandler +{ + bool IMethodHandler.MutatesSolutionState => MutatesSolutionState; + + bool ISolutionRequiredHandler.RequiresLSPSolution => RequiresLSPSolution; + + Task IRequestHandler.HandleRequestAsync(TRequestType request, RequestContext context, CancellationToken cancellationToken) + { + var razorRequestContext = new RazorRequestContext(context); + return HandleRequestAsync(request, razorRequestContext, cancellationToken); + } + + protected abstract bool MutatesSolutionState { get; } + + protected abstract bool RequiresLSPSolution { get; } + + protected abstract Task HandleRequestAsync(TRequestType request, RazorRequestContext context, CancellationToken cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/ExportRazorLspServiceFactoryAttribute.cs b/src/Tools/ExternalAccess/Razor/Features/ExportRazorLspServiceFactoryAttribute.cs new file mode 100644 index 0000000000000..9ab9d67ca0292 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/ExportRazorLspServiceFactoryAttribute.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.Composition; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +[AttributeUsage(AttributeTargets.Class), MetadataAttribute] +internal class ExportRazorLspServiceFactoryAttribute(Type handlerType) : ExportLspServiceFactoryAttribute(handlerType, ProtocolConstants.RoslynLspLanguagesContract, WellKnownLspServerKinds.Any); diff --git a/src/Tools/ExternalAccess/Razor/Features/ExportRazorStatelessLspServiceAttribute.cs b/src/Tools/ExternalAccess/Razor/Features/ExportRazorStatelessLspServiceAttribute.cs new file mode 100644 index 0000000000000..6761c3cc296bc --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/ExportRazorStatelessLspServiceAttribute.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.Composition; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +[AttributeUsage(AttributeTargets.Class), MetadataAttribute] +internal sealed class ExportRazorStatelessLspServiceAttribute(Type handlerType) : ExportStatelessLspServiceAttribute(handlerType, ProtocolConstants.RoslynLspLanguagesContract, WellKnownLspServerKinds.Any); diff --git a/src/Tools/ExternalAccess/Razor/Features/IRazorLspServices.cs b/src/Tools/ExternalAccess/Razor/Features/IRazorLspServices.cs new file mode 100644 index 0000000000000..0e6cd8ca02b14 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/IRazorLspServices.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal interface IRazorLspServices +{ + T? GetService() where T : notnull; + T GetRequiredService() where T : notnull; + + bool TryGetService(Type type, [NotNullWhen(true)] out object? service); + + IEnumerable GetRequiredServices(); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/IRazorRequestWrapper.cs b/src/Tools/ExternalAccess/Razor/Features/IRazorRequestWrapper.cs new file mode 100644 index 0000000000000..8b5623939ae7c --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/IRazorRequestWrapper.cs @@ -0,0 +1,12 @@ +// 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.ExternalAccess.Razor.Features; + +internal interface IRazorRequestWrapper +{ + Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/Microsoft.CodeAnalysis.ExternalAccess.Razor.Features.csproj b/src/Tools/ExternalAccess/Razor/Features/Microsoft.CodeAnalysis.ExternalAccess.Razor.Features.csproj new file mode 100644 index 0000000000000..c148f63ee2968 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/Microsoft.CodeAnalysis.ExternalAccess.Razor.Features.csproj @@ -0,0 +1,58 @@ + + + + + Microsoft.CodeAnalysis.ExternalAccess.Razor.Features + $(NetVSCode) + enable + enable + + + true + Microsoft.CodeAnalysis.ExternalAccess.Razor.Features + + A supporting package for Razor: + https://github.com/dotnet/razor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tools/ExternalAccess/Razor/Features/RazorEndpointAttribute.cs b/src/Tools/ExternalAccess/Razor/Features/RazorEndpointAttribute.cs new file mode 100644 index 0000000000000..3356a326929bd --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/RazorEndpointAttribute.cs @@ -0,0 +1,20 @@ +// 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.Composition; +using Microsoft.CommonLanguageServerProtocol.Framework; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +[MetadataAttribute] +internal class RazorEndpointAttribute : LanguageServerEndpointAttribute +{ + public RazorEndpointAttribute(string method) : base(method, LanguageServerConstants.DefaultLanguageName) + { + } + + public RazorEndpointAttribute(string method, string language) : base(method, language) + { + } +} diff --git a/src/Tools/ExternalAccess/Razor/Features/RazorLspDynamicFileInfoProvider.cs b/src/Tools/ExternalAccess/Razor/Features/RazorLspDynamicFileInfoProvider.cs new file mode 100644 index 0000000000000..ff860ee5e3fe2 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/RazorLspDynamicFileInfoProvider.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class RazorLspDynamicFileInfoProvider : AbstractRazorLspService +{ + public abstract Task GetDynamicFileInfoAsync(Workspace workspace, ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); + public abstract Task RemoveDynamicFileInfoAsync(Workspace workspace, ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); + + public event EventHandler? Updated; + + public void Update(Uri razorUri) + { + Updated?.Invoke(this, razorUri); + } +} diff --git a/src/Tools/ExternalAccess/Razor/Features/RazorRequestContext.cs b/src/Tools/ExternalAccess/Razor/Features/RazorRequestContext.cs new file mode 100644 index 0000000000000..0e960e8f7ec5b --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/RazorRequestContext.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.LanguageServer; +using Microsoft.CodeAnalysis.LanguageServer.Handler; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal readonly struct RazorRequestContext(RequestContext context) +{ + internal string Method => context.Method; + internal Uri? Uri => context.TextDocument?.GetURI(); + /// + internal Workspace? Workspace => context.Workspace; + /// + internal Solution? Solution => context.Solution; + /// + internal TextDocument? TextDocument => context.TextDocument; + + internal T GetRequiredService() where T : class => context.GetRequiredService(); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/RazorWorkspaceService.cs b/src/Tools/ExternalAccess/Razor/Features/RazorWorkspaceService.cs new file mode 100644 index 0000000000000..9257ab33b0757 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/RazorWorkspaceService.cs @@ -0,0 +1,13 @@ +// 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.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal abstract class RazorWorkspaceService : AbstractRazorLspService +{ + public abstract void Initialize(Workspace workspace, string pipeName); + public abstract void NotifyDynamicFile(ProjectId projectId); +} diff --git a/src/Tools/ExternalAccess/Razor/Features/WorkspaceExtensions.cs b/src/Tools/ExternalAccess/Razor/Features/WorkspaceExtensions.cs new file mode 100644 index 0000000000000..aa709b664be1a --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Features/WorkspaceExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Roslyn.LanguageServer.Protocol; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Features; + +internal static class WorkspaceExtensions +{ + public static ValueTask GetTextDocumentAsync(this Workspace workspace, Uri uri, CancellationToken cancellationToken) + { + var identifier = new TextDocumentIdentifier() { Uri = uri }; + return workspace.CurrentSolution.GetTextDocumentAsync(identifier, cancellationToken); + } +} diff --git a/src/Tools/ExternalAccess/Razor/IRazorDocumentOperationService.cs b/src/Tools/ExternalAccess/Razor/IRazorDocumentOperationService.cs deleted file mode 100644 index 616a8b14973a7..0000000000000 --- a/src/Tools/ExternalAccess/Razor/IRazorDocumentOperationService.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. - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal interface IRazorDocumentOperationService - { - /// - /// document version of - /// - bool CanApplyChange { get; } - - /// - /// indicates whether this document supports diagnostics or not - /// - bool SupportDiagnostics { get; } - } -} diff --git a/src/Tools/ExternalAccess/Razor/IRazorDocumentServiceProvider.cs b/src/Tools/ExternalAccess/Razor/IRazorDocumentServiceProvider.cs deleted file mode 100644 index 101b26d992bd2..0000000000000 --- a/src/Tools/ExternalAccess/Razor/IRazorDocumentServiceProvider.cs +++ /dev/null @@ -1,15 +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. - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal interface IRazorDocumentServiceProvider : IRazorDocumentOperationService - { - /// - /// Gets a document specific service provided by the host identified by the service type. - /// If the host does not provide the service, this method returns null. - /// - TService? GetService() where TService : class; - } -} diff --git a/src/Tools/ExternalAccess/Razor/IRazorDynamicFileInfoProvider.cs b/src/Tools/ExternalAccess/Razor/IRazorDynamicFileInfoProvider.cs deleted file mode 100644 index 277b9da9b906d..0000000000000 --- a/src/Tools/ExternalAccess/Razor/IRazorDynamicFileInfoProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal interface IRazorDynamicFileInfoProvider - { - /// - /// return for the context given - /// - /// this file belongs to - /// full path to project file (ex, csproj) - /// full path to non source file (ex, cshtml) - Task GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); - - /// - /// let provider know certain file has been removed - /// - /// this file belongs to - /// full path to project file (ex, csproj) - /// full path to non source file (ex, cshtml) - Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); - - /// - /// indicate content of a file has updated. the event argument "string" should be same as "filepath" given to - /// - event EventHandler Updated; - } -} diff --git a/src/Tools/ExternalAccess/Razor/IRazorMappingService.cs b/src/Tools/ExternalAccess/Razor/IRazorMappingService.cs deleted file mode 100644 index 8cda2f0dc1a8c..0000000000000 --- a/src/Tools/ExternalAccess/Razor/IRazorMappingService.cs +++ /dev/null @@ -1,18 +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 System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal interface IRazorMappingService - { - Task> MapSpansAsync(Document document, IEnumerable spans, CancellationToken cancellationToken); - Task> MapTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); - } -} diff --git a/src/Tools/ExternalAccess/Razor/PublicAPI.Unshipped.txt b/src/Tools/ExternalAccess/Razor/PublicAPI.Unshipped.txt deleted file mode 100644 index 8b137891791fe..0000000000000 --- a/src/Tools/ExternalAccess/Razor/PublicAPI.Unshipped.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs b/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.cs deleted file mode 100644 index f9ccd053fbc40..0000000000000 --- a/src/Tools/ExternalAccess/Razor/RazorAnalyzerAssemblyResolver.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. -#if NET - -using System; -using System.Composition; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.Loader; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.VisualStudio.Composition; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - [Export(typeof(IAnalyzerAssemblyResolver)), Shared] - [method: ImportingConstructor] - [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - internal sealed class RazorAnalyzerAssemblyResolver() : IAnalyzerAssemblyResolver - { - private const string RazorCompilerAssemblyName = "Microsoft.CodeAnalysis.Razor.Compiler"; - private const string RazorUtilsAssemblyName = "Microsoft.AspNetCore.Razor.Utilities.Shared"; - private const string ObjectPoolAssemblyName = "Microsoft.Extensions.ObjectPool"; - - private static readonly object s_loaderLock = new(); - - public static Assembly? ResolveRazorAssembly(AssemblyName assemblyName, string rootDirectory) - { - if (assemblyName.Name is RazorCompilerAssemblyName or RazorUtilsAssemblyName or ObjectPoolAssemblyName) - { - lock (s_loaderLock) - { - var compilerContext = AssemblyLoadContext.GetLoadContext(typeof(Compilation).Assembly)!; - if (compilerContext.Assemblies.SingleOrDefault(a => a.GetName().Name == assemblyName.Name) is Assembly loadedAssembly) - { - return loadedAssembly; - } - - var assembly = Path.Combine(rootDirectory, $"{assemblyName.Name}.dll"); - return compilerContext.LoadFromAssemblyPath(assembly); - } - } - return null; - } - - public Assembly? ResolveAssembly(AssemblyName assemblyName, string directoryName) => ResolveRazorAssembly(assemblyName, directoryName); - } -} -#endif diff --git a/src/Tools/ExternalAccess/Razor/RazorDynamicFileInfo.cs b/src/Tools/ExternalAccess/Razor/RazorDynamicFileInfo.cs deleted file mode 100644 index 73d4a49cdae64..0000000000000 --- a/src/Tools/ExternalAccess/Razor/RazorDynamicFileInfo.cs +++ /dev/null @@ -1,55 +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.Host; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - /// - /// provides info on the given file - /// - /// this will be used to provide dynamic content such as generated content from cshtml to workspace - /// we acquire this from exposed from external components such as razor for cshtml - /// - internal sealed class RazorDynamicFileInfo - { - public RazorDynamicFileInfo(string filePath, SourceCodeKind sourceCodeKind, TextLoader textLoader, IRazorDocumentServiceProvider documentServiceProvider) - { - FilePath = filePath; - SourceCodeKind = sourceCodeKind; - TextLoader = textLoader; - DocumentServiceProvider = documentServiceProvider; - } - - /// - /// for now, return null. in future, we will use this to get right options from editorconfig - /// - public string FilePath { get; } - - /// - /// return for this file - /// - public SourceCodeKind SourceCodeKind { get; } - - /// - /// return to load content for the dynamic file - /// - public TextLoader TextLoader { get; } - - /// - /// return for the contents it provides - /// - public IRazorDocumentServiceProvider DocumentServiceProvider { get; } - - /// - /// Constructs a new from an existing but with updated - /// text loader and document service provider coming from this instance. - /// - public DocumentInfo ToUpdatedDocumentInfo(DocumentInfo existingDocumentInfo) - { - var serviceProvider = new RazorDocumentServiceProviderWrapper(this.DocumentServiceProvider); - return new DocumentInfo(existingDocumentInfo.Attributes, this.TextLoader, serviceProvider); - } - } -} diff --git a/src/Tools/ExternalAccess/Razor/RazorMappedSpanResult.cs b/src/Tools/ExternalAccess/Razor/RazorMappedSpanResult.cs deleted file mode 100644 index cdd07bdf2a5fa..0000000000000 --- a/src/Tools/ExternalAccess/Razor/RazorMappedSpanResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal readonly struct RazorMappedSpanResult - { - public readonly string FilePath; - - public readonly LinePositionSpan LinePositionSpan; - - public readonly TextSpan Span; - - public RazorMappedSpanResult(string filePath, LinePositionSpan linePositionSpan, TextSpan span) - { - if (string.IsNullOrEmpty(filePath)) - { - throw new ArgumentException(nameof(filePath)); - } - - FilePath = filePath; - LinePositionSpan = linePositionSpan; - Span = span; - } - - public bool IsDefault => FilePath == null; - } - - internal readonly record struct RazorMappedEditoResult(string FilePath, TextChange[] TextChanges) - { - public bool IsDefault => FilePath == null || TextChanges == null; - } -} diff --git a/src/Tools/ExternalAccess/Razor/RazorMappingServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorMappingServiceWrapper.cs deleted file mode 100644 index 16625dbf0c1b6..0000000000000 --- a/src/Tools/ExternalAccess/Razor/RazorMappingServiceWrapper.cs +++ /dev/null @@ -1,75 +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.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal sealed class RazorMappingServiceWrapper(IRazorMappingService razorMappingService) : ISpanMappingService - { - private readonly IRazorMappingService _razorMappingService = razorMappingService; - - public bool SupportsMappingImportDirectives => true; - - public async Task> GetMappedTextChangesAsync( - Document oldDocument, - Document newDocument, - CancellationToken cancellationToken) - { - var mappedEdits = await _razorMappingService.MapTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - - var changesCount = mappedEdits.Select(e => e.IsDefault ? 0 : e.TextChanges.Length).Sum(); - var changes = new (string mappedFilePath, TextChange mappedTextChange)[changesCount]; - - var i = 0; - foreach (var mappedEdit in mappedEdits) - { - if (mappedEdit.IsDefault) - { - continue; - } - - foreach (var textChange in mappedEdit.TextChanges) - { - changes[i++] = (mappedEdit.FilePath, textChange); - } - } - - Debug.Assert(i == changesCount); - return changes.ToImmutableArray(); - } - - public async Task> MapSpansAsync( - Document document, - IEnumerable spans, - CancellationToken cancellationToken) - { - var razorSpans = await _razorMappingService.MapSpansAsync(document, spans, cancellationToken).ConfigureAwait(false); - var roslynSpans = new MappedSpanResult[razorSpans.Length]; - for (var i = 0; i < razorSpans.Length; i++) - { - var razorSpan = razorSpans[i]; - if (razorSpan.IsDefault) - { - // Unmapped location - roslynSpans[i] = default; - } - else - { - roslynSpans[i] = new MappedSpanResult(razorSpan.FilePath, razorSpan.LinePositionSpan, razorSpan.Span); - } - } - - return roslynSpans.ToImmutableArray(); - } - } -} diff --git a/src/Tools/ExternalAccess/Razor/RazorSpanMappingServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorSpanMappingServiceWrapper.cs deleted file mode 100644 index d186da23ac528..0000000000000 --- a/src/Tools/ExternalAccess/Razor/RazorSpanMappingServiceWrapper.cs +++ /dev/null @@ -1,71 +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.Host; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor -{ - internal sealed class RazorSpanMappingServiceWrapper : AbstractSpanMappingService - { - private readonly IRazorSpanMappingService _razorSpanMappingService; - - public RazorSpanMappingServiceWrapper(IRazorSpanMappingService razorSpanMappingService) - { - _razorSpanMappingService = razorSpanMappingService ?? throw new ArgumentNullException(nameof(razorSpanMappingService)); - } - - /// - /// Modern razor span mapping service can handle if we add imports. Razor will then rewrite that - /// to their own form. - /// - public override bool SupportsMappingImportDirectives => true; - - public override async Task> GetMappedTextChangesAsync( - Document oldDocument, - Document newDocument, - CancellationToken cancellationToken) - { - var diffService = newDocument.Project.Solution.Services.GetRequiredService(); - - // This is a hack that finds a minimal diff. It's not the ideal algorithm but should cover most scenarios. In the future, - // we should improve this algorithm - see https://github.com/dotnet/roslyn/issues/53346 for additional details. - var textChanges = await diffService.GetTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); - var mappedSpanResults = await MapSpansAsync(oldDocument, textChanges.Select(tc => tc.Span), cancellationToken).ConfigureAwait(false); - - var mappedTextChanges = MatchMappedSpansToTextChanges(textChanges, mappedSpanResults); - return mappedTextChanges; - } - - public override async Task> MapSpansAsync( - Document document, - IEnumerable spans, - CancellationToken cancellationToken) - { - var razorSpans = await _razorSpanMappingService.MapSpansAsync(document, spans, cancellationToken).ConfigureAwait(false); - var roslynSpans = new MappedSpanResult[razorSpans.Length]; - for (var i = 0; i < razorSpans.Length; i++) - { - var razorSpan = razorSpans[i]; - if (razorSpan.IsDefault) - { - // Unmapped location - roslynSpans[i] = default; - } - else - { - roslynSpans[i] = new MappedSpanResult(razorSpan.FilePath, razorSpan.LinePositionSpan, razorSpan.Span); - } - } - - return roslynSpans.ToImmutableArray(); - } - } -} diff --git a/src/Tools/ExternalAccess/Razor/RazorUri.cs b/src/Tools/ExternalAccess/Razor/Shared/Constants.cs similarity index 51% rename from src/Tools/ExternalAccess/Razor/RazorUri.cs rename to src/Tools/ExternalAccess/Razor/Shared/Constants.cs index dd0e03df5dab1..55ab3341d5c49 100644 --- a/src/Tools/ExternalAccess/Razor/RazorUri.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/Constants.cs @@ -2,16 +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 Microsoft.CodeAnalysis.LanguageServer; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; -internal static class RazorUri +internal static class Constants { - public static Uri CreateAbsoluteUri(string absolutePath) - => ProtocolConversions.CreateAbsoluteUri(absolutePath); - - public static string GetDocumentFilePathFromUri(Uri uri) - => ProtocolConversions.GetDocumentFilePathFromUri(uri); + public const string RazorLanguageName = LanguageInfoProvider.RazorLanguageName; } diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorClientLanguageServerManager.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorClientLanguageServerManager.cs new file mode 100644 index 0000000000000..1885c5e887230 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorClientLanguageServerManager.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal interface IRazorClientLanguageServerManager : ILspService +{ + ValueTask SendNotificationAsync(string methodName, CancellationToken cancellationToken); + ValueTask SendNotificationAsync(string methodName, TParams @params, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, CancellationToken cancellationToken); + Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); + ValueTask SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken); +} + +// TODO: Remove this interface once razor inserts and moves off of it. +internal interface IRazorCohostClientLanguageServerManager : IRazorClientLanguageServerManager +{ } diff --git a/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentExcerptService.cs similarity index 97% rename from src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs rename to src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentExcerptService.cs index 8f108e50f09d8..14c006e35d276 100644 --- a/src/Tools/ExternalAccess/Razor/IRazorDocumentExcerptService.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentExcerptService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentOperationService.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentOperationService.cs new file mode 100644 index 0000000000000..5a5b2d2c078ed --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentOperationService.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal interface IRazorDocumentOperationService +{ + /// + /// document version of + /// + bool CanApplyChange { get; } + + /// + /// indicates whether this document supports diagnostics or not + /// + bool SupportDiagnostics { get; } +} diff --git a/src/Tools/ExternalAccess/Razor/IRazorDocumentPropertiesService.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentPropertiesService.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/IRazorDocumentPropertiesService.cs rename to src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentPropertiesService.cs diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentServiceProvider.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentServiceProvider.cs new file mode 100644 index 0000000000000..a693aa5d5e624 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorDocumentServiceProvider.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal interface IRazorDocumentServiceProvider : IRazorDocumentOperationService +{ + /// + /// Gets a document specific service provided by the host identified by the service type. + /// If the host does not provide the service, this method returns null. + /// + TService? GetService() where TService : class; +} diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorDynamicFileInfoProvider.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorDynamicFileInfoProvider.cs new file mode 100644 index 0000000000000..e935bd0e48db8 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorDynamicFileInfoProvider.cs @@ -0,0 +1,29 @@ +// 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 Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.LanguageServer; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal interface IRazorDynamicFileInfoProvider +{ + /// + /// indicate content of a file has updated. the event argument "string" should be same as "filepath" given to + /// + event EventHandler? Updated; + + /// + /// return for the context given + /// + Task GetDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); + + /// + /// let provider know certain file has been removed + /// + Task RemoveDynamicFileInfoAsync(ProjectId projectId, string? projectFilePath, string filePath, CancellationToken cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorMappingService.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorMappingService.cs new file mode 100644 index 0000000000000..c36a3de25bb46 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorMappingService.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal interface IRazorMappingService +{ + Task> MapSpansAsync(Document document, IEnumerable spans, CancellationToken cancellationToken); + Task> MapTextChangesAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken); +} diff --git a/src/Tools/ExternalAccess/Razor/Shared/IRazorServiceProvider.cs b/src/Tools/ExternalAccess/Razor/Shared/IRazorServiceProvider.cs new file mode 100644 index 0000000000000..8810125f7e6bf --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/IRazorServiceProvider.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared; + +internal interface IRazorServiceProvider +{ + /// + /// Gets a document specific service provided by the host identified by the service type. + /// If the host does not provide the service, this method returns null. + /// + TService? GetService() where TService : class; +} diff --git a/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems b/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems new file mode 100644 index 0000000000000..2c3369d760c2a --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.projitems @@ -0,0 +1,31 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + 4853a78a-4ec4-4d86-9f02-d0ddeae85520 + + + Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.shproj b/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.shproj new file mode 100644 index 0000000000000..150d5f8529ce1 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/Microsoft.CodeAnalysis.ExternalAccess.Razor.Shared.shproj @@ -0,0 +1,13 @@ + + + + 4853a78a-4ec4-4d86-9f02-d0ddeae85520 + 14.0 + + + + + + + + diff --git a/src/Tools/ExternalAccess/Razor/RazorClassificationOptionsWrapper.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorClassificationOptionsWrapper.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorClassificationOptionsWrapper.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorClassificationOptionsWrapper.cs diff --git a/src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManager.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorClientLanguageServerManager.cs similarity index 86% rename from src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManager.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorClientLanguageServerManager.cs index bde9f3741451f..aa9caa4c8b8ad 100644 --- a/src/Tools/ExternalAccess/Razor/Cohost/RazorCohostClientLanguageServerManager.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorClientLanguageServerManager.cs @@ -6,9 +6,9 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageServer; -namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.Cohost; +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; -internal class RazorCohostClientLanguageServerManager(IClientLanguageServerManager clientLanguageServerManager) : IRazorCohostClientLanguageServerManager +internal class RazorClientLanguageServerManager(IClientLanguageServerManager clientLanguageServerManager) : IRazorCohostClientLanguageServerManager { public Task SendRequestAsync(string methodName, TParams @params, CancellationToken cancellationToken) => clientLanguageServerManager.SendRequestAsync(methodName, @params, cancellationToken); diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentExcerptServiceWrapper.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorDocumentExcerptServiceWrapper.cs index 56fa64ecf97eb..44f8d1e76e2df 100644 --- a/src/Tools/ExternalAccess/Razor/RazorDocumentExcerptServiceWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentExcerptServiceWrapper.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor { diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentPropertiesServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentPropertiesServiceWrapper.cs similarity index 98% rename from src/Tools/ExternalAccess/Razor/RazorDocumentPropertiesServiceWrapper.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorDocumentPropertiesServiceWrapper.cs index 64fba48936485..5483060f8def5 100644 --- a/src/Tools/ExternalAccess/Razor/RazorDocumentPropertiesServiceWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentPropertiesServiceWrapper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.ExternalAccess.Razor diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentServiceProviderWrapper.cs similarity index 86% rename from src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorDocumentServiceProviderWrapper.cs index 1611270558b17..6f292bc9e852b 100644 --- a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorDocumentServiceProviderWrapper.cs @@ -36,17 +36,13 @@ public RazorDocumentServiceProviderWrapper(IRazorDocumentServiceProvider innerDo ref _lazySpanMappingService, static documentServiceProvider => { - // Razor is transitioning implementations from IRazorSpanMappingService to IRazorMappingService. - // While this is happening the service may not be available. If it is, use the newer implementation, - // otherwise fallback to IRazorSpanMappingService var razorMappingService = documentServiceProvider.GetService(); if (razorMappingService is not null) { return new RazorMappingServiceWrapper(razorMappingService); } - var razorSpanMappingService = documentServiceProvider.GetService(); - return razorSpanMappingService != null ? new RazorSpanMappingServiceWrapper(razorSpanMappingService) : null; + return null; }, _innerDocumentServiceProvider); diff --git a/src/Tools/ExternalAccess/Razor/Shared/RazorDynamicFileInfo.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorDynamicFileInfo.cs new file mode 100644 index 0000000000000..e01767ffaa333 --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorDynamicFileInfo.cs @@ -0,0 +1,54 @@ +// 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.Host; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +/// +/// provides info on the given file +/// +/// this will be used to provide dynamic content such as generated content from cshtml to workspace +/// we acquire this from exposed from external components such as razor for cshtml +/// +internal sealed class RazorDynamicFileInfo +{ + public RazorDynamicFileInfo(string filePath, SourceCodeKind sourceCodeKind, TextLoader textLoader, IRazorDocumentServiceProvider documentServiceProvider) + { + FilePath = filePath; + SourceCodeKind = sourceCodeKind; + TextLoader = textLoader; + DocumentServiceProvider = documentServiceProvider; + } + + /// + /// for now, return null. in future, we will use this to get right options from editorconfig + /// + public string FilePath { get; } + + /// + /// return for this file + /// + public SourceCodeKind SourceCodeKind { get; } + + /// + /// return to load content for the dynamic file + /// + public TextLoader TextLoader { get; } + + /// + /// return for the contents it provides + /// + public IRazorDocumentServiceProvider DocumentServiceProvider { get; } + + /// + /// Constructs a new from an existing but with updated + /// text loader and document service provider coming from this instance. + /// + public DocumentInfo ToUpdatedDocumentInfo(DocumentInfo existingDocumentInfo) + { + var serviceProvider = new RazorDocumentServiceProviderWrapper(this.DocumentServiceProvider); + return new DocumentInfo(existingDocumentInfo.Attributes, this.TextLoader, serviceProvider); + } +} diff --git a/src/Tools/ExternalAccess/Razor/RazorExcerptMode.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorExcerptMode.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorExcerptMode.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorExcerptMode.cs diff --git a/src/Tools/ExternalAccess/Razor/RazorExcerptResult.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorExcerptResult.cs similarity index 100% rename from src/Tools/ExternalAccess/Razor/RazorExcerptResult.cs rename to src/Tools/ExternalAccess/Razor/Shared/RazorExcerptResult.cs diff --git a/src/Tools/ExternalAccess/Razor/Shared/RazorMappedSpanResult.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorMappedSpanResult.cs new file mode 100644 index 0000000000000..96adfdc8fb60b --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorMappedSpanResult.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal readonly struct RazorMappedSpanResult +{ + public readonly string FilePath; + + public readonly LinePositionSpan LinePositionSpan; + + public readonly TextSpan Span; + + public RazorMappedSpanResult(string filePath, LinePositionSpan linePositionSpan, TextSpan span) + { + if (string.IsNullOrEmpty(filePath)) + { + throw new ArgumentException(nameof(filePath)); + } + + FilePath = filePath; + LinePositionSpan = linePositionSpan; + Span = span; + } + + public bool IsDefault => FilePath == null; +} + +internal readonly record struct RazorMappedEditResult(string FilePath, TextChange[] TextChanges) +{ + public bool IsDefault => FilePath == null || TextChanges == null; +} diff --git a/src/Tools/ExternalAccess/Razor/Shared/RazorMappingServiceWrapper.cs b/src/Tools/ExternalAccess/Razor/Shared/RazorMappingServiceWrapper.cs new file mode 100644 index 0000000000000..e7c2b341008aa --- /dev/null +++ b/src/Tools/ExternalAccess/Razor/Shared/RazorMappingServiceWrapper.cs @@ -0,0 +1,73 @@ +// 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 System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Text; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor; + +internal sealed class RazorMappingServiceWrapper(IRazorMappingService razorMappingService) : ISpanMappingService +{ + private readonly IRazorMappingService _razorMappingService = razorMappingService; + + public bool SupportsMappingImportDirectives => true; + + public async Task> GetMappedTextChangesAsync( + Document oldDocument, + Document newDocument, + CancellationToken cancellationToken) + { + var mappedEdits = await _razorMappingService.MapTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false); + + var changesCount = mappedEdits.Select(e => e.IsDefault ? 0 : e.TextChanges.Length).Sum(); + var changes = new (string mappedFilePath, TextChange mappedTextChange)[changesCount]; + + var i = 0; + foreach (var mappedEdit in mappedEdits) + { + if (mappedEdit.IsDefault) + { + continue; + } + + foreach (var textChange in mappedEdit.TextChanges) + { + changes[i++] = (mappedEdit.FilePath, textChange); + } + } + + Debug.Assert(i == changesCount); + return changes.ToImmutableArray(); + } + + public async Task> MapSpansAsync( + Document document, + IEnumerable spans, + CancellationToken cancellationToken) + { + var razorSpans = await _razorMappingService.MapSpansAsync(document, spans, cancellationToken).ConfigureAwait(false); + var roslynSpans = new MappedSpanResult[razorSpans.Length]; + for (var i = 0; i < razorSpans.Length; i++) + { + var razorSpan = razorSpans[i]; + if (razorSpan.IsDefault) + { + // Unmapped location + roslynSpans[i] = default; + } + else + { + roslynSpans[i] = new MappedSpanResult(razorSpan.FilePath, razorSpan.LinePositionSpan, razorSpan.Span); + } + } + + return roslynSpans.ToImmutableArray(); + } +} diff --git a/src/Tools/ExternalAccess/RazorTest/Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests.csproj b/src/Tools/ExternalAccess/RazorTest/Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests.csproj index 3f7a6cdf19ec9..be2b10c7fc1ab 100644 --- a/src/Tools/ExternalAccess/RazorTest/Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests.csproj +++ b/src/Tools/ExternalAccess/RazorTest/Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests.csproj @@ -4,15 +4,17 @@ Library Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests - net472 + $(NetVS);net472 - + + - + - + + \ No newline at end of file diff --git a/src/Tools/ExternalAccess/RazorTest/RazorAnalyzerAssemblyResolverTests.cs b/src/Tools/ExternalAccess/RazorTest/RazorAnalyzerAssemblyResolverTests.cs new file mode 100644 index 0000000000000..d8d39eb32d108 --- /dev/null +++ b/src/Tools/ExternalAccess/RazorTest/RazorAnalyzerAssemblyResolverTests.cs @@ -0,0 +1,189 @@ +// 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. +#if NET + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Loader; +using System.Text; +using System.Threading.Tasks; +using Basic.Reference.Assemblies; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Razor.UnitTests; + +public sealed class RazorAnalyzerAssemblyResolverTests : IDisposable +{ + private TempRoot TempRoot { get; } = new TempRoot(); + private int InitialAssemblyCount { get; } + + public RazorAnalyzerAssemblyResolverTests() + { + InitialAssemblyCount = AssemblyLoadContext.GetLoadContext(this.GetType().Assembly)!.Assemblies.Count(); + } + + public void Dispose() + { + TempRoot.Dispose(); + + // This test should not change the set of assemblies loaded in the current context. + var count = AssemblyLoadContext.GetLoadContext(this.GetType().Assembly)!.Assemblies.Count(); + Assert.Equal(InitialAssemblyCount, count); + } + + private void CreateRazorAssemblies(string directory, string versionNumber = "1.0.0.0") + { + _ = Directory.CreateDirectory(directory); + foreach (var simpleName in RazorAnalyzerAssemblyResolver.RazorAssemblyNames) + { + BuildOne(simpleName); + } + + void BuildOne(string simpleName) + { + var i = simpleName.LastIndexOf('.'); + var typeName = simpleName[(i + 1)..]; + var source = $$""" + using System.Reflection; + + [assembly: AssemblyVersion("{{versionNumber}}")] + [assembly: AssemblyFileVersion("{{versionNumber}}")] + + public sealed class {{typeName}} { } + """; + + var compilation = CSharpCompilation.Create( + simpleName, + [CSharpSyntaxTree.ParseText(source)], + NetStandard20.References.All, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + var result = compilation.Emit(Path.Combine(directory, $"{simpleName}.dll")); + Assert.True(result.Success); + } + } + + private static void RunWithLoader(Action action) + { + var compilerLoadContext = new AssemblyLoadContext("Compiler", isCollectible: true); + var currentLoadContext = new AssemblyLoadContext("Current", isCollectible: true); + var loader = new AnalyzerAssemblyLoader([], [AnalyzerAssemblyLoader.DiskAnalyzerAssemblyResolver], compilerLoadContext); +#pragma warning disable 612 + var resolver = CreateResolver(); +#pragma warning restore 612 + action(resolver, loader, currentLoadContext); + + Assert.Empty(currentLoadContext.Assemblies); + currentLoadContext.Unload(); + compilerLoadContext.Unload(); + } + + [Obsolete] + internal static RazorAnalyzerAssemblyResolver CreateResolver() => new RazorAnalyzerAssemblyResolver(); + + /// + /// When running in Visual Studio the razor generator will be redirected to the razor language + /// services directory. That will not contain all of the necessary DLLs. Anything that is a + /// platform DLL, like the object pool, will be in the VS platform directory. Need to fall back + /// to the compiler context to find those. + /// + [Fact] + public void FallbackToCompilerContext() + { + var dir1 = TempRoot.CreateDirectory().Path; + CreateRazorAssemblies(dir1); + var dir2 = TempRoot.CreateDirectory().Path; + var fileName = $"{RazorAnalyzerAssemblyResolver.ObjectPoolAssemblyName}.dll"; + File.Move(Path.Combine(dir1, fileName), Path.Combine(dir2, fileName)); + + RunWithLoader((resolver, loader, currentLoadContext) => + { + Assembly? expectedAssembly = null; + loader.CompilerLoadContext.Resolving += (context, name) => + { + if (name.Name == RazorAnalyzerAssemblyResolver.ObjectPoolAssemblyName) + { + expectedAssembly = context.LoadFromAssemblyPath(Path.Combine(dir2, fileName)); + return expectedAssembly; + } + + return null; + }; + + var actualAssembly = resolver.Resolve( + loader, + new AssemblyName(RazorAnalyzerAssemblyResolver.ObjectPoolAssemblyName), + currentLoadContext, + dir1); + Assert.NotNull(expectedAssembly); + Assert.NotNull(actualAssembly); + Assert.Same(expectedAssembly, actualAssembly); + }); + } + + [Fact] + public void FirstLoadWins() + { + var dir1 = TempRoot.CreateDirectory().Path; + CreateRazorAssemblies(dir1, versionNumber: "1.0.0.0"); + var dir2 = TempRoot.CreateDirectory().Path; + CreateRazorAssemblies(dir2, versionNumber: "2.0.0.0"); + + RunWithLoader((resolver, loader, currentLoadContext) => + { + var assembly1 = resolver.Resolve( + loader, + new AssemblyName(RazorAnalyzerAssemblyResolver.RazorCompilerAssemblyName), + currentLoadContext, + dir1); + var assembly2 = resolver.Resolve( + loader, + new AssemblyName(RazorAnalyzerAssemblyResolver.RazorCompilerAssemblyName), + currentLoadContext, + dir2); + Assert.Same(assembly1, assembly2); + }); + } + + [Fact] + public void ChooseServiceHubFolder() + { + var dir = TempRoot.CreateDirectory().Path; + CreateRazorAssemblies(dir); + var serviceHubFolder = Path.Combine(dir, RazorAnalyzerAssemblyResolver.ServiceHubCoreFolderName); + CreateRazorAssemblies(serviceHubFolder); + + coreTest(dir, serviceHubFolder); + coreTest(dir + Path.DirectorySeparatorChar, serviceHubFolder); + coreTest(serviceHubFolder, serviceHubFolder); + coreTest(serviceHubFolder + Path.DirectorySeparatorChar, serviceHubFolder); + + void coreTest(string loadDir, string serviceHubDir) + { + var name = Path.GetFileName(loadDir.AsSpan()); + RunWithLoader((resolver, loader, currentLoadContext) => + { + var assembly1 = resolver.Resolve( + loader, + new AssemblyName(RazorAnalyzerAssemblyResolver.RazorCompilerAssemblyName), + currentLoadContext, + loadDir); + var assembly2 = resolver.Resolve( + loader, + new AssemblyName(RazorAnalyzerAssemblyResolver.RazorCompilerAssemblyName), + currentLoadContext, + serviceHubFolder); + Assert.NotNull(assembly1); + Assert.Same(assembly1, assembly2); + Assert.Equal(serviceHubFolder, Path.GetDirectoryName(assembly1.Location)); + }); + } + } +} +#endif diff --git a/src/Tools/ExternalAccess/RazorTest/RazorPredefinedProviderNameTests.cs b/src/Tools/ExternalAccess/RazorTest/RazorPredefinedProviderNameTests.cs index b0a53da76ffcd..2feee9644a518 100644 --- a/src/Tools/ExternalAccess/RazorTest/RazorPredefinedProviderNameTests.cs +++ b/src/Tools/ExternalAccess/RazorTest/RazorPredefinedProviderNameTests.cs @@ -50,14 +50,14 @@ private static ImmutableDictionary GetPredefinedNamesFromFields( { return namesType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public) .Where(field => field.FieldType == typeof(string)) - .ToImmutableDictionary(field => field.Name, field => (string)field.GetValue(null)); + .ToImmutableDictionary(field => field.Name, field => (string)field.GetValue(null)!); } private static ImmutableDictionary GetPredefinedNamesFromProperties(Type namesType) { return namesType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public) .Where(property => property.PropertyType == typeof(string)) - .ToImmutableDictionary(property => property.Name, property => (string)property.GetValue(null)); + .ToImmutableDictionary(property => property.Name, property => (string)property.GetValue(null)!); } } } diff --git a/src/Tools/Replay/Replay.csproj b/src/Tools/Replay/Replay.csproj index fd3d130c7aca8..519b8cf1b36ef 100644 --- a/src/Tools/Replay/Replay.csproj +++ b/src/Tools/Replay/Replay.csproj @@ -27,6 +27,13 @@ vbcscompiler + + + + + + + diff --git a/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs index f92be172c469a..5a1203605807c 100644 --- a/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs +++ b/src/Tools/SemanticSearch/BuildTask/GenerateFilteredReferenceAssembliesTask.cs @@ -66,6 +66,11 @@ public sealed class GenerateFilteredReferenceAssembliesTask : Task [Required] public string ApisDir { get; private set; } = null!; + /// + /// True to report an error if any changes in Semantic Search APIs are detected. + /// + public bool RequireNoApiChanges { get; private set; } = false; + public override bool Execute() { try @@ -123,12 +128,14 @@ internal void ExecuteImpl(IEnumerable<(string apiSpecPath, IReadOnlyList return; } - WriteApis(Path.Combine(ApisDir, assemblyName + ".txt"), peImageBuffer); + WriteApis(assemblyName, peImageBuffer); } } - internal void WriteApis(string outputFilePath, byte[] peImage) + internal void WriteApis(string assemblyName, byte[] peImage) { + string outputFilePath = Path.Combine(ApisDir, assemblyName + ".txt"); + using var readableStream = new MemoryStream(peImage, writable: false); var metadataRef = MetadataReference.CreateFromStream(readableStream); var compilation = CSharpCompilation.Create("Metadata", references: [metadataRef]); @@ -149,9 +156,39 @@ internal void WriteApis(string outputFilePath, byte[] peImage) var newContent = $"# Generated, do not update manually{Environment.NewLine}" + string.Join(Environment.NewLine, apis); + if (RequireNoApiChanges) + { + var oldContent = ""; + + if (File.Exists(outputFilePath)) + { + try + { + oldContent = File.ReadAllText(outputFilePath, Encoding.UTF8); + } + catch (FileNotFoundException) + { + } + catch (Exception e) + { + Log.LogError($"Unable to read '{outputFilePath}': {e.Message}"); + return; + } + } + + if (oldContent != newContent) + { + Log.LogError( + $"APIs listed in file '{outputFilePath}' do not match the public APIs exposed by '{assemblyName}'. " + + $"Build SemanticSearch.ReferenceAssemblies project locally to update the file and review the changes."); + + return; + } + } + try { - File.WriteAllText(outputFilePath, newContent); + File.WriteAllText(outputFilePath, newContent, Encoding.UTF8); Log.LogMessage($"Baseline updated: '{outputFilePath}'"); } catch (Exception e) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt index 2bdd939afa897..31577ead6d1b7 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.CSharp.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo.Equals(Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo) Microsoft.CodeAnalysis.CSharp.AwaitExpressionInfo.Equals(System.Object) @@ -407,6 +407,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExplicitInterfaceSpecifi Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -430,6 +431,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitGroupClause(Microsoft.Co Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxRewriter.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax) @@ -701,6 +703,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExplicitInterfaceSpecifie Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -724,6 +727,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitGroupClause(Microsoft.Cod Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax) @@ -949,6 +953,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExplicitInterfaceSpecif Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionColon(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionColonSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionElement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionElementSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExtensionDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitExternAliasDirective(Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitFieldDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FieldDeclarationSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitFieldExpression(Microsoft.CodeAnalysis.CSharp.Syntax.FieldExpressionSyntax) @@ -972,6 +977,7 @@ Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitGroupClause(Microsoft.C Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIdentifierName(Microsoft.CodeAnalysis.CSharp.Syntax.IdentifierNameSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIfDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IfDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIfStatement(Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax) +Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitIgnoredDirectiveTrivia(Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitElementAccess(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitElementAccessSyntax) Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor`1.VisitImplicitObjectCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitObjectCreationExpressionSyntax) @@ -2451,6 +2457,38 @@ Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax.get_AttributeList Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax.get_Expression Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionStatementSyntax.get_SemicolonToken Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddAttributeLists(Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddConstraintClauses(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddMembers(Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddModifiers(Microsoft.CodeAnalysis.SyntaxToken[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddParameterListParameters(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.AddTypeParameterListParameters(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterSyntax[]) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithAttributeLists(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax}) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithCloseBraceToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithConstraintClauses(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax}) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithKeyword(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithMembers(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax}) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithModifiers(Microsoft.CodeAnalysis.SyntaxTokenList) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithOpenBraceToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithSemicolonToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.WithTypeParameterList(Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax) +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_AttributeLists +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_BaseList +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_CloseBraceToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_ConstraintClauses +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_Identifier +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_Keyword +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_Members +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_Modifiers +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_OpenBraceToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_ParameterList +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_SemicolonToken +Microsoft.CodeAnalysis.CSharp.Syntax.ExtensionDeclarationSyntax.get_TypeParameterList Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) @@ -2791,6 +2829,18 @@ Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_Else Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_IfKeyword Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_OpenParenToken Microsoft.CodeAnalysis.CSharp.Syntax.IfStatementSyntax.get_Statement +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,System.Boolean) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithColonToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithEndOfDirectiveToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithHashToken(Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.WithIsActive(System.Boolean) +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_ColonToken +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_EndOfDirectiveToken +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_HashToken +Microsoft.CodeAnalysis.CSharp.Syntax.IgnoredDirectiveTriviaSyntax.get_IsActive Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax.Accept(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor) Microsoft.CodeAnalysis.CSharp.Syntax.ImplicitArrayCreationExpressionSyntax.Accept``1(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor{``0}) @@ -4947,6 +4997,9 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionStatement(Microsoft.CodeAn Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionStatement(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExpressionStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax},Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax}) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExtensionDeclaration(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterListSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.TypeParameterConstraintClauseSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ExternAliasDirective(System.String) @@ -5022,6 +5075,8 @@ Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.C Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxList{Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax},Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IfStatement(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.StatementSyntax,Microsoft.CodeAnalysis.CSharp.Syntax.ElseClauseSyntax) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,System.Boolean) +Microsoft.CodeAnalysis.CSharp.SyntaxFactory.IgnoredDirectiveTrivia(System.Boolean) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.SyntaxToken,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ImplicitArrayCreationExpression(Microsoft.CodeAnalysis.SyntaxTokenList,Microsoft.CodeAnalysis.CSharp.Syntax.InitializerExpressionSyntax) @@ -5822,6 +5877,8 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExplicitKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionColon Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionElement Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionStatement +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionDeclaration +Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExtensionKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExternAliasDirective Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExternKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.FalseKeyword @@ -5875,6 +5932,7 @@ Microsoft.CodeAnalysis.CSharp.SyntaxKind.IdentifierToken Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfDirectiveTrivia Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfKeyword Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfStatement +Microsoft.CodeAnalysis.CSharp.SyntaxKind.IgnoredDirectiveTrivia Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitArrayCreationExpression Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitElementAccess Microsoft.CodeAnalysis.CSharp.SyntaxKind.ImplicitKeyword diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt index ae3b53b6e80d9..8ca7dbf98abde 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/Microsoft.CodeAnalysis.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually Microsoft.CodeAnalysis.Accessibility Microsoft.CodeAnalysis.Accessibility.Friend Microsoft.CodeAnalysis.Accessibility.Internal @@ -1169,10 +1169,13 @@ Microsoft.CodeAnalysis.IErrorTypeSymbol.get_CandidateSymbols Microsoft.CodeAnalysis.IEventSymbol Microsoft.CodeAnalysis.IEventSymbol.get_AddMethod Microsoft.CodeAnalysis.IEventSymbol.get_ExplicitInterfaceImplementations +Microsoft.CodeAnalysis.IEventSymbol.get_IsPartialDefinition Microsoft.CodeAnalysis.IEventSymbol.get_IsWindowsRuntimeEvent Microsoft.CodeAnalysis.IEventSymbol.get_NullableAnnotation Microsoft.CodeAnalysis.IEventSymbol.get_OriginalDefinition Microsoft.CodeAnalysis.IEventSymbol.get_OverriddenEvent +Microsoft.CodeAnalysis.IEventSymbol.get_PartialDefinitionPart +Microsoft.CodeAnalysis.IEventSymbol.get_PartialImplementationPart Microsoft.CodeAnalysis.IEventSymbol.get_RaiseMethod Microsoft.CodeAnalysis.IEventSymbol.get_RemoveMethod Microsoft.CodeAnalysis.IEventSymbol.get_Type @@ -1468,8 +1471,10 @@ Microsoft.CodeAnalysis.ITypeSymbol.ToMinimalDisplayString(Microsoft.CodeAnalysis Microsoft.CodeAnalysis.ITypeSymbol.WithNullableAnnotation(Microsoft.CodeAnalysis.NullableAnnotation) Microsoft.CodeAnalysis.ITypeSymbol.get_AllInterfaces Microsoft.CodeAnalysis.ITypeSymbol.get_BaseType +Microsoft.CodeAnalysis.ITypeSymbol.get_ExtensionParameter Microsoft.CodeAnalysis.ITypeSymbol.get_Interfaces Microsoft.CodeAnalysis.ITypeSymbol.get_IsAnonymousType +Microsoft.CodeAnalysis.ITypeSymbol.get_IsExtension Microsoft.CodeAnalysis.ITypeSymbol.get_IsNativeIntegerType Microsoft.CodeAnalysis.ITypeSymbol.get_IsReadOnly Microsoft.CodeAnalysis.ITypeSymbol.get_IsRecord @@ -3954,6 +3959,7 @@ Microsoft.CodeAnalysis.TypeKind.Delegate Microsoft.CodeAnalysis.TypeKind.Dynamic Microsoft.CodeAnalysis.TypeKind.Enum Microsoft.CodeAnalysis.TypeKind.Error +Microsoft.CodeAnalysis.TypeKind.Extension Microsoft.CodeAnalysis.TypeKind.FunctionPointer Microsoft.CodeAnalysis.TypeKind.Interface Microsoft.CodeAnalysis.TypeKind.Module diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.Immutable.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.Immutable.txt index 1977066cdea78..0769220e56547 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.Immutable.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.Immutable.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually System.Collections.Frozen.FrozenDictionary System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary``2(System.Collections.Generic.IEnumerable{System.Collections.Generic.KeyValuePair{``0,``1}},System.Collections.Generic.IEqualityComparer{``0}) System.Collections.Frozen.FrozenDictionary.ToFrozenDictionary``2(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``1},System.Collections.Generic.IEqualityComparer{``1}) @@ -20,6 +20,8 @@ System.Collections.Frozen.FrozenDictionary`2.get_Item(`0) System.Collections.Frozen.FrozenDictionary`2.get_Keys System.Collections.Frozen.FrozenDictionary`2.get_Values System.Collections.Frozen.FrozenSet +System.Collections.Frozen.FrozenSet.Create``1(System.Collections.Generic.IEqualityComparer{``0},System.ReadOnlySpan{``0}) +System.Collections.Frozen.FrozenSet.Create``1(System.ReadOnlySpan{``0}) System.Collections.Frozen.FrozenSet.ToFrozenSet``1(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEqualityComparer{``0}) System.Collections.Frozen.FrozenSet`1 System.Collections.Frozen.FrozenSet`1.Contains(`0) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.txt index 62f756acc8c59..4071eee51a37e 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Collections.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually System.Collections.BitArray System.Collections.BitArray.#ctor(System.Boolean[]) System.Collections.BitArray.#ctor(System.Byte[]) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Linq.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Linq.txt index 85c524ab52cba..6bc2f9932b265 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Linq.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Linq.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually System.Linq.Enumerable System.Linq.Enumerable.Aggregate``1(System.Collections.Generic.IEnumerable{``0},System.Func{``0,``0,``0}) System.Linq.Enumerable.Aggregate``2(System.Collections.Generic.IEnumerable{``0},``1,System.Func{``1,``0,``1}) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Runtime.txt b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Runtime.txt index cb02fc218dc5f..e43bf8dc555ae 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Runtime.txt +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/Apis/System.Runtime.txt @@ -1,4 +1,4 @@ -# Generated, do not update manually +# Generated, do not update manually System.AccessViolationException System.AccessViolationException.#ctor System.AccessViolationException.#ctor(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) diff --git a/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj b/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj index e241734e01deb..35388a82d149c 100644 --- a/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj +++ b/src/Tools/SemanticSearch/ReferenceAssemblies/SemanticSearch.ReferenceAssemblies.csproj @@ -45,7 +45,7 @@ '%(ReferencePath.FileName)' == 'Microsoft.CodeAnalysis.CSharp'" /> - + <_InputFile Include="@(ApiSet)" /> <_InputFile Include="@(_InputReference)" /> @@ -58,7 +58,13 @@ - + + + <_RequireNoApiChanges>false + <_RequireNoApiChanges Condition="'$(ContinuousIntegrationBuildCorrectness)' == 'true'">true + + + @@ -66,7 +72,7 @@ - + diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs index 6fe668757a224..084598463ccf0 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs +++ b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/BoundNodeClassWriter.cs @@ -769,6 +769,16 @@ private IEnumerable AllSymbolOrSymbolListFields(TreeType node) return AllFields(node).Where(field => TypeIsSymbol(field) || (IsImmutableArray(field.Type, out var elementType) && TypeIsSymbol(elementType))); } + private IEnumerable AllNonTypeSymbolOrNonTypeSymbolListFields(TreeType node) + { + return AllFields(node).Where(field => IsNonTypeSymbolOrNonTypeSymbolListField(field)); + } + + private bool IsNonTypeSymbolOrNonTypeSymbolListField(Field field) + { + return TypeIsNonTypeSymbol(field) || (IsImmutableArray(field.Type, out var elementType) && TypeIsNonTypeSymbol(elementType)); + } + private NullHandling FieldNullHandling(TreeType node, string fieldName) { Field f = GetField(node, fieldName); @@ -1044,6 +1054,9 @@ string notEquals(Field field) private static bool TypeIsSymbol(Field field) => TypeIsSymbol(field.Type); private static bool TypeIsSymbol(string type) => type.TrimEnd('?').EndsWith("Symbol"); + private static bool TypeIsNonTypeSymbol(Field field) => TypeIsNonTypeSymbol(field.Type); + private static bool TypeIsNonTypeSymbol(string type) => TypeIsSymbol(type) && type.TrimEnd('?') != "TypeSymbol"; + private string StripBound(string name) { if (name.StartsWith("Bound", StringComparison.Ordinal)) @@ -1363,7 +1376,7 @@ private void WriteRewriter() Brace(); foreach (var node in _tree.Types.OfType()) { - if (!AllNodeOrNodeListFields(node).Any() && !AllTypeFields(node).Any()) + if (!AllNodeOrNodeListFields(node).Any() && !AllTypeFields(node).Any() && !AllNonTypeSymbolOrNonTypeSymbolListFields(node).Any()) { WriteLine($"{GetVisitFunctionDeclaration(node.Name, isOverride: true)} => node;"); continue; @@ -1371,20 +1384,51 @@ private void WriteRewriter() WriteLine(GetVisitFunctionDeclaration(node.Name, isOverride: true)); Brace(); bool hadField = false; + + foreach (var field in AllNonTypeSymbolOrNonTypeSymbolListFields(node)) + { + hadField = true; + + if (!IsImmutableArray(field.Type, out string elementType)) + { + WriteLine($"{field.Type} {ToCamelCase(field.Name)} = this.Visit{field.Type.TrimEnd('?')}(node.{field.Name});"); + } + else if (elementType.TrimEnd('?') == "LocalSymbol") + { + WriteLine($"{field.Type} {ToCamelCase(field.Name)} = this.VisitLocals(node.{field.Name});"); + } + else if (elementType.TrimEnd('?') == "MethodSymbol" && field.Name.EndsWith("LocalFunctions")) + { + WriteLine($"{field.Type} {ToCamelCase(field.Name)} = this.VisitDeclaredLocalFunctions(node.{field.Name});"); + } + else + { + WriteLine($"{field.Type} {ToCamelCase(field.Name)} = this.VisitSymbols<{elementType}>(node.{field.Name});"); + } + } + foreach (Field field in AllNodeOrNodeListFields(node)) { hadField = true; WriteNodeVisitCall(field); } + foreach (Field field in AllTypeFields(node)) { hadField = true; WriteLine("TypeSymbol? {0} = this.VisitType(node.{1});", ToCamelCase(field.Name), field.Name); } + if (hadField) { Write("return node.Update"); - ParenList(AllSpecifiableFields(node), field => IsDerivedOrListOfDerived("BoundNode", field.Type) || TypeIsTypeSymbol(field) ? ToCamelCase(field.Name) : string.Format("node.{0}", field.Name)); + ParenList( + AllSpecifiableFields(node), + field => IsDerivedOrListOfDerived("BoundNode", field.Type) || + TypeIsTypeSymbol(field) || + IsNonTypeSymbolOrNonTypeSymbolListField(field) ? + ToCamelCase(field.Name) : + string.Format("node.{0}", field.Name)); WriteLine(";"); } else diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj index 20219430450e9..ee10aa95fbca4 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj @@ -14,6 +14,9 @@ + + + diff --git a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs b/src/VisualStudio/CSharp/Impl/CSharpPackage.cs index 30bfc38cee381..be4d8b5ee2bf5 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs +++ b/src/VisualStudio/CSharp/Impl/CSharpPackage.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.Generic; using System.Runtime.InteropServices; @@ -57,7 +55,7 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService [Guid(Guids.CSharpPackageIdString)] internal sealed class CSharpPackage : AbstractPackage, IVsUserSettingsQuery { - private ObjectBrowserLibraryManager _libraryManager; + private ObjectBrowserLibraryManager? _libraryManager; private uint _libraryManagerCookie; protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) @@ -79,13 +77,13 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke } } - protected override async Task RegisterObjectBrowserLibraryManagerAsync(CancellationToken cancellationToken) + protected override void RegisterObjectBrowserLibraryManager() { - var workspace = this.ComponentModel.GetService(); + Contract.ThrowIfFalse(JoinableTaskFactory.Context.IsOnMainThread); - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + var workspace = this.ComponentModel.GetService(); - if (await GetServiceAsync(typeof(SVsObjectManager)).ConfigureAwait(true) is IVsObjectManager2 objectManager) + if (GetService(typeof(SVsObjectManager)) is IVsObjectManager2 objectManager) { _libraryManager = new ObjectBrowserLibraryManager(this, ComponentModel, workspace); @@ -96,19 +94,19 @@ protected override async Task RegisterObjectBrowserLibraryManagerAsync(Cancellat } } - protected override async Task UnregisterObjectBrowserLibraryManagerAsync(CancellationToken cancellationToken) + protected override void UnregisterObjectBrowserLibraryManager() { - await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + Contract.ThrowIfFalse(JoinableTaskFactory.Context.IsOnMainThread); if (_libraryManagerCookie != 0) { - if (await GetServiceAsync(typeof(SVsObjectManager)).ConfigureAwait(true) is IVsObjectManager2 objectManager) + if (GetService(typeof(SVsObjectManager)) is IVsObjectManager2 objectManager) { objectManager.UnregisterLibrary(_libraryManagerCookie); _libraryManagerCookie = 0; } - _libraryManager.Dispose(); + _libraryManager?.Dispose(); _libraryManager = null; } } @@ -136,7 +134,7 @@ protected override IEnumerable CreateEditorFactories() var editorFactory = new CSharpEditorFactory(this.ComponentModel); var codePageEditorFactory = new CSharpCodePageEditorFactory(editorFactory); - return new IVsEditorFactory[] { editorFactory, codePageEditorFactory }; + return [editorFactory, codePageEditorFactory]; } protected override CSharpLanguageService CreateLanguageService() diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodePageEditorFactory.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodePageEditorFactory.cs index 4d34e27fa8205..dcc7758ce97e3 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodePageEditorFactory.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpCodePageEditorFactory.cs @@ -2,19 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Runtime.InteropServices; using Microsoft.VisualStudio.LanguageServices.Implementation; namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService { [Guid(Guids.CSharpCodePageEditorFactoryIdString)] - internal class CSharpCodePageEditorFactory : AbstractCodePageEditorFactory + internal sealed class CSharpCodePageEditorFactory(AbstractEditorFactory editorFactory) : AbstractCodePageEditorFactory(editorFactory) { - public CSharpCodePageEditorFactory(AbstractEditorFactory editorFactory) - : base(editorFactory) - { - } } } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpDebuggerIntelliSenseContext.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpDebuggerIntelliSenseContext.cs index cc232ea5b71fc..7b1d995740dce 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpDebuggerIntelliSenseContext.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpDebuggerIntelliSenseContext.cs @@ -25,6 +25,8 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService { internal class CSharpDebuggerIntelliSenseContext : AbstractDebuggerIntelliSenseContext { + private const string StatementTerminator = ";"; + public CSharpDebuggerIntelliSenseContext( IWpfTextView view, IVsTextView vsTextView, @@ -60,13 +62,19 @@ internal CSharpDebuggerIntelliSenseContext( { } - protected override int GetAdjustedContextPoint(int contextPoint, Document document) + protected override IProjectionBuffer GetAdjustedBuffer(int contextPoint, Document document, ITrackingSpan debuggerMappedSpan) { // Determine the position in the buffer at which to end the tracking span representing // the part of the imaginary buffer before the text in the view. var tree = document.GetSyntaxTreeSynchronously(CancellationToken.None); var token = tree.FindTokenOnLeftOfPosition(contextPoint, CancellationToken.None); + // Typically, the separator between the text before adjustedStart and debuggerMappedSpan is + // a semicolon (StatementTerminator), unless a specific condition outlined later in the + // method is encountered. + var separatorBeforeDebuggerMappedSpan = StatementTerminator; + var adjustedStart = token.FullSpan.End; + // Special case to handle class designer because it asks for debugger IntelliSense using // spans between members. if (contextPoint > token.Span.End && @@ -74,40 +82,46 @@ protected override int GetAdjustedContextPoint(int contextPoint, Document docume token.Parent.IsKind(SyntaxKind.Block) && token.Parent.Parent is MemberDeclarationSyntax) { - return contextPoint; + adjustedStart = contextPoint; } - - if (token.IsKindOrHasMatchingText(SyntaxKind.CloseBraceToken) && + else if (token.IsKindOrHasMatchingText(SyntaxKind.CloseBraceToken) && token.Parent.IsKind(SyntaxKind.Block)) { - return token.SpanStart; + adjustedStart = token.SpanStart; + } + else if (token.IsKindOrHasMatchingText(SyntaxKind.SemicolonToken) && + token.Parent is StatementSyntax) + { + // If the context is at a semicolon terminated statement, then we use the start of + // that statement as the adjusted context position. This is to ensure the placement + // of debuggerMappedSpan is in the same block as token originally was. For example, + // + // for (int i = 0; i < 10; i++) + // [Console.WriteLine(i);] + // + // where [] denotes CurrentStatementSpan, should use the start of CurrentStatementSpan + // as the adjusted context, and should not place a semicolon before debuggerMappedSpan. + // Not doing either of those would place debuggerMappedSpan outside the for loop. + // We use a space as the separator in this case (instead of an empty string) to help + // the vs editor out and not have a projection seam at the location they will bring + // up completion. + separatorBeforeDebuggerMappedSpan = " "; + adjustedStart = token.Parent.SpanStart; } - return token.FullSpan.End; - } - - protected override ITrackingSpan GetPreviousStatementBufferAndSpan(int contextPoint, Document document) - { - var previousTrackingSpan = ContextBuffer.CurrentSnapshot.CreateTrackingSpan(Span.FromBounds(0, contextPoint), SpanTrackingMode.EdgeNegative); - - // terminate the previous expression/statement - var buffer = ProjectionBufferFactoryService.CreateProjectionBuffer( - projectionEditResolver: null, - sourceSpans: [previousTrackingSpan, this.StatementTerminator], - options: ProjectionBufferOptions.None, - contentType: this.ContentType); + var beforeAdjustedStart = ContextBuffer.CurrentSnapshot.CreateTrackingSpan(Span.FromBounds(0, adjustedStart), SpanTrackingMode.EdgeNegative); + var afterAdjustedStart = ContextBuffer.CurrentSnapshot.CreateTrackingSpanFromIndexToEnd(adjustedStart, SpanTrackingMode.EdgePositive); - return buffer.CurrentSnapshot.CreateTrackingSpan(0, buffer.CurrentSnapshot.Length, SpanTrackingMode.EdgeNegative); + return ProjectionBufferFactoryService.CreateProjectionBuffer( + projectionEditResolver: null, + sourceSpans: [beforeAdjustedStart, separatorBeforeDebuggerMappedSpan, debuggerMappedSpan, StatementTerminator, afterAdjustedStart], + options: ProjectionBufferOptions.None, + contentType: ContentType); } public override bool CompletionStartsOnQuestionMark { get { return false; } } - - protected override string StatementTerminator - { - get { return ";"; } - } } } diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpEditorFactory.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpEditorFactory.cs index b224aec2a8c39..355231b21a254 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpEditorFactory.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpEditorFactory.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.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; @@ -18,13 +16,8 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService { [ExcludeFromCodeCoverage] [Guid(Guids.CSharpEditorFactoryIdString)] - internal class CSharpEditorFactory : AbstractEditorFactory + internal class CSharpEditorFactory(IComponentModel componentModel) : AbstractEditorFactory(componentModel) { - public CSharpEditorFactory(IComponentModel componentModel) - : base(componentModel) - { - } - protected override string ContentTypeName => ContentTypeNames.CSharpContentType; protected override string LanguageName => LanguageNames.CSharp; diff --git a/src/VisualStudio/CSharp/Impl/Options/CSharpVisualStudioCopilotOptionsService.cs b/src/VisualStudio/CSharp/Impl/Options/CSharpVisualStudioCopilotOptionsService.cs index f2e7531ff732c..a9eab90ec968f 100644 --- a/src/VisualStudio/CSharp/Impl/Options/CSharpVisualStudioCopilotOptionsService.cs +++ b/src/VisualStudio/CSharp/Impl/Options/CSharpVisualStudioCopilotOptionsService.cs @@ -10,8 +10,8 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.Internal.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Utilities.UnifiedSettings; namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options; @@ -39,13 +39,12 @@ internal sealed class CSharpVisualStudioCopilotOptionsService : ICopilotOptionsS /// private const string GitHubAccountStatusIsCopilotEntitled = "3DE3FA6E-91B2-46C1-9E9E-DD04975BB890"; - private const string CopilotOptionNamePrefix = "Microsoft.VisualStudio.Conversations"; - // Default value must reflect their default values in ConversationsOptions in Copilot repo. - private readonly CopilotOption _copilotCodeAnalysisOption = new("EnableCSharpCodeAnalysis", false); - private readonly CopilotOption _copilotRefineOption = new("EnableCSharpRefineQuickActionSuggestion", false); - private readonly CopilotOption _copilotOnTheFlyDocsOption = new("EnableOnTheFlyDocs", true); - private readonly CopilotOption _copilotGenerateDocumentationCommentOption = new("EnableCSharpGenerateDocumentationComment", true); + private readonly CopilotOption _copilotCodeAnalysisOption = new("copilot.featureFlags.editor.enableCSharpCodeAnalysis", false); + private readonly CopilotOption _copilotRefineOption = new("copilot.featureFlags.editor.enableCSharpRefineQuickActionSuggestion", false); + private readonly CopilotOption _copilotOnTheFlyDocsOption = new("copilot.general.editor.enableOnTheFlyDocs", true); + private readonly CopilotOption _copilotGenerateDocumentationCommentOption = new("copilot.general.editor.enableGenerateDocumentationComment", true); + private readonly CopilotOption _copilotGenerateMethodImplementationOption = new("copilot.featureFlags.editor.enableCSharpGenerateMethodImplementation", false); private static readonly UIContext s_copilotHasLoadedUIContext = UIContext.FromUIContextGuid(new Guid(CopilotHasLoadedGuid)); private static readonly UIContext s_gitHubAccountStatusDeterminedContext = UIContext.FromUIContextGuid(new Guid(GitHubAccountStatusDetermined)); @@ -53,6 +52,7 @@ internal sealed class CSharpVisualStudioCopilotOptionsService : ICopilotOptionsS private static readonly UIContext s_gitHubAccountStatusSignedInUIContext = UIContext.FromUIContextGuid(new Guid(GitHubAccountStatusSignedIn)); private readonly Task _settingsManagerTask; + private ISettingsReader? _settingsReader; /// /// Determines if Copilot is active and the user is signed in and entitled to use Copilot. @@ -66,7 +66,7 @@ private static bool IsGithubCopilotLoadedAndSignedIn [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CSharpVisualStudioCopilotOptionsService( - IVsService settingsManagerService, + IVsService settingsManagerService, IThreadingContext threadingContext) { _settingsManagerTask = settingsManagerService.GetValueAsync(threadingContext.DisposalToken); @@ -77,11 +77,23 @@ private async Task IsCopilotOptionEnabledAsync(CopilotOption option) if (!IsGithubCopilotLoadedAndSignedIn) return false; - var settingManager = await _settingsManagerTask.ConfigureAwait(false); - // The bool setting is persisted as 0=None, 1=True, 2=False, so it needs to be retrieved as an int. - // If isEnabled is 0 or the value is not persisted, we should return the default value for the option. - var isEnabled = settingManager.GetValueOrDefault($"{CopilotOptionNamePrefix}.{option.Name}", 0); - return isEnabled == 1 || (isEnabled == 0 && option.DefaultValue); + if (_settingsReader == null) + { + var settingsManager = await _settingsManagerTask.ConfigureAwait(false); + _settingsReader = settingsManager.GetReader(); + } + + try + { + if (_settingsReader.GetValue(option.Name) is { Outcome: SettingRetrievalOutcome.Success } setting) + return setting.Value; + + return option.DefaultValue; + } + catch + { + return option.DefaultValue; + } } public Task IsCodeAnalysisOptionEnabledAsync() @@ -96,5 +108,8 @@ public Task IsOnTheFlyDocsOptionEnabledAsync() public Task IsGenerateDocumentationCommentOptionEnabledAsync() => IsCopilotOptionEnabledAsync(_copilotGenerateDocumentationCommentOption); + public Task IsImplementNotImplementedExceptionEnabledAsync() + => IsCopilotOptionEnabledAsync(_copilotGenerateMethodImplementationOption); + private record struct CopilotOption(string Name, bool DefaultValue); } diff --git a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs index 02f811eb9514b..9f5e10f499e86 100644 --- a/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs +++ b/src/VisualStudio/CSharp/Impl/ProjectSystemShim/TempPECompilerService.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -15,7 +14,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.CSharp.ProjectSystemShim { diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchPresenterController.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchPresenterController.cs new file mode 100644 index 0000000000000..bba469dc2de29 --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchPresenterController.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.SemanticSearch; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +/// +/// Executes Semantic Search query and streams results to Find Results tool window. +/// +[Export(typeof(ISemanticSearchPresenterController)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SemanticSearchPresenterController( + IStreamingFindUsagesPresenter resultsPresenter, + VisualStudioWorkspace workspace, + IGlobalOptionService globalOptions) : ISemanticSearchPresenterController +{ + public async Task ExecuteQueryAsync(string query, CancellationToken cancellationToken) + { + var (presenterContext, presenterCancellationToken) = resultsPresenter.StartSearch(ServicesVSResources.Semantic_search_results, StreamingFindUsagesPresenterOptions.Default); + using var queryCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(presenterCancellationToken, cancellationToken); + + var executor = new SemanticSearchQueryExecutor(presenterContext, globalOptions); + await executor.ExecuteAsync(query, queryDocument: null, workspace.CurrentSolution, queryCancellationSource.Token).ConfigureAwait(false); + } +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchQueryExecutor.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchQueryExecutor.cs new file mode 100644 index 0000000000000..09550126798c7 --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchQueryExecutor.cs @@ -0,0 +1,141 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Classification; +using Microsoft.CodeAnalysis.Editor.Host; +using Microsoft.CodeAnalysis.ErrorReporting; +using Microsoft.CodeAnalysis.FindUsages; +using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.SemanticSearch; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +internal sealed class SemanticSearchQueryExecutor( + FindUsagesContext presenterContext, + IOptionsReader options) +{ + private sealed class ResultsObserver(IFindUsagesContext presenterContext, Document? queryDocument) : ISemanticSearchResultsObserver + { + public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) + => presenterContext.OnDefinitionFoundAsync(definition, cancellationToken); + + public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) + => presenterContext.ProgressTracker.AddItemsAsync(itemCount, cancellationToken); + + public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) + => presenterContext.ProgressTracker.ItemsCompletedAsync(itemCount, cancellationToken); + + public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) + => presenterContext.OnDefinitionFoundAsync( + new SearchExceptionDefinitionItem(exception.Message, exception.TypeName, exception.StackTrace, (queryDocument != null) ? new DocumentSpan(queryDocument, exception.Span) : default), cancellationToken); + } + + private readonly OptionsProvider _classificationOptionsProvider = + OptionsProvider.GetProvider(options, static (reader, language) => reader.GetClassificationOptions(language)); + + public async Task ExecuteAsync(string? query, Document? queryDocument, Solution solution, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(query is null ^ queryDocument is null); + + if (solution.ProjectIds is []) + { + try + { + await presenterContext.ReportNoResultsAsync(ServicesVSResources.Search_found_no_results_no_csharp_or_vb_projects_opened, cancellationToken).ConfigureAwait(false); + } + finally + { + // Notify the presenter even if the search has been cancelled. + await presenterContext.OnCompletedAsync(CancellationToken.None).ConfigureAwait(false); + } + + return; + } + + var resultsObserver = new ResultsObserver(presenterContext, queryDocument); + query ??= (await queryDocument!.GetTextAsync(cancellationToken).ConfigureAwait(false)).ToString(); + + ExecuteQueryResult result = default; + var canceled = false; + try + { + result = await RemoteSemanticSearchServiceProxy.ExecuteQueryAsync( + solution, + LanguageNames.CSharp, + query, + SemanticSearchUtilities.ReferenceAssembliesDirectory, + resultsObserver, + _classificationOptionsProvider, + cancellationToken).ConfigureAwait(false); + + foreach (var error in result.compilationErrors) + { + await presenterContext.OnDefinitionFoundAsync(new SearchCompilationFailureDefinitionItem(error, queryDocument), cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) + { + result = new ExecuteQueryResult(compilationErrors: [], e.Message); + } + catch (OperationCanceledException) + { + result = new ExecuteQueryResult(compilationErrors: [], ServicesVSResources.Search_cancelled); + canceled = true; + } + finally + { + var errorMessage = result.ErrorMessage; + + if (errorMessage != null) + { + if (result.ErrorMessageArgs != null) + { + errorMessage = string.Format(errorMessage, result.ErrorMessageArgs); + } + + // not cancellable since we might be reporting cancellation: + await presenterContext.ReportMessageAsync( + errorMessage, + canceled ? NotificationSeverity.Information : NotificationSeverity.Error, + CancellationToken.None).ConfigureAwait(false); + } + + // Notify the presenter even if the search has been cancelled. + await presenterContext.OnCompletedAsync(CancellationToken.None).ConfigureAwait(false); + + ReportTelemetry(query, result, canceled); + } + } + + private static void ReportTelemetry(string queryString, ExecuteQueryResult result, bool canceled) + { + Logger.Log(FunctionId.SemanticSearch_QueryExecution, KeyValueLogMessage.Create(map => + { + map["Query"] = new PiiValue(queryString); + + if (canceled) + { + map["Canceled"] = true; + } + else if (result.ErrorMessage != null) + { + map["ErrorMessage"] = result.ErrorMessage; + + if (result.ErrorMessageArgs != null) + { + map["ErrorMessageArgs"] = new PiiValue(string.Join("|", result.ErrorMessageArgs)); + } + } + + map["ExecutionTimeMilliseconds"] = (long)result.ExecutionTime.TotalMilliseconds; + map["EmitTime"] = (long)result.EmitTime.TotalMilliseconds; + })); + } +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs index 93895a56cfd32..0aa5ada0dc88b 100644 --- a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindow.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -26,13 +27,11 @@ internal sealed class SemanticSearchToolWindow(MefInjection GetContentAsync(CancellationToken cancellationToken) - { - var content = _lazyContent; - if (content != null) - { - return content; - } - - var element = await _impl.InitializeAsync(cancellationToken).ConfigureAwait(false); - Interlocked.CompareExchange(ref _lazyContent, new WpfControlWrapper(element), null); - return _lazyContent; - } + public override Task GetContentAsync(CancellationToken cancellationToken) + => _impl.InitializeAsync(cancellationToken); } diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowController.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowController.cs new file mode 100644 index 0000000000000..baab751b6b65c --- /dev/null +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowController.cs @@ -0,0 +1,59 @@ +// 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.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.SemanticSearch; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Threading; + +namespace Microsoft.VisualStudio.LanguageServices.CSharp; + +/// +/// Controls the Semantic Search tool window. +/// +[Export(typeof(ISemanticSearchToolWindowController)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SemanticSearchToolWindowController( + SemanticSearchToolWindowImpl toolWindowImpl, + IThreadingContext threadingContext, + SVsServiceProvider serviceProvider) : ISemanticSearchToolWindowController +{ + private readonly AsyncLazy _visualStudioExtensibility = new( + () => + { + var shell = (IVsShell)serviceProvider.GetService(typeof(SVsShell)); + ErrorHandler.ThrowOnFailure(shell.LoadPackage(Guids.CSharpPackageId, out var package)); + return RoslynServiceExtensions.GetServiceAsync((AsyncPackage)package, threadingContext.JoinableTaskFactory); + }, + threadingContext.JoinableTaskFactory); + + public async Task UpdateQueryAsync(string query, bool activateWindow, bool executeQuery, CancellationToken cancellationToken) + { + var extensibility = await _visualStudioExtensibility.GetValueAsync(cancellationToken).ConfigureAwait(false); + + await extensibility.Shell().ShowToolWindowAsync(activateWindow, cancellationToken).ConfigureAwait(false); + + // make sure the window has been initialized: + _ = await toolWindowImpl.InitializeAsync(cancellationToken).ConfigureAwait(false); + + await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + toolWindowImpl.SetEditorText(query); + + if (executeQuery) + { + toolWindowImpl.RunQuery(); + } + + await TaskScheduler.Default; + } +} diff --git a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs index ad9cfd908e738..60c7289880cad 100644 --- a/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs +++ b/src/VisualStudio/CSharp/Impl/SemanticSearch/SemanticSearchToolWindowImpl.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; @@ -15,24 +14,21 @@ using System.Windows.Markup; using System.Windows.Media; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Classification; -using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Navigation; -using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SemanticSearch; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Extensibility.VSSdkCompatibility; using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.OLE.Interop; using Microsoft.VisualStudio.PlatformUI; +using Microsoft.VisualStudio.RpcContracts.RemoteUI; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; @@ -52,7 +48,7 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp; [Export(typeof(SemanticSearchToolWindowImpl))] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class SemanticSearchToolWindowImpl( +internal sealed partial class SemanticSearchToolWindowImpl( IHostWorkspaceProvider hostWorkspaceProvider, IThreadingContext threadingContext, ITextEditorFactoryService textEditorFactory, @@ -65,8 +61,8 @@ internal sealed class SemanticSearchToolWindowImpl( IStreamingFindUsagesPresenter resultsPresenter, ITextUndoHistoryRegistry undoHistoryRegistry, ISemanticSearchCopilotService copilotService, - ISemanticSearchCopilotUIProvider copilotUIProvider, - IVsService vsUIShellProvider) : ISemanticSearchWorkspaceHost, OptionsProvider + Lazy copilotUIProvider, // lazy to avoid loading Microsoft.VisualStudio.LanguageServices.ExternalAccess.Copilot + IVsService vsUIShellProvider) : ISemanticSearchWorkspaceHost, IDisposable { private const int ToolBarHeight = 26; private const int ToolBarButtonSize = 20; @@ -92,7 +88,27 @@ private readonly Lazy _semanticSearchWorkspace private IWpfTextView? _textView; private ITextBuffer? _textBuffer; - public async Task InitializeAsync(CancellationToken cancellationToken) + private IRemoteUserControl? _lazyContent; + + public void Dispose() + { + _lazyContent?.Dispose(); + } + + public async Task InitializeAsync(CancellationToken cancellationToken) + { + var content = _lazyContent; + if (content != null) + { + return content; + } + + var element = await CreateContentAsync(cancellationToken).ConfigureAwait(false); + Interlocked.CompareExchange(ref _lazyContent, new WpfControlWrapper(element), null); + return _lazyContent; + } + + private async Task CreateContentAsync(CancellationToken cancellationToken) { // TODO: replace with XAML once we can represent the editor as a XAML element // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1927626 @@ -194,7 +210,12 @@ public async Task InitializeAsync(CancellationToken cancellati private CopilotUI? CreateCopilotUI() { - if (!copilotUIProvider.IsAvailable || !copilotService.IsAvailable) + if (!copilotUIProvider.Value.IsAvailable || !copilotService.IsAvailable) + { + return null; + } + + if (!globalOptions.GetOption(SemanticSearchFeatureFlag.PromptEnabled)) { return null; } @@ -217,7 +238,7 @@ public async Task InitializeAsync(CancellationToken cancellati promptGrid.ColumnDefinitions.Add(new ColumnDefinition { MaxWidth = 600, Width = GridLength.Auto }); promptGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - var promptTextBox = copilotUIProvider.GetTextBox(); + var promptTextBox = copilotUIProvider.Value.GetTextBox(); var panel = new StackPanel() { @@ -481,21 +502,30 @@ async Task ExecuteAsync(CancellationToken cancellationToken) return; } - // Replace text buffer content. Allow using Ctrl+Z to revert to the previous content. - await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(CancellationToken.None); + SetEditorText(query.Text); + } + } - Contract.ThrowIfFalse(undoHistoryRegistry.TryGetHistory(_textBuffer, out var undoHistory)); - using var undoTransaction = undoHistory.CreateTransaction(FeaturesResources.SemanticSearch); + /// + /// Replaces current text buffer content with specified . Allow using Ctrl+Z to revert to the previous content. + /// Must be invoked on UI thread. + /// + public void SetEditorText(string text) + { + Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); + Contract.ThrowIfNull(_textBuffer); - using (var edit = _textBuffer.CreateEdit()) - { - edit.Replace(0, _textBuffer.CurrentSnapshot.Length, query.Text); - edit.Apply(); - } + Contract.ThrowIfFalse(undoHistoryRegistry.TryGetHistory(_textBuffer, out var undoHistory)); + using var undoTransaction = undoHistory.CreateTransaction(FeaturesResources.SemanticSearch); - undoTransaction.Complete(); + using (var edit = _textBuffer.CreateEdit()) + { + edit.Replace(0, _textBuffer.CurrentSnapshot.Length, text); + edit.Apply(); } + + undoTransaction.Complete(); } private void CancelQuery() @@ -510,7 +540,11 @@ private void CancelQuery() UpdateUIState(); } - private void RunQuery() + /// + /// Runs the current query. + /// Must be invoked on UI thread. + /// + public void RunQuery() { Contract.ThrowIfFalse(threadingContext.JoinableTaskContext.IsOnMainThread); Contract.ThrowIfFalse(!IsExecutingUIState()); @@ -529,8 +563,6 @@ private void RunQuery() var querySolution = _semanticSearchWorkspace.Value.CurrentSolution; var queryDocument = SemanticSearchUtilities.GetQueryDocument(querySolution); - var resultsObserver = new ResultsObserver(queryDocument, presenterContext); - var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchToolWindow) + ".Execute"); _ = ExecuteAsync(cancellationSource.Token).ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken); @@ -538,49 +570,13 @@ async Task ExecuteAsync(CancellationToken cancellationToken) { await TaskScheduler.Default; - ExecuteQueryResult result = default; - - var canceled = false; - string? queryString = null; - try { - var solution = workspace.CurrentSolution; - - if (solution.ProjectIds is []) - { - await presenterContext.ReportNoResultsAsync(ServicesVSResources.Search_found_no_results_no_csharp_or_vb_projects_opened, cancellationToken).ConfigureAwait(false); - } - else - { - var query = await queryDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); - queryString = query.ToString(); - - result = await RemoteSemanticSearchServiceProxy.ExecuteQueryAsync( - solution, - LanguageNames.CSharp, - queryString, - SemanticSearchUtilities.ReferenceAssembliesDirectory, - resultsObserver, - this, - cancellationToken).ConfigureAwait(false); - } - } - catch (Exception e) when (FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken, ErrorSeverity.Critical)) - { - result = new ExecuteQueryResult(e.Message); - } - catch (OperationCanceledException) - { - result = new ExecuteQueryResult(ServicesVSResources.Search_cancelled); - canceled = true; + var executor = new SemanticSearchQueryExecutor(presenterContext, globalOptions); + await executor.ExecuteAsync(query: null, queryDocument, workspace.CurrentSolution, cancellationToken).ConfigureAwait(false); } finally { - // Notify the presenter even if the search has been cancelled. - var completionToken = _asyncListener.BeginAsyncOperation(nameof(SemanticSearchToolWindow) + ".Completion"); - _ = CompleteSearchAsync().ReportNonFatalErrorAsync().CompletesAsyncOperation(completionToken); - // Only clear pending source if it is the same as our source (otherwise, another execution has already kicked off): Interlocked.CompareExchange(ref _pendingExecutionCancellationSource, value: null, cancellationSource); @@ -593,51 +589,6 @@ async Task ExecuteAsync(CancellationToken cancellationToken) // Update UI: UpdateUIState(); - - async Task CompleteSearchAsync() - { - var errorMessage = result.ErrorMessage; - - if (errorMessage != null) - { - if (result.ErrorMessageArgs != null) - { - errorMessage = string.Format(errorMessage, result.ErrorMessageArgs); - } - - await presenterContext.ReportMessageAsync( - errorMessage, - canceled ? NotificationSeverity.Information : NotificationSeverity.Error, - CancellationToken.None).ConfigureAwait(false); - } - - await presenterContext.OnCompletedAsync(CancellationToken.None).ConfigureAwait(false); - - if (queryString != null) - { - Logger.Log(FunctionId.SemanticSearch_QueryExecution, KeyValueLogMessage.Create(map => - { - map["Query"] = new PiiValue(queryString); - - if (canceled) - { - map["Canceled"] = true; - } - else if (result.ErrorMessage != null) - { - map["ErrorMessage"] = result.ErrorMessage; - - if (result.ErrorMessageArgs != null) - { - map["ErrorMessageArgs"] = new PiiValue(string.Join("|", result.ErrorMessageArgs)); - } - } - - map["ExecutionTimeMilliseconds"] = (long)result.ExecutionTime.TotalMilliseconds; - map["EmitTime"] = (long)result.EmitTime.TotalMilliseconds; - })); - } - } } } } @@ -665,9 +616,6 @@ public NavigableLocation GetNavigableLocation(TextSpan textSpan) return true; }); - public ValueTask GetOptionsAsync(Microsoft.CodeAnalysis.Host.LanguageServices languageServices, CancellationToken cancellationToken) - => new(globalOptions.GetClassificationOptions(languageServices.Language)); - private sealed class CopilotUI { public required FrameworkElement Control { get; init; } @@ -675,30 +623,6 @@ private sealed class CopilotUI public required ComboBox ModelPicker { get; init; } } - private sealed class ResultsObserver(Document queryDocument, IFindUsagesContext presenterContext) : ISemanticSearchResultsObserver - { - public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationToken cancellationToken) - => presenterContext.OnDefinitionFoundAsync(definition, cancellationToken); - - public ValueTask AddItemsAsync(int itemCount, CancellationToken cancellationToken) - => presenterContext.ProgressTracker.AddItemsAsync(itemCount, cancellationToken); - - public ValueTask ItemsCompletedAsync(int itemCount, CancellationToken cancellationToken) - => presenterContext.ProgressTracker.ItemsCompletedAsync(itemCount, cancellationToken); - - public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) - => presenterContext.OnDefinitionFoundAsync( - new SearchExceptionDefinitionItem(exception.Message, exception.TypeName, exception.StackTrace, new DocumentSpan(queryDocument, exception.Span)), cancellationToken); - - public async ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) - { - foreach (var error in errors) - { - await presenterContext.OnDefinitionFoundAsync(new SearchCompilationFailureDefinitionItem(error, queryDocument), cancellationToken).ConfigureAwait(false); - } - } - } - private sealed class CommandFilter : IOleCommandTarget { private readonly SemanticSearchToolWindowImpl _window; diff --git a/src/VisualStudio/CSharp/Impl/Utilities/CSharpParseOptionsChangingService.cs b/src/VisualStudio/CSharp/Impl/Utilities/CSharpParseOptionsChangingService.cs index 0ad07e8b66f65..04f83a67b30e0 100644 --- a/src/VisualStudio/CSharp/Impl/Utilities/CSharpParseOptionsChangingService.cs +++ b/src/VisualStudio/CSharp/Impl/Utilities/CSharpParseOptionsChangingService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.LanguageServices.Utilities; -using Roslyn.Utilities; using VSLangProj80; namespace Microsoft.VisualStudio.LanguageServices.CSharp.Utilities diff --git a/src/VisualStudio/CSharp/Test/CallHierarchy/CSharpCallHierarchyTests.cs b/src/VisualStudio/CSharp/Test/CallHierarchy/CSharpCallHierarchyTests.cs index e1b1bf3a44b7b..7eb87cd5d2e96 100644 --- a/src/VisualStudio/CSharp/Test/CallHierarchy/CSharpCallHierarchyTests.cs +++ b/src/VisualStudio/CSharp/Test/CallHierarchy/CSharpCallHierarchyTests.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.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.CallHierarchy; @@ -11,505 +9,545 @@ using Roslyn.Test.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CallHierarchy -{ - [UseExportProvider] - [Trait(Traits.Feature, Traits.Features.CallHierarchy)] - public class CSharpCallHierarchyTests - { - [WpfFact] - public async Task InvokeOnMethod() - { - var text = @" -namespace N -{ - class C - { - void G$$oo() - { - } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()"); - } - - [WpfFact] - public async Task InvokeOnProperty() - { - var text = @" -namespace N -{ - class C - { - public int G$$oo { get; set;} - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo"); - } - - [WpfFact] - public async Task InvokeOnEvent() - { - var text = @" -using System; -namespace N -{ - class C - { - public event EventHandler Go$$o; - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo"); - } - - [WpfFact] - public async Task Method_FindCalls() - { - var text = @" -namespace N -{ - class C - { - void G$$oo() - { - } - } +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CallHierarchy; - class G - { - void Main() - { - var c = new C(); - c.Goo(); - } - - void Main2() - { - var c = new C(); - c.Goo(); - } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main()", "N.G.Main2()"]); - } - - [WpfFact] - public async Task Method_InterfaceImplementation() - { - var text = @" -namespace N +[UseExportProvider] +[Trait(Traits.Feature, Traits.Features.CallHierarchy)] +public sealed class CSharpCallHierarchyTests { - interface I + [WpfFact] + public async Task InvokeOnMethod() { - void Goo(); + var text = """ + namespace N + { + class C + { + void G$$oo() + { + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()"); } - class C : I + [WpfFact] + public async Task InvokeOnProperty() { - public void G$$oo() - { - } + var text = """ + namespace N + { + class C + { + public int G$$oo { get; set;} + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo"); } - class G + [WpfFact] + public async Task InvokeOnEvent() { - void Main() - { - I c = new C(); - c.Goo(); - } - - void Main2() - { - var c = new C(); - c.Goo(); - } + var text = """ + using System; + namespace N + { + class C + { + public event EventHandler Go$$o; + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo"); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Calls_To_Interface_Implementation_0, "N.I.Goo()")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main2()"]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_Interface_Implementation_0, "N.I.Goo()"), ["N.G.Main()"]); - } - - [WpfFact] - public async Task Method_CallToOverride() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task Method_FindCalls() { - protected virtual void G$$oo() { } + var text = """ + namespace N + { + class C + { + void G$$oo() + { + } + } + + class G + { + void Main() + { + var c = new C(); + c.Goo(); + } + + void Main2() + { + var c = new C(); + c.Goo(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main()", "N.G.Main2()"]); } - class D : C + [WpfFact] + public async Task Method_InterfaceImplementation() { - protected override void Goo() { } - - void Bar() - { - C c; - c.Goo() - } - - void Baz() - { - D d; - d.Goo(); - } + var text = """ + namespace N + { + interface I + { + void Goo(); + } + + class C : I + { + public void G$$oo() + { + } + } + + class G + { + void Main() + { + I c = new C(); + c.Goo(); + } + + void Main2() + { + var c = new C(); + c.Goo(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Calls_To_Interface_Implementation_0, "N.I.Goo()")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main2()"]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_Interface_Implementation_0, "N.I.Goo()"), ["N.G.Main()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), EditorFeaturesResources.Calls_To_Overrides]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.D.Bar()"]); - testState.VerifyResult(root, EditorFeaturesResources.Calls_To_Overrides, ["N.D.Baz()"]); - } - - [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/829705")] - public async Task Method_CallToBase() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task Method_CallToOverride() { - protected virtual void Goo() { } + var text = """ + namespace N + { + class C + { + protected virtual void G$$oo() { } + } + + class D : C + { + protected override void Goo() { } + + void Bar() + { + C c; + c.Goo() + } + + void Baz() + { + D d; + d.Goo(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), EditorFeaturesResources.Calls_To_Overrides]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.D.Bar()"]); + testState.VerifyResult(root, EditorFeaturesResources.Calls_To_Overrides, ["N.D.Baz()"]); } - class D : C + [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/829705")] + public async Task Method_CallToBase() { - protected override void Goo() { } - - void Bar() - { - C c; - c.Goo() - } - - void Baz() - { - D d; - d.Go$$o(); - } + var text = """ + namespace N + { + class C + { + protected virtual void Goo() { } + } + + class D : C + { + protected override void Goo() { } + + void Bar() + { + C c; + c.Goo() + } + + void Baz() + { + D d; + d.Go$$o(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.D.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Calls_To_Base_Member_0, "N.C.Goo()")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.D.Baz()"]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_Base_Member_0, "N.C.Goo()"), ["N.D.Bar()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.D.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Calls_To_Base_Member_0, "N.C.Goo()")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.D.Baz()"]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_Base_Member_0, "N.C.Goo()"), ["N.D.Bar()"]); - } - - [WpfFact] - public async Task FieldInitializers() - { - var text = @" -namespace N -{ - class C - { - public int goo = Goo(); - protected int Goo$$() { return 0; } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo")]); - testState.VerifyResultName(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), [EditorFeaturesResources.Initializers]); - } - - [WpfFact] - public async Task FieldReferences() - { - var text = @" -namespace N -{ - class C + [WpfFact] + public async Task FieldInitializers() { - public int g$$oo; + var text = """ + namespace N + { + class C + { + public int goo = Goo(); - protected void Goo() { goo = 3; } + protected int Goo$$() { return 0; } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo")]); + testState.VerifyResultName(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), [EditorFeaturesResources.Initializers]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.goo", [string.Format(EditorFeaturesResources.References_To_Field_0, "goo")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.References_To_Field_0, "goo"), ["N.C.Goo()"]); - } - - [WpfFact] - public async Task PropertyGet() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task FieldReferences() { - public int val - { - g$$et + var text = """ + namespace N { - return 0; - } - } + class C + { + public int g$$oo; - public int goo() - { - var x = this.val; - } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.val.get", [string.Format(EditorFeaturesResources.Calls_To_0, "get_val")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "get_val"), ["N.C.goo()"]); - } - - [WpfFact] - public async Task Generic() - { - var text = @" -namespace N -{ - class C - { - public int gen$$eric(this string generic, ref T stuff) - { - return 0; - } - - public int goo() - { - int i; - generic("", ref i); - } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.generic(this string, ref T)", [string.Format(EditorFeaturesResources.Calls_To_0, "generic")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "generic"), ["N.C.goo()"]); - } - - [WpfFact] - public async Task ExtensionMethods() - { - var text = @" -namespace ConsoleApplication10 -{ - class Program - { - static void Main(string[] args) - { - var x = ""string""; - x.BarStr$$ing(); - } - } - - public static class Extensions - { - public static string BarString(this string s) - { - return s; - } + protected void Goo() { goo = 3; } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.goo", [string.Format(EditorFeaturesResources.References_To_Field_0, "goo")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.References_To_Field_0, "goo"), ["N.C.Goo()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "ConsoleApplication10.Extensions.BarString(this string)", [string.Format(EditorFeaturesResources.Calls_To_0, "BarString")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "BarString"), ["ConsoleApplication10.Program.Main(string[])"]); - } - - [WpfFact] - public async Task GenericExtensionMethods() - { - var text = @" -using System.Collections.Generic; -using System.Linq; -namespace N -{ - class Program + + [WpfFact] + public async Task PropertyGet() { - static void Main(string[] args) - { - List x = new List(); - var z = x.Si$$ngle(); - } + var text = """ + namespace N + { + class C + { + public int val + { + g$$et + { + return 0; + } + } + + public int goo() + { + var x = this.val; + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.val.get", [string.Format(EditorFeaturesResources.Calls_To_0, "get_val")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "get_val"), ["N.C.goo()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "System.Linq.Enumerable.Single(this System.Collections.Generic.IEnumerable)", [string.Format(EditorFeaturesResources.Calls_To_0, "Single")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Single"), ["N.Program.Main(string[])"]); - } - - [WpfFact] - public async Task InterfaceImplementors() - { - var text = @" -namespace N -{ - interface I + + [WpfFact] + public async Task Generic() { - void Go$$o(); + var text = """ + namespace N + { + class C + { + public int gen$$eric(this string generic, ref T stuff) + { + return 0; + } + + public int goo() + { + int i; + generic(", ref i); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.generic(this string, ref T)", [string.Format(EditorFeaturesResources.Calls_To_0, "generic")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "generic"), ["N.C.goo()"]); } - class C : I + [WpfFact] + public async Task ExtensionMethods() { - public void Goo() - { - } + var text = """ + namespace ConsoleApplication10 + { + class Program + { + static void Main(string[] args) + { + var x = "string"; + x.BarStr$$ing(); + } + } + + public static class Extensions + { + public static string BarString(this string s) + { + return s; + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "ConsoleApplication10.Extensions.BarString(this string)", [string.Format(EditorFeaturesResources.Calls_To_0, "BarString")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "BarString"), ["ConsoleApplication10.Program.Main(string[])"]); } - class G + [WpfFact] + public async Task GenericExtensionMethods() { - void Main() - { - I c = new C(); - c.Goo(); - } - - void Main2() - { - var c = new C(); - c.Goo(); - } + var text = """ + using System.Collections.Generic; + using System.Linq; + namespace N + { + class Program + { + static void Main(string[] args) + { + List x = new List(); + var z = x.Si$$ngle(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "System.Linq.Enumerable.Single(this System.Collections.Generic.IEnumerable)", [string.Format(EditorFeaturesResources.Calls_To_0, "Single")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Single"), ["N.Program.Main(string[])"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.I.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Implements_0, "Goo")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main()"]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Implements_0, "Goo"), ["N.C.Goo()"]); - } - - [WpfFact] - public async Task NoFindOverridesOnSealedMethod() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task InterfaceImplementors() { - void G$$oo() - { - } + var text = """ + namespace N + { + interface I + { + void Go$$o(); + } + + class C : I + { + public void Goo() + { + } + } + + class G + { + void Main() + { + I c = new C(); + c.Goo(); + } + + void Main2() + { + var c = new C(); + c.Goo(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.I.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), string.Format(EditorFeaturesResources.Implements_0, "Goo")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), ["N.G.Main()"]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Implements_0, "Goo"), ["N.C.Goo()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - Assert.DoesNotContain("Overrides", root.SupportedSearchCategories.Select(s => s.DisplayName)); - } - - [WpfFact] - public async Task FindOverrides() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task NoFindOverridesOnSealedMethod() { - public virtual void G$$oo() - { - } + var text = """ + namespace N + { + class C + { + void G$$oo() + { + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + Assert.DoesNotContain("Overrides", root.SupportedSearchCategories.Select(s => s.DisplayName)); } - class G : C + [WpfFact] + public async Task FindOverrides() { - public override void Goo() - { - } + var text = """ + namespace N + { + class C + { + public virtual void G$$oo() + { + } + } + + class G : C + { + public override void Goo() + { + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), EditorFeaturesResources.Overrides_]); + testState.VerifyResult(root, EditorFeaturesResources.Overrides_, ["N.G.Goo()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), EditorFeaturesResources.Overrides_]); - testState.VerifyResult(root, EditorFeaturesResources.Overrides_, ["N.G.Goo()"]); - } - - [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/844613")] - public async Task AbstractMethodInclusionToOverrides() - { - var text = @" -using System; - -abstract class Base -{ - public abstract void $$M(); -} - -class Derived : Base -{ - public override void M() + + [WpfFact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/844613")] + public async Task AbstractMethodInclusionToOverrides() { - throw new NotImplementedException(); + var text = """ + using System; + + abstract class Base + { + public abstract void $$M(); + } + + class Derived : Base + { + public override void M() + { + throw new NotImplementedException(); + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "Base.M()", [string.Format(EditorFeaturesResources.Calls_To_0, "M"), EditorFeaturesResources.Overrides_, EditorFeaturesResources.Calls_To_Overrides]); + testState.VerifyResult(root, EditorFeaturesResources.Overrides_, ["Derived.M()"]); } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "Base.M()", [string.Format(EditorFeaturesResources.Calls_To_0, "M"), EditorFeaturesResources.Overrides_, EditorFeaturesResources.Calls_To_Overrides]); - testState.VerifyResult(root, EditorFeaturesResources.Overrides_, ["Derived.M()"]); - } - - [WpfFact] - public async Task SearchAfterEditWorks() - { - var text = @" -namespace N -{ - class C + + [WpfFact] + public async Task SearchAfterEditWorks() { - void G$$oo() - { - } - - void M() - { - Goo(); - } - } -}"; - using var testState = CallHierarchyTestState.Create(text); - var root = await testState.GetRootAsync(); + var text = """ + namespace N + { + class C + { + void G$$oo() + { + } + + void M() + { + Goo(); + } + } + } + """; + using var testState = CallHierarchyTestState.Create(text); + var root = await testState.GetRootAsync(); - testState.Workspace.Documents.Single().GetTextBuffer().Insert(0, "/* hello */"); + testState.Workspace.Documents.Single().GetTextBuffer().Insert(0, "/* hello */"); - testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"),]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), expectedCallers: ["N.C.M()"]); - } + testState.VerifyRoot(root, "N.C.Goo()", [string.Format(EditorFeaturesResources.Calls_To_0, "Goo"),]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "Goo"), expectedCallers: ["N.C.M()"]); + } - [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57856")] - public async Task PropertySet() - { - var code = @" -namespace N -{ - class C + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/57856")] + public async Task PropertySet() { - public int Property { get; s$$et; } - void M() - { - Property = 2; - } + var code = """ + namespace N + { + class C + { + public int Property { get; s$$et; } + void M() + { + Property = 2; + } + } + } + """; + using var testState = CallHierarchyTestState.Create(code); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "N.C.Property.set", [string.Format(EditorFeaturesResources.Calls_To_0, "set_Property")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "set_Property"), ["N.C.M()"]); } -}"; - using var testState = CallHierarchyTestState.Create(code); - var root = await testState.GetRootAsync(); - testState.VerifyRoot(root, "N.C.Property.set", [string.Format(EditorFeaturesResources.Calls_To_0, "set_Property")]); - testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, "set_Property"), ["N.C.M()"]); - } + + [WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/77327")] + public async Task PrimaryConstructor() + { + var code = """ + public class $$Class1(string test) + { + } + + class D + { + public void M() + { + var c = new Class1("test"); + } + } + """; + using var testState = CallHierarchyTestState.Create(code); + var root = await testState.GetRootAsync(); + testState.VerifyRoot(root, "Class1.Class1(string)", [string.Format(EditorFeaturesResources.Calls_To_0, ".ctor")]); + testState.VerifyResult(root, string.Format(EditorFeaturesResources.Calls_To_0, ".ctor"), ["D.M()"]); } } diff --git a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyCommandHandler.cs b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyCommandHandler.cs index 826fbb6f7b468..8ae227be08710 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyCommandHandler.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyCommandHandler.cs @@ -2,23 +2,20 @@ // 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.ComponentModel.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Host; -using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.SymbolMapping; +using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Commanding; using Microsoft.VisualStudio.LanguageServices; +using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor.Commanding.Commands; using Microsoft.VisualStudio.Utilities; using Roslyn.Utilities; @@ -30,81 +27,66 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.CallHierarchy; [ContentType(ContentTypeNames.VisualBasicContentType)] [Name("CallHierarchy")] [Order(After = PredefinedCommandHandlerNames.DocumentationComments)] -internal class CallHierarchyCommandHandler : ICommandHandler +[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 CallHierarchyCommandHandler( + IThreadingContext threadingContext, + IUIThreadOperationExecutor threadOperationExecutor, + IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, + [ImportMany] IEnumerable presenters, + CallHierarchyProvider provider) : ICommandHandler { - private readonly IThreadingContext _threadingContext; - private readonly IUIThreadOperationExecutor _threadOperationExecutor; - private readonly IAsynchronousOperationListener _listener; - private readonly ICallHierarchyPresenter _presenter; - private readonly CallHierarchyProvider _provider; + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly IUIThreadOperationExecutor _threadOperationExecutor = threadOperationExecutor; + private readonly IAsynchronousOperationListener _listener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.CallHierarchy); + private readonly ICallHierarchyPresenter _presenter = presenters.FirstOrDefault(); + private readonly CallHierarchyProvider _provider = provider; - public string DisplayName => EditorFeaturesResources.Call_Hierarchy; + public string DisplayName + => EditorFeaturesResources.Call_Hierarchy; - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public CallHierarchyCommandHandler( - IThreadingContext threadingContext, - IUIThreadOperationExecutor threadOperationExecutor, - IAsynchronousOperationListenerProvider asynchronousOperationListenerProvider, - [ImportMany] IEnumerable presenters, - CallHierarchyProvider provider) - { - _threadingContext = threadingContext; - _threadOperationExecutor = threadOperationExecutor; - _listener = asynchronousOperationListenerProvider.GetListener(FeatureAttribute.CallHierarchy); - _presenter = presenters.FirstOrDefault(); - _provider = provider; - } + public CommandState GetCommandState(ViewCallHierarchyCommandArgs args) + => CommandState.Available; public bool ExecuteCommand(ViewCallHierarchyCommandArgs args, CommandExecutionContext context) { + var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges(); + if (document == null) + return false; + + var point = args.TextView.Caret.Position.Point.GetPoint(args.SubjectBuffer, PositionAffinity.Predecessor); + if (point is null) + return false; + // We're showing our own UI, ensure the editor doesn't show anything itself. context.OperationContext.TakeOwnership(); var token = _listener.BeginAsyncOperation(nameof(ExecuteCommand)); - ExecuteCommandAsync(args, context) + ExecuteCommandAsync(document, point.Value.Position) .ReportNonFatalErrorAsync() .CompletesAsyncOperation(token); return true; } - private async Task ExecuteCommandAsync(ViewCallHierarchyCommandArgs args, CommandExecutionContext commandExecutionContext) + private async Task ExecuteCommandAsync(Document document, int caretPosition) { - Document document; - using (var context = _threadOperationExecutor.BeginExecute( EditorFeaturesResources.Call_Hierarchy, ServicesVSResources.Navigating, allowCancellation: true, showProgress: false)) { - document = await args.SubjectBuffer.CurrentSnapshot.GetFullyLoadedOpenDocumentInCurrentContextWithChangesAsync( - commandExecutionContext.OperationContext).ConfigureAwait(true); - if (document == null) - { - return; - } - - var caretPosition = args.TextView.Caret.Position.BufferPosition.Position; var cancellationToken = context.UserCancellationToken; - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var symbolUnderCaret = await SymbolFinder.FindSymbolAtPositionAsync( - semanticModel, caretPosition, document.Project.Solution.Services, cancellationToken).ConfigureAwait(false); + var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync( + document, caretPosition, preferPrimaryConstructor: true, cancellationToken).ConfigureAwait(false); - if (symbolUnderCaret != null) + if (symbolAndProject is (var symbol, var project)) { - // Map symbols so that Call Hierarchy works from metadata-as-source - var mappingService = document.Project.Solution.Services.GetService(); - var mapping = await mappingService.MapSymbolAsync(document, symbolUnderCaret, cancellationToken).ConfigureAwait(false); + var node = await _provider.CreateItemAsync(symbol, project, callsites: [], cancellationToken).ConfigureAwait(false); - if (mapping.Symbol != null) + if (node != null) { - var node = await _provider.CreateItemAsync(mapping.Symbol, mapping.Project, [], cancellationToken).ConfigureAwait(false); - - if (node != null) - { - await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - _presenter.PresentRoot((CallHierarchyItem)node); - return; - } + await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + _presenter.PresentRoot(node); + return; } } @@ -112,10 +94,7 @@ private async Task ExecuteCommandAsync(ViewCallHierarchyCommandArgs args, Comman await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); } - var notificationService = document.Project.Solution.Services.GetService(); + var notificationService = document.Project.Solution.Services.GetRequiredService(); notificationService.SendNotification(EditorFeaturesResources.Cursor_must_be_on_a_member_name, severity: NotificationSeverity.Information); } - - public CommandState GetCommandState(ViewCallHierarchyCommandArgs args) - => CommandState.Available; } diff --git a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyItem.cs b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyItem.cs index 24be31df5a6a3..69e54e1e0a570 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyItem.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyItem.cs @@ -19,12 +19,12 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.CallHierarchy; -internal class CallHierarchyItem : ICallHierarchyMemberItem +internal sealed class CallHierarchyItem : ICallHierarchyMemberItem { private readonly Workspace _workspace; private readonly INavigableLocation _navigableLocation; - private readonly IEnumerable _callsites; - private readonly IEnumerable _finders; + private readonly ImmutableArray _callsites; + private readonly ImmutableArray _finders; private readonly Func _glyphCreator; private readonly CallHierarchyProvider _provider; @@ -32,7 +32,7 @@ public CallHierarchyItem( CallHierarchyProvider provider, ISymbol symbol, INavigableLocation navigableLocation, - IEnumerable finders, + ImmutableArray finders, Func glyphCreator, ImmutableArray callsites, Project project) diff --git a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyProvider.cs b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyProvider.cs index 26041774a48bb..13177bb5ee2ba 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyProvider.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/CallHierarchyProvider.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.VisualStudio.Language.CallHierarchy; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.Utilities; @@ -52,7 +51,7 @@ public CallHierarchyProvider( _streamingPresenter = streamingPresenter; } - public async Task CreateItemAsync( + public async Task CreateItemAsync( ISymbol symbol, Project project, ImmutableArray callsites, CancellationToken cancellationToken) { if (symbol.Kind is SymbolKind.Method or @@ -65,7 +64,7 @@ SymbolKind.Event or var finders = await CreateFindersAsync(symbol, project, cancellationToken).ConfigureAwait(false); var location = await GoToDefinitionHelpers.GetDefinitionLocationAsync( symbol, project.Solution, this.ThreadingContext, _streamingPresenter.Value, cancellationToken).ConfigureAwait(false); - ICallHierarchyMemberItem item = new CallHierarchyItem( + return new CallHierarchyItem( this, symbol, location, @@ -73,8 +72,6 @@ SymbolKind.Event or () => symbol.GetGlyph().GetImageSource(GlyphService), callsites, project); - - return item; } return null; @@ -100,11 +97,11 @@ public FieldInitializerItem CreateInitializerItem(IEnumerable> CreateFindersAsync(ISymbol symbol, Project project, CancellationToken cancellationToken) + public async Task> CreateFindersAsync(ISymbol symbol, Project project, CancellationToken cancellationToken) { if (symbol.Kind is SymbolKind.Property or - SymbolKind.Event or - SymbolKind.Method) + SymbolKind.Event or + SymbolKind.Method) { var finders = new List { @@ -138,14 +135,12 @@ SymbolKind.Event or finders.Add(new ImplementerFinder(symbol, project.Id, AsyncListener, this)); } - return finders; + return finders.ToImmutableArray(); } if (symbol.Kind == SymbolKind.Field) - { return [new FieldReferenceFinder(symbol, project.Id, AsyncListener, this)]; - } - return null; + return []; } } diff --git a/src/VisualStudio/Core/Def/CallHierarchy/FieldInitializerItem.cs b/src/VisualStudio/Core/Def/CallHierarchy/FieldInitializerItem.cs index d05deb2230de3..4c7be3ec97382 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/FieldInitializerItem.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/FieldInitializerItem.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.Collections.Generic; using System.Windows.Media; using Microsoft.VisualStudio.Language.CallHierarchy; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/BaseMemberFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/BaseMemberFinder.cs index b3390745f91ef..1e5116b26bb87 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/BaseMemberFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/BaseMemberFinder.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/CallToOverrideFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/CallToOverrideFinder.cs index 1f0b59391d4a4..87944cc297c2b 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/CallToOverrideFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/CallToOverrideFinder.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.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/FieldReferenceFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/FieldReferenceFinder.cs index 41dd82913cfc0..5cf04ac1a6649 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/FieldReferenceFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/FieldReferenceFinder.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/ImplementerFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/ImplementerFinder.cs index 7713dbaa000d3..2655580251f4e 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/ImplementerFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/ImplementerFinder.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.Generic; using System.Collections.Immutable; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/InterfaceImplementationCallFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/InterfaceImplementationCallFinder.cs index e69f4ce718296..91e58184bf69e 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/InterfaceImplementationCallFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/InterfaceImplementationCallFinder.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/MethodCallFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/MethodCallFinder.cs index 258fa2bd88cf0..0815365e85099 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/MethodCallFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/MethodCallFinder.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/Finders/OverridingMemberFinder.cs b/src/VisualStudio/Core/Def/CallHierarchy/Finders/OverridingMemberFinder.cs index 5744cdbf8fc6a..d18a2ab43d692 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/Finders/OverridingMemberFinder.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/Finders/OverridingMemberFinder.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.Generic; using System.Collections.Immutable; diff --git a/src/VisualStudio/Core/Def/CallHierarchy/ICallHierarchyPresenter.cs b/src/VisualStudio/Core/Def/CallHierarchy/ICallHierarchyPresenter.cs index 37a9af768bb48..b0843a2a19eca 100644 --- a/src/VisualStudio/Core/Def/CallHierarchy/ICallHierarchyPresenter.cs +++ b/src/VisualStudio/Core/Def/CallHierarchy/ICallHierarchyPresenter.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 Microsoft.CodeAnalysis.Editor.Implementation.CallHierarchy; namespace Microsoft.CodeAnalysis.Editor.Host; diff --git a/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs b/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs index 073e8e25ae3a8..9ab08d0a04201 100644 --- a/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs +++ b/src/VisualStudio/Core/Def/ChangeSignature/ChangeSignatureDialogViewModel.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Notification; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.Text.Classification; using Roslyn.Utilities; diff --git a/src/VisualStudio/Core/Def/CodeLens/RemoteCodeLensReferencesService.cs b/src/VisualStudio/Core/Def/CodeLens/RemoteCodeLensReferencesService.cs index 702de2078b64f..222a13c2d8a2b 100644 --- a/src/VisualStudio/Core/Def/CodeLens/RemoteCodeLensReferencesService.cs +++ b/src/VisualStudio/Core/Def/CodeLens/RemoteCodeLensReferencesService.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.CodeLens; @@ -123,7 +122,7 @@ public ValueTask GetProjectCodeLensVersionAsync(Solution solution, var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false); if (client != null) { - var result = await client.TryInvokeAsync( + var result = await client.TryInvokeAsync( solution, (service, solutionInfo, cancellationToken) => service.GetFullyQualifiedNameAsync(solutionInfo, documentId, syntaxNode.Span, cancellationToken), cancellationToken).ConfigureAwait(false); diff --git a/src/VisualStudio/Core/Def/CodeModel/ICodeModelInstanceFactory.cs b/src/VisualStudio/Core/Def/CodeModel/ICodeModelInstanceFactory.cs index 224fc3c9d407b..bba5338ef6fec 100644 --- a/src/VisualStudio/Core/Def/CodeModel/ICodeModelInstanceFactory.cs +++ b/src/VisualStudio/Core/Def/CodeModel/ICodeModelInstanceFactory.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 - namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; internal interface ICodeModelInstanceFactory diff --git a/src/VisualStudio/Core/Def/CodeModel/IProjectCodeModelFactory.cs b/src/VisualStudio/Core/Def/CodeModel/IProjectCodeModelFactory.cs index e9c45b0331bd4..aa0e0a470fb96 100644 --- a/src/VisualStudio/Core/Def/CodeModel/IProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Def/CodeModel/IProjectCodeModelFactory.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; diff --git a/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.ColorSchemeReader.cs b/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.ColorSchemeReader.cs index 45b025871bcf4..77fd8274475aa 100644 --- a/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.ColorSchemeReader.cs +++ b/src/VisualStudio/Core/Def/ColorSchemes/ColorSchemeApplier.ColorSchemeReader.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; diff --git a/src/VisualStudio/Core/Def/CommonControls/MemberSelection.xaml.cs b/src/VisualStudio/Core/Def/CommonControls/MemberSelection.xaml.cs index 0e845726cd3dc..964108e5900af 100644 --- a/src/VisualStudio/Core/Def/CommonControls/MemberSelection.xaml.cs +++ b/src/VisualStudio/Core/Def/CommonControls/MemberSelection.xaml.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.Windows; using System.Windows.Controls; diff --git a/src/VisualStudio/Core/Def/CommonControls/MemberSelectionViewModel.cs b/src/VisualStudio/Core/Def/CommonControls/MemberSelectionViewModel.cs index 2b4039844ae43..21f6721a1b09c 100644 --- a/src/VisualStudio/Core/Def/CommonControls/MemberSelectionViewModel.cs +++ b/src/VisualStudio/Core/Def/CommonControls/MemberSelectionViewModel.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.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; @@ -21,13 +19,13 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CommonControls; internal class MemberSelectionViewModel : AbstractNotifyPropertyChanged { private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor; - private readonly ImmutableDictionary>> _symbolToDependentsMap; + private readonly ImmutableDictionary>>? _symbolToDependentsMap; private readonly ImmutableDictionary _symbolToMemberViewMap; public MemberSelectionViewModel( IUIThreadOperationExecutor uiThreadOperationExecutor, ImmutableArray members, - ImmutableDictionary>> dependentsMap, + ImmutableDictionary>>? dependentsMap, TypeKind destinationTypeKind = TypeKind.Class, bool showDependentsButton = true, bool showPublicButton = true) @@ -115,9 +113,10 @@ public void SelectDependents() showProgress: true, context => { - foreach (var member in Members) + if (_symbolToDependentsMap != null) { - _symbolToDependentsMap[member.Symbol].Wait(context.UserCancellationToken); + foreach (var member in Members) + _symbolToDependentsMap[member.Symbol].Wait(context.UserCancellationToken); } }); @@ -180,11 +179,12 @@ private ImmutableHashSet FindDependentsRecursively(ISymbol member) var currentMember = queue.Dequeue(); result.Add(currentMember); visited.Add(currentMember); - foreach (var dependent in _symbolToDependentsMap[currentMember].Result) + if (_symbolToDependentsMap != null) { - if (!visited.Contains(dependent)) + foreach (var dependent in _symbolToDependentsMap[currentMember].Result) { - queue.Enqueue(dependent); + if (!visited.Contains(dependent)) + queue.Enqueue(dependent); } } } diff --git a/src/VisualStudio/Core/Def/CommonControls/NewTypeDestinationSelection.xaml.cs b/src/VisualStudio/Core/Def/CommonControls/NewTypeDestinationSelection.xaml.cs index 64af5906493e6..ea7a67e166fe4 100644 --- a/src/VisualStudio/Core/Def/CommonControls/NewTypeDestinationSelection.xaml.cs +++ b/src/VisualStudio/Core/Def/CommonControls/NewTypeDestinationSelection.xaml.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.Windows; using System.Windows.Controls; using System.Windows.Input; diff --git a/src/VisualStudio/Core/Def/DebuggerIntelliSense/AbstractDebuggerIntelliSenseContext.cs b/src/VisualStudio/Core/Def/DebuggerIntelliSense/AbstractDebuggerIntelliSenseContext.cs index 9459a5d3ecff1..ec77450e5b849 100644 --- a/src/VisualStudio/Core/Def/DebuggerIntelliSense/AbstractDebuggerIntelliSenseContext.cs +++ b/src/VisualStudio/Core/Def/DebuggerIntelliSense/AbstractDebuggerIntelliSenseContext.cs @@ -92,11 +92,7 @@ protected AbstractDebuggerIntelliSenseContext( public abstract bool CompletionStartsOnQuestionMark { get; } - protected abstract string StatementTerminator { get; } - - protected abstract int GetAdjustedContextPoint(int contextPoint, Document document); - - protected abstract ITrackingSpan GetPreviousStatementBufferAndSpan(int lastTokenEndPoint, Document document); + protected abstract IProjectionBuffer GetAdjustedBuffer(int contextPoint, Document document, ITrackingSpan debuggerMappedSpan); // Since the immediate window doesn't actually tell us when we change lines, we'll have to // determine ourselves when to rebuild our tracking spans to include only the last (input) @@ -152,21 +148,10 @@ private bool TrySetContext( regionEdit.Apply(); } - // Adjust the context point to ensure that the right information is in scope. - // For example, we may need to move the point to the end of the last statement in a method body - // in order to be able to access all local variables. var contextPoint = this.ContextBuffer.CurrentSnapshot.GetLineFromLineNumber(CurrentStatementSpan.iEndLine).Start + CurrentStatementSpan.iEndIndex; - var adjustedContextPoint = GetAdjustedContextPoint(contextPoint, document); - - // Get the previous span/text. We might have to insert another newline or something. - var previousStatementSpan = GetPreviousStatementBufferAndSpan(adjustedContextPoint, document); - - // Build the tracking span that includes the rest of the file - var restOfFileSpan = ContextBuffer.CurrentSnapshot.CreateTrackingSpanFromIndexToEnd(adjustedContextPoint, SpanTrackingMode.EdgePositive); - // Put it all into a projection buffer - _projectionBuffer = this.ProjectionBufferFactoryService.CreateProjectionBuffer(null, - [previousStatementSpan, debuggerMappedSpan, this.StatementTerminator, restOfFileSpan], ProjectionBufferOptions.None, ContentType); + // Get the adjusted buffer + _projectionBuffer = GetAdjustedBuffer(contextPoint, document, debuggerMappedSpan); // Fork the solution using this new primary buffer for the document and all of its linked documents. var forkedSolution = solution.WithDocumentText(document.Id, _projectionBuffer.CurrentSnapshot.AsText(), PreservationMode.PreserveIdentity); diff --git a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseHelpers.cs b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseHelpers.cs index 59f87e3ede631..d79f834673806 100644 --- a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseHelpers.cs +++ b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseHelpers.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 Microsoft.VisualStudio.Text; namespace Microsoft.VisualStudio.LanguageServices.Implementation.DebuggerIntelliSense; diff --git a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs index 2d5179e906f5f..8fd27b88f1d27 100644 --- a/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.cs +++ b/src/VisualStudio/Core/Def/DebuggerIntelliSense/DebuggerIntellisenseWorkspace.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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; diff --git a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Loader.cs b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Loader.cs index 19cb2fe3f95f6..c365e2881eca6 100644 --- a/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Loader.cs +++ b/src/VisualStudio/Core/Def/Diagnostics/VisualStudioDiagnosticAnalyzerProvider.Loader.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.Reflection; using Microsoft.CodeAnalysis; @@ -18,7 +16,7 @@ private sealed class Loader : IAnalyzerAssemblyLoader public Loader() { - _fallbackLoader = new DefaultAnalyzerAssemblyLoader(); + _fallbackLoader = new AnalyzerAssemblyLoader(); } public void AddDependencyLocation(string fullPath) diff --git a/src/VisualStudio/Core/Def/DocumentationComments/VisualStudioDocumentationProvider.cs b/src/VisualStudio/Core/Def/DocumentationComments/VisualStudioDocumentationProvider.cs index 1378403555140..3d01824e40fac 100644 --- a/src/VisualStudio/Core/Def/DocumentationComments/VisualStudioDocumentationProvider.cs +++ b/src/VisualStudio/Core/Def/DocumentationComments/VisualStudioDocumentationProvider.cs @@ -9,7 +9,6 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.DocumentationComments; diff --git a/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs b/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs index f6472a0b34bed..f37d10131c66c 100644 --- a/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs +++ b/src/VisualStudio/Core/Def/EditorConfigSettings/Common/EditorTextUpdater.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Editor; diff --git a/src/VisualStudio/Core/Def/Extensions/SnapshotSpanExtensions.cs b/src/VisualStudio/Core/Def/Extensions/SnapshotSpanExtensions.cs index fd1455dafe5dd..56d15b5166fba 100644 --- a/src/VisualStudio/Core/Def/Extensions/SnapshotSpanExtensions.cs +++ b/src/VisualStudio/Core/Def/Extensions/SnapshotSpanExtensions.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 Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.VisualStudio.Text; using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; diff --git a/src/VisualStudio/Core/Def/Extensions/SourceTextExtensions.cs b/src/VisualStudio/Core/Def/Extensions/SourceTextExtensions.cs index a59a43bc5f594..2fa7365201364 100644 --- a/src/VisualStudio/Core/Def/Extensions/SourceTextExtensions.cs +++ b/src/VisualStudio/Core/Def/Extensions/SourceTextExtensions.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 Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; diff --git a/src/VisualStudio/Core/Def/Extensions/VirtualTreePointExtensions.cs b/src/VisualStudio/Core/Def/Extensions/VirtualTreePointExtensions.cs index a839bfa0c9307..a56262d8de8fc 100644 --- a/src/VisualStudio/Core/Def/Extensions/VirtualTreePointExtensions.cs +++ b/src/VisualStudio/Core/Def/Extensions/VirtualTreePointExtensions.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 Microsoft.CodeAnalysis.Editor.Shared.Utilities; using VsTextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan; diff --git a/src/VisualStudio/Core/Def/Extensions/VsTextSpanExtensions.cs b/src/VisualStudio/Core/Def/Extensions/VsTextSpanExtensions.cs index 925f35ee2919c..6d8f9d9361fc7 100644 --- a/src/VisualStudio/Core/Def/Extensions/VsTextSpanExtensions.cs +++ b/src/VisualStudio/Core/Def/Extensions/VsTextSpanExtensions.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs index 23540ec27030b..bdefef6dfa93f 100644 --- a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs +++ b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.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.Collections.Generic; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioSuppressionFixServiceAccessor.cs b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioSuppressionFixServiceAccessor.cs index 7d8742e8b352c..714fccbf89afc 100644 --- a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioSuppressionFixServiceAccessor.cs +++ b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/Api/ILegacyCodeAnalysisVisualStudioSuppressionFixServiceAccessor.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 Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.CodeAnalysis.ExternalAccess.LegacyCodeAnalysis.Api; diff --git a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs index eba7538fe3e9c..e1fab3941fb6d 100644 --- a/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.cs +++ b/src/VisualStudio/Core/Def/ExternalAccess/LegacyCodeAnalysis/LegacyCodeAnalysisVisualStudioDiagnosticAnalyzerServiceAccessor.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.Generic; using System.Composition; diff --git a/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptRemoteLanguageServiceWorkspaceAccessor.cs b/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptRemoteLanguageServiceWorkspaceAccessor.cs index 4da111c63a1d5..9dfa5292a698d 100644 --- a/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptRemoteLanguageServiceWorkspaceAccessor.cs +++ b/src/VisualStudio/Core/Def/ExternalAccess/VSTypeScript/Api/IVsTypeScriptRemoteLanguageServiceWorkspaceAccessor.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.ExternalAccess.VSTypeScript.Api; diff --git a/src/VisualStudio/Core/Def/ExtractClass/ExtractClassDialog.xaml.cs b/src/VisualStudio/Core/Def/ExtractClass/ExtractClassDialog.xaml.cs index e623674e74e18..1a0dd45ee7dc5 100644 --- a/src/VisualStudio/Core/Def/ExtractClass/ExtractClassDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/ExtractClass/ExtractClassDialog.xaml.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.Windows; using System.Windows.Input; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialog.xaml.cs b/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialog.xaml.cs index 294f36a2880c6..ada2aefe57d5c 100644 --- a/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialog.xaml.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.Windows; using System.Windows.Controls; using System.Windows.Input; diff --git a/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialogViewModel.cs b/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialogViewModel.cs index 3e9883fa74a87..7eb0025b1a22c 100644 --- a/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialogViewModel.cs +++ b/src/VisualStudio/Core/Def/ExtractInterface/ExtractInterfaceDialogViewModel.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -27,7 +25,7 @@ internal ExtractInterfaceDialogViewModel( IUIThreadOperationExecutor uiThreadOperationExecutor, INotificationService notificationService, string defaultInterfaceName, - List conflictingTypeNames, + ImmutableArray conflictingTypeNames, ImmutableArray memberViewModels, string defaultNamespace, string generatedNameTypeParameterSuffix, @@ -49,7 +47,7 @@ internal ExtractInterfaceDialogViewModel( languageName, defaultNamespace, generatedNameTypeParameterSuffix, - [.. conflictingTypeNames], + conflictingTypeNames, syntaxFactsService, canAddDocument); } diff --git a/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs b/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs index a5520a85ea979..697c26bc9a23d 100644 --- a/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs +++ b/src/VisualStudio/Core/Def/ExtractInterface/VisualStudioExtractInterfaceOptionsService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; @@ -26,7 +27,10 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ExtractInterfac [ExportWorkspaceService(typeof(IExtractInterfaceOptionsService), ServiceLayer.Host), Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class VisualStudioExtractInterfaceOptionsService(IGlyphService glyphService, IThreadingContext threadingContext, IUIThreadOperationExecutor uiThreadOperationExecutor) : IExtractInterfaceOptionsService +internal sealed class VisualStudioExtractInterfaceOptionsService( + IGlyphService glyphService, + IThreadingContext threadingContext, + IUIThreadOperationExecutor uiThreadOperationExecutor) : IExtractInterfaceOptionsService { private readonly IGlyphService _glyphService = glyphService; private readonly IThreadingContext _threadingContext = threadingContext; @@ -34,12 +38,11 @@ internal sealed class VisualStudioExtractInterfaceOptionsService(IGlyphService g public ExtractInterfaceOptionsResult GetExtractInterfaceOptions( Document document, - List extractableMembers, + ImmutableArray extractableMembers, string defaultInterfaceName, - List allTypeNames, + ImmutableArray allTypeNames, string defaultNamespace, - string generatedNameTypeParameterSuffix, - CancellationToken cancellationToken) + string generatedNameTypeParameterSuffix) { _threadingContext.ThrowIfNotOnUIThread(); var solution = document.Project.Solution; diff --git a/src/VisualStudio/Core/Def/F1Help/AbstractHelpContextService.cs b/src/VisualStudio/Core/Def/F1Help/AbstractHelpContextService.cs index 2fcaf8540ac7b..044d79d90e967 100644 --- a/src/VisualStudio/Core/Def/F1Help/AbstractHelpContextService.cs +++ b/src/VisualStudio/Core/Def/F1Help/AbstractHelpContextService.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -45,5 +43,5 @@ internal abstract class AbstractHelpContextService : IHelpContextService public abstract Task GetHelpTermAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken); - public abstract string FormatSymbol(ISymbol symbol); + public abstract string? FormatSymbol(ISymbol symbol); } diff --git a/src/VisualStudio/Core/Def/F1Help/IHelpContextService.cs b/src/VisualStudio/Core/Def/F1Help/IHelpContextService.cs index 1f3a8fa6e70ef..0eac1400cc821 100644 --- a/src/VisualStudio/Core/Def/F1Help/IHelpContextService.cs +++ b/src/VisualStudio/Core/Def/F1Help/IHelpContextService.cs @@ -17,5 +17,5 @@ internal interface IHelpContextService : ILanguageService Task GetHelpTermAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken); - string FormatSymbol(ISymbol symbol); + string? FormatSymbol(ISymbol symbol); } diff --git a/src/VisualStudio/Core/Def/FindReferences/ContainingMemberColumnDefinition.cs b/src/VisualStudio/Core/Def/FindReferences/ContainingMemberColumnDefinition.cs index 13a5443c42b87..e9a48eb11a722 100644 --- a/src/VisualStudio/Core/Def/FindReferences/ContainingMemberColumnDefinition.cs +++ b/src/VisualStudio/Core/Def/FindReferences/ContainingMemberColumnDefinition.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.ComponentModel.Composition; using Microsoft.CodeAnalysis.FindSymbols.Finders; diff --git a/src/VisualStudio/Core/Def/FindReferences/Entries/AbstractDocumentSpanEntry.cs b/src/VisualStudio/Core/Def/FindReferences/Entries/AbstractDocumentSpanEntry.cs index 69965dc0e8136..9dcf56e7cffe9 100644 --- a/src/VisualStudio/Core/Def/FindReferences/Entries/AbstractDocumentSpanEntry.cs +++ b/src/VisualStudio/Core/Def/FindReferences/Entries/AbstractDocumentSpanEntry.cs @@ -83,7 +83,7 @@ await documentNavigationService.TryNavigateToPositionAsync( var service = documentSpan.Document.DocumentServiceProvider.GetService(); if (service == null) { - return new MappedSpanResult(documentSpan.Document.FilePath, sourceText.Lines.GetLinePositionSpan(documentSpan.SourceSpan), documentSpan.SourceSpan); + return new MappedSpanResult(documentSpan.Document.FilePath!, sourceText.Lines.GetLinePositionSpan(documentSpan.SourceSpan), documentSpan.SourceSpan); } var results = await service.MapSpansAsync( @@ -91,7 +91,7 @@ await documentNavigationService.TryNavigateToPositionAsync( if (results.IsDefaultOrEmpty) { - return new MappedSpanResult(documentSpan.Document.FilePath, sourceText.Lines.GetLinePositionSpan(documentSpan.SourceSpan), documentSpan.SourceSpan); + return new MappedSpanResult(documentSpan.Document.FilePath!, sourceText.Lines.GetLinePositionSpan(documentSpan.SourceSpan), documentSpan.SourceSpan); } // if span mapping service filtered out the span, make sure diff --git a/src/VisualStudio/Core/Def/FindReferences/StreamingFindUsagesPresenter.cs b/src/VisualStudio/Core/Def/FindReferences/StreamingFindUsagesPresenter.cs index 69a9189727488..1361f431f020d 100644 --- a/src/VisualStudio/Core/Def/FindReferences/StreamingFindUsagesPresenter.cs +++ b/src/VisualStudio/Core/Def/FindReferences/StreamingFindUsagesPresenter.cs @@ -28,7 +28,6 @@ using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Text.Classification; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.FindUsages; diff --git a/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialog.xaml.cs b/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialog.xaml.cs index 6faa8ef9f0fcd..31d98aa3f07d4 100644 --- a/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/GenerateType/GenerateTypeDialog.xaml.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.Collections.Generic; using System.Windows; using System.Windows.Controls; diff --git a/src/VisualStudio/Core/Def/HACK_ThemeColorFixer.cs b/src/VisualStudio/Core/Def/HACK_ThemeColorFixer.cs index e91e1f328f051..4799e923de405 100644 --- a/src/VisualStudio/Core/Def/HACK_ThemeColorFixer.cs +++ b/src/VisualStudio/Core/Def/HACK_ThemeColorFixer.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.ObjectModel; using System.ComponentModel.Composition; diff --git a/src/VisualStudio/Core/Def/IAnalyzerNodeSetup.cs b/src/VisualStudio/Core/Def/IAnalyzerNodeSetup.cs index 3b659254e82f8..8d255e214a5e5 100644 --- a/src/VisualStudio/Core/Def/IAnalyzerNodeSetup.cs +++ b/src/VisualStudio/Core/Def/IAnalyzerNodeSetup.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; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell; diff --git a/src/VisualStudio/Core/Def/ID.CSharpCommands.cs b/src/VisualStudio/Core/Def/ID.CSharpCommands.cs index 1631078fa0112..662c709ad2dea 100644 --- a/src/VisualStudio/Core/Def/ID.CSharpCommands.cs +++ b/src/VisualStudio/Core/Def/ID.CSharpCommands.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 - namespace Microsoft.VisualStudio.LanguageServices; internal static partial class ID diff --git a/src/VisualStudio/Core/Def/IInvisibleEditor.cs b/src/VisualStudio/Core/Def/IInvisibleEditor.cs index 06a5cfa8f15de..09aa4ba17dec0 100644 --- a/src/VisualStudio/Core/Def/IInvisibleEditor.cs +++ b/src/VisualStudio/Core/Def/IInvisibleEditor.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 Microsoft.VisualStudio.Text; diff --git a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs index c0888cb0e04b6..c5168624c3e6d 100644 --- a/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/AbstractEditorFactory.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -25,22 +24,18 @@ using Microsoft.VisualStudio.TextManager.Interop; using Microsoft.VisualStudio.Utilities; using Microsoft.VisualStudio.WinForms.Interfaces; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; /// /// The base class of both the Roslyn editor factories. /// -internal abstract class AbstractEditorFactory : IVsEditorFactory, IVsEditorFactory4, IVsEditorFactoryNotify +internal abstract class AbstractEditorFactory(IComponentModel componentModel) : IVsEditorFactory, IVsEditorFactory4, IVsEditorFactoryNotify { - private readonly IComponentModel _componentModel; + private readonly IComponentModel _componentModel = componentModel; private Microsoft.VisualStudio.OLE.Interop.IServiceProvider? _oleServiceProvider; private bool _encoding; - protected AbstractEditorFactory(IComponentModel componentModel) - => _componentModel = componentModel; - protected abstract string ContentTypeName { get; } protected abstract string LanguageName { get; } diff --git a/src/VisualStudio/Core/Def/Implementation/ICodeModelNavigationPointService.cs b/src/VisualStudio/Core/Def/Implementation/ICodeModelNavigationPointService.cs index 9aaffb5d09402..1ca589511f72c 100644 --- a/src/VisualStudio/Core/Def/Implementation/ICodeModelNavigationPointService.cs +++ b/src/VisualStudio/Core/Def/Implementation/ICodeModelNavigationPointService.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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs b/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs index 5d14623779347..63fd2e3236b9a 100644 --- a/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs +++ b/src/VisualStudio/Core/Def/Implementation/VisualStudioUIContextActivationService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Shell; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; diff --git a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceGlyphFactory.cs b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceGlyphFactory.cs index 3529db24e4879..855b372ac16ee 100644 --- a/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceGlyphFactory.cs +++ b/src/VisualStudio/Core/Def/InheritanceMargin/InheritanceGlyphFactory.cs @@ -13,7 +13,6 @@ using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Formatting; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.InheritanceMargin; diff --git a/src/VisualStudio/Core/Def/Interactive/LogMessage.cs b/src/VisualStudio/Core/Def/Interactive/LogMessage.cs index 27eac0147b20a..5d66eb88594e0 100644 --- a/src/VisualStudio/Core/Def/Interactive/LogMessage.cs +++ b/src/VisualStudio/Core/Def/Interactive/LogMessage.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 - namespace Microsoft.VisualStudio.LanguageServices.Interactive; internal static class LogMessage diff --git a/src/VisualStudio/Core/Def/Interactive/VsUpdateSolutionEvents.cs b/src/VisualStudio/Core/Def/Interactive/VsUpdateSolutionEvents.cs index b9a4540eff33c..75b19605fb0b7 100644 --- a/src/VisualStudio/Core/Def/Interactive/VsUpdateSolutionEvents.cs +++ b/src/VisualStudio/Core/Def/Interactive/VsUpdateSolutionEvents.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.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Interop/Feedback.cs b/src/VisualStudio/Core/Def/Interop/Feedback.cs index 763d1e38ae3bb..2cdf47a14504d 100644 --- a/src/VisualStudio/Core/Def/Interop/Feedback.cs +++ b/src/VisualStudio/Core/Def/Interop/Feedback.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.Runtime.InteropServices; namespace Microsoft.VisualStudio.Feedback.Interop; diff --git a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs index 85206d8e1ab58..78c011159e310 100644 --- a/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs +++ b/src/VisualStudio/Core/Def/LanguageClient/LogHubLspLogger.cs @@ -37,6 +37,8 @@ public void Dispose() _configuration.Dispose(); } + public override IDisposable? CreateContext(string context) => null; + public override void LogDebug(string message, params object[] @params) { _traceSource.TraceEvent(TraceEventType.Verbose, id: 0, message); @@ -65,14 +67,4 @@ public override void LogException(Exception exception, string? message = null, p { _traceSource.TraceEvent(TraceEventType.Error, id: 0, "Exception: {0}", exception); } - - public override void LogStartContext(string message, params object[] @params) - { - _traceSource.TraceEvent(TraceEventType.Start, id: 0, message); - } - - public override void LogEndContext(string message, params object[] @params) - { - _traceSource.TraceEvent(TraceEventType.Stop, id: 0, message); - } } diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.IVsAutoOutliningClient.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.IVsAutoOutliningClient.cs index a85b4b8317d46..41b3ff62c5dd8 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.IVsAutoOutliningClient.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.IVsAutoOutliningClient.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 Microsoft.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.cs index dff463140016e..988564af7482b 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService.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; namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService; diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo2.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo2.cs index cb3078b2cc18c..37d4110b8178c 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo2.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.IVsLanguageDebugInfo2.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 Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs index e56b9e517e887..61e63b0fb5b06 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsCodeWindowManager.cs @@ -158,7 +158,7 @@ private void AddDropdownBar(IVsDropdownBarManager dropdownManager) var navigationBarClient = new NavigationBarClient(dropdownManager, _codeWindow, _languageService.SystemServiceProvider, _languageService.Workspace); var textBuffer = _languageService.EditorAdaptersFactoryService.GetDataBuffer(buffer); var controllerFactoryService = _languageService.Package.ComponentModel.GetService(); - var newController = controllerFactoryService.CreateController(navigationBarClient, textBuffer); + var newController = controllerFactoryService.CreateController(navigationBarClient, textBuffer!); var hr = dropdownManager.AddDropdownBar(cCombos: 3, pClient: navigationBarClient); if (ErrorHandler.Failed(hr)) diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs index abdf5313d6093..790628ecd98ad 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractLanguageService`2.VsLanguageDebugInfo.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text.Shared.Extensions; -using Microsoft.Internal.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.Utilities; @@ -166,13 +165,13 @@ public int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, i public int IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int iCol) => VSConstants.E_NOTIMPL; - public int ResolveName(string pszName, uint dwFlags, out IVsEnumDebugName? ppNames) + public int ResolveName(string? pszName, uint dwFlags, out IVsEnumDebugName? ppNames) { // In VS, this method frequently get's called with an empty string to test if the language service // supports this method (some language services, like F#, implement IVsLanguageDebugInfo but don't // implement this method). In that scenario, there's no sense doing work, so we'll just return // S_FALSE (as the old VB language service did). - if (string.IsNullOrEmpty(pszName)) + if (pszName is null or "") { ppNames = null; return VSConstants.S_FALSE; @@ -227,7 +226,7 @@ private async ValueTask CreateDebugNameAsync( if (mappedSpan != null) span = mappedSpan.Value; - return new VsDebugName(breakpoint.LocationNameOpt, filePath, span); + return new VsDebugName(breakpoint.LocationNameOpt, filePath!, span); } public int ValidateBreakpointLocation(IVsTextBuffer pBuffer, int iLine, int iCol, VsTextSpan[] pCodeSpan) diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs index d53c66478e03d..c1afbf754dc74 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs @@ -2,12 +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. -#nullable disable - using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Packaging; using Microsoft.CodeAnalysis.SymbolSearch; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; @@ -25,10 +24,11 @@ internal abstract partial class AbstractPackage : Ab where TPackage : AbstractPackage where TLanguageService : AbstractLanguageService { - private TLanguageService _languageService; + private TLanguageService? _languageService; - private PackageInstallerService _packageInstallerService; - private VisualStudioSymbolSearchService _symbolSearchService; + private PackageInstallerService? _packageInstallerService; + private VisualStudioSymbolSearchService? _symbolSearchService; + private IVsShell? _shell; protected AbstractPackage() { @@ -40,12 +40,14 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); - var shell = (IVsShell7)await GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); - var solution = (IVsSolution)await GetServiceAsync(typeof(SVsSolution)).ConfigureAwait(true); - cancellationToken.ThrowIfCancellationRequested(); + var shell = (IVsShell7?)await GetServiceAsync(typeof(SVsShell)).ConfigureAwait(true); + var solution = (IVsSolution?)await GetServiceAsync(typeof(SVsSolution)).ConfigureAwait(true); Assumes.Present(shell); Assumes.Present(solution); + _shell = (IVsShell?)shell; + Assumes.Present(_shell); + foreach (var editorFactory in CreateEditorFactories()) { RegisterEditorFactory(editorFactory); @@ -61,7 +63,7 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke _languageService = CreateLanguageService(); await _languageService.SetupAsync(cancellationToken).ConfigureAwait(false); - return _languageService.ComAggregate; + return _languageService.ComAggregate!; }); await shell.LoadPackageAsync(Guids.RoslynPackageId); @@ -69,11 +71,11 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke var miscellaneousFilesWorkspace = this.ComponentModel.GetService(); RegisterMiscellaneousFilesWorkspaceInformation(miscellaneousFilesWorkspace); - if (!IVsShellExtensions.IsInCommandLineMode(JoinableTaskFactory)) + if (!_shell.IsInCommandLineMode()) { // not every derived package support object browser and for those languages // this is a no op - await RegisterObjectBrowserLibraryManagerAsync(cancellationToken).ConfigureAwait(true); + RegisterObjectBrowserLibraryManager(); } LoadComponentsInUIContextOnceSolutionFullyLoadedAsync(cancellationToken).Forget(); @@ -117,9 +119,11 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (!IVsShellExtensions.IsInCommandLineMode(JoinableTaskFactory)) + // Per VS core team, Package.Dispose is called on the UI thread. + Contract.ThrowIfFalse(JoinableTaskFactory.Context.IsOnMainThread); + if (_shell != null && !_shell.IsInCommandLineMode()) { - JoinableTaskFactory.Run(async () => await UnregisterObjectBrowserLibraryManagerAsync(CancellationToken.None).ConfigureAwait(true)); + UnregisterObjectBrowserLibraryManager(); } // If we've created the language service then tell it it's time to clean itself up now. @@ -135,17 +139,15 @@ protected override void Dispose(bool disposing) protected abstract string RoslynLanguageName { get; } - protected virtual Task RegisterObjectBrowserLibraryManagerAsync(CancellationToken cancellationToken) + protected virtual void RegisterObjectBrowserLibraryManager() { // it is virtual rather than abstract to not break other languages which derived from our // base package implementations - return Task.CompletedTask; } - protected virtual Task UnregisterObjectBrowserLibraryManagerAsync(CancellationToken cancellationToken) + protected virtual void UnregisterObjectBrowserLibraryManager() { // it is virtual rather than abstract to not break other languages which derived from our // base package implementations - return Task.CompletedTask; } } diff --git a/src/VisualStudio/Core/Def/Library/AbstractLibraryManager.cs b/src/VisualStudio/Core/Def/Library/AbstractLibraryManager.cs index 1544d9983e218..2e05c7a1d69aa 100644 --- a/src/VisualStudio/Core/Def/Library/AbstractLibraryManager.cs +++ b/src/VisualStudio/Core/Def/Library/AbstractLibraryManager.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 Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.ComponentModelHost; diff --git a/src/VisualStudio/Core/Def/Library/AbstractLibraryManager_IOleCommandTarget.cs b/src/VisualStudio/Core/Def/Library/AbstractLibraryManager_IOleCommandTarget.cs index c66fa76e4e793..1343a9a07c0c4 100644 --- a/src/VisualStudio/Core/Def/Library/AbstractLibraryManager_IOleCommandTarget.cs +++ b/src/VisualStudio/Core/Def/Library/AbstractLibraryManager_IOleCommandTarget.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 Microsoft.VisualStudio.OLE.Interop; diff --git a/src/VisualStudio/Core/Def/Library/AbstractLibraryService.cs b/src/VisualStudio/Core/Def/Library/AbstractLibraryService.cs index 321f868ec4708..4183d6041f559 100644 --- a/src/VisualStudio/Core/Def/Library/AbstractLibraryService.cs +++ b/src/VisualStudio/Core/Def/Library/AbstractLibraryService.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 Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo; diff --git a/src/VisualStudio/Core/Def/Library/ILibraryService.cs b/src/VisualStudio/Core/Def/Library/ILibraryService.cs index f79d93b015ea3..0e21eb290941d 100644 --- a/src/VisualStudio/Core/Def/Library/ILibraryService.cs +++ b/src/VisualStudio/Core/Def/Library/ILibraryService.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 Microsoft.CodeAnalysis.Host; using Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractDescriptionBuilder.LinkFlags.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractDescriptionBuilder.LinkFlags.cs index f1841f69a59e9..9fc3118f44b4d 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractDescriptionBuilder.LinkFlags.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractDescriptionBuilder.LinkFlags.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; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Description.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Description.cs index dfce8ffdb2c53..59800af59ec80 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Description.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Description.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; using System.Threading.Tasks; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_ListItems.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_ListItems.cs index 171dcc085cbd9..f9bbbc26a14ec 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_ListItems.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_ListItems.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. -#nullable disable - -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Search.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Search.cs index 1e1398be7e774..f997206dc6ae1 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Search.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/AbstractObjectBrowserLibraryManager_Search.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Helpers.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Helpers.cs index 2e795297e017e..c8abc4011b95f 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Helpers.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Helpers.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.Diagnostics; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/FolderListItem.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/FolderListItem.cs index d5b6d53b8cddb..744cd107f4400 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/FolderListItem.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/FolderListItem.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 Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.Intellisense; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/MemberListItem.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/MemberListItem.cs index a68f9f5dcce40..5e9307b8e8d7e 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/MemberListItem.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/MemberListItem.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.Diagnostics; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/NamespaceListItem.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/NamespaceListItem.cs index c263a0e1edd62..74f1ebe43b8d4 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/NamespaceListItem.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/NamespaceListItem.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/ProjectListItem.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/ProjectListItem.cs index 8ef7a09270276..101086e302397 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/ProjectListItem.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/ProjectListItem.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 Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.Intellisense; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/SymbolListItem`1.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/SymbolListItem`1.cs index a5b9b7274b9cf..1da6015d840b8 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/SymbolListItem`1.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/SymbolListItem`1.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists; diff --git a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/TypeListItem.cs b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/TypeListItem.cs index 4cbd1a1cfde19..22317eb94dea2 100644 --- a/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/TypeListItem.cs +++ b/src/VisualStudio/Core/Def/Library/ObjectBrowser/Lists/TypeListItem.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.ObjectBrowser.Lists; diff --git a/src/VisualStudio/Core/Def/Library/VsNavInfo/Extensions.cs b/src/VisualStudio/Core/Def/Library/VsNavInfo/Extensions.cs index 3a18805279743..91e299784c948 100644 --- a/src/VisualStudio/Core/Def/Library/VsNavInfo/Extensions.cs +++ b/src/VisualStudio/Core/Def/Library/VsNavInfo/Extensions.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.Collections.Immutable; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNode.cs b/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNode.cs index c3b2a10bc1671..709438a5ba91f 100644 --- a/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNode.cs +++ b/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNode.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 Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Library.VsNavInfo; diff --git a/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNodeEnum.cs b/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNodeEnum.cs index ce60f79f5e596..135f983c60754 100644 --- a/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNodeEnum.cs +++ b/src/VisualStudio/Core/Def/Library/VsNavInfo/NavInfoNodeEnum.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.Collections.Immutable; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Log/VisualStudioErrorLogger.cs b/src/VisualStudio/Core/Def/Log/VisualStudioErrorLogger.cs index f1becce9317c2..279b822a1e962 100644 --- a/src/VisualStudio/Core/Def/Log/VisualStudioErrorLogger.cs +++ b/src/VisualStudio/Core/Def/Log/VisualStudioErrorLogger.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.Composition; using Microsoft.CodeAnalysis.ErrorLogger; diff --git a/src/VisualStudio/Core/Def/MoveStaticMembers/VisualStudioMoveStaticMembersOptionsService.cs b/src/VisualStudio/Core/Def/MoveStaticMembers/VisualStudioMoveStaticMembersOptionsService.cs index b22cf591ac5a4..26c6bf53eab09 100644 --- a/src/VisualStudio/Core/Def/MoveStaticMembers/VisualStudioMoveStaticMembersOptionsService.cs +++ b/src/VisualStudio/Core/Def/MoveStaticMembers/VisualStudioMoveStaticMembersOptionsService.cs @@ -89,7 +89,7 @@ internal static MoveStaticMembersDialogViewModel GetViewModel( INamedTypeSymbol selectedType, ImmutableArray selectedNodeSymbols, LinkedList history, - IGlyphService? glyphService, + IGlyphService glyphService, IUIThreadOperationExecutor uiThreadOperationExecutor) { var membersInType = selectedType.GetMembers(). diff --git a/src/VisualStudio/Core/Def/MoveToNamespace/MoveToNamespaceDialog.xaml.cs b/src/VisualStudio/Core/Def/MoveToNamespace/MoveToNamespaceDialog.xaml.cs index b9e088ec6f740..fe13290217868 100644 --- a/src/VisualStudio/Core/Def/MoveToNamespace/MoveToNamespaceDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/MoveToNamespace/MoveToNamespaceDialog.xaml.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.Windows; using System.Windows.Controls; using System.Windows.Input; diff --git a/src/VisualStudio/Core/Def/MoveToNamespace/NamespaceItem.cs b/src/VisualStudio/Core/Def/MoveToNamespace/NamespaceItem.cs index c37f845c58f9a..78bf6413b0e3a 100644 --- a/src/VisualStudio/Core/Def/MoveToNamespace/NamespaceItem.cs +++ b/src/VisualStudio/Core/Def/MoveToNamespace/NamespaceItem.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 - namespace Microsoft.VisualStudio.LanguageServices.Implementation.MoveToNamespace; internal class NamespaceItem diff --git a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchResultPreviewPanel.cs b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchResultPreviewPanel.cs index 0d368c51a3c4c..535834ed445cc 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchResultPreviewPanel.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/RoslynSearchResultPreviewPanel.cs @@ -8,7 +8,6 @@ using Microsoft.VisualStudio.Search.Data; using Microsoft.VisualStudio.Search.UI.PreviewPanel.Models; using Microsoft.VisualStudio.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.NavigateTo; diff --git a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs index 380e512b391be..fcec4b5135256 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewService.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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; using Microsoft.CodeAnalysis.Navigation; diff --git a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewServiceFactory.cs b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewServiceFactory.cs index ff94e8f3e60bb..c0926b953e9e2 100644 --- a/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewServiceFactory.cs +++ b/src/VisualStudio/Core/Def/NavigateTo/VisualStudioNavigateToPreviewServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Editor.Implementation.NavigateTo; diff --git a/src/VisualStudio/Core/Def/Options/NamingPreferencesReadFallback.cs b/src/VisualStudio/Core/Def/Options/NamingPreferencesReadFallback.cs index 7fcb429cf1364..35813d7175eda 100644 --- a/src/VisualStudio/Core/Def/Options/NamingPreferencesReadFallback.cs +++ b/src/VisualStudio/Core/Def/Options/NamingPreferencesReadFallback.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.CodeStyle; namespace Microsoft.VisualStudio.LanguageServices.Options; diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs index 4827ef4fcb0ae..496ddb9e0f5cf 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionPersisterProvider.cs @@ -17,7 +17,6 @@ using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Options; diff --git a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs index b838cbd7a7fbf..3bf4c29fe1470 100644 --- a/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs +++ b/src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs @@ -372,6 +372,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti {"dotnet_unsupported_report_invalid_json_patterns", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.ReportInvalidJsonPatterns")}, {"visual_studio_enable_key_binding_reset", new FeatureFlagStorage("Roslyn.KeybindingResetEnabled")}, {"visual_studio_enable_semantic_search", new FeatureFlagStorage("Roslyn.SemanticSearchEnabled")}, + {"visual_studio_enable_semantic_search_prompt", new FeatureFlagStorage("Roslyn.ShowPromptInSemanticSearch")}, {"visual_studio_enable_copilot_rename_context", new FeatureFlagStorage("Roslyn.CopilotRenameGetContext")}, {"visual_studio_key_binding_needs_reset", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeedsReset")}, {"visual_studio_key_binding_reset_never_show_again", new LocalUserProfileStorage(@"Roslyn\Internal\KeybindingsStatus", "NeverShowAgain")}, diff --git a/src/VisualStudio/Core/Def/Packaging/Interop/SVsRemoteControlService.cs b/src/VisualStudio/Core/Def/Packaging/Interop/SVsRemoteControlService.cs index bd2fd0150e4ce..63e2bdff34baa 100644 --- a/src/VisualStudio/Core/Def/Packaging/Interop/SVsRemoteControlService.cs +++ b/src/VisualStudio/Core/Def/Packaging/Interop/SVsRemoteControlService.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.Runtime.InteropServices; namespace Microsoft.Internal.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/PickMembers/PickMembersDialogViewModel.cs b/src/VisualStudio/Core/Def/PickMembers/PickMembersDialogViewModel.cs index 85a8a3558835c..b0b5cdefcbd34 100644 --- a/src/VisualStudio/Core/Def/PickMembers/PickMembersDialogViewModel.cs +++ b/src/VisualStudio/Core/Def/PickMembers/PickMembersDialogViewModel.cs @@ -12,7 +12,6 @@ using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; using Microsoft.VisualStudio.LanguageServices.Utilities; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.PickMembers; diff --git a/src/VisualStudio/Core/Def/Preview/ChangeList.cs b/src/VisualStudio/Core/Def/Preview/ChangeList.cs index 9ff78414a30cb..34d06f9f4a56b 100644 --- a/src/VisualStudio/Core/Def/Preview/ChangeList.cs +++ b/src/VisualStudio/Core/Def/Preview/ChangeList.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 Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/FileChange.cs b/src/VisualStudio/Core/Def/Preview/FileChange.cs index dd71eeab868a5..0a38e6c0e32ec 100644 --- a/src/VisualStudio/Core/Def/Preview/FileChange.cs +++ b/src/VisualStudio/Core/Def/Preview/FileChange.cs @@ -20,7 +20,6 @@ using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Differencing; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/PreviewService.cs b/src/VisualStudio/Core/Def/Preview/PreviewService.cs index 9a83d65e10c4b..d4d20ef8a6b97 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewService.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewService.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.Composition; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs index 4a168dfe7d816..e7a18aa7f34f0 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.PreviewDialogWorkspace.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Preview; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.cs b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.cs index f80dc8360174a..99244c6d979db 100644 --- a/src/VisualStudio/Core/Def/Preview/PreviewUpdater.cs +++ b/src/VisualStudio/Core/Def/Preview/PreviewUpdater.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/ReferenceChange.AnalyzerReferenceChange.cs b/src/VisualStudio/Core/Def/Preview/ReferenceChange.AnalyzerReferenceChange.cs index 7bba3480ef1ed..c0b6fd7060341 100644 --- a/src/VisualStudio/Core/Def/Preview/ReferenceChange.AnalyzerReferenceChange.cs +++ b/src/VisualStudio/Core/Def/Preview/ReferenceChange.AnalyzerReferenceChange.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 Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/VisualStudio/Core/Def/Preview/ReferenceChange.MetadataReferenceChange.cs b/src/VisualStudio/Core/Def/Preview/ReferenceChange.MetadataReferenceChange.cs index a087d716425ca..9e1217ac713b2 100644 --- a/src/VisualStudio/Core/Def/Preview/ReferenceChange.MetadataReferenceChange.cs +++ b/src/VisualStudio/Core/Def/Preview/ReferenceChange.MetadataReferenceChange.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/ReferenceChange.ProjectReferenceChange.cs b/src/VisualStudio/Core/Def/Preview/ReferenceChange.ProjectReferenceChange.cs index a21932897ee7a..0f0bd433282fd 100644 --- a/src/VisualStudio/Core/Def/Preview/ReferenceChange.ProjectReferenceChange.cs +++ b/src/VisualStudio/Core/Def/Preview/ReferenceChange.ProjectReferenceChange.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 Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Preview; diff --git a/src/VisualStudio/Core/Def/Preview/SpanChange.cs b/src/VisualStudio/Core/Def/Preview/SpanChange.cs index af8464069f414..7930ef5276571 100644 --- a/src/VisualStudio/Core/Def/Preview/SpanChange.cs +++ b/src/VisualStudio/Core/Def/Preview/SpanChange.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 Microsoft.CodeAnalysis; using Microsoft.VisualStudio.Language.Intellisense; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/Progression/GraphNodeCreation.cs b/src/VisualStudio/Core/Def/Progression/GraphNodeCreation.cs index 75c528fd1ae66..7cbae5050f676 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphNodeCreation.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphNodeCreation.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.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsChildrenGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsChildrenGraphQuery.cs index a6c6ab945d3ce..c116f6dcab525 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsChildrenGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsChildrenGraphQuery.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.Linq; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsGraphQuery.cs index 99ab25eac7c93..f115c3fc5e78f 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/ContainsGraphQuery.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.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementedByGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementedByGraphQuery.cs index e654af1ef0fde..d6b6967233b7b 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementedByGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementedByGraphQuery.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementsGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementsGraphQuery.cs index 764bfc848b9c8..d165a54688759 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementsGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/ImplementsGraphQuery.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritedByGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritedByGraphQuery.cs index 8975ebbfe0215..d934a4ff21587 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritedByGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritedByGraphQuery.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.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritsGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritsGraphQuery.cs index ba7ec18af0d01..701e3ff4a2205 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritsGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/InheritsGraphQuery.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.Collections.Generic; using System.Linq; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/IsCalledByGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/IsCalledByGraphQuery.cs index 7cb8ddb0f210b..837cc7c5a6e24 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/IsCalledByGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/IsCalledByGraphQuery.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.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/OverriddenByGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/OverriddenByGraphQuery.cs index 6790c969280ba..194e01b011904 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/OverriddenByGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/OverriddenByGraphQuery.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/GraphQueries/OverridesGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/GraphQueries/OverridesGraphQuery.cs index c968fb96c39ee..668e8915ba742 100644 --- a/src/VisualStudio/Core/Def/Progression/GraphQueries/OverridesGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/GraphQueries/OverridesGraphQuery.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/IGraphQuery.cs b/src/VisualStudio/Core/Def/Progression/IGraphQuery.cs index cd4e71800219b..c5a9cf73b70d6 100644 --- a/src/VisualStudio/Core/Def/Progression/IGraphQuery.cs +++ b/src/VisualStudio/Core/Def/Progression/IGraphQuery.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/IProgressionLanguageService.cs b/src/VisualStudio/Core/Def/Progression/IProgressionLanguageService.cs index 2fab33e49ce9f..1b548f7e86877 100644 --- a/src/VisualStudio/Core/Def/Progression/IProgressionLanguageService.cs +++ b/src/VisualStudio/Core/Def/Progression/IProgressionLanguageService.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.Collections.Generic; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/IconHelper.cs b/src/VisualStudio/Core/Def/Progression/IconHelper.cs index 1ce8ab9a5528e..102705aa3875a 100644 --- a/src/VisualStudio/Core/Def/Progression/IconHelper.cs +++ b/src/VisualStudio/Core/Def/Progression/IconHelper.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.Generic; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Progression/RoslynGraphCategories.cs b/src/VisualStudio/Core/Def/Progression/RoslynGraphCategories.cs index d723ac45f6f70..040f6671f1af3 100644 --- a/src/VisualStudio/Core/Def/Progression/RoslynGraphCategories.cs +++ b/src/VisualStudio/Core/Def/Progression/RoslynGraphCategories.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 Microsoft.VisualStudio.GraphModel; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Progression; diff --git a/src/VisualStudio/Core/Def/Progression/RoslynGraphProperties.cs b/src/VisualStudio/Core/Def/Progression/RoslynGraphProperties.cs index 87a47f5645376..4cdf37c577932 100644 --- a/src/VisualStudio/Core/Def/Progression/RoslynGraphProperties.cs +++ b/src/VisualStudio/Core/Def/Progression/RoslynGraphProperties.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.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editing; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/CPS/ICodeModelFactory.cs b/src/VisualStudio/Core/Def/ProjectSystem/CPS/ICodeModelFactory.cs index e87bb9c48000b..e6c24df439abc 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/CPS/ICodeModelFactory.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/CPS/ICodeModelFactory.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 - namespace Microsoft.VisualStudio.LanguageServices.ProjectSystem; /// diff --git a/src/VisualStudio/Core/Def/ProjectSystem/CPS/ITempPECompiler.cs b/src/VisualStudio/Core/Def/ProjectSystem/CPS/ITempPECompiler.cs index 220f54a9d31b4..193b8b1ab777f 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/CPS/ITempPECompiler.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/CPS/ITempPECompiler.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.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Extensions/ServiceProviderExtensions.cs b/src/VisualStudio/Core/Def/ProjectSystem/Extensions/ServiceProviderExtensions.cs index 40fb70e7289d4..3889c8391661d 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Extensions/ServiceProviderExtensions.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Extensions/ServiceProviderExtensions.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 Microsoft.VisualStudio.ComponentModelHost; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerConfigFileHost.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerConfigFileHost.cs index cdaf44443a703..bdcd176f224c1 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerConfigFileHost.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerConfigFileHost.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerHost.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerHost.cs index d4c9a366d34bb..13775754f18b8 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerHost.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IAnalyzerHost.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/ICompilerOptionsHostObject.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/ICompilerOptionsHostObject.cs index 0087a7b12744f..ba6d0f49fa0e1 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/ICompilerOptionsHostObject.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/ICompilerOptionsHostObject.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntPtrReturningVsInvisibleEditorManager.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntPtrReturningVsInvisibleEditorManager.cs index c68c19ea147fc..769f6ce0d2b43 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntPtrReturningVsInvisibleEditorManager.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntPtrReturningVsInvisibleEditorManager.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.Runtime.InteropServices; using Microsoft.VisualStudio.Shell.Interop; @@ -24,8 +22,8 @@ internal interface IIntPtrReturningVsInvisibleEditorManager { int RegisterInvisibleEditor( [MarshalAs(UnmanagedType.LPWStr)] string pszMkDocument, - IVsProject pProject, + IVsProject? pProject, uint dwFlags, - IVsSimpleDocFactory pFactory, + IVsSimpleDocFactory? pFactory, out IntPtr ppEditor); } diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntellisenseBuildTarget.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntellisenseBuildTarget.cs index 45bdafca27a95..e594cc3161ff6 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntellisenseBuildTarget.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IIntellisenseBuildTarget.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IProjectSiteEx.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IProjectSiteEx.cs index 01cd5c23e68a2..37bf1ebf9761e 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IProjectSiteEx.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IProjectSiteEx.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IVsUndoState.cs b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IVsUndoState.cs index ed9778fbb1c33..439453020f7bb 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Interop/IVsUndoState.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Interop/IVsUndoState.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.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IAnalyzerConfigFileHost.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IAnalyzerConfigFileHost.cs index 49988ad22e182..882407eba9c5c 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IAnalyzerConfigFileHost.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IAnalyzerConfigFileHost.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 Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_ICompilerOptionsHostObject.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_ICompilerOptionsHostObject.cs index 72b60e461378a..ee9f01b9b79a9 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_ICompilerOptionsHostObject.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_ICompilerOptionsHostObject.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 Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IProjectSiteEx.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IProjectSiteEx.cs index f9fd883426a46..4972be79f793d 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IProjectSiteEx.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IProjectSiteEx.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Legacy; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsHierarchyEvents.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsHierarchyEvents.cs index 222b0a138c4bf..b585dd10e0da9 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsHierarchyEvents.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsHierarchyEvents.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.Diagnostics; using System.IO; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsReportExternalErrors.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsReportExternalErrors.cs index 714db3be20600..7c80eb5c5e185 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsReportExternalErrors.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/AbstractLegacyProject_IVsReportExternalErrors.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.Runtime.InteropServices; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.Shell.Interop; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs index df4ea8ce29de6..f9bdeda04ad80 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/Legacy/SolutionEventsBatchScopeCreator.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.Generic; using System.ComponentModel.Composition; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs index dff438e9f3d47..81b34d0a5b3f7 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/MiscellaneousFilesWorkspace.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.Generic; using System.Collections.Immutable; @@ -48,11 +46,11 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IOpenText /// The mapping of all monikers in the RDT and the of the project and of the open /// file we have created for that open buffer. An entry should only be in here if it's also already in . /// - private readonly Dictionary _monikersToProjectIdAndContainer = new Dictionary(); + private readonly Dictionary _monikersToProjectIdAndContainer = []; private readonly ImmutableArray _metadataReferences; - private IVsTextManager _textManager; + private IVsTextManager? _textManager; [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -80,7 +78,7 @@ public async Task InitializeAsync() _textManager = await _textManagerService.GetValueAsync().ConfigureAwait(false); } - void IOpenTextBufferEventListener.OnOpenDocument(string moniker, ITextBuffer textBuffer, IVsHierarchy _) => TrackOpenedDocument(moniker, textBuffer); + void IOpenTextBufferEventListener.OnOpenDocument(string moniker, ITextBuffer textBuffer, IVsHierarchy? _) => TrackOpenedDocument(moniker, textBuffer); void IOpenTextBufferEventListener.OnCloseDocument(string moniker) => TryUntrackClosingDocument(moniker); @@ -116,11 +114,11 @@ void IOpenTextBufferEventListener.OnSaveDocument(string moniker) { } public void RegisterLanguage(Guid languageGuid, string languageName, string scriptExtension) => _languageInformationByLanguageGuid.Add(languageGuid, new LanguageInformation(languageName, scriptExtension)); - private LanguageInformation TryGetLanguageInformation(string filename) + private LanguageInformation? TryGetLanguageInformation(string filename) { - LanguageInformation languageInformation = null; + LanguageInformation? languageInformation = null; - if (ErrorHandler.Succeeded(_textManager.MapFilenameToLanguageSID(filename, out var fileLanguageGuid))) + if (_textManager != null && ErrorHandler.Succeeded(_textManager.MapFilenameToLanguageSID(filename, out var fileLanguageGuid))) { _languageInformationByLanguageGuid.TryGetValue(fileLanguageGuid, out languageInformation); } @@ -133,6 +131,9 @@ private IEnumerable CreateMetadataReferences() var manager = this.Services.GetService(); var searchPaths = VisualStudioMetadataReferenceManager.GetReferencePaths(); + if (manager == null) + return []; + return from fileName in new[] { "mscorlib.dll", "System.dll", "System.Core.dll" } let fullPath = FileUtilities.ResolveRelativePath(fileName, basePath: null, baseDirectory: null, searchPaths: searchPaths, fileExists: File.Exists) where fullPath != null diff --git a/src/VisualStudio/Core/Def/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs b/src/VisualStudio/Core/Def/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs index 9489914f731b3..3838497b1f843 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/RuleSets/VisualStudioRuleSetManager.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/RuleSets/VisualStudioRuleSetManager.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 Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.TestHooks; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/ViewEventArgs.cs b/src/VisualStudio/Core/Def/ProjectSystem/ViewEventArgs.cs index f78229e628bd7..6bf2797710567 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/ViewEventArgs.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/ViewEventArgs.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 Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectManagementService.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectManagementService.cs index 20f674aa70bab..99028232e90c0 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectManagementService.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioProjectManagementService.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.Generic; using System.Composition; diff --git a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs index fe498f5cf0ee2..407464b3ea61e 100644 --- a/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -22,7 +22,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Notification; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.ProjectSystem; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -31,7 +30,6 @@ using Microsoft.CodeAnalysis.Telemetry; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; -using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Editor; @@ -43,7 +41,6 @@ using Microsoft.VisualStudio.LanguageServices.Utilities; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Shell.ServiceBroker; using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Projection; @@ -305,7 +302,7 @@ public override EnvDTE.FileCodeModel GetFileCodeModel(DocumentId documentId) } else { - return _projectCodeModelFactory.Value.GetOrCreateFileCodeModel(documentId.ProjectId, document.FilePath); + return _projectCodeModelFactory.Value.GetOrCreateFileCodeModel(documentId.ProjectId, document.FilePath!); } } @@ -493,7 +490,7 @@ protected override void ApplyCompilationOptionsChanged(ProjectId projectId, Comp var originalProject = CurrentSolution.GetRequiredProject(projectId); var compilationOptionsService = originalProject.Services.GetRequiredService(); - var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); + var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId)!, ServiceProvider.GlobalProvider); compilationOptionsService.Apply(originalProject.CompilationOptions!, options, storage); } @@ -510,7 +507,7 @@ protected override void ApplyParseOptionsChanged(ProjectId projectId, ParseOptio } var parseOptionsService = CurrentSolution.GetRequiredProject(projectId).Services.GetRequiredService(); - var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId), ServiceProvider.GlobalProvider); + var storage = ProjectPropertyStorage.Create(TryGetDTEProject(projectId)!, ServiceProvider.GlobalProvider); parseOptionsService.Apply(options, storage); } diff --git a/src/VisualStudio/Core/Def/PullMemberUp/MainDialog/PullMemberUpDialog.xaml.cs b/src/VisualStudio/Core/Def/PullMemberUp/MainDialog/PullMemberUpDialog.xaml.cs index c943237befa96..de2348482649f 100644 --- a/src/VisualStudio/Core/Def/PullMemberUp/MainDialog/PullMemberUpDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/PullMemberUp/MainDialog/PullMemberUpDialog.xaml.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.Windows; using System.Windows.Input; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningDialog.xaml.cs b/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningDialog.xaml.cs index 85da8d266c785..e17ef78480e51 100644 --- a/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningDialog.xaml.cs +++ b/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningDialog.xaml.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.Windows; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.VisualStudio.PlatformUI; diff --git a/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningViewModel.cs b/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningViewModel.cs index adb2252f9fa25..190e51729606b 100644 --- a/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningViewModel.cs +++ b/src/VisualStudio/Core/Def/PullMemberUp/WarningDialog/PullMemberUpWarningViewModel.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.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/VisualStudio/Core/Def/RoslynActivityLogger.cs b/src/VisualStudio/Core/Def/RoslynActivityLogger.cs index 307b9cdc4c1d5..cdf5d6d629753 100644 --- a/src/VisualStudio/Core/Def/RoslynActivityLogger.cs +++ b/src/VisualStudio/Core/Def/RoslynActivityLogger.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.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/RoslynPackage.cs b/src/VisualStudio/Core/Def/RoslynPackage.cs index 9baed617356bc..9742018a5ac9a 100644 --- a/src/VisualStudio/Core/Def/RoslynPackage.cs +++ b/src/VisualStudio/Core/Def/RoslynPackage.cs @@ -60,11 +60,11 @@ internal sealed class RoslynPackage : AbstractPackage private const string BackgroundAnalysisScopeOptionKey = "AnalysisScope-DCE33A29A768"; private const byte BackgroundAnalysisScopeOptionVersion = 1; - private static RoslynPackage? _lazyInstance; + private static RoslynPackage? s_lazyInstance; private RuleSetEventHandler? _ruleSetEventHandler; private ColorSchemeApplier? _colorSchemeApplier; - private IDisposable? _solutionEventMonitor; + private SolutionEventMonitor? _solutionEventMonitor; private BackgroundAnalysisScope? _analysisScope; @@ -95,7 +95,7 @@ public BackgroundAnalysisScope? AnalysisScope internal static async ValueTask GetOrLoadAsync(IThreadingContext threadingContext, IAsyncServiceProvider serviceProvider, CancellationToken cancellationToken) { - if (_lazyInstance is null) + if (s_lazyInstance is null) { await threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); @@ -105,11 +105,11 @@ public BackgroundAnalysisScope? AnalysisScope if (ErrorHandler.Succeeded(((IVsShell)shell).IsPackageLoaded(typeof(RoslynPackage).GUID, out var package))) { - _lazyInstance = (RoslynPackage)package; + s_lazyInstance = (RoslynPackage)package; } } - return _lazyInstance; + return s_lazyInstance; } protected override void OnLoadOptions(string key, Stream stream) diff --git a/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchPresenterController.cs b/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchPresenterController.cs new file mode 100644 index 0000000000000..ddc67b487f106 --- /dev/null +++ b/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchPresenterController.cs @@ -0,0 +1,13 @@ +// 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; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal interface ISemanticSearchPresenterController +{ + Task ExecuteQueryAsync(string query, CancellationToken cancellationToken); +} diff --git a/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchToolWindowController.cs b/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchToolWindowController.cs new file mode 100644 index 0000000000000..66b175af5b7f3 --- /dev/null +++ b/src/VisualStudio/Core/Def/SemanticSearch/ISemanticSearchToolWindowController.cs @@ -0,0 +1,13 @@ +// 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; + +namespace Microsoft.CodeAnalysis.SemanticSearch; + +internal interface ISemanticSearchToolWindowController +{ + Task UpdateQueryAsync(string query, bool activateWindow, bool executeQuery, CancellationToken cancellationToken); +} diff --git a/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs b/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs index 4b5e4cadb7d7d..caf12828d2004 100644 --- a/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs +++ b/src/VisualStudio/Core/Def/SemanticSearch/SemanticSearchFeatureFlag.cs @@ -9,6 +9,7 @@ namespace Microsoft.VisualStudio.LanguageServices; internal static class SemanticSearchFeatureFlag { public static readonly Option2 Enabled = new("visual_studio_enable_semantic_search", defaultValue: false); + public static readonly Option2 PromptEnabled = new("visual_studio_enable_semantic_search_prompt", defaultValue: false); /// /// Context id that indicates that Semantic Search feature is enabled. diff --git a/src/VisualStudio/Core/Def/Shared/LogicalStringComparer.cs b/src/VisualStudio/Core/Def/Shared/LogicalStringComparer.cs index 2573efd699996..735e72a1800db 100644 --- a/src/VisualStudio/Core/Def/Shared/LogicalStringComparer.cs +++ b/src/VisualStudio/Core/Def/Shared/LogicalStringComparer.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.Collections.Generic; using System.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/Shared/VisualStudioImageIdService.cs b/src/VisualStudio/Core/Def/Shared/VisualStudioImageIdService.cs index 93e8e460d6340..3150255b82c79 100644 --- a/src/VisualStudio/Core/Def/Shared/VisualStudioImageIdService.cs +++ b/src/VisualStudio/Core/Def/Shared/VisualStudioImageIdService.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.Generic; using System.Collections.Immutable; diff --git a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetInfoService.cs b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetInfoService.cs index e27406337e8d3..2ac13d3aa2c81 100644 --- a/src/VisualStudio/Core/Def/Snippets/AbstractSnippetInfoService.cs +++ b/src/VisualStudio/Core/Def/Snippets/AbstractSnippetInfoService.cs @@ -94,7 +94,7 @@ public IEnumerable GetSnippetsIfAvailable() } } - public bool SnippetShortcutExists_NonBlocking(string shortcut) + public bool SnippetShortcutExists_NonBlocking(string? shortcut) { if (shortcut == null) { diff --git a/src/VisualStudio/Core/Def/Snippets/IVsContainedLanguageHostInternal.cs b/src/VisualStudio/Core/Def/Snippets/IVsContainedLanguageHostInternal.cs index c4f6d4e787a46..4a3a1f6ef1f85 100644 --- a/src/VisualStudio/Core/Def/Snippets/IVsContainedLanguageHostInternal.cs +++ b/src/VisualStudio/Core/Def/Snippets/IVsContainedLanguageHostInternal.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.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/Snippets/IVsExpansionSessionInternal.cs b/src/VisualStudio/Core/Def/Snippets/IVsExpansionSessionInternal.cs index c4319f83f5d7c..d1908ba191a23 100644 --- a/src/VisualStudio/Core/Def/Snippets/IVsExpansionSessionInternal.cs +++ b/src/VisualStudio/Core/Def/Snippets/IVsExpansionSessionInternal.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.Runtime.InteropServices; @@ -32,7 +30,7 @@ internal interface IVsExpansionSessionInternal /// before leaving the calling method. /// [PreserveSig] - int GetSnippetNode([MarshalAs(UnmanagedType.BStr)] string bstrNode, out IntPtr pNode); + int GetSnippetNode([MarshalAs(UnmanagedType.BStr)] string? bstrNode, out IntPtr pNode); void Reserved9(); void Reserved10(); diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs index ce6220f86dc6e..7deebb2a1fbca 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetExpansionClientFactory.cs @@ -18,7 +18,6 @@ using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Text.Editor.Commanding; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Snippets; diff --git a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs index e0ead9738835c..b013eb9190dae 100644 --- a/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.cs +++ b/src/VisualStudio/Core/Def/Snippets/SnippetFunctions/AbstractSnippetFunction.IVsExpansionFunction.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.Runtime.InteropServices; using System.Threading; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs b/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs index b97644aa038cc..bc18e7a50097c 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackFrameViewModel.cs @@ -28,31 +28,22 @@ namespace Microsoft.VisualStudio.LanguageServices.StackTraceExplorer; using StackFrameToken = EmbeddedSyntaxToken; using StackFrameTrivia = EmbeddedSyntaxTrivia; -internal class StackFrameViewModel : FrameViewModel +internal class StackFrameViewModel( + ParsedStackFrame frame, + IThreadingContext threadingContext, + Workspace workspace, + IClassificationFormatMap formatMap, + ClassificationTypeMap typeMap) : FrameViewModel(formatMap, typeMap) { - private readonly ParsedStackFrame _frame; - private readonly IThreadingContext _threadingContext; - private readonly Workspace _workspace; - private readonly IStackTraceExplorerService _stackExplorerService; + private readonly ParsedStackFrame _frame = frame; + private readonly IThreadingContext _threadingContext = threadingContext; + private readonly Workspace _workspace = workspace; + private readonly IStackTraceExplorerService _stackExplorerService = workspace.Services.GetRequiredService(); private readonly Dictionary _definitionCache = []; - private Document? _cachedDocument; + private TextDocument? _cachedDocument; private int _cachedLineNumber; - public StackFrameViewModel( - ParsedStackFrame frame, - IThreadingContext threadingContext, - Workspace workspace, - IClassificationFormatMap formatMap, - ClassificationTypeMap typeMap) - : base(formatMap, typeMap) - { - _frame = frame; - _threadingContext = threadingContext; - _workspace = workspace; - _stackExplorerService = workspace.Services.GetRequiredService(); - } - public override bool ShowMouseOver => true; public void NavigateToClass() @@ -112,14 +103,11 @@ public async Task NavigateToFileAsync(CancellationToken cancellationToken) { try { - var (document, lineNumber) = GetDocumentAndLine(); + var (textDocument, lineNumber) = GetDocumentAndLine(); - if (document is not null) + if (textDocument is not null) { - // While navigating do not activate the tab, which will change focus from the tool window - var options = new NavigationOptions(PreferProvisionalTab: true, ActivateTab: false); - - var sourceText = await document.GetValueTextAsync(cancellationToken).ConfigureAwait(false); + var sourceText = await textDocument.GetValueTextAsync(cancellationToken).ConfigureAwait(false); // If the line number is larger than the total lines in the file // then just go to the end of the file (lines count). This can happen @@ -131,8 +119,12 @@ public async Task NavigateToFileAsync(CancellationToken cancellationToken) if (navigationService is null) return; - var location = await navigationService.TryNavigateToLineAndOffsetAsync( - _threadingContext, _workspace, document.Id, lineNumber - 1, offset: 0, options, cancellationToken).ConfigureAwait(false); + // While navigating do not activate the tab, which will change focus from the tool window + var options = new NavigationOptions(PreferProvisionalTab: true, ActivateTab: false); + + await navigationService.TryNavigateToLineAndOffsetAsync( + _threadingContext, _workspace, textDocument.Id, lineNumber - 1, offset: 0, options, cancellationToken) + .ConfigureAwait(false); } } catch (Exception ex) when (FatalError.ReportAndCatchUnlessCanceled(ex, cancellationToken)) @@ -208,7 +200,7 @@ protected override IEnumerable CreateInlines() yield return MakeClassifiedRun(ClassificationTypeNames.Text, _frame.Root.EndOfLineToken.ToFullString()); } - private (Document? document, int lineNumber) GetDocumentAndLine() + private (TextDocument? document, int lineNumber) GetDocumentAndLine() { if (_cachedDocument is not null) { diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs index ea22f4b2be2a4..e9bad085d3c3f 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerCommandHandler.cs @@ -4,7 +4,6 @@ using System; using System.ComponentModel.Design; -using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerViewModel.cs b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerViewModel.cs index 8bc9c79daab03..303e06c91a5ee 100644 --- a/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerViewModel.cs +++ b/src/VisualStudio/Core/Def/StackTraceExplorer/StackTraceExplorerViewModel.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.VisualStudio.LanguageServices.Utilities; using Microsoft.VisualStudio.Text.Classification; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.StackTraceExplorer; diff --git a/src/VisualStudio/Core/Def/SyncNamespaces/SyncNamespacesCommandHandler.cs b/src/VisualStudio/Core/Def/SyncNamespaces/SyncNamespacesCommandHandler.cs index bbf961860df10..42aa50a166bea 100644 --- a/src/VisualStudio/Core/Def/SyncNamespaces/SyncNamespacesCommandHandler.cs +++ b/src/VisualStudio/Core/Def/SyncNamespaces/SyncNamespacesCommandHandler.cs @@ -10,11 +10,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editor.Host; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Progress; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SyncNamespaces; diff --git a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index e29643e157e4e..b2992e2b257d0 100644 --- a/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -18,12 +18,10 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Progress; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.LanguageServices.Implementation.TableDataSource; -using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.TableControl; @@ -267,9 +265,9 @@ private async Task ApplySuppressionFixAsync( if (!documentDiagnosticsPerLanguage.IsEmpty) { var suppressionFixer = GetSuppressionFixer(documentDiagnosticsPerLanguage.SelectMany(kvp => kvp.Value), language, _codeFixService); - if (suppressionFixer != null) + var suppressionFixAllProvider = suppressionFixer?.GetFixAllProvider(); + if (suppressionFixer != null && suppressionFixAllProvider != null) { - var suppressionFixAllProvider = suppressionFixer.GetFixAllProvider(); newSolution = await _fixMultipleOccurencesService.GetFixAsync( documentDiagnosticsPerLanguage, _workspace, @@ -292,9 +290,9 @@ private async Task ApplySuppressionFixAsync( if (!projectDiagnosticsPerLanguage.IsEmpty) { var suppressionFixer = GetSuppressionFixer(projectDiagnosticsPerLanguage.SelectMany(kvp => kvp.Value), language, _codeFixService); - if (suppressionFixer != null) + var suppressionFixAllProvider = suppressionFixer?.GetFixAllProvider(); + if (suppressionFixer != null && suppressionFixAllProvider != null) { - var suppressionFixAllProvider = suppressionFixer.GetFixAllProvider(); newSolution = await _fixMultipleOccurencesService.GetFixAsync( projectDiagnosticsPerLanguage, _workspace, diff --git a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs index eb8d2f0c68786..7fd735634876b 100644 --- a/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs +++ b/src/VisualStudio/Core/Def/TaskList/ExternalErrorDiagnosticUpdateSource.cs @@ -25,7 +25,6 @@ using Microsoft.VisualStudio.RpcContracts.Utilities; using Microsoft.VisualStudio.Shell.ServiceBroker; using Roslyn.Utilities; -using static Microsoft.ServiceHub.Framework.ServiceBrokerClient; namespace Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; /// diff --git a/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs b/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs index 6347f04d86a89..13bbdb9b5203f 100644 --- a/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs +++ b/src/VisualStudio/Core/Def/TaskList/ProjectExternalErrorReporter.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Globalization; @@ -19,7 +18,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; -using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.Venus; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs b/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs index 8e5b190021260..b146356ef20ab 100644 --- a/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.cs +++ b/src/VisualStudio/Core/Def/Telemetry/CodeMarkerLogger.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.Generic; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs b/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs index 92187c85c6a8b..d6105f7e74e74 100644 --- a/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs +++ b/src/VisualStudio/Core/Def/Telemetry/FileLogger.cs @@ -5,7 +5,6 @@ using System; using System.Globalization; using System.IO; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingHistogramLog.cs b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingHistogramLog.cs index 11e4a262cb250..72bcb15a2c9b9 100644 --- a/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingHistogramLog.cs +++ b/src/VisualStudio/Core/Def/Telemetry/Shared/AggregatingHistogramLog.cs @@ -7,7 +7,6 @@ using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.Telemetry.Metrics; using Microsoft.VisualStudio.Telemetry.Metrics.Events; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Telemetry; diff --git a/src/VisualStudio/Core/Def/UnusedReferences/Dialog/UnusedReferencesTableProvider.ColumnDefinitions.cs b/src/VisualStudio/Core/Def/UnusedReferences/Dialog/UnusedReferencesTableProvider.ColumnDefinitions.cs index 17a2da8e24bab..863e7f7ecf99c 100644 --- a/src/VisualStudio/Core/Def/UnusedReferences/Dialog/UnusedReferencesTableProvider.ColumnDefinitions.cs +++ b/src/VisualStudio/Core/Def/UnusedReferences/Dialog/UnusedReferencesTableProvider.ColumnDefinitions.cs @@ -17,7 +17,6 @@ using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences.Dialog; diff --git a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs index 66165dfc70e97..0206b8e26a775 100644 --- a/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs +++ b/src/VisualStudio/Core/Def/UnusedReferences/UnusedReferenceExtensions.cs @@ -2,11 +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.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.UnusedReferences; using Microsoft.VisualStudio.LanguageServices.ExternalAccess.ProjectSystem.Api; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.UnusedReferences; diff --git a/src/VisualStudio/Core/Def/Utilities/BooleanReverseConverter.cs b/src/VisualStudio/Core/Def/Utilities/BooleanReverseConverter.cs index da84c8bc5311d..c7bf9df1a6140 100644 --- a/src/VisualStudio/Core/Def/Utilities/BooleanReverseConverter.cs +++ b/src/VisualStudio/Core/Def/Utilities/BooleanReverseConverter.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.Globalization; using System.Windows.Data; diff --git a/src/VisualStudio/Core/Def/Utilities/ComEventSink.cs b/src/VisualStudio/Core/Def/Utilities/ComEventSink.cs index c3182f8e4fcce..4f73e0227e462 100644 --- a/src/VisualStudio/Core/Def/Utilities/ComEventSink.cs +++ b/src/VisualStudio/Core/Def/Utilities/ComEventSink.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 Microsoft.VisualStudio.OLE.Interop; diff --git a/src/VisualStudio/Core/Def/Utilities/EnumBoolConverter.cs b/src/VisualStudio/Core/Def/Utilities/EnumBoolConverter.cs index c196e60fbea82..8213827dbd0ed 100644 --- a/src/VisualStudio/Core/Def/Utilities/EnumBoolConverter.cs +++ b/src/VisualStudio/Core/Def/Utilities/EnumBoolConverter.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.Globalization; using System.Windows; diff --git a/src/VisualStudio/Core/Def/Utilities/GlyphExtensions.cs b/src/VisualStudio/Core/Def/Utilities/GlyphExtensions.cs index 1e3e92e780ef8..69ca9501c8372 100644 --- a/src/VisualStudio/Core/Def/Utilities/GlyphExtensions.cs +++ b/src/VisualStudio/Core/Def/Utilities/GlyphExtensions.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.Windows.Media; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Utilities/IVsEnumDebugName.cs b/src/VisualStudio/Core/Def/Utilities/IVsEnumDebugName.cs index 6e07fc874fd38..66c64a14fbf5a 100644 --- a/src/VisualStudio/Core/Def/Utilities/IVsEnumDebugName.cs +++ b/src/VisualStudio/Core/Def/Utilities/IVsEnumDebugName.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.Runtime.InteropServices; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/Utilities/IVsLanguageDebugInfo.cs b/src/VisualStudio/Core/Def/Utilities/IVsLanguageDebugInfo.cs index bed87a9436ee4..037e3060804e6 100644 --- a/src/VisualStudio/Core/Def/Utilities/IVsLanguageDebugInfo.cs +++ b/src/VisualStudio/Core/Def/Utilities/IVsLanguageDebugInfo.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.Runtime.InteropServices; using Microsoft.VisualStudio.TextManager.Interop; @@ -16,7 +14,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; internal interface IVsLanguageDebugInfo { [PreserveSig] - int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, int cLines, out IVsEnumBSTR ppEnum); + int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, int cLines, out IVsEnumBSTR? ppEnum); [PreserveSig] int ValidateBreakpointLocation( @@ -26,16 +24,16 @@ int ValidateBreakpointLocation( [In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct)] TextSpan[] pCodeSpan); [PreserveSig] - int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, [MarshalAs(UnmanagedType.BStr)] out string pbstrName, out int piLineOffset); + int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, [MarshalAs(UnmanagedType.BStr)] out string? pbstrName, out int piLineOffset); [PreserveSig] int GetLocationOfName( [MarshalAs(UnmanagedType.LPWStr)] string pszName, - [MarshalAs(UnmanagedType.BStr)] out string pbstrMkDoc, + [MarshalAs(UnmanagedType.BStr)] out string? pbstrMkDoc, out TextSpan pspanLocation); [PreserveSig] - int ResolveName([MarshalAs(UnmanagedType.LPWStr)] string pszName, uint dwFlags, out IVsEnumDebugName ppNames); + int ResolveName([MarshalAs(UnmanagedType.LPWStr)] string? pszName, uint dwFlags, out IVsEnumDebugName? ppNames); [PreserveSig] int GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCol, out Guid pguidLanguageID); diff --git a/src/VisualStudio/Core/Def/Utilities/IVsShellExtensions.cs b/src/VisualStudio/Core/Def/Utilities/IVsShellExtensions.cs index 13486e307cffb..167641cbcdb2a 100644 --- a/src/VisualStudio/Core/Def/Utilities/IVsShellExtensions.cs +++ b/src/VisualStudio/Core/Def/Utilities/IVsShellExtensions.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. -#nullable disable - using System; -using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.Threading; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; @@ -19,30 +15,21 @@ internal static class IVsShellExtensions /// /// Returns true if devenv is invoked in command line mode for build, e.g. devenv /rebuild MySolution.sln /// - public static bool IsInCommandLineMode(JoinableTaskFactory joinableTaskFactory) + public static bool IsInCommandLineMode(this IVsShell shell) { - var result = s_isInCommandLineMode; - if (result == 0) + if (s_isInCommandLineMode == 0) { - s_isInCommandLineMode = result = joinableTaskFactory.Run(async () => - { - await joinableTaskFactory.SwitchToMainThreadAsync(); - - var shell = ServiceProvider.GlobalProvider.GetService(joinableTaskFactory); - return - (shell != null) && - ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out var result)) && - (bool)result ? 1 : -1; - }); + s_isInCommandLineMode = + ErrorHandler.Succeeded(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out var result)) && + (bool)result ? 1 : -1; } - return result == 1; + return s_isInCommandLineMode == 1; } public static bool TryGetPropertyValue(this IVsShell shell, __VSSPROPID id, out IntPtr value) { - var hresult = shell.GetProperty((int)id, out var objValue); - if (ErrorHandler.Succeeded(hresult) && objValue != null) + if (ErrorHandler.Succeeded(shell.GetProperty((int)id, out var objValue)) && objValue != null) { value = (IntPtr.Size == 4) ? (IntPtr)(int)objValue : (IntPtr)(long)objValue; return true; diff --git a/src/VisualStudio/Core/Def/Utilities/ProjectPropertyStorage.cs b/src/VisualStudio/Core/Def/Utilities/ProjectPropertyStorage.cs index 6a5f8b95e45a3..54bde706b1a77 100644 --- a/src/VisualStudio/Core/Def/Utilities/ProjectPropertyStorage.cs +++ b/src/VisualStudio/Core/Def/Utilities/ProjectPropertyStorage.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.Globalization; using EnvDTE; diff --git a/src/VisualStudio/Core/Def/Utilities/SymbolViewModel.cs b/src/VisualStudio/Core/Def/Utilities/SymbolViewModel.cs index 6f35b512ea03e..2f2a1d961cb27 100644 --- a/src/VisualStudio/Core/Def/Utilities/SymbolViewModel.cs +++ b/src/VisualStudio/Core/Def/Utilities/SymbolViewModel.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.Windows.Media; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/VisualStudio/Core/Def/Utilities/TaskItemsEnum.cs b/src/VisualStudio/Core/Def/Utilities/TaskItemsEnum.cs index f1609915e81bf..d0874bcf7d470 100644 --- a/src/VisualStudio/Core/Def/Utilities/TaskItemsEnum.cs +++ b/src/VisualStudio/Core/Def/Utilities/TaskItemsEnum.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 Microsoft.VisualStudio.Shell.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; diff --git a/src/VisualStudio/Core/Def/Utilities/VisualStudioNavigateToLinkService.cs b/src/VisualStudio/Core/Def/Utilities/VisualStudioNavigateToLinkService.cs index f6f78cbb0a273..d3f2763c361c1 100644 --- a/src/VisualStudio/Core/Def/Utilities/VisualStudioNavigateToLinkService.cs +++ b/src/VisualStudio/Core/Def/Utilities/VisualStudioNavigateToLinkService.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.Composition; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Utilities/VsDebugName.cs b/src/VisualStudio/Core/Def/Utilities/VsDebugName.cs index f6edca0ccebef..1702f78798740 100644 --- a/src/VisualStudio/Core/Def/Utilities/VsDebugName.cs +++ b/src/VisualStudio/Core/Def/Utilities/VsDebugName.cs @@ -2,19 +2,17 @@ // 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.VisualStudio.TextManager.Interop; namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; internal class VsDebugName : IVsDebugName { - private readonly string _name; + private readonly string? _name; private readonly string _document; private readonly TextSpan _textSpan; - public VsDebugName(string name, string document, TextSpan textSpan) + public VsDebugName(string? name, string document, TextSpan textSpan) { _name = name; _document = document; @@ -33,7 +31,7 @@ public int GetLocation(out string pbstrMkDoc, TextSpan[] pspanLocation) return VSConstants.S_OK; } - public int GetName(out string pbstrName) + public int GetName(out string? pbstrName) { pbstrName = _name; return VSConstants.S_OK; diff --git a/src/VisualStudio/Core/Def/Utilities/VsEnumBSTR.cs b/src/VisualStudio/Core/Def/Utilities/VsEnumBSTR.cs index 54bda28c7fafa..689ef82adc426 100644 --- a/src/VisualStudio/Core/Def/Utilities/VsEnumBSTR.cs +++ b/src/VisualStudio/Core/Def/Utilities/VsEnumBSTR.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.Collections.Generic; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/Utilities/VsEnumDebugName.cs b/src/VisualStudio/Core/Def/Utilities/VsEnumDebugName.cs index d2c30793082db..c08137d33c7aa 100644 --- a/src/VisualStudio/Core/Def/Utilities/VsEnumDebugName.cs +++ b/src/VisualStudio/Core/Def/Utilities/VsEnumDebugName.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.Collections.Generic; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs index ccf51298a64f2..c45464fba450b 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingCommandHandler.cs @@ -25,7 +25,6 @@ using Microsoft.VisualStudio.Text.Classification; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; using IAsyncServiceProvider = Microsoft.VisualStudio.Shell.IAsyncServiceProvider; using Task = System.Threading.Tasks.Task; diff --git a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs index 88420dc2c84f2..0fa4cac845d65 100644 --- a/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs +++ b/src/VisualStudio/Core/Def/ValueTracking/ValueTrackingToolWindow.cs @@ -5,7 +5,6 @@ using System; using System.Runtime.InteropServices; using Microsoft.VisualStudio.Shell; -using Roslyn.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using System.Diagnostics.CodeAnalysis; diff --git a/src/VisualStudio/Core/Def/Venus/CodeBlockEnumerator.cs b/src/VisualStudio/Core/Def/Venus/CodeBlockEnumerator.cs index 29cfe6e20be64..d1907892a28e1 100644 --- a/src/VisualStudio/Core/Def/Venus/CodeBlockEnumerator.cs +++ b/src/VisualStudio/Core/Def/Venus/CodeBlockEnumerator.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.Generic; using Microsoft.VisualStudio.TextManager.Interop; diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs index 0b511973a49b5..cf11ec5068991 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguage.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Workspaces.ProjectSystem; using Microsoft.VisualStudio.ComponentModelHost; diff --git a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs index 749fec43366db..bd85f1ad6d873 100644 --- a/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Venus/ContainedLanguageCodeSupport.cs @@ -20,7 +20,6 @@ using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/VisualStudio/Core/Def/Venus/IAdditionalFormattingRuleLanguageService.cs b/src/VisualStudio/Core/Def/Venus/IAdditionalFormattingRuleLanguageService.cs index 9a89c262a487a..eb13ec245e2d6 100644 --- a/src/VisualStudio/Core/Def/Venus/IAdditionalFormattingRuleLanguageService.cs +++ b/src/VisualStudio/Core/Def/Venus/IAdditionalFormattingRuleLanguageService.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 Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Host; diff --git a/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageCodeSupport.cs b/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageCodeSupport.cs index 2634d7722e98b..b590955da3c58 100644 --- a/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageCodeSupport.cs +++ b/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageCodeSupport.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.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageStaticEventBinding.cs b/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageStaticEventBinding.cs index b6a2b88e5fcd9..eebcb233eda55 100644 --- a/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageStaticEventBinding.cs +++ b/src/VisualStudio/Core/Def/Venus/IVsContainedLanguageStaticEventBinding.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.Runtime.CompilerServices; using System.Runtime.InteropServices; diff --git a/src/VisualStudio/Core/Def/Venus/VenusCommandFilter`2.cs b/src/VisualStudio/Core/Def/Venus/VenusCommandFilter`2.cs index f173f52e49bc5..b9fa500c66751 100644 --- a/src/VisualStudio/Core/Def/Venus/VenusCommandFilter`2.cs +++ b/src/VisualStudio/Core/Def/Venus/VenusCommandFilter`2.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 Microsoft.CodeAnalysis.Editor; using Microsoft.VisualStudio.Editor; diff --git a/src/VisualStudio/Core/Def/Venus/VenusTaskExtensions.cs b/src/VisualStudio/Core/Def/Venus/VenusTaskExtensions.cs index b63c065b23669..6295692ae8c28 100644 --- a/src/VisualStudio/Core/Def/Venus/VenusTaskExtensions.cs +++ b/src/VisualStudio/Core/Def/Venus/VenusTaskExtensions.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 Roslyn.Utilities; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.WorkspaceGlobalUndoTransaction.cs b/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.WorkspaceGlobalUndoTransaction.cs index 3b12e746f5205..2fa2e31e63ba5 100644 --- a/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.WorkspaceGlobalUndoTransaction.cs +++ b/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.WorkspaceGlobalUndoTransaction.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.Diagnostics; using System.Runtime.InteropServices; @@ -15,7 +13,6 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text.Operations; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation; diff --git a/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.cs b/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.cs index fcdcd0707d1c5..8da33360a1c82 100644 --- a/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Workspace/GlobalUndoServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs index ffce08637e1bb..a8269bd3e3cd4 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioAddMetadataReferenceCodeActionOperationFactoryWorkspaceService.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.Composition; using System.Threading; diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs index 97ec5a757a3ab..ced22ef8af18a 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioDocumentNavigationService.cs @@ -189,35 +189,30 @@ static VsTextSpan GetVsTextSpanFromPosition(SourceText text, int position, int v documentId = workspace.GetDocumentIdInCurrentContext(documentId); var solution = workspace.CurrentSolution; - var document = solution.GetDocument(documentId); - if (document == null) - { - var project = solution.GetProject(documentId.ProjectId); - if (project is null) - { - // This is a source generated document shown in Solution Explorer, but is no longer valid since - // the configuration and/or platform changed since the last generation completed. - return null; - } + var textDocument = await solution.GetTextDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - var generatedDocument = await project.GetSourceGeneratedDocumentAsync(documentId, cancellationToken).ConfigureAwait(false); - if (generatedDocument == null) - return null; + if (textDocument is null) + { + return null; + } + if (textDocument is SourceGeneratedDocument generatedDocument) + { return _sourceGeneratedFileManager.Value.GetNavigationCallback( generatedDocument, await getTextSpanForMappingAsync(generatedDocument).ConfigureAwait(false)); } // Before attempting to open the document, check if the location maps to a different file that should be opened instead. - var spanMappingService = document.DocumentServiceProvider.GetService(); - if (spanMappingService != null) + if (textDocument is Document document && + textDocument.DocumentServiceProvider.GetService() is ISpanMappingService spanMappingService) { var mappedSpanResult = await GetMappedSpanAsync( spanMappingService, document, await getTextSpanForMappingAsync(document).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + if (mappedSpanResult is { IsDefault: false } mappedSpan) { // Check if the mapped file matches one already in the workspace. diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioFormattingRuleFactoryServiceFactory.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioFormattingRuleFactoryServiceFactory.cs index b51b31da8c863..284dbb6c5f45c 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioFormattingRuleFactoryServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioFormattingRuleFactoryServiceFactory.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.Generic; using System.Composition; diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs index 6de40fd0dc600..dd9fd0caf3f98 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolNavigationService.cs @@ -154,7 +154,13 @@ internal sealed partial class VisualStudioSymbolNavigationService( ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID5.VSFPROPID_OverrideToolTip, result.DocumentTooltip)); } + // Subtle issue. We may already be in a provisional-tab. 'Showing' the window frame here will cause it to + // to take over the curren provisional-tab, cause a wait-indicators in the original to be dismissed (causing + // cancellation). To avoid that problem, we disable cancellation from this point. While not ideal, it is + // not problematic as we already forced the document to be opened here. So actually navigating to the + // location in it is effectively free. windowFrame.Show(); + cancellationToken = default; var openedDocument = textBuffer?.AsTextContainer().GetRelatedDocuments().FirstOrDefault(); if (openedDocument != null) diff --git a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs index 84ea08267b0c5..f263ac5d89f50 100644 --- a/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs +++ b/src/VisualStudio/Core/Def/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.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.Generic; using System.Composition; diff --git a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelObject_CodeGen.cs b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelObject_CodeGen.cs index e3dee51e8da54..b14e52da56eb4 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelObject_CodeGen.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelObject_CodeGen.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Editing; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel { diff --git a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs index 5fd11bf5fc331..ea55acb4d8aad 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/AbstractCodeModelService.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/src/VisualStudio/Core/Impl/CodeModel/Collections/ExternalNamespaceCollection.cs b/src/VisualStudio/Core/Impl/CodeModel/Collections/ExternalNamespaceCollection.cs index 744f0fd55967e..a9e38b50e94e5 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/Collections/ExternalNamespaceCollection.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/Collections/ExternalNamespaceCollection.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop; diff --git a/src/VisualStudio/Core/Impl/CodeModel/Collections/InheritsImplementsCollection.cs b/src/VisualStudio/Core/Impl/CodeModel/Collections/InheritsImplementsCollection.cs index 6cbdce8d418bb..7573eb4ac4957 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/Collections/InheritsImplementsCollection.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/Collections/InheritsImplementsCollection.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop; diff --git a/src/VisualStudio/Core/Impl/CodeModel/Collections/NamespaceCollection.cs b/src/VisualStudio/Core/Impl/CodeModel/Collections/NamespaceCollection.cs index 99fa14ff50151..a0ba0527b6bb9 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/Collections/NamespaceCollection.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/Collections/NamespaceCollection.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop; diff --git a/src/VisualStudio/Core/Impl/CodeModel/Collections/TypeCollection.cs b/src/VisualStudio/Core/Impl/CodeModel/Collections/TypeCollection.cs index c083e1a7863be..afad54777d0da 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/Collections/TypeCollection.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/Collections/TypeCollection.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop; diff --git a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs index d4de1304ce2e1..b0f2fbbc027e7 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections; diff --git a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel_CodeGen.cs b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel_CodeGen.cs index d0658ab9ef87e..424e20e536cea 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel_CodeGen.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/FileCodeModel_CodeGen.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; @@ -13,7 +12,6 @@ using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel { diff --git a/src/VisualStudio/Core/Impl/CodeModel/InternalElements/AbstractCodeElement.cs b/src/VisualStudio/Core/Impl/CodeModel/InternalElements/AbstractCodeElement.cs index de85623793764..5bb1f6c9100c8 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/InternalElements/AbstractCodeElement.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/InternalElements/AbstractCodeElement.cs @@ -10,7 +10,6 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices.Implementation.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities; diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index c1a74289d86d3..a857e351523db 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Shell; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageControl.xaml.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageControl.xaml.cs index c9c3231aa5756..91a28bdbf9061 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageControl.xaml.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageControl.xaml.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.ObjectModel; using System.Linq; using System.Windows; using System.Windows.Controls; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs index f4c3f6d972b2c..cd049428c31f7 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/NamingStyleOptionPageViewModel.cs @@ -5,7 +5,6 @@ #nullable disable using System.Collections.Generic; -using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs index 31f4331490dc8..1784abcef5595 100644 --- a/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs +++ b/src/VisualStudio/Core/Impl/Options/Style/NamingPreferences/SymbolSpecification/SymbolSpecificationViewModel.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs index 0cf1db496727d..53641c4e9701c 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerItem/AnalyzerItemSource.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; -using System.ComponentModel; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs index d1e4d14816e2f..0a1e4f4097683 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/AnalyzerReferenceManager.cs @@ -9,7 +9,6 @@ using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; -using Roslyn.Utilities; using VSLangProj140; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs index 14e0d8ba9f9c8..4c1ba0a29a603 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/DiagnosticItem/DiagnosticItem.BrowseObject.cs @@ -7,7 +7,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.VisualStudio.Shell; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; diff --git a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs index c667b11bce123..216cf417590c7 100644 --- a/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs +++ b/src/VisualStudio/Core/Impl/SolutionExplorer/SourceGeneratedFileItems/SourceGeneratedFileItemSource.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Threading; using Microsoft.Internal.VisualStudio.PlatformUI; using Microsoft.VisualStudio.Language.Intellisense; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer; diff --git a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs index 95062848499db..dddf16ee0e17a 100644 --- a/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs +++ b/src/VisualStudio/Core/Test.Next/Remote/SnapshotSerializationTests.cs @@ -701,12 +701,13 @@ private static AnalyzerFileReference CreateShadowCopiedAnalyzerReference(TempRoo return new AnalyzerFileReference(original, new MockShadowCopyAnalyzerAssemblyLoader(ImmutableDictionary.Empty.Add(original, shadow.Path))); } - private class MissingAnalyzerLoader() : AnalyzerAssemblyLoader([]) + private class MissingAnalyzerLoader() : IAnalyzerAssemblyLoader { - protected override string PreparePathToLoad(string fullPath) - => throw new FileNotFoundException(fullPath); + public void AddDependencyLocation(string fullPath) + { + } - protected override string PrepareSatelliteAssemblyToLoad(string fullPath, string cultureName) + public Assembly LoadFromPath(string fullPath) => throw new FileNotFoundException(fullPath); } diff --git a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs index 80ffa6718ef66..a744a24102c82 100644 --- a/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/SolutionServiceTests.cs @@ -15,7 +15,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.Test; using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Serialization; diff --git a/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Input.cs b/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Input.cs index fd4cb12b0bb53..e48091aca7ab9 100644 --- a/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Input.cs +++ b/src/VisualStudio/Core/Test.Next/UnifiedSettings/TestModel/Input.cs @@ -5,7 +5,6 @@ using System.Text.Json.Serialization; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; using Xunit; using static Microsoft.VisualStudio.LanguageServices.Options.VisualStudioOptionStorage; diff --git a/src/VisualStudio/Core/Test/DebuggerIntelliSense/CSharpDebuggerIntellisenseTests.vb b/src/VisualStudio/Core/Test/DebuggerIntelliSense/CSharpDebuggerIntellisenseTests.vb index ad273f10ae86a..30999902e27b7 100644 --- a/src/VisualStudio/Core/Test/DebuggerIntelliSense/CSharpDebuggerIntellisenseTests.vb +++ b/src/VisualStudio/Core/Test/DebuggerIntelliSense/CSharpDebuggerIntellisenseTests.vb @@ -315,6 +315,52 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.DebuggerIntelliSense End Using End Function + + Public Async Function SingleStatementBlock() As Task + Dim text = + + class Program +{ + static void Main() + { + for (int variable1 = 0; variable1 < 10; variable1++) + [|Console.Write(0);|] + int variable2 = 0; + } +} + + + + Using state = TestState.CreateCSharpTestState(text, False) + state.SendTypeChars("variable") + Await state.WaitForAsynchronousOperationsAsync() + Await state.AssertCompletionItemsContainAll("variable1", "variable2") + End Using + End Function + + + Public Async Function SingleStatementBlockInvokeCompletion() As Task + Dim text = + + class Program +{ + static void Main() + { + for (int variable1 = 0; variable1 < 10; variable1++) + [|Console.Write(0);|] + int variable2 = 0; + } +} + + + + Using state = TestState.CreateCSharpTestState(text, False) + Await state.WaitForAsynchronousOperationsAsync() + state.SendInvokeCompletionList() + Await state.AssertCompletionItemsContainAll("variable1") + End Using + End Function + Public Async Function SignatureHelpInParameterizedConstructor() As Task Dim text = diff --git a/src/VisualStudio/Core/Test/ExtractInterface/ExtractInterfaceViewModelTests.vb b/src/VisualStudio/Core/Test/ExtractInterface/ExtractInterfaceViewModelTests.vb index 02da64ac88ed5..56026455d00db 100644 --- a/src/VisualStudio/Core/Test/ExtractInterface/ExtractInterfaceViewModelTests.vb +++ b/src/VisualStudio/Core/Test/ExtractInterface/ExtractInterfaceViewModelTests.vb @@ -265,7 +265,7 @@ public class $$MyClass }"]]> Dim viewModel = Await GetViewModelAsync(markup, LanguageNames.CSharp, "IMyClass") - Assert.Equal(5, viewModel.MemberContainers.Count) + Assert.Equal(5, viewModel.MemberContainers.Length) Assert.Equal("Goo()", viewModel.MemberContainers.ElementAt(0).SymbolName) Assert.Equal("Goo(int)", viewModel.MemberContainers.ElementAt(1).SymbolName) Assert.Equal("Goo(int, int)", viewModel.MemberContainers.ElementAt(2).SymbolName) @@ -291,7 +291,7 @@ public class $$MyClass Using workspace = EditorTestWorkspace.Create(workspaceXml) Dim doc = workspace.Documents.Single() Dim workspaceDoc = workspace.CurrentSolution.GetDocument(doc.Id) - If (Not doc.CursorPosition.HasValue) Then + If Not doc.CursorPosition.HasValue Then Assert.True(False, "Missing caret location in document.") End If @@ -300,16 +300,15 @@ public class $$MyClass Dim symbol = (Await workspaceDoc.GetSemanticModelAsync()).GetDeclaredSymbol(token.Parent) Dim extractableMembers = DirectCast(symbol, INamedTypeSymbol).GetMembers().Where(Function(s) Not (TypeOf s Is IMethodSymbol) OrElse DirectCast(s, IMethodSymbol).MethodKind <> MethodKind.Constructor) - Dim memberViewModels = extractableMembers.Select(Function(member As ISymbol) - Return New MemberSymbolViewModel(member, Nothing) - End Function) + Dim memberViewModels = extractableMembers.Select( + Function(member As ISymbol) New MemberSymbolViewModel(member, Nothing)) Return New ExtractInterfaceDialogViewModel( workspaceDoc.Project.Services.GetService(Of ISyntaxFactsService)(), notificationService:=New TestNotificationService(), uiThreadOperationExecutor:=Nothing, defaultInterfaceName:=defaultInterfaceName, - conflictingTypeNames:=If(conflictingTypeNames, New List(Of String)), + conflictingTypeNames:=conflictingTypeNames.AsImmutableOrEmpty(), memberViewModels:=memberViewModels.ToImmutableArray(), defaultNamespace:=defaultNamespace, generatedNameTypeParameterSuffix:=generatedNameTypeParameterSuffix, diff --git a/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchPresenterController.cs b/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchPresenterController.cs new file mode 100644 index 0000000000000..72eadbdbd536c --- /dev/null +++ b/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchPresenterController.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.SemanticSearch; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.SemanticSearch; + +[Export(typeof(ICopilotSemanticSearchPresenterController)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CopilotSemanticSearchPresenterController(Lazy controller) : ICopilotSemanticSearchPresenterController +{ + public Task ExecuteQueryAsync(string query, CancellationToken cancellationToken) + => controller.Value.ExecuteQueryAsync(query, cancellationToken); +} diff --git a/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchWindowController.cs b/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchWindowController.cs new file mode 100644 index 0000000000000..f29bdeabc2d81 --- /dev/null +++ b/src/VisualStudio/ExternalAccess/Copilot/Internal/SemanticSearch/CopilotSemanticSearchWindowController.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.SemanticSearch; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.Internal.SemanticSearch; + +[Export(typeof(ICopilotSemanticSearchWindowController)), Shared] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CopilotSemanticSearchWindowController(Lazy controller) : ICopilotSemanticSearchWindowController +{ + public Task UpdateQueryAsync(string query, bool activateWindow, bool executeQuery, CancellationToken cancellationToken) + => controller.Value.UpdateQueryAsync(query, activateWindow, executeQuery, cancellationToken); +} diff --git a/src/VisualStudio/ExternalAccess/Copilot/InternalAPI.Unshipped.txt b/src/VisualStudio/ExternalAccess/Copilot/InternalAPI.Unshipped.txt index d14e794951821..681ffe801e70b 100644 --- a/src/VisualStudio/ExternalAccess/Copilot/InternalAPI.Unshipped.txt +++ b/src/VisualStudio/ExternalAccess/Copilot/InternalAPI.Unshipped.txt @@ -1,4 +1,8 @@ #nullable enable +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchPresenterController +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchPresenterController.ExecuteQueryAsync(string! query, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchWindowController +Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ICopilotSemanticSearchWindowController.UpdateQueryAsync(string! query, bool activateWindow, bool executeQuery, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task! Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotUIProviderImpl Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ISemanticSearchCopilotUIProviderImpl.GetTextBox() -> Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ITextBoxControlImpl! Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch.ITextBoxControlImpl diff --git a/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchPresenterController.cs b/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchPresenterController.cs new file mode 100644 index 0000000000000..d0494d5fa212a --- /dev/null +++ b/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchPresenterController.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; + +internal interface ICopilotSemanticSearchPresenterController +{ + /// + /// Executes semantic seqech and presents the results in Find Results window. + /// + Task ExecuteQueryAsync(string query, CancellationToken cancellationToken); +} diff --git a/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchWindowController.cs b/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchWindowController.cs new file mode 100644 index 0000000000000..0a606e6059a77 --- /dev/null +++ b/src/VisualStudio/ExternalAccess/Copilot/SemanticSearch/ICopilotSemanticSearchWindowController.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.Copilot.SemanticSearch; + +internal interface ICopilotSemanticSearchWindowController +{ + /// + /// Updates the query in Semantic Search window editor. + /// + /// True to show the window and set focus. + /// True to trigger execution. + Task UpdateQueryAsync(string query, bool activateWindow, bool executeQuery, CancellationToken cancellationToken); +} diff --git a/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCommonCompletionProvider.cs b/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCommonCompletionProvider.cs index a6361b67347c1..22229d4af262d 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCommonCompletionProvider.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCommonCompletionProvider.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Completion; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCompletionProviderBase.cs b/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCompletionProviderBase.cs index ea1f447b7bf56..d93a2707d626e 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCompletionProviderBase.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Completion/FSharpCompletionProviderBase.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorFormattingService.cs b/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorFormattingService.cs index 582636f404e6f..8093f3e66d412 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorFormattingService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorFormattingService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorInlineRenameService.cs b/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorInlineRenameService.cs index fc2346566e0b8..e11887167346b 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorInlineRenameService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Editor/IFSharpEditorInlineRenameService.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Text; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameInfo.cs b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameInfo.cs index 3127f9f685dfb..fb30aaad68c73 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameInfo.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameInfo.cs @@ -4,14 +4,12 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Text; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameLocationSet.cs b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameLocationSet.cs index dc6bb255efc47..4c4973457cba3 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameLocationSet.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameLocationSet.cs @@ -4,13 +4,11 @@ #nullable disable -using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Rename; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor diff --git a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameReplacementInfo.cs b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameReplacementInfo.cs index 035d18ef86c94..18f59678164a7 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameReplacementInfo.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Editor/InlineRename/FSharpInlineRenameReplacementInfo.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.Editor; diff --git a/src/VisualStudio/ExternalAccess/FSharp/FSharpEditorFeaturesResources.cs b/src/VisualStudio/ExternalAccess/FSharp/FSharpEditorFeaturesResources.cs index 876c418d31490..583af63031447 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/FSharpEditorFeaturesResources.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/FSharpEditorFeaturesResources.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using Microsoft.CodeAnalysis.Editor; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp { diff --git a/src/VisualStudio/ExternalAccess/FSharp/FSharpGlobalOptions.cs b/src/VisualStudio/ExternalAccess/FSharp/FSharpGlobalOptions.cs index 1735f0020ece9..5aa996990a7c1 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/FSharpGlobalOptions.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/FSharpGlobalOptions.cs @@ -5,11 +5,9 @@ using System; using System.Composition; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.VisualStudio.LanguageServices; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/CommentSelection/FSharpCommentSelectionService.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/CommentSelection/FSharpCommentSelectionService.cs index a22ab48dce0d8..0aca50b9849e0 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/CommentSelection/FSharpCommentSelectionService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/CommentSelection/FSharpCommentSelectionService.cs @@ -5,14 +5,9 @@ #nullable disable using System; -using System.Collections.Immutable; using System.Composition; -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CommentSelection; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.CommentSelection { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs index 7cf82fd0244cd..2d9cf7e135052 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpDocumentDiagnosticAnalyzer.cs @@ -9,13 +9,10 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Diagnostics { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs index 5de0fc5b156ca..cf30efc0f9515 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpSimplifyNameDiagnosticAnalyzer.cs @@ -9,13 +9,10 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Diagnostics { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs index 37a35d397f322..334729930054a 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Diagnostics/FSharpUnusedDeclarationsAnalyzer.cs @@ -9,13 +9,10 @@ using System.Composition; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Simplification; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Diagnostics { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/DocumentHighlighting/FSharpDocumentHighlightsService.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/DocumentHighlighting/FSharpDocumentHighlightsService.cs index ba9ed55d038ba..a7e95ea7dc962 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/DocumentHighlighting/FSharpDocumentHighlightsService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/DocumentHighlighting/FSharpDocumentHighlightsService.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Linq; using System.Composition; using System.Collections.Immutable; using System.Threading; @@ -13,7 +12,6 @@ using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.DocumentHighlighting; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.DocumentHighlighting { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpEditorInlineRenameService.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpEditorInlineRenameService.cs index 0fc2d99295a31..aed68094921ca 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpEditorInlineRenameService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpEditorInlineRenameService.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Editor { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpSmartIndentProvider.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpSmartIndentProvider.cs index 7aff5846e719c..25d50eb1c19a0 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpSmartIndentProvider.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Editor/FSharpSmartIndentProvider.cs @@ -8,19 +8,16 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Implementation.SmartIndent; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; -using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; using Microsoft.VisualStudio.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Editor { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/FSharpGlyphHelpers.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/FSharpGlyphHelpers.cs index 2153890b08d1d..1158717a998ed 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/FSharpGlyphHelpers.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/FSharpGlyphHelpers.cs @@ -4,9 +4,6 @@ #nullable disable -using System; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal { internal static class FSharpGlyphHelpers diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/NavigateTo/FSharpNavigateToMatchKindHelpers.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/NavigateTo/FSharpNavigateToMatchKindHelpers.cs index 176b4218ee131..66761ef5ec483 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/NavigateTo/FSharpNavigateToMatchKindHelpers.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/NavigateTo/FSharpNavigateToMatchKindHelpers.cs @@ -4,10 +4,8 @@ #nullable disable -using System; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.NavigateTo; using Microsoft.CodeAnalysis.NavigateTo; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.NavigateTo { diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs index f2ae319723ab8..5d05d69b38047 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/Navigation/InternalFSharpNavigableItem.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Navigation; using Microsoft.CodeAnalysis.Navigation; diff --git a/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpTriggerReasonHelpers.cs b/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpTriggerReasonHelpers.cs index 17c7bae8e540e..34d3528e4fe49 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpTriggerReasonHelpers.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/Internal/SignatureHelp/FSharpSignatureHelpTriggerReasonHelpers.cs @@ -4,10 +4,8 @@ #nullable disable -using System; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp; using Microsoft.CodeAnalysis.SignatureHelp; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.SignatureHelp { diff --git a/src/VisualStudio/ExternalAccess/FSharp/SignatureHelp/FSharpSignatureHelpTriggerInfo.cs b/src/VisualStudio/ExternalAccess/FSharp/SignatureHelp/FSharpSignatureHelpTriggerInfo.cs index 0da39751d10a5..97b9f0efb206b 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/SignatureHelp/FSharpSignatureHelpTriggerInfo.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/SignatureHelp/FSharpSignatureHelpTriggerInfo.cs @@ -4,8 +4,6 @@ #nullable disable -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp { internal readonly struct FSharpSignatureHelpTriggerInfo diff --git a/src/VisualStudio/ExternalAccess/FSharp/TaskList/FSharpTaskListService.cs b/src/VisualStudio/ExternalAccess/FSharp/TaskList/FSharpTaskListService.cs index 9fbe5e1c26d78..0a102853382f6 100644 --- a/src/VisualStudio/ExternalAccess/FSharp/TaskList/FSharpTaskListService.cs +++ b/src/VisualStudio/ExternalAccess/FSharp/TaskList/FSharpTaskListService.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.TaskList; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.TaskList { diff --git a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpGlyphTests.cs b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpGlyphTests.cs index c5397ca03debe..3ae76d57f0ba0 100644 --- a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpGlyphTests.cs +++ b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpGlyphTests.cs @@ -7,7 +7,6 @@ using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests diff --git a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpHighlightSpanKindTests.cs b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpHighlightSpanKindTests.cs index 732d582ba51db..2375287299314 100644 --- a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpHighlightSpanKindTests.cs +++ b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpHighlightSpanKindTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.DocumentHighlighting; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.DocumentHighlighting; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.DocumentHighlighting; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests diff --git a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpInlineRenameReplacementKindTests.cs b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpInlineRenameReplacementKindTests.cs index d428272bd08bf..8f39b9de10ef3 100644 --- a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpInlineRenameReplacementKindTests.cs +++ b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpInlineRenameReplacementKindTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Editor; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.Editor; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests diff --git a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpNavigateToMatchKindTests.cs b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpNavigateToMatchKindTests.cs index 8511cbb1fc26e..ead2e5d466a0c 100644 --- a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpNavigateToMatchKindTests.cs +++ b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpNavigateToMatchKindTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.NavigateTo; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.NavigateTo; using Microsoft.CodeAnalysis.NavigateTo; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests diff --git a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpSignatureHelpTriggerReasonTests.cs b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpSignatureHelpTriggerReasonTests.cs index ab990c7ab274e..db9c90e2cc70a 100644 --- a/src/VisualStudio/ExternalAccess/FSharpTest/FSharpSignatureHelpTriggerReasonTests.cs +++ b/src/VisualStudio/ExternalAccess/FSharpTest/FSharpSignatureHelpTriggerReasonTests.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.ExternalAccess.FSharp.Internal.SignatureHelp; using Microsoft.CodeAnalysis.ExternalAccess.FSharp.SignatureHelp; using Microsoft.CodeAnalysis.SignatureHelp; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.ExternalAccess.FSharp.UnitTests diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs index b8263d60c0a38..caa3edce69170 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpFindReferences.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -11,7 +10,6 @@ using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.VisualStudio.LanguageServices; -using Microsoft.VisualStudio.Shell.TableControl; using Microsoft.VisualStudio.Shell.TableManager; using Roslyn.VisualStudio.IntegrationTests; using WindowsInput.Native; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs index 234ef73c140d9..7dc154a768b0b 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpGoToDefinition.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.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Structure; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.Shell.TableControl; using Roslyn.Test.Utilities; using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.IntegrationTests.InProcess; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpIntelliSense.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpIntelliSense.cs index 42c0b6d5561f7..dca2852b8ab66 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpIntelliSense.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpIntelliSense.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs index 080b2c5a3da4c..edd87ecde682c 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/CSharp/CSharpRename.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ChangeSignatureDialogInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ChangeSignatureDialogInProcess.cs index 79a35e3673c7b..7b0c2a1d56d94 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ChangeSignatureDialogInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/ChangeSignatureDialogInProcess.cs @@ -14,7 +14,6 @@ using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Microsoft.VisualStudio.LanguageServices.Implementation.ChangeSignature; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using WindowsInput.Native; namespace Roslyn.VisualStudio.NewIntegrationTests.InProcess; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/FindReferencesWindowInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/FindReferencesWindowInProcess.cs index 504735e831152..9eac15176e8fe 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/FindReferencesWindowInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/FindReferencesWindowInProcess.cs @@ -82,7 +82,7 @@ private async Task GetFindReferencesWindowAsync(CancellationT // Dig through to get the Find References control. var toolWindowType = toolWindow.GetType(); - var toolWindowControlField = toolWindowType.GetField("Control"); + var toolWindowControlField = toolWindowType.GetField("_control") ?? toolWindowType.GetField("Control"); var toolWindowControl = toolWindowControlField.GetValue(toolWindow); // Dig further to get the results table (as opposed to the toolbar). diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InlineRenameInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InlineRenameInProcess.cs index 3659285cce716..94712072f37ef 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InlineRenameInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/InlineRenameInProcess.cs @@ -12,7 +12,6 @@ using Microsoft.VisualStudio; using Microsoft.VisualStudio.Extensibility.Testing; using Microsoft.VisualStudio.TextManager.Interop; -using Roslyn.Utilities; using WindowsInput.Native; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/MoveToNamespaceDialogInProcess.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/MoveToNamespaceDialogInProcess.cs index ddd59b5351e8d..41455eb72ba30 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/MoveToNamespaceDialogInProcess.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InProcess/MoveToNamespaceDialogInProcess.cs @@ -14,7 +14,6 @@ using Microsoft.VisualStudio.IntegrationTest.Utilities.Input; using Microsoft.VisualStudio.LanguageServices.Implementation.MoveToNamespace; using Roslyn.Test.Utilities; -using Roslyn.Utilities; namespace Roslyn.VisualStudio.NewIntegrationTests.InProcess; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InfrastructureTests.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InfrastructureTests.cs index 49c93cc14b3aa..e531c36cca73e 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/InfrastructureTests.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/InfrastructureTests.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.IntegrationTest.Utilities; -using Roslyn.Utilities; using Roslyn.VisualStudio.IntegrationTests; using WindowsInput.Native; using Xunit; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs index 2dbb4cc23fd41..d2bb4e39d757e 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicFindReferences.cs @@ -2,11 +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.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.VisualStudio.Shell.TableControl; using Roslyn.VisualStudio.IntegrationTests; using Roslyn.VisualStudio.IntegrationTests.InProcess; using WindowsInput.Native; diff --git a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicIntelliSense.cs b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicIntelliSense.cs index 342d230c979da..fc96edbb79803 100644 --- a/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicIntelliSense.cs +++ b/src/VisualStudio/IntegrationTest/New.IntegrationTests/VisualBasic/BasicIntelliSense.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/VisualStudio/IntegrationTest/TestSetup/TestExtensionErrorHandler.cs b/src/VisualStudio/IntegrationTest/TestSetup/TestExtensionErrorHandler.cs index d716a590c0142..de1df8ff5afcc 100644 --- a/src/VisualStudio/IntegrationTest/TestSetup/TestExtensionErrorHandler.cs +++ b/src/VisualStudio/IntegrationTest/TestSetup/TestExtensionErrorHandler.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Text; @@ -23,25 +22,9 @@ public TestExtensionErrorHandler() public void HandleError(object sender, Exception exception) { - if (exception is ArgumentException argumentException - && argumentException.Message.Contains("SnapshotPoint") - && argumentException.StackTrace.Contains("Microsoft.VisualStudio.Text.Editor.Implementation.WpfTextView.ValidateBufferPosition")) + if (exception.Message == "RemotePartyTerminated" && new System.Diagnostics.StackTrace().ToString().Contains("CodeLens") || + exception.Message == "Cannot access a disposed object.\r\nObject name: 'CodeLensHubClient'.") { - // Known issue https://github.com/dotnet/roslyn/issues/35123 - return; - } - - if (exception is TaskCanceledException taskCanceledException - && taskCanceledException.StackTrace.Contains("Microsoft.CodeAnalysis.Editor.Implementation.Suggestions.SuggestedActionsSourceProvider.SuggestedActionsSource.GetSuggestedActions")) - { - // Workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1070469 - return; - } - - if (exception is ObjectDisposedException objectDisposedException - && objectDisposedException.StackTrace.Contains("Microsoft.VisualStudio.Text.IntraTextTaggerAggregator.Implementation.IntraTextAdornmentTagger")) - { - // Workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1935805 return; } diff --git a/src/VisualStudio/LiveShare/Test/LiveShareTestCompositions.cs b/src/VisualStudio/LiveShare/Test/LiveShareTestCompositions.cs index 20186a69a18ad..f693345a0d82c 100644 --- a/src/VisualStudio/LiveShare/Test/LiveShareTestCompositions.cs +++ b/src/VisualStudio/LiveShare/Test/LiveShareTestCompositions.cs @@ -4,7 +4,6 @@ #nullable disable -using Microsoft.CodeAnalysis.Editor.UnitTests; using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.VisualStudio.LanguageServices.LiveShare.UnitTests diff --git a/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj b/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj index b2f087b84e51f..c6a5af7de2024 100644 --- a/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj +++ b/src/VisualStudio/Setup.Dependencies/Roslyn.VisualStudio.Setup.Dependencies.csproj @@ -22,27 +22,52 @@ This project deploys them to RoslynDev hive to enable F5 scenario, but the resulting VSIX not inserted into VS. --> - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VisualStudio/Setup.ServiceHub/arm64/Roslyn.VisualStudio.ServiceHub.Setup.arm64.csproj b/src/VisualStudio/Setup.ServiceHub/arm64/Roslyn.VisualStudio.ServiceHub.Setup.arm64.csproj index cf34113c6e103..4d504f41a8884 100644 --- a/src/VisualStudio/Setup.ServiceHub/arm64/Roslyn.VisualStudio.ServiceHub.Setup.arm64.csproj +++ b/src/VisualStudio/Setup.ServiceHub/arm64/Roslyn.VisualStudio.ServiceHub.Setup.arm64.csproj @@ -22,6 +22,7 @@ false PublishedProjectOutputGroup + true false false diff --git a/src/VisualStudio/Setup.ServiceHub/x64/Roslyn.VisualStudio.ServiceHub.Setup.x64.csproj b/src/VisualStudio/Setup.ServiceHub/x64/Roslyn.VisualStudio.ServiceHub.Setup.x64.csproj index 214f738f1e1de..ae6b88ad67898 100644 --- a/src/VisualStudio/Setup.ServiceHub/x64/Roslyn.VisualStudio.ServiceHub.Setup.x64.csproj +++ b/src/VisualStudio/Setup.ServiceHub/x64/Roslyn.VisualStudio.ServiceHub.Setup.x64.csproj @@ -14,6 +14,7 @@ false PublishedProjectOutputGroup + true false false diff --git a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj index d54d721a87fa1..aa03f6f3285ef 100644 --- a/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj +++ b/src/VisualStudio/Setup/Roslyn.VisualStudio.Setup.csproj @@ -79,7 +79,7 @@ true BindingRedirect - + Microsoft.CodeAnalysis.ExternalAccess.Razor BuiltProjectOutputGroup true @@ -286,6 +286,7 @@ InteractiveHost\Core PublishedProjectOutputGroup + true false @@ -301,6 +302,7 @@ InteractiveHost\Desktop PublishedProjectOutputGroup + true InteractiveHost.Desktop32 @@ -316,6 +318,7 @@ InteractiveHost\Desktop PublishedProjectOutputGroup + true SemanticSearchRefs @@ -329,14 +332,14 @@ - - - - + + + + - - - + + + diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodePageEditorFactory.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodePageEditorFactory.vb index 626c5d46813ab..e6db83887e5a1 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodePageEditorFactory.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicCodePageEditorFactory.vb @@ -7,7 +7,7 @@ Imports Microsoft.VisualStudio.LanguageServices.Implementation Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic - Friend Class VisualBasicCodePageEditorFactory + Friend NotInheritable Class VisualBasicCodePageEditorFactory Inherits AbstractCodePageEditorFactory Public Sub New(editorFactory As AbstractEditorFactory) diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicDebuggerIntelliSenseContext.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicDebuggerIntelliSenseContext.vb index 63acb0c9c2533..e1948413d2047 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicDebuggerIntelliSenseContext.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicDebuggerIntelliSenseContext.vb @@ -22,6 +22,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Inherits AbstractDebuggerIntelliSenseContext Private _innerMostContainingNodeIsExpression As Boolean + Private Const StatementTerminator As String = vbCrLf Public Sub New(wpfTextView As IWpfTextView, vsTextView As IVsTextView, @@ -58,9 +59,10 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic isImmediateWindow) End Sub - Protected Overrides Function GetAdjustedContextPoint(contextPoint As Integer, document As Document) As Integer + Protected Overrides Function GetAdjustedBuffer(contextPoint As Integer, document As Document, debuggerMappedSpan As ITrackingSpan) As IProjectionBuffer Dim tree = document.GetSyntaxTreeSynchronously(CancellationToken.None) Dim token = tree.FindTokenOnLeftOfPosition(contextPoint, CancellationToken.None) + Dim adjustedStart = token.FullSpan.End Dim containingNode = token.Parent.AncestorsAndSelf().Where(Function(s) TypeOf s Is ExpressionSyntax OrElse TypeOf s Is MethodBaseSyntax OrElse @@ -68,23 +70,30 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic If containingNode IsNot Nothing Then If TypeOf containingNode Is ExpressionSyntax AndAlso Not IsRightSideOfLocalDeclaration(containingNode) Then _innerMostContainingNodeIsExpression = True - Return containingNode.Span.End + adjustedStart = containingNode.Span.End Else Dim statement = containingNode.GetExecutableBlockStatements().FirstOrDefault() If statement IsNot Nothing Then - Return statement.FullSpan.End - ElseIf TypeOf containingNode Is MethodBlockBaseSyntax + adjustedStart = statement.FullSpan.End + ElseIf TypeOf containingNode Is MethodBlockBaseSyntax Then ' Something like ' Sub Goo(o as integer) ' [| End Sub |] - Return DirectCast(containingNode, MethodBlockBaseSyntax).EndBlockStatement.SpanStart + adjustedStart = DirectCast(containingNode, MethodBlockBaseSyntax).EndBlockStatement.SpanStart Else - Return containingNode.Span.End + adjustedStart = containingNode.Span.End End If End If End If - Return token.FullSpan.End + Dim beforeAdjustedStart = GetPreviousStatementBufferAndSpan(adjustedStart, document) + Dim afterAdjustedStart = ContextBuffer.CurrentSnapshot.CreateTrackingSpanFromIndexToEnd(adjustedStart, SpanTrackingMode.EdgePositive) + + Return ProjectionBufferFactoryService.CreateProjectionBuffer( + projectionEditResolver:=Nothing, + sourceSpans:={beforeAdjustedStart, debuggerMappedSpan, StatementTerminator, afterAdjustedStart}, + options:=ProjectionBufferOptions.None, + contentType:=ContentType) End Function Private Shared Function IsRightSideOfLocalDeclaration(containingNode As SyntaxNode) As Boolean @@ -105,7 +114,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Return False End Function - Protected Overrides Function GetPreviousStatementBufferAndSpan(contextPoint As Integer, document As Document) As ITrackingSpan + Private Function GetPreviousStatementBufferAndSpan(contextPoint As Integer, document As Document) As ITrackingSpan ' This text can be validly inserted at the end of an expression context to allow ' intellisense to trigger a new expression context Dim forceExpressionContext = ".__o(" @@ -133,11 +142,5 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Return True End Get End Property - - Protected Overrides ReadOnly Property StatementTerminator As String - Get - Return vbCrLf - End Get - End Property End Class End Namespace diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicEditorFactory.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicEditorFactory.vb index 06e5fd09457e3..f4d974bf94c6c 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicEditorFactory.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicEditorFactory.vb @@ -5,7 +5,6 @@ Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor -Imports Microsoft.CodeAnalysis.Shared.Extensions Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.VisualStudio.ComponentModelHost Imports Microsoft.VisualStudio.LanguageServices.Implementation diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb index f03d6a481434d..3df609cae372b 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb @@ -7,7 +7,6 @@ Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ErrorReporting Imports Microsoft.CodeAnalysis.Options -Imports Microsoft.VisualStudio.LanguageServices.Implementation Imports Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService Imports Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.ObjectBrowser @@ -83,12 +82,12 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic End Try End Function - Protected Overrides Async Function RegisterObjectBrowserLibraryManagerAsync(cancellationToken As CancellationToken) As Task + Protected Overrides Sub RegisterObjectBrowserLibraryManager() Dim workspace As VisualStudioWorkspace = ComponentModel.GetService(Of VisualStudioWorkspace)() - Await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken) + Contract.ThrowIfFalse(JoinableTaskFactory.Context.IsOnMainThread) - Dim objectManager = TryCast(Await GetServiceAsync(GetType(SVsObjectManager)).ConfigureAwait(True), IVsObjectManager2) + Dim objectManager = TryCast(GetService(GetType(SVsObjectManager)), IVsObjectManager2) If objectManager IsNot Nothing Then Me._libraryManager = New ObjectBrowserLibraryManager(Me, ComponentModel, workspace) @@ -96,13 +95,13 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Me._libraryManagerCookie = 0 End If End If - End Function + End Sub - Protected Overrides Async Function UnregisterObjectBrowserLibraryManagerAsync(cancellationToken As CancellationToken) As Task - Await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken) + Protected Overrides Sub UnregisterObjectBrowserLibraryManager() + Contract.ThrowIfFalse(JoinableTaskFactory.Context.IsOnMainThread) If _libraryManagerCookie <> 0 Then - Dim objectManager = TryCast(Await GetServiceAsync(GetType(SVsObjectManager)).ConfigureAwait(True), IVsObjectManager2) + Dim objectManager = TryCast(GetService(GetType(SVsObjectManager)), IVsObjectManager2) If objectManager IsNot Nothing Then objectManager.UnregisterLibrary(Me._libraryManagerCookie) Me._libraryManagerCookie = 0 @@ -111,7 +110,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Me._libraryManager.Dispose() Me._libraryManager = Nothing End If - End Function + End Sub Public Function NeedExport(pageID As String, ByRef needExportParam As Integer) As Integer Implements IVsUserSettingsQuery.NeedExport ' We need to override MPF's definition of NeedExport since it doesn't know about our automation object diff --git a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryModePage.cs b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryModePage.cs index 0acde61bcfe15..83668df1fbde5 100644 --- a/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryModePage.cs +++ b/src/VisualStudio/VisualStudioDiagnosticsToolWindow/OptionPages/ForceLowMemoryModePage.cs @@ -7,7 +7,6 @@ using System; using System.Runtime.InteropServices; using System.Windows.Controls; -using Microsoft.CodeAnalysis.Options; using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.LanguageServices.Implementation.Options; diff --git a/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlRemoveUnnecessaryImportsService.cs b/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlRemoveUnnecessaryImportsService.cs index 97d01f20e52c6..a82020c5da29b 100644 --- a/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlRemoveUnnecessaryImportsService.cs +++ b/src/VisualStudio/Xaml/Impl/Features/OrganizeImports/XamlRemoveUnnecessaryImportsService.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.Xaml.Features.OrganizeImports; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.RemoveUnnecessaryImports; diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs index 3b1b10ba43021..0c08b8ba1f0b5 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Completion/CompletionResolveHandler.cs @@ -18,7 +18,6 @@ using Newtonsoft.Json.Linq; using Roslyn.LanguageServer.Protocol; using Roslyn.Text.Adornments; -using Roslyn.Utilities; using LSP = Roslyn.LanguageServer.Protocol; namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs index 3bf3f21307bda..9ddc087650f3f 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/Diagnostics/WorkspacePullDiagnosticHandler.cs @@ -14,7 +14,6 @@ using Microsoft.VisualStudio.LanguageServices.Xaml.Features.Diagnostics; using Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Extensions; using Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer; -using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Xaml.Implementation.LanguageServer.Handler.Diagnostics { diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs index ab75b1ca74f47..ab12574e46705 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnAutoInsert/OnAutoInsertHandler.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Roslyn.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.AutoInsert; -using Roslyn.Utilities; using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs index d185a95657da5..2265786ce5de2 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/Handler/OnTypeRename/OnTypeRenameHandler.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageServer.Handler; using Roslyn.LanguageServer.Protocol; using Microsoft.VisualStudio.LanguageServices.Xaml.Features.TypeRename; -using Roslyn.Utilities; using Microsoft.CodeAnalysis; namespace Microsoft.VisualStudio.LanguageServices.Xaml.LanguageServer.Handler diff --git a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs index d973ca8e34384..4b45c16dd99a9 100644 --- a/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs +++ b/src/VisualStudio/Xaml/Impl/Implementation/LanguageServer/XamlRequestExecutionQueue.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.LanguageServer; using Microsoft.CodeAnalysis.LanguageServer.Handler; using Microsoft.CommonLanguageServerProtocol.Framework; diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index d5c6575451d44..aa610663a1295 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -1613,8 +1613,8 @@ public override DeclarationModifiers GetModifiers(SyntaxNode declaration) private static SyntaxTokenList GetModifierTokens(SyntaxNode declaration) => CSharpAccessibilityFacts.GetModifierTokens(declaration); - public override SyntaxNode WithModifiers(SyntaxNode declaration, DeclarationModifiers modifiers) - => this.Isolate(declaration, d => this.WithModifiersInternal(d, modifiers)); + internal override TSyntaxNode WithModifiers(TSyntaxNode declaration, DeclarationModifiers modifiers) + => (TSyntaxNode)this.Isolate(declaration, d => this.WithModifiersInternal(d, modifiers)); private SyntaxNode WithModifiersInternal(SyntaxNode declaration, DeclarationModifiers modifiers) { diff --git a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs index 104f5e5fd9889..b24c3bc2cb5d3 100644 --- a/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs +++ b/src/Workspaces/CSharp/Portable/FindSymbols/CSharpDeclaredSymbolInfoFactoryService.cs @@ -218,6 +218,12 @@ protected override void AddLocalFunctionInfos( return null; } + // Extensions don't declare a type of their own. As they have no name, it's not something someone could search + // for with navigate-to. Instead, they just act as a loose block around a set of actual extension members. So + // just return null here to avoid creating anything in this case. + if (typeDeclaration.Kind() == SyntaxKind.ExtensionDeclaration) + return null; + return DeclaredSymbolInfo.Create( stringTable, typeDeclaration.Identifier.ValueText, diff --git a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs index 8e0238e4d88b8..1af732a124a4b 100644 --- a/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs +++ b/src/Workspaces/CSharp/Portable/Formatting/CSharpSyntaxFormattingService.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj index 92415ddba7007..02fadfdfe88b3 100644 --- a/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj +++ b/src/Workspaces/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.Workspaces.csproj @@ -45,6 +45,7 @@ + diff --git a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs index 39aaa3c9a5283..a901004ab6d2c 100644 --- a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs +++ b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.Rewriter.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.OrganizeImports; diff --git a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs index 8cbd26e4b51db..8a67c11256287 100644 --- a/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs +++ b/src/Workspaces/CSharp/Portable/OrganizeImports/CSharpOrganizeImportsService.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.OrganizeImports; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.OrganizeImports; diff --git a/src/Workspaces/CSharp/Portable/ReassignedVariable/CSharpReassignedVariableService.cs b/src/Workspaces/CSharp/Portable/ReassignedVariable/CSharpReassignedVariableService.cs index 5be7ce3817c6a..1f1add0365ed4 100644 --- a/src/Workspaces/CSharp/Portable/ReassignedVariable/CSharpReassignedVariableService.cs +++ b/src/Workspaces/CSharp/Portable/ReassignedVariable/CSharpReassignedVariableService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.ReassignedVariable; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.ReassignedVariable; diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 313e9b4435a0f..32a61d36fbf79 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -399,17 +399,16 @@ private SyntaxToken RenameAndAnnotate(SyntaxToken token, SyntaxToken newToken, b var isMemberGroupReference = _semanticFactsService.IsInsideNameOfExpression(_semanticModel, token.Parent, _cancellationToken); - var renameAnnotation = - new RenameActionAnnotation( - token.Span, - isRenameLocation, - prefix, - suffix, - renameDeclarationLocations: renameDeclarationLocations, - isOriginalTextLocation: isOldText, - isNamespaceDeclarationReference: isNamespaceDeclarationReference, - isInvocationExpression: false, - isMemberGroupReference: isMemberGroupReference); + var renameAnnotation = new RenameActionAnnotation( + token.Span, + isRenameLocation, + prefix, + suffix, + renameDeclarationLocations: renameDeclarationLocations, + isOriginalTextLocation: isOldText, + isNamespaceDeclarationReference: isNamespaceDeclarationReference, + isInvocationExpression: false, + isMemberGroupReference: isMemberGroupReference); newToken = _renameAnnotations.WithAdditionalAnnotations(newToken, renameAnnotation, new RenameTokenSimplificationAnnotation() { OriginalTextSpan = token.Span }); diff --git a/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs index 0b31f73ff3c75..ff38d9d3807a1 100644 --- a/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.SemanticModelReuse; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.SemanticModelReuse; diff --git a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs index f2ed9d5989834..5a58134e3f235 100644 --- a/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs +++ b/src/Workspaces/CSharp/Portable/Simplification/CSharpSimplificationService.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification; diff --git a/src/Workspaces/CSharpTest/EmbeddedLanguages/VirtualChars/CSharpVirtualCharServiceTests.cs b/src/Workspaces/CSharpTest/EmbeddedLanguages/VirtualChars/CSharpVirtualCharServiceTests.cs index 711fac1b21591..982dedb95ba1a 100644 --- a/src/Workspaces/CSharpTest/EmbeddedLanguages/VirtualChars/CSharpVirtualCharServiceTests.cs +++ b/src/Workspaces/CSharpTest/EmbeddedLanguages/VirtualChars/CSharpVirtualCharServiceTests.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.EmbeddedLanguages.VirtualChars diff --git a/src/Workspaces/CSharpTest/Formatting/CSharpFormattingTestBase.cs b/src/Workspaces/CSharpTest/Formatting/CSharpFormattingTestBase.cs index 91bd5f6c19791..481000073bcce 100644 --- a/src/Workspaces/CSharpTest/Formatting/CSharpFormattingTestBase.cs +++ b/src/Workspaces/CSharpTest/Formatting/CSharpFormattingTestBase.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests.Formatting; @@ -21,7 +22,7 @@ protected override SyntaxNode ParseCompilation(string text, ParseOptions? parseO => SyntaxFactory.ParseCompilationUnit(text, options: (CSharpParseOptions?)parseOptions); private protected Task AssertNoFormattingChangesAsync( - string code, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, OptionsCollection? changedOptionSet = null, bool testWithTransformation = true, ParseOptions? parseOptions = null) @@ -30,8 +31,8 @@ private protected Task AssertNoFormattingChangesAsync( } private protected Task AssertFormatAsync( - string expected, - string code, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, OptionsCollection? changedOptionSet = null, bool testWithTransformation = true, ParseOptions? parseOptions = null) @@ -40,8 +41,8 @@ private protected Task AssertFormatAsync( } private protected Task AssertFormatAsync( - string expected, - string code, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string expected, + [StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string code, IEnumerable spans, OptionsCollection? changedOptionSet = null, bool testWithTransformation = true, diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs index ae5911669009c..98e8eff7dd4fc 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests.cs @@ -2,12 +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. -#nullable disable - using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.CSharp.Shared.Extensions; using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Test.Utilities; @@ -15,10968 +14,12534 @@ using Xunit; using static Microsoft.CodeAnalysis.CSharp.Formatting.CSharpFormattingOptions2; -namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting -{ - using static CSharpSyntaxTokens; - - [Trait(Traits.Feature, Traits.Features.Formatting)] - public class FormattingTests : CSharpFormattingTestBase - { - [Fact] - public async Task Format1() - => await AssertFormatAsync("namespace A { }", "namespace A{}"); +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting; - [Fact] - public async Task Format2() - { - var content = @"class A { - }"; +using static CSharpSyntaxTokens; - var expected = @"class A +[Trait(Traits.Feature, Traits.Features.Formatting)] +public sealed class FormattingTests : CSharpFormattingTestBase { -}"; - await AssertFormatAsync(expected, content); - } + [Fact] + public async Task Format1() + => await AssertFormatAsync("namespace A { }", "namespace A{}"); - [Fact] - public async Task Format3() - { - var content = @"class A - { -int i = 20 ; }"; + [Fact] + public async Task Format2() + { + var content = """ + class A { + } + """; - var expected = @"class A -{ - int i = 20; -}"; + var expected = """ + class A + { + } + """; + await AssertFormatAsync(expected, content); + } - await AssertFormatAsync(expected, content); - } + [Fact] + public async Task Format3() + { + var content = """ + class A + { + int i = 20 ; } + """; - [Fact] - public async Task Format4() - { - var content = @"class A - { -int i = 20 ; int j = 1 + 2 ; - T . S = Test ( 10 ) ; - }"; + var expected = """ + class A + { + int i = 20; + } + """; - var expected = @"class A -{ - int i = 20; int j = 1 + 2; - T.S = Test( 10 ); -}"; + await AssertFormatAsync(expected, content); + } - await AssertFormatAsync(expected, content); - } + [Fact] + public async Task Format4() + { + var content = """ + class A + { + int i = 20 ; int j = 1 + 2 ; + T . S = Test ( 10 ) ; + } + """; - [Fact] - public async Task Format5() - { - var content = @"class A - { - List < int > Method < TArg , TArg2 > ( TArg a, TArg2 b ) - { -int i = 20 ; int j = 1 + 2 ; - T . S = Test ( 10 ) ; - } }"; + var expected = """ + class A + { + int i = 20; int j = 1 + 2; + T.S = Test( 10 ); + } + """; - var expected = @"class A -{ - List Method(TArg a, TArg2 b) - { - int i = 20; int j = 1 + 2; - T.S = Test(10); + await AssertFormatAsync(expected, content); } -}"; - await AssertFormatAsync(expected, content); - } - - [Fact] - public async Task Format6() - { - var content = @"class A - { -A a = new A { - Property1 = 1, Property2 = 3, - Property3 = { 1 , 2 , 3 } }; - }"; - - var expected = @"class A -{ - A a = new A + [Fact] + public async Task Format5() { - Property1 = 1, - Property2 = 3, - Property3 = { 1, 2, 3 } - }; -}"; - - await AssertFormatAsync(expected, content); - } + var content = """ + class A + { + List < int > Method < TArg , TArg2 > ( TArg a, TArg2 b ) + { + int i = 20 ; int j = 1 + 2 ; + T . S = Test ( 10 ) ; + } } + """; - [Fact] - public async Task Format7() - { - var content = @"class A - { - var a = from i in new [ ] { 1 , 2 , 3 } where i > 10 select i ; -}"; + var expected = """ + class A + { + List Method(TArg a, TArg2 b) + { + int i = 20; int j = 1 + 2; + T.S = Test(10); + } + } + """; - var expected = @"class A -{ - var a = from i in new[] { 1, 2, 3 } where i > 10 select i; -}"; + await AssertFormatAsync(expected, content); + } - await AssertFormatAsync(expected, content); - } + [Fact] + public async Task Format6() + { + var content = """ + class A + { + A a = new A { + Property1 = 1, Property2 = 3, + Property3 = { 1 , 2 , 3 } }; + } + """; - [Fact] - public async Task Format8() - { - var content = @"class A - { -void Method() -{ - if (true) - { - } - else if (false) - { - } -} -}"; + var expected = """ + class A + { + A a = new A + { + Property1 = 1, + Property2 = 3, + Property3 = { 1, 2, 3 } + }; + } + """; - var expected = @"class A -{ - void Method() - { - if (true) - { - } - else if (false) - { - } + await AssertFormatAsync(expected, content); } -}"; - await AssertFormatAsync(expected, content); - } + [Fact] + public async Task Format7() + { + var content = """ + class A + { + var a = from i in new [ ] { 1 , 2 , 3 } where i > 10 select i ; + } + """; - [Fact] - public async Task Format9() - { - var content = @"class A - { -void Method() -{ - if (true) { } else if (false) { } -} -}"; + var expected = """ + class A + { + var a = from i in new[] { 1, 2, 3 } where i > 10 select i; + } + """; - var expected = @"class A -{ - void Method() - { - if (true) { } else if (false) { } + await AssertFormatAsync(expected, content); } -}"; - await AssertFormatAsync(expected, content); - } - - [Fact] - public async Task Format10() - { - var content = @"class A - { - var a = from i in new [ ] { 1 , 2 , 3 } -where i > 10 select i ; -}"; + [Fact] + public async Task Format8() + { + var content = """ + class A + { + void Method() + { + if (true) + { + } + else if (false) + { + } + } + } + """; - var expected = @"class A -{ - var a = from i in new[] { 1, 2, 3 } - where i > 10 - select i; -}"; + var expected = """ + class A + { + void Method() + { + if (true) + { + } + else if (false) + { + } + } + } + """; - await AssertFormatAsync(expected, content); - } + await AssertFormatAsync(expected, content); + } - [Fact] - public async Task ObjectInitializer() - { - await AssertFormatAsync(@"public class C -{ - public C() + [Fact] + public async Task Format9() { - C c = new C() - { - c = new C() + var content = """ + class A + { + void Method() { - goo = 1, - bar = 2 + if (true) { } else if (false) { } } - }; - } -}", @"public class C -{ - public C() - { - C c = new C() - { - c = new C() - { - goo = 1, - bar = 2 - } - }; - } -}"); - } + } + """; - [Fact] - public async Task AnonymousType() - { - await AssertFormatAsync(@"class C -{ - C() - { - var anonType = new - { - p3 = new + var expected = """ + class A { - p1 = 3, - p2 = null - }, - p4 = true - }; + void Method() + { + if (true) { } else if (false) { } + } + } + """; + + await AssertFormatAsync(expected, content); } -}", @"class C -{ - C() - { - var anonType = new + + [Fact] + public async Task Format10() { - p3= new - { - p1 = 3, - p2 = null - }, - p4 = true - }; + var content = """ + class A + { + var a = from i in new [ ] { 1 , 2 , 3 } + where i > 10 select i ; + } + """; + + var expected = """ + class A + { + var a = from i in new[] { 1, 2, 3 } + where i > 10 + select i; + } + """; + + await AssertFormatAsync(expected, content); } -}"); - } - [Fact] - public async Task MultilineLambda() - { - await AssertFormatAsync(@"class C -{ - C() + [Fact] + public async Task ObjectInitializer() { - System.Func ret = x => + await AssertFormatAsync(""" + public class C + { + public C() + { + C c = new C() { - System.Func ret2 = y => - { - y++; - return y; - }; - return x + 1; + c = new C() + { + goo = 1, + bar = 2 + } }; + } + } + """, """ + public class C + { + public C() + { + C c = new C() + { + c = new C() + { + goo = 1, + bar = 2 + } + }; + } + } + """); } -}", @"class C -{ - C() + + [Fact] + public async Task AnonymousType() { - System.Func ret = x => + await AssertFormatAsync(""" + class C + { + C() + { + var anonType = new { -System.Func ret2 = y => + p3 = new + { + p1 = 3, + p2 = null + }, + p4 = true + }; + } + } + """, """ + class C + { + C() + { + var anonType = new + { + p3= new { - y++; - return y; - }; - return x + 1; - }; + p1 = 3, + p2 = null + }, + p4 = true + }; + } + } + """); } -}"); - } - [Fact] - public async Task AnonymousMethod() - { - await AssertFormatAsync(@"class C -{ - C() + [Fact] + public async Task MultilineLambda() { - timer.Tick += delegate (object sender, EventArgs e) - { - MessageBox.Show(this, ""Timer ticked""); - }; + await AssertFormatAsync(""" + class C + { + C() + { + System.Func ret = x => + { + System.Func ret2 = y => + { + y++; + return y; + }; + return x + 1; + }; + } + } + """, """ + class C + { + C() + { + System.Func ret = x => + { + System.Func ret2 = y => + { + y++; + return y; + }; + return x + 1; + }; + } + } + """); } -}", @"class C -{ - C() + + [Fact] + public async Task AnonymousMethod() { - timer.Tick += delegate(object sender, EventArgs e) - { - MessageBox.Show(this, ""Timer ticked""); - }; + await AssertFormatAsync(""" + class C + { + C() + { + timer.Tick += delegate (object sender, EventArgs e) + { + MessageBox.Show(this, "Timer ticked"); + }; + } + } + """, """ + class C + { + C() + { + timer.Tick += delegate(object sender, EventArgs e) + { + MessageBox.Show(this, "Timer ticked"); + }; + } + } + """); } -}"); - } - [Fact] - public async Task Scen1() - { - await AssertFormatAsync(@"namespace Namespace1 -{ - class Program + [Fact] + public async Task Scen1() { - static int i = 1 + 2; - - static void Main(string[] args) - { - Program p = new Program(); - - if (i < 5) - i = 0; - - for (i = 0; i < 3; i++) - Console.WriteLine(i); - - while (i < 4) - i++; - - do + await AssertFormatAsync(""" + namespace Namespace1 { - } while (i < 4); + class Program + { + static int i = 1 + 2; - Method(i, ""hello"", true); + static void Main(string[] args) + { + Program p = new Program(); - } + if (i < 5) + i = 0; - static void Method(int i, string s, bool b) - { - } - } -}", @"namespace Namespace1 -{ -class Program -{ -static int i=1+2; + for (i = 0; i < 3; i++) + Console.WriteLine(i); -static void Main(string[] args) -{ -Program p=new Program(); + while (i < 4) + i++; -if (i<5) - i=0; - -for (i=0;i<3;i++) - Console.WriteLine(i); + do + { + } while (i < 4); -while (i<4) - i++; + Method(i, "hello", true); -do{ - }while(i<4); + } -Method(i,""hello"",true); + static void Method(int i, string s, bool b) + { + } + } + } + """, """ + namespace Namespace1 + { + class Program + { + static int i=1+2; -} + static void Main(string[] args) + { + Program p=new Program(); -static void Method(int i, string s, bool b) -{ -} -} -}"); - } + if (i<5) + i=0; + + for (i=0;i<3;i++) + Console.WriteLine(i); - [Fact] - public async Task Scen2() - { - await AssertFormatAsync(@"namespace MyNamespace -{ - class Class1 - { - } - enum E - { - } - namespace NestedNamespace - { - } -} + while (i<4) + i++; -namespace Namespace1 -{ - class Class1 - { - int i; - class NestedClass - { - } - T t; - T Method(RR r) where RR : Class1 - { - return default(T); - } - } + do{ + }while(i<4); - struct S - { - string field1; - bool field2; - public void Method() - { - } - } + Method(i,"hello",true); - enum E - { - Enum1 = 10, - Enum2, - Enum3 + } + + static void Method(int i, string s, bool b) + { + } + } + } + """); } - class Program + [Fact] + public async Task Scen2() { - static int i = 10; - - class NestedClass - { - int field; - class NestedClass2 + await AssertFormatAsync(""" + namespace MyNamespace { - int field; - class NestedClass3 + class Class1 { - enum E + } + enum E + { + } + namespace NestedNamespace + { + } + } + + namespace Namespace1 + { + class Class1 + { + int i; + class NestedClass + { + } + T t; + T Method(RR r) where RR : Class1 + { + return default(T); + } + } + + struct S + { + string field1; + bool field2; + public void Method() { } } - int Prop + + enum E { - get { return field; } - set { field = value; } + Enum1 = 10, + Enum2, + Enum3 } - public void Method() + + class Program { + static int i = 10; + + class NestedClass + { + int field; + class NestedClass2 + { + int field; + class NestedClass3 + { + enum E + { + } + } + int Prop + { + get { return field; } + set { field = value; } + } + public void Method() + { + } + } + } + + struct S + { + string field1; + bool field2; + public void Method() + { + } + } + + enum E + { + Enum1 = 10, + Enum2, + Enum3 + } + + public int Prop + { + get { return i; } + set { i = value; } + } + + static void Main() + { + { + Program p = new Program(); + NestedClass n = new NestedClass(); + } + + if (i < 10) + { + Console.WriteLine(i); + } + + switch (i) + { + case 1: + break; + case 2: + break; + default: + break; + } + + for (i = 0; i < 10; i++) + { + i++; + } + + while (i < 10) + { + i++; + } + + try + { + Console.WriteLine(); + } + catch + { + Console.WriteLine(); + } + finally + { + Console.WriteLine(); + } + + } + public void Method(T t) + { + Console.WriteLine(t.ToString()); + } + } } - } + """, """ + namespace MyNamespace + { + class Class1 + { + } + enum E + { + } + namespace NestedNamespace + { + } + } - struct S - { + namespace Namespace1 + { + class Class1 + { + int i; + class NestedClass + { + } + T t; + T Method(RR r) where RR : Class1 + { + return default(T); + } + } + + struct S + { string field1; - bool field2; + bool field2; public void Method() { } - } + } - enum E - { - Enum1 = 10, + enum E + { + Enum1=10, Enum2, Enum3 - } + } - public int Prop - { - get { return i; } - set { i = value; } - } + class Program + { + static int i = 10; - static void Main() - { + class NestedClass + { + int field; + class NestedClass2 { - Program p = new Program(); - NestedClass n = new NestedClass(); + int field; + class NestedClass3 + { + enum E + { + } } - - if (i < 10) + int Prop { - Console.WriteLine(i); + get {return field;} + set {field=value;} } - - switch (i) + public void Method() { - case 1: - break; - case 2: - break; - default: - break; } + } + } - for (i = 0; i < 10; i++) + struct S + { + string field1; + bool field2; + public void Method() { - i++; } + } - while (i < 10) + enum E { - i++; - } + Enum1 = 10, + Enum2, + Enum3 + } - try + public int Prop { - Console.WriteLine(); - } - catch + get {return i;} + set {i=value;} + } + + static void Main() + { + { + Program p=new Program(); + NestedClass n=new NestedClass(); + } + + if (i<10) { - Console.WriteLine(); + Console.WriteLine(i); } - finally + + switch (i) { - Console.WriteLine(); + case 1: + break; + case 2: + break; + default: + break; + } + + for (i=0;i<10;i++) + { + i++; } - } - public void Method(T t) - { - Console.WriteLine(t.ToString()); - } + while (i<10) + { + i++; + } - } -}", @"namespace MyNamespace -{ - class Class1 - { + try + { + Console.WriteLine(); } -enum E -{ -} - namespace NestedNamespace - { + catch + { + Console.WriteLine(); + } + finally + { + Console.WriteLine(); } -} - -namespace Namespace1 -{ -class Class1 -{ -int i; -class NestedClass -{ -} -T t; - T Method(RR r) where RR : Class1 - { - return default(T); - } - } - -struct S -{ -string field1; - bool field2; -public void Method() -{ -} -} -enum E -{ - Enum1=10, -Enum2, -Enum3 - } + } + public void Method(T t) + { + Console.WriteLine(t.ToString()); + } -class Program -{ -static int i = 10; + } + } + """); + } -class NestedClass -{ - int field; -class NestedClass2 -{ -int field; - class NestedClass3 -{ - enum E + [Fact] + public async Task Scen3() + { + await AssertFormatAsync(""" + namespace Namespace1 + { + class Program { + static void Main() + { + Program p = new Program(); + } } -} -int Prop -{ - get {return field;} - set {field=value;} -} -public void Method() -{ -} -} + } + """, """ + namespace Namespace1 + { + class Program + { + static void Main() + { + Program p=new Program(); + } + } + } + """); } -struct S -{ - string field1; - bool field2; -public void Method() -{ -} - } - -enum E -{ - Enum1 = 10, - Enum2, -Enum3 - } - -public int Prop -{ -get {return i;} -set {i=value;} - } - -static void Main() -{ -{ - Program p=new Program(); -NestedClass n=new NestedClass(); + [Fact] + public async Task Scen4() + { + await AssertFormatAsync(""" + class Class1 + { + // public void goo() + // { + // // TODO: Add the implementation for Class1.goo() here. + // + // } } + """, """ + class Class1 + { + // public void goo() + // { + // // TODO: Add the implementation for Class1.goo() here. + // + // } + } + """); + } -if (i<10) -{ - Console.WriteLine(i); -} - -switch (i) -{ - case 1: - break; - case 2: - break; -default: -break; + [Fact] + public async Task Scen5() + { + await AssertFormatAsync(""" + class Class1 + { + public void Method() + { + { + int i = 0; + System.Console.WriteLine(); + } + } + } + """, """ + class Class1 + { + public void Method() + { + { + int i = 0; + System.Console.WriteLine(); + } + } + } + """); } -for (i=0;i<10;i++) -{ - i++; -} - -while (i<10) -{ - i++; - } - -try -{ - Console.WriteLine(); - } -catch -{ - Console.WriteLine(); - } -finally -{ - Console.WriteLine(); + [Fact] + public async Task Scen6() + { + await AssertFormatAsync(""" + namespace Namespace1 + { + class OuterClass + { + class InnerClass + { + } + } } - -} -public void Method(T t) -{ - Console.WriteLine(t.ToString()); + """, """ + namespace Namespace1 + { + class OuterClass + { + class InnerClass + { } - -} -}"); - } - - [Fact] - public async Task Scen3() - { - await AssertFormatAsync(@"namespace Namespace1 -{ - class Program - { - static void Main() - { - Program p = new Program(); - } + } + } + """); } -}", @"namespace Namespace1 -{ -class Program -{ -static void Main() -{ -Program p=new Program(); -} -} -}"); - } - - [Fact] - public async Task Scen4() - { - await AssertFormatAsync(@"class Class1 -{ - // public void goo() - // { - // // TODO: Add the implementation for Class1.goo() here. - // - // } -}", @"class Class1 -{ - // public void goo() -// { -// // TODO: Add the implementation for Class1.goo() here. -// -// } -}"); - } - [Fact] - public async Task Scen5() - { - await AssertFormatAsync(@"class Class1 -{ - public void Method() + [Fact] + public async Task Scen7() { - { + await AssertFormatAsync(""" + class Class1 + { + public void Method() + { + int i = 0; + switch (i) + { + case 0: + break; + } + if (i > 0) goto z; + i = -i; + z: + i = 2 * i; + } + } + """, """ + class Class1 + { + public void Method() + { int i = 0; - System.Console.WriteLine(); - } - } -}", @"class Class1 -{ -public void Method() -{ -{ -int i = 0; - System.Console.WriteLine(); -} -} -}"); - } - - [Fact] - public async Task Scen6() - { - await AssertFormatAsync(@"namespace Namespace1 -{ - class OuterClass - { - class InnerClass - { - } - } -}", @"namespace Namespace1 -{ -class OuterClass -{ -class InnerClass -{ -} -} -}"); - } - - [Fact] - public async Task Scen7() - { - await AssertFormatAsync(@"class Class1 -{ - public void Method() - { - int i = 0; - switch (i) - { + switch (i) + { case 0: - break; - } - if (i > 0) goto z; - i = -i; - z: - i = 2 * i; + break; + } + if (i > 0) goto z; + i = -i; + z: + i = 2 * i; + } + } + """); } -}", @"class Class1 -{ -public void Method() -{ -int i = 0; -switch (i) -{ -case 0: -break; -} -if (i > 0) goto z; -i = -i; -z: -i = 2 * i; -} -}"); - } - [Fact] - public async Task Scen8() - { - await AssertFormatAsync(@"class Class1 -{ - public void Method() + [Fact] + public async Task Scen8() { - int i = 10; - } -}", @"class Class1 - { + await AssertFormatAsync(""" + class Class1 + { public void Method() - { + { int i = 10; - } -}"); - } + } + } + """, """ + class Class1 + { + public void Method() + { + int i = 10; + } + } + """); + } - [Fact] - public async Task IndentStatementsInMethod() - { - await AssertFormatAsync(@"class C -{ - void Goo() + [Fact] + public async Task IndentStatementsInMethod() { - int x = 0; - int y = 0; - int z = 0; + await AssertFormatAsync(""" + class C + { + void Goo() + { + int x = 0; + int y = 0; + int z = 0; + } + } + """, """ + class C + { + void Goo() + { + int x = 0; + int y = 0; + int z = 0; + } + } + """); } -}", @"class C -{ - void Goo() + + [Fact] + public async Task IndentFieldsInClass() { - int x = 0; - int y = 0; - int z = 0; + await AssertFormatAsync(""" + class C + { + int a = 10; + int b; + int c; + } + """, """ + class C + { + int a = 10; + int b; + int c; + } + """); } -}"); - } - - [Fact] - public async Task IndentFieldsInClass() - { - await AssertFormatAsync(@"class C -{ - int a = 10; - int b; - int c; -}", @"class C -{ - int a = 10; - int b; - int c; -}"); - } - [Fact] - public async Task IndentUserDefaultSettingTest() - { - await AssertFormatAsync(@"class Class2 -{ - public void nothing() + [Fact] + public async Task IndentUserDefaultSettingTest() { - nothing_again(() => + await AssertFormatAsync(""" + class Class2 { - Console.WriteLine(""Nothing""); - }); - label1: - int f = 5; - label2: - switch (f) - { - case 1: + public void nothing() { - break; + nothing_again(() => + { + Console.WriteLine("Nothing"); + }); + label1: + int f = 5; + label2: + switch (f) + { + case 1: + { + break; + } + case 2: + int d = f + f; + label3: + d = d - f; + break; + default: + { + int g = f * f; + g = g - f; + break; + } + } + return; + } + + public void nothing_again(Action a) + { + l: + goto l; } - case 2: + } + """, """ + class Class2 + { + public void nothing() + { + nothing_again(() => + { + Console.WriteLine("Nothing"); + }); + label1: + int f = 5; + label2: + switch (f) + { + case 1: + { + break; + } + case 2: int d = f + f; label3: d = d - f; break; - default: - { - int g = f * f; - g = g - f; - break; + default: + { + int g = f * f; + g = g - f; + break; + } + } + return; + } + + public void nothing_again(Action a) + { + l: + goto l; + } } - } - return; + """); } - public void nothing_again(Action a) - { - l: - goto l; - } -}", @"class Class2 + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/766133")] + public async Task RelativeIndentationToFirstTokenInBaseTokenWithObjectInitializers() { - public void nothing() - { - nothing_again(() => - { - Console.WriteLine(""Nothing""); - }); -label1: - int f = 5; -label2: - switch (f) - { - case 1: + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - break; - } - case 2: - int d = f + f; -label3: - d = d - f; - break; - default: - { - int g = f * f; - g = g - f; - break; - } - } - return; - } + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, + }; + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + var summa = new D { + A = 0, + B = 4 + }; + } + } - public void nothing_again(Action a) - { - l: - goto l; - } - }"); - } + class D + { + public int A { get; set; } + public int B { get; set; } + } + """, """ + class Program + { + static void Main(string[] args) + { + var summa = new D + { + A = 0, + B = 4 + }; + } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/766133")] - public async Task RelativeIndentationToFirstTokenInBaseTokenWithObjectInitializers() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class D { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, - }; - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) - { - var summa = new D { - A = 0, - B = 4 - }; + public int A { get; set; } + public int B { get; set; } + } + """, changingOptions); } -} -class D -{ - public int A { get; set; } - public int B { get; set; } -}", @"class Program -{ - static void Main(string[] args) + [Fact] + public async Task RemoveSpacingAroundBinaryOperatorsShouldMakeAtLeastOneSpaceForIsAndAsKeywords() { - var summa = new D + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - A = 0, - B = 4 + { CSharpFormattingOptions2.SpacingAroundBinaryOperator, BinaryOperatorSpacingOptions.Remove } }; - } -} - -class D -{ - public int A { get; set; } - public int B { get; set; } -}", changingOptions); - } - - [Fact] - public async Task RemoveSpacingAroundBinaryOperatorsShouldMakeAtLeastOneSpaceForIsAndAsKeywords() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync(""" + class Class2 { - { CSharpFormattingOptions2.SpacingAroundBinaryOperator, BinaryOperatorSpacingOptions.Remove } - }; - await AssertFormatAsync(@"class Class2 -{ - public void nothing() - { - var a = 1*2+3-4/5; - a+=1; - object o = null; - string s = o as string; - bool b = o is string; + public void nothing() + { + var a = 1*2+3-4/5; + a+=1; + object o = null; + string s = o as string; + bool b = o is string; + } + } + """, """ + class Class2 + { + public void nothing() + { + var a = 1 * 2 + 3 - 4 / 5; + a += 1; + object o = null; + string s = o as string; + bool b = o is string; + } + } + """, changingOptions); } -}", @"class Class2 - { - public void nothing() - { - var a = 1 * 2 + 3 - 4 / 5; - a += 1; - object o = null; - string s = o as string; - bool b = o is string; - } - }", changingOptions); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772298")] - public async Task IndentUserSettingNonDefaultTest_OpenBracesOfLambdaWithNoNewLine() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentBraces, true }, - { IndentBlock, false }, - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, false }, - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.LambdaExpressionBody, false) }, - { LabelPositioning, LabelPositionOptions.LeftMost } - }; - - await AssertFormatAsync(@"class Class2 + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772298")] + public async Task IndentUserSettingNonDefaultTest_OpenBracesOfLambdaWithNoNewLine() { - public void nothing() + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - nothing_again(() => { - Console.WriteLine(""Nothing""); - }); - } - }", @"class Class2 -{ - public void nothing() - { - nothing_again(() => + { IndentBraces, true }, + { IndentBlock, false }, + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, false }, + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.LambdaExpressionBody, false) }, + { LabelPositioning, LabelPositionOptions.LeftMost } + }; + + await AssertFormatAsync(""" + class Class2 + { + public void nothing() + { + nothing_again(() => { + Console.WriteLine("Nothing"); + }); + } + } + """, """ + class Class2 { - Console.WriteLine(""Nothing""); - }); + public void nothing() + { + nothing_again(() => + { + Console.WriteLine("Nothing"); + }); + } + } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - [Fact] - public async Task IndentUserSettingNonDefaultTest() + [Fact] + public async Task IndentUserSettingNonDefaultTest() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentBraces, true }, - { IndentBlock, false }, - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, false }, - { IndentSwitchCaseSectionWhenBlock, false }, - { LabelPositioning, LabelPositionOptions.LeftMost } - }; - - await AssertFormatAsync(@"class Class2 - { - public void nothing() - { - nothing_again(() => - { - Console.WriteLine(""Nothing""); - }); -label1: - int f = 5; -label2: - switch (f) - { - case 1: - { - break; - } - case 2: - int d = f + f; -label3: - d = d - f; - break; - default: - { - int g = f * f; - g = g - f; - break; - } - } - return; - } + { IndentBraces, true }, + { IndentBlock, false }, + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, false }, + { IndentSwitchCaseSectionWhenBlock, false }, + { LabelPositioning, LabelPositionOptions.LeftMost } + }; - public void nothing_again(Action a) - { -l: - goto l; - } - }", @"class Class2 -{ - public void nothing() - { - nothing_again(() => - { - Console.WriteLine(""Nothing""); - }); - label1: - int f = 5; - label2: - switch (f) - { - case 1: + await AssertFormatAsync(""" + class Class2 { - break; - } - case 2: + public void nothing() + { + nothing_again(() => + { + Console.WriteLine("Nothing"); + }); + label1: + int f = 5; + label2: + switch (f) + { + case 1: + { + break; + } + case 2: int d = f + f; label3: d = d - f; break; - default: + default: + { + int g = f * f; + g = g - f; + break; + } + } + return; + } + + public void nothing_again(Action a) + { + l: + goto l; + } + } + """, """ + class Class2 + { + public void nothing() { - int g = f * f; - g = g - f; - break; + nothing_again(() => + { + Console.WriteLine("Nothing"); + }); + label1: + int f = 5; + label2: + switch (f) + { + case 1: + { + break; + } + case 2: + int d = f + f; + label3: + d = d - f; + break; + default: + { + int g = f * f; + g = g - f; + break; + } + } + return; } - } - return; - } - public void nothing_again(Action a) - { - l: - goto l; + public void nothing_again(Action a) + { + l: + goto l; + } + } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task IndentSwitch_IndentCase_IndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentSwitchSection, true }, - { IndentSwitchCaseSection, true }, - { IndentSwitchCaseSectionWhenBlock, true }, - }; - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task IndentSwitch_IndentCase_IndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, true }, + { IndentSwitchCaseSection, true }, + { IndentSwitchCaseSectionWhenBlock, true }, + }; + + await AssertFormatAsync( + """ + class Class2 + { + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } + } + """, + """ + class Class2 + { + void M() { + switch (i) { + case 0: { } - case 1: + case 1: break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; + } + } } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task IndentSwitch_IndentCase_NoIndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentSwitchSection, true }, - { IndentSwitchCaseSection, true }, - { IndentSwitchCaseSectionWhenBlock, false }, - }; - - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task IndentSwitch_IndentCase_NoIndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, true }, + { IndentSwitchCaseSection, true }, + { IndentSwitchCaseSectionWhenBlock, false }, + }; + + await AssertFormatAsync( + """ + class Class2 { + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } } - case 1: + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; + } + } } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task IndentSwitch_NoIndentCase_IndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentSwitchSection, true }, - { IndentSwitchCaseSection, false }, - { IndentSwitchCaseSectionWhenBlock, true }, - }; - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task IndentSwitch_NoIndentCase_IndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, true }, + { IndentSwitchCaseSection, false }, + { IndentSwitchCaseSectionWhenBlock, true }, + }; + + await AssertFormatAsync( + """ + class Class2 + { + void M() { + switch (i) + { + case 0: + { + } + case 1: + break; + } } - case 1: - break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; } - } -}", changedOptionSet: changingOptions); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task IndentSwitch_NoIndentCase_NoIndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + """, + """ + class Class2 { - { IndentSwitchSection, true }, - { IndentSwitchCaseSection, false }, - { IndentSwitchCaseSectionWhenBlock, false }, - }; + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } + } + """, changedOptionSet: changingOptions); + } - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task IndentSwitch_NoIndentCase_NoIndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, true }, + { IndentSwitchCaseSection, false }, + { IndentSwitchCaseSectionWhenBlock, false }, + }; + + await AssertFormatAsync( + """ + class Class2 { + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } } - case 1: - break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task NoIndentSwitch_IndentCase_IndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, true }, - { IndentSwitchCaseSectionWhenBlock, true }, - }; - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task NoIndentSwitch_IndentCase_IndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, true }, + { IndentSwitchCaseSectionWhenBlock, true }, + }; + + await AssertFormatAsync( + """ + class Class2 { + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } } - case 1: - break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task NoIndentSwitch_IndentCase_NoIndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, true }, - { IndentSwitchCaseSectionWhenBlock, false }, - }; - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task NoIndentSwitch_IndentCase_NoIndentWhenBlock() { - switch (i) - { - case 0: + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - } - case 1: - break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; - } - } -}", changedOptionSet: changingOptions); - } + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, true }, + { IndentSwitchCaseSectionWhenBlock, false }, + }; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task NoIndentSwitch_NoIndentCase_IndentWhenBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync( + """ + class Class2 { - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, false }, - { IndentSwitchCaseSectionWhenBlock, true }, - }; + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } + } + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } + } + """, changedOptionSet: changingOptions); + } - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task NoIndentSwitch_NoIndentCase_IndentWhenBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, false }, + { IndentSwitchCaseSectionWhenBlock, true }, + }; + + await AssertFormatAsync( + """ + class Class2 { + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } } - case 1: - break; - } - } -}", -@"class Class2 -{ - void M() - { - switch (i) { - case 0: { - } - case 1: - break; + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } } + """, changedOptionSet: changingOptions); } -}", changedOptionSet: changingOptions); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] - public async Task NoIndentSwitch_NoIndentCase_NoIndentWhenBlock() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/20009")] + public async Task NoIndentSwitch_NoIndentCase_NoIndentWhenBlock() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { IndentSwitchSection, false }, + { IndentSwitchCaseSection, false }, + { IndentSwitchCaseSectionWhenBlock, false }, + }; + + await AssertFormatAsync( + """ + class Class2 { - { IndentSwitchSection, false }, - { IndentSwitchCaseSection, false }, - { IndentSwitchCaseSectionWhenBlock, false }, - }; + void M() + { + switch (i) + { + case 0: + { + } + case 1: + break; + } + } + } + """, + """ + class Class2 + { + void M() + { + switch (i) { + case 0: { + } + case 1: + break; + } + } + } + """, changedOptionSet: changingOptions); + } - await AssertFormatAsync( -@"class Class2 -{ - void M() + [Fact] + public async Task TestWrappingDefault() + { + await AssertFormatAsync(""" + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if (x == 1) x = 2; else x = 3; + do { x = 4; } while (x != 4); + switch (x) { case 1: break; case 2: break; default: break; } + Del d = delegate (int k) { Console.WriteLine(); Console.WriteLine(); }; + } + } + """, """ + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if(x == 1) x = 2; else x =3; + do { x = 4; } while (x != 4); + switch (x) { case 1: break; case 2: break; default: break; } + Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; + } + } + """); + } + + [Fact] + public async Task TestWrappingNonDefault_FormatBlock() { - switch (i) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 0: + { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } + }; + await AssertFormatAsync(""" + class Class5 + { + delegate void Del(int x); + public int Age + { + get + { + int age = 0; return age; + } + } + public int Age2 + { + get + { + int age2 = 0; return age2; + } + set + { + int age2 = value; + } + } + void bar() + { + int x = 0; + if (x == 1) x = 2; else x = 3; + do { x = 4; } while (x != 4); + switch (x) + { + case 1: break; + case 2: break; + default: break; + } + Del d = delegate (int k) { Console.WriteLine(); Console.WriteLine(); }; + } + void goo() + { + int xx = 0; int zz = 0; + } + } + class goo + { + int x = 0; + } + """, """ + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if(x == 1) x = 2; else x =3; + do { x = 4; } while (x != 4); + switch (x) { case 1: break; case 2: break; default: break; } + Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; + } + void goo() { int xx = 0; int zz = 0;} + } + class goo{int x = 0;} + """, changingOptions); + } + + [Fact] + public async Task TestWrappingNonDefault_FormatStatmtMethDecl() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - } - case 1: - break; - } + { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } + }; + await AssertFormatAsync(""" + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if (x == 1) + x = 2; + else + x = 3; + do + { x = 4; } while (x != 4); + switch (x) + { + case 1: + break; + case 2: + break; + default: + break; + } + Del d = delegate (int k) + { Console.WriteLine(); Console.WriteLine(); }; + } + void goo() { int y = 0; int z = 0; } + } + class goo + { + int x = 0; + } + """, """ + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if(x == 1) x = 2; else x =3; + do { x = 4; } while (x != 4); + switch (x) { case 1: break; case 2: break; default: break; } + Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; + } + void goo(){int y=0; int z =0 ;} + } + class goo + { + int x = 0; + } + """, changingOptions); } -}", -@"class Class2 -{ - void M() + + [Fact] + public async Task TestWrappingNonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.WrappingPreserveSingleLine, false }, + { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } + }; + await AssertFormatAsync(""" + class Class5 + { + delegate void Del(int x); + public int Age + { + get + { + int age = 0; + return age; + } + } + public int Age2 + { + get + { + int age2 = 0; + return age2; + } + set + { + int age2 = value; + } + } + void bar() + { + int x = 0; + if (x == 1) + x = 2; + else + x = 3; + do + { + x = 4; + } while (x != 4); + switch (x) + { + case 1: + break; + case 2: + break; + default: + break; + } + Del d = delegate (int k) + { + Console.WriteLine(); + Console.WriteLine(); + }; + } + } + class goo + { + int x = 0; + } + """, """ + class Class5 + { + delegate void Del(int x); + public int Age { get { int age = 0; return age; } } + public int Age2 + { + get { int age2 = 0; return age2; } + set { int age2 = value; } + } + void bar() + { + int x = 0; + if(x == 1) x = 2; else x =3; + do { x = 4; } while (x != 4); + switch (x) { case 1: break; case 2: break; default: break; } + Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; + } + } + class goo{int x = 0;} + """, changingOptions); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/991480")] + public async Task TestLeaveStatementMethodDeclarationSameLineNotAffectingForStatement() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } + }; + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + for (int d = 0; d < 10; ++d) + { } + } + } + """, """ + class Program + { + static void Main(string[] args) + { + for (int d = 0; d < 10; ++d) { } + } + } + """, changingOptions); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751789")] + public async Task NewLineForOpenBracesDefault() { - switch (i) { - case 0: { + await AssertFormatAsync(""" + class f00 + { + void br() + { + Func ret = x => + { + return x + 1; + }; + var obj = new + { + // ... + }; + if (true) + { + System.Console.WriteLine(""); + } + else + { + } + timer.Tick += delegate (object sender, EventArgs e) + + + { + MessageBox.Show(this, "Timer ticked"); + }; + + var obj1 = new goo + { + }; + + async void LocalFunction() + { + } + + try + { + } + catch (Exception e) + { + } + finally + { } + + using (someVar) + { + } + + switch (switchVar) + { + default: + break; + } + } + } + + namespace NS1 + { + public class goo : System.Object + + + + { + public int f { get; set; } + } + } + """, """ + class f00 + { + void br() { + Func ret = x => + { + return x + 1; + }; + var obj = new + { + // ... + }; + if(true) + { + System.Console.WriteLine(""); + } + else + { + } + timer.Tick += delegate (object sender, EventArgs e) + + + { + MessageBox.Show(this, "Timer ticked"); + }; + + var obj1 = new goo + { + }; + + async void LocalFunction() { + } + + try + { + } + catch (Exception e) + { + } + finally + {} + + using (someVar) + { + } + + switch (switchVar) + { + default: + break; + } + } + } + + namespace NS1 { + public class goo : System.Object + + + + { + public int f { get; set; } + } + } + """); } - case 1: - break; + + [Fact, WorkItem("https://developercommunity.visualstudio.com/content/problem/8808/c-structure-guide-lines-for-unsafe-fixed.html")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751789")] + public async Task NewLineForOpenBracesNonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.None } + }; + await AssertFormatAsync(""" + class f00 { + void br() { + Func ret = x => { + return x + 1; + }; + var obj = new { + // ... + }; + if (true) { + System.Console.WriteLine(""); + } + else { + } + timer.Tick += delegate (object sender, EventArgs e) { + MessageBox.Show(this, "Timer ticked"); + }; + + var obj1 = new goo { + }; + + async void LocalFunction() { + } + + try { + } + catch (Exception e) { + } + finally { } + + using (someVar) { + } + + switch (switchVar) { + default: + break; + } + + unsafe { + } + + fixed (int* p = &i) { + } + } + } + + namespace NS1 { + public class goo : System.Object { + } + } + """, """ + class f00 + { + void br() { + Func ret = x => + { + return x + 1; + }; + var obj = new + { + // ... + }; + if(true) + { + System.Console.WriteLine(""); + } + else + { + } + timer.Tick += delegate (object sender, EventArgs e) + + + { + MessageBox.Show(this, "Timer ticked"); + }; + + var obj1 = new goo + { + }; + + async void LocalFunction() + { + } + + try + { + } + catch (Exception e) + { + } + finally + {} + + using (someVar) + { + } + + switch (switchVar) + { + default: + break; + } + + unsafe + { + } + + fixed (int* p = &i) + { + } + } + } + + namespace NS1 { + public class goo : System.Object + + + + { + } + } + """, changingOptions); + } + + [Fact] + public async Task NewLineForKeywordDefault() + { + await AssertFormatAsync(""" + class c + { + void f00() + { + + try + { + // ... + } + catch (Exception e) + { + // ... + } + finally + { + // ... + } + + if (a > b) + { + return 3; + } + else + { + return 0; + } + } + } + """, + """ + class c + { + void f00(){ + + try + { + // ... + } catch (Exception e) + { + // ... + } finally + { + // ... + } + + if (a > b) + { + return 3; + } else + { + return 0; + } + } + } + """); + } + + [Fact] + public async Task NewLineForKeywordNonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.NewLineForElse, false }, + { CSharpFormattingOptions2.NewLineForCatch, false }, + { CSharpFormattingOptions2.NewLineForFinally, false } + }; + await AssertFormatAsync(""" + class c + { + void f00() + { + + try + { + // ... + } catch (Exception e) + { + // ... + } finally + { + // ... + } + if (a > b) + { + return 3; + } else + { + return 0; + } + } + } + """, """ + class c + { + void f00(){ + + try + { + // ... + } + + + catch (Exception e) + { + // ... + } + + + finally + { + // ... + } + if (a > b) + { + return 3; + } + + else + { + return 0; + } + } + } + """, changingOptions); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33458")] + public async Task NoNewLineForElseChecksBraceOwner() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineForElse, false }, + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } + }; + + await AssertFormatAsync(""" + class Class + { + void Method() + { + if (true) + for (int i = 0; i < 10; i++) { + Method(); + } + else + return; + } + } + """, """ + class Class + { + void Method() + { + if (true) + for (int i = 0; i < 10; i++) { + Method(); + } else + return; + } + } + """, changedOptionSet: changingOptions); + } + + [Fact] + public async Task NewLineForExpressionDefault() + { + await AssertFormatAsync(""" + class f00 + { + void br() + { + var queryLowNums = from num in numbers + where num < 5 + select num; + + var queryLowNums = + + from num in numbers + where num < 5 + select num; + + var q = from c in cust + from o in c.Orders + orderby o.Total descending + select new { c.Name, c.OrderID }; + var obj = new + { + X1 = 0, + Y1 = 1, + X2 = 2, + Y2 = 3 + }; + var obj1 = new { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; + MyObject obj = new MyObject + { + X1 = 0, + Y1 = 1, + X2 = 2, + Y2 = 3 + }; + MyObject obj = new MyObject { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; + } + } + """, """ + class f00 + { + void br() + { + var queryLowNums = from num in numbers where num < 5 + select num; + + var queryLowNums = + + from num in numbers where num < 5 + select num; + + var q = from c in cust + from o in c.Orders orderby o.Total descending + select new { c.Name, c.OrderID }; + var obj = new { X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + var obj1 = new { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; + MyObject obj = new MyObject { X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + MyObject obj = new MyObject { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; + } + } + """); + } + + [Fact] + public async Task NewLineForExpressionNonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.NewLineForMembersInObjectInit, false }, + { CSharpFormattingOptions2.NewLineForMembersInAnonymousTypes, false }, + { CSharpFormattingOptions2.NewLineForClausesInQuery, false } + }; + await AssertFormatAsync(""" + class f00 + { + void br() + { + + var queryLowNums = from num in numbers where num < 5 + select num; + + var queryLowNums = + + from num in numbers where num < 5 + select num; + + var q = from c in cust + from o in c.Orders orderby o.Total descending + select new { c.Name, c.OrderID }; + var obj = new + { + X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + MyObject obj = new MyObject + { + X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + } + } + """, """ + class f00 + { + void br() + { + + var queryLowNums = from num in numbers where num < 5 + select num; + + var queryLowNums = + + from num in numbers where num < 5 + select num; + + var q = from c in cust + from o in c.Orders orderby o.Total descending + select new { c.Name, c.OrderID }; + var obj = new { X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + MyObject obj = new MyObject { X1 = 0, Y1 = 1, + X2 = 2, + Y2 = 3 + }; + } + } + """, changingOptions); + } + + [Fact] + public async Task Enums_Bug2586() + { + await AssertFormatAsync(""" + enum E + { + a = 10, + b, + c + } + """, """ + enum E + { + a = 10, + b, + c + } + """); + } + + [Fact] + public async Task DoNotInsertLineBreaksInSingleLineEnum() + => await AssertFormatAsync(@"enum E { a = 10, b, c }", @"enum E { a = 10, b, c }"); + + [Fact] + public async Task AlreadyFormattedSwitchIsNotFormatted_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + switch (3) + { + case 0: + break; + } + } + } + """, """ + class C + { + void M() + { + switch (3) + { + case 0: + break; + } + } + } + """); + } + + [Fact] + public async Task BreaksAreAlignedInSwitchCasesFormatted_Bug2587() + { + await AssertFormatAsync(""" + class C + { + void M() + { + switch (3) + { + case 0: + break; + } + } + } + """, """ + class C + { + void M() + { + switch (3) + { + case 0: + break; + } + } + } + """); + } + + [Fact] + public async Task BreaksAndBracesAreAlignedInSwitchCasesWithBracesFormatted_Bug2587() + { + await AssertFormatAsync(""" + class C + { + void M() + { + switch (3) + { + case 0: + { + break; + } + } + } + } + """, """ + class C + { + void M() + { + switch (3) + { + case 0: + { + break; + } + } + } + } + """); + } + + [Fact] + public async Task LineBreaksAreNotInsertedForSwitchCasesOnASingleLine1() + { + await AssertFormatAsync(""" + class C + { + void M() + { + switch (3) + { + case 0: break; + default: break; + } + } + } + """, """ + class C + { + void M() + { + switch (3) + { + case 0: break; + default: break; + } + } + } + """); + } + + [Fact] + public async Task LineBreaksAreNotInsertedForSwitchCasesOnASingleLine2() + { + await AssertFormatAsync(""" + class C + { + void M() + { + switch (3) + { + case 0: { break; } + default: { break; } + } + } + } + """, """ + class C + { + void M() + { + switch (3) + { + case 0: { break; } + default: { break; } + } + } + } + """); + } + + [Fact] + public async Task FormatLabelAndGoto1_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + Goo: + goto Goo; + } + } + """, """ + class C + { + void M() + { + Goo: + goto Goo; + } + } + """); + } + + [Fact] + public async Task FormatLabelAndGoto2_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + int x = 0; + Goo: + goto Goo; + } + } + """, """ + class C + { + void M() + { + int x = 0; + Goo: + goto Goo; + } + } + """); + } + + [Fact] + public async Task FormatNestedLabelAndGoto1_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + if (true) + { + Goo: + goto Goo; + } + } + } + """, """ + class C + { + void M() + { + if (true) + { + Goo: + goto Goo; + } + } + } + """); + } + + [Fact] + public async Task FormatNestedLabelAndGoto2_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + if (true) + { + int x = 0; + Goo: + goto Goo; + } + } + } + """, """ + class C + { + void M() + { + if (true) + { + int x = 0; + Goo: + goto Goo; + } + } + } + """); + } + + [Fact] + public async Task AlreadyFormattedGotoLabelIsNotFormatted1_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + Goo: + goto Goo; + } + } + """, """ + class C + { + void M() + { + Goo: + goto Goo; + } + } + """); + } + + [Fact] + public async Task AlreadyFormattedGotoLabelIsNotFormatted2_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + Goo: goto Goo; + } + } + """, """ + class C + { + void M() + { + Goo: goto Goo; + } + } + """); + } + + [Fact] + public async Task AlreadyFormattedGotoLabelIsNotFormatted3_Bug2588() + { + await AssertFormatAsync(""" + class C + { + void M() + { + int x = 0; + Goo: + goto Goo; + } + } + """, """ + class C + { + void M() + { + int x = 0; + Goo: + goto Goo; + } + } + """); + } + + [Fact] + public async Task DoNotAddLineBreakBeforeWhere1_Bug2582() + { + await AssertFormatAsync(""" + class C + { + void M() where T : I + { + } + } + """, """ + class C + { + void M() where T : I + { + } + } + """); + } + + [Fact] + public async Task DoNotAddLineBreakBeforeWhere2_Bug2582() + { + await AssertFormatAsync(""" + class C where T : I + { + } + """, """ + class C where T : I + { + } + """); + } + + [Fact] + public async Task DoNotAddSpaceAfterUnaryMinus() + { + await AssertFormatAsync(""" + class C + { + void M() + { + int x = -1; + } + } + """, """ + class C + { + void M() + { + int x = -1; + } + } + """); + } + + [Fact] + public async Task DoNotAddSpaceAfterUnaryPlus() + { + await AssertFormatAsync(""" + class C + { + void M() + { + int x = +1; + } + } + """, """ + class C + { + void M() + { + int x = +1; + } + } + """); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] + public async Task DoNotAddSpaceAfterIncrement() + { + var code = """ + class C + { + void M(int[] i) + { + ++i[0]; + } + } + """; + await AssertFormatAsync(code, code); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] + public async Task DoNotAddSpaceBeforeIncrement() + { + var code = """ + class C + { + void M(int[] i) + { + i[0]++; + } + } + """; + await AssertFormatAsync(code, code); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] + public async Task DoNotAddSpaceAfterDecrement() + { + var code = """ + class C + { + void M(int[] i) + { + --i[0]; + } + } + """; + await AssertFormatAsync(code, code); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] + public async Task DoNotAddSpaceBeforeDecrement() + { + var code = """ + class C + { + void M(int[] i) + { + i[0]--; + } + } + """; + await AssertFormatAsync(code, code); + } + + [Fact] + public async Task Anchoring() + { + await AssertFormatAsync(""" + class C + { + void M() + { + Console.WriteLine("Goo", + 0, 1, + 2); + } + } + """, """ + class C + { + void M() + { + Console.WriteLine("Goo", + 0, 1, + 2); + } + } + """); + } + + [Fact] + public async Task Exclamation() + { + await AssertFormatAsync(""" + class C + { + void M() + { + if (!true) ; + } + } + """, """ + class C + { + void M() + { + if ( ! true ) ; + } + } + """); + } + + [Fact] + public async Task StartAndEndTrivia() + { + await AssertFormatAsync(""" + + + + class C { } + + + + + + """, """ + + + + class C { } + + + + + + """); + } + + [Fact] + public async Task FirstTriviaAndAnchoring1() + { + await AssertFormatAsync(""" + + namespace N + { + class C + { + void Method() + { + int i = + 1 + + + 3; + } + } + } + + + + + """, """ + + namespace N { + class C { + void Method() { + int i = + 1 + + + 3; + } + } + } + + + + + """); + } + + [Fact] + public async Task FirstTriviaAndAnchoring2() + { + await AssertFormatAsync(""" + + namespace N + { + class C + { + int i = + 1 + + + 3; + } + } + + + + + """, """ + + namespace N { + class C { + int i = + 1 + + + 3; + } + } + + + + + """); + } + + [Fact] + public async Task FirstTriviaAndAnchoring3() + { + await AssertFormatAsync(""" + + + class C + { + int i = + 1 + + + 3; + } + + + + + """, """ + + + class C { + int i = + 1 + + + 3; + } + + + + + """); + } + + [Fact] + public async Task Base1() + { + await AssertFormatAsync(""" + class C + { + C() : base() + { + } + } + """, """ + class C + { + C ( ) : base ( ) + { + } + } + """); + } + + [Fact] + public async Task This1() + { + await AssertFormatAsync(""" + class C + { + C(int i) : this() + { + } + + C() { } + } + """, """ + class C + { + C ( int i ) : this ( ) + { + } + + C ( ) { } + } + """); + } + + [Fact] + public async Task QueryExpression1() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + var q = + from c in from b in cs select b select c; + } + } + """, """ + class C + { + int Method() + { + var q = + from c in from b in cs select b select c; + } + } + """); + } + + [Fact] + public async Task QueryExpression2() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + var q = from c in + from b in cs + select b + select c; + } + } + """, """ + class C + { + int Method() + { + var q = from c in + from b in cs + select b + select c; + } + } + """); + } + + [Fact] + public async Task QueryExpression3() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + var q = from c in Get(1 + + 2 + + 3) + from b in Get(1 + + 2 + + 3) + select new { b, c }; + } + } + """, """ + class C + { + int Method() + { + var q = from c in Get(1 + + 2 + + 3) + from b in Get(1 + + 2 + + 3) + select new { b, c }; + } + } + """); + } + + [Fact] + public async Task QueryExpression4() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + var q = + from c in + from b in cs + select b + select c; + } + } + """, """ + class C + { + int Method() + { + var q = + from c in + from b in cs + select b + select c; + } + } + """); + } + + [Fact] + public async Task Label1() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + L: int i = 10; + } + } + """, """ + class C + { + int Method() + { + L : int i = 10 ; + } + } + """); + } + + [Fact] + public async Task Label2() + { + await AssertFormatAsync(""" + class C + { + int Method() + { + int x = 1; + L: int i = 10; + } } + """, """ + class C + { + int Method() + { + int x = 1 ; + L : int i = 10 ; + } + } + """); } -}", changedOptionSet: changingOptions); - } - [Fact] - public async Task TestWrappingDefault() - { - await AssertFormatAsync(@"class Class5 -{ - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() + [Fact] + public async Task Label3() { - int x = 0; - if (x == 1) x = 2; else x = 3; - do { x = 4; } while (x != 4); - switch (x) { case 1: break; case 2: break; default: break; } - Del d = delegate (int k) { Console.WriteLine(); Console.WriteLine(); }; + await AssertFormatAsync(""" + class C + { + int Method() + { + int x = 1; + L: + int i = 10; + } + } + """, """ + class C + { + int Method() + { + int x = 1 ; + L : + int i = 10 ; + } + } + """); } -}", @"class Class5 - { - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() - { - int x = 0; - if(x == 1) x = 2; else x =3; - do { x = 4; } while (x != 4); - switch (x) { case 1: break; case 2: break; default: break; } - Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; - } - }"); - } - [Fact] - public async Task TestWrappingNonDefault_FormatBlock() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } - }; - await AssertFormatAsync(@"class Class5 -{ - delegate void Del(int x); - public int Age + [Fact] + public async Task Label4() { - get - { - int age = 0; return age; - } + await AssertFormatAsync(""" + class C + { + int Method() + { + int x = 1; + L: int i = 10; + int next = 30; + } + } + """, """ + class C + { + int Method() + { + int x = 1 ; + L : int i = 10 ; + int next = 30; + } + } + """); } - public int Age2 + + [Fact] + public async Task Label5() { - get - { - int age2 = 0; return age2; - } - set - { - int age2 = value; - } + await AssertFormatAsync(""" + class C + { + int Method() + { + L: int i = 10; + int next = 30; + } + } + """, """ + class C + { + int Method() + { + L : int i = 10 ; + int next = 30; + } + } + """); } - void bar() + + [Fact] + public async Task Label6() { - int x = 0; - if (x == 1) x = 2; else x = 3; - do { x = 4; } while (x != 4); - switch (x) - { - case 1: break; - case 2: break; - default: break; - } - Del d = delegate (int k) { Console.WriteLine(); Console.WriteLine(); }; + await AssertFormatAsync(""" + class C + { + int Method() + { + L: + int i = 10; + int next = 30; + } + } + """, """ + class C + { + int Method() + { + L : + int i = 10 ; + int next = 30; + } + } + """); } - void goo() + + [Fact] + public async Task Label7() { - int xx = 0; int zz = 0; + await AssertFormatAsync(""" + class C + { + int Method() + { + int i2 = 1; + L: + int i = 10; + int next = 30; + } + } + """, """ + class C + { + int Method() + { + int i2 = 1 ; + L : + int i = 10 ; + int next = 30; + } + } + """); } -} -class goo -{ - int x = 0; -}", @"class Class5 -{ - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() - { - int x = 0; - if(x == 1) x = 2; else x =3; - do { x = 4; } while (x != 4); - switch (x) { case 1: break; case 2: break; default: break; } - Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; - } - void goo() { int xx = 0; int zz = 0;} -} -class goo{int x = 0;}", changingOptions); - } - [Fact] - public async Task TestWrappingNonDefault_FormatStatmtMethDecl() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + [Fact] + public async Task Label8() + { + await AssertFormatAsync(""" + class C { - { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } - }; - await AssertFormatAsync(@"class Class5 -{ - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() - { - int x = 0; - if (x == 1) - x = 2; - else - x = 3; - do - { x = 4; } while (x != 4); - switch (x) - { - case 1: - break; - case 2: - break; - default: - break; - } - Del d = delegate (int k) - { Console.WriteLine(); Console.WriteLine(); }; + int Method() + { + L: + int i = + 10; + } + } + """, """ + class C + { + int Method() + { + L: + int i = + 10; + } + } + """); } - void goo() { int y = 0; int z = 0; } -} -class goo -{ - int x = 0; -}", @"class Class5 -{ - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() - { - int x = 0; - if(x == 1) x = 2; else x =3; - do { x = 4; } while (x != 4); - switch (x) { case 1: break; case 2: break; default: break; } - Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; - } - void goo(){int y=0; int z =0 ;} -} -class goo -{ - int x = 0; -}", changingOptions); - } - [Fact] - public async Task TestWrappingNonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.WrappingPreserveSingleLine, false }, - { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } - }; - await AssertFormatAsync(@"class Class5 -{ - delegate void Del(int x); - public int Age + [Fact] + public async Task AutoProperty() { - get - { - int age = 0; - return age; - } + await AssertFormatAsync(""" + class Class + { + private int Age { get; set; } + public string Names { get; set; } + } + """, """ + class Class + { + private int Age{get; set; } + public string Names { get; set;} + } + """); } - public int Age2 + + [Fact] + public async Task NormalPropertyGet() { - get - { - int age2 = 0; - return age2; - } - set - { - int age2 = value; - } + await AssertFormatAsync(""" + class Class + { + private string name; + public string Names + { + get + { + return name; + } + } + } + """, """ + class Class + { + private string name; + public string Names + { + get + { + return name; + } + } + } + """); } - void bar() + + [Fact] + public async Task NormalPropertyBoth() { - int x = 0; - if (x == 1) - x = 2; - else - x = 3; - do - { - x = 4; - } while (x != 4); - switch (x) - { - case 1: - break; - case 2: - break; - default: - break; - } - Del d = delegate (int k) - { - Console.WriteLine(); - Console.WriteLine(); - }; + await AssertFormatAsync(""" + class Class + { + private string name; + public string Names + { + get + { + return name; + } + set + { + name = value; + } + } + } + """, """ + class Class + { + private string name; + public string Names + { + get + { + return name; + } + set + { + name = value; + } + } + } + """); } -} -class goo -{ - int x = 0; -}", @"class Class5 -{ - delegate void Del(int x); - public int Age { get { int age = 0; return age; } } - public int Age2 - { - get { int age2 = 0; return age2; } - set { int age2 = value; } - } - void bar() - { - int x = 0; - if(x == 1) x = 2; else x =3; - do { x = 4; } while (x != 4); - switch (x) { case 1: break; case 2: break; default: break; } - Del d = delegate(int k) { Console.WriteLine(); Console.WriteLine(); }; - } -} -class goo{int x = 0;}", changingOptions); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/991480")] - public async Task TestLeaveStatementMethodDeclarationSameLineNotAffectingForStatement() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } - }; - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task ErrorHandling1() { - for (int d = 0; d < 10; ++d) - { } + await AssertFormatAsync(""" + class C + { + int Method() + { + int a b c; + } + } + """, """ + class C + { + int Method() + { + int a b c ; + } + } + """); } -}", @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537763")] + public async Task NullableType() { - for (int d = 0; d < 10; ++d) { } + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + int? i = 10; + } + } + """, """ + class Program + { + static void Main(string[] args) + { + int ? i = 10; + } + } + """); } -}", changingOptions); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751789")] - public async Task NewLineForOpenBracesDefault() - { - await AssertFormatAsync(@"class f00 -{ - void br() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537766")] + public async Task SuppressWrappingOnBraces() { - Func ret = x => - { - return x + 1; - }; - var obj = new - { - // ... - }; - if (true) - { - System.Console.WriteLine(""""); - } - else - { - } - timer.Tick += delegate (object sender, EventArgs e) - - -{ - MessageBox.Show(this, ""Timer ticked""); -}; - - var obj1 = new goo - { - }; - - async void LocalFunction() - { - } - - try - { - } - catch (Exception e) - { - } - finally - { } + await AssertFormatAsync(""" + class Class1 + { } - using (someVar) - { - } + """, """ + class Class1 + {} - switch (switchVar) - { - default: - break; - } + """); } -} - -namespace NS1 -{ - public class goo : System.Object - - + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537824")] + public async Task DoWhile() { - public int f { get; set; } - } -}", @"class f00 -{ - void br() { -Func ret = x => - { - return x + 1; - }; -var obj = new - { - // ... - }; -if(true) -{ - System.Console.WriteLine(""""); - } -else -{ -} - timer.Tick += delegate (object sender, EventArgs e) - - -{ - MessageBox.Show(this, ""Timer ticked""); - }; - -var obj1 = new goo + await AssertFormatAsync(""" + public class Class1 { - }; - - async void LocalFunction() { - } - - try - { - } - catch (Exception e) - { - } - finally - {} - - using (someVar) - { - } - - switch (switchVar) - { - default: - break; - } -} -} - -namespace NS1 { -public class goo : System.Object + void Goo() + { + do + { + } while (true); + } + } + """, """ + public class Class1 + { + void Goo() + { + do + { + }while (true); + } + } + """); + } -{ - public int f { get; set; } -} -}"); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537774")] + public async Task SuppressWrappingBug() + { + await AssertFormatAsync(""" + class Class1 + { + int Goo() + { + return 0; + } + } - [Fact, WorkItem("https://developercommunity.visualstudio.com/content/problem/8808/c-structure-guide-lines-for-unsafe-fixed.html")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/751789")] - public async Task NewLineForOpenBracesNonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + """, """ + class Class1 { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBracePlacement.None } - }; - await AssertFormatAsync(@"class f00 { - void br() { - Func ret = x => { - return x + 1; - }; - var obj = new { - // ... - }; - if (true) { - System.Console.WriteLine(""""); - } - else { - } - timer.Tick += delegate (object sender, EventArgs e) { - MessageBox.Show(this, ""Timer ticked""); - }; + int Goo() + {return 0; + } + } - var obj1 = new goo { - }; + """); + } - async void LocalFunction() { - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537768")] + public async Task PreserveLineForAttribute() + { + await AssertFormatAsync(""" + class Class1 + { + [STAThread] + static void Main(string[] args) + { + } + } - try { - } - catch (Exception e) { - } - finally { } + """, """ + class Class1 + { + [STAThread] + static void Main(string[] args) + { + } + } - using (someVar) { - } + """); + } - switch (switchVar) { - default: - break; - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537878")] + public async Task NoFormattingOnMissingTokens() + { + await AssertFormatAsync(""" + namespace ClassLibrary1 + { + class Class1 + { + void Goo() + { + if (true) + } + } + } - unsafe { - } + """, """ + namespace ClassLibrary1 + { + class Class1 + { + void Goo() + { + if (true) + } + } + } - fixed (int* p = &i) { - } + """); } -} -namespace NS1 { - public class goo : System.Object { - } -}", @"class f00 -{ - void br() { -Func ret = x => -{ - return x + 1; - }; -var obj = new - { - // ... - }; -if(true) -{ - System.Console.WriteLine(""""); - } -else -{ -} - timer.Tick += delegate (object sender, EventArgs e) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537783")] + public async Task UnaryExpression() + { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + int a = 6; + a = a++ + 5; + } + } + """, """ + class Program + { + static void Main(string[] args) + { + int a = 6; + a = a++ + 5; + } + } -{ - MessageBox.Show(this, ""Timer ticked""); - }; + """); + } -var obj1 = new goo + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537885")] + public async Task Pointer() + { + await AssertFormatAsync(""" + class Program { - }; + static void Main(string[] args) + { + int* p; + } + } - async void LocalFunction() + """, """ + class Program { - } + static void Main(string[] args) + { + int* p; + } + } - try - { - } - catch (Exception e) - { - } - finally - {} + """); + } - using (someVar) - { - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50723")] + public async Task TuplePointer() + { + var properlyFormattedCode = """ + public unsafe static class Program + { + public static void Main(string[] args) + { + int* intPointer = null; + (int, int)* intIntPointer = null; + } + } - switch (switchVar) - { - default: - break; - } + """; + await AssertFormatAsync(properlyFormattedCode, properlyFormattedCode); + } - unsafe -{ - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537886")] + public async Task Tild() + { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + int j = 103; + j = ~7; + } + } - fixed (int* p = &i) -{ - } -} -} + """, """ + class Program + { + static void Main(string[] args) + { + int j = 103; + j = ~7; + } + } -namespace NS1 { -public class goo : System.Object + """); + } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] + public async Task ArrayInitializer1() + { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + int[] arr = {1,2, + 3,4 + }; + } + } + """, """ + class Program + { + static void Main(string[] args) + { + int[] arr = {1,2, + 3,4 + }; + } + } -{ -} -}", changingOptions); - } + """); + } - [Fact] - public async Task NewLineForKeywordDefault() - { - await AssertFormatAsync(@"class c -{ - void f00() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] + public async Task ArrayInitializer2() { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + int[] arr = new int[] {1,2, + 3,4 + }; + } + } - try - { - // ... - } - catch (Exception e) - { - // ... - } - finally - { - // ... - } + """, """ + class Program + { + static void Main(string[] args) + { + int[] arr = new int [] {1,2, + 3,4 + }; + } + } - if (a > b) - { - return 3; - } - else - { - return 0; - } + """); } -}", - @"class c -{ -void f00(){ - -try -{ - // ... -} catch (Exception e) -{ - // ... -} finally -{ - // ... -} -if (a > b) -{ - return 3; -} else -{ - return 0; -} -} -}"); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] + public async Task ImplicitArrayInitializer() + { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + var arr = new[] {1,2, + 3,4 + }; + } + } - [Fact] - public async Task NewLineForKeywordNonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + """, """ + class Program { - { CSharpFormattingOptions2.NewLineForElse, false }, - { CSharpFormattingOptions2.NewLineForCatch, false }, - { CSharpFormattingOptions2.NewLineForFinally, false } - }; - await AssertFormatAsync(@"class c -{ - void f00() - { + static void Main(string[] args) + { + var arr = new [] {1,2, + 3,4 + } ; + } + } - try - { - // ... - } catch (Exception e) - { - // ... - } finally - { - // ... - } - if (a > b) - { - return 3; - } else - { - return 0; - } + """); } -}", @"class c -{ -void f00(){ -try -{ - // ... -} + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer0() + { + await AssertFormatAsync(""" + F(stackalloc int[] + { + 1, + 2, + }); + """, """ + F(stackalloc int[] + { + 1, + 2, + } ); + """); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer0_Implicit() + { + await AssertFormatAsync(""" + F(stackalloc[] + { + 1, + 2, + } + ); + """, """ + F( stackalloc [] + { + 1, + 2, + } + ); + """); + } -catch (Exception e) -{ - // ... -} + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer1() + { + await AssertFormatAsync(""" + F( + stackalloc int[] + { + 1,2, + 3,4 + } + ); + """, """ + F( + stackalloc int[] + { + 1,2, + 3,4 + } + ); + """); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer1_Implicit() + { + await AssertFormatAsync(""" + F( + stackalloc[] + { + 1,2, + 3,4 + } + ); + """, """ + F( + stackalloc [] + { + 1,2, + 3,4 + } + ); + """); + } - finally -{ - // ... -} -if (a > b) -{ - return 3; -} + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer2() + { + await AssertFormatAsync(""" + var x = (stackalloc int[] {1,2, + 3 + }); + """, """ + var x = (stackalloc int[] {1,2, + 3 + }); + """); + } -else -{ - return 0; -} -} -}", changingOptions); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] + public async Task StackAllocArrayInitializer2_Implicit() + { + await AssertFormatAsync(""" + var x = (stackalloc[] + {1, + 2, 3 + }); + """, """ + var x = (stackalloc [] + {1, + 2, 3 + }); + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33458")] - public async Task NoNewLineForElseChecksBraceOwner() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] + public async Task CollectionInitializer() + { + await AssertFormatAsync(""" + class Program { - { NewLineForElse, false }, - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } - }; + static void Main(string[] args) + { + var arr = new List {1,2, + 3,4 + }; + } + } - await AssertFormatAsync(@"class Class -{ - void Method() - { - if (true) - for (int i = 0; i < 10; i++) { - Method(); + """, """ + class Program + { + static void Main(string[] args) + { + var arr = new List {1,2, + 3,4 + }; + } } - else - return; - } -}", @"class Class -{ - void Method() - { - if (true) - for (int i = 0; i < 10; i++) { - Method(); - } else - return; + + """); } -}", changedOptionSet: changingOptions); - } - [Fact] - public async Task NewLineForExpressionDefault() - { - await AssertFormatAsync(@"class f00 -{ - void br() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537916")] + public async Task AddressOfOperator() { - var queryLowNums = from num in numbers - where num < 5 - select num; - - var queryLowNums = + await AssertFormatAsync(""" + unsafe class Class1 + { + void Method() + { + int a = 12; + int* p = &a; + } + } - from num in numbers - where num < 5 - select num; + """, """ + unsafe class Class1 + { + void Method() + { + int a = 12; + int* p = &a; + } + } - var q = from c in cust - from o in c.Orders - orderby o.Total descending - select new { c.Name, c.OrderID }; - var obj = new - { - X1 = 0, - Y1 = 1, - X2 = 2, - Y2 = 3 - }; - var obj1 = new { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; - MyObject obj = new MyObject - { - X1 = 0, - Y1 = 1, - X2 = 2, - Y2 = 3 - }; - MyObject obj = new MyObject { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; + """); } -}", @"class f00 -{ - void br() - { -var queryLowNums = from num in numbers where num < 5 - select num; - -var queryLowNums = - - from num in numbers where num < 5 - select num; - - var q = from c in cust -from o in c.Orders orderby o.Total descending - select new { c.Name, c.OrderID }; -var obj = new { X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; -var obj1 = new { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; - MyObject obj = new MyObject { X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; -MyObject obj = new MyObject { X1 = 0, Y1 = 1, X2 = 2, Y2 = 3 }; - } -}"); - } - [Fact] - public async Task NewLineForExpressionNonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.NewLineForMembersInObjectInit, false }, - { CSharpFormattingOptions2.NewLineForMembersInAnonymousTypes, false }, - { CSharpFormattingOptions2.NewLineForClausesInQuery, false } - }; - await AssertFormatAsync(@"class f00 -{ - void br() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537885")] + public async Task DereferenceOperator() { + await AssertFormatAsync(""" + unsafe class Class1 + { + void Method() + { + int a = 12; + int* p = &a; + Console.WriteLine(*p); + } + } - var queryLowNums = from num in numbers where num < 5 - select num; + """, """ + unsafe class Class1 + { + void Method() + { + int a = 12; + int* p = & a; + Console.WriteLine(* p); + } + } - var queryLowNums = + """); + } - from num in numbers where num < 5 - select num; + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537905")] + public async Task Namespaces() + { + await AssertFormatAsync(""" + using System; + using System.Data; + """, @"using System; using System.Data;"); + } - var q = from c in cust - from o in c.Orders orderby o.Total descending - select new { c.Name, c.OrderID }; - var obj = new - { - X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; - MyObject obj = new MyObject - { - X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; + [Fact] + public async Task NamespaceDeclaration() + { + await AssertFormatAsync(""" + namespace N + { + } + """, """ + namespace N + { + } + """); } -}", @"class f00 -{ - void br() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537902")] + public async Task DoWhile1() { + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + do { } + while (i < 4); + } + } + """, """ + class Program + { + static void Main(string[] args) + { + do { } + while (i < 4); + } + } + """); + } -var queryLowNums = from num in numbers where num < 5 - select num; + [Fact] + public async Task NewConstraint() + { + await AssertFormatAsync(""" + class Program + { + void Test(T t) where T : new() + { + } + } + """, """ + class Program + { + void Test(T t) where T : new ( ) + { + } + } + """); + } -var queryLowNums = + [Fact] + public async Task UnaryExpressionWithInitializer() + { + await AssertFormatAsync(""" + using System; + using System.Collections.Generic; + using System.Linq; - from num in numbers where num < 5 - select num; + class Program + { + static void Main(string[] args) + { + if ((new int[] { 1, 2, 3 }).Any()) + { + return; + } + } + } + """, """ + using System; + using System.Collections.Generic; + using System.Linq; - var q = from c in cust -from o in c.Orders orderby o.Total descending - select new { c.Name, c.OrderID }; -var obj = new { X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; - MyObject obj = new MyObject { X1 = 0, Y1 = 1, - X2 = 2, - Y2 = 3 - }; + class Program + { + static void Main(string[] args) + { + if ((new int[] { 1, 2, 3 } ).Any()) + { + return; + } + } + } + """); } -}", changingOptions); - } - [Fact] - public async Task Enums_Bug2586() - { - await AssertFormatAsync(@"enum E -{ - a = 10, - b, - c -}", @"enum E -{ - a = 10, - b, - c -}"); - } - - [Fact] - public async Task DoNotInsertLineBreaksInSingleLineEnum() - => await AssertFormatAsync(@"enum E { a = 10, b, c }", @"enum E { a = 10, b, c }"); - - [Fact] - public async Task AlreadyFormattedSwitchIsNotFormatted_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact] + public async Task Attributes1() { - switch (3) - { - case 0: - break; - } + await AssertFormatAsync(""" + class Program + { + [Flags] public void Method() { } + } + """, """ + class Program + { + [ Flags ] public void Method ( ) { } + } + """); } -}", @"class C -{ - void M() + + [Fact] + public async Task Attributes2() { - switch (3) - { - case 0: - break; - } + await AssertFormatAsync(""" + class Program + { + [Flags] + public void Method() { } + } + """, """ + class Program + { + [ Flags ] + public void Method ( ) { } + } + """); } -}"); - } - [Fact] - public async Task BreaksAreAlignedInSwitchCasesFormatted_Bug2587() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538288")] + public async Task ColonColon1() { - switch (3) - { - case 0: - break; - } + await AssertFormatAsync(""" + class Program + { + public void Method() + { + throw new global::System.NotImplementedException(); + } + } + """, """ + class Program + { + public void Method ( ) { + throw new global :: System.NotImplementedException(); + } + } + """); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538354")] + public async Task BugFix3939() { - switch (3) - { - case 0: - break; - } + await AssertFormatAsync(""" + using + System. + Collections. + Generic; + """, """ + using + System. + Collections. + Generic; + """); } -}"); - } - [Fact] - public async Task BreaksAndBracesAreAlignedInSwitchCasesWithBracesFormatted_Bug2587() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538354")] + public async Task Tab1() + => await AssertFormatAsync(@"using System;", @" using System;"); + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538329")] + public async Task SuppressLinkBreakInIfElseStatement() { - switch (3) - { - case 0: + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) { - break; + int a; + if (true) a = 10; + else a = 11; } - } - } -}", @"class C -{ - void M() - { - switch (3) - { - case 0: + } + """, """ + class Program { - break; + static void Main(string[] args) + { + int a; + if (true) a = 10; + else a = 11; } - } + } + """); } -}"); - } - [Fact] - public async Task LineBreaksAreNotInsertedForSwitchCasesOnASingleLine1() - { - await AssertFormatAsync(@"class C -{ - void M() - { - switch (3) - { - case 0: break; - default: break; - } - } -}", @"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538464")] + public async Task BugFix4087() { - switch (3) - { - case 0: break; - default: break; - } + await AssertFormatAsync(""" + class Program + { + static void Main(string[] args) + { + Func fun = x => { return x + 1; } + } + } + """, """ + class Program + { + static void Main(string[] args) + { + Func fun = x => { return x + 1; } + } + } + """); } -}"); - } - [Fact] - public async Task LineBreaksAreNotInsertedForSwitchCasesOnASingleLine2() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538511")] + public async Task AttributeTargetSpecifier() { - switch (3) - { - case 0: { break; } - default: { break; } - } + var code = """ + public class Class1 + { + [method : + void Test() + { + } + } + """; + + var expected = """ + public class Class1 + { + [method: + void Test() + { + } + } + """; + + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538635")] + public async Task Finalizer() { - switch (3) - { - case 0: { break; } - default: { break; } - } + var code = """ + public class Class1 + { + ~ Class1() { } + } + """; + + var expected = """ + public class Class1 + { + ~Class1() { } + } + """; + + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task FormatLabelAndGoto1_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538743")] + public async Task BugFix4442() { - Goo: - goto Goo; + var code = """ + class Program + { + static void Main(string[] args) + { + string str = "ab,die|wo"; + string[] a = str.Split(new char[] { ',', '|' }) + ; + } + } + """; + + await AssertFormatAsync(code, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538658")] + public async Task BugFix4328() { -Goo: -goto Goo; + var code = """ + class Program + { + static void Main(string[] args) + { + double d = new double (); + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + double d = new double(); + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task FormatLabelAndGoto2_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538658")] + public async Task BugFix4515() { - int x = 0; - Goo: - goto Goo; + var code = """ + class Program + { + static void Main(string[] args) + { + var t = typeof ( System.Object ) ; + var t1 = default ( System.Object ) ; + var t2 = sizeof ( System.Object ) ; + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + var t = typeof(System.Object); + var t1 = default(System.Object); + var t2 = sizeof(System.Object); + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact] + public async Task CastExpressionTest() { -int x = 0; -Goo: -goto Goo; + var code = """ + class Program + { + static void Main(string[] args) + { + var a = (int) 1; + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + var a = (int)1; + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task FormatNestedLabelAndGoto1_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact] + public async Task NamedParameter() { - if (true) - { - Goo: - goto Goo; - } + var code = """ + class Program + { + static void Main(string[] args) + { + Main ( args : null ) ; + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + Main(args: null); + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact] + public async Task RefReadonlyParameters() { -if (true) -{ -Goo: -goto Goo; -} + var code = """ + class C + { + int this [ ref readonly int x , ref readonly int y ] { get ; set ; } + void M ( ref readonly int x , ref readonly int y ) { } + } + """; + var expected = """ + class C + { + int this[ref readonly int x, ref readonly int y] { get; set; } + void M(ref readonly int x, ref readonly int y) { } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task FormatNestedLabelAndGoto2_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539259")] + public async Task BugFix5143() { - if (true) - { - int x = 0; - Goo: - goto Goo; - } + var code = """ + class Program + { + static void Main(string[] args) + { + int x = Goo ( + delegate ( int x ) { return x ; } ) ; + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + int x = Goo( + delegate (int x) { return x; }); + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539338")] + public async Task BugFix5251() { -if (true) -{ -int x = 0; -Goo: -goto Goo; -} + var code = """ + class Program + { + public static string Goo { get; private set; } + } + """; + var expected = """ + class Program + { + public static string Goo { get; private set; } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task AlreadyFormattedGotoLabelIsNotFormatted1_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539358")] + public async Task BugFix5277() { - Goo: - goto Goo; + var code = """ + + #if true + #endif + + """; + var expected = """ + + #if true + #endif + + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539542")] + public async Task BugFix5544() { - Goo: - goto Goo; + var code = """ + + class Program + { + unsafe static void Main(string[] args) + { + Program* p; + p -> Goo = 5; + } + } + + """; + var expected = """ + + class Program + { + unsafe static void Main(string[] args) + { + Program* p; + p->Goo = 5; + } + } + + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task AlreadyFormattedGotoLabelIsNotFormatted2_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539587")] + public async Task BugFix5602() { - Goo: goto Goo; + var code = """ + class Bug + { + public static void func() + { + long b = // + } + } + """; + var expected = """ + class Bug + { + public static void func() + { + long b = // + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539616")] + public async Task BugFix5637() { - Goo: goto Goo; + var code = """ + class Bug + { + // test + public static void func() + { + } + } + """; + var expected = """ + class Bug + { + // test + public static void func() + { + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task AlreadyFormattedGotoLabelIsNotFormatted3_Bug2588() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact] + public async Task GenericType() { - int x = 0; - Goo: - goto Goo; + var code = """ + class Bug + { + class N : Bug< T [ ] > + { + } + } + """; + var expected = """ + class Bug + { + class N : Bug + { + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539878")] + public async Task BugFix5978() { - int x = 0; - Goo: - goto Goo; + var code = """ + class Program + { + static void Main(string[] args) + { + int i = 3; + label4: + if (i < 5) + { + label5: + if (i == 4) + { + } + else + { + System.Console.WriteLine("a"); + } + } + } + } + """; + var expected = """ + class Program + { + static void Main(string[] args) + { + int i = 3; + label4: + if (i < 5) + { + label5: + if (i == 4) + { + } + else + { + System.Console.WriteLine("a"); + } + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task DoNotAddLineBreakBeforeWhere1_Bug2582() - { - await AssertFormatAsync(@"class C -{ - void M() where T : I + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539878")] + public async Task BugFix5979() { + var code = """ + delegate int del(int i); + class Program + { + static void Main(string[] args) + { + del q = x => + { + label2: goto label1; + label1: return x; + }; + } + } + """; + var expected = """ + delegate int del(int i); + class Program + { + static void Main(string[] args) + { + del q = x => + { + label2: goto label1; + label1: return x; + }; + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() where T : I + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539891")] + public async Task BugFix5993() { + var code = """ + public class MyClass + { + public static void Main() + { + lab1: + { + lab1:// CS0158 + goto lab1; + } + } + } + """; + var expected = """ + public class MyClass + { + public static void Main() + { + lab1: + { + lab1:// CS0158 + goto lab1; + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - - [Fact] - public async Task DoNotAddLineBreakBeforeWhere2_Bug2582() - { - await AssertFormatAsync(@"class C where T : I -{ -}", @"class C where T : I -{ -}"); - } - [Fact] - public async Task DoNotAddSpaceAfterUnaryMinus() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540315")] + public async Task BugFix6536() { - int x = -1; + var code = """ + public class MyClass + { + public static void Main() + { + int i = - - 1 + + + 1 + - + 1 + - + 1 ; + } + } + """; + var expected = """ + public class MyClass + { + public static void Main() + { + int i = - -1 + + +1 + -+1 + -+1; + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540801")] + public async Task BugFix7211() { - int x = -1; + var code = """ + class Program + { + static void Main(string[] args) + { + while (0 > new int[] { 1 }.Length) + { + System.Console.WriteLine("Hello"); + } + } + } + """; + + var expected = """ + class Program + { + static void Main(string[] args) + { + while (0 > new int[] { 1 }.Length) + { + System.Console.WriteLine("Hello"); + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task DoNotAddSpaceAfterUnaryPlus() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541035")] + public async Task BugFix7564_1() { - int x = +1; + var code = """ + class Program + { + static void Main(string[] args) + { + while (null != new int[] { 1 }) + { + System.Console.WriteLine("Hello"); + } + } + } + """; + + var expected = """ + class Program + { + static void Main(string[] args) + { + while (null != new int[] { 1 }) + { + System.Console.WriteLine("Hello"); + } + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541035")] + public async Task BugFix7564_2() { - int x = +1; + var code = """ + class Program + { + static void Main(string[] args) + { + foreach (var f in new int[] { 5 }) + { + Console.WriteLine(f); + } + } + } + """; + + var expected = """ + class Program + { + static void Main(string[] args) + { + foreach (var f in new int[] { 5 }) + { + Console.WriteLine(f); + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DoNotAddSpaceAfterIncrement() - { - var code = @"class C -{ - void M(int[] i) + [Fact, WorkItem(8385, "DevDiv_Projects/Roslyn")] + public async Task NullCoalescingOperator() { - ++i[0]; + var code = """ + class C + { + void M() + { + object o2 = null??null; + } + } + """; + + var expected = """ + class C + { + void M() + { + object o2 = null ?? null; + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(code, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DoNotAddSpaceBeforeIncrement() - { - var code = @"class C -{ - void M(int[] i) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541925")] + public async Task QueryContinuation() { - i[0]++; + var code = """ + using System.Linq; + class C + { + static void Main(string[] args) + { + var temp = from x in "abc" + let z = x.ToString() + select z into w + select w; + } + } + """; + + var expected = """ + using System.Linq; + class C + { + static void Main(string[] args) + { + var temp = from x in "abc" + let z = x.ToString() + select z into w + select w; + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(code, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DoNotAddSpaceAfterDecrement() - { - var code = @"class C -{ - void M(int[] i) + [Fact] + public async Task QueryContinuation2() { - --i[0]; + var code = """ + using System.Linq; + class C + { + static void Main(string[] args) + { + var temp = from x in "abc" select x into + } + } + """; + + var expected = """ + using System.Linq; + class C + { + static void Main(string[] args) + { + var temp = from x in "abc" + select x into + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(code, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545909")] - public async Task DoNotAddSpaceBeforeDecrement() - { - var code = @"class C -{ - void M(int[] i) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542305")] + public async Task AttributeFormatting1() { - i[0]--; + var code = """ + class Program + { + void AddClass(string name,[OptionalAttribute] object position,[OptionalAttribute] object bases) + { + } + } + """; + + var expected = """ + class Program + { + void AddClass(string name, [OptionalAttribute] object position, [OptionalAttribute] object bases) + { + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(code, code); - } - [Fact] - public async Task Anchoring() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542304")] + public async Task CloseBracesInArgumentList() { - Console.WriteLine(""Goo"", - 0, 1, - 2); + var code = """ + class Program + { + static void Main(string[] args) + { + var relativeIndentationGetter = new Lazy(() => + { + var indentationDelta = operation.IndentationDeltaOrPosition * this.OptionSet.IndentationSize; + var baseIndentation = this.tokenStream.GetCurrentColumn(operation.BaseToken); + + return baseIndentation + indentationDelta; + } , isThreadSafe: true); + } + } + """; + + var expected = """ + class Program + { + static void Main(string[] args) + { + var relativeIndentationGetter = new Lazy(() => + { + var indentationDelta = operation.IndentationDeltaOrPosition * this.OptionSet.IndentationSize; + var baseIndentation = this.tokenStream.GetCurrentColumn(operation.BaseToken); + + return baseIndentation + indentationDelta; + }, isThreadSafe: true); + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542538")] + public async Task MissingTokens() { - Console.WriteLine(""Goo"", - 0, 1, - 2); + var code = """ + using System; + delegate void myDelegate(int name = 1); + class innerClass + { + public innerClass() + { + myDelegate x = (int y=1) => { return; }; + } + } + """; + + var expected = """ + using System; + delegate void myDelegate(int name = 1); + class innerClass + { + public innerClass() + { + myDelegate x = (int y = 1) => { return; }; + } + } + """; + + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task Exclamation() - { - await AssertFormatAsync(@"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542199")] + public async Task ColumnOfVeryFirstToken() { - if (!true) ; + var code = @" W )b"; + + var expected = @"W )b"; + + await AssertFormatAsync(expected, code); } -}", @"class C -{ - void M() + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542718")] + public async Task EmptySuppressionSpan() { - if ( ! true ) ; + var code = """ + enum E + { + a,, + } + """; + + var expected = """ + enum E + { + a,, + } + """; + + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task StartAndEndTrivia() - { - await AssertFormatAsync(@" + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542790")] + public async Task LabelInSwitch() + { + var code = """ + class test + { + public static void Main() + { + string target = "t1"; + switch (target) + { + case "t1": + label1: + goto label1; + case "t2": + label2: + goto label2; + } + } + } + """; + var expected = """ + class test + { + public static void Main() + { + string target = "t1"; + switch (target) + { + case "t1": + label1: + goto label1; + case "t2": + label2: + goto label2; + } + } + } + """; -class C { } + await AssertFormatAsync(expected, code); + } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543112")] + public void FormatArbitaryNode() + { + var expected = """ + public int Prop + { + get + { + return c; + } + set + { + c = value; + } + } + """; + + var property = SyntaxFactory.PropertyDeclaration( + attributeLists: [], + [PublicKeyword], + SyntaxFactory.ParseTypeName("int"), + null, + SyntaxFactory.Identifier("Prop"), + SyntaxFactory.AccessorList([ + SyntaxFactory.AccessorDeclaration( + SyntaxKind.GetAccessorDeclaration, + SyntaxFactory.Block(SyntaxFactory.ParseStatement("return c;"))), + SyntaxFactory.AccessorDeclaration( + SyntaxKind.SetAccessorDeclaration, + SyntaxFactory.Block(SyntaxFactory.ParseStatement("c = value;")))])); + + Assert.NotNull(property); + using var workspace = new AdhocWorkspace(); + var newProperty = Formatter.Format(property, workspace.Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); + + Assert.Equal(expected, newProperty.ToFullString()); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543140")] + public async Task OmittedTypeArgument() + { + var code = """ + using System; + using System.Collections.Generic; + using System.Linq; + + class Program + { + static void Main(string[] args) + { + Console.WriteLine(typeof(Dictionary<, >).IsGenericTypeDefinition); + } + } + """; + var expected = """ + using System; + using System.Collections.Generic; + using System.Linq; -", @" - - -class C { } - - - - -"); - } + class Program + { + static void Main(string[] args) + { + Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); + } + } + """; - [Fact] - public async Task FirstTriviaAndAnchoring1() - { - await AssertFormatAsync(@" -namespace N -{ - class C - { - void Method() - { - int i = - 1 - + - 3; - } + await AssertFormatAsync(expected, code); } -} - + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543131")] + public async Task TryAfterLabel() + { + var code = """ + using System; + class Program + { + static object lockObj = new object(); + static int Main() + { + int sum = 0; + lock (lockObj) + try + { sum = 0; } + catch (Exception ex) + { Console.WriteLine(ex); } + return sum; + } + } + """; -", @" -namespace N { - class C { - void Method() { - int i = - 1 - + - 3; - } -} -} + var expected = """ + using System; + class Program + { + static object lockObj = new object(); + static int Main() + { + int sum = 0; + lock (lockObj) + try + { sum = 0; } + catch (Exception ex) + { Console.WriteLine(ex); } + return sum; + } + } + """; + await AssertFormatAsync(expected, code); + } + [Fact] + public async Task QueryContinuation1() + { + var code = """ + using System.Linq; -"); - } + class Program + { + static void Main(string[] args) + { + var q = from arg in args + group arg by arg.Length into final + where final + .Select(c => c) + .Distinct() + .Count() > 0 + select final; + } + } + """; - [Fact] - public async Task FirstTriviaAndAnchoring2() - { - await AssertFormatAsync(@" -namespace N -{ - class C - { - int i = - 1 - + - 3; - } -} + var expected = """ + using System.Linq; + class Program + { + static void Main(string[] args) + { + var q = from arg in args + group arg by arg.Length into final + where final + .Select(c => c) + .Distinct() + .Count() > 0 + select final; + } + } + """; + await AssertFormatAsync(expected, code); + } -", @" -namespace N { - class C { - int i = - 1 - + - 3; -} -} + [Fact] + public async Task TestCSharpFormattingSpacingOptions() + { + var text = + """ - + interface f1 + { } -"); - } + interface f2 : f1 { } - [Fact] - public async Task FirstTriviaAndAnchoring3() - { - await AssertFormatAsync(@" + struct d2 : f1 { } -class C -{ - int i = - 1 - + - 3; -} + class goo : System . Object + { + public int bar = 1* 2; + public void goobar ( ) + { + goobar ( ); + } + public int toofoobar( int i , int j ) + { + int s = ( int ) ( 34 ); + if ( i < 0 ) + { + } + return toofoobar( i,j ); + } + public string parfoobar(string [ ] str) + { + for(int i = 0 ; i < 28 ; i++) { } + return str[ 5 ]; + } + } + """; + var expectedFormattedText = + """ + interface f1 + { } + interface f2 : f1 { } -", @" - - class C { - int i = - 1 - + - 3; -} - + struct d2 : f1 { } + class goo : System.Object + { + public int bar = 1 * 2; + public void goobar() + { + goobar(); + } + public int toofoobar(int i, int j) + { + int s = (int)(34); + if (i < 0) + { + } + return toofoobar(i, j); + } + public string parfoobar(string[] str) + { + for (int i = 0; i < 28; i++) { } + return str[5]; + } + } + """; -"); - } + await AssertFormatAsync(expectedFormattedText, text); + } - [Fact] - public async Task Base1() - { - await AssertFormatAsync(@"class C -{ - C() : base() + [Fact] + public async Task SpacingFixInTokenBasedForIfAndSwitchCase() { - } -}", @" class C + var code = """ + class Class5{ + void bar() { - C ( ) : base ( ) + if(x == 1) + x = 2; else x = 3; + switch (x) { + case 1: break; case 2: break; default: break;} + } + } + """; + var expectedCode = """ + class Class5 { + void bar() + { + if (x == 1) + x = 2; + else x = 3; + switch (x) + { + case 1: break; + case 2: break; + default: break; + } + } } - } "); - } + """; + await AssertFormatAsync(expectedCode, code); + } - [Fact] - public async Task This1() - { - await AssertFormatAsync(@"class C -{ - C(int i) : this() + [Fact] + public async Task SpacingInDeconstruction() { + var code = """ + class Class5{ + void bar() + { + var(x,y)=(1,2); + } + } + """; + var expectedCode = """ + class Class5 + { + void bar() + { + var (x, y) = (1, 2); + } + } + """; + + await AssertFormatAsync(expectedCode, code); } - C() { } -}", @" class C + [Fact] + public async Task SpacingInNullableTuple() + { + var code = """ + class Class5 { - C ( int i ) : this ( ) + void bar() + { + (int, string) ? x = (1, "hello"); + } + } + """; + var expectedCode = """ + class Class5 { + void bar() + { + (int, string)? x = (1, "hello"); + } } + """; - C ( ) { } - } "); - } + await AssertFormatAsync(expectedCode, code); + } - [Fact] - public async Task QueryExpression1() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task SpacingInTupleArrayCreation() { - var q = - from c in from b in cs select b select c; + var code = """ + class C + { + void bar() + { + (string a, string b)[] ab = new(string a, string b) [1]; + } + } + """; + var expectedCode = """ + class C + { + void bar() + { + (string a, string b)[] ab = new (string a, string b)[1]; + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", @" class C + + [Fact] + public async Task SpacingInTupleArrayCreation2() + { + var code = """ + class C { - int Method() - { - var q = - from c in from b in cs select b select c; - } - } "); - } + void bar() + { + (string a, string b)[] ab = new( + } + } + """; + var expectedCode = """ + class C + { + void bar() + { + (string a, string b)[] ab = new( + } + } + """; - [Fact] - public async Task QueryExpression2() - { - await AssertFormatAsync(@"class C -{ - int Method() + await AssertFormatAsync(expectedCode, code); + } + + [Fact] + public async Task SpacingInImplicitObjectCreation() { - var q = from c in - from b in cs - select b - select c; + var code = """ + class C + { + void bar() + { + C a = new (); + } + } + """; + var expectedCode = """ + class C + { + void bar() + { + C a = new(); + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", @" class C + + [Fact] + public async Task FormatRecursivePattern_Positional() + { + var code = """ + class C { - int Method() - { - var q = from c in - from b in cs - select b - select c; - } - } "); - } + void M() { _ = this is ( 1 , 2 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() { _ = this is (1, 2); } + } + """; - [Fact] - public async Task QueryExpression3() - { - await AssertFormatAsync(@"class C -{ - int Method() + await AssertFormatAsync(expectedCode, code); + } + + [Fact] + public async Task FormatRecursivePattern_Positional_Singleline() { - var q = from c in Get(1 + - 2 + - 3) - from b in Get(1 + - 2 + - 3) - select new { b, c }; + var code = """ + class C + { + void M() { + _ = this is ( 1 , 2 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is (1, 2); + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", @"class C -{ - int Method() + + [Fact] + public async Task FormatRecursivePattern_Positional_Multiline() { - var q = from c in Get(1 + - 2 + - 3) - from b in Get(1 + - 2 + - 3) - select new { b, c }; + var code = """ + class C + { + void M() { + _ = this is ( 1 , + 2 , + 3 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is (1, + 2, + 3); + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}"); - } - [Fact] - public async Task QueryExpression4() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatRecursivePattern_Positional_Multiline2() { - var q = - from c in - from b in cs - select b - select c; + var code = """ + class C + { + void M() { + _ = this is ( 1 , + 2 , + 3 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is (1, + 2, + 3); + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", @" class C + + [Fact] + public async Task FormatRecursivePattern_Positional_Multiline3() + { + var code = """ + class C { - int Method() - { - var q = - from c in - from b in cs - select b - select c; - } - } "); - } + void M() { + _ = this is + ( 1 , + 2 , + 3 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + (1, + 2, + 3); + } + } + """; + await AssertFormatAsync(expectedCode, code); + } - [Fact] - public async Task Label1() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatRecursivePattern_Positional_Multiline4() { - L: int i = 10; + var code = """ + class C + { + void M() { + _ = this is + ( 1 , + 2 , 3 ) ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + (1, + 2, 3); + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", @" class C + + [Fact] + public async Task FormatRecursivePattern_Properties_Singleline() + { + var code = """ + class C { - int Method() - { - L : int i = 10 ; - } - } "); - } + void M() { _ = this is C{ P1 : 1 } ; } + } + """; + var expectedCode = """ + class C + { + void M() { _ = this is C { P1: 1 }; } + } + """; + + await AssertFormatAsync(expectedCode, code); + } - [Fact] - public async Task Label2() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatRecursivePattern_Properties_Multiline() { - int x = 1; - L: int i = 10; - } -}", @" class C + var code = """ + class C { - int Method() - { -int x = 1 ; - L : int i = 10 ; - } - } "); - } + void M() { + _ = this is + { + P1 : 1 , + P2 : 2 + } ; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + { + P1: 1, + P2: 2 + }; + } + } + """; - [Fact] - public async Task Label3() - { - await AssertFormatAsync(@"class C -{ - int Method() - { - int x = 1; - L: - int i = 10; + await AssertFormatAsync(expectedCode, code); } -}", @" class C - { - int Method() - { -int x = 1 ; - L : -int i = 10 ; - } - } "); - } - [Fact] - public async Task Label4() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatRecursivePattern_Properties_Multiline2() { - int x = 1; - L: int i = 10; - int next = 30; - } -}", @" class C + var code = """ + class C { - int Method() - { -int x = 1 ; - L : int i = 10 ; - int next = 30; - } - } "); - } + void M() { + _ = this is { + P1 : 1 , + P2 : 2 + } ; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + { + P1: 1, + P2: 2 + }; + } + } + """; - [Fact] - public async Task Label5() - { - await AssertFormatAsync(@"class C -{ - int Method() - { - L: int i = 10; - int next = 30; + await AssertFormatAsync(expectedCode, code); } -}", @" class C - { - int Method() - { - L : int i = 10 ; - int next = 30; - } - } "); - } - [Fact] - public async Task Label6() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatRecursivePattern_Properties_Multiline3() { - L: - int i = 10; - int next = 30; - } -}", @" class C + var code = """ + class C { - int Method() - { - L : -int i = 10 ; - int next = 30; - } - } "); - } + void M() { + _ = this is { + P1 : 1 , + P2 : 2, P3: 3 + } ; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + { + P1: 1, + P2: 2, P3: 3 + }; + } + } + """; - [Fact] - public async Task Label7() - { - await AssertFormatAsync(@"class C -{ - int Method() - { - int i2 = 1; - L: - int i = 10; - int next = 30; + await AssertFormatAsync(expectedCode, code); } -}", @" class C - { - int Method() - { - int i2 = 1 ; - L : -int i = 10 ; - int next = 30; - } - } "); - } - [Fact] - public async Task Label8() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] + public async Task FormatRecursivePattern_NoSpaceBetweenTypeAndPositionalSubpattern() { - L: - int i = - 10; - } -}", @" class C + var code = """ + class C { - int Method() - { - L: - int i = - 10; - } - } "); - } - - [Fact] - public async Task AutoProperty() - { - await AssertFormatAsync(@"class Class -{ - private int Age { get; set; } - public string Names { get; set; } -}", @" class Class -{ - private int Age{get; set; } - public string Names { get; set;} -}"); - } + void M() { + _ = this is C( 1 , 2 ){} ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is C(1, 2) { }; + } + } + """; + // no space separates the type and the positional pattern + await AssertFormatAsync(expectedCode, code); + } - [Fact] - public async Task NormalPropertyGet() - { - await AssertFormatAsync(@"class Class -{ - private string name; - public string Names + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] + public async Task FormatRecursivePattern_PreferSpaceBetweenTypeAndPositionalSubpattern() { - get + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - return name; - } + { CSharpFormattingOptions2.SpaceAfterMethodCallName, true } + }; + var code = """ + class C + { + void M() { + _ = this is C( 1 , 2 ){} ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is C (1, 2) { }; + } + } + """; + await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); } -}", @"class Class -{ - private string name; - public string Names + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] + public async Task FormatRecursivePattern_PreferSpaceInsidePositionalSubpatternParentheses() { - get - { - return name; - } + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } + }; + var code = """ + class C + { + void M() { + _ = this is C( 1 , 2 ){} ; + _ = this is C( ){} ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is C( 1, 2 ) { }; + _ = this is C() { }; + } + } + """; + await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); } -}"); - } - [Fact] - public async Task NormalPropertyBoth() - { - await AssertFormatAsync(@"class Class -{ - private string name; - public string Names + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] + public async Task FormatRecursivePattern_PreferSpaceInsideEmptyPositionalSubpatternParentheses() { - get - { - return name; - } - set + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - name = value; - } + { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true } + }; + var code = """ + class C + { + void M() { + _ = this is C( 1 , 2 ){} ; + _ = this is C( ){} ; } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is C(1, 2) { }; + _ = this is C( ) { }; + } + } + """; + await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); } -}", @"class Class -{ - private string name; - public string Names + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34683")] + public async Task FormatRecursivePattern_InBinaryOperation() { - get + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - return name; - } - set - { - name = value; - } + { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } + }; + var code = """ + class C + { + void M() + { + return + typeWithAnnotations is { } && true; + } + } + """; + var expectedCode = code; + await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); } -}"); - } - [Fact] - public async Task ErrorHandling1() - { - await AssertFormatAsync(@"class C -{ - int Method() + [Fact] + public async Task FormatPropertyPattern_MultilineAndEmpty() { - int a b c; - } -}", @" class C + var code = """ + class C { - int Method() - { - int a b c ; - } - } "); - } + void M() { + _ = this is + { + }; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is + { + }; + } + } + """; + await AssertFormatAsync(expectedCode, code); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537763")] - public async Task NullableType() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task FormatSwitchExpression_IndentArms() { - int? i = 10; + var code = """ + class C + { + void M() { + _ = this switch + { + { P1: 1} => true, + (0, 1) => true, + _ => false + }; + + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + { P1: 1 } => true, + (0, 1) => true, + _ => false + }; + + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", @"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task FormatPropertyPattern_FollowedByInvocation() { - int ? i = 10; + var code = """ + class C + { + void M() { + _ = this is { } + M(); + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is { } + M(); + } + } + """; + // although 'M' will be parsed into the pattern on line above, we should not wrap the pattern + await AssertFormatAsync(expectedCode, code); } -}"); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537766")] - public async Task SuppressWrappingOnBraces() - { - await AssertFormatAsync(@"class Class1 -{ } -", @"class Class1 -{} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537824")] - public async Task DoWhile() - { - await AssertFormatAsync(@"public class Class1 -{ - void Goo() + [Fact] + public async Task FormatPositionalPattern_FollowedByInvocation() { - do - { - } while (true); + var code = """ + class C + { + void M() { + _ = this is (1, 2) { } + M(); + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is (1, 2) { } + M(); + } + } + """; + // although 'M' will be parsed into the pattern on line above, we should not wrap the pattern + await AssertFormatAsync(expectedCode, code); } -} -", @"public class Class1 -{ - void Goo() + + [Fact] + public async Task FormatPositionalPattern_FollowedByScope() { - do - { - }while (true); + var code = """ + class C + { + void M() { + _ = this is (1, 2) + { + M(); + } + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is (1, 2) + { + M(); + } + } + } + """; + // You should not invoke Format on incomplete code and expect nice results + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537774")] - public async Task SuppressWrappingBug() - { - await AssertFormatAsync(@"class Class1 -{ - int Goo() + [Fact] + public async Task FormatSwitchExpression_MultilineAndNoArms() { - return 0; - } -} -", @"class Class1 -{ - int Goo() - {return 0; + var code = """ + class C + { + void M() { + _ = this switch + { + }; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + }; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537768")] - public async Task PreserveLineForAttribute() - { - await AssertFormatAsync(@"class Class1 -{ - [STAThread] - static void Main(string[] args) + [Fact] + public async Task FormatSwitchExpression_ExpressionAnchoredToArm() { + var code = """ + class C + { + void M() { + _ = this switch + { + { P1: 1} + => true, + (0, 1) + => true, + _ + => false + }; + + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + { P1: 1 } + => true, + (0, 1) + => true, + _ + => false + }; + + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -", @"class Class1 -{ - [STAThread] -static void Main(string[] args) + + [Fact] + public async Task FormatSwitchExpression_NoSpaceBeforeColonInArm() { - } -} -"); - } + var code = """ + class C + { + void M() { + _ = this switch + { + { P1: 1} + => true, + (0, 1) + => true, + _ + => false + }; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537878")] - public async Task NoFormattingOnMissingTokens() - { - await AssertFormatAsync(@"namespace ClassLibrary1 -{ - class Class1 - { - void Goo() - { - if (true) - } + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + { P1: 1 } + => true, + (0, 1) + => true, + _ + => false + }; + + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -", @"namespace ClassLibrary1 -{ - class Class1 + + [Fact] + public async Task FormatSwitchExpression_ArmCommaWantsNewline() { - void Goo() - { - if (true) - } + var code = """ + class C + { + void M() { + _ = this switch + { + { P1: 1} => true, + (0, 1) => true, _ => false + }; + + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + { P1: 1 } => true, + (0, 1) => true, + _ => false + }; + + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537783")] - public async Task UnaryExpression() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task FormatSwitchExpression_ArmCommaPreservesLines() { - int a = 6; - a = a++ + 5; + var code = """ + class C + { + void M() { + _ = this switch + { + { P1: 1} => true, + + (0, 1) => true, _ => false + }; + + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this switch + { + { P1: 1 } => true, + + (0, 1) => true, + _ => false + }; + + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33839")] + public async Task FormatSwitchExpression_ExpressionBody() { - int a = 6; - a = a++ + 5; + var code = """ + + public class Test + { + public object Method(int i) + => i switch + { + 1 => 'a', + 2 => 'b', + _ => null, + }; + } + """; + var expectedCode = """ + + public class Test + { + public object Method(int i) + => i switch + { + 1 => 'a', + 2 => 'b', + _ => null, + }; + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537885")] - public async Task Pointer() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72196")] + [InlineData("[]")] + [InlineData("[a]")] + [InlineData("[a, b]")] + [InlineData("[..]")] + [InlineData("[var a, .., var b]")] + [InlineData("[{ } a, null]")] + [InlineData("[a, []]")] + public async Task FormatSwitchExpression_ListPatternAligned(string listPattern) { - int* p; + var code = $$""" + class C + { + void M() + { + _ = Array.Empty() switch + { + {{listPattern}} => 0, + _ => 1, + }; + } + } + """; + var expectedCode = $$""" + class C + { + void M() + { + _ = Array.Empty() switch + { + {{listPattern}} => 0, + _ => 1, + }; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task FormatSwitchWithPropertyPattern() { - int* p; + var code = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: + break; + } + } + } + """; + var expectedCode = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: + break; + } + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50723")] - public async Task TuplePointer() - { - var properlyFormattedCode = @"public unsafe static class Program -{ - public static void Main(string[] args) + [Fact] + public async Task FormatSwitchWithPropertyPattern_Singleline() { - int* intPointer = null; - (int, int)* intIntPointer = null; + var code = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: break; + } + } + } + """; + var expectedCode = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: break; + } + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -"; - await AssertFormatAsync(properlyFormattedCode, properlyFormattedCode); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537886")] - public async Task Tild() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task FormatSwitchWithPropertyPattern_Singleline2() { - int j = 103; - j = ~7; + var code = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: System.Console.Write(1); + break; + } + } + } + """; + var expectedCode = """ + class C + { + void M() + { + switch (this) + { + case { P1: 1, P2: { P3: 3, P4: 4 } }: + System.Console.Write(1); + break; + } + } + } + """; + await AssertFormatAsync(expectedCode, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task SpacingInTupleExtension() { - int j = 103; - j = ~7; + var code = """ + static class Class5 + { + static void Extension(this(int, string) self) { } + } + """; + var expectedCode = """ + static class Class5 + { + static void Extension(this (int, string) self) { } + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] - public async Task ArrayInitializer1() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact] + public async Task SpacingInNestedDeconstruction() { - int[] arr = {1,2, - 3,4 - }; + var code = """ + class Class5{ + void bar() + { + ( int x1 , var( x2,x3 ) )=(1,(2,3)); + } + } + """; + var expectedCode = """ + class Class5 + { + void bar() + { + (int x1, var (x2, x3)) = (1, (2, 3)); + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact] + public async Task SpacingInSuppressNullableWarningExpression() { - int[] arr = {1,2, - 3,4 - }; + var code = + """ + class C + { + static object F() + { + object? o[] = null; + object? x = null; + object? y = null; + return x ! ?? (y) ! ?? o[0] !; + } + } + """; + var expectedCode = + """ + class C + { + static object F() + { + object? o[] = null; + object? x = null; + object? y = null; + return x! ?? (y)! ?? o[0]!; + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] - public async Task ArrayInitializer2() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545335")] + public async Task PreprocessorOnSameLine() { - int[] arr = new int[] {1,2, - 3,4 - }; + var code = """ + class C + { + }#line default + + #line hidden + """; + + var expected = """ + class C + { + }#line default + + #line hidden + """; + + await AssertFormatAsync(expected, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545626")] + public async Task ArraysInAttributes() { - int[] arr = new int [] {1,2, - 3,4 - }; + var code = """ + [A(X = new int[] { 1 })] + public class A : Attribute + { + public int[] X; + } + """; + + var expected = """ + [A(X = new int[] { 1 })] + public class A : Attribute + { + public int[] X; + } + """; + + await AssertFormatAsync(expected, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] - public async Task ImplicitArrayInitializer() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] + public async Task NoNewLineAfterBraceInExpression() { - var arr = new[] {1,2, - 3,4 - }; + var code = """ + public class A + { + void Method() + { + var po = cancellationToken.CanBeCanceled ? + new ParallelOptions() { CancellationToken = cancellationToken } : + defaultParallelOptions; + } + } + """; + + var expected = """ + public class A + { + void Method() + { + var po = cancellationToken.CanBeCanceled ? + new ParallelOptions() { CancellationToken = cancellationToken } : + defaultParallelOptions; + } + } + """; + + await AssertFormatAsync(expected, code); } -} -", @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] + public async Task NoIndentForNestedUsingWithoutBraces() { - var arr = new [] {1,2, - 3,4 - } ; - } -} -"); - } + var code = """ + class C + { + void M() + { + using (null) + using (null) + { + } + } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer0() - { - await AssertFormatAsync(""" - F(stackalloc int[] - { - 1, - 2, - }); - """, """ - F(stackalloc int[] - { - 1, - 2, - } ); - """); - } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer0_Implicit() - { - await AssertFormatAsync(""" - F(stackalloc[] - { - 1, - 2, - } - ); - """, """ - F( stackalloc [] + var expected = """ + class C + { + void M() + { + using (null) + using (null) { - 1, - 2, } - ); - """); - } + } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer1() - { - await AssertFormatAsync(""" - F( - stackalloc int[] - { - 1,2, - 3,4 - } - ); - """, """ - F( - stackalloc int[] + """; + + await AssertFormatAsync(expected, code); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] + public async Task NoIndentForNestedUsingWithoutBraces2() + { + var code = """ + class C + { + void M() + { + using (null) + using (null) + using (null) + { + } + } + } + + """; + + var expected = """ + class C + { + void M() + { + using (null) + using (null) + using (null) { - 1,2, - 3,4 } - ); - """); - } + } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer1_Implicit() - { - await AssertFormatAsync(""" - F( - stackalloc[] + """; + + await AssertFormatAsync(expected, code); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] + public async Task NoIndentForNestedUsingWithoutBraces3() + { + var code = """ + class C + { + void M() + { + using (null) + using (null) + using (null) { - 1,2, - 3,4 } - ); - """, """ - F( - stackalloc [] + } + } + + """; + + var expected = """ + class C + { + void M() + { + using (null) + using (null) + using (null) { - 1,2, - 3,4 } - ); - """); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer2() - { - await AssertFormatAsync(""" - var x = (stackalloc int[] {1,2, - 3 - }); - """, """ - var x = (stackalloc int[] {1,2, - 3 - }); - """); - } + } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/65498")] - public async Task StackAllocArrayInitializer2_Implicit() - { - await AssertFormatAsync(""" - var x = (stackalloc[] - {1, - 2, 3 - }); - """, """ - var x = (stackalloc [] - {1, - 2, 3 - }); - """); - } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537884")] - public async Task CollectionInitializer() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) - { - var arr = new List {1,2, - 3,4 - }; - } -} -", @"class Program -{ - static void Main(string[] args) - { - var arr = new List {1,2, - 3,4 - }; + await AssertFormatAsync(expected, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537916")] - public async Task AddressOfOperator() - { - await AssertFormatAsync(@"unsafe class Class1 -{ - void Method() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546678")] + public async Task UnicodeWhitespace() { - int a = 12; - int* p = &a; - } -} -", @"unsafe class Class1 -{ - void Method() - { - int a = 12; - int* p = &a; + var code = "\u001A"; + + await AssertFormatAsync("", code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537885")] - public async Task DereferenceOperator() - { - await AssertFormatAsync(@"unsafe class Class1 -{ - void Method() + [Fact, WorkItem(17431, "DevDiv_Projects/Roslyn")] + public async Task NoElasticRuleOnRegularFile() { - int a = 12; - int* p = &a; - Console.WriteLine(*p); + var code = """ + class Consumer + { + public int P + { + } + } + """; + + var expected = """ + class Consumer + { + public int P + { + } + } + """; + + await AssertFormatAsync(expected, code); } -} -", @"unsafe class Class1 -{ - void Method() + + [Fact, WorkItem(584599, "DevDiv_Projects/Roslyn")] + public async Task CaseSection() { - int a = 12; - int* p = & a; - Console.WriteLine(* p); + var code = """ + class C + { + void Method() + { + switch(i) + { + // test1 + case 1: + // test2 + case 2: + // test3 + int i2 = 10; + // test 4 + case 4: + // test 5 + } + } + } + """; + + var expected = """ + class C + { + void Method() + { + switch (i) + { + // test1 + case 1: + // test2 + case 2: + // test3 + int i2 = 10; + // test 4 + case 4: + // test 5 + } + } + } + """; + + await AssertFormatAsync(expected, code); } -} -"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537905")] - public async Task Namespaces() + [Fact, WorkItem(553654, "DevDiv_Projects/Roslyn")] + public async Task Bugfix_553654_LabelStatementIndenting() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - await AssertFormatAsync(@"using System; -using System.Data;", @"using System; using System.Data;"); - } + { CSharpFormattingOptions2.LabelPositioning, LabelPositionOptions.LeftMost } + }; - [Fact] - public async Task NamespaceDeclaration() - { - await AssertFormatAsync(@"namespace N -{ -}", @"namespace N - { -}"); - } + var code = """ + class Program + { + void F() + { + foreach (var x in new int[] { }) + { + goo: + int a = 1; + } + } + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537902")] - public async Task DoWhile1() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) - { - do { } - while (i < 4); - } -}", @"class Program -{ - static void Main(string[] args) - { - do { } - while (i < 4); + var expected = """ + class Program + { + void F() + { + foreach (var x in new int[] { }) + { + goo: + int a = 1; + } + } + } + """; + await AssertFormatAsync(expected, code, changingOptions); } -}"); - } - [Fact] - public async Task NewConstraint() - { - await AssertFormatAsync(@"class Program -{ - void Test(T t) where T : new() - { - } -}", @"class Program -{ - void Test(T t) where T : new ( ) + [Fact, WorkItem(707064, "DevDiv_Projects/Roslyn")] + public async Task Bugfix_707064_SpaceAfterSecondSemiColonInFor() { - } -}"); - } - - [Fact] - public async Task UnaryExpressionWithInitializer() - { - await AssertFormatAsync(@"using System; -using System.Collections.Generic; -using System.Linq; + var code = """ + class Program + { + void F() + { + for (int i = 0; i < 5;) + { + } + } + } + """; -class Program -{ - static void Main(string[] args) - { - if ((new int[] { 1, 2, 3 }).Any()) - { - return; - } + var expected = """ + class Program + { + void F() + { + for (int i = 0; i < 5;) + { + } + } + } + """; + await AssertFormatAsync(expected, code); } -}", @"using System; -using System.Collections.Generic; -using System.Linq; -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772313")] + public async Task Bugfix_772313_ReturnKeywordBeforeQueryClauseDoesNotTriggerNewLineOnFormat() { - if ((new int[] { 1, 2, 3 } ).Any()) - { - return; - } + var code = """ + class C + { + int M() + { + return from c in " + select c; + } + } + """; + + var expected = """ + class C + { + int M() + { + return from c in " + select c; + } + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact] - public async Task Attributes1() - { - await AssertFormatAsync(@"class Program -{ - [Flags] public void Method() { } -}", @"class Program -{ - [ Flags ] public void Method ( ) { } -}"); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772304")] + public async Task Bugfix_772313_PreserveMethodParameterIndentWhenAttributePresent() + { + var code = """ + class C + { + void M + ( + [In] + bool b + ); + } - [Fact] - public async Task Attributes2() - { - await AssertFormatAsync(@"class Program -{ - [Flags] - public void Method() { } -}", @"class Program -{ - [ Flags ] -public void Method ( ) { } -}"); - } + class C + { + void M + ( + [In] + List b + ); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538288")] - public async Task ColonColon1() - { - await AssertFormatAsync(@"class Program -{ - public void Method() - { - throw new global::System.NotImplementedException(); - } -}", @"class Program -{ -public void Method ( ) { - throw new global :: System.NotImplementedException(); -} -}"); - } + class C + { + void M + ( + [In] + [In, In] + List b + ); + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538354")] - public async Task BugFix3939() - { - await AssertFormatAsync(@"using - System. - Collections. - Generic;", @" using - System. - Collections. - Generic;"); - } + var expected = """ + class C + { + void M + ( + [In] + bool b + ); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538354")] - public async Task Tab1() - => await AssertFormatAsync(@"using System;", @" using System;"); + class C + { + void M + ( + [In] + List b + ); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538329")] - public async Task SuppressLinkBreakInIfElseStatement() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) - { - int a; - if (true) a = 10; - else a = 11; - } -}", @"class Program -{ - static void Main(string[] args) - { - int a; - if (true) a = 10; - else a = 11; + class C + { + void M + ( + [In] + [In, In] + List b + ); + } + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538464")] - public async Task BugFix4087() - { - await AssertFormatAsync(@"class Program -{ - static void Main(string[] args) - { - Func fun = x => { return x + 1; } - } -}", @"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/776513")] + public async Task Bugfix_776513_CheckBraceIfNotMissingBeforeApplyingOperationForBracedBlocks() { - Func fun = x => { return x + 1; } + var code = """ + var alwaysTriggerList = new[] + Dim triggerOnlyWithLettersList = + """; + + var expected = """ + var alwaysTriggerList = new[] + Dim triggerOnlyWithLettersList = + """; + await AssertFormatAsync(expected, code); } -}"); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538511")] - public async Task AttributeTargetSpecifier() - { - var code = @"public class Class1 -{ - [method : - void Test() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/769342")] + public async Task ShouldFormatDocCommentWithIndentSameAsTabSizeWithUseTabTrue() { + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + + await AssertFormatAsync(""" + namespace ConsoleApplication1 + { + /// + /// fka;jsgdflkhsjflgkhdsl; + /// + class Program + { + } + } + """, """ + namespace ConsoleApplication1 + { + /// + /// fka;jsgdflkhsjflgkhdsl; + /// + class Program + { + } + } + """, changedOptionSet: optionSet); } -}"; - var expected = @"public class Class1 -{ - [method: - void Test() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/797278")] + public async Task TestSpacingOptionAroundControlFlow() { - } -}"; + const string code = """ - await AssertFormatAsync(expected, code); - } + class Program + { + public void goo() + { + int i; + for(i=0; i<10; i++) + {} - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538635")] - public async Task Finalizer() - { - var code = @"public class Class1 -{ - ~ Class1() { } -}"; + foreach(i in new[] {1,2,3}) + {} - var expected = @"public class Class1 -{ - ~Class1() { } -}"; + if (i==10) + {} - await AssertFormatAsync(expected, code); - } + while(i==10) + {} - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538743")] - public async Task BugFix4442() - { - var code = @"class Program -{ - static void Main(string[] args) - { - string str = ""ab,die|wo""; - string[] a = str.Split(new char[] { ',', '|' }) - ; - } -}"; + switch(i) + { + default: break; + } - await AssertFormatAsync(code, code); - } + do {} while (true); - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538658")] - public async Task BugFix4328() - { - var code = @"class Program -{ - static void Main(string[] args) - { - double d = new double (); - } -}"; - var expected = @"class Program -{ - static void Main(string[] args) - { - double d = new double(); - } -}"; - await AssertFormatAsync(expected, code); - } + try + { } + catch (System.Exception) + { } + catch (System.Exception e) when (true) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538658")] - public async Task BugFix4515() - { - var code = @"class Program -{ - static void Main(string[] args) - { - var t = typeof ( System.Object ) ; - var t1 = default ( System.Object ) ; - var t2 = sizeof ( System.Object ) ; - } -}"; - var expected = @"class Program -{ - static void Main(string[] args) - { - var t = typeof(System.Object); - var t1 = default(System.Object); - var t2 = sizeof(System.Object); - } -}"; - await AssertFormatAsync(expected, code); - } + using(somevar) + { } - [Fact] - public async Task CastExpressionTest() - { - var code = @"class Program -{ - static void Main(string[] args) - { - var a = (int) 1; - } -}"; - var expected = @"class Program -{ - static void Main(string[] args) - { - var a = (int)1; - } -}"; - await AssertFormatAsync(expected, code); - } + lock(somevar) + { } + + fixed(char* p = str) + { } + } + } + """; + const string expected = """ + + class Program + { + public void goo() + { + int i; + for ( i = 0; i < 10; i++ ) + { } + + foreach ( i in new[] { 1, 2, 3 } ) + { } + + if ( i == 10 ) + { } + + while ( i == 10 ) + { } + + switch ( i ) + { + default: break; + } - [Fact] - public async Task NamedParameter() + do { } while ( true ); + + try + { } + catch ( System.Exception ) + { } + catch ( System.Exception e ) when ( true ) + { } + + using ( somevar ) + { } + + lock ( somevar ) + { } + + fixed ( char* p = str ) + { } + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class Program -{ - static void Main(string[] args) - { - Main ( args : null ) ; + { SpaceBetweenParentheses, SpaceBetweenParentheses.DefaultValue.WithFlagValue( SpacePlacementWithinParentheses.ControlFlowStatements, true) }, + }; + + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } -}"; - var expected = @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37031")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/176345")] + public async Task TestSpacingOptionAfterControlFlowKeyword() { - Main(args: null); - } -}"; - await AssertFormatAsync(expected, code); - } + var code = """ - [Fact] - public async Task RefReadonlyParameters() - { - var code = """ - class C + class Program + { + public void goo() { - int this [ ref readonly int x , ref readonly int y ] { get ; set ; } - void M ( ref readonly int x , ref readonly int y ) { } + int i; + for (i=0; i<10; i++) + {} + + foreach (i in new[] {1,2,3}) + {} + + if (i==10) + {} + + while (i==10) + {} + + switch (i) + { + default: break; + } + + do {} while (true); + + try + { } + catch (System.Exception e) when (true) + { } + + using (somevar) + { } + + lock (somevar) + { } + + fixed (somevar) + { } } - """; - var expected = """ - class C + } + """; + var expected = """ + + class Program + { + public void goo() { - int this[ref readonly int x, ref readonly int y] { get; set; } - void M(ref readonly int x, ref readonly int y) { } - } - """; - await AssertFormatAsync(expected, code); - } + int i; + for(i = 0; i < 10; i++) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539259")] - public async Task BugFix5143() - { - var code = @"class Program -{ - static void Main(string[] args) - { - int x = Goo ( - delegate ( int x ) { return x ; } ) ; - } -}"; - var expected = @"class Program -{ - static void Main(string[] args) - { - int x = Goo( - delegate (int x) { return x; }); - } -}"; - await AssertFormatAsync(expected, code); - } + foreach(i in new[] { 1, 2, 3 }) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539338")] - public async Task BugFix5251() - { - var code = @"class Program -{ - public static string Goo { get; private set; } -}"; - var expected = @"class Program -{ - public static string Goo { get; private set; } -}"; - await AssertFormatAsync(expected, code); - } + if(i == 10) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539358")] - public async Task BugFix5277() - { - var code = @" -#if true - #endif -"; - var expected = @" -#if true -#endif -"; - await AssertFormatAsync(expected, code); - } + while(i == 10) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539542")] - public async Task BugFix5544() - { - var code = @" -class Program -{ - unsafe static void Main(string[] args) - { - Program* p; - p -> Goo = 5; - } -} -"; - var expected = @" -class Program -{ - unsafe static void Main(string[] args) - { - Program* p; - p->Goo = 5; - } -} -"; - await AssertFormatAsync(expected, code); - } + switch(i) + { + default: break; + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539587")] - public async Task BugFix5602() - { - var code = @" class Bug - { - public static void func() - { - long b = // - } - }"; - var expected = @"class Bug -{ - public static void func() - { - long b = // - } -}"; - await AssertFormatAsync(expected, code); - } + do { } while(true); - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539616")] - public async Task BugFix5637() - { - var code = @"class Bug -{ - // test - public static void func() - { - } -}"; - var expected = @"class Bug -{ - // test - public static void func() - { - } -}"; - await AssertFormatAsync(expected, code); - } + try + { } + catch(System.Exception e) when(true) + { } - [Fact] - public async Task GenericType() - { - var code = @"class Bug -{ - class N : Bug< T [ ] > - { - } -}"; - var expected = @"class Bug -{ - class N : Bug - { - } -}"; - await AssertFormatAsync(expected, code); - } + using(somevar) + { } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539878")] - public async Task BugFix5978() - { - var code = @"class Program -{ - static void Main(string[] args) - { - int i = 3; - label4: - if (i < 5) - { - label5: -if (i == 4) -{ -} -else -{ -System.Console.WriteLine(""a""); -} - } + lock(somevar) + { } + + fixed(somevar) + { } + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceAfterControlFlowStatementKeyword, false } }; + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } -}"; - var expected = @"class Program -{ - static void Main(string[] args) + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/766212")] + public async Task TestOptionForSpacingAroundCommas() { - int i = 3; - label4: - if (i < 5) - { - label5: - if (i == 4) + var code = """ + + class Program { + public void Main() + { + var a = new[] {1,2,3}; + var digits = new List {1,2,3,4}; + } } - else + """; + var expectedDefault = """ + + class Program { - System.Console.WriteLine(""a""); + public void Main() + { + var a = new[] { 1, 2, 3 }; + var digits = new List { 1, 2, 3, 4 }; + } } - } - } -}"; - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539878")] - public async Task BugFix5979() - { - var code = @"delegate int del(int i); -class Program -{ - static void Main(string[] args) - { - del q = x => - { - label2: goto label1; - label1: return x; - }; - } -}"; - var expected = @"delegate int del(int i); -class Program -{ - static void Main(string[] args) - { - del q = x => - { - label2: goto label1; - label1: return x; - }; - } -}"; - await AssertFormatAsync(expected, code); - } + """; + await AssertFormatAsync(expectedDefault, code); - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539891")] - public async Task BugFix5993() - { - var code = @"public class MyClass -{ - public static void Main() - { - lab1: - { - lab1:// CS0158 - goto lab1; - } - } -}"; - var expected = @"public class MyClass -{ - public static void Main() - { - lab1: - { - lab1:// CS0158 - goto lab1; - } - } -}"; - await AssertFormatAsync(expected, code); - } + var expectedAfterCommaDisabled = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540315")] - public async Task BugFix6536() - { - var code = @"public class MyClass -{ - public static void Main() - { - int i = - - 1 + + + 1 + - + 1 + - + 1 ; - } -}"; - var expected = @"public class MyClass -{ - public static void Main() - { - int i = - -1 + + +1 + -+1 + -+1; - } -}"; - await AssertFormatAsync(expected, code); - } + class Program + { + public void Main() + { + var a = new[] { 1,2,3 }; + var digits = new List { 1,2,3,4 }; + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceAfterComma, false } }; + await AssertFormatAsync(expectedAfterCommaDisabled, code, changedOptionSet: optionSet); - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540801")] - public async Task BugFix7211() - { - var code = @"class Program -{ - static void Main(string[] args) - { - while (0 > new int[] { 1 }.Length) - { - System.Console.WriteLine(""Hello""); - } - } -}"; + var expectedBeforeCommaEnabled = """ - var expected = @"class Program -{ - static void Main(string[] args) - { - while (0 > new int[] { 1 }.Length) - { - System.Console.WriteLine(""Hello""); - } + class Program + { + public void Main() + { + var a = new[] { 1 ,2 ,3 }; + var digits = new List { 1 ,2 ,3 ,4 }; + } + } + """; + optionSet.Add(CSharpFormattingOptions2.SpaceBeforeComma, true); + await AssertFormatAsync(expectedBeforeCommaEnabled, code, changedOptionSet: optionSet); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541035")] - public async Task BugFix7564_1() - { - var code = @"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772308")] + public async Task Bugfix_772308_SeparateSuppressionForEachCaseLabelEvenIfEmpty() { - while (null != new int[] { 1 }) - { - System.Console.WriteLine(""Hello""); - } - } -}"; + var code = """ - var expected = @"class Program -{ - static void Main(string[] args) - { - while (null != new int[] { 1 }) - { - System.Console.WriteLine(""Hello""); - } - } -}"; - await AssertFormatAsync(expected, code); - } + class C + { + int M() + { + switch (1) + { + case 1: return 1; + case 2: return 2; + case 3: + case 4: return 4; + default: + } + } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541035")] - public async Task BugFix7564_2() - { - var code = @"class Program -{ - static void Main(string[] args) - { - foreach (var f in new int[] { 5 }) - { - Console.WriteLine(f); - } - } -}"; + """; - var expected = @"class Program -{ - static void Main(string[] args) - { - foreach (var f in new int[] { 5 }) - { - Console.WriteLine(f); - } - } -}"; - await AssertFormatAsync(expected, code); - } + var expected = """ - [Fact, WorkItem(8385, "DevDiv_Projects/Roslyn")] - public async Task NullCoalescingOperator() - { - var code = @"class C -{ - void M() - { - object o2 = null??null; - } -}"; + class C + { + int M() + { + switch (1) + { + case 1: return 1; + case 2: return 2; + case 3: + case 4: return 4; + default: + } + } + } - var expected = @"class C -{ - void M() - { - object o2 = null ?? null; + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541925")] - public async Task QueryContinuation() - { - var code = @"using System.Linq; -class C -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/844913")] + public async Task QueryExpressionInExpression() { - var temp = from x in ""abc"" - let z = x.ToString() - select z into w - select w; - } -}"; + var code = """ - var expected = @"using System.Linq; -class C -{ - static void Main(string[] args) - { - var temp = from x in ""abc"" - let z = x.ToString() - select z into w - select w; - } -}"; - await AssertFormatAsync(expected, code); - } + class C + { + public void CreateSettingsFile(string path, string comment) { + var xml = new XDocument( + new XDeclaration(1.0, utf8, yes), + new XComment(comment), + new XElement(UserSettings, + new XElement(ToolsOptions, + from t in KnownSettings.DefaultCategories + group t by t.Item1 into cat + select new XElement(ToolsOptionsCategory, + new XAttribute(name, cat.Key), + cat.Select(sc => new XElement(ToolsOptionsSubCategory, new XAttribute(name, sc.Item2))) + ) + ) + ) + ); + UpdateSettingsXml(xml); + xml.Save(path); + SettingsPath = path; + } + } - [Fact] - public async Task QueryContinuation2() - { - var code = @"using System.Linq; -class C -{ - static void Main(string[] args) - { - var temp = from x in ""abc"" select x into - } -}"; + """; - var expected = @"using System.Linq; -class C -{ - static void Main(string[] args) - { - var temp = from x in ""abc"" - select x into - } -}"; - await AssertFormatAsync(expected, code); - } + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542305")] - public async Task AttributeFormatting1() - { - var code = @"class Program -{ - void AddClass(string name,[OptionalAttribute] object position,[OptionalAttribute] object bases) - { - } -}"; + class C + { + public void CreateSettingsFile(string path, string comment) + { + var xml = new XDocument( + new XDeclaration(1.0, utf8, yes), + new XComment(comment), + new XElement(UserSettings, + new XElement(ToolsOptions, + from t in KnownSettings.DefaultCategories + group t by t.Item1 into cat + select new XElement(ToolsOptionsCategory, + new XAttribute(name, cat.Key), + cat.Select(sc => new XElement(ToolsOptionsSubCategory, new XAttribute(name, sc.Item2))) + ) + ) + ) + ); + UpdateSettingsXml(xml); + xml.Save(path); + SettingsPath = path; + } + } - var expected = @"class Program -{ - void AddClass(string name, [OptionalAttribute] object position, [OptionalAttribute] object bases) - { + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542304")] - public async Task CloseBracesInArgumentList() - { - var code = @"class Program -{ - static void Main(string[] args) + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/843479")] + public async Task EmbeddedStatementElse() { - var relativeIndentationGetter = new Lazy(() => + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - var indentationDelta = operation.IndentationDeltaOrPosition * this.OptionSet.IndentationSize; - var baseIndentation = this.tokenStream.GetCurrentColumn(operation.BaseToken); + { CSharpFormattingOptions2.NewLineForElse, false } + }; - return baseIndentation + indentationDelta; - } , isThreadSafe: true); - } -}"; + var code = """ - var expected = @"class Program -{ - static void Main(string[] args) - { - var relativeIndentationGetter = new Lazy(() => - { - var indentationDelta = operation.IndentationDeltaOrPosition * this.OptionSet.IndentationSize; - var baseIndentation = this.tokenStream.GetCurrentColumn(operation.BaseToken); + class C + { + void Method() + { + if (true) + Console.WriteLine(); else + Console.WriteLine(); + } + } - return baseIndentation + indentationDelta; - }, isThreadSafe: true); - } -}"; - await AssertFormatAsync(expected, code); - } + """; + + var expected = """ + + class C + { + void Method() + { + if (true) + Console.WriteLine(); + else + Console.WriteLine(); + } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542538")] - public async Task MissingTokens() - { - var code = @"using System; -delegate void myDelegate(int name = 1); -class innerClass -{ - public innerClass() - { - myDelegate x = (int y=1) => { return; }; + """; + await AssertFormatAsync(expected, code, changingOptions); } -}"; - var expected = @"using System; -delegate void myDelegate(int name = 1); -class innerClass -{ - public innerClass() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772311")] + public async Task LineCommentAtTheEndOfLine() { - myDelegate x = (int y = 1) => { return; }; - } -}"; + var code = """ - await AssertFormatAsync(expected, code); - } + using System; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542199")] - public async Task ColumnOfVeryFirstToken() - { - var code = @" W )b"; + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); // this is a comment + // that I would like to keep - var expected = @"W )b"; + // properly indented + } + } - await AssertFormatAsync(expected, code); - } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542718")] - public async Task EmptySuppressionSpan() - { - var code = @"enum E - { - a,, - }"; + var expected = """ - var expected = @"enum E -{ - a,, -}"; + using System; - await AssertFormatAsync(expected, code); - } + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); // this is a comment + // that I would like to keep - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542790")] - public async Task LabelInSwitch() - { - var code = @"class test -{ - public static void Main() - { - string target = ""t1""; - switch (target) - { - case ""t1"": - label1: - goto label1; - case ""t2"": - label2: - goto label2; - } - } -}"; + // properly indented + } + } - var expected = @"class test -{ - public static void Main() - { - string target = ""t1""; - switch (target) - { - case ""t1"": - label1: - goto label1; - case ""t2"": - label2: - goto label2; - } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543112")] - public void FormatArbitaryNode() - { - var expected = @"public int Prop -{ - get + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] + public async Task BlockCommentAtTheEndOfLine1() { - return c; - } + var code = """ - set - { - c = value; - } -}"; + using System; - var property = SyntaxFactory.PropertyDeclaration( - attributeLists: [], - [PublicKeyword], - SyntaxFactory.ParseTypeName("int"), - null, - SyntaxFactory.Identifier("Prop"), - SyntaxFactory.AccessorList([ - SyntaxFactory.AccessorDeclaration( - SyntaxKind.GetAccessorDeclaration, - SyntaxFactory.Block(SyntaxFactory.ParseStatement("return c;"))), - SyntaxFactory.AccessorDeclaration( - SyntaxKind.SetAccessorDeclaration, - SyntaxFactory.Block(SyntaxFactory.ParseStatement("c = value;")))])); + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); /* this is a comment */ + // that I would like to keep - Assert.NotNull(property); - using var workspace = new AdhocWorkspace(); - var newProperty = Formatter.Format(property, workspace.Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); + // properly indented + } + } - Assert.Equal(expected, newProperty.ToFullString()); - } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543140")] - public async Task OmittedTypeArgument() - { - var code = @"using System; -using System.Collections.Generic; -using System.Linq; - -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(typeof(Dictionary<, >).IsGenericTypeDefinition); - } -}"; + var expected = """ - var expected = @"using System; -using System.Collections.Generic; -using System.Linq; + using System; -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); - } -}"; + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); /* this is a comment */ + // that I would like to keep - await AssertFormatAsync(expected, code); - } + // properly indented + } + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543131")] - public async Task TryAfterLabel() - { - var code = @"using System; -class Program -{ - static object lockObj = new object(); - static int Main() - { - int sum = 0; - lock (lockObj) - try - { sum = 0; } - catch (Exception ex) - { Console.WriteLine(ex); } - return sum; + """; + await AssertFormatAsync(expected, code); } -}"; - var expected = @"using System; -class Program -{ - static object lockObj = new object(); - static int Main() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] + public async Task BlockCommentAtTheEndOfLine2() { - int sum = 0; - lock (lockObj) - try - { sum = 0; } - catch (Exception ex) - { Console.WriteLine(ex); } - return sum; - } -}"; - - await AssertFormatAsync(expected, code); - } + var code = """ - [Fact] - public async Task QueryContinuation1() - { - var code = @"using System.Linq; + using System; -class Program -{ - static void Main(string[] args) - { - var q = from arg in args - group arg by arg.Length into final - where final - .Select(c => c) - .Distinct() - .Count() > 0 - select final; - } -}"; + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); // this is a comment + /* that I would like to keep */ - var expected = @"using System.Linq; + // properly indented + } + } -class Program -{ - static void Main(string[] args) - { - var q = from arg in args - group arg by arg.Length into final - where final - .Select(c => c) - .Distinct() - .Count() > 0 - select final; - } -}"; + """; - await AssertFormatAsync(expected, code); - } + var expected = """ - [Fact] - public async Task TestCSharpFormattingSpacingOptions() - { - var text = -@" -interface f1 -{ } + using System; -interface f2 : f1 { } + class Program + { + static void Main(string[] args) + { + Console.WriteLine(); // this is a comment + /* that I would like to keep */ -struct d2 : f1 { } + // properly indented + } + } -class goo : System . Object -{ - public int bar = 1* 2; - public void goobar ( ) - { - goobar ( ); - } - public int toofoobar( int i , int j ) - { - int s = ( int ) ( 34 ); - if ( i < 0 ) - { - } - return toofoobar( i,j ); - } - public string parfoobar(string [ ] str) - { - for(int i = 0 ; i < 28 ; i++) { } - return str[ 5 ]; + """; + await AssertFormatAsync(expected, code); } -}"; - var expectedFormattedText = -@" -interface f1 -{ } -interface f2 : f1 { } - -struct d2 : f1 { } - -class goo : System.Object -{ - public int bar = 1 * 2; - public void goobar() - { - goobar(); - } - public int toofoobar(int i, int j) - { - int s = (int)(34); - if (i < 0) - { - } - return toofoobar(i, j); - } - public string parfoobar(string[] str) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] + public async Task BlockCommentAtBeginningOfLine() { - for (int i = 0; i < 28; i++) { } - return str[5]; - } -}"; + var code = """ - await AssertFormatAsync(expectedFormattedText, text); - } + using System; - [Fact] - public async Task SpacingFixInTokenBasedForIfAndSwitchCase() - { - var code = @"class Class5{ -void bar() -{ -if(x == 1) -x = 2; else x = 3; -switch (x) { -case 1: break; case 2: break; default: break;} -} -}"; - var expectedCode = @"class Class5 -{ - void bar() - { - if (x == 1) - x = 2; - else x = 3; - switch (x) - { - case 1: break; - case 2: break; - default: break; - } + class Program + { + static void Main( + int x, // Some comment + /*A*/ int y) + { + } + } + + """; + await AssertFormatAsync(code, code); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task SpacingInDeconstruction() - { - var code = @"class Class5{ -void bar() -{ -var(x,y)=(1,2); -} -}"; - var expectedCode = @"class Class5 -{ - void bar() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772311")] + public async Task TestTab() { - var (x, y) = (1, 2); - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + using System; - [Fact] - public async Task SpacingInNullableTuple() - { - var code = @"class Class5 -{ - void bar() - { - (int, string) ? x = (1, ""hello""); - } -}"; - var expectedCode = @"class Class5 -{ - void bar() - { - (int, string)? x = (1, ""hello""); - } -}"; + class Program + { + /// + /// This function is the callback used to execute a command when a menu item is clicked. + /// See the Initialize method to see how the menu item is associated to this function using + /// the OleMenuCommandService service and the MenuCommand class. + /// + private void MenuItemCallback(object sender, EventArgs e) { + // Show a Message Box to prove we were here + IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); + Guid clsid = Guid.Empty; + int result; + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( + 0, + ref clsid, + Rebracer, + string.Format(CultureInfo.CurrentCulture, Inside {0}.MenuItemCallback(), this.ToString()), + string.Empty, + 0, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, + OLEMSGICON.OLEMSGICON_INFO, + 0, // false + out result)); + } + } - await AssertFormatAsync(expectedCode, code); - } + """; - [Fact] - public async Task SpacingInTupleArrayCreation() - { - var code = @"class C -{ - void bar() - { - (string a, string b)[] ab = new(string a, string b) [1]; - } -}"; - var expectedCode = @"class C -{ - void bar() - { - (string a, string b)[] ab = new (string a, string b)[1]; - } -}"; + var expected = """ - await AssertFormatAsync(expectedCode, code); - } + using System; - [Fact] - public async Task SpacingInTupleArrayCreation2() - { - var code = @"class C -{ - void bar() - { - (string a, string b)[] ab = new( - } -}"; - var expectedCode = @"class C -{ - void bar() - { - (string a, string b)[] ab = new( - } -}"; + class Program + { + /// + /// This function is the callback used to execute a command when a menu item is clicked. + /// See the Initialize method to see how the menu item is associated to this function using + /// the OleMenuCommandService service and the MenuCommand class. + /// + private void MenuItemCallback(object sender, EventArgs e) + { + // Show a Message Box to prove we were here + IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); + Guid clsid = Guid.Empty; + int result; + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( + 0, + ref clsid, + Rebracer, + string.Format(CultureInfo.CurrentCulture, Inside { 0}.MenuItemCallback(), this.ToString()), + string.Empty, + 0, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, + OLEMSGICON.OLEMSGICON_INFO, + 0, // false + out result)); + } + } - await AssertFormatAsync(expectedCode, code); - } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - [Fact] - public async Task SpacingInImplicitObjectCreation() - { - var code = @"class C -{ - void bar() - { - C a = new (); + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); + await AssertFormatAsync(expected, expected, changedOptionSet: optionSet); } -}"; - var expectedCode = @"class C -{ - void bar() + + [Fact] + public async Task LeaveBlockSingleLine_False() { - C a = new(); - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + namespace N { class C { int x; } } + """; - [Fact] - public async Task FormatRecursivePattern_Positional() - { - var code = @"class C -{ - void M() { _ = this is ( 1 , 2 ) ; } -}"; - var expectedCode = @"class C -{ - void M() { _ = this is (1, 2); } -}"; + var expected = """ - await AssertFormatAsync(expectedCode, code); - } + namespace N + { + class C + { + int x; + } + } + """; - [Fact] - public async Task FormatRecursivePattern_Positional_Singleline() - { - var code = @"class C -{ - void M() { -_ = this is ( 1 , 2 ) ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is (1, 2); + var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatRecursivePattern_Positional_Multiline() - { - var code = @"class C -{ - void M() { -_ = this is ( 1 , -2 , -3 ) ; } -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task LeaveBlockSingleLine_False2() { - _ = this is (1, - 2, - 3); - } -}"; - await AssertFormatAsync(expectedCode, code); - } + var code = """ - [Fact] - public async Task FormatRecursivePattern_Positional_Multiline2() - { - var code = @"class C -{ - void M() { -_ = this is ( 1 , -2 , -3 ) ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is (1, - 2, - 3); - } -}"; - await AssertFormatAsync(expectedCode, code); - } + class C { void goo() { } } + """; - [Fact] - public async Task FormatRecursivePattern_Positional_Multiline3() - { - var code = @"class C -{ - void M() { -_ = this is -( 1 , -2 , -3 ) ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is - (1, - 2, - 3); - } -}"; - await AssertFormatAsync(expectedCode, code); - } + var expected = """ - [Fact] - public async Task FormatRecursivePattern_Positional_Multiline4() - { - var code = @"class C -{ - void M() { -_ = this is -( 1 , -2 , 3 ) ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is - (1, - 2, 3); + class C + { + void goo() + { + } + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatRecursivePattern_Properties_Singleline() - { - var code = @"class C -{ - void M() { _ = this is C{ P1 : 1 } ; } -}"; - var expectedCode = @"class C -{ - void M() { _ = this is C { P1: 1 }; } -}"; + [Fact] + public async Task LeaveStatementMethodDeclarationSameLine_False() + { + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + void goo() + { + int x = 0; int y = 0; + } + } + """; - [Fact] - public async Task FormatRecursivePattern_Properties_Multiline() - { - var code = @"class C -{ - void M() { -_ = this is -{ -P1 : 1 , -P2 : 2 -} ; -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is - { - P1: 1, - P2: 2 - }; - } -}"; + var expected = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + void goo() + { + int x = 0; + int y = 0; + } + } + """; - [Fact] - public async Task FormatRecursivePattern_Properties_Multiline2() - { - var code = @"class C -{ - void M() { -_ = this is { -P1 : 1 , -P2 : 2 -} ; -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is - { - P1: 1, - P2: 2 - }; + var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatRecursivePattern_Properties_Multiline3() - { - var code = @"class C -{ - void M() { -_ = this is { -P1 : 1 , -P2 : 2, P3: 3 -} ; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0000() { - _ = this is - { - P1: 1, - P2: 2, P3: 3 - }; - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] - public async Task FormatRecursivePattern_NoSpaceBetweenTypeAndPositionalSubpattern() - { - var code = @"class C -{ - void M() { -_ = this is C( 1 , 2 ){} ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is C(1, 2) { }; - } -}"; - // no space separates the type and the positional pattern - await AssertFormatAsync(expectedCode, code); - } + var expected = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] - public async Task FormatRecursivePattern_PreferSpaceBetweenTypeAndPositionalSubpattern() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class Program { - { CSharpFormattingOptions2.SpaceAfterMethodCallName, true } - }; - var code = @"class C -{ - void M() { -_ = this is C( 1 , 2 ){} ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is C (1, 2) { }; - } -}"; - await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); - } + int[] x; + int[,] y; + int[,,] z = new int[1,2,3]; + var a = new[] { 0 }; + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] - public async Task FormatRecursivePattern_PreferSpaceInsidePositionalSubpatternParentheses() + var options = new OptionsCollection(LanguageNames.CSharp) { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } - }; - var code = @"class C -{ - void M() { -_ = this is C( 1 , 2 ){} ; -_ = this is C( ){} ; } -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is C( 1, 2 ) { }; - _ = this is C() { }; + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/27268")] - public async Task FormatRecursivePattern_PreferSpaceInsideEmptyPositionalSubpatternParentheses() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true } - }; - var code = @"class C -{ - void M() { -_ = this is C( 1 , 2 ){} ; -_ = this is C( ){} ; } -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0001() { - _ = this is C(1, 2) { }; - _ = this is C( ) { }; - } -}"; - await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); - } + var code = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/34683")] - public async Task FormatRecursivePattern_InBinaryOperation() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class Program { - { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } - }; - var code = @"class C -{ - void M() - { - return - typeWithAnnotations is { } && true; - } -}"; - var expectedCode = code; - await AssertFormatAsync(expectedCode, code, changedOptionSet: changingOptions); - } + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task FormatPropertyPattern_MultilineAndEmpty() - { - var code = @"class C -{ - void M() { -_ = this is + var expected = """ + + class Program { - }; -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is + int[] x; + int[,] y; + int[,,] z = new int[1, 2, 3]; + var a = new[] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, true }, }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - - [Fact] - public async Task FormatSwitchExpression_IndentArms() - { - var code = @"class C -{ - void M() { -_ = this switch -{ -{ P1: 1} => true, -(0, 1) => true, -_ => false -}; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0010() { - _ = this switch + var code = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[1 ,2 ,3]; + var a = new[] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - { P1: 1 } => true, - (0, 1) => true, - _ => false + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, false }, }; - + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatPropertyPattern_FollowedByInvocation() - { - var code = @"class C -{ - void M() { -_ = this is { } -M(); -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0011() { - _ = this is { } - M(); - } -}"; - // although 'M' will be parsed into the pattern on line above, we should not wrap the pattern - await AssertFormatAsync(expectedCode, code); - } + var code = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[1 , 2 , 3]; + var a = new[] { 0 }; + } + """; - [Fact] - public async Task FormatPositionalPattern_FollowedByInvocation() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class C -{ - void M() { -_ = this is (1, 2) { } -M(); -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this is (1, 2) { } - M(); + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - // although 'M' will be parsed into the pattern on line above, we should not wrap the pattern - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatPositionalPattern_FollowedByScope() - { - var code = @"class C -{ - void M() { -_ = this is (1, 2) -{ - M(); -} -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0100() { - _ = this is (1, 2) + var code = """ + + class Program + { + int[ ] x; + int[, ] y; + int[, , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[ 1,2,3 ]; + var a = new[] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - M(); + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -} -}"; - // You should not invoke Format on incomplete code and expect nice results - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatSwitchExpression_MultilineAndNoArms() - { - var code = @"class C -{ - void M() { -_ = this switch -{ - }; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0101() { - _ = this switch + var code = """ + + class Program + { + int[ ] x; + int[, ] y; + int[, , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[ 1, 2, 3 ]; + var a = new[] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, true }, }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - - [Fact] - public async Task FormatSwitchExpression_ExpressionAnchoredToArm() - { - var code = @"class C -{ - void M() { -_ = this switch -{ -{ P1: 1} -=> true, -(0, 1) - => true, -_ - => false -}; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0110() { - _ = this switch - { - { P1: 1 } - => true, - (0, 1) - => true, - _ - => false - }; + var code = """ - } -}"; - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[ ] x; + int[, ] y; + int[, , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task FormatSwitchExpression_NoSpaceBeforeColonInArm() - { - var code = @"class C -{ - void M() { -_ = this switch -{ -{ P1: 1} -=> true, -(0, 1) - => true, -_ - => false -}; + var expected = """ -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this switch + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[ 1 ,2 ,3 ]; + var a = new[] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - { P1: 1 } - => true, - (0, 1) - => true, - _ - => false + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, false }, }; - + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - - [Fact] - public async Task FormatSwitchExpression_ArmCommaWantsNewline() - { - var code = @"class C -{ - void M() { -_ = this switch -{ -{ P1: 1} => true, -(0, 1) => true, _ => false -}; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0111() { - _ = this switch - { - { P1: 1 } => true, - (0, 1) => true, - _ => false - }; + var code = """ - } -}"; - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[ ] x; + int[, ] y; + int[, , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task FormatSwitchExpression_ArmCommaPreservesLines() - { - var code = @"class C -{ - void M() { -_ = this switch -{ -{ P1: 1} => true, + var expected = """ -(0, 1) => true, _ => false -}; + class Program + { + int[] x; + int[,] y; + int[,,] z = new int[ 1 , 2 , 3 ]; + var a = new[] { 0 }; + } + """; -} -}"; - var expectedCode = @"class C -{ - void M() - { - _ = this switch + var options = new OptionsCollection(LanguageNames.CSharp) { - { P1: 1 } => true, - - (0, 1) => true, - _ => false + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, }; - + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/33839")] - public async Task FormatSwitchExpression_ExpressionBody() - { - var code = @" -public class Test -{ - public object Method(int i) - => i switch -{ -1 => 'a', -2 => 'b', -_ => null, -}; -}"; - var expectedCode = @" -public class Test -{ - public object Method(int i) - => i switch - { - 1 => 'a', - 2 => 'b', - _ => null, - }; -}"; + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1000() + { + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[] x; + int[ ,] y; + int[ , ,] z = new int[1,2,3]; + var a = new[] { 0 }; + } + """; - [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72196")] - [InlineData("[]")] - [InlineData("[a]")] - [InlineData("[a, b]")] - [InlineData("[..]")] - [InlineData("[var a, .., var b]")] - [InlineData("[{ } a, null]")] - [InlineData("[a, []]")] - public async Task FormatSwitchExpression_ListPatternAligned(string listPattern) - { - var code = $$""" - class C - { - void M() - { - _ = Array.Empty() switch - { - {{listPattern}} => 0, - _ => 1, - }; - } - } - """; - var expectedCode = $$""" - class C - { - void M() - { - _ = Array.Empty() switch - { - {{listPattern}} => 0, - _ => 1, - }; - } - } - """; - await AssertFormatAsync(expectedCode, code); - } + var expected = """ - [Fact] - public async Task FormatSwitchWithPropertyPattern() - { - var code = @"class C -{ - void M() - { - switch (this) + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - case { P1: 1, P2: { P3: 3, P4: 4 } }: - break; - } + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expectedCode = @"class C -{ - void M() + + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1001() { - switch (this) + var code = """ + + class Program + { + int[] x; + int[ ,] y; + int[ , ,] z = new int[1,2,3]; + var a = new[] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1, 2, 3]; + var a = new[ ] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - case { P1: 1, P2: { P3: 3, P4: 4 } }: - break; - } + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatSwitchWithPropertyPattern_Singleline() - { - var code = @"class C -{ - void M() + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1010() { - switch (this) + var code = """ + + class Program + { + int[] x; + int[ ,] y; + int[ , ,] z = new int[1,2,3]; + var a = new[] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1 ,2 ,3]; + var a = new[ ] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - case { P1: 1, P2: { P3: 3, P4: 4 } }: break; - } + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expectedCode = @"class C -{ - void M() + + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1011() { - switch (this) - { - case { P1: 1, P2: { P3: 3, P4: 4 } }: break; - } - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[] x; + int[ ,] y; + int[ , ,] z = new int[1,2,3]; + var a = new[] { 0 }; + } + """; - [Fact] - public async Task FormatSwitchWithPropertyPattern_Singleline2() - { - var code = @"class C -{ - void M() - { - switch (this) + var expected = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1 , 2 , 3]; + var a = new[ ] { 0 }; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - case { P1: 1, P2: { P3: 3, P4: 4 } }: System.Console.Write(1); - break; - } + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expectedCode = @"class C -{ - void M() + + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1100() { - switch (this) - { - case { P1: 1, P2: { P3: 3, P4: 4 } }: - System.Console.Write(1); - break; - } - } -}"; - await AssertFormatAsync(expectedCode, code); - } + var code = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task SpacingInTupleExtension() - { - var code = @"static class Class5 -{ - static void Extension(this(int, string) self) { } -}"; - var expectedCode = @"static class Class5 -{ - static void Extension(this (int, string) self) { } -}"; + var expected = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[ 1,2,3 ]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task SpacingInNestedDeconstruction() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class Class5{ -void bar() -{ -( int x1 , var( x2,x3 ) )=(1,(2,3)); -} -}"; - var expectedCode = @"class Class5 -{ - void bar() - { - (int x1, var (x2, x3)) = (1, (2, 3)); + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - await AssertFormatAsync(expectedCode, code); - } + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1101() + { + var code = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[ 1, 2, 3 ]; + var a = new[ ] { 0 }; + } + """; - [Fact] - public async Task SpacingInSuppressNullableWarningExpression() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = -@"class C -{ - static object F() - { - object? o[] = null; - object? x = null; - object? y = null; - return x ! ?? (y) ! ?? o[0] !; + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expectedCode = -@"class C -{ - static object F() + + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1110() { - object? o[] = null; - object? x = null; - object? y = null; - return x! ?? (y)! ?? o[0]!; - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; + + var expected = """ + + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[ 1 ,2 ,3 ]; + var a = new[ ] { 0 }; + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545335")] - public async Task PreprocessorOnSameLine() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class C -{ -}#line default + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, false }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); + } -#line hidden"; + [Fact] + public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1111() + { + var code = """ - var expected = @"class C -{ -}#line default + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[1,2,3]; + var a = new[ ] { 0 }; + } + """; -#line hidden"; + var expected = """ - await AssertFormatAsync(expected, code); - } + class Program + { + int[ ] x; + int[ , ] y; + int[ , , ] z = new int[ 1 , 2 , 3 ]; + var a = new[ ] { 0 }; + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545626")] - public async Task ArraysInAttributes() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"[A(X = new int[] { 1 })] -public class A : Attribute -{ - public int[] X; -}"; + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); + } - var expected = @"[A(X = new int[] { 1 })] -public class A : Attribute -{ - public int[] X; -}"; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14128")] + public async Task SpaceBeforeCommasInLocalFunctionParameters() + { + var code = """ - await AssertFormatAsync(expected, code); - } + class Program + { + void Goo() + { + void LocalFunction(int i, string s) + { + } + } + } + """; + + var expected = """ + + class Program + { + void Goo() + { + void LocalFunction(int i , string s) + { + } + } + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] - public async Task NoNewLineAfterBraceInExpression() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"public class A -{ - void Method() - { - var po = cancellationToken.CanBeCanceled ? - new ParallelOptions() { CancellationToken = cancellationToken } : - defaultParallelOptions; + { SpaceBeforeComma, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expected = @"public class A -{ - void Method() + [Fact] + public async Task ArrayDeclarationShouldFollowEmptySquareBrackets() { - var po = cancellationToken.CanBeCanceled ? - new ParallelOptions() { CancellationToken = cancellationToken } : - defaultParallelOptions; - } -}"; + const string code = """ - await AssertFormatAsync(expected, code); - } + class Program + { + var t = new Goo(new[ ] { "a", "b" }); + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] - public async Task NoIndentForNestedUsingWithoutBraces() - { - var code = @"class C -{ -void M() -{ -using (null) -using (null) -{ -} -} -} -"; + const string expected = """ - var expected = @"class C -{ - void M() - { - using (null) - using (null) + class Program + { + var t = new Goo(new[] { "a", "b" }); + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { - } + { CSharpFormattingOptions2.SpaceWithinSquareBrackets, true }, + { CSharpFormattingOptions2.SpaceBetweenEmptySquareBrackets, false } + }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -} -"; - - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] - public async Task NoIndentForNestedUsingWithoutBraces2() - { - var code = @"class C -{ - void M() + [Fact] + public async Task SquareBracesBefore_True() { - using (null) - using (null) - using (null) + var code = """ + + class Program + { + int[] x; + } + """; + + var expected = """ + + class Program { + int [] x; } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceBeforeOpenSquareBracket, true } }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -} -"; - var expected = @"class C -{ - void M() + [Fact] + public async Task SquareBracesAndValue_True() { - using (null) - using (null) - using (null) - { - } - } -} -"; + var code = """ - await AssertFormatAsync(expected, code); - } + class Program + { + int[3] x; + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530580")] - public async Task NoIndentForNestedUsingWithoutBraces3() - { - var code = @"class C -{ - void M() - { - using (null) - using (null) - using (null) - { - } + var expected = """ + + class Program + { + int[ 3 ] x; + } + """; + + var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceWithinSquareBrackets, true } }; + await AssertFormatAsync(expected, code, changedOptionSet: options); } -} -"; - var expected = @"class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/917351")] + public async Task TestLockStatement() { - using (null) - using (null) - using (null) - { - } - } -} -"; + var code = """ - await AssertFormatAsync(expected, code); - } + class Program + { + public void Method() + { + lock (expression) + { + // goo + } + } + } + """; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546678")] - public async Task UnicodeWhitespace() - { - var code = "\u001A"; + var expected = """ - await AssertFormatAsync("", code); - } + class Program + { + public void Method() + { + lock (expression) { + // goo + } + } + } + """; - [Fact, WorkItem(17431, "DevDiv_Projects/Roslyn")] - public async Task NoElasticRuleOnRegularFile() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class Consumer -{ - public int P + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } + }; + + await AssertFormatAsync(expected, code, changedOptionSet: options); + } + + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962416")] + public async Task TestCheckedAndUncheckedStatement() { + var code = """ + + class Program + { + public void Method() + { + checked + { + // goo + } + unchecked + { + } } -}"; + } + """; - var expected = @"class Consumer -{ - public int P - { - } -}"; + var expected = """ - await AssertFormatAsync(expected, code); - } + class Program + { + public void Method() + { + checked { + // goo + } + unchecked { + } + } + } + """; - [Fact, WorkItem(584599, "DevDiv_Projects/Roslyn")] - public async Task CaseSection() - { - var code = @"class C -{ - void Method() - { - switch(i) + var options = new OptionsCollection(LanguageNames.CSharp) { - // test1 - case 1: - // test2 - case 2: - // test3 - int i2 = 10; - // test 4 - case 4: -// test 5 - } + { NewLineBeforeOpenBrace , NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } + }; + + await AssertFormatAsync(expected, code, changedOptionSet: options); } -}"; - var expected = @"class C -{ - void Method() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/953535")] + public async Task ConditionalMemberAccess() { - switch (i) - { - // test1 - case 1: - // test2 - case 2: - // test3 - int i2 = 10; - // test 4 - case 4: - // test 5 - } - } -}"; + var code = """ - await AssertFormatAsync(expected, code); - } + using System; + class A + { + public A a; + } - [Fact, WorkItem(553654, "DevDiv_Projects/Roslyn")] - public async Task Bugfix_553654_LabelStatementIndenting() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class Program { - { CSharpFormattingOptions2.LabelPositioning, LabelPositionOptions.LeftMost } - }; + static void Main(string[] args) + { + A a = null; + A ?.a = null; + System.Console.WriteLine(args ?[0]); + System.Console.WriteLine(args ?.Length); + } + } + """; - var code = @"class Program -{ - void F() - { - foreach (var x in new int[] { }) - { - goo: - int a = 1; - } + var expected = """ + + using System; + class A + { + public A a; + } + + class Program + { + static void Main(string[] args) + { + A a = null; + A?.a = null; + System.Console.WriteLine(args?[0]); + System.Console.WriteLine(args?.Length); + } + } + """; + var parseOptions = new CSharpParseOptions(); + await AssertFormatAsync(expected, code, parseOptions: parseOptions); } -}"; - var expected = @"class Program -{ - void F() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/924172")] + public async Task IgnoreSpacesInDeclarationStatementEnabled() { - foreach (var x in new int[] { }) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { -goo: - int a = 1; - } + { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } + }; + var code = """ + + class Program + { + static void Main(string[] args) + { + int s; + } + } + """; + + var expected = """ + + class Program + { + static void Main(string[] args) + { + int s; + } + } + """; + await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); } -}"; - await AssertFormatAsync(expected, code, changingOptions); - } - [Fact, WorkItem(707064, "DevDiv_Projects/Roslyn")] - public async Task Bugfix_707064_SpaceAfterSecondSemiColonInFor() - { - var code = @"class Program -{ - void F() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899492")] + public async Task CommentIsLeadingTriviaOfStatementNotLabel() { - for (int i = 0; i < 5;) - { - } + var code = """ + + class C + { + void M() + { + label: + // comment + M(); + M(); + } + } + """; + + var expected = """ + + class C + { + void M() + { + label: + // comment + M(); + M(); + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - var expected = @"class Program -{ - void F() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/991547")] + public async Task DoNotWrappingTryCatchFinallyIfOnSingleLine() { - for (int i = 0; i < 5;) - { - } + var code = """ + + class C + { + void M() + { + try { } + catch { } + finally { } + } + } + """; + + var expected = """ + + class C + { + void M() + { + try { } + catch { } + finally { } + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772313")] - public async Task Bugfix_772313_ReturnKeywordBeforeQueryClauseDoesNotTriggerNewLineOnFormat() - { - var code = @"class C -{ - int M() - { - return from c in "" - select c; + [Fact] + public async Task InterpolatedStrings1() + { + var code = """ + + class C + { + void M() + { + var a = "World"; + var b = $"Hello, {a}"; + } + } + """; + + var expected = """ + + class C + { + void M() + { + var a = "World"; + var b = $"Hello, {a}"; + } + } + """; + + await AssertFormatAsync(expected, code); } -}"; - var expected = @"class C -{ - int M() + [Fact] + public async Task InterpolatedStrings2() { - return from c in "" - select c; - } -}"; - await AssertFormatAsync(expected, code); - } + var code = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772304")] - public async Task Bugfix_772313_PreserveMethodParameterIndentWhenAttributePresent() - { - var code = @"class C -{ - void M - ( - [In] - bool b - ); -} + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $"{a}, {b}"; + } + } + """; -class C -{ - void M - ( - [In] - List b - ); -} + var expected = """ -class C -{ - void M - ( - [In] - [In, In] - List b - ); -}"; - - var expected = @"class C -{ - void M - ( - [In] - bool b - ); -} + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $"{a}, {b}"; + } + } + """; -class C -{ - void M - ( - [In] - List b - ); -} + await AssertFormatAsync(expected, code); + } -class C -{ - void M - ( - [In] - [In, In] - List b - ); -}"; - await AssertFormatAsync(expected, code); - } + [Fact] + public async Task InterpolatedStrings3() + { + var code = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/776513")] - public async Task Bugfix_776513_CheckBraceIfNotMissingBeforeApplyingOperationForBracedBlocks() - { - var code = @"var alwaysTriggerList = new[] - Dim triggerOnlyWithLettersList ="; + class C + { + void M() + { + var a = "World"; + var b = $"Hello, { a }"; + } + } + """; - var expected = @"var alwaysTriggerList = new[] - Dim triggerOnlyWithLettersList ="; - await AssertFormatAsync(expected, code); - } + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/769342")] - public async Task ShouldFormatDocCommentWithIndentSameAsTabSizeWithUseTabTrue() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + class C + { + void M() + { + var a = "World"; + var b = $"Hello, {a}"; + } + } + """; - await AssertFormatAsync(@"namespace ConsoleApplication1 -{ - /// - /// fka;jsgdflkhsjflgkhdsl; - /// - class Program - { - } -}", @"namespace ConsoleApplication1 -{ - /// - /// fka;jsgdflkhsjflgkhdsl; - /// - class Program - { + await AssertFormatAsync(expected, code); } -}", changedOptionSet: optionSet); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/797278")] - public async Task TestSpacingOptionAroundControlFlow() - { - const string code = @" -class Program -{ - public void goo() + [Fact] + public async Task InterpolatedRawStrings3() { - int i; - for(i=0; i<10; i++) - {} + var code = """" - foreach(i in new[] {1,2,3}) - {} + class C + { + void M() + { + var a = "World"; + var b = $"""Hello, { a }"""; + } + } + """"; - if (i==10) - {} + var expected = """" - while(i==10) - {} + class C + { + void M() + { + var a = "World"; + var b = $"""Hello, {a}"""; + } + } + """"; - switch(i) - { - default: break; - } + await AssertFormatAsync(expected, code); + } - do {} while (true); + [Fact] + public async Task InterpolatedStrings4() + { + var code = """ - try - { } - catch (System.Exception) - { } - catch (System.Exception e) when (true) - { } + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $"{ a }, { b }"; + } + } + """; - using(somevar) - { } + var expected = """ - lock(somevar) - { } + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $"{a}, {b}"; + } + } + """; - fixed(char* p = str) - { } + await AssertFormatAsync(expected, code); } -}"; - const string expected = @" -class Program -{ - public void goo() - { - int i; - for ( i = 0; i < 10; i++ ) - { } - foreach ( i in new[] { 1, 2, 3 } ) - { } + [Fact] + public async Task InterpolatedStrings5() + { + var code = """ - if ( i == 10 ) - { } + class C + { + void M() + { + var a = "World"; + var b = $@"Hello, {a}"; + } + } + """; - while ( i == 10 ) - { } + var expected = """ - switch ( i ) - { - default: break; - } + class C + { + void M() + { + var a = "World"; + var b = $@"Hello, {a}"; + } + } + """; - do { } while ( true ); + await AssertFormatAsync(expected, code); + } - try - { } - catch ( System.Exception ) - { } - catch ( System.Exception e ) when ( true ) - { } + [Fact] + public async Task InterpolatedStrings6() + { + var code = """ - using ( somevar ) - { } + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $@"{a}, {b}"; + } + } + """; - lock ( somevar ) - { } + var expected = """ - fixed ( char* p = str ) - { } - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBetweenParentheses, SpaceBetweenParentheses.DefaultValue.WithFlagValue( SpacePlacementWithinParentheses.ControlFlowStatements, true) }, - }; + void M() + { + var a = "Hello"; + var b = "World"; + var c = $@"{a}, {b}"; + } + } + """; - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } + await AssertFormatAsync(expected, code); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37031")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/176345")] - public async Task TestSpacingOptionAfterControlFlowKeyword() - { - var code = @" -class Program -{ - public void goo() + [Fact] + public async Task InterpolatedStrings7() { - int i; - for (i=0; i<10; i++) - {} + var code = """ - foreach (i in new[] {1,2,3}) - {} + class C + { + void M() + { + var a = "World"; + var b = $@"Hello, { a }"; + } + } + """; - if (i==10) - {} + var expected = """ - while (i==10) - {} + class C + { + void M() + { + var a = "World"; + var b = $@"Hello, {a}"; + } + } + """; - switch (i) - { - default: break; - } + await AssertFormatAsync(expected, code); + } - do {} while (true); + [Fact] + public async Task InterpolatedStrings8() + { + var code = """ - try - { } - catch (System.Exception e) when (true) - { } + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $@"{ a }, { b }"; + } + } + """; - using (somevar) - { } + var expected = """ - lock (somevar) - { } + class C + { + void M() + { + var a = "Hello"; + var b = "World"; + var c = $@"{a}, {b}"; + } + } + """; - fixed (somevar) - { } + await AssertFormatAsync(expected, code); } -}"; - var expected = @" -class Program -{ - public void goo() + + [Fact] + public async Task InterpolatedStrings9() { - int i; - for(i = 0; i < 10; i++) - { } + var code = """ - foreach(i in new[] { 1, 2, 3 }) - { } + class C + { + void M() + { + var a = "Hello"; + var c = $"{ a }, World"; + } + } + """; - if(i == 10) - { } + var expected = """ - while(i == 10) - { } + class C + { + void M() + { + var a = "Hello"; + var c = $"{a}, World"; + } + } + """; - switch(i) - { - default: break; - } + await AssertFormatAsync(expected, code); + } - do { } while(true); + [Fact] + public async Task InterpolatedStrings10() + { + var code = """ - try - { } - catch(System.Exception e) when(true) - { } + class C + { + void M() + { + var s = $"{42 , -4 :x}"; + } + } + """; - using(somevar) - { } + var expected = """ - lock(somevar) - { } + class C + { + void M() + { + var s = $"{42,-4:x}"; + } + } + """; - fixed(somevar) - { } + await AssertFormatAsync(expected, code); } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceAfterControlFlowStatementKeyword, false } }; - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/766212")] - public async Task TestOptionForSpacingAroundCommas() - { - var code = @" -class Program -{ - public void Main() - { - var a = new[] {1,2,3}; - var digits = new List {1,2,3,4}; - } -}"; - var expectedDefault = @" -class Program -{ - public void Main() + [Fact] + public async Task InterpolatedRawStrings10() { - var a = new[] { 1, 2, 3 }; - var digits = new List { 1, 2, 3, 4 }; - } -}"; - await AssertFormatAsync(expectedDefault, code); + var code = """" - var expectedAfterCommaDisabled = @" -class Program -{ - public void Main() - { - var a = new[] { 1,2,3 }; - var digits = new List { 1,2,3,4 }; - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceAfterComma, false } }; - await AssertFormatAsync(expectedAfterCommaDisabled, code, changedOptionSet: optionSet); + class C + { + void M() + { + var s = $"""{42 , -4 :x}"""; + } + } + """"; - var expectedBeforeCommaEnabled = @" -class Program -{ - public void Main() - { - var a = new[] { 1 ,2 ,3 }; - var digits = new List { 1 ,2 ,3 ,4 }; + var expected = """" + + class C + { + void M() + { + var s = $"""{42,-4:x}"""; + } + } + """"; + + await AssertFormatAsync(expected, code); } -}"; - optionSet.Add(CSharpFormattingOptions2.SpaceBeforeComma, true); - await AssertFormatAsync(expectedBeforeCommaEnabled, code, changedOptionSet: optionSet); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772308")] - public async Task Bugfix_772308_SeparateSuppressionForEachCaseLabelEvenIfEmpty() - { - var code = @" -class C -{ - int M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] + public async Task InterpolatedStrings11() { - switch (1) - { - case 1: return 1; - case 2: return 2; - case 3: - case 4: return 4; - default: - } + var code = """ + + class C + { + void M() + { + var hostAddress = "host"; + var nasTypeId = "nas"; + var version = "1.2"; + var c = $"{ hostAddress?? ""}/{nasTypeId }/{version??""}"; + } + } + """; + + var expected = """ + + class C + { + void M() + { + var hostAddress = "host"; + var nasTypeId = "nas"; + var version = "1.2"; + var c = $"{hostAddress ?? ""}/{nasTypeId}/{version ?? ""}"; + } + } + """; + + await AssertFormatAsync(expected, code); } -} -"; - var expected = @" -class C -{ - int M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] + public async Task InterpolatedStrings12() { - switch (1) - { - case 1: return 1; - case 2: return 2; - case 3: - case 4: return 4; - default: - } - } -} -"; - await AssertFormatAsync(expected, code); - } + var code = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/844913")] - public async Task QueryExpressionInExpression() - { - var code = @" -class C -{ - public void CreateSettingsFile(string path, string comment) { - var xml = new XDocument( - new XDeclaration(1.0, utf8, yes), - new XComment(comment), - new XElement(UserSettings, - new XElement(ToolsOptions, - from t in KnownSettings.DefaultCategories - group t by t.Item1 into cat - select new XElement(ToolsOptionsCategory, - new XAttribute(name, cat.Key), - cat.Select(sc => new XElement(ToolsOptionsSubCategory, new XAttribute(name, sc.Item2))) - ) - ) - ) - ); - UpdateSettingsXml(xml); - xml.Save(path); - SettingsPath = path; - } - } -"; + class C + { + void M() + { + var a = 1.2M; + var c = $"{ a : 000.00 }"; + } + } + """; - var expected = @" -class C -{ - public void CreateSettingsFile(string path, string comment) - { - var xml = new XDocument( - new XDeclaration(1.0, utf8, yes), - new XComment(comment), - new XElement(UserSettings, - new XElement(ToolsOptions, - from t in KnownSettings.DefaultCategories - group t by t.Item1 into cat - select new XElement(ToolsOptionsCategory, - new XAttribute(name, cat.Key), - cat.Select(sc => new XElement(ToolsOptionsSubCategory, new XAttribute(name, sc.Item2))) - ) - ) - ) - ); - UpdateSettingsXml(xml); - xml.Save(path); - SettingsPath = path; - } -} -"; - await AssertFormatAsync(expected, code); - } + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/843479")] - public async Task EmbeddedStatementElse() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { CSharpFormattingOptions2.NewLineForElse, false } - }; + void M() + { + var a = 1.2M; + var c = $"{a: 000.00 }"; + } + } + """; - var code = @" -class C -{ - void Method() - { - if (true) - Console.WriteLine(); else - Console.WriteLine(); + await AssertFormatAsync(expected, code); } -} -"; - var expected = @" -class C -{ - void Method() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] + public async Task InterpolatedStrings13() { - if (true) - Console.WriteLine(); - else - Console.WriteLine(); - } -} -"; - await AssertFormatAsync(expected, code, changingOptions); - } + var code = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772311")] - public async Task LineCommentAtTheEndOfLine() - { - var code = @" -using System; + class C + { + void M() + { + var a = 1.2M; + var c = $"{ (a > 2?"a":"b"}"; + } + } + """; -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(); // this is a comment - // that I would like to keep + var expected = """ - // properly indented - } -} -"; + class C + { + void M() + { + var a = 1.2M; + var c = $"{(a > 2 ? "a" : "b"}"; + } + } + """; - var expected = @" -using System; + await AssertFormatAsync(expected, code); + } -class Program -{ - static void Main(string[] args) + [Fact] + public async Task InterpolatedStrings14() { - Console.WriteLine(); // this is a comment - // that I would like to keep + var code = """ - // properly indented - } -} -"; - await AssertFormatAsync(expected, code); - } + class C + { + void M() + { + var s = $"{ 42 , -4 :x}"; + } + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] - public async Task BlockCommentAtTheEndOfLine1() - { - var code = @" -using System; + var expected = """ -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(); /* this is a comment */ - // that I would like to keep + class C + { + void M() + { + var s = $"{42,-4:x}"; + } + } + """; - // properly indented + await AssertFormatAsync(expected, code); } -} -"; - - var expected = @" -using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task InterpolatedStrings15() { - Console.WriteLine(); /* this is a comment */ - // that I would like to keep + var code = """ + + class C + { + void M() + { + var s = $"{ 42 , -4 }"; + } + } + """; + + var expected = """ + + class C + { + void M() + { + var s = $"{42,-4}"; + } + } + """; - // properly indented + await AssertFormatAsync(expected, code); } -} -"; - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] - public async Task BlockCommentAtTheEndOfLine2() - { - var code = @" -using System; -class Program -{ - static void Main(string[] args) + [Fact] + public async Task InterpolatedStrings16() { - Console.WriteLine(); // this is a comment - /* that I would like to keep */ + var code = """ - // properly indented - } -} -"; + class C + { + void M() + { + var s = $"{ 42 , -4 : x }"; + } + } + """; - var expected = @" -using System; + var expected = """ -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(); // this is a comment - /* that I would like to keep */ + class C + { + void M() + { + var s = $"{42,-4: x }"; + } + } + """; - // properly indented + await AssertFormatAsync(expected, code); } -} -"; - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38224")] - public async Task BlockCommentAtBeginningOfLine() - { - var code = @" -using System; -class Program -{ - static void Main( - int x, // Some comment - /*A*/ int y) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1151")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1041787")] + public async Task ReconstructWhitespaceStringUsingTabs_SingleLineComment() { - } -} -"; - await AssertFormatAsync(code, code); - } + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + await AssertFormatAsync(""" + using System; - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/772311")] - public async Task TestTab() - { - var code = @" -using System; + class Program + { + static void Main(string[] args) + { + Console.WriteLine(""); // GooBar + } + } + """, """ + using System; -class Program -{ - /// - /// This function is the callback used to execute a command when a menu item is clicked. - /// See the Initialize method to see how the menu item is associated to this function using - /// the OleMenuCommandService service and the MenuCommand class. - /// - private void MenuItemCallback(object sender, EventArgs e) { - // Show a Message Box to prove we were here - IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); - Guid clsid = Guid.Empty; - int result; - Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( - 0, - ref clsid, - Rebracer, - string.Format(CultureInfo.CurrentCulture, Inside {0}.MenuItemCallback(), this.ToString()), - string.Empty, - 0, - OLEMSGBUTTON.OLEMSGBUTTON_OK, - OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, - OLEMSGICON.OLEMSGICON_INFO, - 0, // false - out result)); - } + class Program + { + static void Main(string[] args) + { + Console.WriteLine(""); // GooBar + } + } + """, optionSet); } -"; - - var expected = @" -using System; -class Program -{ - /// - /// This function is the callback used to execute a command when a menu item is clicked. - /// See the Initialize method to see how the menu item is associated to this function using - /// the OleMenuCommandService service and the MenuCommand class. - /// - private void MenuItemCallback(object sender, EventArgs e) - { - // Show a Message Box to prove we were here - IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); - Guid clsid = Guid.Empty; - int result; - Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( - 0, - ref clsid, - Rebracer, - string.Format(CultureInfo.CurrentCulture, Inside { 0}.MenuItemCallback(), this.ToString()), - string.Empty, - 0, - OLEMSGBUTTON.OLEMSGBUTTON_OK, - OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, - OLEMSGICON.OLEMSGICON_INFO, - 0, // false - out result)); - } -} -"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1151")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/961559")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1041787")] + public async Task ReconstructWhitespaceStringUsingTabs_MultiLineComment() + { + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + await AssertFormatAsync(""" + using System; - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - await AssertFormatAsync(expected, expected, changedOptionSet: optionSet); - } + class Program + { + static void Main(string[] args) + { + Console.WriteLine(""); /* GooBar */ + } + } + """, """ + using System; - [Fact] - public async Task LeaveBlockSingleLine_False() - { - var code = @" -namespace N { class C { int x; } }"; + class Program + { + static void Main(string[] args) + { + Console.WriteLine(""); /* GooBar */ + } + } + """, optionSet); + } - var expected = @" -namespace N -{ - class C + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1100920")] + public async Task NoLineOperationAroundInterpolationSyntax() { - int x; + await AssertFormatAsync(""" + class Program + { + static string F(int a, int b, int c) + { + return $"{a} (index: 0x{b}, size: {c}): " + } + } + """, """ + class Program + { + static string F(int a, int b, int c) + { + return $"{a} (index: 0x{ b}, size: { c}): " + } + } + """); } -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } - [Fact] - public async Task LeaveBlockSingleLine_False2() - { - var code = @" -class C { void goo() { } }"; - - var expected = @" -class C -{ - void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/62")] + public async Task SpaceAfterWhenInExceptionFilter() { - } -}"; + const string expected = """ + class C + { + void M() + { + try + { + if (x) + { + G(); + } + } + catch (Exception e) when (H(e)) + { - var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingPreserveSingleLine, false } }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + } + } + } + """; - [Fact] - public async Task LeaveStatementMethodDeclarationSameLine_False() - { - var code = @" -class Program -{ - void goo() - { - int x = 0; int y = 0; - } -}"; + const string code = """ + class C + { + void M() + { + try + { + if(x){ + G(); + } + } + catch(Exception e) when (H(e)) + { - var expected = @" -class Program -{ - void goo() - { - int x = 0; - int y = 0; + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0000() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[1,2,3]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] + [WorkItem("https://github.com/dotnet/roslyn/issues/285")] + public async Task FormatHashInBadDirectiveToZeroColumnAnywhereInsideIfDef() + { + const string code = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0001() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[1, 2, 3]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + # - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0010() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[1 ,2 ,3]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #endif + } + } + """; - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0011() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[1 , 2 , 3]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + const string expected = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0100() - { - var code = @" -class Program -{ - int[ ] x; - int[, ] y; - int[, , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[ 1,2,3 ]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + # - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0101() - { - var code = @" -class Program -{ - int[ ] x; - int[, ] y; - int[, , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[ 1, 2, 3 ]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #endif + } + } + """; + await AssertFormatAsync(expected, code); + } - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0110() - { - var code = @" -class Program -{ - int[ ] x; - int[, ] y; - int[, , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[ 1 ,2 ,3 ]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] + [WorkItem("https://github.com/dotnet/roslyn/issues/285")] + public async Task FormatHashElseToZeroColumnAnywhereInsideIfDef() + { + const string code = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_0111() - { - var code = @" -class Program -{ - int[ ] x; - int[, ] y; - int[, , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[] x; - int[,] y; - int[,,] z = new int[ 1 , 2 , 3 ]; - var a = new[] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #else + Appropriate indentation should be here though # + #endif + } + } + """; - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1000() - { - var code = @" -class Program -{ - int[] x; - int[ ,] y; - int[ , ,] z = new int[1,2,3]; - var a = new[] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + const string expected = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1001() - { - var code = @" -class Program -{ - int[] x; - int[ ,] y; - int[ , ,] z = new int[1,2,3]; - var a = new[] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1, 2, 3]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #else + Appropriate indentation should be here though # + #endif + } + } + """; + await AssertFormatAsync(expected, code); + } - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1010() - { - var code = @" -class Program -{ - int[] x; - int[ ,] y; - int[ , ,] z = new int[1,2,3]; - var a = new[] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1 ,2 ,3]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] + [WorkItem("https://github.com/dotnet/roslyn/issues/285")] + public async Task FormatHashsToZeroColumnAnywhereInsideIfDef() + { + const string code = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1011() - { - var code = @" -class Program -{ - int[] x; - int[ ,] y; - int[ , ,] z = new int[1,2,3]; - var a = new[] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1 , 2 , 3]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #else + # - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1100() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[ 1,2,3 ]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #endif + } + } + """; - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1101() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[ 1, 2, 3 ]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + const string expected = """ + class MyClass + { + static void Main(string[] args) + { + #if false - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1110() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[ 1 ,2 ,3 ]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, false }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #else + # - [Fact] - public async Task SpaceWithinEmptyBracketPrecedencesSpaceBeforeOrAfterComma_1111() - { - var code = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[1,2,3]; - var a = new[ ] { 0 }; -}"; - - var expected = @" -class Program -{ - int[ ] x; - int[ , ] y; - int[ , , ] z = new int[ 1 , 2 , 3 ]; - var a = new[ ] { 0 }; -}"; - - var options = new OptionsCollection(LanguageNames.CSharp) - { - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + #endif + } + } + """; + await AssertFormatAsync(expected, code); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/14128")] - public async Task SpaceBeforeCommasInLocalFunctionParameters() - { - var code = @" -class Program -{ - void Goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1118")] + public void DoNotAssumeCertainNodeAreAlwaysParented() { - void LocalFunction(int i, string s) - { - } + var block = SyntaxFactory.Block(); + Formatter.Format(block, new AdhocWorkspace().Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); } -}"; - var expected = @" -class Program -{ - void Goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/776")] + public async Task SpacingRulesAroundMethodCallAndParenthesisAppliedInAttributeNonDefault() { - void LocalFunction(int i , string s) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - } + { CSharpFormattingOptions2.SpaceAfterMethodCallName, true }, + { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true }, + { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } + }; + await AssertFormatAsync(""" + [Obsolete ( "Test" ), Obsolete ( )] + class Program + { + static void Main(string[] args) + { + } + } + """, """ + [Obsolete("Test"), Obsolete()] + class Program + { + static void Main(string[] args) + { + } + } + """, changingOptions); } -}"; - var options = new OptionsCollection(LanguageNames.CSharp) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/776")] + public async Task SpacingRulesAroundMethodCallAndParenthesisAppliedInAttribute() + { + var code = """ + [Obsolete("Test"), Obsolete()] + class Program { - { SpaceBeforeComma, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } - - [Fact] - public async Task ArrayDeclarationShouldFollowEmptySquareBrackets() - { - const string code = @" -class Program -{ - var t = new Goo(new[ ] { ""a"", ""b"" }); -}"; + static void Main(string[] args) + { + } + } + """; + await AssertFormatAsync(code, code); + } - const string expected = @" -class Program -{ - var t = new Goo(new[] { ""a"", ""b"" }); -}"; + [Fact] + public async Task SpacingInMethodCallArguments_True() + { + const string code = """ - var options = new OptionsCollection(LanguageNames.CSharp) + [Bar(A=1,B=2)] + class Program { - { CSharpFormattingOptions2.SpaceWithinSquareBrackets, true }, - { CSharpFormattingOptions2.SpaceBetweenEmptySquareBrackets, false } - }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + public void goo() + { + var a = typeof(A); + var b = M(a); + var c = default(A); + var d = sizeof(A); + M(); + } + } + """; + const string expected = """ - [Fact] - public async Task SquareBracesBefore_True() + [Bar ( A = 1, B = 2 )] + class Program + { + public void goo() + { + var a = typeof ( A ); + var b = M ( a ); + var c = default ( A ); + var d = sizeof ( A ); + M ( ); + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { - var code = @" -class Program -{ - int[] x; -}"; + { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true }, + { CSharpFormattingOptions2.SpaceAfterMethodCallName, true }, + { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true }, + }; + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1298")] + public async Task DoNotforceAccessorsToNewLineWithPropertyInitializers() + { + var code = """ + using System.Collections.Generic; - var expected = @" -class Program -{ - int [] x; -}"; + class Program + { + public List ValidationExcludeFilters { get; } + = new List(); + } - var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceBeforeOpenSquareBracket, true } }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + public class ExcludeValidation + { + } + """; - [Fact] - public async Task SquareBracesAndValue_True() - { - var code = @" -class Program -{ - int[3] x; -}"; + var expected = """ + using System.Collections.Generic; - var expected = @" -class Program -{ - int[ 3 ] x; -}"; + class Program + { + public List ValidationExcludeFilters { get; } + = new List(); + } - var options = new OptionsCollection(LanguageNames.CSharp) { { CSharpFormattingOptions2.SpaceWithinSquareBrackets, true } }; - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + public class ExcludeValidation + { + } + """; + await AssertFormatAsync(expected, code); + } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/917351")] - public async Task TestLockStatement() - { - var code = @" -class Program -{ - public void Method() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1339")] + public async Task DoNotFormatAutoPropertyInitializerIfNotDifferentLine() { - lock (expression) + var code = """ + class Program { - // goo -} + public int d { get; } + = 3; + static void Main(string[] args) + { + } + } + """; + await AssertFormatAsync(code, code); } -}"; - var expected = @" -class Program -{ - public void Method() + [Fact] + public async Task SpacingForForStatementInfiniteLoop() { - lock (expression) { - // goo - } - } -}"; + var code = """ - var options = new OptionsCollection(LanguageNames.CSharp) + class Program { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } - }; - - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + void Main() + { + for ( ;;) + { + } + } + } + """; + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/962416")] - public async Task TestCheckedAndUncheckedStatement() - { - var code = @" -class Program -{ - public void Method() - { - checked + class Program { - // goo -} - unchecked + void Main() + { + for (; ; ) { + } + } } + """; + await AssertFormatAsync(expected, code); } -}"; - var expected = @" -class Program -{ - public void Method() + [Fact] + public async Task SpacingForForStatementInfiniteLoopWithNoSpaces() { - checked { - // goo - } - unchecked { - } - } -}"; + var code = """ - var options = new OptionsCollection(LanguageNames.CSharp) + class Program { - { NewLineBeforeOpenBrace , NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } - }; - - await AssertFormatAsync(expected, code, changedOptionSet: options); - } + void Main() + { + for ( ; ; ) + { + } + } + } + """; + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/953535")] - public async Task ConditionalMemberAccess() + class Program + { + void Main() + { + for (;;) + { + } + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { - var code = @" -using System; -class A -{ - public A a; -} + { CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, false }, + }; -class Program -{ - static void Main(string[] args) - { - A a = null; - A ?.a = null; - System.Console.WriteLine(args ?[0]); - System.Console.WriteLine(args ?.Length); + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } -}"; - var expected = @" -using System; -class A -{ - public A a; -} - -class Program -{ - static void Main(string[] args) + [Fact] + public async Task SpacingForForStatementInfiniteLoopWithSpacesBefore() { - A a = null; - A?.a = null; - System.Console.WriteLine(args?[0]); - System.Console.WriteLine(args?.Length); - } -}"; - var parseOptions = new CSharpParseOptions(); - await AssertFormatAsync(expected, code, parseOptions: parseOptions); - } + var code = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/924172")] - public async Task IgnoreSpacesInDeclarationStatementEnabled() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class Program { - { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } - }; - var code = @" -class Program -{ - static void Main(string[] args) - { - int s; + void Main() + { + for (;; ) + { + } + } + } + """; + var expected = """ + + class Program + { + void Main() + { + for ( ; ;) + { + } + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, true }, + { CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, false }, + }; + + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } -}"; - var expected = @" -class Program -{ - static void Main(string[] args) + [Fact] + public async Task SpacingForForStatementInfiniteLoopWithSpacesBeforeAndAfter() { - int s; - } -}"; - await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); - } + var code = """ + + class Program + { + void Main() + { + for (;;) + { + } + } + } + """; + var expected = """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/899492")] - public async Task CommentIsLeadingTriviaOfStatementNotLabel() + class Program + { + void Main() + { + for ( ; ; ) + { + } + } + } + """; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { - var code = @" -class C -{ - void M() - { - label: - // comment - M(); - M(); - } -}"; + { CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, true }, + }; - var expected = @" -class C -{ - void M() - { - label: - // comment - M(); - M(); + await AssertFormatAsync(expected, code, changedOptionSet: optionSet); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/991547")] - public async Task DoNotWrappingTryCatchFinallyIfOnSingleLine() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4240")] + [WorkItem("https://github.com/dotnet/roslyn/issues/4421")] + public async Task VerifySpacingAfterMethodDeclarationName_Default() { - try { } - catch { } - finally { } + var code = """ + class Program + { + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } + public void F () { } + } + """; + var expected = """ + class Program + { + public static Program operator +(Program p1, Program p2) { return null; } + public static implicit operator string(Program p) { return null; } + public static void M() { } + public void F() { } + } + """; + await AssertFormatAsync(expected, code); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4421")] + [WorkItem("https://github.com/dotnet/roslyn/issues/4240")] + public async Task VerifySpacingAfterMethodDeclarationName_NonDefault() { - try { } - catch { } - finally { } + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.SpacingAfterMethodDeclarationName, true } + }; + var code = """ + class Program + { + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } + public void F () { } + } + """; + var expected = """ + class Program + { + public static Program operator + (Program p1, Program p2) { return null; } + public static implicit operator string (Program p) { return null; } + public static void M () { } + public void F () { } + } + """; + await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact] - public async Task InterpolatedStrings1() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/939")] + public async Task DoNotFormatInsideArrayInitializers() { - var a = ""World""; - var b = $""Hello, {a}""; + var code = """ + class Program + { + static void Main(string[] args) + { + int[] sss = new[] { + //Comment1 + 2, + 5, 324534, 345345, + //Comment2 + //This comment should not line up with the previous comment + 234234 + //Comment3 + , 234, + 234234 + /* + This is a multiline comment + */ + //Comment4 + }; + } + } + """; + await AssertFormatAsync(code, code); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4280")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1184285")] + public async Task FormatDictionaryInitializers() { - var a = ""World""; - var b = $""Hello, {a}""; + var code = """ + class Program + { + void Main() + { + var sample = new Dictionary {["x"] = "d" ,["z"] = "XX" }; + } + } + """; + var expected = """ + class Program + { + void Main() + { + var sample = new Dictionary { ["x"] = "d", ["z"] = "XX" }; + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - - await AssertFormatAsync(expected, code); - } - [Fact] - public async Task InterpolatedStrings2() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3256")] + public async Task SwitchSectionHonorsNewLineForBracesinControlBlockOption_Default() { - var a = ""Hello""; - var b = ""World""; - var c = $""{a}, {b}""; + var code = """ + class Program + { + public void goo() + { + int f = 1; + switch (f) { + case 1: { + // DO nothing + break; + } + } + } + } + """; + var expected = """ + class Program + { + public void goo() + { + int f = 1; + switch (f) + { + case 1: + { + // DO nothing + break; + } + } + } + } + """; + await AssertFormatAsync(expected, code); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3256")] + public async Task SwitchSectionHonorsNewLineForBracesinControlBlockOption_NonDefault() { - var a = ""Hello""; - var b = ""World""; - var c = $""{a}, {b}""; - } -}"; + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } + }; + var code = """ + class Program + { + public void goo() + { + int f = 1; + switch (f) + { + case 1: + { + // DO nothing + break; + } + } + } + } + """; - await AssertFormatAsync(expected, code); - } + var expected = """ + class Program + { + public void goo() + { + int f = 1; + switch (f) { + case 1: { + // DO nothing + break; + } + } + } + } + """; + await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); + } - [Fact] - public async Task InterpolatedStrings3() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] + public async Task FormattingCodeWithMissingTokensShouldRespectFormatTabsOption1() { - var a = ""World""; - var b = $""Hello, { a }""; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + + await AssertFormatAsync(""" + class Program + { + static void Main() + { + return // Note the missing semicolon + } // The tab here should stay a tab + } + """, """ + class Program + { + static void Main() + { + return // Note the missing semicolon + } // The tab here should stay a tab + } + """, changedOptionSet: optionSet); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] + public async Task FormattingCodeWithMissingTokensShouldRespectFormatTabsOption2() { - var a = ""World""; - var b = $""Hello, {a}""; - } -}"; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - await AssertFormatAsync(expected, code); - } + await AssertFormatAsync(""" + struct Goo + { + private readonly string bar; - [Fact] - public async Task InterpolatedRawStrings3() - { - var code = @" -class C -{ - void M() - { - var a = ""World""; - var b = $""""""Hello, { a }""""""; + public Goo(readonly string bar) + { + } + } + """, """ + struct Goo + { + private readonly string bar; + + public Goo(readonly string bar) + { + } + } + """, changedOptionSet: optionSet); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] + public async Task FormattingCodeWithBrokenLocalDeclarationShouldRespectFormatTabsOption() { - var a = ""World""; - var b = $""""""Hello, {a}""""""; - } -}"; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - await AssertFormatAsync(expected, code); - } + await AssertFormatAsync(""" + class AClass + { + void AMethod(Object anArgument) + { + if (anArgument == null) + { + throw new ArgumentNullException(nameof(anArgument)); + } + anArgument + + DoSomething(); + } + + void DoSomething() + { + } + } + """, """ + class AClass + { + void AMethod(Object anArgument) + { + if (anArgument == null) + { + throw new ArgumentNullException(nameof(anArgument)); + }anArgument + + DoSomething(); + } + + void DoSomething() + { + } + } + """, changedOptionSet: optionSet); + } - [Fact] - public async Task InterpolatedStrings4() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] + public async Task FormattingCodeWithBrokenInterpolatedStringShouldRespectFormatTabsOption() { - var a = ""Hello""; - var b = ""World""; - var c = $""{ a }, { b }""; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + + await AssertFormatAsync(""" + class AClass + { + void Main() + { + Test($"\"_{\""); + Console.WriteLine(args); + } + } + """, """ + class AClass + { + void Main() + { + Test($"\"_{\""); + Console.WriteLine(args); + } + } + """, changedOptionSet: optionSet); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/84")] + [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] + public async Task NewLinesForBracesInPropertiesTest() { - var a = ""Hello""; - var b = ""World""; - var c = $""{a}, {b}""; - } -}"; + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.Properties, false) }, + }; + await AssertFormatAsync(""" + class Class2 + { + int Goo { + get + { + return 1; + } + } - await AssertFormatAsync(expected, code); - } + int MethodGoo() + { + return 42; + } + } + """, """ + class Class2 + { + int Goo + { + get + { + return 1; + } + } - [Fact] - public async Task InterpolatedStrings5() - { - var code = @" -class C -{ - void M() - { - var a = ""World""; - var b = $@""Hello, {a}""; + int MethodGoo() + { + return 42; + } + } + """, changingOptions); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] + [WorkItem("https://github.com/dotnet/roslyn/issues/84")] + public async Task NewLinesForBracesInAccessorsTest() { - var a = ""World""; - var b = $@""Hello, {a}""; - } -}"; + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.Accessors, false) }, + }; + await AssertFormatAsync(""" + class Class2 + { + int Goo + { + get { + return 1; + } + } - await AssertFormatAsync(expected, code); - } + int MethodGoo() + { + return 42; + } + } + """, """ + class Class2 + { + int Goo + { + get + { + return 1; + } + } - [Fact] - public async Task InterpolatedStrings6() - { - var code = @" -class C -{ - void M() - { - var a = ""Hello""; - var b = ""World""; - var c = $@""{a}, {b}""; + int MethodGoo() + { + return 42; + } + } + """, changingOptions); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] + [WorkItem("https://github.com/dotnet/roslyn/issues/84")] + public async Task NewLinesForBracesInPropertiesAndAccessorsTest() { - var a = ""Hello""; - var b = ""World""; - var c = $@""{a}, {b}""; - } -}"; + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue + .WithFlagValue(NewLineBeforeOpenBracePlacement.Properties, false) + .WithFlagValue(NewLineBeforeOpenBracePlacement.Accessors, false)}, + }; + await AssertFormatAsync(""" + class Class2 + { + int Goo { + get { + return 1; + } + } - await AssertFormatAsync(expected, code); - } + int MethodGoo() + { + return 42; + } + } + """, """ + class Class2 + { + int Goo + { + get + { + return 1; + } + } - [Fact] - public async Task InterpolatedStrings7() - { - var code = @" -class C -{ - void M() - { - var a = ""World""; - var b = $@""Hello, { a }""; + int MethodGoo() + { + return 42; + } + } + """, changingOptions); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem(111079, "devdiv.visualstudio.com")] + public async Task TestThrowInIfOnSingleLine() { - var a = ""World""; - var b = $@""Hello, {a}""; - } -}"; + var code = """ - await AssertFormatAsync(expected, code); - } + class C + { + void M() + { + if (true) throw new Exception( + "message"); + } + } - [Fact] - public async Task InterpolatedStrings8() - { - var code = @" -class C -{ - void M() - { - var a = ""Hello""; - var b = ""World""; - var c = $@""{ a }, { b }""; - } -}"; + """; - var expected = @" -class C -{ - void M() - { - var a = ""Hello""; - var b = ""World""; - var c = $@""{a}, {b}""; + await AssertFormatAsync(code, code); } -}"; - - await AssertFormatAsync(expected, code); - } - [Fact] - public async Task InterpolatedStrings9() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://connect.microsoft.com/VisualStudio/feedback/details/1711675/autoformatting-issues")] + public async Task SingleLinePropertiesPreservedWithLeaveStatementsAndMembersOnSingleLineFalse() { - var a = ""Hello""; - var c = $""{ a }, World""; - } -}"; + var changedOptionSet = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.WrappingPreserveSingleLine, true }, + { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false}, + }; - var expected = @" -class C -{ - void M() - { - var a = ""Hello""; - var c = $""{a}, World""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class C + { + string Name { get; set; } + } + """, """ - [Fact] - public async Task InterpolatedStrings10() - { - var code = @" -class C -{ - void M() - { - var s = $""{42 , -4 :x}""; + class C + { + string Name { get ; set ; } + } + """, changedOptionSet: changedOptionSet); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4720")] + public async Task KeepAccessorWithAttributeOnSingleLine() { - var s = $""{42,-4:x}""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class Program + { + public Int32 PaymentMethodID + { + [System.Diagnostics.DebuggerStepThrough] + get { return 10; } + } + } + """, """ - [Fact] - public async Task InterpolatedRawStrings10() - { - var code = @" -class C -{ - void M() - { - var s = $""""""{42 , -4 :x}""""""; + class Program + { + public Int32 PaymentMethodID + { + [System.Diagnostics.DebuggerStepThrough] + get { return 10; } + } + } + """); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] + public async Task KeepConstructorBodyInSameLineAsBaseConstructorInitializer() { - var s = $""""""{42,-4:x}""""""; - } -}"; + var code = """ - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] - public async Task InterpolatedStrings11() - { - var code = @" -class C -{ - void M() - { - var hostAddress = ""host""; - var nasTypeId = ""nas""; - var version = ""1.2""; - var c = $""{ hostAddress?? """"}/{nasTypeId }/{version??""""}""; + class C + { + public C(int s) + : base() { } + public C() + { + } + } + """; + await AssertFormatAsync(code, code); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] + public async Task KeepConstructorBodyInSameLineAsThisConstructorInitializer() { - var hostAddress = ""host""; - var nasTypeId = ""nas""; - var version = ""1.2""; - var c = $""{hostAddress ?? """"}/{nasTypeId}/{version ?? """"}""; - } -}"; - - await AssertFormatAsync(expected, code); - } + var code = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] - public async Task InterpolatedStrings12() - { - var code = @" -class C -{ - void M() - { - var a = 1.2M; - var c = $""{ a : 000.00 }""; + class C + { + public C(int s) + : this() { } + public C() + { + } + } + """; + await AssertFormatAsync(code, code); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] + public async Task KeepConstructorBodyInSameLineAsThisConstructorInitializerAdjustSpace() { - var a = 1.2M; - var c = $""{a: 000.00 }""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class C + { + public C(int s) + : this() { } + public C() + { + } + } + """, """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/59811")] - public async Task InterpolatedStrings13() - { - var code = @" -class C -{ - void M() - { - var a = 1.2M; - var c = $""{ (a > 2?""a"":""b""}""; + class C + { + public C(int s) + : this() { } + public C() + { + } + } + """); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4720")] + public async Task OneSpaceBetweenAccessorsAndAttributes() { - var a = 1.2M; - var c = $""{(a > 2 ? ""a"" : ""b""}""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class Program + { + public int SomeProperty { [SomeAttribute] get; [SomeAttribute] private set; } + } + """, """ - [Fact] - public async Task InterpolatedStrings14() - { - var code = @" -class C -{ - void M() - { - var s = $""{ 42 , -4 :x}""; + class Program + { + public int SomeProperty { [SomeAttribute] get; [SomeAttribute] private set; } + } + """); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7900")] + public async Task FormatEmbeddedStatementInsideLockStatement() { - var s = $""{42,-4:x}""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class C + { + private object _l = new object(); + public void M() + { + lock (_l) Console.WriteLine("d"); + } + } + """, """ - [Fact] - public async Task InterpolatedStrings15() - { - var code = @" -class C -{ - void M() - { - var s = $""{ 42 , -4 }""; + class C + { + private object _l = new object(); + public void M() + { + lock (_l) Console.WriteLine("d"); + } + } + """); } -}"; - var expected = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7900")] + public async Task FormatEmbeddedStatementInsideLockStatementDifferentLine() { - var s = $""{42,-4}""; - } -}"; + await AssertFormatAsync(""" - await AssertFormatAsync(expected, code); - } + class C + { + private object _l = new object(); + public void M() + { + lock (_l) + Console.WriteLine("d"); + } + } + """, """ - [Fact] - public async Task InterpolatedStrings16() - { - var code = @" -class C -{ - void M() - { - var s = $""{ 42 , -4 : x }""; + class C + { + private object _l = new object(); + public void M() + { + lock (_l) + Console.WriteLine("d"); + } + } + """); } -}"; - var expected = @" -class C -{ - void M() + [Fact] + public async Task PropertyDeclarationSimple() { - var s = $""{42,-4: x }""; + var expected = @"if (o is Point p)"; + await AssertFormatBodyAsync(expected, expected); + await AssertFormatBodyAsync(expected, @"if (o is Point p)"); + await AssertFormatBodyAsync(expected, @"if (o is Point p )"); } -}"; - await AssertFormatAsync(expected, code); - } + [Fact] + public async Task PropertyDeclarationTypeOnNewLine() + { + var expected = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1151")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1041787")] - public async Task ReconstructWhitespaceStringUsingTabs_SingleLineComment() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - await AssertFormatAsync(@"using System; + var y = o is + Point p; + """; + await AssertFormatBodyAsync(expected, expected); + await AssertFormatBodyAsync(expected, """ -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(""""); // GooBar - } -}", @"using System; + var y = o is + Point p; + """); -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(""""); // GooBar - } -}", optionSet); - } + await AssertFormatBodyAsync(expected, """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1151")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/961559")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1041787")] - public async Task ReconstructWhitespaceStringUsingTabs_MultiLineComment() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - await AssertFormatAsync(@"using System; + var y = o is + Point p ; + """); -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(""""); /* GooBar */ - } -}", @"using System; + await AssertFormatBodyAsync(expected, """ -class Program -{ - static void Main(string[] args) - { - Console.WriteLine(""""); /* GooBar */ + var y = o is + Point p ; + """); } -}", optionSet); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1100920")] - public async Task NoLineOperationAroundInterpolationSyntax() - { - await AssertFormatAsync(@"class Program -{ - static string F(int a, int b, int c) + [Fact] + public async Task CasePatternDeclarationSimple() { - return $""{a} (index: 0x{b}, size: {c}): "" - } -}", @"class Program -{ - static string F(int a, int b, int c) - { - return $""{a} (index: 0x{ b}, size: { c}): "" - } -}"); - } + var expected = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/62")] - public async Task SpaceAfterWhenInExceptionFilter() - { - const string expected = @"class C -{ - void M() - { - try - { - if (x) + switch (o) { - G(); + case Point p: } - } - catch (Exception e) when (H(e)) - { + """; - } - } -}"; + await AssertFormatBodyAsync(expected, expected); + await AssertFormatBodyAsync(expected, """ - const string code = @"class C -{ - void M() - { - try - { - if(x){ - G(); + switch (o) + { + case Point p : } - } - catch(Exception e) when (H(e)) - { - - } - } -}"; - await AssertFormatAsync(expected, code); - } - - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] - [WorkItem("https://github.com/dotnet/roslyn/issues/285")] - public async Task FormatHashInBadDirectiveToZeroColumnAnywhereInsideIfDef() - { - const string code = @"class MyClass -{ - static void Main(string[] args) - { -#if false + """); - # + await AssertFormatBodyAsync(expected, """ -#endif + switch (o) + { + case Point p : + } + """); } -}"; - const string expected = @"class MyClass -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23703")] + public async Task FormatNullableArray() { -#if false - -# + var code = """ -#endif + class C + { + object[]? F = null; + } + """; + await AssertFormatAsync(code, code); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] - [WorkItem("https://github.com/dotnet/roslyn/issues/285")] - public async Task FormatHashElseToZeroColumnAnywhereInsideIfDef() - { - const string code = @"class MyClass -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23703")] + public async Task FormatConditionalWithArrayAccess() { -#if false + var code = """ - #else - Appropriate indentation should be here though # -#endif + class C + { + void M() + { + _ = array[1] ? 2 : 3; + } + } + """; + await AssertFormatAsync(code, code); } -}"; - const string expected = @"class MyClass -{ - static void Main(string[] args) + private Task AssertFormatBodyAsync(string expected, string input) { -#if false + static string transform(string s) + { + var lines = s.Split([Environment.NewLine], StringSplitOptions.None); + for (var i = 0; i < lines.Length; i++) + { + if (!string.IsNullOrEmpty(lines[i])) + { + lines[i] = new string(' ', count: 8) + lines[i]; + } + } -#else - Appropriate indentation should be here though # -#endif - } -}"; - await AssertFormatAsync(expected, code); + return string.Join(Environment.NewLine, lines); } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1089196")] - [WorkItem("https://github.com/dotnet/roslyn/issues/285")] - public async Task FormatHashsToZeroColumnAnywhereInsideIfDef() - { - const string code = @"class MyClass -{ - static void Main(string[] args) - { -#if false + var pattern = """ - #else - # + class C + {{ + void M() + {{ + {0} + }} + }} + """; -#endif + expected = string.Format(pattern, transform(expected)); + input = string.Format(pattern, transform(input)); + return AssertFormatAsync(expected, input); } -}"; - const string expected = @"class MyClass -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] + public async Task FormatElseBlockBracesOnDifferentLineToNewLines() { -#if false - -#else -# - -#endif - } -}"; - await AssertFormatAsync(expected, code); - } + await AssertFormatAsync(""" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1118")] - public void DoNotAssumeCertainNodeAreAlwaysParented() - { - var block = SyntaxFactory.Block(); - Formatter.Format(block, new AdhocWorkspace().Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); - } + class C + { + public void M() + { + if (true) + { + } + else + { + } + } + } + """, """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/776")] - public async Task SpacingRulesAroundMethodCallAndParenthesisAppliedInAttributeNonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { CSharpFormattingOptions2.SpaceAfterMethodCallName, true }, - { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true }, - { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true } - }; - await AssertFormatAsync(@"[Obsolete ( ""Test"" ), Obsolete ( )] -class Program -{ - static void Main(string[] args) - { - } -}", @"[Obsolete(""Test""), Obsolete()] -class Program -{ - static void Main(string[] args) - { + public void M() + { + if (true) + { + } + else { + } + } + } + """); } -}", changingOptions); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/776")] - public async Task SpacingRulesAroundMethodCallAndParenthesisAppliedInAttribute() - { - var code = @"[Obsolete(""Test""), Obsolete()] -class Program -{ - static void Main(string[] args) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] + public async Task FormatOnElseBlockBracesOnSameLineRemainsInSameLine_1() { + var code = """ + + class C + { + public void M() + { + if (true) + { + } + else { } + } + } + """; + await AssertFormatAsync(code, code); } -}"; - await AssertFormatAsync(code, code); - } - [Fact] - public async Task SpacingInMethodCallArguments_True() - { - const string code = @" -[Bar(A=1,B=2)] -class Program -{ - public void goo() - { - var a = typeof(A); - var b = M(a); - var c = default(A); - var d = sizeof(A); - M(); - } -}"; - const string expected = @" -[Bar ( A = 1, B = 2 )] -class Program -{ - public void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/11572")] + public async Task FormatAttributeOnSameLineAsField() { - var a = typeof ( A ); - var b = M ( a ); - var c = default ( A ); - var d = sizeof ( A ); - M ( ); - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync( + """ + + class C { - { CSharpFormattingOptions2.SpaceWithinMethodCallParentheses, true }, - { CSharpFormattingOptions2.SpaceAfterMethodCallName, true }, - { CSharpFormattingOptions2.SpaceBetweenEmptyMethodCallParentheses, true }, - }; - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } + [Attr] int i; + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1298")] - public async Task DoNotforceAccessorsToNewLineWithPropertyInitializers() - { - var code = @"using System.Collections.Generic; + class C { + [Attr] int i; + } + """); + } -class Program -{ - public List ValidationExcludeFilters { get; } - = new List(); -} + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] + public async Task FormatMultipleAttributeOnSameLineAsField1() + { + await AssertFormatAsync( + """ -public class ExcludeValidation -{ -}"; + class C + { + [Attr1] + [Attr2] + [Attr3][Attr4] int i; + } + """, + """ - var expected = @"using System.Collections.Generic; + class C { + [Attr1] + [Attr2] + [Attr3][Attr4] int i; + } + """); + } -class Program -{ - public List ValidationExcludeFilters { get; } - = new List(); -} + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] + public async Task FormatMultipleAttributesOnSameLineAsField2() + { + await AssertFormatAsync( + """ -public class ExcludeValidation -{ -}"; - await AssertFormatAsync(expected, code); - } + class C + { + [Attr1] + [Attr2] + [Attr3][Attr4] int i; + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/1339")] - public async Task DoNotFormatAutoPropertyInitializerIfNotDifferentLine() - { - var code = @"class Program -{ - public int d { get; } - = 3; - static void Main(string[] args) - { + class C { + [Attr1][Attr2] + [Attr3][Attr4] int i; + } + """); } -}"; - await AssertFormatAsync(code, code); - } - [Fact] - public async Task SpacingForForStatementInfiniteLoop() - { - var code = @" -class Program -{ - void Main() - { - for ( ;;) - { - } - } -}"; - var expected = @" -class Program -{ - void Main() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] + public async Task FormatMultipleAttributeOnSameLineAndFieldOnNewLine() { - for (; ; ) - { - } + await AssertFormatAsync( + """ + + class C + { + [Attr1] + [Attr2] + int i; + } + """, + """ + + class C { + [Attr1][Attr2] + int i; + } + """); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact] - public async Task SpacingForForStatementInfiniteLoopWithNoSpaces() - { - var code = @" -class Program -{ - void Main() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] + public async Task FormatOnElseBlockBracesOnSameLineRemainsInSameLine_2() { - for ( ; ; ) - { - } + var code = """ + + class C + { + public void M() + { + if (true) + { + } + else + { } + } + } + """; + await AssertFormatAsync(code, code); } -}"; - var expected = @" -class Program -{ - void Main() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25098")] + public void FormatSingleStructDeclaration() + => Formatter.Format(SyntaxFactory.StructDeclaration("S"), DefaultWorkspace.Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); + + [Fact] + public async Task FormatIndexExpression() { - for (;;) - { - } - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync(""" + + class C { - { CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, false }, - }; + void M() + { + object x = ^1; + object y = ^1 + } + } + """, """ - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } + class C + { + void M() + { + object x = ^1; + object y = ^1 + } + } + """); + } - [Fact] - public async Task SpacingForForStatementInfiniteLoopWithSpacesBefore() - { - var code = @" -class Program -{ - void Main() + [Fact] + public async Task FormatRangeExpression_NoOperands() { - for (;; ) - { - } + await AssertFormatAsync(""" + + class C + { + void M() + { + object x = ..; + object y = .. + } + } + """, """ + + class C + { + void M() + { + object x = ..; + object y = .. + } + } + """); } -}"; - var expected = @" -class Program -{ - void Main() + + [Fact] + public async Task FormatRangeExpression_RightOperand() { - for ( ; ;) - { - } - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync(""" + + class C { - { CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, true }, - { CSharpFormattingOptions2.SpaceAfterSemicolonsInForStatement, false }, - }; + void M() + { + object x = ..1; + object y = ..1 + } + } + """, """ - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } + class C + { + void M() + { + object x = ..1; + object y = ..1 + } + } + """); + } - [Fact] - public async Task SpacingForForStatementInfiniteLoopWithSpacesBeforeAndAfter() - { - var code = @" -class Program -{ - void Main() + [Fact] + public async Task FormatRangeExpression_LeftOperand() { - for (;;) - { - } + await AssertFormatAsync(""" + + class C + { + void M() + { + object x = 1..; + object y = 1.. + } + } + """, """ + + class C + { + void M() + { + object x = 1..; + object y = 1.. + } + } + """); } -}"; - var expected = @" -class Program -{ - void Main() + + [Fact] + public async Task FormatRangeExpression_BothOperands() { - for ( ; ; ) - { - } - } -}"; - var optionSet = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync(""" + + class C { - { CSharpFormattingOptions2.SpaceBeforeSemicolonsInForStatement, true }, - }; + void M() + { + object x = 1..2; + object y = 1..2 + } + } + """, """ - await AssertFormatAsync(expected, code, changedOptionSet: optionSet); - } + class C + { + void M() + { + object x = 1..2; + object y = 1..2 + } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4240")] - [WorkItem("https://github.com/dotnet/roslyn/issues/4421")] - public async Task VerifySpacingAfterMethodDeclarationName_Default() - { - var code = @"class Program -{ - public static Program operator + (Program p1, Program p2) { return null; } - public static implicit operator string (Program p) { return null; } - public static void M () { } - public void F () { } -}"; - var expected = @"class Program -{ - public static Program operator +(Program p1, Program p2) { return null; } - public static implicit operator string(Program p) { return null; } - public static void M() { } - public void F() { } -}"; - await AssertFormatAsync(expected, code); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32113")] + public async Task FormatCommaAfterCloseBrace_CommaRemainIntheSameLine() + { + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4421")] - [WorkItem("https://github.com/dotnet/roslyn/issues/4240")] - public async Task VerifySpacingAfterMethodDeclarationName_NonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + public class Test { - { CSharpFormattingOptions2.SpacingAfterMethodDeclarationName, true } - }; - var code = @"class Program -{ - public static Program operator + (Program p1, Program p2) { return null; } - public static implicit operator string (Program p) { return null; } - public static void M () { } - public void F () { } -}"; - var expected = @"class Program -{ - public static Program operator + (Program p1, Program p2) { return null; } - public static implicit operator string (Program p) { return null; } - public static void M () { } - public void F () { } -}"; - await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); - } + public void Foo() + { + (Action, Action, Action) tuple = ( + () => { Console.WriteLine(2.997e8); }, + () => { Console.WriteLine(6.67e-11); }, + () => { Console.WriteLine(1.602e-19); } + ); + } + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/939")] - public async Task DoNotFormatInsideArrayInitializers() - { - var code = @"class Program -{ - static void Main(string[] args) - { - int[] sss = new[] { - //Comment1 - 2, - 5, 324534, 345345, - //Comment2 - //This comment should not line up with the previous comment - 234234 - //Comment3 - , 234, - 234234 - /* - This is a multiline comment - */ - //Comment4 - }; - } -}"; - await AssertFormatAsync(code, code); - } + public class Test + { + public void Foo() + { + (Action, Action, Action) tuple = ( + () => { Console.WriteLine(2.997e8); }, + () => { Console.WriteLine(6.67e-11); }, + () => { Console.WriteLine(1.602e-19); } + ); + } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4280")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1184285")] - public async Task FormatDictionaryInitializers() - { - var code = @"class Program -{ - void Main() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32113")] + public async Task FormatCommaAfterCloseBrace_SpaceSurroundWillBeRemoved() { - var sample = new Dictionary {[""x""] = ""d"" ,[""z""] = ""XX"" }; + await AssertFormatAsync( + """ + + public class Test + { + public void Foo() + { + (Action, Action, Action) tuple = ( + () => { Console.WriteLine(2.997e8); }, + () => { Console.WriteLine(6.67e-11); }, + () => { Console.WriteLine(1.602e-19); } + ); + } + } + """, + """ + + public class Test + { + public void Foo() + { + (Action, Action, Action) tuple = ( + () => { Console.WriteLine(2.997e8); } , + () => { Console.WriteLine(6.67e-11); } , + () => { Console.WriteLine(1.602e-19); } + ); + } + } + """); } -}"; - var expected = @"class Program -{ - void Main() + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31571")] + [WorkItem("https://github.com/dotnet/roslyn/issues/33910")] + [CombinatorialData] + public async Task ConversionOperator_CorrectlySpaceArgumentList( + [CombinatorialValues("implicit", "explicit")] string operatorType, + [CombinatorialValues("string", "string[]", "System.Action", "int?", "int*", "(int, int)")] string targetType, + bool spacingAfterMethodDeclarationName) { - var sample = new Dictionary { [""x""] = ""d"", [""z""] = ""XX"" }; + var expectedSpacing = spacingAfterMethodDeclarationName ? " " : ""; + var initialSpacing = spacingAfterMethodDeclarationName ? "" : " "; + var changedOptionSet = new OptionsCollection(LanguageNames.CSharp) { { SpacingAfterMethodDeclarationName, spacingAfterMethodDeclarationName } }; + await AssertFormatAsync( + $$""" + + public unsafe class Test + { + public static {{operatorType}} operator {{targetType}}{{expectedSpacing}}() => throw null; + } + """, + $$""" + + public unsafe class Test + { + public static {{operatorType}} operator {{targetType}}{{initialSpacing}}() => throw null; + } + """, + changedOptionSet: changedOptionSet); } -}"; - await AssertFormatAsync(expected, code); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3256")] - public async Task SwitchSectionHonorsNewLineForBracesinControlBlockOption_Default() - { - var code = @"class Program -{ - public void goo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31868")] + public async Task SpaceAroundDeclaration() { - int f = 1; - switch (f) { - case 1: { - // DO nothing - break; + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } + }; + await AssertFormatAsync( + """ + + class Program + { + public void FixMyType() + { + var myint = 0; } - } + } + """, + """ + + class Program + { + public void FixMyType() + { + var myint = 0; + } + } + """, changedOptionSet: changingOptions); } -}"; - var expected = @"class Program -{ - public void goo() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31868")] + public async Task SpaceAroundDeclarationAndPreserveSingleLine() { - int f = 1; - switch (f) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - case 1: + { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true }, + { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } + }; + await AssertFormatAsync( + """ + + class Program + { + public void FixMyType() { - // DO nothing - break; + var myint = 0; } - } - } -}"; - await AssertFormatAsync(expected, code); - } + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/3256")] - public async Task SwitchSectionHonorsNewLineForBracesinControlBlockOption_NonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class Program { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ControlBlocks, false) } - }; - var code = @"class Program -{ - public void goo() - { - int f = 1; - switch (f) - { - case 1: + public void FixMyType() { - // DO nothing - break; + var myint = 0; } - } + } + """, changedOptionSet: changingOptions); } -}"; - var expected = @"class Program -{ - public void goo() + [Fact] + public async Task ClassConstraint() { - int f = 1; - switch (f) { - case 1: { - // DO nothing - break; - } - } - } -}"; - await AssertFormatAsync(expected, code, changedOptionSet: changingOptions); - } + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] - public async Task FormattingCodeWithMissingTokensShouldRespectFormatTabsOption1() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + class Program + where T : class? + { + } + """, + """ - await AssertFormatAsync(@"class Program -{ - static void Main() - { - return // Note the missing semicolon - } // The tab here should stay a tab -}", @"class Program -{ - static void Main() - { - return // Note the missing semicolon - } // The tab here should stay a tab -}", changedOptionSet: optionSet); - } + class Program + where T : class ? + { + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] - public async Task FormattingCodeWithMissingTokensShouldRespectFormatTabsOption2() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + [Fact] + public async Task SingleLinePropertyPattern1() + { + await AssertFormatAsync( + """ - await AssertFormatAsync(@"struct Goo -{ - private readonly string bar; + using System.Collections.Generic; + class Program + { + public void FixMyType() + { + _ = new List() is + { + Count: { }, + }; + } + } + """, + """ - public Goo(readonly string bar) - { - } -}", @"struct Goo -{ - private readonly string bar; + using System.Collections.Generic; + class Program + { + public void FixMyType() + { + _ = new List() is + { + Count:{}, + }; + } + } + """); + } - public Goo(readonly string bar) - { - } -}", changedOptionSet: optionSet); - } + [Fact] + public async Task SingleLinePropertyPattern2() + { + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] - public async Task FormattingCodeWithBrokenLocalDeclarationShouldRespectFormatTabsOption() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + using System.Collections.Generic; + class Program + { + public void FixMyType(object o) + { + _ = o is List { Count: { } }; + } + } + """, + """ - await AssertFormatAsync(@"class AClass -{ - void AMethod(Object anArgument) - { - if (anArgument == null) - { - throw new ArgumentNullException(nameof(anArgument)); - } - anArgument - - DoSomething(); - } - - void DoSomething() - { - } -}", @"class AClass -{ - void AMethod(Object anArgument) - { - if (anArgument == null) - { - throw new ArgumentNullException(nameof(anArgument)); - }anArgument - - DoSomething(); - } - - void DoSomething() - { - } -}", changedOptionSet: optionSet); - } + using System.Collections.Generic; + class Program + { + public void FixMyType(object o) + { + _ = o is List{Count:{}}; + } + } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4014")] - public async Task FormattingCodeWithBrokenInterpolatedStringShouldRespectFormatTabsOption() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37030")] + public async Task SpaceAroundEnumMemberDeclarationIgnored() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } + }; + await AssertFormatAsync( + """ - await AssertFormatAsync(@"class AClass -{ - void Main() - { - Test($""\""_{\""""); - Console.WriteLine(args); - } -}", @"class AClass -{ - void Main() - { - Test($""\""_{\""""); - Console.WriteLine(args); - } -}", changedOptionSet: optionSet); - } + enum TestEnum + { + Short = 1, + LongItemName = 2 + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/84")] - [WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] - public async Task NewLinesForBracesInPropertiesTest() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + enum TestEnum { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.Properties, false) }, - }; - await AssertFormatAsync(@"class Class2 -{ - int Goo { - get - { - return 1; - } + Short = 1, + LongItemName = 2 + } + """, changedOptionSet: changingOptions); } - int MethodGoo() - { - return 42; - } -}", @"class Class2 -{ - int Goo + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37030")] + public async Task SpaceAroundEnumMemberDeclarationSingle() { - get + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - return 1; - } - } + { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, false } + }; + await AssertFormatAsync( + """ - int MethodGoo() - { - return 42; - } -}", changingOptions); - } + enum TestEnum + { + Short = 1, + LongItemName = 2 + } + """, + """ - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] - [WorkItem("https://github.com/dotnet/roslyn/issues/84")] - public async Task NewLinesForBracesInAccessorsTest() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + enum TestEnum { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.Accessors, false) }, - }; - await AssertFormatAsync(@"class Class2 -{ - int Goo - { - get { - return 1; - } + Short = 1, + LongItemName = 2 + } + """, changedOptionSet: changingOptions); } - int MethodGoo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38895")] + public async Task FormattingNbsp() { - return 42; + await AssertFormatAsync( + """ + + class C + { + List list = new List + { + new C() + }; + } + """, + """ + + class C + { + List list = new List + { +             new C() + }; + } + """.Replace(" ", "\u00A0")); } -}", @"class Class2 -{ - int Goo + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] + public async Task IndentationForMultilineWith() { - get - { - return 1; - } + var code = """ + record C(int X) + { + C M() + { + return this with + { + X = 1 + }; + } + } + """; + var expectedCode = """ + record C(int X) + { + C M() + { + return this with + { + X = 1 + }; + } + } + """; + + await AssertFormatAsync(expectedCode, code); } - int MethodGoo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] + public async Task IndentationForMultilineWith_ArrowBody() { - return 42; + var code = """ + record C(int X) + { + C M() + => this with + { + X = 1 + }; + } + """; + var expectedCode = """ + record C(int X) + { + C M() + => this with + { + X = 1 + }; + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", changingOptions); - } - [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/849870")] - [WorkItem("https://github.com/dotnet/roslyn/issues/84")] - public async Task NewLinesForBracesInPropertiesAndAccessorsTest() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] + public async Task IndentationForMultilineWith_ArrowBody_WithTrailingComma() + { + var code = """ + record C(int X) + { + C M() + => this with { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue - .WithFlagValue(NewLineBeforeOpenBracePlacement.Properties, false) - .WithFlagValue(NewLineBeforeOpenBracePlacement.Accessors, false)}, + X = 1, }; - await AssertFormatAsync(@"class Class2 -{ - int Goo { - get { - return 1; - } + } + """; + var expectedCode = """ + record C(int X) + { + C M() + => this with + { + X = 1, + }; + } + """; + + await AssertFormatAsync(expectedCode, code); } - int MethodGoo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] + public async Task SpacingAfterAttribute() { - return 42; + var code = """ + class C + { + void M([My]string?[]?[] x) + { + } + } + """; + var expectedCode = """ + class C + { + void M([My] string?[]?[] x) + { + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", @"class Class2 -{ - int Goo + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] + public async Task SpacingAfterAttribute_Multiple() { - get - { - return 1; - } + var code = """ + class C + { + void M([My][My] int x) + { + } + } + """; + var expectedCode = """ + class C + { + void M([My][My] int x) + { + } + } + """; + + await AssertFormatAsync(expectedCode, code); } - int MethodGoo() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] + public async Task SpacingAfterAttribute_Multiple2() { - return 42; + var code = """ + class C + { + void M([My] [My] int x) + { + } + } + """; + var expectedCode = """ + class C + { + void M([My][My] int x) + { + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -}", changingOptions); - } - [Fact, WorkItem(111079, "devdiv.visualstudio.com")] - public async Task TestThrowInIfOnSingleLine() - { - var code = @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] + public async Task SpacingAfterAttribute_MultipleOnDeclaration() { - if (true) throw new Exception( - ""message""); + var code = """ + class C + { + [My] [My] void M() + { + } + } + """; + var expectedCode = """ + class C + { + [My] + [My] + void M() + { + } + } + """; + + await AssertFormatAsync(expectedCode, code); } -} -"; - await AssertFormatAsync(code, code); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47442")] + public async Task IndentImplicitObjectCreationInitializer() + { + var code = """ - [Fact, WorkItem("https://connect.microsoft.com/VisualStudio/feedback/details/1711675/autoformatting-issues")] - public async Task SingleLinePropertiesPreservedWithLeaveStatementsAndMembersOnSingleLineFalse() - { - var changedOptionSet = new OptionsCollection(LanguageNames.CSharp) + class C { - { CSharpFormattingOptions2.WrappingPreserveSingleLine, true }, - { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false}, - }; + public string Name { get; set; } + public static C Create1(string name) + => new C() + { + Name = name + }; + public static C Create2(string name) + => new() + { + Name = name + }; + } + """; + var expectedCode = """ - await AssertFormatAsync(@" -class C -{ - string Name { get; set; } -}", @" -class C -{ - string Name { get ; set ; } -}", changedOptionSet: changedOptionSet); - } + class C + { + public string Name { get; set; } + public static C Create1(string name) + => new C() + { + Name = name + }; + public static C Create2(string name) + => new() + { + Name = name + }; + } + """; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4720")] - public async Task KeepAccessorWithAttributeOnSingleLine() - { - await AssertFormatAsync(@" -class Program -{ - public Int32 PaymentMethodID - { - [System.Diagnostics.DebuggerStepThrough] - get { return 10; } - } -}", @" -class Program -{ - public Int32 PaymentMethodID - { - [System.Diagnostics.DebuggerStepThrough] - get { return 10; } + await AssertFormatAsync(expectedCode, code); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] - public async Task KeepConstructorBodyInSameLineAsBaseConstructorInitializer() - { - var code = @" -class C -{ - public C(int s) - : base() { } - public C() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36913")] + public async Task NewLinesForBraces_SwitchExpression_Default() { - } -}"; - await AssertFormatAsync(code, code); - } + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] - public async Task KeepConstructorBodyInSameLineAsThisConstructorInitializer() - { - var code = @" -class C -{ - public C(int s) - : this() { } - public C() - { + class A + { + void br() + { + var msg = 1 switch + { + _ => null + }; + } + } + """, + """ + + class A + { + void br() + { + var msg = 1 switch { + _ => null + }; + } + } + """); } -}"; - await AssertFormatAsync(code, code); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6905")] - public async Task KeepConstructorBodyInSameLineAsThisConstructorInitializerAdjustSpace() - { - await AssertFormatAsync(@" -class C -{ - public C(int s) - : this() { } - public C() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36913")] + public async Task NewLinesForBraces_SwitchExpression_NonDefault() { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) + { + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, + }; + await AssertFormatAsync( + """ + + class A + { + void br() + { + var msg = 1 switch { + _ => null + }; + } + } + """, + """ + + class A + { + void br() + { + var msg = 1 switch + { + _ => null + }; + } + } + """, changedOptionSet: changingOptions); } -}", @" -class C -{ - public C(int s) - : this() { } - public C() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/discussions/49725")] + public async Task NewLinesForBraces_RecordWithInitializer_Default() { + await AssertFormatAsync( + """ + + record R(int X); + class C + { + void Goo(R r) + { + var r2 = r with + { + X = 0 + }; + } + } + """, + """ + + record R(int X); + class C + { + void Goo(R r) + { + var r2 = r with { + X = 0 + }; + } + } + """); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/4720")] - public async Task OneSpaceBetweenAccessorsAndAttributes() + [Fact, WorkItem("https://github.com/dotnet/roslyn/discussions/49725")] + public async Task NewLinesForBraces_RecordWithInitializer_NonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - await AssertFormatAsync(@" -class Program -{ - public int SomeProperty { [SomeAttribute] get; [SomeAttribute] private set; } -}", @" -class Program -{ - public int SomeProperty { [SomeAttribute] get; [SomeAttribute] private set; } -}"); - } + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, + }; + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7900")] - public async Task FormatEmbeddedStatementInsideLockStatement() - { - await AssertFormatAsync(@" -class C -{ - private object _l = new object(); - public void M() - { - lock (_l) Console.WriteLine(""d""); + record R(int X); + class C + { + void Goo(R r) + { + var r2 = r with { + X = 0 + }; + } + } + """, + """ + + record R(int X); + class C + { + void Goo(R r) + { + var r2 = r with + { + X = 0 + }; + } + } + """, changedOptionSet: changingOptions); } -}", @" -class C -{ - private object _l = new object(); - public void M() + + [Fact] + public async Task NoSpacesInPropertyPatterns() { - lock (_l) Console.WriteLine(""d""); + var code = """ + class C + { + int IntProperty { get; set; } + void M() + { + _ = this is { IntProperty : 2 }; + } + } + """; + var expectedCode = """ + class C + { + int IntProperty { get; set; } + void M() + { + _ = this is { IntProperty: 2 }; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/7900")] - public async Task FormatEmbeddedStatementInsideLockStatementDifferentLine() - { - await AssertFormatAsync(@" -class C -{ - private object _l = new object(); - public void M() + [Fact] + public async Task NoSpacesInExtendedPropertyPatterns() { - lock (_l) - Console.WriteLine(""d""); + var code = """ + class C + { + C CProperty { get; set; } + int IntProperty { get; set; } + void M() + { + _ = this is { CProperty . IntProperty : 2 }; + } + } + """; + var expectedCode = """ + class C + { + C CProperty { get; set; } + int IntProperty { get; set; } + void M() + { + _ = this is { CProperty.IntProperty: 2 }; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", @" -class C -{ - private object _l = new object(); - public void M() + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52413")] + public async Task NewLinesForBraces_PropertyPatternClauses_Default() { - lock (_l) - Console.WriteLine(""d""); - } -}"); - } + await AssertFormatAsync( + """ + + class A + { + public string Name { get; } + + public bool IsFoo(A a) + { + return a is + { + Name: "foo", + }; + } + } + """, + """ - [Fact] - public async Task PropertyDeclarationSimple() - { - var expected = @"if (o is Point p)"; - await AssertFormatBodyAsync(expected, expected); - await AssertFormatBodyAsync(expected, @"if (o is Point p)"); - await AssertFormatBodyAsync(expected, @"if (o is Point p )"); - } + class A + { + public string Name { get; } - [Fact] - public async Task PropertyDeclarationTypeOnNewLine() - { - var expected = @" -var y = o is -Point p;"; - await AssertFormatBodyAsync(expected, expected); - await AssertFormatBodyAsync(expected, @" -var y = o is -Point p; "); - - await AssertFormatBodyAsync(expected, @" -var y = o is -Point p ;"); - - await AssertFormatBodyAsync(expected, @" -var y = o is -Point p ;"); - } + public bool IsFoo(A a) + { + return a is { + Name: "foo", + }; + } + } + """); + } - [Fact] - public async Task CasePatternDeclarationSimple() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52413")] + public async Task NewLinesForBraces_PropertyPatternClauses_NonDefault() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - var expected = @" -switch (o) -{ - case Point p: -}"; + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, + }; + await AssertFormatAsync( + """ - await AssertFormatBodyAsync(expected, expected); - await AssertFormatBodyAsync(expected, @" -switch (o) -{ - case Point p : -}"); + class A + { + public string Name { get; } - await AssertFormatBodyAsync(expected, @" -switch (o) -{ - case Point p : -}"); - } + public bool IsFoo(A a) + { + return a is { + Name: "foo", + }; + } + } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23703")] - public async Task FormatNullableArray() - { - var code = @" -class C -{ - object[]? F = null; -}"; - await AssertFormatAsync(code, code); - } + class A + { + public string Name { get; } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/23703")] - public async Task FormatConditionalWithArrayAccess() - { - var code = @" -class C -{ - void M() - { - _ = array[1] ? 2 : 3; + public bool IsFoo(A a) + { + return a is + { + Name: "foo", + }; + } + } + """, changedOptionSet: changingOptions); } -}"; - await AssertFormatAsync(code, code); - } - private Task AssertFormatBodyAsync(string expected, string input) + [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] + [WorkItem(57854, "https://github.com/dotnet/roslyn/issues/57854")] + public async Task NewLinesForBraces_PropertyPatternClauses_NonDefaultInSwitchExpression() + { + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - static string transform(string s) + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, + }; + await AssertFormatAsync( + """ + + class A { - var lines = s.Split([Environment.NewLine], StringSplitOptions.None); - for (var i = 0; i < lines.Length; i++) + public string Name { get; } + + public bool IsFoo(A a) { - if (!string.IsNullOrEmpty(lines[i])) - { - lines[i] = new string(' ', count: 8) + lines[i]; - } + return a switch { + { Name: "foo" } => true, + _ => false, + }; } - - return string.Join(Environment.NewLine, lines); } + """, + """ - var pattern = @" -class C -{{ - void M() - {{ -{0} - }} -}}"; - - expected = string.Format(pattern, transform(expected)); - input = string.Format(pattern, transform(input)); - return AssertFormatAsync(expected, input); - } + class A + { + public string Name { get; } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] - public async Task FormatElseBlockBracesOnDifferentLineToNewLines() - { - await AssertFormatAsync(@" -class C -{ - public void M() - { - if (true) - { - } - else - { - } + public bool IsFoo(A a) + { + return a switch + { + { Name: "foo" } => true, + _ => false, + }; + } + } + """, changedOptionSet: changingOptions); } -}", @" -class C -{ - public void M() + + [Theory, CombinatorialData] + [WorkItem("https://github.com/dotnet/roslyn/issues/52413")] + public async Task NewLinesForBraces_PropertyPatternClauses_SingleLine(bool option) { - if (true) + var changingOptions = new OptionsCollection(LanguageNames.CSharp) { - } - else { - } + { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, option) }, + }; + var code = """ + + class A + { + public string Name { get; } + + public bool IsFoo(A a) + { + return a is { Name: "foo" }; + } + } + """; + await AssertFormatAsync(code, code, changedOptionSet: changingOptions); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] - public async Task FormatOnElseBlockBracesOnSameLineRemainsInSameLine_1() - { - var code = @" -class C -{ - public void M() + [Fact] + public async Task RecordClass() { - if (true) - { - } - else { } - } -}"; - await AssertFormatAsync(code, code); - } + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/11572")] - public async Task FormatAttributeOnSameLineAsField() - { - await AssertFormatAsync( -@" -class C -{ - [Attr] int i; -}", -@" -class C { - [Attr] int i; -}"); - } + record class R(int X); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] - public async Task FormatMultipleAttributeOnSameLineAsField1() - { - await AssertFormatAsync( -@" -class C -{ - [Attr1] - [Attr2] - [Attr3][Attr4] int i; -}", -@" -class C { - [Attr1] - [Attr2] - [Attr3][Attr4] int i; -}"); - } + """, + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] - public async Task FormatMultipleAttributesOnSameLineAsField2() - { - await AssertFormatAsync( -@" -class C -{ - [Attr1] - [Attr2] - [Attr3][Attr4] int i; -}", -@" -class C { - [Attr1][Attr2] - [Attr3][Attr4] int i; -}"); - } + record class R(int X); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/21789")] - public async Task FormatMultipleAttributeOnSameLineAndFieldOnNewLine() - { - await AssertFormatAsync( -@" -class C -{ - [Attr1] - [Attr2] - int i; -}", -@" -class C { - [Attr1][Attr2] - int i; -}"); - } + """); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/6628")] - public async Task FormatOnElseBlockBracesOnSameLineRemainsInSameLine_2() - { - var code = @" -class C -{ - public void M() + [Fact] + public async Task Class() { - if (true) - { - } - else - { } - } -}"; - await AssertFormatAsync(code, code); - } + await AssertFormatAsync( + """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/25098")] - public void FormatSingleStructDeclaration() - => Formatter.Format(SyntaxFactory.StructDeclaration("S"), DefaultWorkspace.Services.SolutionServices, CSharpSyntaxFormattingOptions.Default, CancellationToken.None); + class R(int X); - [Fact] - public async Task FormatIndexExpression() - { - await AssertFormatAsync(@" -class C -{ - void M() - { - object x = ^1; - object y = ^1 - } -}", @" -class C -{ - void M() - { - object x = ^1; - object y = ^1 - } -}"); - } + """, + """ - [Fact] - public async Task FormatRangeExpression_NoOperands() - { - await AssertFormatAsync(@" -class C -{ - void M() - { - object x = ..; - object y = .. - } -}", @" -class C -{ - void M() - { - object x = ..; - object y = .. - } -}"); - } + class R(int X) ; - [Fact] - public async Task FormatRangeExpression_RightOperand() - { - await AssertFormatAsync(@" -class C -{ - void M() - { - object x = ..1; - object y = ..1 - } -}", @" -class C -{ - void M() - { - object x = ..1; - object y = ..1 + """); } -}"); - } - [Fact] - public async Task FormatRangeExpression_LeftOperand() - { - await AssertFormatAsync(@" -class C -{ - void M() - { - object x = 1..; - object y = 1.. - } -}", @" -class C -{ - void M() + [Fact] + public async Task Interface() { - object x = 1..; - object y = 1.. - } -}"); - } + await AssertFormatAsync( + """ - [Fact] - public async Task FormatRangeExpression_BothOperands() - { - await AssertFormatAsync(@" -class C -{ - void M() - { - object x = 1..2; - object y = 1..2 - } -}", @" -class C -{ - void M() - { - object x = 1..2; - object y = 1..2 - } -}"); - } + interface R(int X); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32113")] - public async Task FormatCommaAfterCloseBrace_CommaRemainIntheSameLine() - { - await AssertFormatAsync( - @" -public class Test -{ - public void Foo() - { - (Action, Action, Action) tuple = ( - () => { Console.WriteLine(2.997e8); }, - () => { Console.WriteLine(6.67e-11); }, - () => { Console.WriteLine(1.602e-19); } - ); - } -}", - @" -public class Test -{ - public void Foo() - { - (Action, Action, Action) tuple = ( - () => { Console.WriteLine(2.997e8); }, - () => { Console.WriteLine(6.67e-11); }, - () => { Console.WriteLine(1.602e-19); } - ); + """, + """ + + interface R(int X) ; + + """); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/32113")] - public async Task FormatCommaAfterCloseBrace_SpaceSurroundWillBeRemoved() - { - await AssertFormatAsync( - @" -public class Test -{ - public void Foo() + [Fact] + public async Task RecordStruct() { - (Action, Action, Action) tuple = ( - () => { Console.WriteLine(2.997e8); }, - () => { Console.WriteLine(6.67e-11); }, - () => { Console.WriteLine(1.602e-19); } - ); + await AssertFormatAsync( + """ + + record struct R(int X); + + """, + """ + + record struct R(int X); + + """); } -}", - @" -public class Test -{ - public void Foo() + + [Fact] + public async Task Struct() { - (Action, Action, Action) tuple = ( - () => { Console.WriteLine(2.997e8); } , - () => { Console.WriteLine(6.67e-11); } , - () => { Console.WriteLine(1.602e-19); } - ); - } -}"); - } + await AssertFormatAsync( + """ - [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/31571")] - [WorkItem("https://github.com/dotnet/roslyn/issues/33910")] - [CombinatorialData] - public async Task ConversionOperator_CorrectlySpaceArgumentList( - [CombinatorialValues("implicit", "explicit")] string operatorType, - [CombinatorialValues("string", "string[]", "System.Action", "int?", "int*", "(int, int)")] string targetType, - bool spacingAfterMethodDeclarationName) - { - var expectedSpacing = spacingAfterMethodDeclarationName ? " " : ""; - var initialSpacing = spacingAfterMethodDeclarationName ? "" : " "; - var changedOptionSet = new OptionsCollection(LanguageNames.CSharp) { { SpacingAfterMethodDeclarationName, spacingAfterMethodDeclarationName } }; - await AssertFormatAsync( - $@" -public unsafe class Test -{{ - public static {operatorType} operator {targetType}{expectedSpacing}() => throw null; -}}", - $@" -public unsafe class Test -{{ - public static {operatorType} operator {targetType}{initialSpacing}() => throw null; -}}", - changedOptionSet: changedOptionSet); - } + struct R(int X); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31868")] - public async Task SpaceAroundDeclaration() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } - }; - await AssertFormatAsync( - @" -class Program -{ - public void FixMyType() - { - var myint = 0; + """, + """ + + struct R(int X) ; + + """); } -}", - @" -class Program -{ - public void FixMyType() + + [Fact] + public async Task FormatListPattern() { - var myint = 0; - } -}", changedOptionSet: changingOptions); - } + var code = """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31868")] - public async Task SpaceAroundDeclarationAndPreserveSingleLine() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true }, - { CSharpFormattingOptions2.WrappingKeepStatementsOnSingleLine, false } - }; - await AssertFormatAsync( - @" -class Program -{ - public void FixMyType() - { - var myint = 0; - } -}", - @" -class Program -{ - public void FixMyType() - { - var myint = 0; - } -}", changedOptionSet: changingOptions); - } + void M() { + _ = this is[1,2,>=3]; + } + } + """; + await AssertFormatAsync(code: code, expected: """ - [Fact] - public async Task ClassConstraint() - { - await AssertFormatAsync( - @" -class Program - where T : class? -{ -}", - @" -class Program - where T : class ? -{ -}"); - } + class C + { + void M() + { + _ = this is [1, 2, >= 3]; + } + } + """); - [Fact] - public async Task SingleLinePropertyPattern1() - { - await AssertFormatAsync( - @" -using System.Collections.Generic; -class Program -{ - public void FixMyType() - { - _ = new List() is - { - Count: { }, - }; - } -}", - @" -using System.Collections.Generic; -class Program -{ - public void FixMyType() - { - _ = new List() is + var options = new OptionsCollection(LanguageNames.CSharp) { - Count:{}, + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, }; - } -}"); - } - [Fact] - public async Task SingleLinePropertyPattern2() - { - await AssertFormatAsync( - @" -using System.Collections.Generic; -class Program -{ - public void FixMyType(object o) - { - _ = o is List { Count: { } }; - } -}", - @" -using System.Collections.Generic; -class Program -{ - public void FixMyType(object o) - { - _ = o is List{Count:{}}; - } -}"); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37030")] - public async Task SpaceAroundEnumMemberDeclarationIgnored() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, true } - }; - await AssertFormatAsync( - @" -enum TestEnum -{ - Short = 1, - LongItemName = 2 -}", - @" -enum TestEnum -{ - Short = 1, - LongItemName = 2 -}", changedOptionSet: changingOptions); - } + void M() + { + _ = this is [1,2,>= 3]; + } + } + """); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37030")] - public async Task SpaceAroundEnumMemberDeclarationSingle() + options = new OptionsCollection(LanguageNames.CSharp) { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { CSharpFormattingOptions2.SpacesIgnoreAroundVariableDeclaration, false } - }; - await AssertFormatAsync( - @" -enum TestEnum -{ - Short = 1, - LongItemName = 2 -}", - @" -enum TestEnum -{ - Short = 1, - LongItemName = 2 -}", changedOptionSet: changingOptions); - } + { SpaceBeforeOpenSquareBracket, false }, // ignored + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, + }; - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38895")] - public async Task FormattingNbsp() - { - await AssertFormatAsync( - @" -class C -{ - List list = new List - { -new C() - }; -}", - @" -class C -{ - List list = new List - { -            new C() - }; -}".Replace(" ", "\u00A0")); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] - public async Task IndentationForMultilineWith() - { - var code = @"record C(int X) -{ - C M() - { - return this with -{ -X = 1 -}; + class C + { + void M() + { + _ = this is [ 1 , 2 , >= 3 ]; + } + } + """); } -}"; - var expectedCode = @"record C(int X) -{ - C M() + + [Fact] + public async Task FormatListPattern_Parentheses() { - return this with - { - X = 1 - }; - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class C + { + void M((int[], int[]) a) { + _ = a is([1,2,>=3],[1,2]); + } + } + """; + await AssertFormatAsync(code: code, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] - public async Task IndentationForMultilineWith_ArrowBody() - { - var code = @"record C(int X) -{ - C M() - => this with -{ -X = 1 -}; -}"; - var expectedCode = @"record C(int X) -{ - C M() - => this with + class C + { + void M((int[], int[]) a) + { + _ = a is ([1, 2, >= 3], [1, 2]); + } + } + """); + + var options = new OptionsCollection(LanguageNames.CSharp) { - X = 1 + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, }; -}"; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47438")] - public async Task IndentationForMultilineWith_ArrowBody_WithTrailingComma() - { - var code = @"record C(int X) -{ - C M() - => this with -{ -X = 1, -}; -}"; - var expectedCode = @"record C(int X) -{ - C M() - => this with + class C + { + void M((int[], int[]) a) + { + _ = a is ([1,2,>= 3],[1,2]); + } + } + """); + + options = new OptionsCollection(LanguageNames.CSharp) { - X = 1, + { SpaceBeforeOpenSquareBracket, false }, // ignored + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, }; -}"; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] - public async Task SpacingAfterAttribute() - { - var code = @"class C -{ - void M([My]string?[]?[] x) - { + class C + { + void M((int[ ], int[ ]) a) + { + _ = a is ([ 1 , 2 , >= 3 ], [ 1 , 2 ]); + } + } + """); } -}"; - var expectedCode = @"class C -{ - void M([My] string?[]?[] x) + + [Fact] + public async Task FormatListPattern_TrailingComma() { - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class C + { + void M() { + _ = this is[1,2,>=3,]; + } + } + """; + await AssertFormatAsync(code: code, expected: """ + + class C + { + void M() + { + _ = this is [1, 2, >= 3,]; + } + } + """); - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] - public async Task SpacingAfterAttribute_Multiple() + var options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class C -{ - void M([My][My] int x) - { - } -}"; - var expectedCode = @"class C -{ - void M([My][My] int x) - { - } -}"; + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, + }; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] - public async Task SpacingAfterAttribute_Multiple2() + class C + { + void M() + { + _ = this is [1,2,>= 3,]; + } + } + """); + + options = new OptionsCollection(LanguageNames.CSharp) { - var code = @"class C -{ - void M([My] [My] int x) - { - } -}"; - var expectedCode = @"class C -{ - void M([My][My] int x) - { - } -}"; + { SpaceBeforeOpenSquareBracket, false }, // ignored + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, + }; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/41022")] - public async Task SpacingAfterAttribute_MultipleOnDeclaration() - { - var code = @"class C -{ - [My] [My] void M() - { + class C + { + void M() + { + _ = this is [ 1 , 2 , >= 3 , ]; + } + } + """); } -}"; - var expectedCode = @"class C -{ - [My] - [My] - void M() + + [Fact] + public async Task FormatListPattern_WithNewline() { - } -}"; + var code = """ - await AssertFormatAsync(expectedCode, code); - } + class C + { + void M() { + _ = this is + [1,2,>=3 + ]; + } + } + """; + await AssertFormatAsync(code: code, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/47442")] - public async Task IndentImplicitObjectCreationInitializer() - { - var code = @" -class C -{ - public string Name { get; set; } - public static C Create1(string name) - => new C() - { - Name = name - }; - public static C Create2(string name) - => new() - { - Name = name - }; -}"; - var expectedCode = @" -class C -{ - public string Name { get; set; } - public static C Create1(string name) - => new C() - { - Name = name - }; - public static C Create2(string name) - => new() + class C + { + void M() + { + _ = this is + [1, 2, >= 3 + ]; + } + } + """); + + var options = new OptionsCollection(LanguageNames.CSharp) { - Name = name + { SpaceBetweenEmptySquareBrackets, false }, + { SpaceWithinSquareBrackets, false }, + { SpaceBeforeComma, false }, + { SpaceAfterComma, false }, }; -}"; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36913")] - public async Task NewLinesForBraces_SwitchExpression_Default() - { - await AssertFormatAsync( - @" -class A -{ - void br() - { - var msg = 1 switch + class C + { + void M() + { + _ = this is + [1,2,>= 3 + ]; + } + } + """); + + options = new OptionsCollection(LanguageNames.CSharp) { - _ => null - }; - } -}", - @" -class A -{ - void br() - { - var msg = 1 switch { - _ => null + { SpaceBeforeOpenSquareBracket, false }, // ignored + { SpaceBetweenEmptySquareBrackets, true }, + { SpaceWithinSquareBrackets, true }, + { SpaceBeforeComma, true }, + { SpaceAfterComma, true }, }; - } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/36913")] - public async Task NewLinesForBraces_SwitchExpression_NonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + await AssertFormatAsync(code: code, changedOptionSet: options, expected: """ + + class C { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, - }; - await AssertFormatAsync( - @" -class A -{ - void br() - { - var msg = 1 switch { - _ => null - }; + void M() + { + _ = this is + [ 1 , 2 , >= 3 + ]; + } + } + """); } -}", - @" -class A -{ - void br() + + [Fact] + public async Task FormatSlicePattern() { - var msg = 1 switch - { - _ => null - }; + var code = """ + class C + { + void M() { + _ = this is[ 0,.. var rest ]; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is [0, .. var rest]; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", changedOptionSet: changingOptions); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/discussions/49725")] - public async Task NewLinesForBraces_RecordWithInitializer_Default() - { - await AssertFormatAsync( - @" -record R(int X); -class C -{ - void Goo(R r) + [Fact] + public async Task FormatSlicePattern_NoSpace() { - var r2 = r with - { - X = 0 - }; + var code = """ + class C + { + void M() { + _ = this is[ 0,..var rest ]; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is [0, .. var rest]; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", - @" -record R(int X); -class C -{ - void Goo(R r) + + [Fact] + public async Task FormatSlicePatternWithAnd() { - var r2 = r with { - X = 0 - }; + var code = """ + class C + { + void M() { + _ = this is[ 0,.. {Count: >0} and var rest ]; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is [0, .. { Count: > 0 } and var rest]; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/discussions/49725")] - public async Task NewLinesForBraces_RecordWithInitializer_NonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) - { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, - }; - await AssertFormatAsync( - @" -record R(int X); -class C -{ - void Goo(R r) + [Fact] + public async Task FormatLengthAndListPattern() { - var r2 = r with { - X = 0 - }; + var code = """ + class C + { + void M() { + _ = this is{Count:>0 and var x}and[ 1,2,3 ]; + } + } + """; + var expectedCode = """ + class C + { + void M() + { + _ = this is { Count: > 0 and var x } and [1, 2, 3]; + } + } + """; + await AssertFormatAsync(expectedCode, code); } -}", - @" -record R(int X); -class C -{ - void Goo(R r) + + [Fact] + public async Task LambdaReturnType_01() { - var r2 = r with - { - X = 0 - }; + await AssertFormatAsync( + """ + class Program + { + Delegate D = void () => { }; + } + """, + """ + class Program + { + Delegate D = void () => { }; + } + """); } -}", changedOptionSet: changingOptions); - } - [Fact] - public async Task NoSpacesInPropertyPatterns() - { - var code = @"class C -{ - int IntProperty { get; set; } - void M() + [Fact] + public async Task LambdaReturnType_02() { - _ = this is { IntProperty : 2 }; + await AssertFormatAsync( + """ + class Program + { + Delegate D = A.B () => { }; + } + """, + """ + class Program + { + Delegate D = A.B()=>{ }; + } + """); } -}"; - var expectedCode = @"class C -{ - int IntProperty { get; set; } - void M() + + [Fact] + public async Task LambdaReturnType_03() { - _ = this is { IntProperty: 2 }; + await AssertFormatAsync( + """ + class Program + { + Delegate D = A (x) => x; + } + """, + """ + class Program + { + Delegate D = A < B > ( x ) => x; + } + """); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task NoSpacesInExtendedPropertyPatterns() - { - var code = @"class C -{ - C CProperty { get; set; } - int IntProperty { get; set; } - void M() + [Fact] + public async Task LambdaReturnType_04() { - _ = this is { CProperty . IntProperty : 2 }; + await AssertFormatAsync( + """ + class Program + { + object F = Func((A, B) ((A, B) t) => t); + } + """, + """ + class Program + { + object F = Func((A,B)((A,B)t)=>t); + } + """); } -}"; - var expectedCode = @"class C -{ - C CProperty { get; set; } - int IntProperty { get; set; } - void M() + + [Fact] + public async Task LineSpanDirective() { - _ = this is { CProperty.IntProperty: 2 }; + var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; + await AssertFormatAsync( + """ + class Program + { + static void Main() + { + #line (1, 1) - (1, 100) 5 "a.razor" + } + } + """, + """ + class Program + { + static void Main() + { + #line (1,1)-(1,100) 5 "a.razor" + } + } + """, changedOptionSet: optionSet); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52413")] - public async Task NewLinesForBraces_PropertyPatternClauses_Default() - { - await AssertFormatAsync( - @" -class A -{ - public string Name { get; } - - public bool IsFoo(A a) + [Fact] + public async Task FileScopedNamespace() { - return a is - { - Name: ""foo"", - }; + await AssertFormatAsync( + expected: """ + + namespace NS; + + class C { } + + """, + code: """ + + namespace NS; + + class C { } + + """); } -}", - @" -class A -{ - public string Name { get; } - public bool IsFoo(A a) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewInImplicitObjectCreation() { - return a is { - Name: ""foo"", - }; - } -}"); - } + await AssertFormatAsync( + expected: """ - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/52413")] - public async Task NewLinesForBraces_PropertyPatternClauses_NonDefault() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, - }; - await AssertFormatAsync( - @" -class A -{ - public string Name { get; } + void M() + { + string v = new(); + } + } - public bool IsFoo(A a) - { - return a is { - Name: ""foo"", - }; + """, + code: """ + + class C + { + void M() { + string v = new (); + } + } + + """); } -}", - @" -class A -{ - public string Name { get; } - public bool IsFoo(A a) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewInTupleArrayCreation() { - return a is - { - Name: ""foo"", - }; - } -}", changedOptionSet: changingOptions); - } + await AssertFormatAsync( + expected: """ - [Fact, Trait(Traits.Feature, Traits.Features.Formatting)] - [WorkItem(57854, "https://github.com/dotnet/roslyn/issues/57854")] - public async Task NewLinesForBraces_PropertyPatternClauses_NonDefaultInSwitchExpression() - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, false) }, - }; - await AssertFormatAsync( - @" -class A -{ - public string Name { get; } + void M() + { + var v = new (int, int)[]; + } + } - public bool IsFoo(A a) - { - return a switch { - { Name: ""foo"" } => true, - _ => false, - }; + """, + code: """ + + class C + { + void M() { + var v = new (int, int) [ ]; + } + } + + """); } -}", - @" -class A -{ - public string Name { get; } - public bool IsFoo(A a) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewInArrayCreation() { - return a switch - { - { Name: ""foo"" } => true, - _ => false, - }; - } -}", changedOptionSet: changingOptions); - } + await AssertFormatAsync( + expected: """ - [Theory, CombinatorialData] - [WorkItem("https://github.com/dotnet/roslyn/issues/52413")] - public async Task NewLinesForBraces_PropertyPatternClauses_SingleLine(bool option) - { - var changingOptions = new OptionsCollection(LanguageNames.CSharp) + class C { - { NewLineBeforeOpenBrace, NewLineBeforeOpenBrace.DefaultValue.WithFlagValue(NewLineBeforeOpenBracePlacement.ObjectCollectionArrayInitializers, option) }, - }; - var code = @" -class A -{ - public string Name { get; } + void M() + { + var v = new int[1]; + } + } - public bool IsFoo(A a) - { - return a is { Name: ""foo"" }; + """, + code: """ + + class C + { + void M() { + var v = new int [ 1 ]; + } + } + + """); } -}"; - await AssertFormatAsync(code, code, changedOptionSet: changingOptions); - } - [Fact] - public async Task RecordClass() - { - await AssertFormatAsync( - @" -record class R(int X); -", - @" -record class R(int X); -"); - } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewInImplicitArrayCreation() + { + await AssertFormatAsync( + expected: """ - [Fact] - public async Task Class() - { - await AssertFormatAsync( - @" -class R(int X); -", - @" -class R(int X) ; -"); - } + class C + { + void M() + { + var v = new[] { 1, 2, 3 }; + } + } - [Fact] - public async Task Interface() - { - await AssertFormatAsync( - @" -interface R(int X); -", - @" -interface R(int X) ; -"); - } + """, + code: """ - [Fact] - public async Task RecordStruct() - { - await AssertFormatAsync( - @" -record struct R(int X); -", - @" -record struct R(int X); -"); - } + class C + { + void M() { + var v = new [ ] { 1, 2, 3 }; + } + } - [Fact] - public async Task Struct() - { - await AssertFormatAsync( - @" -struct R(int X); -", - @" -struct R(int X) ; -"); - } + """); + } - [Fact] - public async Task FormatListPattern() - { - var code = @" -class C -{ - void M() { -_ = this is[1,2,>=3]; -} -}"; - await AssertFormatAsync(code: code, expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewInConstructorConstraint() { - _ = this is [1, 2, >= 3]; - } -}"); + await AssertFormatAsync( + expected: """ - var options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; + void M() where T : new() + { + } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is [1,2,>= 3]; - } -}"); + """, + code: """ - options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBeforeOpenSquareBracket, false }, // ignored - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; + void M() where T : new ( ) { + } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is [ 1 , 2 , >= 3 ]; + """); } -}"); - } - [Fact] - public async Task FormatListPattern_Parentheses() - { - var code = @" -class C -{ - void M((int[], int[]) a) { -_ = a is([1,2,>=3],[1,2]); -} -}"; - await AssertFormatAsync(code: code, expected: @" -class C -{ - void M((int[], int[]) a) + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewMethodOverloadWithTupleReturnType() { - _ = a is ([1, 2, >= 3], [1, 2]); - } -}"); + await AssertFormatAsync( + expected: """ - var options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; + new (int, int) M() { } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M((int[], int[]) a) - { - _ = a is ([1,2,>= 3],[1,2]); - } -}"); + """, + code: """ - options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBeforeOpenSquareBracket, false }, // ignored - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; + new (int, int) M() { } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M((int[ ], int[ ]) a) - { - _ = a is ([ 1 , 2 , >= 3 ], [ 1 , 2 ]); + """); } -}"); - } - [Fact] - public async Task FormatListPattern_TrailingComma() - { - var code = @" -class C -{ - void M() { -_ = this is[1,2,>=3,]; -} -}"; - await AssertFormatAsync(code: code, expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewPropertyWithTupleReturnType() { - _ = this is [1, 2, >= 3,]; - } -}"); + await AssertFormatAsync( + expected: """ - var options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; + new (int, int) Property { get; set; } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is [1,2,>= 3,]; - } -}"); + """, + code: """ - options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBeforeOpenSquareBracket, false }, // ignored - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; + new (int, int) Property { get; set; } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is [ 1 , 2 , >= 3 , ]; + """); } -}"); - } - [Fact] - public async Task FormatListPattern_WithNewline() - { - var code = @" -class C -{ - void M() { -_ = this is -[1,2,>=3 -]; -} -}"; - await AssertFormatAsync(code: code, expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] + public async Task NewIndexerWithTupleReturnType() { - _ = this is - [1, 2, >= 3 - ]; - } -}"); + await AssertFormatAsync( + expected: """ - var options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBetweenEmptySquareBrackets, false }, - { SpaceWithinSquareBrackets, false }, - { SpaceBeforeComma, false }, - { SpaceAfterComma, false }, - }; + new (int, int) this[int i] { get => throw null; } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is - [1,2,>= 3 - ]; - } -}"); + """, + code: """ - options = new OptionsCollection(LanguageNames.CSharp) + class C { - { SpaceBeforeOpenSquareBracket, false }, // ignored - { SpaceBetweenEmptySquareBrackets, true }, - { SpaceWithinSquareBrackets, true }, - { SpaceBeforeComma, true }, - { SpaceAfterComma, true }, - }; + new (int, int) this[int i] { get => throw null; } + } - await AssertFormatAsync(code: code, changedOptionSet: options, expected: @" -class C -{ - void M() - { - _ = this is - [ 1 , 2 , >= 3 - ]; + """); } -}"); - } - [Fact] - public async Task FormatSlicePattern() - { - var code = @"class C -{ - void M() { -_ = this is[ 0,.. var rest ]; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnLambda() { - _ = this is [0, .. var rest]; + await AssertFormatAsync( + expected: """ + + var f = [Attribute] () => { }; + + """, + code: """ + + var f = [Attribute] () => { }; + + """); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatSlicePattern_NoSpace() - { - var code = @"class C -{ - void M() { -_ = this is[ 0,..var rest ]; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnLambda_TwoAttributes() { - _ = this is [0, .. var rest]; + await AssertFormatAsync( + expected: """ + + var f = [Attribute][Attribute2] () => { }; + + """, + code: """ + + var f = [Attribute] [Attribute2] () => { }; + + """); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatSlicePatternWithAnd() - { - var code = @"class C -{ - void M() { -_ = this is[ 0,.. {Count: >0} and var rest ]; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnMethod_TwoAttributes() { - _ = this is [0, .. { Count: > 0 } and var rest]; + await AssertFormatAsync( + expected: """ + + [Attribute][Attribute2] + void M() + { } + + """, + code: """ + + [Attribute] [Attribute2] + void M() + { } + + """); } -}"; - await AssertFormatAsync(expectedCode, code); - } - [Fact] - public async Task FormatLengthAndListPattern() - { - var code = @"class C -{ - void M() { -_ = this is{Count:>0 and var x}and[ 1,2,3 ]; -} -}"; - var expectedCode = @"class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnTypeParameter_TwoAttributes() { - _ = this is { Count: > 0 and var x } and [1, 2, 3]; - } -}"; - await AssertFormatAsync(expectedCode, code); - } + await AssertFormatAsync( + expected: """ - [Fact] - public async Task LambdaReturnType_01() - { - await AssertFormatAsync( -@"class Program -{ - Delegate D = void () => { }; -}", -@"class Program -{ - Delegate D = void () => { }; -}"); - } + class C<[Attribute][Attribute2] T> { } - [Fact] - public async Task LambdaReturnType_02() - { - await AssertFormatAsync( -@"class Program -{ - Delegate D = A.B () => { }; -}", -@"class Program -{ - Delegate D = A.B()=>{ }; -}"); - } + """, + code: """ - [Fact] - public async Task LambdaReturnType_03() - { - await AssertFormatAsync( -@"class Program -{ - Delegate D = A (x) => x; -}", -@"class Program -{ - Delegate D = A < B > ( x ) => x; -}"); - } + class C< [Attribute] [Attribute2] T > { } - [Fact] - public async Task LambdaReturnType_04() - { - await AssertFormatAsync( -@"class Program -{ - object F = Func((A, B) ((A, B) t) => t); -}", -@"class Program -{ - object F = Func((A,B)((A,B)t)=>t); -}"); - } + """); + } - [Fact] - public async Task LineSpanDirective() - { - var optionSet = new OptionsCollection(LanguageNames.CSharp) { { FormattingOptions2.UseTabs, true } }; - await AssertFormatAsync( -@"class Program -{ - static void Main() - { -#line (1, 1) - (1, 100) 5 ""a.razor"" - } -}", -@"class Program -{ - static void Main() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnTypeParameter_TwoAttributes_Method() { -#line (1,1)-(1,100) 5 ""a.razor"" - } -}", changedOptionSet: optionSet); - } + await AssertFormatAsync( + expected: """ - [Fact] - public async Task FileScopedNamespace() - { - await AssertFormatAsync( - expected: @" -namespace NS; + class C + { + void M<[Attribute][Attribute2] T>() { } + } -class C { } -", - code: @" -namespace NS; + """, + code: """ - class C { } -"); - } + class C + { + void M< [Attribute] [Attribute2] T > ( ) { } + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewInImplicitObjectCreation() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M() - { - string v = new(); - } -} -", - code: @" -class C -{ - void M() { - string v = new (); + """); } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewInTupleArrayCreation() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnParameter_TwoAttributes() { - var v = new (int, int)[]; - } -} -", - code: @" -class C -{ - void M() { - var v = new (int, int) [ ]; + await AssertFormatAsync( + expected: """ + + class C + { + void M([Attribute][Attribute2] T t) { } + } + + """, + code: """ + + class C + { + void M( [Attribute] [Attribute2] T t ) { } + } + + """); } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewInArrayCreation() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnLambdaWithExplicitType() { - var v = new int[1]; - } -} -", - code: @" -class C -{ - void M() { - var v = new int [ 1 ]; + await AssertFormatAsync( + expected: """ + + var f = [Attribute] int () => 1; + + """, + code: """ + + var f = [Attribute] int () => 1; + + """); } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewInImplicitArrayCreation() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M() + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] + public async Task FormatAttributeOnLambdaInInvocation() { - var v = new[] { 1, 2, 3 }; - } -} -", - code: @" -class C -{ - void M() { - var v = new [ ] { 1, 2, 3 }; + await AssertFormatAsync( + expected: """ + + f([Attribute] () => { }); + + """, + code: """ + + f( [Attribute] () => { }); + + """); } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewInConstructorConstraint() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M() where T : new() + [Fact] + public async Task FormatAttributeOnLambdaParameter() { + await AssertFormatAsync(expected: """ + var f = ([Attribute] int x = 1) => x; + """, code: """ + var f = ( [ Attribute ]int x=1)=>x; + """); } -} -", - code: @" -class C -{ - void M() where T : new ( ) { - } -} -"); - } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewMethodOverloadWithTupleReturnType() - { - await AssertFormatAsync( - expected: @" -class C -{ - new (int, int) M() { } -} -", - code: @" -class C -{ - new (int, int) M() { } -} -"); - } + [Fact] + public async Task FormatRawStringInterpolation() + { + await AssertFormatAsync( + expected: """" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewPropertyWithTupleReturnType() - { - await AssertFormatAsync( - expected: @" -class C -{ - new (int, int) Property { get; set; } -} -", - code: @" -class C -{ - new (int, int) Property { get; set; } -} -"); - } + var s = $"""{s}""" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56498")] - public async Task NewIndexerWithTupleReturnType() - { - await AssertFormatAsync( - expected: @" -class C -{ - new (int, int) this[int i] { get => throw null; } -} -", - code: @" -class C -{ - new (int, int) this[int i] { get => throw null; } -} -"); - } + """", + code: """" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnLambda() - { - await AssertFormatAsync( - expected: @" -var f = [Attribute] () => { }; -", - code: @" -var f = [Attribute] () => { }; -"); - } + var s = $"""{s}""" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnLambda_TwoAttributes() - { - await AssertFormatAsync( - expected: @" -var f = [Attribute][Attribute2] () => { }; -", - code: @" -var f = [Attribute] [Attribute2] () => { }; -"); - } + """"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnMethod_TwoAttributes() - { - await AssertFormatAsync( - expected: @" -[Attribute][Attribute2] -void M() -{ } -", - code: @" - [Attribute] [Attribute2] -void M() -{ } -"); - } + [Fact] + public async Task FormatRawStringInterpolation2() + { + await AssertFormatAsync( + expected: """" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnTypeParameter_TwoAttributes() - { - await AssertFormatAsync( - expected: @" -class C<[Attribute][Attribute2] T> { } -", - code: @" -class C< [Attribute] [Attribute2] T > { } -"); - } + var s = $"""{s,0: x }""" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnTypeParameter_TwoAttributes_Method() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M<[Attribute][Attribute2] T>() { } -} -", - code: @" -class C -{ - void M< [Attribute] [Attribute2] T > ( ) { } -} -"); - } + """", + code: """" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnParameter_TwoAttributes() - { - await AssertFormatAsync( - expected: @" -class C -{ - void M([Attribute][Attribute2] T t) { } -} -", - code: @" -class C -{ - void M( [Attribute] [Attribute2] T t ) { } -} -"); - } + var s = $"""{s, 0 : x }""" - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnLambdaWithExplicitType() - { - await AssertFormatAsync( - expected: @" -var f = [Attribute] int () => 1; -", - code: @" -var f = [Attribute] int () => 1; -"); - } + """"); + } - [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/56543")] - public async Task FormatAttributeOnLambdaInInvocation() - { - await AssertFormatAsync( - expected: @" -f([Attribute] () => { }); -", - code: @" -f( [Attribute] () => { }); -"); - } + [Fact] + public async Task FormatUsingAliasToType1() + { + await AssertFormatAsync( + expected: """ - [Fact] - public async Task FormatAttributeOnLambdaParameter() - { - await AssertFormatAsync(expected: """ - var f = ([Attribute] int x = 1) => x; - """, code: """ - var f = ( [ Attribute ]int x=1)=>x; - """); - } + f([Attribute] () => { }); - [Fact] - public async Task FormatRawStringInterpolation() - { - await AssertFormatAsync( - expected: @" -var s = $""""""{s}"""""" -", - code: @" -var s = $""""""{s}"""""" -"); - } + """, + code: """ - [Fact] - public async Task FormatRawStringInterpolation2() - { - await AssertFormatAsync( - expected: @" -var s = $""""""{s,0: x }"""""" -", - code: @" -var s = $""""""{s, 0 : x }"""""" -"); - } + f( [Attribute] () => { }); - [Fact] - public async Task FormatUsingAliasToType1() - { - await AssertFormatAsync( - expected: @" -f([Attribute] () => { }); -", - code: @" -f( [Attribute] () => { }); -"); - } + """); + } - [Theory] - [InlineData("using X=int ;", "using X = int;")] - [InlineData("global using X=int ;", "global using X = int;")] - [InlineData("using X=nint;", "using X = nint;")] - [InlineData("using X=dynamic;", "using X = dynamic;")] - [InlineData("using X=int [] ;", "using X = int[];")] - [InlineData("using X=(int,int) ;", "using X = (int, int);")] - [InlineData("using unsafe X=int * ;", "using unsafe X = int*;")] - [InlineData("global using unsafe X=int * ;", "global using unsafe X = int*;")] - [InlineData("using X=int ?;", "using X = int?;")] - [InlineData("using X=delegate * ;", "using X = delegate*;")] - public async Task TestNormalizeUsingAlias(string text, string expected) - { - await AssertFormatAsync(expected, text); - } + [Theory] + [InlineData("using X=int ;", "using X = int;")] + [InlineData("global using X=int ;", "global using X = int;")] + [InlineData("using X=nint;", "using X = nint;")] + [InlineData("using X=dynamic;", "using X = dynamic;")] + [InlineData("using X=int [] ;", "using X = int[];")] + [InlineData("using X=(int,int) ;", "using X = (int, int);")] + [InlineData("using unsafe X=int * ;", "using unsafe X = int*;")] + [InlineData("global using unsafe X=int * ;", "global using unsafe X = int*;")] + [InlineData("using X=int ?;", "using X = int?;")] + [InlineData("using X=delegate * ;", "using X = delegate*;")] + public async Task TestNormalizeUsingAlias(string text, string expected) + { + await AssertFormatAsync(expected, text); + } + + [Fact] + public async Task FormatNullConditionalAssignment() + { + await AssertFormatAsync( + expected: """ + x?.y = z; + """, + code: """ + x ? . y = z ; + """); + } + + [Fact] + public async Task TestExtension1() + { + await AssertFormatAsync( + """ + static class C + { + extension(string s) + { + public void M() + { + } + } + } + """, + """ + static class C + { + extension ( string s ) + { + public void M ( ) + { + } + } + } + """, + parseOptions: CSharpParseOptions.Default.WithLanguageVersion(LanguageVersionExtensions.CSharpNext)); } } diff --git a/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs index 4caeafdd8856c..5ca7c0fa62a2c 100644 --- a/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs +++ b/src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Formatting diff --git a/src/Workspaces/CSharpTest/Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj b/src/Workspaces/CSharpTest/Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj index 65dae6561ce44..065c2a4dd7aed 100644 --- a/src/Workspaces/CSharpTest/Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj +++ b/src/Workspaces/CSharpTest/Microsoft.CodeAnalysis.CSharp.Workspaces.UnitTests.csproj @@ -7,6 +7,9 @@ $(NetVSShared);net472 + + + diff --git a/src/Workspaces/CSharpTest/OrganizeImports/OrganizeUsingsTests.cs b/src/Workspaces/CSharpTest/OrganizeImports/OrganizeUsingsTests.cs index 7e6d2ca93dcd6..77bbcba1784b8 100644 --- a/src/Workspaces/CSharpTest/OrganizeImports/OrganizeUsingsTests.cs +++ b/src/Workspaces/CSharpTest/OrganizeImports/OrganizeUsingsTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.OrganizeImports; diff --git a/src/Workspaces/Core/Desktop/TypeForwarders.cs b/src/Workspaces/Core/Desktop/TypeForwarders.cs index bd00d5063caa4..914343bc8d8b4 100644 --- a/src/Workspaces/Core/Desktop/TypeForwarders.cs +++ b/src/Workspaces/Core/Desktop/TypeForwarders.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.Runtime.CompilerServices; // Microsoft.CodeAnalysis.FileTextLoader has been moved to Microsoft.CodeAnalysis.Workspaces.dll. diff --git a/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs b/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs index 6ec35bcccc9f6..eb940aae6922f 100644 --- a/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.cs +++ b/src/Workspaces/Core/Desktop/Workspace/Host/Mef/MefV1HostServices.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.Generic; @@ -26,7 +24,7 @@ public class MefV1HostServices : HostServices, IMefHostExportProvider /// This delegate allows test code to override the behavior of . /// /// - private static CreationHook s_CreationHook; + private static CreationHook? s_CreationHook; // the export provider for the MEF composition private readonly ExportProvider _exportProvider; @@ -80,7 +78,7 @@ protected internal override HostWorkspaceServices CreateWorkspaceServices(Worksp /// public IEnumerable> GetExports() { - var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName, typeof(TMetadata).AssemblyQualifiedName); + var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName!, typeof(TMetadata).AssemblyQualifiedName!); if (!_exportsMap.TryGetValue(key, out var exports)) { exports = ImmutableInterlocked.GetOrAdd(ref _exportsMap, key, _ => @@ -97,7 +95,7 @@ public IEnumerable> GetExports public IEnumerable> GetExports() { - var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName, ""); + var key = new ExportKey(typeof(TExtension).AssemblyQualifiedName!, ""); if (!_exportsMap.TryGetValue(key, out var exports)) { exports = ImmutableInterlocked.GetOrAdd(ref _exportsMap, key, _ => @@ -128,7 +126,7 @@ public bool Equals(ExportKey other) && string.Compare(this.MetadataTypeName, other.MetadataTypeName, StringComparison.OrdinalIgnoreCase) == 0; } - public override bool Equals(object obj) + public override bool Equals(object? obj) => obj is ExportKey exportKey && this.Equals(exportKey); public override int GetHashCode() diff --git a/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs b/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs index c02cef62400ab..971b57a606e9a 100644 --- a/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs +++ b/src/Workspaces/Core/Portable/ChangeNamespace/IChangeNamespaceService.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.ChangeNamespace; diff --git a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs index dd1a9232a399e..d8abbec45dc48 100644 --- a/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/AbstractClassificationService.cs @@ -17,7 +17,6 @@ using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs index 2a0f06553b0dd..9c607c239da06 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Classification; diff --git a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs index c11b685452841..dbd9f775ca9d0 100644 --- a/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs +++ b/src/Workspaces/Core/Portable/CodeActions/CodeAction.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; @@ -658,16 +657,19 @@ protected CodeActionWithNestedActions( internal class DocumentChangeAction : SimpleCodeAction { private readonly Func, CancellationToken, Task> _createChangedDocument; + private readonly Func, CancellationToken, Task>? _createChangedDocumentPreview; private DocumentChangeAction( string title, Func, CancellationToken, Task> createChangedDocument, + Func, CancellationToken, Task>? createChangedDocumentPreview, string? equivalenceKey, CodeActionPriority priority, bool createdFromFactoryMethod) : base(title, equivalenceKey, priority, createdFromFactoryMethod) { _createChangedDocument = createChangedDocument; + _createChangedDocumentPreview = createChangedDocumentPreview; } protected DocumentChangeAction( @@ -675,7 +677,7 @@ protected DocumentChangeAction( Func, CancellationToken, Task> createChangedDocument, string? equivalenceKey, CodeActionPriority priority = CodeActionPriority.Default) - : this(title, createChangedDocument, equivalenceKey, priority, createdFromFactoryMethod: false) + : this(title, createChangedDocument, createChangedDocumentPreview: null, equivalenceKey, priority, createdFromFactoryMethod: false) { } @@ -684,7 +686,16 @@ public static DocumentChangeAction New( Func, CancellationToken, Task> createChangedDocument, string? equivalenceKey, CodeActionPriority priority = CodeActionPriority.Default) - => new(title, createChangedDocument, equivalenceKey, priority, createdFromFactoryMethod: true); + => new(title, createChangedDocument, createChangedDocumentPreview: null, equivalenceKey, priority, createdFromFactoryMethod: true); + + protected override async Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + { + if (_createChangedDocumentPreview is null) + return await base.ComputePreviewOperationsAsync(cancellationToken).ConfigureAwait(false); + + var newDocument = await _createChangedDocumentPreview(CodeAnalysisProgress.None, cancellationToken).ConfigureAwait(false); + return [new ApplyChangesOperation(newDocument.Project.Solution)]; + } protected sealed override Task GetChangedDocumentAsync(IProgress progress, CancellationToken cancellationToken) => _createChangedDocument(progress, cancellationToken); diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs index 942392fbbbd43..56de731c8bfb4 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllContext.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes; diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllState.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllState.cs index dd4b91da6f91e..1e7ee4eddde7e 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllState.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllState.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixesAndRefactorings; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs index 7681dc9c307e9..8bc5930ccccae 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/CommonFixAllState.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Internal.Log; using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs index efd024c4721ba..a88e2f3b6807d 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/FixAllLogger.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Internal.Log; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; diff --git a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs index 66083f1d4ac32..e714ebe17dcd2 100644 --- a/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs +++ b/src/Workspaces/Core/Portable/CodeFixesAndRefactorings/IFixAllState.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.CodeActions; using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; diff --git a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs index 23655f80671ac..ff17b495a58ab 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption.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 Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption2_operators.cs b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption2_operators.cs index b72a6cfb83c81..a4acc94541aaa 100644 --- a/src/Workspaces/Core/Portable/CodeStyle/NotificationOption2_operators.cs +++ b/src/Workspaces/Core/Portable/CodeStyle/NotificationOption2_operators.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.CodeStyle; internal readonly partial record struct NotificationOption2 diff --git a/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs b/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs index 40eaeb56ca70b..29712ea2a2b54 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/CodeAnalysisEventSource.Workspaces.cs @@ -1,8 +1,7 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics.Tracing; namespace Microsoft.CodeAnalysis diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs index 8a7b8371ab1f0..8161311508bbf 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalysisResult.cs @@ -8,7 +8,6 @@ using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs index f76d51c050d14..10573a19b5833 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Diagnostics; internal static partial class DiagnosticAnalyzerExtensions diff --git a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs index cb6d63dd32c32..9342db0363854 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DiagnosticData.cs @@ -248,7 +248,7 @@ private static DiagnosticData Create( private static ImmutableDictionary? GetAdditionalProperties(TextDocument document, Diagnostic diagnostic) { var service = document.Project.GetLanguageService(); - return service?.GetAdditionalProperties(diagnostic); + return service?.GetAdditionalProperties(diagnostic)!; } private static ImmutableArray GetAdditionalLocations(TextDocument document, Diagnostic diagnostic) diff --git a/src/Workspaces/Core/Portable/Diagnostics/DocumentDiagnosticAnalyzer.cs b/src/Workspaces/Core/Portable/Diagnostics/DocumentDiagnosticAnalyzer.cs index b33a58f6cbc59..5a3d481aab3dd 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/DocumentDiagnosticAnalyzer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/DocumentDiagnosticAnalyzer.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index 9457027a23a87..eb30cc820c762 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; @@ -21,6 +22,8 @@ namespace Microsoft.CodeAnalysis.Diagnostics; internal static partial class Extensions { + private static readonly ConditionalWeakTable> s_projectToDiagnosticChecksum = new(); + public static async Task> ToDiagnosticsAsync(this IEnumerable diagnostics, Project project, CancellationToken cancellationToken) { var result = ArrayBuilder.GetInstance(); @@ -429,4 +432,76 @@ await suppressionAnalyzer.AnalyzeAsync( semanticModel, span, hostCompilationWithAnalyzers, analyzerInfoCache.GetDiagnosticDescriptors, reportDiagnostic, cancellationToken).ConfigureAwait(false); } } + + /// + /// Calculates a checksum that contains a project's checksum along with a checksum for each of the project's + /// transitive dependencies. + /// + /// + /// This checksum calculation can be used for cases where a feature needs to know if the semantics in this project + /// changed. For example, for diagnostics or caching computed semantic data. The goal is to ensure that changes to + /// + /// Files inside the current project + /// Project properties of the current project + /// Visible files in referenced projects + /// Project properties in referenced projects + /// + /// are reflected in the metadata we keep so that comparing solutions accurately tells us when we need to recompute + /// semantic work. + /// + /// This method of checking for changes has a few important properties that differentiate it from other methods of determining project version. + /// + /// Changes to methods inside the current project will be reflected to compute updated diagnostics. + /// does not change as it only returns top level changes. + /// Reloading a project without making any changes will re-use cached diagnostics. + /// changes as the project is removed, then added resulting in a version change. + /// + /// + /// This checksum is also affected by the for this project. + /// As such, it is not usable across different sessions of a particular host. + /// + public static Task GetDiagnosticChecksumAsync(this Project? project, CancellationToken cancellationToken) + { + if (project is null) + return SpecializedTasks.Default(); + + var lazyChecksum = s_projectToDiagnosticChecksum.GetValue( + project, + static project => AsyncLazy.Create( + static (project, cancellationToken) => ComputeDiagnosticChecksumAsync(project, cancellationToken), + project)); + + return lazyChecksum.GetValueAsync(cancellationToken); + + static async Task ComputeDiagnosticChecksumAsync(Project project, CancellationToken cancellationToken) + { + var solution = project.Solution; + + using var _ = ArrayBuilder.GetInstance(out var tempChecksumArray); + + // Mix in the SG information for this project. That way if it changes, we will have a different + // checksum (since semantics could have changed because of this). + if (solution.CompilationState.SourceGeneratorExecutionVersionMap.Map.TryGetValue(project.Id, out var executionVersion)) + tempChecksumArray.Add(executionVersion.Checksum); + + // Get the checksum for the project itself. Note: this will normally be cached. As such, even if we + // have a different Project instance (due to a change in an unrelated project), this will be fast to + // compute and return. + var projectChecksum = await project.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); + tempChecksumArray.Add(projectChecksum); + + // Calculate a checksum this project and for each dependent project that could affect semantics for this + // project. We order the projects guid so that we are resilient to the underlying in-memory graph structure + // changing this arbitrarily. + foreach (var projectRef in project.ProjectReferences.OrderBy(r => r.ProjectId.Id)) + { + // Note that these checksums should only actually be calculated once, if the project is unchanged + // the same checksum will be returned. + tempChecksumArray.Add(await GetDiagnosticChecksumAsync( + solution.GetProject(projectRef.ProjectId), cancellationToken).ConfigureAwait(false)); + } + + return Checksum.Create(tempChecksumArray); + } + } } diff --git a/src/Workspaces/Core/Portable/Diagnostics/FileContentLoadAnalyzer.cs b/src/Workspaces/Core/Portable/Diagnostics/FileContentLoadAnalyzer.cs index 5cc83d3c3f6e3..54c0ebe8b2305 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/FileContentLoadAnalyzer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/FileContentLoadAnalyzer.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.Collections.Immutable; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/Core/Portable/Diagnostics/IDiagnosticPropertiesService.cs b/src/Workspaces/Core/Portable/Diagnostics/IDiagnosticPropertiesService.cs index af25c1b92bb95..caed6f4291d16 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/IDiagnosticPropertiesService.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/IDiagnosticPropertiesService.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.Collections.Immutable; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Diagnostics/IWorkspaceVenusSpanMappingService.cs b/src/Workspaces/Core/Portable/Diagnostics/IWorkspaceVenusSpanMappingService.cs index 95fd270f13239..a1b3a0a71b272 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/IWorkspaceVenusSpanMappingService.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/IWorkspaceVenusSpanMappingService.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 Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/Core/Portable/Diagnostics/ProjectDiagnosticAnalyzer.cs b/src/Workspaces/Core/Portable/Diagnostics/ProjectDiagnosticAnalyzer.cs index 735ada738c2d0..771c9cb3288ba 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/ProjectDiagnosticAnalyzer.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/ProjectDiagnosticAnalyzer.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Diagnostics/WellKnownDiagnosticPropertyNames.cs b/src/Workspaces/Core/Portable/Diagnostics/WellKnownDiagnosticPropertyNames.cs index 1530c81b312b6..d5dab9d6df631 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/WellKnownDiagnosticPropertyNames.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/WellKnownDiagnosticPropertyNames.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 - namespace Microsoft.CodeAnalysis.Diagnostics; internal static class WellKnownDiagnosticPropertyNames diff --git a/src/Workspaces/Core/Portable/Differencing/LongestCommonImmutableArraySubsequence.cs b/src/Workspaces/Core/Portable/Differencing/LongestCommonImmutableArraySubsequence.cs index f550bc3d69b26..211d9f212f7bb 100644 --- a/src/Workspaces/Core/Portable/Differencing/LongestCommonImmutableArraySubsequence.cs +++ b/src/Workspaces/Core/Portable/Differencing/LongestCommonImmutableArraySubsequence.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.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Workspaces/Core/Portable/Differencing/LongestCommonSubstring.cs b/src/Workspaces/Core/Portable/Differencing/LongestCommonSubstring.cs index 5561efb597b5b..f2cf5fde07776 100644 --- a/src/Workspaces/Core/Portable/Differencing/LongestCommonSubstring.cs +++ b/src/Workspaces/Core/Portable/Differencing/LongestCommonSubstring.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.Collections.Generic; namespace Microsoft.CodeAnalysis.Differencing; diff --git a/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs b/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs index 553aa1718382f..bab3554fe55e2 100644 --- a/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.cs +++ b/src/Workspaces/Core/Portable/Editing/DeclarationModifiers.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.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Editing/ImportAdder.cs b/src/Workspaces/Core/Portable/Editing/ImportAdder.cs index 4f8fb106f5a6c..b49641ad3fc64 100644 --- a/src/Workspaces/Core/Portable/Editing/ImportAdder.cs +++ b/src/Workspaces/Core/Portable/Editing/ImportAdder.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs b/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs index e18ee200d37fa..43e119533866e 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.Host; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editing; diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs index c7111748afc1f..5d3a60e4ead27 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxEditorExtensions.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.Collections.Generic; namespace Microsoft.CodeAnalysis.Editing; diff --git a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs index 7f1f3d09ca93d..ad653c85131ec 100644 --- a/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs +++ b/src/Workspaces/Core/Portable/Editing/SyntaxGenerator.cs @@ -1228,7 +1228,11 @@ public SyntaxNode AddMembers(SyntaxNode declaration, params SyntaxNode[] members /// /// Changes the for the declaration. /// - public abstract SyntaxNode WithModifiers(SyntaxNode declaration, DeclarationModifiers modifiers); + public SyntaxNode WithModifiers(SyntaxNode declaration, DeclarationModifiers modifiers) + => WithModifiers(declaration, modifiers); + + internal abstract TSyntaxNode WithModifiers(TSyntaxNode declaration, DeclarationModifiers modifiers) + where TSyntaxNode : SyntaxNode; /// /// Gets the for the declaration. diff --git a/src/Workspaces/Core/Portable/Editing/TypeConstraintKind.cs b/src/Workspaces/Core/Portable/Editing/TypeConstraintKind.cs index b0e6ce0af06f5..ae9d8052b6fd5 100644 --- a/src/Workspaces/Core/Portable/Editing/TypeConstraintKind.cs +++ b/src/Workspaces/Core/Portable/Editing/TypeConstraintKind.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; namespace Microsoft.CodeAnalysis.Editing; diff --git a/src/Workspaces/Core/Portable/ErrorReporting/InfoBarUI.cs b/src/Workspaces/Core/Portable/ErrorReporting/InfoBarUI.cs index 4e5761e954958..280a5a0f106cd 100644 --- a/src/Workspaces/Core/Portable/ErrorReporting/InfoBarUI.cs +++ b/src/Workspaces/Core/Portable/ErrorReporting/InfoBarUI.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics.CodeAnalysis; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.ErrorReporting; diff --git a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaObjectPool.cs b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaObjectPool.cs index 548cc97efa0e4..25e40508b6523 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaObjectPool.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaObjectPool.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.Generic; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaSyntaxFactsServiceWrapper.cs b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaSyntaxFactsServiceWrapper.cs index ce7f9820598d6..fbbb622fdce39 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaSyntaxFactsServiceWrapper.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaSyntaxFactsServiceWrapper.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 Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingFatalErrorAccessor.cs b/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingFatalErrorAccessor.cs index ab84ab37acbf9..c49ee8aa59eba 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingFatalErrorAccessor.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/UnitTesting/Api/UnitTestingFatalErrorAccessor.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 Microsoft.CodeAnalysis.ErrorReporting; diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/TaskExtensions.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/TaskExtensions.cs new file mode 100644 index 0000000000000..c1cb797b3f77e --- /dev/null +++ b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/TaskExtensions.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +internal static class TaskExtensions +{ +#pragma warning disable VSTHRD200 // Use "Async" suffix for async methods + public static Task CompletesAsyncOperation(this Task task, VSTypeScriptAsyncToken asyncToken) +#pragma warning restore VSTHRD200 + => task.CompletesAsyncOperation(asyncToken.UnderlyingObject); +} diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsyncToken.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsyncToken.cs new file mode 100644 index 0000000000000..68cc0882a1bae --- /dev/null +++ b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsyncToken.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +internal sealed class VSTypeScriptAsyncToken(IAsyncToken underlyingObject) : IDisposable +{ + internal IAsyncToken UnderlyingObject + => underlyingObject; + + public void Dispose() + => UnderlyingObject.Dispose(); +} diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListener.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListener.cs new file mode 100644 index 0000000000000..f562e164a606a --- /dev/null +++ b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListener.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +internal readonly struct VSTypeScriptAsynchronousOperationListener(IAsynchronousOperationListener underlyingObject) +{ + public VSTypeScriptAsyncToken BeginAsyncOperation(string name, object? tag = null, [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 0) + => new(underlyingObject.BeginAsyncOperation(name, tag, filePath, lineNumber)); +} diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListenerProvider.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListenerProvider.cs new file mode 100644 index 0000000000000..523716898c691 --- /dev/null +++ b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptAsynchronousOperationListenerProvider.cs @@ -0,0 +1,21 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +[Shared] +[Export(typeof(VSTypeScriptAsynchronousOperationListenerProvider))] +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class VSTypeScriptAsynchronousOperationListenerProvider( + IAsynchronousOperationListenerProvider provider) +{ + public VSTypeScriptAsynchronousOperationListener GetListener(string featureName) + => new(provider.GetListener(featureName)); +} diff --git a/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptFeatureAttribute.cs b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptFeatureAttribute.cs new file mode 100644 index 0000000000000..8c2ee1e25ba90 --- /dev/null +++ b/src/Workspaces/Core/Portable/ExternalAccess/VSTypeScript/Api/VSTypeScriptFeatureAttribute.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis.Shared.TestHooks; + +namespace Microsoft.CodeAnalysis.ExternalAccess.VSTypeScript.Api; + +internal static class VSTypeScriptFeatureAttribute +{ + public const string RenameTracking = FeatureAttribute.RenameTracking; + public const string Snippets = FeatureAttribute.Snippets; + public const string Workspace = FeatureAttribute.Workspace; + public const string SolutionCrawlerLegacy = FeatureAttribute.SolutionCrawlerLegacy; +} diff --git a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs index 2d715264d7ef6..def790ba9bc76 100644 --- a/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs +++ b/src/Workspaces/Core/Portable/ExtractMethod/ExtractMethodOptions.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs index 083c3d0937a87..8d6fec248ec0e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentTypeFinder_ProjectIndex.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Storage; using Roslyn.Utilities; @@ -21,7 +22,16 @@ private sealed class ProjectIndex( MultiDictionary delegates, MultiDictionary namedTypes) { - private static readonly ConditionalWeakTable> s_projectToIndex = new(); + /// + /// We cache the project instance per . This allows us to reuse it over a wide set of + /// changes (for example, changing completely unrelated projects that a particular project doesn't depend on). + /// However, doesn't change even when certain things change that will create a + /// substantively different . For example, if the for the project changes, we'll still have the same project state. + /// As such, we store the of the project as well, ensuring that if anything in it or its + /// dependencies changes, we recompute the index. + /// + private static readonly ConditionalWeakTable lazyProjectIndex)>> s_projectToIndex = new(); public readonly MultiDictionary ClassesAndRecordsThatMayDeriveFromSystemObject = classesAndRecordsThatMayDeriveFromSystemObject; public readonly MultiDictionary ValueTypes = valueTypes; @@ -29,18 +39,29 @@ private sealed class ProjectIndex( public readonly MultiDictionary Delegates = delegates; public readonly MultiDictionary NamedTypes = namedTypes; - public static Task GetIndexAsync( + public static async Task GetIndexAsync( Project project, CancellationToken cancellationToken) { - if (!s_projectToIndex.TryGetValue(project.State, out var lazyIndex)) + // Use the checksum of the project. That way if its state *or* SG info changes, we compute a new index with + // accurate information in it. + var checksum = await project.GetDiagnosticChecksumAsync(cancellationToken).ConfigureAwait(false); + if (!s_projectToIndex.TryGetValue(project.State, out var tuple) || + tuple.Value.checksum != checksum) { - lazyIndex = s_projectToIndex.GetValue( - project.State, p => AsyncLazy.Create( - static (project, c) => CreateIndexAsync(project, c), - project)); + tuple = new((checksum, AsyncLazy.Create(CreateIndexAsync, project))); + +#if NET + s_projectToIndex.AddOrUpdate(project.State, tuple); +#else + // Best effort try to update the map with the new data. + s_projectToIndex.Remove(project.State); + // Note: intentionally ignore the return value here. We want to use the value we've computed even if + // another thread beats us to adding things here. + _ = s_projectToIndex.GetValue(project.State, _ => tuple); +#endif } - return lazyIndex.GetValueAsync(cancellationToken); + return await tuple.Value.lazyProjectIndex.GetValueAsync(cancellationToken).ConfigureAwait(false); } private static async Task CreateIndexAsync(Project project, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs index 76c2b5f4e9090..d5458c1696ae9 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.SymbolSet.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.FindSymbols.Finders; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs index ee82ecb3d4526..1652e97e44258 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/FindReferencesSearchEngine.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs index e9944e7ff30c0..d6be466eec277 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractMethodOrPropertyOrEventSymbolReferenceFinder.cs @@ -24,7 +24,7 @@ protected static ImmutableArray GetReferencedAccessorSymbols( // the only accessor method referenced in a foreach-statement is the .Current's // get-accessor - return symbols.CurrentProperty.GetMethod == null + return symbols.CurrentProperty?.GetMethod == null ? [] : [symbols.CurrentProperty.GetMethod]; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractTypeParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractTypeParameterSymbolReferenceFinder.cs index b4f6514a4c740..7ce8df55a4d76 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractTypeParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/AbstractTypeParameterSymbolReferenceFinder.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs index 1fcfdd74b8f02..85aef6b00770c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorInitializerSymbolReferenceFinder.cs @@ -5,11 +5,9 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; @@ -31,8 +29,20 @@ protected override Task DetermineDocumentsToSearchAsync( return FindDocumentsAsync(project, documents, static async (document, name, cancellationToken) => { var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false); + if (index.ContainsBaseConstructorInitializer) - return true; + { + // if we have `partial class C { ... : base(...) }` we have to assume it might be a match, as the base + // type reference might be in a another part of the partial in another file. + if (index.ContainsPartialClass) + return true; + + // Otherwise, if it doesn't have any partial types, ensure that the base type name is referenced in the + // same file. e.g. `partial class C : B { ... base(...) }`. This allows us to greatly filter down the + // number of matches, presuming that most inheriting types in a project are not themselves partial. + if (index.ProbablyContainsIdentifier(name)) + return true; + } if (index.ProbablyContainsIdentifier(name)) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs index e7b4f0c98ed32..01ea7e24cdb94 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ConstructorSymbolReferenceFinder.cs @@ -24,6 +24,27 @@ private ConstructorSymbolReferenceFinder() protected override bool CanFind(IMethodSymbol symbol) => symbol.MethodKind is MethodKind.Constructor or MethodKind.StaticConstructor; + protected override ValueTask> DetermineCascadedSymbolsAsync(IMethodSymbol symbol, Solution solution, FindReferencesSearchOptions options, CancellationToken cancellationToken) + { + if (symbol.MethodKind is MethodKind.Constructor) + { + return new(GetOtherPartsOfPartial(symbol)); + } + + return new([]); + } + + private static ImmutableArray GetOtherPartsOfPartial(IMethodSymbol symbol) + { + if (symbol.PartialDefinitionPart != null) + return [symbol.PartialDefinitionPart]; + + if (symbol.PartialImplementationPart != null) + return [symbol.PartialImplementationPart]; + + return []; + } + protected override Task> DetermineGlobalAliasesAsync(IMethodSymbol symbol, Project project, CancellationToken cancellationToken) { var containingType = symbol.ContainingType; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs index d0347dca087c1..e961f81af4f5c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/EventSymbolReferenceFinder.cs @@ -32,7 +32,18 @@ protected sealed override ValueTask> DetermineCascadedSy .WhereAsArray(n => symbol.Equals(n.AssociatedSymbol)) .CastArray(); - return new(backingFields.Concat(associatedNamedTypes)); + return new(GetOtherPartsOfPartial(symbol).Concat(backingFields).Concat(associatedNamedTypes)); + } + + private static ImmutableArray GetOtherPartsOfPartial(IEventSymbol symbol) + { + if (symbol.PartialDefinitionPart != null) + return [symbol.PartialDefinitionPart]; + + if (symbol.PartialImplementationPart != null) + return [symbol.PartialImplementationPart]; + + return []; } protected sealed override async Task DetermineDocumentsToSearchAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs index 647a4cb6565e2..379f2f543d2bd 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/ExplicitConversionSymbolReferenceFinder.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs index b5cad870099dc..f0150097e7a10 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/MethodTypeParameterSymbolReferenceFinder.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Collections; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs index 1ab72fbc61b13..676c22b84c25f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/PreprocessingSymbolReferenceFinder.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.FindSymbols.Finders; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs index 6b1ffc51734be..27c7eddcdcd89 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/Finders/TypeParameterSymbolReferenceFinder.cs @@ -32,6 +32,8 @@ protected override Task DetermineDocumentsToSearchAsync( // parameter has a different name in different parts that we won't find it. However, // this only happens in error situations. It is not legal in C# to use a different // name for a type parameter in different parts. - return FindDocumentsAsync(project, documents, processResult, processResultData, cancellationToken, symbol.Name, symbol.ContainingType.Name); + return symbol.ContainingType is { IsExtension: true, ContainingType.Name: var staticClassName } + ? FindDocumentsAsync(project, documents, processResult, processResultData, cancellationToken, symbol.Name, staticClassName) + : FindDocumentsAsync(project, documents, processResult, processResultData, cancellationToken, symbol.Name, symbol.ContainingType.Name); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/MetadataUnifyingSymbolHashSet.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/MetadataUnifyingSymbolHashSet.cs index 7f31eb6cc5306..9d7bc98372cb6 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/MetadataUnifyingSymbolHashSet.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/MetadataUnifyingSymbolHashSet.cs @@ -3,11 +3,12 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.FindSymbols; -internal sealed class MetadataUnifyingSymbolHashSet : HashSet +internal sealed class MetadataUnifyingSymbolHashSet : HashSet, IPooled { private static readonly ObjectPool s_metadataUnifyingSymbolHashSetPool = new(() => []); @@ -15,12 +16,19 @@ public MetadataUnifyingSymbolHashSet() : base(MetadataUnifyingEquivalenceCompare { } - public static MetadataUnifyingSymbolHashSet AllocateFromPool() - => s_metadataUnifyingSymbolHashSetPool.Allocate(); + public static PooledDisposer GetInstance(out MetadataUnifyingSymbolHashSet instance) + { + instance = s_metadataUnifyingSymbolHashSetPool.Allocate(); + Debug.Assert(instance.Count == 0); + + return new PooledDisposer(instance); + } - public static void ClearAndFree(MetadataUnifyingSymbolHashSet set) + public void Free(bool discardLargeInstances) { - set.Clear(); - s_metadataUnifyingSymbolHashSetPool.Free(set); + // ignore discardLargeInstances as we don't limit our pooled hashset capacities + Clear(); + + s_metadataUnifyingSymbolHashSetPool.Free(this); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs index 4095dec2e2f8e..697da28bdee54 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/NoOpStreamingFindReferencesProgress.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs index 83667475722d8..92074d8deaa54 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/StreamingFindReferencesProgress.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 System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs b/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs index b9ba9ac99f35f..1fffd8c2b258f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/IStreamingFindReferencesProgress.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 System.Diagnostics; using System.Linq; diff --git a/src/Workspaces/Core/Portable/FindSymbols/ReferencedSymbol.cs b/src/Workspaces/Core/Portable/FindSymbols/ReferencedSymbol.cs index 45e5736b371ec..210d608fa30a3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/ReferencedSymbol.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/ReferencedSymbol.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.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs index 5b055b96423bd..c11f879bec1a9 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/Shared/AbstractSyntaxIndex_Persistence.cs @@ -23,7 +23,7 @@ internal abstract partial class AbstractSyntaxIndex /// that we will not try to read previously cached data from a prior version of roslyn with a different format and /// will instead regenerate all the indices with the new format. /// - private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("45"); + private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("46"); /// /// Cache of ParseOptions to a checksum for the contained diff --git a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs index 5e5bd005202b1..f3735feaa8a5b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/StreamingProgressCollector.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.Generic; using System.Collections.Immutable; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs index 2e6e6f5a832f9..0359e6fadce0f 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs @@ -4,12 +4,10 @@ using System.Collections.Generic; using System.Collections.Immutable; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs index cc875c63257e5..ff76b9b36a1ca 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/MetadataInfo.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols.SymbolTree; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs index 9a4229298e319..bd74857164f06 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.ContextInfo.cs @@ -37,7 +37,8 @@ public ContextInfo( bool containsCollectionInitializer, bool containsAttribute, bool containsDirective, - bool containsPrimaryConstructorBaseType) + bool containsPrimaryConstructorBaseType, + bool containsPartialClass) : this(predefinedTypes, predefinedOperators, ConvertToContainingNodeFlag( containsForEachStatement, @@ -58,7 +59,8 @@ public ContextInfo( containsCollectionInitializer, containsAttribute, containsDirective, - containsPrimaryConstructorBaseType)) + containsPrimaryConstructorBaseType, + containsPartialClass)) { } @@ -88,7 +90,8 @@ private static ContainingNodes ConvertToContainingNodeFlag( bool containsCollectionInitializer, bool containsAttribute, bool containsDirective, - bool containsPrimaryConstructorBaseType) + bool containsPrimaryConstructorBaseType, + bool containsPartialClass) { var containingNodes = ContainingNodes.None; @@ -111,6 +114,7 @@ private static ContainingNodes ConvertToContainingNodeFlag( containingNodes |= containsAttribute ? ContainingNodes.ContainsAttribute : 0; containingNodes |= containsDirective ? ContainingNodes.ContainsDirective : 0; containingNodes |= containsPrimaryConstructorBaseType ? ContainingNodes.ContainsPrimaryConstructorBaseType : 0; + containingNodes |= containsPartialClass ? ContainingNodes.ContainsPartialClass : 0; return containingNodes; } @@ -136,6 +140,9 @@ public bool ContainsImplicitObjectCreation public bool ContainsLockStatement => (_containingNodes & ContainingNodes.ContainsLockStatement) == ContainingNodes.ContainsLockStatement; + public bool ContainsPartialClass + => (_containingNodes & ContainingNodes.ContainsPartialClass) == ContainingNodes.ContainsPartialClass; + public bool ContainsUsingStatement => (_containingNodes & ContainingNodes.ContainsUsingStatement) == ContainingNodes.ContainsUsingStatement; @@ -225,6 +232,7 @@ private enum ContainingNodes ContainsAttribute = 1 << 16, ContainsDirective = 1 << 17, ContainsPrimaryConstructorBaseType = 1 << 18, + ContainsPartialClass = 1 << 19, } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index c0170cd52c5bb..6299181a4f5de 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -42,6 +41,7 @@ private static SyntaxTreeIndex CreateIndex( { var syntaxFacts = project.LanguageServices.GetRequiredService(); var ignoreCase = !syntaxFacts.IsCaseSensitive; + var partialKeywordKind = syntaxFacts.SyntaxKinds.PartialKeyword; var isCaseSensitive = !ignoreCase; GetIdentifierSet(ignoreCase, out var identifiers, out var escapedIdentifiers); @@ -75,6 +75,7 @@ private static SyntaxTreeIndex CreateIndex( var containsAttribute = false; var containsDirective = root.ContainsDirectives; var containsPrimaryConstructorBaseType = false; + var containsPartialClass = false; var predefinedTypes = (int)PredefinedType.None; var predefinedOperators = (int)PredefinedOperator.None; @@ -107,6 +108,7 @@ private static SyntaxTreeIndex CreateIndex( containsCollectionInitializer = containsCollectionInitializer || syntaxFacts.IsObjectCollectionInitializer(node); containsAttribute = containsAttribute || syntaxFacts.IsAttribute(node); containsPrimaryConstructorBaseType = containsPrimaryConstructorBaseType || syntaxFacts.IsPrimaryConstructorBaseType(node); + containsPartialClass = containsPartialClass || IsPartialClass(node); TryAddAliasInfo(syntaxFacts, ref aliasInfo, node); @@ -201,7 +203,8 @@ private static SyntaxTreeIndex CreateIndex( containsCollectionInitializer, containsAttribute, containsDirective, - containsPrimaryConstructorBaseType), + containsPrimaryConstructorBaseType, + containsPartialClass), aliasInfo, interceptsLocationInfo); } @@ -211,6 +214,21 @@ private static SyntaxTreeIndex CreateIndex( StringLiteralHashSetPool.ClearAndFree(stringLiterals); LongLiteralHashSetPool.ClearAndFree(longLiterals); } + + bool IsPartialClass(SyntaxNode node) + { + if (!syntaxFacts.IsClassDeclaration(node)) + return false; + + var modifiers = syntaxFacts.GetModifiers(node); + foreach (var modifier in modifiers) + { + if (modifier.RawKind == partialKeywordKind) + return true; + } + + return false; + } } private static bool IsGlobalSuppressMessageAttribute(ISyntaxFactsService syntaxFacts, SyntaxNode node) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs index 1ba1ff4d09862..7d4ff66ba23c7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Forwarders.cs @@ -32,6 +32,7 @@ internal sealed partial class SyntaxTreeIndex public bool ContainsImplicitObjectCreation => _contextInfo.ContainsImplicitObjectCreation; public bool ContainsIndexerMemberCref => _contextInfo.ContainsIndexerMemberCref; public bool ContainsLockStatement => _contextInfo.ContainsLockStatement; + public bool ContainsPartialClass => _contextInfo.ContainsPartialClass; public bool ContainsQueryExpression => _contextInfo.ContainsQueryExpression; public bool ContainsThisConstructorInitializer => _contextInfo.ContainsThisConstructorInitializer; public bool ContainsTupleExpressionOrTupleType => _contextInfo.ContainsTupleExpressionOrTupleType; diff --git a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs index e56e065978dd7..760a5b61de539 100644 --- a/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs +++ b/src/Workspaces/Core/Portable/Formatting/AbstractFormattingService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/Core/Portable/Log/EmptyLogger.cs b/src/Workspaces/Core/Portable/Log/EmptyLogger.cs index 2ab26a36b0288..7a118d9eae121 100644 --- a/src/Workspaces/Core/Portable/Log/EmptyLogger.cs +++ b/src/Workspaces/Core/Portable/Log/EmptyLogger.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; namespace Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/Workspaces/Core/Portable/Log/EtwLogger.cs b/src/Workspaces/Core/Portable/Log/EtwLogger.cs index 898ca25dc3110..fef6bf415270b 100644 --- a/src/Workspaces/Core/Portable/Log/EtwLogger.cs +++ b/src/Workspaces/Core/Portable/Log/EtwLogger.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.Diagnostics.Tracing; using System.Threading; diff --git a/src/Workspaces/Core/Portable/Log/IErrorLogger.cs b/src/Workspaces/Core/Portable/Log/IErrorLogger.cs index 73100b5a12b39..79325d0824dfe 100644 --- a/src/Workspaces/Core/Portable/Log/IErrorLogger.cs +++ b/src/Workspaces/Core/Portable/Log/IErrorLogger.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 Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Log/InteractionClass.cs b/src/Workspaces/Core/Portable/Log/InteractionClass.cs index fc0abeb2fe404..065a6f1d11e2a 100644 --- a/src/Workspaces/Core/Portable/Log/InteractionClass.cs +++ b/src/Workspaces/Core/Portable/Log/InteractionClass.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; namespace Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/Workspaces/Core/Portable/Log/WorkspaceErrorLogger.cs b/src/Workspaces/Core/Portable/Log/WorkspaceErrorLogger.cs index 597435b4dba98..c004f8e8ae079 100644 --- a/src/Workspaces/Core/Portable/Log/WorkspaceErrorLogger.cs +++ b/src/Workspaces/Core/Portable/Log/WorkspaceErrorLogger.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.Composition; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj index dcbbdeb8f2597..c2be591a52c53 100644 --- a/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Microsoft.CodeAnalysis.Workspaces.csproj @@ -37,9 +37,10 @@ - - + + + @@ -134,6 +135,7 @@ + diff --git a/src/Workspaces/Core/Portable/Options/IGlobalOptionService.cs b/src/Workspaces/Core/Portable/Options/IGlobalOptionService.cs index b5aaff7a08579..66b0223340ad5 100644 --- a/src/Workspaces/Core/Portable/Options/IGlobalOptionService.cs +++ b/src/Workspaces/Core/Portable/Options/IGlobalOptionService.cs @@ -2,10 +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.Generic; using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/Core/Portable/Options/OptionKey.cs b/src/Workspaces/Core/Portable/Options/OptionKey.cs index f0e20986fe191..d35c100b36977 100644 --- a/src/Workspaces/Core/Portable/Options/OptionKey.cs +++ b/src/Workspaces/Core/Portable/Options/OptionKey.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/Core/Portable/Options/OptionSet.cs b/src/Workspaces/Core/Portable/Options/OptionSet.cs index fa85f4bd826ad..77b8e5c7f7993 100644 --- a/src/Workspaces/Core/Portable/Options/OptionSet.cs +++ b/src/Workspaces/Core/Portable/Options/OptionSet.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.CodeStyle; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs index d5fefcb9143b4..19b08a1276974 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatchKind.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 Roslyn.Utilities; namespace Microsoft.CodeAnalysis.PatternMatching; diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs index 1b25dd803c39b..c485e69071993 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.PatternSegment.cs @@ -2,10 +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. -#nullable disable - using System; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.PatternMatching; diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs index 01fbfb1ab3bef..d00c5e73b1ee2 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.TextChunk.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 Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt index 8b137891791fe..b875c8fa4eccf 100644 --- a/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Workspaces/Core/Portable/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ - +*REMOVED*abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode +Microsoft.CodeAnalysis.Editing.SyntaxGenerator.WithModifiers(Microsoft.CodeAnalysis.SyntaxNode declaration, Microsoft.CodeAnalysis.Editing.DeclarationModifiers modifiers) -> Microsoft.CodeAnalysis.SyntaxNode diff --git a/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs b/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs index 19cb44579d20b..91487e267bb9f 100644 --- a/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs +++ b/src/Workspaces/Core/Portable/Recommendations/AbstractRecommendationServiceRunner.cs @@ -388,7 +388,17 @@ protected ImmutableArray LookupSymbolsInContainer( INamespaceOrTypeSymbol container, int position, bool excludeInstance) { if (excludeInstance) - return _context.SemanticModel.LookupStaticMembers(position, container); + { + var staticMembers = _context.SemanticModel.LookupStaticMembers(position, container); + if (container is not INamedTypeSymbol) + return staticMembers; + + var staticExtensionsMembers = _context.SemanticModel + .LookupSymbols(position, container, includeReducedExtensionMethods: true) + .WhereAsArray(static (s, staticMembers) => s is { IsStatic: true, ContainingType.IsExtension: true } && !staticMembers.Contains(s), staticMembers); + + return [.. staticMembers, .. staticExtensionsMembers]; + } var containerMembers = SuppressDefaultTupleElements( container, diff --git a/src/Workspaces/Core/Portable/Remote/ExportRemoteServiceCallbackDispatcherAttribute.cs b/src/Workspaces/Core/Portable/Remote/ExportRemoteServiceCallbackDispatcherAttribute.cs index 151b79a118599..621010e21f7a0 100644 --- a/src/Workspaces/Core/Portable/Remote/ExportRemoteServiceCallbackDispatcherAttribute.cs +++ b/src/Workspaces/Core/Portable/Remote/ExportRemoteServiceCallbackDispatcherAttribute.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Core/Portable/Remote/IRemoteKeepAliveService.cs b/src/Workspaces/Core/Portable/Remote/IRemoteKeepAliveService.cs index 024db2a78d641..44fef3920f41a 100644 --- a/src/Workspaces/Core/Portable/Remote/IRemoteKeepAliveService.cs +++ b/src/Workspaces/Core/Portable/Remote/IRemoteKeepAliveService.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs index ed62b06e69b62..8d8f373ce19be 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.Session.cs @@ -10,7 +10,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs index 8bffb26643a13..103368e0802a2 100644 --- a/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs +++ b/src/Workspaces/Core/Portable/Rename/ConflictEngine/ConflictResolver.cs @@ -11,7 +11,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs index ba47ab9ec4d61..953abf73dcbaa 100644 --- a/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs +++ b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs @@ -2,15 +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.Composition; using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Rename.ConflictEngine; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs b/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs index 15d6b885e6b8b..7df6929dcabe9 100644 --- a/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs +++ b/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs @@ -8,7 +8,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Rename.ConflictEngine; diff --git a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs index def784de0012b..471b1941a4742 100644 --- a/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs +++ b/src/Workspaces/Core/Portable/Rename/RenameUtilities.cs @@ -327,55 +327,24 @@ public static async Task FindDefinitionSymbolAsync( // If we're renaming a property, it might be a synthesized property for a method // backing field. - if (symbol.Kind == SymbolKind.Parameter) + if (symbol is IParameterSymbol { ContainingSymbol: IMethodSymbol { AssociatedSymbol: IPropertySymbol associatedParameterProperty } containingMethod }) { - if (symbol.ContainingSymbol.Kind == SymbolKind.Method) - { - var containingMethod = (IMethodSymbol)symbol.ContainingSymbol; - if (containingMethod.AssociatedSymbol is IPropertySymbol) - { - var associatedPropertyOrEvent = (IPropertySymbol)containingMethod.AssociatedSymbol; - var ordinal = containingMethod.Parameters.IndexOf((IParameterSymbol)symbol); - if (ordinal < associatedPropertyOrEvent.Parameters.Length) - { - return associatedPropertyOrEvent.Parameters[ordinal]; - } - } - } + var ordinal = containingMethod.Parameters.IndexOf((IParameterSymbol)symbol); + if (ordinal < associatedParameterProperty.Parameters.Length) + return associatedParameterProperty.Parameters[ordinal]; } // if we are renaming a compiler generated delegate for an event, cascade to the event - if (symbol.Kind == SymbolKind.NamedType) - { - var typeSymbol = (INamedTypeSymbol)symbol; - if (typeSymbol.IsImplicitlyDeclared && typeSymbol.IsDelegateType() && typeSymbol.AssociatedSymbol != null) - { - return typeSymbol.AssociatedSymbol; - } - } + if (symbol is INamedTypeSymbol { IsImplicitlyDeclared: true, TypeKind: TypeKind.Delegate, AssociatedSymbol: not null } typeSymbol) + return typeSymbol.AssociatedSymbol; // If we are renaming a constructor or destructor, we wish to rename the whole type - if (symbol.Kind == SymbolKind.Method) - { - var methodSymbol = (IMethodSymbol)symbol; - if (methodSymbol.MethodKind is MethodKind.Constructor or - MethodKind.StaticConstructor or - MethodKind.Destructor) - { - return methodSymbol.ContainingType; - } - } + if (symbol is IMethodSymbol { MethodKind: MethodKind.Constructor or MethodKind.StaticConstructor or MethodKind.Destructor }) + return symbol.ContainingType; // If we are renaming a backing field for a property, cascade to the property - if (symbol.Kind == SymbolKind.Field) - { - var fieldSymbol = (IFieldSymbol)symbol; - if (fieldSymbol.IsImplicitlyDeclared && - fieldSymbol.AssociatedSymbol.IsKind(SymbolKind.Property)) - { - return fieldSymbol.AssociatedSymbol; - } - } + if (symbol is IFieldSymbol { IsImplicitlyDeclared: true, AssociatedSymbol: IPropertySymbol associatedProperty }) + return associatedProperty; // in case this is e.g. an overridden property accessor, we'll treat the property itself as the definition symbol var property = await TryGetPropertyFromAccessorOrAnOverrideAsync(bestSymbol, solution, cancellationToken).ConfigureAwait(false); diff --git a/src/Workspaces/Core/Portable/Rename/Renamer.SyncNamespaceDocumentAction.cs b/src/Workspaces/Core/Portable/Rename/Renamer.SyncNamespaceDocumentAction.cs index dcb74146489ce..939aa383eb500 100644 --- a/src/Workspaces/Core/Portable/Rename/Renamer.SyncNamespaceDocumentAction.cs +++ b/src/Workspaces/Core/Portable/Rename/Renamer.SyncNamespaceDocumentAction.cs @@ -7,7 +7,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ChangeNamespace; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Core/Portable/Rename/Renamer.cs b/src/Workspaces/Core/Portable/Rename/Renamer.cs index ff7be7c19f700..7e79da036c022 100644 --- a/src/Workspaces/Core/Portable/Rename/Renamer.cs +++ b/src/Workspaces/Core/Portable/Rename/Renamer.cs @@ -8,8 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.SearchResult.cs b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.SearchResult.cs index 09b5525a06faa..4c469290d3611 100644 --- a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.SearchResult.cs +++ b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.SearchResult.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.FindSymbols; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Rename; diff --git a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.cs b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.cs index 35dbd0265304c..6c3440d1a04d1 100644 --- a/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.cs +++ b/src/Workspaces/Core/Portable/Rename/SymbolicRenameLocations.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs index 58b31221b2e31..a9cadd71cb2a9 100644 --- a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Serialization/IOptionsSerializationService.cs b/src/Workspaces/Core/Portable/Serialization/IOptionsSerializationService.cs index a742660c977ca..8e2e8c4f4f076 100644 --- a/src/Workspaces/Core/Portable/Serialization/IOptionsSerializationService.cs +++ b/src/Workspaces/Core/Portable/Serialization/IOptionsSerializationService.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; using Microsoft.CodeAnalysis.Host; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs index 2a46a92588341..340d7a155c9fe 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializableSourceText.cs @@ -15,7 +15,6 @@ using static Microsoft.CodeAnalysis.Host.TemporaryStorageService; #if DEBUG -using System.Linq; #endif namespace Microsoft.CodeAnalysis.Serialization; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/BackgroundAnalysisScopeExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/BackgroundAnalysisScopeExtensions.cs index 055abe3c1a694..255c995a19327 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/BackgroundAnalysisScopeExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/BackgroundAnalysisScopeExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.SolutionCrawler; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs index dc01cc80dc577..e1d14075ad59c 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/DocumentExtensions.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/FileLinePositionSpanExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/FileLinePositionSpanExtensions.cs index d7ed3e8f755da..ad82edce0d405 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/FileLinePositionSpanExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/FileLinePositionSpanExtensions.cs @@ -4,7 +4,6 @@ using System; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs index 2c72995aba362..ec237d9a40532 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/IMethodSymbolExtensions.cs @@ -3,12 +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.Linq; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index 9cf71fefedc3f..37d4a15b0195a 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -645,7 +645,17 @@ private static bool ElementNameIs(XElement element, string name) public static ImmutableArray FilterToVisibleAndBrowsableSymbols( this ImmutableArray symbols, bool hideAdvancedMembers, Compilation compilation) where T : ISymbol { - symbols = symbols.RemoveOverriddenSymbolsWithinSet(); + if (symbols.Length == 0) + return []; + + using var _ = MetadataUnifyingSymbolHashSet.GetInstance(out var overriddenSymbols); + + foreach (var symbol in symbols) + { + var overriddenMember = symbol.GetOverriddenMember(); + if (overriddenMember != null) + overriddenSymbols.Add(overriddenMember); + } // Since all symbols are from the same compilation, find the required attribute // constructors once and reuse. @@ -653,30 +663,19 @@ public static ImmutableArray FilterToVisibleAndBrowsableSymbols( // PERF: HasUnsupportedMetadata may require recreating the syntax tree to get the base class, so first // check to see if we're referencing a symbol defined in source. - return symbols.WhereAsArray((s, arg) => + var filteredSymbols = symbols.WhereAsArray(static (s, arg) => // Check if symbol is namespace (which is always visible) first to avoid realizing all locations // of each namespace symbol, which might end up allocating in LOH + !arg.overriddenSymbols.Contains(s) && (s is INamespaceSymbol || s.Locations.Any(static loc => loc.IsInSource) || !s.HasUnsupportedMetadata) && !s.IsDestructor() && s.IsEditorBrowsable( arg.hideAdvancedMembers, arg.editorBrowsableInfo.Compilation, arg.editorBrowsableInfo), - (hideAdvancedMembers, editorBrowsableInfo)); - } - - private static ImmutableArray RemoveOverriddenSymbolsWithinSet(this ImmutableArray symbols) where T : ISymbol - { - var overriddenSymbols = new MetadataUnifyingSymbolHashSet(); - - foreach (var symbol in symbols) - { - var overriddenMember = symbol.GetOverriddenMember(); - if (overriddenMember != null && !overriddenSymbols.Contains(overriddenMember)) - overriddenSymbols.Add(overriddenMember); - } + arg: (hideAdvancedMembers, editorBrowsableInfo, overriddenSymbols)); - return symbols.WhereAsArray(s => !overriddenSymbols.Contains(s)); + return filteredSymbols; } public static ImmutableArray FilterToVisibleAndBrowsableSymbolsAndNotUnsafeSymbols( diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SafeHandleLease.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SafeHandleLease.cs index c97015d5e201b..fcf5bcc237321 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SafeHandleLease.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SafeHandleLease.cs @@ -4,7 +4,6 @@ using System; using System.Runtime.InteropServices; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs index 04799df307317..343deb91c658c 100644 --- a/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs +++ b/src/Workspaces/Core/Portable/Shared/TestHooks/FeatureAttribute.cs @@ -18,6 +18,7 @@ internal static class FeatureAttribute public const string CodeModel = nameof(CodeModel); public const string ColorScheme = nameof(ColorScheme); public const string CompletionSet = nameof(CompletionSet); + public const string CopilotImplementNotImplementedException = nameof(CopilotImplementNotImplementedException); public const string CopilotSuggestions = nameof(CopilotSuggestions); public const string DesignerAttributes = nameof(DesignerAttributes); public const string DiagnosticService = nameof(DiagnosticService); diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Graph.cs b/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Graph.cs index 05632f8418f70..f902c29113e50 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Graph.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Graph.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Node.cs b/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Node.cs index f1d81852e6810..14f6a8810b12d 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Node.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/ExtensionOrderer.Node.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.Generic; diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/SemanticMap.Walker.cs b/src/Workspaces/Core/Portable/Shared/Utilities/SemanticMap.Walker.cs index e17598e9f492d..bfc0b801fefa6 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/SemanticMap.Walker.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/SemanticMap.Walker.cs @@ -4,7 +4,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs b/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs index 9a99200dcc57f..b97e0f47d1ef7 100644 --- a/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs +++ b/src/Workspaces/Core/Portable/Shared/Utilities/XmlFragmentParser.cs @@ -9,7 +9,6 @@ using System.IO; using System.Xml; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs b/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs index 31c3ecc9163ef..3b456841701fa 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/InvocationReasons_Constants.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using System.Collections.Immutable; - namespace Microsoft.CodeAnalysis.SolutionCrawler; internal readonly partial struct InvocationReasons diff --git a/src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs b/src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs index 04cde7b87a27b..41f9e02ce7ed1 100644 --- a/src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.cs +++ b/src/Workspaces/Core/Portable/SolutionCrawler/PredefinedInvocationReasons.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 - namespace Microsoft.CodeAnalysis.SolutionCrawler; internal static class PredefinedInvocationReasons diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Database.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Database.cs index aa6ae8b5a5c90..67a77bba01a78 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Database.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Database.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.SQLite.v2; internal enum Database diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs index 3a77291393a0d..f031e22a1522d 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/Interop/SqlStatement.cs @@ -5,7 +5,6 @@ using System; using System.Text; using Microsoft.CodeAnalysis.SQLite.Interop; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2.Interop; diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorageService.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorageService.cs index 9f1570f00f3b6..c17666916d22d 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorageService.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorageService.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Storage; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2; diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs index 1021f3f6e872e..74befe59bd8c9 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_FlushWrites.cs @@ -5,7 +5,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2; diff --git a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs index 75fd83781412a..4acf727ffb6be 100644 --- a/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs +++ b/src/Workspaces/Core/Portable/Storage/SQLite/v2/SQLitePersistentStorage_StringIds.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.SQLite.Interop; using Microsoft.CodeAnalysis.SQLite.v2.Interop; using Microsoft.CodeAnalysis.Storage; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SQLite.v2; diff --git a/src/Workspaces/Core/Portable/Telemetry/ITelemetryLog.cs b/src/Workspaces/Core/Portable/Telemetry/ITelemetryLog.cs index 585b3ec32a858..c96bba2b4bb39 100644 --- a/src/Workspaces/Core/Portable/Telemetry/ITelemetryLog.cs +++ b/src/Workspaces/Core/Portable/Telemetry/ITelemetryLog.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using Microsoft.CodeAnalysis.Internal.Log; namespace Microsoft.CodeAnalysis.Telemetry; diff --git a/src/Workspaces/Core/Portable/Workspace/DocumentActiveContextChangedEventArgs.cs b/src/Workspaces/Core/Portable/Workspace/DocumentActiveContextChangedEventArgs.cs index 8f5d28b512b64..c477787e6594e 100644 --- a/src/Workspaces/Core/Portable/Workspace/DocumentActiveContextChangedEventArgs.cs +++ b/src/Workspaces/Core/Portable/Workspace/DocumentActiveContextChangedEventArgs.cs @@ -2,11 +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. -#nullable disable - using System; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/DocumentEventArgs.cs b/src/Workspaces/Core/Portable/Workspace/DocumentEventArgs.cs index bee387ad8a8b1..fd930457e41ce 100644 --- a/src/Workspaces/Core/Portable/Workspace/DocumentEventArgs.cs +++ b/src/Workspaces/Core/Portable/Workspace/DocumentEventArgs.cs @@ -2,10 +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. -#nullable disable - using System; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Caching/IWorkspaceCacheService.cs b/src/Workspaces/Core/Portable/Workspace/Host/Caching/IWorkspaceCacheService.cs index 6c2cf88538f16..2e4c486d9219b 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Caching/IWorkspaceCacheService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Caching/IWorkspaceCacheService.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; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/AbstractSpanMappingService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/AbstractSpanMappingService.cs index e297815f4850d..efde1ef60f746 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/AbstractSpanMappingService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/AbstractSpanMappingService.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs index b30eb4f687061..0cdac83b98a19 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentExcerptService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentOperationService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentOperationService.cs index 67185e9a4a0a7..bb013103bee19 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentOperationService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/IDocumentOperationService.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 - namespace Microsoft.CodeAnalysis.Host; /// diff --git a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/ISpanMappingService.cs b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/ISpanMappingService.cs index acb0bf65e0f9f..99a99fdeee7d7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/ISpanMappingService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/DocumentService/ISpanMappingService.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.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/IWorkspaceEventListenerProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/IWorkspaceEventListenerProvider.cs index c60a730c9a519..b68a7371e27a0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/EventListener/IWorkspaceEventListenerProvider.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/EventListener/IWorkspaceEventListenerProvider.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.Generic; using System.Collections.Immutable; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/HostServices.cs b/src/Workspaces/Core/Portable/Workspace/Host/HostServices.cs index 7e433f92f2b14..d2b49dc35382a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/HostServices.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/HostServices.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 - namespace Microsoft.CodeAnalysis.Host; /// diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Mef/ILanguageServiceFactory.cs b/src/Workspaces/Core/Portable/Workspace/Host/Mef/ILanguageServiceFactory.cs index 5917bff8aa89b..ad56650e5911f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Mef/ILanguageServiceFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Mef/ILanguageServiceFactory.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 - namespace Microsoft.CodeAnalysis.Host.Mef; /// diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Mef/IWorkspaceServiceFactory.cs b/src/Workspaces/Core/Portable/Workspace/Host/Mef/IWorkspaceServiceFactory.cs index 271ad1bf80719..bad0c61f57215 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Mef/IWorkspaceServiceFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Mef/IWorkspaceServiceFactory.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 - namespace Microsoft.CodeAnalysis.Host.Mef; /// diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/DefaultAnalyzerService.cs b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/DefaultAnalyzerService.cs index 55d4ea4546492..4e50f52536036 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/DefaultAnalyzerService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/DefaultAnalyzerService.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.Composition; using Microsoft.CodeAnalysis.Host.Mef; @@ -13,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Host; [ExportWorkspaceService(typeof(IAnalyzerService)), Shared] internal sealed class DefaultAnalyzerService : IAnalyzerService { - private readonly DefaultAnalyzerAssemblyLoader _loader = new(); + private readonly AnalyzerAssemblyLoader _loader = new(); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerAssemblyLoaderProvider.cs b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerAssemblyLoaderProvider.cs index 9acb18e7fd0e8..6a74e816a1bf7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerAssemblyLoaderProvider.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerAssemblyLoaderProvider.cs @@ -35,12 +35,13 @@ internal interface IAnalyzerAssemblyLoaderProvider : IWorkspaceService /// internal abstract class AbstractAnalyzerAssemblyLoaderProvider : IAnalyzerAssemblyLoaderProvider { - private readonly ImmutableArray _externalResolvers; +#if NET private readonly Lazy _shadowCopyLoader; + private readonly ImmutableArray _assemblyResolvers; - public AbstractAnalyzerAssemblyLoaderProvider(IEnumerable externalResolvers) + public AbstractAnalyzerAssemblyLoaderProvider(IEnumerable assemblyResolvers) { - _externalResolvers = [.. externalResolvers]; + _assemblyResolvers = [.. assemblyResolvers]; _shadowCopyLoader = new(CreateNewShadowCopyLoader); } @@ -48,17 +49,46 @@ public IAnalyzerAssemblyLoaderInternal SharedShadowCopyLoader => _shadowCopyLoader.Value; public IAnalyzerAssemblyLoaderInternal CreateNewShadowCopyLoader() - => this.WrapLoader(DefaultAnalyzerAssemblyLoader.CreateNonLockingLoader( + => this.WrapLoader(AnalyzerAssemblyLoader.CreateNonLockingLoader( Path.Combine(Path.GetTempPath(), nameof(Roslyn), "AnalyzerAssemblyLoader"), - _externalResolvers)); + pathResolvers: default, + _assemblyResolvers)); +#else + private readonly Lazy _shadowCopyLoader; + + public AbstractAnalyzerAssemblyLoaderProvider() + { + _shadowCopyLoader = new(CreateNewShadowCopyLoader); + } + + public IAnalyzerAssemblyLoaderInternal SharedShadowCopyLoader + => _shadowCopyLoader.Value; + + public IAnalyzerAssemblyLoaderInternal CreateNewShadowCopyLoader() + => this.WrapLoader(AnalyzerAssemblyLoader.CreateNonLockingLoader( + Path.Combine(Path.GetTempPath(), nameof(Roslyn), "AnalyzerAssemblyLoader"), + pathResolvers: default)); +#endif protected virtual IAnalyzerAssemblyLoaderInternal WrapLoader(IAnalyzerAssemblyLoaderInternal loader) => loader; } [ExportWorkspaceService(typeof(IAnalyzerAssemblyLoaderProvider)), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class DefaultAnalyzerAssemblyLoaderProvider( - [ImportMany] IEnumerable externalResolvers) - : AbstractAnalyzerAssemblyLoaderProvider(externalResolvers); +internal sealed class DefaultAnalyzerAssemblyLoaderProvider : AbstractAnalyzerAssemblyLoaderProvider +{ +#if NET + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DefaultAnalyzerAssemblyLoaderProvider([ImportMany] IEnumerable assemblyResolvers) + : base(assemblyResolvers) + { + } +#else + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public DefaultAnalyzerAssemblyLoaderProvider() + { + } +#endif +} diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerService.cs b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerService.cs index 9fbfd46fab7b6..f4c21256290f5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IAnalyzerService.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 - namespace Microsoft.CodeAnalysis.Host; // Obsolete - used only for MSBuild workspace extensibility, diff --git a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IMetadataService.cs b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IMetadataService.cs index be13a41b192b2..cfc670a491954 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IMetadataService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/Metadata/IMetadataService.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 - namespace Microsoft.CodeAnalysis.Host; internal interface IMetadataService : IWorkspaceService diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageConfiguration.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageConfiguration.cs index 0e747988fbb70..d7cf48a9fd132 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageConfiguration.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageConfiguration.cs @@ -10,7 +10,6 @@ using System.Linq; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Storage; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageFaultInjectionService.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageFaultInjectionService.cs index 3b5ddfdb06549..d193b73108df5 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageFaultInjectionService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageFaultInjectionService.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; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorage.cs b/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorage.cs index d458ba5ca481c..dd5fbb1842f03 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorage.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/TemporaryStorage/ITemporaryStorage.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.IO; using System.Threading; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IRuleSetFile.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IRuleSetFile.cs index 1bf90df106eb1..1394b208f1856 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IRuleSetFile.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/IRuleSetFile.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.ProjectUpdateState.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.ProjectUpdateState.cs index 93962e6fa2e0f..b77f38e86c83c 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.ProjectUpdateState.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.ProjectUpdateState.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Workspaces.ProjectSystem; diff --git a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs index e31420944a198..a08cafbe655a5 100644 --- a/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs +++ b/src/Workspaces/Core/Portable/Workspace/ProjectSystem/ProjectSystemProjectFactory.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs index 59be45860e30e..c966aea508619 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Document.cs @@ -7,7 +7,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentDiagnostic.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentDiagnostic.cs index 6a616117f2836..0ed4eab2779d2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentDiagnostic.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentDiagnostic.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 - namespace Microsoft.CodeAnalysis; public class DocumentDiagnostic(WorkspaceDiagnosticKind kind, string message, DocumentId documentId) : WorkspaceDiagnostic(kind, message) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.EquivalenceResult.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.EquivalenceResult.cs index 730d282e1330c..b4f7e9449b0a7 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.EquivalenceResult.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.EquivalenceResult.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 - namespace Microsoft.CodeAnalysis; internal partial class DocumentState diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs index 07baa5aaf65f9..df54d1161d0ef 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentState.cs @@ -3,9 +3,7 @@ // 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 System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text; @@ -497,6 +495,20 @@ internal DocumentState UpdateTree(SyntaxNode newRoot, PreservationMode mode) // use the encoding that we get from the new root var encoding = newRoot.SyntaxTree.Encoding; + if (encoding is null) + { + // The new tree doesn't specify an encoding. For these cases, continue to use the previous encoding of the + // document. + if (TryGetSyntaxTree(out var priorTree)) + { + // this is most likely available since UpdateTree is normally called after modifying the existing tree. + encoding = priorTree.Encoding; + } + else if (TryGetText(out var priorText)) + { + encoding = priorText.Encoding; + } + } var syntaxTreeFactory = LanguageServices.GetRequiredService(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/IDocumentTextDifferencingService.cs b/src/Workspaces/Core/Portable/Workspace/Solution/IDocumentTextDifferencingService.cs index d89e12d177a2a..b84bd15888288 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/IDocumentTextDifferencingService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/IDocumentTextDifferencingService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/PreservationMode.cs b/src/Workspaces/Core/Portable/Workspace/Solution/PreservationMode.cs index 5dd506da3a935..815ab649152b2 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/PreservationMode.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/PreservationMode.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 - namespace Microsoft.CodeAnalysis; /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.EquivalenceResult.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.EquivalenceResult.cs index dd937f489b9b2..681cc69572653 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.EquivalenceResult.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.EquivalenceResult.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 - namespace Microsoft.CodeAnalysis; public partial class Project diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs index 8c2c27c46d89f..7cde4a683971c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Project.cs @@ -544,36 +544,6 @@ public Task GetDependentSemanticVersionAsync(CancellationToken can public Task GetSemanticVersionAsync(CancellationToken cancellationToken = default) => State.GetSemanticVersionAsync(cancellationToken); - /// - /// Calculates a checksum that contains a project's checksum along with a checksum for each of the project's - /// transitive dependencies. - /// - /// - /// This checksum calculation can be used for cases where a feature needs to know if the semantics in this project - /// changed. For example, for diagnostics or caching computed semantic data. The goal is to ensure that changes to - /// - /// Files inside the current project - /// Project properties of the current project - /// Visible files in referenced projects - /// Project properties in referenced projects - /// - /// are reflected in the metadata we keep so that comparing solutions accurately tells us when we need to recompute - /// semantic work. - /// - /// This method of checking for changes has a few important properties that differentiate it from other methods of determining project version. - /// - /// Changes to methods inside the current project will be reflected to compute updated diagnostics. - /// does not change as it only returns top level changes. - /// Reloading a project without making any changes will re-use cached diagnostics. - /// changes as the project is removed, then added resulting in a version change. - /// - /// - /// This checksum is also affected by the for this project. - /// As such, it is not usable across different sessions of a particular host. - /// - internal Task GetDependentChecksumAsync(CancellationToken cancellationToken) - => Solution.CompilationState.GetDependentChecksumAsync(this.Id, cancellationToken); - /// /// Creates a new instance of this project updated to have the new assembly name. /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectCone.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectCone.cs index 19b6c7f9ee372..34bdf18a7e6d0 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectCone.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectCone.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Frozen; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_AddProject.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_AddProject.cs index a4f220b6b68d8..da0a6f30bc601 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_AddProject.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_AddProject.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. -using System.Collections.Immutable; - namespace Microsoft.CodeAnalysis; public partial class ProjectDependencyGraph diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDiagnostic.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDiagnostic.cs index 510311dd2dc0b..29454c0e31cb8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDiagnostic.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDiagnostic.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 - namespace Microsoft.CodeAnalysis; public class ProjectDiagnostic(WorkspaceDiagnosticKind kind, string message, ProjectId projectId) : WorkspaceDiagnostic(kind, message) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs index 66568e3f6d5f7..2f92683ad69dd 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.ICompilationTracker.cs @@ -53,7 +53,6 @@ bool ContainsAssemblyOrModuleOrDynamic( Task GetDependentVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); Task GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); - Task GetDependentChecksumAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken); /// /// Gets the source generator files generated by this . Fork(newProjectState, translate); + /// /// Creates a new instance of the compilation info, retaining any already built /// compilation state as the now 'old' state /// - public ICompilationTracker Fork( + public RegularCompilationTracker Fork( ProjectState newProjectState, TranslationAction? translate) { @@ -680,7 +683,10 @@ private async Task HasSuccessfullyLoadedSlowAsync( return finalState.HasSuccessfullyLoaded; } - public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration) + ICompilationTracker ICompilationTracker.WithCreateCreationPolicy(bool forceRegeneration) + => WithCreateCreationPolicy(forceRegeneration); + + public RegularCompilationTracker WithCreateCreationPolicy(bool forceRegeneration) { var state = this.ReadState(); @@ -736,7 +742,10 @@ public ICompilationTracker WithCreateCreationPolicy(bool forceRegeneration) skeletonReferenceCacheToClone: _skeletonReferenceCache); } - public ICompilationTracker WithDoNotCreateCreationPolicy() + ICompilationTracker ICompilationTracker.WithDoNotCreateCreationPolicy() + => WithDoNotCreateCreationPolicy(); + + public RegularCompilationTracker WithDoNotCreateCreationPolicy() { var state = this.ReadState(); @@ -1008,7 +1017,6 @@ public CompilationTrackerValidationException(string message, Exception inner) : private AsyncLazy? _lazyDependentVersion; private AsyncLazy? _lazyDependentSemanticVersion; - private AsyncLazy? _lazyDependentChecksum; public Task GetDependentVersionAsync( SolutionCompilationState compilationState, CancellationToken cancellationToken) @@ -1087,66 +1095,6 @@ private async Task ComputeDependentSemanticVersionAsync( return version; } - public Task GetDependentChecksumAsync( - SolutionCompilationState compilationState, CancellationToken cancellationToken) - { - if (_lazyDependentChecksum == null) - { - // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange( - ref _lazyDependentChecksum, - AsyncLazy.Create(static (arg, c) => - arg.self.ComputeDependentChecksumAsync(arg.compilationState, c), - arg: (self: this, compilationState)), - null); - } - - return _lazyDependentChecksum.GetValueAsync(cancellationToken); - } - - private async Task ComputeDependentChecksumAsync( - SolutionCompilationState solution, CancellationToken cancellationToken) - { - using var _ = ArrayBuilder.GetInstance(out var tempChecksumArray); - - // Mix in the SG information for this project. That way if it changes, we will have a different - // checksum (since semantics could have changed because of this). - if (solution.SourceGeneratorExecutionVersionMap.Map.TryGetValue(this.ProjectState.Id, out var executionVersion)) - tempChecksumArray.Add(executionVersion.Checksum); - - // Get the checksum for the project itself. - var projectChecksum = await this.ProjectState.GetChecksumAsync(cancellationToken).ConfigureAwait(false); - tempChecksumArray.Add(projectChecksum); - - // Calculate a checksum this project and for each dependent project that could affect semantics for this - // project. We order the projects so that we are resilient to the underlying in-memory graph structure - // changing this arbitrarily. We do not want that to cause us to change our semantic version.. Note: we - // use the project filepath+name as a unique way to reference a project. This matches the logic in our - // persistence-service implementation as to how information is associated with a project. - var transitiveDependencies = solution.SolutionState.GetProjectDependencyGraph().GetProjectsThatThisProjectTransitivelyDependsOn(this.ProjectState.Id); - var orderedProjectIds = transitiveDependencies.OrderBy(id => - { - var depProject = solution.SolutionState.GetRequiredProjectState(id); - return (depProject.FilePath, depProject.Name); - }); - - foreach (var projectId in orderedProjectIds) - { - // Mix in the SG information for the dependent project. That way if it changes, we will have a - // different checksum (since semantics could have changed because of this). - if (solution.SourceGeneratorExecutionVersionMap.Map.TryGetValue(projectId, out executionVersion)) - tempChecksumArray.Add(executionVersion.Checksum); - - // Note that these checksums should only actually be calculated once, if the project is unchanged - // the same checksum will be returned. - var referencedProject = solution.SolutionState.GetRequiredProjectState(projectId); - var referencedProjectChecksum = await referencedProject.GetChecksumAsync(cancellationToken).ConfigureAwait(false); - tempChecksumArray.Add(referencedProjectChecksum); - } - - return Checksum.Create(tempChecksumArray); - } - #endregion } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.WithFrozenSourceGeneratedDocumentsCompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.WithFrozenSourceGeneratedDocumentsCompilationTracker.cs index 0f56b6fa9968d..c16d37789870a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.WithFrozenSourceGeneratedDocumentsCompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.WithFrozenSourceGeneratedDocumentsCompilationTracker.cs @@ -28,8 +28,6 @@ private sealed class WithFrozenSourceGeneratedDocumentsCompilationTracker : ICom { private readonly TextDocumentStates _replacementDocumentStates; - private AsyncLazy? _lazyDependentChecksum; - /// /// The lazily-produced compilation that has the generated document updated. This is initialized by call to /// . @@ -37,7 +35,7 @@ private sealed class WithFrozenSourceGeneratedDocumentsCompilationTracker : ICom [DisallowNull] private Compilation? _compilationWithReplacements; - public ICompilationTracker UnderlyingTracker { get; } + public RegularCompilationTracker UnderlyingTracker { get; } public ProjectState ProjectState => UnderlyingTracker.ProjectState; public GeneratorDriver? GeneratorDriver => UnderlyingTracker.GeneratorDriver; @@ -48,7 +46,7 @@ private sealed class WithFrozenSourceGeneratedDocumentsCompilationTracker : ICom private SkeletonReferenceCache _skeletonReferenceCache; public WithFrozenSourceGeneratedDocumentsCompilationTracker( - ICompilationTracker underlyingTracker, + RegularCompilationTracker underlyingTracker, TextDocumentStates replacementDocumentStates) { this.UnderlyingTracker = underlyingTracker; @@ -145,28 +143,6 @@ public Task GetDependentVersionAsync(SolutionCompilationState comp public Task GetDependentSemanticVersionAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken) => UnderlyingTracker.GetDependentSemanticVersionAsync(compilationState, cancellationToken); - public Task GetDependentChecksumAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken) - { - if (_lazyDependentChecksum == null) - { - var tmp = compilationState; // temp. local to avoid a closure allocation for the fast path - // note: solution is captured here, but it will go away once GetValueAsync executes. - Interlocked.CompareExchange( - ref _lazyDependentChecksum, - AsyncLazy.Create(static (arg, c) => - arg.self.ComputeDependentChecksumAsync(arg.tmp, c), - arg: (self: this, tmp)), - null); - } - - return _lazyDependentChecksum.GetValueAsync(cancellationToken); - } - - private async Task ComputeDependentChecksumAsync(SolutionCompilationState compilationState, CancellationToken cancellationToken) - => Checksum.Create( - await UnderlyingTracker.GetDependentChecksumAsync(compilationState, cancellationToken).ConfigureAwait(false), - (await _replacementDocumentStates.GetDocumentChecksumsAndIdsAsync(cancellationToken).ConfigureAwait(false)).Checksum); - public async ValueTask> GetSourceGeneratedDocumentStatesAsync( SolutionCompilationState compilationState, bool withFrozenSourceGeneratedDocuments, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs index 42a42b6becaad..95269c31933cc 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState.cs @@ -1110,9 +1110,6 @@ public Task GetDependentVersionAsync(ProjectId projectId, Cancella public Task GetDependentSemanticVersionAsync(ProjectId projectId, CancellationToken cancellationToken) => this.GetCompilationTracker(projectId).GetDependentSemanticVersionAsync(this, cancellationToken); - public Task GetDependentChecksumAsync(ProjectId projectId, CancellationToken cancellationToken) - => this.GetCompilationTracker(projectId).GetDependentChecksumAsync(this, cancellationToken); - public bool TryGetCompilation(ProjectId projectId, [NotNullWhen(returnValue: true)] out Compilation? compilation) { this.SolutionState.CheckContainsProject(projectId); @@ -1388,7 +1385,12 @@ public SolutionCompilationState WithFrozenSourceGeneratedDocuments( existingTracker = CreateCompilationTracker(projectId, arg.SolutionState); } - trackerMap[projectId] = new WithFrozenSourceGeneratedDocumentsCompilationTracker(existingTracker, new(documentStatesForProject)); + // We should never be wrapping a WithFrozenSourceGeneratedDocumentsCompilationTracker with another + // WithFrozenSourceGeneratedDocumentsCompilationTracker. If we do, that's a straight bug that + // should fail immediately. So blind casting is here is appropriate. + var regularCompilationTracker = (RegularCompilationTracker)existingTracker; + trackerMap[projectId] = new WithFrozenSourceGeneratedDocumentsCompilationTracker( + regularCompilationTracker, new(documentStatesForProject)); } }, (documentStatesByProjectId, this.SolutionState), diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs index ac0918671d4ff..db9b1b5dc66b1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionCompilationState_Checksum.cs @@ -6,12 +6,10 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs index 92c49675d850a..bd71bc3fdc0d9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution_SemanticModelCaching.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs index deddcb770fcfe..2dac3eca73250 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentState.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics.Contracts; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.SourceGeneration; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs index 90115c90fed6c..8a6aa479fc826 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratorIdentity.cs @@ -7,7 +7,6 @@ using System.Runtime.Serialization; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDifferenceTypes.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDifferenceTypes.cs index 0d07214dae653..133f095641f65 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDifferenceTypes.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDifferenceTypes.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; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs index 7b7d0946f6418..3a9709df1273e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/TextDocument.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/SourceGeneratorExecution.cs b/src/Workspaces/Core/Portable/Workspace/SourceGeneratorExecution.cs index 500c1abdde35c..845686f901a96 100644 --- a/src/Workspaces/Core/Portable/Workspace/SourceGeneratorExecution.cs +++ b/src/Workspaces/Core/Portable/Workspace/SourceGeneratorExecution.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Host; internal enum SourceGeneratorExecutionPreference diff --git a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs index da91f2c6f1a09..a97af4ec15f2f 100644 --- a/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs +++ b/src/Workspaces/Core/Portable/Workspace/TextExtensions.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceChangeKind.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceChangeKind.cs index 90f0a0d17832f..f6331752ea5f4 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceChangeKind.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceChangeKind.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 - namespace Microsoft.CodeAnalysis; public enum WorkspaceChangeKind diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnostic.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnostic.cs index c9d6229cf663e..fdbdd0243cb94 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnostic.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnostic.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis; public class WorkspaceDiagnostic(WorkspaceDiagnosticKind kind, string message) diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticDescriptors.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticDescriptors.cs index 4aa0dce91ecaf..725380a9a9a5d 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticDescriptors.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticDescriptors.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 - namespace Microsoft.CodeAnalysis; internal sealed class WorkspaceDiagnosticDescriptors diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticEventArgs.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticEventArgs.cs index 60fdf17281c9b..2e5bd0004c57e 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticEventArgs.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceDiagnosticEventArgs.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; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs index 5811a90a8cfb8..aef7f056656e1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace_Events.cs @@ -78,17 +78,22 @@ protected Task RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind kind, Solutio projectId = documentId.ProjectId; } - var args = new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId); - + WorkspaceChangeEventArgs args = null; var ev = GetEventHandlers(WorkspaceChangedImmediateEventName); - RaiseEventForHandlers(ev, args, FunctionId.Workspace_EventsImmediate); + + if (ev.HasHandlers) + { + args = new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId); + RaiseEventForHandlers(ev, sender: this, args, FunctionId.Workspace_EventsImmediate); + } ev = GetEventHandlers(WorkspaceChangeEventName); if (ev.HasHandlers) { + args ??= new WorkspaceChangeEventArgs(kind, oldSolution, newSolution, projectId, documentId); return this.ScheduleTask(() => { - RaiseEventForHandlers(ev, args, FunctionId.Workspace_Events); + RaiseEventForHandlers(ev, sender: this, args, FunctionId.Workspace_Events); }, WorkspaceChangeEventName); } else @@ -98,12 +103,13 @@ protected Task RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind kind, Solutio static void RaiseEventForHandlers( EventMap.EventHandlerSet> handlers, + Workspace sender, WorkspaceChangeEventArgs args, FunctionId functionId) { using (Logger.LogBlock(functionId, (s, p, d, k) => $"{s.Id} - {p} - {d} {args.Kind.ToString()}", args.NewSolution, args.ProjectId, args.DocumentId, args.Kind, CancellationToken.None)) { - handlers.RaiseEvent(static (handler, args) => handler(args.NewSolution.Workspace, args), args); + handlers.RaiseEvent(static (handler, arg) => handler(arg.sender, arg.args), (sender, args)); } } } diff --git a/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs index 4984445d61f22..c528983d55ab8 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/MockCodeCleanupProvider.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; diff --git a/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs b/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs index cfadd6ca7288b..a40fe57391503 100644 --- a/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.cs +++ b/src/Workspaces/CoreTest/Differencing/LongestCommonSubsequenceTests.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.Text; using Xunit; diff --git a/src/Workspaces/CoreTest/Differencing/MatchTests.cs b/src/Workspaces/CoreTest/Differencing/MatchTests.cs index fadfcfdc0fb48..fa9c549bb7256 100644 --- a/src/Workspaces/CoreTest/Differencing/MatchTests.cs +++ b/src/Workspaces/CoreTest/Differencing/MatchTests.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 Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs b/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs index 7553741ef1e89..c75c69740f1a2 100644 --- a/src/Workspaces/CoreTest/Differencing/TestTreeComparer.cs +++ b/src/Workspaces/CoreTest/Differencing/TestTreeComparer.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.Generic; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs index bab48686708c0..5d003c2c632f5 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.TestSolutionsAndProject.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.Generic; using System.Linq; diff --git a/src/Workspaces/CoreTest/FunctionIdTests.cs b/src/Workspaces/CoreTest/FunctionIdTests.cs index d1cfeae7f1be0..b022251da2c07 100644 --- a/src/Workspaces/CoreTest/FunctionIdTests.cs +++ b/src/Workspaces/CoreTest/FunctionIdTests.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.Generic; using Microsoft.CodeAnalysis.Internal.Log; diff --git a/src/Workspaces/CoreTest/Options/EditorConfigNamingStyleParserTests.cs b/src/Workspaces/CoreTest/Options/EditorConfigNamingStyleParserTests.cs index 427bae7fd5494..4ccb7d7878f13 100644 --- a/src/Workspaces/CoreTest/Options/EditorConfigNamingStyleParserTests.cs +++ b/src/Workspaces/CoreTest/Options/EditorConfigNamingStyleParserTests.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/Workspaces/CoreTest/Options/NamingStylePreferencesTests.cs b/src/Workspaces/CoreTest/Options/NamingStylePreferencesTests.cs index d6a84049fc6eb..91e4719618b20 100644 --- a/src/Workspaces/CoreTest/Options/NamingStylePreferencesTests.cs +++ b/src/Workspaces/CoreTest/Options/NamingStylePreferencesTests.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.Linq; using System.Xml.Linq; diff --git a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs index 0e9c68fc1b5bb..a686b89c49ab8 100644 --- a/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs +++ b/src/Workspaces/CoreTest/Remote/ServiceDescriptorTests.cs @@ -18,12 +18,10 @@ using MessagePack; using MessagePack.Formatters; using Microsoft.CodeAnalysis.AddImport; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Simplification; using Microsoft.CodeAnalysis.DocumentationComments; @@ -36,7 +34,6 @@ using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.UnitTests; using Microsoft.CodeAnalysis.VisualBasic.CodeGeneration; -using Microsoft.CodeAnalysis.VisualBasic.CodeStyle; using Microsoft.CodeAnalysis.VisualBasic.Formatting; using Microsoft.CodeAnalysis.VisualBasic.Simplification; using Roslyn.Test.Utilities; diff --git a/src/Workspaces/CoreTest/Shared/Extensions/TelemetryExtensions/TelemetryExtensionTests.cs b/src/Workspaces/CoreTest/Shared/Extensions/TelemetryExtensions/TelemetryExtensionTests.cs index 2dcc610ce941b..86c1eb8d3a57e 100644 --- a/src/Workspaces/CoreTest/Shared/Extensions/TelemetryExtensions/TelemetryExtensionTests.cs +++ b/src/Workspaces/CoreTest/Shared/Extensions/TelemetryExtensions/TelemetryExtensionTests.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 Microsoft.CodeAnalysis.Shared.Extensions; using Xunit; diff --git a/src/Workspaces/CoreTest/Shared/Extensions/TextSpanExtensions/SubtractTests.cs b/src/Workspaces/CoreTest/Shared/Extensions/TextSpanExtensions/SubtractTests.cs index 3e431a299a4db..7fbbeb34e1b92 100644 --- a/src/Workspaces/CoreTest/Shared/Extensions/TextSpanExtensions/SubtractTests.cs +++ b/src/Workspaces/CoreTest/Shared/Extensions/TextSpanExtensions/SubtractTests.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 Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; diff --git a/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs b/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs index 0b04a4d145f39..6aaab605082bb 100644 --- a/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/ProjectInfoTests.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Immutable; -using System.IO; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs index fdb847ccc5e62..cc0f85f6d993c 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionTests.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.Generic; using System.Collections.Immutable; diff --git a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs index 24126d40f789c..52743b8027436 100644 --- a/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs +++ b/src/Workspaces/CoreTest/SolutionTests/SolutionWithSourceGeneratorTests.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Remote.Testing; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/CoreTest/UtilityTest/DocumentationCommentTests.cs b/src/Workspaces/CoreTest/UtilityTest/DocumentationCommentTests.cs index 2664563f9815e..89e187edc3da5 100644 --- a/src/Workspaces/CoreTest/UtilityTest/DocumentationCommentTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/DocumentationCommentTests.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 Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/EditDistanceTests.cs b/src/Workspaces/CoreTest/UtilityTest/EditDistanceTests.cs index 03f61dd4753d8..de4445d9088ab 100644 --- a/src/Workspaces/CoreTest/UtilityTest/EditDistanceTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/EditDistanceTests.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.Linq; using Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs b/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs index a135e545e529a..f4ffc4f1c78f2 100644 --- a/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/ExceptionHelpersTests.cs @@ -2,13 +2,10 @@ // 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.Text.Json; using Microsoft.CodeAnalysis.ErrorReporting; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.UnitTests diff --git a/src/Workspaces/CoreTest/UtilityTest/FilePathUtilitiesTests.cs b/src/Workspaces/CoreTest/UtilityTest/FilePathUtilitiesTests.cs index 6d198f0a9b8d0..f687a8aa1e89c 100644 --- a/src/Workspaces/CoreTest/UtilityTest/FilePathUtilitiesTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/FilePathUtilitiesTests.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 Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/FormattingRangeHelperTests.cs b/src/Workspaces/CoreTest/UtilityTest/FormattingRangeHelperTests.cs index 0a42b5e98df10..8150a77dc81e0 100644 --- a/src/Workspaces/CoreTest/UtilityTest/FormattingRangeHelperTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/FormattingRangeHelperTests.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 Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Utilities; using Roslyn.Test.Utilities; diff --git a/src/Workspaces/CoreTest/UtilityTest/SerializableBytesTests.cs b/src/Workspaces/CoreTest/UtilityTest/SerializableBytesTests.cs index 5dacf5cd41ff3..7229cdcb82419 100644 --- a/src/Workspaces/CoreTest/UtilityTest/SerializableBytesTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/SerializableBytesTests.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.IO; using System.Threading; diff --git a/src/Workspaces/CoreTest/UtilityTest/SpellCheckerTests.cs b/src/Workspaces/CoreTest/UtilityTest/SpellCheckerTests.cs index 3d90395b6a7a4..3547841433026 100644 --- a/src/Workspaces/CoreTest/UtilityTest/SpellCheckerTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/SpellCheckerTests.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 Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs b/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs index 62f5b9e0a5517..311821d39b5a1 100644 --- a/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.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 Roslyn.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTest/UtilityTest/TaskExtensionsTests.cs b/src/Workspaces/CoreTest/UtilityTest/TaskExtensionsTests.cs index 4ad2eb003fc47..e21cbb5b649c5 100644 --- a/src/Workspaces/CoreTest/UtilityTest/TaskExtensionsTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/TaskExtensionsTests.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.Runtime.CompilerServices; using System.Threading; diff --git a/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs b/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs index 5e37a6187cde0..28f4b3cb79e5c 100644 --- a/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceServiceTests/GlobalOptionServiceTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Workspaces/CoreTest/WorkspaceTests/DynamicFileInfoProviderMefTests.cs b/src/Workspaces/CoreTest/WorkspaceTests/DynamicFileInfoProviderMefTests.cs index 47ebc52769afa..3e3afdf8eb6f7 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/DynamicFileInfoProviderMefTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/DynamicFileInfoProviderMefTests.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.Linq; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceReferenceTests.cs b/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceReferenceTests.cs index 6e0ac037e2caa..ed4bd9f80652f 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceReferenceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/WorkspaceReferenceTests.cs @@ -4,7 +4,6 @@ #nullable disable -using System.Collections.Immutable; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; diff --git a/src/Workspaces/CoreTestUtilities/Fakes/MockWorkspaceEventListenerProvider.cs b/src/Workspaces/CoreTestUtilities/Fakes/MockWorkspaceEventListenerProvider.cs index 9f9e6a2f1547c..7fe42945fb3c8 100644 --- a/src/Workspaces/CoreTestUtilities/Fakes/MockWorkspaceEventListenerProvider.cs +++ b/src/Workspaces/CoreTestUtilities/Fakes/MockWorkspaceEventListenerProvider.cs @@ -19,6 +19,6 @@ internal sealed class MockWorkspaceEventListenerProvider() : IWorkspaceServiceFa { public IEnumerable? EventListeners; - public IWorkspaceService? CreateService(HostWorkspaceServices workspaceServices) - => EventListeners != null ? new DefaultWorkspaceEventListenerServiceFactory.Service(workspaceServices.Workspace, EventListeners) : null; + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => EventListeners != null ? new DefaultWorkspaceEventListenerServiceFactory.Service(workspaceServices.Workspace, EventListeners) : null!; } diff --git a/src/Workspaces/CoreTestUtilities/MEF/IMefHostExportProviderExtensions.cs b/src/Workspaces/CoreTestUtilities/MEF/IMefHostExportProviderExtensions.cs index 3cac780a71373..1fd996980f76a 100644 --- a/src/Workspaces/CoreTestUtilities/MEF/IMefHostExportProviderExtensions.cs +++ b/src/Workspaces/CoreTestUtilities/MEF/IMefHostExportProviderExtensions.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.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs b/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs index ac269e5f96997..cadd0ccff9d83 100644 --- a/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs +++ b/src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.NamingStyles; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.UnitTests { diff --git a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs index 3b6e7ad8ab153..1a11e5bd9879f 100644 --- a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs +++ b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext+DenyExecutionSynchronizationContext.cs @@ -6,7 +6,6 @@ using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Test.Utilities { diff --git a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext.cs b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext.cs index 0672cf8596567..bf526cbf46f59 100644 --- a/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext.cs +++ b/src/Workspaces/CoreTestUtilities/TestExportJoinableTaskContext.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Threading; using Roslyn.Test.Utilities; -using Roslyn.Utilities; using Xunit.Sdk; namespace Microsoft.CodeAnalysis.Test.Utilities diff --git a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs b/src/Workspaces/CoreTestUtilities/TestTextLoader.cs index b21164fce03e3..01c2832f1f4a0 100644 --- a/src/Workspaces/CoreTestUtilities/TestTextLoader.cs +++ b/src/Workspaces/CoreTestUtilities/TestTextLoader.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/AbstractTestHostProject.cs b/src/Workspaces/CoreTestUtilities/Workspaces/AbstractTestHostProject.cs index 4386053a286ff..f81c5ed5db2ac 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/AbstractTestHostProject.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/AbstractTestHostProject.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.IO; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestHostSolution.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestHostSolution.cs index 342f684f71906..00eb9ffcd4204 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestHostSolution.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestHostSolution.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 Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Test.Utilities; diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs index 84b7ad0cef98d..f885cca71ae7f 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlConsumption.cs @@ -15,7 +15,6 @@ using System.Threading; using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.DecompiledSource; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Extensions; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs index d88eb5524d93f..52f5b8b502e2c 100644 --- a/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs +++ b/src/Workspaces/CoreTestUtilities/Workspaces/TestWorkspace_XmlCreation.cs @@ -10,7 +10,6 @@ using System.Xml.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.VisualBasic; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Test.Utilities { diff --git a/src/Workspaces/MSBuild/BuildHost/MSBuild/ProjectFile/ProjectFile.cs b/src/Workspaces/MSBuild/BuildHost/MSBuild/ProjectFile/ProjectFile.cs index b7f87db9decd5..cb763993aad9b 100644 --- a/src/Workspaces/MSBuild/BuildHost/MSBuild/ProjectFile/ProjectFile.cs +++ b/src/Workspaces/MSBuild/BuildHost/MSBuild/ProjectFile/ProjectFile.cs @@ -274,7 +274,7 @@ private ImmutableArray GetRelativeFolders(MSB.Framework.ITaskItem docume { var filePath = documentItem.ItemSpec; var relativePath = PathUtilities.GetDirectoryName(PathUtilities.GetRelativePath(_projectDirectory, filePath)); - var folders = relativePath == null ? [] : relativePath.Split(PathUtilities.DirectorySeparatorChar, PathUtilities.AltDirectorySeparatorChar).ToImmutableArray(); + var folders = relativePath == null ? [] : relativePath.Split([PathUtilities.DirectorySeparatorChar, PathUtilities.AltDirectorySeparatorChar], StringSplitOptions.RemoveEmptyEntries).ToImmutableArray(); return folders; } } diff --git a/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj b/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj index 9b1558f7d9866..969b4616a298f 100644 --- a/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj +++ b/src/Workspaces/MSBuild/BuildHost/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.csproj @@ -27,6 +27,8 @@ The version targeted is only used for build time, since we use MSBuildLocator to discover the proper version at runtime. --> <_MsbuildVersion>17.3.4 + + true diff --git a/src/Workspaces/MSBuild/BuildHost/Program.cs b/src/Workspaces/MSBuild/BuildHost/Program.cs index 6f12720481727..7fee5ac1ad2ea 100644 --- a/src/Workspaces/MSBuild/BuildHost/Program.cs +++ b/src/Workspaces/MSBuild/BuildHost/Program.cs @@ -8,7 +8,6 @@ using System.Globalization; using System.IO.Pipes; using System.Threading.Tasks; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.MSBuild; diff --git a/src/Workspaces/MSBuild/Test/NetCoreTests.cs b/src/Workspaces/MSBuild/Test/NetCoreTests.cs index cb387d55893ef..3ef697d6e90e2 100644 --- a/src/Workspaces/MSBuild/Test/NetCoreTests.cs +++ b/src/Workspaces/MSBuild/Test/NetCoreTests.cs @@ -503,8 +503,10 @@ public async Task TestOpenProject_VBNetCoreAppWithGlobalImportAndLibrary() // Assert that there is a project reference between VBProject.vbproj and Library.csproj AssertSingleProjectReference(project, libraryFilePath); - // Assert that the project does not have any diagnostics in Program.vb var document = project.Documents.First(d => d.Name == "Program.vb"); + Assert.Empty(document.Folders); + + // Assert that the project does not have any diagnostics in Program.vb var semanticModel = await document.GetSemanticModelAsync(); var diagnostics = semanticModel.GetDiagnostics(); Assert.Empty(diagnostics.Where(d => d.Severity >= DiagnosticSeverity.Warning)); diff --git a/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs b/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs index 1a13e707c5e97..5d728c83faa76 100644 --- a/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs +++ b/src/Workspaces/MSBuild/Test/VisualStudioMSBuildWorkspaceTests.cs @@ -14,6 +14,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; @@ -21,16 +22,15 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.UnitTests; using Microsoft.CodeAnalysis.UnitTests.TestFiles; +using Microsoft.VisualStudio.Threading; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; -using static Microsoft.CodeAnalysis.MSBuild.UnitTests.SolutionGeneration; +using Xunit.Abstractions; using static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts; +using static Microsoft.CodeAnalysis.MSBuild.UnitTests.SolutionGeneration; using CS = Microsoft.CodeAnalysis.CSharp; using VB = Microsoft.CodeAnalysis.VisualBasic; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.VisualStudio.Threading; -using Xunit.Abstractions; namespace Microsoft.CodeAnalysis.MSBuild.UnitTests { @@ -63,6 +63,7 @@ public async Task TestOpenSolution_SingleProjectSolution() var solution = await workspace.OpenSolutionAsync(solutionFilePath); var project = solution.Projects.First(); var document = project.Documents.First(); + Assert.Empty(document.Folders); var tree = await document.GetSyntaxTreeAsync(); var type = tree.GetRoot().DescendantTokens().First(t => t.ToString() == "class").Parent; Assert.NotNull(type); @@ -2898,13 +2899,14 @@ class C { }"; Assert.Equal(encoding.EncodingName, text.Encoding.EncodingName); Assert.Equal(fileContent, text.ToString()); - // update root blindly again, after observing encoding, see that encoding is overridden to null + // update root blindly again, after observing encoding, see that encoding is preserved + // 🐉 Tools rely on encoding preservation; see https://github.com/dotnet/sdk/issues/46780 var doc3 = document.WithSyntaxRoot(gen.CompilationUnit()); // empty CU var doc3text = await doc3.GetTextAsync(); - Assert.Null(doc3text.Encoding); + Assert.Same(text.Encoding, doc3text.Encoding); var doc3tree = await doc3.GetSyntaxTreeAsync(); - Assert.Null(doc3tree.Encoding); - Assert.Null(doc3tree.GetText().Encoding); + Assert.Same(text.Encoding, doc3tree.Encoding); + Assert.Same(text.Encoding, doc3tree.GetText().Encoding); // change doc to have no encoding, still succeeds at writing to disk with old encoding var root = await document.GetSyntaxRootAsync(); diff --git a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs index 0c480adc03c3a..58bc9c227422d 100644 --- a/src/Workspaces/Remote/Core/AbstractAssetProvider.cs +++ b/src/Workspaces/Remote/Core/AbstractAssetProvider.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs index 2293822f7227e..9b2aa7210ae2f 100644 --- a/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs +++ b/src/Workspaces/Remote/Core/BrokeredServiceConnection.cs @@ -12,7 +12,6 @@ using Microsoft.ServiceHub.Framework; using Microsoft.VisualStudio.Threading; using Microsoft.Win32.SafeHandles; -using Roslyn.Utilities; using StreamJsonRpc; using StreamJsonRpc.Protocol; diff --git a/src/Workspaces/Remote/Core/ProjectSystem/MetadataReferenceInfo.cs b/src/Workspaces/Remote/Core/ProjectSystem/MetadataReferenceInfo.cs index f929d9a1dd79a..fbba554877b15 100644 --- a/src/Workspaces/Remote/Core/ProjectSystem/MetadataReferenceInfo.cs +++ b/src/Workspaces/Remote/Core/ProjectSystem/MetadataReferenceInfo.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Runtime.Serialization; namespace Microsoft.CodeAnalysis.Remote.ProjectSystem; diff --git a/src/Workspaces/Remote/Core/RemoteCallback.cs b/src/Workspaces/Remote/Core/RemoteCallback.cs index eebafafb93834..f99a5ca28d25a 100644 --- a/src/Workspaces/Remote/Core/RemoteCallback.cs +++ b/src/Workspaces/Remote/Core/RemoteCallback.cs @@ -9,8 +9,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.ServiceHub.Framework; -using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.Remote diff --git a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs index 276918f235f6b..ed4d833345ad0 100644 --- a/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs +++ b/src/Workspaces/Remote/Core/Serialization/MessagePackFormatters.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { diff --git a/src/Workspaces/Remote/Core/ServiceBrokerExtensions.cs b/src/Workspaces/Remote/Core/ServiceBrokerExtensions.cs index 6780550a6818c..ccded891e778d 100644 --- a/src/Workspaces/Remote/Core/ServiceBrokerExtensions.cs +++ b/src/Workspaces/Remote/Core/ServiceBrokerExtensions.cs @@ -6,7 +6,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.ServiceHub.Framework; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.BrokeredServices; diff --git a/src/Workspaces/Remote/Core/ServiceDescriptor.cs b/src/Workspaces/Remote/Core/ServiceDescriptor.cs index 496a137d09634..58cbfb27ca21a 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptor.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptor.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using Microsoft.ServiceHub.Framework; -using Roslyn.Utilities; using StreamJsonRpc; namespace Microsoft.CodeAnalysis.Remote diff --git a/src/Workspaces/Remote/Core/ServiceDescriptors.cs b/src/Workspaces/Remote/Core/ServiceDescriptors.cs index f60effa5d1b62..ba153c6bd072b 100644 --- a/src/Workspaces/Remote/Core/ServiceDescriptors.cs +++ b/src/Workspaces/Remote/Core/ServiceDescriptors.cs @@ -20,7 +20,6 @@ using Microsoft.CodeAnalysis.ExternalAccess.UnitTesting; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindUsages; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.InheritanceMargin; using Microsoft.CodeAnalysis.LegacySolutionEvents; using Microsoft.CodeAnalysis.NavigateTo; @@ -35,7 +34,6 @@ using Microsoft.CodeAnalysis.TaskList; using Microsoft.CodeAnalysis.UnusedReferences; using Microsoft.CodeAnalysis.ValueTracking; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote { diff --git a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs index 0dd69213db851..2d9e659739324 100644 --- a/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs +++ b/src/Workspaces/Remote/Core/SolutionAssetStorage.Scope.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/Core/SolutionAssetStorage.cs b/src/Workspaces/Remote/Core/SolutionAssetStorage.cs index d5f80f5030c92..16b9d4fb6021f 100644 --- a/src/Workspaces/Remote/Core/SolutionAssetStorage.cs +++ b/src/Workspaces/Remote/Core/SolutionAssetStorage.cs @@ -8,8 +8,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Serialization; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub.CoreComponents/CoreComponents.Shared.targets b/src/Workspaces/Remote/ServiceHub.CoreComponents/CoreComponents.Shared.targets index 2698aed711c6f..558f628f2022c 100644 --- a/src/Workspaces/Remote/ServiceHub.CoreComponents/CoreComponents.Shared.targets +++ b/src/Workspaces/Remote/ServiceHub.CoreComponents/CoreComponents.Shared.targets @@ -38,7 +38,21 @@ - + + + + <_ExcludeRuntimeLibraries Condition="'$(OfficialBuild)' == 'true'">false + + <_PublishPaths Include="@(PublishItemsOutputGroupOutputs->'%(OutputPath)')" /> <_PublishDllPaths Include="@(_PublishPaths)" Condition="'%(Extension)' == '.dll'" /> @@ -58,12 +72,24 @@ <_R2RAssemblies Include="SQLitePCLRaw.batteries_v2.dll" /> <_R2RAssemblies Include="StreamJsonRpc.dll" /> <_R2RAssemblies Include="System.IO.Pipelines.dll" /> - <_R2RAssemblies Include="System.Text.Json.dll" /> - <_R2RAssemblies Include="System.Text.Encodings.Web.dll" /> <_R2RAssemblies Include="Microsoft.NET.StringTools.dll" /> + + + <_R2RRuntimeAssemblies Include="System.Collections.Immutable.dll" /> + <_R2RRuntimeAssemblies Include="System.Reflection.Metadata.dll" /> + <_R2RRuntimeAssemblies Include="System.Text.Encodings.Web.dll" /> + <_R2RRuntimeAssemblies Include="System.Text.Json.dll" /> + <_R2RRuntimeAssemblies Include="System.Threading.Tasks.Dataflow.dll" /> + <_R2RAssemblies Include="@(_R2RRuntimeAssemblies)" Condition="'$(_ExcludeRuntimeLibraries)' != 'true'" /> - <_AllPublishedAssemblyPaths Include="@(_PublishDllPaths)" Exclude="%(FileName).EndsWith('.resources')" /> + <_AllPublishedAssemblyPaths Include="@(_PublishDllPaths)" /> + + <_AllPublishedAssemblyPaths Remove="@(_AllPublishedAssemblyPaths)" Condition="$([MSBuild]::ValueOrDefault('%(FileName)', '').EndsWith('.resources'))" /> + + <_AllPublishedAssemblyPaths Remove="@(_AllPublishedAssemblyPaths)" Condition="$([MSBuild]::ValueOrDefault('%(FullPath)', '').StartsWith('$(PublishDir)runtimes'))" /> + <_AllPublishedAssemblies Include="@(_AllPublishedAssemblyPaths->'%(FileName)%(Extension)')" > <_FullFilePath>%(FullPath) @@ -78,31 +104,42 @@ <_R2RAssemblyPaths Include="@(_AllPublishedAssemblyPaths)" Exclude="@(_NoR2RAssemblyPaths)" /> - <_RuntimeLibraries Include="$(_RuntimeLibrariesPath)**\*.dll" /> - <_WinRuntimeLibraries Include="$(_WinRuntimeLibrariesPath)**\*.dll" /> + <_RuntimeLibraryPaths Include="$(_RuntimeLibrariesPath)**\*.dll" /> + <_RuntimeLibraryPaths Include="$(_WinRuntimeLibrariesPath)**\*.dll" /> + <_RuntimeLibraryPaths Remove="@(_RuntimeLibraryPaths)" Condition="$([MSBuild]::ValueOrDefault('%(FileName)', '').EndsWith('.resources'))" /> - <_RuntimeLibrariesInPublishDir Include="@(_RuntimeLibraries->'$(PublishDir)%(FileName)%(Extension)')" /> - <_RuntimeLibrariesInPublishDir Include="@(_WinRuntimeLibraries->'$(PublishDir)%(FileName)%(Extension)')" /> + <_RuntimeLibraries Include="@(_RuntimeLibraryPaths->'%(FileName)%(Extension)')"> + <_FullFilePath>%(FullPath) + - <_NonRuntimeAssembliesInPublishDir Include="@(_PublishDllPaths)" Exclude="@(_RuntimeLibrariesInPublishDir)" /> + + <_NonRuntimeAssemblyPathsInPublishDir Include="@(_AllPublishedAssemblyPaths)" Exclude="@(_RuntimeLibraryPaths->'$(PublishDir)%(FileName)%(Extension)')" /> + <_RuntimeLibraryPathsInPublishDir Include="@(_AllPublishedAssemblyPaths)" Exclude="@(_NonRuntimeAssemblyPathsInPublishDir)" /> + <_RuntimeLibrariesInPublishDir Include="@(_RuntimeLibraryPathsInPublishDir->'%(FileName)%(Extension)')" /> + + <_RuntimeLibrariesNotInPublishDir Include="@(_RuntimeLibraries)" Exclude="@(_RuntimeLibrariesInPublishDir)" /> + <_RuntimeLibraryPathsNotInPublishDir Include="@(_RuntimeLibrariesNotInPublishDir->'%(_FullFilePath)')" Exclude="@(_RuntimeLibrariesInPublishDir)" /> $(PublishDir)CrossGen\ $(CrossgenWorkDir)OriginalAssemblies\ $(CrossgenWorkDir)Symbols\ + $(CrossgenWorkDir)RspFiles\ - + - <_CrossgenTargetsAsDependencies Include="$(OriginalAssemblyDir)*.dll" /> - <_NonCrossgenTargetsAsDependencies Include="@(_NonRuntimeAssembliesInPublishDir)" Exclude="@(_R2RAssemblyPaths)" /> + <_CrossgenTargetsAsDependencies Include="$(OriginalAssemblyDir)**\*.dll" /> + <_NonCrossgenTargetsAsDependencies Include="@(_AllPublishedAssemblyPaths)" Exclude="@(_R2RAssemblyPaths)" /> <_CrossgenTargetPaths Include="@(_CrossgenTargetsAsDependencies)"> - $(PublishDir)%(_CrossgenTargetsAsDependencies.Filename)%(_CrossgenTargetsAsDependencies.Extension) + $(PublishDir)%(RecursiveDir)\%(_CrossgenTargetsAsDependencies.Filename)%(_CrossgenTargetsAsDependencies.Extension) + <_RelativeDirectory>%(RecursiveDir) @@ -139,7 +176,7 @@ - + - <_ExcludeRuntimeLibraries Condition="'$(OfficialBuild)' == 'true'">true - - <_VsixItem Include="@(_PublishPaths)" /> @@ -208,7 +232,7 @@ <_VsixItem Remove="@(_VsixItem)" Condition="$([MSBuild]::ValueOrDefault('%(FullPath)', '').StartsWith('$(PublishDir)runtimes'))" /> - <_VsixItem Remove="@(_RuntimeLibrariesInPublishDir)" Condition="'$(_ExcludeRuntimeLibraries)' == 'true'" /> + <_VsixItem Remove="@(_RuntimeLibraryPathsInPublishDir)" Condition="'$(_ExcludeRuntimeLibraries)' == 'true'" /> diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.cs deleted file mode 100644 index fd36b3e185862..0000000000000 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoader.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 System.IO; -using System.Collections.Immutable; - -namespace Microsoft.CodeAnalysis.Remote.Diagnostics; - -/// -/// For analyzers shipped in Roslyn, different set of assemblies might be used when running -/// in-proc and OOP e.g. in-proc (VS) running on desktop clr and OOP running on ServiceHub .Net6 -/// host. We need to make sure to use the ones from the same location as the remote. -/// -internal sealed class RemoteAnalyzerAssemblyLoader : AnalyzerAssemblyLoader -{ - private readonly string _baseDirectory; - - public RemoteAnalyzerAssemblyLoader(string baseDirectory, ImmutableArray? externalResolvers = null) - : base(externalResolvers ?? []) - { - _baseDirectory = baseDirectory; - } - - protected override string PreparePathToLoad(string fullPath) - { - var fixedPath = Path.GetFullPath(Path.Combine(_baseDirectory, Path.GetFileName(fullPath))); - return File.Exists(fixedPath) ? fixedPath : fullPath; - } - - protected override string PrepareSatelliteAssemblyToLoad(string fullPath, string cultureName) - { - var fixedPath = Path.GetFullPath(Path.Combine(_baseDirectory, cultureName, Path.GetFileName(fullPath))); - return File.Exists(fixedPath) ? fixedPath : fullPath; - } -} diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs index f6e503cb7ba66..2d05eea91eb86 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerAssemblyLoaderService.cs @@ -15,8 +15,21 @@ namespace Microsoft.CodeAnalysis.Remote.Diagnostics; /// Customizes the path where to store shadow-copies of analyzer assemblies. /// [ExportWorkspaceService(typeof(IAnalyzerAssemblyLoaderProvider), [WorkspaceKind.RemoteWorkspace]), Shared] -[method: ImportingConstructor] -[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed class RemoteAnalyzerAssemblyLoaderService( - [ImportMany] IEnumerable externalResolvers) - : AbstractAnalyzerAssemblyLoaderProvider(externalResolvers.ToImmutableArray()); +internal sealed class RemoteAnalyzerAssemblyLoaderService : AbstractAnalyzerAssemblyLoaderProvider +{ +#pragma warning disable IDE02900 // primary constructor +#if NET + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RemoteAnalyzerAssemblyLoaderService([ImportMany] IEnumerable assemblyResolvers) + : base(assemblyResolvers) + { + } +#else + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public RemoteAnalyzerAssemblyLoaderService() + { + } +#endif +} diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerPathResolver.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerPathResolver.cs new file mode 100644 index 0000000000000..b1cd93589461e --- /dev/null +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteAnalyzerPathResolver.cs @@ -0,0 +1,31 @@ +// 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.IO; +using System.Collections.Immutable; +using System.Globalization; + +namespace Microsoft.CodeAnalysis.Remote.Diagnostics; + +/// +/// For analyzers shipped in Roslyn, different set of assemblies might be used when running +/// in-proc and OOP e.g. in-proc (VS) running on desktop clr and OOP running on ServiceHub .Net6 +/// host. We need to make sure to use the ones from the same location as the remote. +/// +internal sealed class RemoteAnalyzerPathResolver(string baseDirectory) : IAnalyzerPathResolver +{ + private readonly string _baseDirectory = baseDirectory; + + private string GetFixedPath(string analyzerPath) + => Path.GetFullPath(Path.Combine(_baseDirectory, Path.GetFileName(analyzerPath))); + + public bool IsAnalyzerPathHandled(string originalAnalyzerPath) + => File.Exists(GetFixedPath(originalAnalyzerPath)); + + public string GetResolvedAnalyzerPath(string originalAnalyzerPath) + => GetFixedPath(originalAnalyzerPath); + + public string? GetResolvedSatellitePath(string originalAnalyzerPath, CultureInfo cultureInfo) + => AnalyzerAssemblyLoader.GetSatelliteAssemblyPath(GetFixedPath(originalAnalyzerPath), cultureInfo); +} diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs index 428cb91515795..934c7a12b76fb 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteDocumentDifferenceService.cs @@ -18,30 +18,20 @@ namespace Microsoft.CodeAnalysis.Remote; /// Default is optimized for typing case in editor where we have events /// for each typing. But in remote workspace, we aggregate changes and update solution in bulk and we don't have concept /// of active file making default implementation unsuitable. Functionally, default one is still correct, but it often -/// time makes us to do more than we need. Basically, it always says this project has semantic change which can cause -/// a lot of re-analysis. +/// time makes us to do more than we need. Basically, it always says this project has semantic change which can cause a +/// lot of re-analysis. /// internal class RemoteDocumentDifferenceService : IDocumentDifferenceService { [ExportLanguageService(typeof(IDocumentDifferenceService), LanguageNames.CSharp, layer: ServiceLayer.Host), Shared] - internal sealed class CSharpDocumentDifferenceService : RemoteDocumentDifferenceService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpDocumentDifferenceService() - { - } - } + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class CSharpDocumentDifferenceService() : RemoteDocumentDifferenceService; [ExportLanguageService(typeof(IDocumentDifferenceService), LanguageNames.VisualBasic, layer: ServiceLayer.Host), Shared] - internal sealed class VisualBasicDocumentDifferenceService : AbstractDocumentDifferenceService - { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public VisualBasicDocumentDifferenceService() - { - } - } + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class VisualBasicDocumentDifferenceService() : RemoteDocumentDifferenceService; public Task GetChangedMemberAsync(Document oldDocument, Document newDocument, CancellationToken cancellationToken) { diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteHostTestData.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteHostTestData.cs index e9867b69da7c4..613c41d0e371f 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteHostTestData.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteHostTestData.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. -using System; -using Microsoft.CodeAnalysis.Host; - namespace Microsoft.CodeAnalysis.Remote; /// diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteServiceBrokerProvider.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteServiceBrokerProvider.cs index 0a77ef7d1abfb..76d786ae763a8 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteServiceBrokerProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteServiceBrokerProvider.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.BrokeredServices; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.ServiceHub.Framework; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote.Host; diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteSolutionCache.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteSolutionCache.cs index d5b3c3be1ef77..18ef7c3fabd0f 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteSolutionCache.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteSolutionCache.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.InFlightSolution.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.InFlightSolution.cs index eb70bc2e000a7..32f89b847124b 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.InFlightSolution.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.InFlightSolution.cs @@ -8,8 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.VisualStudio.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs index a74ca6f544d62..04e5fc7c59928 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.SolutionCreator.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.ErrorReporting; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs index 4177e90130d7a..7ee0da3b27c20 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/RemoteWorkspace.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; -using Microsoft.VisualStudio.Telemetry; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; using static Microsoft.VisualStudio.Threading.ThreadingTools; diff --git a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj index 91da959adde14..54e0ca60be11f 100644 --- a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/Workspaces/Remote/ServiceHub/Services/AsynchronousOperationListener/RemoteAsynchronousOperationListenerService.cs b/src/Workspaces/Remote/ServiceHub/Services/AsynchronousOperationListener/RemoteAsynchronousOperationListenerService.cs index 7c300e43e717e..e58ac47a6bb35 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/AsynchronousOperationListener/RemoteAsynchronousOperationListenerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/AsynchronousOperationListener/RemoteAsynchronousOperationListenerService.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.TestHooks; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs index acea0f21b289f..a79cd330b7dd1 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.FactoryBase.cs @@ -7,7 +7,6 @@ using System.Globalization; using System.IO; using System.IO.Pipelines; -using System.Runtime; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.ErrorReporting; @@ -15,7 +14,6 @@ using Microsoft.ServiceHub.Framework; using Microsoft.ServiceHub.Framework.Services; using Nerdbank.Streams; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs index 3a5967c980aa4..27d5a0a2ebb47 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/BrokeredServiceBase.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.Runtime; diff --git a/src/Workspaces/Remote/ServiceHub/Services/ClientOptionsProvider.cs b/src/Workspaces/Remote/ServiceHub/Services/ClientOptionsProvider.cs index 1f18c201cd072..553d8b455abf7 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ClientOptionsProvider.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ClientOptionsProvider.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs index f13d42d978535..a40e6218fc866 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ConvertTupleToStructCodeRefactoringProvider/RemoteConvertTupleToStructCodeRefactoringService.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.ConvertTupleToStruct; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs b/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs index f6db2bbe579a5..cfa2ab365dca0 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DependentTypeFinder/RemoteDependentTypeFinderService.cs @@ -2,14 +2,12 @@ // 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.FindSymbols; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs index 6ff32cf4ac6e1..0de3f7d834c6c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/DiagnosticComputer.cs @@ -506,7 +506,11 @@ private static (ImmutableArray projectAnalyzers, ImmutableAr if (hostAnalyzerIds.Any()) { // If any host analyzers are active, make sure to also include any project diagnostic suppressors - hostBuilder.AddRange(projectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor)); + 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()); @@ -591,7 +595,13 @@ private async Task CreateCompilationWithAnal var analyzers = reference.GetAnalyzers(_project.Language); projectAnalyzerBuilder.AddRange(analyzers); - hostAnalyzerBuilder.AddRange(analyzers.WhereAsArray(static a => a is DiagnosticSuppressor)); + + 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); + analyzerMapBuilder.AppendAnalyzerMap(analyzers); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs index 633ff4bc2f275..2bdb29b05e6e5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/PerformanceQueue.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote.Diagnostics; diff --git a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs index 53691abc123f8..84341edad9d2a 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/DiagnosticAnalyzer/RemoteDiagnosticAnalyzerService.cs @@ -8,13 +8,10 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Remote.Diagnostics; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Telemetry; -using Roslyn.Utilities; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs index 451a80dd1ab58..700ec0765e293 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EditAndContinue/EditAndContinueLogReporter.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Threading; using Microsoft.VisualStudio.Debugger.Contracts.HotReload; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue; diff --git a/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs b/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs index 3986f4d2d7618..762709e92af0c 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/EncapsulateField/RemoteEncapsulateFieldService.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.EncapsulateField; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs b/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs index 1948462558784..f262e6e265941 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/FindUsages/RemoteFindUsagesService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Classification; diff --git a/src/Workspaces/Remote/ServiceHub/Services/FullyQualify/RemoteFullyQualifyService.cs b/src/Workspaces/Remote/ServiceHub/Services/FullyQualify/RemoteFullyQualifyService.cs index 3a43c914dc1a4..809383521855d 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/FullyQualify/RemoteFullyQualifyService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/FullyQualify/RemoteFullyQualifyService.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. -using System.Collections; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes.FullyQualify; diff --git a/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs b/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs index bfa385fe18bc0..320060bf361a2 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/LegacySolutionEvents/RemoteLegacySolutionEventsAggregationService.cs @@ -2,13 +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.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LegacySolutionEvents; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/NavigationBar/RemoteNavigationBarItemService.cs b/src/Workspaces/Remote/ServiceHub/Services/NavigationBar/RemoteNavigationBarItemService.cs index 4786cf00b8a2a..e6278b8cec5dd 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/NavigationBar/RemoteNavigationBarItemService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/NavigationBar/RemoteNavigationBarItemService.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.NavigationBar; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteWorkspaceConfigurationService.cs b/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteWorkspaceConfigurationService.cs index 528ddd7abaa31..b6e922e5a54e0 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteWorkspaceConfigurationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/ProcessTelemetry/RemoteWorkspaceConfigurationService.cs @@ -4,10 +4,7 @@ using System; using System.Composition; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Storage; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs index 6369abb39128b..6053c02cd7338 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticClassification/RemoteSemanticClassificationService.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs b/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs index 8446af1308920..7714a0dabd2d5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SemanticSearch/RemoteSemanticSearchService.cs @@ -2,12 +2,10 @@ // 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.Classification; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FindUsages; using Microsoft.CodeAnalysis.SemanticSearch; @@ -45,9 +43,6 @@ public ValueTask OnDefinitionFoundAsync(DefinitionItem definition, CancellationT public ValueTask OnUserCodeExceptionAsync(UserCodeExceptionInfo exception, CancellationToken cancellationToken) => callback.InvokeAsync((callback, cancellationToken) => callback.OnUserCodeExceptionAsync(callbackId, exception, cancellationToken), cancellationToken); - - public ValueTask OnCompilationFailureAsync(ImmutableArray errors, CancellationToken cancellationToken) - => callback.InvokeAsync((callback, cancellationToken) => callback.OnCompilationFailureAsync(callbackId, errors, cancellationToken), cancellationToken); } /// @@ -66,7 +61,7 @@ public ValueTask ExecuteQueryAsync( var service = solution.Services.GetLanguageServices(language).GetService(); if (service == null) { - return new ExecuteQueryResult(FeaturesResources.Semantic_search_only_supported_on_net_core); + return new ExecuteQueryResult(compilationErrors: [], FeaturesResources.Semantic_search_only_supported_on_net_core); } var clientCallbacks = new ClientCallbacks(callback, callbackId); diff --git a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs index 3c979bffd663a..a5c8a2bf46c57 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SourceGeneration/RemoteSourceGenerationService.cs @@ -10,8 +10,6 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.SourceGeneration; diff --git a/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs index 2c9c8ad1438c3..76c4d4f5cad68 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SymbolFinder/RemoteSymbolFinderService.cs @@ -3,14 +3,11 @@ // 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.FindSymbols; -using Microsoft.CodeAnalysis.FindSymbols.SymbolTree; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/Remote/ServiceHub/Services/TaskList/RemoteTaskListService.cs b/src/Workspaces/Remote/ServiceHub/Services/TaskList/RemoteTaskListService.cs index 72deab8b83e9b..ade6d02e42b56 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/TaskList/RemoteTaskListService.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/TaskList/RemoteTaskListService.cs @@ -6,9 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.TaskList; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Remote; diff --git a/src/Workspaces/Remote/ServiceHubTest/RemoteAnalyzerAssemblyLoaderTests.cs b/src/Workspaces/Remote/ServiceHubTest/RemoteAnalyzerAssemblyLoaderTests.cs index 5eaf725e8399c..1227316941eae 100644 --- a/src/Workspaces/Remote/ServiceHubTest/RemoteAnalyzerAssemblyLoaderTests.cs +++ b/src/Workspaces/Remote/ServiceHubTest/RemoteAnalyzerAssemblyLoaderTests.cs @@ -16,14 +16,19 @@ namespace Microsoft.CodeAnalysis.Remote.UnitTests { public class RemoteAnalyzerAssemblyLoaderTests { + private static AnalyzerAssemblyLoader Create(string baseDirectory) => new( + [new RemoteAnalyzerPathResolver(baseDirectory)], + [AnalyzerAssemblyLoader.StreamAnalyzerAssemblyResolver], + compilerLoadContext: null); + [Fact] public void NonIdeAnalyzerAssemblyShouldBeLoadedInSeparateALC() { using var testFixture = new AssemblyLoadTestFixture(); - var remoteAssemblyInCurrentAlc = typeof(RemoteAnalyzerAssemblyLoader).GetTypeInfo().Assembly; + var remoteAssemblyInCurrentAlc = typeof(RemoteAnalyzerPathResolver).GetTypeInfo().Assembly; var remoteAssemblyLocation = remoteAssemblyInCurrentAlc.Location; - var loader = new RemoteAnalyzerAssemblyLoader(Path.GetDirectoryName(remoteAssemblyLocation)!); + var loader = Create(Path.GetDirectoryName(remoteAssemblyLocation)!); // Try to load MS.CA.Remote.ServiceHub.dll as an analyzer assembly via RemoteAnalyzerAssemblyLoader // since it's not one of the special assemblies listed in RemoteAnalyzerAssemblyLoader, @@ -45,7 +50,7 @@ public void IdeAnalyzerAssemblyShouldBeLoadedInLoaderALC() // Try to load MS.CA.Features.dll as an analyzer assembly via RemoteAnalyzerAssemblyLoader // since it's listed as one of the special assemblies in RemoteAnalyzerAssemblyLoader, // RemoteAnalyzerAssemblyLoader should loaded in its own ALC. - var loader = new RemoteAnalyzerAssemblyLoader(Path.GetDirectoryName(featuresAssemblyLocation)!); + var loader = Create(Path.GetDirectoryName(featuresAssemblyLocation)!); loader.AddDependencyLocation(featuresAssemblyLocation); var featuresAssemblyLoadedViaRemoteLoader = loader.LoadFromPath(featuresAssemblyLocation); @@ -61,7 +66,7 @@ public void CompilerAssemblyShouldBeLoadedInLoaderALC() var compilerAssemblyInCurrentAlc = typeof(SyntaxNode).GetTypeInfo().Assembly; var compilerAssemblyLocation = compilerAssemblyInCurrentAlc.Location; - var loader = new RemoteAnalyzerAssemblyLoader(Path.GetDirectoryName(compilerAssemblyLocation)!); + var loader = Create(Path.GetDirectoryName(compilerAssemblyLocation)!); loader.AddDependencyLocation(compilerAssemblyLocation); var compilerAssemblyLoadedViaRemoteLoader = loader.LoadFromPath(compilerAssemblyLocation); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs index c4bd47a6e864b..948c320fa4cbf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeGeneration/CSharpSyntaxTokens.cs @@ -32,6 +32,7 @@ internal static class CSharpSyntaxTokens public static readonly SyntaxToken EndOfDocumentationCommentToken = Token(SyntaxKind.EndOfDocumentationCommentToken); public static readonly SyntaxToken EqualsToken = Token(SyntaxKind.EqualsToken); public static readonly SyntaxToken ExplicitKeyword = Token(SyntaxKind.ExplicitKeyword); + public static readonly SyntaxToken ExtensionKeyword = Token(SyntaxKind.ExtensionKeyword); public static readonly SyntaxToken ExternKeyword = Token(SyntaxKind.ExternKeyword); public static readonly SyntaxToken FileKeyword = Token(SyntaxKind.FileKeyword); public static readonly SyntaxToken FloatKeyword = Token(SyntaxKind.FloatKeyword); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs index 2f29d8f73a090..c1cbdb9b17557 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CodeStyle/CSharpCodeStyleOptions_Parsing.cs @@ -5,7 +5,6 @@ using System; using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeStyle; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeStyle; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs index 991dbfe11d3c8..3b5dfe1af5ffd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/CSharpVirtualCharService.cs @@ -14,7 +14,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.EmbeddedLanguages.VirtualChars; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs index 1c678e7400cf3..f49f387ffb699 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/EmbeddedLanguages/VirtualChars/RuneExtensions.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. -using System; -using System.Collections.Generic; using System.Text; using Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index 9d08af3d5e0b4..70e1207b654b4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -4,14 +4,9 @@ #nullable disable -using System.Collections.Generic; -using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs index dcfb7721ff8e2..7cf81835842be 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/DirectiveSyntaxExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Runtime.CompilerServices; using System.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs index 1e7c5eef36bae..57abe1d1c85d5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ExpressionSyntaxExtensions.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -493,8 +492,8 @@ or SyntaxKind.ObjectInitializerExpression return true; } - if (!(expression is ObjectCreationExpressionSyntax) && - !(expression is AnonymousObjectCreationExpressionSyntax) && + if (expression is not ObjectCreationExpressionSyntax && + expression is not AnonymousObjectCreationExpressionSyntax && !expression.IsLeftSideOfAssignExpression()) { var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ITypeSymbolExtensions.cs index 304762df06b73..aab4c0035fdf6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ITypeSymbolExtensions.cs @@ -44,13 +44,13 @@ public static bool TryGetPrimaryConstructor(this INamedTypeSymbol typeSymbol, [N Debug.Assert(typeSymbol.GetParameters().IsDefaultOrEmpty, "If GetParameters extension handles record, we can remove the handling here."); // A bit hacky to determine the parameters of primary constructor associated with a given record. - // Simplifying is tracked by: https://github.com/dotnet/roslyn/issues/53092. - // Note: When the issue is handled, we can remove the logic here and handle things in GetParameters extension. BUT - // if GetParameters extension method gets updated to handle records, we need to test EVERY usage - // of the extension method and make sure the change is applicable to all these usages. + // Simplifying is tracked by: https://github.com/dotnet/roslyn/issues/53092. Note: When the issue is + // handled, we can remove the logic here and handle things in GetParameters extension. BUT if GetParameters + // extension method gets updated to handle records, we need to test EVERY usage of the extension method and + // make sure the change is applicable to all these usages. primaryConstructor = typeSymbol.InstanceConstructors.FirstOrDefault( - c => c.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is RecordDeclarationSyntax or ClassDeclarationSyntax or StructDeclarationSyntax); + c => c.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() is TypeDeclarationSyntax); return primaryConstructor is not null; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs index d4aa3c168bb89..e29e9dc61cba8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/LanguageVersionExtensions.cs @@ -27,6 +27,9 @@ public static bool SupportsCollectionExpressions(this LanguageVersion languageVe public static bool SupportsPrimaryConstructors(this LanguageVersion languageVersion) => languageVersion.IsCSharp12OrAbove(); + public static bool SupportsExtensions(this LanguageVersion languageVersion) + => languageVersion.IsCSharp14OrAbove(); + /// /// Corresponds to Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.CSharpNext. /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs index a8d8ba4ab5a73..5ad67cba5ccff 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/MemberDeclarationSyntaxExtensions.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.Extensions; @@ -33,10 +32,11 @@ public static SyntaxToken GetNameToken(this MemberDeclarationSyntax member) case SyntaxKind.EnumDeclaration: return ((EnumDeclarationSyntax)member).Identifier; case SyntaxKind.ClassDeclaration: - case SyntaxKind.RecordDeclaration: + case SyntaxKind.ExtensionDeclaration: case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.StructDeclaration: return ((TypeDeclarationSyntax)member).Identifier; case SyntaxKind.DelegateDeclaration: return ((DelegateDeclarationSyntax)member).Identifier; @@ -72,10 +72,11 @@ public static int GetArity(this MemberDeclarationSyntax member) switch (member.Kind()) { case SyntaxKind.ClassDeclaration: - case SyntaxKind.RecordDeclaration: + case SyntaxKind.ExtensionDeclaration: case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.StructDeclaration: return ((TypeDeclarationSyntax)member).Arity; case SyntaxKind.DelegateDeclaration: return ((DelegateDeclarationSyntax)member).Arity; @@ -87,17 +88,20 @@ public static int GetArity(this MemberDeclarationSyntax member) return 0; } - public static TypeParameterListSyntax GetTypeParameterList(this MemberDeclarationSyntax member) +#nullable enable + + public static TypeParameterListSyntax? GetTypeParameterList(this MemberDeclarationSyntax member) { if (member != null) { switch (member.Kind()) { case SyntaxKind.ClassDeclaration: - case SyntaxKind.RecordDeclaration: + case SyntaxKind.ExtensionDeclaration: case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.StructDeclaration: + case SyntaxKind.RecordDeclaration: case SyntaxKind.RecordStructDeclaration: + case SyntaxKind.StructDeclaration: return ((TypeDeclarationSyntax)member).TypeParameterList; case SyntaxKind.DelegateDeclaration: return ((DelegateDeclarationSyntax)member).TypeParameterList; @@ -109,6 +113,8 @@ public static TypeParameterListSyntax GetTypeParameterList(this MemberDeclaratio return null; } +#nullable disable + public static MemberDeclarationSyntax WithParameterList( this MemberDeclarationSyntax member, BaseParameterListSyntax parameterList) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs index e654b58820d16..929b8052de79a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ParenthesizedExpressionSyntaxExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs index 9a0645dc6e0c0..1235118b417ee 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTreeExtensions.cs @@ -349,14 +349,14 @@ SyntaxKind.Utf8SingleLineRawStringLiteralToken or if (kind is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) { var sourceText = token.SyntaxTree!.GetText(cancellationToken); - var startDelimeterLength = 0; - var endDelimeterLength = 0; + var startDelimiterLength = 0; + var endDelimiterLength = 0; for (int i = token.SpanStart, n = token.Span.End; i < n; i++) { if (sourceText[i] != '"') break; - startDelimeterLength++; + startDelimiterLength++; } for (int i = token.Span.End - 1, n = token.Span.Start; i >= n; i--) @@ -364,17 +364,17 @@ SyntaxKind.Utf8SingleLineRawStringLiteralToken or if (sourceText[i] != '"') break; - endDelimeterLength++; + endDelimiterLength++; } - return token.Span.Length == startDelimeterLength || - (token.Span.Length > startDelimeterLength && endDelimeterLength < startDelimeterLength); + return token.Span.Length == startDelimiterLength || + (token.Span.Length > startDelimiterLength && endDelimiterLength < startDelimiterLength); } else { - var startDelimeterLength = token.IsVerbatimStringLiteral() ? 2 : 1; - return token.Span.Length == startDelimeterLength || - (token.Span.Length > startDelimeterLength && token.Text[^1] != lastChar); + var startDelimiterLength = token.IsVerbatimStringLiteral() ? 2 : 1; + return token.Span.Length == startDelimiterLength || + (token.Span.Length > startDelimiterLength && token.Text[^1] != lastChar); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTriviaListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTriviaListExtensions.cs index 99692c2e045a0..3bd36245042af 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTriviaListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/SyntaxTriviaListExtensions.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/AggregatedFormattingResult.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/AggregatedFormattingResult.cs index 2b8003af0e4b8..9000a086b5182 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/AggregatedFormattingResult.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/AggregatedFormattingResult.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs index cec283c043333..432d11ef13dcc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/CSharpStructuredTriviaFormatEngine.cs @@ -5,7 +5,6 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.CSharp.LanguageService; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.DocumentationCommentExteriorCommentRewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.DocumentationCommentExteriorCommentRewriter.cs index 7aaed1ac65c46..01230dacd8f57 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.DocumentationCommentExteriorCommentRewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.DocumentationCommentExteriorCommentRewriter.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs index 4a24d6718a9f6..9ba3a979c455b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/CSharpTriviaFormatter.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs index cc2d613fcce73..b47080399003e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.CodeShapeAnalyzer.cs @@ -5,7 +5,6 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs index 21c63a5e8f9a0..e5232f68e654b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ComplexTrivia.cs @@ -6,10 +6,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs index d62c4c9a6d9a4..38f7131220906 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.FormattedComplexTrivia.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs index 1db933c501f3b..3aafea68d6215 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.ModifiedComplexTrivia.cs @@ -5,10 +5,8 @@ using System; using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs index 1ec647df3cd22..839ff26c9d1f7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaDataFactory.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaRewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaRewriter.cs index 58f85e2643cad..96eb2c20e1d7f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaRewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Engine/Trivia/TriviaRewriter.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/AnchorIndentationFormattingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/AnchorIndentationFormattingRule.cs index 29bd5fc527641..df428cb9c26f3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/AnchorIndentationFormattingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/AnchorIndentationFormattingRule.cs @@ -57,7 +57,7 @@ public override void AddAnchorIndentationOperations(List typeSymbol.TryGetPrimaryConstructor(out primaryConstructor); + #if !CODE_STYLE public async Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpBlockFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpBlockFacts.cs index bbdecabd8cbea..06ea1e6c99591 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpBlockFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpBlockFacts.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpHeaderFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpHeaderFacts.cs index 73e965f2e5cf9..4fc1dee41714e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpHeaderFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpHeaderFacts.cs @@ -28,11 +28,27 @@ public override bool IsOnTypeHeader(SyntaxNode root, int position, bool fullHead if (node == null) return false; - var lastToken = (node as TypeDeclarationSyntax)?.TypeParameterList?.GetLastToken() ?? node.Identifier; - if (fullHeader) - lastToken = node.BaseList?.GetLastToken() ?? lastToken; + return IsOnHeader(root, position, node, GetLastToken()); - return IsOnHeader(root, position, node, lastToken); + SyntaxToken GetLastToken() + { + if (fullHeader && node.BaseList != null) + return node.BaseList.GetLastToken(); + + if (node is TypeDeclarationSyntax { TypeParameterList.GreaterThanToken: var greaterThanToken }) + return greaterThanToken; + + // .Identifier may be default in the case of an extension type. + if (node.Identifier != default) + return node.Identifier; + + return node switch + { + TypeDeclarationSyntax typeDeclaration => typeDeclaration.Keyword, + EnumDeclarationSyntax enumDeclaration => enumDeclaration.EnumKeyword, + _ => throw ExceptionUtilities.Unreachable(), + }; + } } public override bool IsOnPropertyDeclarationHeader(SyntaxNode root, int position, [NotNullWhen(true)] out SyntaxNode? propertyDeclaration) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 9cbc626969729..2b88dca401e00 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -75,7 +75,10 @@ public bool SupportsImplicitImplementationOfNonPublicInterfaceMembers(ParseOptio => options.LanguageVersion() >= LanguageVersion.CSharp10; public bool SupportsFieldExpression(ParseOptions options) - => options.LanguageVersion() >= LanguageVersionExtensions.CSharpNext; + => options.LanguageVersion().IsCSharp14OrAbove(); + + public bool SupportsNullConditionalAssignment(ParseOptions options) + => options.LanguageVersion().IsCSharp14OrAbove(); public SyntaxToken ParseToken(string text) => SyntaxFactory.ParseToken(text); @@ -104,7 +107,7 @@ public bool IsOperator(SyntaxToken token) return (SyntaxFacts.IsAnyUnaryExpression(kind) && - (token.Parent is PrefixUnaryExpressionSyntax || token.Parent is PostfixUnaryExpressionSyntax || token.Parent is OperatorDeclarationSyntax)) || + (token.Parent is PrefixUnaryExpressionSyntax or PostfixUnaryExpressionSyntax or OperatorDeclarationSyntax)) || (SyntaxFacts.IsBinaryExpression(kind) && (token.Parent is BinaryExpressionSyntax or OperatorDeclarationSyntax or RelationalPatternSyntax)) || (SyntaxFacts.IsAssignmentExpressionOperatorToken(kind) && token.Parent is AssignmentExpressionSyntax); } @@ -731,8 +734,6 @@ EnumMemberDeclarationSyntax or public bool IsTopLevelNodeWithMembers([NotNullWhen(true)] SyntaxNode? node) => node is BaseNamespaceDeclarationSyntax or BaseTypeDeclarationSyntax; - private const string dotToken = "."; - public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, string? rootNamespace = null) { if (node == null) @@ -755,7 +756,7 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin } } - var names = ArrayBuilder.GetInstance(); + using var _ = ArrayBuilder.GetInstance(out var names); // containing type(s) var parent = node.GetAncestor() ?? node.Parent; while (parent is TypeDeclarationSyntax) @@ -779,7 +780,7 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin if (name != null) { builder.Append(name); - builder.Append(dotToken); + builder.Append('.'); } } @@ -795,7 +796,7 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin return pooled.ToStringAndFree(); } - private static string? GetName(SyntaxNode node, DisplayNameOptions options) + private string? GetName(SyntaxNode node, DisplayNameOptions options) { const string missingTokenPlaceholder = "?"; @@ -813,72 +814,72 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin return GetName(((BaseNamespaceDeclarationSyntax)node).Name, options); case SyntaxKind.QualifiedName: var qualified = (QualifiedNameSyntax)node; - return GetName(qualified.Left, options) + dotToken + GetName(qualified.Right, options); + return $"{GetName(qualified.Left, options)}.{GetName(qualified.Right, options)}"; } - string? name = null; if (node is MemberDeclarationSyntax memberDeclaration) { - if (memberDeclaration.Kind() == SyntaxKind.ConversionOperatorDeclaration) + if (memberDeclaration is ConversionOperatorDeclarationSyntax conversionOperator) + return ConvertToSingleLine(conversionOperator.Type).ToString(); + + var nameToken = memberDeclaration.GetNameToken(); + if (nameToken != default) { - name = (memberDeclaration as ConversionOperatorDeclarationSyntax)?.Type.ToString(); + using var _ = PooledStringBuilder.GetInstance(out var builder); + if (memberDeclaration.Kind() == SyntaxKind.DestructorDeclaration) + builder.Append('~'); + + builder.Append(nameToken.IsMissing ? missingTokenPlaceholder : nameToken.Text); + + if ((options & DisplayNameOptions.IncludeTypeParameters) != 0) + AppendTypeParameterList(builder, memberDeclaration.GetTypeParameterList()); + + return builder.ToString(); } - else + else if (memberDeclaration is ExtensionDeclarationSyntax extensionDeclaration) { - var nameToken = memberDeclaration.GetNameToken(); - if (nameToken != default) - { - name = nameToken.IsMissing ? missingTokenPlaceholder : nameToken.Text; - if (memberDeclaration.Kind() == SyntaxKind.DestructorDeclaration) - { - name = "~" + name; - } + using var _ = PooledStringBuilder.GetInstance(out var builder); + builder.Append("extension"); - if ((options & DisplayNameOptions.IncludeTypeParameters) != 0) - { - var pooled = PooledStringBuilder.GetInstance(); - var builder = pooled.Builder; - builder.Append(name); - AppendTypeParameterList(builder, memberDeclaration.GetTypeParameterList()); - name = pooled.ToStringAndFree(); - } - } - else - { - Debug.Assert(memberDeclaration.Kind() == SyntaxKind.IncompleteMember); - name = "?"; - } + if ((options & DisplayNameOptions.IncludeTypeParameters) != 0) + AppendTypeParameterList(builder, memberDeclaration.GetTypeParameterList()); + + AppendParameterList(builder, extensionDeclaration.ParameterList); + return builder.ToString(); } - } - else - { - if (node is VariableDeclaratorSyntax fieldDeclarator) + else { - var nameToken = fieldDeclarator.Identifier; - if (nameToken != default) - { - name = nameToken.IsMissing ? missingTokenPlaceholder : nameToken.Text; - } + Debug.Assert(memberDeclaration.Kind() == SyntaxKind.IncompleteMember); + return "?"; } } + else if (node is VariableDeclaratorSyntax fieldDeclarator) + { + var nameToken = fieldDeclarator.Identifier; + return nameToken.IsMissing ? missingTokenPlaceholder : nameToken.Text; + } - Debug.Assert(name != null, "Unexpected node type " + node.Kind()); - return name; - } + Debug.Fail("Unexpected node type " + node.Kind()); + return null; - private static void AppendTypeParameterList(StringBuilder builder, TypeParameterListSyntax typeParameterList) - { - if (typeParameterList != null && typeParameterList.Parameters.Count > 0) + static void AppendTypeParameterList(StringBuilder builder, TypeParameterListSyntax? typeParameterList) { - builder.Append('<'); - builder.Append(typeParameterList.Parameters[0].Identifier.ValueText); - for (var i = 1; i < typeParameterList.Parameters.Count; i++) + if (typeParameterList != null) { - builder.Append(", "); - builder.Append(typeParameterList.Parameters[i].Identifier.ValueText); + builder.Append('<'); + builder.Append(string.Join(", ", typeParameterList.Parameters.Select(static p => p.Identifier.ValueText))); + builder.Append('>'); } + } - builder.Append('>'); + void AppendParameterList(StringBuilder builder, ParameterListSyntax? parameterList) + { + if (parameterList != null) + { + builder.Append('('); + builder.Append(string.Join(", ", parameterList.Parameters.Select(p => ConvertToSingleLine(p.Type)))); + builder.Append(')'); + } } } @@ -912,53 +913,19 @@ protected override void AppendMembers(SyntaxNode? node, ArrayBuilder public TextSpan GetMemberBodySpanForSpeculativeBinding(SyntaxNode node) { if (node.Span.IsEmpty) - { return default; - } var member = GetContainingMemberDeclaration(node, node.SpanStart); if (member == null) - { return default; - } // TODO: currently we only support method for now - if (member is BaseMethodDeclarationSyntax method) - { - if (method.Body == null) - { - return default; - } - - return GetBlockBodySpan(method.Body); - } + if (member is BaseMethodDeclarationSyntax { Body: not null } method) + return TextSpan.FromBounds(method.Body.OpenBraceToken.Span.End, method.Body.CloseBraceToken.SpanStart); return default; } - public bool ContainsInMemberBody([NotNullWhen(true)] SyntaxNode? node, TextSpan span) - { - switch (node) - { - case ConstructorDeclarationSyntax constructor: - return (constructor.Body != null && GetBlockBodySpan(constructor.Body).Contains(span)) || - (constructor.Initializer != null && constructor.Initializer.Span.Contains(span)); - case BaseMethodDeclarationSyntax method: - return method.Body != null && GetBlockBodySpan(method.Body).Contains(span); - case BasePropertyDeclarationSyntax property: - return property.AccessorList != null && property.AccessorList.Span.Contains(span); - case EnumMemberDeclarationSyntax @enum: - return @enum.EqualsValue != null && @enum.EqualsValue.Span.Contains(span); - case BaseFieldDeclarationSyntax field: - return field.Declaration != null && field.Declaration.Span.Contains(span); - } - - return false; - } - - private static TextSpan GetBlockBodySpan(BlockSyntax body) - => TextSpan.FromBounds(body.OpenBraceToken.Span.End, body.CloseBraceToken.SpanStart); - public SyntaxNode? TryGetBindableParent(SyntaxToken token) { var node = token.Parent; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs index 918895ef01572..5afb9f1eadcfb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxKinds.cs @@ -70,6 +70,7 @@ public int Convert(TSyntaxKind kind) where TSyntaxKind : struct public int FalseKeyword => (int)SyntaxKind.FalseKeyword; public int IfKeyword => (int)SyntaxKind.IfKeyword; public int NewKeyword => (int)SyntaxKind.NewKeyword; + public int PartialKeyword => (int)SyntaxKind.PartialKeyword; public int TrueKeyword => (int)SyntaxKind.TrueKeyword; public int UsingKeyword => (int)SyntaxKind.UsingKeyword; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpInferredMemberNameSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpInferredMemberNameSimplifier.cs index f7e9aef695f80..7d066aa2b7e1a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpInferredMemberNameSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/CSharpInferredMemberNameSimplifier.cs @@ -2,9 +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 Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs index f6488396cf1f0..19f48c1475d69 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Simplification/Simplifiers/CastSimplifier.cs @@ -937,7 +937,7 @@ private static bool IsNullOrErrorType(TypeInfo info) => IsNullOrErrorType(info.Type) || IsNullOrErrorType(info.ConvertedType); private static bool IsNullOrErrorType([NotNullWhen(false)] ITypeSymbol? type) - => type is null || type is IErrorTypeSymbol; + => type is null or IErrorTypeSymbol; private static bool CastRemovalWouldCauseUnintendedReferenceComparisonWarning( ExpressionSyntax expression, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs index bfc64ac68efb1..6da04e237e4ce 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/SpeculationAnalyzer.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Linq; using System.Threading; -using System.Xml.Serialization; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TokenComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TokenComparer.cs index cd25294eb1469..dfdc92b74aa92 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TokenComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TokenComparer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; -using Microsoft.CodeAnalysis.CSharp.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs index a2b5aae07ad25..c15705abd5ba6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.State.cs @@ -5,7 +5,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs index 48e8f45c34dc2..2133f04188828 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpTypeStyleHelper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs index 679bf4d52bf17..67bee3e072b04 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/TypeStyle/CSharpUseExplicitTypeHelper.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/UsingsAndExternAliasesDirectiveComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/UsingsAndExternAliasesDirectiveComparer.cs index 0e4a9f22c20b0..dee7d8be5ce4f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/UsingsAndExternAliasesDirectiveComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Utilities/UsingsAndExternAliasesDirectiveComparer.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs index 0061f26bf1b40..2aa8408f2b58e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeGeneration/CodeGenerationOptions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Microsoft.CodeAnalysis.AddImport; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CollectionExpressionPreference.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CollectionExpressionPreference.cs index b5973d10a29b1..fab94e9fc3b46 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CollectionExpressionPreference.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/CollectionExpressionPreference.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. -using System; -using System.Collections.Generic; -using System.Text; using Microsoft.CodeAnalysis.CodeStyle; namespace Microsoft.CodeAnalysis.Shared.CodeStyle; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/EditorConfigSeverityStrings.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/EditorConfigSeverityStrings.cs index 8876b66bc702b..cadb2413571d2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/EditorConfigSeverityStrings.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/EditorConfigSeverityStrings.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. -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.CodeAnalysis; internal static class EditorConfigSeverityStrings diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/FadingOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/FadingOptions.cs index 0a4cd95db9c33..a53cb54f98835 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/FadingOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/FadingOptions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NotificationOption2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NotificationOption2.cs index 06bbca420adfc..62887390e339b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NotificationOption2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/NotificationOption2.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeStyle; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/VisualBasic/VisualBasicCodeStyleOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/VisualBasic/VisualBasicCodeStyleOptions.cs index 1d5e7a2694f63..9c2f9882bc202 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/VisualBasic/VisualBasicCodeStyleOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/CodeStyle/VisualBasic/VisualBasicCodeStyleOptions.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Text; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTreeHelpers.NodeEnumerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTreeHelpers.NodeEnumerator.cs index ab41aa435c149..317c13ea42e6c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTreeHelpers.NodeEnumerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Collections/IntervalTreeHelpers.NodeEnumerator.cs @@ -4,7 +4,6 @@ using System.Collections; using System.Collections.Generic; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Collections; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/LanguageConstants.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/LanguageConstants.cs index d8558e45d1f0d..f2420c2298109 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/LanguageConstants.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/LanguageConstants.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Text; - namespace Microsoft.CodeAnalysis.EditorConfig; internal static class LanguageConstants diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/IEditorConfigOptionAccumulator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/IEditorConfigOptionAccumulator.cs index 247262637cee0..b242b074d7683 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/IEditorConfigOptionAccumulator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/IEditorConfigOptionAccumulator.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EditorConfig.Parsing; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/NamingStyles/NamingStyleOptionAccumulator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/NamingStyles/NamingStyleOptionAccumulator.cs index 656e3458348a1..e384fee0bfb5e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/NamingStyles/NamingStyleOptionAccumulator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EditorConfig/Parsing/NamingStyles/NamingStyleOptionAccumulator.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.EditorConfigNamingStyleParser; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/Common/EmbeddedDiagnostic.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/Common/EmbeddedDiagnostic.cs index b17f4d4bd1235..88344f39c637f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/Common/EmbeddedDiagnostic.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/Common/EmbeddedDiagnostic.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.Common; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs index 187e045eef7ab..e9c6524b84869 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/AbstractVirtualCharService.cs @@ -7,7 +7,6 @@ using System.Text; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.LanguageService; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualChar.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualChar.cs index 9f4ac3d1d8e47..0e2fd7c6e3231 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualChar.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualChar.cs @@ -5,7 +5,6 @@ using System; using System.Text; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EmbeddedLanguages.VirtualChars; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Enumerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Enumerator.cs index 331512aa7fbe7..941dd26488984 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Enumerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/EmbeddedLanguages/VirtualChars/VirtualCharSequence.Enumerator.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections; using System.Collections.Generic; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalysisContextExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalysisContextExtensions.cs index f4f351d4ae6a2..2b17437a2af43 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalysisContextExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalysisContextExtensions.cs @@ -5,7 +5,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs index d4f598d420175..b61dd3a2f2c52 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/AnalyzerConfigOptionsExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions.cs index 096a23b825d91..22e039c02b096 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.CodeStyle; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions_Shared.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions_Shared.cs index 52fe393accbf3..4b717c36220c7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions_Shared.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DiagnosticSeverityExtensions_Shared.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DirectiveInfo.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DirectiveInfo.cs index 4563c330271a9..87c680a6fb5ee 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DirectiveInfo.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/DirectiveInfo.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ICompilationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ICompilationExtensions.cs index 2a8b64755a480..d28f62304e326 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ICompilationExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ICompilationExtensions.cs @@ -3,8 +3,10 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -69,12 +71,24 @@ public static ImmutableArray GetReferencedAssemblySymbols(this return builder.ToImmutableAndFree(); } + public static INamedTypeSymbol? ArgumentExceptionType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(ArgumentException).FullName!); + + public static INamedTypeSymbol? ArgumentNullExceptionType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(ArgumentNullException).FullName!); + public static INamedTypeSymbol? ArrayType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(Array).FullName!); public static INamedTypeSymbol? AttributeType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(Attribute).FullName!); + public static INamedTypeSymbol? BlockingCollectionOfTType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(BlockingCollection<>).FullName!); + + public static INamedTypeSymbol? CollectionOfTType(this Compilation compilation) + => compilation.GetTypeByMetadataName(typeof(Collection<>).FullName!); + public static INamedTypeSymbol? ExceptionType(this Compilation compilation) => compilation.GetTypeByMetadataName(typeof(Exception).FullName!); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/IMethodSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/IMethodSymbolExtensions.cs index b1d0f13b0eb05..3486531d1de76 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/IMethodSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/IMethodSymbolExtensions.cs @@ -6,9 +6,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.CodeAnalysis.LanguageService; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/INamespaceOrTypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/INamespaceOrTypeSymbolExtensions.cs index 169623435a5d6..51d9f11b43b9e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/INamespaceOrTypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/INamespaceOrTypeSymbolExtensions.cs @@ -8,7 +8,6 @@ using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs index 8bd37a405f656..6a62c91ab702d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ISymbolExtensions_Accessibility.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CollectTypeParameterSymbolsVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CollectTypeParameterSymbolsVisitor.cs index 054a38b5a4d15..9211e17456337 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CollectTypeParameterSymbolsVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CollectTypeParameterSymbolsVisitor.cs @@ -2,20 +2,23 @@ // 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.Generic; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class ITypeSymbolExtensions { private sealed class CollectTypeParameterSymbolsVisitor( - IList typeParameters, - bool onlyMethodTypeParameters) : SymbolVisitor + ArrayBuilder typeParameters, + bool onlyMethodTypeParameters) : SymbolVisitor, IDisposable { - private readonly HashSet _visited = []; + private readonly PooledHashSet _visited = PooledHashSet.GetInstance(); + + public void Dispose() + { + _visited.Free(); + } public override void DefaultVisit(ISymbol node) => throw new NotImplementedException(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CompilationTypeGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CompilationTypeGenerator.cs index 39c5c0d5682d7..53fd146c7f233 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CompilationTypeGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.CompilationTypeGenerator.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 - namespace Microsoft.CodeAnalysis.Shared.Extensions; internal static partial class ITypeSymbolExtensions diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs index dfa471e44ec1e..c5eb6a4d7b6de 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ITypeSymbolExtensions.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; @@ -796,20 +797,34 @@ public static bool IsInlineArray([NotNullWhen(true)] this ITypeSymbol? type) { return type?.Accept(new UnnamedErrorTypeRemover(compilation)); } - public static IList GetReferencedMethodTypeParameters( - this ITypeSymbol? type, IList? result = null) + + public static void AddReferencedMethodTypeParameters( + this ITypeSymbol? type, ArrayBuilder result) { - result ??= []; - type?.Accept(new CollectTypeParameterSymbolsVisitor(result, onlyMethodTypeParameters: true)); - return result; + AddReferencedTypeParameters(type, result, onlyMethodTypeParameters: true); } - public static IList GetReferencedTypeParameters( - this ITypeSymbol? type, IList? result = null) + public static void AddReferencedTypeParameters( + this ITypeSymbol? type, ArrayBuilder result) { - result ??= []; - type?.Accept(new CollectTypeParameterSymbolsVisitor(result, onlyMethodTypeParameters: false)); - return result; + AddReferencedTypeParameters(type, result, onlyMethodTypeParameters: false); + } + + private static void AddReferencedTypeParameters( + this ITypeSymbol? type, ArrayBuilder result, bool onlyMethodTypeParameters) + { + if (type != null) + { + using var collector = new CollectTypeParameterSymbolsVisitor(result, onlyMethodTypeParameters); + type.Accept(collector); + } + } + + public static IList GetReferencedTypeParameters(this ITypeSymbol? type) + { + using var _ = ArrayBuilder.GetInstance(out var result); + AddReferencedTypeParameters(type, result); + return result.ToList(); } [return: NotNullIfNotNull(parameterName: nameof(type))] diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index 48ba16001283e..5e059879b6066 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/LocationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/LocationExtensions.cs index ef1b7a56b34ef..192df5e591963 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/LocationExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/LocationExtensions.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ObjectWriterExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ObjectWriterExtensions.cs index cbc86569e6d57..475be240853d2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ObjectWriterExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ObjectWriterExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ReportDiagnosticExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ReportDiagnosticExtensions.cs index 9e7045ca5abf7..5200414715f38 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ReportDiagnosticExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ReportDiagnosticExtensions.cs @@ -4,7 +4,6 @@ using System; using Microsoft.CodeAnalysis.CodeStyle; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SemanticEquivalence.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SemanticEquivalence.cs index b9805bdfe3e96..e0b11b761b9e2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SemanticEquivalence.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SemanticEquivalence.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SourceTextExtensions_SharedWithCodeStyle.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SourceTextExtensions_SharedWithCodeStyle.cs index 32f8bd1348542..76fb6dbb226a7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SourceTextExtensions_SharedWithCodeStyle.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SourceTextExtensions_SharedWithCodeStyle.cs @@ -5,7 +5,6 @@ using System; using System.Threading; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/StackExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/StackExtensions.cs index 0df5a82729fd4..1a192e8bc6ab5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/StackExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/StackExtensions.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxTreeExtensions.cs index 77d4b1e1d214c..2752f6e9cd32b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/SyntaxTreeExtensions.cs @@ -17,7 +17,9 @@ internal static partial class SyntaxTreeExtensions { public static bool OverlapsHiddenPosition([NotNullWhen(returnValue: true)] this SyntaxTree? tree, TextSpan span, CancellationToken cancellationToken) { - if (tree == null) + // Short-circuit if there are no line mappings to avoid potentially realizing the source text. + // All lines are visible if there are no line mappings. + if (tree == null || tree.GetLineMappings(cancellationToken).IsEmpty()) { return false; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs index a7a322b08ecb8..821d7f87990d0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/CustomDataFlowAnalysis.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FlowAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/DataFlowAnalyzer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/DataFlowAnalyzer.cs index 1637735d46d1d..462a45c1e7daf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/DataFlowAnalyzer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/DataFlowAnalyzer.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.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/FlowCaptureKind.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/FlowCaptureKind.cs index cae9bc1b8b879..9b5de6361b601 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/FlowCaptureKind.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/FlowCaptureKind.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 - namespace Microsoft.CodeAnalysis.FlowAnalysis; /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs index 66bf828dcf33f..f1ed7ccdaae96 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.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.Generic; using Microsoft.CodeAnalysis.PooledObjects; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs index 9f4c4a4d77edf..42c30ebf2196a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.OperationTreeAnalysisData.cs @@ -2,15 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FlowAnalysis.SymbolUsageAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs index 44910ce0b3446..2268196be9bcd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.Walker.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FlowAnalysis.SymbolUsageAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs index 18b5732b867af..e0fcc56a680cd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageResult.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.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.IndentationData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.IndentationData.cs index 823d39dd0d745..df97551d56dca 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.IndentationData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.IndentationData.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs index bc847a5e2e56f..cf9a64fabd374 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.InitialContextFinder.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs index 16d91559bef3d..0df5565d06940 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Context/FormattingContext.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/DocumentFormattingOptions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/DocumentFormattingOptions.cs index 8480f2cd540d2..d7472246f1389 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/DocumentFormattingOptions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/DocumentFormattingOptions.cs @@ -3,10 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractAggregatedFormattingResult.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractAggregatedFormattingResult.cs index 4253c2f4d7cca..88574b4bf80a6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractAggregatedFormattingResult.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractAggregatedFormattingResult.cs @@ -8,7 +8,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs index 51c35aff5e931..490b310bafb99 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.AbstractComplexTrivia.cs @@ -4,7 +4,6 @@ using System.Threading; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs index 2e1c7f1a60ce4..ad716e23bbf7b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.FormattedWhitespace.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs index 474e09b6a354d..495dc6548c390 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.ModifiedWhitespace.cs @@ -4,8 +4,6 @@ using System; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs index bda9b5b3785b4..542a744ec66cc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.Whitespace.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs index 71a0fce380856..d9847c2da7662 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractTriviaDataFactory.cs @@ -4,10 +4,6 @@ using System; using System.Collections.Generic; -using System.Security.Principal; -using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs index b22e34f0e2904..bb99dbb29cdf3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/ChainedFormattingRules.cs @@ -8,7 +8,6 @@ using System.Collections.Immutable; using System.Linq; using System.Reflection; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs index 1bc2f1c870430..9f027e199acda 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenData.cs @@ -3,10 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenPairWithOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenPairWithOperations.cs index 6e24485386c9e..1742783d2ff37 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenPairWithOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenPairWithOperations.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Formatting.Rules; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs index 7fc5e874ff56c..cba2d36c6850e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs @@ -7,11 +7,9 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Debug.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Debug.cs index 9afc40db1754b..447fe86b4076c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Debug.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Debug.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Node.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Node.cs index 0569ec47103ee..851889b01bded 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Node.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.Node.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.NodeAndText.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.NodeAndText.cs index 11a2596964922..4b4df723f977d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.NodeAndText.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.NodeAndText.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.StructuredTrivia.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.StructuredTrivia.cs index c11ac58f56781..5bd16b06d4f34 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.StructuredTrivia.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.StructuredTrivia.cs @@ -4,7 +4,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.cs index af1e22a020df9..31c6c1b9f86d3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TreeData.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs index 4f62d0df988a8..4c40452640fe2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TriviaData.cs @@ -5,9 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs index 2f56cc8771715..6e2e935413c3a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/FormattingExtensions.cs @@ -13,7 +13,6 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAlignTokensOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAlignTokensOperationAction.cs index 623f0ea92ae89..ce1b3f15e21a8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAlignTokensOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAlignTokensOperationAction.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAnchorIndentationOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAnchorIndentationOperationAction.cs index 77c4b9a484870..aad6a25ec9098 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAnchorIndentationOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextAnchorIndentationOperationAction.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustNewLinesOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustNewLinesOperation.cs index 84c51ec522e9f..6ed6763af6a7d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustNewLinesOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustNewLinesOperation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustSpacesOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustSpacesOperation.cs index 7e99042f0aa5d..87bc36ebd1420 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustSpacesOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextGetAdjustSpacesOperation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextIndentBlockOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextIndentBlockOperationAction.cs index 1c4e8bcf02125..5e8fb587ab840 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextIndentBlockOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextIndentBlockOperationAction.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Collections.Immutable; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs index f0a02813eb808..64a0891413dcd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/NextSuppressOperationAction.cs @@ -2,10 +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 Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustNewLinesOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustNewLinesOperation.cs index 6bd175857e779..269f43f873199 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustNewLinesOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustNewLinesOperation.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Formatting.Rules; /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustSpacesOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustSpacesOperation.cs index 0696ae808b9a6..952f06eb5e80d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustSpacesOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AdjustSpacesOperation.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.Formatting.Rules; /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs index b91e75d3caaa5..23700e9de0bbc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/AnchorIndentationOperation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs index ea13eaf68014c..00c050309b163 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/FormattingOperations.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/IndentBlockOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/IndentBlockOperation.cs index 47200381d83de..fa545ead82ba9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/IndentBlockOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/IndentBlockOperation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/SuppressOperation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/SuppressOperation.cs index c0c304dd28574..47bac08fa1188 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/SuppressOperation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Rules/Operations/SuppressOperation.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Formatting.Rules; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs index b99da34f0b4c1..96b39acc5e2e5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/TriviaEngine/AbstractTriviaFormatter.cs @@ -8,7 +8,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Helpers/RemoveUnnecessaryImports/RemoveUnnecessaryImportsHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Helpers/RemoveUnnecessaryImports/RemoveUnnecessaryImportsHelpers.cs index 733a72386b148..ac011f6908b1e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Helpers/RemoveUnnecessaryImports/RemoveUnnecessaryImportsHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Helpers/RemoveUnnecessaryImports/RemoveUnnecessaryImportsHelpers.cs @@ -2,10 +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.Linq; -using System.Text; using Microsoft.CodeAnalysis.LanguageService; namespace Microsoft.CodeAnalysis.Shared.Helpers.RemoveUnnecessaryImports; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/AbstractIndentation.Indenter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/AbstractIndentation.Indenter.cs index 9c2e9583ece61..63a2e0ce43f52 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/AbstractIndentation.Indenter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/AbstractIndentation.Indenter.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Indentation; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/ISmartTokenFormatter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/ISmartTokenFormatter.cs index bd162af4bdde3..07b04326d45e3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/ISmartTokenFormatter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Indentation/ISmartTokenFormatter.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Indentation; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index d92c2dec5c408..8c37a0ce21abc 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -641,6 +641,10 @@ internal enum FunctionId Copilot_Generate_Documentation_Accepted = 823, Copilot_Generate_Documentation_Canceled = 824, + Copilot_Implement_NotImplementedException_Fix_Registered = 830, + Copilot_Implement_NotImplementedException_Failed = 831, + Copilot_Implement_NotImplementedException_Completed = 832, + Copilot_Rename = 851, VSCode_LanguageServer_Started = 860, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingRule.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingRule.cs index 21aa46499162d..e697a1bf349d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingRule.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingRule.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.EditorConfig.Parsing; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingStyle.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingStyle.cs index 75d86a84044c2..40fea29c47c0d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingStyle.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/EditorConfig/EditorConfigNamingStyleParser_NamingStyle.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.EditorConfig.Parsing.NamingStyles; using Microsoft.CodeAnalysis.NamingStyles; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Naming/IdentifierNameParts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Naming/IdentifierNameParts.cs index 3faa922906d29..a4049bc0b136e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Naming/IdentifierNameParts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/Naming/IdentifierNameParts.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.Collections.Immutable; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; using Microsoft.CodeAnalysis.NamingStyles; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerable.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerable.cs index 0c0bae6082807..abed7cc066568 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerable.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerable.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 Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.NamingStyles; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerator.cs index b4eadecfdbd93..f263d790b8277 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyle.WordSpanEnumerator.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.Diagnostics; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyleRules.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyleRules.cs index c70501242743d..ac44ce18aebb1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyleRules.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/NamingStyles/NamingStyleRules.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.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs index 04475f8160883..48fd3a6008d68 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledDisposer.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. -using System; -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis.PooledObjects; [NonCopyable] diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IEditorConfigValueSerializer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IEditorConfigValueSerializer.cs index c2b269f535131..66037f83cc4c9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IEditorConfigValueSerializer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IEditorConfigValueSerializer.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. -using System; - namespace Microsoft.CodeAnalysis.Options; internal interface IEditorConfigValueSerializer diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IOptionReader.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IOptionReader.cs index 078b8430ad7c2..832cd6889a9c0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IOptionReader.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/IOptionReader.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Text; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/Option2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/Option2.cs index 01e7d62ba20fe..0e4634707a473 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/Option2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/Option2.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Immutable; using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionDefinition.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionDefinition.cs index 8f0c4f3347445..0ef0bfb8a9e03 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionDefinition.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionDefinition.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionGroup.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionGroup.cs index a1f05a1ea3376..93e6639dc75e1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionGroup.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionGroup.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. -using System; - namespace Microsoft.CodeAnalysis.Options; /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionKey2.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionKey2.cs index b2eaabcd75f7c..f3916eeda0a33 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionKey2.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/OptionKey2.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using Roslyn.Utilities; #if CODE_STYLE using WorkspacesResources = Microsoft.CodeAnalysis.CodeStyleResources; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/PublicOptionFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/PublicOptionFactory.cs index f3165ed53e9ba..aba0e3e615fe6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/PublicOptionFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Options/PublicOptionFactory.cs @@ -3,9 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/HeaderFacts/AbstractHeaderFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/HeaderFacts/AbstractHeaderFacts.cs index ad5bb68d9be47..1ed0432144e9a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/HeaderFacts/AbstractHeaderFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/HeaderFacts/AbstractHeaderFacts.cs @@ -3,16 +3,11 @@ // 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 System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/Precedence/IPrecedenceService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/Precedence/IPrecedenceService.cs index 9092747c8aa3d..2fd4b1266e744 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/Precedence/IPrecedenceService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/Precedence/IPrecedenceService.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 - namespace Microsoft.CodeAnalysis.Precedence; internal interface IPrecedenceService diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SelectedMembers/AbstractSelectedMembers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SelectedMembers/AbstractSelectedMembers.cs index 840b4cb27697f..3d5cefb9fd5c9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SelectedMembers/AbstractSelectedMembers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SelectedMembers/AbstractSelectedMembers.cs @@ -5,7 +5,6 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ForEachSymbols.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ForEachSymbols.cs index 2de67f0cca83e..01a953c7f803e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ForEachSymbols.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ForEachSymbols.cs @@ -2,29 +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. -#nullable disable - namespace Microsoft.CodeAnalysis.LanguageService; -internal readonly struct ForEachSymbols +internal readonly struct ForEachSymbols( + IMethodSymbol? getEnumeratorMethod, + IMethodSymbol? moveNextMethod, + IPropertySymbol? currentProperty, + IMethodSymbol? disposeMethod, + ITypeSymbol? elementType) { - public readonly IMethodSymbol GetEnumeratorMethod; - public readonly IMethodSymbol MoveNextMethod; - public readonly IPropertySymbol CurrentProperty; - public readonly IMethodSymbol DisposeMethod; - public readonly ITypeSymbol ElementType; - - internal ForEachSymbols(IMethodSymbol getEnumeratorMethod, - IMethodSymbol moveNextMethod, - IPropertySymbol currentProperty, - IMethodSymbol disposeMethod, - ITypeSymbol elementType) - : this() - { - this.GetEnumeratorMethod = getEnumeratorMethod; - this.MoveNextMethod = moveNextMethod; - this.CurrentProperty = currentProperty; - this.DisposeMethod = disposeMethod; - this.ElementType = elementType; - } + public readonly IMethodSymbol? GetEnumeratorMethod = getEnumeratorMethod; + public readonly IMethodSymbol? MoveNextMethod = moveNextMethod; + public readonly IPropertySymbol? CurrentProperty = currentProperty; + public readonly IMethodSymbol? DisposeMethod = disposeMethod; + public readonly ITypeSymbol? ElementType = elementType; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs index 7f05d5afec83f..7db9c7e856f5e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SemanticFacts/ISemanticFacts.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.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.LanguageService; @@ -121,6 +119,8 @@ internal partial interface ISemanticFacts /// IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node); + bool TryGetPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol? primaryConstructor); + #if !CODE_STYLE /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractDocumentationCommentService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractDocumentationCommentService.cs index 535cdc8f2fea2..dc7b3a1f74f77 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractDocumentationCommentService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/AbstractDocumentationCommentService.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. -#nullable disable - -using System.Diagnostics; using System.Linq; using System.Threading; using Roslyn.Utilities; @@ -59,8 +56,11 @@ private string GetDocumentationCommentPrefix(TDocumentationCommentTriviaSyntax d } public string GetBannerText( - TDocumentationCommentTriviaSyntax documentationComment, int maxBannerLength, CancellationToken cancellationToken) + TDocumentationCommentTriviaSyntax? documentationComment, int maxBannerLength, CancellationToken cancellationToken) { + if (documentationComment is null) + return ""; + // TODO: Consider unifying code to extract text from an Xml Documentation Comment (https://github.com/dotnet/roslyn/issues/2290) var summaryElement = documentationComment.ChildNodes().OfType() @@ -187,6 +187,6 @@ private static bool HasLeadingWhitespace(string tokenText) private static bool HasTrailingWhitespace(string tokenText) => tokenText.Length > 0 && char.IsWhiteSpace(tokenText[^1]); - public string GetBannerText(SyntaxNode documentationCommentTriviaSyntax, int maxBannerLength, CancellationToken cancellationToken) - => GetBannerText((TDocumentationCommentTriviaSyntax)documentationCommentTriviaSyntax, maxBannerLength, cancellationToken); + public string GetBannerText(SyntaxNode? documentationCommentTriviaSyntax, int maxBannerLength, CancellationToken cancellationToken) + => GetBannerText((TDocumentationCommentTriviaSyntax?)documentationCommentTriviaSyntax, maxBannerLength, cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ExternalSourceInfo.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ExternalSourceInfo.cs index d24d9ae046f56..204524d138621 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ExternalSourceInfo.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ExternalSourceInfo.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 - namespace Microsoft.CodeAnalysis.LanguageService; internal readonly struct ExternalSourceInfo(int? startLine, bool ends) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/IDocumentationCommentService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/IDocumentationCommentService.cs index d00e6e8acd562..0d6ea43f0d990 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/IDocumentationCommentService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/IDocumentationCommentService.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. -#nullable disable - using System.Threading; namespace Microsoft.CodeAnalysis.LanguageService; internal interface IDocumentationCommentService { - string GetBannerText(SyntaxNode documentationCommentTriviaSyntax, int bannerLength, CancellationToken cancellationToken); + string GetBannerText(SyntaxNode? documentationCommentTriviaSyntax, int bannerLength, CancellationToken cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 7a2544c4bf028..220d7208706c5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -100,6 +100,7 @@ internal interface ISyntaxFacts bool SupportsIsNotTypeExpression(ParseOptions options); bool SupportsLocalFunctionDeclaration(ParseOptions options); bool SupportsNotPattern(ParseOptions options); + bool SupportsNullConditionalAssignment(ParseOptions options); bool SupportsRecord(ParseOptions options); bool SupportsRecordStruct(ParseOptions options); bool SupportsTargetTypedConditionalExpression(ParseOptions options); @@ -419,8 +420,6 @@ void GetPartsOfTupleExpression(SyntaxNode node, SyntaxList GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration); - // Violation. This is a feature level API. - bool ContainsInMemberBody([NotNullWhen(true)] SyntaxNode? node, TextSpan span); // Violation. This is a feature level API. TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree tree, int position, CancellationToken cancellationToken); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs index 72a9316a37c15..71cdf02df8537 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxKinds.cs @@ -56,6 +56,7 @@ internal interface ISyntaxKinds int? GlobalStatement { get; } int IfKeyword { get; } int NewKeyword { get; } + int PartialKeyword { get; } int TrueKeyword { get; } int UsingKeyword { get; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Simplification/ISimplification.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Simplification/ISimplification.cs index 22d9d03149504..347476a795392 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Simplification/ISimplification.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Simplification/ISimplification.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.AnonymousTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.AnonymousTypeSymbolKey.cs index 53b771db7aadd..b108155317187 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.AnonymousTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.AnonymousTypeSymbolKey.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -21,7 +20,7 @@ public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visi var properties = symbol.GetMembers().OfType().ToImmutableArray(); var propertyTypes = properties.SelectAsArray(p => p.Type); - var propertyNames = properties.SelectAsArray(p => p.Name); + var propertyNames = properties.SelectAsArray(p => (string?)p.Name); var propertyIsReadOnly = properties.SelectAsArray(p => p.SetMethod == null); var propertyLocations = properties.SelectAsArray(p => p.Locations.FirstOrDefault()); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BodyLevelSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BodyLevelSymbolKey.cs index 80aa37a6a9439..9f16312007ddb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BodyLevelSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BodyLevelSymbolKey.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -17,12 +16,12 @@ internal partial struct SymbolKey { private static class BodyLevelSymbolKey { - public static ImmutableArray GetBodyLevelSourceLocations(ISymbol symbol, CancellationToken cancellationToken) + public static ImmutableArray GetBodyLevelSourceLocations(ISymbol symbol, CancellationToken cancellationToken) { Contract.ThrowIfFalse(IsBodyLevelSymbol(symbol)); Contract.ThrowIfTrue(symbol.DeclaringSyntaxReferences.IsEmpty && symbol.Locations.IsEmpty); - using var _ = ArrayBuilder.GetInstance(out var result); + using var _ = ArrayBuilder.GetInstance(out var result); foreach (var location in symbol.Locations) { @@ -63,7 +62,7 @@ public static void Create(ISymbol symbol, SymbolKeyWriter visitor) var locations = GetBodyLevelSourceLocations(symbol, cancellationToken); - Contract.ThrowIfFalse(locations.All(loc => loc.IsInSource)); + Contract.ThrowIfFalse(locations.All(loc => loc != null && loc.IsInSource)); visitor.WriteLocationArray(locations.Distinct()); // and the containingSymbol/ordinal for resilience @@ -75,7 +74,7 @@ public static void Create(ISymbol symbol, SymbolKeyWriter visitor) int GetOrdinal() { - var syntaxTree = locations[0].SourceTree; + var syntaxTree = locations[0]!.SourceTree; var compilation = ((ISourceAssemblySymbol)symbol.ContainingAssembly).Compilation; // See if we can find an appropriate container for this local and attempt to find this local's index diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BuiltinOperatorSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BuiltinOperatorSymbolKey.cs index c4ff59c58d610..d2098901818c7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BuiltinOperatorSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.BuiltinOperatorSymbolKey.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ErrorTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ErrorTypeSymbolKey.cs index 319e43c9e32e0..3eba6c3e4e83a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ErrorTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ErrorTypeSymbolKey.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -50,9 +49,9 @@ public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visi /// For a symbol like System.Collections.Generic.IEnumerable, this would produce "Generic", /// "Collections", "System" /// - private static ImmutableArray GetContainingNamespaceNamesInReverse(INamespaceSymbol namespaceSymbol) + private static ImmutableArray GetContainingNamespaceNamesInReverse(INamespaceSymbol namespaceSymbol) { - using var _ = ArrayBuilder.GetInstance(out var builder); + using var _ = ArrayBuilder.GetInstance(out var builder); while (namespaceSymbol != null && namespaceSymbol.Name != "") { builder.Add(namespaceSymbol.Name); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.EventSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.EventSymbolKey.cs index a000d15c9925d..ec572d6ebba0b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.EventSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.EventSymbolKey.cs @@ -14,6 +14,7 @@ public sealed override void Create(IEventSymbol symbol, SymbolKeyWriter visitor) { visitor.WriteString(symbol.MetadataName); visitor.WriteSymbolKey(symbol.ContainingType); + visitor.WriteBoolean(symbol.PartialDefinitionPart is not null); } protected sealed override SymbolKeyResolution Resolve( @@ -21,6 +22,7 @@ protected sealed override SymbolKeyResolution Resolve( { var metadataName = reader.ReadString(); var containingTypeResolution = reader.ReadSymbolKey(contextualSymbol?.ContainingType, out var containingTypeFailureReason); + var isPartialImplementationPart = reader.ReadBoolean(); if (containingTypeFailureReason != null) { @@ -28,8 +30,18 @@ protected sealed override SymbolKeyResolution Resolve( return default; } - using var result = GetMembersOfNamedType(containingTypeResolution, metadataName); - return CreateResolution(result, $"({nameof(EventSymbolKey)} '{metadataName}' not found)", out failureReason); + using var events = GetMembersOfNamedType(containingTypeResolution, metadataName); + + if (isPartialImplementationPart) + { + for (var i = 0; i < events.Builder.Count; i++) + { + var candidate = events.Builder[i]; + events.Builder[i] = candidate.PartialImplementationPart ?? candidate; + } + } + + return CreateResolution(events, $"({nameof(EventSymbolKey)} '{metadataName}' not found)", out failureReason); } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs index 3269109e31629..e63eaf56e773c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.FunctionPointerTypeSymbolKey.cs @@ -4,8 +4,6 @@ using System.Collections.Immutable; using System.Reflection.Metadata; -using System.Runtime.CompilerServices; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs index 785458b4296dc..6b3690c569ec4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.MethodSymbolKey.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs index e6391311b7bc2..6bca171a77198 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamedTypeSymbolKey.cs @@ -2,9 +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 Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -19,21 +17,16 @@ public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visi visitor.WriteSymbolKey(symbol.ContainingSymbol); visitor.WriteString(symbol.Name); visitor.WriteInteger(symbol.Arity); - visitor.WriteString(symbol.IsFileLocal - ? symbol.DeclaringSyntaxReferences[0].SyntaxTree.FilePath - : null); + // Include the metadata name for extensions. We need this to uniquely find it when resolving. + visitor.WriteString(symbol.IsExtension ? symbol.MetadataName : null); + // Include the file path for 'file-local' types. We need this to uniquely find it when resolving. + visitor.WriteString(symbol.IsFileLocal ? symbol.DeclaringSyntaxReferences[0].SyntaxTree.FilePath : null); visitor.WriteBoolean(symbol.IsUnboundGenericType); visitor.WriteBoolean(symbol.IsNativeIntegerType); visitor.WriteBoolean(symbol.SpecialType == SpecialType.System_IntPtr); - if (!symbol.Equals(symbol.ConstructedFrom) && !symbol.IsUnboundGenericType) - { - visitor.WriteSymbolKeyArray(symbol.TypeArguments); - } - else - { - visitor.WriteSymbolKeyArray(ImmutableArray.Empty); - } + visitor.WriteSymbolKeyArray( + symbol.Equals(symbol.ConstructedFrom) || symbol.IsUnboundGenericType ? [] : symbol.TypeArguments); } protected sealed override SymbolKeyResolution Resolve( @@ -42,6 +35,7 @@ protected sealed override SymbolKeyResolution Resolve( var containingSymbolResolution = reader.ReadSymbolKey(contextualSymbol?.ContainingSymbol, out var containingSymbolFailureReason); var name = reader.ReadRequiredString(); var arity = reader.ReadInteger(); + var extensionMetadataName = reader.ReadString(); var filePath = reader.ReadString(); var isUnboundGenericType = reader.ReadBoolean(); var isNativeIntegerType = reader.ReadBoolean(); @@ -73,7 +67,8 @@ protected sealed override SymbolKeyResolution Resolve( var normalResolution = ResolveNormalNamedType( containingSymbolResolution, containingSymbolFailureReason, - name, arity, filePath, isUnboundGenericType, typeArgumentsArray, + name, extensionMetadataName, arity, filePath, + isUnboundGenericType, typeArgumentsArray, out failureReason); if (normalResolution.SymbolCount > 0) @@ -140,10 +135,11 @@ private static SymbolKeyResolution ResolveNormalNamedType( SymbolKeyResolution containingSymbolResolution, string? containingSymbolFailureReason, string name, + string? extensionMetadataName, int arity, string? filePath, bool isUnboundGenericType, - ITypeSymbol[] typeArgumentsArray, + ITypeSymbol[] typeArguments, out string? failureReason) { if (containingSymbolFailureReason != null) @@ -154,43 +150,46 @@ private static SymbolKeyResolution ResolveNormalNamedType( using var result = PooledArrayBuilder.GetInstance(); foreach (var nsOrType in containingSymbolResolution.OfType()) - { - Resolve( - result, nsOrType, name, arity, filePath, - isUnboundGenericType, typeArgumentsArray); - } + Resolve(nsOrType, result); return CreateResolution(result, $"({nameof(NamedTypeSymbolKey)} failed)", out failureReason); - } - private static void Resolve( - PooledArrayBuilder result, - INamespaceOrTypeSymbol container, - string name, - int arity, - string? filePath, - bool isUnboundGenericType, - ITypeSymbol[] typeArguments) - { - foreach (var type in container.GetTypeMembers(name, arity)) + void Resolve( + INamespaceOrTypeSymbol container, + PooledArrayBuilder result) { - // if this is a file-local type, then only resolve to a file-local type from this same file - if (filePath != null) + if (extensionMetadataName != null) { - if (!type.IsFileLocal || - // note: if we found 'IsFile' returned true, we can assume DeclaringSyntaxReferences is non-empty. - type.DeclaringSyntaxReferences[0].SyntaxTree.FilePath != filePath) + // Unfortunately, no fast index from metadata name to type, so we have to iterate all nested types. + foreach (var type in container.GetTypeMembers()) { - continue; + if (type.MetadataName == extensionMetadataName) + result.AddIfNotNull(Construct(type, isUnboundGenericType, typeArguments)); } } - else if (type.IsFileLocal) + else { - // since this key lacks a file path it can't match against a file-local type - continue; + foreach (var type in container.GetTypeMembers(name, arity)) + { + // if this is a file-local type, then only resolve to a file-local type from this same file + if (filePath != null) + { + if (!type.IsFileLocal || + // note: if we found 'IsFile' returned true, we can assume DeclaringSyntaxReferences is non-empty. + type.DeclaringSyntaxReferences[0].SyntaxTree.FilePath != filePath) + { + continue; + } + } + else if (type.IsFileLocal) + { + // since this key lacks a file path it can't match against a file-local type + continue; + } + + result.AddIfNotNull(Construct(type, isUnboundGenericType, typeArguments)); + } } - - result.AddIfNotNull(Construct(type, isUnboundGenericType, typeArguments)); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs index c9126832e26e9..7b72e2f5ddb21 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.NamespaceSymbolKey.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ParameterSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ParameterSymbolKey.cs index 77417acc4ecc2..56d5d72914c2d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ParameterSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.ParameterSymbolKey.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Immutable; -using System.Diagnostics; namespace Microsoft.CodeAnalysis; @@ -69,6 +68,9 @@ protected sealed override SymbolKeyResolution Resolve( Resolve(result, reader, metadataName, ordinal, delegateInvoke.Parameters); } + break; + case INamedTypeSymbol { IsExtension: true, ExtensionParameter: { } extensionParameter }: + Resolve(result, reader, metadataName, ordinal, [extensionParameter]); break; } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs index f94870bd8cdc3..92c7c17a25576 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.SymbolKeyWriter.cs @@ -11,7 +11,6 @@ using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; @@ -290,12 +289,10 @@ internal void WriteBooleanArray(ImmutableArray array) // annotating WriteStringArray and WriteLocationArray as allowing null elements // then causes issues where we can't pass ImmutableArrays of non-null elements -#nullable disable - - internal void WriteStringArray(ImmutableArray strings) + internal void WriteStringArray(ImmutableArray strings) => WriteArray(strings, _writeString); - internal void WriteLocationArray(ImmutableArray array) + internal void WriteLocationArray(ImmutableArray array) => WriteArray(array, _writeLocation); #nullable enable diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TupleTypeSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TupleTypeSymbolKey.cs index bdef24ce5c2b9..10114d6fcccbf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TupleTypeSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TupleTypeSymbolKey.cs @@ -2,12 +2,10 @@ // 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.Linq; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; -using System.Runtime.CompilerServices; namespace Microsoft.CodeAnalysis; @@ -24,7 +22,7 @@ public sealed override void Create(INamedTypeSymbol symbol, SymbolKeyWriter visi var isError = symbol.TupleUnderlyingType!.TypeKind == TypeKind.Error; using var _1 = ArrayBuilder.GetInstance(out var friendlyNames); - using var _2 = ArrayBuilder.GetInstance(out var locations); + using var _2 = ArrayBuilder.GetInstance(out var locations); foreach (var element in symbol.TupleElements) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TypeParameterOrdinalSymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TypeParameterOrdinalSymbolKey.cs index ce64540557095..86ae1340865cf 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TypeParameterOrdinalSymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.TypeParameterOrdinalSymbolKey.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. -using Roslyn.Utilities; - namespace Microsoft.CodeAnalysis; internal partial struct SymbolKey diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs index d993f44ba3e89..551ee7a466d5b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/SymbolKey/SymbolKey.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.Serialization; using System.Threading; @@ -149,13 +148,14 @@ public static bool CanCreate(ISymbol symbol, CancellationToken cancellationToken if (IsBodyLevelSymbol(symbol)) { var locations = BodyLevelSymbolKey.GetBodyLevelSourceLocations(symbol, cancellationToken); - if (locations.Length == 0) + var firstNonNull = locations.FirstOrDefault(l => l != null); + if (firstNonNull is null) return false; // Ensure that the tree we're looking at is actually in this compilation. It may not be in the // compilation in the case of work done with a speculative model. var compilation = ((ISourceAssemblySymbol)symbol.ContainingAssembly).Compilation; - return compilation.SyntaxTrees.Contains(locations.First().SourceTree); + return compilation.SyntaxTrees.Contains(firstNonNull.SourceTree); } return true; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AliasSymbolCache.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AliasSymbolCache.cs index db5673ac2f6ed..bd8b7f0682752 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AliasSymbolCache.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/AliasSymbolCache.cs @@ -2,11 +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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs index aa20832c3047f..04dd66c8b5ca2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/CommonFormattingHelpers.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/FixedSizeArrayBuilder.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/FixedSizeArrayBuilder.cs index 03da91ea894ee..7ed172ae159a9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/FixedSizeArrayBuilder.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/FixedSizeArrayBuilder.cs @@ -8,7 +8,6 @@ using System.Runtime.InteropServices; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; /// /// A bare-bones array builder, focused on the case of producing s where the final array diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PathMetadataUtilities.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PathMetadataUtilities.cs index 5754d73ddfbc0..723d3b6ee04ef 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PathMetadataUtilities.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/PathMetadataUtilities.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis.LanguageService; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs index 99c936f6700f8..718921282f409 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ProducerConsumer.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs index e8d75f5e52533..69a62f4531a1b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/ReferenceCountedDisposableCache.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; namespace Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs index f9400d9c0065e..47b79e3624176 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SpecializedTasks.cs @@ -7,10 +7,8 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.PooledObjects; namespace Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs index 9c664d0c861c0..b86f86f550442 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.EquivalenceVisitor.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -599,8 +598,8 @@ private bool PropertiesAreEquivalent(IPropertySymbol x, IPropertySymbol y, Dicti x.IsIndexer == y.IsIndexer && x.MetadataName == y.MetadataName && x.Parameters.Length == y.Parameters.Length && - IsPartialMethodDefinitionPart(x) == IsPartialMethodDefinitionPart(y) && - IsPartialMethodImplementationPart(x) == IsPartialMethodImplementationPart(y) && + IsPartialPropertyDefinitionPart(x) == IsPartialPropertyDefinitionPart(y) && + IsPartialPropertyImplementationPart(x) == IsPartialPropertyImplementationPart(y) && ParametersAreEquivalent(x.Parameters, y.Parameters, equivalentTypesWithDifferingAssemblies) && AreEquivalent(x.ContainingSymbol, y.ContainingSymbol, equivalentTypesWithDifferingAssemblies); } @@ -609,6 +608,8 @@ private bool EventsAreEquivalent(IEventSymbol x, IEventSymbol y, Dictionary symbol.PartialDefinitionPart != null; - private static bool IsPartialMethodDefinitionPart(IPropertySymbol symbol) + private static bool IsPartialPropertyDefinitionPart(IPropertySymbol symbol) => symbol.PartialImplementationPart != null; - private static bool IsPartialMethodImplementationPart(IPropertySymbol symbol) + private static bool IsPartialPropertyImplementationPart(IPropertySymbol symbol) + => symbol.PartialDefinitionPart != null; + + private static bool IsPartialEventDefinitionPart(IEventSymbol symbol) + => symbol.PartialImplementationPart != null; + + private static bool IsPartialEventImplementationPart(IEventSymbol symbol) => symbol.PartialDefinitionPart != null; private static TypeKind GetTypeKind(INamedTypeSymbol x) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb index 540dba1f7d0b4..31b8674d601df 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SemanticFacts/VisualBasicSemanticFacts.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports System.Collections.Immutable +Imports System.Diagnostics.CodeAnalysis Imports System.Runtime.InteropServices Imports System.Threading Imports Microsoft.CodeAnalysis @@ -328,6 +329,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return False End Function + Public Function TryGetPrimaryConstructor(typeSymbol As INamedTypeSymbol, ByRef primaryConstructor As IMethodSymbol) As Boolean Implements ISemanticFacts.TryGetPrimaryConstructor + ' VB does not support primary constructors + Return False + End Function + #If Not CODE_STYLE Then Public Function GetInterceptorSymbolAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of ISymbol) Implements ISemanticFacts.GetInterceptorSymbolAsync diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 7b2795760a82b..1a7b3fdd1e281 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -86,6 +86,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return False End Function + Public Function SupportsNullConditionalAssignment(options As ParseOptions) As Boolean Implements ISyntaxFacts.SupportsNullConditionalAssignment + Return False + End Function + Public Function ParseToken(text As String) As SyntaxToken Implements ISyntaxFacts.ParseToken Return SyntaxFactory.ParseToken(text, startStatement:=True) End Function @@ -834,58 +838,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Return Nothing End Function - Public Function ContainsInMemberBody(node As SyntaxNode, span As TextSpan) As Boolean Implements ISyntaxFacts.ContainsInMemberBody - Dim method = TryCast(node, MethodBlockBaseSyntax) - If method IsNot Nothing Then - Return method.Statements.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan(method.Statements), span) - End If - - Dim [event] = TryCast(node, EventBlockSyntax) - If [event] IsNot Nothing Then - Return [event].Accessors.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan([event].Accessors), span) - End If - - Dim [property] = TryCast(node, PropertyBlockSyntax) - If [property] IsNot Nothing Then - Return [property].Accessors.Count > 0 AndAlso ContainsExclusively(GetSyntaxListSpan([property].Accessors), span) - End If - - Dim field = TryCast(node, FieldDeclarationSyntax) - If field IsNot Nothing Then - Return field.Declarators.Count > 0 AndAlso ContainsExclusively(GetSeparatedSyntaxListSpan(field.Declarators), span) - End If - - Dim [enum] = TryCast(node, EnumMemberDeclarationSyntax) - If [enum] IsNot Nothing Then - Return [enum].Initializer IsNot Nothing AndAlso ContainsExclusively([enum].Initializer.Span, span) - End If - - Dim propStatement = TryCast(node, PropertyStatementSyntax) - If propStatement IsNot Nothing Then - Return propStatement.Initializer IsNot Nothing AndAlso ContainsExclusively(propStatement.Initializer.Span, span) - End If - - Return False - End Function - - Private Shared Function ContainsExclusively(outerSpan As TextSpan, innerSpan As TextSpan) As Boolean - If innerSpan.IsEmpty Then - Return outerSpan.Contains(innerSpan.Start) - End If - - Return outerSpan.Contains(innerSpan) - End Function - - Private Shared Function GetSyntaxListSpan(Of T As SyntaxNode)(list As SyntaxList(Of T)) As TextSpan - Debug.Assert(list.Count > 0) - Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) - End Function - - Private Shared Function GetSeparatedSyntaxListSpan(Of T As SyntaxNode)(list As SeparatedSyntaxList(Of T)) As TextSpan - Debug.Assert(list.Count > 0) - Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End) - End Function - Public Function GetMembersOfTypeDeclaration(typeDeclaration As SyntaxNode) As SyntaxList(Of SyntaxNode) Implements ISyntaxFacts.GetMembersOfTypeDeclaration Return DirectCast(typeDeclaration, TypeBlockSyntax).Members End Function diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb index 6921dc24cad4d..0641f531b5c81 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxKinds.vb @@ -73,6 +73,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService Public ReadOnly Property IfKeyword As Integer = SyntaxKind.IfKeyword Implements ISyntaxKinds.IfKeyword Public ReadOnly Property NewKeyword As Integer = SyntaxKind.NewKeyword Implements ISyntaxKinds.NewKeyword Public ReadOnly Property TrueKeyword As Integer = SyntaxKind.TrueKeyword Implements ISyntaxKinds.TrueKeyword + Public ReadOnly Property PartialKeyword As Integer = SyntaxKind.PartialKeyword Implements ISyntaxKinds.PartialKeyword Public ReadOnly Property UsingKeyword As Integer = SyntaxKind.UsingKeyword Implements ISyntaxKinds.UsingKeyword Public ReadOnly Property AliasQualifiedName As Integer? Implements ISyntaxKinds.AliasQualifiedName diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs index 5746ccb5dbe43..29460fa0a930b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/AttributeGenerator.cs @@ -2,12 +2,10 @@ // 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.Linq; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; @@ -103,7 +101,7 @@ ExpressionSyntax GenerateAttributeSyntax(TypedConstant constant) { // In the case of a string constant with value "x", see if the originating syntax was a `nameof(x)` // expression and attempt to preserve that. - if (existingSyntax?.ArgumentList != null && constant.Value is string stringValue) + if (existingSyntax?.ArgumentList != null && constant.Kind is not TypedConstantKind.Array && constant.Value is string stringValue) { foreach (var existingArgument in existingSyntax.ArgumentList.Arguments) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpCodeGenerationServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpCodeGenerationServiceFactory.cs index fca862869b8fd..cb23e9e7743d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpCodeGenerationServiceFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/CSharpCodeGenerationServiceFactory.cs @@ -11,14 +11,10 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; [ExportLanguageServiceFactory(typeof(ICodeGenerationService), LanguageNames.CSharp), Shared] -internal partial class CSharpCodeGenerationServiceFactory : ILanguageServiceFactory +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpCodeGenerationServiceFactory() : ILanguageServiceFactory { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpCodeGenerationServiceFactory() - { - } - public ILanguageService CreateLanguageService(HostLanguageServices provider) => new CSharpCodeGenerationService(provider.LanguageServices); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs index 5abbf3f7fc960..26a248502e32c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/EnumMemberGenerator.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs index 710d68afe5e25..6ce8a205e147e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/FieldGenerator.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/NamedTypeGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/NamedTypeGenerator.cs index 810a767498d1b..31c4d36df8885 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/NamedTypeGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/NamedTypeGenerator.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; -using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/OperatorGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/OperatorGenerator.cs index 0039c17873040..3df3a22f146fb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/OperatorGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/OperatorGenerator.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Collections; -using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs index fde1d2f232ee3..3b27bb15ad4e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/ParameterGenerator.cs @@ -6,7 +6,6 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/StatementGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/StatementGenerator.cs index 3e76d39f431b0..1063f9a47dc78 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/StatementGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/CodeGeneration/StatementGenerator.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis.CodeGeneration; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index d7cd0a61f63cc..2858f4a16eeed 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -759,8 +759,8 @@ public static bool IsTypeContext( position, context: null, validModifiers: SyntaxKindSet.AllMemberModifiers, - validTypeDeclarations: SyntaxKindSet.ClassInterfaceStructRecordTypeDeclarations, - canBePartial: false, + validTypeDeclarations: SyntaxKindSet.NonEnumTypeDeclarations, + canBePartial: true, cancellationToken: cancellationToken) || syntaxTree.IsLocalFunctionDeclarationContext(position, cancellationToken); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeParameterSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeParameterSymbolExtensions.cs index 70ae62d93d8bf..39dec2a10a67d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeParameterSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ITypeParameterSymbolExtensions.cs @@ -7,6 +7,7 @@ using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp.Extensions; @@ -23,21 +24,19 @@ public static SyntaxList GenerateConstraint public static SyntaxList GenerateConstraintClauses( this IEnumerable typeParameters) { - var clauses = new List(); + using var _ = ArrayBuilder.GetInstance(out var clauses); foreach (var typeParameter in typeParameters) - { AddConstraintClauses(clauses, typeParameter); - } return [.. clauses]; } private static void AddConstraintClauses( - List clauses, + ArrayBuilder clauses, ITypeParameterSymbol typeParameter) { - var constraints = new List(); + using var _ = ArrayBuilder.GetInstance(out var constraints); if (typeParameter.HasReferenceTypeConstraint) { @@ -64,15 +63,11 @@ private static void AddConstraintClauses( foreach (var type in constraintTypes) { if (type.SpecialType != SpecialType.System_Object) - { constraints.Add(TypeConstraint(type.GenerateTypeSyntax())); - } } if (typeParameter.HasConstructorConstraint) - { constraints.Add(ConstructorConstraint()); - } if (typeParameter.AllowsRefLikeType) { @@ -81,9 +76,7 @@ private static void AddConstraintClauses( } if (constraints.Count == 0) - { return; - } clauses.Add(TypeParameterConstraintClause( typeParameter.Name.ToIdentifierName(), diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/InternalExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/InternalExtensions.cs index d08422ebc6976..8f7a5c42e8f7a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/InternalExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/InternalExtensions.cs @@ -2,11 +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. -#nullable disable - using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; -using System; using System.Threading; namespace Microsoft.CodeAnalysis.CSharp; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/NameSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/NameSyntaxExtensions.cs index e6365dca83b7e..8499f073aa258 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/NameSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/NameSyntaxExtensions.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.Generic; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/SyntaxTreeExtensions.cs index a23cedffb461b..d9ba83277bf08 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/SyntaxTreeExtensions.cs @@ -6,7 +6,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/TypeDeclarationSyntaxExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/TypeDeclarationSyntaxExtensions.cs index b15d8d2e2ff26..a182161fcd7c0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/TypeDeclarationSyntaxExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/TypeDeclarationSyntaxExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Formatting/CSharpSyntaxFormattingOptionsProviders.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Formatting/CSharpSyntaxFormattingOptionsProviders.cs index 48f26ccbd62e1..3745f82189a8d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Formatting/CSharpSyntaxFormattingOptionsProviders.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Formatting/CSharpSyntaxFormattingOptionsProviders.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Formatting; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.Indenter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.Indenter.cs index ee52e25add6e0..ebf447439ff9a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.Indenter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.Indenter.cs @@ -2,15 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Indentation; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -220,8 +217,7 @@ private static IndentationResult GetIndentationBasedOnToken(Indenter indenter, S token.IsCloseBraceOfEmbeddedBlock()) { RoslynDebug.Assert( - token.Parent != null && - (token.Parent.Parent is StatementSyntax || token.Parent.Parent is ElseClauseSyntax)); + token.Parent?.Parent is StatementSyntax or ElseClauseSyntax); var embeddedStatementOwner = token.Parent.Parent; while (embeddedStatementOwner.IsEmbeddedStatement()) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.cs index 266479d1d0ba5..2016adb504076 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Indentation/CSharpIndentationService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; using System.Composition; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs index 49f49d397cb4f..c2a88b278536c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpAddImportsService.cs @@ -12,11 +12,9 @@ using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Options; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AddImport; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpCommandLineParserService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpCommandLineParserService.cs index d3ce253d31fc3..127b88668413f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpCommandLineParserService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpCommandLineParserService.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.Collections.Generic; using System.Composition; using System.Diagnostics.CodeAnalysis; @@ -21,7 +19,7 @@ public CSharpCommandLineParserService() { } - public CommandLineArguments Parse(IEnumerable arguments, string baseDirectory, bool isInteractive, string sdkDirectory) + public CommandLineArguments Parse(IEnumerable arguments, string? baseDirectory, bool isInteractive, string? sdkDirectory) { #if SCRIPTING var parser = isInteractive ? CSharpCommandLineParser.Interactive : CSharpCommandLineParser.Default; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpGeneratedCodeRecognitionService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpGeneratedCodeRecognitionService.cs index 3cc83101ac218..87bdbcdb5876a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpGeneratedCodeRecognitionService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpGeneratedCodeRecognitionService.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.Composition; using Microsoft.CodeAnalysis.GeneratedCodeRecognition; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpHeaderFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpHeaderFactsService.cs index 465b92b158ed3..e51ec21b5d970 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpHeaderFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpHeaderFactsService.cs @@ -11,11 +11,6 @@ namespace Microsoft.CodeAnalysis.CSharp; [ExportLanguageService(typeof(IHeaderFactsService), LanguageNames.CSharp), Shared] -internal class CSharpHeaderFactsServices : CSharpHeaderFacts, IHeaderFactsService -{ - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpHeaderFactsServices() - { - } -} +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class CSharpHeaderFactsServices() : CSharpHeaderFacts, IHeaderFactsService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs index 332951421c61a..c0c7c2a271e36 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpRemoveUnnecessaryImportsService.Rewriter.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Helpers.RemoveUnnecessaryImports; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs index 5b4c5b3e3ebde..23c5da364f17c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpReplaceDiscardDeclarationsWithAssignmentsService.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.Composition; using System.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs index 68f20fb393153..57dcf24f1f50e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.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. -#nullable disable - -using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -13,7 +10,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; using Microsoft.CodeAnalysis.CSharp.LanguageService; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.CSharp.Utilities; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsServiceFactory.cs index bce2c53d42150..612553bc42e42 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsServiceFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSymbolDeclarationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSymbolDeclarationService.cs index be60e6d598219..6aba9ac7d279c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSymbolDeclarationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSymbolDeclarationService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Immutable; using System.Composition; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxFactsServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxFactsServiceFactory.cs index d557682787412..3ef3d9fe56959 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxFactsServiceFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxFactsServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs index f937f38177b8a..8f67b9074a5e7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxGeneratorInternal.cs @@ -16,7 +16,6 @@ using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxKindsServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxKindsServiceFactory.cs index 71aa1577bef1f..1480f101ee7f4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxKindsServiceFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSyntaxKindsServiceFactory.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.Composition; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index 8b9aeb302b308..da96d78dfa53a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -4,7 +4,6 @@ #nullable disable -using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.cs index 7d7990d485e2d..8be0838a107e4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpTypeInferenceService.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.Composition; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/InitializeParameter/InitializeParameterHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/InitializeParameter/InitializeParameterHelpers.cs index dbd5e6e5b6286..1bce0204bd6f8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/InitializeParameter/InitializeParameterHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/InitializeParameter/InitializeParameterHelpers.cs @@ -19,7 +19,6 @@ using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.InitializeParameter; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Simplification/CSharpSimplifierOptionsProviders.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Simplification/CSharpSimplifierOptionsProviders.cs index 5c91aa8381248..a00e05637574f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Simplification/CSharpSimplifierOptionsProviders.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Simplification/CSharpSimplifierOptionsProviders.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.CSharp.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs index 9559bfd70b617..0b1707d33682c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Utilities/SyntaxKindSet.cs @@ -85,6 +85,16 @@ internal class SyntaxKindSet SyntaxKind.RecordStructDeclaration, }; + public static readonly ISet NonEnumTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) + { + SyntaxKind.ClassDeclaration, + SyntaxKind.ExtensionDeclaration, + SyntaxKind.InterfaceDeclaration, + SyntaxKind.RecordDeclaration, + SyntaxKind.RecordStructDeclaration, + SyntaxKind.StructDeclaration, + }; + public static readonly ISet ClassInterfaceRecordTypeDeclarations = new HashSet(SyntaxFacts.EqualityComparer) { SyntaxKind.InterfaceDeclaration, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/ForkingSyntaxEditorBasedCodeFixProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/ForkingSyntaxEditorBasedCodeFixProvider.cs index 90cae65a5d562..e8b6f1d6bbe88 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/ForkingSyntaxEditorBasedCodeFixProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixes/ForkingSyntaxEditorBasedCodeFixProvider.cs @@ -2,12 +2,10 @@ // 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.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/FixAllHelper.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/FixAllHelper.cs index b5f3c537124d9..882581b50d9b3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/FixAllHelper.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeFixesAndRefactorings/FixAllHelper.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Roslyn.Utilities; using FixAllScope = Microsoft.CodeAnalysis.CodeFixes.FixAllScope; namespace Microsoft.CodeAnalysis.CodeFixesAndRefactorings; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService.cs index b45ba582898e3..10d45074d8c38 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService_FindDeclaration.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService_FindDeclaration.cs index c46c83718ab33..87fff6b2c2f82 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService_FindDeclaration.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/AbstractCodeGenerationService_FindDeclaration.cs @@ -7,7 +7,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerationOptionsProviders.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerationOptionsProviders.cs index 5adc859cea244..8b1953dddd7c1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerationOptionsProviders.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerationOptionsProviders.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs index 312998cb290d1..3325f4796a16e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/CodeGenerator.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/ICodeGenerationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/ICodeGenerationService.cs index 3595fd075bed2..7483075918a9a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/ICodeGenerationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/ICodeGenerationService.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConstructorSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConstructorSymbol.cs index 4ba1d6ad75bed..3d4c238631dd2 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConstructorSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConstructorSymbol.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.Collections.Immutable; #if CODE_STYLE @@ -15,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; internal sealed class CodeGenerationConstructorSymbol( - INamedTypeSymbol containingType, + INamedTypeSymbol? containingType, ImmutableArray attributes, Accessibility accessibility, DeclarationModifiers modifiers, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConversionSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConversionSymbol.cs index 303ef6233fe75..f063c96b3f67a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConversionSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationConversionSymbol.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.Collections.Immutable; #if CODE_STYLE @@ -15,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; internal sealed class CodeGenerationConversionSymbol( - INamedTypeSymbol containingType, + INamedTypeSymbol? containingType, ImmutableArray attributes, Accessibility declaredAccessibility, DeclarationModifiers modifiers, @@ -23,7 +21,7 @@ internal sealed class CodeGenerationConversionSymbol( IParameterSymbol fromType, bool isImplicit, ImmutableArray toTypeAttributes, - string documentationCommentXml) : CodeGenerationMethodSymbol(containingType, + string? documentationCommentXml) : CodeGenerationMethodSymbol(containingType, attributes, declaredAccessibility, modifiers, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationDestructorSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationDestructorSymbol.cs index bff02db796c93..e24f5b82e6c68 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationDestructorSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationDestructorSymbol.cs @@ -2,14 +2,12 @@ // 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.Immutable; namespace Microsoft.CodeAnalysis.CodeGeneration; internal sealed class CodeGenerationDestructorSymbol( - INamedTypeSymbol containingType, + INamedTypeSymbol? containingType, ImmutableArray attributes) : CodeGenerationMethodSymbol(containingType, attributes, Accessibility.NotApplicable, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationEventSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationEventSymbol.cs index bc60a6bfd6e10..2c05b57bbc671 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationEventSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationEventSymbol.cs @@ -59,5 +59,11 @@ public override TResult Accept(SymbolVisitor null; + public IEventSymbol? PartialImplementationPart => null; + + public IEventSymbol? PartialDefinitionPart => null; + + public bool IsPartialDefinition => false; + public static ImmutableArray TypeCustomModifiers => []; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationNamespaceOrTypeSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationNamespaceOrTypeSymbol.cs index 39f916d228630..0330b8cba065d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationNamespaceOrTypeSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationNamespaceOrTypeSymbol.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.Collections.Immutable; #if CODE_STYLE diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationOperatorSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationOperatorSymbol.cs index 0a7ac510f3da9..ada9dde7c160a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationOperatorSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationOperatorSymbol.cs @@ -2,10 +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. -#nullable disable - using System.Collections.Immutable; -using Roslyn.Utilities; #if CODE_STYLE using Microsoft.CodeAnalysis.Internal.Editing; @@ -16,7 +13,7 @@ namespace Microsoft.CodeAnalysis.CodeGeneration; internal sealed class CodeGenerationOperatorSymbol( - INamedTypeSymbol containingType, + INamedTypeSymbol? containingType, ImmutableArray attributes, Accessibility accessibility, DeclarationModifiers modifiers, @@ -24,7 +21,7 @@ internal sealed class CodeGenerationOperatorSymbol( CodeGenerationOperatorKind operatorKind, ImmutableArray parameters, ImmutableArray returnTypeAttributes, - string documentationCommentXml) : CodeGenerationMethodSymbol(containingType, + string? documentationCommentXml) : CodeGenerationMethodSymbol(containingType, attributes, accessibility, modifiers, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs index 610fafd8b76b1..281eb98632cdb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/Symbols/CodeGenerationTypeSymbol.cs @@ -47,6 +47,10 @@ public ImmutableArray AllInterfaces public bool IsNativeIntegerType => false; + public bool IsExtension => false; + + public IParameterSymbol ExtensionParameter => null; + public static ImmutableArray TupleElementTypes => default; public static ImmutableArray TupleElementNames => default; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/SyntaxAnnotationExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/SyntaxAnnotationExtensions.cs index 75acc5f00a712..c291d3901e3da 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/SyntaxAnnotationExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeGeneration/SyntaxAnnotationExtensions.cs @@ -4,7 +4,6 @@ using System; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeGeneration; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/CodeRefactoringHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/CodeRefactoringHelpers.cs index e9e59ea0e3b6e..54a227a9b3410 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/CodeRefactoringHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/CodeRefactoringHelpers.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. -using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/IRefactoringHelpersService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/IRefactoringHelpersService.cs index 6f25b6784edab..abdf86630343b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/IRefactoringHelpersService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/CodeRefactorings/IRefactoringHelpersService.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Editing/ImportAdderService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Editing/ImportAdderService.cs index b836a2d0d8f01..91ee326561e99 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Editing/ImportAdderService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Editing/ImportAdderService.cs @@ -2,14 +2,12 @@ // 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.AddImport; -using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.PooledObjects; @@ -38,7 +36,6 @@ public async Task AddImportsAsync( { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var addImportsService = document.GetRequiredLanguageService(); - var codeGenerator = document.GetRequiredLanguageService(); var generator = document.GetRequiredLanguageService(); // Create a simple interval tree for simplification spans. @@ -53,13 +50,20 @@ public async Task AddImportsAsync( // // We'll dive under the parent because it overlaps with the span. But we only want to include (and dive // into) B and C not A and D. - var nodes = root.DescendantNodesAndSelf(OverlapsWithSpan).Where(OverlapsWithSpan); - if (strategy == Strategy.AddImportsFromSymbolAnnotations) - return await AddImportDirectivesFromSymbolAnnotationsAsync(document, nodes, addImportsService, generator, options, cancellationToken).ConfigureAwait(false); + { + var nodes = root.DescendantNodesAndSelf(n => OverlapsWithSpan(n) && n.ContainsAnnotations).Where(OverlapsWithSpan); + var annotatedNodes = nodes.Where(x => x.HasAnnotations(SymbolAnnotation.Kind)); + + return await AddImportDirectivesFromSymbolAnnotationsAsync(document, annotatedNodes, addImportsService, generator, options, cancellationToken).ConfigureAwait(false); + } if (strategy == Strategy.AddImportsFromSyntaxes) + { + var nodes = root.DescendantNodesAndSelf(OverlapsWithSpan).Where(OverlapsWithSpan); + return await AddImportDirectivesFromSyntaxesAsync(document, nodes, addImportsService, generator, options, cancellationToken).ConfigureAwait(false); + } throw ExceptionUtilities.UnexpectedValue(strategy); @@ -165,7 +169,7 @@ private async Task AddImportDirectivesFromSyntaxesAsync( private async Task AddImportDirectivesFromSymbolAnnotationsAsync( Document document, - IEnumerable syntaxNodes, + IEnumerable annotatedNodes, IAddImportsService addImportsService, SyntaxGenerator generator, AddImportPlacementOptions options, @@ -182,7 +186,6 @@ private async Task AddImportDirectivesFromSymbolAnnotationsAsync( #endif SyntaxNode? first = null, last = null; - var annotatedNodes = syntaxNodes.Where(x => x.HasAnnotations(SymbolAnnotation.Kind)); foreach (var annotatedNode in annotatedNodes) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/ISyntaxContextService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/ISyntaxContextService.cs index 05ca8e515ee58..a1352745bcf34 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/ISyntaxContextService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ContextQuery/ISyntaxContextService.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Threading; -using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.Shared.Extensions.ContextQuery; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs index 0fc7f4c3b746a..555379d592453 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs @@ -4,20 +4,15 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.GeneratedCodeRecognition; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.SemanticModelReuse; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; -using Microsoft.CodeAnalysis.CodeStyle; #if DEBUG using System.Collections.Immutable; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IMethodSymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IMethodSymbolExtensions.cs index 6e27c285e13e3..958462ac47447 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IMethodSymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IMethodSymbolExtensions.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.CodeAnalysis.CodeGeneration; using Microsoft.CodeAnalysis.LanguageService; @@ -163,13 +161,13 @@ public static IMethodSymbol RemoveInaccessibleAttributesAndAttributesOfTypes( method, containingType: method.ContainingType, explicitInterfaceImplementations: method.ExplicitInterfaceImplementations, - attributes: method.GetAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg), + attributes: method.GetAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg: arg), parameters: method.Parameters.SelectAsArray(static (p, arg) => CodeGenerationSymbolFactory.CreateParameterSymbol( - p.GetAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg), + p.GetAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg: arg), p.RefKind, p.IsParams, p.Type, p.Name, p.IsOptional, p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null), arg), - returnTypeAttributes: method.GetReturnTypeAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg)); + returnTypeAttributes: method.GetReturnTypeAttributes().WhereAsArray(static (a, arg) => !shouldRemoveAttribute(a, arg), arg: arg)); static bool shouldRemoveAttribute(AttributeData a, (INamedTypeSymbol[] removeAttributeTypes, ISymbol accessibleWithin) arg) => arg.removeAttributeTypes.Any(attr => attr.Equals(a.AttributeClass)) || diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IPropertySymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IPropertySymbolExtensions.cs index 2b2a1a8d5c1f3..bdec134f9580a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IPropertySymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/IPropertySymbolExtensions.cs @@ -59,7 +59,7 @@ public static IPropertySymbol RemoveInaccessibleAttributesAndAttributesOfTypes( property.Name, property.Parameters.SelectAsArray(static (p, arg) => CodeGenerationSymbolFactory.CreateParameterSymbol( - p.GetAttributes().WhereAsArray(static (a, arg) => !ShouldRemoveAttribute(a, arg), arg), + p.GetAttributes().WhereAsArray(static (a, arg) => !ShouldRemoveAttribute(a, arg), arg: arg), p.RefKind, p.IsParams, p.Type, p.Name, p.IsOptional, p.HasExplicitDefaultValue, p.HasExplicitDefaultValue ? p.ExplicitDefaultValue : null), arg), property.GetMethod, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs index 13a9669003389..cc5b2d7b12c6d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/ProjectExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/StringBuilderExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/StringBuilderExtensions.cs index e9113e2ba7338..05735ef7839b7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/StringBuilderExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/StringBuilderExtensions.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 System.Text; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions.cs index 2f943663ea358..605627f735c8c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions.cs @@ -10,7 +10,6 @@ using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; #if CODE_STYLE using DeclarationModifiers = Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs index 3e342e41ce85f..0b41c1f9c88f0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/SyntaxGeneratorExtensions_Negate.cs @@ -5,13 +5,11 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.Simplification; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/FindSymbols/LinkedFileReferenceLocationEqualityComparer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/FindSymbols/LinkedFileReferenceLocationEqualityComparer.cs index 2c6eb0fa0b091..551d15bca06ca 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/FindSymbols/LinkedFileReferenceLocationEqualityComparer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/FindSymbols/LinkedFileReferenceLocationEqualityComparer.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/AbstractIndentationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/AbstractIndentationService.cs index e89266c0d77c2..6d83b3ceff91d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/AbstractIndentationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/AbstractIndentationService.cs @@ -2,13 +2,10 @@ // 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.Threading; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Formatting.Rules; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Indentation; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/IIndentationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/IIndentationService.cs index 5643ec0963334..78d3ffaa51dbb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/IIndentationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Indentation/IIndentationService.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Indentation; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/AbstractAddImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/AbstractAddImportsService.cs index 655d0553ebb12..933c093a5c265 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/AbstractAddImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/AbstractAddImportsService.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Shared.Extensions; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/IAddImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/IAddImportsService.cs index 628274b8720bc..32e04f1b2d79a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/IAddImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/AddImports/IAddImportsService.cs @@ -3,17 +3,10 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Runtime.Serialization; using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeCleanup; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.AddImport; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/CommandLine/ICommandLineParserService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/CommandLine/ICommandLineParserService.cs index 1146d0ec6db11..662e93d076f50 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/CommandLine/ICommandLineParserService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/CommandLine/ICommandLineParserService.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. -#nullable disable - using System.Collections.Generic; namespace Microsoft.CodeAnalysis.Host; internal interface ICommandLineParserService : ILanguageService { - CommandLineArguments Parse(IEnumerable arguments, string baseDirectory, bool isInteractive, string sdkDirectory); + CommandLineArguments Parse(IEnumerable arguments, string? baseDirectory, bool isInteractive, string? sdkDirectory); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/AbstractGeneratedCodeRecognitionService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/AbstractGeneratedCodeRecognitionService.cs index efc0f1e2c912d..ba5e45e4b2486 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/AbstractGeneratedCodeRecognitionService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/AbstractGeneratedCodeRecognitionService.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.GeneratedCodeRecognition; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/IGeneratedCodeRecognitionService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/IGeneratedCodeRecognitionService.cs index ede540ccc1ecb..05f5c1c44aba1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/IGeneratedCodeRecognitionService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/GeneratedCodeRecognition/IGeneratedCodeRecognitionService.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs index c872efc5ce4ab..c065d0d30c728 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/IMoveDeclarationNearReferenceService.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsService.cs index be6a0da6fe3b4..eaa1a47979f85 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/RemoveUnnecessaryImports/AbstractRemoveUnnecessaryImportsService.cs @@ -4,11 +4,8 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Formatting; -using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/ReplaceDiscardDeclarationsWithAssignments/IReplaceDiscardDeclarationsWithAssignmentsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/ReplaceDiscardDeclarationsWithAssignments/IReplaceDiscardDeclarationsWithAssignmentsService.cs index 9fb096ff9995e..260b9956e815d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/ReplaceDiscardDeclarationsWithAssignments/IReplaceDiscardDeclarationsWithAssignmentsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/ReplaceDiscardDeclarationsWithAssignments/IReplaceDiscardDeclarationsWithAssignmentsService.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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index c3682a87189e6..4274201c8822f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -257,6 +258,9 @@ public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode public IPreprocessingSymbol GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) => SemanticFacts.GetPreprocessingSymbol(semanticModel, node); + public bool TryGetPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol primaryConstructor) + => SemanticFacts.TryGetPrimaryConstructor(typeSymbol, out primaryConstructor); + #if !CODE_STYLE public Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs index 129fb1bf548d3..bba14b60f1fd4 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxFactsService/ISyntaxFactsService.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.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs index 271db0c4aaf77..01f6344bf9fe6 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SyntaxGeneratorInternalExtensions/SyntaxGeneratorInternal.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.LanguageService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.AbstractTypeInferrer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.AbstractTypeInferrer.cs index 8880a61959ed0..69caff6ac6b41 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.AbstractTypeInferrer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.AbstractTypeInferrer.cs @@ -2,14 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.LanguageService.TypeInferenceService; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.cs index fd197962297c5..613b33ec07caa 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/AbstractTypeInferenceService.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.Collections.Immutable; using System.Linq; using System.Threading; @@ -15,7 +13,7 @@ internal abstract partial class AbstractTypeInferenceService : ITypeInferenceSer protected abstract AbstractTypeInferrer CreateTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken); private static ImmutableArray InferTypeBasedOnNameIfEmpty( - SemanticModel semanticModel, ImmutableArray result, string nameOpt) + SemanticModel semanticModel, ImmutableArray result, string? nameOpt) { if (result.IsEmpty && nameOpt != null) { @@ -26,7 +24,7 @@ private static ImmutableArray InferTypeBasedOnNameIfEmpty( } private static ImmutableArray InferTypeBasedOnNameIfEmpty( - SemanticModel semanticModel, ImmutableArray result, string nameOpt) + SemanticModel semanticModel, ImmutableArray result, string? nameOpt) { if (result.IsEmpty && nameOpt != null) { @@ -80,7 +78,7 @@ private static bool Matches(string name, string prefix) public ImmutableArray InferTypes( SemanticModel semanticModel, int position, - string nameOpt, CancellationToken cancellationToken) + string? nameOpt, CancellationToken cancellationToken) { var result = CreateTypeInferrer(semanticModel, cancellationToken) .InferTypes(position) @@ -92,7 +90,7 @@ public ImmutableArray InferTypes( public ImmutableArray InferTypes( SemanticModel semanticModel, SyntaxNode expression, - string nameOpt, CancellationToken cancellationToken) + string? nameOpt, CancellationToken cancellationToken) { var result = CreateTypeInferrer(semanticModel, cancellationToken) .InferTypes(expression) @@ -104,7 +102,7 @@ public ImmutableArray InferTypes( public ImmutableArray GetTypeInferenceInfo( SemanticModel semanticModel, int position, - string nameOpt, CancellationToken cancellationToken) + string? nameOpt, CancellationToken cancellationToken) { var result = CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(position); return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt); @@ -112,7 +110,7 @@ public ImmutableArray GetTypeInferenceInfo( public ImmutableArray GetTypeInferenceInfo( SemanticModel semanticModel, SyntaxNode expression, - string nameOpt, CancellationToken cancellationToken) + string? nameOpt, CancellationToken cancellationToken) { var result = CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(expression); return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/ITypeInferenceService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/ITypeInferenceService.cs index b3eda1cefe41f..4fd105c2c36f1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/ITypeInferenceService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/TypeInferenceService/ITypeInferenceService.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.Collections.Immutable; using System.Threading; using Microsoft.CodeAnalysis.Host; @@ -26,11 +24,11 @@ namespace Microsoft.CodeAnalysis.LanguageService; /// internal interface ITypeInferenceService : ILanguageService { - ImmutableArray InferTypes(SemanticModel semanticModel, SyntaxNode expression, string nameOpt, CancellationToken cancellationToken); - ImmutableArray InferTypes(SemanticModel semanticModel, int position, string nameOpt, CancellationToken cancellationToken); + ImmutableArray InferTypes(SemanticModel semanticModel, SyntaxNode expression, string? nameOpt, CancellationToken cancellationToken); + ImmutableArray InferTypes(SemanticModel semanticModel, int position, string? nameOpt, CancellationToken cancellationToken); - ImmutableArray GetTypeInferenceInfo(SemanticModel semanticModel, int position, string nameOpt, CancellationToken cancellationToken); - ImmutableArray GetTypeInferenceInfo(SemanticModel semanticModel, SyntaxNode expression, string nameOpt, CancellationToken cancellationToken); + ImmutableArray GetTypeInferenceInfo(SemanticModel semanticModel, int position, string? nameOpt, CancellationToken cancellationToken); + ImmutableArray GetTypeInferenceInfo(SemanticModel semanticModel, SyntaxNode expression, string? nameOpt, CancellationToken cancellationToken); } internal readonly record struct TypeInferenceInfo(ITypeSymbol InferredType, bool IsParams) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Progress/CodeAnalysisProgressExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Progress/CodeAnalysisProgressExtensions.cs index aa8d667c69311..c375e84c4bc74 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Progress/CodeAnalysisProgressExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Progress/CodeAnalysisProgressExtensions.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using Microsoft.CodeAnalysis.Shared.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameActionAnnotation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameActionAnnotation.cs index 470faf912bc49..3a1cf60e23f3a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameActionAnnotation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameActionAnnotation.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 Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Rename.ConflictEngine; @@ -16,8 +14,8 @@ namespace Microsoft.CodeAnalysis.Rename.ConflictEngine; internal sealed class RenameActionAnnotation( TextSpan originalSpan, bool isRenameLocation, - string prefix, - string suffix, + string? prefix, + string? suffix, bool isOriginalTextLocation, RenameDeclarationLocationReference[] renameDeclarationLocations, bool isNamespaceDeclarationReference, @@ -44,13 +42,13 @@ internal sealed class RenameActionAnnotation( /// When replacing the annotated token this string will be prepended to the token's value. This is used when renaming compiler /// generated fields and methods backing properties (e.g. "get_X" or "_X" for property "X"). /// - public readonly string Prefix = prefix; + public readonly string? Prefix = prefix; /// /// When replacing the annotated token this string will be appended to the token's value. This is used when renaming compiler /// generated types whose names are derived from user given names (e.g. "XEventHandler" for event "X"). /// - public readonly string Suffix = suffix; + public readonly string? Suffix = suffix; /// /// A single dimensional array of annotations to verify after rename. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameAnnotation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameAnnotation.cs index 4a0f346335e0a..48047015ce8d9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameAnnotation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameAnnotation.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 - namespace Microsoft.CodeAnalysis.Rename.ConflictEngine; internal class RenameAnnotation diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameInvalidIdentifierAnnotation.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameInvalidIdentifierAnnotation.cs index fd4aa97c58179..af5c8811f38df 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameInvalidIdentifierAnnotation.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Rename/Annotations/RenameInvalidIdentifierAnnotation.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 - namespace Microsoft.CodeAnalysis.Rename.ConflictEngine; internal sealed class RenameInvalidIdentifierAnnotation : RenameAnnotation diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.IExpressionRewriter.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.IExpressionRewriter.cs index 061738fe22064..c19046e1a453e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.IExpressionRewriter.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.IExpressionRewriter.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.Threading; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.cs index 6cab3564d4759..c32ba6f97368b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractReducer.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 Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.Simplification; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractSimplificationService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractSimplificationService.cs index abc030986c383..cf1f0e196799b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractSimplificationService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/AbstractSimplificationService.cs @@ -196,7 +196,7 @@ async ValueTask ReduceOneNodeOrTokenAsync( cancellationToken.ThrowIfCancellationRequested(); using var rewriter = reducer.GetOrCreateRewriter(); - rewriter.Initialize(document.Project.ParseOptions, options, cancellationToken); + rewriter.Initialize(document.Project.ParseOptions!, options, cancellationToken); do { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs index c9bde8bce7b1c..fe4d9aa307878 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Simplification/SimplificationHelpers.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Linq.Expressions; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs index abd63a0705eb1..4706251f3c9eb 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Utilities/ParsedDocument.cs @@ -2,16 +2,12 @@ // 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.Reflection.Metadata; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguageMetadata.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguageMetadata.cs index a457a27adbf0e..b17df7eb6a8a3 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguageMetadata.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguageMetadata.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 - namespace Microsoft.CodeAnalysis.Host.Mef; /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguagesMetadata.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguagesMetadata.cs index a0ab7c8d479f6..00debe8a7803b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguagesMetadata.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/ILanguagesMetadata.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.Collections.Generic; namespace Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/IMefHostExportProvider.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/IMefHostExportProvider.cs index f327d6b68d4cb..a65b49295d2a0 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/IMefHostExportProvider.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/IMefHostExportProvider.cs @@ -2,11 +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. -#nullable disable - using System; using System.Collections.Generic; -using System.Linq; namespace Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/MefConstruction.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/MefConstruction.cs index d5d26a219928a..9a32aaa6c1a8b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/MefConstruction.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Workspace/Mef/MefConstruction.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 - namespace Microsoft.CodeAnalysis.Host.Mef; internal static class MefConstruction diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs index 562598812ed69..aa3dbc8befe1c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs @@ -4,7 +4,6 @@ using System; using System.Composition; -using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Host.Mef; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicHeaderFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicHeaderFactsService.vb index 0ebc3730ce03d..dc22c820aa3be 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicHeaderFactsService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicHeaderFactsService.vb @@ -8,7 +8,7 @@ Imports Microsoft.CodeAnalysis.LanguageService Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService - Friend Class VisualBasicHeaderFactsService + Friend NotInheritable Class VisualBasicHeaderFactsService Inherits VisualBasicHeaderFacts Implements IHeaderFactsService diff --git a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb index a51af9b5cd3bf..c0aa21a33191b 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeGeneration/VisualBasicSyntaxGenerator.vb @@ -2368,8 +2368,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Return VisualBasicAccessibilityFacts.GetModifierTokens(declaration) End Function - Public Overrides Function WithModifiers(declaration As SyntaxNode, modifiers As DeclarationModifiers) As SyntaxNode - Return Isolate(declaration, Function(d) Me.WithModifiersInternal(d, modifiers)) + Friend Overrides Function WithModifiers(Of TSyntaxNode As SyntaxNode)(declaration As TSyntaxNode, modifiers As DeclarationModifiers) As TSyntaxNode + Return DirectCast(Isolate(declaration, Function(d) Me.WithModifiersInternal(d, modifiers)), TSyntaxNode) End Function Private Function WithModifiersInternal(declaration As SyntaxNode, modifiers As DeclarationModifiers) As SyntaxNode